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);
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);
});
for (prop in o){
if ( o.hasOwnProperty(prop) ) {
clone[prop] = (o[prop] instanceof Object)? doop(o[prop]) : o[prop];
} }
} });
return clone;
}
return o;
};
})(); delete doc.borrowed;
}
});
}
/**
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')
};
/** var jsdoc = {
@class tag: {
@classdesc Represents a single JSDoc comment. Tag: require('jsdoc/tag').Tag,
@param {string} docletSrc - The raw source code of the jsdoc comment. dictionary: require('jsdoc/tag/dictionary')
@param {object=} meta - Properties describing the code related to this comment. },
*/ name: require('jsdoc/name')
exports.Doclet = function(docletSrc, meta) { };
var newTags = [];
/** The original text of the comment from the source code. */ /**
this.comment = docletSrc; @class
this.setMeta(meta); @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 = [];
docletSrc = unwrap(docletSrc); /** The original text of the comment from the source code. */
docletSrc = fixDescription(docletSrc); this.comment = docletSrc;
this.setMeta(meta);
newTags = toTags.call(this, docletSrc); 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(); for (var i = 0, leni = newTags.length; i < leni; i++) {
} this.addTag(newTags[i].title, newTags[i].text);
/** 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) {
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;
} }
/** 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); /** Called once after all tags have been added. */
tagSrcs = split(docletSrc); 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) );
}
}
for each(tagSrc in tagSrcs) { /** Add a tag to this doclet.
tags.push( {title: tagSrc.title, text: tagSrc.text} ); @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);
return tags; 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 unwrap(docletSrc) { function split(docletSrc) {
if (!docletSrc) { return ''; } var tagSrcs = [];
// note: keep trailing whitespace for @examples // split out the basic tags, keep surrounding whitespace
// extra opening/closing stars are ignored // like: @tagTitle tagBody
// left margin is considered a star and a space docletSrc
// use the /m flag on regex to avoid having to guess what this platform's newline is .replace(/^(\s*)@(\S)/gm, '$1\\@$2') // replace splitter ats with an arbitrary sequence
docletSrc = .split('\\@') // then split on that arbitrary sequence
docletSrc.replace(/^\/\*\*+/, '') // remove opening slash+stars .forEach(function($) {
.replace(/\**\*\/$/, "\\Z") // replace closing star slash with end-marker if ($) {
.replace(/^\s*(\* ?|\\Z)/gm, '') // remove left margin like: spaces+star or spaces+end-marker var parsedTag = $.match(/^(\S+)(:?\s+(\S[\s\S]*))?/);
.replace(/\s*\\Z$/g, ''); // remove end-marker
return docletSrc; if (parsedTag) {
} var [, tagTitle, tagText] = parsedTag;
function fixDescription(docletSrc) { if (tagTitle) {
if (!/^\s*@/.test(docletSrc)) { tagSrcs.push({
docletSrc = '@description ' + docletSrc; title: tagTitle,
} text: tagText
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 = {
tagDictionary: require('jsdoc/tag/dictionary')
};
var puncToScope = { '.': 'static', '~': 'inner', '#': 'instance' }, var jsdoc = {
scopeToPunc = { 'static': '.', 'inner': '~', 'instance': '#' }, tagDictionary: require('jsdoc/tag/dictionary')
Token = Packages.org.mozilla.javascript.Token; };
/** var puncToScope = { '.': 'static', '~': 'inner', '#': 'instance' },
Resolves the longname, memberof, variation and name values of the given doclet. scopeToPunc = { 'static': '.', 'inner': '~', 'instance': '#' },
@param {module:jsdoc/doclet.Doclet} doclet Token = Packages.org.mozilla.javascript.Token;
*/
exports.resolve = function(doclet) {
var name = doclet.name,
memberof = doclet.memberof || '',
about = {},
parentDoc;
name = name? (''+name).replace(/\.prototype\.?/g, '#') : ''; /**
Resolves the longname, memberof, variation and name values of the given doclet.
@param {module:jsdoc/doclet.Doclet} doclet
*/
exports.resolve = function(doclet) {
var name = doclet.name,
memberof = doclet.memberof || '',
about = {},
parentDoc;
// member of a var in an outer scope? name = name? (''+name).replace(/\.prototype\.?/g, '#') : '';
if (name && !memberof && doclet.meta.code && doclet.meta.code.funcscope) {
name = doclet.longname = doclet.meta.code.funcscope + '~' + name; // member of a var in an outer scope?
if (name && !memberof && doclet.meta.code && doclet.meta.code.funcscope) {
name = doclet.longname = doclet.meta.code.funcscope + '~' + name;
}
if (memberof) { // @memberof tag given
memberof = memberof.replace(/\.prototype\.?/g, '#');
// the name is a fullname, like @name foo.bar, @memberof foo
if (name && name.indexOf(memberof) === 0) {
about = exports.shorten(name);
} }
else if (name && /([#.~])$/.test(memberof) ) { // like @memberof foo# or @memberof foo~
if (memberof) { // @memberof tag given about = exports.shorten(memberof + name);
memberof = memberof.replace(/\.prototype\.?/g, '#');
// the name is a fullname, like @name foo.bar, @memberof foo
if (name && name.indexOf(memberof) === 0) {
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 && doclet.scope ) { // like @memberof foo# or @memberof foo~
about = exports.shorten(name); 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);
}
} }
}
else {
if (doclet.name && doclet.memberof && !doclet.longname) {
doclet.scope = 'static'; // default scope when none is provided
if (about.variation) { doclet.setLongname(doclet.memberof + scopeToPunc[doclet.scope] + doclet.name);
doclet.variation = about.variation;
} }
} }
/** if (about.variation) {
@inner doclet.variation = about.variation;
@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; /**
@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, '"') + '"';
}
} }
RegExp.escape = RegExp.escape || function(str) { return name;
var specials = new RegExp("[.*+?|()\\[\\]{}\\\\]", "g"); // .*+?|()[]{}\ }
return str.replace(specials, "\\$&");
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;
@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 ); 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, '' );
} }
return longname; 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
Given a longname like "a.b#c(2)", slice it up into ["a.b", "#", 'c', '2'], var i = atoms.length;
representing the memberof, the scope, the name, and variation. while (i--) {
@param {string} longname longname = longname.replace('@{'+i+'}@', atoms[i]);
@returns {object} Representing the properties of the given name. memberof = memberof.replace('@{'+i+'}@', atoms[i]);
*/ scope = scope.replace('@{'+i+'}@', atoms[i]);
exports.shorten = function(longname) { name = name.replace('@{'+i+'}@', atoms[i]);
// 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};
} }
})(); ////
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 @class
@default @classdesc Represents a JavaScript package.
@type {string} @param {string} json - The contents of package.json.
*/ */
this.kind = 'package'; exports.Package = function(json) {
/** The source files associated with this package.
@type {Array<String>}
*/
this.files = [];
json = JSON.parse(json); /** The kind of this package.
@readonly
@default
@type {string}
*/
this.kind = 'package';
/** The name of this package. json = JSON.parse(json);
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. /** The name of this package.
@type {string} This value is found in the package.json file passed in as a command line option.
*/ @type {string}
this.longname = this.kind + ':' + this.name; */
this.name = json.name;
/** The description of this package. /** The longname of this package.
@type {string} @type {string}
*/ */
this.description = json.description; this.longname = this.kind + ':' + this.name;
/** /** The description of this package.
The hash summary of the source file. @type {string}
@type {string} */
@since 3.2.0 this.description = json.description;
*/
this.version = json.version;
/** /**
* The licenses of this package. The hash summary of the source file.
* @type {Array<Object>} @type {string}
* @example @since 3.2.0
* "licenses": [ */
* { this.version = json.version;
* "type": "GPLv2",
* "url": "http://www.example.com/licenses/gpl.html" /**
* } * The licenses of this package.
* ] * @type {Array<Object>}
*/ * @example
this.licenses = json.licenses; * "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 /**
parser.on('jsdocCommentFound', function(e) { Attach these event handlers to a particular instance of a parser.
var newDoclet = new jsdoc.doclet.Doclet(e.comment, e); @param parser
*/
exports.attachTo = function(parser) {
var jsdoc = {doclet: require('jsdoc/doclet')};
if (!newDoclet.name) { // handles JSDoc comments that include a @name tag -- the code is ignored in such a case
return false; // only interested in virtual comments (with a @name) here parser.on('jsdocCommentFound', function(e) {
} var newDoclet = new jsdoc.doclet.Doclet(e.comment, e);
addDoclet.call(this, newDoclet); if (!newDoclet.name) {
if (newDoclet.kind === 'module') { return false; // only interested in virtual comments (with a @name) here
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;
}
e.doclet = newDoclet;
});
for (var i = 0, l = subDoclets.length; i < l; i++) { // handles named symbols in the code, may or may not have a JSDoc comment attached
newSymbolDoclet.call(this, subDoclets[i], e); 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);
}
});
// an undocumented symbol right after a virtual comment? rhino mistakenly connected the two function newSymbolDoclet(docletSrc, e) {
if (newDoclet.name) { // there was a @name in comment var newDoclet = new jsdoc.doclet.Doclet(docletSrc, e);
// try again, without the comment
e.comment = '@undocumented';
newDoclet = new jsdoc.doclet.Doclet(e.comment, e);
}
if (newDoclet.alias) { // an undocumented symbol right after a virtual comment? rhino mistakenly connected the two
if (newDoclet.alias === '{@thisClass}') { if (newDoclet.name) { // there was a @name in comment
memberofName = this.resolveThis(e.astnode); // try again, without the comment
e.comment = '@undocumented';
newDoclet = new jsdoc.doclet.Doclet(e.comment, e);
}
// "class" refers to the owner of the prototype, not the prototype itself if (newDoclet.alias) {
if ( /^(.+?)(\.prototype|#)$/.test(memberofName) ) { if (newDoclet.alias === '{@thisClass}') {
memberofName = RegExp.$1; memberofName = this.resolveThis(e.astnode);
}
newDoclet.alias = memberofName; // "class" refers to the owner of the prototype, not the prototype itself
if ( /^(.+?)(\.prototype|#)$/.test(memberofName) ) {
memberofName = RegExp.$1;
} }
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;
if ( /^((module.)?exports|this)(\.|$)/.test(newDoclet.name) ) {
var nameStartsWith = RegExp.$1;
newDoclet.name = newDoclet.name.replace(/^(exports|this)(\.|$)/, ''); 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);
} }
} }
} }
newDoclet.postProcess();
} }
else {
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; 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 * @class
* @mixes module:common/events * @mixes module:common/events
* *
* @example <caption>Create a new parser.</caption> * @example <caption>Create a new parser.</caption>
* var jsdocParser = new (require('jsdoc/src/parser').Parser)(); * var jsdocParser = new (require('jsdoc/src/parser').Parser)();
*/ */
exports.Parser = function() { exports.Parser = function() {
this._resultBuffer = []; this._resultBuffer = [];
this.refs = {}; this.refs = {};
} }
require('common/util').mixin(exports.Parser.prototype, require('common/events')); require('common/util').mixin(exports.Parser.prototype, require('common/events'));
/** /**
* Parse the given source files for JSDoc comments. * Parse the given source files for JSDoc comments.
* @param {Array.<string>} sourceFiles An array of filepaths to the JavaScript sources. * @param {Array.<string>} sourceFiles An array of filepaths to the JavaScript sources.
* @param {string} [encoding=utf8] * @param {string} [encoding=utf8]
* *
* @fires jsdocCommentFound * @fires jsdocCommentFound
* @fires symbolFound * @fires symbolFound
* @fires newDoclet * @fires newDoclet
* @fires fileBegin * @fires fileBegin
* @fires fileComplete * @fires fileComplete
* *
* @example <caption>Parse two source files.</caption> * @example <caption>Parse two source files.</caption>
* var myFiles = ['file1.js', 'file2.js']; * var myFiles = ['file1.js', 'file2.js'];
* var docs = jsdocParser.parse(myFiles); * var docs = jsdocParser.parse(myFiles);
*/ */
exports.Parser.prototype.parse = function(sourceFiles, encoding) { exports.Parser.prototype.parse = function(sourceFiles, encoding) {
const SCHEMA = 'javascript:'; const SCHEMA = 'javascript:';
var sourceCode = '', var sourceCode = '',
filename = ''; filename = '';
if (typeof sourceFiles === 'string') { sourceFiles = [sourceFiles]; } if (typeof sourceFiles === 'string') { sourceFiles = [sourceFiles]; }
for (i = 0, leni = sourceFiles.length; i < leni; i++) { for (i = 0, leni = sourceFiles.length; i < leni; i++) {
if (sourceFiles[i].indexOf(SCHEMA) === 0) { if (sourceFiles[i].indexOf(SCHEMA) === 0) {
sourceCode = sourceFiles[i].substr(SCHEMA.length); sourceCode = sourceFiles[i].substr(SCHEMA.length);
filename = '[[string' + i + ']]'; filename = '[[string' + i + ']]';
}
else {
filename = sourceFiles[i];
try {
sourceCode = require('fs').readFileSync(filename, encoding);
}
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;
}
/**
* @returns {Array<Doclet>} The accumulated results of any calls to parse.
*/
exports.Parser.prototype.results = function() {
return this._resultBuffer;
}
/**
* @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.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.
*/
exports.Parser.prototype.resolveThis = function(node) {
var memberof = {};
if (node.enclosingFunction) { /**
* @returns {Array<Doclet>} The accumulated results of any calls to parse.
*/
exports.Parser.prototype.results = function() {
return this._resultBuffer;
}
/**
* @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];
if (!memberof.doclet) return ''; // global?
return memberof.doclet.longname||memberof.doclet.name;
}
}
memberof.id = 'astnode'+parent.hashCode(); /**
memberof.doclet = this.refs[memberof.id]; * 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 (!memberof.doclet) return ''; // global? 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;
if (parent.type === Token.COLON) parent = parent.parent; // go up one more
/** memberof.id = 'astnode'+parent.hashCode();
* Resolve what function a var is limited to. memberof.doclet = this.refs[memberof.id];
* @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 if (!memberof.doclet) return ''; // global?
doclet = this.refs['astnode'+enclosingFunction.hashCode()];
if ( doclet && doclet.meta.vars && ~doclet.meta.vars.indexOf(basename) ) { return memberof.doclet.longname||memberof.doclet.name;
return doclet.longname; }
} else {
return ''; // global?
}
}
return this.resolveVar(enclosingFunction, basename); /**
* 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.
*/
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()) { // look for stand-alone doc comments
if (node.type === Token.SCRIPT && node.comments) {
e = { // note: ALL comments are seen in this block...
comment: commentSrc, for each(var comment in node.comments.toArray()) {
lineno: comment.getLineno(), if (comment.commentType !== Token.CommentType.JSDOC) {
filename: currentSourceName 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 (basename !== 'this') e.code.funcscope = currentParser.resolveVar(node, basename);
if ( isValidJsdoc(e.comment) ) {
currentParser.fire('symbolFound', e, currentParser);
} }
if (e.doclet) { if (commentSrc = ''+comment.toSource()) {
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) ) { 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.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; if ( isValidJsdoc(commentSrc) ) {
} currentParser.fire('jsdocCommentFound', e, currentParser);
/** @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;
}
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; 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 nodeToString(node) {
var str;
if (!node) return; // objlit keys with unsafe variable-name characters must be quoted
if (!/^[$_a-z][$_a-z0-9]*$/i.test(about.name) ) {
if (node.type === Token.GETPROP) { about.name = '"'+about.name.replace(/"/g, '\\"')+'"';
str = [nodeToString(node.target), node.property.string].join('.'); }
} }
else if (node.type === Token.VAR) { about.node = node.right;
str = nodeToString(node.target) about.value = nodeToString(about.node);
} about.type = getTypeName(node.right);
else if (node.type === Token.NAME) { return about;
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 // 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 = {
mixin: require('common/util').mixin,
events: require('common/events')
};
var fs = require('fs'); var common = {
mixin: require('common/util').mixin,
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. @constructor
@param {Array.<string>} searchPaths @mixes module:common.events
@param {number} [depth=1] */
@fires sourceFileFound exports.Scanner = function() {
*/ }
exports.Scanner.prototype.scan = function(searchPaths, depth, includeMatch, excludeMatch) { common.mixin(exports.Scanner.prototype, common.events);
var filePaths = [],
that = this;
searchPaths = searchPaths || []; /**
depth = depth || 1; 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.forEach(function($) { searchPaths = searchPaths || [];
if ( fs.stat($).isFile() ) { depth = depth || 1;
filePaths.push($);
}
else {
filePaths = filePaths.concat(fs.ls($, depth));
}
});
filePaths = filePaths.filter(function($) { searchPaths.forEach(function($) {
if (includeMatch && !includeMatch.test($)) { if ( fs.stat($).isFile() ) {
return false filePaths.push($);
} }
else {
filePaths = filePaths.concat(fs.ls($, depth));
}
});
if (excludeMatch && excludeMatch.test($)) { filePaths = filePaths.filter(function($) {
return false if (includeMatch && !includeMatch.test($)) {
} return false
}
return true; if (excludeMatch && excludeMatch.test($)) {
}); return false
}
filePaths = filePaths.filter(function($) { return true;
var e = { fileName: $ }; });
that.fire('sourceFileFound', e);
return !e.defaultPrevented; filePaths = filePaths.filter(function($) {
}); var e = { fileName: $ };
that.fire('sourceFileFound', e);
return filePaths; 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 = {
tag: {
dictionary: require('jsdoc/tag/dictionary'),
validator: require('jsdoc/tag/validator'),
type: require('jsdoc/tag/type')
}
};
/** var jsdoc = {
Constructs a new tag object. Calls the tag validator. tag: {
@class dictionary: require('jsdoc/tag/dictionary'),
@classdesc Represents a single doclet tag. validator: require('jsdoc/tag/validator'),
@param {string} tagTitle type: require('jsdoc/tag/type')
@param {string=} tagBody }
@param {object=} meta };
*/
exports.Tag = function(tagTitle, tagBody, meta) {
var tagDef = jsdoc.tag.dictionary.lookUp(tagTitle),
meta = meta || {};
this.originalTitle = trim(tagTitle); /**
Constructs a new tag object. Calls the tag validator.
@class
@classdesc Represents a single doclet tag.
@param {string} tagTitle
@param {string=} tagBody
@param {object=} meta
*/
exports.Tag = function(tagTitle, tagBody, meta) {
var tagDef = jsdoc.tag.dictionary.lookUp(tagTitle),
meta = meta || {};
/** The title part of the tag: @title text */ this.originalTitle = trim(tagTitle);
this.title = jsdoc.tag.dictionary.normalise( this.originalTitle );
/** The text part of the tag: @title text */ /** The title part of the tag: @title text */
this.text = trim(tagBody, tagDef.keepsWhitespace); this.title = jsdoc.tag.dictionary.normalise( this.originalTitle );
if (this.text) { /** The text part of the tag: @title text */
this.text = trim(tagBody, tagDef.keepsWhitespace);
if (tagDef.onTagText) { if (this.text) {
this.text = tagDef.onTagText(this.text);
}
if (tagDef.canHaveType) { if (tagDef.onTagText) {
this.text = tagDef.onTagText(this.text);
}
/** The value propertiy represents the result of parsing the tag text. */ if (tagDef.canHaveType) {
this.value = {};
var [ /** The value propertiy represents the result of parsing the tag text. */
/*Array.<string>*/ typeNames, this.value = {};
/*any*/ remainingText,
/*?boolean*/ optional,
/*?boolean*/ nullable,
/*?boolean*/ variable
] = jsdoc.tag.type.parse(this.text);
if (typeNames.length) { var [
this.value.type = { /*Array.<string>*/ typeNames,
names: typeNames, /*any*/ remainingText,
optional: optional, /*?boolean*/ optional,
nullable: nullable, /*?boolean*/ nullable,
variable: variable /*?boolean*/ variable
}; ] = jsdoc.tag.type.parse(this.text);
if (typeNames.length) {
this.value.type = {
names: typeNames,
optional: optional,
nullable: nullable,
variable: variable
};
}
if (remainingText) {
if (tagDef.canHaveName) {
var [paramName, paramDesc, paramOptional, paramDefault]
= parseParamText(remainingText);
// note the dash is a special case: as a param name it means "no name"
if (paramName && paramName !== '-') { this.value.name = paramName; }
if (paramDesc) { this.value.description = paramDesc; }
if (paramOptional) { this.value.optional = paramOptional; }
if (paramDefault) { this.value.defaultvalue = paramDefault; }
} }
else {
if (remainingText) { this.value.description = remainingText;
if (tagDef.canHaveName) {
var [paramName, paramDesc, paramOptional, paramDefault]
= parseParamText(remainingText);
// note the dash is a special case: as a param name it means "no name"
if (paramName && paramName !== '-') { this.value.name = paramName; }
if (paramDesc) { this.value.description = paramDesc; }
if (paramOptional) { this.value.optional = paramOptional; }
if (paramDefault) { this.value.defaultvalue = paramDefault; }
}
else {
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 ''; }
if (newlines) {
return text.replace(/^[\n\r\f]+|[\n\r\f]+$/g, '');
} }
else {
function trim(text, newlines) { return text.replace(/^\s+|\s+$/g, '');
if (!text) { return ''; }
if (newlines) {
return text.replace(/^[\n\r\f]+|[\n\r\f]+$/g, '');
}
else {
return text.replace(/^\s+|\s+$/g, '');
}
} }
}
/** /**
Parse the parameter name and parameter desc from the tag text. Parse the parameter name and parameter desc from the tag text.
@inner @inner
@method parseParamText @method parseParamText
@memberof module:jsdoc/tag @memberof module:jsdoc/tag
@param {string} tagText @param {string} tagText
@returns {Array.<string, string, boolean, boolean>} [pname, pdesc, poptional, pdefault]. @returns {Array.<string, string, boolean, boolean>} [pname, pdesc, poptional, pdefault].
*/ */
function parseParamText(tagText) { function parseParamText(tagText) {
var pname, pdesc, poptional, pdefault; var pname, pdesc, poptional, pdefault;
// like: pname, pname pdesc, or name - pdesc // like: pname, pname pdesc, or name - pdesc
tagText.match(/^(\[[^\]]+\]|\S+)((?:\s*\-\s*|\s+)(\S[\s\S]*))?$/); tagText.match(/^(\[[^\]]+\]|\S+)((?:\s*\-\s*|\s+)(\S[\s\S]*))?$/);
pname = RegExp.$1;
pdesc = RegExp.$3;
if ( /^\[\s*(.+?)\s*\]$/.test(pname) ) {
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 = {},
_definitions = {},
_namespaces = [];
function _TagDefinition(title, etc) { var _synonyms = {},
etc = etc || {}; _definitions = {},
_namespaces = [];
this.title = dictionary.normalise(title); function _TagDefinition(title, etc) {
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) { _TagDefinition.prototype.synonym = function(synonymName) {
_synonyms[synonymName.toLowerCase()] = this.title; _synonyms[synonymName.toLowerCase()] = this.title;
return this; // chainable return this; // chainable
} }
/** @exports jsdoc/tag/dictionary */ /** @exports jsdoc/tag/dictionary */
var dictionary = { var dictionary = {
/** @function */ /** @function */
defineTag: function(title, opts) { defineTag: function(title, opts) {
_definitions[title] = new _TagDefinition(title, opts); _definitions[title] = new _TagDefinition(title, opts);
if (opts.isNamespace) { if (opts.isNamespace) {
_namespaces.push(title); _namespaces.push(title);
}
return _definitions[title];
},
/** @function */
lookUp: function(title) {
title = dictionary.normalise(title);
if ( _definitions.hasOwnProperty(title) ) {
return _definitions[title];
}
return false;
},
/** @function */
isNamespace: function(kind) {
return ( ~ _namespaces.indexOf(kind) );
},
/** @function */
normalise: function(title) {
canonicalName = title.toLowerCase();
if ( _synonyms.hasOwnProperty(canonicalName) ) {
return _synonyms[canonicalName];
}
return canonicalName;
} }
};
require('jsdoc/tag/dictionary/definitions').defineTags(dictionary); return _definitions[title];
},
module.exports = dictionary; /** @function */
lookUp: function(title) {
title = dictionary.normalise(title);
if ( _definitions.hasOwnProperty(title) ) {
return _definitions[title];
}
return false;
},
/** @function */
isNamespace: function(kind) {
return ( ~ _namespaces.indexOf(kind) );
},
/** @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 @param {string} tagValue
@returns {Array.<string>} @returns {Array.<string>}
*/ */
exports.parse = function(tagValue) { exports.parse = function(tagValue) {
if (typeof tagValue !== 'string') { tagValue = ''; } if (typeof tagValue !== 'string') { tagValue = ''; }
var type = '', var type = '',
text = '', text = '',
count = 0, count = 0,
optional, optional,
nullable, nullable,
variable; variable;
// type expressions start with '{' // type expressions start with '{'
if (tagValue[0] === '{') { if (tagValue[0] === '{') {
count++; count++;
// find matching closer '}' // find matching closer '}'
for (var i = 1, leni = tagValue.length; i < leni; i++) { for (var i = 1, leni = tagValue.length; i < leni; i++) {
if (tagValue[i] === '\\') { i++; continue; } // backslash escapes the next character if (tagValue[i] === '\\') { i++; continue; } // backslash escapes the next character
if (tagValue[i] === '{') { count++; } if (tagValue[i] === '{') { count++; }
else if (tagValue[i] === '}') { count--; } else if (tagValue[i] === '}') { count--; }
if (count === 0) { if (count === 0) {
type = trim(tagValue.slice(1, i)) type = trim(tagValue.slice(1, i))
.replace(/\\\{/g, '{') // unescape escaped curly braces .replace(/\\\{/g, '{') // unescape escaped curly braces
.replace(/\\\}/g, '}'); .replace(/\\\}/g, '}');
text = trim(tagValue.slice(i+1)); text = trim(tagValue.slice(i+1));
break; break;
}
} }
} }
if (type === '') { text = tagValue; }
[type, optional] = parseOptional(type);
[type, nullable] = parseNullable(type);
[type, variable] = parseVariable(type);
type = parseTypes(type); // make it into an array
return [type, text, optional, nullable, variable];
} }
function parseOptional(type) { if (type === '') { text = tagValue; }
var optional = null;
// {sometype=} means optional [type, optional] = parseOptional(type);
if ( /(.+)=$/.test(type) ) { [type, nullable] = parseNullable(type);
[type, variable] = parseVariable(type);
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;
}
return [type, optional];
}
function parseNullable(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 /** @private */
if ( /^([\?\!])(.+)$/.test(type) ) { function trim(text) {
type = RegExp.$2; return text.replace(/^\s+|\s+$/g, '');
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 */
function trim(text) {
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.
*/
exports.validate = function(tag, meta) {
var tagDef = dictionary.lookUp(tag.title);
if (!tagDef && !env.conf.tags.allowUnknownTags) { /**
throw new UnknownTagError(tag.title, meta); Validate the given tag.
} */
exports.validate = function(tag, meta) {
var tagDef = dictionary.lookUp(tag.title);
if (!tag.text) { if (!tagDef && !env.conf.tags.allowUnknownTags) {
if (tagDef.mustHaveValue) { throw new UnknownTagError(tag.title, meta);
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;
function TagValueRequiredError(tagName, meta) { if (!tag.text) {
this.name = 'TagValueRequiredError'; if (tagDef.mustHaveValue) {
this.message = 'The @' + tagName + ' tag requires a value. File: ' + meta.filename + ', Line: ' + meta.lineno + '\n' + meta.comment; throw new TagValueRequiredError(tag.title, meta);
}
} }
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); @param {any} object
return output; */
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;
}
const INDENTATION = ' '; // 4 spaces /**
var indentBy, @param {string} openingBrace - The opening brace to add, like "{".
output; @private
@inner
@memberof module:common/dumper
*/
function indent(openingBrace) {
indentBy++;
if (openingBrace) output += openingBrace + '\n';
}
function pad(depth) { /**
var padding = ''; @param {string|boolean} closingBrace - The closing brace to add, like "}" or if boolean
while (depth--) { `false` no closing brace or trailing newline.
padding += INDENTATION; @private
} @inner
return padding; @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) {
@param {string} openingBrace - The opening brace to add, like "{". var value;
@private
@inner if ( value = getValue(object) ) {
@memberof module:common/dumper output += value + ',\n';
*/
function indent(openingBrace) {
indentBy++;
if (openingBrace) output += openingBrace + '\n';
} }
else if ( isUnwalkable(object) ) {
/** output += '<Object>,\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';
} }
else if ( isRegExp(object) ) {
var seen = []; output += '<RegExp ' + object + '>,\n'
seen.has = function(object) {
for (var i = 0, l = seen.length; i < l; i++) {
if (seen[i] === object) { return true; }
}
return false;
} }
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);
}
function walk(object) { indent('[');
var value; 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);
}
if ( value = getValue(object) ) { indent('{');
output += value + ',\n'; for (var p in object) {
} if ( object.hasOwnProperty(p) ) {
else if ( isUnwalkable(object) ) { output += pad(indentBy) + stringify(p) + ': ';
output += '<Object>,\n' walk( object[p] );
}
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('}');
} }
outdent('}');
} }
}
function getValue(o) { // see: https://developer.mozilla.org/en/JavaScript/Reference/Operators/Special/typeof function getValue(o) { // see: https://developer.mozilla.org/en/JavaScript/Reference/Operators/Special/typeof
if (o === null) { return 'null'; } if (o === null) { return 'null'; }
if ( /^(string|boolean|number|undefined)$/.test(typeof o) ) { if ( /^(string|boolean|number|undefined)$/.test(typeof o) ) {
return ''+stringify(o); return ''+stringify(o);
}
} }
}
function stringify(o) { function stringify(o) {
return JSON.stringify(o); return JSON.stringify(o);
} }
function isUnwalkable(o) { // some objects are unwalkable, like Java native objects function isUnwalkable(o) { // some objects are unwalkable, like Java native objects
return (typeof o === 'object' && typeof o.constructor === 'undefined'); return (typeof o === 'object' && typeof o.constructor === 'undefined');
} }
function isArray(o) { function isArray(o) {
return o && (o instanceof Array) || o.constructor === Array; return o && (o instanceof Array) || o.constructor === Array;
} }
function isRegExp(o) { function isRegExp(o) {
return (o instanceof RegExp) || return (o instanceof RegExp) ||
(typeof o.constructor !== 'undefined' && o.constructor.name === 'RegExp'); (typeof o.constructor !== 'undefined' && o.constructor.name === 'RegExp');
} }
function isDate(o) { function isDate(o) {
return o && (o instanceof Date) || return o && (o instanceof Date) ||
(typeof o.constructor !== 'undefined' && o.constructor.name === 'Date'); (typeof o.constructor !== 'undefined' && o.constructor.name === 'Date');
} }
function isFunction(o) { function isFunction(o) {
return o && (typeof o === 'function' || o instanceof Function);// || return o && (typeof o === 'function' || o instanceof Function);// ||
//(typeof o.constructor !== 'undefined' && (o.constructor||{}).name === 'Function'); //(typeof o.constructor !== 'undefined' && (o.constructor||{}).name === 'Function');
} }
function isObject(o) { function isObject(o) {
return o && o instanceof Object || return o && o instanceof Object ||
(typeof o.constructor !== 'undefined' && o.constructor.name === '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));
}
};