eslint-plugin-barrel-rules 1.4.2 → 1.4.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/README.ko.md +1 -1
- package/README.md +1 -1
- package/dist/index.cjs +253 -8
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +260 -8
- package/package.json +1 -1
package/README.ko.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
# **Advanced Barrel Pattern Enforcement for JavaScript/TypeScript Projects**
|
|
4
4
|
|
|
5
5
|
<div align="center">
|
|
6
|
-
<img src="https://img.shields.io/badge/version-1.4.
|
|
6
|
+
<img src="https://img.shields.io/badge/version-1.4.3-blue.svg" alt="Version"/>
|
|
7
7
|
<img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License"/>
|
|
8
8
|
<img src="https://img.shields.io/badge/PRs-welcome-brightgreen.svg" alt="PRs Welcome"/>
|
|
9
9
|
</div>
|
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
# **Advanced Barrel Pattern Enforcement for JavaScript/TypeScript Projects**
|
|
4
4
|
|
|
5
5
|
<div align="center">
|
|
6
|
-
<img src="https://img.shields.io/badge/version-1.4.
|
|
6
|
+
<img src="https://img.shields.io/badge/version-1.4.3-blue.svg" alt="Version"/>
|
|
7
7
|
<img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License"/>
|
|
8
8
|
<img src="https://img.shields.io/badge/PRs-welcome-brightgreen.svg" alt="PRs Welcome"/>
|
|
9
9
|
</div>
|
package/dist/index.cjs
CHANGED
|
@@ -42,8 +42,8 @@ var import_resolve = __toESM(require("resolve"), 1);
|
|
|
42
42
|
// src/utils/glob.ts
|
|
43
43
|
var import_fast_glob = __toESM(require("fast-glob"), 1);
|
|
44
44
|
var Glob = class {
|
|
45
|
-
static resolvePath(
|
|
46
|
-
const globResult = import_fast_glob.default.sync(
|
|
45
|
+
static resolvePath(path5, baseDir) {
|
|
46
|
+
const globResult = import_fast_glob.default.sync(path5, {
|
|
47
47
|
cwd: baseDir,
|
|
48
48
|
onlyDirectories: true,
|
|
49
49
|
absolute: true
|
|
@@ -325,13 +325,13 @@ var isolateBarrelFile = {
|
|
|
325
325
|
create(context) {
|
|
326
326
|
const option = context.options[0];
|
|
327
327
|
const baseDir = option.baseDir;
|
|
328
|
-
const absoluteGlobalAllowPaths = option.globalAllowPaths.flatMap((
|
|
329
|
-
return Glob.resolvePath(
|
|
328
|
+
const absoluteGlobalAllowPaths = option.globalAllowPaths.flatMap((path5) => {
|
|
329
|
+
return Glob.resolvePath(path5, baseDir);
|
|
330
330
|
});
|
|
331
331
|
const absoluteIsolations = option.isolations.flatMap((isolation) => {
|
|
332
332
|
const isolationPaths = Glob.resolvePath(isolation.path, baseDir);
|
|
333
|
-
const allowedPaths = isolation.allowedPaths.flatMap((
|
|
334
|
-
return Glob.resolvePath(
|
|
333
|
+
const allowedPaths = isolation.allowedPaths.flatMap((path5) => {
|
|
334
|
+
return Glob.resolvePath(path5, baseDir);
|
|
335
335
|
});
|
|
336
336
|
return isolationPaths.map((isolationPath) => ({
|
|
337
337
|
isolationPath,
|
|
@@ -447,12 +447,257 @@ var noWildcard = {
|
|
|
447
447
|
}
|
|
448
448
|
};
|
|
449
449
|
|
|
450
|
+
// src/rules/no-cycle.ts
|
|
451
|
+
var import_types4 = require("@typescript-eslint/types");
|
|
452
|
+
var import_path4 = __toESM(require("path"), 1);
|
|
453
|
+
var import_resolve3 = __toESM(require("resolve"), 1);
|
|
454
|
+
var importGraph = /* @__PURE__ */ new Map();
|
|
455
|
+
var fileToImports = /* @__PURE__ */ new Map();
|
|
456
|
+
var barrelExportsCache = /* @__PURE__ */ new Map();
|
|
457
|
+
function detectCycle(currentFile, visited, recStack, path5) {
|
|
458
|
+
visited.add(currentFile);
|
|
459
|
+
recStack.add(currentFile);
|
|
460
|
+
path5.push(currentFile);
|
|
461
|
+
const imports = importGraph.get(currentFile) || /* @__PURE__ */ new Set();
|
|
462
|
+
for (const importedFile of imports) {
|
|
463
|
+
if (!visited.has(importedFile)) {
|
|
464
|
+
const cycle = detectCycle(importedFile, visited, recStack, [...path5]);
|
|
465
|
+
if (cycle) {
|
|
466
|
+
return cycle;
|
|
467
|
+
}
|
|
468
|
+
} else if (recStack.has(importedFile)) {
|
|
469
|
+
const cycleStart = path5.indexOf(importedFile);
|
|
470
|
+
return [...path5.slice(cycleStart), importedFile];
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
recStack.delete(currentFile);
|
|
474
|
+
return null;
|
|
475
|
+
}
|
|
476
|
+
function isBarrelFile(filePath) {
|
|
477
|
+
const fileName = import_path4.default.basename(filePath);
|
|
478
|
+
return BARREL_ENTRY_POINT_FILE_NAMES.includes(
|
|
479
|
+
fileName
|
|
480
|
+
);
|
|
481
|
+
}
|
|
482
|
+
function getExportedModulesFromBarrel(barrelFileDir, sourceCode) {
|
|
483
|
+
const exportedModules = [];
|
|
484
|
+
const ast = sourceCode.ast;
|
|
485
|
+
for (const statement of ast.body) {
|
|
486
|
+
if (statement.type === "ExportNamedDeclaration" || statement.type === "ExportAllDeclaration") {
|
|
487
|
+
if (statement.source) {
|
|
488
|
+
const exportPath = statement.source.value;
|
|
489
|
+
try {
|
|
490
|
+
const resolvedPath = import_resolve3.default.sync(exportPath, {
|
|
491
|
+
basedir: barrelFileDir,
|
|
492
|
+
extensions: RESOLVE_EXTENSIONS
|
|
493
|
+
});
|
|
494
|
+
if (!exportedModules.includes(resolvedPath)) {
|
|
495
|
+
exportedModules.push(resolvedPath);
|
|
496
|
+
}
|
|
497
|
+
} catch (e) {
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
if (statement.type === "ImportDeclaration") {
|
|
502
|
+
if (statement.source) {
|
|
503
|
+
const importPath = statement.source.value;
|
|
504
|
+
if (importPath.startsWith(".") || importPath.startsWith("/")) {
|
|
505
|
+
try {
|
|
506
|
+
const resolvedPath = import_resolve3.default.sync(importPath, {
|
|
507
|
+
basedir: barrelFileDir,
|
|
508
|
+
extensions: RESOLVE_EXTENSIONS
|
|
509
|
+
});
|
|
510
|
+
if (!exportedModules.includes(resolvedPath)) {
|
|
511
|
+
exportedModules.push(resolvedPath);
|
|
512
|
+
}
|
|
513
|
+
} catch (e) {
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
return exportedModules;
|
|
520
|
+
}
|
|
521
|
+
var noCycle = {
|
|
522
|
+
meta: {
|
|
523
|
+
type: "problem",
|
|
524
|
+
docs: {
|
|
525
|
+
description: "Detect circular dependencies and enforce relative imports in barrel files."
|
|
526
|
+
},
|
|
527
|
+
schema: [],
|
|
528
|
+
messages: {
|
|
529
|
+
CircularDependency: "Circular dependency detected: {{cyclePath}}. This creates a dependency cycle that can cause runtime errors and make code harder to maintain.",
|
|
530
|
+
BarrelInternalImportDisallowed: "Barrel files (index.ts) must use relative imports (./ or ../) for internal modules. Importing via barrel file or absolute path is not allowed. Use relative path: '{{relativePath}}'",
|
|
531
|
+
TransformedAliasResolveFailed: "Transformed alias resolve failed. please check the alias config."
|
|
532
|
+
}
|
|
533
|
+
},
|
|
534
|
+
defaultOptions: [],
|
|
535
|
+
create(context) {
|
|
536
|
+
const absoluteCurrentFilePath = context.getFilename();
|
|
537
|
+
if (!importGraph.has(absoluteCurrentFilePath)) {
|
|
538
|
+
importGraph.set(absoluteCurrentFilePath, /* @__PURE__ */ new Set());
|
|
539
|
+
}
|
|
540
|
+
const currentImportsSet = importGraph.get(absoluteCurrentFilePath);
|
|
541
|
+
currentImportsSet.clear();
|
|
542
|
+
if (!fileToImports.has(absoluteCurrentFilePath)) {
|
|
543
|
+
fileToImports.set(absoluteCurrentFilePath, /* @__PURE__ */ new Set());
|
|
544
|
+
}
|
|
545
|
+
if (isBarrelFile(absoluteCurrentFilePath)) {
|
|
546
|
+
const barrelFileDir = import_path4.default.dirname(absoluteCurrentFilePath);
|
|
547
|
+
const sourceCode = context.getSourceCode();
|
|
548
|
+
const exportedModules = getExportedModulesFromBarrel(
|
|
549
|
+
barrelFileDir,
|
|
550
|
+
sourceCode
|
|
551
|
+
);
|
|
552
|
+
barrelExportsCache.set(absoluteCurrentFilePath, exportedModules);
|
|
553
|
+
}
|
|
554
|
+
function resolveImportPath(rawImportPath) {
|
|
555
|
+
try {
|
|
556
|
+
const aliasResult = Alias.resolvePath(
|
|
557
|
+
rawImportPath,
|
|
558
|
+
import_path4.default.dirname(absoluteCurrentFilePath)
|
|
559
|
+
);
|
|
560
|
+
if (aliasResult.type === "success") {
|
|
561
|
+
const resolvedPath = aliasResult.absolutePath;
|
|
562
|
+
try {
|
|
563
|
+
const fs = require("fs");
|
|
564
|
+
const stats = fs.statSync(resolvedPath);
|
|
565
|
+
if (stats.isDirectory()) {
|
|
566
|
+
return import_resolve3.default.sync("index", {
|
|
567
|
+
basedir: resolvedPath,
|
|
568
|
+
extensions: RESOLVE_EXTENSIONS
|
|
569
|
+
});
|
|
570
|
+
}
|
|
571
|
+
} catch (e) {
|
|
572
|
+
try {
|
|
573
|
+
return import_resolve3.default.sync(resolvedPath, {
|
|
574
|
+
basedir: import_path4.default.dirname(absoluteCurrentFilePath),
|
|
575
|
+
extensions: RESOLVE_EXTENSIONS
|
|
576
|
+
});
|
|
577
|
+
} catch (e2) {
|
|
578
|
+
return resolvedPath;
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
return resolvedPath;
|
|
582
|
+
} else {
|
|
583
|
+
if (!rawImportPath.startsWith(".") && !rawImportPath.startsWith("/") || rawImportPath.includes("/node_modules/")) {
|
|
584
|
+
return null;
|
|
585
|
+
}
|
|
586
|
+
return import_resolve3.default.sync(rawImportPath, {
|
|
587
|
+
basedir: import_path4.default.dirname(absoluteCurrentFilePath),
|
|
588
|
+
extensions: RESOLVE_EXTENSIONS
|
|
589
|
+
});
|
|
590
|
+
}
|
|
591
|
+
} catch (e) {
|
|
592
|
+
return null;
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
function checker(node) {
|
|
596
|
+
if (!node.source) {
|
|
597
|
+
return;
|
|
598
|
+
}
|
|
599
|
+
const rawImportPath = node.source.value;
|
|
600
|
+
const absoluteImportPath = resolveImportPath(rawImportPath);
|
|
601
|
+
if (!absoluteImportPath) {
|
|
602
|
+
return;
|
|
603
|
+
}
|
|
604
|
+
if (isBarrelFile(absoluteCurrentFilePath)) {
|
|
605
|
+
const barrelFileDir = import_path4.default.dirname(absoluteCurrentFilePath);
|
|
606
|
+
const isInternalModule = absoluteImportPath.startsWith(barrelFileDir + import_path4.default.sep) || absoluteImportPath === barrelFileDir;
|
|
607
|
+
if (isInternalModule) {
|
|
608
|
+
if (!rawImportPath.startsWith("./") && !rawImportPath.startsWith("../")) {
|
|
609
|
+
const relativePath = import_path4.default.relative(
|
|
610
|
+
barrelFileDir,
|
|
611
|
+
absoluteImportPath
|
|
612
|
+
);
|
|
613
|
+
const suggestedRelativePath = relativePath.startsWith(".") ? relativePath : `./${relativePath}`;
|
|
614
|
+
context.report({
|
|
615
|
+
node,
|
|
616
|
+
messageId: "BarrelInternalImportDisallowed",
|
|
617
|
+
data: {
|
|
618
|
+
relativePath: suggestedRelativePath.replace(/\\/g, "/")
|
|
619
|
+
}
|
|
620
|
+
});
|
|
621
|
+
return;
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
const isBarrelImportingInternal = isBarrelFile(absoluteCurrentFilePath) && absoluteImportPath.startsWith(
|
|
626
|
+
import_path4.default.dirname(absoluteCurrentFilePath) + import_path4.default.sep
|
|
627
|
+
);
|
|
628
|
+
const currentImports = importGraph.get(absoluteCurrentFilePath);
|
|
629
|
+
if (currentImports) {
|
|
630
|
+
currentImports.add(absoluteImportPath);
|
|
631
|
+
if (isBarrelFile(absoluteImportPath)) {
|
|
632
|
+
const exportedModules = barrelExportsCache.get(absoluteImportPath) || [];
|
|
633
|
+
for (const exportedModule of exportedModules) {
|
|
634
|
+
if (exportedModule !== absoluteCurrentFilePath) {
|
|
635
|
+
currentImports.add(exportedModule);
|
|
636
|
+
const exportedModuleImports = importGraph.get(exportedModule);
|
|
637
|
+
if (exportedModuleImports && exportedModuleImports.has(absoluteCurrentFilePath)) {
|
|
638
|
+
const cyclePath = `${absoluteCurrentFilePath} \u2192 ${absoluteImportPath} \u2192 ${exportedModule} \u2192 ${absoluteImportPath} \u2192 ${absoluteCurrentFilePath}`;
|
|
639
|
+
context.report({
|
|
640
|
+
node,
|
|
641
|
+
messageId: "CircularDependency",
|
|
642
|
+
data: {
|
|
643
|
+
cyclePath
|
|
644
|
+
}
|
|
645
|
+
});
|
|
646
|
+
return;
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
if (isBarrelImportingInternal) {
|
|
653
|
+
return;
|
|
654
|
+
}
|
|
655
|
+
const importedFileImports = importGraph.get(absoluteImportPath);
|
|
656
|
+
if (importedFileImports && importedFileImports.has(absoluteCurrentFilePath)) {
|
|
657
|
+
const cyclePath = `${absoluteCurrentFilePath} \u2192 ${absoluteImportPath} \u2192 ${absoluteCurrentFilePath}`;
|
|
658
|
+
context.report({
|
|
659
|
+
node,
|
|
660
|
+
messageId: "CircularDependency",
|
|
661
|
+
data: {
|
|
662
|
+
cyclePath
|
|
663
|
+
}
|
|
664
|
+
});
|
|
665
|
+
return;
|
|
666
|
+
}
|
|
667
|
+
const visited = /* @__PURE__ */ new Set();
|
|
668
|
+
const recStack = /* @__PURE__ */ new Set();
|
|
669
|
+
const cycle = detectCycle(absoluteCurrentFilePath, visited, recStack, []);
|
|
670
|
+
if (cycle && cycle.length > 0) {
|
|
671
|
+
const cyclePath = cycle.join(" \u2192 ");
|
|
672
|
+
context.report({
|
|
673
|
+
node,
|
|
674
|
+
messageId: "CircularDependency",
|
|
675
|
+
data: {
|
|
676
|
+
cyclePath
|
|
677
|
+
}
|
|
678
|
+
});
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
return {
|
|
682
|
+
ImportDeclaration(node) {
|
|
683
|
+
return checker(node);
|
|
684
|
+
},
|
|
685
|
+
ExportNamedDeclaration(node) {
|
|
686
|
+
return checker(node);
|
|
687
|
+
},
|
|
688
|
+
ExportAllDeclaration(node) {
|
|
689
|
+
return checker(node);
|
|
690
|
+
}
|
|
691
|
+
};
|
|
692
|
+
}
|
|
693
|
+
};
|
|
694
|
+
|
|
450
695
|
// src/index.ts
|
|
451
696
|
var rules = {
|
|
452
697
|
"enforce-barrel-pattern": enforceBarrelPattern,
|
|
453
698
|
"isolate-barrel-file": isolateBarrelFile,
|
|
454
|
-
"no-wildcard": noWildcard
|
|
455
|
-
|
|
699
|
+
"no-wildcard": noWildcard,
|
|
700
|
+
"no-cycle": noCycle
|
|
456
701
|
};
|
|
457
702
|
// Annotate the CommonJS export names for ESM import in node:
|
|
458
703
|
0 && (module.exports = {
|
package/dist/index.d.cts
CHANGED
|
@@ -14,6 +14,7 @@ declare const rules: {
|
|
|
14
14
|
globalAllowPaths: string[];
|
|
15
15
|
}[], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
16
16
|
"no-wildcard": _typescript_eslint_utils_ts_eslint.RuleModule<"NoWildcardImport" | "NoExportAll", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
17
|
+
"no-cycle": _typescript_eslint_utils_ts_eslint.RuleModule<"TransformedAliasResolveFailed" | "CircularDependency" | "BarrelInternalImportDisallowed", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
17
18
|
};
|
|
18
19
|
|
|
19
20
|
export { rules };
|
package/dist/index.d.ts
CHANGED
|
@@ -14,6 +14,7 @@ declare const rules: {
|
|
|
14
14
|
globalAllowPaths: string[];
|
|
15
15
|
}[], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
16
16
|
"no-wildcard": _typescript_eslint_utils_ts_eslint.RuleModule<"NoWildcardImport" | "NoExportAll", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
17
|
+
"no-cycle": _typescript_eslint_utils_ts_eslint.RuleModule<"TransformedAliasResolveFailed" | "CircularDependency" | "BarrelInternalImportDisallowed", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
17
18
|
};
|
|
18
19
|
|
|
19
20
|
export { rules };
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
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
|
+
|
|
1
8
|
// src/rules/enforce-barrel-pattern.ts
|
|
2
9
|
import "@typescript-eslint/types";
|
|
3
10
|
import path2 from "path";
|
|
@@ -6,8 +13,8 @@ import resolve from "resolve";
|
|
|
6
13
|
// src/utils/glob.ts
|
|
7
14
|
import FastGlob from "fast-glob";
|
|
8
15
|
var Glob = class {
|
|
9
|
-
static resolvePath(
|
|
10
|
-
const globResult = FastGlob.sync(
|
|
16
|
+
static resolvePath(path5, baseDir) {
|
|
17
|
+
const globResult = FastGlob.sync(path5, {
|
|
11
18
|
cwd: baseDir,
|
|
12
19
|
onlyDirectories: true,
|
|
13
20
|
absolute: true
|
|
@@ -289,13 +296,13 @@ var isolateBarrelFile = {
|
|
|
289
296
|
create(context) {
|
|
290
297
|
const option = context.options[0];
|
|
291
298
|
const baseDir = option.baseDir;
|
|
292
|
-
const absoluteGlobalAllowPaths = option.globalAllowPaths.flatMap((
|
|
293
|
-
return Glob.resolvePath(
|
|
299
|
+
const absoluteGlobalAllowPaths = option.globalAllowPaths.flatMap((path5) => {
|
|
300
|
+
return Glob.resolvePath(path5, baseDir);
|
|
294
301
|
});
|
|
295
302
|
const absoluteIsolations = option.isolations.flatMap((isolation) => {
|
|
296
303
|
const isolationPaths = Glob.resolvePath(isolation.path, baseDir);
|
|
297
|
-
const allowedPaths = isolation.allowedPaths.flatMap((
|
|
298
|
-
return Glob.resolvePath(
|
|
304
|
+
const allowedPaths = isolation.allowedPaths.flatMap((path5) => {
|
|
305
|
+
return Glob.resolvePath(path5, baseDir);
|
|
299
306
|
});
|
|
300
307
|
return isolationPaths.map((isolationPath) => ({
|
|
301
308
|
isolationPath,
|
|
@@ -411,12 +418,257 @@ var noWildcard = {
|
|
|
411
418
|
}
|
|
412
419
|
};
|
|
413
420
|
|
|
421
|
+
// src/rules/no-cycle.ts
|
|
422
|
+
import "@typescript-eslint/types";
|
|
423
|
+
import path4 from "path";
|
|
424
|
+
import resolve3 from "resolve";
|
|
425
|
+
var importGraph = /* @__PURE__ */ new Map();
|
|
426
|
+
var fileToImports = /* @__PURE__ */ new Map();
|
|
427
|
+
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
|
+
function isBarrelFile(filePath) {
|
|
448
|
+
const fileName = path4.basename(filePath);
|
|
449
|
+
return BARREL_ENTRY_POINT_FILE_NAMES.includes(
|
|
450
|
+
fileName
|
|
451
|
+
);
|
|
452
|
+
}
|
|
453
|
+
function getExportedModulesFromBarrel(barrelFileDir, sourceCode) {
|
|
454
|
+
const exportedModules = [];
|
|
455
|
+
const ast = sourceCode.ast;
|
|
456
|
+
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
|
+
}
|
|
470
|
+
}
|
|
471
|
+
}
|
|
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
|
+
}
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
return exportedModules;
|
|
491
|
+
}
|
|
492
|
+
var noCycle = {
|
|
493
|
+
meta: {
|
|
494
|
+
type: "problem",
|
|
495
|
+
docs: {
|
|
496
|
+
description: "Detect circular dependencies and enforce relative imports in barrel files."
|
|
497
|
+
},
|
|
498
|
+
schema: [],
|
|
499
|
+
messages: {
|
|
500
|
+
CircularDependency: "Circular dependency detected: {{cyclePath}}. This creates a dependency cycle that can cause runtime errors and make code harder to maintain.",
|
|
501
|
+
BarrelInternalImportDisallowed: "Barrel files (index.ts) must use relative imports (./ or ../) for internal modules. Importing via barrel file or absolute path is not allowed. Use relative path: '{{relativePath}}'",
|
|
502
|
+
TransformedAliasResolveFailed: "Transformed alias resolve failed. please check the alias config."
|
|
503
|
+
}
|
|
504
|
+
},
|
|
505
|
+
defaultOptions: [],
|
|
506
|
+
create(context) {
|
|
507
|
+
const absoluteCurrentFilePath = context.getFilename();
|
|
508
|
+
if (!importGraph.has(absoluteCurrentFilePath)) {
|
|
509
|
+
importGraph.set(absoluteCurrentFilePath, /* @__PURE__ */ new Set());
|
|
510
|
+
}
|
|
511
|
+
const currentImportsSet = importGraph.get(absoluteCurrentFilePath);
|
|
512
|
+
currentImportsSet.clear();
|
|
513
|
+
if (!fileToImports.has(absoluteCurrentFilePath)) {
|
|
514
|
+
fileToImports.set(absoluteCurrentFilePath, /* @__PURE__ */ new Set());
|
|
515
|
+
}
|
|
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
|
|
560
|
+
});
|
|
561
|
+
}
|
|
562
|
+
} catch (e) {
|
|
563
|
+
return null;
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
function checker(node) {
|
|
567
|
+
if (!node.source) {
|
|
568
|
+
return;
|
|
569
|
+
}
|
|
570
|
+
const rawImportPath = node.source.value;
|
|
571
|
+
const absoluteImportPath = resolveImportPath(rawImportPath);
|
|
572
|
+
if (!absoluteImportPath) {
|
|
573
|
+
return;
|
|
574
|
+
}
|
|
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;
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
}
|
|
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
|
+
}
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
if (isBarrelImportingInternal) {
|
|
624
|
+
return;
|
|
625
|
+
}
|
|
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, []);
|
|
641
|
+
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
|
+
});
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
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
|
+
}
|
|
662
|
+
};
|
|
663
|
+
}
|
|
664
|
+
};
|
|
665
|
+
|
|
414
666
|
// src/index.ts
|
|
415
667
|
var rules = {
|
|
416
668
|
"enforce-barrel-pattern": enforceBarrelPattern,
|
|
417
669
|
"isolate-barrel-file": isolateBarrelFile,
|
|
418
|
-
"no-wildcard": noWildcard
|
|
419
|
-
|
|
670
|
+
"no-wildcard": noWildcard,
|
|
671
|
+
"no-cycle": noCycle
|
|
420
672
|
};
|
|
421
673
|
export {
|
|
422
674
|
rules
|