@compodoc/compodoc
Version:
The missing documentation tool for your Angular application
1,084 lines (1,014 loc) • 66.9 kB
text/typescript
import * as path from 'path';
import * as _ from 'lodash';
import { Project, ts, SyntaxKind } from 'ts-morph';
import { IsKindType, kindToType } from '../../utils/kind-to-type';
import { logger } from '../../utils/logger';
import { cleanLifecycleHooksFromMethods, markedtags, mergeTagsAndArgs } from '../../utils/utils';
import ComponentsTreeEngine from '../engines/components-tree.engine';
import { FrameworkDependencies } from './framework-dependencies';
import ImportsUtil from '../../utils/imports.util';
import {
getModuleWithProviders,
isIgnore,
isModuleWithProviders,
JsdocParserUtil
} from '../../utils';
import ExtendsMerger from '../../utils/extends-merger.util';
import RouterParserUtil from '../../utils/router-parser.util';
import { CodeGenerator } from './angular/code-generator';
import { ComponentDepFactory } from './angular/deps/component-dep.factory';
import { ControllerDepFactory } from './angular/deps/controller-dep.factory';
import { DirectiveDepFactory } from './angular/deps/directive-dep.factory';
import { ComponentCache } from './angular/deps/helpers/component-helper';
import { JsDocHelper } from './angular/deps/helpers/js-doc-helper';
import { ModuleHelper } from './angular/deps/helpers/module-helper';
import { SymbolHelper } from './angular/deps/helpers/symbol-helper';
import { ModuleDepFactory } from './angular/deps/module-dep.factory';
import { EntityDepFactory } from './angular/deps/entity-dep.factory';
import Configuration from '../configuration';
import {
IDep,
IEnumDecDep,
IFunctionDecDep,
IInjectableDep,
IInterfaceDep,
IPipeDep,
ITypeAliasDecDep
} from './angular/dependencies.interfaces';
import { v4 as uuidv4 } from 'uuid';
const crypto = require('crypto');
const { marked } = require('marked');
const ast = new Project();
// TypeScript reference : https://github.com/Microsoft/TypeScript/blob/master/lib/typescript.d.ts
export class AngularDependencies extends FrameworkDependencies {
private engine: any;
private cache: ComponentCache = new ComponentCache();
private moduleHelper = new ModuleHelper(this.cache);
private jsDocHelper = new JsDocHelper();
private symbolHelper = new SymbolHelper();
private jsdocParserUtil = new JsdocParserUtil();
constructor(files: string[], options: any) {
super(files, options);
}
public getDependencies() {
let deps = {
modules: [],
modulesForGraph: [],
components: [],
controllers: [],
entities: [],
injectables: [],
interceptors: [],
guards: [],
pipes: [],
directives: [],
routes: [],
classes: [],
interfaces: [],
miscellaneous: {
variables: [],
functions: [],
typealiases: [],
enumerations: []
},
routesTree: undefined
};
const sourceFiles = this.program.getSourceFiles() || [];
sourceFiles.map((file: ts.SourceFile) => {
const filePath = file.fileName;
if (path.extname(filePath) === '.ts' || path.extname(filePath) === '.tsx') {
if (!Configuration.mainData.angularJSProject && path.extname(filePath) === '.js') {
logger.info('parsing', filePath);
this.getSourceFileDecorators(file, deps);
} else {
if (
filePath.lastIndexOf('.d.ts') === -1 &&
filePath.lastIndexOf('spec.ts') === -1
) {
logger.info('parsing', filePath);
this.getSourceFileDecorators(file, deps);
}
}
}
return deps;
});
// End of file scanning
// Try merging inside the same file declarated variables & modules with imports | exports | declarations | providers
if (deps.miscellaneous.variables.length > 0) {
deps.miscellaneous.variables.forEach(_variable => {
let newVar = [];
// link ...VAR to VAR values, recursively
((_var, _newVar) => {
// getType pr reconstruire....
const elementsMatcher = variabelToReplace => {
if (variabelToReplace.initializer) {
if (variabelToReplace.initializer.elements) {
if (variabelToReplace.initializer.elements.length > 0) {
variabelToReplace.initializer.elements.forEach(element => {
// Direct value -> Kind 79
if (
element.text &&
element.kind === SyntaxKind.Identifier
) {
newVar.push({
name: element.text,
type: this.symbolHelper.getType(element.text)
});
}
// if _variable is ArrayLiteralExpression 203
// and has SpreadElements in his elements
// merge them
if (
element.kind === SyntaxKind.SpreadElement &&
element.expression
) {
const el = deps.miscellaneous.variables.find(
variable =>
variable.name === element.expression.text
);
if (el) {
elementsMatcher(el);
}
}
});
}
}
}
};
elementsMatcher(_var);
})(_variable, newVar);
const onLink = mod => {
const process = (initialArray, _var) => {
let indexToClean = 0;
let found = false;
const findVariableInArray = (el, index) => {
if (el.name === _var.name) {
indexToClean = index;
found = true;
}
};
initialArray.forEach(findVariableInArray);
// Clean indexes to replace
if (found) {
initialArray.splice(indexToClean, 1);
// Add variable
newVar.forEach(newEle => {
if (
typeof _.find(initialArray, { name: newEle.name }) ===
'undefined'
) {
initialArray.push(newEle);
}
});
}
};
process(mod.imports, _variable);
process(mod.exports, _variable);
process(mod.controllers, _variable);
process(mod.declarations, _variable);
process(mod.providers, _variable);
};
deps.modules.forEach(onLink);
deps.modulesForGraph.forEach(onLink);
});
}
/**
* If one thing extends another, merge them, only for internal sources
* - classes
* - components
* - injectables
* - directives
* for
* - inputs
* - outputs
* - properties
* - methods
*/
deps = ExtendsMerger.merge(deps);
// RouterParserUtil.printModulesRoutes();
// RouterParserUtil.printRoutes();
if (!Configuration.mainData.disableRoutesGraph) {
RouterParserUtil.linkModulesAndRoutes();
RouterParserUtil.constructModulesTree();
deps.routesTree = RouterParserUtil.constructRoutesTree();
}
return deps;
}
private processClass(node, file, srcFile, outputSymbols, fileBody) {
const name = this.getSymboleName(node);
const IO = this.getClassIO(file, srcFile, node, fileBody);
const sourceCode = srcFile.getText();
const hash = crypto.createHash('sha512').update(sourceCode).digest('hex');
const deps: any = {
name,
id: 'class-' + name + '-' + hash,
file: file,
deprecated: IO.deprecated,
deprecationMessage: IO.deprecationMessage,
type: 'class',
sourceCode: srcFile.getText()
};
let excludeFromClassArray = false;
if (IO.constructor) {
deps.constructorObj = IO.constructor;
}
if (IO.properties) {
deps.properties = IO.properties;
}
if (IO.description) {
deps.description = IO.description;
}
if (IO.rawdescription) {
deps.rawdescription = IO.rawdescription;
}
if (IO.methods) {
deps.methods = IO.methods;
}
if (IO.indexSignatures) {
deps.indexSignatures = IO.indexSignatures;
}
if (IO.extends) {
deps.extends = IO.extends;
}
if (IO.jsdoctags && IO.jsdoctags.length > 0) {
deps.jsdoctags = IO.jsdoctags[0].tags;
}
if (IO.accessors) {
deps.accessors = IO.accessors;
}
if (IO.inputs) {
deps.inputsClass = IO.inputs;
}
if (IO.outputs) {
deps.outputsClass = IO.outputs;
}
if (IO.hostBindings) {
deps.hostBindings = IO.hostBindings;
}
if (IO.hostListeners) {
deps.hostListeners = IO.hostListeners;
}
if (Configuration.mainData.disableLifeCycleHooks) {
deps.methods = cleanLifecycleHooksFromMethods(deps.methods);
}
if (IO.implements && IO.implements.length > 0) {
deps.implements = IO.implements;
if (this.isGuard(IO.implements)) {
// We don't want the Guard to show up in the Classes menu
excludeFromClassArray = true;
deps.type = 'guard';
outputSymbols.guards.push(deps);
}
}
if (typeof IO.ignore === 'undefined') {
this.debug(deps);
if (!excludeFromClassArray) {
outputSymbols.classes.push(deps);
}
} else {
this.ignore(deps);
}
}
private getSourceFileDecorators(initialSrcFile: ts.SourceFile, outputSymbols: any): void {
const cleaner = (process.cwd() + path.sep).replace(/\\/g, '/');
const fileName = initialSrcFile.fileName.replace(cleaner, '');
let scannedFile = initialSrcFile;
// Search in file for variable statement as routes definitions
const astFile =
typeof ast.getSourceFile(initialSrcFile.fileName) !== 'undefined'
? ast.getSourceFile(initialSrcFile.fileName)
: ast.addSourceFileAtPath(initialSrcFile.fileName);
const variableRoutesStatements = astFile.getVariableStatements();
let hasRoutesStatements = false;
if (variableRoutesStatements.length > 0) {
// Clean file for spread and dynamics inside routes definitions
variableRoutesStatements.forEach(s => {
const variableDeclarations = s.getDeclarations();
let len = variableDeclarations.length;
let i = 0;
for (i; i < len; i++) {
if (variableDeclarations[i].compilerNode.type) {
if (
variableDeclarations[i].compilerNode.type.typeName &&
variableDeclarations[i].compilerNode.type.typeName.text === 'Routes'
) {
hasRoutesStatements = true;
}
}
}
});
}
if (hasRoutesStatements && !Configuration.mainData.disableRoutesGraph) {
// Clean file for spread and dynamics inside routes definitions
logger.info('Analysing routes definitions and clean them if necessary');
// scannedFile = RouterParserUtil.cleanFileIdentifiers(astFile).compilerNode;
RouterParserUtil.cleanFileSpreads(astFile);
scannedFile = RouterParserUtil.cleanCallExpressions(astFile).compilerNode;
scannedFile = RouterParserUtil.cleanFileDynamics(astFile).compilerNode;
scannedFile.kind = SyntaxKind.SourceFile;
}
ts.forEachChild(scannedFile, (initialNode: ts.Node) => {
if (
this.jsDocHelper.hasJSDocInternalTag(fileName, scannedFile, initialNode) &&
Configuration.mainData.disableInternal
) {
return;
}
const parseNode = (file, srcFile, node, fileBody) => {
const sourceCode = srcFile.getText();
const hash = crypto.createHash('sha512').update(sourceCode).digest('hex');
if (node.decorators) {
let classWithCustomDecorator = false;
const visitDecorator = (visitedDecorator, index) => {
let deps: IDep;
const name = this.getSymboleName(node);
const props = this.findProperties(visitedDecorator, srcFile);
const IO = this.componentHelper.getComponentIO(
file,
srcFile,
node,
fileBody
);
if (this.isModule(visitedDecorator)) {
const moduleDep = new ModuleDepFactory(this.moduleHelper).create(
file,
srcFile,
name,
props,
IO
);
if (RouterParserUtil.hasRouterModuleInImports(moduleDep.imports)) {
RouterParserUtil.addModuleWithRoutes(
name,
this.moduleHelper.getModuleImportsRaw(props, srcFile),
file
);
}
deps = moduleDep;
if (typeof IO.ignore === 'undefined') {
RouterParserUtil.addModule(name, moduleDep.imports);
outputSymbols.modules.push(moduleDep);
outputSymbols.modulesForGraph.push(moduleDep);
}
} else if (this.isComponent(visitedDecorator)) {
if (props.length === 0) {
return;
}
const componentDep = new ComponentDepFactory(
this.componentHelper
).create(file, srcFile, name, props, IO);
deps = componentDep;
if (typeof IO.ignore === 'undefined') {
ComponentsTreeEngine.addComponent(componentDep);
outputSymbols.components.push(componentDep);
}
} else if (this.isController(visitedDecorator)) {
const controllerDep = new ControllerDepFactory().create(
file,
srcFile,
name,
props,
IO
);
deps = controllerDep;
if (typeof IO.ignore === 'undefined') {
outputSymbols.controllers.push(controllerDep);
}
} else if (this.isEntity(visitedDecorator)) {
const entityDep = new EntityDepFactory().create(
file,
srcFile,
name,
props,
IO
);
deps = entityDep;
if (deps.name === 'Comment') {
console.log(deps.properties[0]);
}
if (typeof IO.ignore === 'undefined') {
outputSymbols.entities.push(entityDep);
}
} else if (this.isInjectable(visitedDecorator)) {
const injectableDeps: IInjectableDep = {
name,
id: 'injectable-' + name + '-' + hash,
file: file,
properties: IO.properties,
methods: IO.methods,
deprecated: IO.deprecated,
deprecationMessage: IO.deprecationMessage,
description: IO.description,
rawdescription: IO.rawdescription,
sourceCode: srcFile.getText(),
exampleUrls: this.componentHelper.getComponentExampleUrls(
srcFile.getText()
)
};
if (IO.constructor) {
injectableDeps.constructorObj = IO.constructor;
}
if (IO.jsdoctags && IO.jsdoctags.length > 0) {
injectableDeps.jsdoctags = IO.jsdoctags[0].tags;
}
if (IO.accessors) {
injectableDeps.accessors = IO.accessors;
}
if (IO.extends) {
injectableDeps.extends = IO.extends;
}
deps = injectableDeps;
if (typeof IO.ignore === 'undefined') {
if (_.includes(IO.implements, 'HttpInterceptor')) {
injectableDeps.type = 'interceptor';
outputSymbols.interceptors.push(injectableDeps);
} else if (this.isGuard(IO.implements)) {
injectableDeps.type = 'guard';
outputSymbols.guards.push(injectableDeps);
} else {
injectableDeps.type = 'injectable';
this.addNewEntityInStore(
injectableDeps,
outputSymbols.injectables
);
}
}
} else if (this.isPipe(visitedDecorator)) {
const pipeDeps: IPipeDep = {
name,
id: 'pipe-' + name + '-' + hash,
file: file,
type: 'pipe',
deprecated: IO.deprecated,
deprecationMessage: IO.deprecationMessage,
description: IO.description,
rawdescription: IO.rawdescription,
properties: IO.properties,
methods: IO.methods,
pure: this.componentHelper.getComponentPure(props, srcFile),
ngname: this.componentHelper.getComponentName(props, srcFile),
sourceCode: srcFile.getText(),
exampleUrls: this.componentHelper.getComponentExampleUrls(
srcFile.getText()
)
};
if (IO.jsdoctags && IO.jsdoctags.length > 0) {
pipeDeps.jsdoctags = IO.jsdoctags[0].tags;
}
deps = pipeDeps;
if (typeof IO.ignore === 'undefined') {
outputSymbols.pipes.push(pipeDeps);
}
} else if (this.isDirective(visitedDecorator)) {
const directiveDeps = new DirectiveDepFactory(
this.componentHelper
).create(file, srcFile, name, props, IO);
deps = directiveDeps;
if (typeof IO.ignore === 'undefined') {
outputSymbols.directives.push(directiveDeps);
}
} else {
const hasMultipleDecoratorsWithInternalOne = this.hasInternalDecorator(
node.decorators
);
// Just a class
if (
!classWithCustomDecorator &&
!hasMultipleDecoratorsWithInternalOne
) {
classWithCustomDecorator = true;
this.processClass(node, file, srcFile, outputSymbols, fileBody);
}
}
this.cache.set(name, deps);
if (typeof IO.ignore === 'undefined') {
this.debug(deps);
} else {
this.ignore(deps);
}
};
const filterByDecorators = filteredNode => {
if (filteredNode.expression && filteredNode.expression.expression) {
let _test = /(NgModule|Component|Injectable|Pipe|Directive)/.test(
filteredNode.expression.expression.text
);
if (!_test && ts.isClassDeclaration(node)) {
_test = true;
}
return _test;
}
if (ts.isClassDeclaration(node)) {
return true;
}
return false;
};
node.decorators.filter(filterByDecorators).forEach(visitDecorator);
} else if (node.symbol) {
if (node.symbol.flags === ts.SymbolFlags.Class) {
this.processClass(node, file, srcFile, outputSymbols, fileBody);
} else if (node.symbol.flags === ts.SymbolFlags.Interface) {
const name = this.getSymboleName(node);
const IO = this.getInterfaceIO(file, srcFile, node, fileBody);
const interfaceDeps: IInterfaceDep = {
name,
id: 'interface-' + name + '-' + hash,
file: file,
deprecated: IO.deprecated,
deprecationMessage: IO.deprecationMessage,
type: 'interface',
sourceCode: srcFile.getText()
};
if (IO.properties) {
interfaceDeps.properties = IO.properties;
}
if (IO.indexSignatures) {
interfaceDeps.indexSignatures = IO.indexSignatures;
}
if (IO.kind) {
interfaceDeps.kind = IO.kind;
}
if (IO.description) {
interfaceDeps.description = IO.description;
interfaceDeps.rawdescription = IO.rawdescription;
}
if (IO.methods) {
interfaceDeps.methods = IO.methods;
}
if (IO.extends) {
interfaceDeps.extends = IO.extends;
}
if (typeof IO.ignore === 'undefined') {
this.debug(interfaceDeps);
outputSymbols.interfaces.push(interfaceDeps);
} else {
this.ignore(interfaceDeps);
}
} else if (ts.isFunctionDeclaration(node)) {
const infos = this.visitFunctionDeclaration(node);
const name = infos.name;
const deprecated = infos.deprecated;
const deprecationMessage = infos.deprecationMessage;
const functionDep: IFunctionDecDep = {
name,
file: file,
ctype: 'miscellaneous',
subtype: 'function',
deprecated,
deprecationMessage,
description: this.visitEnumTypeAliasFunctionDeclarationDescription(node)
};
if (infos.args) {
functionDep.args = infos.args;
}
if (infos.returnType) {
functionDep.returnType = infos.returnType;
}
if (infos.jsdoctags && infos.jsdoctags.length > 0) {
functionDep.jsdoctags = infos.jsdoctags;
}
if (typeof infos.ignore === 'undefined') {
if (
!(
this.hasPrivateJSDocTag(functionDep.jsdoctags) &&
Configuration.mainData.disablePrivate
)
) {
this.debug(functionDep);
outputSymbols.miscellaneous.functions.push(functionDep);
}
}
} else if (ts.isEnumDeclaration(node)) {
const infos = this.visitEnumDeclaration(node);
const name = infos.name;
const deprecated = infos.deprecated;
const deprecationMessage = infos.deprecationMessage;
const enumDeps: IEnumDecDep = {
name,
childs: infos.members,
ctype: 'miscellaneous',
subtype: 'enum',
deprecated,
deprecationMessage,
description:
this.visitEnumTypeAliasFunctionDeclarationDescription(node),
file: file
};
if (!isIgnore(node)) {
this.debug(enumDeps);
outputSymbols.miscellaneous.enumerations.push(enumDeps);
}
} else if (ts.isTypeAliasDeclaration(node)) {
const infos = this.visitTypeDeclaration(node);
const name = infos.name;
const deprecated = infos.deprecated;
const deprecationMessage = infos.deprecationMessage;
const typeAliasDeps: ITypeAliasDecDep = {
name,
ctype: 'miscellaneous',
subtype: 'typealias',
rawtype: this.classHelper.visitType(node),
file: file,
deprecated,
deprecationMessage,
description: this.visitEnumTypeAliasFunctionDeclarationDescription(node)
};
if (node.type) {
typeAliasDeps.kind = node.type.kind;
if (typeAliasDeps.rawtype === '') {
typeAliasDeps.rawtype = this.classHelper.visitType(node);
}
}
if (
typeAliasDeps.kind &&
typeAliasDeps.kind === SyntaxKind.TemplateLiteralType &&
node.type
) {
typeAliasDeps.rawtype = srcFile.text.substring(
node.type.pos,
node.type.end
);
}
if (!isIgnore(node)) {
outputSymbols.miscellaneous.typealiases.push(typeAliasDeps);
}
if (typeof infos.ignore === 'undefined') {
this.debug(typeAliasDeps);
}
} else if (ts.isModuleDeclaration(node)) {
if (node.body) {
if (node.body.statements && node.body.statements.length > 0) {
node.body.statements.forEach(statement =>
parseNode(file, srcFile, statement, node.body)
);
}
}
}
} else {
const IO = this.getRouteIO(file, srcFile, node);
if (IO.routes) {
let newRoutes;
try {
newRoutes = RouterParserUtil.cleanRawRouteParsed(IO.routes);
} catch (e) {
// tslint:disable-next-line:max-line-length
logger.error(
'Routes parsing error, maybe a trailing comma or an external variable, trying to fix that later after sources scanning.'
);
newRoutes = IO.routes.replace(/ /gm, '');
RouterParserUtil.addIncompleteRoute({
data: newRoutes,
file: file
});
return true;
}
outputSymbols.routes = [...outputSymbols.routes, ...newRoutes];
}
if (ts.isClassDeclaration(node)) {
this.processClass(node, file, srcFile, outputSymbols, fileBody);
}
if (ts.isExpressionStatement(node) || ts.isIfStatement(node)) {
const bootstrapModuleReference = 'bootstrapModule';
// Find the root module with bootstrapModule call
// 1. find a simple call : platformBrowserDynamic().bootstrapModule(AppModule);
// 2. or inside a call :
// () => {
// platformBrowserDynamic().bootstrapModule(AppModule);
// });
// 3. with a catch : platformBrowserDynamic().bootstrapModule(AppModule).catch(error => console.error(error));
// 4. with parameters : platformBrowserDynamic().bootstrapModule(AppModule, {}).catch(error => console.error(error));
// Find recusively in expression nodes one with name 'bootstrapModule'
let rootModule;
let resultNode;
if (srcFile.text.indexOf(bootstrapModuleReference) !== -1) {
if (node.expression) {
resultNode = this.findExpressionByNameInExpressions(
node.expression,
'bootstrapModule'
);
}
if (typeof node.thenStatement !== 'undefined') {
if (
node.thenStatement.statements &&
node.thenStatement.statements.length > 0
) {
let firstStatement = node.thenStatement.statements[0];
resultNode = this.findExpressionByNameInExpressions(
firstStatement.expression,
'bootstrapModule'
);
}
}
if (!resultNode) {
if (
node.expression &&
node.expression.arguments &&
node.expression.arguments.length > 0
) {
resultNode = this.findExpressionByNameInExpressionArguments(
node.expression.arguments,
'bootstrapModule'
);
}
}
if (resultNode) {
if (resultNode.arguments.length > 0) {
_.forEach(resultNode.arguments, (argument: any) => {
if (argument.text) {
rootModule = argument.text;
}
});
}
if (rootModule) {
RouterParserUtil.setRootModule(rootModule);
}
}
}
}
if (ts.isVariableStatement(node) && !RouterParserUtil.isVariableRoutes(node)) {
let isDestructured = false;
// Check for destructuring array
const nodeVariableDeclarations = node.declarationList.declarations;
if (nodeVariableDeclarations) {
if (nodeVariableDeclarations.length > 0) {
if (
nodeVariableDeclarations[0].name &&
nodeVariableDeclarations[0].name.kind ===
SyntaxKind.ArrayBindingPattern
) {
isDestructured = true;
}
}
}
const visitVariableNode = variableNode => {
const infos: any = this.visitVariableDeclaration(variableNode);
if (infos) {
const name = infos.name;
const deprecated = infos.deprecated;
const deprecationMessage = infos.deprecationMessage;
const deps: any = {
name,
ctype: 'miscellaneous',
subtype: 'variable',
file: file,
deprecated,
deprecationMessage
};
deps.type = infos.type ? infos.type : '';
if (infos.defaultValue) {
deps.defaultValue = infos.defaultValue;
}
if (infos.initializer) {
deps.initializer = infos.initializer;
}
if (
variableNode.jsDoc &&
variableNode.jsDoc.length > 0 &&
variableNode.jsDoc[0].comment
) {
const rawDescription = this.jsdocParserUtil.parseJSDocNode(
variableNode.jsDoc[0]
);
deps.rawdescription = rawDescription;
deps.description = marked(rawDescription);
}
if (isModuleWithProviders(variableNode)) {
const routingInitializer = getModuleWithProviders(variableNode);
RouterParserUtil.addModuleWithRoutes(
name,
[routingInitializer],
file
);
RouterParserUtil.addModule(name, [routingInitializer]);
}
if (!isIgnore(variableNode)) {
this.debug(deps);
outputSymbols.miscellaneous.variables.push(deps);
}
}
};
if (isDestructured) {
if (nodeVariableDeclarations[0].name.elements) {
const destructuredVariables =
nodeVariableDeclarations[0].name.elements;
for (let i = 0; i < destructuredVariables.length; i++) {
const destructuredVariable = destructuredVariables[i];
const name = destructuredVariable.name
? destructuredVariable.name.escapedText
: '';
const deps: any = {
name,
ctype: 'miscellaneous',
subtype: 'variable',
file: file
};
if (nodeVariableDeclarations[0].initializer) {
if (nodeVariableDeclarations[0].initializer.elements) {
deps.initializer =
nodeVariableDeclarations[0].initializer.elements[i];
}
deps.defaultValue = deps.initializer
? this.classHelper.stringifyDefaultValue(
deps.initializer
)
: undefined;
}
if (!isIgnore(destructuredVariables[i])) {
this.debug(deps);
outputSymbols.miscellaneous.variables.push(deps);
}
}
}
} else {
visitVariableNode(node);
}
}
if (ts.isTypeAliasDeclaration(node)) {
const infos = this.visitTypeDeclaration(node);
const name = infos.name;
const deprecated = infos.deprecated;
const deprecationMessage = infos.deprecationMessage;
const deps: ITypeAliasDecDep = {
name,
ctype: 'miscellaneous',
subtype: 'typealias',
rawtype: this.classHelper.visitType(node),
file: file,
deprecated,
deprecationMessage,
description: this.visitEnumTypeAliasFunctionDeclarationDescription(node)
};
if (node.type) {
deps.kind = node.type.kind;
}
if (
deps.kind &&
deps.kind === SyntaxKind.TemplateLiteralType &&
node.type
) {
deps.rawtype = srcFile.text.substring(node.type.pos, node.type.end);
}
if (!isIgnore(node)) {
this.debug(deps);
outputSymbols.miscellaneous.typealiases.push(deps);
}
}
if (ts.isFunctionDeclaration(node)) {
const infos = this.visitFunctionDeclaration(node);
const name = infos.name;
const deprecated = infos.deprecated;
const deprecationMessage = infos.deprecationMessage;
const functionDep: IFunctionDecDep = {
name,
ctype: 'miscellaneous',
subtype: 'function',
file: file,
deprecated,
deprecationMessage,
description: this.visitEnumTypeAliasFunctionDeclarationDescription(node)
};
if (infos.args) {
functionDep.args = infos.args;
}
if (infos.returnType) {
functionDep.returnType = infos.returnType;
}
if (infos.jsdoctags && infos.jsdoctags.length > 0) {
functionDep.jsdoctags = infos.jsdoctags;
}
if (typeof infos.ignore === 'undefined') {
if (
!(
this.hasPrivateJSDocTag(functionDep.jsdoctags) &&
Configuration.mainData.disablePrivate
)
) {
this.debug(functionDep);
outputSymbols.miscellaneous.functions.push(functionDep);
}
}
}
if (ts.isEnumDeclaration(node)) {
const infos = this.visitEnumDeclaration(node);
const name = infos.name;
const deprecated = infos.deprecated;
const deprecationMessage = infos.deprecationMessage;
const enumDeps: IEnumDecDep = {
name,
childs: infos.members,
ctype: 'miscellaneous',
subtype: 'enum',
deprecated,
deprecationMessage,
description:
this.visitEnumTypeAliasFunctionDeclarationDescription(node),
file: file
};
if (!isIgnore(node)) {
this.debug(enumDeps);
outputSymbols.miscellaneous.enumerations.push(enumDeps);
}
}
}
};
parseNode(fileName, scannedFile, initialNode);
});
}
/**
* Function to in a specific store an entity, and check before is there is not the same one
* in that store : same name, id and file
* @param entity Entity to store
* @param store Store
*/
private addNewEntityInStore(entity, store) {
const findSameEntityInStore = _.filter(store, {
name: entity.name,
id: entity.id,
file: entity.file
});
if (findSameEntityInStore.length === 0) {
store.push(entity);
}
}
private debug(deps: IDep) {
if (deps) {
logger.debug('found', `${deps.name}`);
} else {
return;
}
['imports', 'exports', 'declarations', 'providers', 'bootstrap'].forEach(symbols => {
if (deps[symbols] && deps[symbols].length > 0) {
logger.debug('', `- ${symbols}:`);
deps[symbols]
.map(i => i.name)
.forEach(d => {
logger.debug('', `\t- ${d}`);
});
}
});
}
private ignore(deps: IDep) {
if (deps) {
logger.warn('ignore', `${deps.name}`);
} else {
return;
}
}
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 findExpressionByNameInExpressions(entryNode, name) {
let result;
const loop = function (node, z) {
if (node) {
if (node.expression && !node.expression.name) {
loop(node.expression, z);
}
if (node.expression && node.expression.name) {
if (node.expression.name.text === z) {
result = node;
} else {
loop(node.expression, z);
}
}
}
};
loop(entryNode, name);
return result;
}
private findExpressionByNameInExpressionArguments(arg, name) {
let result;
const that = this;
let i = 0;
let len = arg.length;
const loop = function (node, z) {
if (node.body) {
if (node.body.statements && node.body.statements.length > 0) {
let j = 0;
const leng = node.body.statements.length;
for (j; j < leng; j++) {
result = that.findExpressionByNameInExpressions(node.body.statements[j], z);
}
}
}
};
for (i; i < len; i++) {
loop(arg[i], name);
}
return result;
}
private parseDecorators(decorators, type: string): boolean {
let result = false;
if (decorators.length > 1) {
_.forEach(decorators, function (decorator: any) {
if (decorator.expression.expression) {
if (decorator.expression.expression.text === type) {
result = true;
}
}
});
} else {