fix multiple naming issues with members of a class that is the default export (#1120)

This commit is contained in:
Jeff Williams 2015-12-07 15:56:07 -08:00
parent 25ab7b6631
commit 41d33e8399
7 changed files with 194 additions and 47 deletions

View File

@ -197,13 +197,25 @@ var nodeToValue = exports.nodeToValue = function(node) {
if (parent.type === Syntax.ClassExpression) { if (parent.type === Syntax.ClassExpression) {
str = nodeToValue(parent.parent); str = nodeToValue(parent.parent);
} }
// for the constructor of a module's default export, use a special name
else if (node.kind === 'constructor' && parent.parent &&
parent.parent.type === Syntax.ExportDefaultDeclaration) {
str = 'module.exports';
}
// if the method is a member of a module's default export, ignore the name, because it's
// irrelevant
else if (parent.parent && parent.parent.type === Syntax.ExportDefaultDeclaration) {
str = '';
}
// otherwise, use the class's name // otherwise, use the class's name
else { else {
str = nodeToValue(parent.id); str = parent.id ? nodeToValue(parent.id) : '';
} }
if (node.kind !== 'constructor') { if (node.kind !== 'constructor') {
str += node.static ? name.SCOPE.PUNC.STATIC : name.SCOPE.PUNC.INSTANCE; if (str) {
str += node.static ? name.SCOPE.PUNC.STATIC : name.SCOPE.PUNC.INSTANCE;
}
str += nodeToValue(node.key); str += nodeToValue(node.key);
} }
break; break;

View File

@ -7,6 +7,9 @@ var escape = require('escape-string-regexp');
var jsdoc = { var jsdoc = {
doclet: require('jsdoc/doclet'), doclet: require('jsdoc/doclet'),
name: require('jsdoc/name'), name: require('jsdoc/name'),
src: {
syntax: require('jsdoc/src/syntax')
},
util: { util: {
logger: require('jsdoc/util/logger') logger: require('jsdoc/util/logger')
} }
@ -16,6 +19,7 @@ var util = require('util');
var currentModule = null; var currentModule = null;
var SCOPE_NAMES = jsdoc.name.SCOPE.NAMES; var SCOPE_NAMES = jsdoc.name.SCOPE.NAMES;
var SCOPE_PUNC = jsdoc.name.SCOPE.PUNC; var SCOPE_PUNC = jsdoc.name.SCOPE.PUNC;
var Syntax = jsdoc.src.syntax.Syntax;
var unresolvedName = /^((?:module.)?exports|this)(\.|$)/; var unresolvedName = /^((?:module.)?exports|this)(\.|$)/;
function CurrentModule(doclet) { function CurrentModule(doclet) {
@ -90,9 +94,21 @@ function setCurrentModule(doclet) {
function setModuleScopeMemberOf(doclet) { function setModuleScopeMemberOf(doclet) {
// handle module symbols that are _not_ assigned to module.exports // handle module symbols that are _not_ assigned to module.exports
if (currentModule && currentModule.longname !== doclet.name) { if (currentModule && currentModule.longname !== doclet.name) {
// if we don't already know the scope, it must be an inner member
if (!doclet.scope) { if (!doclet.scope) {
doclet.addTag('inner'); // is this a method definition? if so, get the scope from the node directly
if (doclet.meta && doclet.meta.code && doclet.meta.code.node &&
doclet.meta.code.node.type === Syntax.MethodDefinition) {
if (doclet.meta.code.node.static) {
doclet.addTag('static');
}
else {
doclet.addTag('instance');
}
}
// otherwise, it must be an inner member
else {
doclet.addTag('inner');
}
} }
// if the doclet isn't a memberof anything yet, and it's not a global, it must be a memberof // if the doclet isn't a memberof anything yet, and it's not a global, it must be a memberof

View File

@ -1,4 +1,16 @@
/** @module */ /** @module test */
/** Test class */ /** Test class */
export default class { } export default class {
/** Test constructor */
constructor(foo) {
/** Test member */
this.foo = foo;
}
/** Test method */
test() {}
/** Test static method */
static staticTest() {}
}

View File

@ -1,19 +1,28 @@
/** /**
Describe the module here. * Describe the module here.
@module mymodule/config * @module mymodule/config
*/ */
/** /**
Create a new configuration. * Create a new configuration.
*
@classdesc Describe the class here. * @classdesc Describe the class here.
@class * @class
@alias module:mymodule/config * @alias module:mymodule/config
@param {string} id * @param {string} id
*/ */
function Config(id) { function Config(id) {
/** Document me. */ /** Document me. */
this.id = id; this.id = id;
} }
module.exports = Config; /**
* Get the configuration ID.
*
* @return {string} The configuration ID.
*/
Config.prototype.getId = function() {
return this.id;
}
module.exports = Config;

22
test/fixtures/moduleisconstructor2.js vendored Normal file
View File

@ -0,0 +1,22 @@
/**
* Describe the module here.
* @module mymodule/config
*/
/** Describe the class here. */
export default class Config {
/** Create a new configuration. */
constructor(id) {
/** Document me. */
this.id = id;
}
/**
* Get the configuration ID.
*
* @return {string} The configuration ID.
*/
getId() {
return this.id;
}
}

View File

@ -1,11 +1,25 @@
'use strict'; 'use strict';
describe('anonymous class', function() { describe('anonymous class', function() {
it('should not crash JSDoc', function() { var docSet = jasmine.getDocSetFromFile('test/fixtures/anonymousclass.js');
function getDocs() { var klass = docSet.getByLongname('module:test')[2];
return jasmine.getDocSetFromFile('test/fixtures/anonymousclass.js'); var foo = docSet.getByLongname('module:test#foo')[0];
} var klassTest = docSet.getByLongname('module:test#test')[0];
var klassStaticTest = docSet.getByLongname('module:test.staticTest')[0];
expect(getDocs).not.toThrow(); it('should merge the constructor docs with the class docs', function() {
expect(klass.description).toBe('Test constructor');
});
it('should use the correct longname for instance properties', function() {
expect(foo.description).toBe('Test member');
});
it('should use the correct longname for instance methods', function() {
expect(klassTest.description).toBe('Test method');
});
it('should use the correct longname for static methods', function() {
expect(klassStaticTest.description).toBe('Test static method');
}); });
}); });

View File

@ -1,37 +1,99 @@
'use strict'; 'use strict';
describe('module that exports a constructor', function() { describe('module that exports a constructor', function() {
var docSet = jasmine.getDocSetFromFile('test/fixtures/moduleisconstructor.js'); describe('pre-ES2015 module', function() {
var modules = docSet.doclets.filter(function(doclet) { var docSet = jasmine.getDocSetFromFile('test/fixtures/moduleisconstructor.js');
return doclet.kind === 'module'; var modules = docSet.doclets.filter(function(doclet) {
}); return doclet.kind === 'module';
var classes = docSet.doclets.filter(function(doclet) { });
return doclet.kind === 'class'; var classes = docSet.doclets.filter(function(doclet) {
}); return doclet.kind === 'class';
});
var getId = docSet.getByLongname('module:mymodule/config#getId')[0];
var id = docSet.getByLongname('module:mymodule/config#id')[0];
it('should include one doclet whose kind is "module"', function() { it('should include one doclet whose kind is "module"', function() {
expect(modules.length).toBe(1); expect(modules.length).toBe(1);
expect(modules[0].kind).toBe('module'); expect(modules[0].kind).toBe('module');
});
it('should include one doclet whose kind is "class"', function() {
expect(classes.length).toBe(1);
expect(classes[0].kind).toBe('class');
});
describe('class doclet', function() {
it('should include a "description" property that contains the constructor description', function() {
expect(classes[0].description).toEqual('Create a new configuration.');
}); });
it('should include a "class-description" property', function() { it('should include one doclet whose kind is "class"', function() {
expect(classes[0].classdesc).toEqual('Describe the class here.'); expect(classes.length).toBe(1);
expect(classes[0].kind).toBe('class');
});
describe('class doclet', function() {
it('should include a "description" property that contains the constructor description', function() {
expect(classes[0].description).toEqual('Create a new configuration.');
});
it('should include a "classdesc" property', function() {
expect(classes[0].classdesc).toEqual('Describe the class here.');
});
});
describe('module doclet', function() {
it('should include a "description" property that contains the module description', function() {
expect(modules[0].description).toEqual('Describe the module here.');
});
});
describe('instance members', function() {
it('should use the correct longname for instance properties', function() {
expect(id.description).toBe('Document me.');
});
it('should use the correct longname for instance methods', function() {
expect(getId.description).toBe('Get the configuration ID.');
});
}); });
}); });
describe('module doclet', function() { describe('ES2015 module', function() {
it('should include a "description" property that contains the module description', function() { var docSet = jasmine.getDocSetFromFile('test/fixtures/moduleisconstructor2.js');
expect(modules[0].description).toEqual('Describe the module here.'); var modules = docSet.doclets.filter(function(doclet) {
return doclet.kind === 'module';
});
var classes = docSet.doclets.filter(function(doclet) {
return doclet.kind === 'class' && doclet.classdesc && doclet.description;
});
var getId = docSet.getByLongname('module:mymodule/config#getId')[0];
var id = docSet.getByLongname('module:mymodule/config#id')[0];
it('should include one doclet whose kind is "module"', function() {
expect(modules.length).toBe(1);
expect(modules[0].kind).toBe('module');
});
it('should include one complete class doclet', function() {
expect(classes.length).toBe(1);
expect(classes[0].kind).toBe('class');
});
describe('class doclet', function() {
it('should include a "description" property that contains the constructor description', function() {
expect(classes[0].description).toEqual('Create a new configuration.');
});
it('should include a "classdesc" property', function() {
expect(classes[0].classdesc).toEqual('Describe the class here.');
});
});
describe('module doclet', function() {
it('should include a "description" property that contains the module description', function() {
expect(modules[0].description).toEqual('Describe the module here.');
});
});
describe('instance members', function() {
it('should use the correct longname for instance properties', function() {
expect(id.description).toBe('Document me.');
});
it('should use the correct longname for instance methods', function() {
expect(getId.description).toBe('Get the configuration ID.');
});
}); });
}); });
}); });