PHP WebShell
Текущая директория: /opt/BitGoJS/node_modules/jsdoc/lib/jsdoc/src
Просмотр файла: parser.js
/**
* @module jsdoc/src/parser
*/
const EventEmitter = require('events').EventEmitter;
const fs = require('jsdoc/fs');
const jsdoc = {
doclet: require('jsdoc/doclet'),
env: require('jsdoc/env'),
name: require('jsdoc/name'),
src: {
astnode: require('jsdoc/src/astnode'),
syntax: require('jsdoc/src/syntax')
},
util: {
doop: require('jsdoc/util/doop')
}
};
const logger = require('jsdoc/util/logger');
const hasOwnProp = Object.prototype.hasOwnProperty;
const Syntax = jsdoc.src.syntax.Syntax;
// TODO: docs
const PARSERS = exports.PARSERS = {
js: 'jsdoc/src/parser'
};
/* eslint-disable no-script-url */
// Prefix for JavaScript strings that were provided in lieu of a filename.
const SCHEMA = 'javascript:';
/* eslint-enable no-script-url */
class DocletCache {
constructor() {
this._doclets = {};
}
get(name) {
if ( !hasOwnProp.call(this._doclets, name) ) {
return null;
}
// always return the most recent doclet
return this._doclets[name][this._doclets[name].length - 1];
}
put(name, value) {
if ( !hasOwnProp.call(this._doclets, name) ) {
this._doclets[name] = [];
}
this._doclets[name].push(value);
}
}
// TODO: docs
exports.createParser = type => {
let modulePath;
if (!type) {
/* istanbul ignore next */
type = 'js';
}
if (hasOwnProp.call(PARSERS, type)) {
modulePath = PARSERS[type];
}
else {
logger.fatal('The parser type "%s" is not recognized.', type);
return null;
}
return new (require(modulePath).Parser)();
};
// TODO: docs
function pretreat(code) {
return code
// comment out hashbang at the top of the file, like: #!/usr/bin/env node
.replace(/^(#![\S \t]+\r?\n)/, '// $1')
// to support code minifiers that preserve /*! comments, treat /*!* as equivalent to /**
.replace(/\/\*!\*/g, '/**')
// merge adjacent doclets
.replace(/\*\/\/\*\*+/g, '@also');
}
// TODO: docs
function definedInScope(doclet, basename) {
return Boolean(doclet) && Boolean(doclet.meta) && Boolean(doclet.meta.vars) &&
Boolean(basename) && hasOwnProp.call(doclet.meta.vars, basename);
}
// TODO: docs
/**
* @alias module:jsdoc/src/parser.Parser
* @extends module:events.EventEmitter
*/
class Parser extends EventEmitter {
// TODO: docs
constructor(builderInstance, visitorInstance, walkerInstance) {
super();
this.clear();
this._astBuilder = builderInstance || new (require('jsdoc/src/astbuilder').AstBuilder)();
this._visitor = visitorInstance || new (require('jsdoc/src/visitor').Visitor)();
this._walker = walkerInstance || new (require('jsdoc/src/walker').Walker)();
this._visitor.setParser(this);
Object.defineProperties(this, {
astBuilder: {
get() {
return this._astBuilder;
}
},
visitor: {
get() {
return this._visitor;
}
},
walker: {
get() {
return this._walker;
}
}
});
}
// TODO: docs
clear() {
this._resultBuffer = [];
this._resultBuffer.index = {
borrowed: [],
documented: {},
longname: {},
memberof: {}
};
this._byNodeId = new DocletCache();
this._byLongname = new DocletCache();
this._byLongname.put(jsdoc.name.LONGNAMES.GLOBAL, {
meta: {}
});
}
// TODO: update docs
/**
* Parse the given source files for JSDoc comments.
* @param {Array.<string>} sourceFiles An array of filepaths to the JavaScript sources.
* @param {string} [encoding]
*
* @fires module:jsdoc/src/parser.Parser.parseBegin
* @fires module:jsdoc/src/parser.Parser.fileBegin
* @fires module:jsdoc/src/parser.Parser.jsdocCommentFound
* @fires module:jsdoc/src/parser.Parser.symbolFound
* @fires module:jsdoc/src/parser.Parser.newDoclet
* @fires module:jsdoc/src/parser.Parser.fileComplete
* @fires module:jsdoc/src/parser.Parser.parseComplete
*
* @example <caption>Parse two source files.</caption>
* var myFiles = ['file1.js', 'file2.js'];
* var docs = jsdocParser.parse(myFiles);
*/
parse(sourceFiles, encoding) {
encoding = encoding || jsdoc.env.conf.encoding || 'utf8';
let filename = '';
let sourceCode = '';
let sourceFile;
const parsedFiles = [];
const e = {};
if (typeof sourceFiles === 'string') {
sourceFiles = [sourceFiles];
}
e.sourcefiles = sourceFiles;
logger.debug('Parsing source files: %j', sourceFiles);
this.emit('parseBegin', e);
for (let i = 0, l = sourceFiles.length; i < l; i++) {
sourceCode = '';
sourceFile = sourceFiles[i];
if (sourceFile.indexOf(SCHEMA) === 0) {
sourceCode = sourceFile.substr(SCHEMA.length);
filename = `[[string${i}]]`;
}
else {
filename = sourceFile;
try {
sourceCode = fs.readFileSync(filename, encoding);
}
catch (err) {
logger.error('Unable to read and parse the source file %s: %s', filename, err);
}
}
if (sourceCode.length) {
this._parseSourceCode(sourceCode, filename);
parsedFiles.push(filename);
}
}
this.emit('parseComplete', {
sourcefiles: parsedFiles,
doclets: this._resultBuffer
});
logger.debug('Finished parsing source files.');
return this._resultBuffer;
}
// TODO: docs
fireProcessingComplete(doclets) {
this.emit('processingComplete', { doclets: doclets });
}
// TODO: docs
results() {
return this._resultBuffer;
}
// TODO: update docs
/**
* @param {module:jsdoc/doclet.Doclet} doclet The parse result to add to the result buffer.
*/
addResult(doclet) {
const index = this._resultBuffer.index;
this._resultBuffer.push(doclet);
// track all doclets by longname
if ( !hasOwnProp.call(index.longname, doclet.longname) ) {
index.longname[doclet.longname] = [];
}
index.longname[doclet.longname].push(doclet);
// track all doclets that have a memberof by memberof
if (doclet.memberof) {
if ( !hasOwnProp.call(index.memberof, doclet.memberof) ) {
index.memberof[doclet.memberof] = [];
}
index.memberof[doclet.memberof].push(doclet);
}
// track longnames of documented symbols
if (!doclet.undocumented) {
if ( !hasOwnProp.call(index.documented, doclet.longname) ) {
index.documented[doclet.longname] = [];
}
index.documented[doclet.longname].push(doclet);
}
// track doclets with a `borrowed` property
if ( hasOwnProp.call(doclet, 'borrowed') ) {
index.borrowed.push(doclet);
}
}
// TODO: docs
addAstNodeVisitor(visitor) {
this._visitor.addAstNodeVisitor(visitor);
}
// TODO: docs
getAstNodeVisitors() {
return this._visitor.getAstNodeVisitors();
}
/** @private */
_parseSourceCode(sourceCode, sourceName) {
let ast;
let e = {
filename: sourceName
};
this.emit('fileBegin', e);
logger.info('Parsing %s ...', sourceName);
if (!e.defaultPrevented) {
e = {
filename: sourceName,
source: sourceCode
};
this.emit('beforeParse', e);
sourceCode = e.source;
sourceName = e.filename;
sourceCode = pretreat(e.source);
ast = this._astBuilder.build(sourceCode, sourceName);
if (ast) {
this._walkAst(ast, this._visitor, sourceName);
}
}
this.emit('fileComplete', e);
}
/** @private */
_walkAst(ast, visitor, sourceName) {
this._walker.recurse(ast, visitor, sourceName);
}
// TODO: docs
addDocletRef(e) {
let fakeDoclet;
let node;
if (e && e.code && e.code.node) {
node = e.code.node;
if (e.doclet) {
// allow lookup from node ID => doclet
this._byNodeId.put(node.nodeId, e.doclet);
this._byLongname.put(e.doclet.longname, e.doclet);
}
// keep references to undocumented anonymous functions, too, as they might have scoped vars
else if (
(node.type === Syntax.FunctionDeclaration || node.type === Syntax.FunctionExpression ||
node.type === Syntax.ArrowFunctionExpression) &&
!this._getDocletById(node.nodeId) ) {
fakeDoclet = {
longname: jsdoc.name.LONGNAMES.ANONYMOUS,
meta: {
code: e.code
}
};
this._byNodeId.put(node.nodeId, fakeDoclet);
this._byLongname.put(fakeDoclet.longname, fakeDoclet);
}
}
}
// TODO: docs
_getDocletById(id) {
return this._byNodeId.get(id);
}
/**
* Retrieve the most recently seen doclet that has the given longname.
*
* @param {string} longname - The longname to search for.
* @return {module:jsdoc/doclet.Doclet?} The most recent doclet for the longname.
*/
_getDocletByLongname(longname) {
return this._byLongname.get(longname);
}
// TODO: docs
/**
* Given a node, determine what the node is a member of.
* @param {node} node
* @returns {string} The long name of the node that this is a member of.
*/
astnodeToMemberof(node) {
let basename;
let doclet;
let scope;
const result = {};
const type = node.type;
if ( (type === Syntax.FunctionDeclaration || type === Syntax.FunctionExpression ||
type === Syntax.ArrowFunctionExpression || type === Syntax.VariableDeclarator) &&
node.enclosingScope ) {
doclet = this._getDocletById(node.enclosingScope.nodeId);
if (!doclet) {
result.memberof = jsdoc.name.LONGNAMES.ANONYMOUS + jsdoc.name.SCOPE.PUNC.INNER;
}
else {
result.memberof = doclet.longname + jsdoc.name.SCOPE.PUNC.INNER;
}
}
else if (type === Syntax.ClassPrivateProperty || type === Syntax.ClassProperty) {
doclet = this._getDocletById(node.enclosingScope.nodeId);
if (!doclet) {
result.memberof = jsdoc.name.LONGNAMES.ANONYMOUS + jsdoc.name.SCOPE.PUNC.INSTANCE;
}
else {
result.memberof = doclet.longname + jsdoc.name.SCOPE.PUNC.INSTANCE;
}
}
else if (type === Syntax.MethodDefinition && node.kind === 'constructor') {
doclet = this._getDocletById(node.enclosingScope.nodeId);
// global classes aren't a member of anything
if (doclet.memberof) {
result.memberof = doclet.memberof + jsdoc.name.SCOPE.PUNC.INNER;
}
}
// special case for methods in classes that are returned by arrow function expressions; for
// other method definitions, we get the memberof from the node name elsewhere. yes, this is
// confusing...
else if (type === Syntax.MethodDefinition && node.parent.parent.parent &&
node.parent.parent.parent.type === Syntax.ArrowFunctionExpression) {
doclet = this._getDocletById(node.enclosingScope.nodeId);
if (doclet) {
result.memberof = doclet.longname +
(node.static === true ?
jsdoc.name.SCOPE.PUNC.STATIC :
jsdoc.name.SCOPE.PUNC.INSTANCE);
}
}
else {
// check local references for aliases
scope = node;
basename = jsdoc.name.getBasename( jsdoc.src.astnode.nodeToValue(node) );
// walk up the scope chain until we find the scope in which the node is defined
while (scope.enclosingScope) {
doclet = this._getDocletById(scope.enclosingScope.nodeId);
if ( doclet && definedInScope(doclet, basename) ) {
result.memberof = doclet.meta.vars[basename];
result.basename = basename;
break;
}
else {
// move up
scope = scope.enclosingScope;
}
}
// do we know that it's a global?
doclet = this._getDocletByLongname(jsdoc.name.LONGNAMES.GLOBAL);
if ( doclet && definedInScope(doclet, basename) ) {
result.memberof = doclet.meta.vars[basename];
result.basename = basename;
}
else {
doclet = this._getDocletById(node.parent.nodeId);
// set the result if we found a doclet. (if we didn't, the AST node may describe a
// global symbol.)
if (doclet) {
result.memberof = doclet.longname || doclet.name;
}
}
}
return result;
}
/**
* Get the doclet for the lowest-level class, if any, that is in the scope chain for a given node.
*
* @param {Object} node - The node whose scope chain will be searched.
* @return {module:jsdoc/doclet.Doclet?} The doclet for the lowest-level class in the node's scope
* chain.
*/
_getParentClass({enclosingScope}) {
let doclet;
let nameAtoms;
let scope = enclosingScope;
function isClass(d) {
return d && d.kind === 'class';
}
while (scope) {
// get the doclet, if any, for the parent scope
doclet = this._getDocletById(scope.nodeId);
if (doclet) {
// is the doclet for a class? if so, we're done
if ( isClass(doclet) ) {
break;
}
// is the doclet for an instance member of a class? if so, try to get the doclet for the
// owning class
nameAtoms = jsdoc.name.shorten(doclet.longname);
if (nameAtoms.scope === jsdoc.name.SCOPE.PUNC.INSTANCE) {
doclet = this._getDocletByLongname(nameAtoms.memberof);
if ( isClass(doclet) ) {
break;
}
}
}
// move up to the next parent scope
scope = scope.enclosingScope;
}
return (isClass(doclet) ? doclet : null);
}
// TODO: docs
/**
* Resolve what "this" refers to relative to a node.
* @param {node} node - The "this" node
* @returns {string} The longname of the enclosing node.
*/
resolveThis(node) {
let doclet;
let parentClass;
let result;
// Properties are handled below.
if (node.type !== Syntax.Property && node.enclosingScope) {
// For ES2015 constructor functions, we use the class declaration to resolve `this`.
if (node.parent && node.parent.type === Syntax.MethodDefinition &&
node.parent.kind === 'constructor') {
doclet = this._getDocletById(node.parent.parent.parent.nodeId);
}
// Otherwise, if there's an enclosing scope, we use the enclosing scope to resolve `this`.
else {
doclet = this._getDocletById(node.enclosingScope.nodeId);
}
if (!doclet) {
result = jsdoc.name.LONGNAMES.ANONYMOUS; // TODO handle global this?
}
else if (doclet.this) {
result = doclet.this;
}
else if (doclet.kind === 'function' && doclet.memberof) {
parentClass = this._getParentClass(node);
// like: function Foo() { this.bar = function(n) { /** blah */ this.name = n; };
// or: Foo.prototype.bar = function(n) { /** blah */ this.name = n; };
// or: var Foo = exports.Foo = function(n) { /** blah */ this.name = n; };
// or: Foo.constructor = function(n) { /** blah */ this.name = n; }
if ( parentClass || /\.constructor$/.test(doclet.longname) ) {
result = doclet.memberof;
}
// like: function notAClass(n) { /** global this */ this.name = n; }
else {
result = doclet.longname;
}
}
// like: var foo = function(n) { /** blah */ this.bar = n; }
else if ( doclet.kind === 'member' && jsdoc.src.astnode.isAssignment(node) ) {
result = doclet.longname;
}
// walk up to the closest class we can find
else if (doclet.kind === 'class' || doclet.kind === 'interface' || doclet.kind === 'module') {
result = doclet.longname;
}
else if (node.enclosingScope) {
result = this.resolveThis(node.enclosingScope);
}
}
// For object properties, we use the node's parent (the object) instead.
else {
doclet = this._getDocletById(node.parent.nodeId);
if (!doclet) {
// The object wasn't documented, so we don't know what name to use.
result = '';
}
else {
result = doclet.longname;
}
}
return result;
}
/**
* Given an AST node representing an object property, find the doclets for the parent object or
* objects.
*
* If the object is part of a simple assignment (for example, `var foo = { x: 1 }`), this method
* returns a single doclet (in this case, the doclet for `foo`).
*
* If the object is part of a chained assignment (for example, `var foo = exports.FOO = { x: 1 }`,
* this method returns multiple doclets (in this case, the doclets for `foo` and `exports.FOO`).
*
* @param {Object} node - An AST node representing an object property.
* @return {Array.<module:jsdoc/doclet.Doclet>} An array of doclets for the parent object or objects, or
* an empty array if no doclets are found.
*/
resolvePropertyParents({parent}) {
let currentAncestor = parent;
let nextAncestor = currentAncestor.parent;
let doclet;
const doclets = [];
while (currentAncestor) {
doclet = this._getDocletById(currentAncestor.nodeId);
if (doclet) {
doclets.push(doclet);
}
// if the next ancestor is an assignment expression (for example, `exports.FOO` in
// `var foo = exports.FOO = { x: 1 }`, keep walking upwards
if (nextAncestor && nextAncestor.type === Syntax.AssignmentExpression) {
nextAncestor = nextAncestor.parent;
currentAncestor = currentAncestor.parent;
}
// otherwise, we're done
else {
currentAncestor = null;
}
}
return doclets;
}
// TODO: docs
/**
* 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.
*/
resolveVar({enclosingScope, type}, basename) {
let doclet;
let result;
const scope = enclosingScope;
// HACK: return an empty string for function declarations so they don't end up in anonymous
// scope (see #685 and #693)
if (type === Syntax.FunctionDeclaration) {
result = '';
}
else if (!scope) {
result = ''; // global
}
else {
doclet = this._getDocletById(scope.nodeId);
if ( definedInScope(doclet, basename) ) {
result = doclet.longname;
}
else {
result = this.resolveVar(scope, basename);
}
}
return result;
}
// TODO: docs
resolveEnum(e) {
const doclets = this.resolvePropertyParents(e.code.node.parent);
doclets.forEach(doclet => {
if (doclet && doclet.isEnum) {
doclet.properties = doclet.properties || [];
// members of an enum inherit the enum's type
if (doclet.type && !e.doclet.type) {
// clone the type to prevent circular refs
e.doclet.type = jsdoc.util.doop(doclet.type);
}
delete e.doclet.undocumented;
e.doclet.defaultvalue = e.doclet.meta.code.value;
// add the doclet to the parent's properties
doclet.properties.push(e.doclet);
}
});
}
}
exports.Parser = Parser;
// TODO: document other events
/**
* Fired once for each JSDoc comment in the current source code.
* @event jsdocCommentFound
* @memberof module:jsdoc/src/parser.Parser
* @type {Object}
* @property {string} comment The text content of the JSDoc comment
* @property {number} lineno The line number associated with the found comment.
* @property {number} columnno The column number associated with the found comment.
* @property {string} filename The file name associated with the found comment.
*/
Выполнить команду
Для локальной разработки. Не используйте в интернете!