diff --git a/lib/parsers/javascript.js b/lib/parsers/javascript.js index b821eff..0ce9120 100644 --- a/lib/parsers/javascript.js +++ b/lib/parsers/javascript.js @@ -84,42 +84,56 @@ module.exports = function (data) { var code = commentShebang(data.source), ast = espree.parse(code, espreeConfig); - types.visit(ast, { - visitNode: function (path) { - /** - * Parse a comment with doctrine and decorate the result with file position and code context. - * - * @param {Object} comment the current state of the parsed JSDoc comment - * @return {undefined} this emits data - */ - function parseComment(comment) { - var context = { - loc: extend({}, path.value.loc), - file: data.file - }; + var visited = {}; - // This is non-enumerable so that it doesn't get stringified in output; e.g. by the - // documentation binary. - Object.defineProperty(context, 'ast', { - enumerable: false, - value: path - }); + function walkComments(ast, type) { + types.visit(ast, { + visitNode: function (path) { + /** + * Parse a comment with doctrine and decorate the result with file position and code context. + * + * @param {Object} comment the current state of the parsed JSDoc comment + * @return {undefined} this emits data + */ + function parseComment(comment) { + var context = { + loc: extend({}, path.value.loc), + file: data.file + }; + var key = JSON.stringify(comment.loc); - if (path.parent && path.parent.node) { - context.code = code.substring - .apply(code, path.parent.node.range); + if (visited[key]) { + return; + } + + visited[key] = true; + + // This is non-enumerable so that it doesn't get stringified in output; e.g. by the + // documentation binary. + Object.defineProperty(context, 'ast', { + enumerable: false, + value: path + }); + + if (path.parent && path.parent.node) { + context.code = code.substring + .apply(code, path.parent.node.range); + } + + results.push(parse(comment.value, comment.loc, context)); } - results.push(parse(comment.value, comment.loc, context)); + (path.value[type] || []) + .filter(isJSDocComment) + .forEach(parseComment); + + this.traverse(path); } + }); + } - (path.value.leadingComments || []) - .filter(isJSDocComment) - .forEach(parseComment); - - this.traverse(path); - } - }); + walkComments(ast, 'leadingComments'); + walkComments(ast, 'trailingComments'); return results; }; diff --git a/test/fixture/trailing.input.js b/test/fixture/trailing.input.js new file mode 100644 index 0000000..65adb00 --- /dev/null +++ b/test/fixture/trailing.input.js @@ -0,0 +1,18 @@ +/** + * ONE + * @return {number} something + */ +function fooBar() { + return 1; +} +/** + * TWO + * @return {number} something + */ +function fooBaz() { + return 2; +} +/** + * this is a type + * @class Something + */ diff --git a/test/fixture/trailing.output.custom.md b/test/fixture/trailing.output.custom.md new file mode 100644 index 0000000..9174207 --- /dev/null +++ b/test/fixture/trailing.output.custom.md @@ -0,0 +1,25 @@ +# Something + +this is a type + + + +# fooBar + +ONE + + +Returns **number** something + + + + +# fooBaz + +TWO + + +Returns **number** something + + + diff --git a/test/fixture/trailing.output.json b/test/fixture/trailing.output.json new file mode 100644 index 0000000..bd19658 --- /dev/null +++ b/test/fixture/trailing.output.json @@ -0,0 +1,169 @@ +[ + { + "description": "this is a type", + "tags": [ + { + "title": "class", + "description": null, + "lineNumber": 2, + "type": null, + "name": "Something" + } + ], + "loc": { + "start": { + "line": 15, + "column": 0 + }, + "end": { + "line": 18, + "column": 3 + } + }, + "context": { + "loc": { + "start": { + "line": 12, + "column": 0 + }, + "end": { + "line": 14, + "column": 1 + } + }, + "code": "function fooBar() {\n return 1;\n}\n/**\n * TWO\n * @return {number} something\n */\nfunction fooBaz() {\n return 2;\n}" + }, + "errors": [], + "class": { + "name": "Something" + }, + "name": "Something", + "kind": "class", + "members": { + "instance": [], + "static": [] + }, + "events": [], + "path": [ + "Something" + ] + }, + { + "description": "ONE", + "tags": [ + { + "title": "returns", + "description": "something", + "lineNumber": 2, + "type": { + "type": "NameExpression", + "name": "number" + } + } + ], + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 4, + "column": 3 + } + }, + "context": { + "loc": { + "start": { + "line": 5, + "column": 0 + }, + "end": { + "line": 7, + "column": 1 + } + }, + "code": "function fooBar() {\n return 1;\n}\n/**\n * TWO\n * @return {number} something\n */\nfunction fooBaz() {\n return 2;\n}" + }, + "errors": [], + "returns": [ + { + "title": "returns", + "description": "something", + "lineNumber": 2, + "type": { + "type": "NameExpression", + "name": "number" + } + } + ], + "name": "fooBar", + "kind": "function", + "members": { + "instance": [], + "static": [] + }, + "events": [], + "path": [ + "fooBar" + ] + }, + { + "description": "TWO", + "tags": [ + { + "title": "returns", + "description": "something", + "lineNumber": 2, + "type": { + "type": "NameExpression", + "name": "number" + } + } + ], + "loc": { + "start": { + "line": 8, + "column": 0 + }, + "end": { + "line": 11, + "column": 3 + } + }, + "context": { + "loc": { + "start": { + "line": 12, + "column": 0 + }, + "end": { + "line": 14, + "column": 1 + } + }, + "code": "function fooBar() {\n return 1;\n}\n/**\n * TWO\n * @return {number} something\n */\nfunction fooBaz() {\n return 2;\n}" + }, + "errors": [], + "returns": [ + { + "title": "returns", + "description": "something", + "lineNumber": 2, + "type": { + "type": "NameExpression", + "name": "number" + } + } + ], + "name": "fooBaz", + "kind": "function", + "members": { + "instance": [], + "static": [] + }, + "events": [], + "path": [ + "fooBaz" + ] + } +] \ No newline at end of file diff --git a/test/fixture/trailing.output.md b/test/fixture/trailing.output.md new file mode 100644 index 0000000..9174207 --- /dev/null +++ b/test/fixture/trailing.output.md @@ -0,0 +1,25 @@ +# Something + +this is a type + + + +# fooBar + +ONE + + +Returns **number** something + + + + +# fooBaz + +TWO + + +Returns **number** something + + + diff --git a/test/fixture/trailing.output.md.json b/test/fixture/trailing.output.md.json new file mode 100644 index 0000000..1d49eed --- /dev/null +++ b/test/fixture/trailing.output.md.json @@ -0,0 +1,334 @@ +{ + "type": "root", + "children": [ + { + "type": "root", + "children": [ + { + "depth": 1, + "type": "heading", + "children": [ + { + "type": "text", + "value": "Something" + } + ] + }, + { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "this is a type", + "position": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 15 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 15 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 15 + } + } + } + ] + }, + { + "type": "root", + "children": [ + { + "depth": 1, + "type": "heading", + "children": [ + { + "type": "text", + "value": "fooBar" + } + ] + }, + { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "ONE", + "position": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 4 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 4 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 4 + } + } + }, + { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "Returns " + }, + { + "type": "strong", + "children": [ + { + "type": "text", + "value": "number" + } + ] + }, + { + "type": "text", + "value": " " + }, + { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "something", + "position": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 10 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 10 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 10 + } + } + } + ] + } + ] + } + ] + }, + { + "type": "root", + "children": [ + { + "depth": 1, + "type": "heading", + "children": [ + { + "type": "text", + "value": "fooBaz" + } + ] + }, + { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "TWO", + "position": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 4 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 4 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 4 + } + } + }, + { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "Returns " + }, + { + "type": "strong", + "children": [ + { + "type": "text", + "value": "number" + } + ] + }, + { + "type": "text", + "value": " " + }, + { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": "something", + "position": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 10 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 10 + }, + "indent": [] + } + } + ], + "position": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 10 + } + } + } + ] + } + ] + } + ] + } + ] +} \ No newline at end of file