compodoc
Version:
The missing documentation tool for your Angular application
138 lines (123 loc) • 5.31 kB
text/typescript
import * as ts from 'typescript';
export function isVariableLike(node: Node): node is VariableLikeDeclaration {
if (node) {
switch (node.kind) {
case ts.SyntaxKind.BindingElement:
case ts.SyntaxKind.EnumMember:
case ts.SyntaxKind.Parameter:
case ts.SyntaxKind.PropertyAssignment:
case ts.SyntaxKind.PropertyDeclaration:
case ts.SyntaxKind.PropertySignature:
case ts.SyntaxKind.ShorthandPropertyAssignment:
case ts.SyntaxKind.VariableDeclaration:
return true;
}
}
return false;
}
export function some<T>(array: T[], predicate?: (value: T) => boolean): boolean {
if (array) {
if (predicate) {
for (const v of array) {
if (predicate(v)) {
return true;
}
}
}
else {
return array.length > 0;
}
}
return false;
}
export function concatenate<T>(array1: T[], array2: T[]): T[] {
if (!some(array2)) return array1;
if (!some(array1)) return array2;
return [...array1, ...array2];
}
export function isParameter(node: Node): node is ParameterDeclaration {
return node.kind === ts.SyntaxKind.Parameter;
}
export function getJSDocParameterTags(param: Node): JSDocParameterTag[] {
if (!isParameter(param)) {
return undefined;
}
const func = param.parent as FunctionLikeDeclaration;
const tags = getJSDocTags(func, ts.SyntaxKind.JSDocParameterTag) as JSDocParameterTag[];
if (!param.name) {
// this is an anonymous jsdoc param from a `function(type1, type2): type3` specification
const i = func.parameters.indexOf(param);
const paramTags = filter(tags, tag => tag.kind === ts.SyntaxKind.JSDocParameterTag);
if (paramTags && 0 <= i && i < paramTags.length) {
return [paramTags[i]];
}
}
else if (param.name.kind === ts.SyntaxKind.Identifier) {
const name = (param.name as Identifier).text;
return filter(tags, tag => tag.kind === ts.SyntaxKind.JSDocParameterTag && tag.parameterName.text === name);
}
else {
// TODO: it's a destructured parameter, so it should look up an "object type" series of multiple lines
// But multi-line object types aren't supported yet either
return undefined;
}
}
export let JSDocTagsParser = (function() {
let _getJSDocs = (node: Node):(JSDoc | JSDocTag)[] => {
//console.log('getJSDocs: ', node);
let cache: (JSDoc | JSDocTag)[] = node.jsDocCache;
if (!cache) {
getJSDocsWorker(node);
node.jsDocCache = cache;
}
return cache;
function getJSDocsWorker(node: Node) {
const parent = node.parent;
// Try to recognize this pattern when node is initializer of variable declaration and JSDoc comments are on containing variable statement.
// /**
// * @param {number} name
// * @returns {number}
// */
// var x = function(name) { return name.length; }
const isInitializerOfVariableDeclarationInStatement =
isVariableLike(parent) &&
parent.initializer === node &&
parent.parent.parent.kind === ts.SyntaxKind.VariableStatement;
const isVariableOfVariableDeclarationStatement = isVariableLike(node) &&
parent.parent.kind === ts.SyntaxKind.VariableStatement;
const variableStatementNode =
isInitializerOfVariableDeclarationInStatement ? parent.parent.parent :
isVariableOfVariableDeclarationStatement ? parent.parent :
undefined;
if (variableStatementNode) {
getJSDocsWorker(variableStatementNode);
}
// Also recognize when the node is the RHS of an assignment expression
const isSourceOfAssignmentExpressionStatement =
parent && parent.parent &&
parent.kind === ts.SyntaxKind.BinaryExpression &&
(parent as BinaryExpression).operatorToken.kind === ts.SyntaxKind.EqualsToken &&
parent.parent.kind === ts.SyntaxKind.ExpressionStatement;
if (isSourceOfAssignmentExpressionStatement) {
getJSDocsWorker(parent.parent);
}
const isModuleDeclaration = node.kind === ts.SyntaxKind.ModuleDeclaration &&
parent && parent.kind === ts.SyntaxKind.ModuleDeclaration;
const isPropertyAssignmentExpression = parent && parent.kind === ts.SyntaxKind.PropertyAssignment;
if (isModuleDeclaration || isPropertyAssignmentExpression) {
getJSDocsWorker(parent);
}
// Pull parameter comments from declaring function as well
if (node.kind === ts.SyntaxKind.Parameter) {
cache = concatenate(cache, getJSDocParameterTags(node));
}
if (isVariableLike(node) && node.initializer) {
cache = concatenate(cache, node.initializer.jsDoc);
}
cache = concatenate(cache, node.jsDoc);
}
}
return {
getJSDocs: _getJSDocs
}
})();