






















@@ -0,0 +1,131 @@
1+import path from "node:path";
2+3+const JS_DIST_FILE_RE = /^dist\/.*\.(?:cjs|js|mjs)$/u;
4+5+function normalizePackagePath(value) {
6+return value.replace(/\\/gu, "/").replace(/^package\//u, "");
7+}
8+9+function stripSpecifierSuffix(value) {
10+return value.replace(/[?#].*$/u, "");
11+}
12+13+function resolveDistImportPath(importerPath, specifier) {
14+if (!specifier.startsWith(".")) {
15+return null;
16+}
17+const stripped = stripSpecifierSuffix(specifier);
18+if (!stripped) {
19+return null;
20+}
21+return path.posix.normalize(path.posix.join(path.posix.dirname(importerPath), stripped));
22+}
23+24+function findStatementStart(source, index) {
25+return (
26+Math.max(
27+source.lastIndexOf(";", index),
28+source.lastIndexOf("{", index),
29+source.lastIndexOf("}", index),
30+source.lastIndexOf("\n", index),
31+source.lastIndexOf("\r", index),
32+) + 1
33+);
34+}
35+36+function isImportSpecifierContext(source, index) {
37+const dynamicPrefix = source.slice(Math.max(0, index - 32), index);
38+if (/\bimport\s*\(\s*$/u.test(dynamicPrefix)) {
39+return true;
40+}
41+const statementPrefix = source.slice(findStatementStart(source, index), index).trimStart();
42+return (
43+/^(?:import|export)\b[\s\S]*\bfrom\s*$/u.test(statementPrefix) ||
44+/^import\s*$/u.test(statementPrefix)
45+);
46+}
47+48+function collectImportSpecifiers(source) {
49+const specifiers = [];
50+let inBlockComment = false;
51+let inLineComment = false;
52+for (let index = 0; index < source.length; index += 1) {
53+if (inBlockComment) {
54+if (source[index] === "*" && source[index + 1] === "/") {
55+inBlockComment = false;
56+index += 1;
57+}
58+continue;
59+}
60+if (inLineComment) {
61+if (source[index] === "\n" || source[index] === "\r") {
62+inLineComment = false;
63+}
64+continue;
65+}
66+if (source[index] === "/" && source[index + 1] === "*") {
67+inBlockComment = true;
68+index += 1;
69+continue;
70+}
71+if (source[index] === "/" && source[index + 1] === "/") {
72+inLineComment = true;
73+index += 1;
74+continue;
75+}
76+77+const quote = source[index];
78+if (quote !== '"' && quote !== "'") {
79+continue;
80+}
81+82+let cursor = index + 1;
83+let value = "";
84+while (cursor < source.length) {
85+const char = source[cursor];
86+if (char === "\\") {
87+value += source.slice(cursor, cursor + 2);
88+cursor += 2;
89+continue;
90+}
91+if (char === quote) {
92+break;
93+}
94+value += char;
95+cursor += 1;
96+}
97+if (cursor >= source.length) {
98+break;
99+}
100+101+if (value.startsWith(".")) {
102+if (isImportSpecifierContext(source, index)) {
103+specifiers.push(value);
104+}
105+}
106+index = cursor;
107+}
108+return specifiers;
109+}
110+111+export function collectPackageDistImportErrors(params) {
112+const files = [...new Set(params.files.map(normalizePackagePath))];
113+const fileSet = new Set(files);
114+const errors = [];
115+116+for (const importerPath of files.toSorted((left, right) => left.localeCompare(right))) {
117+if (!JS_DIST_FILE_RE.test(importerPath) || importerPath.includes("/node_modules/")) {
118+continue;
119+}
120+const source = params.readText(importerPath);
121+for (const specifier of collectImportSpecifiers(source)) {
122+const importedPath = resolveDistImportPath(importerPath, specifier);
123+if (!importedPath || fileSet.has(importedPath)) {
124+continue;
125+}
126+errors.push(`${importerPath} imports missing ${importedPath}`);
127+}
128+}
129+130+return errors;
131+}
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。