Skip to content

Commit 37c4340

Browse files
committed
ShowComponent hierarchy is now rendering routing relations (for now only where the routes definition specifies a component as outlet)
1 parent c8e5025 commit 37c4340

File tree

3 files changed

+98
-13
lines changed

3 files changed

+98
-13
lines changed

src/commands/showComponentHierarchy.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ export class ShowComponentHierarchy extends ShowHierarchyBase {
9393
const component = componentDict[selector];
9494
if (component.isRoot) {
9595
this.generateDirectedGraphNodes(component.subComponents, component, true, '', appendNodes);
96-
this.generateDirectedGraphEdges(component.subComponents, selector, "", appendEdges);
96+
this.generateDirectedGraphEdges(componentDict, component.subComponents, component, "", appendEdges);
9797
}
9898
}
9999
}
@@ -112,14 +112,20 @@ export class ShowComponentHierarchy extends ShowHierarchyBase {
112112
}
113113
}
114114

115-
private generateDirectedGraphEdges(subComponents: Component[], selector: string, parentSelector: string, appendEdges: (edgeList: Edge[]) => void) {
115+
private generateDirectedGraphEdges(componentDict: { [selector: string]: Component; }, subComponents: Component[], currentComponent: Component, parentSelector: string, appendEdges: (edgeList: Edge[]) => void) {
116116
if (parentSelector.length > 0) {
117117
const id = this.edges.length;
118-
appendEdges([new Edge(id.toString(), parentSelector, selector, ArrowType.uses)]);
118+
appendEdges([new Edge(id.toString(), parentSelector, currentComponent.selector, ArrowType.uses)]);
119119
}
120-
if (subComponents.length > 0 && selector !== parentSelector) {
120+
if (currentComponent.componentsRoutingToThis !== undefined && currentComponent.componentsRoutingToThis.length > 0) {
121+
currentComponent.componentsRoutingToThis.forEach(componentRoutingToThis => {
122+
const id = this.edges.length;
123+
appendEdges([new Edge(id.toString(), componentRoutingToThis.selector, currentComponent.selector, ArrowType.route)]);
124+
});
125+
}
126+
if (subComponents.length > 0 && currentComponent.selector !== parentSelector) {
121127
subComponents.forEach((subComponent) => {
122-
this.generateDirectedGraphEdges(subComponent.subComponents, subComponent.selector, selector, appendEdges);
128+
this.generateDirectedGraphEdges(componentDict, subComponent.subComponents, subComponent, currentComponent.selector, appendEdges);
123129
});
124130
}
125131
}

src/componentManager.ts

Lines changed: 73 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,37 @@
11
import * as fs from 'fs';
22
import path = require('path');
33

4-
import { Config, FileSystemUtils } from '@src';
4+
import { Config, FileSystemUtils, StringUtils } from '@src';
55
import { Component } from '@model';
66

77
export class ComponentManager {
88
private static componentRegex = /@Component\({/ig;
9+
private static componentClassNameRegex = /export\s+class\s+(.*?)\s+/ims;
910
private static templateUrlRegex = /.*templateUrl:.+\/(.+)'/i;
1011
private static selectorRegex = /.*selector:.+'(.+)'/i;
1112
private static endBracketRegex = /}\)/i;
1213
private static routerOutletRegex = /<router-outlet.*?>.*?<\/router-outlet>/ims;
1314

15+
private static routesRegex = /:\s*?Routes\s*?=\s*?\[(.*?)\]/ims;
16+
private static loadChildrenRegex = /loadChildren: .*?then\s*\(.+?=>.+?\.(.+?)\)/i;
17+
private static routeComponentRegex = /component:\s*?(\w+?)\b/ig;
18+
private static childrenRegex = /children\s*?:\s*?\[(.*?)\]/ims;
19+
20+
1421
public static scanWorkspaceForComponents(directoryPath: string): { [selector: string]: Component; } {
15-
const fsUtils = new FileSystemUtils();
1622
const config = new Config();
17-
const componentFilenames = fsUtils.listFiles(directoryPath, config.excludeDirectories, ComponentManager.isComponentFile);
23+
const fsUtils = new FileSystemUtils();
24+
const componentOrModuleFilenames = fsUtils.listFiles(directoryPath, config.excludeDirectories, this.isComponentOrModuleFile);
25+
const componentFilenames = componentOrModuleFilenames.filter(FileSystemUtils.isComponentFile);
1826
const components = ComponentManager.scanComponents(componentFilenames);
1927
ComponentManager.enrichComponentsFromComponentTemplates(components);
28+
const moduleFilenames = componentOrModuleFilenames.filter(FileSystemUtils.isModuleFile);
29+
ComponentManager.enrichComponentsFromModules(moduleFilenames, components);
2030
return components;
2131
}
2232

23-
private static isComponentFile(filename: string): boolean {
24-
return filename.endsWith('.component.ts');
33+
private static isComponentOrModuleFile(filename: string): boolean {
34+
return FileSystemUtils.isComponentFile(filename) || FileSystemUtils.isModuleFile(filename);
2535
}
2636

2737
private static scanComponents(componentFilenames: string[]): { [selector: string]: Component; } {
@@ -55,6 +65,10 @@ export class ComponentManager {
5565
}
5666
}
5767
}
68+
const componentClassNameMatch = this.componentClassNameRegex.exec(content);
69+
if (componentClassNameMatch !== null) {
70+
currentComponent.name = componentClassNameMatch[1];
71+
}
5872
compHash[currentComponent.selector] = currentComponent;
5973
});
6074
return compHash;
@@ -91,4 +105,58 @@ export class ComponentManager {
91105
const match = this.routerOutletRegex.exec(template.toString());
92106
return match !== null;
93107
}
108+
109+
private static enrichComponentsFromModules(moduleFilenames: string[], componentHash: { [selector: string]: Component; }) {
110+
moduleFilenames.forEach((moduleFilename) => {
111+
const moduleContent = fs.readFileSync(moduleFilename);
112+
const match = this.routesRegex.exec(moduleContent.toString());
113+
if (match !== null) {
114+
let routesBody = match[1];
115+
this.parseRoutesBody(routesBody, moduleFilename, componentHash);
116+
}
117+
});
118+
}
119+
120+
private static parseRoutesBody(routesBody: string, moduleFilename: string, componentDict: { [selector: string]: Component; }) {
121+
routesBody = StringUtils.removeComments(routesBody);
122+
const routesBodyParts = routesBody.split(",");
123+
// We assume that the routing module has a corresponding module with the same name
124+
// This only works if the routing module is named like 'moduleComponentName'-routing.module.ts
125+
if (!FileSystemUtils.isRoutingModuleFile(moduleFilename)) {
126+
return;
127+
}
128+
const moduleComponentFilename = moduleFilename.replace(FileSystemUtils.routingModuleFileExtension, FileSystemUtils.componentFileExtension);
129+
const componentDictKey = Object.keys(componentDict).find(key => componentDict[key].filename.endsWith(moduleComponentFilename));
130+
// if we didn't find the corresponding component we stop because we would not be able to link the components found in the routes to the current module component
131+
if (componentDictKey === undefined) {
132+
return;
133+
}
134+
const moduleComponent = componentDict[componentDictKey];
135+
routesBodyParts.forEach((routesBodyPart) => {
136+
const componentMatch = this.routeComponentRegex.exec(routesBodyPart);
137+
if (componentMatch !== null) {
138+
const componentName = componentMatch[1];
139+
const componentSelector = Object.keys(componentDict).find(key => componentDict[key].name === componentName);
140+
if (componentSelector !== undefined) {
141+
const component = componentDict[componentSelector];
142+
if (component !== undefined && componentSelector !== moduleComponent.selector) {
143+
component.componentsRoutingToThis.push(moduleComponent);
144+
}
145+
}
146+
}
147+
else {
148+
const loadChildrenMatch = this.loadChildrenRegex.exec(routesBodyPart);
149+
if (loadChildrenMatch !== null) {
150+
const moduleName = loadChildrenMatch[1];
151+
console.log(`${moduleComponent.name} routing to module ${moduleName}`);
152+
}
153+
}
154+
const childrenMatch = this.childrenRegex.exec(routesBodyPart);
155+
if (childrenMatch !== null) {
156+
const childrenRoutesBody = childrenMatch[1];
157+
this.parseRoutesBody(childrenRoutesBody, moduleFilename, componentDict);
158+
}
159+
});
160+
161+
}
94162
}

src/filesystemUtils.ts

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,17 +26,28 @@ export class FileSystemUtils {
2626
}
2727

2828
public fileExists(filename: string): boolean {
29-
try{
29+
try {
3030
return fs.lstatSync(filename).isFile();
3131
} catch {
3232
return false;
3333
}
3434
}
35-
35+
36+
public static componentFileExtension = '.component.ts';
37+
public static isComponentFile(filename: string): boolean {
38+
return filename.toLowerCase().endsWith(FileSystemUtils.componentFileExtension);
39+
}
40+
public static isModuleFile(filename: string): boolean {
41+
return filename.toLowerCase().endsWith('.module.ts');
42+
}
43+
public static routingModuleFileExtension = '-routing.module.ts';
44+
public static isRoutingModuleFile(filename: string): boolean {
45+
return filename.toLowerCase().endsWith(FileSystemUtils.routingModuleFileExtension);
46+
}
47+
3648
private isDirectory(directoryName: any): boolean {
3749
return fs.lstatSync(directoryName).isDirectory();
3850
}
39-
4051
public listDirectories(
4152
dir: string,
4253
excludeDirectories: string[]

0 commit comments

Comments
 (0)