Combine flatten and normalize into a single step; alphabetize

This commit is contained in:
John Firebaugh 2016-03-14 15:15:00 -07:00
parent 47eb1eaf26
commit c0efef36ae
9 changed files with 161 additions and 209 deletions

View File

@ -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;

View File

@ -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": [

View File

@ -12,7 +12,7 @@ var area = function() {
/**
* Sets the chart data.
* @method
* @function
*/
chart.data = function(_) {
};

View File

@ -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": [
{

View File

@ -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

View File

@ -1,6 +1,6 @@
/**
* This function returns the number one.
* @return {Number} numberone
* @returns {Number} numberone
*/
module.exports = function () {
// this returns 1

View File

@ -3,5 +3,5 @@ function fooBaz() {
}
/**
* this is a type
* @return {number} nothing
* @returns {number} nothing
*/

View File

@ -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;

View File

@ -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": [