From 300be03e054b05729401613e5047c2b42f8e0132 Mon Sep 17 00:00:00 2001 From: Ryan Hendrickson Date: Sun, 3 Sep 2017 23:16:14 -0400 Subject: [PATCH] feat: implement @hideconstructor Per usejsdoc.org, this tag goes either in a comment attached to a function that has declared that function to be a class, or in a comment on an ES6 class constructor. --- __tests__/lib/infer/params.js | 24 ++++++++++++++++++++++++ __tests__/lib/parse.js | 8 ++++++++ declarations/comment.js | 2 ++ src/infer/params.js | 13 +++++++++++-- src/parse.js | 1 + src/parsers/javascript.js | 34 +++++++++++++++++++++++++--------- 6 files changed, 71 insertions(+), 11 deletions(-) diff --git a/__tests__/lib/infer/params.js b/__tests__/lib/infer/params.js index 01eba07d3..66ea1b209 100644 --- a/__tests__/lib/infer/params.js +++ b/__tests__/lib/infer/params.js @@ -163,4 +163,28 @@ test('inferParams', function() { expect( evaluate('/** Test */ export default function f(x) {}').params ).toEqual([{ lineNumber: 1, name: 'x', title: 'param' }]); + + expect( + evaluate(function() { + /** + * @class + * @hideconstructor + */ + function SomeClass(foo, bar) {} + }).params + ).toEqual([]); + + expect( + evaluate(` + /** + * Test + */ + class SomeClass { + /** + * @hideconstructor + */ + constructor(foo, bar) {} + } + `).params + ).toEqual([]); }); diff --git a/__tests__/lib/parse.js b/__tests__/lib/parse.js index 2349ec8ff..f8c7acb7c 100644 --- a/__tests__/lib/parse.js +++ b/__tests__/lib/parse.js @@ -444,6 +444,14 @@ test('parse - @global', function() { ).toBe('global'); }); +test('parse - @hideconstructor', function() { + expect( + evaluate(function() { + /** @hideconstructor */ + })[0].hideconstructor + ).toBe(true); +}); + test('parse - @host', function() {}); test('parse - @ignore', function() { diff --git a/declarations/comment.js b/declarations/comment.js index 17d2c3e63..9f0ddab56 100644 --- a/declarations/comment.js +++ b/declarations/comment.js @@ -84,6 +84,7 @@ type Comment = { classdesc?: Remark, members: CommentMembers, + constructorComment?: Comment, name?: string, kind?: Kind, @@ -100,6 +101,7 @@ type Comment = { since?: string, lends?: string, override?: boolean, + hideconstructor?: true, type?: DoctrineType, diff --git a/src/infer/params.js b/src/infer/params.js index cb9791000..c0503a44c 100644 --- a/src/infer/params.js +++ b/src/infer/params.js @@ -23,7 +23,10 @@ function inferParams(comment: Comment) { // If this is an ES6 class with a constructor method, infer // parameters from that constructor method. - if (t.isClassDeclaration(path)) { + if ( + t.isClassDeclaration(path) && + !(comment.constructorComment && comment.constructorComment.hideconstructor) + ) { let constructor = path.node.body.body.find(item => { // https://github.com/babel/babylon/blob/master/ast/spec.md#classbody return t.isClassMethod(item) && item.kind === 'constructor'; @@ -37,6 +40,10 @@ function inferParams(comment: Comment) { return comment; } + if (comment.kind === 'class' && comment.hideconstructor) { + return comment; + } + return inferAndCombineParams(path.node.params, comment); } @@ -277,7 +284,9 @@ function mergeTopNodes(inferred, explicit) { var errors = explicitTagsWithoutInference.map(tag => { return { - message: `An explicit parameter named ${tag.name || ''} was specified but didn't match ` + + message: + `An explicit parameter named ${tag.name || + ''} was specified but didn't match ` + `inferred information ${Array.from(inferredNames).join(', ')}`, commentLineNumber: tag.lineNumber }; diff --git a/src/parse.js b/src/parse.js index c1cef57b7..d21a69ef3 100644 --- a/src/parse.js +++ b/src/parse.js @@ -168,6 +168,7 @@ var flatteners = { global(result) { result.scope = 'global'; }, + hideconstructor: flattenBoolean, host: synonym('external'), ignore: flattenBoolean, implements: todo, diff --git a/src/parsers/javascript.js b/src/parsers/javascript.js index b6ebfd584..900e153b6 100644 --- a/src/parsers/javascript.js +++ b/src/parsers/javascript.js @@ -36,9 +36,10 @@ function leftPad(str, width) { */ function parseJavaScript(data: Object, config: DocumentationConfig) { var visited = new Set(); + const commentsByNode = new Map(); var ast = parseToAst(data.source); - var addComment = _addComment.bind(null, visited); + var addComment = _addComment.bind(null, visited, commentsByNode); return _.flatMap( config.documentExported @@ -54,6 +55,7 @@ function parseJavaScript(data: Object, config: DocumentationConfig) { function _addComment( visited, + commentsByNode, data, commentValue, commentLoc, @@ -89,19 +91,33 @@ function _addComment( value: path }); - // #689 - if (t.isClassMethod(path) && path.node.kind === 'constructor') { - debuglog( - 'A constructor was documented explicitly: document along with the class instead' - ); - } - if (path.parentPath && path.parentPath.node) { var parentNode = path.parentPath.node; context.code = data.source.substring(parentNode.start, parentNode.end); } } - return parse(commentValue, commentLoc, context); + const comment = parse(commentValue, commentLoc, context); + if (includeContext) { + commentsByNode.set(path.node, comment); + + if (t.isClassMethod(path) && path.node.kind === 'constructor') { + // #689 + if (!comment.hideconstructor) { + debuglog( + 'A constructor was documented explicitly: document along with the class instead' + ); + } + + const parentComment = commentsByNode.get( + path.parentPath.parentPath.node + ); + if (parentComment) { + parentComment.constructorComment = comment; + return; + } + } + } + return comment; } }