@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,747 @@
1
+ # API Reference
2
+
3
+ Complete API documentation for using the Graph Analyzer programmatically.
4
+
5
+ ## Table of Contents
6
+
7
+ - [Installation](#installation)
8
+ - [Quick Start](#quick-start)
9
+ - [Core Classes](#core-classes)
10
+ - [Interfaces](#interfaces)
11
+ - [Usage Examples](#usage-examples)
12
+ - [Advanced Topics](#advanced-topics)
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ npm install --save-dev graph-analyzer
18
+ ```
19
+
20
+ ## Quick Start
21
+
22
+ ```typescript
23
+ import { GraphAnalyzer, ParseEntryFile, ParseTsConfig } from 'graph-analyzer';
24
+ import * as fs from 'fs';
25
+ import * as ts from 'typescript';
26
+
27
+ // Load and parse entry file
28
+ const entryContent = fs.readFileSync('src/main.ts', 'utf8');
29
+ const sourceFile = ts.createSourceFile(
30
+ 'src/main.ts',
31
+ entryContent,
32
+ ts.ScriptTarget.Latest,
33
+ true
34
+ );
35
+
36
+ // Parse TypeScript config
37
+ const tsConfigContent = fs.readFileSync('tsconfig.json', 'utf8');
38
+ const tsConfig = new ParseTsConfig(tsConfigContent, process.cwd());
39
+
40
+ // Parse entry file
41
+ const entryFile = new ParseEntryFile(sourceFile, 'src/main.ts', tsConfig);
42
+ entryFile.parse();
43
+
44
+ // Build module graph (simplified - see full example below)
45
+ const modulesGraph = new Map();
46
+ modulesGraph.set('entry', entryFile);
47
+
48
+ // Analyze and generate output
49
+ const analyzer = new GraphAnalyzer(modulesGraph, 'src/main.ts', {
50
+ outputFormat: 'json',
51
+ jsonOutputPath: './graph.json'
52
+ });
53
+
54
+ const output = analyzer.parse();
55
+ console.log(output);
56
+ ```
57
+
58
+ ## Core Classes
59
+
60
+ ### GraphAnalyzer
61
+
62
+ Main class for analyzing dependency graphs and generating output.
63
+
64
+ #### Constructor
65
+
66
+ ```typescript
67
+ constructor(
68
+ graph: Map<string, ParseNsModule | ParseEntryFile>,
69
+ entryPoint: string,
70
+ options?: GraphAnalyzerOptions
71
+ )
72
+ ```
73
+
74
+ **Parameters**:
75
+ - `graph`: Map of module name to parsed module
76
+ - `entryPoint`: Path to entry file
77
+ - `options`: Optional configuration
78
+
79
+ **Options**:
80
+ ```typescript
81
+ interface GraphAnalyzerOptions {
82
+ outputFormat?: 'json' | 'png' | 'both';
83
+ outputPath?: string;
84
+ jsonOutputPath?: string;
85
+ pngOutputPath?: string;
86
+ }
87
+ ```
88
+
89
+ #### Methods
90
+
91
+ ##### `parse(): GraphOutput | void`
92
+
93
+ Analyze the graph and generate output based on format option.
94
+
95
+ **Returns**:
96
+ - `GraphOutput` if format is 'json' or 'both'
97
+ - `void` if format is 'png'
98
+
99
+ **Example**:
100
+ ```typescript
101
+ const analyzer = new GraphAnalyzer(graph, 'src/main.ts', {
102
+ outputFormat: 'json'
103
+ });
104
+ const output = analyzer.parse();
105
+ ```
106
+
107
+ ##### `generateJson(): GraphOutput`
108
+
109
+ Generate JSON output.
110
+
111
+ **Returns**: `GraphOutput` object
112
+
113
+ **Example**:
114
+ ```typescript
115
+ const jsonOutput = analyzer.generateJson();
116
+ console.log(jsonOutput.metadata.totalProviders);
117
+ ```
118
+
119
+ ##### `generatePng(): void`
120
+
121
+ Generate PNG visualization using Graphviz.
122
+
123
+ **Side Effects**: Writes PNG file to disk
124
+
125
+ **Example**:
126
+ ```typescript
127
+ analyzer.generatePng();
128
+ ```
129
+
130
+ ---
131
+
132
+ ### JsonFormatter
133
+
134
+ Formats dependency graph as structured JSON.
135
+
136
+ #### Constructor
137
+
138
+ ```typescript
139
+ constructor(
140
+ graph: Map<string, ParseNsModule | ParseEntryFile>,
141
+ entryPoint: string
142
+ )
143
+ ```
144
+
145
+ **Parameters**:
146
+ - `graph`: Module graph to format
147
+ - `entryPoint`: Entry file path
148
+
149
+ #### Methods
150
+
151
+ ##### `format(): GraphOutput`
152
+
153
+ Format graph as structured object.
154
+
155
+ **Returns**: `GraphOutput` with modules, providers, and metadata
156
+
157
+ **Example**:
158
+ ```typescript
159
+ const formatter = new JsonFormatter(graph, 'src/main.ts');
160
+ const output = formatter.format();
161
+ ```
162
+
163
+ ##### `formatAsString(indent?: number): string`
164
+
165
+ Format graph as JSON string.
166
+
167
+ **Parameters**:
168
+ - `indent`: Number of spaces for indentation (default: 2)
169
+
170
+ **Returns**: JSON string
171
+
172
+ **Example**:
173
+ ```typescript
174
+ const jsonString = formatter.formatAsString(4);
175
+ fs.writeFileSync('graph.json', jsonString);
176
+ ```
177
+
178
+ ---
179
+
180
+ ### DependencyExtractor
181
+
182
+ Extracts dependency injection metadata from classes.
183
+
184
+ #### Constructor
185
+
186
+ ```typescript
187
+ constructor(
188
+ sourceFile: ts.SourceFile,
189
+ tsConfig: ParseTsConfig
190
+ )
191
+ ```
192
+
193
+ **Parameters**:
194
+ - `sourceFile`: TypeScript source file
195
+ - `tsConfig`: TypeScript configuration
196
+
197
+ #### Methods
198
+
199
+ ##### `extractDependencies(classDeclaration: ts.ClassDeclaration): Dependency[]`
200
+
201
+ Extract all dependencies from a class.
202
+
203
+ **Parameters**:
204
+ - `classDeclaration`: TypeScript class declaration node
205
+
206
+ **Returns**: Array of `Dependency` objects
207
+
208
+ **Example**:
209
+ ```typescript
210
+ const extractor = new DependencyExtractor(sourceFile, tsConfig);
211
+ const dependencies = extractor.extractDependencies(classNode);
212
+ ```
213
+
214
+ ---
215
+
216
+ ### ParseTsConfig
217
+
218
+ Parses and manages TypeScript configuration.
219
+
220
+ #### Constructor
221
+
222
+ ```typescript
223
+ constructor(
224
+ configContent: string,
225
+ basePath: string
226
+ )
227
+ ```
228
+
229
+ **Parameters**:
230
+ - `configContent`: tsconfig.json content as string
231
+ - `basePath`: Base directory path
232
+
233
+ **Example**:
234
+ ```typescript
235
+ const content = fs.readFileSync('tsconfig.json', 'utf8');
236
+ const tsConfig = new ParseTsConfig(content, process.cwd());
237
+ ```
238
+
239
+ #### Methods
240
+
241
+ ##### `getCompilerOptions(): ts.CompilerOptions`
242
+
243
+ Get TypeScript compiler options.
244
+
245
+ **Returns**: Compiler options object
246
+
247
+ ---
248
+
249
+ ### ParseEntryFile
250
+
251
+ Parses application entry point file.
252
+
253
+ #### Constructor
254
+
255
+ ```typescript
256
+ constructor(
257
+ sourceFile: ts.SourceFile,
258
+ filePath: string,
259
+ tsConfig: ParseTsConfig
260
+ )
261
+ ```
262
+
263
+ **Parameters**:
264
+ - `sourceFile`: TypeScript source file
265
+ - `filePath`: File path
266
+ - `tsConfig`: TypeScript configuration
267
+
268
+ #### Methods
269
+
270
+ ##### `parse(): void`
271
+
272
+ Parse entry file and extract root module.
273
+
274
+ **Side Effects**: Populates internal state
275
+
276
+ ##### `getName(): string | null`
277
+
278
+ Get root module name.
279
+
280
+ **Returns**: Module name or null
281
+
282
+ **Example**:
283
+ ```typescript
284
+ const entryFile = new ParseEntryFile(sourceFile, 'src/main.ts', tsConfig);
285
+ entryFile.parse();
286
+ const rootModule = entryFile.getName(); // 'AppModule'
287
+ ```
288
+
289
+ ---
290
+
291
+ ### ParseNsModule
292
+
293
+ Parses Nexus IoC module.
294
+
295
+ #### Constructor
296
+
297
+ ```typescript
298
+ constructor(
299
+ sourceFile: ts.SourceFile,
300
+ filePath: string,
301
+ tsConfig: ParseTsConfig
302
+ )
303
+ ```
304
+
305
+ #### Methods
306
+
307
+ ##### `parse(): void`
308
+
309
+ Parse module decorator and metadata.
310
+
311
+ ##### `getProviders(): Provider[]`
312
+
313
+ Get module's providers.
314
+
315
+ **Returns**: Array of provider objects
316
+
317
+ ##### `getImports(): string[]`
318
+
319
+ Get imported module names.
320
+
321
+ **Returns**: Array of module names
322
+
323
+ ##### `getExports(): string[]`
324
+
325
+ Get exported provider tokens.
326
+
327
+ **Returns**: Array of token names
328
+
329
+ **Example**:
330
+ ```typescript
331
+ const module = new ParseNsModule(sourceFile, 'app.module.ts', tsConfig);
332
+ module.parse();
333
+
334
+ const providers = module.getProviders();
335
+ const imports = module.getImports();
336
+ const exports = module.getExports();
337
+ ```
338
+
339
+ ---
340
+
341
+ ## Interfaces
342
+
343
+ ### GraphOutput
344
+
345
+ Complete graph analysis output.
346
+
347
+ ```typescript
348
+ interface GraphOutput {
349
+ modules: ModuleInfo[];
350
+ providers: ProviderInfo[];
351
+ metadata: GraphMetadata;
352
+ }
353
+ ```
354
+
355
+ ### ModuleInfo
356
+
357
+ Information about a single module.
358
+
359
+ ```typescript
360
+ interface ModuleInfo {
361
+ name: string;
362
+ path: string;
363
+ imports: string[];
364
+ exports: string[];
365
+ providers: string[];
366
+ isGlobal: boolean;
367
+ }
368
+ ```
369
+
370
+ ### ProviderInfo
371
+
372
+ Information about a single provider.
373
+
374
+ ```typescript
375
+ interface ProviderInfo {
376
+ token: string;
377
+ type: 'Class' | 'UseValue' | 'UseFactory' | 'UseClass';
378
+ module: string;
379
+ scope?: 'Singleton' | 'Transient' | 'Request';
380
+ dependencies?: Dependency[];
381
+ value?: any;
382
+ factory?: string;
383
+ useClass?: string;
384
+ }
385
+ ```
386
+
387
+ ### Dependency
388
+
389
+ Dependency injection metadata.
390
+
391
+ ```typescript
392
+ interface Dependency {
393
+ type: 'constructor' | 'property';
394
+ index?: number; // For constructor dependencies
395
+ key?: string; // For property dependencies
396
+ token: string;
397
+ tokenType: 'class' | 'string' | 'symbol';
398
+ optional: boolean;
399
+ raw?: string;
400
+ }
401
+ ```
402
+
403
+ ### GraphMetadata
404
+
405
+ Analysis metadata.
406
+
407
+ ```typescript
408
+ interface GraphMetadata {
409
+ entryPoint: string;
410
+ rootModule: string;
411
+ analyzedAt: string;
412
+ version: string;
413
+ totalModules: number;
414
+ totalProviders: number;
415
+ }
416
+ ```
417
+
418
+ ---
419
+
420
+ ## Usage Examples
421
+
422
+ ### Example 1: Basic Analysis
423
+
424
+ ```typescript
425
+ import { GraphAnalyzer, ParseEntryFile, ParseTsConfig } from 'graph-analyzer';
426
+ import * as fs from 'fs';
427
+ import * as ts from 'typescript';
428
+
429
+ function analyzeApplication(entryPath: string): void {
430
+ // Load files
431
+ const entryContent = fs.readFileSync(entryPath, 'utf8');
432
+ const tsConfigContent = fs.readFileSync('tsconfig.json', 'utf8');
433
+
434
+ // Parse
435
+ const sourceFile = ts.createSourceFile(
436
+ entryPath,
437
+ entryContent,
438
+ ts.ScriptTarget.Latest,
439
+ true
440
+ );
441
+
442
+ const tsConfig = new ParseTsConfig(tsConfigContent, process.cwd());
443
+ const entryFile = new ParseEntryFile(sourceFile, entryPath, tsConfig);
444
+ entryFile.parse();
445
+
446
+ // Build graph
447
+ const graph = new Map();
448
+ graph.set('entry', entryFile);
449
+
450
+ // Analyze
451
+ const analyzer = new GraphAnalyzer(graph, entryPath, {
452
+ outputFormat: 'both',
453
+ jsonOutputPath: './output/graph.json',
454
+ pngOutputPath: './output/graph.png'
455
+ });
456
+
457
+ analyzer.parse();
458
+ console.log('Analysis complete!');
459
+ }
460
+
461
+ analyzeApplication('src/main.ts');
462
+ ```
463
+
464
+ ### Example 2: Extract Specific Information
465
+
466
+ ```typescript
467
+ import { JsonFormatter } from 'graph-analyzer';
468
+
469
+ function findCircularDependencies(graph: Map<any, any>): string[] {
470
+ const formatter = new JsonFormatter(graph, 'src/main.ts');
471
+ const output = formatter.format();
472
+
473
+ const circular: string[] = [];
474
+ const visited = new Set<string>();
475
+ const stack = new Set<string>();
476
+
477
+ function visit(moduleName: string): void {
478
+ if (stack.has(moduleName)) {
479
+ circular.push(moduleName);
480
+ return;
481
+ }
482
+ if (visited.has(moduleName)) return;
483
+
484
+ visited.add(moduleName);
485
+ stack.add(moduleName);
486
+
487
+ const module = output.modules.find(m => m.name === moduleName);
488
+ if (module) {
489
+ module.imports.forEach(visit);
490
+ }
491
+
492
+ stack.delete(moduleName);
493
+ }
494
+
495
+ output.modules.forEach(m => visit(m.name));
496
+ return circular;
497
+ }
498
+ ```
499
+
500
+ ### Example 3: Custom Output Processing
501
+
502
+ ```typescript
503
+ import { JsonFormatter } from 'graph-analyzer';
504
+
505
+ interface DependencyReport {
506
+ totalProviders: number;
507
+ providersByType: Record<string, number>;
508
+ unusedProviders: string[];
509
+ globalModules: string[];
510
+ }
511
+
512
+ function generateReport(graph: Map<any, any>): DependencyReport {
513
+ const formatter = new JsonFormatter(graph, 'src/main.ts');
514
+ const output = formatter.format();
515
+
516
+ // Count providers by type
517
+ const providersByType: Record<string, number> = {};
518
+ output.providers.forEach(p => {
519
+ providersByType[p.type] = (providersByType[p.type] || 0) + 1;
520
+ });
521
+
522
+ // Find unused providers (not injected anywhere)
523
+ const usedTokens = new Set<string>();
524
+ output.providers.forEach(p => {
525
+ p.dependencies?.forEach(d => usedTokens.add(d.token));
526
+ });
527
+
528
+ const unusedProviders = output.providers
529
+ .map(p => p.token)
530
+ .filter(token => !usedTokens.has(token));
531
+
532
+ // Find global modules
533
+ const globalModules = output.modules
534
+ .filter(m => m.isGlobal)
535
+ .map(m => m.name);
536
+
537
+ return {
538
+ totalProviders: output.metadata.totalProviders,
539
+ providersByType,
540
+ unusedProviders,
541
+ globalModules
542
+ };
543
+ }
544
+ ```
545
+
546
+ ### Example 4: Integration with Build Tools
547
+
548
+ ```typescript
549
+ // webpack-plugin.ts
550
+ import { GraphAnalyzer } from 'graph-analyzer';
551
+
552
+ class DependencyGraphPlugin {
553
+ apply(compiler: any) {
554
+ compiler.hooks.emit.tapAsync(
555
+ 'DependencyGraphPlugin',
556
+ (compilation: any, callback: any) => {
557
+ // Analyze during build
558
+ const analyzer = new GraphAnalyzer(/* ... */);
559
+ const output = analyzer.parse();
560
+
561
+ // Add to build assets
562
+ compilation.assets['dependency-graph.json'] = {
563
+ source: () => JSON.stringify(output, null, 2),
564
+ size: () => JSON.stringify(output).length
565
+ };
566
+
567
+ callback();
568
+ }
569
+ );
570
+ }
571
+ }
572
+ ```
573
+
574
+ ### Example 5: Validation Rules
575
+
576
+ ```typescript
577
+ import { JsonFormatter, GraphOutput } from 'graph-analyzer';
578
+
579
+ interface ValidationRule {
580
+ name: string;
581
+ validate: (output: GraphOutput) => string[];
582
+ }
583
+
584
+ const rules: ValidationRule[] = [
585
+ {
586
+ name: 'No circular dependencies',
587
+ validate: (output) => {
588
+ // Implementation from Example 2
589
+ return [];
590
+ }
591
+ },
592
+ {
593
+ name: 'All providers used',
594
+ validate: (output) => {
595
+ const errors: string[] = [];
596
+ const usedTokens = new Set<string>();
597
+
598
+ output.providers.forEach(p => {
599
+ p.dependencies?.forEach(d => usedTokens.add(d.token));
600
+ });
601
+
602
+ output.providers.forEach(p => {
603
+ if (!usedTokens.has(p.token) && !output.modules.some(m => m.exports.includes(p.token))) {
604
+ errors.push(`Unused provider: ${p.token}`);
605
+ }
606
+ });
607
+
608
+ return errors;
609
+ }
610
+ },
611
+ {
612
+ name: 'No missing dependencies',
613
+ validate: (output) => {
614
+ const errors: string[] = [];
615
+ const availableTokens = new Set(output.providers.map(p => p.token));
616
+
617
+ output.providers.forEach(p => {
618
+ p.dependencies?.forEach(d => {
619
+ if (!d.optional && !availableTokens.has(d.token)) {
620
+ errors.push(`Missing dependency: ${d.token} required by ${p.token}`);
621
+ }
622
+ });
623
+ });
624
+
625
+ return errors;
626
+ }
627
+ }
628
+ ];
629
+
630
+ function validateGraph(graph: Map<any, any>): void {
631
+ const formatter = new JsonFormatter(graph, 'src/main.ts');
632
+ const output = formatter.format();
633
+
634
+ let hasErrors = false;
635
+
636
+ rules.forEach(rule => {
637
+ const errors = rule.validate(output);
638
+ if (errors.length > 0) {
639
+ console.error(`\n❌ ${rule.name}:`);
640
+ errors.forEach(err => console.error(` - ${err}`));
641
+ hasErrors = true;
642
+ } else {
643
+ console.log(`✅ ${rule.name}`);
644
+ }
645
+ });
646
+
647
+ if (hasErrors) {
648
+ process.exit(1);
649
+ }
650
+ }
651
+ ```
652
+
653
+ ## Advanced Topics
654
+
655
+ ### Custom Parsers
656
+
657
+ Extend the parser system for custom provider types:
658
+
659
+ ```typescript
660
+ import { ProviderParser } from 'graph-analyzer';
661
+
662
+ class CustomProviderParser extends ProviderParser {
663
+ parse(): Provider {
664
+ // Custom parsing logic
665
+ return {
666
+ token: 'CustomToken',
667
+ type: 'Custom',
668
+ // ...
669
+ };
670
+ }
671
+ }
672
+ ```
673
+
674
+ ### AST Manipulation
675
+
676
+ Work directly with TypeScript AST:
677
+
678
+ ```typescript
679
+ import * as ts from 'typescript';
680
+ import { tsquery } from '@phenomnomnominal/tsquery';
681
+
682
+ function findAllInjectDecorators(sourceFile: ts.SourceFile): ts.Decorator[] {
683
+ return tsquery(
684
+ sourceFile,
685
+ 'Decorator[expression.expression.name="Inject"]'
686
+ ) as ts.Decorator[];
687
+ }
688
+ ```
689
+
690
+ ### Performance Optimization
691
+
692
+ For large codebases:
693
+
694
+ ```typescript
695
+ // Cache parsed modules
696
+ const moduleCache = new Map<string, ParseNsModule>();
697
+
698
+ function getOrParseModule(path: string): ParseNsModule {
699
+ if (moduleCache.has(path)) {
700
+ return moduleCache.get(path)!;
701
+ }
702
+
703
+ const module = new ParseNsModule(/* ... */);
704
+ module.parse();
705
+ moduleCache.set(path, module);
706
+ return module;
707
+ }
708
+ ```
709
+
710
+ ## TypeScript Types
711
+
712
+ All types are exported and can be imported:
713
+
714
+ ```typescript
715
+ import type {
716
+ GraphOutput,
717
+ ModuleInfo,
718
+ ProviderInfo,
719
+ Dependency,
720
+ GraphMetadata,
721
+ GraphAnalyzerOptions
722
+ } from 'graph-analyzer';
723
+ ```
724
+
725
+ ## Error Handling
726
+
727
+ ```typescript
728
+ try {
729
+ const analyzer = new GraphAnalyzer(graph, entryPoint);
730
+ const output = analyzer.parse();
731
+ } catch (error) {
732
+ if (error.message.includes('Empty entry module')) {
733
+ console.error('No root module found in entry file');
734
+ } else if (error.message.includes('File not found')) {
735
+ console.error('Entry file does not exist');
736
+ } else {
737
+ console.error('Analysis failed:', error);
738
+ }
739
+ }
740
+ ```
741
+
742
+ ## See Also
743
+
744
+ - [README](../README.md) - Overview and quick start
745
+ - [CLI Guide](./CLI_GUIDE.md) - Command-line usage
746
+ - [Architecture](./ARCHITECTURE.md) - Technical details
747
+