auto-cr-rules 2.0.76 → 2.0.78
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.
|
@@ -16,10 +16,19 @@ exports.noCircularDependencies = void 0;
|
|
|
16
16
|
var fs_1 = __importDefault(require("fs"));
|
|
17
17
|
var path_1 = __importDefault(require("path"));
|
|
18
18
|
var types_1 = require("../types");
|
|
19
|
+
/**
|
|
20
|
+
* 检测相对路径形成的循环依赖:
|
|
21
|
+
* - 仅解析 .ts/.tsx/.js/.jsx/.mjs/.cjs;
|
|
22
|
+
* - 从当前文件的 import 出发,沿依赖图寻找回到自身的路径;
|
|
23
|
+
* - 输出完整环路链路,便于定位循环发生的文件。
|
|
24
|
+
*/
|
|
19
25
|
var SUPPORTED_EXTENSIONS = ['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs'];
|
|
26
|
+
// 避免在大型仓库里构图过深或过大导致卡顿。
|
|
20
27
|
var MAX_GRAPH_NODES = 2000;
|
|
21
28
|
var MAX_GRAPH_DEPTH = 80;
|
|
29
|
+
// 跨文件缓存解析结果,减少重复 IO 与正则扫描。
|
|
22
30
|
var resolvedImportCache = new Map();
|
|
31
|
+
// 记录已上报的环路(做规范化),避免同一环路重复报错。
|
|
23
32
|
var reportedCycles = new Set();
|
|
24
33
|
exports.noCircularDependencies = (0, types_1.defineRule)('no-circular-dependencies', { tag: 'base', severity: types_1.RuleSeverity.Warning }, function (_a) {
|
|
25
34
|
var _b, _c;
|
|
@@ -28,7 +37,7 @@ exports.noCircularDependencies = (0, types_1.defineRule)('no-circular-dependenci
|
|
|
28
37
|
var root = resolveProjectRoot(origin);
|
|
29
38
|
var moduleStart = (_c = (_b = ast.span) === null || _b === void 0 ? void 0 : _b.start) !== null && _c !== void 0 ? _c : 0;
|
|
30
39
|
var lineIndex = buildLineIndex(source);
|
|
31
|
-
//
|
|
40
|
+
// 先用 SWC 提供的 import 列表初始化当前文件的依赖,保证准确性与性能。
|
|
32
41
|
resolvedImportCache.set(origin, resolveFromReferences(origin, helpers.imports, root));
|
|
33
42
|
for (var _i = 0, _d = helpers.imports; _i < _d.length; _i++) {
|
|
34
43
|
var reference = _d[_i];
|
|
@@ -39,6 +48,7 @@ exports.noCircularDependencies = (0, types_1.defineRule)('no-circular-dependenci
|
|
|
39
48
|
if (!target) {
|
|
40
49
|
continue;
|
|
41
50
|
}
|
|
51
|
+
// 从目标模块回溯,如果能再次回到 origin,即存在环路。
|
|
42
52
|
var pathToOrigin = findPathToOrigin(target, origin, root);
|
|
43
53
|
if (!pathToOrigin) {
|
|
44
54
|
continue;
|
|
@@ -49,6 +59,7 @@ exports.noCircularDependencies = (0, types_1.defineRule)('no-circular-dependenci
|
|
|
49
59
|
continue;
|
|
50
60
|
}
|
|
51
61
|
reportedCycles.add(cycleKey);
|
|
62
|
+
// 统一输出相对路径,便于直接定位到仓库内文件。
|
|
52
63
|
var displayChain = formatCycle(cycle, root);
|
|
53
64
|
var description = messages.circularDependency({ chain: displayChain });
|
|
54
65
|
var suggestions = language === 'zh'
|
|
@@ -60,6 +71,7 @@ exports.noCircularDependencies = (0, types_1.defineRule)('no-circular-dependenci
|
|
|
60
71
|
{ text: 'Split modules to avoid mutual dependencies.' },
|
|
61
72
|
{ text: 'Extract shared logic into a dedicated module to break the cycle.' },
|
|
62
73
|
];
|
|
74
|
+
// 优先使用 SWC 的 span 计算行号,作为高可信位置;失败时再用文本匹配兜底。
|
|
63
75
|
var computedLine = reference.span
|
|
64
76
|
? resolveLine(lineIndex, bytePosToCharIndex(source, moduleStart, reference.span.start))
|
|
65
77
|
: undefined;
|
|
@@ -74,6 +86,7 @@ exports.noCircularDependencies = (0, types_1.defineRule)('no-circular-dependenci
|
|
|
74
86
|
}, reference.span);
|
|
75
87
|
}
|
|
76
88
|
});
|
|
89
|
+
// 选择项目根目录:优先使用当前工作目录,找不到则向上寻找最近的 package.json。
|
|
77
90
|
var resolveProjectRoot = function (filePath) {
|
|
78
91
|
var cwd = path_1.default.resolve(process.cwd());
|
|
79
92
|
if (isWithinRoot(filePath, cwd)) {
|
|
@@ -90,10 +103,12 @@ var resolveProjectRoot = function (filePath) {
|
|
|
90
103
|
}
|
|
91
104
|
return cwd;
|
|
92
105
|
};
|
|
106
|
+
// 防止解析路径逃逸到仓库外,避免跨项目误报。
|
|
93
107
|
var isWithinRoot = function (filePath, root) {
|
|
94
108
|
var relative = path_1.default.relative(root, filePath);
|
|
95
109
|
return relative === '' || (!relative.startsWith('..') && !path_1.default.isAbsolute(relative));
|
|
96
110
|
};
|
|
111
|
+
// 将当前文件的 import 列表解析为真实文件路径(只处理相对路径)。
|
|
97
112
|
var resolveFromReferences = function (origin, references, root) {
|
|
98
113
|
var resolved = new Set();
|
|
99
114
|
for (var _i = 0, references_1 = references; _i < references_1.length; _i++) {
|
|
@@ -108,6 +123,8 @@ var resolveFromReferences = function (origin, references, root) {
|
|
|
108
123
|
}
|
|
109
124
|
return Array.from(resolved);
|
|
110
125
|
};
|
|
126
|
+
// DFS 搜索依赖图中是否存在一条从 start 回到 origin 的路径。
|
|
127
|
+
// 通过节点数与深度上限,避免超大项目中搜索失控。
|
|
111
128
|
var findPathToOrigin = function (start, origin, root) {
|
|
112
129
|
var nodesVisited = 0;
|
|
113
130
|
var visiting = new Set();
|
|
@@ -145,6 +162,8 @@ var findPathToOrigin = function (start, origin, root) {
|
|
|
145
162
|
};
|
|
146
163
|
return walk(start, 0);
|
|
147
164
|
};
|
|
165
|
+
// 读取文件并通过简单正则抽取 import/require/export-from。
|
|
166
|
+
// 这里不重新解析 AST,成本低但可能漏掉非常规写法。
|
|
148
167
|
var getResolvedImports = function (filePath, root) {
|
|
149
168
|
var cached = resolvedImportCache.get(filePath);
|
|
150
169
|
if (cached) {
|
|
@@ -179,6 +198,7 @@ var readFileSafe = function (filePath) {
|
|
|
179
198
|
return null;
|
|
180
199
|
}
|
|
181
200
|
};
|
|
201
|
+
// 用正则匹配常见的导入写法,覆盖 import / dynamic import / require / export-from。
|
|
182
202
|
var extractImportSpecifiers = function (source) {
|
|
183
203
|
var results = [];
|
|
184
204
|
var patterns = [
|
|
@@ -196,6 +216,8 @@ var extractImportSpecifiers = function (source) {
|
|
|
196
216
|
}
|
|
197
217
|
return results;
|
|
198
218
|
};
|
|
219
|
+
// 解析相对路径到真实文件:支持自动补全扩展名与目录 index。
|
|
220
|
+
// 并确保解析结果在项目根目录内。
|
|
199
221
|
var resolveImportFile = function (fromFile, specifier, root) {
|
|
200
222
|
if (!specifier.startsWith('.')) {
|
|
201
223
|
return null;
|
|
@@ -267,6 +289,7 @@ var resolveFromDirectory = function (basePath) {
|
|
|
267
289
|
}
|
|
268
290
|
return null;
|
|
269
291
|
};
|
|
292
|
+
// 将环路规范化为稳定 key,避免同一环路从不同入口重复报错。
|
|
270
293
|
var buildCycleKey = function (cycle) {
|
|
271
294
|
if (cycle.length <= 2) {
|
|
272
295
|
return cycle.join('->');
|
|
@@ -282,6 +305,7 @@ var buildCycleKey = function (cycle) {
|
|
|
282
305
|
}
|
|
283
306
|
return best;
|
|
284
307
|
};
|
|
308
|
+
// 输出尽量相对路径,便于直接定位文件。
|
|
285
309
|
var formatCycle = function (cycle, root) {
|
|
286
310
|
var formatted = cycle.map(function (entry) {
|
|
287
311
|
var relative = path_1.default.relative(root, entry);
|
package/package.json
CHANGED