@nexus-ioc/graph-analyzer 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/README.md +63 -26
  3. package/dist/analyzer/circular-dependency-detector.d.ts +29 -9
  4. package/dist/analyzer/circular-dependency-detector.js +106 -63
  5. package/dist/analyzer/circular-dependency-detector.js.map +1 -1
  6. package/dist/analyzer/module-depth-analyzer.d.ts +10 -13
  7. package/dist/analyzer/module-depth-analyzer.js +40 -52
  8. package/dist/analyzer/module-depth-analyzer.js.map +1 -1
  9. package/dist/analyzer/provider-scope-analyzer.d.ts +7 -44
  10. package/dist/analyzer/provider-scope-analyzer.js +13 -84
  11. package/dist/analyzer/provider-scope-analyzer.js.map +1 -1
  12. package/dist/analyzer/unused-provider-detector.d.ts +11 -9
  13. package/dist/analyzer/unused-provider-detector.js +35 -55
  14. package/dist/analyzer/unused-provider-detector.js.map +1 -1
  15. package/dist/cli.js +34 -129
  16. package/dist/cli.js.map +1 -1
  17. package/dist/config/config-loader.js +3 -27
  18. package/dist/config/config-loader.js.map +1 -1
  19. package/dist/config/config-schema.d.ts +2 -16
  20. package/dist/config/config-schema.js +0 -18
  21. package/dist/config/config-schema.js.map +1 -1
  22. package/dist/graph/build-nexus-graph-model.d.ts +13 -0
  23. package/dist/graph/build-nexus-graph-model.js +202 -0
  24. package/dist/graph/build-nexus-graph-model.js.map +1 -0
  25. package/dist/graph/nexus-graph-model.d.ts +112 -0
  26. package/dist/graph/nexus-graph-model.js +10 -0
  27. package/dist/graph/nexus-graph-model.js.map +1 -0
  28. package/dist/graph/scope-resolution.d.ts +16 -0
  29. package/dist/graph/scope-resolution.js +46 -0
  30. package/dist/graph/scope-resolution.js.map +1 -0
  31. package/dist/index.d.ts +27 -29
  32. package/dist/index.js +34 -34
  33. package/dist/index.js.map +1 -1
  34. package/dist/interfaces/graph-output.interface.d.ts +49 -14
  35. package/dist/utils/error-formatter.js +1 -6
  36. package/dist/utils/error-formatter.js.map +1 -1
  37. package/dist/utils/validator.d.ts +0 -5
  38. package/dist/utils/validator.js +0 -60
  39. package/dist/utils/validator.js.map +1 -1
  40. package/dist/visualize/json-formatter.d.ts +5 -6
  41. package/dist/visualize/json-formatter.js +49 -47
  42. package/dist/visualize/json-formatter.js.map +1 -1
  43. package/package.json +5 -4
  44. package/dist/interfaces/providers.interface.d.ts +0 -10
  45. package/dist/interfaces/providers.interface.js +0 -3
  46. package/dist/interfaces/providers.interface.js.map +0 -1
  47. package/dist/parser/dependency-extractor.d.ts +0 -88
  48. package/dist/parser/dependency-extractor.js +0 -334
  49. package/dist/parser/dependency-extractor.js.map +0 -1
  50. package/dist/parser/parse-entry-file.d.ts +0 -15
  51. package/dist/parser/parse-entry-file.js +0 -46
  52. package/dist/parser/parse-entry-file.js.map +0 -1
  53. package/dist/parser/parse-imports.d.ts +0 -15
  54. package/dist/parser/parse-imports.js +0 -65
  55. package/dist/parser/parse-imports.js.map +0 -1
  56. package/dist/parser/parse-ns-module.d.ts +0 -39
  57. package/dist/parser/parse-ns-module.js +0 -122
  58. package/dist/parser/parse-ns-module.js.map +0 -1
  59. package/dist/parser/parse-provider.d.ts +0 -2
  60. package/dist/parser/parse-provider.js +0 -7
  61. package/dist/parser/parse-provider.js.map +0 -1
  62. package/dist/parser/parse-ts-config.d.ts +0 -10
  63. package/dist/parser/parse-ts-config.js +0 -40
  64. package/dist/parser/parse-ts-config.js.map +0 -1
  65. package/dist/parser/parser-rec.d.ts +0 -1
  66. package/dist/parser/parser-rec.js +0 -52
  67. package/dist/parser/parser-rec.js.map +0 -1
  68. package/dist/parser/parser.d.ts +0 -1
  69. package/dist/parser/parser.js +0 -61
  70. package/dist/parser/parser.js.map +0 -1
  71. package/dist/parser/provider/class-parser.d.ts +0 -20
  72. package/dist/parser/provider/class-parser.js +0 -137
  73. package/dist/parser/provider/class-parser.js.map +0 -1
  74. package/dist/parser/provider/providers-parser.d.ts +0 -9
  75. package/dist/parser/provider/providers-parser.js +0 -32
  76. package/dist/parser/provider/providers-parser.js.map +0 -1
  77. package/dist/parser/provider/use-class-parser.d.ts +0 -25
  78. package/dist/parser/provider/use-class-parser.js +0 -161
  79. package/dist/parser/provider/use-class-parser.js.map +0 -1
  80. package/dist/parser/provider/use-factory-parser.d.ts +0 -15
  81. package/dist/parser/provider/use-factory-parser.js +0 -53
  82. package/dist/parser/provider/use-factory-parser.js.map +0 -1
  83. package/dist/parser/provider/use-value-parser.d.ts +0 -13
  84. package/dist/parser/provider/use-value-parser.js +0 -41
  85. package/dist/parser/provider/use-value-parser.js.map +0 -1
  86. package/dist/parser/rules.d.ts +0 -10
  87. package/dist/parser/rules.js +0 -37
  88. package/dist/parser/rules.js.map +0 -1
  89. package/dist/visualize/generator.d.ts +0 -26
  90. package/dist/visualize/generator.js +0 -75
  91. package/dist/visualize/generator.js.map +0 -1
  92. package/dist/visualize/graph-analyzer.d.ts +0 -126
  93. package/dist/visualize/graph-analyzer.js +0 -199
  94. package/dist/visualize/graph-analyzer.js.map +0 -1
  95. package/dist/visualize/html-generator.d.ts +0 -71
  96. package/dist/visualize/html-generator.js +0 -1312
  97. package/dist/visualize/html-generator.js.map +0 -1
package/CHANGELOG.md CHANGED
@@ -5,6 +5,16 @@ All notable changes to the @nexus-ioc/graph-analyzer package will be documented
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## 0.2.0 (2026-10-12)
9
+
10
+ ### BREAKING CHANGES
11
+
12
+ - `scopeAnalysis` JSON shape: Transient providers were previously mislabelled `"Singleton"` in `providerScopes` and are now labelled `"Transient"`. `scopedProviders` is renamed to `transientProviders`. `scopeMismatches` and the `ScopeMismatch` export are removed, because `Scope.Scoped` no longer exists in `@nexus-ioc/core`.
13
+
14
+ ### Added
15
+
16
+ - Child scopes: the graph follows `@Module({ children })`. JSON modules gain `scope` (the module that roots the child scope) and `children`, and a provider dependency gets `crossScope: true` when a child-scope provider gets it from an ancestor scope (approximate).
17
+
8
18
  ## [0.1.0] - 2025-01-18
9
19
 
10
20
  **First public release of @nexus-ioc/graph-analyzer!**
package/README.md CHANGED
@@ -8,7 +8,7 @@
8
8
  [![Tests](https://img.shields.io/badge/tests-443%20passing-brightgreen.svg)](./src)
9
9
  [![Node.js](https://img.shields.io/badge/node-%3E%3D14.0.0-brightgreen.svg)](https://nodejs.org/)
10
10
 
11
- Analyze and visualize dependency injection graphs in [Nexus IoC](https://github.com/Isqanderm/ioc) applications. Extract complete metadata about modules, providers, and dependencies **without running your application**. Detect circular dependencies, unused providers, scope mismatches, and visualize your architecture with interactive HTML graphs.
11
+ Analyze and visualize dependency injection graphs in [Nexus IoC](https://github.com/Isqanderm/ioc) applications. Extract complete metadata about modules, providers, and dependencies **without running your application**. Detect circular dependencies, unused providers, provider scopes, and visualize your architecture with interactive HTML graphs.
12
12
 
13
13
  ## ✨ Features
14
14
 
@@ -24,7 +24,7 @@ Analyze and visualize dependency injection graphs in [Nexus IoC](https://github.
24
24
  - 🔄 **Circular Dependency Detection** - Detect circular imports and provider dependencies
25
25
  - 🗑️ **Unused Provider Detection** - Find providers that are registered but never injected
26
26
  - 📊 **Module Depth Analysis** - Analyze module hierarchy depth and complexity metrics
27
- - 🎯 **Provider Scope Analysis** - Detect scope mismatches (Singleton depending on Request-scoped)
27
+ - 🎯 **Provider Scope Analysis** - Report each provider's scope (Singleton or Transient)
28
28
  - ⚠️ **Missing Decorator Detection** - Identify dependencies without explicit `@Inject` decorators
29
29
 
30
30
  ### Visualization & Output
@@ -141,7 +141,7 @@ graph-analyzer [options] <entry-file>
141
141
  | `--check-circular` | Detect circular dependencies in modules and providers | `false` |
142
142
  | `--check-unused` | Detect providers that are registered but never injected | `false` |
143
143
  | `--check-depth` | Analyze module hierarchy depth and complexity | `false` |
144
- | `--check-scope` | Detect provider scope mismatches | `false` |
144
+ | `--check-scope` | Report provider scopes (Singleton or Transient) | `false` |
145
145
  | `--check-ci` | Enable all checks (circular, unused, depth, scope) | `false` |
146
146
  | `--deep-module-threshold <n>` | Threshold for identifying deep modules | `5` |
147
147
 
@@ -177,7 +177,7 @@ npx graph-analyzer --check-unused -f html src/main.ts
177
177
  # Analyze module depth and complexity
178
178
  npx graph-analyzer --check-depth --deep-module-threshold 3 src/main.ts
179
179
 
180
- # Detect scope mismatches (Singleton depending on Request-scoped)
180
+ # Report provider scopes (Singleton or Transient)
181
181
  npx graph-analyzer --check-scope src/main.ts
182
182
 
183
183
  # HTML with dark theme and WebStorm links
@@ -302,41 +302,77 @@ npx graph-analyzer --check-depth --deep-module-threshold 5 src/main.ts
302
302
 
303
303
  ### Provider Scope Analysis
304
304
 
305
- Detects scope mismatches where Singleton providers depend on Request-scoped providers.
305
+ Reports the scope of every provider. `Scope.Scoped` was removed from `@nexus-ioc/core`, so only `Singleton` and `Transient` exist and there are no scope mismatches to detect.
306
306
 
307
307
  ```bash
308
308
  npx graph-analyzer --check-scope src/main.ts
309
309
  ```
310
310
 
311
- **Detects:**
312
- - Singleton providers depending on Request-scoped providers (memory leak risk)
313
- - Scope inconsistencies that can cause unexpected behavior
314
- - Provides suggestions for fixing scope issues
315
-
316
311
  **Example Output:**
317
312
  ```json
318
313
  {
319
314
  "analysis": {
320
315
  "scopeAnalysis": {
321
- "scopeMismatches": [
322
- {
323
- "provider": "UserService",
324
- "providerScope": "Singleton",
325
- "dependency": "RequestContext",
326
- "dependencyScope": "Request",
327
- "severity": "error",
328
- "message": "Singleton provider 'UserService' depends on Request-scoped provider 'RequestContext'",
329
- "suggestions": [
330
- "Change 'UserService' to Request scope",
331
- "Change 'RequestContext' to Singleton scope if it doesn't need request-specific state"
332
- ]
333
- }
316
+ "totalProviders": 2,
317
+ "singletonProviders": 1,
318
+ "transientProviders": 1,
319
+ "providerScopes": [
320
+ { "token": "UserService", "module": "AppModule", "scope": "Singleton", "type": "Class", "dependencies": ["RequestContext"] },
321
+ { "token": "RequestContext", "module": "AppModule", "scope": "Transient", "type": "Class", "dependencies": [] }
334
322
  ]
335
323
  }
336
324
  }
337
325
  }
338
326
  ```
339
327
 
328
+ **Migration from earlier output:**
329
+ - Transient providers were previously labelled `"Singleton"` in `providerScopes`; they are now labelled `"Transient"`.
330
+ - `scopedProviders` is renamed to `transientProviders`.
331
+ - `scopeMismatches` and the `ScopeMismatch` export are removed.
332
+
333
+ ### Child Scopes
334
+
335
+ Modules listed in `@Module({ children })` (direct class references and the `lazy(() => import("...").then((m) => m.X))` shape) are not imports: each one opens a child container scope that is created at runtime with `createChild()`. The analyzer follows them, resolves each scope's dependencies with the declaring module's visibility as the fallback, and reports the scopes in the output. Applications without `children` are unchanged.
336
+
337
+ **Example Output:**
338
+ ```json
339
+ {
340
+ "modules": [
341
+ {
342
+ "name": "AppModule",
343
+ "path": "/path/to/app.module.ts",
344
+ "imports": [],
345
+ "exports": [],
346
+ "providers": ["EventBus"],
347
+ "isGlobal": false,
348
+ "scope": "AppModule",
349
+ "children": [{ "name": "ProfileModule", "path": "/path/to/profile.module.ts" }]
350
+ },
351
+ {
352
+ "name": "ProfileModule",
353
+ "path": "/path/to/profile.module.ts",
354
+ "imports": [],
355
+ "exports": [],
356
+ "providers": ["ProfileStore"],
357
+ "isGlobal": false,
358
+ "scope": "ProfileModule"
359
+ }
360
+ ],
361
+ "providers": [
362
+ {
363
+ "token": "ProfileStore",
364
+ "type": "Class",
365
+ "module": { "name": "ProfileModule", "path": "/path/to/profile.module.ts" },
366
+ "dependencies": [{ "token": "EventBus", "optional": false, "crossScope": true }]
367
+ }
368
+ ]
369
+ }
370
+ ```
371
+
372
+ - `scope` on a module is the name of the module that roots its scope (a module present in several scopes is tagged with the first one). `children` lists the modules it declares as children.
373
+ - `crossScope: true` on a dependency means a provider in a child scope gets that token from an ancestor scope. It is approximate: it only checks that some module of an ancestor scope registers the token, not the imports/exports visibility rules. `@nexus-ioc/type-checker` reports the visibility-correct answer as `fromParentScope`.
374
+ - Circular dependency detection is scope-aware: a child-scope provider that shadows a parent token (the parent's `A -> B` plus a child's `B -> A`) is not reported as a cycle, while a real cycle inside a child scope still is.
375
+
340
376
  ### Missing Decorator Detection
341
377
 
342
378
  Identifies dependencies that lack explicit `@Inject` decorators.
@@ -403,7 +439,7 @@ The JSON output provides a structured representation of your dependency graph wi
403
439
  "circularDependencies": [],
404
440
  "unusedProviders": [],
405
441
  "depthAnalysis": { "maxDepth": 3, "averageDepth": 2.1 },
406
- "scopeAnalysis": { "scopeMismatches": [] }
442
+ "scopeAnalysis": { "totalProviders": 0, "singletonProviders": 0, "transientProviders": 0, "providerScopes": [] }
407
443
  },
408
444
  "metadata": {
409
445
  "entryPoint": "/path/to/main.ts",
@@ -493,7 +529,7 @@ class UserService {
493
529
 
494
530
  ✅ **Different Provider Types**
495
531
  ```typescript
496
- @NsModule({
532
+ @Module({
497
533
  providers: [
498
534
  UserService, // Class
499
535
  { provide: 'CONFIG', useValue: config }, // UseValue
@@ -505,11 +541,12 @@ class UserService {
505
541
 
506
542
  ### Token Types
507
543
 
508
- The analyzer detects three types of injection tokens:
544
+ The analyzer detects four types of injection tokens:
509
545
 
510
546
  - **Class tokens**: `@Inject(UserService)`
511
547
  - **String tokens**: `@Inject('CONFIG')`
512
548
  - **Symbol tokens**: `@Inject(Symbol.for('key'))`
549
+ - **InjectionToken constants**: `@Inject(CONFIG)` where `const CONFIG = new InjectionToken<Config>('CONFIG')`. The constant is one token across re-exports, and graph labels show the const name.
513
550
 
514
551
  ## 🏗️ Architecture
515
552
 
@@ -1,5 +1,4 @@
1
- import type { ParseEntryFile } from "../parser/parse-entry-file";
2
- import type { ParseNsModule } from "../parser/parse-ns-module";
1
+ import type { NexusGraphModel } from "../graph/nexus-graph-model";
3
2
  /**
4
3
  * Represents a circular dependency cycle
5
4
  */
@@ -30,11 +29,14 @@ export interface CircularDependencyAnalysis {
30
29
  * Detects circular dependencies in module imports and provider dependencies
31
30
  *
32
31
  * Uses depth-first search (DFS) to detect cycles in the dependency graph.
33
- * Can detect both module-level circular imports and provider-level circular dependencies.
32
+ * Can detect both module-level circular imports and provider-level circular
33
+ * dependencies — the latter covering both constructor-injected dependencies
34
+ * and `useFactory` `inject` tokens, since `NexusGraphModel` merges both into
35
+ * a single provider dependency list.
34
36
  *
35
37
  * @example
36
38
  * ```typescript
37
- * const detector = new CircularDependencyDetector(modulesGraph);
39
+ * const detector = new CircularDependencyDetector(graphModel);
38
40
  * const analysis = detector.analyze();
39
41
  *
40
42
  * if (analysis.hasCircularDependencies) {
@@ -46,13 +48,15 @@ export interface CircularDependencyAnalysis {
46
48
  * ```
47
49
  */
48
50
  export declare class CircularDependencyDetector {
49
- private readonly graph;
51
+ private readonly graphModel;
52
+ /** `provider.id -> provider.token`, populated by `buildProviderDependencyMap()`. */
53
+ private readonly providerLabels;
50
54
  /**
51
55
  * Create a new CircularDependencyDetector instance
52
56
  *
53
- * @param graph - Map of module names to parsed modules
57
+ * @param graphModel - The application's graph model
54
58
  */
55
- constructor(graph: Map<string, ParseNsModule | ParseEntryFile>);
59
+ constructor(graphModel: NexusGraphModel);
56
60
  /**
57
61
  * Analyze the dependency graph for circular dependencies
58
62
  *
@@ -71,9 +75,12 @@ export declare class CircularDependencyDetector {
71
75
  */
72
76
  private detectModuleCircularDependencies;
73
77
  /**
74
- * Depth-first search for module circular dependencies
78
+ * Depth-first search for module circular dependencies. Traverses by
79
+ * `GraphModuleNode.id` (collision-free); `cyclePath`/`path` are built
80
+ * from ids and translated to display names only when a cycle is reported.
75
81
  */
76
82
  private dfsModules;
83
+ private moduleLabel;
77
84
  /**
78
85
  * Detect circular dependencies in provider dependencies
79
86
  *
@@ -84,9 +91,22 @@ export declare class CircularDependencyDetector {
84
91
  */
85
92
  private detectProviderCircularDependencies;
86
93
  /**
87
- * Build a map of provider tokens to their dependencies
94
+ * Build a map of provider ids to their dependency ids and a map of
95
+ * provider ids to display tokens, across every module in the graph.
96
+ * Keyed by `GraphProviderNode.id`/`GraphProviderDependency.tokenId`
97
+ * (collision-free), not the rendered `token` — two different provider
98
+ * classes named alike must not collapse into one map entry.
88
99
  */
89
100
  private buildProviderDependencyMap;
101
+ /**
102
+ * With child scopes, every scope has its own provider instances: a node
103
+ * is `(scope, provider id)`, and an edge goes to the nearest scope (own
104
+ * first, then ancestors) that registers the dependency. This keeps a
105
+ * child provider that overrides a parent token from being merged with
106
+ * the parent's provider into a false cycle.
107
+ */
108
+ private buildScopedProviderDependencyMap;
109
+ private providerLabel;
90
110
  /**
91
111
  * Depth-first search for provider circular dependencies
92
112
  */
@@ -1,15 +1,19 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.CircularDependencyDetector = void 0;
4
+ const scope_resolution_1 = require("../graph/scope-resolution");
4
5
  /**
5
6
  * Detects circular dependencies in module imports and provider dependencies
6
7
  *
7
8
  * Uses depth-first search (DFS) to detect cycles in the dependency graph.
8
- * Can detect both module-level circular imports and provider-level circular dependencies.
9
+ * Can detect both module-level circular imports and provider-level circular
10
+ * dependencies — the latter covering both constructor-injected dependencies
11
+ * and `useFactory` `inject` tokens, since `NexusGraphModel` merges both into
12
+ * a single provider dependency list.
9
13
  *
10
14
  * @example
11
15
  * ```typescript
12
- * const detector = new CircularDependencyDetector(modulesGraph);
16
+ * const detector = new CircularDependencyDetector(graphModel);
13
17
  * const analysis = detector.analyze();
14
18
  *
15
19
  * if (analysis.hasCircularDependencies) {
@@ -24,10 +28,12 @@ class CircularDependencyDetector {
24
28
  /**
25
29
  * Create a new CircularDependencyDetector instance
26
30
  *
27
- * @param graph - Map of module names to parsed modules
31
+ * @param graphModel - The application's graph model
28
32
  */
29
- constructor(graph) {
30
- this.graph = graph;
33
+ constructor(graphModel) {
34
+ this.graphModel = graphModel;
35
+ /** `provider.id -> provider.token`, populated by `buildProviderDependencyMap()`. */
36
+ this.providerLabels = new Map();
31
37
  }
32
38
  /**
33
39
  * Analyze the dependency graph for circular dependencies
@@ -64,52 +70,59 @@ class CircularDependencyDetector {
64
70
  const visited = new Set();
65
71
  const recursionStack = new Set();
66
72
  const path = [];
67
- const entryModule = this.graph.get("entry");
68
- if (!entryModule || !entryModule.name) {
73
+ const entryModuleId = this.graphModel.entryModuleId;
74
+ if (!entryModuleId) {
69
75
  return cycles;
70
76
  }
71
77
  // Start DFS from entry module
72
- this.dfsModules(entryModule.name, visited, recursionStack, path, cycles);
78
+ this.dfsModules(entryModuleId, visited, recursionStack, path, cycles);
73
79
  return cycles;
74
80
  }
75
81
  /**
76
- * Depth-first search for module circular dependencies
82
+ * Depth-first search for module circular dependencies. Traverses by
83
+ * `GraphModuleNode.id` (collision-free); `cyclePath`/`path` are built
84
+ * from ids and translated to display names only when a cycle is reported.
77
85
  */
78
- dfsModules(moduleName, visited, recursionStack, path, cycles) {
86
+ dfsModules(moduleId, visited, recursionStack, path, cycles) {
79
87
  // Mark current node as visited and add to recursion stack
80
- visited.add(moduleName);
81
- recursionStack.add(moduleName);
82
- path.push(moduleName);
83
- const parseNsModule = this.graph.get(moduleName);
84
- if (!parseNsModule) {
88
+ visited.add(moduleId);
89
+ recursionStack.add(moduleId);
90
+ path.push(moduleId);
91
+ const module = this.graphModel.modules.get(moduleId);
92
+ if (!module) {
85
93
  // Clean up and return
86
- recursionStack.delete(moduleName);
94
+ recursionStack.delete(moduleId);
87
95
  path.pop();
88
96
  return;
89
97
  }
90
98
  // Visit all imported modules
91
- for (const importedModule of parseNsModule.imports) {
92
- if (!visited.has(importedModule)) {
99
+ for (const importedModule of module.imports) {
100
+ const importedId = importedModule.id;
101
+ if (!visited.has(importedId)) {
93
102
  // Recursively visit unvisited module
94
- this.dfsModules(importedModule, visited, recursionStack, path, cycles);
103
+ this.dfsModules(importedId, visited, recursionStack, path, cycles);
95
104
  }
96
- else if (recursionStack.has(importedModule)) {
105
+ else if (recursionStack.has(importedId)) {
97
106
  // Found a cycle - extract the cycle path
98
- const cycleStartIndex = path.indexOf(importedModule);
107
+ const cycleStartIndex = path.indexOf(importedId);
99
108
  const cyclePath = path.slice(cycleStartIndex);
100
- cyclePath.push(importedModule); // Complete the cycle
109
+ cyclePath.push(importedId); // Complete the cycle
110
+ const cycleLabels = cyclePath.map((id) => this.moduleLabel(id));
101
111
  cycles.push({
102
112
  type: "module",
103
- cycle: cyclePath,
113
+ cycle: cycleLabels,
104
114
  severity: "error",
105
- message: `Circular module import detected: ${cyclePath.join(" -> ")}`,
115
+ message: `Circular module import detected: ${cycleLabels.join(" -> ")}`,
106
116
  });
107
117
  }
108
118
  }
109
119
  // Remove from recursion stack and path
110
- recursionStack.delete(moduleName);
120
+ recursionStack.delete(moduleId);
111
121
  path.pop();
112
122
  }
123
+ moduleLabel(moduleId) {
124
+ return this.graphModel.modules.get(moduleId)?.name ?? moduleId;
125
+ }
113
126
  /**
114
127
  * Detect circular dependencies in provider dependencies
115
128
  *
@@ -126,76 +139,106 @@ class CircularDependencyDetector {
126
139
  // Build provider dependency map
127
140
  const providerDeps = this.buildProviderDependencyMap();
128
141
  // Run DFS from each provider
129
- for (const providerToken of providerDeps.keys()) {
130
- if (!visited.has(providerToken)) {
131
- this.dfsProviders(providerToken, providerDeps, visited, recursionStack, path, cycles);
142
+ for (const providerId of providerDeps.keys()) {
143
+ if (!visited.has(providerId)) {
144
+ this.dfsProviders(providerId, providerDeps, visited, recursionStack, path, cycles);
132
145
  }
133
146
  }
134
147
  return cycles;
135
148
  }
136
149
  /**
137
- * Build a map of provider tokens to their dependencies
150
+ * Build a map of provider ids to their dependency ids and a map of
151
+ * provider ids to display tokens, across every module in the graph.
152
+ * Keyed by `GraphProviderNode.id`/`GraphProviderDependency.tokenId`
153
+ * (collision-free), not the rendered `token` — two different provider
154
+ * classes named alike must not collapse into one map entry.
138
155
  */
139
156
  buildProviderDependencyMap() {
157
+ this.providerLabels.clear();
158
+ if (this.graphModel.scopes) {
159
+ return this.buildScopedProviderDependencyMap();
160
+ }
140
161
  const providerDeps = new Map();
141
- // Traverse all modules
142
- for (const [key, value] of this.graph.entries()) {
143
- if (key === "entry")
144
- continue;
145
- const parseNsModule = value;
146
- if (!parseNsModule.providers)
147
- continue;
148
- // Extract dependencies for each provider
149
- for (const provider of parseNsModule.providers) {
150
- if (!provider.token)
151
- continue;
152
- const dependencies = [];
153
- if (provider.dependencies && Array.isArray(provider.dependencies)) {
154
- for (const dep of provider.dependencies) {
155
- if (dep.token && !dep.optional) {
156
- dependencies.push(dep.token);
157
- }
158
- }
162
+ for (const module of this.graphModel.modules.values()) {
163
+ for (const provider of module.providers) {
164
+ const dependencies = provider.dependencies
165
+ .filter((dependency) => !dependency.optional && dependency.tokenId)
166
+ .map((dependency) => dependency.tokenId);
167
+ providerDeps.set(provider.id, dependencies);
168
+ this.providerLabels.set(provider.id, provider.token);
169
+ }
170
+ }
171
+ return providerDeps;
172
+ }
173
+ /**
174
+ * With child scopes, every scope has its own provider instances: a node
175
+ * is `(scope, provider id)`, and an edge goes to the nearest scope (own
176
+ * first, then ancestors) that registers the dependency. This keeps a
177
+ * child provider that overrides a parent token from being merged with
178
+ * the parent's provider into a false cycle.
179
+ */
180
+ buildScopedProviderDependencyMap() {
181
+ const providerDeps = new Map();
182
+ const scopeIndex = new scope_resolution_1.GraphScopeIndex(this.graphModel);
183
+ const key = (scopeRootId, providerId) => `${scopeRootId}\u0000${providerId}`;
184
+ for (const scope of this.graphModel.scopes ?? []) {
185
+ for (const moduleId of scope.moduleIds) {
186
+ const module = this.graphModel.modules.get(moduleId);
187
+ for (const provider of module?.providers ?? []) {
188
+ const dependencies = provider.dependencies
189
+ .filter((dependency) => !dependency.optional && dependency.tokenId)
190
+ .map((dependency) => {
191
+ const tokenId = dependency.tokenId;
192
+ const providingScope = scopeIndex.providingScope(scope.rootId, tokenId) ??
193
+ scope.rootId;
194
+ return key(providingScope, tokenId);
195
+ });
196
+ const providerKey = key(scope.rootId, provider.id);
197
+ providerDeps.set(providerKey, dependencies);
198
+ this.providerLabels.set(providerKey, provider.token);
159
199
  }
160
- providerDeps.set(provider.token, dependencies);
161
200
  }
162
201
  }
163
202
  return providerDeps;
164
203
  }
204
+ providerLabel(providerId) {
205
+ return this.providerLabels.get(providerId) ?? providerId;
206
+ }
165
207
  /**
166
208
  * Depth-first search for provider circular dependencies
167
209
  */
168
- dfsProviders(providerToken, providerDeps, visited, recursionStack, path, cycles) {
210
+ dfsProviders(providerId, providerDeps, visited, recursionStack, path, cycles) {
169
211
  // Mark current node as visited and add to recursion stack
170
- visited.add(providerToken);
171
- recursionStack.add(providerToken);
172
- path.push(providerToken);
173
- const dependencies = providerDeps.get(providerToken) || [];
212
+ visited.add(providerId);
213
+ recursionStack.add(providerId);
214
+ path.push(providerId);
215
+ const dependencies = providerDeps.get(providerId) || [];
174
216
  // Visit all dependencies
175
- for (const depToken of dependencies) {
176
- if (!providerDeps.has(depToken)) {
217
+ for (const depId of dependencies) {
218
+ if (!providerDeps.has(depId)) {
177
219
  // Dependency is not a provider (might be external), skip
178
220
  continue;
179
221
  }
180
- if (!visited.has(depToken)) {
222
+ if (!visited.has(depId)) {
181
223
  // Recursively visit unvisited dependency
182
- this.dfsProviders(depToken, providerDeps, visited, recursionStack, path, cycles);
224
+ this.dfsProviders(depId, providerDeps, visited, recursionStack, path, cycles);
183
225
  }
184
- else if (recursionStack.has(depToken)) {
226
+ else if (recursionStack.has(depId)) {
185
227
  // Found a cycle - extract the cycle path
186
- const cycleStartIndex = path.indexOf(depToken);
228
+ const cycleStartIndex = path.indexOf(depId);
187
229
  const cyclePath = path.slice(cycleStartIndex);
188
- cyclePath.push(depToken); // Complete the cycle
230
+ cyclePath.push(depId); // Complete the cycle
231
+ const cycleLabels = cyclePath.map((id) => this.providerLabel(id));
189
232
  cycles.push({
190
233
  type: "provider",
191
- cycle: cyclePath,
234
+ cycle: cycleLabels,
192
235
  severity: "error",
193
- message: `Circular provider dependency detected: ${cyclePath.join(" -> ")}`,
236
+ message: `Circular provider dependency detected: ${cycleLabels.join(" -> ")}`,
194
237
  });
195
238
  }
196
239
  }
197
240
  // Remove from recursion stack and path
198
- recursionStack.delete(providerToken);
241
+ recursionStack.delete(providerId);
199
242
  path.pop();
200
243
  }
201
244
  }
@@ -1 +1 @@
1
- {"version":3,"file":"circular-dependency-detector.js","sourceRoot":"","sources":["../../src/analyzer/circular-dependency-detector.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"circular-dependency-detector.js","sourceRoot":"","sources":["../../src/analyzer/circular-dependency-detector.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,4 @@
1
- import type { ParseEntryFile } from "../parser/parse-entry-file";
2
- import type { ParseNsModule } from "../parser/parse-ns-module";
1
+ import type { NexusGraphModel } from "../graph/nexus-graph-model";
3
2
  /**
4
3
  * Information about a module's position in the hierarchy
5
4
  */
@@ -59,7 +58,7 @@ export interface ModuleDepthAnalysis {
59
58
  *
60
59
  * @example
61
60
  * ```typescript
62
- * const analyzer = new ModuleDepthAnalyzer(modulesGraph);
61
+ * const analyzer = new ModuleDepthAnalyzer(graphModel);
63
62
  * const analysis = analyzer.analyze();
64
63
  *
65
64
  * console.log(`Max depth: ${analysis.maxDepth}`);
@@ -71,15 +70,15 @@ export interface ModuleDepthAnalysis {
71
70
  * ```
72
71
  */
73
72
  export declare class ModuleDepthAnalyzer {
74
- private readonly graph;
73
+ private readonly graphModel;
75
74
  private readonly deepModuleThreshold;
76
75
  /**
77
76
  * Create a new ModuleDepthAnalyzer instance
78
77
  *
79
- * @param graph - Map of module names to parsed modules
78
+ * @param graphModel - The application's graph model
80
79
  * @param deepModuleThreshold - Custom threshold for deep modules (optional)
81
80
  */
82
- constructor(graph: Map<string, ParseNsModule | ParseEntryFile>, deepModuleThreshold?: number);
81
+ constructor(graphModel: NexusGraphModel, deepModuleThreshold?: number);
83
82
  /**
84
83
  * Analyze the module hierarchy depth and complexity
85
84
  *
@@ -87,19 +86,17 @@ export declare class ModuleDepthAnalyzer {
87
86
  */
88
87
  analyze(): ModuleDepthAnalysis;
89
88
  /**
90
- * Get all modules from the graph (excluding entry file)
91
- */
92
- private getModules;
93
- /**
94
- * Calculate depth for each module using BFS from entry point
89
+ * Calculate depth for each module (by id) using BFS from entry point
95
90
  */
96
91
  private calculateModuleDepths;
97
92
  /**
98
- * Calculate complexity metrics for each module
93
+ * Calculate complexity metrics for each module. `moduleDepths` is keyed
94
+ * by id; the returned `ModuleDepthInfo[]` reports display names, as before.
99
95
  */
100
96
  private calculateComplexityMetrics;
101
97
  /**
102
- * Calculate fan-in for all modules (how many modules depend on each module)
98
+ * Calculate fan-in for all modules (how many modules depend on each
99
+ * module), keyed by id
103
100
  */
104
101
  private calculateFanIn;
105
102
  /**