driftdetect-core 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.
- package/dist/analyzers/ast-analyzer.d.ts +251 -0
- package/dist/analyzers/ast-analyzer.d.ts.map +1 -0
- package/dist/analyzers/ast-analyzer.js +548 -0
- package/dist/analyzers/ast-analyzer.js.map +1 -0
- package/dist/analyzers/flow-analyzer.d.ts +241 -0
- package/dist/analyzers/flow-analyzer.d.ts.map +1 -0
- package/dist/analyzers/flow-analyzer.js +1219 -0
- package/dist/analyzers/flow-analyzer.js.map +1 -0
- package/dist/analyzers/index.d.ts +18 -0
- package/dist/analyzers/index.d.ts.map +1 -0
- package/dist/analyzers/index.js +19 -0
- package/dist/analyzers/index.js.map +1 -0
- package/dist/analyzers/semantic-analyzer.d.ts +252 -0
- package/dist/analyzers/semantic-analyzer.d.ts.map +1 -0
- package/dist/analyzers/semantic-analyzer.js +1182 -0
- package/dist/analyzers/semantic-analyzer.js.map +1 -0
- package/dist/analyzers/type-analyzer.d.ts +289 -0
- package/dist/analyzers/type-analyzer.d.ts.map +1 -0
- package/dist/analyzers/type-analyzer.js +1269 -0
- package/dist/analyzers/type-analyzer.js.map +1 -0
- package/dist/analyzers/types.d.ts +537 -0
- package/dist/analyzers/types.d.ts.map +1 -0
- package/dist/analyzers/types.js +11 -0
- package/dist/analyzers/types.js.map +1 -0
- package/dist/config/config-loader.d.ts +166 -0
- package/dist/config/config-loader.d.ts.map +1 -0
- package/dist/config/config-loader.js +429 -0
- package/dist/config/config-loader.js.map +1 -0
- package/dist/config/config-validator.d.ts +204 -0
- package/dist/config/config-validator.d.ts.map +1 -0
- package/dist/config/config-validator.js +632 -0
- package/dist/config/config-validator.js.map +1 -0
- package/dist/config/defaults.d.ts +8 -0
- package/dist/config/defaults.d.ts.map +1 -0
- package/dist/config/defaults.js +26 -0
- package/dist/config/defaults.js.map +1 -0
- package/dist/config/index.d.ts +10 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +10 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/types.d.ts +47 -0
- package/dist/config/types.d.ts.map +1 -0
- package/dist/config/types.js +7 -0
- package/dist/config/types.js.map +1 -0
- package/dist/index.d.ts +37 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +39 -0
- package/dist/index.js.map +1 -0
- package/dist/manifest/exporter.d.ts +21 -0
- package/dist/manifest/exporter.d.ts.map +1 -0
- package/dist/manifest/exporter.js +339 -0
- package/dist/manifest/exporter.js.map +1 -0
- package/dist/manifest/index.d.ts +14 -0
- package/dist/manifest/index.d.ts.map +1 -0
- package/dist/manifest/index.js +15 -0
- package/dist/manifest/index.js.map +1 -0
- package/dist/manifest/manifest-store.d.ts +111 -0
- package/dist/manifest/manifest-store.d.ts.map +1 -0
- package/dist/manifest/manifest-store.js +418 -0
- package/dist/manifest/manifest-store.js.map +1 -0
- package/dist/manifest/types.d.ts +238 -0
- package/dist/manifest/types.d.ts.map +1 -0
- package/dist/manifest/types.js +11 -0
- package/dist/manifest/types.js.map +1 -0
- package/dist/matcher/confidence-scorer.d.ts +188 -0
- package/dist/matcher/confidence-scorer.d.ts.map +1 -0
- package/dist/matcher/confidence-scorer.js +302 -0
- package/dist/matcher/confidence-scorer.js.map +1 -0
- package/dist/matcher/index.d.ts +24 -0
- package/dist/matcher/index.d.ts.map +1 -0
- package/dist/matcher/index.js +26 -0
- package/dist/matcher/index.js.map +1 -0
- package/dist/matcher/outlier-detector.d.ts +252 -0
- package/dist/matcher/outlier-detector.d.ts.map +1 -0
- package/dist/matcher/outlier-detector.js +544 -0
- package/dist/matcher/outlier-detector.js.map +1 -0
- package/dist/matcher/pattern-matcher.d.ts +169 -0
- package/dist/matcher/pattern-matcher.d.ts.map +1 -0
- package/dist/matcher/pattern-matcher.js +692 -0
- package/dist/matcher/pattern-matcher.js.map +1 -0
- package/dist/matcher/types.d.ts +476 -0
- package/dist/matcher/types.d.ts.map +1 -0
- package/dist/matcher/types.js +36 -0
- package/dist/matcher/types.js.map +1 -0
- package/dist/parsers/base-parser.d.ts +282 -0
- package/dist/parsers/base-parser.d.ts.map +1 -0
- package/dist/parsers/base-parser.js +421 -0
- package/dist/parsers/base-parser.js.map +1 -0
- package/dist/parsers/css-parser.d.ts +225 -0
- package/dist/parsers/css-parser.d.ts.map +1 -0
- package/dist/parsers/css-parser.js +477 -0
- package/dist/parsers/css-parser.js.map +1 -0
- package/dist/parsers/index.d.ts +15 -0
- package/dist/parsers/index.d.ts.map +1 -0
- package/dist/parsers/index.js +15 -0
- package/dist/parsers/index.js.map +1 -0
- package/dist/parsers/json-parser.d.ts +219 -0
- package/dist/parsers/json-parser.d.ts.map +1 -0
- package/dist/parsers/json-parser.js +602 -0
- package/dist/parsers/json-parser.js.map +1 -0
- package/dist/parsers/markdown-parser.d.ts +276 -0
- package/dist/parsers/markdown-parser.d.ts.map +1 -0
- package/dist/parsers/markdown-parser.js +731 -0
- package/dist/parsers/markdown-parser.js.map +1 -0
- package/dist/parsers/parser-manager.d.ts +294 -0
- package/dist/parsers/parser-manager.d.ts.map +1 -0
- package/dist/parsers/parser-manager.js +738 -0
- package/dist/parsers/parser-manager.js.map +1 -0
- package/dist/parsers/python-parser.d.ts +204 -0
- package/dist/parsers/python-parser.d.ts.map +1 -0
- package/dist/parsers/python-parser.js +517 -0
- package/dist/parsers/python-parser.js.map +1 -0
- package/dist/parsers/types.d.ts +43 -0
- package/dist/parsers/types.d.ts.map +1 -0
- package/dist/parsers/types.js +7 -0
- package/dist/parsers/types.js.map +1 -0
- package/dist/parsers/typescript-parser.d.ts +264 -0
- package/dist/parsers/typescript-parser.d.ts.map +1 -0
- package/dist/parsers/typescript-parser.js +658 -0
- package/dist/parsers/typescript-parser.js.map +1 -0
- package/dist/rules/evaluator.d.ts +305 -0
- package/dist/rules/evaluator.d.ts.map +1 -0
- package/dist/rules/evaluator.js +579 -0
- package/dist/rules/evaluator.js.map +1 -0
- package/dist/rules/index.d.ts +13 -0
- package/dist/rules/index.d.ts.map +1 -0
- package/dist/rules/index.js +13 -0
- package/dist/rules/index.js.map +1 -0
- package/dist/rules/quick-fix-generator.d.ts +334 -0
- package/dist/rules/quick-fix-generator.d.ts.map +1 -0
- package/dist/rules/quick-fix-generator.js +1075 -0
- package/dist/rules/quick-fix-generator.js.map +1 -0
- package/dist/rules/rule-engine.d.ts +241 -0
- package/dist/rules/rule-engine.d.ts.map +1 -0
- package/dist/rules/rule-engine.js +585 -0
- package/dist/rules/rule-engine.js.map +1 -0
- package/dist/rules/severity-manager.d.ts +394 -0
- package/dist/rules/severity-manager.d.ts.map +1 -0
- package/dist/rules/severity-manager.js +619 -0
- package/dist/rules/severity-manager.js.map +1 -0
- package/dist/rules/types.d.ts +370 -0
- package/dist/rules/types.d.ts.map +1 -0
- package/dist/rules/types.js +133 -0
- package/dist/rules/types.js.map +1 -0
- package/dist/rules/variant-manager.d.ts +388 -0
- package/dist/rules/variant-manager.d.ts.map +1 -0
- package/dist/rules/variant-manager.js +777 -0
- package/dist/rules/variant-manager.js.map +1 -0
- package/dist/scanner/change-detector.d.ts +164 -0
- package/dist/scanner/change-detector.d.ts.map +1 -0
- package/dist/scanner/change-detector.js +263 -0
- package/dist/scanner/change-detector.js.map +1 -0
- package/dist/scanner/dependency-graph.d.ts +270 -0
- package/dist/scanner/dependency-graph.d.ts.map +1 -0
- package/dist/scanner/dependency-graph.js +436 -0
- package/dist/scanner/dependency-graph.js.map +1 -0
- package/dist/scanner/file-walker.d.ts +127 -0
- package/dist/scanner/file-walker.d.ts.map +1 -0
- package/dist/scanner/file-walker.js +526 -0
- package/dist/scanner/file-walker.js.map +1 -0
- package/dist/scanner/index.d.ts +12 -0
- package/dist/scanner/index.d.ts.map +1 -0
- package/dist/scanner/index.js +12 -0
- package/dist/scanner/index.js.map +1 -0
- package/dist/scanner/types.d.ts +218 -0
- package/dist/scanner/types.d.ts.map +1 -0
- package/dist/scanner/types.js +10 -0
- package/dist/scanner/types.js.map +1 -0
- package/dist/scanner/worker-pool.d.ts +317 -0
- package/dist/scanner/worker-pool.d.ts.map +1 -0
- package/dist/scanner/worker-pool.js +571 -0
- package/dist/scanner/worker-pool.js.map +1 -0
- package/dist/store/cache-manager.d.ts +179 -0
- package/dist/store/cache-manager.d.ts.map +1 -0
- package/dist/store/cache-manager.js +391 -0
- package/dist/store/cache-manager.js.map +1 -0
- package/dist/store/history-store.d.ts +314 -0
- package/dist/store/history-store.d.ts.map +1 -0
- package/dist/store/history-store.js +707 -0
- package/dist/store/history-store.js.map +1 -0
- package/dist/store/index.d.ts +20 -0
- package/dist/store/index.d.ts.map +1 -0
- package/dist/store/index.js +26 -0
- package/dist/store/index.js.map +1 -0
- package/dist/store/lock-file-manager.d.ts +202 -0
- package/dist/store/lock-file-manager.d.ts.map +1 -0
- package/dist/store/lock-file-manager.js +475 -0
- package/dist/store/lock-file-manager.js.map +1 -0
- package/dist/store/pattern-store.d.ts +289 -0
- package/dist/store/pattern-store.d.ts.map +1 -0
- package/dist/store/pattern-store.js +936 -0
- package/dist/store/pattern-store.js.map +1 -0
- package/dist/store/schema-validator.d.ts +159 -0
- package/dist/store/schema-validator.d.ts.map +1 -0
- package/dist/store/schema-validator.js +1096 -0
- package/dist/store/schema-validator.js.map +1 -0
- package/dist/store/types.d.ts +585 -0
- package/dist/store/types.d.ts.map +1 -0
- package/dist/store/types.js +82 -0
- package/dist/store/types.js.map +1 -0
- package/dist/types/analysis.d.ts +19 -0
- package/dist/types/analysis.d.ts.map +1 -0
- package/dist/types/analysis.js +5 -0
- package/dist/types/analysis.js.map +1 -0
- package/dist/types/common.d.ts +7 -0
- package/dist/types/common.d.ts.map +1 -0
- package/dist/types/common.js +5 -0
- package/dist/types/common.js.map +1 -0
- package/dist/types/index.d.ts +12 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +10 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/patterns.d.ts +40 -0
- package/dist/types/patterns.d.ts.map +1 -0
- package/dist/types/patterns.js +7 -0
- package/dist/types/patterns.js.map +1 -0
- package/dist/types/violations.d.ts +7 -0
- package/dist/types/violations.d.ts.map +1 -0
- package/dist/types/violations.js +7 -0
- package/dist/types/violations.js.map +1 -0
- package/package.json +46 -0
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dependency Graph - Import/export relationship tracking
|
|
3
|
+
*
|
|
4
|
+
* Builds and maintains a graph of module dependencies,
|
|
5
|
+
* enabling circular dependency detection and topological sorting.
|
|
6
|
+
*
|
|
7
|
+
* @requirements 2.3 - THE Scanner SHALL build and maintain a dependency graph of imports/exports
|
|
8
|
+
* @requirements 2.4 - THE Scanner SHALL detect circular dependencies and flag them
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Type of import statement
|
|
12
|
+
*/
|
|
13
|
+
export type ImportType = 'es-module' | 'commonjs' | 'dynamic' | 'type-only';
|
|
14
|
+
/**
|
|
15
|
+
* Type of export statement
|
|
16
|
+
*/
|
|
17
|
+
export type ExportType = 'named' | 'default' | 're-export' | 'namespace' | 'type-only';
|
|
18
|
+
/**
|
|
19
|
+
* Information about an import statement
|
|
20
|
+
*/
|
|
21
|
+
export interface ImportInfo {
|
|
22
|
+
/** The source module path (as written in the import statement) */
|
|
23
|
+
source: string;
|
|
24
|
+
/** Resolved absolute path to the imported module (if resolvable) */
|
|
25
|
+
resolvedPath?: string;
|
|
26
|
+
/** Imported specifiers (e.g., ['foo', 'bar'] for import { foo, bar }) */
|
|
27
|
+
specifiers: ImportSpecifier[];
|
|
28
|
+
/** Type of import */
|
|
29
|
+
type: ImportType;
|
|
30
|
+
/** Whether this is a side-effect only import (import './styles.css') */
|
|
31
|
+
sideEffectOnly: boolean;
|
|
32
|
+
/** Line number where the import appears */
|
|
33
|
+
line: number;
|
|
34
|
+
/** Column number where the import appears */
|
|
35
|
+
column: number;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Individual import specifier
|
|
39
|
+
*/
|
|
40
|
+
export interface ImportSpecifier {
|
|
41
|
+
/** Name as exported from the source module */
|
|
42
|
+
imported: string;
|
|
43
|
+
/** Local name in the importing module (may differ due to aliasing) */
|
|
44
|
+
local: string;
|
|
45
|
+
/** Whether this is a default import */
|
|
46
|
+
isDefault: boolean;
|
|
47
|
+
/** Whether this is a namespace import (import * as foo) */
|
|
48
|
+
isNamespace: boolean;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Information about an export statement
|
|
52
|
+
*/
|
|
53
|
+
export interface ExportInfo {
|
|
54
|
+
/** Name of the exported symbol */
|
|
55
|
+
name: string;
|
|
56
|
+
/** Type of export */
|
|
57
|
+
type: ExportType;
|
|
58
|
+
/** Source module for re-exports */
|
|
59
|
+
source?: string;
|
|
60
|
+
/** Original name if re-exported with alias */
|
|
61
|
+
originalName?: string;
|
|
62
|
+
/** Line number where the export appears */
|
|
63
|
+
line: number;
|
|
64
|
+
/** Column number where the export appears */
|
|
65
|
+
column: number;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Represents a module (file) in the dependency graph
|
|
69
|
+
*/
|
|
70
|
+
export interface ModuleNode {
|
|
71
|
+
/** Absolute path to the module */
|
|
72
|
+
path: string;
|
|
73
|
+
/** All imports in this module */
|
|
74
|
+
imports: ImportInfo[];
|
|
75
|
+
/** All exports from this module */
|
|
76
|
+
exports: ExportInfo[];
|
|
77
|
+
/** Paths of modules this module imports (dependencies) */
|
|
78
|
+
dependencies: Set<string>;
|
|
79
|
+
/** Paths of modules that import this module (dependents) */
|
|
80
|
+
dependents: Set<string>;
|
|
81
|
+
/** Whether this module has been fully analyzed */
|
|
82
|
+
analyzed: boolean;
|
|
83
|
+
/** Last modification time when analyzed */
|
|
84
|
+
analyzedAt?: Date;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Represents an edge in the dependency graph
|
|
88
|
+
*/
|
|
89
|
+
export interface DependencyEdge {
|
|
90
|
+
/** Source module (the one doing the importing) */
|
|
91
|
+
from: string;
|
|
92
|
+
/** Target module (the one being imported) */
|
|
93
|
+
to: string;
|
|
94
|
+
/** Import information for this edge */
|
|
95
|
+
importInfo: ImportInfo;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Result of circular dependency detection
|
|
99
|
+
*/
|
|
100
|
+
export interface CircularDependencyResult {
|
|
101
|
+
/** Whether any circular dependencies were found */
|
|
102
|
+
hasCircular: boolean;
|
|
103
|
+
/** All detected cycles (each cycle is an array of file paths) */
|
|
104
|
+
cycles: string[][];
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Options for dependency graph operations
|
|
108
|
+
*/
|
|
109
|
+
export interface DependencyGraphOptions {
|
|
110
|
+
/** Whether to track type-only imports separately */
|
|
111
|
+
trackTypeImports?: boolean;
|
|
112
|
+
/** Whether to include node_modules in the graph */
|
|
113
|
+
includeNodeModules?: boolean;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Dependency Graph class for tracking module relationships
|
|
117
|
+
*
|
|
118
|
+
* @requirements 2.3 - Build and maintain a dependency graph of imports/exports
|
|
119
|
+
* @requirements 2.4 - Detect circular dependencies and flag them
|
|
120
|
+
*/
|
|
121
|
+
export declare class DependencyGraph {
|
|
122
|
+
/** Map of module path to module node */
|
|
123
|
+
private modules;
|
|
124
|
+
/** All edges in the graph */
|
|
125
|
+
private edges;
|
|
126
|
+
/** Configuration options */
|
|
127
|
+
private options;
|
|
128
|
+
constructor(options?: DependencyGraphOptions);
|
|
129
|
+
/**
|
|
130
|
+
* Add a module to the dependency graph
|
|
131
|
+
*
|
|
132
|
+
* @param path - Absolute path to the module
|
|
133
|
+
* @param imports - Import statements in the module
|
|
134
|
+
* @param exports - Export statements in the module
|
|
135
|
+
*/
|
|
136
|
+
addModule(path: string, imports: ImportInfo[], exports: ExportInfo[]): void;
|
|
137
|
+
/**
|
|
138
|
+
* Remove a module from the dependency graph
|
|
139
|
+
*
|
|
140
|
+
* @param path - Absolute path to the module to remove
|
|
141
|
+
*/
|
|
142
|
+
removeModule(path: string): void;
|
|
143
|
+
/**
|
|
144
|
+
* Get all modules that this module imports (direct dependencies)
|
|
145
|
+
*
|
|
146
|
+
* @param path - Absolute path to the module
|
|
147
|
+
* @returns Array of paths to dependencies
|
|
148
|
+
*/
|
|
149
|
+
getDependencies(path: string): string[];
|
|
150
|
+
/**
|
|
151
|
+
* Get all modules that import this module (direct dependents)
|
|
152
|
+
*
|
|
153
|
+
* @param path - Absolute path to the module
|
|
154
|
+
* @returns Array of paths to dependents
|
|
155
|
+
*/
|
|
156
|
+
getDependents(path: string): string[];
|
|
157
|
+
/**
|
|
158
|
+
* Get all transitive dependencies of a module (recursive)
|
|
159
|
+
*
|
|
160
|
+
* @param path - Absolute path to the module
|
|
161
|
+
* @returns Array of all transitive dependency paths
|
|
162
|
+
*/
|
|
163
|
+
getTransitiveDependencies(path: string): string[];
|
|
164
|
+
/**
|
|
165
|
+
* Get all transitive dependents of a module (recursive)
|
|
166
|
+
*
|
|
167
|
+
* @param path - Absolute path to the module
|
|
168
|
+
* @returns Array of all transitive dependent paths
|
|
169
|
+
*/
|
|
170
|
+
getTransitiveDependents(path: string): string[];
|
|
171
|
+
/**
|
|
172
|
+
* Get modules in topological order (build order)
|
|
173
|
+
* Modules with no dependencies come first
|
|
174
|
+
*
|
|
175
|
+
* @returns Array of module paths in topological order
|
|
176
|
+
* @throws Error if the graph contains cycles
|
|
177
|
+
*/
|
|
178
|
+
getTopologicalOrder(): string[];
|
|
179
|
+
/**
|
|
180
|
+
* Check if the graph has any circular dependencies
|
|
181
|
+
*
|
|
182
|
+
* @returns true if circular dependencies exist
|
|
183
|
+
* @requirements 2.4
|
|
184
|
+
*/
|
|
185
|
+
hasCircularDependency(): boolean;
|
|
186
|
+
/**
|
|
187
|
+
* Get all circular dependencies in the graph
|
|
188
|
+
*
|
|
189
|
+
* @returns Array of cycles, where each cycle is an array of file paths
|
|
190
|
+
* @requirements 2.4
|
|
191
|
+
*/
|
|
192
|
+
getCircularDependencies(): string[][];
|
|
193
|
+
/**
|
|
194
|
+
* Get detailed circular dependency information
|
|
195
|
+
*
|
|
196
|
+
* @returns CircularDependencyResult with all cycles
|
|
197
|
+
* @requirements 2.4
|
|
198
|
+
*/
|
|
199
|
+
detectCircularDependencies(): CircularDependencyResult;
|
|
200
|
+
/**
|
|
201
|
+
* Get a module node by path
|
|
202
|
+
*
|
|
203
|
+
* @param path - Absolute path to the module
|
|
204
|
+
* @returns ModuleNode or undefined if not found
|
|
205
|
+
*/
|
|
206
|
+
getModule(path: string): ModuleNode | undefined;
|
|
207
|
+
/**
|
|
208
|
+
* Check if a module exists in the graph
|
|
209
|
+
*
|
|
210
|
+
* @param path - Absolute path to the module
|
|
211
|
+
* @returns true if the module exists
|
|
212
|
+
*/
|
|
213
|
+
hasModule(path: string): boolean;
|
|
214
|
+
/**
|
|
215
|
+
* Get all modules in the graph
|
|
216
|
+
*
|
|
217
|
+
* @returns Array of all module paths
|
|
218
|
+
*/
|
|
219
|
+
getAllModules(): string[];
|
|
220
|
+
/**
|
|
221
|
+
* Get all edges in the graph
|
|
222
|
+
*
|
|
223
|
+
* @returns Array of all dependency edges
|
|
224
|
+
*/
|
|
225
|
+
getAllEdges(): DependencyEdge[];
|
|
226
|
+
/**
|
|
227
|
+
* Get the number of modules in the graph
|
|
228
|
+
*/
|
|
229
|
+
get size(): number;
|
|
230
|
+
/**
|
|
231
|
+
* Get the number of edges in the graph
|
|
232
|
+
*/
|
|
233
|
+
get edgeCount(): number;
|
|
234
|
+
/**
|
|
235
|
+
* Clear all modules and edges from the graph
|
|
236
|
+
*/
|
|
237
|
+
clear(): void;
|
|
238
|
+
/**
|
|
239
|
+
* Get modules with no dependencies (leaf nodes)
|
|
240
|
+
*
|
|
241
|
+
* @returns Array of module paths with no dependencies
|
|
242
|
+
*/
|
|
243
|
+
getLeafModules(): string[];
|
|
244
|
+
/**
|
|
245
|
+
* Get modules with no dependents (root nodes)
|
|
246
|
+
*
|
|
247
|
+
* @returns Array of module paths with no dependents
|
|
248
|
+
*/
|
|
249
|
+
getRootModules(): string[];
|
|
250
|
+
/**
|
|
251
|
+
* Get modules that are part of any circular dependency
|
|
252
|
+
*
|
|
253
|
+
* @returns Array of module paths involved in cycles
|
|
254
|
+
*/
|
|
255
|
+
getModulesInCycles(): string[];
|
|
256
|
+
/**
|
|
257
|
+
* Check if a specific cycle already exists in the cycles array
|
|
258
|
+
* Cycles are considered equal if they contain the same nodes
|
|
259
|
+
*/
|
|
260
|
+
private cycleExists;
|
|
261
|
+
/**
|
|
262
|
+
* Normalize a file path for consistent comparison
|
|
263
|
+
*/
|
|
264
|
+
private normalizePath;
|
|
265
|
+
/**
|
|
266
|
+
* Check if an import source is a node_modules package
|
|
267
|
+
*/
|
|
268
|
+
private isNodeModule;
|
|
269
|
+
}
|
|
270
|
+
//# sourceMappingURL=dependency-graph.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dependency-graph.d.ts","sourceRoot":"","sources":["../../src/scanner/dependency-graph.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,436 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dependency Graph - Import/export relationship tracking
|
|
3
|
+
*
|
|
4
|
+
* Builds and maintains a graph of module dependencies,
|
|
5
|
+
* enabling circular dependency detection and topological sorting.
|
|
6
|
+
*
|
|
7
|
+
* @requirements 2.3 - THE Scanner SHALL build and maintain a dependency graph of imports/exports
|
|
8
|
+
* @requirements 2.4 - THE Scanner SHALL detect circular dependencies and flag them
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Dependency Graph class for tracking module relationships
|
|
12
|
+
*
|
|
13
|
+
* @requirements 2.3 - Build and maintain a dependency graph of imports/exports
|
|
14
|
+
* @requirements 2.4 - Detect circular dependencies and flag them
|
|
15
|
+
*/
|
|
16
|
+
export class DependencyGraph {
|
|
17
|
+
/** Map of module path to module node */
|
|
18
|
+
modules = new Map();
|
|
19
|
+
/** All edges in the graph */
|
|
20
|
+
edges = [];
|
|
21
|
+
/** Configuration options */
|
|
22
|
+
options;
|
|
23
|
+
constructor(options = {}) {
|
|
24
|
+
this.options = {
|
|
25
|
+
trackTypeImports: true,
|
|
26
|
+
includeNodeModules: false,
|
|
27
|
+
...options,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Add a module to the dependency graph
|
|
32
|
+
*
|
|
33
|
+
* @param path - Absolute path to the module
|
|
34
|
+
* @param imports - Import statements in the module
|
|
35
|
+
* @param exports - Export statements in the module
|
|
36
|
+
*/
|
|
37
|
+
addModule(path, imports, exports) {
|
|
38
|
+
const normalizedPath = this.normalizePath(path);
|
|
39
|
+
// Filter imports based on options
|
|
40
|
+
const filteredImports = imports.filter((imp) => {
|
|
41
|
+
if (!this.options.trackTypeImports && imp.type === 'type-only') {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
if (!this.options.includeNodeModules && this.isNodeModule(imp.source)) {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
return true;
|
|
48
|
+
});
|
|
49
|
+
// Create or update the module node
|
|
50
|
+
const existingNode = this.modules.get(normalizedPath);
|
|
51
|
+
const dependencies = new Set();
|
|
52
|
+
// Build dependency set from imports
|
|
53
|
+
for (const imp of filteredImports) {
|
|
54
|
+
if (imp.resolvedPath) {
|
|
55
|
+
const resolvedNormalized = this.normalizePath(imp.resolvedPath);
|
|
56
|
+
dependencies.add(resolvedNormalized);
|
|
57
|
+
// Add edge
|
|
58
|
+
this.edges.push({
|
|
59
|
+
from: normalizedPath,
|
|
60
|
+
to: resolvedNormalized,
|
|
61
|
+
importInfo: imp,
|
|
62
|
+
});
|
|
63
|
+
// Update the dependent's dependents set
|
|
64
|
+
const targetModule = this.modules.get(resolvedNormalized);
|
|
65
|
+
if (targetModule) {
|
|
66
|
+
targetModule.dependents.add(normalizedPath);
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
// Create a placeholder node for the target
|
|
70
|
+
this.modules.set(resolvedNormalized, {
|
|
71
|
+
path: resolvedNormalized,
|
|
72
|
+
imports: [],
|
|
73
|
+
exports: [],
|
|
74
|
+
dependencies: new Set(),
|
|
75
|
+
dependents: new Set([normalizedPath]),
|
|
76
|
+
analyzed: false,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
const moduleNode = {
|
|
82
|
+
path: normalizedPath,
|
|
83
|
+
imports: filteredImports,
|
|
84
|
+
exports,
|
|
85
|
+
dependencies,
|
|
86
|
+
dependents: existingNode?.dependents ?? new Set(),
|
|
87
|
+
analyzed: true,
|
|
88
|
+
analyzedAt: new Date(),
|
|
89
|
+
};
|
|
90
|
+
this.modules.set(normalizedPath, moduleNode);
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Remove a module from the dependency graph
|
|
94
|
+
*
|
|
95
|
+
* @param path - Absolute path to the module to remove
|
|
96
|
+
*/
|
|
97
|
+
removeModule(path) {
|
|
98
|
+
const normalizedPath = this.normalizePath(path);
|
|
99
|
+
const module = this.modules.get(normalizedPath);
|
|
100
|
+
if (!module) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
// Remove this module from all dependents' dependency sets
|
|
104
|
+
for (const dependent of module.dependents) {
|
|
105
|
+
const dependentModule = this.modules.get(dependent);
|
|
106
|
+
if (dependentModule) {
|
|
107
|
+
dependentModule.dependencies.delete(normalizedPath);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
// Remove this module from all dependencies' dependent sets
|
|
111
|
+
for (const dependency of module.dependencies) {
|
|
112
|
+
const dependencyModule = this.modules.get(dependency);
|
|
113
|
+
if (dependencyModule) {
|
|
114
|
+
dependencyModule.dependents.delete(normalizedPath);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
// Remove edges involving this module
|
|
118
|
+
this.edges = this.edges.filter((edge) => edge.from !== normalizedPath && edge.to !== normalizedPath);
|
|
119
|
+
// Remove the module
|
|
120
|
+
this.modules.delete(normalizedPath);
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Get all modules that this module imports (direct dependencies)
|
|
124
|
+
*
|
|
125
|
+
* @param path - Absolute path to the module
|
|
126
|
+
* @returns Array of paths to dependencies
|
|
127
|
+
*/
|
|
128
|
+
getDependencies(path) {
|
|
129
|
+
const normalizedPath = this.normalizePath(path);
|
|
130
|
+
const module = this.modules.get(normalizedPath);
|
|
131
|
+
if (!module) {
|
|
132
|
+
return [];
|
|
133
|
+
}
|
|
134
|
+
return Array.from(module.dependencies);
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Get all modules that import this module (direct dependents)
|
|
138
|
+
*
|
|
139
|
+
* @param path - Absolute path to the module
|
|
140
|
+
* @returns Array of paths to dependents
|
|
141
|
+
*/
|
|
142
|
+
getDependents(path) {
|
|
143
|
+
const normalizedPath = this.normalizePath(path);
|
|
144
|
+
const module = this.modules.get(normalizedPath);
|
|
145
|
+
if (!module) {
|
|
146
|
+
return [];
|
|
147
|
+
}
|
|
148
|
+
return Array.from(module.dependents);
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Get all transitive dependencies of a module (recursive)
|
|
152
|
+
*
|
|
153
|
+
* @param path - Absolute path to the module
|
|
154
|
+
* @returns Array of all transitive dependency paths
|
|
155
|
+
*/
|
|
156
|
+
getTransitiveDependencies(path) {
|
|
157
|
+
const normalizedPath = this.normalizePath(path);
|
|
158
|
+
const visited = new Set();
|
|
159
|
+
const result = [];
|
|
160
|
+
const visit = (modulePath) => {
|
|
161
|
+
if (visited.has(modulePath)) {
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
visited.add(modulePath);
|
|
165
|
+
const module = this.modules.get(modulePath);
|
|
166
|
+
if (!module) {
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
for (const dep of module.dependencies) {
|
|
170
|
+
if (dep !== normalizedPath) {
|
|
171
|
+
result.push(dep);
|
|
172
|
+
visit(dep);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
visit(normalizedPath);
|
|
177
|
+
return result;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Get all transitive dependents of a module (recursive)
|
|
181
|
+
*
|
|
182
|
+
* @param path - Absolute path to the module
|
|
183
|
+
* @returns Array of all transitive dependent paths
|
|
184
|
+
*/
|
|
185
|
+
getTransitiveDependents(path) {
|
|
186
|
+
const normalizedPath = this.normalizePath(path);
|
|
187
|
+
const visited = new Set();
|
|
188
|
+
const result = [];
|
|
189
|
+
const visit = (modulePath) => {
|
|
190
|
+
if (visited.has(modulePath)) {
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
visited.add(modulePath);
|
|
194
|
+
const module = this.modules.get(modulePath);
|
|
195
|
+
if (!module) {
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
for (const dependent of module.dependents) {
|
|
199
|
+
if (dependent !== normalizedPath) {
|
|
200
|
+
result.push(dependent);
|
|
201
|
+
visit(dependent);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
visit(normalizedPath);
|
|
206
|
+
return result;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Get modules in topological order (build order)
|
|
210
|
+
* Modules with no dependencies come first
|
|
211
|
+
*
|
|
212
|
+
* @returns Array of module paths in topological order
|
|
213
|
+
* @throws Error if the graph contains cycles
|
|
214
|
+
*/
|
|
215
|
+
getTopologicalOrder() {
|
|
216
|
+
const result = [];
|
|
217
|
+
const visited = new Set();
|
|
218
|
+
const visiting = new Set(); // For cycle detection
|
|
219
|
+
const visit = (path) => {
|
|
220
|
+
if (visited.has(path)) {
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
if (visiting.has(path)) {
|
|
224
|
+
throw new Error(`Circular dependency detected involving: ${path}`);
|
|
225
|
+
}
|
|
226
|
+
visiting.add(path);
|
|
227
|
+
const module = this.modules.get(path);
|
|
228
|
+
if (module) {
|
|
229
|
+
for (const dep of module.dependencies) {
|
|
230
|
+
visit(dep);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
visiting.delete(path);
|
|
234
|
+
visited.add(path);
|
|
235
|
+
result.push(path);
|
|
236
|
+
};
|
|
237
|
+
// Visit all modules
|
|
238
|
+
for (const path of this.modules.keys()) {
|
|
239
|
+
visit(path);
|
|
240
|
+
}
|
|
241
|
+
return result;
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Check if the graph has any circular dependencies
|
|
245
|
+
*
|
|
246
|
+
* @returns true if circular dependencies exist
|
|
247
|
+
* @requirements 2.4
|
|
248
|
+
*/
|
|
249
|
+
hasCircularDependency() {
|
|
250
|
+
return this.getCircularDependencies().length > 0;
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Get all circular dependencies in the graph
|
|
254
|
+
*
|
|
255
|
+
* @returns Array of cycles, where each cycle is an array of file paths
|
|
256
|
+
* @requirements 2.4
|
|
257
|
+
*/
|
|
258
|
+
getCircularDependencies() {
|
|
259
|
+
const cycles = [];
|
|
260
|
+
const visited = new Set();
|
|
261
|
+
const recursionStack = new Set();
|
|
262
|
+
const path = [];
|
|
263
|
+
const dfs = (node) => {
|
|
264
|
+
visited.add(node);
|
|
265
|
+
recursionStack.add(node);
|
|
266
|
+
path.push(node);
|
|
267
|
+
const module = this.modules.get(node);
|
|
268
|
+
if (module) {
|
|
269
|
+
for (const neighbor of module.dependencies) {
|
|
270
|
+
if (!visited.has(neighbor)) {
|
|
271
|
+
dfs(neighbor);
|
|
272
|
+
}
|
|
273
|
+
else if (recursionStack.has(neighbor)) {
|
|
274
|
+
// Found a cycle - extract it from the path
|
|
275
|
+
const cycleStart = path.indexOf(neighbor);
|
|
276
|
+
if (cycleStart !== -1) {
|
|
277
|
+
const cycle = [...path.slice(cycleStart), neighbor];
|
|
278
|
+
// Only add if this cycle hasn't been found before
|
|
279
|
+
if (!this.cycleExists(cycles, cycle)) {
|
|
280
|
+
cycles.push(cycle);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
path.pop();
|
|
287
|
+
recursionStack.delete(node);
|
|
288
|
+
};
|
|
289
|
+
// Run DFS from each unvisited node
|
|
290
|
+
for (const node of this.modules.keys()) {
|
|
291
|
+
if (!visited.has(node)) {
|
|
292
|
+
dfs(node);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
return cycles;
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Get detailed circular dependency information
|
|
299
|
+
*
|
|
300
|
+
* @returns CircularDependencyResult with all cycles
|
|
301
|
+
* @requirements 2.4
|
|
302
|
+
*/
|
|
303
|
+
detectCircularDependencies() {
|
|
304
|
+
const cycles = this.getCircularDependencies();
|
|
305
|
+
return {
|
|
306
|
+
hasCircular: cycles.length > 0,
|
|
307
|
+
cycles,
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Get a module node by path
|
|
312
|
+
*
|
|
313
|
+
* @param path - Absolute path to the module
|
|
314
|
+
* @returns ModuleNode or undefined if not found
|
|
315
|
+
*/
|
|
316
|
+
getModule(path) {
|
|
317
|
+
return this.modules.get(this.normalizePath(path));
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* Check if a module exists in the graph
|
|
321
|
+
*
|
|
322
|
+
* @param path - Absolute path to the module
|
|
323
|
+
* @returns true if the module exists
|
|
324
|
+
*/
|
|
325
|
+
hasModule(path) {
|
|
326
|
+
return this.modules.has(this.normalizePath(path));
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* Get all modules in the graph
|
|
330
|
+
*
|
|
331
|
+
* @returns Array of all module paths
|
|
332
|
+
*/
|
|
333
|
+
getAllModules() {
|
|
334
|
+
return Array.from(this.modules.keys());
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* Get all edges in the graph
|
|
338
|
+
*
|
|
339
|
+
* @returns Array of all dependency edges
|
|
340
|
+
*/
|
|
341
|
+
getAllEdges() {
|
|
342
|
+
return [...this.edges];
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* Get the number of modules in the graph
|
|
346
|
+
*/
|
|
347
|
+
get size() {
|
|
348
|
+
return this.modules.size;
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* Get the number of edges in the graph
|
|
352
|
+
*/
|
|
353
|
+
get edgeCount() {
|
|
354
|
+
return this.edges.length;
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* Clear all modules and edges from the graph
|
|
358
|
+
*/
|
|
359
|
+
clear() {
|
|
360
|
+
this.modules.clear();
|
|
361
|
+
this.edges = [];
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* Get modules with no dependencies (leaf nodes)
|
|
365
|
+
*
|
|
366
|
+
* @returns Array of module paths with no dependencies
|
|
367
|
+
*/
|
|
368
|
+
getLeafModules() {
|
|
369
|
+
const result = [];
|
|
370
|
+
for (const [path, module] of this.modules) {
|
|
371
|
+
if (module.dependencies.size === 0) {
|
|
372
|
+
result.push(path);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
return result;
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Get modules with no dependents (root nodes)
|
|
379
|
+
*
|
|
380
|
+
* @returns Array of module paths with no dependents
|
|
381
|
+
*/
|
|
382
|
+
getRootModules() {
|
|
383
|
+
const result = [];
|
|
384
|
+
for (const [path, module] of this.modules) {
|
|
385
|
+
if (module.dependents.size === 0) {
|
|
386
|
+
result.push(path);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
return result;
|
|
390
|
+
}
|
|
391
|
+
/**
|
|
392
|
+
* Get modules that are part of any circular dependency
|
|
393
|
+
*
|
|
394
|
+
* @returns Array of module paths involved in cycles
|
|
395
|
+
*/
|
|
396
|
+
getModulesInCycles() {
|
|
397
|
+
const cycles = this.getCircularDependencies();
|
|
398
|
+
const modulesInCycles = new Set();
|
|
399
|
+
for (const cycle of cycles) {
|
|
400
|
+
for (const module of cycle) {
|
|
401
|
+
modulesInCycles.add(module);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
return Array.from(modulesInCycles);
|
|
405
|
+
}
|
|
406
|
+
/**
|
|
407
|
+
* Check if a specific cycle already exists in the cycles array
|
|
408
|
+
* Cycles are considered equal if they contain the same nodes
|
|
409
|
+
*/
|
|
410
|
+
cycleExists(cycles, newCycle) {
|
|
411
|
+
const newCycleSet = new Set(newCycle.slice(0, -1)); // Exclude the repeated node at the end
|
|
412
|
+
for (const existingCycle of cycles) {
|
|
413
|
+
const existingSet = new Set(existingCycle.slice(0, -1));
|
|
414
|
+
if (existingSet.size === newCycleSet.size &&
|
|
415
|
+
[...newCycleSet].every((node) => existingSet.has(node))) {
|
|
416
|
+
return true;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
return false;
|
|
420
|
+
}
|
|
421
|
+
/**
|
|
422
|
+
* Normalize a file path for consistent comparison
|
|
423
|
+
*/
|
|
424
|
+
normalizePath(path) {
|
|
425
|
+
// Normalize path separators and remove trailing slashes
|
|
426
|
+
return path.replace(/\\/g, '/').replace(/\/+$/, '');
|
|
427
|
+
}
|
|
428
|
+
/**
|
|
429
|
+
* Check if an import source is a node_modules package
|
|
430
|
+
*/
|
|
431
|
+
isNodeModule(source) {
|
|
432
|
+
// Node modules don't start with . or /
|
|
433
|
+
return !source.startsWith('.') && !source.startsWith('/');
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
//# sourceMappingURL=dependency-graph.js.map
|