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,516 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dependency Graph Builder Tests
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
6
|
+
|
|
7
|
+
describe('DependencyGraph', () => {
|
|
8
|
+
describe('ES6 imports', () => {
|
|
9
|
+
it('parses default import', async () => {
|
|
10
|
+
const { DependencyGraph } = await import('./dependency-graph.js');
|
|
11
|
+
|
|
12
|
+
const graph = new DependencyGraph({
|
|
13
|
+
basePath: '/project',
|
|
14
|
+
fileExists: () => true,
|
|
15
|
+
readFile: vi.fn().mockImplementation((path) => {
|
|
16
|
+
if (path === '/project/src/index.js') {
|
|
17
|
+
return `import utils from './utils';`;
|
|
18
|
+
}
|
|
19
|
+
return '';
|
|
20
|
+
}),
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
await graph.build('/project/src/index.js');
|
|
24
|
+
const result = graph.getGraph();
|
|
25
|
+
|
|
26
|
+
expect(result.edges).toHaveLength(1);
|
|
27
|
+
expect(result.edges[0].toName).toContain('utils');
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('parses named imports', async () => {
|
|
31
|
+
const { DependencyGraph } = await import('./dependency-graph.js');
|
|
32
|
+
|
|
33
|
+
const graph = new DependencyGraph({
|
|
34
|
+
basePath: '/project',
|
|
35
|
+
fileExists: () => true,
|
|
36
|
+
readFile: vi.fn().mockImplementation((path) => {
|
|
37
|
+
if (path === '/project/src/index.js') {
|
|
38
|
+
return `import { foo, bar } from './helpers';`;
|
|
39
|
+
}
|
|
40
|
+
return '';
|
|
41
|
+
}),
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
await graph.build('/project/src/index.js');
|
|
45
|
+
const result = graph.getGraph();
|
|
46
|
+
|
|
47
|
+
expect(result.edges).toHaveLength(1);
|
|
48
|
+
expect(result.edges[0].toName).toContain('helpers');
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('parses namespace import', async () => {
|
|
52
|
+
const { DependencyGraph } = await import('./dependency-graph.js');
|
|
53
|
+
|
|
54
|
+
const graph = new DependencyGraph({
|
|
55
|
+
basePath: '/project',
|
|
56
|
+
fileExists: () => true,
|
|
57
|
+
readFile: vi.fn().mockImplementation((path) => {
|
|
58
|
+
if (path === '/project/src/index.js') {
|
|
59
|
+
return `import * as utils from './utils';`;
|
|
60
|
+
}
|
|
61
|
+
return '';
|
|
62
|
+
}),
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
await graph.build('/project/src/index.js');
|
|
66
|
+
const result = graph.getGraph();
|
|
67
|
+
|
|
68
|
+
expect(result.edges).toHaveLength(1);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('parses side-effect import', async () => {
|
|
72
|
+
const { DependencyGraph } = await import('./dependency-graph.js');
|
|
73
|
+
|
|
74
|
+
const graph = new DependencyGraph({
|
|
75
|
+
basePath: '/project',
|
|
76
|
+
fileExists: () => true,
|
|
77
|
+
readFile: vi.fn().mockImplementation((path) => {
|
|
78
|
+
if (path === '/project/src/index.js') {
|
|
79
|
+
return `import './polyfills';`;
|
|
80
|
+
}
|
|
81
|
+
return '';
|
|
82
|
+
}),
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
await graph.build('/project/src/index.js');
|
|
86
|
+
const result = graph.getGraph();
|
|
87
|
+
|
|
88
|
+
expect(result.edges).toHaveLength(1);
|
|
89
|
+
expect(result.edges[0].toName).toContain('polyfills');
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
describe('CommonJS requires', () => {
|
|
94
|
+
it('parses require statement', async () => {
|
|
95
|
+
const { DependencyGraph } = await import('./dependency-graph.js');
|
|
96
|
+
|
|
97
|
+
const graph = new DependencyGraph({
|
|
98
|
+
basePath: '/project',
|
|
99
|
+
fileExists: () => true,
|
|
100
|
+
readFile: vi.fn().mockImplementation((path) => {
|
|
101
|
+
if (path === '/project/src/index.js') {
|
|
102
|
+
return `const utils = require('./utils');`;
|
|
103
|
+
}
|
|
104
|
+
return '';
|
|
105
|
+
}),
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
await graph.build('/project/src/index.js');
|
|
109
|
+
const result = graph.getGraph();
|
|
110
|
+
|
|
111
|
+
expect(result.edges).toHaveLength(1);
|
|
112
|
+
expect(result.edges[0].toName).toContain('utils');
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('parses destructured require', async () => {
|
|
116
|
+
const { DependencyGraph } = await import('./dependency-graph.js');
|
|
117
|
+
|
|
118
|
+
const graph = new DependencyGraph({
|
|
119
|
+
basePath: '/project',
|
|
120
|
+
fileExists: () => true,
|
|
121
|
+
readFile: vi.fn().mockImplementation((path) => {
|
|
122
|
+
if (path === '/project/src/index.js') {
|
|
123
|
+
return `const { foo, bar } = require('./helpers');`;
|
|
124
|
+
}
|
|
125
|
+
return '';
|
|
126
|
+
}),
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
await graph.build('/project/src/index.js');
|
|
130
|
+
const result = graph.getGraph();
|
|
131
|
+
|
|
132
|
+
expect(result.edges).toHaveLength(1);
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
describe('dynamic imports', () => {
|
|
137
|
+
it('parses dynamic import', async () => {
|
|
138
|
+
const { DependencyGraph } = await import('./dependency-graph.js');
|
|
139
|
+
|
|
140
|
+
const graph = new DependencyGraph({
|
|
141
|
+
basePath: '/project',
|
|
142
|
+
fileExists: () => true,
|
|
143
|
+
readFile: vi.fn().mockImplementation((path) => {
|
|
144
|
+
if (path === '/project/src/index.js') {
|
|
145
|
+
return `const module = await import('./lazy');`;
|
|
146
|
+
}
|
|
147
|
+
return '';
|
|
148
|
+
}),
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
await graph.build('/project/src/index.js');
|
|
152
|
+
const result = graph.getGraph();
|
|
153
|
+
|
|
154
|
+
expect(result.edges).toHaveLength(1);
|
|
155
|
+
expect(result.edges[0].toName).toContain('lazy');
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
describe('path resolution', () => {
|
|
160
|
+
it('resolves relative paths', async () => {
|
|
161
|
+
const { DependencyGraph } = await import('./dependency-graph.js');
|
|
162
|
+
|
|
163
|
+
const graph = new DependencyGraph({
|
|
164
|
+
basePath: '/project',
|
|
165
|
+
fileExists: () => true,
|
|
166
|
+
readFile: vi.fn().mockImplementation((path) => {
|
|
167
|
+
if (path === '/project/src/components/Button.js') {
|
|
168
|
+
return `import utils from '../utils';`;
|
|
169
|
+
}
|
|
170
|
+
return '';
|
|
171
|
+
}),
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
await graph.build('/project/src/components/Button.js');
|
|
175
|
+
const result = graph.getGraph();
|
|
176
|
+
|
|
177
|
+
expect(result.edges[0].to).toBe('/project/src/utils.js');
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
it('marks node_modules as external', async () => {
|
|
181
|
+
const { DependencyGraph } = await import('./dependency-graph.js');
|
|
182
|
+
|
|
183
|
+
const graph = new DependencyGraph({
|
|
184
|
+
basePath: '/project',
|
|
185
|
+
fileExists: () => true,
|
|
186
|
+
readFile: vi.fn().mockImplementation((path) => {
|
|
187
|
+
if (path === '/project/src/index.js') {
|
|
188
|
+
return `
|
|
189
|
+
import React from 'react';
|
|
190
|
+
import lodash from 'lodash';
|
|
191
|
+
`;
|
|
192
|
+
}
|
|
193
|
+
return '';
|
|
194
|
+
}),
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
await graph.build('/project/src/index.js');
|
|
198
|
+
const result = graph.getGraph();
|
|
199
|
+
|
|
200
|
+
expect(result.external).toContain('react');
|
|
201
|
+
expect(result.external).toContain('lodash');
|
|
202
|
+
expect(result.edges).toHaveLength(0); // No internal edges
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
it('supports tsconfig paths', async () => {
|
|
206
|
+
const { DependencyGraph } = await import('./dependency-graph.js');
|
|
207
|
+
|
|
208
|
+
const graph = new DependencyGraph({
|
|
209
|
+
basePath: '/project',
|
|
210
|
+
fileExists: () => true,
|
|
211
|
+
tsConfigPaths: {
|
|
212
|
+
'@/*': ['src/*'],
|
|
213
|
+
'@utils/*': ['src/utils/*'],
|
|
214
|
+
},
|
|
215
|
+
readFile: vi.fn().mockImplementation((path) => {
|
|
216
|
+
if (path === '/project/src/index.js') {
|
|
217
|
+
return `import utils from '@/utils/helpers';`;
|
|
218
|
+
}
|
|
219
|
+
return '';
|
|
220
|
+
}),
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
await graph.build('/project/src/index.js');
|
|
224
|
+
const result = graph.getGraph();
|
|
225
|
+
|
|
226
|
+
expect(result.edges).toHaveLength(1);
|
|
227
|
+
expect(result.edges[0].to).toContain('src/utils/helpers');
|
|
228
|
+
});
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
describe('circular dependencies', () => {
|
|
232
|
+
it('handles circular references without infinite loop', async () => {
|
|
233
|
+
const { DependencyGraph } = await import('./dependency-graph.js');
|
|
234
|
+
|
|
235
|
+
const graph = new DependencyGraph({
|
|
236
|
+
basePath: '/project',
|
|
237
|
+
fileExists: () => true,
|
|
238
|
+
readFile: vi.fn().mockImplementation((path) => {
|
|
239
|
+
if (path === '/project/src/a.js') {
|
|
240
|
+
return `import b from './b';`;
|
|
241
|
+
}
|
|
242
|
+
if (path === '/project/src/b.js') {
|
|
243
|
+
return `import a from './a';`;
|
|
244
|
+
}
|
|
245
|
+
return '';
|
|
246
|
+
}),
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
// Should not hang
|
|
250
|
+
await graph.build('/project/src/a.js');
|
|
251
|
+
const result = graph.getGraph();
|
|
252
|
+
|
|
253
|
+
expect(result.nodes).toHaveLength(2);
|
|
254
|
+
expect(graph.hasCircular()).toBe(true);
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
it('detects circular dependency', async () => {
|
|
258
|
+
const { DependencyGraph } = await import('./dependency-graph.js');
|
|
259
|
+
|
|
260
|
+
const graph = new DependencyGraph({
|
|
261
|
+
basePath: '/project',
|
|
262
|
+
fileExists: () => true,
|
|
263
|
+
readFile: vi.fn().mockImplementation((path) => {
|
|
264
|
+
if (path === '/project/src/a.js') return `import './b';`;
|
|
265
|
+
if (path === '/project/src/b.js') return `import './c';`;
|
|
266
|
+
if (path === '/project/src/c.js') return `import './a';`;
|
|
267
|
+
return '';
|
|
268
|
+
}),
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
await graph.build('/project/src/a.js');
|
|
272
|
+
|
|
273
|
+
expect(graph.hasCircular()).toBe(true);
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
it('returns false when no circular deps', async () => {
|
|
277
|
+
const { DependencyGraph } = await import('./dependency-graph.js');
|
|
278
|
+
|
|
279
|
+
const graph = new DependencyGraph({
|
|
280
|
+
basePath: '/project',
|
|
281
|
+
fileExists: () => true,
|
|
282
|
+
readFile: vi.fn().mockImplementation((path) => {
|
|
283
|
+
if (path === '/project/src/a.js') return `import './b';`;
|
|
284
|
+
if (path === '/project/src/b.js') return `import './c';`;
|
|
285
|
+
if (path === '/project/src/c.js') return ``;
|
|
286
|
+
return '';
|
|
287
|
+
}),
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
await graph.build('/project/src/a.js');
|
|
291
|
+
|
|
292
|
+
expect(graph.hasCircular()).toBe(false);
|
|
293
|
+
});
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
describe('graph statistics', () => {
|
|
297
|
+
it('counts total files', async () => {
|
|
298
|
+
const { DependencyGraph } = await import('./dependency-graph.js');
|
|
299
|
+
|
|
300
|
+
const graph = new DependencyGraph({
|
|
301
|
+
basePath: '/project',
|
|
302
|
+
fileExists: () => true,
|
|
303
|
+
readFile: vi.fn().mockImplementation((path) => {
|
|
304
|
+
if (path === '/project/src/index.js') {
|
|
305
|
+
return `
|
|
306
|
+
import './a';
|
|
307
|
+
import './b';
|
|
308
|
+
`;
|
|
309
|
+
}
|
|
310
|
+
return '';
|
|
311
|
+
}),
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
await graph.build('/project/src/index.js');
|
|
315
|
+
const result = graph.getGraph();
|
|
316
|
+
|
|
317
|
+
expect(result.stats.totalFiles).toBe(3);
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
it('counts total edges', async () => {
|
|
321
|
+
const { DependencyGraph } = await import('./dependency-graph.js');
|
|
322
|
+
|
|
323
|
+
const graph = new DependencyGraph({
|
|
324
|
+
basePath: '/project',
|
|
325
|
+
fileExists: () => true,
|
|
326
|
+
readFile: vi.fn().mockImplementation((path) => {
|
|
327
|
+
if (path === '/project/src/index.js') {
|
|
328
|
+
return `
|
|
329
|
+
import './a';
|
|
330
|
+
import './b';
|
|
331
|
+
`;
|
|
332
|
+
}
|
|
333
|
+
if (path === '/project/src/a.js') {
|
|
334
|
+
return `import './b';`;
|
|
335
|
+
}
|
|
336
|
+
return '';
|
|
337
|
+
}),
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
await graph.build('/project/src/index.js');
|
|
341
|
+
const result = graph.getGraph();
|
|
342
|
+
|
|
343
|
+
expect(result.stats.totalEdges).toBe(3);
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
it('counts external dependencies', async () => {
|
|
347
|
+
const { DependencyGraph } = await import('./dependency-graph.js');
|
|
348
|
+
|
|
349
|
+
const graph = new DependencyGraph({
|
|
350
|
+
basePath: '/project',
|
|
351
|
+
fileExists: () => true,
|
|
352
|
+
readFile: vi.fn().mockImplementation((path) => {
|
|
353
|
+
if (path === '/project/src/index.js') {
|
|
354
|
+
return `
|
|
355
|
+
import React from 'react';
|
|
356
|
+
import express from 'express';
|
|
357
|
+
import lodash from 'lodash';
|
|
358
|
+
`;
|
|
359
|
+
}
|
|
360
|
+
return '';
|
|
361
|
+
}),
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
await graph.build('/project/src/index.js');
|
|
365
|
+
const result = graph.getGraph();
|
|
366
|
+
|
|
367
|
+
expect(result.stats.externalDeps).toBe(3);
|
|
368
|
+
});
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
describe('getImporters and getImports', () => {
|
|
372
|
+
it('returns files that import a given file', async () => {
|
|
373
|
+
const { DependencyGraph } = await import('./dependency-graph.js');
|
|
374
|
+
|
|
375
|
+
const graph = new DependencyGraph({
|
|
376
|
+
basePath: '/project',
|
|
377
|
+
fileExists: () => true,
|
|
378
|
+
readFile: vi.fn().mockImplementation((path) => {
|
|
379
|
+
if (path === '/project/src/index.js') return `import './utils';`;
|
|
380
|
+
if (path === '/project/src/app.js') return `import './utils';`;
|
|
381
|
+
return '';
|
|
382
|
+
}),
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
await graph.build('/project/src/index.js');
|
|
386
|
+
await graph.build('/project/src/app.js');
|
|
387
|
+
|
|
388
|
+
const importers = graph.getImporters('/project/src/utils.js');
|
|
389
|
+
expect(importers).toHaveLength(2);
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
it('returns files that a given file imports', async () => {
|
|
393
|
+
const { DependencyGraph } = await import('./dependency-graph.js');
|
|
394
|
+
|
|
395
|
+
const graph = new DependencyGraph({
|
|
396
|
+
basePath: '/project',
|
|
397
|
+
fileExists: () => true,
|
|
398
|
+
readFile: vi.fn().mockImplementation((path) => {
|
|
399
|
+
if (path === '/project/src/index.js') {
|
|
400
|
+
return `
|
|
401
|
+
import './a';
|
|
402
|
+
import './b';
|
|
403
|
+
import './c';
|
|
404
|
+
`;
|
|
405
|
+
}
|
|
406
|
+
return '';
|
|
407
|
+
}),
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
await graph.build('/project/src/index.js');
|
|
411
|
+
|
|
412
|
+
const imports = graph.getImports('/project/src/index.js');
|
|
413
|
+
expect(imports).toHaveLength(3);
|
|
414
|
+
});
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
describe('export from', () => {
|
|
418
|
+
it('parses export from statements', async () => {
|
|
419
|
+
const { DependencyGraph } = await import('./dependency-graph.js');
|
|
420
|
+
|
|
421
|
+
const graph = new DependencyGraph({
|
|
422
|
+
basePath: '/project',
|
|
423
|
+
fileExists: () => true,
|
|
424
|
+
readFile: vi.fn().mockImplementation((path) => {
|
|
425
|
+
if (path === '/project/src/index.js') {
|
|
426
|
+
return `export { foo, bar } from './utils';`;
|
|
427
|
+
}
|
|
428
|
+
return '';
|
|
429
|
+
}),
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
await graph.build('/project/src/index.js');
|
|
433
|
+
const result = graph.getGraph();
|
|
434
|
+
|
|
435
|
+
expect(result.edges).toHaveLength(1);
|
|
436
|
+
expect(result.edges[0].toName).toContain('utils');
|
|
437
|
+
});
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
describe('clear', () => {
|
|
441
|
+
it('clears the graph', async () => {
|
|
442
|
+
const { DependencyGraph } = await import('./dependency-graph.js');
|
|
443
|
+
|
|
444
|
+
const graph = new DependencyGraph({
|
|
445
|
+
basePath: '/project',
|
|
446
|
+
fileExists: () => true,
|
|
447
|
+
readFile: vi.fn().mockResolvedValue(`import './a';`),
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
await graph.build('/project/src/index.js');
|
|
451
|
+
expect(graph.getFiles().length).toBeGreaterThan(0);
|
|
452
|
+
|
|
453
|
+
graph.clear();
|
|
454
|
+
expect(graph.getFiles()).toHaveLength(0);
|
|
455
|
+
});
|
|
456
|
+
});
|
|
457
|
+
|
|
458
|
+
describe('directory scanning', () => {
|
|
459
|
+
it('finds all source files', async () => {
|
|
460
|
+
const { DependencyGraph } = await import('./dependency-graph.js');
|
|
461
|
+
|
|
462
|
+
const mockFiles = {
|
|
463
|
+
'/project/src': [
|
|
464
|
+
{ name: 'index.js', isDirectory: () => false },
|
|
465
|
+
{ name: 'utils', isDirectory: () => true },
|
|
466
|
+
],
|
|
467
|
+
'/project/src/utils': [
|
|
468
|
+
{ name: 'helpers.js', isDirectory: () => false },
|
|
469
|
+
{ name: 'format.ts', isDirectory: () => false },
|
|
470
|
+
],
|
|
471
|
+
};
|
|
472
|
+
|
|
473
|
+
const graph = new DependencyGraph({
|
|
474
|
+
basePath: '/project',
|
|
475
|
+
fileExists: () => true,
|
|
476
|
+
readDir: vi.fn().mockImplementation((dir) => {
|
|
477
|
+
return mockFiles[dir] || [];
|
|
478
|
+
}),
|
|
479
|
+
readFile: vi.fn().mockResolvedValue(''),
|
|
480
|
+
});
|
|
481
|
+
|
|
482
|
+
const files = await graph.findFiles('/project/src');
|
|
483
|
+
|
|
484
|
+
expect(files).toContain('/project/src/index.js');
|
|
485
|
+
expect(files).toContain('/project/src/utils/helpers.js');
|
|
486
|
+
expect(files).toContain('/project/src/utils/format.ts');
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
it('ignores node_modules', async () => {
|
|
490
|
+
const { DependencyGraph } = await import('./dependency-graph.js');
|
|
491
|
+
|
|
492
|
+
const mockFiles = {
|
|
493
|
+
'/project': [
|
|
494
|
+
{ name: 'src', isDirectory: () => true },
|
|
495
|
+
{ name: 'node_modules', isDirectory: () => true },
|
|
496
|
+
],
|
|
497
|
+
'/project/src': [
|
|
498
|
+
{ name: 'index.js', isDirectory: () => false },
|
|
499
|
+
],
|
|
500
|
+
'/project/node_modules': [
|
|
501
|
+
{ name: 'react', isDirectory: () => true },
|
|
502
|
+
],
|
|
503
|
+
};
|
|
504
|
+
|
|
505
|
+
const graph = new DependencyGraph({
|
|
506
|
+
basePath: '/project',
|
|
507
|
+
readDir: vi.fn().mockImplementation((dir) => mockFiles[dir] || []),
|
|
508
|
+
});
|
|
509
|
+
|
|
510
|
+
const files = await graph.findFiles('/project', ['node_modules']);
|
|
511
|
+
|
|
512
|
+
expect(files).toContain('/project/src/index.js');
|
|
513
|
+
expect(files.some(f => f.includes('node_modules'))).toBe(false);
|
|
514
|
+
});
|
|
515
|
+
});
|
|
516
|
+
});
|