Merge pull request #36 from tschaub/require

Use Rhino's require implementation.
This commit is contained in:
Michael Mathews 2011-09-22 23:20:23 -07:00
commit a9574d413b
22 changed files with 2301 additions and 2555 deletions

2
jsdoc
View File

@ -3,6 +3,6 @@
# rhino discards the path to the current script file, so we must add it back # rhino discards the path to the current script file, so we must add it back
PWD=`pwd` PWD=`pwd`
BASEDIR=`dirname $0` BASEDIR=`dirname $0`
java -classpath ${BASEDIR}/lib/js.jar org.mozilla.javascript.tools.shell.Main ${BASEDIR}/jsdoc.js --dirname=${PWD}/${BASEDIR} $@ java -classpath ${BASEDIR}/lib/js.jar org.mozilla.javascript.tools.shell.Main -modules ${BASEDIR}/node_modules -modules ${BASEDIR}/rhino_modules ${BASEDIR}/jsdoc.js --dirname=${PWD}/${BASEDIR} $@
#java -classpath ${BASEDIR}/lib/js.jar org.mozilla.javascript.tools.debugger.Main -debug ${BASEDIR}/jsdoc.js --dirname=${PWD}/${BASEDIR} $@ #java -classpath ${BASEDIR}/lib/js.jar org.mozilla.javascript.tools.debugger.Main -debug ${BASEDIR}/jsdoc.js --dirname=${PWD}/${BASEDIR} $@

View File

@ -30,7 +30,6 @@ for (var i = 0; i < arguments.length; i++) {
} }
} }
load(__dirname + '/lib/require.js');
load(__dirname + '/lib/rhino-shim.js'); load(__dirname + '/lib/rhino-shim.js');
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//

View File

@ -1,222 +0,0 @@
/*
Rhino-Require is Public Domain
<http://en.wikipedia.org/wiki/Public_Domain>
The author or authors of this code dedicate any and all copyright interest
in this code to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and successors. We
intend this dedication to be an overt act of relinquishment in perpetuity of
all present and future rights to this code under copyright law.
*/
(function(global) {
var require = global.require = function(id) {
if (typeof arguments[0] !== 'string') throw 'USAGE: require(moduleId)';
var moduleContent = '',
moduleUrl;
moduleUrl = require.resolve(id);
moduleContent = '';
var file = new java.io.File(moduleUrl);
try {
var scanner = new java.util.Scanner(file).useDelimiter("\\Z");
moduleContent = String( scanner.next() );
}
catch(e) {
throw 'Unable to read file at: '+moduleUrl+', '+e;
}
if (moduleContent) {
try {
var f = new Function('require', 'exports', 'module', '__dirname', moduleContent),
exports = require.cache[moduleUrl] || {},
module = { id: id, uri: moduleUrl, exports: exports };
require._root.unshift(toDir(moduleUrl));
(function(__dirname) {
/*debug*///var lineno=1;print('\n== '+moduleUrl+' ===============\n1'+moduleContent.replace(/(\n)/g, function(m, i){return '\n'+(++lineno);}));
f.call({}, require, exports, module, __dirname);
})(require._root[0]);
require._root.shift();
}
catch(e) {
throw 'Unable to require source code from "' + moduleUrl + '": ' + e.toSource();
}
exports = module.exports || exports;
require.cache[id] = exports;
}
else {
throw 'The requested module cannot be returned: no content for id: "' + id + '" in paths: ' + require.paths.join(', ');
}
return exports;
}
require._root = [__dirname]; // the dir of the script that is calling require()
require.paths = [];
require.cache = {}; // cache module exports. Like: {id: exported}
var SLASH = Packages.java.io.File.separator;
/** Given a module id, try to find the path to the associated module.
*/
require.resolve = function(id) {
var parts = id.match(/^(\.\/|\/)?(.+)$/),
isRelative = false,
isAbsolute = false,
isInModule = false,
basename = id,
url = '';
if (parts) {
isRelative = parts[1] === './';
isAbsolute = parts[1] === '/';
isInModule = !(isRelative || isAbsolute);
basename = parts[2];
}
if (typeof basename === 'undefined') {
throw new Error('Malformed module identifier: '+id);
}
if (isAbsolute) {
rootedId = id;
}
else if (isRelative) {
var root = require._root[0],
rootedId = root + '/' + basename;
}
if (rootedId) {
if ( url = loadAsFile(rootedId) ) { return url; }
else if ( url = loadAsDir(rootedId) ) { return url; }
}
else if (isInModule) {
var url,
paths = require.paths;
for (var i = 0, len = paths.length; i < len; i++) {
rootedId = paths[i] + '/' + basename;
if ( url = loadAsFile(rootedId) ) { return url; }
else if ( url = loadAsDir(rootedId) ) { return url; }
}
if (url = findInNodemodules(require._root[0], basename, 'rhino_modules')) { return url; }
if (url = findInNodemodules(require._root[0], basename, 'node_modules')) { return url; }
}
throw new Error('Module not found: '+id);
}
function loadAsFile(id) {
if ( isFile(id) ) { return id; }
if ( isFile(id + '.js') ) { return id + '.js'; }
}
function loadAsDir(id) {
// look for the "main" property of the package.json file
if ( isFile(id + '/' + 'package.json') ) {
var packageJson = readFileSync(id + '/' + 'package.json', 'utf-8');
eval( 'packageJson = '+ packageJson);
if (packageJson.hasOwnProperty('main')) {
var main = deDotPath(id + '/' + packageJson.main);
return require.resolve(main);
}
}
if ( isFile(id + '/' + 'index.js') ) {
return id + '/' + 'index.js';
}
}
function findInNodemodules(root, id, moduleFolderName) {
var dirs = root.split('/'),
dir = '',
rootedId;
while (dirs.length) {
dir = dirs.join('/');
rootedId = dir + '/' + moduleFolderName + '/' + id;
if ( url = loadAsFile(rootedId) ) { return url; }
else if ( url = loadAsDir(rootedId) ) { return url; }
dirs.pop();
}
}
/** Given a path, return the base directory of that path.
@example toDir('/foo/bar/somefile.js'); => '/foo/bar'
*/
function toDir(path) {
var file = new java.io.File(path);
if (file.isDirectory()) {
return path;
}
var parts = path.split('/');
parts.pop();
return parts.join('/');
}
/** Returns true if the given path exists and is a file.
*/
function isFile(path) {
var file = new java.io.File(path);
if (file.isFile()) {
return true;
}
return false;
}
/** Returns true if the given path exists and is a directory.
*/
function isDir(path) {
var file = new java.io.File(path);
if (file.isDirectory()) {
return true;
}
return false;
}
/**
Resolve dots in filepaths.
*/
function deDotPath(path) {
return String(path)
.replace(/(\/|\\)[^\/\\]+\/\.\.(\/|\\)/g, '/')
.replace(/(\/|\\)\.(\/|\\|$)/g, '/');
}
function readFileSync(filename, encoding, callback) {
if (typeof arguments[1] === 'function') {
encoding = null;
callback = arguments[1];
}
encoding = encoding || java.lang.System.getProperty('file.encoding');
try {
var content = new java.util.Scanner(
new java.io.File(filename),
encoding
).useDelimiter("\\Z");
return String( content.next() );
}
catch (e) {
return '';
}
}
})(this);

View File

@ -1,10 +1,10 @@
function readFileSync(filename, encoding) { exports.readFileSync = function(filename, encoding) {
encoding = encoding || 'utf-8'; encoding = encoding || 'utf-8';
return readFile(filename, encoding); return readFile(filename, encoding);
} };
function readdirSync(path) { var readdirSync = exports.readdirSync = function(path) {
var dir, var dir,
files; files;
@ -14,9 +14,9 @@ function readdirSync(path) {
files = dir.list(); files = dir.list();
return files; return files;
} };
function ls(dir, recurse, _allFiles, _path) { var ls = exports.ls = function(dir, recurse, _allFiles, _path) {
var files, var files,
file; file;
@ -56,9 +56,9 @@ function ls(dir, recurse, _allFiles, _path) {
} }
return _allFiles; return _allFiles;
} };
function stat(path, encoding) { var stat = exports.stat = function(path, encoding) {
var f = new java.io.File(path) var f = new java.io.File(path)
return { return {
isFile: function() { isFile: function() {
@ -69,9 +69,9 @@ function stat(path, encoding) {
} }
} }
} };
function mkPath(/**Array*/ path) { exports.mkPath = function(/**Array*/ path) {
if (path.constructor != Array) path = path.split(/[\\\/]/); if (path.constructor != Array) path = path.split(/[\\\/]/);
var make = ""; var make = "";
for (var i = 0, l = path.length; i < l; i++) { for (var i = 0, l = path.length; i < l; i++) {
@ -80,7 +80,7 @@ function mkPath(/**Array*/ path) {
makeDir(make); makeDir(make);
} }
} }
} };
function makeDir(/**string*/ path) { function makeDir(/**string*/ path) {
var dirPath = toDir(path); var dirPath = toDir(path);
@ -102,7 +102,7 @@ function exists(path) {
return true; return true;
} }
function toDir(path) { var toDir = exports.toDir = function(path) {
var f = new java.io.File(path); var f = new java.io.File(path);
if (f.isDirectory()){ if (f.isDirectory()){
@ -113,9 +113,9 @@ function toDir(path) {
parts.pop(); parts.pop();
return parts.join('/'); return parts.join('/');
} };
function copyFile(inFile, outDir, fileName) { exports.copyFile = function(inFile, outDir, fileName) {
if (fileName == null) fileName = toFile(inFile); if (fileName == null) fileName = toFile(inFile);
outDir = toDir(outDir); outDir = toDir(outDir);
@ -131,14 +131,14 @@ function copyFile(inFile, outDir, fileName) {
} }
bos.close(); bos.close();
bis.close(); bis.close();
} };
function toFile(path) { function toFile(path) {
var parts = path.split(/[\\\/]/); var parts = path.split(/[\\\/]/);
return parts.pop(); return parts.pop();
} }
function writeFileSync(filename, data, encoding) { exports.writeFileSync = function(filename, data, encoding) {
encoding = encoding || 'utf-8'; encoding = encoding || 'utf-8';
var out = new Packages.java.io.PrintWriter( var out = new Packages.java.io.PrintWriter(
@ -155,16 +155,4 @@ function writeFileSync(filename, data, encoding) {
out.flush(); out.flush();
out.close(); out.close();
} }
} };
module.exports = {
readFileSync: readFileSync,
writeFileSync: writeFileSync,
readdirSync: readdirSync,
stat: stat,
ls: ls,
mkPath: mkPath,
toDir: toDir,
copyFile: copyFile
};

View File

@ -4,67 +4,64 @@
@author Michael Mathews <micmath@gmail.com> @author Michael Mathews <micmath@gmail.com>
@license Apache License 2.0 - See file 'LICENSE.md' in this project. @license Apache License 2.0 - See file 'LICENSE.md' in this project.
*/ */
(function() {
// requires docs to have been indexed: docs.index must be defined here // requires docs to have been indexed: docs.index must be defined here
/** /**
Take a copy of the docs for borrowed symbols and attach them to the Take a copy of the docs for borrowed symbols and attach them to the
docs for the borrowing symbol. This process changes the symbols involved, docs for the borrowing symbol. This process changes the symbols involved,
moving docs from the "borrowed" array and into the general docs, then moving docs from the "borrowed" array and into the general docs, then
deleting the "borrowed" array. deleting the "borrowed" array.
*/ */
exports.resolveBorrows = function(docs) { exports.resolveBorrows = function(docs) {
if (!docs.index) { if (!docs.index) {
throw 'Docs has not been indexed: docs.index must be defined here.'; throw 'Docs has not been indexed: docs.index must be defined here.';
}
docs.forEach(function(doc) {
if (doc.borrowed) {
doc.borrowed.forEach(function(b, i) {
var lent = docs.index[b.from], // lent is an array
asName = b['as'] || b.from;
if (lent) {
var cloned = doop(lent);
cloned.forEach(function(clone) {
asName = asName.replace(/^prototype\./, '#');
var parts = asName.split('#');
if (parts.length === 2) clone.scope = 'instance';
else clone.scope = 'static';
asName = parts.pop();
clone.name = asName;
clone.memberof = doc.longname;
clone.longname = clone.memberof + (clone.scope === 'instance'? '#': '.') + clone.name;
docs.push(clone);
});
}
});
delete doc.borrowed;
}
});
} }
/** docs.forEach(function(doc) {
Deep clone a simple object. if (doc.borrowed) {
@private doc.borrowed.forEach(function(b, i) {
*/ var lent = docs.index[b.from], // lent is an array
function doop(o) { asName = b['as'] || b.from;
if (o instanceof Object && o.constructor != Function) {
var clone = o instanceof Array ? [] : {}, prop; if (lent) {
var cloned = doop(lent);
for (prop in o){
if ( o.hasOwnProperty(prop) ) { cloned.forEach(function(clone) {
clone[prop] = (o[prop] instanceof Object)? doop(o[prop]) : o[prop]; asName = asName.replace(/^prototype\./, '#');
var parts = asName.split('#');
if (parts.length === 2) clone.scope = 'instance';
else clone.scope = 'static';
asName = parts.pop();
clone.name = asName;
clone.memberof = doc.longname;
clone.longname = clone.memberof + (clone.scope === 'instance'? '#': '.') + clone.name;
docs.push(clone);
});
} }
} });
return clone;
delete doc.borrowed;
} }
return o; });
}; }
})(); /**
Deep clone a simple object.
@private
*/
function doop(o) {
if (o instanceof Object && o.constructor != Function) {
var clone = o instanceof Array ? [] : {}, prop;
for (prop in o){
if ( o.hasOwnProperty(prop) ) {
clone[prop] = (o[prop] instanceof Object)? doop(o[prop]) : o[prop];
}
}
return clone;
}
return o;
};

View File

@ -10,298 +10,296 @@
@requires jsdoc/name @requires jsdoc/name
@requires jsdoc/tag/dictionary @requires jsdoc/tag/dictionary
*/ */
(function() {
var jsdoc = {
tag: {
Tag: require('jsdoc/tag').Tag,
dictionary: require('jsdoc/tag/dictionary')
},
name: require('jsdoc/name')
};
/**
@class
@classdesc Represents a single JSDoc comment.
@param {string} docletSrc - The raw source code of the jsdoc comment.
@param {object=} meta - Properties describing the code related to this comment.
*/
exports.Doclet = function(docletSrc, meta) {
var newTags = [];
/** The original text of the comment from the source code. */
this.comment = docletSrc;
this.setMeta(meta);
docletSrc = unwrap(docletSrc);
docletSrc = fixDescription(docletSrc);
newTags = toTags.call(this, docletSrc); var jsdoc = {
tag: {
Tag: require('jsdoc/tag').Tag,
dictionary: require('jsdoc/tag/dictionary')
},
name: require('jsdoc/name')
};
/**
@class
@classdesc Represents a single JSDoc comment.
@param {string} docletSrc - The raw source code of the jsdoc comment.
@param {object=} meta - Properties describing the code related to this comment.
*/
exports.Doclet = function(docletSrc, meta) {
var newTags = [];
/** The original text of the comment from the source code. */
this.comment = docletSrc;
this.setMeta(meta);
docletSrc = unwrap(docletSrc);
docletSrc = fixDescription(docletSrc);
for (var i = 0, leni = newTags.length; i < leni; i++) { newTags = toTags.call(this, docletSrc);
this.addTag(newTags[i].title, newTags[i].text);
}
this.postProcess();
}
/** Called once after all tags have been added. */
exports.Doclet.prototype.postProcess = function() {
if (!this.preserveName) { jsdoc.name.resolve(this); }
if (this.name && !this.longname) {
this.setLongname(this.name);
}
if (this.memberof === '') {
delete(this.memberof);
}
if (!this.kind && this.meta && this.meta.code) {
this.addTag( 'kind', codetypeToKind(this.meta.code.type) );
}
}
/** Add a tag to this doclet.
@param {string} title - The title of the tag being added.
@param {string} [text] - The text of the tag being added.
*/
exports.Doclet.prototype.addTag = function(title, text) {
var tagDef = jsdoc.tag.dictionary.lookUp(title),
newTag = new jsdoc.tag.Tag(title, text, this.meta);
if (tagDef && tagDef.onTagged) { for (var i = 0, leni = newTags.length; i < leni; i++) {
tagDef.onTagged(this, newTag) this.addTag(newTags[i].title, newTags[i].text);
}
if (!tagDef) {
this.tags = this.tags || [];
this.tags.push(newTag);
}
applyTag.call(this, newTag);
}
/** Set the `memberof` property of this doclet.
@param {string} sid - The longname of the symbol that this doclet is a member of.
*/
exports.Doclet.prototype.setMemberof = function(sid) {
if (/^<global>\.?/.test(sid)) { sid = sid.replace(/^<global>.?/, ''); }
/**
The longname of the symbol that contains this one, if any.
@type string
*/
this.memberof = sid.replace(/\.prototype/g, '#');
}
/** Set the `longname` property of this doclet.
@param {string} name
*/
exports.Doclet.prototype.setLongname = function(name) {
if (/^<global>\.?/.test(name)) { name = name.replace(/^<global>\.?/, ''); }
/**
The fully resolved symbol name.
@type string
*/
this.longname = name;
if (jsdoc.tag.dictionary.isNamespace(this.kind)) {
this.longname = jsdoc.name.applyNamespace(this.longname, this.kind);
}
}
/** Add a symbol to this doclet's `borrowed` array.
@param {string} source - The longname of the symbol that is the source.
@param {string} target - The name the symbol is being assigned to.
*/
exports.Doclet.prototype.borrow = function(source, target) {
var about = {from: source};
if (target) about.as = target;
if (!this.borrowed) {
/**
A list of symbols that are borrowed by this one, if any.
@type Array.<string>
*/
this.borrowed = [];
}
this.borrowed.push(about);
}
exports.Doclet.prototype.mix = function(source) {
if (!this.mixes) {
/**
A list of symbols that are mixed into this one, if any.
@type Array.<string>
*/
this.mixes = [];
}
this.mixes.push(source);
}
/** Add a symbol to this doclet's `augments` array.
@param {string} base - The longname of the base symbol.
*/
exports.Doclet.prototype.augment = function(base) {
if (!this.augments) {
/**
A list of symbols that are augmented by this one, if any.
@type Array.<string>
*/
this.augments = [];
}
this.augments.push(base);
}
/**
Set the `meta` property of this doclet.
@param {object} meta
*/
exports.Doclet.prototype.setMeta = function(meta) {
if (!this.meta) {
/**
Information about the source code associated with this doclet.
@namespace
*/
this.meta = {};
}
if (meta.lineno) {
/**
The line number of the code associated with this doclet.
@type number
*/
this.meta.lineno = meta.lineno;
}
if (meta.lineno) {
/**
The name of the file containing the code associated with this doclet.
@type string
*/
this.meta.filename = meta.filename;
}
/**
Information about the code symbol.
@namespace
*/
this.meta.code = (this.meta.code || {});
if (meta.id) this.meta.code.id = meta.id;
if (meta.code) {
if (meta.code.name) {
/** The name of the symbol in the source code. */
this.meta.code.name = meta.code.name;
}
if (meta.code.type) {
/** The type of the symbol in the source code. */
this.meta.code.type = meta.code.type;
}
if (meta.code.node) {
this.meta.code.node = meta.code.node;
}
if (meta.code.funcscope) {
this.meta.code.funcscope = meta.code.funcscope;
}
if (meta.code.value) {
/** The value of the symbol in the source code. */
this.meta.code.value = meta.code.value;
}
}
}
function applyTag(tag) {
if (tag.title === 'name') {
this.name = tag.value;
}
if (tag.title === 'kind') {
this.kind = tag.value;
}
if (tag.title === 'description') {
this.description = tag.value;
}
if (tag.title === 'scope') {
this.scope = tag.value;
}
}
// use the meta info about the source code to guess what the doclet kind should be
function codetypeToKind(type) {
var kind = (type || '').toLowerCase();
if (kind !== 'function') {
return 'property';
}
return kind;
} }
/** this.postProcess();
Convert the raw source of the doclet comment into an array of Tag objects. }
@private
*/
function toTags(docletSrc) {
var tagSrcs,
tags = [];
docletSrc = unwrap(docletSrc);
tagSrcs = split(docletSrc);
for each(tagSrc in tagSrcs) {
tags.push( {title: tagSrc.title, text: tagSrc.text} );
}
return tags;
}
function unwrap(docletSrc) {
if (!docletSrc) { return ''; }
// note: keep trailing whitespace for @examples
// extra opening/closing stars are ignored
// left margin is considered a star and a space
// use the /m flag on regex to avoid having to guess what this platform's newline is
docletSrc =
docletSrc.replace(/^\/\*\*+/, '') // remove opening slash+stars
.replace(/\**\*\/$/, "\\Z") // replace closing star slash with end-marker
.replace(/^\s*(\* ?|\\Z)/gm, '') // remove left margin like: spaces+star or spaces+end-marker
.replace(/\s*\\Z$/g, ''); // remove end-marker
return docletSrc; /** Called once after all tags have been added. */
} exports.Doclet.prototype.postProcess = function() {
if (!this.preserveName) { jsdoc.name.resolve(this); }
function fixDescription(docletSrc) { if (this.name && !this.longname) {
if (!/^\s*@/.test(docletSrc)) { this.setLongname(this.name);
docletSrc = '@description ' + docletSrc; }
} if (this.memberof === '') {
return docletSrc; delete(this.memberof);
} }
if (!this.kind && this.meta && this.meta.code) {
function split(docletSrc) { this.addTag( 'kind', codetypeToKind(this.meta.code.type) );
var tagSrcs = []; }
}
// split out the basic tags, keep surrounding whitespace
// like: @tagTitle tagBody
docletSrc
.replace(/^(\s*)@(\S)/gm, '$1\\@$2') // replace splitter ats with an arbitrary sequence
.split('\\@') // then split on that arbitrary sequence
.forEach(function($) {
if ($) {
var parsedTag = $.match(/^(\S+)(:?\s+(\S[\s\S]*))?/);
if (parsedTag) {
var [, tagTitle, tagText] = parsedTag;
if (tagTitle) { /** Add a tag to this doclet.
tagSrcs.push({ @param {string} title - The title of the tag being added.
title: tagTitle, @param {string} [text] - The text of the tag being added.
text: tagText */
}); exports.Doclet.prototype.addTag = function(title, text) {
} var tagDef = jsdoc.tag.dictionary.lookUp(title),
newTag = new jsdoc.tag.Tag(title, text, this.meta);
if (tagDef && tagDef.onTagged) {
tagDef.onTagged(this, newTag)
}
if (!tagDef) {
this.tags = this.tags || [];
this.tags.push(newTag);
}
applyTag.call(this, newTag);
}
/** Set the `memberof` property of this doclet.
@param {string} sid - The longname of the symbol that this doclet is a member of.
*/
exports.Doclet.prototype.setMemberof = function(sid) {
if (/^<global>\.?/.test(sid)) { sid = sid.replace(/^<global>.?/, ''); }
/**
The longname of the symbol that contains this one, if any.
@type string
*/
this.memberof = sid.replace(/\.prototype/g, '#');
}
/** Set the `longname` property of this doclet.
@param {string} name
*/
exports.Doclet.prototype.setLongname = function(name) {
if (/^<global>\.?/.test(name)) { name = name.replace(/^<global>\.?/, ''); }
/**
The fully resolved symbol name.
@type string
*/
this.longname = name;
if (jsdoc.tag.dictionary.isNamespace(this.kind)) {
this.longname = jsdoc.name.applyNamespace(this.longname, this.kind);
}
}
/** Add a symbol to this doclet's `borrowed` array.
@param {string} source - The longname of the symbol that is the source.
@param {string} target - The name the symbol is being assigned to.
*/
exports.Doclet.prototype.borrow = function(source, target) {
var about = {from: source};
if (target) about.as = target;
if (!this.borrowed) {
/**
A list of symbols that are borrowed by this one, if any.
@type Array.<string>
*/
this.borrowed = [];
}
this.borrowed.push(about);
}
exports.Doclet.prototype.mix = function(source) {
if (!this.mixes) {
/**
A list of symbols that are mixed into this one, if any.
@type Array.<string>
*/
this.mixes = [];
}
this.mixes.push(source);
}
/** Add a symbol to this doclet's `augments` array.
@param {string} base - The longname of the base symbol.
*/
exports.Doclet.prototype.augment = function(base) {
if (!this.augments) {
/**
A list of symbols that are augmented by this one, if any.
@type Array.<string>
*/
this.augments = [];
}
this.augments.push(base);
}
/**
Set the `meta` property of this doclet.
@param {object} meta
*/
exports.Doclet.prototype.setMeta = function(meta) {
if (!this.meta) {
/**
Information about the source code associated with this doclet.
@namespace
*/
this.meta = {};
}
if (meta.lineno) {
/**
The line number of the code associated with this doclet.
@type number
*/
this.meta.lineno = meta.lineno;
}
if (meta.lineno) {
/**
The name of the file containing the code associated with this doclet.
@type string
*/
this.meta.filename = meta.filename;
}
/**
Information about the code symbol.
@namespace
*/
this.meta.code = (this.meta.code || {});
if (meta.id) this.meta.code.id = meta.id;
if (meta.code) {
if (meta.code.name) {
/** The name of the symbol in the source code. */
this.meta.code.name = meta.code.name;
}
if (meta.code.type) {
/** The type of the symbol in the source code. */
this.meta.code.type = meta.code.type;
}
if (meta.code.node) {
this.meta.code.node = meta.code.node;
}
if (meta.code.funcscope) {
this.meta.code.funcscope = meta.code.funcscope;
}
if (meta.code.value) {
/** The value of the symbol in the source code. */
this.meta.code.value = meta.code.value;
}
}
}
function applyTag(tag) {
if (tag.title === 'name') {
this.name = tag.value;
}
if (tag.title === 'kind') {
this.kind = tag.value;
}
if (tag.title === 'description') {
this.description = tag.value;
}
if (tag.title === 'scope') {
this.scope = tag.value;
}
}
// use the meta info about the source code to guess what the doclet kind should be
function codetypeToKind(type) {
var kind = (type || '').toLowerCase();
if (kind !== 'function') {
return 'property';
}
return kind;
}
/**
Convert the raw source of the doclet comment into an array of Tag objects.
@private
*/
function toTags(docletSrc) {
var tagSrcs,
tags = [];
docletSrc = unwrap(docletSrc);
tagSrcs = split(docletSrc);
for each(tagSrc in tagSrcs) {
tags.push( {title: tagSrc.title, text: tagSrc.text} );
}
return tags;
}
function unwrap(docletSrc) {
if (!docletSrc) { return ''; }
// note: keep trailing whitespace for @examples
// extra opening/closing stars are ignored
// left margin is considered a star and a space
// use the /m flag on regex to avoid having to guess what this platform's newline is
docletSrc =
docletSrc.replace(/^\/\*\*+/, '') // remove opening slash+stars
.replace(/\**\*\/$/, "\\Z") // replace closing star slash with end-marker
.replace(/^\s*(\* ?|\\Z)/gm, '') // remove left margin like: spaces+star or spaces+end-marker
.replace(/\s*\\Z$/g, ''); // remove end-marker
return docletSrc;
}
function fixDescription(docletSrc) {
if (!/^\s*@/.test(docletSrc)) {
docletSrc = '@description ' + docletSrc;
}
return docletSrc;
}
function split(docletSrc) {
var tagSrcs = [];
// split out the basic tags, keep surrounding whitespace
// like: @tagTitle tagBody
docletSrc
.replace(/^(\s*)@(\S)/gm, '$1\\@$2') // replace splitter ats with an arbitrary sequence
.split('\\@') // then split on that arbitrary sequence
.forEach(function($) {
if ($) {
var parsedTag = $.match(/^(\S+)(:?\s+(\S[\s\S]*))?/);
if (parsedTag) {
var [, tagTitle, tagText] = parsedTag;
if (tagTitle) {
tagSrcs.push({
title: tagTitle,
text: tagText
});
} }
} }
}); }
});
return tagSrcs;
}
})(); return tagSrcs;
}

View File

@ -5,178 +5,177 @@
@author Michael Mathews <micmath@gmail.com> @author Michael Mathews <micmath@gmail.com>
@license Apache License 2.0 - See file 'LICENSE.md' in this project. @license Apache License 2.0 - See file 'LICENSE.md' in this project.
*/ */
(function() {
var jsdoc = { var jsdoc = {
tagDictionary: require('jsdoc/tag/dictionary') tagDictionary: require('jsdoc/tag/dictionary')
}; };
var puncToScope = { '.': 'static', '~': 'inner', '#': 'instance' },
scopeToPunc = { 'static': '.', 'inner': '~', 'instance': '#' },
Token = Packages.org.mozilla.javascript.Token;
var puncToScope = { '.': 'static', '~': 'inner', '#': 'instance' }, /**
scopeToPunc = { 'static': '.', 'inner': '~', 'instance': '#' }, Resolves the longname, memberof, variation and name values of the given doclet.
Token = Packages.org.mozilla.javascript.Token; @param {module:jsdoc/doclet.Doclet} doclet
*/
/** exports.resolve = function(doclet) {
Resolves the longname, memberof, variation and name values of the given doclet. var name = doclet.name,
@param {module:jsdoc/doclet.Doclet} doclet memberof = doclet.memberof || '',
*/ about = {},
exports.resolve = function(doclet) { parentDoc;
var name = doclet.name,
memberof = doclet.memberof || '',
about = {},
parentDoc;
name = name? (''+name).replace(/\.prototype\.?/g, '#') : ''; name = name? (''+name).replace(/\.prototype\.?/g, '#') : '';
// member of a var in an outer scope? // member of a var in an outer scope?
if (name && !memberof && doclet.meta.code && doclet.meta.code.funcscope) { if (name && !memberof && doclet.meta.code && doclet.meta.code.funcscope) {
name = doclet.longname = doclet.meta.code.funcscope + '~' + name; name = doclet.longname = doclet.meta.code.funcscope + '~' + name;
} }
if (memberof) { // @memberof tag given if (memberof) { // @memberof tag given
memberof = memberof.replace(/\.prototype\.?/g, '#'); memberof = memberof.replace(/\.prototype\.?/g, '#');
// the name is a fullname, like @name foo.bar, @memberof foo // the name is a fullname, like @name foo.bar, @memberof foo
if (name && name.indexOf(memberof) === 0) { if (name && name.indexOf(memberof) === 0) {
about = exports.shorten(name); about = exports.shorten(name);
}
else if (name && /([#.~])$/.test(memberof) ) { // like @memberof foo# or @memberof foo~
about = exports.shorten(memberof + name);
}
else if (name && doclet.scope ) { // like @memberof foo# or @memberof foo~
about = exports.shorten(memberof + scopeToPunc[doclet.scope] + name);
}
} }
else { // no @memberof else if (name && /([#.~])$/.test(memberof) ) { // like @memberof foo# or @memberof foo~
about = exports.shorten(name); about = exports.shorten(memberof + name);
} }
else if (name && doclet.scope ) { // like @memberof foo# or @memberof foo~
about = exports.shorten(memberof + scopeToPunc[doclet.scope] + name);
}
}
else { // no @memberof
about = exports.shorten(name);
}
if (about.name) { if (about.name) {
doclet.name = about.name; doclet.name = about.name;
} }
if (about.memberof) { if (about.memberof) {
doclet.setMemberof(about.memberof); doclet.setMemberof(about.memberof);
} }
if (about.longname && !doclet.longname) { if (about.longname && !doclet.longname) {
doclet.setLongname(about.longname); doclet.setLongname(about.longname);
} }
if (doclet.scope === 'global') { // via @global tag? if (doclet.scope === 'global') { // via @global tag?
doclet.setLongname(doclet.name); doclet.setLongname(doclet.name);
delete doclet.memberof; delete doclet.memberof;
} }
else if (about.scope) { else if (about.scope) {
if (about.memberof === '<global>') { // via @memberof <global> ? if (about.memberof === '<global>') { // via @memberof <global> ?
delete doclet.scope; delete doclet.scope;
}
else {
doclet.scope = puncToScope[about.scope];
}
} }
else { else {
if (doclet.name && doclet.memberof && !doclet.longname) { doclet.scope = puncToScope[about.scope];
doclet.scope = 'static'; // default scope when none is provided
doclet.setLongname(doclet.memberof + scopeToPunc[doclet.scope] + doclet.name);
}
}
if (about.variation) {
doclet.variation = about.variation;
} }
} }
else {
/** if (doclet.name && doclet.memberof && !doclet.longname) {
@inner doclet.scope = 'static'; // default scope when none is provided
@memberof module:jsdoc/name
@param {string} name
@param {string} kind
@returns {string} The name with unsafe names enclosed in quotes.
*/
function quoteUnsafe(name, kind) { // docspaced names may have unsafe characters which need to be quoted by us
if ( (jsdoc.tagDictionary.lookUp(kind).setsDocletDocspace) && /[^$_a-zA-Z0-9\/]/.test(name) ) {
if (!/^[a-z_$-\/]+:\"/i.test(name)) {
return '"' + name.replace(/\"/g, '"') + '"';
}
}
return name;
}
RegExp.escape = RegExp.escape || function(str) {
var specials = new RegExp("[.*+?|()\\[\\]{}\\\\]", "g"); // .*+?|()[]{}\
return str.replace(specials, "\\$&");
}
/**
@method module:jsdoc/name.applyNamespace
@param {string} longname The full longname of the symbol.
@param {string} ns The namespace to be applied.
@returns {string} The longname with the namespace applied.
*/
exports.applyNamespace = function(longname, ns) {
var nameParts = exports.shorten(longname),
name = nameParts.name,
longname = nameParts.longname;
if ( !/^[a-zA-Z]+?:.+$/i.test(name) ) {
longname = longname.replace( new RegExp(RegExp.escape(name)+'$'), ns + ':' + name );
}
return longname;
}
/**
Given a longname like "a.b#c(2)", slice it up into ["a.b", "#", 'c', '2'],
representing the memberof, the scope, the name, and variation.
@param {string} longname
@returns {object} Representing the properties of the given name.
*/
exports.shorten = function(longname) {
// quoted strings in a longname are atomic, convert to tokens
var atoms = [], token;
// handle quoted names like foo["bar"]
longname = longname.replace(/(\[?".+?"\]?)/g, function($) {
var dot = '';
if ( /^\[/.test($) ) {
dot = '.';
$ = $.replace( /^\[/g, '' ).replace( /\]$/g, '' );
}
token = '@{' + atoms.length + '}@'; doclet.setLongname(doclet.memberof + scopeToPunc[doclet.scope] + doclet.name);
atoms.push($);
return dot + token; // foo["bar"] => foo.@{1}@
});
longname = longname.replace( /\.prototype\.?/g, '#' );
var parts = longname?
(longname.match( /^(:?(.+)([#.~]))?(.+?)$/ ) || []).reverse()
: [''];
var name = parts[0] || '', // ensure name is always initialised to avoid error being thrown when calling replace on undefined [gh-24]
scope = parts[1] || '', // ., ~, or #
memberof = parts[2] || '',
variation;
// like /** @name foo.bar(2) */
if ( /(.+)\(([^)]+)\)$/.test(name) ) {
name = RegExp.$1, variation = RegExp.$2;
} }
//// restore quoted strings back again
var i = atoms.length;
while (i--) {
longname = longname.replace('@{'+i+'}@', atoms[i]);
memberof = memberof.replace('@{'+i+'}@', atoms[i]);
scope = scope.replace('@{'+i+'}@', atoms[i]);
name = name.replace('@{'+i+'}@', atoms[i]);
}
////
return {longname: longname, memberof: memberof, scope: scope, name: name, variation: variation};
} }
})(); if (about.variation) {
doclet.variation = about.variation;
}
}
/**
@inner
@memberof module:jsdoc/name
@param {string} name
@param {string} kind
@returns {string} The name with unsafe names enclosed in quotes.
*/
function quoteUnsafe(name, kind) { // docspaced names may have unsafe characters which need to be quoted by us
if ( (jsdoc.tagDictionary.lookUp(kind).setsDocletDocspace) && /[^$_a-zA-Z0-9\/]/.test(name) ) {
if (!/^[a-z_$-\/]+:\"/i.test(name)) {
return '"' + name.replace(/\"/g, '"') + '"';
}
}
return name;
}
RegExp.escape = RegExp.escape || function(str) {
var specials = new RegExp("[.*+?|()\\[\\]{}\\\\]", "g"); // .*+?|()[]{}\
return str.replace(specials, "\\$&");
}
/**
@method module:jsdoc/name.applyNamespace
@param {string} longname The full longname of the symbol.
@param {string} ns The namespace to be applied.
@returns {string} The longname with the namespace applied.
*/
exports.applyNamespace = function(longname, ns) {
var nameParts = exports.shorten(longname),
name = nameParts.name,
longname = nameParts.longname;
if ( !/^[a-zA-Z]+?:.+$/i.test(name) ) {
longname = longname.replace( new RegExp(RegExp.escape(name)+'$'), ns + ':' + name );
}
return longname;
}
/**
Given a longname like "a.b#c(2)", slice it up into ["a.b", "#", 'c', '2'],
representing the memberof, the scope, the name, and variation.
@param {string} longname
@returns {object} Representing the properties of the given name.
*/
exports.shorten = function(longname) {
// quoted strings in a longname are atomic, convert to tokens
var atoms = [], token;
// handle quoted names like foo["bar"]
longname = longname.replace(/(\[?".+?"\]?)/g, function($) {
var dot = '';
if ( /^\[/.test($) ) {
dot = '.';
$ = $.replace( /^\[/g, '' ).replace( /\]$/g, '' );
}
token = '@{' + atoms.length + '}@';
atoms.push($);
return dot + token; // foo["bar"] => foo.@{1}@
});
longname = longname.replace( /\.prototype\.?/g, '#' );
var parts = longname?
(longname.match( /^(:?(.+)([#.~]))?(.+?)$/ ) || []).reverse()
: [''];
var name = parts[0] || '', // ensure name is always initialised to avoid error being thrown when calling replace on undefined [gh-24]
scope = parts[1] || '', // ., ~, or #
memberof = parts[2] || '',
variation;
// like /** @name foo.bar(2) */
if ( /(.+)\(([^)]+)\)$/.test(name) ) {
name = RegExp.$1, variation = RegExp.$2;
}
//// restore quoted strings back again
var i = atoms.length;
while (i--) {
longname = longname.replace('@{'+i+'}@', atoms[i]);
memberof = memberof.replace('@{'+i+'}@', atoms[i]);
scope = scope.replace('@{'+i+'}@', atoms[i]);
name = name.replace('@{'+i+'}@', atoms[i]);
}
////
return {longname: longname, memberof: memberof, scope: scope, name: name, variation: variation};
}

View File

@ -4,70 +4,69 @@
@author Michael Mathews <micmath@gmail.com> @author Michael Mathews <micmath@gmail.com>
@license Apache License 2.0 - See file 'LICENSE.md' in this project. @license Apache License 2.0 - See file 'LICENSE.md' in this project.
*/ */
(function() {
var common = { var common = {
args: require('common/args') args: require('common/args')
};
var argParser = new common.args.ArgParser(),
ourOptions,
defaults = {
template: 'default',
destination: './out/'
}; };
var argParser = new common.args.ArgParser(), argParser.addOption('t', 'template', true, 'The name of the template to use. Default: the "default" template');
ourOptions, argParser.addOption('c', 'configure', true, 'The path to the configuration file. Default: jsdoc __dirname + /conf.json');
defaults = { argParser.addOption('e', 'encoding', true, 'Assume this encoding when reading all source files. Default: utf-8');
template: 'default', argParser.addOption('T', 'test', false, 'Run all tests and quit.');
destination: './out/' argParser.addOption('d', 'destination', true, 'The path to the output folder. Use "console" to dump data to the console. Default: console');
}; argParser.addOption('r', 'recurse', false, 'Recurse into subdirectories when scanning for source code files.');
argParser.addOption('h', 'help', false, 'Print this message and quit.');
argParser.addOption('t', 'template', true, 'The name of the template to use. Default: the "default" template'); argParser.addOption('X', 'explain', false, 'Dump all found doclet internals to console and quit.');
argParser.addOption('c', 'configure', true, 'The path to the configuration file. Default: jsdoc __dirname + /conf.json'); argParser.addOption('q', 'query', true, 'Provide a querystring to define custom variable names/values to add to the options hash.');
argParser.addOption('e', 'encoding', true, 'Assume this encoding when reading all source files. Default: utf-8');
argParser.addOption('T', 'test', false, 'Run all tests and quit.');
argParser.addOption('d', 'destination', true, 'The path to the output folder. Use "console" to dump data to the console. Default: console');
argParser.addOption('r', 'recurse', false, 'Recurse into subdirectories when scanning for source code files.');
argParser.addOption('h', 'help', false, 'Print this message and quit.');
argParser.addOption('X', 'explain', false, 'Dump all found doclet internals to console and quit.');
argParser.addOption('q', 'query', true, 'Provide a querystring to define custom variable names/values to add to the options hash.');
// TODO [-R, recurseonly] = a number representing the depth to recurse // TODO [-R, recurseonly] = a number representing the depth to recurse
// TODO [-f, filter] = a regex to filter on <-- this can be better defined in the configs? // TODO [-f, filter] = a regex to filter on <-- this can be better defined in the configs?
/** /**
Set the options for this app. Set the options for this app.
@throws {Error} Illegal arguments will throw errors. @throws {Error} Illegal arguments will throw errors.
@param {string|String[]} args The command line arguments for this app. @param {string|String[]} args The command line arguments for this app.
*/ */
exports.parse = function(args) { exports.parse = function(args) {
args = args || []; args = args || [];
if (typeof args === 'string' || args.constructor === String) { if (typeof args === 'string' || args.constructor === String) {
args = (''+args).split(/\s+/g); args = (''+args).split(/\s+/g);
} }
ourOptions = argParser.parse(args, defaults); ourOptions = argParser.parse(args, defaults);
return ourOptions;
}
/**
Display help message for options.
*/
exports.help = function() {
return argParser.help();
}
/**
Get a named option.
@param {string} name The name of the option.
@return {string} The value associated with the given name.
*//**
Get all the options for this app.
@return {Object} A collection of key/values representing all the options.
*/
exports.get = function(name) {
if (typeof name === 'undefined') {
return ourOptions; return ourOptions;
} }
else {
/** return ourOptions[name];
Display help message for options.
*/
exports.help = function() {
return argParser.help();
} }
}
/**
Get a named option.
@param {string} name The name of the option.
@return {string} The value associated with the given name.
*//**
Get all the options for this app.
@return {Object} A collection of key/values representing all the options.
*/
exports.get = function(name) {
if (typeof name === 'undefined') {
return ourOptions;
}
else {
return ourOptions[name];
}
}
})();

View File

@ -8,62 +8,61 @@
@module jsdoc/package @module jsdoc/package
@see http://wiki.commonjs.org/wiki/Packages/1.0 @see http://wiki.commonjs.org/wiki/Packages/1.0
*/ */
(function() {
/**
@class
@classdesc Represents a JavaScript package.
@param {string} json - The contents of package.json.
*/
exports.Package = function(json) {
/** The source files associated with this package.
@type {Array<String>}
*/
this.files = [];
/** The kind of this package.
@readonly
@default
@type {string}
*/
this.kind = 'package';
json = JSON.parse(json);
/** The name of this package.
This value is found in the package.json file passed in as a command line option.
@type {string}
*/
this.name = json.name;
/** The longname of this package.
@type {string}
*/
this.longname = this.kind + ':' + this.name;
/** The description of this package.
@type {string}
*/
this.description = json.description;
/**
The hash summary of the source file.
@type {string}
@since 3.2.0
*/
this.version = json.version;
/**
* The licenses of this package.
* @type {Array<Object>}
* @example
* "licenses": [
* {
* "type": "GPLv2",
* "url": "http://www.example.com/licenses/gpl.html"
* }
* ]
*/
this.licenses = json.licenses;
}
})(); /**
@class
@classdesc Represents a JavaScript package.
@param {string} json - The contents of package.json.
*/
exports.Package = function(json) {
/** The source files associated with this package.
@type {Array<String>}
*/
this.files = [];
/** The kind of this package.
@readonly
@default
@type {string}
*/
this.kind = 'package';
json = JSON.parse(json);
/** The name of this package.
This value is found in the package.json file passed in as a command line option.
@type {string}
*/
this.name = json.name;
/** The longname of this package.
@type {string}
*/
this.longname = this.kind + ':' + this.name;
/** The description of this package.
@type {string}
*/
this.description = json.description;
/**
The hash summary of the source file.
@type {string}
@since 3.2.0
*/
this.version = json.version;
/**
* The licenses of this package.
* @type {Array<Object>}
* @example
* "licenses": [
* {
* "type": "GPLv2",
* "url": "http://www.example.com/licenses/gpl.html"
* }
* ]
*/
this.licenses = json.licenses;
}

View File

@ -2,151 +2,151 @@
@module jsdoc/src/handlers @module jsdoc/src/handlers
*/ */
(function() {
var currentModule = null; var currentModule = null;
/**
Attach these event handlers to a particular instance of a parser.
@param parser
*/
exports.attachTo = function(parser) {
var jsdoc = {doclet: require('jsdoc/doclet')};
/** // handles JSDoc comments that include a @name tag -- the code is ignored in such a case
Attach these event handlers to a particular instance of a parser. parser.on('jsdocCommentFound', function(e) {
@param parser var newDoclet = new jsdoc.doclet.Doclet(e.comment, e);
*/
exports.attachTo = function(parser) {
var jsdoc = {doclet: require('jsdoc/doclet')};
// handles JSDoc comments that include a @name tag -- the code is ignored in such a case if (!newDoclet.name) {
parser.on('jsdocCommentFound', function(e) { return false; // only interested in virtual comments (with a @name) here
var newDoclet = new jsdoc.doclet.Doclet(e.comment, e); }
if (!newDoclet.name) {
return false; // only interested in virtual comments (with a @name) here
}
addDoclet.call(this, newDoclet);
if (newDoclet.kind === 'module') {
currentModule = newDoclet.longname;
}
e.doclet = newDoclet;
});
// handles named symbols in the code, may or may not have a JSDoc comment attached addDoclet.call(this, newDoclet);
parser.on('symbolFound', function(e) { if (newDoclet.kind === 'module') {
var subDoclets = e.comment.split(/@also\b/g); currentModule = newDoclet.longname;
}
for (var i = 0, l = subDoclets.length; i < l; i++) { e.doclet = newDoclet;
newSymbolDoclet.call(this, subDoclets[i], e); });
}
}); // handles named symbols in the code, may or may not have a JSDoc comment attached
parser.on('symbolFound', function(e) {
var subDoclets = e.comment.split(/@also\b/g);
function newSymbolDoclet(docletSrc, e) { for (var i = 0, l = subDoclets.length; i < l; i++) {
var newDoclet = new jsdoc.doclet.Doclet(docletSrc, e); newSymbolDoclet.call(this, subDoclets[i], e);
}
});
function newSymbolDoclet(docletSrc, e) {
var newDoclet = new jsdoc.doclet.Doclet(docletSrc, e);
// an undocumented symbol right after a virtual comment? rhino mistakenly connected the two // an undocumented symbol right after a virtual comment? rhino mistakenly connected the two
if (newDoclet.name) { // there was a @name in comment if (newDoclet.name) { // there was a @name in comment
// try again, without the comment // try again, without the comment
e.comment = '@undocumented'; e.comment = '@undocumented';
newDoclet = new jsdoc.doclet.Doclet(e.comment, e); newDoclet = new jsdoc.doclet.Doclet(e.comment, e);
} }
if (newDoclet.alias) { if (newDoclet.alias) {
if (newDoclet.alias === '{@thisClass}') { if (newDoclet.alias === '{@thisClass}') {
memberofName = this.resolveThis(e.astnode); memberofName = this.resolveThis(e.astnode);
// "class" refers to the owner of the prototype, not the prototype itself // "class" refers to the owner of the prototype, not the prototype itself
if ( /^(.+?)(\.prototype|#)$/.test(memberofName) ) { if ( /^(.+?)(\.prototype|#)$/.test(memberofName) ) {
memberofName = RegExp.$1; memberofName = RegExp.$1;
}
newDoclet.alias = memberofName;
} }
newDoclet.addTag('name', newDoclet.alias); newDoclet.alias = memberofName;
newDoclet.postProcess();
} }
else if (e.code && e.code.name) { // we need to get the symbol name from code newDoclet.addTag('name', newDoclet.alias);
newDoclet.addTag('name', e.code.name); newDoclet.postProcess();
if (!newDoclet.memberof && e.astnode) { }
var memberofName, else if (e.code && e.code.name) { // we need to get the symbol name from code
scope; newDoclet.addTag('name', e.code.name);
if ( /^((module.)?exports|this)(\.|$)/.test(newDoclet.name) ) { if (!newDoclet.memberof && e.astnode) {
var nameStartsWith = RegExp.$1; var memberofName,
scope;
newDoclet.name = newDoclet.name.replace(/^(exports|this)(\.|$)/, ''); if ( /^((module.)?exports|this)(\.|$)/.test(newDoclet.name) ) {
var nameStartsWith = RegExp.$1;
newDoclet.name = newDoclet.name.replace(/^(exports|this)(\.|$)/, '');
// like /** @module foo */ exports.bar = 1; // like /** @module foo */ exports.bar = 1;
if (nameStartsWith === 'exports' && currentModule) { if (nameStartsWith === 'exports' && currentModule) {
memberofName = currentModule;
scope = 'static';
}
else if (newDoclet.name === 'module.exports' && currentModule) {
newDoclet.addTag('name', currentModule);
newDoclet.postProcess();
}
else {
// like /** @module foo */ exports = {bar: 1};
// or /** blah */ this.foo = 1;
memberofName = this.resolveThis(e.astnode);
scope = nameStartsWith === 'exports'? 'static' : 'instance';
// like /** @module foo */ this.bar = 1;
if (nameStartsWith === 'this' && currentModule && !memberofName) {
memberofName = currentModule; memberofName = currentModule;
scope = 'static'; scope = 'static';
} }
else if (newDoclet.name === 'module.exports' && currentModule) {
newDoclet.addTag('name', currentModule);
newDoclet.postProcess();
}
else {
// like /** @module foo */ exports = {bar: 1};
// or /** blah */ this.foo = 1;
memberofName = this.resolveThis(e.astnode);
scope = nameStartsWith === 'exports'? 'static' : 'instance';
// like /** @module foo */ this.bar = 1;
if (nameStartsWith === 'this' && currentModule && !memberofName) {
memberofName = currentModule;
scope = 'static';
}
}
if (memberofName) {
if (newDoclet.name) {
newDoclet.name = memberofName + (scope === 'instance'? '#' : '.') + newDoclet.name;
}
else { newDoclet.name = memberofName; }
}
}
else {
memberofName = this.astnodeToMemberof(e.astnode);
} }
if (memberofName) { newDoclet.addTag( 'memberof', memberofName); } if (memberofName) {
else { if (newDoclet.name) {
if (currentModule) { newDoclet.name = memberofName + (scope === 'instance'? '#' : '.') + newDoclet.name;
if (!newDoclet.scope) newDoclet.addTag( 'inner');
if (!newDoclet.memberof && newDoclet.scope !== 'global') newDoclet.addTag( 'memberof', currentModule);
} }
else { newDoclet.name = memberofName; }
} }
} }
else {
memberofName = this.astnodeToMemberof(e.astnode);
}
newDoclet.postProcess(); if (memberofName) { newDoclet.addTag( 'memberof', memberofName); }
} else {
else { if (currentModule) {
return false; if (!newDoclet.scope) newDoclet.addTag( 'inner');
} if (!newDoclet.memberof && newDoclet.scope !== 'global') newDoclet.addTag( 'memberof', currentModule);
addDoclet.call(this, newDoclet);
e.doclet = newDoclet;
}
//parser.on('fileBegin', function(e) { });
parser.on('fileComplete', function(e) {
currentModule = null;
});
function addDoclet(newDoclet) {
if (newDoclet) {
e = { doclet: newDoclet };
this.fire('newDoclet', e);
if (!e.defaultPrevented) {
if ( !filter(newDoclet) ) {
this.addResult(newDoclet);
} }
} }
} }
}
function filter(doclet) {
// you can't document prototypes
if ( /#$/.test(doclet.longname) ) return true;
// you can't document symbols added by the parser with a dummy name
if (doclet.meta.code && doclet.meta.code.name === '____') return true;
newDoclet.postProcess();
}
else {
return false; return false;
} }
addDoclet.call(this, newDoclet);
e.doclet = newDoclet;
} }
})();
//parser.on('fileBegin', function(e) { });
parser.on('fileComplete', function(e) {
currentModule = null;
});
function addDoclet(newDoclet) {
if (newDoclet) {
e = { doclet: newDoclet };
this.fire('newDoclet', e);
if (!e.defaultPrevented) {
if ( !filter(newDoclet) ) {
this.addResult(newDoclet);
}
}
}
}
function filter(doclet) {
// you can't document prototypes
if ( /#$/.test(doclet.longname) ) return true;
// you can't document symbols added by the parser with a dummy name
if (doclet.meta.code && doclet.meta.code.name === '____') return true;
return false;
}
}

View File

@ -5,498 +5,495 @@
* @requires common/events * @requires common/events
*/ */
(function() { var Token = Packages.org.mozilla.javascript.Token,
var Token = Packages.org.mozilla.javascript.Token, currentParser = null,
currentParser = null, currentSourceName = '';
currentSourceName = '';
/**
* @class
* @mixes module:common/events
*
* @example <caption>Create a new parser.</caption>
* var jsdocParser = new (require('jsdoc/src/parser').Parser)();
*/
exports.Parser = function() {
this._resultBuffer = [];
this.refs = {};
}
require('common/util').mixin(exports.Parser.prototype, require('common/events'));
/** /**
* Parse the given source files for JSDoc comments. * @class
* @param {Array.<string>} sourceFiles An array of filepaths to the JavaScript sources. * @mixes module:common/events
* @param {string} [encoding=utf8] *
* * @example <caption>Create a new parser.</caption>
* @fires jsdocCommentFound * var jsdocParser = new (require('jsdoc/src/parser').Parser)();
* @fires symbolFound */
* @fires newDoclet exports.Parser = function() {
* @fires fileBegin this._resultBuffer = [];
* @fires fileComplete this.refs = {};
* }
* @example <caption>Parse two source files.</caption> require('common/util').mixin(exports.Parser.prototype, require('common/events'));
* var myFiles = ['file1.js', 'file2.js'];
* var docs = jsdocParser.parse(myFiles); /**
*/ * Parse the given source files for JSDoc comments.
exports.Parser.prototype.parse = function(sourceFiles, encoding) { * @param {Array.<string>} sourceFiles An array of filepaths to the JavaScript sources.
const SCHEMA = 'javascript:'; * @param {string} [encoding=utf8]
var sourceCode = '', *
filename = ''; * @fires jsdocCommentFound
* @fires symbolFound
if (typeof sourceFiles === 'string') { sourceFiles = [sourceFiles]; } * @fires newDoclet
* @fires fileBegin
for (i = 0, leni = sourceFiles.length; i < leni; i++) { * @fires fileComplete
if (sourceFiles[i].indexOf(SCHEMA) === 0) { *
sourceCode = sourceFiles[i].substr(SCHEMA.length); * @example <caption>Parse two source files.</caption>
filename = '[[string' + i + ']]'; * var myFiles = ['file1.js', 'file2.js'];
} * var docs = jsdocParser.parse(myFiles);
else { */
filename = sourceFiles[i]; exports.Parser.prototype.parse = function(sourceFiles, encoding) {
try { const SCHEMA = 'javascript:';
sourceCode = require('fs').readFileSync(filename, encoding); var sourceCode = '',
} filename = '';
catch(e) {
console.log('FILE READ ERROR: in module:jsdoc/parser.parseFiles: "' + filename + '" ' + e);
continue;
}
}
currentParser = this;
this._parseSourceCode(sourceCode, filename);
currentParser = null;
}
return this._resultBuffer;
}
/** if (typeof sourceFiles === 'string') { sourceFiles = [sourceFiles]; }
* @returns {Array<Doclet>} The accumulated results of any calls to parse.
*/
exports.Parser.prototype.results = function() {
return this._resultBuffer;
}
/** for (i = 0, leni = sourceFiles.length; i < leni; i++) {
* @param {Object} o The parse result to add to the result buffer. if (sourceFiles[i].indexOf(SCHEMA) === 0) {
*/ sourceCode = sourceFiles[i].substr(SCHEMA.length);
exports.Parser.prototype.addResult = function(o) { filename = '[[string' + i + ']]';
this._resultBuffer.push(o);
}
/**
* Empty any accumulated results of calls to parse.
*/
exports.Parser.prototype.clear = function() {
currentParser = null;
currentSourceName = '';
this._resultBuffer = [];
}
/** @private */
exports.Parser.prototype._parseSourceCode = function(sourceCode, sourceName) {
currentSourceName = sourceName;
sourceCode = pretreat(sourceCode);
var ast = parserFactory().parse(sourceCode, sourceName, 1);
var e = {filename: currentSourceName};
this.fire('fileBegin', e);
if (!e.defaultPrevented) {
ast.visit(
new Packages.org.mozilla.javascript.ast.NodeVisitor({
visit: visitNode
})
);
}
this.fire('fileComplete', e);
currentSourceName = '';
}
function pretreat(code) {
return code
// merge adjacent doclets
.replace(/\*\/\/\*\*+/g, '@also')
// make lent objectliterals documentable by giving them a dummy name
.replace(/(\/\*\*[\s\S]*?@lends\b[\s\S]*?\*\/\s*)\{/g, '$1____ = {');
}
/**
* Given a node, determine what the node is a member of.
* @param {astnode} node
* @returns {string} The long name of the node that this is a member of.
*/
exports.Parser.prototype.astnodeToMemberof = function(node) {
var memberof = {};
if (node.type === Token.VAR || node.type === Token.FUNCTION) {
if (node.enclosingFunction) { // an inner var or func
memberof.id = 'astnode'+node.enclosingFunction.hashCode();
memberof.doclet = this.refs[memberof.id];
if (!memberof.doclet) {
return '<anonymous>~';
}
return (memberof.doclet.longname||memberof.doclet.name) + '~';
}
} }
else { else {
memberof.id = 'astnode'+node.parent.hashCode(); filename = sourceFiles[i];
memberof.doclet = this.refs[memberof.id]; try {
if (!memberof.doclet) return ''; // global? sourceCode = require('fs').readFileSync(filename, encoding);
return memberof.doclet.longname||memberof.doclet.name; }
catch(e) {
console.log('FILE READ ERROR: in module:jsdoc/parser.parseFiles: "' + filename + '" ' + e);
continue;
}
} }
currentParser = this;
this._parseSourceCode(sourceCode, filename);
currentParser = null;
} }
/** return this._resultBuffer;
* Resolve what "this" refers too, relative to a node. }
* @param {astnode} node - The "this" node
* @returns {string} The longname of the enclosing node. /**
*/ * @returns {Array<Doclet>} The accumulated results of any calls to parse.
exports.Parser.prototype.resolveThis = function(node) { */
var memberof = {}; exports.Parser.prototype.results = function() {
return this._resultBuffer;
if (node.enclosingFunction) { }
/**
* @param {Object} o The parse result to add to the result buffer.
*/
exports.Parser.prototype.addResult = function(o) {
this._resultBuffer.push(o);
}
/**
* Empty any accumulated results of calls to parse.
*/
exports.Parser.prototype.clear = function() {
currentParser = null;
currentSourceName = '';
this._resultBuffer = [];
}
/** @private */
exports.Parser.prototype._parseSourceCode = function(sourceCode, sourceName) {
currentSourceName = sourceName;
sourceCode = pretreat(sourceCode);
var ast = parserFactory().parse(sourceCode, sourceName, 1);
var e = {filename: currentSourceName};
this.fire('fileBegin', e);
if (!e.defaultPrevented) {
ast.visit(
new Packages.org.mozilla.javascript.ast.NodeVisitor({
visit: visitNode
})
);
}
this.fire('fileComplete', e);
currentSourceName = '';
}
function pretreat(code) {
return code
// merge adjacent doclets
.replace(/\*\/\/\*\*+/g, '@also')
// make lent objectliterals documentable by giving them a dummy name
.replace(/(\/\*\*[\s\S]*?@lends\b[\s\S]*?\*\/\s*)\{/g, '$1____ = {');
}
/**
* Given a node, determine what the node is a member of.
* @param {astnode} node
* @returns {string} The long name of the node that this is a member of.
*/
exports.Parser.prototype.astnodeToMemberof = function(node) {
var memberof = {};
if (node.type === Token.VAR || node.type === Token.FUNCTION) {
if (node.enclosingFunction) { // an inner var or func
memberof.id = 'astnode'+node.enclosingFunction.hashCode(); memberof.id = 'astnode'+node.enclosingFunction.hashCode();
memberof.doclet = this.refs[memberof.id]; memberof.doclet = this.refs[memberof.id];
if (!memberof.doclet) { if (!memberof.doclet) {
return '<anonymous>'; // TODO handle global this? return '<anonymous>~';
}
if (memberof.doclet['this']) {
return memberof.doclet['this'];
}
// like: Foo.constructor = function(n) { /** blah */ this.name = n; }
else if (memberof.doclet.kind === 'function' && memberof.doclet.memberof) {
return memberof.doclet.memberof;
}
// walk up to the closest class we can find
else if (memberof.doclet.kind === 'class' || memberof.doclet.kind === 'module') {
return memberof.doclet.longname||memberof.doclet.name;
}
else {
if (node.enclosingFunction){
return this.resolveThis(node.enclosingFunction/*memberof.doclet.meta.code.val*/);
}
else return ''; // TODO handle global this?
} }
return (memberof.doclet.longname||memberof.doclet.name) + '~';
} }
else if (node.parent) { }
var parent = node.parent; else {
if (parent.type === Token.COLON) parent = parent.parent; // go up one more memberof.id = 'astnode'+node.parent.hashCode();
memberof.doclet = this.refs[memberof.id];
memberof.id = 'astnode'+parent.hashCode(); if (!memberof.doclet) return ''; // global?
memberof.doclet = this.refs[memberof.id]; return memberof.doclet.longname||memberof.doclet.name;
}
if (!memberof.doclet) return ''; // global? }
/**
* Resolve what "this" refers too, relative to a node.
* @param {astnode} node - The "this" node
* @returns {string} The longname of the enclosing node.
*/
exports.Parser.prototype.resolveThis = function(node) {
var memberof = {};
if (node.enclosingFunction) {
memberof.id = 'astnode'+node.enclosingFunction.hashCode();
memberof.doclet = this.refs[memberof.id];
if (!memberof.doclet) {
return '<anonymous>'; // TODO handle global this?
}
if (memberof.doclet['this']) {
return memberof.doclet['this'];
}
// like: Foo.constructor = function(n) { /** blah */ this.name = n; }
else if (memberof.doclet.kind === 'function' && memberof.doclet.memberof) {
return memberof.doclet.memberof;
}
// walk up to the closest class we can find
else if (memberof.doclet.kind === 'class' || memberof.doclet.kind === 'module') {
return memberof.doclet.longname||memberof.doclet.name; return memberof.doclet.longname||memberof.doclet.name;
} }
else { else {
return ''; // global? if (node.enclosingFunction){
return this.resolveThis(node.enclosingFunction/*memberof.doclet.meta.code.val*/);
}
else return ''; // TODO handle global this?
} }
} }
else if (node.parent) {
/** var parent = node.parent;
* Resolve what function a var is limited to. if (parent.type === Token.COLON) parent = parent.parent; // go up one more
* @param {astnode} node
* @param {string} basename The leftmost name in the long name: in foo.bar.zip the basename is foo.
*/
exports.Parser.prototype.resolveVar = function(node, basename) {
var doclet,
enclosingFunction = node.enclosingFunction;
if (!enclosingFunction) { return ''; } // global memberof.id = 'astnode'+parent.hashCode();
doclet = this.refs['astnode'+enclosingFunction.hashCode()]; memberof.doclet = this.refs[memberof.id];
if (!memberof.doclet) return ''; // global?
return memberof.doclet.longname||memberof.doclet.name;
}
else {
return ''; // global?
}
}
if ( doclet && doclet.meta.vars && ~doclet.meta.vars.indexOf(basename) ) { /**
return doclet.longname; * Resolve what function a var is limited to.
} * @param {astnode} node
* @param {string} basename The leftmost name in the long name: in foo.bar.zip the basename is foo.
return this.resolveVar(enclosingFunction, basename); */
exports.Parser.prototype.resolveVar = function(node, basename) {
var doclet,
enclosingFunction = node.enclosingFunction;
if (!enclosingFunction) { return ''; } // global
doclet = this.refs['astnode'+enclosingFunction.hashCode()];
if ( doclet && doclet.meta.vars && ~doclet.meta.vars.indexOf(basename) ) {
return doclet.longname;
} }
/** @private */ return this.resolveVar(enclosingFunction, basename);
function visitNode(node) { }
var e,
commentSrc;
// look for stand-alone doc comments /** @private */
if (node.type === Token.SCRIPT && node.comments) { function visitNode(node) {
// note: ALL comments are seen in this block... var e,
for each(var comment in node.comments.toArray()) { commentSrc;
if (comment.commentType !== Token.CommentType.JSDOC) {
continue;
}
if (commentSrc = ''+comment.toSource()) {
e = { // look for stand-alone doc comments
comment: commentSrc, if (node.type === Token.SCRIPT && node.comments) {
lineno: comment.getLineno(), // note: ALL comments are seen in this block...
filename: currentSourceName for each(var comment in node.comments.toArray()) {
}; if (comment.commentType !== Token.CommentType.JSDOC) {
continue;
if ( isValidJsdoc(commentSrc) ) {
currentParser.fire('jsdocCommentFound', e, currentParser);
}
}
} }
}
else if (node.type === Token.ASSIGN) {
e = {
id: 'astnode'+node.hashCode(), // the id of the ASSIGN node
comment: String(node.jsDoc||'@undocumented'),
lineno: node.getLineno(),
filename: currentSourceName,
astnode: node,
code: aboutNode(node)
};
var basename = e.code.name.replace(/^([$a-z_][$a-z_0-9]*).*?$/i, '$1'); if (commentSrc = ''+comment.toSource()) {
if (basename !== 'this') e.code.funcscope = currentParser.resolveVar(node, basename);
if ( isValidJsdoc(e.comment) ) { e = {
currentParser.fire('symbolFound', e, currentParser); comment: commentSrc,
} lineno: comment.getLineno(),
filename: currentSourceName
if (e.doclet) {
currentParser.refs['astnode'+e.code.node.hashCode()] = e.doclet; // allow lookup from value => doclet
}
}
else if (node.type === Token.COLON) { // assignment within an object literal
e = {
id: 'astnode'+node.hashCode(), // the id of the COLON node
comment: String(node.left.jsDoc||'@undocumented'),
lineno: node.getLineno(),
filename: currentSourceName,
astnode: node,
code: aboutNode(node)
};
if ( isValidJsdoc(e.comment) ) {
currentParser.fire('symbolFound', e, currentParser);
}
if (e.doclet) {
currentParser.refs['astnode'+e.code.node.hashCode()] = e.doclet; // allow lookup from value => doclet
}
}
else if (node.type == Token.VAR || node.type == Token.LET || node.type == Token.CONST) {
if (node.variables) {
return true; // we'll get each var separately on future visits
}
if (node.parent.variables.toArray()[0] === node) { // like /** blah */ var a=1, b=2, c=3;
// the first var assignment gets any jsDoc before the whole var series
node.jsDoc = node.parent.jsDoc;
}
e = {
id: 'astnode'+node.hashCode(), // the id of the VARIABLE node
comment: String(node.jsDoc||'@undocumented'),
lineno: node.getLineno(),
filename: currentSourceName,
astnode: node,
code: aboutNode(node)
};
// keep track of vars in a function scope
if (node.enclosingFunction) {
var func = 'astnode'+node.enclosingFunction.hashCode(),
funcDoc = currentParser.refs[func];
if (funcDoc) {
funcDoc.meta.vars = funcDoc.meta.vars || [];
funcDoc.meta.vars.push(e.code.name);
}
}
if ( isValidJsdoc(e.comment) ) {
currentParser.fire('symbolFound', e, currentParser);
}
if (e.doclet) {
currentParser.refs['astnode'+e.code.node.hashCode()] = e.doclet; // allow lookup from value => doclet
}
}
else if (node.type == Token.FUNCTION) {
e = {
id: 'astnode'+node.hashCode(), // the id of the COLON node
comment: String(node.jsDoc||'@undocumented'),
lineno: node.getLineno(),
filename: currentSourceName,
astnode: node,
code: aboutNode(node)
};
e.code.name = String(node.name) || '';
// keep track of vars in a function scope
if (node.enclosingFunction) {
var func = 'astnode'+node.enclosingFunction.hashCode(),
funcDoc = currentParser.refs[func];
if (funcDoc) {
funcDoc.meta.vars = funcDoc.meta.vars || [];
funcDoc.meta.vars.push(e.code.name);
}
}
var basename = e.code.name.replace(/^([$a-z_][$a-z_0-9]*).*?$/i, '$1');
e.code.funcscope = currentParser.resolveVar(node, basename);
if ( isValidJsdoc(e.comment) ) {
currentParser.fire('symbolFound', e, currentParser);
}
if (e.doclet) {
currentParser.refs['astnode'+e.code.node.hashCode()] = e.doclet; // allow lookup from value => doclet
}
else if (!currentParser.refs['astnode'+e.code.node.hashCode()]) { // keep references to undocumented anonymous functions too as they might have scoped vars
currentParser.refs['astnode'+e.code.node.hashCode()] = {
longname: '<anonymous>',
meta: { code: e.code }
}; };
}
} if ( isValidJsdoc(commentSrc) ) {
currentParser.fire('jsdocCommentFound', e, currentParser);
return true;
}
/** @private */
function parserFactory() {
var cx = Packages.org.mozilla.javascript.Context.getCurrentContext();
var ce = new Packages.org.mozilla.javascript.CompilerEnvirons();
ce.setRecordingComments(true);
ce.setRecordingLocalJsDocComments(true);
ce.setLanguageVersion(180);
ce.initFromContext(cx);
return new Packages.org.mozilla.javascript.Parser(ce, ce.getErrorReporter());
}
/**
* Attempts to find the name and type of the given node.
* @private
* @memberof module:src/parser.Parser
*/
function aboutNode(node) {
about = {};
if (node.type == Token.FUNCTION) {
about.name = '' + node.name;
about.type = 'function';
about.node = node;
return about;
}
if (node.type == Token.VAR || node.type == Token.LET || node.type == Token.CONST) {
about.name = nodeToString(node.target);
if (node.initializer) { // like var i = 0;
about.node = node.initializer;
about.value = nodeToString(about.node);
about.type = getTypeName(node.initializer);
}
else { // like var i;
about.node = node.target;
about.value = nodeToString(about.node);
about.type = 'undefined';
}
return about;
}
if (node.type === Token.ASSIGN || node.type === Token.COLON) {
about.name = nodeToString(node.left);
if (node.type === Token.COLON) {
// objlit keys with unsafe variable-name characters must be quoted
if (!/^[$_a-z][$_a-z0-9]*$/i.test(about.name) ) {
about.name = '"'+about.name.replace(/"/g, '\\"')+'"';
} }
} }
about.node = node.right; }
about.value = nodeToString(about.node); }
about.type = getTypeName(node.right); else if (node.type === Token.ASSIGN) {
return about; e = {
id: 'astnode'+node.hashCode(), // the id of the ASSIGN node
comment: String(node.jsDoc||'@undocumented'),
lineno: node.getLineno(),
filename: currentSourceName,
astnode: node,
code: aboutNode(node)
};
var basename = e.code.name.replace(/^([$a-z_][$a-z_0-9]*).*?$/i, '$1');
if (basename !== 'this') e.code.funcscope = currentParser.resolveVar(node, basename);
if ( isValidJsdoc(e.comment) ) {
currentParser.fire('symbolFound', e, currentParser);
} }
// type 39 (NAME) if (e.doclet) {
var string = nodeToString(node); currentParser.refs['astnode'+e.code.node.hashCode()] = e.doclet; // allow lookup from value => doclet
if (string) {
about.name = string;
return about;
} }
}
else if (node.type === Token.COLON) { // assignment within an object literal
e = {
id: 'astnode'+node.hashCode(), // the id of the COLON node
comment: String(node.left.jsDoc||'@undocumented'),
lineno: node.getLineno(),
filename: currentSourceName,
astnode: node,
code: aboutNode(node)
};
if ( isValidJsdoc(e.comment) ) {
currentParser.fire('symbolFound', e, currentParser);
}
if (e.doclet) {
currentParser.refs['astnode'+e.code.node.hashCode()] = e.doclet; // allow lookup from value => doclet
}
}
else if (node.type == Token.VAR || node.type == Token.LET || node.type == Token.CONST) {
if (node.variables) {
return true; // we'll get each var separately on future visits
}
if (node.parent.variables.toArray()[0] === node) { // like /** blah */ var a=1, b=2, c=3;
// the first var assignment gets any jsDoc before the whole var series
node.jsDoc = node.parent.jsDoc;
}
e = {
id: 'astnode'+node.hashCode(), // the id of the VARIABLE node
comment: String(node.jsDoc||'@undocumented'),
lineno: node.getLineno(),
filename: currentSourceName,
astnode: node,
code: aboutNode(node)
};
// keep track of vars in a function scope
if (node.enclosingFunction) {
var func = 'astnode'+node.enclosingFunction.hashCode(),
funcDoc = currentParser.refs[func];
if (funcDoc) {
funcDoc.meta.vars = funcDoc.meta.vars || [];
funcDoc.meta.vars.push(e.code.name);
}
}
if ( isValidJsdoc(e.comment) ) {
currentParser.fire('symbolFound', e, currentParser);
}
if (e.doclet) {
currentParser.refs['astnode'+e.code.node.hashCode()] = e.doclet; // allow lookup from value => doclet
}
}
else if (node.type == Token.FUNCTION) {
e = {
id: 'astnode'+node.hashCode(), // the id of the COLON node
comment: String(node.jsDoc||'@undocumented'),
lineno: node.getLineno(),
filename: currentSourceName,
astnode: node,
code: aboutNode(node)
};
e.code.name = String(node.name) || '';
// keep track of vars in a function scope
if (node.enclosingFunction) {
var func = 'astnode'+node.enclosingFunction.hashCode(),
funcDoc = currentParser.refs[func];
if (funcDoc) {
funcDoc.meta.vars = funcDoc.meta.vars || [];
funcDoc.meta.vars.push(e.code.name);
}
}
var basename = e.code.name.replace(/^([$a-z_][$a-z_0-9]*).*?$/i, '$1');
e.code.funcscope = currentParser.resolveVar(node, basename);
if ( isValidJsdoc(e.comment) ) {
currentParser.fire('symbolFound', e, currentParser);
}
if (e.doclet) {
currentParser.refs['astnode'+e.code.node.hashCode()] = e.doclet; // allow lookup from value => doclet
}
else if (!currentParser.refs['astnode'+e.code.node.hashCode()]) { // keep references to undocumented anonymous functions too as they might have scoped vars
currentParser.refs['astnode'+e.code.node.hashCode()] = {
longname: '<anonymous>',
meta: { code: e.code }
};
}
}
return true;
}
/** @private */
function parserFactory() {
var cx = Packages.org.mozilla.javascript.Context.getCurrentContext();
var ce = new Packages.org.mozilla.javascript.CompilerEnvirons();
ce.setRecordingComments(true);
ce.setRecordingLocalJsDocComments(true);
ce.setLanguageVersion(180);
ce.initFromContext(cx);
return new Packages.org.mozilla.javascript.Parser(ce, ce.getErrorReporter());
}
/**
* Attempts to find the name and type of the given node.
* @private
* @memberof module:src/parser.Parser
*/
function aboutNode(node) {
about = {};
if (node.type == Token.FUNCTION) {
about.name = '' + node.name;
about.type = 'function';
about.node = node;
return about; return about;
} }
/** @private if (node.type == Token.VAR || node.type == Token.LET || node.type == Token.CONST) {
@memberof module:src/parser.Parser about.name = nodeToString(node.target);
*/ if (node.initializer) { // like var i = 0;
function nodeToString(node) { about.node = node.initializer;
var str; about.value = nodeToString(about.node);
about.type = getTypeName(node.initializer);
if (!node) return;
if (node.type === Token.GETPROP) {
str = [nodeToString(node.target), node.property.string].join('.');
} }
else if (node.type === Token.VAR) { else { // like var i;
str = nodeToString(node.target) about.node = node.target;
} about.value = nodeToString(about.node);
else if (node.type === Token.NAME) { about.type = 'undefined';
str = node.string;
}
else if (node.type === Token.STRING) {
str = node.value;
}
else if (node.type === Token.NUMBER) {
str = node.value;
}
else if (node.type === Token.THIS) {
str = 'this';
}
else if (node.type === Token.GETELEM) {
str = node.toSource(); // like: Foo['Bar']
}
else {
str = getTypeName(node);
} }
return '' + str; return about;
}; }
/** @private if (node.type === Token.ASSIGN || node.type === Token.COLON) {
@memberof module:src/parser.Parser about.name = nodeToString(node.left);
*/ if (node.type === Token.COLON) {
function getTypeName(node) {
var type = ''; // objlit keys with unsafe variable-name characters must be quoted
if (!/^[$_a-z][$_a-z0-9]*$/i.test(about.name) ) {
if (node) { about.name = '"'+about.name.replace(/"/g, '\\"')+'"';
type = ''+ Packages.org.mozilla.javascript.Token.typeToName(node.getType()); }
} }
about.node = node.right;
return type; about.value = nodeToString(about.node);
about.type = getTypeName(node.right);
return about;
} }
/** @private // type 39 (NAME)
@memberof module:src/parser.Parser var string = nodeToString(node);
*/ if (string) {
function isValidJsdoc(commentSrc) { about.name = string;
return commentSrc.indexOf('/***') !== 0; /*** ignore comments that start with many stars ***/ return about;
}
return about;
}
/** @private
@memberof module:src/parser.Parser
*/
function nodeToString(node) {
var str;
if (!node) return;
if (node.type === Token.GETPROP) {
str = [nodeToString(node.target), node.property.string].join('.');
}
else if (node.type === Token.VAR) {
str = nodeToString(node.target)
}
else if (node.type === Token.NAME) {
str = node.string;
}
else if (node.type === Token.STRING) {
str = node.value;
}
else if (node.type === Token.NUMBER) {
str = node.value;
}
else if (node.type === Token.THIS) {
str = 'this';
}
else if (node.type === Token.GETELEM) {
str = node.toSource(); // like: Foo['Bar']
}
else {
str = getTypeName(node);
} }
})(); return '' + str;
};
/** @private
@memberof module:src/parser.Parser
*/
function getTypeName(node) {
var type = '';
if (node) {
type = ''+ Packages.org.mozilla.javascript.Token.typeToName(node.getType());
}
return type;
}
/** @private
@memberof module:src/parser.Parser
*/
function isValidJsdoc(commentSrc) {
return commentSrc.indexOf('/***') !== 0; /*** ignore comments that start with many stars ***/
}
/** /**
Fired whenever the parser encounters a JSDoc comment in the current source code. Fired whenever the parser encounters a JSDoc comment in the current source code.
@ -506,4 +503,4 @@
@param {string} e.comment The text content of the JSDoc comment @param {string} e.comment The text content of the JSDoc comment
@param {number} e.lineno The line number associated with the found comment. @param {number} e.lineno The line number associated with the found comment.
@param {string} e.filename The file name associated with the found comment. @param {string} e.filename The file name associated with the found comment.
*/ */

View File

@ -6,64 +6,63 @@
@license Apache License 2.0 - See file 'LICENSE.md' in this project. @license Apache License 2.0 - See file 'LICENSE.md' in this project.
*/ */
(function() {
var common = { var common = {
mixin: require('common/util').mixin, mixin: require('common/util').mixin,
events: require('common/events') events: require('common/events')
}; };
var fs = require('fs');
/**
@constructor
@mixes module:common.events
*/
exports.Scanner = function() {
}
common.mixin(exports.Scanner.prototype, common.events);
/**
Recursively searches the given searchPaths for js files.
@param {Array.<string>} searchPaths
@param {number} [depth=1]
@fires sourceFileFound
*/
exports.Scanner.prototype.scan = function(searchPaths, depth, includeMatch, excludeMatch) {
var filePaths = [],
that = this;
searchPaths = searchPaths || [];
depth = depth || 1;
searchPaths.forEach(function($) {
if ( fs.stat($).isFile() ) {
filePaths.push($);
}
else {
filePaths = filePaths.concat(fs.ls($, depth));
}
});
var fs = require('fs'); filePaths = filePaths.filter(function($) {
if (includeMatch && !includeMatch.test($)) {
return false
}
if (excludeMatch && excludeMatch.test($)) {
return false
}
return true;
});
/** filePaths = filePaths.filter(function($) {
@constructor var e = { fileName: $ };
@mixes module:common.events that.fire('sourceFileFound', e);
*/
exports.Scanner = function() { return !e.defaultPrevented;
} });
common.mixin(exports.Scanner.prototype, common.events);
/** return filePaths;
Recursively searches the given searchPaths for js files. }
@param {Array.<string>} searchPaths
@param {number} [depth=1]
@fires sourceFileFound
*/
exports.Scanner.prototype.scan = function(searchPaths, depth, includeMatch, excludeMatch) {
var filePaths = [],
that = this;
searchPaths = searchPaths || [];
depth = depth || 1;
searchPaths.forEach(function($) {
if ( fs.stat($).isFile() ) {
filePaths.push($);
}
else {
filePaths = filePaths.concat(fs.ls($, depth));
}
});
filePaths = filePaths.filter(function($) {
if (includeMatch && !includeMatch.test($)) {
return false
}
if (excludeMatch && excludeMatch.test($)) {
return false
}
return true;
});
filePaths = filePaths.filter(function($) {
var e = { fileName: $ };
that.fire('sourceFileFound', e);
return !e.defaultPrevented;
});
return filePaths;
}
})();

View File

@ -11,126 +11,124 @@
@requires jsdoc/tag/validator @requires jsdoc/tag/validator
@requires jsdoc/tag/type @requires jsdoc/tag/type
*/ */
(function() {
var jsdoc = { var jsdoc = {
tag: { tag: {
dictionary: require('jsdoc/tag/dictionary'), dictionary: require('jsdoc/tag/dictionary'),
validator: require('jsdoc/tag/validator'), validator: require('jsdoc/tag/validator'),
type: require('jsdoc/tag/type') type: require('jsdoc/tag/type')
} }
}; };
/** /**
Constructs a new tag object. Calls the tag validator. Constructs a new tag object. Calls the tag validator.
@class @class
@classdesc Represents a single doclet tag. @classdesc Represents a single doclet tag.
@param {string} tagTitle @param {string} tagTitle
@param {string=} tagBody @param {string=} tagBody
@param {object=} meta @param {object=} meta
*/ */
exports.Tag = function(tagTitle, tagBody, meta) { exports.Tag = function(tagTitle, tagBody, meta) {
var tagDef = jsdoc.tag.dictionary.lookUp(tagTitle), var tagDef = jsdoc.tag.dictionary.lookUp(tagTitle),
meta = meta || {}; meta = meta || {};
this.originalTitle = trim(tagTitle); this.originalTitle = trim(tagTitle);
/** The title part of the tag: @title text */ /** The title part of the tag: @title text */
this.title = jsdoc.tag.dictionary.normalise( this.originalTitle ); this.title = jsdoc.tag.dictionary.normalise( this.originalTitle );
/** The text part of the tag: @title text */ /** The text part of the tag: @title text */
this.text = trim(tagBody, tagDef.keepsWhitespace); this.text = trim(tagBody, tagDef.keepsWhitespace);
if (this.text) { if (this.text) {
if (tagDef.onTagText) { if (tagDef.onTagText) {
this.text = tagDef.onTagText(this.text); this.text = tagDef.onTagText(this.text);
} }
if (tagDef.canHaveType) { if (tagDef.canHaveType) {
/** The value propertiy represents the result of parsing the tag text. */
this.value = {};
/** The value propertiy represents the result of parsing the tag text. */ var [
this.value = {}; /*Array.<string>*/ typeNames,
/*any*/ remainingText,
var [ /*?boolean*/ optional,
/*Array.<string>*/ typeNames, /*?boolean*/ nullable,
/*any*/ remainingText, /*?boolean*/ variable
/*?boolean*/ optional, ] = jsdoc.tag.type.parse(this.text);
/*?boolean*/ nullable,
/*?boolean*/ variable if (typeNames.length) {
] = jsdoc.tag.type.parse(this.text); this.value.type = {
names: typeNames,
if (typeNames.length) { optional: optional,
this.value.type = { nullable: nullable,
names: typeNames, variable: variable
optional: optional, };
nullable: nullable, }
variable: variable
};
}
if (remainingText) { if (remainingText) {
if (tagDef.canHaveName) { if (tagDef.canHaveName) {
var [paramName, paramDesc, paramOptional, paramDefault] var [paramName, paramDesc, paramOptional, paramDefault]
= parseParamText(remainingText); = parseParamText(remainingText);
// note the dash is a special case: as a param name it means "no name" // note the dash is a special case: as a param name it means "no name"
if (paramName && paramName !== '-') { this.value.name = paramName; } if (paramName && paramName !== '-') { this.value.name = paramName; }
if (paramDesc) { this.value.description = paramDesc; } if (paramDesc) { this.value.description = paramDesc; }
if (paramOptional) { this.value.optional = paramOptional; } if (paramOptional) { this.value.optional = paramOptional; }
if (paramDefault) { this.value.defaultvalue = paramDefault; } if (paramDefault) { this.value.defaultvalue = paramDefault; }
} }
else { else {
this.value.description = remainingText; this.value.description = remainingText;
}
} }
} }
else { }
this.value = this.text; else {
} this.value = this.text;
} }
}
jsdoc.tag.validator.validate(this, meta);
} jsdoc.tag.validator.validate(this, meta);
}
function trim(text, newlines) {
if (!text) { return ''; }
function trim(text, newlines) { if (newlines) {
if (!text) { return ''; } return text.replace(/^[\n\r\f]+|[\n\r\f]+$/g, '');
if (newlines) {
return text.replace(/^[\n\r\f]+|[\n\r\f]+$/g, '');
}
else {
return text.replace(/^\s+|\s+$/g, '');
}
} }
else {
return text.replace(/^\s+|\s+$/g, '');
}
}
/**
Parse the parameter name and parameter desc from the tag text.
@inner
@method parseParamText
@memberof module:jsdoc/tag
@param {string} tagText
@returns {Array.<string, string, boolean, boolean>} [pname, pdesc, poptional, pdefault].
*/
function parseParamText(tagText) {
var pname, pdesc, poptional, pdefault;
/** // like: pname, pname pdesc, or name - pdesc
Parse the parameter name and parameter desc from the tag text. tagText.match(/^(\[[^\]]+\]|\S+)((?:\s*\-\s*|\s+)(\S[\s\S]*))?$/);
@inner pname = RegExp.$1;
@method parseParamText pdesc = RegExp.$3;
@memberof module:jsdoc/tag
@param {string} tagText if ( /^\[\s*(.+?)\s*\]$/.test(pname) ) {
@returns {Array.<string, string, boolean, boolean>} [pname, pdesc, poptional, pdefault].
*/
function parseParamText(tagText) {
var pname, pdesc, poptional, pdefault;
// like: pname, pname pdesc, or name - pdesc
tagText.match(/^(\[[^\]]+\]|\S+)((?:\s*\-\s*|\s+)(\S[\s\S]*))?$/);
pname = RegExp.$1; pname = RegExp.$1;
pdesc = RegExp.$3; poptional = true;
if ( /^\[\s*(.+?)\s*\]$/.test(pname) ) { if ( /^(.+?)\s*=\s*(.+)$/.test(pname) ) {
pname = RegExp.$1; pname = RegExp.$1;
poptional = true; pdefault = RegExp.$2;
if ( /^(.+?)\s*=\s*(.+)$/.test(pname) ) {
pname = RegExp.$1;
pdefault = RegExp.$2;
}
} }
return [pname, pdesc, poptional, pdefault];
} }
return [pname, pdesc, poptional, pdefault];
})(); }

View File

@ -3,71 +3,74 @@
@author Michael Mathews <micmath@gmail.com> @author Michael Mathews <micmath@gmail.com>
@license Apache License 2.0 - See file 'LICENSE.md' in this project. @license Apache License 2.0 - See file 'LICENSE.md' in this project.
*/ */
(function() {
var _synonyms = {}, var _synonyms = {},
_definitions = {}, _definitions = {},
_namespaces = []; _namespaces = [];
function _TagDefinition(title, etc) {
etc = etc || {};
function _TagDefinition(title, etc) { this.title = dictionary.normalise(title);
etc = etc || {};
for (var p in etc) {
this.title = dictionary.normalise(title); if (etc.hasOwnProperty(p)) {
this[p] = etc[p];
for (var p in etc) {
if (etc.hasOwnProperty(p)) {
this[p] = etc[p];
}
} }
} }
}
_TagDefinition.prototype.synonym = function(synonymName) {
_synonyms[synonymName.toLowerCase()] = this.title; _TagDefinition.prototype.synonym = function(synonymName) {
return this; // chainable _synonyms[synonymName.toLowerCase()] = this.title;
} return this; // chainable
}
/** @exports jsdoc/tag/dictionary */
var dictionary = { /** @exports jsdoc/tag/dictionary */
/** @function */ var dictionary = {
defineTag: function(title, opts) { /** @function */
_definitions[title] = new _TagDefinition(title, opts); defineTag: function(title, opts) {
_definitions[title] = new _TagDefinition(title, opts);
if (opts.isNamespace) {
_namespaces.push(title);
}
return _definitions[title];
},
/** @function */
lookUp: function(title) {
title = dictionary.normalise(title);
if ( _definitions.hasOwnProperty(title) ) {
return _definitions[title];
}
return false;
},
/** @function */ if (opts.isNamespace) {
isNamespace: function(kind) { _namespaces.push(title);
return ( ~ _namespaces.indexOf(kind) );
},
/** @function */
normalise: function(title) {
canonicalName = title.toLowerCase();
if ( _synonyms.hasOwnProperty(canonicalName) ) {
return _synonyms[canonicalName];
}
return canonicalName;
} }
};
return _definitions[title];
},
/** @function */
lookUp: function(title) {
title = dictionary.normalise(title);
if ( _definitions.hasOwnProperty(title) ) {
return _definitions[title];
}
return false;
},
require('jsdoc/tag/dictionary/definitions').defineTags(dictionary); /** @function */
isNamespace: function(kind) {
return ( ~ _namespaces.indexOf(kind) );
},
module.exports = dictionary; /** @function */
normalise: function(title) {
})(); canonicalName = title.toLowerCase();
if ( _synonyms.hasOwnProperty(canonicalName) ) {
return _synonyms[canonicalName];
}
return canonicalName;
}
};
require('jsdoc/tag/dictionary/definitions').defineTags(dictionary);
for (var prop in dictionary) {
if (dictionary.hasOwnProperty(prop)) {
exports[prop] = dictionary[prop];
}
}

File diff suppressed because it is too large Load Diff

View File

@ -5,109 +5,107 @@
@license Apache License 2.0 - See file 'LICENSE.md' in this project. @license Apache License 2.0 - See file 'LICENSE.md' in this project.
*/ */
(function() {
/**
@param {string} tagValue
@returns {Array.<string>}
*/
exports.parse = function(tagValue) {
if (typeof tagValue !== 'string') { tagValue = ''; }
var type = '',
text = '',
count = 0,
optional,
nullable,
variable;
// type expressions start with '{'
if (tagValue[0] === '{') {
count++;
// find matching closer '}'
for (var i = 1, leni = tagValue.length; i < leni; i++) {
if (tagValue[i] === '\\') { i++; continue; } // backslash escapes the next character
if (tagValue[i] === '{') { count++; }
else if (tagValue[i] === '}') { count--; }
if (count === 0) { /**
type = trim(tagValue.slice(1, i)) @param {string} tagValue
.replace(/\\\{/g, '{') // unescape escaped curly braces @returns {Array.<string>}
.replace(/\\\}/g, '}'); */
text = trim(tagValue.slice(i+1)); exports.parse = function(tagValue) {
break; if (typeof tagValue !== 'string') { tagValue = ''; }
} var type = '',
text = '',
count = 0,
optional,
nullable,
variable;
// type expressions start with '{'
if (tagValue[0] === '{') {
count++;
// find matching closer '}'
for (var i = 1, leni = tagValue.length; i < leni; i++) {
if (tagValue[i] === '\\') { i++; continue; } // backslash escapes the next character
if (tagValue[i] === '{') { count++; }
else if (tagValue[i] === '}') { count--; }
if (count === 0) {
type = trim(tagValue.slice(1, i))
.replace(/\\\{/g, '{') // unescape escaped curly braces
.replace(/\\\}/g, '}');
text = trim(tagValue.slice(i+1));
break;
} }
} }
}
if (type === '') { text = tagValue; } if (type === '') { text = tagValue; }
[type, optional] = parseOptional(type);
[type, nullable] = parseNullable(type);
[type, variable] = parseVariable(type);
type = parseTypes(type); // make it into an array [type, optional] = parseOptional(type);
[type, nullable] = parseNullable(type);
[type, variable] = parseVariable(type);
return [type, text, optional, nullable, variable]; type = parseTypes(type); // make it into an array
return [type, text, optional, nullable, variable];
}
function parseOptional(type) {
var optional = null;
// {sometype=} means optional
if ( /(.+)=$/.test(type) ) {
type = RegExp.$1;
optional = true;
} }
function parseOptional(type) { return [type, optional];
var optional = null; }
// {sometype=} means optional function parseNullable(type) {
if ( /(.+)=$/.test(type) ) { var nullable = null;
// {?sometype} means nullable, {!sometype} means not-nullable
if ( /^([\?\!])(.+)$/.test(type) ) {
type = RegExp.$2;
nullable = (RegExp.$1 === '?')? true : false;
}
return [type, nullable];
}
function parseVariable(type) {
var variable = null;
// {...sometype} means variable number of that type
if ( /^(\.\.\.)(.+)$/.test(type) ) {
type = RegExp.$2;
variable = true;
}
return [type, variable];
}
function parseTypes(type) {
var types = [];
if ( ~type.indexOf('|') ) {
// remove optional parens, like: { ( string | number ) }
// see: http://code.google.com/closure/compiler/docs/js-for-compiler.html#types
if ( /^\s*\(\s*(.+)\s*\)\s*$/.test(type) ) {
type = RegExp.$1; type = RegExp.$1;
optional = true;
} }
types = type.split(/\s*\|\s*/g);
return [type, optional]; }
else if (type) {
types = [type];
} }
function parseNullable(type) { return types;
var nullable = null; }
// {?sometype} means nullable, {!sometype} means not-nullable
if ( /^([\?\!])(.+)$/.test(type) ) {
type = RegExp.$2;
nullable = (RegExp.$1 === '?')? true : false;
}
return [type, nullable];
}
function parseVariable(type) {
var variable = null;
// {...sometype} means variable number of that type
if ( /^(\.\.\.)(.+)$/.test(type) ) {
type = RegExp.$2;
variable = true;
}
return [type, variable];
}
function parseTypes(type) {
var types = [];
if ( ~type.indexOf('|') ) {
// remove optional parens, like: { ( string | number ) }
// see: http://code.google.com/closure/compiler/docs/js-for-compiler.html#types
if ( /^\s*\(\s*(.+)\s*\)\s*$/.test(type) ) {
type = RegExp.$1;
}
types = type.split(/\s*\|\s*/g);
}
else if (type) {
types = [type];
}
return types;
}
/** @private */ /** @private */
function trim(text) { function trim(text) {
return text.replace(/^\s+|\s+$/g, ''); return text.replace(/^\s+|\s+$/g, '');
} }
})();

View File

@ -5,48 +5,47 @@
@author Michael Mathews <micmath@gmail.com> @author Michael Mathews <micmath@gmail.com>
@license Apache License 2.0 - See file 'LICENSE.md' in this project. @license Apache License 2.0 - See file 'LICENSE.md' in this project.
*/ */
(function() {
var dictionary = require('jsdoc/tag/dictionary'); var dictionary = require('jsdoc/tag/dictionary');
/** /**
Validate the given tag. Validate the given tag.
*/ */
exports.validate = function(tag, meta) { exports.validate = function(tag, meta) {
var tagDef = dictionary.lookUp(tag.title); var tagDef = dictionary.lookUp(tag.title);
if (!tagDef && !env.conf.tags.allowUnknownTags) { if (!tagDef && !env.conf.tags.allowUnknownTags) {
throw new UnknownTagError(tag.title, meta); throw new UnknownTagError(tag.title, meta);
}
if (!tag.text) {
if (tagDef.mustHaveValue) {
throw new TagValueRequiredError(tag.title, meta);
}
}
else {
if (tagDef.mustNotHaveValue) {
throw new TagValueNotPermittedError(tag.title, meta);
}
}
}
function UnknownTagError(tagName, meta) {
this.name = 'UnknownTagError';
this.message = 'The @' + tagName + ' tag is not a known tag. File: ' + meta.filename + ', Line: ' + meta.lineno + '\n' + meta.comment;
} }
UnknownTagError.prototype = Error.prototype;
if (!tag.text) {
function TagValueRequiredError(tagName, meta) { if (tagDef.mustHaveValue) {
this.name = 'TagValueRequiredError'; throw new TagValueRequiredError(tag.title, meta);
this.message = 'The @' + tagName + ' tag requires a value. File: ' + meta.filename + ', Line: ' + meta.lineno + '\n' + meta.comment; }
} }
TagValueRequiredError.prototype = Error.prototype; else {
if (tagDef.mustNotHaveValue) {
function TagValueNotPermittedError(tagName, message, meta) { throw new TagValueNotPermittedError(tag.title, meta);
this.name = 'TagValueNotPermittedError'; }
this.message = 'The @' + tagName + ' tag does not permit a value: "' + message + '". File: ' + meta.filename + ', Line: ' + meta.lineno + '\n' + meta.comment;
} }
TagValueNotPermittedError.prototype = Error.prototype; }
})(); function UnknownTagError(tagName, meta) {
this.name = 'UnknownTagError';
this.message = 'The @' + tagName + ' tag is not a known tag. File: ' + meta.filename + ', Line: ' + meta.lineno + '\n' + meta.comment;
}
UnknownTagError.prototype = Error.prototype;
function TagValueRequiredError(tagName, meta) {
this.name = 'TagValueRequiredError';
this.message = 'The @' + tagName + ' tag requires a value. File: ' + meta.filename + ', Line: ' + meta.lineno + '\n' + meta.comment;
}
TagValueRequiredError.prototype = Error.prototype;
function TagValueNotPermittedError(tagName, message, meta) {
this.name = 'TagValueNotPermittedError';
this.message = 'The @' + tagName + ' tag does not permit a value: "' + message + '". File: ' + meta.filename + ', Line: ' + meta.lineno + '\n' + meta.comment;
}
TagValueNotPermittedError.prototype = Error.prototype;

View File

@ -4,155 +4,154 @@
@author Michael Mathews <micmath@gmail.com> @author Michael Mathews <micmath@gmail.com>
@license Apache License 2.0 - See file 'LICENSE.md' in this project. @license Apache License 2.0 - See file 'LICENSE.md' in this project.
*/ */
(function() {
/**
@param {any} object
*/
exports.dump = function(object) {
indentBy = 0;
output = '';
walk(object);
outdent(false);
return output;
}
const INDENTATION = ' '; // 4 spaces
var indentBy,
output;
function pad(depth) {
var padding = '';
while (depth--) {
padding += INDENTATION;
}
return padding;
}
/**
@param {string} openingBrace - The opening brace to add, like "{".
@private
@inner
@memberof module:common/dumper
*/
function indent(openingBrace) {
indentBy++;
if (openingBrace) output += openingBrace + '\n';
}
/**
@param {string|boolean} closingBrace - The closing brace to add, like "}" or if boolean
`false` no closing brace or trailing newline.
@private
@inner
@memberof module:common/dumper
*/
function outdent(closingBrace) {
indentBy--;
output = output.replace(/,\n$/, '\n'); // trim trailing comma
if (closingBrace === false) { output = output.replace(/\n$/, ''); }
else if (closingBrace) output += pad(indentBy) + closingBrace + ',\n';
}
var seen = [];
seen.has = function(object) {
for (var i = 0, l = seen.length; i < l; i++) {
if (seen[i] === object) { return true; }
}
return false;
}
function walk(object) {
var value;
if ( value = getValue(object) ) {
output += value + ',\n';
}
else if ( isUnwalkable(object) ) {
output += '<Object>,\n'
}
else if ( isRegExp(object) ) {
output += '<RegExp ' + object + '>,\n'
}
else if ( isDate(object) ) {
output += '<Date ' + object.toUTCString() + '>,\n'
}
else if ( isFunction(object) ) {
output += '<Function' + (object.name? ' '+ object.name : '') + '>,\n';
}
else if ( isArray(object) ) {
if ( seen.has(object) ) {
output += '<CircularRef>,\n';
return;
}
else {
seen.push(object);
}
indent('[');
for (var i = 0, leni = object.length; i < leni; i++) {
output += pad(indentBy); // + i + ': ';
walk( object[i] );
}
outdent(']');
}
else if ( isObject(object) ) {
if ( seen.has(object) ) {
output += '<CircularRef>,\n';
return;
}
else {
seen.push(object);
}
indent('{');
for (var p in object) {
if ( object.hasOwnProperty(p) ) {
output += pad(indentBy) + stringify(p) + ': ';
walk( object[p] );
}
}
outdent('}');
}
}
function getValue(o) { // see: https://developer.mozilla.org/en/JavaScript/Reference/Operators/Special/typeof
if (o === null) { return 'null'; }
if ( /^(string|boolean|number|undefined)$/.test(typeof o) ) {
return ''+stringify(o);
}
}
function stringify(o) {
return JSON.stringify(o);
}
function isUnwalkable(o) { // some objects are unwalkable, like Java native objects
return (typeof o === 'object' && typeof o.constructor === 'undefined');
}
function isArray(o) {
return o && (o instanceof Array) || o.constructor === Array;
}
function isRegExp(o) {
return (o instanceof RegExp) ||
(typeof o.constructor !== 'undefined' && o.constructor.name === 'RegExp');
}
function isDate(o) {
return o && (o instanceof Date) ||
(typeof o.constructor !== 'undefined' && o.constructor.name === 'Date');
}
function isFunction(o) {
return o && (typeof o === 'function' || o instanceof Function);// ||
//(typeof o.constructor !== 'undefined' && (o.constructor||{}).name === 'Function');
}
function isObject(o) {
return o && o instanceof Object ||
(typeof o.constructor !== 'undefined' && o.constructor.name === 'Object');
}
})(); /**
@param {any} object
*/
exports.dump = function(object) {
indentBy = 0;
output = '';
walk(object);
outdent(false);
return output;
}
const INDENTATION = ' '; // 4 spaces
var indentBy,
output;
function pad(depth) {
var padding = '';
while (depth--) {
padding += INDENTATION;
}
return padding;
}
/**
@param {string} openingBrace - The opening brace to add, like "{".
@private
@inner
@memberof module:common/dumper
*/
function indent(openingBrace) {
indentBy++;
if (openingBrace) output += openingBrace + '\n';
}
/**
@param {string|boolean} closingBrace - The closing brace to add, like "}" or if boolean
`false` no closing brace or trailing newline.
@private
@inner
@memberof module:common/dumper
*/
function outdent(closingBrace) {
indentBy--;
output = output.replace(/,\n$/, '\n'); // trim trailing comma
if (closingBrace === false) { output = output.replace(/\n$/, ''); }
else if (closingBrace) output += pad(indentBy) + closingBrace + ',\n';
}
var seen = [];
seen.has = function(object) {
for (var i = 0, l = seen.length; i < l; i++) {
if (seen[i] === object) { return true; }
}
return false;
}
function walk(object) {
var value;
if ( value = getValue(object) ) {
output += value + ',\n';
}
else if ( isUnwalkable(object) ) {
output += '<Object>,\n'
}
else if ( isRegExp(object) ) {
output += '<RegExp ' + object + '>,\n'
}
else if ( isDate(object) ) {
output += '<Date ' + object.toUTCString() + '>,\n'
}
else if ( isFunction(object) ) {
output += '<Function' + (object.name? ' '+ object.name : '') + '>,\n';
}
else if ( isArray(object) ) {
if ( seen.has(object) ) {
output += '<CircularRef>,\n';
return;
}
else {
seen.push(object);
}
indent('[');
for (var i = 0, leni = object.length; i < leni; i++) {
output += pad(indentBy); // + i + ': ';
walk( object[i] );
}
outdent(']');
}
else if ( isObject(object) ) {
if ( seen.has(object) ) {
output += '<CircularRef>,\n';
return;
}
else {
seen.push(object);
}
indent('{');
for (var p in object) {
if ( object.hasOwnProperty(p) ) {
output += pad(indentBy) + stringify(p) + ': ';
walk( object[p] );
}
}
outdent('}');
}
}
function getValue(o) { // see: https://developer.mozilla.org/en/JavaScript/Reference/Operators/Special/typeof
if (o === null) { return 'null'; }
if ( /^(string|boolean|number|undefined)$/.test(typeof o) ) {
return ''+stringify(o);
}
}
function stringify(o) {
return JSON.stringify(o);
}
function isUnwalkable(o) { // some objects are unwalkable, like Java native objects
return (typeof o === 'object' && typeof o.constructor === 'undefined');
}
function isArray(o) {
return o && (o instanceof Array) || o.constructor === Array;
}
function isRegExp(o) {
return (o instanceof RegExp) ||
(typeof o.constructor !== 'undefined' && o.constructor.name === 'RegExp');
}
function isDate(o) {
return o && (o instanceof Date) ||
(typeof o.constructor !== 'undefined' && o.constructor.name === 'Date');
}
function isFunction(o) {
return o && (typeof o === 'function' || o instanceof Function);// ||
//(typeof o.constructor !== 'undefined' && (o.constructor||{}).name === 'Function');
}
function isObject(o) {
return o && o instanceof Object ||
(typeof o.constructor !== 'undefined' && o.constructor.name === 'Object');
}

17
rhino_modules/path.js Normal file
View File

@ -0,0 +1,17 @@
exports.basename = function(path) {
var parts = path.split('/');
parts.pop();
path = parts.join('/');
return path;
};
exports.existsSync = function(path) {
var file = new java.io.File(path);
if (file.isFile()) {
return true;
}
return false;
};

View File

@ -1,18 +0,0 @@
module.exports = {
basename : function(path) {
var parts = path.split('/');
parts.pop();
path = parts.join('/');
return path;
},
existsSync: function(path) {
var file = new java.io.File(path);
if (file.isFile()) {
return true;
}
return false;
}
};

4
rhino_modules/sys.js Normal file
View File

@ -0,0 +1,4 @@
exports.puts = function(str) {
print(String(str));
};

View File

@ -1,6 +0,0 @@
module.exports = {
'puts' : function(str) {
print(String(str));
}
};