auto-cr-rules
Version:
Extensible static analysis rule set for the auto-cr automated code review toolkit
132 lines (131 loc) • 5.09 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.noDeepRelativeImports = void 0;
var types_1 = require("../types");
var MAX_DEPTH = 2;
exports.noDeepRelativeImports = (0, types_1.defineRule)('no-deep-relative-imports', { tag: 'base', severity: types_1.RuleSeverity.Warning }, function (_a) {
var _b, _c;
var ast = _a.ast, helpers = _a.helpers, messages = _a.messages, language = _a.language, source = _a.source;
// Build a per-file line index so we can convert byte offsets emitted by SWC back to line numbers.
var moduleStart = (_c = (_b = ast.span) === null || _b === void 0 ? void 0 : _b.start) !== null && _c !== void 0 ? _c : 0;
var lineIndex = buildLineIndex(source);
for (var _i = 0, _d = helpers.imports; _i < _d.length; _i++) {
var reference = _d[_i];
if (!helpers.isRelativePath(reference.value)) {
continue;
}
var depth = helpers.relativeDepth(reference.value);
if (depth > MAX_DEPTH) {
var description = messages.noDeepRelativeImports({ value: reference.value, maxDepth: MAX_DEPTH });
var suggestions = language === 'zh'
? [
{ text: '使用别名路径(如 @shared/deep/utils)' },
{ text: '或在上层聚合导出,避免过深相对路径。' },
]
: [
{ text: 'Use a path alias (for example: @shared/deep/utils).' },
{ text: 'Create an index file at a higher level to re-export the module and shorten the import.' },
];
var computedLine = reference.span
? resolveLine(lineIndex, bytePosToCharIndex(source, moduleStart, reference.span.start))
: undefined;
var fallbackLine = findImportLine(source, reference.value);
// Prefer the larger value so we never point at leading block comments when the byte offset is truncated.
var line = selectLineNumber(computedLine, fallbackLine);
helpers.reportViolation({
description: description,
code: reference.value,
suggestions: suggestions,
span: reference.span,
line: line,
}, reference.span);
}
}
});
var buildLineIndex = function (source) {
// Track every newline so we can binary-search the surrounding line for any byte position.
var offsets = [0];
for (var index = 0; index < source.length; index += 1) {
if (source[index] === '\n') {
offsets.push(index + 1);
}
}
return { offsets: offsets };
};
var resolveLine = function (_a, position) {
var offsets = _a.offsets;
var low = 0;
var high = offsets.length - 1;
while (low <= high) {
var mid = Math.floor((low + high) / 2);
var current = offsets[mid];
if (current === position) {
return mid + 1;
}
if (current < position) {
low = mid + 1;
}
else {
high = mid - 1;
}
}
return high + 1;
};
var readUtf8Character = function (source, index, code) {
if (code <= 0x7f) {
return { bytes: 1, nextIndex: index + 1 };
}
if (code <= 0x7ff) {
return { bytes: 2, nextIndex: index + 1 };
}
if (code >= 0xd800 && code <= 0xdbff && index + 1 < source.length) {
var next = source.charCodeAt(index + 1);
if (next >= 0xdc00 && next <= 0xdfff) {
return { bytes: 4, nextIndex: index + 2 };
}
}
return { bytes: 3, nextIndex: index + 1 };
};
var bytePosToCharIndex = function (source, moduleStart, bytePos) {
var target = Math.max(bytePos - moduleStart, 0);
if (target === 0) {
return 0;
}
var index = 0;
var byteOffset = 0;
while (index < source.length) {
var code = source.charCodeAt(index);
var _a = readUtf8Character(source, index, code), bytes = _a.bytes, nextIndex = _a.nextIndex;
if (byteOffset + bytes > target) {
return index;
}
byteOffset += bytes;
index = nextIndex;
}
return source.length;
};
var findImportLine = function (source, value) {
var lines = source.split(/\r?\n/);
for (var index = 0; index < lines.length; index += 1) {
var line = lines[index];
// Look for the literal import string. This is slower than span math but provides a robust fallback.
if (line.includes('import') && line.includes(value)) {
return index + 1;
}
}
return undefined;
};
var selectLineNumber = function (computed, fallback) {
// If one of the sources is missing, prefer the other. When both exist, use the larger line number so
// we avoid pointing at comment blocks above the actual statement.
if (fallback === undefined) {
return computed;
}
if (computed === undefined) {
return fallback;
}
if (computed < fallback) {
return fallback;
}
return computed;
};