mirror of
https://github.com/jsdoc/jsdoc.git
synced 2025-12-08 19:46:11 +00:00
refactor
This commit is contained in:
parent
17fb5be064
commit
9be79c198e
@ -14,22 +14,61 @@ var jsdoc = {
|
|||||||
var util = require('util');
|
var util = require('util');
|
||||||
|
|
||||||
var currentModule = null;
|
var currentModule = null;
|
||||||
|
var SCOPE_PUNC = jsdoc.name.SCOPE.PUNC;
|
||||||
|
var unresolvedName = /^((?:module.)?exports|this)(\.|$)/;
|
||||||
|
|
||||||
var moduleRegExp = /^((?:module.)?exports|this)(\.|$)/;
|
function filterByLongname(doclet) {
|
||||||
|
// you can't document prototypes
|
||||||
|
if ( /#$/.test(doclet.longname) ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
function getNewDoclet(comment, e) {
|
return false;
|
||||||
var Doclet = jsdoc.doclet.Doclet;
|
}
|
||||||
|
|
||||||
|
function createDoclet(comment, e) {
|
||||||
var doclet;
|
var doclet;
|
||||||
var err;
|
var err;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
doclet = new Doclet(comment, e);
|
doclet = new jsdoc.doclet.Doclet(comment, e);
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
err = new Error( util.format('cannot create a doclet for the comment "%s": %s',
|
err = new Error( util.format('cannot create a doclet for the comment "%s": %s',
|
||||||
comment.replace(/[\r\n]/g, ''), error.message) );
|
comment.replace(/[\r\n]/g, ''), error.message) );
|
||||||
jsdoc.util.logger.error(err);
|
jsdoc.util.logger.error(err);
|
||||||
doclet = new Doclet('', e);
|
doclet = new jsdoc.doclet.Doclet('', e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return doclet;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a doclet for a `symbolFound` event. The doclet represents an actual symbol that is defined
|
||||||
|
* in the code.
|
||||||
|
*
|
||||||
|
* Here's why this function is useful. 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
|
||||||
|
* possible that the comment and symbol have nothing to do with one another.
|
||||||
|
*
|
||||||
|
* To handle this case, this function checks the new doclet to see if we've already added a name
|
||||||
|
* property by parsing the JSDoc comment. If so, this method creates a replacement doclet that
|
||||||
|
* ignores the attached JSDoc comment and only looks at the code.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
function createSymbolDoclet(comment, e) {
|
||||||
|
var doclet = createDoclet(comment, e);
|
||||||
|
|
||||||
|
if (doclet.name) {
|
||||||
|
// try again, without the comment
|
||||||
|
e.comment = '@undocumented';
|
||||||
|
doclet = createDoclet(e.comment, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
return doclet;
|
return doclet;
|
||||||
@ -55,135 +94,142 @@ function setDefaultScopeMemberOf(doclet) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
function addDoclet(parser, newDoclet) {
|
||||||
* 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;
|
var e;
|
||||||
if (newDoclet) {
|
if (newDoclet) {
|
||||||
setCurrentModule(newDoclet);
|
setCurrentModule(newDoclet);
|
||||||
e = { doclet: newDoclet };
|
e = { doclet: newDoclet };
|
||||||
parser.emit('newDoclet', e);
|
parser.emit('newDoclet', e);
|
||||||
|
|
||||||
if ( !e.defaultPrevented && !filter(e.doclet) ) {
|
if ( !e.defaultPrevented && !filterByLongname(e.doclet) ) {
|
||||||
parser.addResult(e.doclet);
|
parser.addResult(e.doclet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: for clarity, decompose into smaller functions
|
function processAlias(parser, doclet, astNode) {
|
||||||
function newSymbolDoclet(docletSrc, e) {
|
var memberofName;
|
||||||
var memberofName = null,
|
|
||||||
newDoclet = getNewDoclet(docletSrc, e);
|
|
||||||
|
|
||||||
// A JSDoc comment can define a symbol name by including:
|
if (doclet.alias === '{@thisClass}') {
|
||||||
//
|
memberofName = parser.resolveThis(astNode);
|
||||||
// + 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
|
// "class" refers to the owner of the prototype, not the prototype itself
|
||||||
if ( /^(.+?)(\.prototype|#)$/.test(memberofName) ) {
|
if ( /^(.+?)(\.prototype|#)$/.test(memberofName) ) {
|
||||||
memberofName = RegExp.$1;
|
memberofName = RegExp.$1;
|
||||||
}
|
}
|
||||||
newDoclet.alias = memberofName;
|
doclet.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
|
doclet.addTag('name', doclet.alias);
|
||||||
// `module.exports`, which identifies the module object itself)
|
doclet.postProcess();
|
||||||
if (newDoclet.name !== 'module.exports') {
|
}
|
||||||
newDoclet.name = newDoclet.name.replace(moduleRegExp, '');
|
|
||||||
|
function findModuleMemberof(parser, doclet, astNode, nameStartsWith) {
|
||||||
|
var memberof = '';
|
||||||
|
var scopePunc = '';
|
||||||
|
|
||||||
|
// remove stuff that indicates module membership (but don't touch the name `module.exports`,
|
||||||
|
// which identifies the module object itself)
|
||||||
|
if (doclet.name !== 'module.exports') {
|
||||||
|
doclet.name = doclet.name.replace(unresolvedName, '');
|
||||||
}
|
}
|
||||||
|
|
||||||
// like /** @module foo */ exports.bar = 1;
|
// like /** @module foo */ exports.bar = 1;
|
||||||
// or /** @module foo */ module.exports.bar = 1;
|
// or /** @module foo */ module.exports.bar = 1;
|
||||||
// but not /** @module foo */ module.exports = 1;
|
// but not /** @module foo */ module.exports = 1;
|
||||||
if ( (nameStartsWith === 'exports' || nameStartsWith === 'module.exports') &&
|
if ( (nameStartsWith === 'exports' || nameStartsWith === 'module.exports') &&
|
||||||
newDoclet.name !== 'module.exports' && currentModule ) {
|
doclet.name !== 'module.exports' && currentModule ) {
|
||||||
memberofName = currentModule;
|
memberof = currentModule;
|
||||||
scope = 'static';
|
scopePunc = SCOPE_PUNC.STATIC;
|
||||||
}
|
}
|
||||||
else if (newDoclet.name === 'module.exports' && currentModule) {
|
else if (doclet.name === 'module.exports' && currentModule) {
|
||||||
newDoclet.addTag('name', currentModule);
|
doclet.addTag('name', currentModule);
|
||||||
newDoclet.postProcess();
|
doclet.postProcess();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// like /** @module foo */ exports = {bar: 1};
|
// like /** @module foo */ exports = {bar: 1};
|
||||||
// or /** blah */ this.foo = 1;
|
// or /** blah */ this.foo = 1;
|
||||||
memberofName = parser.resolveThis(e.astnode);
|
memberof = parser.resolveThis(astNode);
|
||||||
scope = nameStartsWith === 'exports' ? 'static' : 'instance';
|
scopePunc = (nameStartsWith === 'exports') ?
|
||||||
|
SCOPE_PUNC.STATIC :
|
||||||
|
SCOPE_PUNC.INSTANCE;
|
||||||
|
|
||||||
// like /** @module foo */ this.bar = 1;
|
// like /** @module foo */ this.bar = 1;
|
||||||
if (nameStartsWith === 'this' && currentModule && !memberofName) {
|
if (nameStartsWith === 'this' && currentModule && !memberof) {
|
||||||
memberofName = currentModule;
|
memberof = currentModule;
|
||||||
scope = 'static';
|
scopePunc = SCOPE_PUNC.STATIC;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (memberofName) {
|
return {
|
||||||
if (newDoclet.name) {
|
memberof: memberof,
|
||||||
newDoclet.name = memberofName + (scope === 'instance' ? '#' : '.') +
|
scopePunc: scopePunc
|
||||||
newDoclet.name;
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function addSymbolMemberof(parser, doclet, astNode) {
|
||||||
|
var basename;
|
||||||
|
var memberof;
|
||||||
|
var memberofInfo;
|
||||||
|
var scopePunc;
|
||||||
|
var unresolved;
|
||||||
|
|
||||||
|
// TODO: is this the correct behavior, given that we don't always use the AST node?
|
||||||
|
if (!astNode) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
else { newDoclet.name = memberofName; }
|
|
||||||
|
// check to see if the doclet name is an unresolved reference to the module wrapper
|
||||||
|
unresolved = unresolvedName.exec(doclet.name);
|
||||||
|
if (unresolved) {
|
||||||
|
memberofInfo = findModuleMemberof(parser, doclet, astNode, unresolved[1]);
|
||||||
|
memberof = memberofInfo.memberof;
|
||||||
|
scopePunc = memberofInfo.scopePunc;
|
||||||
|
|
||||||
|
if (memberof) {
|
||||||
|
doclet.name = doclet.name ?
|
||||||
|
memberof + scopePunc + doclet.name :
|
||||||
|
memberof;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
memberofName = parser.astnodeToMemberof(e.astnode);
|
memberofInfo = parser.astnodeToMemberof(astNode);
|
||||||
if( Array.isArray(memberofName) ) {
|
if( Array.isArray(memberofInfo) ) {
|
||||||
basename = memberofName[1];
|
basename = memberofInfo[1];
|
||||||
memberofName = memberofName[0];
|
memberof = memberofInfo[0];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
memberof = memberofInfo;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (memberofName) {
|
// if we found a memberof name, apply it to the doclet
|
||||||
newDoclet.addTag('memberof', memberofName);
|
if (memberof) {
|
||||||
|
doclet.addTag('memberof', memberof);
|
||||||
if (basename) {
|
if (basename) {
|
||||||
newDoclet.name = (newDoclet.name || '')
|
doclet.name = (doclet.name || '')
|
||||||
.replace(new RegExp('^' + escape(basename) + '.'), '');
|
.replace(new RegExp('^' + escape(basename) + '.'), '');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// otherwise, use the defaults
|
||||||
else {
|
else {
|
||||||
setDefaultScopeMemberOf(newDoclet);
|
setDefaultScopeMemberOf(doclet);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function newSymbolDoclet(parser, docletSrc, e) {
|
||||||
|
var memberofName = null;
|
||||||
|
var newDoclet = createSymbolDoclet(docletSrc, e);
|
||||||
|
|
||||||
|
// if there's an alias, use that as the symbol name
|
||||||
|
if (newDoclet.alias) {
|
||||||
|
processAlias(parser, newDoclet, e.astnode);
|
||||||
|
}
|
||||||
|
// otherwise, get the symbol name from the code
|
||||||
|
else if (e.code && e.code.name) {
|
||||||
|
newDoclet.addTag('name', e.code.name);
|
||||||
|
if (!newDoclet.memberof) {
|
||||||
|
addSymbolMemberof(parser, newDoclet, e.astnode);
|
||||||
}
|
}
|
||||||
|
|
||||||
newDoclet.postProcess();
|
newDoclet.postProcess();
|
||||||
@ -198,10 +244,15 @@ exports.attachTo = function(parser) {
|
|||||||
newDoclet.scope = 'global';
|
newDoclet.scope = 'global';
|
||||||
}
|
}
|
||||||
|
|
||||||
addDoclet.call(parser, newDoclet);
|
addDoclet(parser, newDoclet);
|
||||||
e.doclet = newDoclet;
|
e.doclet = newDoclet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attach these event handlers to a particular instance of a parser.
|
||||||
|
* @param parser
|
||||||
|
*/
|
||||||
|
exports.attachTo = function(parser) {
|
||||||
// Handle JSDoc "virtual comments" that include one of the following:
|
// Handle JSDoc "virtual comments" that include one of the following:
|
||||||
//
|
//
|
||||||
// + A `@name` tag
|
// + A `@name` tag
|
||||||
@ -211,7 +262,7 @@ exports.attachTo = function(parser) {
|
|||||||
var newDoclet;
|
var newDoclet;
|
||||||
|
|
||||||
for (var i = 0, l = comments.length; i < l; i++) {
|
for (var i = 0, l = comments.length; i < l; i++) {
|
||||||
newDoclet = getNewDoclet(comments[i], e);
|
newDoclet = createDoclet(comments[i], e);
|
||||||
|
|
||||||
// we're only interested in virtual comments here
|
// we're only interested in virtual comments here
|
||||||
if (!newDoclet.name) {
|
if (!newDoclet.name) {
|
||||||
@ -220,7 +271,7 @@ exports.attachTo = function(parser) {
|
|||||||
|
|
||||||
setDefaultScopeMemberOf(newDoclet);
|
setDefaultScopeMemberOf(newDoclet);
|
||||||
newDoclet.postProcess();
|
newDoclet.postProcess();
|
||||||
addDoclet.call(parser, newDoclet);
|
addDoclet(parser, newDoclet);
|
||||||
|
|
||||||
e.doclet = newDoclet;
|
e.doclet = newDoclet;
|
||||||
}
|
}
|
||||||
@ -231,7 +282,7 @@ exports.attachTo = function(parser) {
|
|||||||
var comments = e.comment.split(/@also\b/g);
|
var comments = e.comment.split(/@also\b/g);
|
||||||
|
|
||||||
for (var i = 0, l = comments.length; i < l; i++) {
|
for (var i = 0, l = comments.length; i < l; i++) {
|
||||||
newSymbolDoclet.call(parser, comments[i], e);
|
newSymbolDoclet(parser, comments[i], e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user