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) {
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
else {
str = nodeToValue(parent.id);
str = parent.id ? nodeToValue(parent.id) : '';
}
if (node.kind !== 'constructor') {
if (str) {
str += node.static ? name.SCOPE.PUNC.STATIC : name.SCOPE.PUNC.INSTANCE;
}
str += nodeToValue(node.key);
}
break;

View File

@ -7,6 +7,9 @@ var escape = require('escape-string-regexp');
var jsdoc = {
doclet: require('jsdoc/doclet'),
name: require('jsdoc/name'),
src: {
syntax: require('jsdoc/src/syntax')
},
util: {
logger: require('jsdoc/util/logger')
}
@ -16,6 +19,7 @@ var util = require('util');
var currentModule = null;
var SCOPE_NAMES = jsdoc.name.SCOPE.NAMES;
var SCOPE_PUNC = jsdoc.name.SCOPE.PUNC;
var Syntax = jsdoc.src.syntax.Syntax;
var unresolvedName = /^((?:module.)?exports|this)(\.|$)/;
function CurrentModule(doclet) {
@ -90,10 +94,22 @@ function setCurrentModule(doclet) {
function setModuleScopeMemberOf(doclet) {
// handle module symbols that are _not_ assigned to module.exports
if (currentModule && currentModule.longname !== doclet.name) {
// if we don't already know the scope, it must be an inner member
if (!doclet.scope) {
// 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
// the current module

View File

@ -1,4 +1,16 @@
/** @module */
/** @module test */
/** 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.
@module mymodule/config
*/
* Describe the module here.
* @module mymodule/config
*/
/**
Create a new configuration.
@classdesc Describe the class here.
@class
@alias module:mymodule/config
@param {string} id
*/
* Create a new configuration.
*
* @classdesc Describe the class here.
* @class
* @alias module:mymodule/config
* @param {string} id
*/
function Config(id) {
/** Document me. */
this.id = id;
}
/**
* 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';
describe('anonymous class', function() {
it('should not crash JSDoc', function() {
function getDocs() {
return jasmine.getDocSetFromFile('test/fixtures/anonymousclass.js');
}
var docSet = jasmine.getDocSetFromFile('test/fixtures/anonymousclass.js');
var klass = docSet.getByLongname('module:test')[2];
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,6 +1,7 @@
'use strict';
describe('module that exports a constructor', function() {
describe('pre-ES2015 module', function() {
var docSet = jasmine.getDocSetFromFile('test/fixtures/moduleisconstructor.js');
var modules = docSet.doclets.filter(function(doclet) {
return doclet.kind === 'module';
@ -8,6 +9,8 @@ describe('module that exports a constructor', function() {
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() {
expect(modules.length).toBe(1);
@ -24,7 +27,7 @@ describe('module that exports a constructor', function() {
expect(classes[0].description).toEqual('Create a new configuration.');
});
it('should include a "class-description" property', function() {
it('should include a "classdesc" property', function() {
expect(classes[0].classdesc).toEqual('Describe the class here.');
});
});
@ -34,4 +37,63 @@ describe('module that exports a constructor', 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('ES2015 module', function() {
var docSet = jasmine.getDocSetFromFile('test/fixtures/moduleisconstructor2.js');
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.');
});
});
});
});