diff --git a/lib/jsdoc/doclet.js b/lib/jsdoc/doclet.js index c86b119f..431cd135 100644 --- a/lib/jsdoc/doclet.js +++ b/lib/jsdoc/doclet.js @@ -58,7 +58,7 @@ function codeToKind(code) { kind = 'function'; } } - else if (code.type === Syntax.ClassDeclaration) { + else if (code.type === Syntax.ClassDeclaration || code.type === Syntax.ClassExpression) { kind = 'class'; } else if ( code.node && code.node.parent && isFunction(code.node.parent) ) { @@ -123,9 +123,14 @@ function toTags(docletSrc) { } function fixDescription(docletSrc, meta) { + var isClass; + if (!/^\s*@/.test(docletSrc) && docletSrc.replace(/\s/g, '').length) { - docletSrc = (meta.code && meta.code.type === Syntax.ClassDeclaration ? '@classdesc' : - '@description') + ' ' + docletSrc; + isClass = meta.code && + (meta.code.type === Syntax.ClassDeclaration || + meta.code.type === Syntax.ClassExpression); + + docletSrc = (isClass ? '@classdesc' : '@description') + ' ' + docletSrc; } return docletSrc; } diff --git a/lib/jsdoc/src/astnode.js b/lib/jsdoc/src/astnode.js index bcc4dde7..03bcacdb 100644 --- a/lib/jsdoc/src/astnode.js +++ b/lib/jsdoc/src/astnode.js @@ -106,6 +106,7 @@ var addNodeProperties = exports.addNodeProperties = function(node) { // TODO: docs var nodeToValue = exports.nodeToValue = function(node) { + var parent; var str; var tempObject; @@ -157,7 +158,13 @@ var nodeToValue = exports.nodeToValue = function(node) { break; case Syntax.MethodDefinition: - str = nodeToValue(node.parent.parent.id); + parent = node.parent.parent; + // for class expressions, we want the name of the variable the class is assigned to + if (parent.type === Syntax.ClassExpression) { + parent = parent.parent; + } + + str = nodeToValue(parent.id); if (node.kind !== 'constructor') { str += node.static ? name.SCOPE.PUNC.STATIC : name.SCOPE.PUNC.INSTANCE; str += nodeToValue(node.key); @@ -264,7 +271,7 @@ var getInfo = exports.getInfo = function(node) { info.paramnames = getParamNames(node.right); break; - // like: "class Foo { }" + // like: "class Foo {}" case Syntax.ClassDeclaration: info.node = node; info.name = nodeToValue(node.id); diff --git a/lib/jsdoc/src/handlers.js b/lib/jsdoc/src/handlers.js index 5a76625e..2a5ab5a6 100644 --- a/lib/jsdoc/src/handlers.js +++ b/lib/jsdoc/src/handlers.js @@ -282,7 +282,7 @@ function newSymbolDoclet(parser, docletSrc, e) { } // handle cases where the doclet kind is auto-detected from the node type - if (e.code.kind) { + if (e.code.kind && newDoclet.kind === 'member') { newDoclet.kind = e.code.kind; } diff --git a/lib/jsdoc/src/visitor.js b/lib/jsdoc/src/visitor.js index 01fd4564..29828c0a 100644 --- a/lib/jsdoc/src/visitor.js +++ b/lib/jsdoc/src/visitor.js @@ -458,6 +458,10 @@ Visitor.prototype.makeSymbolFoundEvent = function(node, parser, filename) { // like: class foo {} case Syntax.ClassDeclaration: + // falls through + + // like: let MyClass = class {} + case Syntax.ClassExpression: e = new SymbolFound(node, filename, extras); trackVars(parser, node, e); @@ -564,7 +568,6 @@ Visitor.prototype.makeSymbolFoundEvent = function(node, parser, filename) { // for now, log a warning for all ES6 nodes, since we don't do anything useful with them case Syntax.ArrowFunctionExpression: - case Syntax.ClassExpression: case Syntax.ExportAllDeclaration: case Syntax.ExportDefaultDeclaration: case Syntax.ExportNamedDeclaration: diff --git a/test/fixtures/classtag2.js b/test/fixtures/classtag2.js index 69ec2a23..ee3e5c8f 100644 --- a/test/fixtures/classtag2.js +++ b/test/fixtures/classtag2.js @@ -1,5 +1,15 @@ /** - Describe the Subscription class here. + * Describe the Subscription class here. */ class Subscription { + /** Force the subscription to expire. */ + expire() {} +} + +/** + * Describe the Subscriber class here. + */ +const Subscriber = class Foo { + /** Check whether the subscriber has a callback. */ + hasCallback() {} } diff --git a/test/fixtures/constanttag2.js b/test/fixtures/constanttag2.js new file mode 100644 index 00000000..d37cf48b --- /dev/null +++ b/test/fixtures/constanttag2.js @@ -0,0 +1,4 @@ +/** + * ES 2015 class assigned to a const + */ +const Foo = class bar {} diff --git a/test/specs/documentation/const.js b/test/specs/documentation/const.js index 3e2c89cd..441bc49a 100644 --- a/test/specs/documentation/const.js +++ b/test/specs/documentation/const.js @@ -7,4 +7,15 @@ describe('const declarations', function() { expect(myPocket.kind).toBe('constant'); }); + + if (jasmine.jsParser !== 'rhino') { + describe('ES 2015 only', function() { + it('should not override kind="class" when a const is autodetected', function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/constanttag2.js'); + var foo = docSet.getByLongname('Foo')[0]; + + expect(foo.kind).toBe('class'); + }); + }); + } }); diff --git a/test/specs/tags/classtag.js b/test/specs/tags/classtag.js index 54b2bbc4..83eb011d 100644 --- a/test/specs/tags/classtag.js +++ b/test/specs/tags/classtag.js @@ -18,12 +18,33 @@ describe('@class tag', function() { 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]; it('When a symbol is a class declaration, the doclet does not require the @class tag', function() { expect(subscription.kind).toBe('class'); - expect(subscription.longname).toBe('Subscription'); + expect(subscription.name).toBe('Subscription'); expect(subscription.classdesc).toBe('Describe the Subscription class here.'); }); + + 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, its members get the correct longname and memberof', function() { + expect(hasCallback.kind).toBe('function'); + expect(hasCallback.name).toBe('hasCallback'); + expect(hasCallback.memberof).toBe('Subscriber'); + }); }); } });