UNPKG

66.9 kBPlain TextView Raw
1import * as path from 'path';
2
3import * as _ from 'lodash';
4import { Project, ts, SyntaxKind } from 'ts-morph';
5
6import { IsKindType, kindToType } from '../../utils/kind-to-type';
7import { logger } from '../../utils/logger';
8import { cleanLifecycleHooksFromMethods, markedtags, mergeTagsAndArgs } from '../../utils/utils';
9import ComponentsTreeEngine from '../engines/components-tree.engine';
10
11import { FrameworkDependencies } from './framework-dependencies';
12
13import ImportsUtil from '../../utils/imports.util';
14
15import {
16 getModuleWithProviders,
17 isIgnore,
18 isModuleWithProviders,
19 JsdocParserUtil
20} from '../../utils';
21
22import ExtendsMerger from '../../utils/extends-merger.util';
23
24import RouterParserUtil from '../../utils/router-parser.util';
25
26import { CodeGenerator } from './angular/code-generator';
27
28import { ComponentDepFactory } from './angular/deps/component-dep.factory';
29import { ControllerDepFactory } from './angular/deps/controller-dep.factory';
30import { DirectiveDepFactory } from './angular/deps/directive-dep.factory';
31import { ComponentCache } from './angular/deps/helpers/component-helper';
32import { JsDocHelper } from './angular/deps/helpers/js-doc-helper';
33import { ModuleHelper } from './angular/deps/helpers/module-helper';
34import { SymbolHelper } from './angular/deps/helpers/symbol-helper';
35import { ModuleDepFactory } from './angular/deps/module-dep.factory';
36import { EntityDepFactory } from './angular/deps/entity-dep.factory';
37
38import Configuration from '../configuration';
39
40import {
41 IDep,
42 IEnumDecDep,
43 IFunctionDecDep,
44 IInjectableDep,
45 IInterfaceDep,
46 IPipeDep,
47 ITypeAliasDecDep
48} from './angular/dependencies.interfaces';
49
50import { v4 as uuidv4 } from 'uuid';
51
52const crypto = require('crypto');
53const { marked } = require('marked');
54const ast = new Project();
55
56// TypeScript reference : https://github.com/Microsoft/TypeScript/blob/master/lib/typescript.d.ts
57
58export class AngularDependencies extends FrameworkDependencies {
59 private engine: any;
60 private cache: ComponentCache = new ComponentCache();
61 private moduleHelper = new ModuleHelper(this.cache);
62 private jsDocHelper = new JsDocHelper();
63 private symbolHelper = new SymbolHelper();
64 private jsdocParserUtil = new JsdocParserUtil();
65
66 constructor(files: string[], options: any) {
67 super(files, options);
68 }
69
70 public getDependencies() {
71 let deps = {
72 modules: [],
73 modulesForGraph: [],
74 components: [],
75 controllers: [],
76 entities: [],
77 injectables: [],
78 interceptors: [],
79 guards: [],
80 pipes: [],
81 directives: [],
82 routes: [],
83 classes: [],
84 interfaces: [],
85 miscellaneous: {
86 variables: [],
87 functions: [],
88 typealiases: [],
89 enumerations: []
90 },
91 routesTree: undefined
92 };
93
94 const sourceFiles = this.program.getSourceFiles() || [];
95
96 sourceFiles.map((file: ts.SourceFile) => {
97 const filePath = file.fileName;
98
99 if (path.extname(filePath) === '.ts' || path.extname(filePath) === '.tsx') {
100 if (!Configuration.mainData.angularJSProject && path.extname(filePath) === '.js') {
101 logger.info('parsing', filePath);
102 this.getSourceFileDecorators(file, deps);
103 } else {
104 if (
105 filePath.lastIndexOf('.d.ts') === -1 &&
106 filePath.lastIndexOf('spec.ts') === -1
107 ) {
108 logger.info('parsing', filePath);
109 this.getSourceFileDecorators(file, deps);
110 }
111 }
112 }
113
114 return deps;
115 });
116
117 // End of file scanning
118 // Try merging inside the same file declarated variables & modules with imports | exports | declarations | providers
119
120 if (deps.miscellaneous.variables.length > 0) {
121 deps.miscellaneous.variables.forEach(_variable => {
122 let newVar = [];
123
124 // link ...VAR to VAR values, recursively
125 ((_var, _newVar) => {
126 // getType pr reconstruire....
127 const elementsMatcher = variabelToReplace => {
128 if (variabelToReplace.initializer) {
129 if (variabelToReplace.initializer.elements) {
130 if (variabelToReplace.initializer.elements.length > 0) {
131 variabelToReplace.initializer.elements.forEach(element => {
132 // Direct value -> Kind 79
133 if (
134 element.text &&
135 element.kind === SyntaxKind.Identifier
136 ) {
137 newVar.push({
138 name: element.text,
139 type: this.symbolHelper.getType(element.text)
140 });
141 }
142 // if _variable is ArrayLiteralExpression 203
143 // and has SpreadElements in his elements
144 // merge them
145 if (
146 element.kind === SyntaxKind.SpreadElement &&
147 element.expression
148 ) {
149 const el = deps.miscellaneous.variables.find(
150 variable =>
151 variable.name === element.expression.text
152 );
153 if (el) {
154 elementsMatcher(el);
155 }
156 }
157 });
158 }
159 }
160 }
161 };
162 elementsMatcher(_var);
163 })(_variable, newVar);
164
165 const onLink = mod => {
166 const process = (initialArray, _var) => {
167 let indexToClean = 0;
168 let found = false;
169 const findVariableInArray = (el, index) => {
170 if (el.name === _var.name) {
171 indexToClean = index;
172 found = true;
173 }
174 };
175 initialArray.forEach(findVariableInArray);
176 // Clean indexes to replace
177 if (found) {
178 initialArray.splice(indexToClean, 1);
179 // Add variable
180 newVar.forEach(newEle => {
181 if (
182 typeof _.find(initialArray, { name: newEle.name }) ===
183 'undefined'
184 ) {
185 initialArray.push(newEle);
186 }
187 });
188 }
189 };
190 process(mod.imports, _variable);
191 process(mod.exports, _variable);
192 process(mod.controllers, _variable);
193 process(mod.declarations, _variable);
194 process(mod.providers, _variable);
195 };
196
197 deps.modules.forEach(onLink);
198 deps.modulesForGraph.forEach(onLink);
199 });
200 }
201
202 /**
203 * If one thing extends another, merge them, only for internal sources
204 * - classes
205 * - components
206 * - injectables
207 * - directives
208 * for
209 * - inputs
210 * - outputs
211 * - properties
212 * - methods
213 */
214 deps = ExtendsMerger.merge(deps);
215
216 // RouterParserUtil.printModulesRoutes();
217 // RouterParserUtil.printRoutes();
218
219 if (!Configuration.mainData.disableRoutesGraph) {
220 RouterParserUtil.linkModulesAndRoutes();
221 RouterParserUtil.constructModulesTree();
222
223 deps.routesTree = RouterParserUtil.constructRoutesTree();
224 }
225
226 return deps;
227 }
228
229 private processClass(node, file, srcFile, outputSymbols, fileBody) {
230 const name = this.getSymboleName(node);
231 const IO = this.getClassIO(file, srcFile, node, fileBody);
232 const sourceCode = srcFile.getText();
233 const hash = crypto.createHash('sha512').update(sourceCode).digest('hex');
234 const deps: any = {
235 name,
236 id: 'class-' + name + '-' + hash,
237 file: file,
238 deprecated: IO.deprecated,
239 deprecationMessage: IO.deprecationMessage,
240 type: 'class',
241 sourceCode: srcFile.getText()
242 };
243 let excludeFromClassArray = false;
244
245 if (IO.constructor) {
246 deps.constructorObj = IO.constructor;
247 }
248 if (IO.properties) {
249 deps.properties = IO.properties;
250 }
251 if (IO.description) {
252 deps.description = IO.description;
253 }
254 if (IO.rawdescription) {
255 deps.rawdescription = IO.rawdescription;
256 }
257 if (IO.methods) {
258 deps.methods = IO.methods;
259 }
260 if (IO.indexSignatures) {
261 deps.indexSignatures = IO.indexSignatures;
262 }
263 if (IO.extends) {
264 deps.extends = IO.extends;
265 }
266 if (IO.jsdoctags && IO.jsdoctags.length > 0) {
267 deps.jsdoctags = IO.jsdoctags[0].tags;
268 }
269 if (IO.accessors) {
270 deps.accessors = IO.accessors;
271 }
272 if (IO.inputs) {
273 deps.inputsClass = IO.inputs;
274 }
275 if (IO.outputs) {
276 deps.outputsClass = IO.outputs;
277 }
278 if (IO.hostBindings) {
279 deps.hostBindings = IO.hostBindings;
280 }
281 if (IO.hostListeners) {
282 deps.hostListeners = IO.hostListeners;
283 }
284 if (Configuration.mainData.disableLifeCycleHooks) {
285 deps.methods = cleanLifecycleHooksFromMethods(deps.methods);
286 }
287 if (IO.implements && IO.implements.length > 0) {
288 deps.implements = IO.implements;
289
290 if (this.isGuard(IO.implements)) {
291 // We don't want the Guard to show up in the Classes menu
292 excludeFromClassArray = true;
293 deps.type = 'guard';
294
295 outputSymbols.guards.push(deps);
296 }
297 }
298 if (typeof IO.ignore === 'undefined') {
299 this.debug(deps);
300
301 if (!excludeFromClassArray) {
302 outputSymbols.classes.push(deps);
303 }
304 } else {
305 this.ignore(deps);
306 }
307 }
308
309 private getSourceFileDecorators(initialSrcFile: ts.SourceFile, outputSymbols: any): void {
310 const cleaner = (process.cwd() + path.sep).replace(/\\/g, '/');
311 const fileName = initialSrcFile.fileName.replace(cleaner, '');
312 let scannedFile = initialSrcFile;
313
314 // Search in file for variable statement as routes definitions
315
316 const astFile =
317 typeof ast.getSourceFile(initialSrcFile.fileName) !== 'undefined'
318 ? ast.getSourceFile(initialSrcFile.fileName)
319 : ast.addSourceFileAtPath(initialSrcFile.fileName);
320
321 const variableRoutesStatements = astFile.getVariableStatements();
322 let hasRoutesStatements = false;
323
324 if (variableRoutesStatements.length > 0) {
325 // Clean file for spread and dynamics inside routes definitions
326 variableRoutesStatements.forEach(s => {
327 const variableDeclarations = s.getDeclarations();
328 let len = variableDeclarations.length;
329 let i = 0;
330 for (i; i < len; i++) {
331 if (variableDeclarations[i].compilerNode.type) {
332 if (
333 variableDeclarations[i].compilerNode.type.typeName &&
334 variableDeclarations[i].compilerNode.type.typeName.text === 'Routes'
335 ) {
336 hasRoutesStatements = true;
337 }
338 }
339 }
340 });
341 }
342
343 if (hasRoutesStatements && !Configuration.mainData.disableRoutesGraph) {
344 // Clean file for spread and dynamics inside routes definitions
345 logger.info('Analysing routes definitions and clean them if necessary');
346
347 // scannedFile = RouterParserUtil.cleanFileIdentifiers(astFile).compilerNode;
348 RouterParserUtil.cleanFileSpreads(astFile);
349
350 scannedFile = RouterParserUtil.cleanCallExpressions(astFile).compilerNode;
351 scannedFile = RouterParserUtil.cleanFileDynamics(astFile).compilerNode;
352
353 scannedFile.kind = SyntaxKind.SourceFile;
354 }
355
356 ts.forEachChild(scannedFile, (initialNode: ts.Node) => {
357 if (
358 this.jsDocHelper.hasJSDocInternalTag(fileName, scannedFile, initialNode) &&
359 Configuration.mainData.disableInternal
360 ) {
361 return;
362 }
363 const parseNode = (file, srcFile, node, fileBody) => {
364 const sourceCode = srcFile.getText();
365 const hash = crypto.createHash('sha512').update(sourceCode).digest('hex');
366
367 if (node.decorators) {
368 let classWithCustomDecorator = false;
369 const visitDecorator = (visitedDecorator, index) => {
370 let deps: IDep;
371
372 const name = this.getSymboleName(node);
373 const props = this.findProperties(visitedDecorator, srcFile);
374 const IO = this.componentHelper.getComponentIO(
375 file,
376 srcFile,
377 node,
378 fileBody
379 );
380
381 if (this.isModule(visitedDecorator)) {
382 const moduleDep = new ModuleDepFactory(this.moduleHelper).create(
383 file,
384 srcFile,
385 name,
386 props,
387 IO
388 );
389 if (RouterParserUtil.hasRouterModuleInImports(moduleDep.imports)) {
390 RouterParserUtil.addModuleWithRoutes(
391 name,
392 this.moduleHelper.getModuleImportsRaw(props, srcFile),
393 file
394 );
395 }
396 deps = moduleDep;
397 if (typeof IO.ignore === 'undefined') {
398 RouterParserUtil.addModule(name, moduleDep.imports);
399 outputSymbols.modules.push(moduleDep);
400 outputSymbols.modulesForGraph.push(moduleDep);
401 }
402 } else if (this.isComponent(visitedDecorator)) {
403 if (props.length === 0) {
404 return;
405 }
406 const componentDep = new ComponentDepFactory(
407 this.componentHelper
408 ).create(file, srcFile, name, props, IO);
409 deps = componentDep;
410 if (typeof IO.ignore === 'undefined') {
411 ComponentsTreeEngine.addComponent(componentDep);
412 outputSymbols.components.push(componentDep);
413 }
414 } else if (this.isController(visitedDecorator)) {
415 const controllerDep = new ControllerDepFactory().create(
416 file,
417 srcFile,
418 name,
419 props,
420 IO
421 );
422 deps = controllerDep;
423 if (typeof IO.ignore === 'undefined') {
424 outputSymbols.controllers.push(controllerDep);
425 }
426 } else if (this.isEntity(visitedDecorator)) {
427 const entityDep = new EntityDepFactory().create(
428 file,
429 srcFile,
430 name,
431 props,
432 IO
433 );
434 deps = entityDep;
435 if (deps.name === 'Comment') {
436 console.log(deps.properties[0]);
437 }
438
439 if (typeof IO.ignore === 'undefined') {
440 outputSymbols.entities.push(entityDep);
441 }
442 } else if (this.isInjectable(visitedDecorator)) {
443 const injectableDeps: IInjectableDep = {
444 name,
445 id: 'injectable-' + name + '-' + hash,
446 file: file,
447 properties: IO.properties,
448 methods: IO.methods,
449 deprecated: IO.deprecated,
450 deprecationMessage: IO.deprecationMessage,
451 description: IO.description,
452 rawdescription: IO.rawdescription,
453 sourceCode: srcFile.getText(),
454 exampleUrls: this.componentHelper.getComponentExampleUrls(
455 srcFile.getText()
456 )
457 };
458 if (IO.constructor) {
459 injectableDeps.constructorObj = IO.constructor;
460 }
461 if (IO.jsdoctags && IO.jsdoctags.length > 0) {
462 injectableDeps.jsdoctags = IO.jsdoctags[0].tags;
463 }
464 if (IO.accessors) {
465 injectableDeps.accessors = IO.accessors;
466 }
467 if (IO.extends) {
468 injectableDeps.extends = IO.extends;
469 }
470 deps = injectableDeps;
471 if (typeof IO.ignore === 'undefined') {
472 if (_.includes(IO.implements, 'HttpInterceptor')) {
473 injectableDeps.type = 'interceptor';
474 outputSymbols.interceptors.push(injectableDeps);
475 } else if (this.isGuard(IO.implements)) {
476 injectableDeps.type = 'guard';
477 outputSymbols.guards.push(injectableDeps);
478 } else {
479 injectableDeps.type = 'injectable';
480 this.addNewEntityInStore(
481 injectableDeps,
482 outputSymbols.injectables
483 );
484 }
485 }
486 } else if (this.isPipe(visitedDecorator)) {
487 const pipeDeps: IPipeDep = {
488 name,
489 id: 'pipe-' + name + '-' + hash,
490 file: file,
491 type: 'pipe',
492 deprecated: IO.deprecated,
493 deprecationMessage: IO.deprecationMessage,
494 description: IO.description,
495 rawdescription: IO.rawdescription,
496 properties: IO.properties,
497 methods: IO.methods,
498 pure: this.componentHelper.getComponentPure(props, srcFile),
499 ngname: this.componentHelper.getComponentName(props, srcFile),
500 sourceCode: srcFile.getText(),
501 exampleUrls: this.componentHelper.getComponentExampleUrls(
502 srcFile.getText()
503 )
504 };
505 if (IO.jsdoctags && IO.jsdoctags.length > 0) {
506 pipeDeps.jsdoctags = IO.jsdoctags[0].tags;
507 }
508 deps = pipeDeps;
509 if (typeof IO.ignore === 'undefined') {
510 outputSymbols.pipes.push(pipeDeps);
511 }
512 } else if (this.isDirective(visitedDecorator)) {
513 const directiveDeps = new DirectiveDepFactory(
514 this.componentHelper
515 ).create(file, srcFile, name, props, IO);
516 deps = directiveDeps;
517 if (typeof IO.ignore === 'undefined') {
518 outputSymbols.directives.push(directiveDeps);
519 }
520 } else {
521 const hasMultipleDecoratorsWithInternalOne = this.hasInternalDecorator(
522 node.decorators
523 );
524 // Just a class
525 if (
526 !classWithCustomDecorator &&
527 !hasMultipleDecoratorsWithInternalOne
528 ) {
529 classWithCustomDecorator = true;
530 this.processClass(node, file, srcFile, outputSymbols, fileBody);
531 }
532 }
533 this.cache.set(name, deps);
534
535 if (typeof IO.ignore === 'undefined') {
536 this.debug(deps);
537 } else {
538 this.ignore(deps);
539 }
540 };
541
542 const filterByDecorators = filteredNode => {
543 if (filteredNode.expression && filteredNode.expression.expression) {
544 let _test = /(NgModule|Component|Injectable|Pipe|Directive)/.test(
545 filteredNode.expression.expression.text
546 );
547 if (!_test && ts.isClassDeclaration(node)) {
548 _test = true;
549 }
550 return _test;
551 }
552 if (ts.isClassDeclaration(node)) {
553 return true;
554 }
555 return false;
556 };
557
558 node.decorators.filter(filterByDecorators).forEach(visitDecorator);
559 } else if (node.symbol) {
560 if (node.symbol.flags === ts.SymbolFlags.Class) {
561 this.processClass(node, file, srcFile, outputSymbols, fileBody);
562 } else if (node.symbol.flags === ts.SymbolFlags.Interface) {
563 const name = this.getSymboleName(node);
564 const IO = this.getInterfaceIO(file, srcFile, node, fileBody);
565 const interfaceDeps: IInterfaceDep = {
566 name,
567 id: 'interface-' + name + '-' + hash,
568 file: file,
569 deprecated: IO.deprecated,
570 deprecationMessage: IO.deprecationMessage,
571 type: 'interface',
572 sourceCode: srcFile.getText()
573 };
574 if (IO.properties) {
575 interfaceDeps.properties = IO.properties;
576 }
577 if (IO.indexSignatures) {
578 interfaceDeps.indexSignatures = IO.indexSignatures;
579 }
580 if (IO.kind) {
581 interfaceDeps.kind = IO.kind;
582 }
583 if (IO.description) {
584 interfaceDeps.description = IO.description;
585 interfaceDeps.rawdescription = IO.rawdescription;
586 }
587 if (IO.methods) {
588 interfaceDeps.methods = IO.methods;
589 }
590 if (IO.extends) {
591 interfaceDeps.extends = IO.extends;
592 }
593 if (typeof IO.ignore === 'undefined') {
594 this.debug(interfaceDeps);
595 outputSymbols.interfaces.push(interfaceDeps);
596 } else {
597 this.ignore(interfaceDeps);
598 }
599 } else if (ts.isFunctionDeclaration(node)) {
600 const infos = this.visitFunctionDeclaration(node);
601 const name = infos.name;
602 const deprecated = infos.deprecated;
603 const deprecationMessage = infos.deprecationMessage;
604 const functionDep: IFunctionDecDep = {
605 name,
606 file: file,
607 ctype: 'miscellaneous',
608 subtype: 'function',
609 deprecated,
610 deprecationMessage,
611 description: this.visitEnumTypeAliasFunctionDeclarationDescription(node)
612 };
613 if (infos.args) {
614 functionDep.args = infos.args;
615 }
616 if (infos.returnType) {
617 functionDep.returnType = infos.returnType;
618 }
619 if (infos.jsdoctags && infos.jsdoctags.length > 0) {
620 functionDep.jsdoctags = infos.jsdoctags;
621 }
622 if (typeof infos.ignore === 'undefined') {
623 if (
624 !(
625 this.hasPrivateJSDocTag(functionDep.jsdoctags) &&
626 Configuration.mainData.disablePrivate
627 )
628 ) {
629 this.debug(functionDep);
630 outputSymbols.miscellaneous.functions.push(functionDep);
631 }
632 }
633 } else if (ts.isEnumDeclaration(node)) {
634 const infos = this.visitEnumDeclaration(node);
635 const name = infos.name;
636 const deprecated = infos.deprecated;
637 const deprecationMessage = infos.deprecationMessage;
638 const enumDeps: IEnumDecDep = {
639 name,
640 childs: infos.members,
641 ctype: 'miscellaneous',
642 subtype: 'enum',
643 deprecated,
644 deprecationMessage,
645 description:
646 this.visitEnumTypeAliasFunctionDeclarationDescription(node),
647 file: file
648 };
649
650 if (!isIgnore(node)) {
651 this.debug(enumDeps);
652 outputSymbols.miscellaneous.enumerations.push(enumDeps);
653 }
654 } else if (ts.isTypeAliasDeclaration(node)) {
655 const infos = this.visitTypeDeclaration(node);
656 const name = infos.name;
657 const deprecated = infos.deprecated;
658 const deprecationMessage = infos.deprecationMessage;
659 const typeAliasDeps: ITypeAliasDecDep = {
660 name,
661 ctype: 'miscellaneous',
662 subtype: 'typealias',
663 rawtype: this.classHelper.visitType(node),
664 file: file,
665 deprecated,
666 deprecationMessage,
667 description: this.visitEnumTypeAliasFunctionDeclarationDescription(node)
668 };
669 if (node.type) {
670 typeAliasDeps.kind = node.type.kind;
671 if (typeAliasDeps.rawtype === '') {
672 typeAliasDeps.rawtype = this.classHelper.visitType(node);
673 }
674 }
675
676 if (
677 typeAliasDeps.kind &&
678 typeAliasDeps.kind === SyntaxKind.TemplateLiteralType &&
679 node.type
680 ) {
681 typeAliasDeps.rawtype = srcFile.text.substring(
682 node.type.pos,
683 node.type.end
684 );
685 }
686
687 if (!isIgnore(node)) {
688 outputSymbols.miscellaneous.typealiases.push(typeAliasDeps);
689 }
690
691 if (typeof infos.ignore === 'undefined') {
692 this.debug(typeAliasDeps);
693 }
694 } else if (ts.isModuleDeclaration(node)) {
695 if (node.body) {
696 if (node.body.statements && node.body.statements.length > 0) {
697 node.body.statements.forEach(statement =>
698 parseNode(file, srcFile, statement, node.body)
699 );
700 }
701 }
702 }
703 } else {
704 const IO = this.getRouteIO(file, srcFile, node);
705 if (IO.routes) {
706 let newRoutes;
707 try {
708 newRoutes = RouterParserUtil.cleanRawRouteParsed(IO.routes);
709 } catch (e) {
710 // tslint:disable-next-line:max-line-length
711 logger.error(
712 'Routes parsing error, maybe a trailing comma or an external variable, trying to fix that later after sources scanning.'
713 );
714 newRoutes = IO.routes.replace(/ /gm, '');
715 RouterParserUtil.addIncompleteRoute({
716 data: newRoutes,
717 file: file
718 });
719 return true;
720 }
721 outputSymbols.routes = [...outputSymbols.routes, ...newRoutes];
722 }
723 if (ts.isClassDeclaration(node)) {
724 this.processClass(node, file, srcFile, outputSymbols, fileBody);
725 }
726 if (ts.isExpressionStatement(node) || ts.isIfStatement(node)) {
727 const bootstrapModuleReference = 'bootstrapModule';
728 // Find the root module with bootstrapModule call
729 // 1. find a simple call : platformBrowserDynamic().bootstrapModule(AppModule);
730 // 2. or inside a call :
731 // () => {
732 // platformBrowserDynamic().bootstrapModule(AppModule);
733 // });
734 // 3. with a catch : platformBrowserDynamic().bootstrapModule(AppModule).catch(error => console.error(error));
735 // 4. with parameters : platformBrowserDynamic().bootstrapModule(AppModule, {}).catch(error => console.error(error));
736 // Find recusively in expression nodes one with name 'bootstrapModule'
737 let rootModule;
738 let resultNode;
739 if (srcFile.text.indexOf(bootstrapModuleReference) !== -1) {
740 if (node.expression) {
741 resultNode = this.findExpressionByNameInExpressions(
742 node.expression,
743 'bootstrapModule'
744 );
745 }
746 if (typeof node.thenStatement !== 'undefined') {
747 if (
748 node.thenStatement.statements &&
749 node.thenStatement.statements.length > 0
750 ) {
751 let firstStatement = node.thenStatement.statements[0];
752 resultNode = this.findExpressionByNameInExpressions(
753 firstStatement.expression,
754 'bootstrapModule'
755 );
756 }
757 }
758 if (!resultNode) {
759 if (
760 node.expression &&
761 node.expression.arguments &&
762 node.expression.arguments.length > 0
763 ) {
764 resultNode = this.findExpressionByNameInExpressionArguments(
765 node.expression.arguments,
766 'bootstrapModule'
767 );
768 }
769 }
770 if (resultNode) {
771 if (resultNode.arguments.length > 0) {
772 _.forEach(resultNode.arguments, (argument: any) => {
773 if (argument.text) {
774 rootModule = argument.text;
775 }
776 });
777 }
778 if (rootModule) {
779 RouterParserUtil.setRootModule(rootModule);
780 }
781 }
782 }
783 }
784 if (ts.isVariableStatement(node) && !RouterParserUtil.isVariableRoutes(node)) {
785 let isDestructured = false;
786 // Check for destructuring array
787 const nodeVariableDeclarations = node.declarationList.declarations;
788 if (nodeVariableDeclarations) {
789 if (nodeVariableDeclarations.length > 0) {
790 if (
791 nodeVariableDeclarations[0].name &&
792 nodeVariableDeclarations[0].name.kind ===
793 SyntaxKind.ArrayBindingPattern
794 ) {
795 isDestructured = true;
796 }
797 }
798 }
799
800 const visitVariableNode = variableNode => {
801 const infos: any = this.visitVariableDeclaration(variableNode);
802 if (infos) {
803 const name = infos.name;
804 const deprecated = infos.deprecated;
805 const deprecationMessage = infos.deprecationMessage;
806 const deps: any = {
807 name,
808 ctype: 'miscellaneous',
809 subtype: 'variable',
810 file: file,
811 deprecated,
812 deprecationMessage
813 };
814 deps.type = infos.type ? infos.type : '';
815 if (infos.defaultValue) {
816 deps.defaultValue = infos.defaultValue;
817 }
818 if (infos.initializer) {
819 deps.initializer = infos.initializer;
820 }
821 if (
822 variableNode.jsDoc &&
823 variableNode.jsDoc.length > 0 &&
824 variableNode.jsDoc[0].comment
825 ) {
826 const rawDescription = this.jsdocParserUtil.parseJSDocNode(
827 variableNode.jsDoc[0]
828 );
829 deps.rawdescription = rawDescription;
830 deps.description = marked(rawDescription);
831 }
832 if (isModuleWithProviders(variableNode)) {
833 const routingInitializer = getModuleWithProviders(variableNode);
834 RouterParserUtil.addModuleWithRoutes(
835 name,
836 [routingInitializer],
837 file
838 );
839 RouterParserUtil.addModule(name, [routingInitializer]);
840 }
841 if (!isIgnore(variableNode)) {
842 this.debug(deps);
843 outputSymbols.miscellaneous.variables.push(deps);
844 }
845 }
846 };
847
848 if (isDestructured) {
849 if (nodeVariableDeclarations[0].name.elements) {
850 const destructuredVariables =
851 nodeVariableDeclarations[0].name.elements;
852
853 for (let i = 0; i < destructuredVariables.length; i++) {
854 const destructuredVariable = destructuredVariables[i];
855 const name = destructuredVariable.name
856 ? destructuredVariable.name.escapedText
857 : '';
858 const deps: any = {
859 name,
860 ctype: 'miscellaneous',
861 subtype: 'variable',
862 file: file
863 };
864 if (nodeVariableDeclarations[0].initializer) {
865 if (nodeVariableDeclarations[0].initializer.elements) {
866 deps.initializer =
867 nodeVariableDeclarations[0].initializer.elements[i];
868 }
869 deps.defaultValue = deps.initializer
870 ? this.classHelper.stringifyDefaultValue(
871 deps.initializer
872 )
873 : undefined;
874 }
875
876 if (!isIgnore(destructuredVariables[i])) {
877 this.debug(deps);
878 outputSymbols.miscellaneous.variables.push(deps);
879 }
880 }
881 }
882 } else {
883 visitVariableNode(node);
884 }
885 }
886 if (ts.isTypeAliasDeclaration(node)) {
887 const infos = this.visitTypeDeclaration(node);
888 const name = infos.name;
889 const deprecated = infos.deprecated;
890 const deprecationMessage = infos.deprecationMessage;
891 const deps: ITypeAliasDecDep = {
892 name,
893 ctype: 'miscellaneous',
894 subtype: 'typealias',
895 rawtype: this.classHelper.visitType(node),
896 file: file,
897 deprecated,
898 deprecationMessage,
899 description: this.visitEnumTypeAliasFunctionDeclarationDescription(node)
900 };
901 if (node.type) {
902 deps.kind = node.type.kind;
903 }
904 if (
905 deps.kind &&
906 deps.kind === SyntaxKind.TemplateLiteralType &&
907 node.type
908 ) {
909 deps.rawtype = srcFile.text.substring(node.type.pos, node.type.end);
910 }
911 if (!isIgnore(node)) {
912 this.debug(deps);
913 outputSymbols.miscellaneous.typealiases.push(deps);
914 }
915 }
916 if (ts.isFunctionDeclaration(node)) {
917 const infos = this.visitFunctionDeclaration(node);
918 const name = infos.name;
919 const deprecated = infos.deprecated;
920 const deprecationMessage = infos.deprecationMessage;
921 const functionDep: IFunctionDecDep = {
922 name,
923 ctype: 'miscellaneous',
924 subtype: 'function',
925 file: file,
926 deprecated,
927 deprecationMessage,
928 description: this.visitEnumTypeAliasFunctionDeclarationDescription(node)
929 };
930 if (infos.args) {
931 functionDep.args = infos.args;
932 }
933 if (infos.returnType) {
934 functionDep.returnType = infos.returnType;
935 }
936 if (infos.jsdoctags && infos.jsdoctags.length > 0) {
937 functionDep.jsdoctags = infos.jsdoctags;
938 }
939 if (typeof infos.ignore === 'undefined') {
940 if (
941 !(
942 this.hasPrivateJSDocTag(functionDep.jsdoctags) &&
943 Configuration.mainData.disablePrivate
944 )
945 ) {
946 this.debug(functionDep);
947 outputSymbols.miscellaneous.functions.push(functionDep);
948 }
949 }
950 }
951 if (ts.isEnumDeclaration(node)) {
952 const infos = this.visitEnumDeclaration(node);
953 const name = infos.name;
954 const deprecated = infos.deprecated;
955 const deprecationMessage = infos.deprecationMessage;
956 const enumDeps: IEnumDecDep = {
957 name,
958 childs: infos.members,
959 ctype: 'miscellaneous',
960 subtype: 'enum',
961 deprecated,
962 deprecationMessage,
963 description:
964 this.visitEnumTypeAliasFunctionDeclarationDescription(node),
965 file: file
966 };
967 if (!isIgnore(node)) {
968 this.debug(enumDeps);
969 outputSymbols.miscellaneous.enumerations.push(enumDeps);
970 }
971 }
972 }
973 };
974
975 parseNode(fileName, scannedFile, initialNode);
976 });
977 }
978
979 /**
980 * Function to in a specific store an entity, and check before is there is not the same one
981 * in that store : same name, id and file
982 * @param entity Entity to store
983 * @param store Store
984 */
985 private addNewEntityInStore(entity, store) {
986 const findSameEntityInStore = _.filter(store, {
987 name: entity.name,
988 id: entity.id,
989 file: entity.file
990 });
991 if (findSameEntityInStore.length === 0) {
992 store.push(entity);
993 }
994 }
995
996 private debug(deps: IDep) {
997 if (deps) {
998 logger.debug('found', `${deps.name}`);
999 } else {
1000 return;
1001 }
1002 ['imports', 'exports', 'declarations', 'providers', 'bootstrap'].forEach(symbols => {
1003 if (deps[symbols] && deps[symbols].length > 0) {
1004 logger.debug('', `- ${symbols}:`);
1005 deps[symbols]
1006 .map(i => i.name)
1007 .forEach(d => {
1008 logger.debug('', `\t- ${d}`);
1009 });
1010 }
1011 });
1012 }
1013
1014 private ignore(deps: IDep) {
1015 if (deps) {
1016 logger.warn('ignore', `${deps.name}`);
1017 } else {
1018 return;
1019 }
1020 }
1021
1022 private checkForDeprecation(tags: any[], result: { [key in string | number]: any }) {
1023 _.forEach(tags, tag => {
1024 if (tag.tagName && tag.tagName.text && tag.tagName.text.indexOf('deprecated') > -1) {
1025 result.deprecated = true;
1026 result.deprecationMessage = tag.comment || '';
1027 }
1028 });
1029 }
1030
1031 private findExpressionByNameInExpressions(entryNode, name) {
1032 let result;
1033 const loop = function (node, z) {
1034 if (node) {
1035 if (node.expression && !node.expression.name) {
1036 loop(node.expression, z);
1037 }
1038 if (node.expression && node.expression.name) {
1039 if (node.expression.name.text === z) {
1040 result = node;
1041 } else {
1042 loop(node.expression, z);
1043 }
1044 }
1045 }
1046 };
1047 loop(entryNode, name);
1048 return result;
1049 }
1050
1051 private findExpressionByNameInExpressionArguments(arg, name) {
1052 let result;
1053 const that = this;
1054 let i = 0;
1055 let len = arg.length;
1056 const loop = function (node, z) {
1057 if (node.body) {
1058 if (node.body.statements && node.body.statements.length > 0) {
1059 let j = 0;
1060 const leng = node.body.statements.length;
1061 for (j; j < leng; j++) {
1062 result = that.findExpressionByNameInExpressions(node.body.statements[j], z);
1063 }
1064 }
1065 }
1066 };
1067 for (i; i < len; i++) {
1068 loop(arg[i], name);
1069 }
1070 return result;
1071 }
1072
1073 private parseDecorators(decorators, type: string): boolean {
1074 let result = false;
1075 if (decorators.length > 1) {
1076 _.forEach(decorators, function (decorator: any) {
1077 if (decorator.expression.expression) {
1078 if (decorator.expression.expression.text === type) {
1079 result = true;
1080 }
1081 }
1082 });
1083 } else {
1084 if (decorators[0].expression.expression) {
1085 if (decorators[0].expression.expression.text === type) {
1086 result = true;
1087 }
1088 }
1089 }
1090 return result;
1091 }
1092
1093 private parseDecorator(decorator, type: string): boolean {
1094 let result = false;
1095 if (decorator.expression.expression) {
1096 if (decorator.expression.expression.text === type) {
1097 result = true;
1098 }
1099 }
1100 return result;
1101 }
1102
1103 private isController(metadata) {
1104 return this.parseDecorator(metadata, 'Controller');
1105 }
1106
1107 private isEntity(metadata) {
1108 return this.parseDecorator(metadata, 'Entity');
1109 }
1110
1111 private isComponent(metadata) {
1112 return this.parseDecorator(metadata, 'Component');
1113 }
1114
1115 private isPipe(metadata) {
1116 return this.parseDecorator(metadata, 'Pipe');
1117 }
1118
1119 private isDirective(metadata) {
1120 return this.parseDecorator(metadata, 'Directive');
1121 }
1122
1123 private isInjectable(metadata) {
1124 return this.parseDecorator(metadata, 'Injectable');
1125 }
1126
1127 private isModule(metadata) {
1128 return this.parseDecorator(metadata, 'NgModule') || this.parseDecorator(metadata, 'Module');
1129 }
1130
1131 private hasInternalDecorator(metadatas) {
1132 return (
1133 this.parseDecorators(metadatas, 'Controller') ||
1134 this.parseDecorators(metadatas, 'Component') ||
1135 this.parseDecorators(metadatas, 'Pipe') ||
1136 this.parseDecorators(metadatas, 'Directive') ||
1137 this.parseDecorators(metadatas, 'Injectable') ||
1138 this.parseDecorators(metadatas, 'NgModule') ||
1139 this.parseDecorators(metadatas, 'Module')
1140 );
1141 }
1142
1143 private isGuard(ioImplements: string[]): boolean {
1144 return (
1145 _.includes(ioImplements, 'CanActivate') ||
1146 _.includes(ioImplements, 'CanActivateChild') ||
1147 _.includes(ioImplements, 'CanDeactivate') ||
1148 _.includes(ioImplements, 'Resolve') ||
1149 _.includes(ioImplements, 'CanLoad')
1150 );
1151 }
1152
1153 private getSymboleName(node): string {
1154 return node.name.text;
1155 }
1156
1157 private findProperties(
1158 visitedNode: ts.Decorator,
1159 sourceFile: ts.SourceFile
1160 ): ReadonlyArray<ts.ObjectLiteralElementLike> {
1161 if (
1162 visitedNode.expression &&
1163 visitedNode.expression.arguments &&
1164 visitedNode.expression.arguments.length > 0
1165 ) {
1166 const pop = visitedNode.expression.arguments[0];
1167
1168 if (pop && pop.properties && pop.properties.length >= 0) {
1169 return pop.properties;
1170 } else if (pop && pop.kind && pop.kind === SyntaxKind.StringLiteral) {
1171 return [pop];
1172 } else {
1173 logger.warn('Empty metadatas, trying to find it with imports.');
1174 return ImportsUtil.findValueInImportOrLocalVariables(pop.text, sourceFile);
1175 }
1176 }
1177
1178 return [];
1179 }
1180
1181 private isAngularLifecycleHook(methodName) {
1182 /**
1183 * Copyright https://github.com/ng-bootstrap/ng-bootstrap
1184 */
1185 const ANGULAR_LIFECYCLE_METHODS = [
1186 'ngOnInit',
1187 'ngOnChanges',
1188 'ngDoCheck',
1189 'ngOnDestroy',
1190 'ngAfterContentInit',
1191 'ngAfterContentChecked',
1192 'ngAfterViewInit',
1193 'ngAfterViewChecked',
1194 'writeValue',
1195 'registerOnChange',
1196 'registerOnTouched',
1197 'setDisabledState'
1198 ];
1199 return ANGULAR_LIFECYCLE_METHODS.indexOf(methodName) >= 0;
1200 }
1201
1202 private visitTypeDeclaration(node: ts.TypeAliasDeclaration) {
1203 const result: any = {
1204 deprecated: false,
1205 deprecationMessage: '',
1206 name: node.name.text,
1207 kind: node.kind
1208 };
1209 const jsdoctags = this.jsdocParserUtil.getJSDocs(node);
1210
1211 if (jsdoctags && jsdoctags.length >= 1 && jsdoctags[0].tags) {
1212 this.checkForDeprecation(jsdoctags[0].tags, result);
1213 result.jsdoctags = markedtags(jsdoctags[0].tags);
1214 }
1215 return result;
1216 }
1217
1218 private visitArgument(arg) {
1219 if (arg.name && arg.name.kind == SyntaxKind.ObjectBindingPattern) {
1220 let results = [];
1221
1222 const destrucuredGroupId = uuidv4();
1223
1224 results = arg.name.elements.map(element => this.visitArgument(element));
1225
1226 results = results.map(result => {
1227 result.destrucuredGroupId = destrucuredGroupId;
1228 return result;
1229 });
1230
1231 if (arg.name.elements && arg.type && arg.type.members) {
1232 if (arg.name.elements.length === arg.type.members.length) {
1233 for (let i = 0; i < arg.name.elements.length; i++) {
1234 results[i].type = this.classHelper.visitType(arg.type.members[i]);
1235 }
1236 }
1237 }
1238
1239 if (arg.name.elements && arg.type && arg.type.typeName) {
1240 results[0].type = this.classHelper.visitType(arg.type);
1241 }
1242
1243 return results;
1244 } else {
1245 const result: any = {
1246 name: arg.name.text,
1247 type: this.classHelper.visitType(arg),
1248 deprecated: false,
1249 deprecationMessage: ''
1250 };
1251
1252 if (arg.dotDotDotToken) {
1253 result.dotDotDotToken = true;
1254 }
1255 if (arg.questionToken) {
1256 result.optional = true;
1257 }
1258 if (arg.initializer) {
1259 result.defaultValue = arg.initializer
1260 ? this.classHelper.stringifyDefaultValue(arg.initializer)
1261 : undefined;
1262 }
1263 if (arg.type) {
1264 result.type = this.mapType(arg.type.kind);
1265 if (arg.type.kind === SyntaxKind.TypeReference) {
1266 // try replace TypeReference with typeName
1267 if (arg.type.typeName) {
1268 result.type = arg.type.typeName.text;
1269 }
1270 }
1271 }
1272 const jsdoctags = this.jsdocParserUtil.getJSDocs(arg);
1273
1274 if (jsdoctags && jsdoctags.length >= 1 && jsdoctags[0].tags) {
1275 this.checkForDeprecation(jsdoctags[0].tags, result);
1276 }
1277 return result;
1278 }
1279 }
1280
1281 private mapType(type): string | undefined {
1282 switch (type) {
1283 case SyntaxKind.NullKeyword:
1284 return 'null';
1285 case SyntaxKind.AnyKeyword:
1286 return 'any';
1287 case SyntaxKind.BooleanKeyword:
1288 return 'boolean';
1289 case SyntaxKind.NeverKeyword:
1290 return 'never';
1291 case SyntaxKind.NumberKeyword:
1292 return 'number';
1293 case SyntaxKind.StringKeyword:
1294 return 'string';
1295 case SyntaxKind.UndefinedKeyword:
1296 return 'undefined';
1297 case SyntaxKind.TypeReference:
1298 return 'typeReference';
1299 }
1300 }
1301
1302 private hasPrivateJSDocTag(tags): boolean {
1303 let result = false;
1304 if (tags) {
1305 tags.forEach(tag => {
1306 if (tag.tagName && tag.tagName.text && tag.tagName.text === 'private') {
1307 result = true;
1308 }
1309 });
1310 }
1311 return result;
1312 }
1313
1314 private visitFunctionDeclaration(method: ts.FunctionDeclaration) {
1315 const methodName = method.name ? method.name.text : 'Unnamed function';
1316 const resultArguments = [];
1317 const result: any = {
1318 deprecated: false,
1319 deprecationMessage: '',
1320 name: methodName
1321 };
1322
1323 for (let i = 0; i < method.parameters.length; i++) {
1324 const argument = method.parameters[i];
1325 if (argument) {
1326 const argumentParsed = this.visitArgument(argument);
1327 if (argumentParsed.length > 0) {
1328 for (let j = 0; j < argumentParsed.length; j++) {
1329 const argumentParsedInside = argumentParsed[j];
1330 argumentParsedInside.destructuredParameter = true;
1331 resultArguments.push(argumentParsedInside);
1332 }
1333 } else {
1334 resultArguments.push(argumentParsed);
1335 }
1336 }
1337 }
1338
1339 result.args = resultArguments;
1340
1341 const jsdoctags = this.jsdocParserUtil.getJSDocs(method);
1342
1343 if (typeof method.type !== 'undefined') {
1344 result.returnType = this.classHelper.visitType(method.type);
1345 }
1346
1347 if (method.modifiers) {
1348 if (method.modifiers.length > 0) {
1349 let kinds = method.modifiers
1350 .map(modifier => {
1351 return modifier.kind;
1352 })
1353 .reverse();
1354 if (
1355 _.indexOf(kinds, SyntaxKind.PublicKeyword) !== -1 &&
1356 _.indexOf(kinds, SyntaxKind.StaticKeyword) !== -1
1357 ) {
1358 kinds = kinds.filter(kind => kind !== SyntaxKind.PublicKeyword);
1359 }
1360 }
1361 }
1362 if (jsdoctags && jsdoctags.length >= 1 && jsdoctags[0].tags) {
1363 this.checkForDeprecation(jsdoctags[0].tags, result);
1364 result.jsdoctags = markedtags(jsdoctags[0].tags);
1365 _.forEach(jsdoctags[0].tags, tag => {
1366 if (tag.tagName) {
1367 if (tag.tagName.text) {
1368 if (tag.tagName.text.indexOf('ignore') > -1) {
1369 result.ignore = true;
1370 }
1371 }
1372 }
1373 });
1374 }
1375 if (result.jsdoctags && result.jsdoctags.length > 0) {
1376 result.jsdoctags = mergeTagsAndArgs(result.args, result.jsdoctags);
1377 } else if (result.args.length > 0) {
1378 result.jsdoctags = mergeTagsAndArgs(result.args);
1379 }
1380 return result;
1381 }
1382
1383 private visitVariableDeclaration(node) {
1384 if (node.declarationList && node.declarationList.declarations) {
1385 let i = 0;
1386 const len = node.declarationList.declarations.length;
1387 for (i; i < len; i++) {
1388 const result: any = {
1389 name: node.declarationList.declarations[i].name.text,
1390 defaultValue: node.declarationList.declarations[i].initializer
1391 ? this.classHelper.stringifyDefaultValue(
1392 node.declarationList.declarations[i].initializer
1393 )
1394 : undefined,
1395 deprecated: false,
1396 deprecationMessage: ''
1397 };
1398 if (node.declarationList.declarations[i].initializer) {
1399 result.initializer = node.declarationList.declarations[i].initializer;
1400 }
1401 if (node.declarationList.declarations[i].type) {
1402 result.type = this.classHelper.visitType(
1403 node.declarationList.declarations[i].type
1404 );
1405 }
1406 if (typeof result.type === 'undefined' && result.initializer) {
1407 result.type = kindToType(result.initializer.kind);
1408 }
1409 const jsdoctags = this.jsdocParserUtil.getJSDocs(
1410 node.declarationList.declarations[i]
1411 );
1412 if (jsdoctags && jsdoctags.length >= 1 && jsdoctags[0].tags) {
1413 this.checkForDeprecation(jsdoctags[0].tags, result);
1414 }
1415 return result;
1416 }
1417 }
1418 }
1419
1420 private visitEnumTypeAliasFunctionDeclarationDescription(node): string {
1421 let description: string = '';
1422 if (node.jsDoc) {
1423 if (node.jsDoc.length > 0) {
1424 if (typeof node.jsDoc[0].comment !== 'undefined') {
1425 const rawDescription = this.jsdocParserUtil.parseJSDocNode(node.jsDoc[0]);
1426 description = marked(rawDescription);
1427 }
1428 }
1429 }
1430 return description;
1431 }
1432
1433 private visitEnumDeclaration(node: ts.EnumDeclaration) {
1434 const result: any = {
1435 deprecated: false,
1436 deprecationMessage: '',
1437 name: node.name.text,
1438 members: []
1439 };
1440 if (node.members) {
1441 let i = 0;
1442 let len = node.members.length;
1443 let memberjsdoctags = [];
1444 for (i; i < len; i++) {
1445 const member: any = {
1446 name: node.members[i].name.text,
1447 deprecated: false,
1448 deprecationMessage: ''
1449 };
1450 if (node.members[i].initializer) {
1451 // if the initializer kind is a number do cast to the number type
1452 member.value = IsKindType.NUMBER(node.members[i].initializer.kind)
1453 ? Number(node.members[i].initializer.text)
1454 : node.members[i].initializer.text;
1455 }
1456 memberjsdoctags = this.jsdocParserUtil.getJSDocs(node.members[i]);
1457 if (memberjsdoctags && memberjsdoctags.length >= 1 && memberjsdoctags[0].tags) {
1458 this.checkForDeprecation(memberjsdoctags[0].tags, member);
1459 }
1460 result.members.push(member);
1461 }
1462 }
1463 const jsdoctags = this.jsdocParserUtil.getJSDocs(node);
1464 if (jsdoctags && jsdoctags.length >= 1 && jsdoctags[0].tags) {
1465 this.checkForDeprecation(jsdoctags[0].tags, result);
1466 }
1467 return result;
1468 }
1469
1470 private visitEnumDeclarationForRoutes(fileName, node) {
1471 if (node.declarationList.declarations) {
1472 let i = 0;
1473 let len = node.declarationList.declarations.length;
1474 for (i; i < len; i++) {
1475 const routesInitializer = node.declarationList.declarations[i].initializer;
1476 const data = new CodeGenerator().generate(routesInitializer);
1477 RouterParserUtil.addRoute({
1478 name: node.declarationList.declarations[i].name.text,
1479 data: RouterParserUtil.cleanRawRoute(data),
1480 filename: fileName
1481 });
1482 return [
1483 {
1484 routes: data
1485 }
1486 ];
1487 }
1488 }
1489 return [];
1490 }
1491
1492 private getRouteIO(filename: string, sourceFile: ts.SourceFile, node: ts.Node) {
1493 let res;
1494 if (sourceFile.statements) {
1495 res = sourceFile.statements.reduce((directive, statement) => {
1496 if (RouterParserUtil.isVariableRoutes(statement)) {
1497 if (statement.pos === node.pos && statement.end === node.end) {
1498 return directive.concat(
1499 this.visitEnumDeclarationForRoutes(filename, statement)
1500 );
1501 }
1502 }
1503
1504 return directive;
1505 }, []);
1506 return res[0] || {};
1507 } else {
1508 return {};
1509 }
1510 }
1511
1512 private getClassIO(filename: string, sourceFile: ts.SourceFile, node: ts.Node, fileBody) {
1513 /**
1514 * Copyright https://github.com/ng-bootstrap/ng-bootstrap
1515 */
1516 const reducedSource = fileBody ? fileBody.statements : sourceFile.statements;
1517 const res = reducedSource.reduce((directive, statement) => {
1518 if (ts.isClassDeclaration(statement)) {
1519 if (statement.pos === node.pos && statement.end === node.end) {
1520 return directive.concat(
1521 this.classHelper.visitClassDeclaration(filename, statement, sourceFile)
1522 );
1523 }
1524 }
1525
1526 return directive;
1527 }, []);
1528
1529 return res[0] || {};
1530 }
1531
1532 private getInterfaceIO(filename: string, sourceFile, node, fileBody) {
1533 /**
1534 * Copyright https://github.com/ng-bootstrap/ng-bootstrap
1535 */
1536 const reducedSource = fileBody ? fileBody.statements : sourceFile.statements;
1537 const res = reducedSource.reduce((directive, statement) => {
1538 if (ts.isInterfaceDeclaration(statement)) {
1539 if (statement.pos === node.pos && statement.end === node.end) {
1540 return directive.concat(
1541 this.classHelper.visitClassDeclaration(filename, statement, sourceFile)
1542 );
1543 }
1544 }
1545
1546 return directive;
1547 }, []);
1548
1549 return res[0] || {};
1550 }
1551}