node-query/node_modules/grunt-jsdoc/node_modules/jsdoc/lib/jsdoc/src/handlers.js

232 lines
8.0 KiB
JavaScript

/**
* @module jsdoc/src/handlers
*/
'use strict';
var jsdoc = {
doclet: require('jsdoc/doclet'),
name: require('jsdoc/name'),
util: {
logger: require('jsdoc/util/logger')
}
};
var util = require('util');
var currentModule = null;
var moduleRegExp = /^((?:module.)?exports|this)(\.|$)/;
function getNewDoclet(comment, e) {
var Doclet = jsdoc.doclet.Doclet;
var doclet;
var err;
try {
doclet = new Doclet(comment, e);
}
catch (error) {
err = new Error( util.format('cannot create a doclet for the comment "%s": %s',
comment.replace(/[\r\n]/g, ''), error.message) );
jsdoc.util.logger.error(err);
doclet = new Doclet('', e);
}
return doclet;
}
function setCurrentModule(doclet) {
if (doclet.kind === 'module') {
currentModule = doclet.longname;
}
}
function setDefaultScopeMemberOf(doclet) {
// add @inner and @memberof tags unless the current module exports only this symbol
if (currentModule && currentModule !== doclet.name) {
// add @inner unless the current module exports only this symbol
if (!doclet.scope) {
doclet.addTag('inner');
}
if (!doclet.memberof && doclet.scope !== 'global') {
doclet.addTag('memberof', currentModule);
}
}
}
/**
* Attach these event handlers to a particular instance of a parser.
* @param parser
*/
exports.attachTo = function(parser) {
function filter(doclet) {
// you can't document prototypes
if ( /#$/.test(doclet.longname) ) {
return true;
}
return false;
}
function addDoclet(newDoclet) {
var e;
if (newDoclet) {
setCurrentModule(newDoclet);
e = { doclet: newDoclet };
parser.emit('newDoclet', e);
if ( !e.defaultPrevented && !filter(e.doclet) ) {
parser.addResult(e.doclet);
}
}
}
// TODO: for clarity, decompose into smaller functions
function newSymbolDoclet(docletSrc, e) {
var memberofName = null,
newDoclet = getNewDoclet(docletSrc, e);
// A JSDoc comment can define a symbol name by including:
//
// + A `@name` tag
// + Another tag that accepts a name, such as `@function`
//
// When the JSDoc comment defines a symbol name, we treat it as a "virtual comment" for a
// symbol that isn't actually present in the code. And if a virtual comment is attached to
// a symbol, it's quite possible that the comment and symbol have nothing to do with one
// another.
//
// As a result, if we create a doclet for a `symbolFound` event, and we've already added a
// name attribute by parsing the JSDoc comment, we need to create a new doclet that ignores
// the attached JSDoc comment and only looks at the code.
if (newDoclet.name) {
// try again, without the comment
e.comment = '@undocumented';
newDoclet = getNewDoclet(e.comment, e);
}
if (newDoclet.alias) {
if (newDoclet.alias === '{@thisClass}') {
memberofName = parser.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;
}
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 basename = null,
scope = '';
if ( moduleRegExp.test(newDoclet.name) ) {
var nameStartsWith = RegExp.$1;
// remove stuff that indicates module membership (but don't touch the name
// `module.exports`, which identifies the module object itself)
if (newDoclet.name !== 'module.exports') {
newDoclet.name = newDoclet.name.replace(moduleRegExp, '');
}
// like /** @module foo */ exports.bar = 1;
// or /** @module foo */ module.exports.bar = 1;
// but not /** @module foo */ module.exports = 1;
if ( (nameStartsWith === 'exports' || nameStartsWith === 'module.exports') &&
newDoclet.name !== 'module.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 = parser.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 = parser.astnodeToMemberof(e.astnode);
if( Array.isArray(memberofName) ) {
basename = memberofName[1];
memberofName = memberofName[0];
}
}
if (memberofName) {
newDoclet.addTag('memberof', memberofName);
if (basename) {
newDoclet.name = (newDoclet.name || '')
.replace(new RegExp('^' + RegExp.escape(basename) + '.'), '');
}
}
else {
setDefaultScopeMemberOf(newDoclet);
}
}
newDoclet.postProcess();
}
else {
return false;
}
// set the scope to global unless a) the doclet is a memberof something or b) the current
// module exports only this symbol
if (!newDoclet.memberof && currentModule !== newDoclet.name) {
newDoclet.scope = 'global';
}
addDoclet.call(parser, newDoclet);
e.doclet = newDoclet;
}
// handles JSDoc comments that include a @name tag -- the code is ignored in such a case
parser.on('jsdocCommentFound', function(e) {
var newDoclet = getNewDoclet(e.comment, e);
if (!newDoclet.name) {
return false; // only interested in virtual comments (with a @name) here
}
setDefaultScopeMemberOf(newDoclet);
newDoclet.postProcess();
addDoclet.call(parser, newDoclet);
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);
for (var i = 0, l = subDoclets.length; i < l; i++) {
newSymbolDoclet.call(parser, subDoclets[i], e);
}
});
parser.on('fileComplete', function(e) {
currentModule = null;
});
};