tlc-claude-code 1.2.29 → 1.4.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/dashboard/dist/components/AuditPane.d.ts +30 -0
- package/dashboard/dist/components/AuditPane.js +127 -0
- package/dashboard/dist/components/AuditPane.test.d.ts +1 -0
- package/dashboard/dist/components/AuditPane.test.js +339 -0
- package/dashboard/dist/components/CompliancePane.d.ts +39 -0
- package/dashboard/dist/components/CompliancePane.js +96 -0
- package/dashboard/dist/components/CompliancePane.test.d.ts +1 -0
- package/dashboard/dist/components/CompliancePane.test.js +183 -0
- package/dashboard/dist/components/SSOPane.d.ts +36 -0
- package/dashboard/dist/components/SSOPane.js +71 -0
- package/dashboard/dist/components/SSOPane.test.d.ts +1 -0
- package/dashboard/dist/components/SSOPane.test.js +155 -0
- package/dashboard/dist/components/UsagePane.d.ts +13 -0
- package/dashboard/dist/components/UsagePane.js +51 -0
- package/dashboard/dist/components/UsagePane.test.d.ts +1 -0
- package/dashboard/dist/components/UsagePane.test.js +142 -0
- package/dashboard/dist/components/WorkspaceDocsPane.d.ts +19 -0
- package/dashboard/dist/components/WorkspaceDocsPane.js +130 -0
- package/dashboard/dist/components/WorkspaceDocsPane.test.d.ts +1 -0
- package/dashboard/dist/components/WorkspaceDocsPane.test.js +242 -0
- package/dashboard/dist/components/WorkspacePane.d.ts +18 -0
- package/dashboard/dist/components/WorkspacePane.js +17 -0
- package/dashboard/dist/components/WorkspacePane.test.d.ts +1 -0
- package/dashboard/dist/components/WorkspacePane.test.js +84 -0
- package/dashboard/dist/components/ZeroRetentionPane.d.ts +44 -0
- package/dashboard/dist/components/ZeroRetentionPane.js +83 -0
- package/dashboard/dist/components/ZeroRetentionPane.test.d.ts +1 -0
- package/dashboard/dist/components/ZeroRetentionPane.test.js +160 -0
- package/package.json +1 -1
- package/server/lib/access-control-doc.js +541 -0
- package/server/lib/access-control-doc.test.js +672 -0
- package/server/lib/adr-generator.js +423 -0
- package/server/lib/adr-generator.test.js +586 -0
- package/server/lib/agent-progress-monitor.js +223 -0
- package/server/lib/agent-progress-monitor.test.js +202 -0
- package/server/lib/architecture-command.js +450 -0
- package/server/lib/architecture-command.test.js +754 -0
- package/server/lib/ast-analyzer.js +324 -0
- package/server/lib/ast-analyzer.test.js +437 -0
- package/server/lib/audit-attribution.js +191 -0
- package/server/lib/audit-attribution.test.js +359 -0
- package/server/lib/audit-classifier.js +202 -0
- package/server/lib/audit-classifier.test.js +209 -0
- package/server/lib/audit-command.js +275 -0
- package/server/lib/audit-command.test.js +325 -0
- package/server/lib/audit-exporter.js +380 -0
- package/server/lib/audit-exporter.test.js +464 -0
- package/server/lib/audit-logger.js +236 -0
- package/server/lib/audit-logger.test.js +364 -0
- package/server/lib/audit-query.js +257 -0
- package/server/lib/audit-query.test.js +352 -0
- package/server/lib/audit-storage.js +269 -0
- package/server/lib/audit-storage.test.js +272 -0
- package/server/lib/auth-system.test.js +4 -1
- package/server/lib/boundary-detector.js +427 -0
- package/server/lib/boundary-detector.test.js +320 -0
- package/server/lib/budget-alerts.js +138 -0
- package/server/lib/budget-alerts.test.js +235 -0
- package/server/lib/bulk-repo-init.js +342 -0
- package/server/lib/bulk-repo-init.test.js +388 -0
- package/server/lib/candidates-tracker.js +210 -0
- package/server/lib/candidates-tracker.test.js +300 -0
- package/server/lib/checkpoint-manager.js +251 -0
- package/server/lib/checkpoint-manager.test.js +474 -0
- package/server/lib/circular-detector.js +337 -0
- package/server/lib/circular-detector.test.js +353 -0
- package/server/lib/cohesion-analyzer.js +310 -0
- package/server/lib/cohesion-analyzer.test.js +447 -0
- package/server/lib/compliance-checklist.js +866 -0
- package/server/lib/compliance-checklist.test.js +476 -0
- package/server/lib/compliance-command.js +616 -0
- package/server/lib/compliance-command.test.js +551 -0
- package/server/lib/compliance-reporter.js +692 -0
- package/server/lib/compliance-reporter.test.js +707 -0
- package/server/lib/contract-testing.js +625 -0
- package/server/lib/contract-testing.test.js +342 -0
- package/server/lib/conversion-planner.js +469 -0
- package/server/lib/conversion-planner.test.js +361 -0
- package/server/lib/convert-command.js +351 -0
- package/server/lib/convert-command.test.js +608 -0
- package/server/lib/coupling-calculator.js +189 -0
- package/server/lib/coupling-calculator.test.js +509 -0
- package/server/lib/data-flow-doc.js +665 -0
- package/server/lib/data-flow-doc.test.js +659 -0
- package/server/lib/dependency-graph.js +367 -0
- package/server/lib/dependency-graph.test.js +516 -0
- package/server/lib/duplication-detector.js +349 -0
- package/server/lib/duplication-detector.test.js +401 -0
- package/server/lib/ephemeral-storage.js +249 -0
- package/server/lib/ephemeral-storage.test.js +254 -0
- package/server/lib/evidence-collector.js +627 -0
- package/server/lib/evidence-collector.test.js +901 -0
- package/server/lib/example-service.js +616 -0
- package/server/lib/example-service.test.js +397 -0
- package/server/lib/flow-diagram-generator.js +474 -0
- package/server/lib/flow-diagram-generator.test.js +446 -0
- package/server/lib/idp-manager.js +626 -0
- package/server/lib/idp-manager.test.js +587 -0
- package/server/lib/impact-scorer.js +184 -0
- package/server/lib/impact-scorer.test.js +211 -0
- package/server/lib/memory-exclusion.js +326 -0
- package/server/lib/memory-exclusion.test.js +241 -0
- package/server/lib/mermaid-generator.js +358 -0
- package/server/lib/mermaid-generator.test.js +301 -0
- package/server/lib/messaging-patterns.js +750 -0
- package/server/lib/messaging-patterns.test.js +213 -0
- package/server/lib/mfa-handler.js +452 -0
- package/server/lib/mfa-handler.test.js +490 -0
- package/server/lib/microservice-template.js +386 -0
- package/server/lib/microservice-template.test.js +325 -0
- package/server/lib/new-project-microservice.js +450 -0
- package/server/lib/new-project-microservice.test.js +600 -0
- package/server/lib/oauth-flow.js +375 -0
- package/server/lib/oauth-flow.test.js +487 -0
- package/server/lib/oauth-registry.js +190 -0
- package/server/lib/oauth-registry.test.js +306 -0
- package/server/lib/readme-generator.js +490 -0
- package/server/lib/readme-generator.test.js +493 -0
- package/server/lib/refactor-command.js +326 -0
- package/server/lib/refactor-command.test.js +528 -0
- package/server/lib/refactor-executor.js +254 -0
- package/server/lib/refactor-executor.test.js +305 -0
- package/server/lib/refactor-observer.js +292 -0
- package/server/lib/refactor-observer.test.js +422 -0
- package/server/lib/refactor-progress.js +193 -0
- package/server/lib/refactor-progress.test.js +251 -0
- package/server/lib/refactor-reporter.js +237 -0
- package/server/lib/refactor-reporter.test.js +247 -0
- package/server/lib/repo-dependency-tracker.js +261 -0
- package/server/lib/repo-dependency-tracker.test.js +350 -0
- package/server/lib/retention-policy.js +281 -0
- package/server/lib/retention-policy.test.js +486 -0
- package/server/lib/role-mapper.js +236 -0
- package/server/lib/role-mapper.test.js +395 -0
- package/server/lib/saml-provider.js +765 -0
- package/server/lib/saml-provider.test.js +643 -0
- package/server/lib/security-policy-generator.js +682 -0
- package/server/lib/security-policy-generator.test.js +544 -0
- package/server/lib/semantic-analyzer.js +198 -0
- package/server/lib/semantic-analyzer.test.js +474 -0
- package/server/lib/sensitive-detector.js +112 -0
- package/server/lib/sensitive-detector.test.js +209 -0
- package/server/lib/service-interaction-diagram.js +700 -0
- package/server/lib/service-interaction-diagram.test.js +638 -0
- package/server/lib/service-scaffold.js +486 -0
- package/server/lib/service-scaffold.test.js +373 -0
- package/server/lib/service-summary.js +553 -0
- package/server/lib/service-summary.test.js +619 -0
- package/server/lib/session-purge.js +460 -0
- package/server/lib/session-purge.test.js +312 -0
- package/server/lib/shared-kernel.js +578 -0
- package/server/lib/shared-kernel.test.js +255 -0
- package/server/lib/sso-command.js +544 -0
- package/server/lib/sso-command.test.js +552 -0
- package/server/lib/sso-session.js +492 -0
- package/server/lib/sso-session.test.js +670 -0
- package/server/lib/traefik-config.js +282 -0
- package/server/lib/traefik-config.test.js +312 -0
- package/server/lib/usage-command.js +218 -0
- package/server/lib/usage-command.test.js +391 -0
- package/server/lib/usage-formatter.js +192 -0
- package/server/lib/usage-formatter.test.js +267 -0
- package/server/lib/usage-history.js +122 -0
- package/server/lib/usage-history.test.js +206 -0
- package/server/lib/workspace-command.js +249 -0
- package/server/lib/workspace-command.test.js +264 -0
- package/server/lib/workspace-config.js +270 -0
- package/server/lib/workspace-config.test.js +312 -0
- package/server/lib/workspace-docs-command.js +547 -0
- package/server/lib/workspace-docs-command.test.js +692 -0
- package/server/lib/workspace-memory.js +451 -0
- package/server/lib/workspace-memory.test.js +403 -0
- package/server/lib/workspace-scanner.js +452 -0
- package/server/lib/workspace-scanner.test.js +677 -0
- package/server/lib/workspace-test-runner.js +315 -0
- package/server/lib/workspace-test-runner.test.js +294 -0
- package/server/lib/zero-retention-command.js +439 -0
- package/server/lib/zero-retention-command.test.js +448 -0
- package/server/lib/zero-retention.js +322 -0
- package/server/lib/zero-retention.test.js +258 -0
- package/server/package-lock.json +14 -0
- package/server/package.json +1 -0
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dependency Graph Builder
|
|
3
|
+
* Build file dependency graph from import/require statements
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const path = require('path');
|
|
7
|
+
const fs = require('fs');
|
|
8
|
+
|
|
9
|
+
class DependencyGraph {
|
|
10
|
+
constructor(options = {}) {
|
|
11
|
+
this.options = options;
|
|
12
|
+
this.basePath = options.basePath || process.cwd();
|
|
13
|
+
this.extensions = options.extensions || ['.js', '.ts', '.jsx', '.tsx', '.mjs'];
|
|
14
|
+
this.tsConfigPaths = options.tsConfigPaths || {};
|
|
15
|
+
this.graph = new Map(); // file -> { imports: [], importedBy: [] }
|
|
16
|
+
this.external = new Set(); // node_modules dependencies
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Build dependency graph from entry points or directory
|
|
21
|
+
*/
|
|
22
|
+
async build(entryPoints) {
|
|
23
|
+
const files = Array.isArray(entryPoints) ? entryPoints : [entryPoints];
|
|
24
|
+
const visited = new Set();
|
|
25
|
+
|
|
26
|
+
for (const file of files) {
|
|
27
|
+
await this.processFile(file, visited);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return this.getGraph();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Build graph from all files in a directory
|
|
35
|
+
*/
|
|
36
|
+
async buildFromDirectory(dir, options = {}) {
|
|
37
|
+
const { ignore = ['node_modules', '.git', 'dist', 'build'] } = options;
|
|
38
|
+
const files = await this.findFiles(dir, ignore);
|
|
39
|
+
|
|
40
|
+
for (const file of files) {
|
|
41
|
+
await this.processFile(file, new Set());
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return this.getGraph();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Find all source files in directory
|
|
49
|
+
*/
|
|
50
|
+
async findFiles(dir, ignore = []) {
|
|
51
|
+
const results = [];
|
|
52
|
+
const readDir = this.options.readDir || fs.promises.readdir;
|
|
53
|
+
const stat = this.options.stat || fs.promises.stat;
|
|
54
|
+
|
|
55
|
+
const scan = async (currentDir) => {
|
|
56
|
+
const entries = await readDir(currentDir, { withFileTypes: true });
|
|
57
|
+
|
|
58
|
+
for (const entry of entries) {
|
|
59
|
+
const fullPath = path.join(currentDir, entry.name);
|
|
60
|
+
|
|
61
|
+
if (ignore.some(pattern => fullPath.includes(pattern))) {
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (entry.isDirectory()) {
|
|
66
|
+
await scan(fullPath);
|
|
67
|
+
} else if (this.extensions.some(ext => entry.name.endsWith(ext))) {
|
|
68
|
+
results.push(fullPath);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
await scan(dir);
|
|
74
|
+
return results;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Process a single file
|
|
79
|
+
*/
|
|
80
|
+
async processFile(filePath, visited) {
|
|
81
|
+
const absolutePath = path.isAbsolute(filePath)
|
|
82
|
+
? filePath
|
|
83
|
+
: path.resolve(this.basePath, filePath);
|
|
84
|
+
|
|
85
|
+
if (visited.has(absolutePath)) {
|
|
86
|
+
return; // Prevent infinite loops on circular deps
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
visited.add(absolutePath);
|
|
90
|
+
|
|
91
|
+
if (!this.graph.has(absolutePath)) {
|
|
92
|
+
this.graph.set(absolutePath, { imports: [], importedBy: [] });
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
try {
|
|
96
|
+
const content = await this.readFile(absolutePath);
|
|
97
|
+
const imports = this.parseImports(content, absolutePath);
|
|
98
|
+
|
|
99
|
+
for (const imp of imports) {
|
|
100
|
+
const resolved = this.resolveImport(imp, absolutePath);
|
|
101
|
+
|
|
102
|
+
if (resolved.external) {
|
|
103
|
+
this.external.add(resolved.module);
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (resolved.path) {
|
|
108
|
+
// Add to graph
|
|
109
|
+
this.graph.get(absolutePath).imports.push(resolved.path);
|
|
110
|
+
|
|
111
|
+
if (!this.graph.has(resolved.path)) {
|
|
112
|
+
this.graph.set(resolved.path, { imports: [], importedBy: [] });
|
|
113
|
+
}
|
|
114
|
+
this.graph.get(resolved.path).importedBy.push(absolutePath);
|
|
115
|
+
|
|
116
|
+
// Recursively process
|
|
117
|
+
await this.processFile(resolved.path, visited);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
} catch (error) {
|
|
121
|
+
// File doesn't exist or can't be read
|
|
122
|
+
if (this.options.verbose) {
|
|
123
|
+
console.error(`Error processing ${absolutePath}:`, error.message);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Read file content
|
|
130
|
+
*/
|
|
131
|
+
async readFile(filePath) {
|
|
132
|
+
const readFile = this.options.readFile || fs.promises.readFile;
|
|
133
|
+
return await readFile(filePath, 'utf-8');
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Parse import statements from code
|
|
138
|
+
*/
|
|
139
|
+
parseImports(code, filePath) {
|
|
140
|
+
const imports = [];
|
|
141
|
+
|
|
142
|
+
// ES6 imports: import x from 'y', import { x } from 'y', import 'y'
|
|
143
|
+
const es6Regex = /import\s+(?:(?:[\w*{}\s,]+)\s+from\s+)?['"]([^'"]+)['"]/g;
|
|
144
|
+
let match;
|
|
145
|
+
while ((match = es6Regex.exec(code)) !== null) {
|
|
146
|
+
imports.push({ type: 'es6', module: match[1], raw: match[0] });
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// CommonJS: require('x'), require("x")
|
|
150
|
+
const cjsRegex = /require\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
|
|
151
|
+
while ((match = cjsRegex.exec(code)) !== null) {
|
|
152
|
+
imports.push({ type: 'commonjs', module: match[1], raw: match[0] });
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Dynamic imports: import('x')
|
|
156
|
+
const dynamicRegex = /import\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
|
|
157
|
+
while ((match = dynamicRegex.exec(code)) !== null) {
|
|
158
|
+
imports.push({ type: 'dynamic', module: match[1], raw: match[0] });
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Export from: export { x } from 'y'
|
|
162
|
+
const exportFromRegex = /export\s+(?:[\w*{}\s,]+)\s+from\s+['"]([^'"]+)['"]/g;
|
|
163
|
+
while ((match = exportFromRegex.exec(code)) !== null) {
|
|
164
|
+
imports.push({ type: 'export-from', module: match[1], raw: match[0] });
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return imports;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Resolve import path to absolute path
|
|
172
|
+
*/
|
|
173
|
+
resolveImport(imp, fromFile) {
|
|
174
|
+
const modulePath = imp.module;
|
|
175
|
+
|
|
176
|
+
// Check if external (node_modules)
|
|
177
|
+
if (!modulePath.startsWith('.') && !modulePath.startsWith('/')) {
|
|
178
|
+
// Check tsconfig paths first
|
|
179
|
+
const aliasResolved = this.resolveAlias(modulePath);
|
|
180
|
+
if (aliasResolved) {
|
|
181
|
+
return { path: aliasResolved, external: false };
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return { module: modulePath.split('/')[0], external: true };
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Relative path
|
|
188
|
+
const fromDir = path.dirname(fromFile);
|
|
189
|
+
let resolved = path.resolve(fromDir, modulePath);
|
|
190
|
+
|
|
191
|
+
// Try adding extensions
|
|
192
|
+
resolved = this.resolveExtension(resolved);
|
|
193
|
+
|
|
194
|
+
return { path: resolved, external: false };
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Resolve TypeScript path aliases
|
|
199
|
+
*/
|
|
200
|
+
resolveAlias(modulePath) {
|
|
201
|
+
for (const [alias, paths] of Object.entries(this.tsConfigPaths)) {
|
|
202
|
+
const pattern = alias.replace('*', '(.*)');
|
|
203
|
+
const regex = new RegExp(`^${pattern}$`);
|
|
204
|
+
const match = modulePath.match(regex);
|
|
205
|
+
|
|
206
|
+
if (match) {
|
|
207
|
+
const replacement = paths[0].replace('*', match[1] || '');
|
|
208
|
+
return path.resolve(this.basePath, replacement);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Try resolving with different extensions
|
|
216
|
+
*/
|
|
217
|
+
resolveExtension(filePath) {
|
|
218
|
+
// Already has extension
|
|
219
|
+
if (this.extensions.some(ext => filePath.endsWith(ext))) {
|
|
220
|
+
return filePath;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Try each extension
|
|
224
|
+
for (const ext of this.extensions) {
|
|
225
|
+
const withExt = filePath + ext;
|
|
226
|
+
if (this.fileExists(withExt)) {
|
|
227
|
+
return withExt;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Try index files
|
|
232
|
+
for (const ext of this.extensions) {
|
|
233
|
+
const indexFile = path.join(filePath, `index${ext}`);
|
|
234
|
+
if (this.fileExists(indexFile)) {
|
|
235
|
+
return indexFile;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Return original with first extension as fallback
|
|
240
|
+
return filePath + this.extensions[0];
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Check if file exists
|
|
245
|
+
*/
|
|
246
|
+
fileExists(filePath) {
|
|
247
|
+
if (this.options.fileExists) {
|
|
248
|
+
return this.options.fileExists(filePath);
|
|
249
|
+
}
|
|
250
|
+
try {
|
|
251
|
+
fs.accessSync(filePath);
|
|
252
|
+
return true;
|
|
253
|
+
} catch {
|
|
254
|
+
return false;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Get the built graph
|
|
260
|
+
*/
|
|
261
|
+
getGraph() {
|
|
262
|
+
const nodes = [];
|
|
263
|
+
const edges = [];
|
|
264
|
+
|
|
265
|
+
for (const [file, data] of this.graph.entries()) {
|
|
266
|
+
nodes.push({
|
|
267
|
+
id: file,
|
|
268
|
+
name: path.relative(this.basePath, file),
|
|
269
|
+
imports: data.imports.length,
|
|
270
|
+
importedBy: data.importedBy.length,
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
for (const imp of data.imports) {
|
|
274
|
+
edges.push({
|
|
275
|
+
from: file,
|
|
276
|
+
to: imp,
|
|
277
|
+
fromName: path.relative(this.basePath, file),
|
|
278
|
+
toName: path.relative(this.basePath, imp),
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
return {
|
|
284
|
+
nodes,
|
|
285
|
+
edges,
|
|
286
|
+
external: Array.from(this.external),
|
|
287
|
+
stats: {
|
|
288
|
+
totalFiles: nodes.length,
|
|
289
|
+
totalEdges: edges.length,
|
|
290
|
+
externalDeps: this.external.size,
|
|
291
|
+
},
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Get files that import a given file
|
|
297
|
+
*/
|
|
298
|
+
getImporters(filePath) {
|
|
299
|
+
const absolute = path.isAbsolute(filePath)
|
|
300
|
+
? filePath
|
|
301
|
+
: path.resolve(this.basePath, filePath);
|
|
302
|
+
|
|
303
|
+
const node = this.graph.get(absolute);
|
|
304
|
+
return node ? node.importedBy : [];
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Get files that a given file imports
|
|
309
|
+
*/
|
|
310
|
+
getImports(filePath) {
|
|
311
|
+
const absolute = path.isAbsolute(filePath)
|
|
312
|
+
? filePath
|
|
313
|
+
: path.resolve(this.basePath, filePath);
|
|
314
|
+
|
|
315
|
+
const node = this.graph.get(absolute);
|
|
316
|
+
return node ? node.imports : [];
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Check if graph has circular dependencies
|
|
321
|
+
*/
|
|
322
|
+
hasCircular() {
|
|
323
|
+
const visited = new Set();
|
|
324
|
+
const stack = new Set();
|
|
325
|
+
|
|
326
|
+
const dfs = (node) => {
|
|
327
|
+
if (stack.has(node)) return true;
|
|
328
|
+
if (visited.has(node)) return false;
|
|
329
|
+
|
|
330
|
+
visited.add(node);
|
|
331
|
+
stack.add(node);
|
|
332
|
+
|
|
333
|
+
const data = this.graph.get(node);
|
|
334
|
+
if (data) {
|
|
335
|
+
for (const imp of data.imports) {
|
|
336
|
+
if (dfs(imp)) return true;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
stack.delete(node);
|
|
341
|
+
return false;
|
|
342
|
+
};
|
|
343
|
+
|
|
344
|
+
for (const node of this.graph.keys()) {
|
|
345
|
+
if (dfs(node)) return true;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
return false;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* Get all files in the graph
|
|
353
|
+
*/
|
|
354
|
+
getFiles() {
|
|
355
|
+
return Array.from(this.graph.keys());
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Clear the graph
|
|
360
|
+
*/
|
|
361
|
+
clear() {
|
|
362
|
+
this.graph.clear();
|
|
363
|
+
this.external.clear();
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
module.exports = { DependencyGraph };
|