@nexus-ioc/graph-analyzer 0.1.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 (98) hide show
  1. package/CHANGELOG.md +112 -0
  2. package/LICENSE +21 -0
  3. package/README.md +742 -0
  4. package/dist/analyzer/circular-dependency-detector.d.ts +94 -0
  5. package/dist/analyzer/circular-dependency-detector.js +203 -0
  6. package/dist/analyzer/circular-dependency-detector.js.map +1 -0
  7. package/dist/analyzer/module-depth-analyzer.d.ts +117 -0
  8. package/dist/analyzer/module-depth-analyzer.js +221 -0
  9. package/dist/analyzer/module-depth-analyzer.js.map +1 -0
  10. package/dist/analyzer/provider-scope-analyzer.d.ts +91 -0
  11. package/dist/analyzer/provider-scope-analyzer.js +137 -0
  12. package/dist/analyzer/provider-scope-analyzer.js.map +1 -0
  13. package/dist/analyzer/unused-provider-detector.d.ts +73 -0
  14. package/dist/analyzer/unused-provider-detector.js +147 -0
  15. package/dist/analyzer/unused-provider-detector.js.map +1 -0
  16. package/dist/cli.d.ts +2 -0
  17. package/dist/cli.js +537 -0
  18. package/dist/cli.js.map +1 -0
  19. package/dist/config/config-loader.d.ts +27 -0
  20. package/dist/config/config-loader.js +205 -0
  21. package/dist/config/config-loader.js.map +1 -0
  22. package/dist/config/config-schema.d.ts +83 -0
  23. package/dist/config/config-schema.js +97 -0
  24. package/dist/config/config-schema.js.map +1 -0
  25. package/dist/index.d.ts +41 -0
  26. package/dist/index.js +50 -0
  27. package/dist/index.js.map +1 -0
  28. package/dist/interfaces/graph-output.interface.d.ts +126 -0
  29. package/dist/interfaces/graph-output.interface.js +3 -0
  30. package/dist/interfaces/graph-output.interface.js.map +1 -0
  31. package/dist/interfaces/providers.interface.d.ts +10 -0
  32. package/dist/interfaces/providers.interface.js +3 -0
  33. package/dist/interfaces/providers.interface.js.map +1 -0
  34. package/dist/parser/dependency-extractor.d.ts +88 -0
  35. package/dist/parser/dependency-extractor.js +334 -0
  36. package/dist/parser/dependency-extractor.js.map +1 -0
  37. package/dist/parser/parse-entry-file.d.ts +15 -0
  38. package/dist/parser/parse-entry-file.js +46 -0
  39. package/dist/parser/parse-entry-file.js.map +1 -0
  40. package/dist/parser/parse-imports.d.ts +15 -0
  41. package/dist/parser/parse-imports.js +65 -0
  42. package/dist/parser/parse-imports.js.map +1 -0
  43. package/dist/parser/parse-ns-module.d.ts +39 -0
  44. package/dist/parser/parse-ns-module.js +122 -0
  45. package/dist/parser/parse-ns-module.js.map +1 -0
  46. package/dist/parser/parse-provider.d.ts +2 -0
  47. package/dist/parser/parse-provider.js +7 -0
  48. package/dist/parser/parse-provider.js.map +1 -0
  49. package/dist/parser/parse-ts-config.d.ts +10 -0
  50. package/dist/parser/parse-ts-config.js +40 -0
  51. package/dist/parser/parse-ts-config.js.map +1 -0
  52. package/dist/parser/parser-rec.d.ts +1 -0
  53. package/dist/parser/parser-rec.js +52 -0
  54. package/dist/parser/parser-rec.js.map +1 -0
  55. package/dist/parser/parser.d.ts +1 -0
  56. package/dist/parser/parser.js +61 -0
  57. package/dist/parser/parser.js.map +1 -0
  58. package/dist/parser/provider/class-parser.d.ts +20 -0
  59. package/dist/parser/provider/class-parser.js +137 -0
  60. package/dist/parser/provider/class-parser.js.map +1 -0
  61. package/dist/parser/provider/providers-parser.d.ts +9 -0
  62. package/dist/parser/provider/providers-parser.js +32 -0
  63. package/dist/parser/provider/providers-parser.js.map +1 -0
  64. package/dist/parser/provider/use-class-parser.d.ts +25 -0
  65. package/dist/parser/provider/use-class-parser.js +161 -0
  66. package/dist/parser/provider/use-class-parser.js.map +1 -0
  67. package/dist/parser/provider/use-factory-parser.d.ts +15 -0
  68. package/dist/parser/provider/use-factory-parser.js +53 -0
  69. package/dist/parser/provider/use-factory-parser.js.map +1 -0
  70. package/dist/parser/provider/use-value-parser.d.ts +13 -0
  71. package/dist/parser/provider/use-value-parser.js +41 -0
  72. package/dist/parser/provider/use-value-parser.js.map +1 -0
  73. package/dist/parser/rules.d.ts +10 -0
  74. package/dist/parser/rules.js +37 -0
  75. package/dist/parser/rules.js.map +1 -0
  76. package/dist/utils/error-formatter.d.ts +27 -0
  77. package/dist/utils/error-formatter.js +177 -0
  78. package/dist/utils/error-formatter.js.map +1 -0
  79. package/dist/utils/validator.d.ts +45 -0
  80. package/dist/utils/validator.js +252 -0
  81. package/dist/utils/validator.js.map +1 -0
  82. package/dist/visualize/generator.d.ts +26 -0
  83. package/dist/visualize/generator.js +75 -0
  84. package/dist/visualize/generator.js.map +1 -0
  85. package/dist/visualize/graph-analyzer.d.ts +126 -0
  86. package/dist/visualize/graph-analyzer.js +199 -0
  87. package/dist/visualize/graph-analyzer.js.map +1 -0
  88. package/dist/visualize/html-generator.d.ts +71 -0
  89. package/dist/visualize/html-generator.js +1312 -0
  90. package/dist/visualize/html-generator.js.map +1 -0
  91. package/dist/visualize/json-formatter.d.ts +106 -0
  92. package/dist/visualize/json-formatter.js +252 -0
  93. package/dist/visualize/json-formatter.js.map +1 -0
  94. package/docs/API_REFERENCE.md +747 -0
  95. package/docs/ARCHITECTURE.md +486 -0
  96. package/docs/CLI_GUIDE.md +576 -0
  97. package/docs/HTML_VISUALIZATION.md +365 -0
  98. package/package.json +85 -0
@@ -0,0 +1,486 @@
1
+ # Graph Analyzer Architecture
2
+
3
+ This document explains the technical architecture of the Nexus IoC Graph Analyzer, including how it works internally and the design decisions behind it.
4
+
5
+ ## Table of Contents
6
+
7
+ - [Overview](#overview)
8
+ - [Architecture Diagram](#architecture-diagram)
9
+ - [Core Components](#core-components)
10
+ - [Analysis Pipeline](#analysis-pipeline)
11
+ - [Dependency Extraction](#dependency-extraction)
12
+ - [Design Decisions](#design-decisions)
13
+ - [Performance Considerations](#performance-considerations)
14
+
15
+ ## Overview
16
+
17
+ The Graph Analyzer is a static analysis tool that builds a complete dependency injection graph from TypeScript source code without executing it. It uses the TypeScript Compiler API to parse and analyze code, extracting metadata about modules, providers, and their dependencies.
18
+
19
+ ### Key Principles
20
+
21
+ 1. **Static Analysis Only** - No code execution required
22
+ 2. **AST-Based** - Uses TypeScript's Abstract Syntax Tree
23
+ 3. **Decorator-Aware** - Understands both legacy and TC39 Stage 3 decorators
24
+ 4. **Import Resolution** - Follows module imports to build complete graph
25
+ 5. **Type-Safe** - Leverages TypeScript's type system
26
+
27
+ ## Architecture Diagram
28
+
29
+ ```
30
+ ┌─────────────────────────────────────────────────────────────┐
31
+ │ Entry Point │
32
+ │ (CLI or API) │
33
+ └────────────────────────┬────────────────────────────────────┘
34
+ │
35
+ ▼
36
+ ┌─────────────────────────────────────────────────────────────┐
37
+ │ ParseTsConfig │
38
+ │ (Load TypeScript Config) │
39
+ └────────────────────────┬────────────────────────────────────┘
40
+ │
41
+ ▼
42
+ ┌─────────────────────────────────────────────────────────────┐
43
+ │ ParseEntryFile │
44
+ │ (Find Root Module from Entry) │
45
+ └────────────────────────┬────────────────────────────────────┘
46
+ │
47
+ ▼
48
+ ┌─────────────────────────────────────────────────────────────┐
49
+ │ Module Traversal │
50
+ │ (Recursively Parse All Modules) │
51
+ │ │
52
+ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
53
+ │ │ ParseNsModule│───▶│ ParseNsModule│───▶│ ParseNsModule│ │
54
+ │ └──────────────┘ └──────────────┘ └──────────────┘ │
55
+ └────────────────────────┬────────────────────────────────────┘
56
+ │
57
+ ▼
58
+ ┌─────────────────────────────────────────────────────────────┐
59
+ │ Provider Analysis │
60
+ │ │
61
+ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
62
+ │ │ ClassParser │ │UseValueParser│ │UseFactoryPar.│ │
63
+ │ └──────┬───────┘ └──────────────┘ └──────────────┘ │
64
+ │ │ │
65
+ │ ▼ │
66
+ │ ┌──────────────────────────────────┐ │
67
+ │ │ DependencyExtractor │ │
68
+ │ │ (Extract @Inject, @Optional) │ │
69
+ │ └──────────────────────────────────┘ │
70
+ └────────────────────────┬────────────────────────────────────┘
71
+ │
72
+ ▼
73
+ ┌─────────────────────────────────────────────────────────────┐
74
+ │ Graph Building │
75
+ │ (Construct Dependency Graph) │
76
+ └────────────────────────┬────────────────────────────────────┘
77
+ │
78
+ ▼
79
+ ┌─────────────────────────────────────────────────────────────┐
80
+ │ Output Generation │
81
+ │ │
82
+ │ ┌──────────────┐ ┌──────────────┐ │
83
+ │ │JsonFormatter │ │GraphVisualizer│ │
84
+ │ │ (JSON Data) │ │ (PNG Image) │ │
85
+ │ └──────────────┘ └──────────────┘ │
86
+ └─────────────────────────────────────────────────────────────┘
87
+ ```
88
+
89
+ ## Core Components
90
+
91
+ ### 1. ParseTsConfig
92
+
93
+ **Purpose**: Load and parse TypeScript configuration
94
+
95
+ **Responsibilities**:
96
+ - Read tsconfig.json
97
+ - Parse compiler options
98
+ - Resolve path aliases
99
+ - Provide configuration to other components
100
+
101
+ **Key Methods**:
102
+ - `constructor(configContent, basePath)` - Initialize with config
103
+ - `getCompilerOptions()` - Get TypeScript compiler options
104
+
105
+ ### 2. ParseEntryFile
106
+
107
+ **Purpose**: Parse the application entry point
108
+
109
+ **Responsibilities**:
110
+ - Find the root module from entry file
111
+ - Extract bootstrap information
112
+ - Identify the starting point for graph traversal
113
+
114
+ **Key Methods**:
115
+ - `parse()` - Parse entry file and find root module
116
+ - `getName()` - Get root module name
117
+
118
+ ### 3. ParseNsModule
119
+
120
+ **Purpose**: Parse individual Nexus IoC modules
121
+
122
+ **Responsibilities**:
123
+ - Extract module metadata (@NsModule decorator)
124
+ - Parse imports, exports, providers
125
+ - Identify global modules
126
+ - Trigger provider parsing
127
+
128
+ **Key Methods**:
129
+ - `parse()` - Parse module decorator and metadata
130
+ - `getProviders()` - Get module's providers
131
+ - `getImports()` - Get imported modules
132
+
133
+ ### 4. Provider Parsers
134
+
135
+ **ClassParser**: Parses class providers
136
+ - Extracts class metadata
137
+ - Triggers dependency extraction
138
+ - Handles scope (Singleton, Transient, Request)
139
+
140
+ **UseValueParser**: Parses value providers
141
+ - Extracts static values
142
+ - No dependency extraction needed
143
+
144
+ **UseFactoryParser**: Parses factory providers
145
+ - Extracts factory function
146
+ - Analyzes factory dependencies
147
+
148
+ **UseClassParser**: Parses class-based providers
149
+ - Similar to ClassParser
150
+ - Handles interface-to-implementation mapping
151
+
152
+ ### 5. DependencyExtractor
153
+
154
+ **Purpose**: Extract dependency injection metadata from classes
155
+
156
+ **Responsibilities**:
157
+ - Parse @Inject decorators on constructor parameters
158
+ - Parse @Inject decorators on properties
159
+ - Detect @Optional decorators
160
+ - Support TC39 Stage 3 static dependencies
161
+ - Identify token types (class, string, symbol)
162
+
163
+ **Key Methods**:
164
+ - `extractDependencies(classDeclaration)` - Main extraction method
165
+ - `extractConstructorDependencies()` - Constructor injection
166
+ - `extractPropertyDependencies()` - Property injection
167
+ - `extractStaticDependencies()` - TC39 Stage 3 pattern
168
+
169
+ **Algorithm**:
170
+
171
+ ```typescript
172
+ 1. Find class declaration in AST
173
+ 2. Check for static dependencies array (TC39 Stage 3)
174
+ - If found, parse structured dependency objects
175
+ - Return early
176
+ 3. Find constructor
177
+ - For each parameter:
178
+ - Check for @Inject decorator
179
+ - Extract token (class, string, or symbol)
180
+ - Check for @Optional decorator
181
+ - Store with index
182
+ 4. Find properties
183
+ - For each property:
184
+ - Check for @Inject decorator
185
+ - Extract token
186
+ - Check for @Optional decorator
187
+ - Store with property name
188
+ 5. Return combined dependencies
189
+ ```
190
+
191
+ ### 6. GraphAnalyzer
192
+
193
+ **Purpose**: Coordinate analysis and output generation
194
+
195
+ **Responsibilities**:
196
+ - Accept module graph
197
+ - Generate JSON output via JsonFormatter
198
+ - Generate PNG output via Graphviz
199
+ - Handle output format options
200
+
201
+ **Key Methods**:
202
+ - `parse()` - Main analysis method
203
+ - `generateJson()` - Create JSON output
204
+ - `generatePng()` - Create PNG visualization
205
+
206
+ ### 7. JsonFormatter
207
+
208
+ **Purpose**: Format graph as structured JSON
209
+
210
+ **Responsibilities**:
211
+ - Traverse module graph
212
+ - Extract all metadata
213
+ - Create structured output
214
+ - Generate statistics
215
+
216
+ **Output Structure**:
217
+ ```typescript
218
+ interface GraphOutput {
219
+ modules: ModuleInfo[];
220
+ providers: ProviderInfo[];
221
+ metadata: GraphMetadata;
222
+ }
223
+ ```
224
+
225
+ ## Analysis Pipeline
226
+
227
+ ### Phase 1: Configuration Loading
228
+
229
+ ```typescript
230
+ const tsConfig = new ParseTsConfig(configContent, basePath);
231
+ ```
232
+
233
+ - Loads tsconfig.json
234
+ - Parses compiler options
235
+ - Sets up path alias resolution
236
+
237
+ ### Phase 2: Entry Point Analysis
238
+
239
+ ```typescript
240
+ const entryFile = new ParseEntryFile(sourceFile, filePath, tsConfig);
241
+ entryFile.parse();
242
+ const rootModuleName = entryFile.getName();
243
+ ```
244
+
245
+ - Parses entry file (e.g., main.ts)
246
+ - Finds bootstrap call
247
+ - Extracts root module name
248
+
249
+ ### Phase 3: Module Graph Traversal
250
+
251
+ ```typescript
252
+ const modulesGraph = new Map();
253
+ // Recursive traversal
254
+ function traverseModule(moduleName) {
255
+ const module = new ParseNsModule(sourceFile, filePath, tsConfig);
256
+ module.parse();
257
+ modulesGraph.set(moduleName, module);
258
+
259
+ for (const importedModule of module.getImports()) {
260
+ if (!modulesGraph.has(importedModule)) {
261
+ traverseModule(importedModule);
262
+ }
263
+ }
264
+ }
265
+ ```
266
+
267
+ - Starts from root module
268
+ - Recursively follows imports
269
+ - Builds complete module graph
270
+ - Detects circular imports
271
+
272
+ ### Phase 4: Provider Analysis
273
+
274
+ For each module:
275
+
276
+ ```typescript
277
+ const providers = module.getProviders();
278
+ for (const provider of providers) {
279
+ const parser = selectParser(provider); // ClassParser, UseValueParser, etc.
280
+ const metadata = parser.parse();
281
+
282
+ if (parser instanceof ClassParser) {
283
+ const dependencies = dependencyExtractor.extractDependencies(classNode);
284
+ metadata.dependencies = dependencies;
285
+ }
286
+ }
287
+ ```
288
+
289
+ - Parses each provider
290
+ - Extracts provider metadata
291
+ - For class providers, extracts dependencies
292
+
293
+ ### Phase 5: Output Generation
294
+
295
+ ```typescript
296
+ const analyzer = new GraphAnalyzer(modulesGraph, entryPoint, options);
297
+ const output = analyzer.parse();
298
+ ```
299
+
300
+ - Creates GraphAnalyzer instance
301
+ - Generates requested output format(s)
302
+ - Writes to file(s)
303
+
304
+ ## Dependency Extraction
305
+
306
+ ### Legacy Decorators (TypeScript Experimental)
307
+
308
+ ```typescript
309
+ @Injectable()
310
+ class UserService {
311
+ constructor(
312
+ @Inject(UserRepository) private repo: UserRepository
313
+ ) {}
314
+ }
315
+ ```
316
+
317
+ **AST Pattern**:
318
+ ```
319
+ ClassDeclaration
320
+ └── Constructor
321
+ └── Parameter
322
+ └── Decorator (@Inject)
323
+ └── CallExpression
324
+ └── Argument (UserRepository)
325
+ ```
326
+
327
+ **Extraction**:
328
+ 1. Find constructor node
329
+ 2. Iterate parameters
330
+ 3. Find @Inject decorator
331
+ 4. Extract argument as token
332
+ 5. Check for @Optional decorator
333
+
334
+ ### TC39 Stage 3 Decorators
335
+
336
+ ```typescript
337
+ @Injectable()
338
+ class UserService {
339
+ static dependencies = [
340
+ { index: 0, token: UserRepository, optional: false }
341
+ ];
342
+ }
343
+ ```
344
+
345
+ **AST Pattern**:
346
+ ```
347
+ ClassDeclaration
348
+ └── PropertyDeclaration (static dependencies)
349
+ └── ArrayLiteralExpression
350
+ └── ObjectLiteralExpression
351
+ ├── index: 0
352
+ ├── token: UserRepository
353
+ └── optional: false
354
+ ```
355
+
356
+ **Extraction**:
357
+ 1. Find static dependencies property
358
+ 2. Parse array literal
359
+ 3. Extract structured objects
360
+ 4. Map to Dependency interface
361
+
362
+ ### Token Type Detection
363
+
364
+ ```typescript
365
+ function detectTokenType(token: ts.Node): 'class' | 'string' | 'symbol' {
366
+ if (ts.isIdentifier(token)) return 'class';
367
+ if (ts.isStringLiteral(token)) return 'string';
368
+ if (isSymbolExpression(token)) return 'symbol';
369
+ }
370
+ ```
371
+
372
+ ## Design Decisions
373
+
374
+ ### Why TypeScript Compiler API?
375
+
376
+ **Pros**:
377
+ - Accurate AST parsing
378
+ - Type information available
379
+ - Handles all TypeScript syntax
380
+ - Well-maintained by Microsoft
381
+
382
+ **Cons**:
383
+ - Large dependency
384
+ - Complex API
385
+ - Performance overhead
386
+
387
+ **Decision**: The accuracy and completeness outweigh the complexity.
388
+
389
+ ### Why tsquery?
390
+
391
+ **Pros**:
392
+ - CSS-selector-like syntax for AST queries
393
+ - Simpler than manual AST traversal
394
+ - Good for finding specific patterns
395
+
396
+ **Usage**:
397
+ ```typescript
398
+ const decorators = tsquery(node, 'Decorator[expression.expression.name="Inject"]');
399
+ ```
400
+
401
+ ### Why Separate Parsers?
402
+
403
+ Each provider type (Class, UseValue, UseFactory, UseClass) has different parsing logic:
404
+ - **Separation of Concerns**: Each parser handles one type
405
+ - **Extensibility**: Easy to add new provider types
406
+ - **Testability**: Each parser can be tested independently
407
+
408
+ ### Why Two-Phase Analysis?
409
+
410
+ 1. **Module Traversal** - Build complete graph
411
+ 2. **Dependency Extraction** - Extract detailed metadata
412
+
413
+ **Rationale**:
414
+ - Allows circular dependency detection
415
+ - Enables lazy loading of class files
416
+ - Separates graph structure from implementation details
417
+
418
+ ## Performance Considerations
419
+
420
+ ### Caching
421
+
422
+ - Parsed modules are cached in Map
423
+ - Prevents re-parsing same module
424
+ - Reduces file I/O
425
+
426
+ ### Lazy Loading
427
+
428
+ - Class files loaded only when needed
429
+ - Import resolution on-demand
430
+ - Reduces memory footprint
431
+
432
+ ### AST Reuse
433
+
434
+ - TypeScript AST is reused across parsers
435
+ - No re-parsing of same file
436
+ - Faster analysis
437
+
438
+ ### Optimization Opportunities
439
+
440
+ 1. **Parallel Parsing**: Parse independent modules in parallel
441
+ 2. **Incremental Analysis**: Only re-analyze changed files
442
+ 3. **AST Caching**: Cache parsed ASTs to disk
443
+ 4. **Selective Analysis**: Only analyze specific modules
444
+
445
+ ## Error Handling
446
+
447
+ ### File Not Found
448
+
449
+ ```typescript
450
+ if (!fs.existsSync(filePath)) {
451
+ throw new Error(`File not found: ${filePath}`);
452
+ }
453
+ ```
454
+
455
+ ### Invalid Decorator
456
+
457
+ ```typescript
458
+ if (!decorator || !decorator.expression) {
459
+ console.warn(`Invalid decorator on ${className}`);
460
+ continue;
461
+ }
462
+ ```
463
+
464
+ ### Circular Dependencies
465
+
466
+ ```typescript
467
+ const visited = new Set();
468
+ function detectCircular(module, path = []) {
469
+ if (visited.has(module)) {
470
+ console.warn(`Circular dependency: ${path.join(' -> ')}`);
471
+ return;
472
+ }
473
+ visited.add(module);
474
+ // Continue traversal
475
+ }
476
+ ```
477
+
478
+ ## Future Enhancements
479
+
480
+ 1. **Incremental Analysis**: Only re-analyze changed files
481
+ 2. **Watch Mode**: Continuously monitor and update graph
482
+ 3. **Plugin System**: Allow custom analyzers
483
+ 4. **Performance Profiling**: Identify bottlenecks
484
+ 5. **Validation Rules**: Detect anti-patterns
485
+ 6. **Interactive Visualization**: Web-based graph explorer
486
+