Skip to content

Commit c743c74

Browse files
perf(core): use topology tree for calculating distance
1 parent bf7768b commit c743c74

File tree

3 files changed

+92
-26
lines changed

3 files changed

+92
-26
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import { Module } from '../module';
2+
import { TreeNode } from './tree-node';
3+
4+
export class TopologyTree {
5+
private root: TreeNode<Module>;
6+
private links: Map<
7+
Module,
8+
{
9+
node: TreeNode<Module>;
10+
depth: number;
11+
}
12+
> = new Map();
13+
14+
static from(root: Module) {
15+
const tree = new TopologyTree();
16+
tree.root = new TreeNode<Module>({
17+
value: root,
18+
parent: null,
19+
});
20+
21+
tree.traverseAndCloneTree(tree.root);
22+
return tree;
23+
}
24+
25+
public walk(callback: (value: Module, depth: number) => void) {
26+
function walkNode(node: TreeNode<Module>, depth = 1) {
27+
callback(node.value, depth);
28+
node.children.forEach(child => walkNode(child, depth + 1));
29+
}
30+
walkNode(this.root);
31+
}
32+
33+
private traverseAndCloneTree(node: TreeNode<Module>, depth = 1) {
34+
node.value.imports.forEach(child => {
35+
if (!child) {
36+
return;
37+
}
38+
if (this.links.has(child)) {
39+
const existingSubtree = this.links.get(child)!;
40+
if (existingSubtree.depth < depth) {
41+
existingSubtree.node.relink(node);
42+
existingSubtree.depth = depth;
43+
}
44+
return;
45+
}
46+
47+
const childNode = new TreeNode<Module>({
48+
value: child,
49+
parent: node,
50+
});
51+
node.addChild(childNode);
52+
this.links.set(child, {
53+
node: childNode,
54+
depth,
55+
});
56+
57+
this.traverseAndCloneTree(childNode, depth + 1);
58+
});
59+
}
60+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
export class TreeNode<T> {
2+
public readonly value: T;
3+
public readonly children = new Set<TreeNode<T>>();
4+
private parent: TreeNode<T> | null;
5+
6+
constructor({ value, parent }: { value: T; parent: TreeNode<T> | null }) {
7+
this.value = value;
8+
this.parent = parent;
9+
}
10+
11+
addChild(child: TreeNode<T>) {
12+
this.children.add(child);
13+
}
14+
15+
removeChild(child: TreeNode<T>) {
16+
this.children.delete(child);
17+
}
18+
19+
relink(parent: TreeNode<T>) {
20+
this.parent = parent;
21+
this.parent.addChild(this);
22+
}
23+
}

packages/core/scanner.ts

+9-26
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ import { NestContainer } from './injector/container';
5050
import { InstanceWrapper } from './injector/instance-wrapper';
5151
import { InternalCoreModuleFactory } from './injector/internal-core-module/internal-core-module-factory';
5252
import { Module } from './injector/module';
53+
import { TopologyTree } from './injector/topology-tree/topology-tree';
5354
import { GraphInspector } from './inspector/graph-inspector';
5455
import { UuidFactory } from './inspector/uuid-factory';
5556
import { ModuleDefinition } from './interfaces/module-definition.interface';
@@ -395,37 +396,19 @@ export class DependenciesScanner {
395396

396397
public calculateModulesDistance() {
397398
const modulesGenerator = this.container.getModules().values();
398-
399399
// Skip "InternalCoreModule" from calculating distance
400400
modulesGenerator.next();
401401

402-
const calculateDistance = (
403-
moduleRef: Module,
404-
distance = 1,
405-
modulesStack: Module[] = [],
406-
) => {
407-
const localModulesStack = [...modulesStack];
408-
if (!moduleRef || localModulesStack.includes(moduleRef)) {
402+
const rootModule = modulesGenerator.next().value!;
403+
404+
// Convert modules to an acyclic connected graph
405+
const tree = TopologyTree.from(rootModule);
406+
tree.walk((moduleRef, depth) => {
407+
if (moduleRef.isGlobal) {
409408
return;
410409
}
411-
localModulesStack.push(moduleRef);
412-
413-
const moduleImports = moduleRef.imports;
414-
moduleImports.forEach(importedModuleRef => {
415-
if (importedModuleRef) {
416-
if (
417-
distance > importedModuleRef.distance &&
418-
!importedModuleRef.isGlobal
419-
) {
420-
importedModuleRef.distance = distance;
421-
}
422-
calculateDistance(importedModuleRef, distance + 1, localModulesStack);
423-
}
424-
});
425-
};
426-
427-
const rootModule = modulesGenerator.next().value;
428-
calculateDistance(rootModule!);
410+
moduleRef.distance = depth;
411+
});
429412
}
430413

431414
public async insertImport(related: any, token: string, context: string) {

0 commit comments

Comments
 (0)