@ts-stack/cycle-detector 1.0.2 → 1.0.3
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/index.js +253 -90
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
- package/src/index.ts +269 -92
package/dist/index.js
CHANGED
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
import fs from 'node:fs';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import ts from 'typescript';
|
|
5
|
+
// Global shared structures for maximum performance
|
|
5
6
|
const graph = new Map();
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
*/
|
|
7
|
+
const allUniqueCycles = [];
|
|
8
|
+
const globalDetectedCycles = new Set();
|
|
9
|
+
const packageMetaCache = new Map();
|
|
10
|
+
const compilerOptionsCache = new Map();
|
|
11
|
+
let globalProjectPath;
|
|
12
12
|
function parseArgs() {
|
|
13
13
|
const args = [...process.argv.slice(2)];
|
|
14
14
|
let projectPath;
|
|
@@ -24,10 +24,6 @@ function parseArgs() {
|
|
|
24
24
|
}
|
|
25
25
|
return { entryPatterns, projectPath };
|
|
26
26
|
}
|
|
27
|
-
/**
|
|
28
|
-
* Checks if an import/export declaration is type-only.
|
|
29
|
-
* Type-only imports are stripped during compilation and don't cause runtime cycles.
|
|
30
|
-
*/
|
|
31
27
|
function isRuntimeImport(node) {
|
|
32
28
|
if (ts.isExportDeclaration(node)) {
|
|
33
29
|
if (!node.moduleSpecifier)
|
|
@@ -36,10 +32,9 @@ function isRuntimeImport(node) {
|
|
|
36
32
|
}
|
|
37
33
|
if (ts.isImportDeclaration(node)) {
|
|
38
34
|
if (!node.importClause)
|
|
39
|
-
return true;
|
|
40
|
-
if (node.importClause.
|
|
41
|
-
return false;
|
|
42
|
-
// Check individual specifiers: import { type A, B } from './foo'
|
|
35
|
+
return true;
|
|
36
|
+
if (node.importClause.phaseModifier)
|
|
37
|
+
return false;
|
|
43
38
|
const namedBindings = node.importClause.namedBindings;
|
|
44
39
|
if (namedBindings && ts.isNamedImports(namedBindings)) {
|
|
45
40
|
return !namedBindings.elements.every((el) => el.isTypeOnly);
|
|
@@ -48,92 +43,199 @@ function isRuntimeImport(node) {
|
|
|
48
43
|
}
|
|
49
44
|
return false;
|
|
50
45
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
const result = ts.resolveModuleName(moduleName, containingFile, options, ts.sys);
|
|
57
|
-
if (result.resolvedModule && !result.resolvedModule.isExternalLibraryImport) {
|
|
58
|
-
return result.resolvedModule.resolvedFileName;
|
|
46
|
+
function getCompilerOptionsForFile(filePath) {
|
|
47
|
+
const currentDir = path.dirname(filePath);
|
|
48
|
+
const cachedOptions = compilerOptionsCache.get(currentDir);
|
|
49
|
+
if (cachedOptions !== undefined) {
|
|
50
|
+
return cachedOptions;
|
|
59
51
|
}
|
|
60
|
-
|
|
52
|
+
const configPath = ts.findConfigFile(currentDir, ts.sys.fileExists, 'tsconfig.json') || globalProjectPath;
|
|
53
|
+
if (configPath) {
|
|
54
|
+
const resolvedConfigPath = path.resolve(configPath);
|
|
55
|
+
const configDir = path.dirname(resolvedConfigPath);
|
|
56
|
+
const cachedConfig = compilerOptionsCache.get(resolvedConfigPath);
|
|
57
|
+
if (cachedConfig !== undefined) {
|
|
58
|
+
compilerOptionsCache.set(currentDir, cachedConfig);
|
|
59
|
+
return cachedConfig;
|
|
60
|
+
}
|
|
61
|
+
try {
|
|
62
|
+
const configFile = ts.readConfigFile(resolvedConfigPath, ts.sys.readFile);
|
|
63
|
+
const parsedConfig = ts.parseJsonConfigFileContent(configFile.config, ts.sys, configDir);
|
|
64
|
+
const options = parsedConfig.options || {};
|
|
65
|
+
compilerOptionsCache.set(resolvedConfigPath, options);
|
|
66
|
+
compilerOptionsCache.set(currentDir, options);
|
|
67
|
+
return options;
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
// Fallback
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return {};
|
|
61
74
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
const sourceFile = ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, true);
|
|
87
|
-
const imports = [];
|
|
88
|
-
function walk(node) {
|
|
89
|
-
if (ts.isImportDeclaration(node) || ts.isExportDeclaration(node)) {
|
|
90
|
-
if (isRuntimeImport(node)) {
|
|
91
|
-
const specifier = node.moduleSpecifier;
|
|
92
|
-
if (specifier && ts.isStringLiteral(specifier)) {
|
|
93
|
-
const resolved = resolveModule(specifier.text, filePath, compilerOptions);
|
|
94
|
-
if (resolved && !imports.includes(resolved))
|
|
95
|
-
imports.push(resolved);
|
|
75
|
+
function getPackageMeta(filePath) {
|
|
76
|
+
let currentDir = path.dirname(filePath);
|
|
77
|
+
const visitedDirs = [];
|
|
78
|
+
while (currentDir && currentDir !== path.parse(currentDir).root) {
|
|
79
|
+
const cached = packageMetaCache.get(currentDir);
|
|
80
|
+
if (cached !== undefined) {
|
|
81
|
+
for (const d of visitedDirs)
|
|
82
|
+
packageMetaCache.set(d, cached);
|
|
83
|
+
return cached;
|
|
84
|
+
}
|
|
85
|
+
const pkgJsonPath = path.join(currentDir, 'package.json');
|
|
86
|
+
if (fs.existsSync(pkgJsonPath)) {
|
|
87
|
+
try {
|
|
88
|
+
const content = fs.readFileSync(pkgJsonPath, 'utf8');
|
|
89
|
+
const pkg = JSON.parse(content);
|
|
90
|
+
let outDirName = 'dist';
|
|
91
|
+
const mainField = pkg.main || pkg.types || pkg.typings || '';
|
|
92
|
+
if (mainField) {
|
|
93
|
+
const parts = path.normalize(mainField).split(path.sep);
|
|
94
|
+
if (parts.length > 1 && parts[0] !== '.' && parts[0] !== '..') {
|
|
95
|
+
outDirName = parts[0];
|
|
96
|
+
}
|
|
97
|
+
else if (parts.length > 2 && (parts[0] === '.' || parts[0] === '..')) {
|
|
98
|
+
outDirName = parts[1];
|
|
96
99
|
}
|
|
97
100
|
}
|
|
101
|
+
let srcDirName = 'src';
|
|
102
|
+
if (fs.existsSync(path.join(currentDir, 'source')))
|
|
103
|
+
srcDirName = 'source';
|
|
104
|
+
else if (fs.existsSync(path.join(currentDir, 'lib')))
|
|
105
|
+
srcDirName = 'lib';
|
|
106
|
+
const meta = { pkgDir: currentDir, srcDirName, outDirName };
|
|
107
|
+
packageMetaCache.set(currentDir, meta);
|
|
108
|
+
for (const d of visitedDirs)
|
|
109
|
+
packageMetaCache.set(d, meta);
|
|
110
|
+
return meta;
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
packageMetaCache.set(currentDir, null);
|
|
114
|
+
for (const d of visitedDirs)
|
|
115
|
+
packageMetaCache.set(d, null);
|
|
116
|
+
return null;
|
|
98
117
|
}
|
|
99
|
-
ts.forEachChild(node, walk);
|
|
100
118
|
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
for (const dep of imports)
|
|
104
|
-
parseFile(dep);
|
|
119
|
+
visitedDirs.push(currentDir);
|
|
120
|
+
currentDir = path.dirname(currentDir);
|
|
105
121
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
function resolveModule(moduleName, containingFile, options) {
|
|
125
|
+
const result = ts.resolveModuleName(moduleName, containingFile, options, ts.sys);
|
|
126
|
+
if (!result.resolvedModule)
|
|
127
|
+
return null;
|
|
128
|
+
const resolvedFileName = path.resolve(result.resolvedModule.resolvedFileName);
|
|
129
|
+
if (resolvedFileName.includes(`${path.sep}node_modules${path.sep}`)) {
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
const meta = getPackageMeta(resolvedFileName);
|
|
133
|
+
if (meta) {
|
|
134
|
+
const { pkgDir, srcDirName, outDirName } = meta;
|
|
135
|
+
const srcDirPath = path.join(pkgDir, srcDirName);
|
|
136
|
+
if (resolvedFileName.startsWith(srcDirPath + path.sep)) {
|
|
137
|
+
return resolvedFileName;
|
|
138
|
+
}
|
|
139
|
+
const outDirPath = path.join(pkgDir, outDirName);
|
|
140
|
+
if (resolvedFileName.startsWith(outDirPath + path.sep) || resolvedFileName === outDirPath) {
|
|
141
|
+
const relativeToOut = path.relative(outDirPath, resolvedFileName);
|
|
142
|
+
let baseName = relativeToOut;
|
|
143
|
+
if (baseName.endsWith('.d.ts'))
|
|
144
|
+
baseName = baseName.slice(0, -5);
|
|
145
|
+
else if (baseName.endsWith('.d.mts'))
|
|
146
|
+
baseName = baseName.slice(0, -6);
|
|
147
|
+
else if (baseName.endsWith('.d.cts'))
|
|
148
|
+
baseName = baseName.slice(0, -6);
|
|
149
|
+
else if (baseName.endsWith('.js'))
|
|
150
|
+
baseName = baseName.slice(0, -3);
|
|
151
|
+
else if (baseName.endsWith('.mjs'))
|
|
152
|
+
baseName = baseName.slice(0, -4);
|
|
153
|
+
else if (baseName.endsWith('.cjs'))
|
|
154
|
+
baseName = baseName.slice(0, -4);
|
|
155
|
+
else if (baseName.endsWith('.jsx'))
|
|
156
|
+
baseName = baseName.slice(0, -4);
|
|
157
|
+
const extensions = ['.ts', '.tsx', '.mts', '.cts'];
|
|
158
|
+
for (const ext of extensions) {
|
|
159
|
+
const targetSrcFile = path.join(srcDirPath, baseName + ext);
|
|
160
|
+
if (fs.existsSync(targetSrcFile)) {
|
|
161
|
+
return targetSrcFile;
|
|
162
|
+
}
|
|
117
163
|
}
|
|
118
|
-
|
|
119
|
-
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
if (result.resolvedModule.isExternalLibraryImport) {
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
return resolvedFileName;
|
|
170
|
+
}
|
|
171
|
+
function getCanonicalCycleKey(cycle) {
|
|
172
|
+
const nodes = cycle.slice(0, -1);
|
|
173
|
+
if (nodes.length === 0)
|
|
174
|
+
return '';
|
|
175
|
+
let minIdx = 0;
|
|
176
|
+
for (let i = 1; i < nodes.length; i++) {
|
|
177
|
+
if (nodes[i] < nodes[minIdx]) {
|
|
178
|
+
minIdx = i;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
const rotated = [...nodes.slice(minIdx), ...nodes.slice(0, minIdx)];
|
|
182
|
+
rotated.push(rotated[0]);
|
|
183
|
+
return rotated.join('|');
|
|
184
|
+
}
|
|
185
|
+
function parseFile(filePath) {
|
|
186
|
+
if (graph.has(filePath) || !fs.existsSync(filePath))
|
|
187
|
+
return;
|
|
188
|
+
graph.set(filePath, []);
|
|
189
|
+
const options = getCompilerOptionsForFile(filePath);
|
|
190
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
191
|
+
const sourceFile = ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, true);
|
|
192
|
+
const imports = [];
|
|
193
|
+
function walk(node) {
|
|
194
|
+
if (ts.isImportDeclaration(node) || ts.isExportDeclaration(node)) {
|
|
195
|
+
if (isRuntimeImport(node)) {
|
|
196
|
+
const specifier = node.moduleSpecifier;
|
|
197
|
+
if (specifier && ts.isStringLiteral(specifier)) {
|
|
198
|
+
const resolved = resolveModule(specifier.text, filePath, options);
|
|
199
|
+
if (resolved && !imports.includes(resolved))
|
|
200
|
+
imports.push(resolved);
|
|
201
|
+
}
|
|
120
202
|
}
|
|
121
203
|
}
|
|
122
|
-
|
|
123
|
-
visited.set(node, 'VISITED');
|
|
204
|
+
ts.forEachChild(node, walk);
|
|
124
205
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
206
|
+
walk(sourceFile);
|
|
207
|
+
graph.set(filePath, imports);
|
|
208
|
+
for (const dep of imports)
|
|
209
|
+
parseFile(dep);
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Checks if a specific target file is reachable from a starting entry point in the graph.
|
|
213
|
+
*/
|
|
214
|
+
function canReach(start, target) {
|
|
215
|
+
const seen = new Set();
|
|
216
|
+
const stack = [start];
|
|
217
|
+
while (stack.length > 0) {
|
|
218
|
+
const current = stack.pop();
|
|
219
|
+
if (current === target)
|
|
220
|
+
return true;
|
|
221
|
+
if (seen.has(current))
|
|
222
|
+
continue;
|
|
223
|
+
seen.add(current);
|
|
224
|
+
const deps = graph.get(current) || [];
|
|
225
|
+
for (const dep of deps) {
|
|
226
|
+
stack.push(dep);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
return false;
|
|
128
230
|
}
|
|
129
231
|
function main() {
|
|
130
232
|
const { entryPatterns, projectPath } = parseArgs();
|
|
131
|
-
|
|
132
|
-
|
|
233
|
+
globalProjectPath = projectPath;
|
|
234
|
+
if (entryPatterns.length === 0) {
|
|
235
|
+
console.error('❌ Error: Please specify at least one entry point or glob pattern.');
|
|
133
236
|
process.exit(1);
|
|
134
237
|
}
|
|
135
238
|
const entryPoints = [];
|
|
136
|
-
// Expand glob patterns or fall back to direct paths
|
|
137
239
|
for (const pattern of entryPatterns) {
|
|
138
240
|
const matches = fs.globSync ? fs.globSync(pattern) : [pattern];
|
|
139
241
|
for (const match of matches) {
|
|
@@ -152,25 +254,86 @@ function main() {
|
|
|
152
254
|
console.error('❌ Error: No entry files found matching the provided paths or patterns.');
|
|
153
255
|
process.exit(1);
|
|
154
256
|
}
|
|
155
|
-
console.log(`🔍 Found ${entryPoints.length} entry point(s) for analysis...\n`);
|
|
257
|
+
console.log(`🔍 Found ${entryPoints.length} entry point(s) for analysis. Building graph...\n`);
|
|
258
|
+
// Phase 1: Deep parse all files globally across all entry points
|
|
259
|
+
for (const entryPoint of entryPoints) {
|
|
260
|
+
parseFile(entryPoint);
|
|
261
|
+
}
|
|
262
|
+
// Phase 2: Traverse the global graph to discover all unique cycles
|
|
263
|
+
const visited = new Map();
|
|
264
|
+
const currentStack = [];
|
|
265
|
+
function findCycles(node) {
|
|
266
|
+
visited.set(node, 'VISITING');
|
|
267
|
+
currentStack.push(node);
|
|
268
|
+
for (const neighbor of graph.get(node) || []) {
|
|
269
|
+
const state = visited.get(neighbor);
|
|
270
|
+
if (state === 'VISITING') {
|
|
271
|
+
const startIdx = currentStack.indexOf(neighbor);
|
|
272
|
+
const cycle = currentStack.slice(startIdx);
|
|
273
|
+
cycle.push(neighbor);
|
|
274
|
+
const key = getCanonicalCycleKey(cycle);
|
|
275
|
+
if (!globalDetectedCycles.has(key)) {
|
|
276
|
+
globalDetectedCycles.add(key);
|
|
277
|
+
allUniqueCycles.push(cycle);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
else if (!state) {
|
|
281
|
+
findCycles(neighbor);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
currentStack.pop();
|
|
285
|
+
visited.set(node, 'VISITED');
|
|
286
|
+
}
|
|
287
|
+
for (const entryPoint of entryPoints) {
|
|
288
|
+
if (!visited.has(entryPoint)) {
|
|
289
|
+
findCycles(entryPoint);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
// Phase 3: Intelligently distribute and attribute cycles to their native entry points
|
|
293
|
+
const entryPointCycles = new Map();
|
|
294
|
+
for (const ep of entryPoints) {
|
|
295
|
+
entryPointCycles.set(ep, []);
|
|
296
|
+
}
|
|
297
|
+
for (const cycle of allUniqueCycles) {
|
|
298
|
+
const firstFile = cycle[0];
|
|
299
|
+
// Find the entrypoint whose source folder directly hosts this file
|
|
300
|
+
const matchedEp = entryPoints.find((ep) => {
|
|
301
|
+
const epDir = path.dirname(ep);
|
|
302
|
+
return firstFile.startsWith(epDir + path.sep) || firstFile === ep;
|
|
303
|
+
});
|
|
304
|
+
if (matchedEp) {
|
|
305
|
+
entryPointCycles.get(matchedEp).push(cycle);
|
|
306
|
+
}
|
|
307
|
+
else {
|
|
308
|
+
// Cross-package cycle or edge case: assign to the first entry point that can reach it
|
|
309
|
+
const reachingEp = entryPoints.find((ep) => canReach(ep, firstFile));
|
|
310
|
+
if (reachingEp) {
|
|
311
|
+
entryPointCycles.get(reachingEp).push(cycle);
|
|
312
|
+
}
|
|
313
|
+
else {
|
|
314
|
+
entryPointCycles.get(entryPoints[0]).push(cycle);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
// Phase 4: Output the clean, perfectly targeted report
|
|
156
319
|
let globalHasCycles = false;
|
|
157
320
|
for (const entryPoint of entryPoints) {
|
|
158
|
-
const
|
|
159
|
-
const cycles =
|
|
321
|
+
const absoluteEntry = path.resolve(entryPoint);
|
|
322
|
+
const cycles = entryPointCycles.get(entryPoint) || [];
|
|
160
323
|
if (cycles.length > 0) {
|
|
161
324
|
globalHasCycles = true;
|
|
162
|
-
console.error(`❌ [${
|
|
325
|
+
console.error(`❌ [${absoluteEntry}] — Found ${cycles.length} circular dependencies:`);
|
|
163
326
|
cycles.forEach((cycle, index) => {
|
|
164
|
-
const readableCycle = cycle.map((p) => path.
|
|
327
|
+
const readableCycle = cycle.map((p) => path.resolve(p)).join('\n -> ');
|
|
165
328
|
console.error(` ${index + 1}) ${readableCycle}`);
|
|
166
329
|
});
|
|
167
330
|
console.error('');
|
|
168
331
|
}
|
|
169
332
|
else {
|
|
170
|
-
console.log(`✅ [${
|
|
333
|
+
console.log(`✅ [${absoluteEntry}] — Clean!`);
|
|
171
334
|
}
|
|
172
335
|
}
|
|
173
|
-
if (globalHasCycles) {
|
|
336
|
+
if (globalHasCycles || globalDetectedCycles.size > 0) {
|
|
174
337
|
console.error('💥 Validation failed. Circular dependencies detected.');
|
|
175
338
|
process.exit(1);
|
|
176
339
|
}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ts-stack/cycle-detector",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.3",
|
|
5
5
|
"bin": {
|
|
6
6
|
"cycle-detector": "./dist/index.js"
|
|
7
7
|
},
|
|
@@ -45,6 +45,7 @@
|
|
|
45
45
|
"nodemon": "^3.1.14",
|
|
46
46
|
"prettier": "^3.8.4",
|
|
47
47
|
"rimraf": "^5.0.10",
|
|
48
|
+
"ts-node": "^10.9.2",
|
|
48
49
|
"typescript": "^5.9.3",
|
|
49
50
|
"typescript-eslint": "^8.62.0"
|
|
50
51
|
},
|
package/src/index.ts
CHANGED
|
@@ -6,14 +6,16 @@ import ts from 'typescript';
|
|
|
6
6
|
|
|
7
7
|
type NodeState = 'VISITING' | 'VISITED';
|
|
8
8
|
|
|
9
|
+
// Global shared structures for maximum performance
|
|
9
10
|
const graph = new Map<string, string[]>();
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
|
|
11
|
+
const allUniqueCycles: string[][] = [];
|
|
12
|
+
const globalDetectedCycles = new Set<string>();
|
|
13
|
+
|
|
14
|
+
const packageMetaCache = new Map<string, { pkgDir: string; srcDirName: string; outDirName: string; } | null>();
|
|
15
|
+
const compilerOptionsCache = new Map<string, ts.CompilerOptions>();
|
|
16
|
+
|
|
17
|
+
let globalProjectPath: string | undefined;
|
|
13
18
|
|
|
14
|
-
/**
|
|
15
|
-
* Parses command line arguments to separate options from entry point patterns.
|
|
16
|
-
*/
|
|
17
19
|
function parseArgs() {
|
|
18
20
|
const args = [...process.argv.slice(2)];
|
|
19
21
|
let projectPath: string | undefined;
|
|
@@ -31,10 +33,6 @@ function parseArgs() {
|
|
|
31
33
|
return { entryPatterns, projectPath };
|
|
32
34
|
}
|
|
33
35
|
|
|
34
|
-
/**
|
|
35
|
-
* Checks if an import/export declaration is type-only.
|
|
36
|
-
* Type-only imports are stripped during compilation and don't cause runtime cycles.
|
|
37
|
-
*/
|
|
38
36
|
function isRuntimeImport(node: ts.ImportDeclaration | ts.ExportDeclaration): boolean {
|
|
39
37
|
if (ts.isExportDeclaration(node)) {
|
|
40
38
|
if (!node.moduleSpecifier) return false;
|
|
@@ -42,10 +40,9 @@ function isRuntimeImport(node: ts.ImportDeclaration | ts.ExportDeclaration): boo
|
|
|
42
40
|
}
|
|
43
41
|
|
|
44
42
|
if (ts.isImportDeclaration(node)) {
|
|
45
|
-
if (!node.importClause) return true;
|
|
46
|
-
if (node.importClause.
|
|
43
|
+
if (!node.importClause) return true;
|
|
44
|
+
if (node.importClause.phaseModifier) return false;
|
|
47
45
|
|
|
48
|
-
// Check individual specifiers: import { type A, B } from './foo'
|
|
49
46
|
const namedBindings = node.importClause.namedBindings;
|
|
50
47
|
if (namedBindings && ts.isNamedImports(namedBindings)) {
|
|
51
48
|
return !namedBindings.elements.every((el) => el.isTypeOnly);
|
|
@@ -56,106 +53,217 @@ function isRuntimeImport(node: ts.ImportDeclaration | ts.ExportDeclaration): boo
|
|
|
56
53
|
return false;
|
|
57
54
|
}
|
|
58
55
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
if (result.resolvedModule && !result.resolvedModule.isExternalLibraryImport) {
|
|
66
|
-
return result.resolvedModule.resolvedFileName;
|
|
56
|
+
function getCompilerOptionsForFile(filePath: string): ts.CompilerOptions {
|
|
57
|
+
const currentDir = path.dirname(filePath);
|
|
58
|
+
|
|
59
|
+
const cachedOptions = compilerOptionsCache.get(currentDir);
|
|
60
|
+
if (cachedOptions !== undefined) {
|
|
61
|
+
return cachedOptions;
|
|
67
62
|
}
|
|
68
|
-
|
|
63
|
+
|
|
64
|
+
const configPath = ts.findConfigFile(currentDir, ts.sys.fileExists, 'tsconfig.json') || globalProjectPath;
|
|
65
|
+
|
|
66
|
+
if (configPath) {
|
|
67
|
+
const resolvedConfigPath = path.resolve(configPath);
|
|
68
|
+
const configDir = path.dirname(resolvedConfigPath);
|
|
69
|
+
|
|
70
|
+
const cachedConfig = compilerOptionsCache.get(resolvedConfigPath);
|
|
71
|
+
if (cachedConfig !== undefined) {
|
|
72
|
+
compilerOptionsCache.set(currentDir, cachedConfig);
|
|
73
|
+
return cachedConfig;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
try {
|
|
77
|
+
const configFile = ts.readConfigFile(resolvedConfigPath, ts.sys.readFile);
|
|
78
|
+
const parsedConfig = ts.parseJsonConfigFileContent(configFile.config, ts.sys, configDir);
|
|
79
|
+
const options = parsedConfig.options || {};
|
|
80
|
+
|
|
81
|
+
compilerOptionsCache.set(resolvedConfigPath, options);
|
|
82
|
+
compilerOptionsCache.set(currentDir, options);
|
|
83
|
+
return options;
|
|
84
|
+
} catch {
|
|
85
|
+
// Fallback
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return {};
|
|
69
90
|
}
|
|
70
91
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
graph.set(filePath, []);
|
|
97
|
-
|
|
98
|
-
const content = fs.readFileSync(filePath, 'utf8');
|
|
99
|
-
const sourceFile = ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, true);
|
|
100
|
-
const imports: string[] = [];
|
|
101
|
-
|
|
102
|
-
function walk(node: ts.Node) {
|
|
103
|
-
if (ts.isImportDeclaration(node) || ts.isExportDeclaration(node)) {
|
|
104
|
-
if (isRuntimeImport(node)) {
|
|
105
|
-
const specifier = node.moduleSpecifier;
|
|
106
|
-
if (specifier && ts.isStringLiteral(specifier)) {
|
|
107
|
-
const resolved = resolveModule(specifier.text, filePath, compilerOptions);
|
|
108
|
-
if (resolved && !imports.includes(resolved)) imports.push(resolved);
|
|
92
|
+
function getPackageMeta(filePath: string) {
|
|
93
|
+
let currentDir = path.dirname(filePath);
|
|
94
|
+
const visitedDirs: string[] = [];
|
|
95
|
+
|
|
96
|
+
while (currentDir && currentDir !== path.parse(currentDir).root) {
|
|
97
|
+
const cached = packageMetaCache.get(currentDir);
|
|
98
|
+
if (cached !== undefined) {
|
|
99
|
+
for (const d of visitedDirs) packageMetaCache.set(d, cached);
|
|
100
|
+
return cached;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const pkgJsonPath = path.join(currentDir, 'package.json');
|
|
104
|
+
if (fs.existsSync(pkgJsonPath)) {
|
|
105
|
+
try {
|
|
106
|
+
const content = fs.readFileSync(pkgJsonPath, 'utf8');
|
|
107
|
+
const pkg = JSON.parse(content);
|
|
108
|
+
|
|
109
|
+
let outDirName = 'dist';
|
|
110
|
+
const mainField = pkg.main || pkg.types || pkg.typings || '';
|
|
111
|
+
if (mainField) {
|
|
112
|
+
const parts = path.normalize(mainField).split(path.sep);
|
|
113
|
+
if (parts.length > 1 && parts[0] !== '.' && parts[0] !== '..') {
|
|
114
|
+
outDirName = parts[0];
|
|
115
|
+
} else if (parts.length > 2 && (parts[0] === '.' || parts[0] === '..')) {
|
|
116
|
+
outDirName = parts[1];
|
|
109
117
|
}
|
|
110
118
|
}
|
|
119
|
+
|
|
120
|
+
let srcDirName = 'src';
|
|
121
|
+
if (fs.existsSync(path.join(currentDir, 'source'))) srcDirName = 'source';
|
|
122
|
+
else if (fs.existsSync(path.join(currentDir, 'lib'))) srcDirName = 'lib';
|
|
123
|
+
|
|
124
|
+
const meta = { pkgDir: currentDir, srcDirName, outDirName };
|
|
125
|
+
packageMetaCache.set(currentDir, meta);
|
|
126
|
+
for (const d of visitedDirs) packageMetaCache.set(d, meta);
|
|
127
|
+
return meta;
|
|
128
|
+
} catch {
|
|
129
|
+
packageMetaCache.set(currentDir, null);
|
|
130
|
+
for (const d of visitedDirs) packageMetaCache.set(d, null);
|
|
131
|
+
return null;
|
|
111
132
|
}
|
|
112
|
-
ts.forEachChild(node, walk);
|
|
113
133
|
}
|
|
114
134
|
|
|
115
|
-
|
|
116
|
-
|
|
135
|
+
visitedDirs.push(currentDir);
|
|
136
|
+
currentDir = path.dirname(currentDir);
|
|
137
|
+
}
|
|
117
138
|
|
|
118
|
-
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function resolveModule(moduleName: string, containingFile: string, options: ts.CompilerOptions): string | null {
|
|
143
|
+
const result = ts.resolveModuleName(moduleName, containingFile, options, ts.sys);
|
|
144
|
+
if (!result.resolvedModule) return null;
|
|
145
|
+
|
|
146
|
+
const resolvedFileName = path.resolve(result.resolvedModule.resolvedFileName);
|
|
147
|
+
|
|
148
|
+
if (resolvedFileName.includes(`${path.sep}node_modules${path.sep}`)) {
|
|
149
|
+
return null;
|
|
119
150
|
}
|
|
120
151
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
152
|
+
const meta = getPackageMeta(resolvedFileName);
|
|
153
|
+
if (meta) {
|
|
154
|
+
const { pkgDir, srcDirName, outDirName } = meta;
|
|
155
|
+
const srcDirPath = path.join(pkgDir, srcDirName);
|
|
125
156
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
157
|
+
if (resolvedFileName.startsWith(srcDirPath + path.sep)) {
|
|
158
|
+
return resolvedFileName;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const outDirPath = path.join(pkgDir, outDirName);
|
|
162
|
+
if (resolvedFileName.startsWith(outDirPath + path.sep) || resolvedFileName === outDirPath) {
|
|
163
|
+
const relativeToOut = path.relative(outDirPath, resolvedFileName);
|
|
164
|
+
let baseName = relativeToOut;
|
|
165
|
+
|
|
166
|
+
if (baseName.endsWith('.d.ts')) baseName = baseName.slice(0, -5);
|
|
167
|
+
else if (baseName.endsWith('.d.mts')) baseName = baseName.slice(0, -6);
|
|
168
|
+
else if (baseName.endsWith('.d.cts')) baseName = baseName.slice(0, -6);
|
|
169
|
+
else if (baseName.endsWith('.js')) baseName = baseName.slice(0, -3);
|
|
170
|
+
else if (baseName.endsWith('.mjs')) baseName = baseName.slice(0, -4);
|
|
171
|
+
else if (baseName.endsWith('.cjs')) baseName = baseName.slice(0, -4);
|
|
172
|
+
else if (baseName.endsWith('.jsx')) baseName = baseName.slice(0, -4);
|
|
173
|
+
|
|
174
|
+
const extensions = ['.ts', '.tsx', '.mts', '.cts'];
|
|
175
|
+
for (const ext of extensions) {
|
|
176
|
+
const targetSrcFile = path.join(srcDirPath, baseName + ext);
|
|
177
|
+
if (fs.existsSync(targetSrcFile)) {
|
|
178
|
+
return targetSrcFile;
|
|
179
|
+
}
|
|
135
180
|
}
|
|
136
181
|
}
|
|
182
|
+
}
|
|
137
183
|
|
|
138
|
-
|
|
139
|
-
|
|
184
|
+
if (result.resolvedModule.isExternalLibraryImport) {
|
|
185
|
+
return null;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return resolvedFileName;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function getCanonicalCycleKey(cycle: string[]): string {
|
|
192
|
+
const nodes = cycle.slice(0, -1);
|
|
193
|
+
if (nodes.length === 0) return '';
|
|
194
|
+
|
|
195
|
+
let minIdx = 0;
|
|
196
|
+
for (let i = 1; i < nodes.length; i++) {
|
|
197
|
+
if (nodes[i] < nodes[minIdx]) {
|
|
198
|
+
minIdx = i;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const rotated = [...nodes.slice(minIdx), ...nodes.slice(0, minIdx)];
|
|
203
|
+
rotated.push(rotated[0]);
|
|
204
|
+
return rotated.join('|');
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function parseFile(filePath: string) {
|
|
208
|
+
if (graph.has(filePath) || !fs.existsSync(filePath)) return;
|
|
209
|
+
graph.set(filePath, []);
|
|
210
|
+
|
|
211
|
+
const options = getCompilerOptionsForFile(filePath);
|
|
212
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
213
|
+
const sourceFile = ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, true);
|
|
214
|
+
const imports: string[] = [];
|
|
215
|
+
|
|
216
|
+
function walk(node: ts.Node) {
|
|
217
|
+
if (ts.isImportDeclaration(node) || ts.isExportDeclaration(node)) {
|
|
218
|
+
if (isRuntimeImport(node)) {
|
|
219
|
+
const specifier = node.moduleSpecifier;
|
|
220
|
+
if (specifier && ts.isStringLiteral(specifier)) {
|
|
221
|
+
const resolved = resolveModule(specifier.text, filePath, options);
|
|
222
|
+
if (resolved && !imports.includes(resolved)) imports.push(resolved);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
ts.forEachChild(node, walk);
|
|
140
227
|
}
|
|
141
228
|
|
|
142
|
-
|
|
143
|
-
|
|
229
|
+
walk(sourceFile);
|
|
230
|
+
graph.set(filePath, imports);
|
|
144
231
|
|
|
145
|
-
|
|
232
|
+
for (const dep of imports) parseFile(dep);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Checks if a specific target file is reachable from a starting entry point in the graph.
|
|
237
|
+
*/
|
|
238
|
+
function canReach(start: string, target: string): boolean {
|
|
239
|
+
const seen = new Set<string>();
|
|
240
|
+
const stack = [start];
|
|
241
|
+
|
|
242
|
+
while (stack.length > 0) {
|
|
243
|
+
const current = stack.pop()!;
|
|
244
|
+
if (current === target) return true;
|
|
245
|
+
if (seen.has(current)) continue;
|
|
246
|
+
seen.add(current);
|
|
247
|
+
|
|
248
|
+
const deps = graph.get(current) || [];
|
|
249
|
+
for (const dep of deps) {
|
|
250
|
+
stack.push(dep);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
return false;
|
|
146
254
|
}
|
|
147
255
|
|
|
148
256
|
function main() {
|
|
149
257
|
const { entryPatterns, projectPath } = parseArgs();
|
|
258
|
+
globalProjectPath = projectPath;
|
|
150
259
|
|
|
151
|
-
if (entryPatterns.length
|
|
152
|
-
console.error('❌ Error: Please specify at least one entry point or glob pattern
|
|
260
|
+
if (entryPatterns.length === 0) {
|
|
261
|
+
console.error('❌ Error: Please specify at least one entry point or glob pattern.');
|
|
153
262
|
process.exit(1);
|
|
154
263
|
}
|
|
155
264
|
|
|
156
265
|
const entryPoints: string[] = [];
|
|
157
266
|
|
|
158
|
-
// Expand glob patterns or fall back to direct paths
|
|
159
267
|
for (const pattern of entryPatterns) {
|
|
160
268
|
const matches = fs.globSync ? fs.globSync(pattern) : [pattern];
|
|
161
269
|
|
|
@@ -179,28 +287,97 @@ function main() {
|
|
|
179
287
|
process.exit(1);
|
|
180
288
|
}
|
|
181
289
|
|
|
182
|
-
console.log(`🔍 Found ${entryPoints.length} entry point(s) for analysis...\n`);
|
|
290
|
+
console.log(`🔍 Found ${entryPoints.length} entry point(s) for analysis. Building graph...\n`);
|
|
291
|
+
|
|
292
|
+
// Phase 1: Deep parse all files globally across all entry points
|
|
293
|
+
for (const entryPoint of entryPoints) {
|
|
294
|
+
parseFile(entryPoint);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// Phase 2: Traverse the global graph to discover all unique cycles
|
|
298
|
+
const visited = new Map<string, NodeState>();
|
|
299
|
+
const currentStack: string[] = [];
|
|
300
|
+
|
|
301
|
+
function findCycles(node: string) {
|
|
302
|
+
visited.set(node, 'VISITING');
|
|
303
|
+
currentStack.push(node);
|
|
304
|
+
|
|
305
|
+
for (const neighbor of graph.get(node) || []) {
|
|
306
|
+
const state = visited.get(neighbor);
|
|
307
|
+
if (state === 'VISITING') {
|
|
308
|
+
const startIdx = currentStack.indexOf(neighbor);
|
|
309
|
+
const cycle = currentStack.slice(startIdx);
|
|
310
|
+
cycle.push(neighbor);
|
|
311
|
+
|
|
312
|
+
const key = getCanonicalCycleKey(cycle);
|
|
313
|
+
if (!globalDetectedCycles.has(key)) {
|
|
314
|
+
globalDetectedCycles.add(key);
|
|
315
|
+
allUniqueCycles.push(cycle);
|
|
316
|
+
}
|
|
317
|
+
} else if (!state) {
|
|
318
|
+
findCycles(neighbor);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
currentStack.pop();
|
|
323
|
+
visited.set(node, 'VISITED');
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
for (const entryPoint of entryPoints) {
|
|
327
|
+
if (!visited.has(entryPoint)) {
|
|
328
|
+
findCycles(entryPoint);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// Phase 3: Intelligently distribute and attribute cycles to their native entry points
|
|
333
|
+
const entryPointCycles = new Map<string, string[][]>();
|
|
334
|
+
for (const ep of entryPoints) {
|
|
335
|
+
entryPointCycles.set(ep, []);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
for (const cycle of allUniqueCycles) {
|
|
339
|
+
const firstFile = cycle[0];
|
|
340
|
+
|
|
341
|
+
// Find the entrypoint whose source folder directly hosts this file
|
|
342
|
+
const matchedEp = entryPoints.find((ep) => {
|
|
343
|
+
const epDir = path.dirname(ep);
|
|
344
|
+
return firstFile.startsWith(epDir + path.sep) || firstFile === ep;
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
if (matchedEp) {
|
|
348
|
+
entryPointCycles.get(matchedEp)!.push(cycle);
|
|
349
|
+
} else {
|
|
350
|
+
// Cross-package cycle or edge case: assign to the first entry point that can reach it
|
|
351
|
+
const reachingEp = entryPoints.find((ep) => canReach(ep, firstFile));
|
|
352
|
+
if (reachingEp) {
|
|
353
|
+
entryPointCycles.get(reachingEp)!.push(cycle);
|
|
354
|
+
} else {
|
|
355
|
+
entryPointCycles.get(entryPoints[0])!.push(cycle);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
}
|
|
183
359
|
|
|
360
|
+
// Phase 4: Output the clean, perfectly targeted report
|
|
184
361
|
let globalHasCycles = false;
|
|
185
362
|
|
|
186
363
|
for (const entryPoint of entryPoints) {
|
|
187
|
-
const
|
|
188
|
-
const cycles =
|
|
364
|
+
const absoluteEntry = path.resolve(entryPoint);
|
|
365
|
+
const cycles = entryPointCycles.get(entryPoint) || [];
|
|
189
366
|
|
|
190
367
|
if (cycles.length > 0) {
|
|
191
368
|
globalHasCycles = true;
|
|
192
|
-
console.error(`❌ [${
|
|
369
|
+
console.error(`❌ [${absoluteEntry}] — Found ${cycles.length} circular dependencies:`);
|
|
193
370
|
cycles.forEach((cycle, index) => {
|
|
194
|
-
const readableCycle = cycle.map((p) => path.
|
|
371
|
+
const readableCycle = cycle.map((p) => path.resolve(p)).join('\n -> ');
|
|
195
372
|
console.error(` ${index + 1}) ${readableCycle}`);
|
|
196
373
|
});
|
|
197
374
|
console.error('');
|
|
198
375
|
} else {
|
|
199
|
-
console.log(`✅ [${
|
|
376
|
+
console.log(`✅ [${absoluteEntry}] — Clean!`);
|
|
200
377
|
}
|
|
201
378
|
}
|
|
202
379
|
|
|
203
|
-
if (globalHasCycles) {
|
|
380
|
+
if (globalHasCycles || globalDetectedCycles.size > 0) {
|
|
204
381
|
console.error('💥 Validation failed. Circular dependencies detected.');
|
|
205
382
|
process.exit(1);
|
|
206
383
|
} else {
|