mirror of
https://github.com/documentationjs/documentation.git
synced 2026-01-25 14:26:29 +00:00
* feat(core): Switch to Promises everywhere. Adopt Node v4 ES6 Big changes: * Uses template strings where appropriate * Config and argument parsing is unified and there is no such thing as formatterOptions anymore. All user-passed options go through mergeConfig. * The node API surface changed (again): `buildSync` is removed, building operations return Promises. * Now using Flow for internal type annotations. More changes: * Remove buildSync command * feat(inference): Partially implement object shorthand support * Refs #649 * Use Flow annotations to enforce types * Keep flow but switch to comment syntax * Clarify types * More flow improvements * Turn server into class * LinkerStack becomes class too * Fix comment description type * Run flow on lint * Many more flow fixes * More intense flow refactoring * Simplify inference steps * Update inference tests, flow errors down to 1 * Continue refining types * Fix more flow issues * Use 'use strict' everywhere * Make 'ast' property configurable * Fix many tests * Fix more tests * Fix more tests * Fix augments * Test Markdown meta support * Improve test coverage * Switch back from for of to for for speed
233 lines
6.6 KiB
JavaScript
233 lines
6.6 KiB
JavaScript
|
|
'use strict';
|
|
/* @flow */
|
|
|
|
var t = require('babel-types'),
|
|
findTarget = require('./finders').findTarget,
|
|
flowDoctrine = require('../flow_doctrine');
|
|
|
|
function addPrefix(doc, prefix) {
|
|
if (!Array.isArray(doc) && doc.name) {
|
|
doc.name = prefix + doc.name;
|
|
}
|
|
return doc;
|
|
}
|
|
|
|
/**
|
|
* Given a parameter like
|
|
*
|
|
* function a(b = 1)
|
|
*
|
|
* Format it as an optional parameter in JSDoc land
|
|
*
|
|
* @param {Object} param ESTree node
|
|
* @returns {Object} JSDoc param
|
|
*/
|
|
function paramWithDefaultToDoc(param, comment, i)/*: CommentTag | Array<CommentTag> */ {
|
|
var newParam = paramToDoc(param.left, comment, i, '');
|
|
|
|
var defaultValue = comment.context.code.substring(
|
|
param.right.start, param.right.end);
|
|
|
|
// this is a destructuring parameter with defaults
|
|
if (Array.isArray(newParam)) {
|
|
newParam[0].default = defaultValue;
|
|
return newParam;
|
|
}
|
|
|
|
var optionalParam/*: CommentTag */ = {
|
|
title: 'param',
|
|
name: newParam.name,
|
|
'default': defaultValue
|
|
};
|
|
|
|
if (newParam.type) {
|
|
optionalParam.type = {
|
|
type: 'OptionalType',
|
|
expression: newParam.type
|
|
};
|
|
}
|
|
|
|
return optionalParam;
|
|
}
|
|
|
|
/**
|
|
* Babel parses JavaScript source code and produces an abstract syntax
|
|
* tree that includes methods and their arguments. This function takes
|
|
* that AST and uses it to infer details that would otherwise need
|
|
* explicit documentation, like the names of comments and their
|
|
* default values.
|
|
*
|
|
* It is especially careful to allow the user and the machine to collaborate:
|
|
* documentation.js should not overwrite any details that the user
|
|
* explicitly sets.
|
|
*
|
|
* @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> | CommentTag */ {
|
|
|
|
function destructuringPropertyToDoc(property)/*: Array<CommentTag> | 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) + '.');
|
|
}
|
|
throw new Error(`Unknown property encountered: ${property.type}`);
|
|
}
|
|
|
|
function destructuringObjectParamToDoc(param)/*: Array<CommentTag> */ {
|
|
return [{
|
|
title: 'param',
|
|
name: '$' + String(i),
|
|
type: flowDoctrine(param) || {
|
|
type: 'NameExpression',
|
|
name: 'Object'
|
|
}
|
|
}].concat(param.properties.map(destructuringPropertyToDoc));
|
|
}
|
|
|
|
function destructuringArrayParamToDoc(param)/*: Array<CommentTag> */ {
|
|
return [{
|
|
title: 'param',
|
|
name: '$' + String(i),
|
|
type: flowDoctrine(param) || {
|
|
type: 'NameExpression',
|
|
name: 'Array'
|
|
}
|
|
}].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);
|
|
}
|
|
|
|
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));
|
|
}
|
|
|
|
/**
|
|
* Infers param tags by reading function parameter names
|
|
*
|
|
* @param {Object} comment parsed comment
|
|
* @returns {Object} comment with parameters
|
|
*/
|
|
function inferParams(comment/*: Comment */) {
|
|
var path = findTarget(comment.context.ast);
|
|
|
|
// In case of `/** */ var x = function () {}` findTarget returns
|
|
// the declarator.
|
|
if (t.isVariableDeclarator(path)) {
|
|
path = path.get('init');
|
|
}
|
|
|
|
if (!t.isFunction(path)) {
|
|
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;
|
|
}
|
|
});
|
|
|
|
var paramOrder = {};
|
|
var i = 0;
|
|
|
|
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
|
|
};
|
|
}
|
|
paramOrder[doc.name] = i++;
|
|
});
|
|
|
|
return comment;
|
|
}
|
|
|
|
module.exports = inferParams;
|