diff --git a/declarations/comment.js b/declarations/comment.js index a876bd6e0..1e12d0c4c 100644 --- a/declarations/comment.js +++ b/declarations/comment.js @@ -58,7 +58,21 @@ declare type CommentContextGitHub = { url: string }; -declare type CommentTag = { +declare type CommentTagBase = { + title: string +}; + +declare type CommentTag = CommentTagBase & { + name?: string, + title: string, + description?: Object, + default?: any, + lineNumber?: number, + type?: DoctrineType, + properties?: Array +}; + +declare type CommentTagNamed = CommentTag & { name?: string, title: string, description?: Object, @@ -88,18 +102,19 @@ declare type Remark = { declare type Access = 'private' | 'public' | 'protected'; declare type Scope = 'instance' | 'static' | 'inner' | 'global'; -declare type Kind = 'class' | - 'constant' | - 'event' | - 'external' | - 'file' | - 'function' | - 'member' | - 'mixin' | - 'module' | - 'namespace' | - 'typedef' | - 'interface'; +declare type Kind = + | 'class' + | 'constant' + | 'event' + | 'external' + | 'file' + | 'function' + | 'member' + | 'mixin' + | 'module' + | 'namespace' + | 'typedef' + | 'interface'; declare type Comment = { errors: Array, @@ -152,4 +167,4 @@ declare type ReducedComment = { name: string, kind: ?Kind, scope?: ?Scope -} +}; diff --git a/index.js b/index.js index 6ae6d0987..0c91f4c86 100644 --- a/index.js +++ b/index.js @@ -45,13 +45,10 @@ function pipeline() { }; } - - -function configure(indexes, args)/*: Promise */ { +function configure(indexes, args) /*: Promise */ { let mergedConfig = mergeConfig(args); return mergedConfig.then(config => { - let expandedInputs = expandInputs(indexes, config); return expandedInputs.then(inputs => { @@ -71,8 +68,10 @@ function configure(indexes, args)/*: Promise */ { * @param {Object} config options * @returns {Promise>} promise with results */ -function expandInputs(indexes/*: string|Array */, - config /*: DocumentationConfig */) { +function expandInputs( + indexes /*: string|Array */, + config /*: DocumentationConfig */ +) { // Ensure that indexes is an array of strings indexes = [].concat(indexes); @@ -91,23 +90,24 @@ function buildInternal(inputsAndConfig) { config.access = ['public', 'undefined', 'protected']; } - var parseFn = (config.polyglot) ? polyglot : parseJavaScript; + var parseFn = config.polyglot ? polyglot : parseJavaScript; var buildPipeline = pipeline( inferName, inferAccess(config.inferPrivate), inferAugments, inferKind, + nest, inferParams, inferProperties, inferReturn, inferMembership(), inferType, - nest, config.github && github, - garbageCollect); + garbageCollect + ); - let extractedComments = _.flatMap(inputs, function (sourceFile) { + let extractedComments = _.flatMap(inputs, function(sourceFile) { if (!sourceFile.source) { sourceFile.source = fs.readFileSync(sourceFile.file, 'utf8'); } @@ -115,16 +115,17 @@ function buildInternal(inputsAndConfig) { return parseFn(sourceFile, config).map(buildPipeline); }).filter(Boolean); - return filterAccess(config.access, - hierarchy( - sort(extractedComments, config))); + return filterAccess( + config.access, + hierarchy(sort(extractedComments, config)) + ); } function lintInternal(inputsAndConfig) { let inputs = inputsAndConfig.inputs; let config = inputsAndConfig.config; - let parseFn = (config.polyglot) ? polyglot : parseJavaScript; + let parseFn = config.polyglot ? polyglot : parseJavaScript; let lintPipeline = pipeline( lintComments, @@ -137,7 +138,8 @@ function lintInternal(inputsAndConfig) { inferReturn, inferMembership(), inferType, - nest); + nest + ); let extractedComments = _.flatMap(inputs, sourceFile => { if (!sourceFile.source) { @@ -183,8 +185,7 @@ function lintInternal(inputsAndConfig) { * } * }); */ -let lint = (indexes, args) => configure(indexes, args) - .then(lintInternal); +let lint = (indexes, args) => configure(indexes, args).then(lintInternal); /** * Generate JavaScript documentation as a list of parsed JSDoc @@ -227,8 +228,7 @@ let lint = (indexes, args) => configure(indexes, args) * // any other kind of code data. * }); */ -let build = (indexes, args) => configure(indexes, args) - .then(buildInternal); +let build = (indexes, args) => configure(indexes, args).then(buildInternal); /** * Documentation's formats are modular methods that take comments @@ -240,9 +240,8 @@ let build = (indexes, args) => configure(indexes, args) var formats = { html: require('./lib/output/html'), md: require('./lib/output/markdown'), - remark: (comments/*: Array */, config/*: DocumentationConfig */) => - markdownAST(comments, config) - .then(res => JSON.stringify(res, null, 2)), + remark: (comments /*: Array */, config /*: DocumentationConfig */) => + markdownAST(comments, config).then(res => JSON.stringify(res, null, 2)), json: require('./lib/output/json') }; diff --git a/lib/infer/params.js b/lib/infer/params.js index ffa430034..6cf3124e3 100644 --- a/lib/infer/params.js +++ b/lib/infer/params.js @@ -2,14 +2,86 @@ /* @flow */ var t = require('babel-types'), + generate = require('babel-generator').default, + _ = require('lodash'), findTarget = require('./finders').findTarget, flowDoctrine = require('../flow_doctrine'); -function addPrefix(doc, prefix) { - if (!Array.isArray(doc) && doc.name) { - doc.name = prefix + doc.name; +// TODO: use a constant +const PATH_SPLIT_CAPTURING = /(\[])?(\.)/g; + +function addPrefix(doc /*: CommentTagNamed */, prefix) { + return _.assign(doc, { + name: prefix + doc.name + }); +} + +function destructuringObjectParamToDoc(param, i, prefix) /*: CommentTag */ { + return { + title: 'param', + name: '$' + String(i), + anonymous: true, + type: (param.typeAnnotation && flowDoctrine(param)) || { + type: 'NameExpression', + name: 'Object' + }, + properties: param.properties.map(prop => + destructuringPropertyToDoc(prop, i, prefix)) + }; +} + +function destructuringPropertyToDoc( + property, + i /*: number */, + prefix /*: string */ +) /*: CommentTag */ { + switch (property.type) { + case 'ObjectProperty': + // Object properties can rename their arguments, like + // function f({ x: y }) + // We want to document that as x, not y: y is the internal name. + // So we use the key. In the case where they don't rename, + // key and value are the same. + return paramToDoc(property, i, prefix + '$' + String(i) + '.'); + case 'Identifier': + // if the destructuring type is an array, the elements + // in it are identifiers + return paramToDoc(property, i, prefix + '$' + String(i) + '.'); + case 'RestProperty': + case 'RestElement': + case 'ObjectPattern': + return paramToDoc(property, i, prefix + '$' + String(i) + '.'); + default: + throw new Error(`Unknown property encountered: ${property.type}`); } - return doc; +} + +function destructuringObjectPropertyToDoc( + param, + i /*: number */, + prefix /*: string */ +) /*: CommentTag */ { + return _.assign(paramToDoc(param.value, i, prefix), { + name: prefix + param.key.name + }); +} + +function destructuringArrayParamToDoc( + param, + i /*: number */, + prefix /*: string */ +) /*: CommentTag */ { + return { + title: 'param', + name: '$' + String(i), + anonymous: true, + type: (param.typeAnnotation && flowDoctrine(param)) || { + type: 'NameExpression', + name: 'Array' + }, + properties: param.elements.map(element => + destructuringPropertyToDoc(element, i, prefix)) + }; } /** @@ -22,38 +94,31 @@ function addPrefix(doc, prefix) { * @param {Object} param ESTree node * @returns {Object} JSDoc param */ -function paramWithDefaultToDoc( - param, - comment, - i -) /*: CommentTag | Array */ { - var newParam = paramToDoc(param.left, comment, i, ''); +function paramWithDefaultToDoc(param, i) /*: CommentTag */ { + const newParam = paramToDoc(param.left, i, ''); - var defaultValue = comment.context.code.substring( - param.right.start, - param.right.end - ); + return _.assign(newParam, { + default: generate(param.right).code, + type: { + type: 'OptionalType', + expression: newParam.type + } + }); +} - // this is a destructuring parameter with defaults - if (Array.isArray(newParam)) { - newParam[0].default = defaultValue; - return newParam; +function restParamToDoc(param) /*: CommentTag */ { + let type /*: DoctrineType */ = { + type: 'RestType' + }; + if (param.typeAnnotation) { + type.expression = flowDoctrine(param.typeAnnotation.typeAnnotation); } - - var optionalParam /*: CommentTag */ = { + return { title: 'param', - name: newParam.name, - default: defaultValue + name: param.argument.name, + lineNumber: param.loc.start.line, + type }; - - if (newParam.type) { - optionalParam.type = { - type: 'OptionalType', - expression: newParam.type - }; - } - - return optionalParam; } /** @@ -69,128 +134,43 @@ function paramWithDefaultToDoc( * * @private * @param {Object} param the abstract syntax tree of the parameter in JavaScript - * @param {Object} comment the full comment object * @param {number} i the number of this parameter, in argument order * @param {string} prefix of the comment, if it is nested, like in the case of destructuring * @returns {Object} parameter with inference. */ function paramToDoc( param, - comment /*: Comment */, i /*: number */, prefix /*: string */ -) /*: Array | CommentTag */ { - function destructuringPropertyToDoc( - property - ) /*: Array | CommentTag */ { - if (property.type === 'ObjectProperty') { - return paramToDoc( - property.value, - comment, - i, - prefix + '$' + String(i) + '.' - ); - } else if (property.type === 'Identifier') { - // if the destructuring type is an array, the elements - // in it are identifiers - return paramToDoc(property, comment, i, prefix + '$' + String(i) + '.'); - } else if (property.type === 'RestProperty') { - return paramToDoc(property, comment, i, prefix + '$' + String(i) + '.'); - } else if (property.type === 'RestElement') { - return paramToDoc(property, comment, i, prefix + '$' + String(i) + '.'); - } - throw new Error(`Unknown property encountered: ${property.type}`); - } - - function destructuringObjectParamToDoc(param) /*: Array */ { - return [ - { +) /*: CommentTag */ { + // ES6 default + switch (param.type) { + case 'AssignmentPattern': // (a = b) + return addPrefix(paramWithDefaultToDoc(param, i), prefix); + case 'ObjectPattern': // { a } + return destructuringObjectParamToDoc(param, i, prefix); + case 'ArrayPattern': + return destructuringArrayParamToDoc(param, i, prefix); + // TODO: do we need both? + case 'ObjectProperty': + return destructuringObjectPropertyToDoc(param, i, prefix); + case 'RestProperty': + case 'RestElement': + return addPrefix(restParamToDoc(param), prefix); + default: + var newParam /*: CommentTagNamed */ = { title: 'param', - name: '$' + String(i), - type: flowDoctrine(param) || { - type: 'NameExpression', - name: 'Object' - } - } - ].concat(param.properties.map(destructuringPropertyToDoc)); - } + name: param.name, + lineNumber: param.loc.start.line + }; - function destructuringArrayParamToDoc(param) /*: Array */ { - return [ - { - title: 'param', - name: '$' + String(i), - type: flowDoctrine(param) || { - type: 'NameExpression', - name: 'Array' - } + // Flow/TS annotations + if (param.typeAnnotation && param.typeAnnotation.typeAnnotation) { + newParam.type = flowDoctrine(param.typeAnnotation.typeAnnotation); } - ].concat(param.elements.map(destructuringPropertyToDoc)); - } - function restParamToDoc(param) /*: CommentTag */ { - let type /*: DoctrineType */ = { - type: 'RestType' - }; - if (param.typeAnnotation) { - type.expression = flowDoctrine(param.typeAnnotation.typeAnnotation); - } - var newParam = { - title: 'param', - name: param.argument.name, - lineNumber: param.loc.start.line, - type - }; - return newParam; - } - - // ES6 default - if (param.type === 'AssignmentPattern') { - return addPrefix(paramWithDefaultToDoc(param, comment, i), prefix); - } - - if (param.type === 'ObjectPattern') { - return destructuringObjectParamToDoc(param); - } - - if (param.type === 'ArrayPattern') { - return destructuringArrayParamToDoc(param); - } - - if (param.type === 'RestProperty' || param.type === 'RestElement') { - return addPrefix(restParamToDoc(param), prefix); + return addPrefix(newParam, prefix); } - - var newParam /*: CommentTag */ = { - title: 'param', - name: param.name, - lineNumber: param.loc.start.line - }; - - // Flow/TS annotations - if (param.typeAnnotation && param.typeAnnotation.typeAnnotation) { - newParam.type = flowDoctrine(param.typeAnnotation.typeAnnotation); - } - - return addPrefix(newParam, prefix); -} - -function insertBeforeDependents(comment, comments) { - var dependentNamePrefix = comment.name + '.'; - for ( - var insertionIndex = 0; - insertionIndex < comments.length; - insertionIndex++ - ) { - let commentName = comments[insertionIndex].name; - if (commentName && commentName.indexOf(dependentNamePrefix) === 0) { - break; - } - } - return comments - .slice(0, insertionIndex) - .concat(comment) - .concat(comments.slice(insertionIndex)); } /** @@ -212,49 +192,107 @@ function inferParams(comment /*: Comment */) { return comment; } - // Ensure that explicitly specified parameters are not overridden - // by inferred parameters - var existingParams = {}; - comment.params.forEach(function(param) { - if (typeof param.name === 'string') { - existingParams[param.name] = param; - } + // Then merge the trees. This is the hard part. + return _.assign(comment, { + params: mergeTrees( + path.node.params.map((param, i) => paramToDoc(param, i, '')), + comment.params + ) }); +} - var paramOrder = {}; - var i = 0; +/** + * Recurse through a potentially nested parameter tag, + * replacing the auto-generated name, like $0, with an explicit + * name provided from a JSDoc comment + */ +function renameTree(node, explicitName) { + var parts = node.name.split(PATH_SPLIT_CAPTURING); + parts[0] = explicitName; + node.name = parts.join(''); + if (node.properties) { + node.properties.forEach(property => renameTree(property, explicitName)); + } +} - path.node.params - .reduce( - function(params, param, i) { - return params.concat(paramToDoc(param, comment, i, '')); - }, - [] - ) - .forEach(function(doc) { - if (!existingParams.hasOwnProperty(doc.name)) { - // This type is not explicitly documented - comment.params = insertBeforeDependents(doc, comment.params); - } else if (!existingParams[doc.name].type) { - // This param has a description, but potentially it can - // be have an inferred type. Infer its type without - // dropping the description. - if (doc.type) { - existingParams[doc.name].type = doc.type; - } - } else if ( - existingParams[doc.name].type.type !== 'OptionalType' && doc.default - ) { - existingParams[doc.name].type = { - type: 'OptionalType', - expression: existingParams[doc.name].type, - default: doc.default - }; +var mapTags = tags => new Map(tags.map(tag => [tag.name, tag])); + +function mergeTrees(inferred, explicit) { + // The first order of business is ensuring that the root types are specified + // in the right order. For the order of arguments, the inferred reality + // is the ground-truth: a function like + // function addThem(a, b, c) {} + // Should always see (a, b, c) in that order + + // First, if all parameters are specified, allow explicit names to apply + // to destructuring parameters, which do not have inferred names. This is + // _only_ enabled in the case in which all parameters are specified explicitly + if (inferred.length === explicit.length) { + for (var i = 0; i < inferred.length; i++) { + if (inferred[i].anonymous === true) { + renameTree(inferred[i], explicit[i].name); } - paramOrder[doc.name] = i++; - }); + } + } + + return mergeTopNodes(inferred, explicit); +} + +function mergeTopNodes(inferred, explicit) { + const mapExplicit = mapTags(explicit); - return comment; + return inferred.map(inferredTag => { + const explicitTag = mapExplicit.get(inferredTag.name); + return explicitTag ? combineTags(inferredTag, explicitTag) : inferredTag; + }); +} + +// This method is used for _non-root_ properties only - we use mergeTopNodes +// for root properties, which strictly requires inferred only. In this case, +// we combine all tags: +// - inferred & explicit +// - explicit only +// - inferred only +function mergeNodes(inferred, explicit) { + const intersection = _.intersectionBy(inferred, explicit, tag => tag.name); + const explicitOnly = _.differenceBy(explicit, inferred, tag => tag.name); + const inferredOnly = _.differenceBy(inferred, explicit, tag => tag.name); + const mapExplicit = mapTags(explicit); + + return intersection + .map(inferredTag => { + const explicitTag = mapExplicit.get(inferredTag.name); + return explicitTag ? combineTags(inferredTag, explicitTag) : inferredTag; + }) + .concat(explicitOnly) + .concat(inferredOnly); +} + +function combineTags(inferredTag, explicitTag) { + let type = explicitTag.type; + var defaultValue; + if (!explicitTag.type) { + type = inferredTag.type; + } else if (explicitTag.type.type !== 'OptionalType' && inferredTag.default) { + type = { + type: 'OptionalType', + expression: explicitTag.type + }; + defaultValue = inferredTag.default; + } + + return _.assign( + explicitTag, + { + properties: mergeNodes( + inferredTag.properties || [], + explicitTag.properties || [] + ), + type + }, + defaultValue ? { default: defaultValue } : {} + ); } module.exports = inferParams; +module.exports.mergeTrees = mergeTrees; diff --git a/lib/nest.js b/lib/nest.js index 753adf6e3..4cc0c8559 100644 --- a/lib/nest.js +++ b/lib/nest.js @@ -1,9 +1,35 @@ /* @flow */ 'use strict'; +var _ = require('lodash'); + +const PATH_SPLIT = /(?:\[])?\./g; + +function rejectUnnamedTags( + tags /*: Array */ +) /*: Array */ { + return tags.filter(tag => typeof tag.name === 'string'); +} + +var tagDepth = tag => tag.name.split(PATH_SPLIT).length; + /** * Nest nestable tags, like param and property, into nested * arrays that are suitable for output. + * Okay, so we're building a tree of comments, with the tag.name + * being the indexer. We sort by depth, so that we add each + * level of the tree incrementally, and throw if we run against + * a node that doesn't have a parent. + * + * foo.abe + * foo.bar.baz + * foo.bar.a + * foo.bar[].bax + * + * foo -> .abe + * \-> .bar -> .baz + * \-> .a + * \-> [].baz * * @private * @param {Object} comment a comment with tags @@ -11,52 +37,50 @@ * @param {string} target the tag to nest into * @returns {Object} nested comment */ -function nestTag( - comment /*: Comment */, - tagTitle /*: string */, - target /*: string */ -) { - if (!comment[target]) { - return comment; - } +var nestTag = ( + tags /*: Array */ + // Use lodash here both for brevity and also because, unlike JavaScript's + // sort, it's stable. +) => + _.sortBy(rejectUnnamedTags(tags), tagDepth).reduce( + (memo, tag) => { + function insertTag(node, parts) { + // The 'done' case: we're at parts.length === 1, + // this is where the node should be placed. We reliably + // get to this case because the recursive method + // is always passed parts.slice(1) + if (parts.length === 1) { + _.assign(node, { + properties: (node.properties || []).concat(tag) + }); + } else { + // The recursive case: try to find the child that owns + // this tag. + let child = node.properties && + node.properties.find( + property => parts[0] === _.last(property.name.split(PATH_SPLIT)) + ); - var result = [], index = {}; + if (!child) { + if (tag.name.match(/^(\$\d+)/)) { + throw new Error( + `Parent of ${tag.name} not found. To document a destructuring\n` + + `type, add a @param tag in its position to specify the name of the\n` + + `destructured parameter` + ); + } + throw new Error(`Parent of ${tag.name} not found`); + } - comment[target].forEach(tag => { - var tagName = tag.name; - if (tagName) { - index[tagName] = tag; - var parts = tagName - .split(/(\[\])?\./) - .filter(part => part && part !== '[]'); - if (parts.length > 1) { - var parent = index[parts.slice(0, -1).join('.')]; - if (parent === undefined) { - comment.errors.push({ - message: '@' + - tagTitle + - ' ' + - tag.name + - "'s parent " + - parts[0] + - ' not found', - commentLineNumber: tag.lineNumber - }); - result.push(tag); - return; + insertTag(child, parts.slice(1)); } - parent.properties = parent.properties || []; - parent.properties.push(tag); - } else { - result.push(tag); } - } - }); - comment[target] = result; - - return comment; -} + insertTag(memo, tag.name.split(PATH_SPLIT)); + return memo; + }, + { properties: [] } + ).properties; /** * Nests @@ -70,8 +94,11 @@ function nestTag( * @param {Object} comment input comment * @return {Object} nested comment */ -function nest(comment /*: Comment*/) { - return nestTag(nestTag(comment, 'param', 'params'), 'property', 'properties'); -} +var nest = (comment /*: Comment*/) => + _.assign(comment, { + params: nestTag(comment.params), + properties: nestTag(comment.properties) + }); module.exports = nest; +module.exports.nestTag = nestTag; diff --git a/lib/output/markdown_ast.js b/lib/output/markdown_ast.js index 29e0537a3..2a7f10bec 100644 --- a/lib/output/markdown_ast.js +++ b/lib/output/markdown_ast.js @@ -72,6 +72,7 @@ function buildMarkdownAST( } function paramList(params /*: Array */) { + if (params.length === 0) return []; return u( 'list', { ordered: false }, diff --git a/test/fixture/_multi-file-input.json b/test/fixture/_multi-file-input.json index 11a72d624..cf00595cf 100644 --- a/test/fixture/_multi-file-input.json +++ b/test/fixture/_multi-file-input.json @@ -167,7 +167,8 @@ "type": { "type": "NameExpression", "name": "Number" - } + }, + "properties": [] } ], "properties": [], diff --git a/test/fixture/class.output.json b/test/fixture/class.output.json index 37f1d9b70..d909a684e 100644 --- a/test/fixture/class.output.json +++ b/test/fixture/class.output.json @@ -329,7 +329,8 @@ "type": { "type": "NameExpression", "name": "boolean" - } + }, + "properties": [] } ], "properties": [], diff --git a/test/fixture/es6-class.output.json b/test/fixture/es6-class.output.json index 78ce308f3..45aaee754 100644 --- a/test/fixture/es6-class.output.json +++ b/test/fixture/es6-class.output.json @@ -295,7 +295,8 @@ "type": { "type": "NameExpression", "name": "string" - } + }, + "properties": [] } ], "properties": [], diff --git a/test/fixture/es6.output-toc.md b/test/fixture/es6.output-toc.md index 95a3c2c68..7e731557a 100644 --- a/test/fixture/es6.output-toc.md +++ b/test/fixture/es6.output-toc.md @@ -7,9 +7,9 @@ have any parameter descriptions. **Parameters** -- `$0` **any** (optional, default `{}`) - - `$0.phoneNumbers` (optional, default `[]`) - - `$0.emailAddresses` (optional, default `[]`) +- `$0` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)?** (optional, default `{}`) + - `$0.phoneNumbers` **any?** (optional, default `[]`) + - `$0.emailAddresses` **any?** (optional, default `[]`) - `$0.params` **...any** ## destructure @@ -18,7 +18,7 @@ Similar, but with an array **Parameters** -- `$0` **any** +- `$0` **[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)** - `$0.a` - `$0.b` - `$0.c` @@ -113,7 +113,7 @@ This tests our support of optional parameters in ES6 **Parameters** -- `foo` (optional, default `'bar'`) +- `foo` **any?** (optional, default `'bar'`) ## iAmProtected diff --git a/test/fixture/es6.output.json b/test/fixture/es6.output.json index 7ada1a342..a63054ec4 100644 --- a/test/fixture/es6.output.json +++ b/test/fixture/es6.output.json @@ -86,20 +86,32 @@ { "title": "param", "name": "$0", + "anonymous": true, "type": { - "type": "AllLiteral" + "type": "OptionalType", + "expression": { + "type": "NameExpression", + "name": "Object" + } }, - "default": "{}", "properties": [ { "title": "param", "name": "$0.phoneNumbers", - "default": "[]" + "lineNumber": 6, + "default": "[]", + "type": { + "type": "OptionalType" + } }, { "title": "param", "name": "$0.emailAddresses", - "default": "[]" + "lineNumber": 6, + "default": "[]", + "type": { + "type": "OptionalType" + } }, { "title": "param", @@ -109,7 +121,8 @@ "type": "RestType" } } - ] + ], + "default": "{}" } ], "properties": [], @@ -227,8 +240,10 @@ { "title": "param", "name": "$0", + "anonymous": true, "type": { - "type": "AllLiteral" + "type": "NameExpression", + "name": "Array" }, "properties": [ { @@ -448,7 +463,8 @@ "name": "Number" } ] - } + }, + "properties": [] }, { "title": "param", @@ -1069,7 +1085,8 @@ "type": { "type": "NameExpression", "name": "number" - } + }, + "properties": [] }, { "title": "param", @@ -1130,7 +1147,8 @@ "type": { "type": "NameExpression", "name": "number" - } + }, + "properties": [] } ], "properties": [], @@ -2299,7 +2317,11 @@ { "title": "param", "name": "foo", - "default": "'bar'" + "lineNumber": 114, + "default": "'bar'", + "type": { + "type": "OptionalType" + } } ], "properties": [], @@ -2761,7 +2783,7 @@ { "title": "param", "name": "compareFunction", - "default": "(a: T, b: T): boolean => a === b", + "lineNumber": 153, "type": { "type": "OptionalType", "expression": { @@ -2789,7 +2811,8 @@ "name": "boolean" } } - } + }, + "default": "(a: T, b: T): boolean => a === b" } ], "properties": [], diff --git a/test/fixture/es6.output.md b/test/fixture/es6.output.md index 985716788..84c9e963e 100644 --- a/test/fixture/es6.output.md +++ b/test/fixture/es6.output.md @@ -30,9 +30,9 @@ have any parameter descriptions. **Parameters** -- `$0` **any** (optional, default `{}`) - - `$0.phoneNumbers` (optional, default `[]`) - - `$0.emailAddresses` (optional, default `[]`) +- `$0` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)?** (optional, default `{}`) + - `$0.phoneNumbers` **any?** (optional, default `[]`) + - `$0.emailAddresses` **any?** (optional, default `[]`) - `$0.params` **...any** ## destructure @@ -41,7 +41,7 @@ Similar, but with an array **Parameters** -- `$0` **any** +- `$0` **[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)** - `$0.a` - `$0.b` - `$0.c` @@ -136,7 +136,7 @@ This tests our support of optional parameters in ES6 **Parameters** -- `foo` (optional, default `'bar'`) +- `foo` **any?** (optional, default `'bar'`) ## iAmProtected diff --git a/test/fixture/es6.output.md.json b/test/fixture/es6.output.md.json index 7826d97d5..74617b58a 100644 --- a/test/fixture/es6.output.md.json +++ b/test/fixture/es6.output.md.json @@ -84,9 +84,20 @@ { "type": "strong", "children": [ + { + "href": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object", + "url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object", + "type": "link", + "children": [ + { + "type": "text", + "value": "Object" + } + ] + }, { "type": "text", - "value": "any" + "value": "?" } ] }, @@ -131,6 +142,19 @@ "type": "text", "value": " " }, + { + "type": "strong", + "children": [ + { + "type": "text", + "value": "any" + }, + { + "type": "text", + "value": "?" + } + ] + }, { "type": "text", "value": " " @@ -170,6 +194,19 @@ "type": "text", "value": " " }, + { + "type": "strong", + "children": [ + { + "type": "text", + "value": "any" + }, + { + "type": "text", + "value": "?" + } + ] + }, { "type": "text", "value": " " @@ -312,8 +349,15 @@ "type": "strong", "children": [ { - "type": "text", - "value": "any" + "href": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array", + "url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array", + "type": "link", + "children": [ + { + "type": "text", + "value": "Array" + } + ] } ] }, @@ -1773,6 +1817,19 @@ "type": "text", "value": " " }, + { + "type": "strong", + "children": [ + { + "type": "text", + "value": "any" + }, + { + "type": "text", + "value": "?" + } + ] + }, { "type": "text", "value": " " diff --git a/test/fixture/html/nested.config-output.html b/test/fixture/html/nested.config-output.html index b5feef73f..07423b9a8 100644 --- a/test/fixture/html/nested.config-output.html +++ b/test/fixture/html/nested.config-output.html @@ -346,6 +346,22 @@

+ + + + + + + + + + + + + + +
NameDescription
+
@@ -354,6 +370,22 @@

+ + + + + + + + + + + + + + +
NameDescription
+ @@ -422,6 +454,22 @@

+ + + + + + + + + + + + + + +
NameDescription
+ @@ -489,6 +537,22 @@

+ + + + + + + + + + + + + + +
NameDescription
+
@@ -499,6 +563,22 @@

+ + + + + + + + + + + + + + +
NameDescription
+ @@ -567,6 +647,22 @@

+ + + + + + + + + + + + + + +
NameDescription
+ @@ -792,6 +888,22 @@

+ + + + + + + + + + + + + + +
NameDescription
+ diff --git a/test/fixture/html/nested.output.files b/test/fixture/html/nested.output.files index 238baa907..d73b3720c 100644 --- a/test/fixture/html/nested.output.files +++ b/test/fixture/html/nested.output.files @@ -308,6 +308,22 @@ This is a [link to something that does not exist]DoesNot + + + + + + + + + + + + + + +
NameDescription
+
@@ -316,6 +332,22 @@ This is a [link to something that does not exist]DoesNot + + + + + + + + + + + + + + +
NameDescription
+
@@ -384,6 +416,22 @@ the referenced class type

+ + + + + + + + + + + + + + +
NameDescription
+ @@ -451,6 +499,22 @@ the referenced class type

+ + + + + + + + + + + + + + +
NameDescription
+
@@ -461,6 +525,22 @@ the referenced class type

+ + + + + + + + + + + + + + +
NameDescription
+ @@ -529,6 +609,22 @@ the referenced class type

+ + + + + + + + + + + + + + +
NameDescription
+ @@ -754,6 +850,22 @@ k.isArrayOfBuffers(); + + + + + + + + + + + + + + +
NameDescription
+ diff --git a/test/fixture/inline-link.output.json b/test/fixture/inline-link.output.json index 95ae461d5..060654315 100644 --- a/test/fixture/inline-link.output.json +++ b/test/fixture/inline-link.output.json @@ -158,7 +158,8 @@ "type": { "type": "NameExpression", "name": "number" - } + }, + "properties": [] } ], "properties": [], @@ -581,7 +582,8 @@ "type": { "type": "NameExpression", "name": "number" - } + }, + "properties": [] } ], "properties": [], diff --git a/test/fixture/lends.output.json b/test/fixture/lends.output.json index 4a231651f..3fc2c210f 100644 --- a/test/fixture/lends.output.json +++ b/test/fixture/lends.output.json @@ -272,7 +272,8 @@ "type": { "type": "NameExpression", "name": "string" - } + }, + "properties": [] } ], "properties": [], @@ -523,7 +524,8 @@ "type": { "type": "NameExpression", "name": "string" - } + }, + "properties": [] } ], "properties": [], diff --git a/test/fixture/literal_types.output.json b/test/fixture/literal_types.output.json index 858b92501..5300b7035 100644 --- a/test/fixture/literal_types.output.json +++ b/test/fixture/literal_types.output.json @@ -96,7 +96,8 @@ "value": 3.14 } ] - } + }, + "properties": [] } ], "properties": [], diff --git a/test/fixture/memberedclass.output.json b/test/fixture/memberedclass.output.json index a817352c9..6d125b531 100644 --- a/test/fixture/memberedclass.output.json +++ b/test/fixture/memberedclass.output.json @@ -268,7 +268,8 @@ "type": { "type": "NameExpression", "name": "boolean" - } + }, + "properties": [] } ], "properties": [], diff --git a/test/fixture/merge-infered-type.output.json b/test/fixture/merge-infered-type.output.json index 61f6b15c2..b424e90b9 100644 --- a/test/fixture/merge-infered-type.output.json +++ b/test/fixture/merge-infered-type.output.json @@ -183,6 +183,7 @@ } } }, + "properties": [], "type": { "type": "NameExpression", "name": "number" diff --git a/test/fixture/multisignature.output.json b/test/fixture/multisignature.output.json index ba7426e9a..4a22e59c0 100644 --- a/test/fixture/multisignature.output.json +++ b/test/fixture/multisignature.output.json @@ -335,7 +335,8 @@ "type": { "type": "NameExpression", "name": "Date" - } + }, + "properties": [] } ], "properties": [], diff --git a/test/fixture/nest_params.output.json b/test/fixture/nest_params.output.json index 70a9cf890..a444a5767 100644 --- a/test/fixture/nest_params.output.json +++ b/test/fixture/nest_params.output.json @@ -339,7 +339,8 @@ "name": "string" } }, - "default": "minion" + "default": "minion", + "properties": [] } ], "properties": [], diff --git a/test/fixture/params.input.js b/test/fixture/params.input.js index cea9becdf..c9522355c 100644 --- a/test/fixture/params.input.js +++ b/test/fixture/params.input.js @@ -8,7 +8,8 @@ function addThem(a, b, c, { d, e, f }) { /** * This method has partially inferred params - * @param {String} $0.fishes number of kinds of fish + * @param {Object} options + * @param {String} options.fishes number of kinds of fish */ function fishesAndFoxes({ fishes, foxes }) { return fishes + foxes; @@ -99,8 +100,9 @@ function foo(address) { * This tests our support for iterator rest inside an * iterator destructure (RestElement) * - * @param {any} $0.x head of iterator - * @param {any[]} ...$0.xs body of iterator + * @param {Array} input + * @param {any} input.x head of iterator + * @param {any[]} ...input.xs body of iterator * * @returns {any[]} rotated such that the last element was the first */ diff --git a/test/fixture/params.output.json b/test/fixture/params.output.json index 01241aec6..326347054 100644 --- a/test/fixture/params.output.json +++ b/test/fixture/params.output.json @@ -90,6 +90,11 @@ "errors": [], "examples": [], "params": [ + { + "title": "param", + "name": "a", + "lineNumber": 5 + }, { "title": "param", "name": "b", @@ -149,12 +154,8 @@ "type": { "type": "NameExpression", "name": "number" - } - }, - { - "title": "param", - "name": "a", - "lineNumber": 5 + }, + "properties": [] }, { "title": "param", @@ -164,8 +165,10 @@ { "title": "param", "name": "$3", + "anonymous": true, "type": { - "type": "AllLiteral" + "type": "NameExpression", + "name": "Object" }, "properties": [ { @@ -264,13 +267,23 @@ "tags": [ { "title": "param", - "description": "number of kinds of fish", + "description": null, "lineNumber": 2, + "type": { + "type": "NameExpression", + "name": "Object" + }, + "name": "options" + }, + { + "title": "param", + "description": "number of kinds of fish", + "lineNumber": 3, "type": { "type": "NameExpression", "name": "String" }, - "name": "$0.fishes" + "name": "options.fishes" } ], "loc": { @@ -279,18 +292,18 @@ "column": 0 }, "end": { - "line": 12, + "line": 13, "column": 3 } }, "context": { "loc": { "start": { - "line": 13, + "line": 14, "column": 0 }, "end": { - "line": 15, + "line": 16, "column": 1 } } @@ -301,15 +314,17 @@ "params": [ { "title": "param", - "name": "$0", + "name": "options", + "lineNumber": 2, "type": { - "type": "AllLiteral" + "type": "NameExpression", + "name": "Object" }, "properties": [ { "title": "param", - "name": "$0.fishes", - "lineNumber": 2, + "name": "options.fishes", + "lineNumber": 3, "description": { "type": "root", "children": [ @@ -365,12 +380,13 @@ "type": { "type": "NameExpression", "name": "String" - } + }, + "properties": [] }, { "title": "param", - "name": "$0.foxes", - "lineNumber": 13 + "name": "options.foxes", + "lineNumber": 14 } ] } @@ -464,22 +480,22 @@ ], "loc": { "start": { - "line": 17, + "line": 18, "column": 0 }, "end": { - "line": 20, + "line": 21, "column": 3 } }, "context": { "loc": { "start": { - "line": 21, + "line": 22, "column": 0 }, "end": { - "line": 23, + "line": 24, "column": 1 } } @@ -497,9 +513,10 @@ "expression": { "type": "NameExpression", "name": "number" - }, - "default": "2" - } + } + }, + "properties": [], + "default": "2" } ], "properties": [], @@ -580,22 +597,22 @@ "tags": [], "loc": { "start": { - "line": 25, + "line": 26, "column": 0 }, "end": { - "line": 27, + "line": 28, "column": 3 } }, "context": { "loc": { "start": { - "line": 28, + "line": 29, "column": 0 }, "end": { - "line": 34, + "line": 35, "column": 1 } } @@ -682,22 +699,22 @@ ], "loc": { "start": { - "line": 29, + "line": 30, "column": 2 }, "end": { - "line": 32, + "line": 33, "column": 5 } }, "context": { "loc": { "start": { - "line": 33, + "line": 34, "column": 2 }, "end": { - "line": 33, + "line": 34, "column": 14 } } @@ -765,7 +782,8 @@ "type": { "type": "NameExpression", "name": "number" - } + }, + "properties": [] } ], "properties": [], @@ -865,22 +883,22 @@ "tags": [], "loc": { "start": { - "line": 36, + "line": 37, "column": 0 }, "end": { - "line": 38, + "line": 39, "column": 3 } }, "context": { "loc": { "start": { - "line": 39, + "line": 40, "column": 0 }, "end": { - "line": 46, + "line": 47, "column": 2 } } @@ -957,22 +975,22 @@ "tags": [], "loc": { "start": { - "line": 40, + "line": 41, "column": 2 }, "end": { - "line": 42, + "line": 43, "column": 5 } }, "context": { "loc": { "start": { - "line": 43, + "line": 44, "column": 2 }, "end": { - "line": 45, + "line": 46, "column": 3 } } @@ -984,7 +1002,7 @@ { "title": "param", "name": "x", - "lineNumber": 43 + "lineNumber": 44 } ], "properties": [], @@ -1180,22 +1198,22 @@ ], "loc": { "start": { - "line": 48, + "line": 49, "column": 0 }, "end": { - "line": 59, + "line": 60, "column": 3 } }, "context": { "loc": { "start": { - "line": 60, + "line": 61, "column": 0 }, "end": { - "line": 60, + "line": 61, "column": 22 } } @@ -1207,264 +1225,7 @@ "description": "var address = new Address6('2001::/32');" } ], - "params": [ - { - "title": "param", - "name": "address", - "lineNumber": 5, - "description": { - "type": "root", - "children": [ - { - "type": "paragraph", - "children": [ - { - "type": "text", - "value": "An IPv6 address string", - "position": { - "start": { - "line": 1, - "column": 1, - "offset": 0 - }, - "end": { - "line": 1, - "column": 23, - "offset": 22 - }, - "indent": [] - } - } - ], - "position": { - "start": { - "line": 1, - "column": 1, - "offset": 0 - }, - "end": { - "line": 1, - "column": 23, - "offset": 22 - }, - "indent": [] - } - } - ], - "position": { - "start": { - "line": 1, - "column": 1, - "offset": 0 - }, - "end": { - "line": 1, - "column": 23, - "offset": 22 - } - } - }, - "type": { - "type": "NameExpression", - "name": "string" - } - }, - { - "title": "param", - "name": "groups", - "lineNumber": 6, - "description": { - "type": "root", - "children": [ - { - "type": "paragraph", - "children": [ - { - "type": "text", - "value": "How many octets to parse", - "position": { - "start": { - "line": 1, - "column": 1, - "offset": 0 - }, - "end": { - "line": 1, - "column": 25, - "offset": 24 - }, - "indent": [] - } - } - ], - "position": { - "start": { - "line": 1, - "column": 1, - "offset": 0 - }, - "end": { - "line": 1, - "column": 25, - "offset": 24 - }, - "indent": [] - } - } - ], - "position": { - "start": { - "line": 1, - "column": 1, - "offset": 0 - }, - "end": { - "line": 1, - "column": 25, - "offset": 24 - } - } - }, - "type": { - "type": "OptionalType", - "expression": { - "type": "NameExpression", - "name": "number" - } - }, - "default": "8" - }, - { - "title": "param", - "name": "third", - "lineNumber": 7, - "description": { - "type": "root", - "children": [ - { - "type": "paragraph", - "children": [ - { - "type": "text", - "value": "A third argument", - "position": { - "start": { - "line": 1, - "column": 1, - "offset": 0 - }, - "end": { - "line": 1, - "column": 17, - "offset": 16 - }, - "indent": [] - } - } - ], - "position": { - "start": { - "line": 1, - "column": 1, - "offset": 0 - }, - "end": { - "line": 1, - "column": 17, - "offset": 16 - }, - "indent": [] - } - } - ], - "position": { - "start": { - "line": 1, - "column": 1, - "offset": 0 - }, - "end": { - "line": 1, - "column": 17, - "offset": 16 - } - } - }, - "type": { - "type": "NullableType", - "expression": { - "type": "NameExpression", - "name": "number" - }, - "prefix": true - } - }, - { - "title": "param", - "name": "foo", - "lineNumber": 8, - "description": { - "type": "root", - "children": [ - { - "type": "paragraph", - "children": [ - { - "type": "text", - "value": "to properly be parsed", - "position": { - "start": { - "line": 1, - "column": 1, - "offset": 0 - }, - "end": { - "line": 1, - "column": 22, - "offset": 21 - }, - "indent": [] - } - } - ], - "position": { - "start": { - "line": 1, - "column": 1, - "offset": 0 - }, - "end": { - "line": 1, - "column": 22, - "offset": 21 - }, - "indent": [] - } - } - ], - "position": { - "start": { - "line": 1, - "column": 1, - "offset": 0 - }, - "end": { - "line": 1, - "column": 22, - "offset": 21 - } - } - }, - "type": { - "type": "OptionalType", - "expression": { - "type": "NameExpression", - "name": "Array" - } - }, - "default": "[1]" - } - ], + "params": [], "properties": [], "returns": [], "sees": [], @@ -1658,22 +1419,22 @@ ], "loc": { "start": { - "line": 62, + "line": 63, "column": 0 }, "end": { - "line": 73, + "line": 74, "column": 3 } }, "context": { "loc": { "start": { - "line": 74, + "line": 75, "column": 0 }, "end": { - "line": 76, + "line": 77, "column": 1 } } @@ -2118,22 +1879,22 @@ ], "loc": { "start": { - "line": 78, + "line": 79, "column": 0 }, "end": { - "line": 85, + "line": 86, "column": 3 } }, "context": { "loc": { "start": { - "line": 86, + "line": 87, "column": 0 }, "end": { - "line": 86, + "line": 87, "column": 37 } } @@ -2203,9 +1964,10 @@ "expression": { "type": "NameExpression", "name": "number" - }, - "default": "123" - } + } + }, + "properties": [], + "default": "123" } ], "properties": [], @@ -2358,22 +2120,22 @@ ], "loc": { "start": { - "line": 88, + "line": 89, "column": 0 }, "end": { - "line": 93, + "line": 94, "column": 3 } }, "context": { "loc": { "start": { - "line": 94, + "line": 95, "column": 0 }, "end": { - "line": 96, + "line": 97, "column": 1 } } @@ -2437,7 +2199,8 @@ "offset": 22 } } - } + }, + "properties": [] } ], "properties": [], @@ -2522,18 +2285,28 @@ "tags": [ { "title": "param", - "description": "head of iterator", + "description": null, "lineNumber": 4, "type": { "type": "NameExpression", - "name": "any" + "name": "Array" }, - "name": "$0.x" + "name": "input" }, { "title": "param", - "description": "...$0.xs body of iterator", + "description": "head of iterator", "lineNumber": 5, + "type": { + "type": "NameExpression", + "name": "any" + }, + "name": "input.x" + }, + { + "title": "param", + "description": "...input.xs body of iterator", + "lineNumber": 6, "type": { "type": "TypeApplication", "expression": { @@ -2555,7 +2328,7 @@ { "title": "returns", "description": "rotated such that the last element was the first", - "lineNumber": 7, + "lineNumber": 8, "type": { "type": "TypeApplication", "expression": { @@ -2573,22 +2346,22 @@ ], "loc": { "start": { - "line": 98, + "line": 99, "column": 0 }, "end": { - "line": 106, + "line": 108, "column": 3 } }, "context": { "loc": { "start": { - "line": 107, + "line": 109, "column": 0 }, "end": { - "line": 109, + "line": 111, "column": 1 } } @@ -2603,15 +2376,17 @@ "params": [ { "title": "param", - "name": "$0", + "name": "input", + "lineNumber": 4, "type": { - "type": "AllLiteral" + "type": "NameExpression", + "name": "Array" }, "properties": [ { "title": "param", - "name": "$0.x", - "lineNumber": 4, + "name": "input.x", + "lineNumber": 5, "description": { "type": "root", "children": [ @@ -2667,12 +2442,13 @@ "type": { "type": "NameExpression", "name": "any" - } + }, + "properties": [] }, { "title": "param", - "name": "$0.xs", - "lineNumber": 107, + "name": "input.xs", + "lineNumber": 109, "type": { "type": "RestType" } diff --git a/test/fixture/params.output.md b/test/fixture/params.output.md index c9df3621a..6f3e70c6c 100644 --- a/test/fixture/params.output.md +++ b/test/fixture/params.output.md @@ -21,10 +21,10 @@ This function returns the number one. **Parameters** -- `b` **[number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)** the second param - `a` +- `b` **[number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)** the second param - `c` -- `$3` **any** +- `$3` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** - `$3.d` - `$3.e` - `$3.f` @@ -35,9 +35,9 @@ This method has partially inferred params **Parameters** -- `$0` **any** - - `$0.fishes` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** number of kinds of fish - - `$0.foxes` +- `options` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** + - `options.fishes` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** number of kinds of fish + - `options.foxes` ## withDefault @@ -45,7 +45,7 @@ This method has a type in the description and a default in the code **Parameters** -- `x` **[number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?= 2** +- `x` **[number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?** (optional, default `2`) ## Foo @@ -77,13 +77,6 @@ Represents an IPv6 address This tests our support of optional parameters -**Parameters** - -- `address` **[string](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** An IPv6 address string -- `groups` **[number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?** How many octets to parse (optional, default `8`) -- `third` **[number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?** A third argument -- `foo` **[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)?** to properly be parsed (optional, default `[1]`) - **Examples** ```javascript @@ -112,7 +105,7 @@ values specified in code. **Parameters** -- `x` **[number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?= 123** an argument +- `x` **[number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?** an argument (optional, default `123`) Returns **[number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)** some @@ -132,8 +125,8 @@ iterator destructure (RestElement) **Parameters** -- `$0` **any** - - `$0.x` **any** head of iterator - - `$0.xs` **...any** +- `input` **[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)** + - `input.x` **any** head of iterator + - `input.xs` **...any** Returns **[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)<any>** rotated such that the last element was the first diff --git a/test/fixture/params.output.md.json b/test/fixture/params.output.md.json index f8f56c216..0529351e6 100644 --- a/test/fixture/params.output.md.json +++ b/test/fixture/params.output.md.json @@ -63,6 +63,28 @@ "ordered": false, "type": "list", "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "inlineCode", + "value": "a" + }, + { + "type": "text", + "value": " " + }, + { + "type": "text", + "value": " " + } + ] + } + ] + }, { "type": "listItem", "children": [ @@ -136,28 +158,6 @@ } ] }, - { - "type": "listItem", - "children": [ - { - "type": "paragraph", - "children": [ - { - "type": "inlineCode", - "value": "a" - }, - { - "type": "text", - "value": " " - }, - { - "type": "text", - "value": " " - } - ] - } - ] - }, { "type": "listItem", "children": [ @@ -198,8 +198,15 @@ "type": "strong", "children": [ { - "type": "text", - "value": "any" + "href": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object", + "url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object", + "type": "link", + "children": [ + { + "type": "text", + "value": "Object" + } + ] } ] }, @@ -351,7 +358,7 @@ "children": [ { "type": "inlineCode", - "value": "$0" + "value": "options" }, { "type": "text", @@ -361,8 +368,15 @@ "type": "strong", "children": [ { - "type": "text", - "value": "any" + "href": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object", + "url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object", + "type": "link", + "children": [ + { + "type": "text", + "value": "Object" + } + ] } ] }, @@ -384,7 +398,7 @@ "children": [ { "type": "inlineCode", - "value": "$0.fishes" + "value": "options.fishes" }, { "type": "text", @@ -457,7 +471,7 @@ "children": [ { "type": "inlineCode", - "value": "$0.foxes" + "value": "options.foxes" }, { "type": "text", @@ -566,16 +580,29 @@ { "type": "text", "value": "?" - }, - { - "type": "text", - "value": "= 2" } ] }, { "type": "text", "value": " " + }, + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": " (optional, default " + }, + { + "type": "inlineCode", + "value": "2" + }, + { + "type": "text", + "value": ")" + } + ] } ] } @@ -968,359 +995,6 @@ "indent": [] } }, - { - "type": "strong", - "children": [ - { - "type": "text", - "value": "Parameters" - } - ] - }, - { - "ordered": false, - "type": "list", - "children": [ - { - "type": "listItem", - "children": [ - { - "type": "paragraph", - "children": [ - { - "type": "inlineCode", - "value": "address" - }, - { - "type": "text", - "value": " " - }, - { - "type": "strong", - "children": [ - { - "href": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String", - "url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String", - "type": "link", - "children": [ - { - "type": "text", - "value": "string" - } - ] - } - ] - }, - { - "type": "text", - "value": " " - }, - { - "type": "paragraph", - "children": [ - { - "type": "text", - "value": "An IPv6 address string", - "position": { - "start": { - "line": 1, - "column": 1, - "offset": 0 - }, - "end": { - "line": 1, - "column": 23, - "offset": 22 - }, - "indent": [] - } - } - ], - "position": { - "start": { - "line": 1, - "column": 1, - "offset": 0 - }, - "end": { - "line": 1, - "column": 23, - "offset": 22 - }, - "indent": [] - } - } - ] - } - ] - }, - { - "type": "listItem", - "children": [ - { - "type": "paragraph", - "children": [ - { - "type": "inlineCode", - "value": "groups" - }, - { - "type": "text", - "value": " " - }, - { - "type": "strong", - "children": [ - { - "href": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number", - "url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number", - "type": "link", - "children": [ - { - "type": "text", - "value": "number" - } - ] - }, - { - "type": "text", - "value": "?" - } - ] - }, - { - "type": "text", - "value": " " - }, - { - "type": "paragraph", - "children": [ - { - "type": "text", - "value": "How many octets to parse", - "position": { - "start": { - "line": 1, - "column": 1, - "offset": 0 - }, - "end": { - "line": 1, - "column": 25, - "offset": 24 - }, - "indent": [] - } - } - ], - "position": { - "start": { - "line": 1, - "column": 1, - "offset": 0 - }, - "end": { - "line": 1, - "column": 25, - "offset": 24 - }, - "indent": [] - } - }, - { - "type": "paragraph", - "children": [ - { - "type": "text", - "value": " (optional, default " - }, - { - "type": "inlineCode", - "value": "8" - }, - { - "type": "text", - "value": ")" - } - ] - } - ] - } - ] - }, - { - "type": "listItem", - "children": [ - { - "type": "paragraph", - "children": [ - { - "type": "inlineCode", - "value": "third" - }, - { - "type": "text", - "value": " " - }, - { - "type": "strong", - "children": [ - { - "href": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number", - "url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number", - "type": "link", - "children": [ - { - "type": "text", - "value": "number" - } - ] - }, - { - "type": "text", - "value": "?" - } - ] - }, - { - "type": "text", - "value": " " - }, - { - "type": "paragraph", - "children": [ - { - "type": "text", - "value": "A third argument", - "position": { - "start": { - "line": 1, - "column": 1, - "offset": 0 - }, - "end": { - "line": 1, - "column": 17, - "offset": 16 - }, - "indent": [] - } - } - ], - "position": { - "start": { - "line": 1, - "column": 1, - "offset": 0 - }, - "end": { - "line": 1, - "column": 17, - "offset": 16 - }, - "indent": [] - } - } - ] - } - ] - }, - { - "type": "listItem", - "children": [ - { - "type": "paragraph", - "children": [ - { - "type": "inlineCode", - "value": "foo" - }, - { - "type": "text", - "value": " " - }, - { - "type": "strong", - "children": [ - { - "href": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array", - "url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array", - "type": "link", - "children": [ - { - "type": "text", - "value": "Array" - } - ] - }, - { - "type": "text", - "value": "?" - } - ] - }, - { - "type": "text", - "value": " " - }, - { - "type": "paragraph", - "children": [ - { - "type": "text", - "value": "to properly be parsed", - "position": { - "start": { - "line": 1, - "column": 1, - "offset": 0 - }, - "end": { - "line": 1, - "column": 22, - "offset": 21 - }, - "indent": [] - } - } - ], - "position": { - "start": { - "line": 1, - "column": 1, - "offset": 0 - }, - "end": { - "line": 1, - "column": 22, - "offset": 21 - }, - "indent": [] - } - }, - { - "type": "paragraph", - "children": [ - { - "type": "text", - "value": " (optional, default " - }, - { - "type": "inlineCode", - "value": "[1]" - }, - { - "type": "text", - "value": ")" - } - ] - } - ] - } - ] - } - ] - }, { "type": "strong", "children": [ @@ -1954,10 +1628,6 @@ { "type": "text", "value": "?" - }, - { - "type": "text", - "value": "= 123" } ] }, @@ -1999,6 +1669,23 @@ }, "indent": [] } + }, + { + "type": "paragraph", + "children": [ + { + "type": "text", + "value": " (optional, default " + }, + { + "type": "inlineCode", + "value": "123" + }, + { + "type": "text", + "value": ")" + } + ] } ] } @@ -2261,7 +1948,7 @@ "children": [ { "type": "inlineCode", - "value": "$0" + "value": "input" }, { "type": "text", @@ -2271,8 +1958,15 @@ "type": "strong", "children": [ { - "type": "text", - "value": "any" + "href": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array", + "url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array", + "type": "link", + "children": [ + { + "type": "text", + "value": "Array" + } + ] } ] }, @@ -2294,7 +1988,7 @@ "children": [ { "type": "inlineCode", - "value": "$0.x" + "value": "input.x" }, { "type": "text", @@ -2360,7 +2054,7 @@ "children": [ { "type": "inlineCode", - "value": "$0.xs" + "value": "input.xs" }, { "type": "text", diff --git a/test/fixture/simple-two.output.json b/test/fixture/simple-two.output.json index a6d86ceea..f8ed76562 100644 --- a/test/fixture/simple-two.output.json +++ b/test/fixture/simple-two.output.json @@ -167,7 +167,8 @@ "type": { "type": "NameExpression", "name": "Number" - } + }, + "properties": [] } ], "properties": [], diff --git a/test/lib/infer/params.js b/test/lib/infer/params.js index 628f3b71a..c56e778ba 100644 --- a/test/lib/infer/params.js +++ b/test/lib/infer/params.js @@ -18,6 +18,101 @@ function evaluate(fn, file) { return inferParams(toComment(fn, file)); } +test('mergeTrees', function(t) { + t.deepEqual( + inferParams.mergeTrees( + [], + [ + { + title: 'param', + description: 'First arg!', + name: 'a', + type: { + type: 'NameExpression', + name: 'string' + } + } + ] + ), + [ + // { + // title: 'param', + // description: 'First arg!', + // name: 'a', + // type: { + // type: 'NameExpression', + // name: 'string' + // } + // } + ] + ); + + t.deepEqual( + inferParams.mergeTrees( + [ + { + title: 'param', + name: '$0', + anonymous: true, + parameterIndex: 0, + type: { + type: 'NameExpression', + name: 'object' + }, + properties: [ + { + title: 'param', + name: '$0.a', + parameterIndex: 0, + type: { + type: 'NameExpression', + name: 'string' + }, + properties: [] + } + ] + } + ], + [ + { + title: 'param', + description: 'First arg!', + name: 'a', + type: { + type: 'NameExpression', + name: 'object' + } + } + ] + ), + [ + { + title: 'param', + description: 'First arg!', + name: 'a', + type: { + type: 'NameExpression', + name: 'object' + }, + properties: [ + { + title: 'param', + name: 'a.a', + parameterIndex: 0, + type: { + type: 'NameExpression', + name: 'string' + }, + properties: [] + } + ] + } + ] + ); + + t.end(); +}); + test('inferParams', function(t) { t.deepEqual( evaluate(function() { @@ -35,6 +130,86 @@ test('inferParams', function(t) { [{ lineNumber: 3, name: 'x', title: 'param' }] ); + t.deepEqual( + evaluate(function() { + /** + * Test + * @param {Object} a renamed destructuring param + */ + var f = function({ x }) {}; + }).params, + [ + { + description: { + children: [ + { + children: [ + { + position: { + end: { + column: 28, + line: 1, + offset: 27 + }, + indent: [], + start: { + column: 1, + line: 1, + offset: 0 + } + }, + type: 'text', + value: 'renamed destructuring param' + } + ], + position: { + end: { + column: 28, + line: 1, + offset: 27 + }, + indent: [], + start: { + column: 1, + line: 1, + offset: 0 + } + }, + type: 'paragraph' + } + ], + position: { + end: { + column: 28, + line: 1, + offset: 27 + }, + start: { + column: 1, + line: 1, + offset: 0 + } + }, + type: 'root' + }, + lineNumber: 2, + name: 'a', + properties: [ + { + lineNumber: 6, + name: 'a.x', + title: 'param' + } + ], + title: 'param', + type: { + name: 'Object', + type: 'NameExpression' + } + } + ] + ); + t.deepEqual(evaluate('/** Test */ var f = (x) => {}').params, [ { lineNumber: 1, name: 'x', title: 'param' } ]); @@ -49,6 +224,78 @@ test('inferParams', function(t) { [{ lineNumber: 5, name: 'x', title: 'param' }] ); + t.deepEqual( + evaluate(function() { + /** Test */ + function f(x = 4) {} + }).params, + [ + { + default: '4', + name: 'x', + title: 'param', + lineNumber: 3, + type: { + expression: null, + type: 'OptionalType' + } + } + ], + 'default params' + ); + + t.deepEqual( + evaluate(function() { + /** Test + * @param {number} x + */ + function f(x = 4) {} + }).params, + [ + { + default: '4', + name: 'x', + title: 'param', + lineNumber: 1, + properties: [], + type: { + expression: { + type: 'NameExpression', + name: 'number' + }, + type: 'OptionalType' + } + } + ], + 'default params with type' + ); + + t.deepEqual( + evaluate(function() { + /** Test */ + function f({ x: y }) {} + }).params, + [ + { + anonymous: true, + name: '$0', + properties: [ + { + lineNumber: 3, + name: '$0.x', + title: 'param' + } + ], + title: 'param', + type: { + name: 'Object', + type: 'NameExpression' + } + } + ], + 'renaming' + ); + t.deepEqual(evaluate('/** Test */ export function f(x) {}').params, [ { lineNumber: 1, name: 'x', title: 'param' } ]); diff --git a/test/lib/nest.js b/test/lib/nest.js index 8210ce45b..fb603cabd 100644 --- a/test/lib/nest.js +++ b/test/lib/nest.js @@ -1,101 +1,117 @@ 'use strict'; -var test = require('tap').test, - parse = require('../../lib/parsers/javascript'), - nest = require('../../lib/nest'); +var test = require('tap').test; +var nestTag = require('../../lib/nest').nestTag; -function toComment(fn, filename) { - return parse( - { - file: filename, - source: fn instanceof Function ? '(' + fn.toString() + ')' : fn - }, - {} - ).map(nest); -} +// Print a tree of tags in a way that's easy to test. +var printTree = indent => + node => + `${new Array(indent + 1).join(' ')}- ${node.name}${node.properties ? '\n' : ''}${(node.properties || [ + ]) + .map(printTree(indent + 1)) + .join('\n')}`; -test('nest params - no params', function(t) { - t.deepEqual( - toComment(function() { - /** @name foo */ - })[0].params, - [], - 'no params' - ); - t.end(); -}); +var printNesting = params => + printTree(0)({ properties: nestTag(params), name: 'root' }); -test('nest params - no nesting', function(t) { - var result = toComment(function() { - /** @param {Object} foo */ - }); - t.equal(result[0].params.length, 1); - t.equal(result[0].params[0].name, 'foo'); - t.equal(result[0].params[0].properties, undefined); +test('nest params - basic', function(t) { + var params = [ + 'foo', + 'foo.bar', + 'foo.bar.third', + 'foo.third', + 'foo.third[].baz' + ].map(name => ({ name })); + t.equal( + printNesting(params), + `- root + - foo + - foo.bar + - foo.bar.third + - foo.third + - foo.third[].baz` + ); t.end(); }); -test('nest params - basic', function(t) { - var result = toComment(function() { - /** - * @param {Object} foo - * @param {string} foo.bar - * @param {string} foo.baz - */ - }); - t.equal(result[0].params.length, 1); - t.equal(result[0].params[0].name, 'foo'); - t.equal(result[0].params[0].properties.length, 2); - t.equal(result[0].params[0].properties[0].name, 'foo.bar'); - t.equal(result[0].params[0].properties[1].name, 'foo.baz'); +test('nest params - multiple roots', function(t) { + var params = ['a', 'b', 'c'].map(name => ({ name })); + t.equal( + printNesting(params), + `- root + - a + - b + - c` + ); t.end(); }); -test('nest properties - basic', function(t) { - var result = toComment(function() { - /** - * @property {Object} foo - * @property {string} foo.bar - * @property {string} foo.baz - */ +test('nest params - missing parent', function(t) { + var params = ['foo', 'foo.bar.third'].map(name => ({ name })); + t.throws(() => { + nestTag(params); }); - t.equal(result[0].properties.length, 1); - t.equal(result[0].properties[0].name, 'foo'); - t.equal(result[0].properties[0].properties.length, 2); - t.equal(result[0].properties[0].properties[0].name, 'foo.bar'); - t.equal(result[0].properties[0].properties[1].name, 'foo.baz'); t.end(); }); -test('nest params - array', function(t) { - var result = toComment(function() { - /** - * @param {Object[]} employees - The employees who are responsible for the project. - * @param {string} employees[].name - The name of an employee. - * @param {string} employees[].department - The employee's department. - */ - }); - t.equal(result[0].params.length, 1); - t.equal(result[0].params[0].name, 'employees'); - t.equal(result[0].params[0].properties.length, 2); - t.equal(result[0].params[0].properties[0].name, 'employees[].name'); - t.equal(result[0].params[0].properties[1].name, 'employees[].department'); +test('nest params - #658', function(t) { + var params = [ + 'state', + 'payload', + 'payload.input_meter_levels', + 'payload.input_meter_levels[].peak', + 'payload.input_meter_levels[].rms', + 'payload.output_meter_levels', + 'payload.output_meter_levels[].peak', + 'payload.output_meter_levels[].rms' + ].map(name => ({ name })); + t.equal( + printNesting(params), + `- root + - state + - payload + - payload.input_meter_levels + - payload.input_meter_levels[].peak + - payload.input_meter_levels[].rms + - payload.output_meter_levels + - payload.output_meter_levels[].peak + - payload.output_meter_levels[].rms` + ); t.end(); }); -test('nest params - missing parent', function(t) { - var result = toComment(function() { - /** @param {string} foo.bar */ - }); - t.equal(result[0].params.length, 1); - t.deepEqual( - result[0].errors[0], - { - message: "@param foo.bar's parent foo not found", - commentLineNumber: 0 - }, - 'correct error message' +test('nest params - #554', function(t) { + var params = [ + 'x', + 'yIn', + 'options', + 'options.sgOption', + 'options.minMaxRatio', + 'options.broadRatio', + 'options.noiseLevel', + 'options.maxCriteria', + 'options.smoothY', + 'options.realTopDetection', + 'options.heightFactor', + 'options.boundaries', + 'options.derivativeThreshold' + ].map(name => ({ name })); + t.equal( + printNesting(params), + `- root + - x + - yIn + - options + - options.sgOption + - options.minMaxRatio + - options.broadRatio + - options.noiseLevel + - options.maxCriteria + - options.smoothY + - options.realTopDetection + - options.heightFactor + - options.boundaries + - options.derivativeThreshold` ); - t.equal(result[0].params[0].name, 'foo.bar'); t.end(); }); diff --git a/test/lib/parse.js b/test/lib/parse.js index 558979c2a..ebc18d566 100644 --- a/test/lib/parse.js +++ b/test/lib/parse.js @@ -115,7 +115,9 @@ test('parse - @description', function(t) { * @description This tagged description wins, and [is markdown](http://markdown.com). */ })[0].description, - remark().parse('This tagged description wins, and [is markdown](http://markdown.com).'), + remark().parse( + 'This tagged description wins, and [is markdown](http://markdown.com).' + ), 'description' );