From c0efef36ae3df2c9683f32e1a1c2dae32ea9f339 Mon Sep 17 00:00:00 2001 From: John Firebaugh Date: Mon, 14 Mar 2016 15:15:00 -0700 Subject: [PATCH] Combine flatten and normalize into a single step; alphabetize --- lib/parse.js | 342 ++++++++---------- test/fixture/_external-deps-included.json | 6 +- test/fixture/factory.input.js | 2 +- test/fixture/factory.output.json | 6 +- .../fixture/node_modules/external/lib/main.js | 2 +- test/fixture/node_modules/external2/index.js | 2 +- test/fixture/trailing-only.input.js | 2 +- test/fixture/trailing.input.js | 4 +- test/fixture/trailing.output.json | 4 +- 9 files changed, 161 insertions(+), 209 deletions(-) diff --git a/lib/parse.js b/lib/parse.js index f818541..39fb8fc 100644 --- a/lib/parse.js +++ b/lib/parse.js @@ -1,203 +1,177 @@ 'use strict'; var doctrine = require('doctrine'); -var extend = require('extend'); - -var synonyms = { - 'virtual': 'abstract', - 'extends': 'augments', - 'constructor': 'class', - 'const': 'constant', - 'defaultvalue': 'default', - 'desc': 'description', - 'host': 'external', - 'fileoverview': 'file', - 'overview': 'file', - 'emits': 'fires', - 'func': 'function', - 'method': 'function', - 'var': 'member', - 'arg': 'param', - 'argument': 'param', - 'prop': 'property', - 'return': 'returns', - 'exception': 'throws', - 'linkcode': 'link', - 'linkplain': 'link' -}; - -/** - * Normalizes synonymous tags to the canonical tag type listed on http://usejsdoc.org/. - * - * For example, given the input object: - * - * { tags: [ - * { title: "virtual" }, - * { title: "return", ... } - * ]} - * - * The output object will be: - * - * { tags: [ - * { title: "abstract" }, - * { title: "returns", ... } - * ]} - * - * The following synonyms are normalized: - * - * * virtual -> abstract - * * extends -> augments - * * constructor -> class - * * const -> constant - * * defaultvalue -> default - * * desc -> description - * * host -> external - * * fileoverview, overview -> file - * * emits -> fires - * * func, method -> function - * * var -> member - * * arg, argument -> param - * * prop -> property - * * return -> returns - * * exception -> throws - * * linkcode, linkplain -> link - * - * @param {Object} comment parsed comment - * @return {Object} comment with normalized properties - * @private - */ -function normalize(comment) { - return extend({}, comment, { - tags: comment.tags.map(function (tag) { - var canonical = synonyms[tag.title]; - return canonical ? extend({}, tag, { title: canonical }) : tag; - }) - }); -} - -function flattenName(result, tag) { - result[tag.title] = tag.name; -} - -function flattenDescription(result, tag) { - result[tag.title] = tag.description; -} - -function flattenTypedName(result, tag) { - result[tag.title] = { - name: tag.name - }; - - if (tag.type) { - result[tag.title].type = tag.type; - } -} var flatteners = { - 'name': flattenName, - 'function': flattenName, - 'mixin': flattenName, + 'access': function (result, tag) { + result.access = tag.access; + }, 'alias': flattenName, - 'memberof': flattenDescription, - 'version': flattenDescription, - 'since': flattenDescription, - 'copyright': flattenDescription, - 'author': flattenDescription, - 'license': flattenDescription, - 'classdesc': flattenDescription, - 'lends': flattenDescription, - 'event': flattenDescription, - 'external': flattenDescription, - 'file': flattenDescription, - 'callback': flattenDescription, - 'description': flattenDescription, - 'summary': flattenDescription, - 'deprecated': flattenDescription, - 'class': flattenTypedName, - 'constant': flattenTypedName, - 'member': flattenTypedName, - 'module': flattenTypedName, - 'namespace': flattenTypedName, - 'typedef': flattenTypedName, - 'kind': function (result, tag) { - result.kind = tag.kind; - }, - 'property': function (result, tag) { - if (!result.properties) { - result.properties = []; - } - result.properties.push(tag); - }, - 'param': function (result, tag) { - if (!result.params) { - result.params = []; - } - result.params.push(tag); - }, - 'throws': function (result, tag) { - if (!result.throws) { - result.throws = []; - } - result.throws.push(tag); - }, - 'returns': function (result, tag) { - if (!result.returns) { - result.returns = []; - } - result.returns.push(tag); - }, + 'arg': synonym('param'), + 'argument': synonym('param'), 'augments': function (result, tag) { if (!result.augments) { result.augments = []; } result.augments.push(tag); }, + 'author': flattenDescription, + 'callback': flattenDescription, + 'class': flattenTypedName, + 'classdesc': flattenDescription, + 'const': synonym('constant'), + 'constant': flattenTypedName, + 'constructor': synonym('class'), + 'copyright': flattenDescription, + 'defaultvalue': synonym('default'), + 'deprecated': flattenDescription, + 'desc': synonym('description'), + 'description': flattenDescription, + 'emits': synonym('fires'), + 'event': flattenDescription, 'example': function (result, tag) { if (!result.examples) { result.examples = []; } result.examples.push(tag); }, + 'exception': synonym('throws'), + 'extends': synonym('augments'), + 'external': flattenDescription, + 'file': flattenDescription, + 'fileoverview': synonym('file'), + 'func': synonym('function'), + 'function': flattenName, + 'global': function (result) { + result.scope = 'global'; + }, + 'host': synonym('external'), + 'inner': function (result) { + result.scope = 'inner'; + }, + 'instance': function (result) { + result.scope = 'instance'; + }, + 'kind': function (result, tag) { + result.kind = tag.kind; + }, + 'lends': flattenDescription, + 'license': flattenDescription, + 'linkcode': synonym('link'), + 'linkplain': synonym('link'), + 'member': flattenTypedName, + 'memberof': flattenDescription, + 'method': synonym('function'), + 'mixin': flattenName, + 'module': flattenTypedName, + 'name': flattenName, + 'namespace': flattenTypedName, + 'overview': synonym('file'), + 'param': function (result, tag) { + if (!result.params) { + result.params = []; + } + result.params.push(tag); + }, + 'private': function (result) { + result.access = 'private'; + }, + 'prop': synonym('property'), + 'property': function (result, tag) { + if (!result.properties) { + result.properties = []; + } + result.properties.push(tag); + }, + 'protected': function (result) { + result.access = 'protected'; + }, + 'public': function (result) { + result.access = 'public'; + }, + 'return': synonym('returns'), + 'returns': function (result, tag) { + if (!result.returns) { + result.returns = []; + } + result.returns.push(tag); + }, 'see': function (result, tag) { if (!result.sees) { result.sees = []; } result.sees.push(tag.description); }, + 'since': flattenDescription, + 'static': function (result) { + result.scope = 'static'; + }, + 'summary': flattenDescription, + 'throws': function (result, tag) { + if (!result.throws) { + result.throws = []; + } + result.throws.push(tag); + }, 'todo': function (result, tag) { if (!result.todos) { result.todos = []; } result.todos.push(tag.description); }, - 'global': function (result) { - result.scope = 'global'; - }, - 'static': function (result) { - result.scope = 'static'; - }, - 'instance': function (result) { - result.scope = 'instance'; - }, - 'inner': function (result) { - result.scope = 'inner'; - }, - 'access': function (result, tag) { - result.access = tag.access; - }, - 'public': function (result) { - result.access = 'public'; - }, - 'protected': function (result) { - result.access = 'protected'; - }, - 'private': function (result) { - result.access = 'private'; - } + 'typedef': flattenTypedName, + 'var': synonym('member'), + 'version': flattenDescription, + 'virtual': synonym('abstract') }; +function synonym(key) { + return function (result, tag) { + return flatteners[key](result, tag, key); + }; +} + +function flattenName(result, tag, key) { + result[key] = tag.name; +} + +function flattenDescription(result, tag, key) { + result[key] = tag.description; +} + +function flattenTypedName(result, tag, key) { + result[key] = { + name: tag.name + }; + + if (tag.type) { + result[key].type = tag.type; + } +} + /** - * Flattens tags in an opinionated way. + * Parse a comment with doctrine, decorate the result with file position and code + * context, handle parsing errors, and fix up various infelicities in the structure + * outputted by doctrine. + * + * The following tags are treated as synonyms for a canonical tag: + * + * * `@virtual` -> `@abstract` + * * `@extends` -> `@augments` + * * `@constructor` -> `@class` + * * `@const` -> `@constant` + * * `@defaultvalue` -> `@default` + * * `@desc` -> `@description` + * * `@host` -> `@external` + * * `@fileoverview`, `@overview` -> `@file` + * * `@emits` -> `@fires` + * * `@func`, `@method` -> `@function` + * * `@var` -> `@member` + * * `@arg`, `@argument` -> `@param` + * * `@prop` -> `@property` + * * `@return` -> `@returns` + * * `@exception` -> `@throws` + * * `@linkcode`, `@linkplain` -> `@link` * * The following tags are assumed to be singletons, and are flattened * to a top-level property on the result whose value is extracted from @@ -245,30 +219,11 @@ var flatteners = { * The assumed default value is `"public"`, so `@access public` or `@public` * tags result in no `access` property. * - * @param {Object} comment a parsed comment - * @return {Object} comment with tags flattened - * @private - */ -function flatten(comment) { - var result = extend({}, comment); - - comment.tags.forEach(function (tag) { - (flatteners[tag.title] || function () {})(result, tag); - }); - - return result; -} - -/** - * Parse a comment with doctrine, decorate the result with file position and code - * context, handle parsing errors, and fix up various infelicities in the structure - * outputted by doctrine. - * * @param {string} comment input to be parsed * @param {Object} loc location of the input * @param {Object} context code context of the input - * @return {Object} an object conforming to the - * [documentation JSON API](https://github.com/documentationjs/api-json) schema + * @return {Comment} an object conforming to the + * [documentation schema](https://github.com/documentationjs/api-json) */ function parseJSDoc(comment, loc, context) { var result = doctrine.parse(comment, { @@ -286,20 +241,17 @@ function parseJSDoc(comment, loc, context) { result.context = context; result.errors = []; - var i = 0; - while (i < result.tags.length) { - var tag = result.tags[i]; + result.tags.forEach(function (tag) { if (tag.errors) { for (var j = 0; j < tag.errors.length; j++) { result.errors.push({message: tag.errors[j]}); } - result.tags.splice(i, 1); } else { - i++; + (flatteners[tag.title] || function () {})(result, tag, tag.title); } - } + }); - return flatten(normalize(result)); + return result; } module.exports = parseJSDoc; diff --git a/test/fixture/_external-deps-included.json b/test/fixture/_external-deps-included.json index a971a49..c407cd7 100644 --- a/test/fixture/_external-deps-included.json +++ b/test/fixture/_external-deps-included.json @@ -40,7 +40,7 @@ "description": "This function returns the number one.", "tags": [ { - "title": "returns", + "title": "return", "description": "numberone", "lineNumber": 2, "type": { @@ -75,7 +75,7 @@ "errors": [], "returns": [ { - "title": "returns", + "title": "return", "description": "numberone", "lineNumber": 2, "type": { @@ -128,7 +128,7 @@ "column": 2 } }, - "code": "'use strict';\n\nvar otherDep = require('external2');\n\n/**\n * This function returns the number one.\n * @return {Number} numberone\n */\nmodule.exports = function () {\n // this returns 1\n return otherDep() - 1;\n};\n" + "code": "'use strict';\n\nvar otherDep = require('external2');\n\n/**\n * This function returns the number one.\n * @returns {Number} numberone\n */\nmodule.exports = function () {\n // this returns 1\n return otherDep() - 1;\n};\n" }, "errors": [], "returns": [ diff --git a/test/fixture/factory.input.js b/test/fixture/factory.input.js index b6bffbb..d133d75 100644 --- a/test/fixture/factory.input.js +++ b/test/fixture/factory.input.js @@ -12,7 +12,7 @@ var area = function() { /** * Sets the chart data. - * @method + * @function */ chart.data = function(_) { }; diff --git a/test/fixture/factory.output.json b/test/fixture/factory.output.json index 7c2f2d5..19b28ae 100644 --- a/test/fixture/factory.output.json +++ b/test/fixture/factory.output.json @@ -33,7 +33,7 @@ "column": 2 } }, - "code": "/**\n * an area chart generator\n * @returns {area} chart\n */\nvar area = function() {\n\n /**\n * @class area\n */\n var chart = function(selection) {\n };\n\n /**\n * Sets the chart data.\n * @method\n */\n chart.data = function(_) {\n };\n\n return chart;\n};\n" + "code": "/**\n * an area chart generator\n * @returns {area} chart\n */\nvar area = function() {\n\n /**\n * @class area\n */\n var chart = function(selection) {\n };\n\n /**\n * Sets the chart data.\n * @function\n */\n chart.data = function(_) {\n };\n\n return chart;\n};\n" }, "errors": [], "returns": [ @@ -89,7 +89,7 @@ "column": 4 } }, - "code": "/**\n * an area chart generator\n * @returns {area} chart\n */\nvar area = function() {\n\n /**\n * @class area\n */\n var chart = function(selection) {\n };\n\n /**\n * Sets the chart data.\n * @method\n */\n chart.data = function(_) {\n };\n\n return chart;\n};\n" + "code": "/**\n * an area chart generator\n * @returns {area} chart\n */\nvar area = function() {\n\n /**\n * @class area\n */\n var chart = function(selection) {\n };\n\n /**\n * Sets the chart data.\n * @function\n */\n chart.data = function(_) {\n };\n\n return chart;\n};\n" }, "errors": [], "class": { @@ -143,7 +143,7 @@ "column": 4 } }, - "code": "/**\n * an area chart generator\n * @returns {area} chart\n */\nvar area = function() {\n\n /**\n * @class area\n */\n var chart = function(selection) {\n };\n\n /**\n * Sets the chart data.\n * @method\n */\n chart.data = function(_) {\n };\n\n return chart;\n};\n" + "code": "/**\n * an area chart generator\n * @returns {area} chart\n */\nvar area = function() {\n\n /**\n * @class area\n */\n var chart = function(selection) {\n };\n\n /**\n * Sets the chart data.\n * @function\n */\n chart.data = function(_) {\n };\n\n return chart;\n};\n" }, "errors": [ { diff --git a/test/fixture/node_modules/external/lib/main.js b/test/fixture/node_modules/external/lib/main.js index 101f94b..ccac66c 100644 --- a/test/fixture/node_modules/external/lib/main.js +++ b/test/fixture/node_modules/external/lib/main.js @@ -4,7 +4,7 @@ var otherDep = require('external2'); /** * This function returns the number one. - * @return {Number} numberone + * @returns {Number} numberone */ module.exports = function () { // this returns 1 diff --git a/test/fixture/node_modules/external2/index.js b/test/fixture/node_modules/external2/index.js index 6f53a38..140136f 100644 --- a/test/fixture/node_modules/external2/index.js +++ b/test/fixture/node_modules/external2/index.js @@ -1,6 +1,6 @@ /** * This function returns the number one. - * @return {Number} numberone + * @returns {Number} numberone */ module.exports = function () { // this returns 1 diff --git a/test/fixture/trailing-only.input.js b/test/fixture/trailing-only.input.js index 1e1a65e..b2571bd 100644 --- a/test/fixture/trailing-only.input.js +++ b/test/fixture/trailing-only.input.js @@ -3,5 +3,5 @@ function fooBaz() { } /** * this is a type - * @return {number} nothing + * @returns {number} nothing */ diff --git a/test/fixture/trailing.input.js b/test/fixture/trailing.input.js index 65adb00..5e11b97 100644 --- a/test/fixture/trailing.input.js +++ b/test/fixture/trailing.input.js @@ -1,13 +1,13 @@ /** * ONE - * @return {number} something + * @returns {number} something */ function fooBar() { return 1; } /** * TWO - * @return {number} something + * @returns {number} something */ function fooBaz() { return 2; diff --git a/test/fixture/trailing.output.json b/test/fixture/trailing.output.json index c41bd2d..30a14d8 100644 --- a/test/fixture/trailing.output.json +++ b/test/fixture/trailing.output.json @@ -33,7 +33,7 @@ "column": 1 } }, - "code": "/**\n * ONE\n * @return {number} something\n */\nfunction fooBar() {\n return 1;\n}\n/**\n * TWO\n * @return {number} something\n */\nfunction fooBaz() {\n return 2;\n}\n/**\n * this is a type\n * @class Something\n */\n" + "code": "/**\n * ONE\n * @returns {number} something\n */\nfunction fooBar() {\n return 1;\n}\n/**\n * TWO\n * @returns {number} something\n */\nfunction fooBaz() {\n return 2;\n}\n/**\n * this is a type\n * @class Something\n */\n" }, "errors": [], "returns": [ @@ -91,7 +91,7 @@ "column": 1 } }, - "code": "/**\n * ONE\n * @return {number} something\n */\nfunction fooBar() {\n return 1;\n}\n/**\n * TWO\n * @return {number} something\n */\nfunction fooBaz() {\n return 2;\n}\n/**\n * this is a type\n * @class Something\n */\n" + "code": "/**\n * ONE\n * @returns {number} something\n */\nfunction fooBar() {\n return 1;\n}\n/**\n * TWO\n * @returns {number} something\n */\nfunction fooBaz() {\n return 2;\n}\n/**\n * this is a type\n * @class Something\n */\n" }, "errors": [], "returns": [