1 | import * as Handlebars from 'handlebars';
|
2 | import * as JSON5 from 'json5';
|
3 | import * as _ from 'lodash';
|
4 | import * as path from 'path';
|
5 | import { Project, ts, SourceFile, SyntaxKind, Node } from 'ts-morph';
|
6 |
|
7 | import FileEngine from '../app/engines/file.engine';
|
8 | import { RoutingGraphNode } from '../app/nodes/routing-graph-node';
|
9 |
|
10 | import ImportsUtil from './imports.util';
|
11 | import { logger } from './logger';
|
12 |
|
13 | const traverse = require('traverse');
|
14 |
|
15 | const ast = new Project();
|
16 |
|
17 | export class RouterParserUtil {
|
18 | private routes: any[] = [];
|
19 | private incompleteRoutes = [];
|
20 | private modules = [];
|
21 | private modulesTree;
|
22 | private rootModule: string;
|
23 | private cleanModulesTree;
|
24 | private modulesWithRoutes = [];
|
25 | private transformAngular8ImportSyntax =
|
26 | /(['"]loadChildren['"]:)\(\)(:[^)]+?)?=>"import\((\\'|'|"|`)([^'"]+?)(\\'|'|"|`)\)\.then\(\(?\w+?\)?=>\S+?\.([^)]+?)\)(\\'|'|")/g;
|
27 | private transformAngular8ImportSyntaxAsyncAwait =
|
28 | /(['"]loadChildren['"]:)\(\)(:[^)]+?)?=>\("import\((\\'|'|"|`)([^'"]+?)(\\'|'|"|`)\)"\)\.['"]([^)]+?)['"]/g;
|
29 |
|
30 | private static instance: RouterParserUtil;
|
31 | private constructor() {}
|
32 | public static getInstance() {
|
33 | if (!RouterParserUtil.instance) {
|
34 | RouterParserUtil.instance = new RouterParserUtil();
|
35 | }
|
36 | return RouterParserUtil.instance;
|
37 | }
|
38 |
|
39 | public addRoute(route): void {
|
40 | this.routes.push(route);
|
41 | this.routes = _.sortBy(_.uniqWith(this.routes, _.isEqual), ['name']);
|
42 | }
|
43 |
|
44 | public addIncompleteRoute(route): void {
|
45 | this.incompleteRoutes.push(route);
|
46 | this.incompleteRoutes = _.sortBy(_.uniqWith(this.incompleteRoutes, _.isEqual), ['name']);
|
47 | }
|
48 |
|
49 | public addModuleWithRoutes(moduleName, moduleImports, filename): void {
|
50 | this.modulesWithRoutes.push({
|
51 | name: moduleName,
|
52 | importsNode: moduleImports,
|
53 | filename: filename
|
54 | });
|
55 | this.modulesWithRoutes = _.sortBy(_.uniqWith(this.modulesWithRoutes, _.isEqual), ['name']);
|
56 | }
|
57 |
|
58 | public addModule(moduleName: string, moduleImports): void {
|
59 | this.modules.push({
|
60 | name: moduleName,
|
61 | importsNode: moduleImports
|
62 | });
|
63 | this.modules = _.sortBy(_.uniqWith(this.modules, _.isEqual), ['name']);
|
64 | }
|
65 |
|
66 | public cleanRawRouteParsed(route: string): object {
|
67 | let routesWithoutSpaces = route.replace(/ /gm, '');
|
68 | const testTrailingComma = routesWithoutSpaces.indexOf('},]');
|
69 | if (testTrailingComma !== -1) {
|
70 | routesWithoutSpaces = routesWithoutSpaces.replace('},]', '}]');
|
71 | }
|
72 |
|
73 | routesWithoutSpaces = routesWithoutSpaces.replace(
|
74 | this.transformAngular8ImportSyntax,
|
75 | '$1"$4#$6"'
|
76 | );
|
77 |
|
78 | routesWithoutSpaces = routesWithoutSpaces.replace(
|
79 | this.transformAngular8ImportSyntaxAsyncAwait,
|
80 | '$1"$4#$6"'
|
81 | );
|
82 |
|
83 | return JSON5.parse(routesWithoutSpaces);
|
84 | }
|
85 |
|
86 | public cleanRawRoute(route: string): string {
|
87 | let routesWithoutSpaces = route.replace(/ /gm, '');
|
88 | let testTrailingComma = routesWithoutSpaces.indexOf('},]');
|
89 | if (testTrailingComma !== -1) {
|
90 | routesWithoutSpaces = routesWithoutSpaces.replace('},]', '}]');
|
91 | }
|
92 |
|
93 | routesWithoutSpaces = routesWithoutSpaces.replace(
|
94 | this.transformAngular8ImportSyntax,
|
95 | '$1"$4#$6"'
|
96 | );
|
97 |
|
98 | routesWithoutSpaces = routesWithoutSpaces.replace(
|
99 | this.transformAngular8ImportSyntaxAsyncAwait,
|
100 | '$1"$4#$6"'
|
101 | );
|
102 |
|
103 | return routesWithoutSpaces;
|
104 | }
|
105 |
|
106 | public setRootModule(module: string): void {
|
107 | this.rootModule = module;
|
108 | }
|
109 |
|
110 | public hasRouterModuleInImports(imports: Array<any>): boolean {
|
111 | for (let i = 0; i < imports.length; i++) {
|
112 | if (
|
113 | imports[i].name.indexOf('RouterModule.forChild') !== -1 ||
|
114 | imports[i].name.indexOf('RouterModule.forRoot') !== -1 ||
|
115 | imports[i].name.indexOf('RouterModule') !== -1
|
116 | ) {
|
117 | return true;
|
118 | }
|
119 | }
|
120 |
|
121 | return false;
|
122 | }
|
123 |
|
124 | public fixIncompleteRoutes(miscellaneousVariables: Array<any>): void {
|
125 | let matchingVariables = [];
|
126 |
|
127 |
|
128 | for (let i = 0; i < this.incompleteRoutes.length; i++) {
|
129 | for (let j = 0; j < miscellaneousVariables.length; j++) {
|
130 | if (this.incompleteRoutes[i].data.indexOf(miscellaneousVariables[j].name) !== -1) {
|
131 | console.log('found one misc var inside incompleteRoute');
|
132 | console.log(miscellaneousVariables[j].name);
|
133 | matchingVariables.push(miscellaneousVariables[j]);
|
134 | }
|
135 | }
|
136 |
|
137 | this.incompleteRoutes[i].data = this.incompleteRoutes[i].data.replace('[', '');
|
138 | this.incompleteRoutes[i].data = this.incompleteRoutes[i].data.replace(']', '');
|
139 | }
|
140 | }
|
141 |
|
142 | public linkModulesAndRoutes(): void {
|
143 | let i = 0;
|
144 | let len = this.modulesWithRoutes.length;
|
145 | for (i; i < len; i++) {
|
146 | _.forEach(this.modulesWithRoutes[i].importsNode, (node: ts.PropertyDeclaration) => {
|
147 | let initializer = node.initializer as ts.ArrayLiteralExpression;
|
148 | if (initializer) {
|
149 | if (initializer.elements) {
|
150 | _.forEach(initializer.elements, (element: ts.CallExpression) => {
|
151 |
|
152 | if (element.arguments) {
|
153 | _.forEach(element.arguments, (argument: ts.Identifier) => {
|
154 | _.forEach(this.routes, route => {
|
155 | if (
|
156 | argument.text &&
|
157 | route.name === argument.text &&
|
158 | route.filename === this.modulesWithRoutes[i].filename
|
159 | ) {
|
160 | route.module = this.modulesWithRoutes[i].name;
|
161 | } else if (
|
162 | argument.text &&
|
163 | route.name === argument.text &&
|
164 | route.filename !== this.modulesWithRoutes[i].filename
|
165 | ) {
|
166 | let argumentImportPath =
|
167 | ImportsUtil.findFilePathOfImportedVariable(
|
168 | argument.text,
|
169 | this.modulesWithRoutes[i].filename
|
170 | );
|
171 |
|
172 | argumentImportPath = argumentImportPath
|
173 | .replace(process.cwd() + path.sep, '')
|
174 | .replace(/\\/g, '/');
|
175 |
|
176 | if (
|
177 | argument.text &&
|
178 | route.name === argument.text &&
|
179 | route.filename === argumentImportPath
|
180 | ) {
|
181 | route.module = this.modulesWithRoutes[i].name;
|
182 | }
|
183 | }
|
184 | });
|
185 | });
|
186 | }
|
187 | });
|
188 | }
|
189 | }
|
190 | |
191 |
|
192 |
|
193 |
|
194 | if (ts.isCallExpression(node)) {
|
195 | if (node.arguments) {
|
196 | _.forEach(node.arguments, (argument: ts.Identifier) => {
|
197 | _.forEach(this.routes, route => {
|
198 | if (
|
199 | argument.text &&
|
200 | route.name === argument.text &&
|
201 | route.filename === this.modulesWithRoutes[i].filename
|
202 | ) {
|
203 | route.module = this.modulesWithRoutes[i].name;
|
204 | }
|
205 | });
|
206 | });
|
207 | }
|
208 | }
|
209 | });
|
210 | }
|
211 | }
|
212 |
|
213 | public foundRouteWithModuleName(moduleName: string): any {
|
214 | return _.find(this.routes, { module: moduleName });
|
215 | }
|
216 |
|
217 | public foundLazyModuleWithPath(modulePath: string): string {
|
218 |
|
219 | let split = modulePath.split('#');
|
220 | let lazyModulePath = split[0];
|
221 | let lazyModuleName = split[1];
|
222 | return lazyModuleName;
|
223 | }
|
224 |
|
225 | public constructRoutesTree() {
|
226 |
|
227 |
|
228 |
|
229 | traverse(this.modulesTree).forEach(function (node) {
|
230 | if (node) {
|
231 | if (node.parent) {
|
232 | delete node.parent;
|
233 | }
|
234 | if (node.initializer) {
|
235 | delete node.initializer;
|
236 | }
|
237 | if (node.importsNode) {
|
238 | delete node.importsNode;
|
239 | }
|
240 | }
|
241 | });
|
242 |
|
243 | this.cleanModulesTree = _.cloneDeep(this.modulesTree);
|
244 |
|
245 | let routesTree = {
|
246 | name: '<root>',
|
247 | kind: 'module',
|
248 | className: this.rootModule,
|
249 | children: []
|
250 | };
|
251 |
|
252 | let loopModulesParser = node => {
|
253 | if (node.children && node.children.length > 0) {
|
254 |
|
255 | for (let i in node.children) {
|
256 | let route = this.foundRouteWithModuleName(node.children[i].name);
|
257 | if (route && route.data) {
|
258 | try {
|
259 | route.children = JSON5.parse(route.data);
|
260 | } catch (e) {
|
261 | logger.error(
|
262 | 'Error during generation of routes JSON file, maybe a trailing comma or an external variable inside one route.'
|
263 | );
|
264 | }
|
265 | delete route.data;
|
266 | route.kind = 'module';
|
267 | routesTree.children.push(route);
|
268 | }
|
269 | if (node.children[i].children) {
|
270 | loopModulesParser(node.children[i]);
|
271 | }
|
272 | }
|
273 | } else {
|
274 |
|
275 | let rawRoutes = this.foundRouteWithModuleName(node.name);
|
276 |
|
277 | if (rawRoutes) {
|
278 | let routes = JSON5.parse(rawRoutes.data);
|
279 | if (routes) {
|
280 | let i = 0;
|
281 | let len = routes.length;
|
282 | let routeAddedOnce = false;
|
283 | for (i; i < len; i++) {
|
284 | let route = routes[i];
|
285 | if (routes[i].component) {
|
286 | routeAddedOnce = true;
|
287 | routesTree.children.push({
|
288 | kind: 'component',
|
289 | component: routes[i].component,
|
290 | path: routes[i].path
|
291 | });
|
292 | }
|
293 | }
|
294 | if (!routeAddedOnce) {
|
295 | routesTree.children = [...routesTree.children, ...routes];
|
296 | }
|
297 | }
|
298 | }
|
299 | }
|
300 | };
|
301 |
|
302 | let startModule = _.find(this.cleanModulesTree, { name: this.rootModule });
|
303 |
|
304 | if (startModule) {
|
305 | loopModulesParser(startModule);
|
306 |
|
307 |
|
308 | }
|
309 |
|
310 | let cleanedRoutesTree = undefined;
|
311 |
|
312 | let cleanRoutesTree = route => {
|
313 | for (let i in route.children) {
|
314 | let routes = route.children[i].routes;
|
315 | }
|
316 | return route;
|
317 | };
|
318 |
|
319 | cleanedRoutesTree = cleanRoutesTree(routesTree);
|
320 |
|
321 |
|
322 |
|
323 | let loopInsideModule = (mod, _rawModule) => {
|
324 | if (mod.children) {
|
325 | for (let z in mod.children) {
|
326 | let route = this.foundRouteWithModuleName(mod.children[z].name);
|
327 | if (typeof route !== 'undefined') {
|
328 | if (route.data) {
|
329 | route.children = JSON5.parse(route.data);
|
330 | delete route.data;
|
331 | route.kind = 'module';
|
332 | _rawModule.children.push(route);
|
333 | }
|
334 | }
|
335 | }
|
336 | } else {
|
337 | let route = this.foundRouteWithModuleName(mod.name);
|
338 | if (typeof route !== 'undefined') {
|
339 | if (route.data) {
|
340 | route.children = JSON5.parse(route.data);
|
341 | delete route.data;
|
342 | route.kind = 'module';
|
343 | _rawModule.children.push(route);
|
344 | }
|
345 | }
|
346 | }
|
347 | };
|
348 |
|
349 | let loopRoutesParser = route => {
|
350 | if (route.children) {
|
351 | for (let i in route.children) {
|
352 | if (route.children[i].loadChildren) {
|
353 | let child = this.foundLazyModuleWithPath(route.children[i].loadChildren);
|
354 | let module: RoutingGraphNode = _.find(this.cleanModulesTree, {
|
355 | name: child
|
356 | });
|
357 | if (module) {
|
358 | let _rawModule: RoutingGraphNode = {};
|
359 | _rawModule.kind = 'module';
|
360 | _rawModule.children = [];
|
361 | _rawModule.module = module.name;
|
362 | loopInsideModule(module, _rawModule);
|
363 |
|
364 | route.children[i].children = [];
|
365 | route.children[i].children.push(_rawModule);
|
366 | }
|
367 | }
|
368 | loopRoutesParser(route.children[i]);
|
369 | }
|
370 | }
|
371 | };
|
372 | loopRoutesParser(cleanedRoutesTree);
|
373 |
|
374 | return cleanedRoutesTree;
|
375 | }
|
376 |
|
377 | public constructModulesTree(): void {
|
378 | let getNestedChildren = (arr, parent?) => {
|
379 | let out = [];
|
380 | for (let i in arr) {
|
381 | if (arr[i].parent === parent) {
|
382 | let children = getNestedChildren(arr, arr[i].name);
|
383 | if (children.length) {
|
384 | arr[i].children = children;
|
385 | }
|
386 | out.push(arr[i]);
|
387 | }
|
388 | }
|
389 | return out;
|
390 | };
|
391 |
|
392 |
|
393 | _.forEach(this.modules, firstLoopModule => {
|
394 | _.forEach(firstLoopModule.importsNode, importNode => {
|
395 | _.forEach(this.modules, module => {
|
396 | if (module.name === importNode.name) {
|
397 | module.parent = firstLoopModule.name;
|
398 | }
|
399 | });
|
400 | });
|
401 | });
|
402 | this.modulesTree = getNestedChildren(this.modules);
|
403 | }
|
404 |
|
405 | public generateRoutesIndex(outputFolder: string, routes: Array<any>): Promise<void> {
|
406 | return FileEngine.get(__dirname + '/../src/templates/partials/routes-index.hbs').then(
|
407 | data => {
|
408 | let template: any = Handlebars.compile(data);
|
409 | let result = template({
|
410 | routes: JSON.stringify(routes)
|
411 | });
|
412 | let testOutputDir = outputFolder.match(process.cwd());
|
413 |
|
414 | if (testOutputDir && testOutputDir.length > 0) {
|
415 | outputFolder = outputFolder.replace(process.cwd() + path.sep, '');
|
416 | }
|
417 |
|
418 | return FileEngine.write(
|
419 | outputFolder + path.sep + '/js/routes/routes_index.js',
|
420 | result
|
421 | );
|
422 | },
|
423 | err => Promise.reject('Error during routes index generation')
|
424 | );
|
425 | }
|
426 |
|
427 | public routesLength(): number {
|
428 | let _n = 0;
|
429 | let routesParser = route => {
|
430 | if (typeof route.path !== 'undefined') {
|
431 | _n += 1;
|
432 | }
|
433 | if (route.children) {
|
434 | for (let j in route.children) {
|
435 | routesParser(route.children[j]);
|
436 | }
|
437 | }
|
438 | };
|
439 |
|
440 | for (let i in this.routes) {
|
441 | routesParser(this.routes[i]);
|
442 | }
|
443 |
|
444 | return _n;
|
445 | }
|
446 |
|
447 | public printRoutes(): void {
|
448 | console.log('');
|
449 | console.log('printRoutes: ');
|
450 | console.log(this.routes);
|
451 | }
|
452 |
|
453 | public printModulesRoutes(): void {
|
454 | console.log('');
|
455 | console.log('printModulesRoutes: ');
|
456 | console.log(this.modulesWithRoutes);
|
457 | }
|
458 |
|
459 | public isVariableRoutes(node) {
|
460 | let result = false;
|
461 | if (node.declarationList && node.declarationList.declarations) {
|
462 | let i = 0;
|
463 | let len = node.declarationList.declarations.length;
|
464 | for (i; i < len; i++) {
|
465 | if (node.declarationList.declarations[i].type) {
|
466 | if (
|
467 | node.declarationList.declarations[i].type.typeName &&
|
468 | node.declarationList.declarations[i].type.typeName.text === 'Routes'
|
469 | ) {
|
470 | result = true;
|
471 | }
|
472 | }
|
473 | }
|
474 | }
|
475 | return result;
|
476 | }
|
477 |
|
478 | public cleanFileIdentifiers(sourceFile: SourceFile): SourceFile {
|
479 | let file = sourceFile;
|
480 | const identifiers = file.getDescendantsOfKind(SyntaxKind.Identifier).filter(p => {
|
481 | return (
|
482 | Node.isArrayLiteralExpression(p.getParentOrThrow()) ||
|
483 | Node.isPropertyAssignment(p.getParentOrThrow())
|
484 | );
|
485 | });
|
486 |
|
487 | let identifiersInRoutesVariableStatement = [];
|
488 |
|
489 | for (const identifier of identifiers) {
|
490 |
|
491 | let foundParentVariableStatement = false;
|
492 | let parent = identifier.getParentWhile(n => {
|
493 | if (n.getKind() === SyntaxKind.VariableStatement) {
|
494 | if (this.isVariableRoutes(n.compilerNode)) {
|
495 | foundParentVariableStatement = true;
|
496 | }
|
497 | }
|
498 | return true;
|
499 | });
|
500 | if (foundParentVariableStatement) {
|
501 | identifiersInRoutesVariableStatement.push(identifier);
|
502 | }
|
503 | }
|
504 |
|
505 |
|
506 | for (const identifier of identifiersInRoutesVariableStatement) {
|
507 | const identifierDeclaration = identifier
|
508 | .getSymbolOrThrow()
|
509 | .getValueDeclarationOrThrow();
|
510 | if (
|
511 | !Node.isPropertyAssignment(identifierDeclaration) &&
|
512 | Node.isVariableDeclaration(identifierDeclaration) &&
|
513 | Node.isPropertyAssignment(identifierDeclaration) &&
|
514 | !Node.isVariableDeclaration(identifierDeclaration)
|
515 | ) {
|
516 | throw new Error(
|
517 | `Not implemented referenced declaration kind: ${identifierDeclaration.getKindName()}`
|
518 | );
|
519 | }
|
520 | if (Node.isVariableDeclaration(identifierDeclaration)) {
|
521 | identifier.replaceWithText(identifierDeclaration.getInitializerOrThrow().getText());
|
522 | }
|
523 | }
|
524 |
|
525 | return file;
|
526 | }
|
527 |
|
528 | public cleanFileSpreads(sourceFile: SourceFile): SourceFile {
|
529 | let file = sourceFile;
|
530 | const spreadElements = file
|
531 | .getDescendantsOfKind(SyntaxKind.SpreadElement)
|
532 | .filter(p => Node.isArrayLiteralExpression(p.getParentOrThrow()));
|
533 |
|
534 | let spreadElementsInRoutesVariableStatement = [];
|
535 |
|
536 | for (const spreadElement of spreadElements) {
|
537 |
|
538 | let foundParentVariableStatement = false;
|
539 | let parent = spreadElement.getParentWhile(n => {
|
540 | if (n.getKind() === SyntaxKind.VariableStatement) {
|
541 | if (this.isVariableRoutes(n.compilerNode)) {
|
542 | foundParentVariableStatement = true;
|
543 | }
|
544 | }
|
545 | return true;
|
546 | });
|
547 | if (foundParentVariableStatement) {
|
548 | spreadElementsInRoutesVariableStatement.push(spreadElement);
|
549 | }
|
550 | }
|
551 |
|
552 |
|
553 | for (const spreadElement of spreadElementsInRoutesVariableStatement) {
|
554 | let spreadElementIdentifier = spreadElement.getExpression().getText(),
|
555 | searchedImport,
|
556 | aliasOriginalName = '',
|
557 | foundWithAliasInImports = false,
|
558 | foundWithAlias = false;
|
559 |
|
560 |
|
561 | const imports = file.getImportDeclarations();
|
562 |
|
563 | imports.forEach(i => {
|
564 | let namedImports = i.getNamedImports(),
|
565 | namedImportsLength = namedImports.length,
|
566 | j = 0;
|
567 |
|
568 | if (namedImportsLength > 0) {
|
569 | for (j; j < namedImportsLength; j++) {
|
570 | let importName = namedImports[j].getNameNode().getText() as string,
|
571 | importAlias;
|
572 |
|
573 | if (namedImports[j].getAliasNode()) {
|
574 | importAlias = namedImports[j].getAliasNode().getText();
|
575 | }
|
576 |
|
577 | if (importName === spreadElementIdentifier) {
|
578 | foundWithAliasInImports = true;
|
579 | searchedImport = i;
|
580 | break;
|
581 | }
|
582 | if (importAlias === spreadElementIdentifier) {
|
583 | foundWithAliasInImports = true;
|
584 | foundWithAlias = true;
|
585 | aliasOriginalName = importName;
|
586 | searchedImport = i;
|
587 | break;
|
588 | }
|
589 | }
|
590 | }
|
591 | });
|
592 |
|
593 | let referencedDeclaration;
|
594 |
|
595 | if (foundWithAliasInImports) {
|
596 | if (typeof searchedImport !== 'undefined') {
|
597 | const routePathIsBad = path => {
|
598 | return typeof ast.getSourceFile(path) == 'undefined';
|
599 | };
|
600 |
|
601 | const getIndicesOf = (searchStr, str, caseSensitive) => {
|
602 | var searchStrLen = searchStr.length;
|
603 | if (searchStrLen == 0) {
|
604 | return [];
|
605 | }
|
606 | var startIndex = 0,
|
607 | index,
|
608 | indices = [];
|
609 | if (!caseSensitive) {
|
610 | str = str.toLowerCase();
|
611 | searchStr = searchStr.toLowerCase();
|
612 | }
|
613 | while ((index = str.indexOf(searchStr, startIndex)) > -1) {
|
614 | indices.push(index);
|
615 | startIndex = index + searchStrLen;
|
616 | }
|
617 | return indices;
|
618 | };
|
619 |
|
620 | const dirNamePath = path.dirname(file.getFilePath());
|
621 | const searchedImportPath = searchedImport.getModuleSpecifierValue();
|
622 | const leadingFilePath = searchedImportPath.split('/').shift();
|
623 |
|
624 | let importPath = path.resolve(
|
625 | dirNamePath + '/' + searchedImport.getModuleSpecifierValue() + '.ts'
|
626 | );
|
627 |
|
628 | if (routePathIsBad(importPath)) {
|
629 | let leadingIndices = getIndicesOf(leadingFilePath, importPath, true);
|
630 | if (leadingIndices.length > 1) {
|
631 |
|
632 | let startIndex = leadingIndices[0];
|
633 | let endIndex = leadingIndices[leadingIndices.length - 1];
|
634 | importPath =
|
635 | importPath.slice(0, startIndex) + importPath.slice(endIndex);
|
636 | } else {
|
637 |
|
638 | importPath =
|
639 | path.dirname(dirNamePath) + '/' + searchedImportPath + '.ts';
|
640 | }
|
641 | }
|
642 | const sourceFileImport =
|
643 | typeof ast.getSourceFile(importPath) !== 'undefined'
|
644 | ? ast.getSourceFile(importPath)
|
645 | : ast.addSourceFileAtPath(importPath);
|
646 | if (sourceFileImport) {
|
647 | let variableName = foundWithAlias
|
648 | ? aliasOriginalName
|
649 | : spreadElementIdentifier;
|
650 | referencedDeclaration =
|
651 | sourceFileImport.getVariableDeclaration(variableName);
|
652 | }
|
653 | }
|
654 | } else {
|
655 |
|
656 | referencedDeclaration = spreadElement
|
657 | .getExpression()
|
658 | .getSymbolOrThrow()
|
659 | .getValueDeclarationOrThrow();
|
660 | }
|
661 |
|
662 | if (!Node.isVariableDeclaration(referencedDeclaration)) {
|
663 | throw new Error(
|
664 | `Not implemented referenced declaration kind: ${referencedDeclaration.getKindName()}`
|
665 | );
|
666 | }
|
667 |
|
668 | const referencedArray = referencedDeclaration.getInitializerIfKindOrThrow(
|
669 | SyntaxKind.ArrayLiteralExpression
|
670 | );
|
671 | const spreadElementArray = spreadElement.getParentIfKindOrThrow(
|
672 | SyntaxKind.ArrayLiteralExpression
|
673 | );
|
674 | const insertIndex = spreadElementArray.getElements().indexOf(spreadElement);
|
675 | spreadElementArray.removeElement(spreadElement);
|
676 | spreadElementArray.insertElements(
|
677 | insertIndex,
|
678 | referencedArray.getElements().map(e => e.getText())
|
679 | );
|
680 | }
|
681 |
|
682 | return file;
|
683 | }
|
684 |
|
685 | public cleanFileDynamics(sourceFile: SourceFile): SourceFile {
|
686 | let file = sourceFile;
|
687 | const propertyAccessExpressions = file
|
688 | .getDescendantsOfKind(SyntaxKind.PropertyAccessExpression)
|
689 | .filter(p => !Node.isPropertyAccessExpression(p.getParentOrThrow()));
|
690 |
|
691 | let propertyAccessExpressionsInRoutesVariableStatement = [];
|
692 |
|
693 | for (const propertyAccessExpression of propertyAccessExpressions) {
|
694 |
|
695 | let foundParentVariableStatement = false;
|
696 | let parent = propertyAccessExpression.getParentWhile(n => {
|
697 | if (n.getKind() === SyntaxKind.VariableStatement) {
|
698 | if (this.isVariableRoutes(n.compilerNode)) {
|
699 | foundParentVariableStatement = true;
|
700 | }
|
701 | }
|
702 | return true;
|
703 | });
|
704 | if (foundParentVariableStatement) {
|
705 | propertyAccessExpressionsInRoutesVariableStatement.push(propertyAccessExpression);
|
706 | }
|
707 | }
|
708 |
|
709 |
|
710 | for (const propertyAccessExpression of propertyAccessExpressionsInRoutesVariableStatement) {
|
711 | const propertyAccessExpressionNodeName = propertyAccessExpression.getNameNode();
|
712 | if (propertyAccessExpressionNodeName) {
|
713 | try {
|
714 | const propertyAccessExpressionNodeNameSymbol =
|
715 | propertyAccessExpressionNodeName.getSymbolOrThrow();
|
716 | if (propertyAccessExpressionNodeNameSymbol) {
|
717 | const referencedDeclaration =
|
718 | propertyAccessExpressionNodeNameSymbol.getValueDeclarationOrThrow();
|
719 | if (
|
720 | !Node.isPropertyAssignment(referencedDeclaration) &&
|
721 | Node.isEnumMember(referencedDeclaration) &&
|
722 | Node.isPropertyAssignment(referencedDeclaration) &&
|
723 | !Node.isEnumMember(referencedDeclaration)
|
724 | ) {
|
725 | throw new Error(
|
726 | `Not implemented referenced declaration kind: ${referencedDeclaration.getKindName()}`
|
727 | );
|
728 | }
|
729 | if (typeof referencedDeclaration.getInitializerOrThrow !== 'undefined') {
|
730 | propertyAccessExpression.replaceWithText(
|
731 | referencedDeclaration.getInitializerOrThrow().getText()
|
732 | );
|
733 | }
|
734 | }
|
735 | } catch (e) {}
|
736 | }
|
737 | }
|
738 |
|
739 | return file;
|
740 | }
|
741 |
|
742 | |
743 |
|
744 |
|
745 |
|
746 | public cleanCallExpressions(sourceFile: SourceFile): SourceFile {
|
747 | let file = sourceFile;
|
748 |
|
749 | const variableStatements = sourceFile.getVariableDeclaration(v => {
|
750 | let result = false;
|
751 | const type = v.compilerNode.type;
|
752 | if (typeof type !== 'undefined' && typeof type.typeName !== 'undefined') {
|
753 | result = type.typeName.text === 'Routes';
|
754 | }
|
755 | return result;
|
756 | });
|
757 |
|
758 | const initializer = variableStatements.getInitializer();
|
759 |
|
760 | for (const callExpr of initializer.getDescendantsOfKind(SyntaxKind.CallExpression)) {
|
761 | if (callExpr.wasForgotten()) {
|
762 | continue;
|
763 | }
|
764 | callExpr.replaceWithText(writer => writer.quote(callExpr.getText()));
|
765 | }
|
766 |
|
767 | return file;
|
768 | }
|
769 |
|
770 | |
771 |
|
772 |
|
773 |
|
774 |
|
775 |
|
776 |
|
777 |
|
778 |
|
779 |
|
780 |
|
781 |
|
782 |
|
783 |
|
784 |
|
785 |
|
786 |
|
787 |
|
788 |
|
789 |
|
790 |
|
791 |
|
792 |
|
793 |
|
794 |
|
795 |
|
796 |
|
797 | public cleanRoutesDefinitionWithImport(
|
798 | initializer: ts.ArrayLiteralExpression,
|
799 | node: ts.Node,
|
800 | sourceFile: ts.SourceFile
|
801 | ): ts.Node {
|
802 | initializer.elements.forEach((element: ts.ObjectLiteralExpression) => {
|
803 | element.properties.forEach((property: ts.PropertyAssignment) => {
|
804 | let propertyName = property.name.getText(),
|
805 | propertyInitializer = property.initializer;
|
806 | switch (propertyName) {
|
807 | case 'path':
|
808 | case 'redirectTo':
|
809 | case 'outlet':
|
810 | case 'pathMatch':
|
811 | if (propertyInitializer) {
|
812 | if (propertyInitializer.kind !== SyntaxKind.StringLiteral) {
|
813 |
|
814 |
|
815 | if (
|
816 | propertyInitializer.kind === SyntaxKind.PropertyAccessExpression
|
817 | ) {
|
818 | let lastObjectLiteralAttributeName =
|
819 | propertyInitializer.name.getText(),
|
820 | firstObjectLiteralAttributeName;
|
821 | if (propertyInitializer.expression) {
|
822 | firstObjectLiteralAttributeName =
|
823 | propertyInitializer.expression.getText();
|
824 | let result =
|
825 | ImportsUtil.findPropertyValueInImportOrLocalVariables(
|
826 | firstObjectLiteralAttributeName +
|
827 | '.' +
|
828 | lastObjectLiteralAttributeName,
|
829 | sourceFile
|
830 | );
|
831 | if (result !== '') {
|
832 | propertyInitializer.kind = 9;
|
833 | propertyInitializer.text = result;
|
834 | }
|
835 | }
|
836 | }
|
837 | }
|
838 | }
|
839 | break;
|
840 | }
|
841 | });
|
842 | });
|
843 | return initializer;
|
844 | }
|
845 | }
|
846 |
|
847 | export default RouterParserUtil.getInstance();
|