1 | import * as path from 'path';
|
2 |
|
3 | import * as _ from 'lodash';
|
4 | import { Project, ts, SyntaxKind } from 'ts-morph';
|
5 |
|
6 | import { IsKindType, kindToType } from '../../utils/kind-to-type';
|
7 | import { logger } from '../../utils/logger';
|
8 | import { cleanLifecycleHooksFromMethods, markedtags, mergeTagsAndArgs } from '../../utils/utils';
|
9 | import ComponentsTreeEngine from '../engines/components-tree.engine';
|
10 |
|
11 | import { FrameworkDependencies } from './framework-dependencies';
|
12 |
|
13 | import ImportsUtil from '../../utils/imports.util';
|
14 |
|
15 | import {
|
16 | getModuleWithProviders,
|
17 | isIgnore,
|
18 | isModuleWithProviders,
|
19 | JsdocParserUtil
|
20 | } from '../../utils';
|
21 |
|
22 | import ExtendsMerger from '../../utils/extends-merger.util';
|
23 |
|
24 | import RouterParserUtil from '../../utils/router-parser.util';
|
25 |
|
26 | import { CodeGenerator } from './angular/code-generator';
|
27 |
|
28 | import { ComponentDepFactory } from './angular/deps/component-dep.factory';
|
29 | import { ControllerDepFactory } from './angular/deps/controller-dep.factory';
|
30 | import { DirectiveDepFactory } from './angular/deps/directive-dep.factory';
|
31 | import { ComponentCache } from './angular/deps/helpers/component-helper';
|
32 | import { JsDocHelper } from './angular/deps/helpers/js-doc-helper';
|
33 | import { ModuleHelper } from './angular/deps/helpers/module-helper';
|
34 | import { SymbolHelper } from './angular/deps/helpers/symbol-helper';
|
35 | import { ModuleDepFactory } from './angular/deps/module-dep.factory';
|
36 | import { EntityDepFactory } from './angular/deps/entity-dep.factory';
|
37 |
|
38 | import Configuration from '../configuration';
|
39 |
|
40 | import {
|
41 | IDep,
|
42 | IEnumDecDep,
|
43 | IFunctionDecDep,
|
44 | IInjectableDep,
|
45 | IInterfaceDep,
|
46 | IPipeDep,
|
47 | ITypeAliasDecDep
|
48 | } from './angular/dependencies.interfaces';
|
49 |
|
50 | import { v4 as uuidv4 } from 'uuid';
|
51 |
|
52 | const crypto = require('crypto');
|
53 | const { marked } = require('marked');
|
54 | const ast = new Project();
|
55 |
|
56 |
|
57 |
|
58 | export 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 |
|
118 |
|
119 |
|
120 | if (deps.miscellaneous.variables.length > 0) {
|
121 | deps.miscellaneous.variables.forEach(_variable => {
|
122 | let newVar = [];
|
123 |
|
124 |
|
125 | ((_var, _newVar) => {
|
126 |
|
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 |
|
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 |
|
143 |
|
144 |
|
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 |
|
177 | if (found) {
|
178 | initialArray.splice(indexToClean, 1);
|
179 |
|
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 |
|
204 |
|
205 |
|
206 |
|
207 |
|
208 |
|
209 |
|
210 |
|
211 |
|
212 |
|
213 |
|
214 | deps = ExtendsMerger.merge(deps);
|
215 |
|
216 |
|
217 |
|
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 |
|
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 |
|
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 |
|
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 |
|
345 | logger.info('Analysing routes definitions and clean them if necessary');
|
346 |
|
347 |
|
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 |
|
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 |
|
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 |
|
729 |
|
730 |
|
731 |
|
732 |
|
733 |
|
734 |
|
735 |
|
736 |
|
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 |
|
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 |
|
981 |
|
982 |
|
983 |
|
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 |
|
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 |
|
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 |
|
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 |
|
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 |
|
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 | }
|