correctly document the constructor of a class exported from an ES2015 module (#1272)

This commit is contained in:
Jeff Williams 2017-07-03 15:34:25 -07:00
parent 5660c94adf
commit 6cb96f5164
5 changed files with 84 additions and 58 deletions

View File

@ -206,6 +206,16 @@ var nodeToValue = exports.nodeToValue = function(node) {
parent.parent.type === Syntax.ExportDefaultDeclaration) {
str = 'module.exports';
}
// for the constructor of a module's named export, use the name of the export
// declaration
else if (node.kind === 'constructor' && parent.parent &&
parent.parent.type === Syntax.ExportNamedDeclaration) {
str = nodeToValue(parent.parent);
}
// for other constructors, use the name of the parent class
else if (node.kind === 'constructor') {
str = nodeToValue(parent);
}
// 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) {

View File

@ -296,7 +296,18 @@ function makeDefaultParamFinisher(parser) {
function makeConstructorFinisher(parser) {
return function(e) {
var doclet = e.doclet;
var parentDoclet = parser._getDocletById(e.code.node.parent.parent.nodeId);
var parentDoclet;
// for class declarations that are named module exports, the node that's documented is the
// ExportNamedDeclaration, not the ClassDeclaration
if (e.code.node.parent.parent.parent &&
e.code.node.parent.parent.parent.type === Syntax.ExportNamedDeclaration) {
parentDoclet = parser._getDocletById(e.code.node.parent.parent.parent.nodeId);
}
// otherwise, we want the ClassDeclaration
else {
parentDoclet = parser._getDocletById(e.code.node.parent.parent.nodeId);
}
if (!doclet || !parentDoclet || parentDoclet.undocumented) {
return;

View File

@ -5,6 +5,8 @@
/** Test class. */
export class Foo {
/** Test class constructor. */
constructor() {}
/** Test method. */
testMethod() {}

View File

@ -14,63 +14,61 @@ describe('@class tag', function() {
expect(news.longname).toBe('NewsSource');
});
if (jasmine.jsParser !== 'rhino') {
describe('ES 2015 classes', function() {
var docSet2 = jasmine.getDocSetFromFile('test/fixtures/classtag2.js');
var subscription = docSet2.getByLongname('Subscription')[0];
var expire = docSet2.getByLongname('Subscription#expire')[0];
var subscriber = docSet2.getByLongname('Subscriber')[0];
var hasCallback = docSet2.getByLongname('Subscriber#hasCallback')[0];
var expiringSubscription = docSet2.getByLongname('subclasses.ExpiringSubscription')[0];
var invalidSubscriptionFoo = docSet2.getByLongname('subclasses.InvalidSubscription#foo')[0];
describe('ES 2015 classes', function() {
var docSet2 = jasmine.getDocSetFromFile('test/fixtures/classtag2.js');
var subscription = docSet2.getByLongname('Subscription')[0];
var expire = docSet2.getByLongname('Subscription#expire')[0];
var subscriber = docSet2.getByLongname('Subscriber')[0];
var hasCallback = docSet2.getByLongname('Subscriber#hasCallback')[0];
var expiringSubscription = docSet2.getByLongname('subclasses.ExpiringSubscription')[0];
var invalidSubscriptionFoo = docSet2.getByLongname('subclasses.InvalidSubscription#foo')[0];
it('When a symbol is a class declaration, the doclet does not require the @class tag', function() {
expect(subscription.kind).toBe('class');
expect(subscription.name).toBe('Subscription');
expect(subscription.classdesc).toBe('Describe the Subscription class here.');
});
it('When a symbol is a class declaration, the constructor info is merged into the doclet for the symbol', function() {
expect(subscription.description).toBe('Describe the constructor here.');
expect(subscription.params.length).toBe(1);
expect(subscription.params[0].name).toBe('name');
});
it('When a symbol is a class declaration, its members get the correct longname and memberof', function() {
expect(expire.kind).toBe('function');
expect(expire.name).toBe('expire');
expect(expire.memberof).toBe('Subscription');
});
it('When a symbol is a class expression, the doclet does not require the @class tag', function() {
expect(subscriber.kind).toBe('class');
expect(subscriber.name).toBe('Subscriber');
expect(subscriber.classdesc).toBe('Describe the Subscriber class here.');
});
it('When a symbol is a class expression, the constructor info is merged into the doclet for the symbol', function() {
expect(subscriber.description).toBe('Describe the constructor here.');
expect(subscriber.params.length).toBe(1);
expect(subscriber.params[0].name).toBe('name');
});
it('When a symbol is a class expression, its members get the correct longname and memberof', function() {
expect(hasCallback.kind).toBe('function');
expect(hasCallback.name).toBe('hasCallback');
expect(hasCallback.memberof).toBe('Subscriber');
});
it('When a class expression is assigned to an object property, it is parsed correctly', function() {
expect(expiringSubscription.kind).toBe('class');
expect(expiringSubscription.name).toBe('ExpiringSubscription');
expect(expiringSubscription.params[0].name).toBe('name');
});
it('When a class is a static memberof something else, the class\' instance methods have the correct scope', function() {
expect(invalidSubscriptionFoo.kind).toBe('function');
expect(invalidSubscriptionFoo.name).toBe('foo');
expect(invalidSubscriptionFoo.scope).toBe('instance');
});
it('When a symbol is a class declaration, the doclet does not require the @class tag', function() {
expect(subscription.kind).toBe('class');
expect(subscription.name).toBe('Subscription');
expect(subscription.classdesc).toBe('Describe the Subscription class here.');
});
}
it('When a symbol is a class declaration, the constructor info is merged into the doclet for the symbol', function() {
expect(subscription.description).toBe('Describe the constructor here.');
expect(subscription.params.length).toBe(1);
expect(subscription.params[0].name).toBe('name');
});
it('When a symbol is a class declaration, its members get the correct longname and memberof', function() {
expect(expire.kind).toBe('function');
expect(expire.name).toBe('expire');
expect(expire.memberof).toBe('Subscription');
});
it('When a symbol is a class expression, the doclet does not require the @class tag', function() {
expect(subscriber.kind).toBe('class');
expect(subscriber.name).toBe('Subscriber');
expect(subscriber.classdesc).toBe('Describe the Subscriber class here.');
});
it('When a symbol is a class expression, the constructor info is merged into the doclet for the symbol', function() {
expect(subscriber.description).toBe('Describe the constructor here.');
expect(subscriber.params.length).toBe(1);
expect(subscriber.params[0].name).toBe('name');
});
it('When a symbol is a class expression, its members get the correct longname and memberof', function() {
expect(hasCallback.kind).toBe('function');
expect(hasCallback.name).toBe('hasCallback');
expect(hasCallback.memberof).toBe('Subscriber');
});
it('When a class expression is assigned to an object property, it is parsed correctly', function() {
expect(expiringSubscription.kind).toBe('class');
expect(expiringSubscription.name).toBe('ExpiringSubscription');
expect(expiringSubscription.params[0].name).toBe('name');
});
it('When a class is a static memberof something else, the class\' instance methods have the correct scope', function() {
expect(invalidSubscriptionFoo.kind).toBe('function');
expect(invalidSubscriptionFoo.name).toBe('foo');
expect(invalidSubscriptionFoo.scope).toBe('instance');
});
});
});

View File

@ -173,6 +173,11 @@ describe('@module tag', function() {
expect(foo).toBeDefined();
});
it('should merge the doclet for the constructor with the doclet for the ' +
'class', function() {
expect(foo.description).toBe('Test class constructor.');
});
it('should identify the correct scope for the exported class\'s methods', function() {
expect(testMethod).toBeDefined();
});