@compodoc/compodoc
Version:
The missing documentation tool for your Angular application
1,231 lines (1,146 loc) • 62.9 kB
text/typescript
import * as _ from 'lodash';
import { ts, SyntaxKind } from 'ts-morph';
import { getNamesCompareFn, mergeTagsAndArgs, markedtags } from '../../../../../utils/utils';
import { kindToType } from '../../../../../utils/kind-to-type';
import { JsdocParserUtil } from '../../../../../utils/jsdoc-parser.util';
import { isIgnore } from '../../../../../utils';
import AngularVersionUtil from '../../../../..//utils/angular-version.util';
import BasicTypeUtil from '../../../../../utils/basic-type.util';
import { StringifyObjectLiteralExpression } from '../../../../../utils/object-literal-expression.util';
import DependenciesEngine from '../../../../engines/dependencies.engine';
import Configuration from '../../../../configuration';
import { StringifyArrowFunction } from '../../../../../utils/arrow-function.util';
const crypto = require('crypto');
const { marked } = require('marked');
export class ClassHelper {
private jsdocParserUtil = new JsdocParserUtil();
constructor(private typeChecker: ts.TypeChecker) {}
/**
* HELPERS
*/
public stringifyDefaultValue(node: ts.Node): string {
/**
* Copyright https://github.com/ng-bootstrap/ng-bootstrap
*/
if (node.getText()) {
return node.getText();
} else if (node.kind === SyntaxKind.FalseKeyword) {
return 'false';
} else if (node.kind === SyntaxKind.TrueKeyword) {
return 'true';
}
}
private checkForDeprecation(tags: any[], result: { [key in string | number]: any }) {
_.forEach(tags, tag => {
if (tag.tagName && tag.tagName.text && tag.tagName.text.indexOf('deprecated') > -1) {
result.deprecated = true;
result.deprecationMessage = tag.comment || '';
}
});
}
private getDecoratorOfType(node, decoratorType) {
let decorators = node.decorators || [];
let result = [];
const len = decorators.length;
if (len > 1) {
for (let i = 0; i < decorators.length; i++) {
if (decorators[i].expression.expression) {
if (decorators[i].expression.expression.text === decoratorType) {
result.push(decorators[i]);
}
}
}
if (result.length > 0) {
return result;
}
} else {
if (len === 1 && decorators[0].expression && decorators[0].expression.expression) {
if (decorators[0].expression.expression.text === decoratorType) {
result.push(decorators[0]);
return result;
}
}
}
return undefined;
}
private formatDecorators(decorators) {
let _decorators = [];
_.forEach(decorators, (decorator: any) => {
if (decorator.expression) {
if (decorator.expression.text) {
_decorators.push({ name: decorator.expression.text });
}
if (decorator.expression.expression) {
let info: any = { name: decorator.expression.expression.text };
if (decorator.expression.arguments) {
info.stringifiedArguments = this.stringifyArguments(
decorator.expression.arguments
);
}
_decorators.push(info);
}
}
});
return _decorators;
}
private handleFunction(arg): string {
if (arg.function.length === 0) {
return `${arg.name}${this.getOptionalString(arg)}: () => void`;
}
let argums = arg.function.map(argu => {
let _result = DependenciesEngine.find(argu.type);
if (_result) {
if (_result.source === 'internal') {
let path = _result.data.type;
if (_result.data.type === 'class') {
path = 'classe';
}
return `${argu.name}${this.getOptionalString(arg)}: <a href="../${path}s/${
_result.data.name
}.html">${argu.type}</a>`;
} else {
let path = AngularVersionUtil.getApiLink(
_result.data,
Configuration.mainData.angularVersion
);
return `${argu.name}${this.getOptionalString(
arg
)}: <a href="${path}" target="_blank">${argu.type}</a>`;
}
} else if (BasicTypeUtil.isKnownType(argu.type)) {
let path = BasicTypeUtil.getTypeUrl(argu.type);
return `${argu.name}${this.getOptionalString(
arg
)}: <a href="${path}" target="_blank">${argu.type}</a>`;
} else {
if (argu.name && argu.type) {
return `${argu.name}${this.getOptionalString(arg)}: ${argu.type}`;
} else {
if (argu.name) {
return `${argu.name.text}`;
} else {
return '';
}
}
}
});
return `${arg.name}${this.getOptionalString(arg)}: (${argums}) => void`;
}
private getOptionalString(arg): string {
return arg.optional ? '?' : '';
}
private stringifyArguments(args) {
let stringifyArgs = [];
stringifyArgs = args
.map(arg => {
const _result = DependenciesEngine.find(arg.type);
if (_result) {
if (_result.source === 'internal') {
let path = _result.data.type;
if (_result.data.type === 'class') {
path = 'classe';
}
return `${arg.name}${this.getOptionalString(arg)}: <a href="../${path}s/${
_result.data.name
}.html">${arg.type}</a>`;
} else {
let path = AngularVersionUtil.getApiLink(
_result.data,
Configuration.mainData.angularVersion
);
return `${arg.name}${this.getOptionalString(
arg
)}: <a href="${path}" target="_blank">${arg.type}</a>`;
}
} else if (arg.dotDotDotToken) {
return `...${arg.name}: ${arg.type}`;
} else if (arg.function) {
return this.handleFunction(arg);
} else if (arg.expression && arg.name) {
return arg.expression.text + '.' + arg.name.text;
} else if (arg.expression && arg.kind === SyntaxKind.NewExpression) {
return 'new ' + arg.expression.text + '()';
} else if (arg.kind && arg.kind === SyntaxKind.StringLiteral) {
return `'` + arg.text + `'`;
} else if (
arg.kind &&
arg.kind === SyntaxKind.ArrayLiteralExpression &&
arg.elements &&
arg.elements.length > 0
) {
let i = 0,
len = arg.elements.length,
result = '[';
for (i; i < len; i++) {
result += `'` + arg.elements[i].text + `'`;
if (i < len - 1) {
result += ', ';
}
}
result += ']';
return result;
} else if (
arg.kind &&
arg.kind === SyntaxKind.ArrowFunction &&
arg.parameters &&
arg.parameters.length > 0
) {
return StringifyArrowFunction(arg);
} else if (arg.kind && arg.kind === SyntaxKind.ObjectLiteralExpression) {
return StringifyObjectLiteralExpression(arg);
} else if (BasicTypeUtil.isKnownType(arg.type)) {
const path = BasicTypeUtil.getTypeUrl(arg.type);
return `${arg.name}${this.getOptionalString(
arg
)}: <a href="${path}" target="_blank">${arg.type}</a>`;
} else {
if (arg.type) {
let finalStringifiedArgument = '';
let separator = ':';
if (arg.name) {
finalStringifiedArgument += arg.name;
}
if (
arg.kind === SyntaxKind.AsExpression &&
arg.expression &&
arg.expression.text
) {
finalStringifiedArgument += arg.expression.text;
separator = ' as';
}
if (arg.optional) {
finalStringifiedArgument += this.getOptionalString(arg);
}
if (arg.type) {
finalStringifiedArgument += separator + ' ' + this.visitType(arg.type);
}
return finalStringifiedArgument;
} else if (arg.text) {
return `${arg.text}`;
} else {
return `${arg.name}${this.getOptionalString(arg)}`;
}
}
})
.join(', ');
return stringifyArgs;
}
private getPosition(node: ts.Node, sourceFile: ts.SourceFile): ts.LineAndCharacter {
let position: ts.LineAndCharacter;
if (node.name && node.name.end) {
position = ts.getLineAndCharacterOfPosition(sourceFile, node.name.end);
} else {
position = ts.getLineAndCharacterOfPosition(sourceFile, node.pos);
}
return position;
}
private addAccessor(accessors, nodeAccessor, sourceFile) {
let nodeName = '';
if (nodeAccessor.name) {
nodeName = nodeAccessor.name.text;
let jsdoctags = this.jsdocParserUtil.getJSDocs(nodeAccessor);
if (!accessors[nodeName]) {
accessors[nodeName] = {
name: nodeName,
setSignature: undefined,
getSignature: undefined
};
}
if (nodeAccessor.kind === SyntaxKind.SetAccessor) {
let setSignature = {
name: nodeName,
type: 'void',
deprecated: false,
deprecationMessage: '',
args: nodeAccessor.parameters.map(param => this.visitArgument(param)),
returnType: nodeAccessor.type ? this.visitType(nodeAccessor.type) : 'void',
line: this.getPosition(nodeAccessor, sourceFile).line + 1
};
if (nodeAccessor.jsDoc && nodeAccessor.jsDoc.length >= 1) {
const comment = this.jsdocParserUtil.getMainCommentOfNode(
nodeAccessor,
sourceFile
);
if (typeof comment !== 'undefined') {
const cleanedDescription = this.jsdocParserUtil.parseComment(comment);
setSignature.rawdescription = cleanedDescription;
setSignature.description = marked(cleanedDescription);
}
}
if (jsdoctags && jsdoctags.length >= 1 && jsdoctags[0].tags) {
this.checkForDeprecation(jsdoctags[0].tags, setSignature);
setSignature.jsdoctags = markedtags(jsdoctags[0].tags);
}
if (setSignature.jsdoctags && setSignature.jsdoctags.length > 0) {
setSignature.jsdoctags = mergeTagsAndArgs(
setSignature.args,
setSignature.jsdoctags
);
} else if (setSignature.args && setSignature.args.length > 0) {
setSignature.jsdoctags = mergeTagsAndArgs(setSignature.args);
}
accessors[nodeName].setSignature = setSignature;
}
if (nodeAccessor.kind === SyntaxKind.GetAccessor) {
let getSignature = {
name: nodeName,
type: nodeAccessor.type ? kindToType(nodeAccessor.type.kind) : '',
returnType: nodeAccessor.type ? this.visitType(nodeAccessor.type) : '',
line: this.getPosition(nodeAccessor, sourceFile).line + 1
};
if (nodeAccessor.jsDoc && nodeAccessor.jsDoc.length >= 1) {
const comment = this.jsdocParserUtil.getMainCommentOfNode(
nodeAccessor,
sourceFile
);
if (typeof comment !== 'undefined') {
const cleanedDescription = this.jsdocParserUtil.parseComment(comment);
getSignature.rawdescription = cleanedDescription;
getSignature.description = marked(cleanedDescription);
}
}
if (jsdoctags && jsdoctags.length >= 1 && jsdoctags[0].tags) {
this.checkForDeprecation(jsdoctags[0].tags, getSignature);
getSignature.jsdoctags = markedtags(jsdoctags[0].tags);
}
accessors[nodeName].getSignature = getSignature;
}
}
}
private isDirectiveDecorator(decorator: ts.Decorator): boolean {
if (decorator.expression.expression) {
let decoratorIdentifierText = decorator.expression.expression.text;
return (
decoratorIdentifierText === 'Directive' || decoratorIdentifierText === 'Component'
);
} else {
return false;
}
}
private isServiceDecorator(decorator) {
return decorator.expression.expression
? decorator.expression.expression.text === 'Injectable'
: false;
}
private isPrivate(member): boolean {
/**
* Copyright https://github.com/ng-bootstrap/ng-bootstrap
*/
if (member.modifiers) {
const isPrivate: boolean = member.modifiers.some(
modifier => modifier.kind === SyntaxKind.PrivateKeyword
);
if (isPrivate) {
return true;
}
}
// Check for ECMAScript Private Fields
if (member.name && member.name.escapedText) {
const isPrivate: boolean = member.name.escapedText.indexOf('#') === 0;
if (isPrivate) {
return true;
}
}
return this.isHiddenMember(member);
}
private isProtected(member): boolean {
if (member.modifiers) {
const isProtected: boolean = member.modifiers.some(
modifier => modifier.kind === SyntaxKind.ProtectedKeyword
);
if (isProtected) {
return true;
}
}
return this.isHiddenMember(member);
}
private isInternal(member): boolean {
/**
* Copyright https://github.com/ng-bootstrap/ng-bootstrap
*/
const internalTags: string[] = ['internal'];
if (member.jsDoc) {
for (const doc of member.jsDoc) {
if (doc.tags) {
for (const tag of doc.tags) {
if (internalTags.indexOf(tag.tagName.text) > -1) {
return true;
}
}
}
}
}
return false;
}
private isPublic(member): boolean {
if (member.modifiers) {
const isPublic: boolean = member.modifiers.some(
modifier => modifier.kind === SyntaxKind.PublicKeyword
);
if (isPublic) {
return true;
}
}
return this.isHiddenMember(member);
}
private isHiddenMember(member): boolean {
/**
* Copyright https://github.com/ng-bootstrap/ng-bootstrap
*/
const internalTags: string[] = ['hidden'];
if (member.jsDoc) {
for (const doc of member.jsDoc) {
if (doc.tags) {
for (const tag of doc.tags) {
if (internalTags.indexOf(tag.tagName.text) > -1) {
return true;
}
}
}
}
}
return false;
}
private isPipeDecorator(decorator) {
return decorator.expression.expression
? decorator.expression.expression.text === 'Pipe'
: false;
}
private isControllerDecorator(decorator) {
return decorator.expression.expression
? decorator.expression.expression.text === 'Controller'
: false;
}
private isModuleDecorator(decorator) {
return decorator.expression.expression
? decorator.expression.expression.text === 'NgModule' ||
decorator.expression.expression.text === 'Module'
: false;
}
/**
* VISITERS
*/
public visitClassDeclaration(
fileName: string,
classDeclaration: ts.ClassDeclaration | ts.InterfaceDeclaration,
sourceFile?: ts.SourceFile
): any {
let symbol = this.typeChecker.getSymbolAtLocation(classDeclaration.name);
let rawdescription = '';
let deprecated = false;
let deprecationMessage = '';
let description = '';
let jsdoctags = [];
if (symbol) {
const comment = this.jsdocParserUtil.getMainCommentOfNode(classDeclaration, sourceFile);
rawdescription = this.jsdocParserUtil.parseComment(comment);
description = marked(rawdescription);
if (symbol.valueDeclaration && isIgnore(symbol.valueDeclaration)) {
return [{ ignore: true }];
}
if (symbol.declarations && symbol.declarations.length > 0) {
let declarationsjsdoctags = this.jsdocParserUtil.getJSDocs(symbol.declarations[0]);
if (
declarationsjsdoctags &&
declarationsjsdoctags.length >= 1 &&
declarationsjsdoctags[0].tags
) {
const deprecation = { deprecated: false, deprecationMessage: '' };
this.checkForDeprecation(declarationsjsdoctags[0].tags, deprecation);
deprecated = deprecation.deprecated;
deprecationMessage = deprecation.deprecationMessage;
}
if (isIgnore(symbol.declarations[0])) {
return [{ ignore: true }];
}
}
if (symbol.valueDeclaration) {
jsdoctags = this.jsdocParserUtil.getJSDocs(symbol.valueDeclaration);
if (jsdoctags && jsdoctags.length >= 1 && jsdoctags[0].tags) {
const deprecation = { deprecated: false, deprecationMessage: '' };
this.checkForDeprecation(jsdoctags[0].tags, deprecation);
deprecated = deprecation.deprecated;
deprecationMessage = deprecation.deprecationMessage;
jsdoctags = markedtags(jsdoctags[0].tags);
}
}
}
let className = classDeclaration.name.text;
let members;
let implementsElements = [];
let extendsElement;
if (typeof ts.getEffectiveImplementsTypeNodes !== 'undefined') {
let implementedTypes = ts.getEffectiveImplementsTypeNodes(classDeclaration);
if (implementedTypes) {
let i = 0;
let len = implementedTypes.length;
for (i; i < len; i++) {
if (implementedTypes[i].expression) {
implementsElements.push(implementedTypes[i].expression.text);
}
}
}
}
if (typeof ts.getClassExtendsHeritageElement !== 'undefined') {
let extendsTypes = ts.getClassExtendsHeritageElement(classDeclaration);
if (extendsTypes) {
if (extendsTypes.expression) {
extendsElement = extendsTypes.expression.text;
}
}
}
members = this.visitMembers(classDeclaration.members, sourceFile);
if (classDeclaration.decorators) {
// Loop and search for official decorators at top-level :
// Angular : @NgModule, @Component, @Directive, @Injectable, @Pipe
// Nestjs : @Controller, @Module, @Injectable
// Stencil : @Component
let isDirective = false;
let isService = false;
let isPipe = false;
let isModule = false;
let isController = false;
for (let a = 0; a < classDeclaration.decorators.length; a++) {
//console.log(classDeclaration.decorators[i].expression);
// RETURN TOO EARLY FOR MANY DECORATORS !!!!
// iterating through the decorators array we have to keep the flags `true` values from the previous loop iteration
isDirective =
isDirective || this.isDirectiveDecorator(classDeclaration.decorators[a]);
isService = isService || this.isServiceDecorator(classDeclaration.decorators[a]);
isPipe = isPipe || this.isPipeDecorator(classDeclaration.decorators[a]);
isModule = isModule || this.isModuleDecorator(classDeclaration.decorators[a]);
isController =
isController || this.isControllerDecorator(classDeclaration.decorators[a]);
}
if (isDirective) {
return {
deprecated,
deprecationMessage,
description,
rawdescription: rawdescription,
inputs: members.inputs,
outputs: members.outputs,
hostBindings: members.hostBindings,
hostListeners: members.hostListeners,
properties: members.properties,
methods: members.methods,
indexSignatures: members.indexSignatures,
kind: members.kind,
constructor: members.constructor,
jsdoctags: jsdoctags,
extends: extendsElement,
implements: implementsElements,
accessors: members.accessors
};
} else if (isService) {
return [
{
fileName,
className,
deprecated,
deprecationMessage,
description,
rawdescription: rawdescription,
methods: members.methods,
indexSignatures: members.indexSignatures,
properties: members.properties,
kind: members.kind,
constructor: members.constructor,
jsdoctags: jsdoctags,
extends: extendsElement,
implements: implementsElements,
accessors: members.accessors
}
];
} else if (isPipe) {
return [
{
fileName,
className,
deprecated,
deprecationMessage,
description,
rawdescription: rawdescription,
jsdoctags: jsdoctags,
properties: members.properties,
methods: members.methods
}
];
} else if (isModule) {
return [
{
fileName,
className,
deprecated,
deprecationMessage,
description,
rawdescription: rawdescription,
jsdoctags: jsdoctags,
methods: members.methods
}
];
} else {
return [
{
deprecated,
deprecationMessage,
description,
rawdescription: rawdescription,
methods: members.methods,
indexSignatures: members.indexSignatures,
properties: members.properties,
kind: members.kind,
constructor: members.constructor,
jsdoctags: jsdoctags,
extends: extendsElement,
implements: implementsElements,
accessors: members.accessors
}
];
}
} else if (description) {
return [
{
deprecated,
deprecationMessage,
description,
rawdescription: rawdescription,
inputs: members.inputs,
outputs: members.outputs,
hostBindings: members.hostBindings,
hostListeners: members.hostListeners,
methods: members.methods,
indexSignatures: members.indexSignatures,
properties: members.properties,
kind: members.kind,
constructor: members.constructor,
jsdoctags: jsdoctags,
extends: extendsElement,
implements: implementsElements,
accessors: members.accessors
}
];
} else {
return [
{
deprecated,
deprecationMessage,
methods: members.methods,
inputs: members.inputs,
outputs: members.outputs,
hostBindings: members.hostBindings,
hostListeners: members.hostListeners,
indexSignatures: members.indexSignatures,
properties: members.properties,
kind: members.kind,
constructor: members.constructor,
jsdoctags: jsdoctags,
extends: extendsElement,
implements: implementsElements,
accessors: members.accessors
}
];
}
return [];
}
private visitMembers(members, sourceFile) {
/**
* Copyright https://github.com/ng-bootstrap/ng-bootstrap
*/
let inputs = [];
let outputs = [];
let methods = [];
let properties = [];
let indexSignatures = [];
let kind;
let inputDecorator;
let hostBindings = [];
let hostListeners = [];
let constructor;
let outputDecorator;
let accessors = {};
let result = {};
for (let i = 0; i < members.length; i++) {
// Allows typescript guess type when using ts.is*
let member = members[i];
inputDecorator = this.getDecoratorOfType(member, 'Input');
outputDecorator = this.getDecoratorOfType(member, 'Output');
const parsedHostBindings = this.getDecoratorOfType(member, 'HostBinding');
const parsedHostListeners = this.getDecoratorOfType(member, 'HostListener');
kind = member.kind;
if (isIgnore(member)) {
continue;
}
if (inputDecorator && inputDecorator.length > 0) {
inputs.push(this.visitInputAndHostBinding(member, inputDecorator[0], sourceFile));
if (ts.isSetAccessorDeclaration(member)) {
this.addAccessor(accessors, members[i], sourceFile);
}
} else if (outputDecorator && outputDecorator.length > 0) {
outputs.push(this.visitOutput(member, outputDecorator[0], sourceFile));
} else if (parsedHostBindings && parsedHostBindings.length > 0) {
let k = 0,
lenHB = parsedHostBindings.length;
for (k; k < lenHB; k++) {
hostBindings.push(
this.visitInputAndHostBinding(member, parsedHostBindings[k], sourceFile)
);
}
} else if (parsedHostListeners && parsedHostListeners.length > 0) {
let l = 0,
lenHL = parsedHostListeners.length;
for (l; l < lenHL; l++) {
hostListeners.push(
this.visitHostListener(member, parsedHostListeners[l], sourceFile)
);
}
}
if (!this.isHiddenMember(member)) {
if (!(this.isPrivate(member) && Configuration.mainData.disablePrivate)) {
if (!(this.isInternal(member) && Configuration.mainData.disableInternal)) {
if (
!(this.isProtected(member) && Configuration.mainData.disableProtected)
) {
if (ts.isMethodDeclaration(member) || ts.isMethodSignature(member)) {
methods.push(this.visitMethodDeclaration(member, sourceFile));
} else if (
ts.isPropertyDeclaration(member) ||
ts.isPropertySignature(member)
) {
if (!inputDecorator && !outputDecorator) {
properties.push(this.visitProperty(member, sourceFile));
}
} else if (ts.isCallSignatureDeclaration(member)) {
properties.push(this.visitCallDeclaration(member, sourceFile));
} else if (
ts.isGetAccessorDeclaration(member) ||
ts.isSetAccessorDeclaration(member)
) {
this.addAccessor(accessors, members[i], sourceFile);
} else if (ts.isIndexSignatureDeclaration(member)) {
indexSignatures.push(
this.visitIndexDeclaration(member, sourceFile)
);
} else if (ts.isConstructorDeclaration(member)) {
let _constructorProperties = this.visitConstructorProperties(
member,
sourceFile
);
let j = 0;
let len = _constructorProperties.length;
for (j; j < len; j++) {
properties.push(_constructorProperties[j]);
}
constructor = this.visitConstructorDeclaration(member, sourceFile);
}
}
}
}
}
}
inputs.sort(getNamesCompareFn());
outputs.sort(getNamesCompareFn());
hostBindings.sort(getNamesCompareFn());
hostListeners.sort(getNamesCompareFn());
properties.sort(getNamesCompareFn());
methods.sort(getNamesCompareFn());
indexSignatures.sort(getNamesCompareFn());
result = {
inputs,
outputs,
hostBindings,
hostListeners,
methods,
properties,
indexSignatures,
kind,
constructor
};
if (Object.keys(accessors).length) {
result['accessors'] = accessors;
}
return result;
}
private visitTypeName(typeName: ts.Identifier) {
if (typeName.escapedText) {
return typeName.escapedText;
}
if (typeName.text) {
return typeName.text;
}
if (typeName.left && typeName.right) {
return this.visitTypeName(typeName.left) + '.' + this.visitTypeName(typeName.right);
}
return '';
}
public visitType(node): string {
let _return = 'void';
if (!node) {
return _return;
}
if (node.typeName) {
_return = this.visitTypeName(node.typeName);
} else if (node.type) {
if (node.type.kind) {
_return = kindToType(node.type.kind);
}
if (node.type.typeName) {
_return = this.visitTypeName(node.type.typeName);
}
if (node.type.typeArguments) {
_return += '<';
const typeArguments = [];
for (const argument of node.type.typeArguments) {
typeArguments.push(this.visitType(argument));
}
_return += typeArguments.join(' | ');
_return += '>';
}
if (node.type.elementType) {
const _firstPart = this.visitType(node.type.elementType);
_return = _firstPart + kindToType(node.type.kind);
if (node.type.elementType.kind === SyntaxKind.ParenthesizedType) {
_return = '(' + _firstPart + ')' + kindToType(node.type.kind);
}
}
const parseTypesOrElements = (arr, separator) => {
let i = 0;
let len = arr.length;
for (i; i < len; i++) {
let type = arr[i];
if (type.elementType) {
const _firstPart = this.visitType(type.elementType);
if (type.elementType.kind === SyntaxKind.ParenthesizedType) {
_return += '(' + _firstPart + ')' + kindToType(type.kind);
} else {
_return += _firstPart + kindToType(type.kind);
}
} else {
if (ts.isLiteralTypeNode(type) && type.literal) {
if (type.literal.text) {
_return += '"' + type.literal.text + '"';
} else {
_return += kindToType(type.literal.kind);
}
} else {
_return += kindToType(type.kind);
}
if (type.typeName) {
_return += this.visitTypeName(type.typeName);
}
if (type.kind === SyntaxKind.RestType && type.type) {
_return += '...' + this.visitType(type.type);
}
if (type.typeArguments) {
_return += '<';
const typeArguments = [];
for (const argument of type.typeArguments) {
typeArguments.push(this.visitType(argument));
}
_return += typeArguments.join(separator);
_return += '>';
}
}
if (i < len - 1) {
_return += separator;
}
}
};
if (node.type.elements && ts.isTupleTypeNode(node.type)) {
_return += '[';
parseTypesOrElements(node.type.elements, ', ');
_return += ']';
}
if (node.type.types && ts.isUnionTypeNode(node.type)) {
parseTypesOrElements(node.type.types, ' | ');
}
if (node.type.elementTypes) {
let elementTypes = node.type.elementTypes;
let i = 0;
let len = elementTypes.length;
if (len > 0) {
_return = '[';
for (i; i < len; i++) {
let type = elementTypes[i];
if (type.kind === SyntaxKind.ArrayType && type.elementType) {
_return += kindToType(type.elementType.kind);
_return += kindToType(type.kind);
} else {
_return += kindToType(type.kind);
}
if (ts.isLiteralTypeNode(type) && type.literal) {
if (type.literal.text) {
_return += '"' + type.literal.text + '"';
} else {
_return += kindToType(type.literal.kind);
}
}
if (type.typeName) {
_return += this.visitTypeName(type.typeName);
}
if (type.kind === SyntaxKind.RestType && type.type) {
_return += '...' + this.visitType(type.type);
}
if (
type.kind === SyntaxKind.TypeReference &&
type.typeName &&
typeof type.typeName.escapedText !== 'undefined' &&
type.typeName.escapedText === ''
) {
continue;
}
if (i < len - 1) {
_return += ', ';
}
}
_return += ']';
}
}
} else if (node.elementType) {
_return = kindToType(node.elementType.kind) + kindToType(node.kind);
if (node.elementType.typeName) {
_return = this.visitTypeName(node.elementType.typeName) + kindToType(node.kind);
}
} else if (node.types && ts.isUnionTypeNode(node)) {
_return = '';
let i = 0;
let len = node.types.length;
for (i; i < len; i++) {
let type = node.types[i];
_return += kindToType(type.kind);
if (ts.isLiteralTypeNode(type) && type.literal) {
if (type.literal.text) {
_return += '"' + type.literal.text + '"';
} else {
_return += kindToType(type.literal.kind);
}
}
if (type.typeName) {
_return += this.visitTypeName(type.typeName);
}
if (i < len - 1) {
_return += ' | ';
}
}
} else if (node.dotDotDotToken) {
_return = 'any[]';
} else {
_return = kindToType(node.kind);
if (
_return === '' &&
node.initializer &&
node.initializer.kind &&
(node.kind === SyntaxKind.PropertyDeclaration || node.kind === SyntaxKind.Parameter)
) {
_return = kindToType(node.initializer.kind);
}
if (node.kind === SyntaxKind.TypeParameter) {
_return = node.name.text;
}
if (node.kind === SyntaxKind.LiteralType) {
_return = node.literal.text;
}
}
if (node.typeArguments && node.typeArguments.length > 0) {
_return += '<';
let i = 0,
len = node.typeArguments.length;
for (i; i < len; i++) {
let argument = node.typeArguments[i];
_return += this.visitType(argument);
if (i >= 0 && i < len - 1) {
_return += ', ';
}
}
_return += '>';
}
return _return;
}
private visitCallDeclaration(method: ts.CallSignatureDeclaration, sourceFile: ts.SourceFile) {
let sourceCode = sourceFile.getText();
let hash = crypto.createHash('sha512').update(sourceCode).digest('hex');
let result: any = {
id: 'call-declaration-' + hash,
args: method.parameters ? method.parameters.map(prop => this.visitArgument(prop)) : [],
returnType: this.visitType(method.type),
line: this.getPosition(method, sourceFile).line + 1,
deprecated: false,
deprecationMessage: ''
};
if (method.jsDoc) {
const comment = this.jsdocParserUtil.getMainCommentOfNode(method, sourceFile);
const cleanedDescription = this.jsdocParserUtil.parseComment(comment);
result.rawdescription = cleanedDescription;
result.description = marked(cleanedDescription);
}
let jsdoctags = this.jsdocParserUtil.getJSDocs(method);
if (jsdoctags && jsdoctags.length >= 1 && jsdoctags[0].tags) {
this.checkForDeprecation(jsdoctags[0].tags, result);
result.jsdoctags = markedtags(jsdoctags[0].tags);
}
return result;
}
private visitIndexDeclaration(
method: ts.IndexSignatureDeclaration,
sourceFile?: ts.SourceFile
) {
let sourceCode = sourceFile.getText();
let hash = crypto.createHash('sha512').update(sourceCode).digest('hex');
let result = {
id: 'index-declaration-' + hash,
args: method.parameters ? method.parameters.map(prop => this.visitArgument(prop)) : [],
returnType: this.visitType(method.type),
line: this.getPosition(method, sourceFile).line + 1,
deprecated: false,
deprecationMessage: ''
};
const jsdoctags = this.jsdocParserUtil.getJSDocs(method);
if (method.jsDoc) {
const comment = this.jsdocParserUtil.getMainCommentOfNode(method, sourceFile);
const cleanedDescription = this.jsdocParserUtil.parseComment(comment);
result.rawdescription = cleanedDescription;
result.description = marked(cleanedDescription);
}
if (jsdoctags && jsdoctags.length >= 1) {
if (jsdoctags[0].tags) {
this.checkForDeprecation(jsdoctags[0].tags, result);
if (method.jsDoc) {
result.jsdoctags = markedtags(jsdoctags[0].tags);
}
}
}
return result;
}
private visitConstructorDeclaration(
method: ts.ConstructorDeclaration,
sourceFile?: ts.SourceFile
) {
/**
* Copyright https://github.com/ng-bootstrap/ng-bootstrap
*/
let result: any = {
name: 'constructor',
description: '',
deprecated: false,
deprecationMessage: '',
args: method.parameters ? method.parameters.map(prop => this.visitArgument(prop)) : [],
line: this.getPosition(method, sourceFile).line + 1
};
let jsdoctags = this.jsdocParserUtil.getJSDocs(method);
if (method.jsDoc) {
const comment = this.jsdocParserUtil.getMainCommentOfNode(method, sourceFile);
const cleanedDescription = this.jsdocParserUtil.parseComment(comment);
result.rawdescription = cleanedDescription;
result.description = marked(cleanedDescription);
}
if (method.modifiers) {
if (method.modifiers.length > 0) {
let kinds = method.modifiers.map(modifier => {
return modifier.kind;
});
if (
_.indexOf(kinds, SyntaxKind.PublicKeyword) !== -1 &&
_.indexOf(kinds, SyntaxKind.StaticKeyword) !== -1
) {
kinds = kinds.filter(kind => kind !== SyntaxKind.PublicKeyword);
}
result.modifierKind = kinds;
}
}
if (jsdoctags && jsdoctags.length >= 1 && jsdoctags[0].tags) {
this.checkForDeprecation(jsdoctags[0].tags, result);
result.jsdoctags = markedtags(jsdoctags[0].tags);
}
if (result.jsdoctags && result.jsdoctags.length > 0) {
result.jsdoctags = mergeTagsAndArgs(result.args, result.jsdoctags);
} else if (result.args.length > 0) {
result.jsdoctags = mergeTagsAndArgs(result.args);
}
return result;
}
private visitProperty(property: ts.PropertyDeclaration, sourceFile) {
let result: any = {
name: property.name.text,
defaultValue: property.initializer
? this.stringifyDefaultValue(property.initializer)
: undefined,
deprecated: false,
deprecationMessage: '',
type: this.visitType(property),
optional: typeof property.questionToken !== 'undefined',
description: '',
line: this.getPosition(property, sourceFile).line + 1
};
let jsdoctags;
if (property.initializer && property.initializer.kind === SyntaxKind.ArrowFunction) {
result.defaultValue = '() => {...}';
}
if (typeof result.name === 'undefined' && typeof property.name.expression !== 'undefined') {
result.name = property.name.expression.text;
}
jsdoctags = this.jsdocParserUtil.getJSDocs(property);
if (property.jsDoc) {
const comment = this.jsdocParserUtil.getMainCommentOfNode(property, sourceFile);
const cleanedDescription = this.jsdocParserUtil.parseComment(comment);
result.rawdescription = cleanedDescription;
result.description = marked(cleanedDescription);
}
if (property.decorators) {
result.decorators = this.formatDecorators(property.decorators);
}
if (property.modifiers) {
if (property.modifiers.length > 0) {
let kinds = property.modifiers.map(modifier => {
return modifier.kind;
});
if (
_.indexOf(kinds, SyntaxKind.PublicKeyword) !== -1 &&
_.indexOf(kinds, SyntaxKind.StaticKeyword) !== -1
) {
kinds = kinds.filter(kind => kind !== SyntaxKind.PublicKeyword);
}
result.modifierKind = kinds;
}
}
// Check for ECMAScript Private Fields
if (this.isPrivate(property)) {
if (!result.modifierKind) {
result.modifierKind = [];
}
let hasAlreadyPrivateLeyword = false;
result.modifierKind.forEach(modifierKind => {
if (modifierKind === SyntaxKind.PrivateKeyword) {
hasAlreadyPrivateLeyword = true;
}
});
if (!hasAlreadyPrivateLeyword) {
result.modifierKind.push(SyntaxKind.PrivateKeyword);
}
}
if (jsdoctags && jsdoctags.length >= 1 && jsdoctags[0].tags) {
this.checkForDeprecation(jsdoctags[0].tags, result);
if (property.jsDoc) {
result.jsdoctags = markedtags(jsdoctags[0].tags);
}
}
return result;
}
private visitConstructorProperties(constr, sourceFile) {
if (constr.parameters) {
let _parameters = [];
let i = 0;
let len = constr.parameters.length;
for (i; i < len; i++) {
if (this.isPublic(constr.parameters[i])) {
_parameters.push(this.visitProperty(constr.parameters[i], sourceFile));
}
}
/**
* Merge JSDoc tags description from constructor with parameters
*/
if (constr.jsDoc) {
if (constr.jsDoc.length > 0) {
let constrTags = constr.jsDoc[0].tags;
if (constrTags && constrTags.length > 0) {
constrTags.forEach(tag => {
_parameters.forEach(param => {