eslint-plugin-barrel-rules 1.4.3 → 1.4.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
@@ -1,10 +1,3 @@
1
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
- }) : x)(function(x) {
4
- if (typeof require !== "undefined") return require.apply(this, arguments);
5
- throw Error('Dynamic require of "' + x + '" is not supported');
6
- });
7
-
8
1
  // src/rules/enforce-barrel-pattern.ts
9
2
  import "@typescript-eslint/types";
10
3
  import path2 from "path";
@@ -26,55 +19,125 @@ var Glob = class {
26
19
  // src/utils/alias.ts
27
20
  import { loadConfig } from "tsconfig-paths";
28
21
  import path from "path";
29
- var Alias = class {
22
+ var Alias = class _Alias {
30
23
  constructor() {
31
24
  }
25
+ /**
26
+ * Resolves an alias path to an absolute file path
27
+ *
28
+ * @param rawPath - The alias path to resolve (e.g., "@entities/user", "@utils")
29
+ * @param currentFileDir - The directory of the current file (used to find tsconfig.json)
30
+ * @returns Result object with absolutePath and type ("success" or "fail")
31
+ *
32
+ * @example
33
+ * // With wildcard alias: "@entities/*" -> "src/entities/*"
34
+ * Alias.resolvePath("@entities/user", "/project/src/features")
35
+ * // Returns: { absolutePath: "/project/src/entities/user", type: "success" }
36
+ *
37
+ * @example
38
+ * // Without wildcard: "@utils" -> "src/utils/index"
39
+ * Alias.resolvePath("@utils", "/project/src/features")
40
+ * // Returns: { absolutePath: "/project/src/utils/index", type: "success" }
41
+ */
32
42
  static resolvePath(rawPath, currentFileDir) {
33
43
  try {
34
44
  const configResult = loadConfig(currentFileDir);
35
- if (configResult.resultType === "success") {
36
- for (const [pattern, targets] of Object.entries(configResult.paths)) {
37
- const origin = targets[0];
38
- if (pattern.includes("*")) {
39
- const patternRegex = new RegExp(
40
- `^${pattern.replace("*", "(.*)")}$`
41
- );
42
- const match = rawPath.match(patternRegex);
43
- if (match) {
44
- const [, matchedPath] = match;
45
- const extendedOrigin = origin.replace("*", matchedPath);
46
- const absolutePath = path.resolve(
47
- `${configResult.absoluteBaseUrl}/${extendedOrigin}`
48
- );
49
- return {
50
- absolutePath,
51
- type: "success"
52
- };
53
- }
54
- } else {
55
- if (rawPath === pattern) {
56
- const absolutePath = path.resolve(
57
- `${configResult.absoluteBaseUrl}/${origin}`
58
- );
59
- return {
60
- absolutePath,
61
- type: "success"
62
- };
63
- }
64
- }
45
+ if (configResult.resultType !== "success") {
46
+ return this.createFailResult(rawPath);
47
+ }
48
+ const { paths, absoluteBaseUrl } = configResult;
49
+ for (const [aliasPattern, targetPaths] of Object.entries(paths)) {
50
+ const targetPath = targetPaths[0];
51
+ const matchResult = this.tryMatchPattern(
52
+ rawPath,
53
+ aliasPattern,
54
+ targetPath,
55
+ absoluteBaseUrl
56
+ );
57
+ if (matchResult) {
58
+ return matchResult;
65
59
  }
66
60
  }
67
- return {
68
- absolutePath: rawPath,
69
- type: "fail"
70
- };
71
- } catch (e) {
72
- return {
73
- absolutePath: rawPath,
74
- type: "fail"
75
- };
61
+ return _Alias.createFailResult(rawPath);
62
+ } catch (error) {
63
+ return _Alias.createFailResult(rawPath);
64
+ }
65
+ }
66
+ /**
67
+ * Attempts to match a raw path against an alias pattern
68
+ *
69
+ * @param rawPath - The path to match (e.g., "@entities/user")
70
+ * @param aliasPattern - The alias pattern from tsconfig (e.g., "@entities/*")
71
+ * @param targetPath - The target path from tsconfig (e.g., "src/entities/*")
72
+ * @param baseUrl - The absolute base URL from tsconfig
73
+ * @returns Success result if matched, null otherwise
74
+ */
75
+ static tryMatchPattern(rawPath, aliasPattern, targetPath, baseUrl) {
76
+ const hasWildcard = aliasPattern.includes("*");
77
+ if (hasWildcard) {
78
+ return this.matchWildcardPattern(
79
+ rawPath,
80
+ aliasPattern,
81
+ targetPath,
82
+ baseUrl
83
+ );
84
+ } else {
85
+ return this.matchExactPattern(rawPath, aliasPattern, targetPath, baseUrl);
76
86
  }
77
87
  }
88
+ /**
89
+ * Matches a wildcard alias pattern (e.g., "@entities/*")
90
+ *
91
+ * Pattern: "@entities/*" -> Target: "src/entities/*"
92
+ * Input: "@entities/user" -> Matches: "user" -> Output: "src/entities/user"
93
+ *
94
+ * Note: The original implementation uses simple string replacement for regex,
95
+ * which works because alias patterns typically don't contain special regex chars.
96
+ * We maintain this behavior for compatibility.
97
+ */
98
+ static matchWildcardPattern(rawPath, aliasPattern, targetPath, baseUrl) {
99
+ const regexPattern = `^${aliasPattern.replace(/\*/g, "(.*)")}$`;
100
+ const regex = new RegExp(regexPattern);
101
+ const match = rawPath.match(regex);
102
+ if (!match) {
103
+ return null;
104
+ }
105
+ const capturedPath = match[1];
106
+ const resolvedTargetPath = targetPath.replace(/\*/g, capturedPath);
107
+ const absolutePath = path.resolve(`${baseUrl}/${resolvedTargetPath}`);
108
+ return _Alias.createSuccessResult(absolutePath);
109
+ }
110
+ /**
111
+ * Matches an exact alias pattern (no wildcard)
112
+ *
113
+ * Pattern: "@utils" -> Target: "src/utils/index"
114
+ * Input: "@utils" -> Output: "src/utils/index"
115
+ */
116
+ static matchExactPattern(rawPath, aliasPattern, targetPath, baseUrl) {
117
+ if (rawPath !== aliasPattern) {
118
+ return null;
119
+ }
120
+ const absolutePath = path.resolve(`${baseUrl}/${targetPath}`);
121
+ return _Alias.createSuccessResult(absolutePath);
122
+ }
123
+ /**
124
+ * Creates a success result
125
+ */
126
+ static createSuccessResult(absolutePath) {
127
+ return {
128
+ absolutePath,
129
+ type: "success"
130
+ };
131
+ }
132
+ /**
133
+ * Creates a fail result
134
+ */
135
+ static createFailResult(originalPath) {
136
+ return {
137
+ absolutePath: originalPath,
138
+ type: "fail"
139
+ };
140
+ }
78
141
  };
79
142
 
80
143
  // src/utils/constants.ts
@@ -200,16 +263,16 @@ var enforceBarrelPattern = {
200
263
  if (!rawImportPath.startsWith(".") && !rawImportPath.startsWith("/") || rawImportPath.includes("/node_modules/")) {
201
264
  return;
202
265
  }
203
- absoluteImportPath = resolve.sync(rawImportPath, {
204
- basedir: path2.dirname(absoluteCurrentFilePath),
205
- extensions: RESOLVE_EXTENSIONS
206
- });
266
+ try {
267
+ absoluteImportPath = resolve.sync(rawImportPath, {
268
+ basedir: path2.dirname(absoluteCurrentFilePath),
269
+ extensions: RESOLVE_EXTENSIONS
270
+ });
271
+ } catch (e) {
272
+ return;
273
+ }
207
274
  }
208
275
  } catch (e) {
209
- context.report({
210
- node,
211
- messageId: "TransformedAliasResolveFailed"
212
- });
213
276
  return;
214
277
  }
215
278
  {
@@ -223,8 +286,8 @@ var enforceBarrelPattern = {
223
286
  const targetPathEntryPointed = targetPathEntryPoints.includes(absoluteImportPath);
224
287
  const importedEnforceBarrelFile = absoluteImportPath.startsWith(closedTargetPath);
225
288
  const currentFileInEnforceBarrel = absoluteCurrentFilePath.startsWith(closedTargetPath);
226
- const importedOutsideOfTargetPath = !currentFileInEnforceBarrel && importedEnforceBarrelFile;
227
- const invalidImported = !targetPathEntryPointed && importedOutsideOfTargetPath;
289
+ const importedOutsideOfBarrel = !currentFileInEnforceBarrel && importedEnforceBarrelFile;
290
+ const invalidImported = !targetPathEntryPointed && importedOutsideOfBarrel;
228
291
  if (invalidImported) {
229
292
  matchedLatestTargetPath = absoluteTargetPath;
230
293
  }
@@ -330,7 +393,7 @@ var isolateBarrelFile = {
330
393
  return;
331
394
  }
332
395
  } else {
333
- if (!rawImportPath.startsWith(".") && !rawImportPath.startsWith("/")) {
396
+ if (!rawImportPath.startsWith(".") && !rawImportPath.startsWith("/") || rawImportPath.includes("/node_modules/")) {
334
397
  return;
335
398
  }
336
399
  absoluteImportPath = resolve2.sync(rawImportPath, {
@@ -345,31 +408,26 @@ var isolateBarrelFile = {
345
408
  });
346
409
  return;
347
410
  }
348
- const isolationIndex = absoluteIsolations.findIndex((isolation) => {
349
- const absoluteIsolationPath = isolation.isolationPath;
350
- const closedIsolationPath = absoluteIsolationPath + "/";
351
- return absoluteCurrentFilePath.startsWith(closedIsolationPath);
352
- });
353
- const matchedIsolation = absoluteIsolations[isolationIndex];
354
- if (!matchedIsolation) return;
355
- const isAllowedImport = matchedIsolation.allowedPaths.some(
356
- (allowedPath) => {
357
- const same = absoluteImportPath === allowedPath;
358
- const closedAllowedPath = allowedPath + "/";
359
- const sub = absoluteImportPath.startsWith(closedAllowedPath);
360
- return same || sub;
361
- }
362
- );
363
- const isGlobalAllowedImport = absoluteGlobalAllowPaths.some(
364
- (allowedPath) => {
365
- const same = absoluteImportPath === allowedPath;
366
- const closedAllowedPath = allowedPath + "/";
367
- const sub = absoluteImportPath.startsWith(closedAllowedPath);
368
- return same || sub;
411
+ const matchedIsolationIndex = absoluteIsolations.findIndex(
412
+ (isolation) => {
413
+ const closedIsolationPath = isolation.isolationPath + "/";
414
+ return absoluteCurrentFilePath.startsWith(closedIsolationPath);
369
415
  }
370
416
  );
371
- const allowedImport = isAllowedImport || isGlobalAllowedImport;
372
- if (!allowedImport) {
417
+ const matchedIsolation = absoluteIsolations[matchedIsolationIndex];
418
+ if (!matchedIsolation) return;
419
+ const isAllowedImport = [
420
+ //global allowed import paths
421
+ ...absoluteGlobalAllowPaths,
422
+ //allowed import paths in the matched isolation
423
+ ...matchedIsolation.allowedPaths
424
+ ].some((allowedPath) => {
425
+ const same = absoluteImportPath === allowedPath;
426
+ const closedAllowedPath = allowedPath + "/";
427
+ const sub = absoluteImportPath.startsWith(closedAllowedPath);
428
+ return same || sub;
429
+ });
430
+ if (!isAllowedImport) {
373
431
  context.report({
374
432
  node,
375
433
  messageId: "IsolatedBarrelImportDisallowed"
@@ -421,73 +479,128 @@ var noWildcard = {
421
479
  // src/rules/no-cycle.ts
422
480
  import "@typescript-eslint/types";
423
481
  import path4 from "path";
482
+ import fs from "fs";
424
483
  import resolve3 from "resolve";
425
484
  var importGraph = /* @__PURE__ */ new Map();
426
- var fileToImports = /* @__PURE__ */ new Map();
427
485
  var barrelExportsCache = /* @__PURE__ */ new Map();
428
- function detectCycle(currentFile, visited, recStack, path5) {
429
- visited.add(currentFile);
430
- recStack.add(currentFile);
431
- path5.push(currentFile);
432
- const imports = importGraph.get(currentFile) || /* @__PURE__ */ new Set();
433
- for (const importedFile of imports) {
434
- if (!visited.has(importedFile)) {
435
- const cycle = detectCycle(importedFile, visited, recStack, [...path5]);
436
- if (cycle) {
437
- return cycle;
438
- }
439
- } else if (recStack.has(importedFile)) {
440
- const cycleStart = path5.indexOf(importedFile);
441
- return [...path5.slice(cycleStart), importedFile];
442
- }
443
- }
444
- recStack.delete(currentFile);
445
- return null;
446
- }
447
486
  function isBarrelFile(filePath) {
448
487
  const fileName = path4.basename(filePath);
449
488
  return BARREL_ENTRY_POINT_FILE_NAMES.includes(
450
489
  fileName
451
490
  );
452
491
  }
453
- function getExportedModulesFromBarrel(barrelFileDir, sourceCode) {
454
- const exportedModules = [];
455
- const ast = sourceCode.ast;
492
+ function isExternalImport(rawPath) {
493
+ if (!rawPath.startsWith(".") && !rawPath.startsWith("/")) {
494
+ return true;
495
+ }
496
+ if (rawPath.includes("/node_modules/")) {
497
+ return true;
498
+ }
499
+ return false;
500
+ }
501
+ function resolveImportPath(rawImportPath, currentFileDir) {
502
+ try {
503
+ const aliasResult = Alias.resolvePath(rawImportPath, currentFileDir);
504
+ if (aliasResult.type === "success") {
505
+ return resolveAliasPath(aliasResult.absolutePath, currentFileDir);
506
+ }
507
+ if (isExternalImport(rawImportPath)) {
508
+ return null;
509
+ }
510
+ return resolve3.sync(rawImportPath, {
511
+ basedir: currentFileDir,
512
+ extensions: RESOLVE_EXTENSIONS
513
+ });
514
+ } catch (e) {
515
+ return null;
516
+ }
517
+ }
518
+ function resolveAliasPath(resolvedPath, currentFileDir) {
519
+ try {
520
+ const stats = fs.statSync(resolvedPath);
521
+ if (stats.isDirectory()) {
522
+ return resolve3.sync("index", {
523
+ basedir: resolvedPath,
524
+ extensions: RESOLVE_EXTENSIONS
525
+ });
526
+ }
527
+ return resolvedPath;
528
+ } catch (e) {
529
+ try {
530
+ return resolve3.sync(resolvedPath, {
531
+ basedir: currentFileDir,
532
+ extensions: RESOLVE_EXTENSIONS
533
+ });
534
+ } catch (e2) {
535
+ return resolvedPath;
536
+ }
537
+ }
538
+ }
539
+ function getBarrelExports(barrelFileDir, ast) {
540
+ const exports = [];
456
541
  for (const statement of ast.body) {
457
- if (statement.type === "ExportNamedDeclaration" || statement.type === "ExportAllDeclaration") {
458
- if (statement.source) {
459
- const exportPath = statement.source.value;
460
- try {
461
- const resolvedPath = resolve3.sync(exportPath, {
462
- basedir: barrelFileDir,
463
- extensions: RESOLVE_EXTENSIONS
464
- });
465
- if (!exportedModules.includes(resolvedPath)) {
466
- exportedModules.push(resolvedPath);
467
- }
468
- } catch (e) {
469
- }
542
+ if ((statement.type === "ExportNamedDeclaration" || statement.type === "ExportAllDeclaration") && statement.source) {
543
+ const exportPath = statement.source.value;
544
+ const resolved = tryResolve(exportPath, barrelFileDir);
545
+ if (resolved && !exports.includes(resolved)) {
546
+ exports.push(resolved);
470
547
  }
471
548
  }
472
- if (statement.type === "ImportDeclaration") {
473
- if (statement.source) {
474
- const importPath = statement.source.value;
475
- if (importPath.startsWith(".") || importPath.startsWith("/")) {
476
- try {
477
- const resolvedPath = resolve3.sync(importPath, {
478
- basedir: barrelFileDir,
479
- extensions: RESOLVE_EXTENSIONS
480
- });
481
- if (!exportedModules.includes(resolvedPath)) {
482
- exportedModules.push(resolvedPath);
483
- }
484
- } catch (e) {
485
- }
549
+ if (statement.type === "ImportDeclaration" && statement.source) {
550
+ const importPath = statement.source.value;
551
+ if (importPath.startsWith(".") || importPath.startsWith("/")) {
552
+ const resolved = tryResolve(importPath, barrelFileDir);
553
+ if (resolved && !exports.includes(resolved)) {
554
+ exports.push(resolved);
486
555
  }
487
556
  }
488
557
  }
489
558
  }
490
- return exportedModules;
559
+ return exports;
560
+ }
561
+ function tryResolve(importPath, basedir) {
562
+ try {
563
+ return resolve3.sync(importPath, {
564
+ basedir,
565
+ extensions: RESOLVE_EXTENSIONS
566
+ });
567
+ } catch (e) {
568
+ return null;
569
+ }
570
+ }
571
+ function detectCycle(startFile, visited, recStack, currentPath) {
572
+ visited.add(startFile);
573
+ recStack.add(startFile);
574
+ currentPath.push(startFile);
575
+ const imports = importGraph.get(startFile) || /* @__PURE__ */ new Set();
576
+ for (const importedFile of imports) {
577
+ if (!visited.has(importedFile)) {
578
+ const cycle = detectCycle(importedFile, visited, recStack, [
579
+ ...currentPath
580
+ ]);
581
+ if (cycle) return cycle;
582
+ } else if (recStack.has(importedFile)) {
583
+ const cycleStart = currentPath.indexOf(importedFile);
584
+ return [...currentPath.slice(cycleStart), importedFile];
585
+ }
586
+ }
587
+ recStack.delete(startFile);
588
+ return null;
589
+ }
590
+ function hasBidirectionalCycle(fileA, fileB) {
591
+ var _a;
592
+ const bImports = importGraph.get(fileB);
593
+ return (_a = bImports == null ? void 0 : bImports.has(fileA)) != null ? _a : false;
594
+ }
595
+ function hasCycleThroughBarrel(currentFile, exportedModules) {
596
+ for (const exportedModule of exportedModules) {
597
+ if (exportedModule === currentFile) continue;
598
+ const moduleImports = importGraph.get(exportedModule);
599
+ if (moduleImports == null ? void 0 : moduleImports.has(currentFile)) {
600
+ return exportedModule;
601
+ }
602
+ }
603
+ return null;
491
604
  }
492
605
  var noCycle = {
493
606
  meta: {
@@ -504,161 +617,88 @@ var noCycle = {
504
617
  },
505
618
  defaultOptions: [],
506
619
  create(context) {
507
- const absoluteCurrentFilePath = context.getFilename();
508
- if (!importGraph.has(absoluteCurrentFilePath)) {
509
- importGraph.set(absoluteCurrentFilePath, /* @__PURE__ */ new Set());
620
+ const currentFile = context.getFilename();
621
+ const currentDir = path4.dirname(currentFile);
622
+ if (!importGraph.has(currentFile)) {
623
+ importGraph.set(currentFile, /* @__PURE__ */ new Set());
510
624
  }
511
- const currentImportsSet = importGraph.get(absoluteCurrentFilePath);
512
- currentImportsSet.clear();
513
- if (!fileToImports.has(absoluteCurrentFilePath)) {
514
- fileToImports.set(absoluteCurrentFilePath, /* @__PURE__ */ new Set());
625
+ importGraph.get(currentFile).clear();
626
+ if (isBarrelFile(currentFile)) {
627
+ const ast = context.getSourceCode().ast;
628
+ const exports = getBarrelExports(currentDir, ast);
629
+ barrelExportsCache.set(currentFile, exports);
515
630
  }
516
- if (isBarrelFile(absoluteCurrentFilePath)) {
517
- const barrelFileDir = path4.dirname(absoluteCurrentFilePath);
518
- const sourceCode = context.getSourceCode();
519
- const exportedModules = getExportedModulesFromBarrel(
520
- barrelFileDir,
521
- sourceCode
522
- );
523
- barrelExportsCache.set(absoluteCurrentFilePath, exportedModules);
524
- }
525
- function resolveImportPath(rawImportPath) {
526
- try {
527
- const aliasResult = Alias.resolvePath(
528
- rawImportPath,
529
- path4.dirname(absoluteCurrentFilePath)
530
- );
531
- if (aliasResult.type === "success") {
532
- const resolvedPath = aliasResult.absolutePath;
533
- try {
534
- const fs = __require("fs");
535
- const stats = fs.statSync(resolvedPath);
536
- if (stats.isDirectory()) {
537
- return resolve3.sync("index", {
538
- basedir: resolvedPath,
539
- extensions: RESOLVE_EXTENSIONS
540
- });
541
- }
542
- } catch (e) {
543
- try {
544
- return resolve3.sync(resolvedPath, {
545
- basedir: path4.dirname(absoluteCurrentFilePath),
546
- extensions: RESOLVE_EXTENSIONS
547
- });
548
- } catch (e2) {
549
- return resolvedPath;
550
- }
551
- }
552
- return resolvedPath;
553
- } else {
554
- if (!rawImportPath.startsWith(".") && !rawImportPath.startsWith("/") || rawImportPath.includes("/node_modules/")) {
555
- return null;
556
- }
557
- return resolve3.sync(rawImportPath, {
558
- basedir: path4.dirname(absoluteCurrentFilePath),
559
- extensions: RESOLVE_EXTENSIONS
631
+ function checkImport(node) {
632
+ if (!node.source) return;
633
+ const rawPath = node.source.value;
634
+ const absolutePath = resolveImportPath(rawPath, currentDir);
635
+ if (!absolutePath) return;
636
+ if (isBarrelFile(currentFile)) {
637
+ const error = checkBarrelInternalImport(rawPath, absolutePath);
638
+ if (error) {
639
+ context.report({
640
+ node,
641
+ messageId: "BarrelInternalImportDisallowed",
642
+ data: { relativePath: error }
560
643
  });
644
+ return;
561
645
  }
562
- } catch (e) {
563
- return null;
564
646
  }
565
- }
566
- function checker(node) {
567
- if (!node.source) {
568
- return;
647
+ const isBarrelInternal = isBarrelFile(currentFile) && absolutePath.startsWith(currentDir + path4.sep);
648
+ addToGraph(absolutePath);
649
+ if (isBarrelInternal) return;
650
+ const cycleError = checkForCycles(absolutePath);
651
+ if (cycleError) {
652
+ context.report({
653
+ node,
654
+ messageId: "CircularDependency",
655
+ data: { cyclePath: cycleError }
656
+ });
569
657
  }
570
- const rawImportPath = node.source.value;
571
- const absoluteImportPath = resolveImportPath(rawImportPath);
572
- if (!absoluteImportPath) {
573
- return;
658
+ }
659
+ function checkBarrelInternalImport(rawPath, absolutePath) {
660
+ const isInternal = absolutePath.startsWith(currentDir + path4.sep) || absolutePath === currentDir;
661
+ if (!isInternal) return null;
662
+ if (rawPath.startsWith("./") || rawPath.startsWith("../")) {
663
+ return null;
574
664
  }
575
- if (isBarrelFile(absoluteCurrentFilePath)) {
576
- const barrelFileDir = path4.dirname(absoluteCurrentFilePath);
577
- const isInternalModule = absoluteImportPath.startsWith(barrelFileDir + path4.sep) || absoluteImportPath === barrelFileDir;
578
- if (isInternalModule) {
579
- if (!rawImportPath.startsWith("./") && !rawImportPath.startsWith("../")) {
580
- const relativePath = path4.relative(
581
- barrelFileDir,
582
- absoluteImportPath
583
- );
584
- const suggestedRelativePath = relativePath.startsWith(".") ? relativePath : `./${relativePath}`;
585
- context.report({
586
- node,
587
- messageId: "BarrelInternalImportDisallowed",
588
- data: {
589
- relativePath: suggestedRelativePath.replace(/\\/g, "/")
590
- }
591
- });
592
- return;
665
+ const relative = path4.relative(currentDir, absolutePath);
666
+ const suggested = relative.startsWith(".") ? relative : `./${relative}`;
667
+ return suggested.replace(/\\/g, "/");
668
+ }
669
+ function addToGraph(absolutePath) {
670
+ const imports = importGraph.get(currentFile);
671
+ imports.add(absolutePath);
672
+ if (isBarrelFile(absolutePath)) {
673
+ const exports = barrelExportsCache.get(absolutePath) || [];
674
+ for (const exp of exports) {
675
+ if (exp !== currentFile) {
676
+ imports.add(exp);
593
677
  }
594
678
  }
595
679
  }
596
- const isBarrelImportingInternal = isBarrelFile(absoluteCurrentFilePath) && absoluteImportPath.startsWith(
597
- path4.dirname(absoluteCurrentFilePath) + path4.sep
598
- );
599
- const currentImports = importGraph.get(absoluteCurrentFilePath);
600
- if (currentImports) {
601
- currentImports.add(absoluteImportPath);
602
- if (isBarrelFile(absoluteImportPath)) {
603
- const exportedModules = barrelExportsCache.get(absoluteImportPath) || [];
604
- for (const exportedModule of exportedModules) {
605
- if (exportedModule !== absoluteCurrentFilePath) {
606
- currentImports.add(exportedModule);
607
- const exportedModuleImports = importGraph.get(exportedModule);
608
- if (exportedModuleImports && exportedModuleImports.has(absoluteCurrentFilePath)) {
609
- const cyclePath = `${absoluteCurrentFilePath} \u2192 ${absoluteImportPath} \u2192 ${exportedModule} \u2192 ${absoluteImportPath} \u2192 ${absoluteCurrentFilePath}`;
610
- context.report({
611
- node,
612
- messageId: "CircularDependency",
613
- data: {
614
- cyclePath
615
- }
616
- });
617
- return;
618
- }
619
- }
620
- }
680
+ }
681
+ function checkForCycles(absolutePath) {
682
+ if (isBarrelFile(absolutePath)) {
683
+ const exports = barrelExportsCache.get(absolutePath) || [];
684
+ const cycleModule = hasCycleThroughBarrel(currentFile, exports);
685
+ if (cycleModule) {
686
+ return `${currentFile} \u2192 ${absolutePath} \u2192 ${cycleModule} \u2192 ${absolutePath} \u2192 ${currentFile}`;
621
687
  }
622
688
  }
623
- if (isBarrelImportingInternal) {
624
- return;
689
+ if (hasBidirectionalCycle(currentFile, absolutePath)) {
690
+ return `${currentFile} \u2192 ${absolutePath} \u2192 ${currentFile}`;
625
691
  }
626
- const importedFileImports = importGraph.get(absoluteImportPath);
627
- if (importedFileImports && importedFileImports.has(absoluteCurrentFilePath)) {
628
- const cyclePath = `${absoluteCurrentFilePath} \u2192 ${absoluteImportPath} \u2192 ${absoluteCurrentFilePath}`;
629
- context.report({
630
- node,
631
- messageId: "CircularDependency",
632
- data: {
633
- cyclePath
634
- }
635
- });
636
- return;
637
- }
638
- const visited = /* @__PURE__ */ new Set();
639
- const recStack = /* @__PURE__ */ new Set();
640
- const cycle = detectCycle(absoluteCurrentFilePath, visited, recStack, []);
692
+ const cycle = detectCycle(currentFile, /* @__PURE__ */ new Set(), /* @__PURE__ */ new Set(), []);
641
693
  if (cycle && cycle.length > 0) {
642
- const cyclePath = cycle.join(" \u2192 ");
643
- context.report({
644
- node,
645
- messageId: "CircularDependency",
646
- data: {
647
- cyclePath
648
- }
649
- });
694
+ return cycle.join(" \u2192 ");
650
695
  }
696
+ return null;
651
697
  }
652
698
  return {
653
- ImportDeclaration(node) {
654
- return checker(node);
655
- },
656
- ExportNamedDeclaration(node) {
657
- return checker(node);
658
- },
659
- ExportAllDeclaration(node) {
660
- return checker(node);
661
- }
699
+ ImportDeclaration: checkImport,
700
+ ExportNamedDeclaration: checkImport,
701
+ ExportAllDeclaration: checkImport
662
702
  };
663
703
  }
664
704
  };