@ts-stack/cycle-detector 1.0.2 → 1.0.4

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 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 visited = new Map();
7
- const currentStack = [];
8
- const detectedCycles = [];
9
- /**
10
- * Parses command line arguments to separate options from entry point patterns.
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; // Side-effect import: import './foo'
40
- if (node.importClause.isTypeOnly)
41
- return false; // import type { X } from './foo'
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,300 @@ function isRuntimeImport(node) {
48
43
  }
49
44
  return false;
50
45
  }
51
- /**
52
- * Resolves module paths using the TypeScript Compiler API.
53
- * Respects tsconfig paths/aliases and ESM extensions.
54
- */
46
+ function getCompilerOptionsForFile(filePath) {
47
+ const currentDir = path.dirname(filePath);
48
+ const cachedOptions = compilerOptionsCache.get(currentDir);
49
+ if (cachedOptions !== undefined) {
50
+ return cachedOptions;
51
+ }
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 {};
74
+ }
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];
99
+ }
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;
117
+ }
118
+ }
119
+ visitedDirs.push(currentDir);
120
+ currentDir = path.dirname(currentDir);
121
+ }
122
+ return null;
123
+ }
55
124
  function resolveModule(moduleName, containingFile, options) {
56
125
  const result = ts.resolveModuleName(moduleName, containingFile, options, ts.sys);
57
- if (result.resolvedModule && !result.resolvedModule.isExternalLibraryImport) {
58
- return result.resolvedModule.resolvedFileName;
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;
59
131
  }
60
- return null;
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
+ }
163
+ }
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
+ }
202
+ }
203
+ }
204
+ ts.forEachChild(node, walk);
205
+ }
206
+ walk(sourceFile);
207
+ graph.set(filePath, imports);
208
+ for (const dep of imports)
209
+ parseFile(dep);
61
210
  }
62
211
  /**
63
- * Analyzes a single entry point, builds its dependency graph, and detects cycles.
212
+ * Checks if a specific file import creates an immediate execution (top-level) risk.
64
213
  */
65
- function analyzeEntryPoint(entryPoint, projectPath) {
66
- graph.clear();
67
- visited.clear();
68
- currentStack.length = 0;
69
- detectedCycles.length = 0;
70
- // Locate tsconfig.json automatically if not explicitly provided
71
- const configPath = projectPath
72
- ? path.resolve(projectPath)
73
- : ts.findConfigFile(path.dirname(entryPoint), ts.sys.fileExists, 'tsconfig.json');
74
- let compilerOptions = {};
75
- if (configPath && fs.existsSync(configPath)) {
76
- const configFile = ts.readConfigFile(configPath, ts.sys.readFile);
77
- const parsedConfig = ts.parseJsonConfigFileContent(configFile.config, ts.sys, path.dirname(configPath));
78
- compilerOptions = parsedConfig.options;
79
- }
80
- // Recursive AST parsing to build the graph
81
- function parseFile(filePath) {
82
- if (graph.has(filePath) || !fs.existsSync(filePath))
83
- return;
84
- graph.set(filePath, []);
85
- const content = fs.readFileSync(filePath, 'utf8');
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);
214
+ function hasTopLevelUsage(fromFile, toFile) {
215
+ if (!fs.existsSync(fromFile))
216
+ return false;
217
+ const options = getCompilerOptionsForFile(fromFile);
218
+ const content = fs.readFileSync(fromFile, 'utf8');
219
+ const sourceFile = ts.createSourceFile(fromFile, content, ts.ScriptTarget.Latest, true);
220
+ const importedSymbols = new Set();
221
+ let hasSideEffectOrReExport = false;
222
+ // 1. Collect all names imported from 'toFile'
223
+ function findImports(node) {
224
+ if (ts.isImportDeclaration(node) || ts.isExportDeclaration(node)) {
225
+ if (isRuntimeImport(node)) {
226
+ const specifier = node.moduleSpecifier;
227
+ if (specifier && ts.isStringLiteral(specifier)) {
228
+ const resolved = resolveModule(specifier.text, fromFile, options);
229
+ if (resolved === toFile) {
230
+ if (ts.isImportDeclaration(node) && node.importClause) {
231
+ const clause = node.importClause;
232
+ if (clause.name)
233
+ importedSymbols.add(clause.name.text); // default import
234
+ if (clause.namedBindings) {
235
+ if (ts.isNamespaceImport(clause.namedBindings)) {
236
+ importedSymbols.add(clause.namedBindings.name.text); // import * as namespace
237
+ }
238
+ else if (ts.isNamedImports(clause.namedBindings)) {
239
+ for (const el of clause.namedBindings.elements) {
240
+ importedSymbols.add(el.name.text); // named imports
241
+ }
242
+ }
243
+ }
244
+ }
245
+ else {
246
+ // Re-exports (export * from...) or side-effect imports (import './file')
247
+ // trigger top-level evaluation instantly.
248
+ hasSideEffectOrReExport = true;
249
+ }
96
250
  }
97
251
  }
98
252
  }
99
- ts.forEachChild(node, walk);
100
253
  }
101
- walk(sourceFile);
102
- graph.set(filePath, imports);
103
- for (const dep of imports)
104
- parseFile(dep);
254
+ ts.forEachChild(node, findImports);
105
255
  }
106
- // Graph coloring DFS algorithm for cycle detection
107
- function findCycles(node) {
108
- visited.set(node, 'VISITING');
109
- currentStack.push(node);
110
- for (const neighbor of graph.get(node) || []) {
111
- const state = visited.get(neighbor);
112
- if (state === 'VISITING') {
113
- const startIdx = currentStack.indexOf(neighbor);
114
- const cycle = currentStack.slice(startIdx);
115
- cycle.push(neighbor);
116
- detectedCycles.push(cycle);
256
+ findImports(sourceFile);
257
+ if (hasSideEffectOrReExport)
258
+ return true;
259
+ if (importedSymbols.size === 0)
260
+ return false;
261
+ let dangerousTopLevelUsage = false;
262
+ // 2. Check if collected symbols are used outside of lazy blocks (functions/methods)
263
+ function checkNodeUsage(node, isInsideLazyScope) {
264
+ if (dangerousTopLevelUsage)
265
+ return;
266
+ let currentScopeLazy = isInsideLazyScope;
267
+ if (ts.isFunctionDeclaration(node) ||
268
+ ts.isFunctionExpression(node) ||
269
+ ts.isArrowFunction(node) ||
270
+ ts.isMethodDeclaration(node) ||
271
+ ts.isConstructorDeclaration(node) ||
272
+ ts.isGetAccessorDeclaration(node) ||
273
+ ts.isSetAccessorDeclaration(node)) {
274
+ currentScopeLazy = true;
275
+ }
276
+ if (ts.isPropertyDeclaration(node)) {
277
+ const isStatic = node.modifiers?.some(m => m.kind === ts.SyntaxKind.StaticKeyword);
278
+ if (!isStatic) {
279
+ currentScopeLazy = true;
117
280
  }
118
- else if (!state) {
119
- findCycles(neighbor);
281
+ }
282
+ if (!currentScopeLazy && ts.isIdentifier(node)) {
283
+ if (importedSymbols.has(node.text)) {
284
+ const parent = node.parent;
285
+ // Verify it's an actual usage reference, not the import clause definition itself
286
+ const isImportDeclarationRef = ts.isImportSpecifier(parent) ||
287
+ ts.isImportClause(parent) ||
288
+ ts.isNamespaceImport(parent);
289
+ if (!isImportDeclarationRef) {
290
+ // Verify it's not just a TypeScript Type usage context (which is safe in runtime)
291
+ let isInTypeContext = false;
292
+ let checkParent = parent;
293
+ while (checkParent && checkParent !== sourceFile) {
294
+ if (ts.isTypeNode(checkParent) ||
295
+ ts.isTypeReferenceNode(checkParent) ||
296
+ ts.isTypeAliasDeclaration(checkParent) ||
297
+ ts.isInterfaceDeclaration(checkParent)) {
298
+ isInTypeContext = true;
299
+ break;
300
+ }
301
+ checkParent = checkParent.parent;
302
+ }
303
+ if (!isInTypeContext) {
304
+ dangerousTopLevelUsage = true;
305
+ return;
306
+ }
307
+ }
120
308
  }
121
309
  }
122
- currentStack.pop();
123
- visited.set(node, 'VISITED');
310
+ ts.forEachChild(node, (n) => checkNodeUsage(n, currentScopeLazy));
311
+ }
312
+ checkNodeUsage(sourceFile, false);
313
+ return dangerousTopLevelUsage;
314
+ }
315
+ function canReach(start, target) {
316
+ const seen = new Set();
317
+ const stack = [start];
318
+ while (stack.length > 0) {
319
+ const current = stack.pop();
320
+ if (current === target)
321
+ return true;
322
+ if (seen.has(current))
323
+ continue;
324
+ seen.add(current);
325
+ const deps = graph.get(current) || [];
326
+ for (const dep of deps) {
327
+ stack.push(dep);
328
+ }
124
329
  }
125
- parseFile(entryPoint);
126
- findCycles(entryPoint);
127
- return detectedCycles;
330
+ return false;
128
331
  }
129
332
  function main() {
130
333
  const { entryPatterns, projectPath } = parseArgs();
131
- if (entryPatterns.length == 0) {
132
- console.error('❌ Error: Please specify at least one entry point or glob pattern (e.g., packages/*/src/index.ts)');
334
+ globalProjectPath = projectPath;
335
+ if (entryPatterns.length === 0) {
336
+ console.error('❌ Error: Please specify at least one entry point or glob pattern.');
133
337
  process.exit(1);
134
338
  }
135
339
  const entryPoints = [];
136
- // Expand glob patterns or fall back to direct paths
137
340
  for (const pattern of entryPatterns) {
138
341
  const matches = fs.globSync ? fs.globSync(pattern) : [pattern];
139
342
  for (const match of matches) {
@@ -152,30 +355,104 @@ function main() {
152
355
  console.error('❌ Error: No entry files found matching the provided paths or patterns.');
153
356
  process.exit(1);
154
357
  }
155
- console.log(`🔍 Found ${entryPoints.length} entry point(s) for analysis...\n`);
358
+ console.log(`🔍 Found ${entryPoints.length} entry point(s) for analysis. Building graph...\n`);
359
+ // Phase 1: Deep parse all files globally across all entry points
360
+ for (const entryPoint of entryPoints) {
361
+ parseFile(entryPoint);
362
+ }
363
+ // Phase 2: Traverse the global graph to discover all unique cycles
364
+ const visited = new Map();
365
+ const currentStack = [];
366
+ function findCycles(node) {
367
+ visited.set(node, 'VISITING');
368
+ currentStack.push(node);
369
+ for (const neighbor of graph.get(node) || []) {
370
+ const state = visited.get(neighbor);
371
+ if (state === 'VISITING') {
372
+ const startIdx = currentStack.indexOf(neighbor);
373
+ const cycle = currentStack.slice(startIdx);
374
+ cycle.push(neighbor);
375
+ const key = getCanonicalCycleKey(cycle);
376
+ if (!globalDetectedCycles.has(key)) {
377
+ globalDetectedCycles.add(key);
378
+ allUniqueCycles.push(cycle);
379
+ }
380
+ }
381
+ else if (!state) {
382
+ findCycles(neighbor);
383
+ }
384
+ }
385
+ currentStack.pop();
386
+ visited.set(node, 'VISITED');
387
+ }
388
+ for (const entryPoint of entryPoints) {
389
+ if (!visited.has(entryPoint)) {
390
+ findCycles(entryPoint);
391
+ }
392
+ }
393
+ // Phase 2.5: Smart AST Filtering (Filter out runtime-safe / lazy cycles)
394
+ const criticalCycles = [];
395
+ for (const cycle of allUniqueCycles) {
396
+ let isHarmfulCycle = false;
397
+ // A cycle is harmful if AT LEAST ONE link in it uses imports on top-level
398
+ for (let i = 0; i < cycle.length - 1; i++) {
399
+ if (hasTopLevelUsage(cycle[i], cycle[i + 1])) {
400
+ isHarmfulCycle = true;
401
+ break;
402
+ }
403
+ }
404
+ if (isHarmfulCycle) {
405
+ criticalCycles.push(cycle);
406
+ }
407
+ }
408
+ // Phase 3: Intelligently distribute critical cycles to their native entry points
409
+ const entryPointCycles = new Map();
410
+ for (const ep of entryPoints) {
411
+ entryPointCycles.set(ep, []);
412
+ }
413
+ for (const cycle of criticalCycles) {
414
+ const firstFile = cycle[0];
415
+ const matchedEp = entryPoints.find((ep) => {
416
+ const epDir = path.dirname(ep);
417
+ return firstFile.startsWith(epDir + path.sep) || firstFile === ep;
418
+ });
419
+ if (matchedEp) {
420
+ entryPointCycles.get(matchedEp).push(cycle);
421
+ }
422
+ else {
423
+ const reachingEp = entryPoints.find((ep) => canReach(ep, firstFile));
424
+ if (reachingEp) {
425
+ entryPointCycles.get(reachingEp).push(cycle);
426
+ }
427
+ else {
428
+ entryPointCycles.get(entryPoints[0]).push(cycle);
429
+ }
430
+ }
431
+ }
432
+ // Phase 4: Output the clean, perfectly targeted report
156
433
  let globalHasCycles = false;
157
434
  for (const entryPoint of entryPoints) {
158
- const relativeEntry = path.relative(process.cwd(), entryPoint);
159
- const cycles = analyzeEntryPoint(entryPoint, projectPath);
435
+ const absoluteEntry = path.resolve(entryPoint);
436
+ const cycles = entryPointCycles.get(entryPoint) || [];
160
437
  if (cycles.length > 0) {
161
438
  globalHasCycles = true;
162
- console.error(`❌ [${relativeEntry}] — Found ${cycles.length} circular dependencies:`);
439
+ console.error(`❌ [${absoluteEntry}] — Found ${cycles.length} critical circular dependencies:`);
163
440
  cycles.forEach((cycle, index) => {
164
- const readableCycle = cycle.map((p) => path.relative(process.cwd(), p)).join(' -> ');
441
+ const readableCycle = cycle.map((p) => path.resolve(p)).join('\n -> ');
165
442
  console.error(` ${index + 1}) ${readableCycle}`);
166
443
  });
167
444
  console.error('');
168
445
  }
169
446
  else {
170
- console.log(`✅ [${relativeEntry}] — Clean!`);
447
+ console.log(`✅ [${absoluteEntry}] — Clean!`);
171
448
  }
172
449
  }
173
- if (globalHasCycles) {
174
- console.error('💥 Validation failed. Circular dependencies detected.');
450
+ if (globalHasCycles || criticalCycles.length > 0) {
451
+ console.error('💥 Validation failed. Critical circular dependencies detected.');
175
452
  process.exit(1);
176
453
  }
177
454
  else {
178
- console.log('🎉 All packages checked. No circular dependencies found!');
455
+ console.log('🎉 All packages checked. No critical circular dependencies found!');
179
456
  process.exit(0);
180
457
  }
181
458
  }
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.2",
4
+ "version": "1.0.4",
5
5
  "bin": {
6
6
  "cycle-detector": "./dist/index.js"
7
7
  },
@@ -28,7 +28,9 @@
28
28
  "clean": "rimraf dist*"
29
29
  },
30
30
  "keywords": [
31
- "lint",
31
+ "ts-stack",
32
+ "dependencies",
33
+ "circular-dependencies",
32
34
  "circular dependencies"
33
35
  ],
34
36
  "author": "Kostia Tretiak",
@@ -45,6 +47,7 @@
45
47
  "nodemon": "^3.1.14",
46
48
  "prettier": "^3.8.4",
47
49
  "rimraf": "^5.0.10",
50
+ "ts-node": "^10.9.2",
48
51
  "typescript": "^5.9.3",
49
52
  "typescript-eslint": "^8.62.0"
50
53
  },
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 visited = new Map<string, NodeState>();
11
- const currentStack: string[] = [];
12
- const detectedCycles: string[][] = [];
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; // Side-effect import: import './foo'
46
- if (node.importClause.isTypeOnly) return false; // import type { X } from './foo'
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,333 @@ function isRuntimeImport(node: ts.ImportDeclaration | ts.ExportDeclaration): boo
56
53
  return false;
57
54
  }
58
55
 
59
- /**
60
- * Resolves module paths using the TypeScript Compiler API.
61
- * Respects tsconfig paths/aliases and ESM extensions.
62
- */
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;
62
+ }
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 {};
90
+ }
91
+
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];
117
+ }
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;
132
+ }
133
+ }
134
+
135
+ visitedDirs.push(currentDir);
136
+ currentDir = path.dirname(currentDir);
137
+ }
138
+
139
+ return null;
140
+ }
141
+
63
142
  function resolveModule(moduleName: string, containingFile: string, options: ts.CompilerOptions): string | null {
64
143
  const result = ts.resolveModuleName(moduleName, containingFile, options, ts.sys);
65
- if (result.resolvedModule && !result.resolvedModule.isExternalLibraryImport) {
66
- return result.resolvedModule.resolvedFileName;
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;
67
150
  }
68
- return null;
151
+
152
+ const meta = getPackageMeta(resolvedFileName);
153
+ if (meta) {
154
+ const { pkgDir, srcDirName, outDirName } = meta;
155
+ const srcDirPath = path.join(pkgDir, srcDirName);
156
+
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
+ }
180
+ }
181
+ }
182
+ }
183
+
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);
227
+ }
228
+
229
+ walk(sourceFile);
230
+ graph.set(filePath, imports);
231
+
232
+ for (const dep of imports) parseFile(dep);
69
233
  }
70
234
 
71
235
  /**
72
- * Analyzes a single entry point, builds its dependency graph, and detects cycles.
236
+ * Checks if a specific file import creates an immediate execution (top-level) risk.
73
237
  */
74
- function analyzeEntryPoint(entryPoint: string, projectPath?: string): string[][] {
75
- graph.clear();
76
- visited.clear();
77
- currentStack.length = 0;
78
- detectedCycles.length = 0;
79
-
80
- // Locate tsconfig.json automatically if not explicitly provided
81
- const configPath = projectPath
82
- ? path.resolve(projectPath)
83
- : ts.findConfigFile(path.dirname(entryPoint), ts.sys.fileExists, 'tsconfig.json');
84
-
85
- let compilerOptions: ts.CompilerOptions = {};
86
-
87
- if (configPath && fs.existsSync(configPath)) {
88
- const configFile = ts.readConfigFile(configPath, ts.sys.readFile);
89
- const parsedConfig = ts.parseJsonConfigFileContent(configFile.config, ts.sys, path.dirname(configPath));
90
- compilerOptions = parsedConfig.options;
91
- }
92
-
93
- // Recursive AST parsing to build the graph
94
- function parseFile(filePath: string) {
95
- if (graph.has(filePath) || !fs.existsSync(filePath)) return;
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);
238
+ function hasTopLevelUsage(fromFile: string, toFile: string): boolean {
239
+ if (!fs.existsSync(fromFile)) return false;
240
+
241
+ const options = getCompilerOptionsForFile(fromFile);
242
+ const content = fs.readFileSync(fromFile, 'utf8');
243
+ const sourceFile = ts.createSourceFile(fromFile, content, ts.ScriptTarget.Latest, true);
244
+
245
+ const importedSymbols = new Set<string>();
246
+ let hasSideEffectOrReExport = false;
247
+
248
+ // 1. Collect all names imported from 'toFile'
249
+ function findImports(node: ts.Node) {
250
+ if (ts.isImportDeclaration(node) || ts.isExportDeclaration(node)) {
251
+ if (isRuntimeImport(node)) {
252
+ const specifier = node.moduleSpecifier;
253
+ if (specifier && ts.isStringLiteral(specifier)) {
254
+ const resolved = resolveModule(specifier.text, fromFile, options);
255
+ if (resolved === toFile) {
256
+ if (ts.isImportDeclaration(node) && node.importClause) {
257
+ const clause = node.importClause;
258
+ if (clause.name) importedSymbols.add(clause.name.text); // default import
259
+ if (clause.namedBindings) {
260
+ if (ts.isNamespaceImport(clause.namedBindings)) {
261
+ importedSymbols.add(clause.namedBindings.name.text); // import * as namespace
262
+ } else if (ts.isNamedImports(clause.namedBindings)) {
263
+ for (const el of clause.namedBindings.elements) {
264
+ importedSymbols.add(el.name.text); // named imports
265
+ }
266
+ }
267
+ }
268
+ } else {
269
+ // Re-exports (export * from...) or side-effect imports (import './file')
270
+ // trigger top-level evaluation instantly.
271
+ hasSideEffectOrReExport = true;
272
+ }
109
273
  }
110
274
  }
111
275
  }
112
- ts.forEachChild(node, walk);
113
276
  }
277
+ ts.forEachChild(node, findImports);
278
+ }
279
+
280
+ findImports(sourceFile);
281
+
282
+ if (hasSideEffectOrReExport) return true;
283
+ if (importedSymbols.size === 0) return false;
284
+
285
+ let dangerousTopLevelUsage = false;
114
286
 
115
- walk(sourceFile);
116
- graph.set(filePath, imports);
287
+ // 2. Check if collected symbols are used outside of lazy blocks (functions/methods)
288
+ function checkNodeUsage(node: ts.Node, isInsideLazyScope: boolean) {
289
+ if (dangerousTopLevelUsage) return;
117
290
 
118
- for (const dep of imports) parseFile(dep);
291
+ let currentScopeLazy = isInsideLazyScope;
292
+
293
+ if (
294
+ ts.isFunctionDeclaration(node) ||
295
+ ts.isFunctionExpression(node) ||
296
+ ts.isArrowFunction(node) ||
297
+ ts.isMethodDeclaration(node) ||
298
+ ts.isConstructorDeclaration(node) ||
299
+ ts.isGetAccessorDeclaration(node) ||
300
+ ts.isSetAccessorDeclaration(node)
301
+ ) {
302
+ currentScopeLazy = true;
119
303
  }
120
304
 
121
- // Graph coloring DFS algorithm for cycle detection
122
- function findCycles(node: string) {
123
- visited.set(node, 'VISITING');
124
- currentStack.push(node);
305
+ if (ts.isPropertyDeclaration(node)) {
306
+ const isStatic = node.modifiers?.some(m => m.kind === ts.SyntaxKind.StaticKeyword);
307
+ if (!isStatic) {
308
+ currentScopeLazy = true;
309
+ }
310
+ }
125
311
 
126
- for (const neighbor of graph.get(node) || []) {
127
- const state = visited.get(neighbor);
128
- if (state === 'VISITING') {
129
- const startIdx = currentStack.indexOf(neighbor);
130
- const cycle = currentStack.slice(startIdx);
131
- cycle.push(neighbor);
132
- detectedCycles.push(cycle);
133
- } else if (!state) {
134
- findCycles(neighbor);
312
+ if (!currentScopeLazy && ts.isIdentifier(node)) {
313
+ if (importedSymbols.has(node.text)) {
314
+ const parent = node.parent;
315
+
316
+ // Verify it's an actual usage reference, not the import clause definition itself
317
+ const isImportDeclarationRef =
318
+ ts.isImportSpecifier(parent) ||
319
+ ts.isImportClause(parent) ||
320
+ ts.isNamespaceImport(parent);
321
+
322
+ if (!isImportDeclarationRef) {
323
+ // Verify it's not just a TypeScript Type usage context (which is safe in runtime)
324
+ let isInTypeContext = false;
325
+ let checkParent: ts.Node | undefined = parent;
326
+ while (checkParent && checkParent !== sourceFile) {
327
+ if (
328
+ ts.isTypeNode(checkParent) ||
329
+ ts.isTypeReferenceNode(checkParent) ||
330
+ ts.isTypeAliasDeclaration(checkParent) ||
331
+ ts.isInterfaceDeclaration(checkParent)
332
+ ) {
333
+ isInTypeContext = true;
334
+ break;
335
+ }
336
+ checkParent = checkParent.parent;
337
+ }
338
+
339
+ if (!isInTypeContext) {
340
+ dangerousTopLevelUsage = true;
341
+ return;
342
+ }
135
343
  }
136
344
  }
137
-
138
- currentStack.pop();
139
- visited.set(node, 'VISITED');
140
345
  }
141
346
 
142
- parseFile(entryPoint);
143
- findCycles(entryPoint);
347
+ ts.forEachChild(node, (n) => checkNodeUsage(n, currentScopeLazy));
348
+ }
349
+
350
+ checkNodeUsage(sourceFile, false);
351
+ return dangerousTopLevelUsage;
352
+ }
353
+
354
+ function canReach(start: string, target: string): boolean {
355
+ const seen = new Set<string>();
356
+ const stack = [start];
144
357
 
145
- return detectedCycles;
358
+ while (stack.length > 0) {
359
+ const current = stack.pop()!;
360
+ if (current === target) return true;
361
+ if (seen.has(current)) continue;
362
+ seen.add(current);
363
+
364
+ const deps = graph.get(current) || [];
365
+ for (const dep of deps) {
366
+ stack.push(dep);
367
+ }
368
+ }
369
+ return false;
146
370
  }
147
371
 
148
372
  function main() {
149
373
  const { entryPatterns, projectPath } = parseArgs();
374
+ globalProjectPath = projectPath;
150
375
 
151
- if (entryPatterns.length == 0) {
152
- console.error('❌ Error: Please specify at least one entry point or glob pattern (e.g., packages/*/src/index.ts)');
376
+ if (entryPatterns.length === 0) {
377
+ console.error('❌ Error: Please specify at least one entry point or glob pattern.');
153
378
  process.exit(1);
154
379
  }
155
380
 
156
381
  const entryPoints: string[] = [];
157
382
 
158
- // Expand glob patterns or fall back to direct paths
159
383
  for (const pattern of entryPatterns) {
160
384
  const matches = fs.globSync ? fs.globSync(pattern) : [pattern];
161
385
 
@@ -179,32 +403,118 @@ function main() {
179
403
  process.exit(1);
180
404
  }
181
405
 
182
- console.log(`🔍 Found ${entryPoints.length} entry point(s) for analysis...\n`);
406
+ console.log(`🔍 Found ${entryPoints.length} entry point(s) for analysis. Building graph...\n`);
407
+
408
+ // Phase 1: Deep parse all files globally across all entry points
409
+ for (const entryPoint of entryPoints) {
410
+ parseFile(entryPoint);
411
+ }
412
+
413
+ // Phase 2: Traverse the global graph to discover all unique cycles
414
+ const visited = new Map<string, NodeState>();
415
+ const currentStack: string[] = [];
416
+
417
+ function findCycles(node: string) {
418
+ visited.set(node, 'VISITING');
419
+ currentStack.push(node);
420
+
421
+ for (const neighbor of graph.get(node) || []) {
422
+ const state = visited.get(neighbor);
423
+ if (state === 'VISITING') {
424
+ const startIdx = currentStack.indexOf(neighbor);
425
+ const cycle = currentStack.slice(startIdx);
426
+ cycle.push(neighbor);
427
+
428
+ const key = getCanonicalCycleKey(cycle);
429
+ if (!globalDetectedCycles.has(key)) {
430
+ globalDetectedCycles.add(key);
431
+ allUniqueCycles.push(cycle);
432
+ }
433
+ } else if (!state) {
434
+ findCycles(neighbor);
435
+ }
436
+ }
437
+
438
+ currentStack.pop();
439
+ visited.set(node, 'VISITED');
440
+ }
441
+
442
+ for (const entryPoint of entryPoints) {
443
+ if (!visited.has(entryPoint)) {
444
+ findCycles(entryPoint);
445
+ }
446
+ }
447
+
448
+ // Phase 2.5: Smart AST Filtering (Filter out runtime-safe / lazy cycles)
449
+ const criticalCycles: string[][] = [];
450
+
451
+ for (const cycle of allUniqueCycles) {
452
+ let isHarmfulCycle = false;
453
+
454
+ // A cycle is harmful if AT LEAST ONE link in it uses imports on top-level
455
+ for (let i = 0; i < cycle.length - 1; i++) {
456
+ if (hasTopLevelUsage(cycle[i], cycle[i + 1])) {
457
+ isHarmfulCycle = true;
458
+ break;
459
+ }
460
+ }
461
+
462
+ if (isHarmfulCycle) {
463
+ criticalCycles.push(cycle);
464
+ }
465
+ }
466
+
467
+ // Phase 3: Intelligently distribute critical cycles to their native entry points
468
+ const entryPointCycles = new Map<string, string[][]>();
469
+ for (const ep of entryPoints) {
470
+ entryPointCycles.set(ep, []);
471
+ }
472
+
473
+ for (const cycle of criticalCycles) {
474
+ const firstFile = cycle[0];
475
+
476
+ const matchedEp = entryPoints.find((ep) => {
477
+ const epDir = path.dirname(ep);
478
+ return firstFile.startsWith(epDir + path.sep) || firstFile === ep;
479
+ });
480
+
481
+ if (matchedEp) {
482
+ entryPointCycles.get(matchedEp)!.push(cycle);
483
+ } else {
484
+ const reachingEp = entryPoints.find((ep) => canReach(ep, firstFile));
485
+ if (reachingEp) {
486
+ entryPointCycles.get(reachingEp)!.push(cycle);
487
+ } else {
488
+ entryPointCycles.get(entryPoints[0])!.push(cycle);
489
+ }
490
+ }
491
+ }
183
492
 
493
+ // Phase 4: Output the clean, perfectly targeted report
184
494
  let globalHasCycles = false;
185
495
 
186
496
  for (const entryPoint of entryPoints) {
187
- const relativeEntry = path.relative(process.cwd(), entryPoint);
188
- const cycles = analyzeEntryPoint(entryPoint, projectPath);
497
+ const absoluteEntry = path.resolve(entryPoint);
498
+ const cycles = entryPointCycles.get(entryPoint) || [];
189
499
 
190
500
  if (cycles.length > 0) {
191
501
  globalHasCycles = true;
192
- console.error(`❌ [${relativeEntry}] — Found ${cycles.length} circular dependencies:`);
502
+ console.error(`❌ [${absoluteEntry}] — Found ${cycles.length} critical circular dependencies:`);
193
503
  cycles.forEach((cycle, index) => {
194
- const readableCycle = cycle.map((p) => path.relative(process.cwd(), p)).join(' -> ');
504
+ const readableCycle = cycle.map((p) => path.resolve(p)).join('\n -> ');
195
505
  console.error(` ${index + 1}) ${readableCycle}`);
196
506
  });
197
507
  console.error('');
198
508
  } else {
199
- console.log(`✅ [${relativeEntry}] — Clean!`);
509
+ console.log(`✅ [${absoluteEntry}] — Clean!`);
200
510
  }
201
511
  }
202
512
 
203
- if (globalHasCycles) {
204
- console.error('💥 Validation failed. Circular dependencies detected.');
513
+ if (globalHasCycles || criticalCycles.length > 0) {
514
+ console.error('💥 Validation failed. Critical circular dependencies detected.');
205
515
  process.exit(1);
206
516
  } else {
207
- console.log('🎉 All packages checked. No circular dependencies found!');
517
+ console.log('🎉 All packages checked. No critical circular dependencies found!');
208
518
  process.exit(0);
209
519
  }
210
520
  }
package/eslint.config.mjs DELETED
@@ -1,60 +0,0 @@
1
- // @ts-check
2
-
3
- import eslint from '@eslint/js';
4
- import { defineConfig } from 'eslint/config';
5
- import tseslint from 'typescript-eslint';
6
-
7
- export default defineConfig([
8
- eslint.configs.recommended,
9
- ...tseslint.configs.recommended,
10
- {
11
- languageOptions: {
12
- parserOptions: {
13
- projectService: true,
14
- },
15
- },
16
- },
17
- {
18
- rules: {
19
- semi: ['error', 'always'],
20
- quotes: ['error', 'single', { avoidEscape: true }],
21
- '@typescript-eslint/no-floating-promises': 'error',
22
- '@typescript-eslint/no-misused-promises': 1,
23
- '@typescript-eslint/no-unused-expressions': 'warn',
24
- '@typescript-eslint/consistent-type-imports': 1,
25
- '@typescript-eslint/no-empty-object-type': 0,
26
- '@typescript-eslint/no-non-null-assertion': 0,
27
- '@typescript-eslint/no-empty-function': 0,
28
- '@typescript-eslint/no-empty-interface': 0,
29
- '@typescript-eslint/explicit-function-return-type': 0,
30
- '@typescript-eslint/explicit-module-boundary-types': 0,
31
- '@typescript-eslint/no-explicit-any': 0,
32
- '@typescript-eslint/no-inferrable-types': 0,
33
- '@typescript-eslint/no-non-null-asserted-optional-chain': 0,
34
- '@typescript-eslint/no-unused-vars': 0,
35
- '@typescript-eslint/triple-slash-reference': 0,
36
- '@typescript-eslint/no-unsafe-function-type': 0,
37
- 'no-unused-private-class-members': 'warn',
38
- 'no-useless-assignment': 'warn',
39
- 'no-restricted-imports': ['error', 'fs'],
40
- 'prefer-const': 'warn',
41
- 'no-async-promise-executor': 0,
42
- 'no-prototype-builtins': 0,
43
- },
44
- },
45
- {
46
- ignores: [
47
- '**/dist*',
48
- '**/*.d.ts',
49
- 'website/*',
50
- 'node_modules/*',
51
- 'eslint.config.mjs',
52
- '**/jest.config.ts',
53
- '**/vitest.config.ts',
54
- '**/jest.setup.js',
55
- '**/jest.d.ts',
56
- '**/jest.matchers.ts',
57
- 'packages/openapi/ui/*',
58
- ],
59
- },
60
- ]);