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
PWD=`pwd`
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} $@

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');
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//

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';
return readFile(filename, encoding);
}
};
function readdirSync(path) {
var readdirSync = exports.readdirSync = function(path) {
var dir,
files;
@ -14,9 +14,9 @@ function readdirSync(path) {
files = dir.list();
return files;
}
};
function ls(dir, recurse, _allFiles, _path) {
var ls = exports.ls = function(dir, recurse, _allFiles, _path) {
var files,
file;
@ -56,9 +56,9 @@ function ls(dir, recurse, _allFiles, _path) {
}
return _allFiles;
}
};
function stat(path, encoding) {
var stat = exports.stat = function(path, encoding) {
var f = new java.io.File(path)
return {
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(/[\\\/]/);
var make = "";
for (var i = 0, l = path.length; i < l; i++) {
@ -80,7 +80,7 @@ function mkPath(/**Array*/ path) {
makeDir(make);
}
}
}
};
function makeDir(/**string*/ path) {
var dirPath = toDir(path);
@ -102,7 +102,7 @@ function exists(path) {
return true;
}
function toDir(path) {
var toDir = exports.toDir = function(path) {
var f = new java.io.File(path);
if (f.isDirectory()){
@ -113,9 +113,9 @@ function toDir(path) {
parts.pop();
return parts.join('/');
}
};
function copyFile(inFile, outDir, fileName) {
exports.copyFile = function(inFile, outDir, fileName) {
if (fileName == null) fileName = toFile(inFile);
outDir = toDir(outDir);
@ -131,14 +131,14 @@ function copyFile(inFile, outDir, fileName) {
}
bos.close();
bis.close();
}
};
function toFile(path) {
var parts = path.split(/[\\\/]/);
return parts.pop();
}
function writeFileSync(filename, data, encoding) {
exports.writeFileSync = function(filename, data, encoding) {
encoding = encoding || 'utf-8';
var out = new Packages.java.io.PrintWriter(
@ -155,16 +155,4 @@ function writeFileSync(filename, data, encoding) {
out.flush();
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>
@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
/**
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,
moving docs from the "borrowed" array and into the general docs, then
deleting the "borrowed" array.
*/
exports.resolveBorrows = function(docs) {
if (!docs.index) {
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;
}
});
// 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
docs for the borrowing symbol. This process changes the symbols involved,
moving docs from the "borrowed" array and into the general docs, then
deleting the "borrowed" array.
*/
exports.resolveBorrows = function(docs) {
if (!docs.index) {
throw 'Docs has not been indexed: docs.index must be defined here.';
}
/**
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];
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);
});
}
}
return clone;
});
delete doc.borrowed;
}
return o;
};
})();
});
}
/**
Deep clone a simple object.
@private
*/
function doop(o) {
if (o instanceof Object && o.constructor != Function) {
var clone = o instanceof Array ? [] : {}, prop;
for (prop in o){
if ( o.hasOwnProperty(prop) ) {
clone[prop] = (o[prop] instanceof Object)? doop(o[prop]) : o[prop];
}
}
return clone;
}
return o;
};

View File

@ -10,298 +10,296 @@
@requires jsdoc/name
@requires jsdoc/tag/dictionary
*/
(function() {
var jsdoc = {
tag: {
Tag: require('jsdoc/tag').Tag,
dictionary: require('jsdoc/tag/dictionary')
},
name: require('jsdoc/name')
};
/**
@class
@classdesc Represents a single JSDoc comment.
@param {string} docletSrc - The raw source code of the jsdoc comment.
@param {object=} meta - Properties describing the code related to this comment.
*/
exports.Doclet = function(docletSrc, meta) {
var newTags = [];
/** The original text of the comment from the source code. */
this.comment = docletSrc;
this.setMeta(meta);
docletSrc = unwrap(docletSrc);
docletSrc = fixDescription(docletSrc);
newTags = toTags.call(this, docletSrc);
var jsdoc = {
tag: {
Tag: require('jsdoc/tag').Tag,
dictionary: require('jsdoc/tag/dictionary')
},
name: require('jsdoc/name')
};
/**
@class
@classdesc Represents a single JSDoc comment.
@param {string} docletSrc - The raw source code of the jsdoc comment.
@param {object=} meta - Properties describing the code related to this comment.
*/
exports.Doclet = function(docletSrc, meta) {
var newTags = [];
/** The original text of the comment from the source code. */
this.comment = docletSrc;
this.setMeta(meta);
docletSrc = unwrap(docletSrc);
docletSrc = fixDescription(docletSrc);
for (var i = 0, leni = newTags.length; i < leni; i++) {
this.addTag(newTags[i].title, newTags[i].text);
}
this.postProcess();
}
/** Called once after all tags have been added. */
exports.Doclet.prototype.postProcess = function() {
if (!this.preserveName) { jsdoc.name.resolve(this); }
if (this.name && !this.longname) {
this.setLongname(this.name);
}
if (this.memberof === '') {
delete(this.memberof);
}
if (!this.kind && this.meta && this.meta.code) {
this.addTag( 'kind', codetypeToKind(this.meta.code.type) );
}
}
/** Add a tag to this doclet.
@param {string} title - The title of the tag being added.
@param {string} [text] - The text of the tag being added.
*/
exports.Doclet.prototype.addTag = function(title, text) {
var tagDef = jsdoc.tag.dictionary.lookUp(title),
newTag = new jsdoc.tag.Tag(title, text, this.meta);
newTags = toTags.call(this, docletSrc);
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;
for (var i = 0, leni = newTags.length; i < leni; i++) {
this.addTag(newTags[i].title, newTags[i].text);
}
/**
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
this.postProcess();
}
return docletSrc;
}
function fixDescription(docletSrc) {
if (!/^\s*@/.test(docletSrc)) {
docletSrc = '@description ' + docletSrc;
}
return docletSrc;
}
function split(docletSrc) {
var tagSrcs = [];
// split out the basic tags, keep surrounding whitespace
// like: @tagTitle tagBody
docletSrc
.replace(/^(\s*)@(\S)/gm, '$1\\@$2') // replace splitter ats with an arbitrary sequence
.split('\\@') // then split on that arbitrary sequence
.forEach(function($) {
if ($) {
var parsedTag = $.match(/^(\S+)(:?\s+(\S[\s\S]*))?/);
if (parsedTag) {
var [, tagTitle, tagText] = parsedTag;
/** 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) );
}
}
if (tagTitle) {
tagSrcs.push({
title: tagTitle,
text: tagText
});
}
/** 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;
}
/**
Convert the raw source of the doclet comment into an array of Tag objects.
@private
*/
function toTags(docletSrc) {
var tagSrcs,
tags = [];
docletSrc = unwrap(docletSrc);
tagSrcs = split(docletSrc);
for each(tagSrc in tagSrcs) {
tags.push( {title: tagSrc.title, text: tagSrc.text} );
}
return tags;
}
function unwrap(docletSrc) {
if (!docletSrc) { return ''; }
// note: keep trailing whitespace for @examples
// extra opening/closing stars are ignored
// left margin is considered a star and a space
// use the /m flag on regex to avoid having to guess what this platform's newline is
docletSrc =
docletSrc.replace(/^\/\*\*+/, '') // remove opening slash+stars
.replace(/\**\*\/$/, "\\Z") // replace closing star slash with end-marker
.replace(/^\s*(\* ?|\\Z)/gm, '') // remove left margin like: spaces+star or spaces+end-marker
.replace(/\s*\\Z$/g, ''); // remove end-marker
return docletSrc;
}
function fixDescription(docletSrc) {
if (!/^\s*@/.test(docletSrc)) {
docletSrc = '@description ' + docletSrc;
}
return docletSrc;
}
function split(docletSrc) {
var tagSrcs = [];
// split out the basic tags, keep surrounding whitespace
// like: @tagTitle tagBody
docletSrc
.replace(/^(\s*)@(\S)/gm, '$1\\@$2') // replace splitter ats with an arbitrary sequence
.split('\\@') // then split on that arbitrary sequence
.forEach(function($) {
if ($) {
var parsedTag = $.match(/^(\S+)(:?\s+(\S[\s\S]*))?/);
if (parsedTag) {
var [, tagTitle, tagText] = parsedTag;
if (tagTitle) {
tagSrcs.push({
title: tagTitle,
text: tagText
});
}
}
});
return tagSrcs;
}
}
});
})();
return tagSrcs;
}

View File

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

View File

@ -4,70 +4,69 @@
@author Michael Mathews <micmath@gmail.com>
@license Apache License 2.0 - See file 'LICENSE.md' in this project.
*/
(function() {
var common = {
args: require('common/args')
var common = {
args: require('common/args')
};
var argParser = new common.args.ArgParser(),
ourOptions,
defaults = {
template: 'default',
destination: './out/'
};
var argParser = new common.args.ArgParser(),
ourOptions,
defaults = {
template: 'default',
destination: './out/'
};
argParser.addOption('t', 'template', true, 'The name of the template to use. Default: the "default" template');
argParser.addOption('c', 'configure', true, 'The path to the configuration file. Default: jsdoc __dirname + /conf.json');
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.');
argParser.addOption('t', 'template', true, 'The name of the template to use. Default: the "default" template');
argParser.addOption('c', 'configure', true, 'The path to the configuration file. Default: jsdoc __dirname + /conf.json');
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 [-f, filter] = a regex to filter on <-- this can be better defined in the configs?
/**
Set the options for this app.
@throws {Error} Illegal arguments will throw errors.
@param {string|String[]} args The command line arguments for this app.
*/
exports.parse = function(args) {
args = args || [];
if (typeof args === 'string' || args.constructor === String) {
args = (''+args).split(/\s+/g);
}
ourOptions = argParser.parse(args, defaults);
/**
Set the options for this app.
@throws {Error} Illegal arguments will throw errors.
@param {string|String[]} args The command line arguments for this app.
*/
exports.parse = function(args) {
args = args || [];
if (typeof args === 'string' || args.constructor === String) {
args = (''+args).split(/\s+/g);
}
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;
}
/**
Display help message for options.
*/
exports.help = function() {
return argParser.help();
else {
return ourOptions[name];
}
/**
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
@see http://wiki.commonjs.org/wiki/Packages/1.0
*/
(function() {
/**
@class
@classdesc Represents a JavaScript package.
@param {string} json - The contents of package.json.
*/
exports.Package = function(json) {
/** The source files associated with this package.
@type {Array<String>}
*/
this.files = [];
/** The kind of this package.
@readonly
@default
@type {string}
*/
this.kind = 'package';
json = JSON.parse(json);
/** The name of this package.
This value is found in the package.json file passed in as a command line option.
@type {string}
*/
this.name = json.name;
/** The longname of this package.
@type {string}
*/
this.longname = this.kind + ':' + this.name;
/** The description of this package.
@type {string}
*/
this.description = json.description;
/**
The hash summary of the source file.
@type {string}
@since 3.2.0
*/
this.version = json.version;
/**
* The licenses of this package.
* @type {Array<Object>}
* @example
* "licenses": [
* {
* "type": "GPLv2",
* "url": "http://www.example.com/licenses/gpl.html"
* }
* ]
*/
this.licenses = json.licenses;
}
})();
/**
@class
@classdesc Represents a JavaScript package.
@param {string} json - The contents of package.json.
*/
exports.Package = function(json) {
/** The source files associated with this package.
@type {Array<String>}
*/
this.files = [];
/** The kind of this package.
@readonly
@default
@type {string}
*/
this.kind = 'package';
json = JSON.parse(json);
/** The name of this package.
This value is found in the package.json file passed in as a command line option.
@type {string}
*/
this.name = json.name;
/** The longname of this package.
@type {string}
*/
this.longname = this.kind + ':' + this.name;
/** The description of this package.
@type {string}
*/
this.description = json.description;
/**
The hash summary of the source file.
@type {string}
@since 3.2.0
*/
this.version = json.version;
/**
* The licenses of this package.
* @type {Array<Object>}
* @example
* "licenses": [
* {
* "type": "GPLv2",
* "url": "http://www.example.com/licenses/gpl.html"
* }
* ]
*/
this.licenses = json.licenses;
}

View File

@ -2,151 +2,151 @@
@module jsdoc/src/handlers
*/
(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')};
/**
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) {
var newDoclet = new jsdoc.doclet.Doclet(e.comment, e);
// handles JSDoc comments that include a @name tag -- the code is ignored in such a case
parser.on('jsdocCommentFound', function(e) {
var newDoclet = new jsdoc.doclet.Doclet(e.comment, e);
if (!newDoclet.name) {
return false; // only interested in virtual comments (with a @name) here
}
addDoclet.call(this, newDoclet);
if (newDoclet.kind === 'module') {
currentModule = newDoclet.longname;
}
e.doclet = newDoclet;
});
if (!newDoclet.name) {
return false; // only interested in virtual comments (with a @name) here
}
// handles named symbols in the code, may or may not have a JSDoc comment attached
parser.on('symbolFound', function(e) {
var subDoclets = e.comment.split(/@also\b/g);
for (var i = 0, l = subDoclets.length; i < l; i++) {
newSymbolDoclet.call(this, subDoclets[i], e);
}
});
addDoclet.call(this, newDoclet);
if (newDoclet.kind === 'module') {
currentModule = newDoclet.longname;
}
e.doclet = newDoclet;
});
// handles named symbols in the code, may or may not have a JSDoc comment attached
parser.on('symbolFound', function(e) {
var subDoclets = e.comment.split(/@also\b/g);
function newSymbolDoclet(docletSrc, e) {
var newDoclet = new jsdoc.doclet.Doclet(docletSrc, e);
for (var i = 0, l = subDoclets.length; i < l; i++) {
newSymbolDoclet.call(this, subDoclets[i], e);
}
});
function newSymbolDoclet(docletSrc, e) {
var newDoclet = new jsdoc.doclet.Doclet(docletSrc, e);
// an undocumented symbol right after a virtual comment? rhino mistakenly connected the two
if (newDoclet.name) { // there was a @name in comment
// try again, without the comment
e.comment = '@undocumented';
newDoclet = new jsdoc.doclet.Doclet(e.comment, e);
}
if (newDoclet.alias) {
if (newDoclet.alias === '{@thisClass}') {
memberofName = this.resolveThis(e.astnode);
// "class" refers to the owner of the prototype, not the prototype itself
if ( /^(.+?)(\.prototype|#)$/.test(memberofName) ) {
memberofName = RegExp.$1;
}
newDoclet.alias = memberofName;
// an undocumented symbol right after a virtual comment? rhino mistakenly connected the two
if (newDoclet.name) { // there was a @name in comment
// try again, without the comment
e.comment = '@undocumented';
newDoclet = new jsdoc.doclet.Doclet(e.comment, e);
}
if (newDoclet.alias) {
if (newDoclet.alias === '{@thisClass}') {
memberofName = this.resolveThis(e.astnode);
// "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.postProcess();
newDoclet.alias = memberofName;
}
else if (e.code && e.code.name) { // we need to get the symbol name from code
newDoclet.addTag('name', e.code.name);
if (!newDoclet.memberof && e.astnode) {
var memberofName,
scope;
if ( /^((module.)?exports|this)(\.|$)/.test(newDoclet.name) ) {
var nameStartsWith = RegExp.$1;
newDoclet.name = newDoclet.name.replace(/^(exports|this)(\.|$)/, '');
newDoclet.addTag('name', newDoclet.alias);
newDoclet.postProcess();
}
else if (e.code && e.code.name) { // we need to get the symbol name from code
newDoclet.addTag('name', e.code.name);
if (!newDoclet.memberof && e.astnode) {
var memberofName,
scope;
if ( /^((module.)?exports|this)(\.|$)/.test(newDoclet.name) ) {
var nameStartsWith = RegExp.$1;
newDoclet.name = newDoclet.name.replace(/^(exports|this)(\.|$)/, '');
// like /** @module foo */ exports.bar = 1;
if (nameStartsWith === 'exports' && currentModule) {
// like /** @module foo */ exports.bar = 1;
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;
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); }
else {
if (currentModule) {
if (!newDoclet.scope) newDoclet.addTag( 'inner');
if (!newDoclet.memberof && newDoclet.scope !== 'global') newDoclet.addTag( 'memberof', currentModule);
if (memberofName) {
if (newDoclet.name) {
newDoclet.name = memberofName + (scope === 'instance'? '#' : '.') + newDoclet.name;
}
else { newDoclet.name = memberofName; }
}
}
else {
memberofName = this.astnodeToMemberof(e.astnode);
}
newDoclet.postProcess();
}
else {
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);
if (memberofName) { newDoclet.addTag( 'memberof', memberofName); }
else {
if (currentModule) {
if (!newDoclet.scope) newDoclet.addTag( 'inner');
if (!newDoclet.memberof && newDoclet.scope !== 'global') newDoclet.addTag( 'memberof', currentModule);
}
}
}
}
function filter(doclet) {
// you can't document prototypes
if ( /#$/.test(doclet.longname) ) return true;
// you can't document symbols added by the parser with a dummy name
if (doclet.meta.code && doclet.meta.code.name === '____') return true;
newDoclet.postProcess();
}
else {
return false;
}
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
*/
(function() {
var Token = Packages.org.mozilla.javascript.Token,
currentParser = null,
currentSourceName = '';
/**
* @class
* @mixes module:common/events
*
* @example <caption>Create a new parser.</caption>
* var jsdocParser = new (require('jsdoc/src/parser').Parser)();
*/
exports.Parser = function() {
this._resultBuffer = [];
this.refs = {};
}
require('common/util').mixin(exports.Parser.prototype, require('common/events'));
var Token = Packages.org.mozilla.javascript.Token,
currentParser = null,
currentSourceName = '';
/**
* Parse the given source files for JSDoc comments.
* @param {Array.<string>} sourceFiles An array of filepaths to the JavaScript sources.
* @param {string} [encoding=utf8]
*
* @fires jsdocCommentFound
* @fires symbolFound
* @fires newDoclet
* @fires fileBegin
* @fires fileComplete
*
* @example <caption>Parse two source files.</caption>
* var myFiles = ['file1.js', 'file2.js'];
* var docs = jsdocParser.parse(myFiles);
*/
exports.Parser.prototype.parse = function(sourceFiles, encoding) {
const SCHEMA = 'javascript:';
var sourceCode = '',
filename = '';
if (typeof sourceFiles === 'string') { sourceFiles = [sourceFiles]; }
for (i = 0, leni = sourceFiles.length; i < leni; i++) {
if (sourceFiles[i].indexOf(SCHEMA) === 0) {
sourceCode = sourceFiles[i].substr(SCHEMA.length);
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;
}
/**
* @class
* @mixes module:common/events
*
* @example <caption>Create a new parser.</caption>
* var jsdocParser = new (require('jsdoc/src/parser').Parser)();
*/
exports.Parser = function() {
this._resultBuffer = [];
this.refs = {};
}
require('common/util').mixin(exports.Parser.prototype, require('common/events'));
/**
* Parse the given source files for JSDoc comments.
* @param {Array.<string>} sourceFiles An array of filepaths to the JavaScript sources.
* @param {string} [encoding=utf8]
*
* @fires jsdocCommentFound
* @fires symbolFound
* @fires newDoclet
* @fires fileBegin
* @fires fileComplete
*
* @example <caption>Parse two source files.</caption>
* var myFiles = ['file1.js', 'file2.js'];
* var docs = jsdocParser.parse(myFiles);
*/
exports.Parser.prototype.parse = function(sourceFiles, encoding) {
const SCHEMA = 'javascript:';
var sourceCode = '',
filename = '';
/**
* @returns {Array<Doclet>} The accumulated results of any calls to parse.
*/
exports.Parser.prototype.results = function() {
return this._resultBuffer;
}
if (typeof sourceFiles === 'string') { sourceFiles = [sourceFiles]; }
/**
* @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) + '~';
}
for (i = 0, leni = sourceFiles.length; i < leni; i++) {
if (sourceFiles[i].indexOf(SCHEMA) === 0) {
sourceCode = sourceFiles[i].substr(SCHEMA.length);
filename = '[[string' + i + ']]';
}
else {
memberof.id = 'astnode'+node.parent.hashCode();
memberof.doclet = this.refs[memberof.id];
if (!memberof.doclet) return ''; // global?
return memberof.doclet.longname||memberof.doclet.name;
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;
}
/**
* 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) {
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>'; // 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;
}
else {
if (node.enclosingFunction){
return this.resolveThis(node.enclosingFunction/*memberof.doclet.meta.code.val*/);
}
else return ''; // TODO handle global this?
return '<anonymous>~';
}
return (memberof.doclet.longname||memberof.doclet.name) + '~';
}
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();
memberof.doclet = this.refs[memberof.id];
if (!memberof.doclet) return ''; // global?
}
else {
memberof.id = 'astnode'+node.parent.hashCode();
memberof.doclet = this.refs[memberof.id];
if (!memberof.doclet) return ''; // global?
return memberof.doclet.longname||memberof.doclet.name;
}
}
/**
* Resolve what "this" refers too, relative to a node.
* @param {astnode} node - The "this" node
* @returns {string} The longname of the enclosing node.
*/
exports.Parser.prototype.resolveThis = function(node) {
var memberof = {};
if (node.enclosingFunction) {
memberof.id = 'astnode'+node.enclosingFunction.hashCode();
memberof.doclet = this.refs[memberof.id];
if (!memberof.doclet) {
return '<anonymous>'; // TODO handle global this?
}
if (memberof.doclet['this']) {
return memberof.doclet['this'];
}
// like: Foo.constructor = function(n) { /** blah */ this.name = n; }
else if (memberof.doclet.kind === 'function' && memberof.doclet.memberof) {
return memberof.doclet.memberof;
}
// walk up to the closest class we can find
else if (memberof.doclet.kind === 'class' || memberof.doclet.kind === 'module') {
return memberof.doclet.longname||memberof.doclet.name;
}
else {
return ''; // global?
if (node.enclosingFunction){
return this.resolveThis(node.enclosingFunction/*memberof.doclet.meta.code.val*/);
}
else return ''; // TODO handle global this?
}
}
/**
* 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;
else if (node.parent) {
var parent = node.parent;
if (parent.type === Token.COLON) parent = parent.parent; // go up one more
if (!enclosingFunction) { return ''; } // global
doclet = this.refs['astnode'+enclosingFunction.hashCode()];
memberof.id = 'astnode'+parent.hashCode();
memberof.doclet = this.refs[memberof.id];
if (!memberof.doclet) return ''; // global?
return memberof.doclet.longname||memberof.doclet.name;
}
else {
return ''; // global?
}
}
if ( doclet && doclet.meta.vars && ~doclet.meta.vars.indexOf(basename) ) {
return doclet.longname;
}
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 */
function visitNode(node) {
var e,
commentSrc;
return this.resolveVar(enclosingFunction, basename);
}
// look for stand-alone doc comments
if (node.type === Token.SCRIPT && node.comments) {
// note: ALL comments are seen in this block...
for each(var comment in node.comments.toArray()) {
if (comment.commentType !== Token.CommentType.JSDOC) {
continue;
}
if (commentSrc = ''+comment.toSource()) {
/** @private */
function visitNode(node) {
var e,
commentSrc;
e = {
comment: commentSrc,
lineno: comment.getLineno(),
filename: currentSourceName
};
if ( isValidJsdoc(commentSrc) ) {
currentParser.fire('jsdocCommentFound', e, currentParser);
}
}
// look for stand-alone doc comments
if (node.type === Token.SCRIPT && node.comments) {
// note: ALL comments are seen in this block...
for each(var comment in node.comments.toArray()) {
if (comment.commentType !== Token.CommentType.JSDOC) {
continue;
}
}
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 (commentSrc = ''+comment.toSource()) {
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.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 }
e = {
comment: commentSrc,
lineno: comment.getLineno(),
filename: currentSourceName
};
}
}
return true;
}
/** @private */
function parserFactory() {
var cx = Packages.org.mozilla.javascript.Context.getCurrentContext();
var ce = new Packages.org.mozilla.javascript.CompilerEnvirons();
ce.setRecordingComments(true);
ce.setRecordingLocalJsDocComments(true);
ce.setLanguageVersion(180);
ce.initFromContext(cx);
return new Packages.org.mozilla.javascript.Parser(ce, ce.getErrorReporter());
}
/**
* Attempts to find the name and type of the given node.
* @private
* @memberof module:src/parser.Parser
*/
function aboutNode(node) {
about = {};
if (node.type == Token.FUNCTION) {
about.name = '' + node.name;
about.type = 'function';
about.node = node;
return about;
}
if (node.type == Token.VAR || node.type == Token.LET || node.type == Token.CONST) {
about.name = nodeToString(node.target);
if (node.initializer) { // like var i = 0;
about.node = node.initializer;
about.value = nodeToString(about.node);
about.type = getTypeName(node.initializer);
}
else { // like var i;
about.node = node.target;
about.value = nodeToString(about.node);
about.type = 'undefined';
}
return about;
}
if (node.type === Token.ASSIGN || node.type === Token.COLON) {
about.name = nodeToString(node.left);
if (node.type === Token.COLON) {
// objlit keys with unsafe variable-name characters must be quoted
if (!/^[$_a-z][$_a-z0-9]*$/i.test(about.name) ) {
about.name = '"'+about.name.replace(/"/g, '\\"')+'"';
if ( isValidJsdoc(commentSrc) ) {
currentParser.fire('jsdocCommentFound', e, currentParser);
}
}
about.node = node.right;
about.value = nodeToString(about.node);
about.type = getTypeName(node.right);
return about;
}
}
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);
}
// type 39 (NAME)
var string = nodeToString(node);
if (string) {
about.name = string;
return about;
if (e.doclet) {
currentParser.refs['astnode'+e.code.node.hashCode()] = e.doclet; // allow lookup from value => doclet
}
}
else if (node.type === Token.COLON) { // assignment within an object literal
e = {
id: 'astnode'+node.hashCode(), // the id of the COLON node
comment: String(node.left.jsDoc||'@undocumented'),
lineno: node.getLineno(),
filename: currentSourceName,
astnode: node,
code: aboutNode(node)
};
if ( isValidJsdoc(e.comment) ) {
currentParser.fire('symbolFound', e, currentParser);
}
if (e.doclet) {
currentParser.refs['astnode'+e.code.node.hashCode()] = e.doclet; // allow lookup from value => doclet
}
}
else if (node.type == Token.VAR || node.type == Token.LET || node.type == Token.CONST) {
if (node.variables) {
return true; // we'll get each var separately on future visits
}
if (node.parent.variables.toArray()[0] === node) { // like /** blah */ var a=1, b=2, c=3;
// the first var assignment gets any jsDoc before the whole var series
node.jsDoc = node.parent.jsDoc;
}
e = {
id: 'astnode'+node.hashCode(), // the id of the VARIABLE node
comment: String(node.jsDoc||'@undocumented'),
lineno: node.getLineno(),
filename: currentSourceName,
astnode: node,
code: aboutNode(node)
};
// keep track of vars in a function scope
if (node.enclosingFunction) {
var func = 'astnode'+node.enclosingFunction.hashCode(),
funcDoc = currentParser.refs[func];
if (funcDoc) {
funcDoc.meta.vars = funcDoc.meta.vars || [];
funcDoc.meta.vars.push(e.code.name);
}
}
if ( isValidJsdoc(e.comment) ) {
currentParser.fire('symbolFound', e, currentParser);
}
if (e.doclet) {
currentParser.refs['astnode'+e.code.node.hashCode()] = e.doclet; // allow lookup from value => doclet
}
}
else if (node.type == Token.FUNCTION) {
e = {
id: 'astnode'+node.hashCode(), // the id of the COLON node
comment: String(node.jsDoc||'@undocumented'),
lineno: node.getLineno(),
filename: currentSourceName,
astnode: node,
code: aboutNode(node)
};
e.code.name = String(node.name) || '';
// keep track of vars in a function scope
if (node.enclosingFunction) {
var func = 'astnode'+node.enclosingFunction.hashCode(),
funcDoc = currentParser.refs[func];
if (funcDoc) {
funcDoc.meta.vars = funcDoc.meta.vars || [];
funcDoc.meta.vars.push(e.code.name);
}
}
var basename = e.code.name.replace(/^([$a-z_][$a-z_0-9]*).*?$/i, '$1');
e.code.funcscope = currentParser.resolveVar(node, basename);
if ( isValidJsdoc(e.comment) ) {
currentParser.fire('symbolFound', e, currentParser);
}
if (e.doclet) {
currentParser.refs['astnode'+e.code.node.hashCode()] = e.doclet; // allow lookup from value => doclet
}
else if (!currentParser.refs['astnode'+e.code.node.hashCode()]) { // keep references to undocumented anonymous functions too as they might have scoped vars
currentParser.refs['astnode'+e.code.node.hashCode()] = {
longname: '<anonymous>',
meta: { code: e.code }
};
}
}
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;
}
/** @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('.');
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 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);
else { // like var i;
about.node = node.target;
about.value = nodeToString(about.node);
about.type = 'undefined';
}
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 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, '\\"')+'"';
}
}
return type;
about.node = node.right;
about.value = nodeToString(about.node);
about.type = getTypeName(node.right);
return about;
}
/** @private
@memberof module:src/parser.Parser
*/
function isValidJsdoc(commentSrc) {
return commentSrc.indexOf('/***') !== 0; /*** ignore comments that start with many stars ***/
// type 39 (NAME)
var string = nodeToString(node);
if (string) {
about.name = string;
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.
@ -506,4 +503,4 @@
@param {string} e.comment The text content of the JSDoc 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.
*/
*/

View File

@ -6,64 +6,63 @@
@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 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.
@param {Array.<string>} searchPaths
@param {number} [depth=1]
@fires sourceFileFound
*/
exports.Scanner.prototype.scan = function(searchPaths, depth, includeMatch, excludeMatch) {
var filePaths = [],
that = this;
searchPaths = searchPaths || [];
depth = depth || 1;
searchPaths.forEach(function($) {
if ( fs.stat($).isFile() ) {
filePaths.push($);
}
else {
filePaths = filePaths.concat(fs.ls($, depth));
}
});
var fs = require('fs');
filePaths = filePaths.filter(function($) {
if (includeMatch && !includeMatch.test($)) {
return false
}
if (excludeMatch && excludeMatch.test($)) {
return false
}
return true;
});
/**
@constructor
@mixes module:common.events
*/
exports.Scanner = function() {
}
common.mixin(exports.Scanner.prototype, common.events);
filePaths = filePaths.filter(function($) {
var e = { fileName: $ };
that.fire('sourceFileFound', e);
return !e.defaultPrevented;
});
/**
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;
return filePaths;
}
searchPaths = searchPaths || [];
depth = depth || 1;
searchPaths.forEach(function($) {
if ( fs.stat($).isFile() ) {
filePaths.push($);
}
else {
filePaths = filePaths.concat(fs.ls($, depth));
}
});
filePaths = filePaths.filter(function($) {
if (includeMatch && !includeMatch.test($)) {
return false
}
if (excludeMatch && excludeMatch.test($)) {
return false
}
return true;
});
filePaths = filePaths.filter(function($) {
var e = { fileName: $ };
that.fire('sourceFileFound', e);
return !e.defaultPrevented;
});
return filePaths;
}
})();

View File

@ -11,126 +11,124 @@
@requires jsdoc/tag/validator
@requires jsdoc/tag/type
*/
(function() {
var jsdoc = {
tag: {
dictionary: require('jsdoc/tag/dictionary'),
validator: require('jsdoc/tag/validator'),
type: require('jsdoc/tag/type')
}
};
/**
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 || {};
this.originalTitle = trim(tagTitle);
/** The title part of the tag: @title text */
this.title = jsdoc.tag.dictionary.normalise( this.originalTitle );
/** The text part of the tag: @title text */
this.text = trim(tagBody, tagDef.keepsWhitespace);
if (this.text) {
if (tagDef.onTagText) {
this.text = tagDef.onTagText(this.text);
}
if (tagDef.canHaveType) {
var jsdoc = {
tag: {
dictionary: require('jsdoc/tag/dictionary'),
validator: require('jsdoc/tag/validator'),
type: require('jsdoc/tag/type')
}
};
/**
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 || {};
this.originalTitle = trim(tagTitle);
/** The title part of the tag: @title text */
this.title = jsdoc.tag.dictionary.normalise( this.originalTitle );
/** The text part of the tag: @title text */
this.text = trim(tagBody, tagDef.keepsWhitespace);
if (this.text) {
if (tagDef.onTagText) {
this.text = tagDef.onTagText(this.text);
}
if (tagDef.canHaveType) {
/** The value propertiy represents the result of parsing the tag text. */
this.value = {};
/** The value propertiy represents the result of parsing the tag text. */
this.value = {};
var [
/*Array.<string>*/ typeNames,
/*any*/ remainingText,
/*?boolean*/ optional,
/*?boolean*/ nullable,
/*?boolean*/ variable
] = jsdoc.tag.type.parse(this.text);
if (typeNames.length) {
this.value.type = {
names: typeNames,
optional: optional,
nullable: nullable,
variable: variable
};
}
var [
/*Array.<string>*/ typeNames,
/*any*/ remainingText,
/*?boolean*/ optional,
/*?boolean*/ nullable,
/*?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 {
this.value.description = remainingText;
}
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 {
this.value.description = remainingText;
}
}
else {
this.value = this.text;
}
}
jsdoc.tag.validator.validate(this, meta);
}
}
else {
this.value = this.text;
}
}
jsdoc.tag.validator.validate(this, meta);
}
function trim(text, newlines) {
if (!text) { return ''; }
function trim(text, newlines) {
if (!text) { return ''; }
if (newlines) {
return text.replace(/^[\n\r\f]+|[\n\r\f]+$/g, '');
}
else {
return text.replace(/^\s+|\s+$/g, '');
}
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.
@inner
@method parseParamText
@memberof module:jsdoc/tag
@param {string} tagText
@returns {Array.<string, string, boolean, boolean>} [pname, pdesc, poptional, pdefault].
*/
function parseParamText(tagText) {
var pname, pdesc, poptional, pdefault;
/**
Parse the parameter name and parameter desc from the tag text.
@inner
@method parseParamText
@memberof module:jsdoc/tag
@param {string} tagText
@returns {Array.<string, string, boolean, boolean>} [pname, pdesc, poptional, pdefault].
*/
function parseParamText(tagText) {
var pname, pdesc, poptional, pdefault;
// like: pname, pname pdesc, or name - pdesc
tagText.match(/^(\[[^\]]+\]|\S+)((?:\s*\-\s*|\s+)(\S[\s\S]*))?$/);
// like: pname, pname pdesc, or name - pdesc
tagText.match(/^(\[[^\]]+\]|\S+)((?:\s*\-\s*|\s+)(\S[\s\S]*))?$/);
pname = RegExp.$1;
pdesc = RegExp.$3;
if ( /^\[\s*(.+?)\s*\]$/.test(pname) ) {
pname = RegExp.$1;
pdesc = RegExp.$3;
if ( /^\[\s*(.+?)\s*\]$/.test(pname) ) {
poptional = true;
if ( /^(.+?)\s*=\s*(.+)$/.test(pname) ) {
pname = RegExp.$1;
poptional = true;
if ( /^(.+?)\s*=\s*(.+)$/.test(pname) ) {
pname = RegExp.$1;
pdefault = RegExp.$2;
}
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>
@license Apache License 2.0 - See file 'LICENSE.md' in this project.
*/
(function() {
var _synonyms = {},
_definitions = {},
_namespaces = [];
var _synonyms = {},
_definitions = {},
_namespaces = [];
function _TagDefinition(title, etc) {
etc = etc || {};
function _TagDefinition(title, etc) {
etc = etc || {};
this.title = dictionary.normalise(title);
for (var p in etc) {
if (etc.hasOwnProperty(p)) {
this[p] = etc[p];
}
this.title = dictionary.normalise(title);
for (var p in etc) {
if (etc.hasOwnProperty(p)) {
this[p] = etc[p];
}
}
_TagDefinition.prototype.synonym = function(synonymName) {
_synonyms[synonymName.toLowerCase()] = this.title;
return this; // chainable
}
/** @exports jsdoc/tag/dictionary */
var dictionary = {
/** @function */
defineTag: function(title, opts) {
_definitions[title] = new _TagDefinition(title, opts);
if (opts.isNamespace) {
_namespaces.push(title);
}
return _definitions[title];
},
/** @function */
lookUp: function(title) {
title = dictionary.normalise(title);
if ( _definitions.hasOwnProperty(title) ) {
return _definitions[title];
}
return false;
},
}
_TagDefinition.prototype.synonym = function(synonymName) {
_synonyms[synonymName.toLowerCase()] = this.title;
return this; // chainable
}
/** @exports jsdoc/tag/dictionary */
var dictionary = {
/** @function */
defineTag: function(title, opts) {
_definitions[title] = new _TagDefinition(title, opts);
/** @function */
isNamespace: function(kind) {
return ( ~ _namespaces.indexOf(kind) );
},
/** @function */
normalise: function(title) {
canonicalName = title.toLowerCase();
if ( _synonyms.hasOwnProperty(canonicalName) ) {
return _synonyms[canonicalName];
}
return canonicalName;
if (opts.isNamespace) {
_namespaces.push(title);
}
};
return _definitions[title];
},
/** @function */
lookUp: function(title) {
title = dictionary.normalise(title);
if ( _definitions.hasOwnProperty(title) ) {
return _definitions[title];
}
return false;
},
require('jsdoc/tag/dictionary/definitions').defineTags(dictionary);
/** @function */
isNamespace: function(kind) {
return ( ~ _namespaces.indexOf(kind) );
},
module.exports = dictionary;
})();
/** @function */
normalise: function(title) {
canonicalName = title.toLowerCase();
if ( _synonyms.hasOwnProperty(canonicalName) ) {
return _synonyms[canonicalName];
}
return canonicalName;
}
};
require('jsdoc/tag/dictionary/definitions').defineTags(dictionary);
for (var prop in dictionary) {
if (dictionary.hasOwnProperty(prop)) {
exports[prop] = dictionary[prop];
}
}

File diff suppressed because it is too large Load Diff

View File

@ -5,109 +5,107 @@
@license Apache License 2.0 - See file 'LICENSE.md' in this project.
*/
(function() {
/**
@param {string} tagValue
@returns {Array.<string>}
*/
exports.parse = function(tagValue) {
if (typeof tagValue !== 'string') { tagValue = ''; }
var type = '',
text = '',
count = 0,
optional,
nullable,
variable;
// type expressions start with '{'
if (tagValue[0] === '{') {
count++;
// find matching closer '}'
for (var i = 1, leni = tagValue.length; i < leni; i++) {
if (tagValue[i] === '\\') { i++; continue; } // backslash escapes the next character
if (tagValue[i] === '{') { count++; }
else if (tagValue[i] === '}') { count--; }
if (count === 0) {
type = trim(tagValue.slice(1, i))
.replace(/\\\{/g, '{') // unescape escaped curly braces
.replace(/\\\}/g, '}');
text = trim(tagValue.slice(i+1));
break;
}
/**
@param {string} tagValue
@returns {Array.<string>}
*/
exports.parse = function(tagValue) {
if (typeof tagValue !== 'string') { tagValue = ''; }
var type = '',
text = '',
count = 0,
optional,
nullable,
variable;
// type expressions start with '{'
if (tagValue[0] === '{') {
count++;
// find matching closer '}'
for (var i = 1, leni = tagValue.length; i < leni; i++) {
if (tagValue[i] === '\\') { i++; continue; } // backslash escapes the next character
if (tagValue[i] === '{') { count++; }
else if (tagValue[i] === '}') { count--; }
if (count === 0) {
type = trim(tagValue.slice(1, i))
.replace(/\\\{/g, '{') // unescape escaped curly braces
.replace(/\\\}/g, '}');
text = trim(tagValue.slice(i+1));
break;
}
}
}
if (type === '') { text = tagValue; }
[type, optional] = parseOptional(type);
[type, nullable] = parseNullable(type);
[type, variable] = parseVariable(type);
if (type === '') { text = tagValue; }
type = parseTypes(type); // make it into an array
[type, optional] = parseOptional(type);
[type, nullable] = parseNullable(type);
[type, variable] = parseVariable(type);
return [type, text, optional, nullable, variable];
type = parseTypes(type); // make it into an array
return [type, text, optional, nullable, variable];
}
function parseOptional(type) {
var optional = null;
// {sometype=} means optional
if ( /(.+)=$/.test(type) ) {
type = RegExp.$1;
optional = true;
}
function parseOptional(type) {
var optional = null;
// {sometype=} means optional
if ( /(.+)=$/.test(type) ) {
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;
optional = true;
}
return [type, optional];
types = type.split(/\s*\|\s*/g);
}
else if (type) {
types = [type];
}
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;
}
types = type.split(/\s*\|\s*/g);
}
else if (type) {
types = [type];
}
return types;
}
return types;
}
/** @private */
function trim(text) {
return text.replace(/^\s+|\s+$/g, '');
}
})();
/** @private */
function trim(text) {
return text.replace(/^\s+|\s+$/g, '');
}

View File

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

17
rhino_modules/path.js Normal file
View File

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

View File

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

4
rhino_modules/sys.js Normal file
View File

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

View File

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