mirror of
https://github.com/jsdoc/jsdoc.git
synced 2025-12-08 19:46:11 +00:00
esprima parser (currently disabled)
This commit is contained in:
parent
c058a60ed1
commit
735a9b790c
@ -20,6 +20,7 @@
|
||||
"async": "0.1.22",
|
||||
"catharsis": "0.5.6",
|
||||
"crypto-browserify": "git+https://github.com/dominictarr/crypto-browserify.git#95c5d505",
|
||||
"esprima": "1.0.4",
|
||||
"js2xmlparser": "0.1.0",
|
||||
"jshint": "0.9.1",
|
||||
"markdown": "git+https://github.com/jsdoc3/markdown-js.git",
|
||||
|
||||
6
jsdoc.js
6
jsdoc.js
@ -88,10 +88,8 @@ require('lib/jsdoc/util/global').app = {
|
||||
jsdoc: {
|
||||
scanner: new (require('jsdoc/src/scanner').Scanner)(),
|
||||
// TODO: allow the config file to specify the parser module
|
||||
parser: (function() {
|
||||
var modulePath = require('jsdoc/util/runtime').getModulePath('jsdoc/src/parser');
|
||||
return new ( require(modulePath) ).Parser();
|
||||
})(),
|
||||
//parser: require('jsdoc/src/parser').createParser('esprima'),
|
||||
parser: require('jsdoc/src/parser').createParser('rhino'),
|
||||
name: require('jsdoc/name')
|
||||
}
|
||||
};
|
||||
|
||||
@ -12,7 +12,7 @@ var _ = require('underscore');
|
||||
var jsdoc = {
|
||||
name: require('jsdoc/name'),
|
||||
src: {
|
||||
Syntax: require('jsdoc/src/syntax').Syntax,
|
||||
Syntax: require('jsdoc/src/syntax').Syntax
|
||||
},
|
||||
tag: {
|
||||
Tag: require('jsdoc/tag').Tag,
|
||||
|
||||
@ -7,9 +7,8 @@
|
||||
var error = require('jsdoc/util/error');
|
||||
var path = require('jsdoc/path');
|
||||
|
||||
exports.installPlugins = function(plugins, p) {
|
||||
exports.installPlugins = function(plugins, parser) {
|
||||
var dictionary = require('jsdoc/tag/dictionary');
|
||||
var parser = p;
|
||||
|
||||
var eventName;
|
||||
var plugin;
|
||||
@ -17,34 +16,41 @@ exports.installPlugins = function(plugins, p) {
|
||||
|
||||
for (var i = 0, l = plugins.length; i < l; i++) {
|
||||
pluginPath = path.getResourcePath(path.dirname(plugins[i]), path.basename(plugins[i]));
|
||||
|
||||
if (!pluginPath) {
|
||||
error.handle(new Error('Unable to find the plugin "' + plugins[i] + '"'));
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
plugin = require(pluginPath);
|
||||
|
||||
// allow user-defined plugins to...
|
||||
//...register event handlers
|
||||
if (plugin.handlers) {
|
||||
Object.keys(plugin.handlers).forEach(function(eventName) {
|
||||
parser.on(eventName, plugin.handlers[eventName]);
|
||||
});
|
||||
plugin = require(pluginPath);
|
||||
|
||||
// allow user-defined plugins to...
|
||||
//...register event handlers
|
||||
if (plugin.handlers) {
|
||||
Object.keys(plugin.handlers).forEach(function(eventName) {
|
||||
parser.on(eventName, plugin.handlers[eventName]);
|
||||
});
|
||||
}
|
||||
|
||||
//...define tags
|
||||
if (plugin.defineTags) {
|
||||
plugin.defineTags(dictionary);
|
||||
}
|
||||
|
||||
//...add a Rhino node visitor (deprecated in JSDoc 3.3)
|
||||
if (plugin.nodeVisitor) {
|
||||
if ( !parser.addNodeVisitor ) {
|
||||
error.handle( new Error('Unable to add the Rhino node visitor from ' + plugins[i] +
|
||||
', because JSDoc is not using the Rhino JavaScript parser.') );
|
||||
}
|
||||
|
||||
//...define tags
|
||||
if (plugin.defineTags) {
|
||||
plugin.defineTags(dictionary);
|
||||
}
|
||||
|
||||
//...add a Rhino node visitor (deprecated in JSDoc 3.3)
|
||||
if (plugin.nodeVisitor) {
|
||||
else {
|
||||
parser.addNodeVisitor(plugin.nodeVisitor);
|
||||
}
|
||||
}
|
||||
|
||||
//...add a Mozilla Parser API node visitor
|
||||
if (plugin.astNodeVisitor) {
|
||||
parser.addAstNodeVisitor(plugin.astNodeVisitor);
|
||||
}
|
||||
//...add a Mozilla Parser API node visitor
|
||||
if (plugin.astNodeVisitor) {
|
||||
parser.addAstNodeVisitor(plugin.astNodeVisitor);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
379
lib/jsdoc/src/astbuilder.js
Normal file
379
lib/jsdoc/src/astbuilder.js
Normal file
@ -0,0 +1,379 @@
|
||||
var Syntax = require('jsdoc/src/syntax').Syntax;
|
||||
|
||||
// TODO: should set e.stopPropagation == true for consistency with Rhino, right?
|
||||
var VISITOR_CONTINUE = true;
|
||||
var VISITOR_STOP = false;
|
||||
|
||||
// Counter for generating unique node IDs.
|
||||
var uid = 100000000;
|
||||
|
||||
// TODO: docs
|
||||
var acceptsLeadingComments = exports.acceptsLeadingComments = (function() {
|
||||
var accepts = {};
|
||||
var commentable = [
|
||||
Syntax.AssignmentExpression,
|
||||
Syntax.CallExpression,
|
||||
Syntax.FunctionDeclaration,
|
||||
Syntax.FunctionExpression,
|
||||
Syntax.MemberExpression,
|
||||
Syntax.Property,
|
||||
Syntax.TryStatement,
|
||||
Syntax.VariableDeclaration,
|
||||
Syntax.VariableDeclarator,
|
||||
Syntax.WithStatement
|
||||
];
|
||||
|
||||
for (var i = 0, l = commentable.length; i < l; i++) {
|
||||
accepts[commentable[i]] = true;
|
||||
}
|
||||
|
||||
return accepts;
|
||||
})();
|
||||
|
||||
// TODO: docs
|
||||
var searchChildNodes = (function() {
|
||||
var search = {};
|
||||
var shouldSearch = [
|
||||
// TODO: add to this as needed
|
||||
];
|
||||
|
||||
for (var i = 0, l = shouldSearch.length; i < l; i++) {
|
||||
search[shouldSearch[i]] = true;
|
||||
}
|
||||
|
||||
return search;
|
||||
})();
|
||||
|
||||
// TODO: docs
|
||||
// check whether node1 is before node2
|
||||
function isBefore(beforeRange, afterRange) {
|
||||
return beforeRange[1] <= afterRange[0];
|
||||
}
|
||||
|
||||
// TODO: docs
|
||||
function isBetween(middleRange, beforeRange, afterRange) {
|
||||
return isBefore(middleRange, afterRange) && (beforeRange ?
|
||||
isBefore(beforeRange, middleRange) : true);
|
||||
}
|
||||
|
||||
// TODO: docs
|
||||
function isWithin(innerRange, outerRange) {
|
||||
return innerRange[0] >= outerRange[0] && innerRange[1] <= outerRange[1];
|
||||
}
|
||||
|
||||
// TODO: docs
|
||||
function isLeadingComment(comment, before, after) {
|
||||
return !!before && !!after && !!acceptsLeadingComments[after.type] &&
|
||||
isBefore(before.range, after.range) && isBetween(comment.range, before.range, after.range);
|
||||
}
|
||||
|
||||
// TODO: docs
|
||||
function isJsdocComment(comment) {
|
||||
return comment && (comment.type === 'Block') && (comment.value[0] === '*');
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the raw comment string to a block comment node.
|
||||
*
|
||||
* @private
|
||||
* @param {!Object} comment - A comment node with `type` and `value` properties.
|
||||
*/
|
||||
function addRawComment(comment) {
|
||||
comment.raw = comment.raw || ('/*' + comment.value + '*/');
|
||||
return comment;
|
||||
}
|
||||
|
||||
// TODO: docs
|
||||
function scrubComments(comments) {
|
||||
var comment;
|
||||
|
||||
var scrubbed = [];
|
||||
|
||||
for (var i = 0, l = comments.length; i < l; i++) {
|
||||
comment = comments[i];
|
||||
if ( isJsdocComment(comment) ) {
|
||||
scrubbed.push( addRawComment(comment) );
|
||||
}
|
||||
}
|
||||
|
||||
return scrubbed;
|
||||
}
|
||||
|
||||
// TODO: docs
|
||||
var AstBuilder = exports.AstBuilder = function() {};
|
||||
|
||||
// TODO: docs
|
||||
AstBuilder.prototype.build = function(source, filename) {
|
||||
var ast;
|
||||
|
||||
var esprimaOpts = {
|
||||
comment: true,
|
||||
loc: true,
|
||||
range: true,
|
||||
// can probably remove when 1.1.0 is released
|
||||
raw: true,
|
||||
tokens: true
|
||||
};
|
||||
|
||||
ast = require('esprima').parse(source, esprimaOpts);
|
||||
this._postProcess(filename, ast);
|
||||
|
||||
return ast;
|
||||
};
|
||||
|
||||
// TODO: docs
|
||||
function atomSorter(a, b) {
|
||||
var aRange = a.range;
|
||||
var bRange = b.range;
|
||||
var result = 0;
|
||||
|
||||
// does a end before b starts?
|
||||
if ( isBefore(aRange, bRange) ) {
|
||||
result = -1;
|
||||
}
|
||||
// does a enclose b?
|
||||
else if ( isWithin(bRange, aRange) ) {
|
||||
result = -1;
|
||||
}
|
||||
// does a start before b?
|
||||
else if (aRange[0] < bRange[0]) {
|
||||
result = -1;
|
||||
}
|
||||
// are the ranges non-identical? if so, b must be first
|
||||
else if ( aRange[0] !== bRange[0] || aRange[1] !== bRange[1] ) {
|
||||
result = 1;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// TODO: docs
|
||||
function CommentAttacher(comments, tokens) {
|
||||
this._comments = comments || [];
|
||||
this._tokens = tokens || [];
|
||||
|
||||
this._tokenIndex = 0;
|
||||
this._previousNodeEnd = 0;
|
||||
this._astRoot = null;
|
||||
this._strayComments = [];
|
||||
|
||||
this._resetPendingComment()
|
||||
._resetCandidates();
|
||||
}
|
||||
|
||||
// TODO: docs
|
||||
CommentAttacher.prototype._resetPendingComment = function() {
|
||||
this._pendingComment = null;
|
||||
this._pendingCommentRange = null;
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
// TODO: docs
|
||||
CommentAttacher.prototype._resetCandidates = function() {
|
||||
this._candidates = [];
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
// TODO: docs
|
||||
CommentAttacher.prototype._nextComment = function() {
|
||||
return this._comments[0] || null;
|
||||
};
|
||||
|
||||
// TODO: docs
|
||||
CommentAttacher.prototype._nextToken = function() {
|
||||
return this._tokens[this._tokenIndex] || null;
|
||||
};
|
||||
|
||||
// TODO: docs
|
||||
CommentAttacher.prototype._extractComments = function(index, count) {
|
||||
return this._comments.splice(index, count);
|
||||
};
|
||||
|
||||
// TODO: docs
|
||||
// find the index of the atom whose end position is closest to (but not after) the specified
|
||||
// position
|
||||
CommentAttacher.prototype._nextIndexBefore = function(startIndex, atoms, position) {
|
||||
var atom;
|
||||
|
||||
var newIndex = startIndex;
|
||||
|
||||
for (var i = newIndex, l = atoms.length; i < l; i++) {
|
||||
atom = atoms[i];
|
||||
|
||||
if (atom.range[1] > position) {
|
||||
break;
|
||||
}
|
||||
else {
|
||||
newIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
return newIndex;
|
||||
};
|
||||
|
||||
// TODO: docs
|
||||
CommentAttacher.prototype._advanceTokenIndex = function(node) {
|
||||
var position = node.range[0];
|
||||
|
||||
this._tokenIndex = this._nextIndexBefore(this._tokenIndex, this._tokens, position);
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
// TODO: docs
|
||||
CommentAttacher.prototype._fastForwardComments = function(node) {
|
||||
var position = node.range[0];
|
||||
var commentIndex = this._nextIndexBefore(0, this._comments, position);
|
||||
|
||||
// all comments before the node (except the last one) are considered stray comments
|
||||
if (commentIndex > 0) {
|
||||
this._strayComments = this._strayComments.concat( this._comments.splice(0,
|
||||
commentIndex) );
|
||||
}
|
||||
};
|
||||
|
||||
// TODO: docs
|
||||
CommentAttacher.prototype._attachPendingComment = function() {
|
||||
var target;
|
||||
|
||||
if (this._pendingComment) {
|
||||
if (this._candidates.length > 0) {
|
||||
target = this._candidates[this._candidates.length - 1];
|
||||
target.leadingComments = target.leadingComments || [];
|
||||
target.leadingComments.push(this._pendingComment);
|
||||
}
|
||||
else {
|
||||
this._strayComments.push(this._pendingComment);
|
||||
}
|
||||
}
|
||||
|
||||
this._resetPendingComment()
|
||||
._resetCandidates();
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
// TODO: docs
|
||||
CommentAttacher.prototype._isEligible = function(node) {
|
||||
var atoms;
|
||||
var token;
|
||||
|
||||
var isEligible = false;
|
||||
|
||||
var comment = this._nextComment();
|
||||
if (comment) {
|
||||
atoms = [node, comment];
|
||||
token = this._nextToken();
|
||||
if (token) {
|
||||
atoms.push(token);
|
||||
}
|
||||
|
||||
atoms.sort(atomSorter);
|
||||
|
||||
// a candidate node must immediately follow the comment
|
||||
if (atoms.indexOf(node) === atoms.indexOf(comment) + 1) {
|
||||
isEligible = true;
|
||||
}
|
||||
}
|
||||
|
||||
return isEligible;
|
||||
};
|
||||
|
||||
// TODO: docs
|
||||
CommentAttacher.prototype._shouldSkipNode = function(node) {
|
||||
return !isWithin(node.range, this._pendingCommentRange) && !searchChildNodes[node.type];
|
||||
};
|
||||
|
||||
// TODO: docs
|
||||
// TODO: this is overdesigned. should be able to ditch searchChildNodes. how often do we get
|
||||
// multiple candidate nodes?
|
||||
CommentAttacher.prototype.visit = function(node) {
|
||||
var isEligible;
|
||||
|
||||
// bail if we're out of comments
|
||||
if ( !this._nextComment() ) {
|
||||
return VISITOR_STOP;
|
||||
}
|
||||
|
||||
// set the AST root if necessary
|
||||
this._astRoot = this._astRoot || node;
|
||||
|
||||
// move to the next token, and fast-forward past comments that can no longer be attached
|
||||
this._advanceTokenIndex(node);
|
||||
this._fastForwardComments(node);
|
||||
// now we can check whether the current node is in the right position to accept the next comment
|
||||
isEligible = this._isEligible(node);
|
||||
|
||||
// if we already had a pending comment, and the current node is in the wrong place to be a
|
||||
// comment target, try attaching the comment
|
||||
if ( (this._pendingComment && !isWithin(node.range, this._pendingCommentRange)) ||
|
||||
!searchChildNodes[node.type] ) {
|
||||
this._attachPendingComment();
|
||||
}
|
||||
|
||||
// okay, now that we've done all that bookkeeping, we can check whether the current node accepts
|
||||
// leading comments and add it to the candidate list if needed
|
||||
if (isEligible && acceptsLeadingComments[node.type]) {
|
||||
// make sure we don't go past the end of the outermost target node
|
||||
if (!this._pendingCommentRange) {
|
||||
this._pendingCommentRange = node.range.slice(0);
|
||||
}
|
||||
this._candidates.push(node);
|
||||
|
||||
// we have a candidate node, so pend the current comment if necessary
|
||||
this._pendingComment = this._pendingComment || this._comments.splice(0, 1)[0];
|
||||
}
|
||||
|
||||
return VISITOR_CONTINUE;
|
||||
};
|
||||
|
||||
// TODO: docs
|
||||
CommentAttacher.prototype.finish = function() {
|
||||
// any remaining comments are stray comments
|
||||
this._strayComments = this._strayComments.concat(this._comments);
|
||||
|
||||
// deal with the pending comment, if there is one
|
||||
this._attachPendingComment();
|
||||
|
||||
// attach stray comments to the AST root
|
||||
if (this._strayComments.length) {
|
||||
this._astRoot.trailingComments = this._strayComments.slice(0);
|
||||
}
|
||||
};
|
||||
|
||||
// TODO: docs
|
||||
// TODO: refactor to make this extensible
|
||||
/**
|
||||
* @param {string} filename - The full path to the source file.
|
||||
* @param {Object} ast - An abstract syntax tree that conforms to the Mozilla Parser API.
|
||||
*/
|
||||
AstBuilder.prototype._postProcess = function(filename, ast) {
|
||||
var Walker = require('jsdoc/src/walker').Walker;
|
||||
|
||||
var attachComments = !!ast.comments && !!ast.comments.length;
|
||||
var commentAttacher = new CommentAttacher( scrubComments(ast.comments.slice(0)), ast.tokens );
|
||||
var visitor = {
|
||||
visit: function(node) {
|
||||
if (attachComments) {
|
||||
attachComments = commentAttacher.visit(node);
|
||||
}
|
||||
|
||||
if (!node.nodeId) {
|
||||
Object.defineProperty(node, 'nodeId', {
|
||||
value: 'astnode' + uid++
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var walker = new Walker();
|
||||
walker.recurse(filename, ast, visitor);
|
||||
|
||||
commentAttacher.finish();
|
||||
|
||||
// remove the comment/token arrays; we no longer need then
|
||||
ast.comments = [];
|
||||
ast.tokens = [];
|
||||
};
|
||||
@ -135,7 +135,8 @@ exports.attachTo = function(parser) {
|
||||
if (memberofName) {
|
||||
newDoclet.addTag('memberof', memberofName);
|
||||
if (basename) {
|
||||
newDoclet.name = newDoclet.name.replace(new RegExp('^' + RegExp.escape(basename) + '.'), '');
|
||||
newDoclet.name = (newDoclet.name || '')
|
||||
.replace(new RegExp('^' + RegExp.escape(basename) + '.'), '');
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
@ -10,10 +10,39 @@ var util = require('util');
|
||||
|
||||
var hasOwnProp = Object.prototype.hasOwnProperty;
|
||||
|
||||
// Counter for generating unique node IDs.
|
||||
var uid = 100000000;
|
||||
// Prefix for JavaScript strings that were provided in lieu of a filename.
|
||||
var SCHEMA = 'javascript:';
|
||||
// TODO: docs
|
||||
var PARSERS = exports.PARSERS = {
|
||||
esprima: 'jsdoc/src/parser',
|
||||
rhino: 'rhino/jsdoc/src/parser'
|
||||
};
|
||||
|
||||
// TODO: docs
|
||||
exports.createParser = function(type) {
|
||||
var path = require('jsdoc/path');
|
||||
var runtime = require('jsdoc/util/runtime');
|
||||
|
||||
var modulePath;
|
||||
|
||||
if (!type) {
|
||||
type = runtime.isRhino() ? 'rhino' : 'esprima';
|
||||
}
|
||||
|
||||
if (PARSERS[type]) {
|
||||
modulePath = PARSERS[type];
|
||||
}
|
||||
else {
|
||||
modulePath = path.join( path.getResourcePath(path.dirname(type)), path.basename(type) );
|
||||
}
|
||||
|
||||
try {
|
||||
return new ( require(modulePath) ).Parser();
|
||||
}
|
||||
catch (e) {
|
||||
throw new Error('Unable to create the parser type "' + type + '": ' + e);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// TODO: docs
|
||||
@ -27,10 +56,7 @@ var SCHEMA = 'javascript:';
|
||||
var Parser = exports.Parser = function(builderInstance, visitorInstance, walkerInstance) {
|
||||
this.clear();
|
||||
|
||||
// TODO: replace with a runtime-agnostic default builder
|
||||
var runtime = require('jsdoc/util/runtime');
|
||||
this._astBuilder = builderInstance ||
|
||||
new ( require(runtime.getModulePath('jsdoc/src/astbuilder')) )();
|
||||
this._astBuilder = builderInstance || new (require('jsdoc/src/astbuilder')).AstBuilder();
|
||||
this._visitor = visitorInstance || new (require('jsdoc/src/visitor')).Visitor(this);
|
||||
this._walker = walkerInstance || new (require('jsdoc/src/walker')).Walker();
|
||||
|
||||
@ -64,17 +90,6 @@ Parser.prototype.clear = function() {
|
||||
};
|
||||
};
|
||||
|
||||
// TODO: docs
|
||||
/**
|
||||
* Create a unique identifier for a node.
|
||||
*
|
||||
* @private
|
||||
* @return {string} The unique node ID.
|
||||
*/
|
||||
Parser.prototype.getUniqueId = function() {
|
||||
return 'astnode' + uid++;
|
||||
};
|
||||
|
||||
// TODO: update docs
|
||||
/**
|
||||
* Parse the given source files for JSDoc comments.
|
||||
@ -433,7 +448,7 @@ Parser.prototype.getBasename = function(name) {
|
||||
|
||||
// TODO: docs
|
||||
function definedInScope(doclet, basename) {
|
||||
return doclet && doclet.meta && doclet.meta.vars &&
|
||||
return !!doclet && !!doclet.meta && !!doclet.meta.vars &&
|
||||
hasOwnProp.call(doclet.meta.vars, basename);
|
||||
}
|
||||
|
||||
@ -470,7 +485,7 @@ Parser.prototype.astnodeToMemberof = function(node) {
|
||||
// walk up the scope chain until we find the scope in which the node is defined
|
||||
while (scope.enclosingScope) {
|
||||
doclet = this._getDoclet(scope.enclosingScope.nodeId);
|
||||
if ( definedInScope(doclet, basename) ) {
|
||||
if ( doclet && definedInScope(doclet, basename) ) {
|
||||
result = [doclet.meta.vars[basename], basename];
|
||||
break;
|
||||
}
|
||||
@ -482,12 +497,11 @@ Parser.prototype.astnodeToMemberof = function(node) {
|
||||
|
||||
// do we know that it's a global?
|
||||
doclet = this.refs.__global__;
|
||||
if ( definedInScope(doclet, basename) ) {
|
||||
if ( doclet && definedInScope(doclet, basename) ) {
|
||||
result = [doclet.meta.vars[basename], basename];
|
||||
}
|
||||
|
||||
// have we seen the node's parent? if so, use that
|
||||
// TODO: is this behavior correct? when do we get here?
|
||||
else if (node.parent) {
|
||||
doclet = this._getDoclet(node.parent.nodeId);
|
||||
|
||||
|
||||
@ -131,10 +131,8 @@ function isValidJsdoc(commentSrc) {
|
||||
|
||||
// TODO: docs
|
||||
function hasJsdocComments(node) {
|
||||
return (
|
||||
(node && node.leadingComments && node.leadingComments.length > 0) ||
|
||||
(node && node.type === Syntax.Program && node.trailingComments &&
|
||||
node.trailingComments.length > 0) );
|
||||
return (node && node.leadingComments && node.leadingComments.length > 0) ||
|
||||
(node && node.trailingComments && node.trailingComments.length > 0);
|
||||
}
|
||||
|
||||
// TODO: docs
|
||||
@ -169,7 +167,7 @@ Visitor.prototype.visitNodeComments = function(node, parser, filename) {
|
||||
comments = node.leadingComments.slice(0);
|
||||
}
|
||||
|
||||
if (node.type === Syntax.Program && node.trailingComments && node.trailingComments.length) {
|
||||
if (node.trailingComments && node.trailingComments.length) {
|
||||
comments = comments.concat( node.trailingComments.slice(0) );
|
||||
}
|
||||
|
||||
|
||||
@ -29,7 +29,7 @@ function moveComments(source, target) {
|
||||
}
|
||||
}
|
||||
|
||||
function ignore(node, parent, state, cb) {}
|
||||
function leafNode(node, parent, state, cb) {}
|
||||
|
||||
|
||||
// TODO: docs
|
||||
@ -68,7 +68,7 @@ walkers[Syntax.BlockStatement] = function(node, parent, state, cb) {
|
||||
}
|
||||
};
|
||||
|
||||
walkers[Syntax.BreakStatement] = ignore;
|
||||
walkers[Syntax.BreakStatement] = leafNode;
|
||||
|
||||
walkers[Syntax.CallExpression] = function(node, parent, state, cb) {
|
||||
var i;
|
||||
@ -82,7 +82,7 @@ walkers[Syntax.CallExpression] = function(node, parent, state, cb) {
|
||||
}
|
||||
};
|
||||
|
||||
walkers[Syntax.CatchClause] = ignore;
|
||||
walkers[Syntax.CatchClause] = leafNode;
|
||||
|
||||
// TODO: verify correctness
|
||||
walkers[Syntax.ComprehensionBlock] = walkers[Syntax.AssignmentExpression];
|
||||
@ -106,16 +106,16 @@ walkers[Syntax.ConditionalExpression] = function(node, parent, state, cb) {
|
||||
cb(node.alternate, node, state);
|
||||
};
|
||||
|
||||
walkers[Syntax.ContinueStatement] = ignore;
|
||||
walkers[Syntax.ContinueStatement] = leafNode;
|
||||
|
||||
walkers[Syntax.DebuggerStatement] = ignore;
|
||||
walkers[Syntax.DebuggerStatement] = leafNode;
|
||||
|
||||
walkers[Syntax.DoWhileStatement] = function(node, parent, state, cb) {
|
||||
cb(node.test, node, state);
|
||||
cb(node.body, node, state);
|
||||
};
|
||||
|
||||
walkers[Syntax.EmptyStatement] = ignore;
|
||||
walkers[Syntax.EmptyStatement] = leafNode;
|
||||
|
||||
walkers[Syntax.ExpressionStatement] = function(node, parent, state, cb) {
|
||||
cb(node.expression, node, state);
|
||||
@ -167,7 +167,7 @@ walkers[Syntax.FunctionDeclaration] = function(node, parent, state, cb) {
|
||||
|
||||
walkers[Syntax.FunctionExpression] = walkers[Syntax.FunctionDeclaration];
|
||||
|
||||
walkers[Syntax.Identifier] = ignore;
|
||||
walkers[Syntax.Identifier] = leafNode;
|
||||
|
||||
walkers[Syntax.IfStatement] = function(node, parent, state, cb) {
|
||||
cb(node.test, node, state);
|
||||
@ -194,7 +194,7 @@ walkers[Syntax.LetStatement] = function(node, parent, state, cb) {
|
||||
cb(node.body, node, state);
|
||||
};
|
||||
|
||||
walkers[Syntax.Literal] = ignore;
|
||||
walkers[Syntax.Literal] = leafNode;
|
||||
|
||||
walkers[Syntax.LogicalExpression] = walkers[Syntax.AssignmentExpression];
|
||||
|
||||
@ -254,7 +254,7 @@ walkers[Syntax.SwitchStatement] = function(node, parent, state, cb) {
|
||||
}
|
||||
};
|
||||
|
||||
walkers[Syntax.ThisExpression] = ignore;
|
||||
walkers[Syntax.ThisExpression] = leafNode;
|
||||
|
||||
walkers[Syntax.ThrowStatement] = function(node, parent, state, cb) {
|
||||
cb(node.argument, node, state);
|
||||
@ -328,22 +328,35 @@ walkers[Syntax.YieldExpression] = function(node, parent, state, cb) {
|
||||
* @todo docs
|
||||
* @memberof module:jsdoc/src/walker
|
||||
*/
|
||||
var Walker = exports.Walker = function() {};
|
||||
var Walker = exports.Walker = function(walkerFuncs) {
|
||||
// TODO: finish or remove
|
||||
/*
|
||||
walkerFuncs = walkerFuncs || walkers;
|
||||
|
||||
var emitter = this._emitter = new ( require('events') ).EventEmitter();
|
||||
|
||||
Object.keys(walkerFuncs).forEach(function(nodeType) {
|
||||
emitter.on(nodeType, function(e) {
|
||||
walkerFuncs[nodeType](e.node, e.parent, e.state, e.cb);
|
||||
});
|
||||
});
|
||||
*/
|
||||
};
|
||||
|
||||
function getCurrentScope(scopes) {
|
||||
return scopes[scopes.length - 1] || null;
|
||||
}
|
||||
|
||||
// TODO: docs
|
||||
Walker.prototype.recurse = function(filename, ast, visitor) {
|
||||
Walker.prototype._recurse = function(ast) {
|
||||
// TODO: look for other state that we can track/attach during the walk
|
||||
var state = {
|
||||
//emitter: this._emitter,
|
||||
nodes: [],
|
||||
scopes: []
|
||||
};
|
||||
|
||||
function cb(node, parent, state, override) {
|
||||
var type = override || node.type;
|
||||
function cb(node, parent, state) {
|
||||
var isScope = isScopeNode(node);
|
||||
|
||||
if (node.parent === undefined) {
|
||||
@ -363,7 +376,7 @@ Walker.prototype.recurse = function(filename, ast, visitor) {
|
||||
}
|
||||
state.nodes.push(node);
|
||||
|
||||
walkers[type](node, parent, state, cb);
|
||||
walkers[node.type](node, parent, state, cb);
|
||||
|
||||
if (isScope) {
|
||||
state.scopes.pop();
|
||||
@ -372,8 +385,20 @@ Walker.prototype.recurse = function(filename, ast, visitor) {
|
||||
|
||||
cb(ast, null, state);
|
||||
|
||||
for (var i = 0, l = state.nodes.length; i < l; i++) {
|
||||
visitor.visit.call(visitor, state.nodes[i], filename);
|
||||
return state;
|
||||
};
|
||||
|
||||
// TODO: docs
|
||||
// TODO: if visitor.visit returns false, we shouldn't visit child nodes!
|
||||
// TODO: skip the AST root node to be consistent with Rhino
|
||||
// TODO: call the visitor when we encounter each node, rather than batching them up at the end
|
||||
Walker.prototype.recurse = function(filename, ast, visitor) {
|
||||
var state = this._recurse(ast);
|
||||
|
||||
if (visitor) {
|
||||
for (var i = 0, l = state.nodes.length; i < l; i++) {
|
||||
visitor.visit.call(visitor, state.nodes[i], filename);
|
||||
}
|
||||
}
|
||||
|
||||
return ast;
|
||||
|
||||
3908
node_modules/esprima/esprima.js
generated
vendored
Normal file
3908
node_modules/esprima/esprima.js
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
59
node_modules/esprima/package.json
generated
vendored
Normal file
59
node_modules/esprima/package.json
generated
vendored
Normal file
@ -0,0 +1,59 @@
|
||||
{
|
||||
"name": "esprima",
|
||||
"description": "ECMAScript parsing infrastructure for multipurpose analysis",
|
||||
"homepage": "http://esprima.org",
|
||||
"main": "esprima.js",
|
||||
"bin": {
|
||||
"esparse": "./bin/esparse.js",
|
||||
"esvalidate": "./bin/esvalidate.js"
|
||||
},
|
||||
"files": [
|
||||
"bin",
|
||||
"test/run.js",
|
||||
"test/runner.js",
|
||||
"test/test.js",
|
||||
"test/compat.js",
|
||||
"test/reflect.js",
|
||||
"esprima.js"
|
||||
],
|
||||
"version": "1.0.4",
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
},
|
||||
"maintainers": [
|
||||
{
|
||||
"name": "Ariya Hidayat",
|
||||
"email": "ariya.hidayat@gmail.com",
|
||||
"url": "http://ariya.ofilabs.com"
|
||||
}
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "http://github.com/ariya/esprima.git"
|
||||
},
|
||||
"licenses": [
|
||||
{
|
||||
"type": "BSD",
|
||||
"url": "http://github.com/ariya/esprima/raw/master/LICENSE.BSD"
|
||||
}
|
||||
],
|
||||
"keywords": [
|
||||
"ast",
|
||||
"ecmascript",
|
||||
"javascript",
|
||||
"parser",
|
||||
"syntax"
|
||||
],
|
||||
"scripts": {
|
||||
"test": "node test/run.js",
|
||||
"benchmark": "node test/benchmarks.js",
|
||||
"benchmark-quick": "node test/benchmarks.js quick"
|
||||
},
|
||||
"readme": "**Esprima** ([esprima.org](http://esprima.org)) is a high performance,\nstandard-compliant [ECMAScript](http://www.ecma-international.org/publications/standards/Ecma-262.htm)\nparser written in ECMAScript (also popularly known as\n[JavaScript](http://en.wikipedia.org/wiki/JavaScript>JavaScript)).\nEsprima is created and maintained by [Ariya Hidayat](http://twitter.com/ariyahidayat),\nwith the help of [many contributors](https://github.com/ariya/esprima/contributors).\n\nEsprima runs on web browsers (IE 6+, Firefox 1+, Safari 3+, Chrome 1+, Konqueror 4.6+, Opera 8+) as well as\n[Node.js](http://nodejs.org).\n\n### Features\n\n- Full support for [ECMAScript 5.1](http://www.ecma-international.org/publications/standards/Ecma-262.htm)(ECMA-262)\n- Sensible [syntax tree format](http://esprima.org/doc/index.html#ast) compatible with Mozilla\n[Parser AST](https://developer.mozilla.org/en/SpiderMonkey/Parser_API)\n- Heavily tested (> 550 [unit tests](http://esprima.org/test/) with solid 100% statement coverage)\n- Optional tracking of syntax node location (index-based and line-column)\n- Experimental support for ES6/Harmony (module, class, destructuring, ...)\n\nEsprima is blazing fast (see the [benchmark suite](http://esprima.org/test/benchmarks.html)).\nIt is up to 3x faster than UglifyJS v1 and it is still [competitive](http://esprima.org/test/compare.html)\nwith the new generation of fast parsers.\n\n### Applications\n\nEsprima serves as the basis for many popular JavaScript development tools:\n\n- Code coverage analysis: [node-cover](https://github.com/itay/node-cover), [Istanbul](https://github.com/yahoo/Istanbul)\n- Documentation tool: [JFDoc](https://github.com/thejohnfreeman/jfdoc), [JSDuck](https://github.com/senchalabs/jsduck)\n- Language extension: [LLJS](http://mbebenita.github.com/LLJS/) (low-level JS),\n[Sweet.js](http://sweetjs.org/) (macro)\n- ES6/Harmony transpiler: [Six](https://github.com/matthewrobb/six), [Harmonizr](https://github.com/jdiamond/harmonizr)\n- Eclipse Orion smart editing ([outline view](https://github.com/aclement/esprima-outline), [content assist](http://contraptionsforprogramming.blogspot.com/2012/02/better-javascript-content-assist-in.html))\n- Source code modification: [Esmorph](https://github.com/ariya/esmorph), [Code Painter](https://github.com/fawek/codepainter),\n- Source transformation: [node-falafel](https://github.com/substack/node-falafel), [Esmangle](https://github.com/Constellation/esmangle), [escodegen](https://github.com/Constellation/escodegen)\n\n### Questions?\n- [Documentation](http://esprima.org/doc)\n- [Issue tracker](http://issues.esprima.org): [known problems](http://code.google.com/p/esprima/issues/list?q=Defect)\nand [future plans](http://code.google.com/p/esprima/issues/list?q=Enhancement)\n- [Mailing list](http://groups.google.com/group/esprima)\n- [Contribution guide](http://esprima.org/doc/index.html#contribution)\n\nFollow [@Esprima](http://twitter.com/Esprima) on Twitter to get the\ndevelopment updates.\nFeedback and contribution are welcomed!\n\n### License\n\nCopyright (C) 2012, 2011 [Ariya Hidayat](http://ariya.ofilabs.com/about)\n and other contributors.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n * Redistributions of source code must retain the above copyright\n notice, this list of conditions and the following disclaimer.\n\n * Redistributions in binary form must reproduce the above copyright\n notice, this list of conditions and the following disclaimer in the\n documentation and/or other materials provided with the distribution.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\nARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY\nDIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\nLOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\nON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF\nTHIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n",
|
||||
"readmeFilename": "README.md",
|
||||
"bugs": {
|
||||
"url": "https://github.com/ariya/esprima/issues"
|
||||
},
|
||||
"_id": "esprima@1.0.4",
|
||||
"_from": "esprima@1.0.4"
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "jsdoc",
|
||||
"version": "3.2.0-dev",
|
||||
"revision": "1373039791834",
|
||||
"revision": "1382363021327",
|
||||
"description": "An API documentation generator for JavaScript.",
|
||||
"keywords": [ "documentation", "javascript" ],
|
||||
"licenses": [
|
||||
@ -20,6 +20,7 @@
|
||||
"async": "0.1.22",
|
||||
"catharsis": "0.5.6",
|
||||
"crypto-browserify": "git+https://github.com/dominictarr/crypto-browserify.git#95c5d505",
|
||||
"esprima": "1.0.4",
|
||||
"js2xmlparser": "0.1.0",
|
||||
"jshint": "0.9.1",
|
||||
"markdown": "git+https://github.com/jsdoc3/markdown-js.git",
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
* @module rhino/jsdoc/src/astbuilder
|
||||
*/
|
||||
|
||||
var AstBuilder = module.exports = function() {
|
||||
var AstBuilder = exports.AstBuilder = function() {
|
||||
this._builder = new Packages.org.jsdoc.AstBuilder();
|
||||
};
|
||||
|
||||
|
||||
@ -10,7 +10,7 @@ var Parser = exports.Parser = function() {
|
||||
throw new Error('You must run JSDoc on Mozilla Rhino to use the Rhino parser.');
|
||||
}
|
||||
|
||||
astBuilder = new ( require(runtime.getModulePath('jsdoc/src/astbuilder')) )();
|
||||
astBuilder = new ( require(runtime.getModulePath('jsdoc/src/astbuilder')) ).AstBuilder();
|
||||
visitor = new ( require(runtime.getModulePath('jsdoc/src/visitor')) ).Visitor(this);
|
||||
|
||||
Parser.super_.call(this, astBuilder, visitor);
|
||||
|
||||
7
test/fixtures/testPlugin1.js
vendored
7
test/fixtures/testPlugin1.js
vendored
@ -30,10 +30,3 @@ exports.defineTags = function(dictionary) {
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
exports.nodeVisitor = {
|
||||
visitNode: function(node, e, parser, currentSourceName) {
|
||||
myGlobal.jsdocPluginsTest.plugin1.visitNode = true;
|
||||
e.stopPropagation = true;
|
||||
}
|
||||
};
|
||||
|
||||
6
test/fixtures/testPlugin2.js
vendored
6
test/fixtures/testPlugin2.js
vendored
@ -21,9 +21,3 @@ exports.handlers = {
|
||||
myGlobal.jsdocPluginsTest.plugin2.fileComplete = true;
|
||||
}
|
||||
};
|
||||
|
||||
exports.nodeVisitor = {
|
||||
visitNode: function() {
|
||||
myGlobal.jsdocPluginsTest.plugin2.visitNode = true;
|
||||
}
|
||||
};
|
||||
|
||||
@ -112,8 +112,9 @@ jasmine.asyncSpecDone = function() {
|
||||
jasmine.getDocSetFromFile = function(filename, parser) {
|
||||
var sourceCode = fs.readFileSync(__dirname + '/' + filename, 'utf8');
|
||||
var runtime = require('jsdoc/util/runtime');
|
||||
var testParser = parser ||
|
||||
new ( require(runtime.getModulePath('jsdoc/src/parser')) ).Parser();
|
||||
// TODO: change to runtime-appropriate parser (and/or get a config setting?)
|
||||
//var testParser = parser || require('jsdoc/src/parser').createParser('esprima');
|
||||
var testParser = parser || require('jsdoc/src/parser').createParser('rhino');
|
||||
var indexAll = require('jsdoc/borrow').indexAll;
|
||||
var doclets;
|
||||
|
||||
|
||||
4
test/specs/jsdoc/src/astbuilder.js
Normal file
4
test/specs/jsdoc/src/astbuilder.js
Normal file
@ -0,0 +1,4 @@
|
||||
/*global describe: true, env: true, expect: true, it: true, xdescribe: true */
|
||||
xdescribe('jsdoc/src/astbuilder', function() {
|
||||
// TODO
|
||||
});
|
||||
4
test/specs/jsdoc/src/syntax.js
Normal file
4
test/specs/jsdoc/src/syntax.js
Normal file
@ -0,0 +1,4 @@
|
||||
/*global describe: true, env: true, expect: true, it: true, xdescribe: true */
|
||||
xdescribe('jsdoc/src/syntax', function() {
|
||||
// TODO
|
||||
});
|
||||
4
test/specs/jsdoc/src/visitor.js
Normal file
4
test/specs/jsdoc/src/visitor.js
Normal file
@ -0,0 +1,4 @@
|
||||
/*global describe: true, env: true, expect: true, it: true, xdescribe: true */
|
||||
xdescribe('jsdoc/src/visitor', function() {
|
||||
// TODO
|
||||
});
|
||||
4
test/specs/jsdoc/src/walker.js
Normal file
4
test/specs/jsdoc/src/walker.js
Normal file
@ -0,0 +1,4 @@
|
||||
/*global describe: true, env: true, expect: true, it: true, xdescribe: true */
|
||||
xdescribe('jsdoc/src/walker', function() {
|
||||
// TODO
|
||||
});
|
||||
@ -1,5 +1,5 @@
|
||||
/*global app: true, describe: true, expect: true, it: true, jasmine: true */
|
||||
|
||||
// TODO: consolidate with specs/jsdoc/parser and specs/jsdoc/plugins
|
||||
describe("plugins", function() {
|
||||
var myGlobal = require('jsdoc/util/global');
|
||||
myGlobal.jsdocPluginsTest = myGlobal.jsdocPluginsTest || {};
|
||||
@ -43,13 +43,4 @@ describe("plugins", function() {
|
||||
expect(test[0].longname).toEqual("test");
|
||||
expect(test[0].foo).toEqual(true);
|
||||
});
|
||||
|
||||
it("should call the plugin's visitNode function", function() {
|
||||
expect(myGlobal.jsdocPluginsTest.plugin1.visitNode).toBeDefined();
|
||||
expect(myGlobal.jsdocPluginsTest.plugin1.visitNode).toEqual(true);
|
||||
});
|
||||
|
||||
it("should not call a second plugin's visitNode function if the first stopped propagation", function() {
|
||||
expect(myGlobal.jsdocPluginsTest.plugin2.visitNode).not.toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
@ -149,6 +149,31 @@ describe('rhino/jsdoc/src/parser', function() {
|
||||
expect(doclet.name).toBeDefined();
|
||||
expect(doclet.name).toBe('bar');
|
||||
});
|
||||
|
||||
it('should not call a second Rhino node visitor if the first visitor stopped ' +
|
||||
'propagation', function() {
|
||||
var doclet;
|
||||
|
||||
var visitor1 = {
|
||||
visitNode: function(rhinoNode, e, parser, sourceName) {
|
||||
e.stopPropagation = true;
|
||||
}
|
||||
};
|
||||
var visitor2 = {
|
||||
visitNode: function(rhinoNode, e, parser, sourceName) {
|
||||
e.propertyThatWillNeverBeSet = ':(';
|
||||
}
|
||||
};
|
||||
|
||||
require('jsdoc/src/handlers').attachTo(parser);
|
||||
parser.addNodeVisitor(visitor1);
|
||||
parser.addNodeVisitor(visitor2);
|
||||
parser.parse(sourceCode);
|
||||
|
||||
doclet = parser.results()[0];
|
||||
|
||||
expect(doclet.propertyThatWillNeverBeSet).not.toBeDefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user