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/README.ko.md +1 -1
- package/README.md +1 -1
- package/dist/index.cjs +310 -263
- package/dist/index.js +310 -270
- package/package.json +1 -1
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
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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
|
-
|
|
204
|
-
|
|
205
|
-
|
|
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
|
|
227
|
-
const invalidImported = !targetPathEntryPointed &&
|
|
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
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
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
|
|
372
|
-
if (!
|
|
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
|
|
454
|
-
|
|
455
|
-
|
|
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
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
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
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
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
|
|
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
|
|
508
|
-
|
|
509
|
-
|
|
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
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
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
|
-
|
|
517
|
-
|
|
518
|
-
const
|
|
519
|
-
const
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
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
|
-
|
|
567
|
-
if (
|
|
568
|
-
|
|
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
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
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
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
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
|
-
|
|
597
|
-
|
|
598
|
-
)
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
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 (
|
|
624
|
-
return
|
|
689
|
+
if (hasBidirectionalCycle(currentFile, absolutePath)) {
|
|
690
|
+
return `${currentFile} \u2192 ${absolutePath} \u2192 ${currentFile}`;
|
|
625
691
|
}
|
|
626
|
-
const
|
|
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
|
-
|
|
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
|
|
654
|
-
|
|
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
|
};
|