This commit is contained in:
1 parent
8efd032cc6
commit
df38dccbf1
735 files changed
+128321
-120870
No files matched your search
+75
-60
@@ -2,81 +2,96 @@ import { access, readdir, readFile } from "node:fs/promises";
|
||||
import { dirname, extname, join, relative, resolve, sep } from "node:path";
|
||||
|
||||
export interface MissingLocalImport {
|
||||
importer: string;
|
||||
specifier: string;
|
||||
importer: string;
|
||||
specifier: string;
|
||||
}
|
||||
|
||||
const sourceExtensions = new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]);
|
||||
const sourceExtensions = new Set([
|
||||
".ts",
|
||||
".tsx",
|
||||
".js",
|
||||
".jsx",
|
||||
".mjs",
|
||||
".cjs",
|
||||
]);
|
||||
const resolutionSuffixes = [
|
||||
".ts",
|
||||
".tsx",
|
||||
".js",
|
||||
".jsx",
|
||||
".mjs",
|
||||
".cjs",
|
||||
".css",
|
||||
"/index.ts",
|
||||
"/index.tsx",
|
||||
"/index.js",
|
||||
"/index.jsx",
|
||||
".ts",
|
||||
".tsx",
|
||||
".js",
|
||||
".jsx",
|
||||
".mjs",
|
||||
".cjs",
|
||||
".css",
|
||||
"/index.ts",
|
||||
"/index.tsx",
|
||||
"/index.js",
|
||||
"/index.jsx",
|
||||
];
|
||||
const importPattern =
|
||||
/(?:import|export)\s+(?:[^"']*?\s+from\s+)?["']([^"']+)["']|import\(\s*["']([^"']+)["']\s*\)|require\(\s*["']([^"']+)["']\s*\)/g;
|
||||
/(?:import|export)\s+(?:[^"']*?\s+from\s+)?["']([^"']+)["']|import\(\s*["']([^"']+)["']\s*\)|require\(\s*["']([^"']+)["']\s*\)/g;
|
||||
|
||||
async function collectSourceFiles(directory: string): Promise<string[]> {
|
||||
const entries = await readdir(directory, { withFileTypes: true });
|
||||
const files = await Promise.all(
|
||||
entries.map(async (entry) => {
|
||||
const path = join(directory, entry.name);
|
||||
if (entry.isDirectory()) return collectSourceFiles(path);
|
||||
if (/\.(?:test|spec)\.[cm]?[jt]sx?$/.test(entry.name)) return [];
|
||||
return sourceExtensions.has(extname(entry.name)) ? [path] : [];
|
||||
}),
|
||||
);
|
||||
return files.flat();
|
||||
const entries = await readdir(directory, { withFileTypes: true });
|
||||
const files = await Promise.all(
|
||||
entries.map(async (entry) => {
|
||||
const path = join(directory, entry.name);
|
||||
if (entry.isDirectory()) return collectSourceFiles(path);
|
||||
if (/\.(?:test|spec)\.[cm]?[jt]sx?$/.test(entry.name)) return [];
|
||||
return sourceExtensions.has(extname(entry.name)) ? [path] : [];
|
||||
}),
|
||||
);
|
||||
return files.flat();
|
||||
}
|
||||
|
||||
async function exists(path: string): Promise<boolean> {
|
||||
try {
|
||||
await access(path);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
await access(path);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function resolvesToLocalFile(base: string): Promise<boolean> {
|
||||
if (extname(base) && (await exists(base))) return true;
|
||||
for (const suffix of resolutionSuffixes) {
|
||||
if (await exists(`${base}${suffix}`)) return true;
|
||||
}
|
||||
return false;
|
||||
if (extname(base) && (await exists(base))) return true;
|
||||
for (const suffix of resolutionSuffixes) {
|
||||
if (await exists(`${base}${suffix}`)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export async function findMissingLocalImports(root: string): Promise<MissingLocalImport[]> {
|
||||
const src = resolve(root, "src");
|
||||
const files = await collectSourceFiles(src);
|
||||
const missing: MissingLocalImport[] = [];
|
||||
export async function findMissingLocalImports(
|
||||
root: string,
|
||||
): Promise<MissingLocalImport[]> {
|
||||
const src = resolve(root, "src");
|
||||
const files = await collectSourceFiles(src);
|
||||
const missing: MissingLocalImport[] = [];
|
||||
|
||||
for (const importer of files) {
|
||||
const source = await readFile(importer, "utf8");
|
||||
for (const match of source.matchAll(importPattern)) {
|
||||
const specifier = match[1] ?? match[2] ?? match[3];
|
||||
if (!specifier?.startsWith("@/") && !specifier?.startsWith("./") && !specifier?.startsWith("../"))
|
||||
continue;
|
||||
const base = specifier.startsWith("@/")
|
||||
? resolve(src, specifier.slice(2))
|
||||
: resolve(dirname(importer), specifier);
|
||||
if (!(await resolvesToLocalFile(base))) {
|
||||
missing.push({
|
||||
importer: relative(root, importer).split(sep).join("/"),
|
||||
specifier,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const importer of files) {
|
||||
const source = await readFile(importer, "utf8");
|
||||
for (const match of source.matchAll(importPattern)) {
|
||||
const specifier = match[1] ?? match[2] ?? match[3];
|
||||
if (
|
||||
!specifier?.startsWith("@/") &&
|
||||
!specifier?.startsWith("./") &&
|
||||
!specifier?.startsWith("../")
|
||||
)
|
||||
continue;
|
||||
const base = specifier.startsWith("@/")
|
||||
? resolve(src, specifier.slice(2))
|
||||
: resolve(dirname(importer), specifier);
|
||||
if (!(await resolvesToLocalFile(base))) {
|
||||
missing.push({
|
||||
importer: relative(root, importer).split(sep).join("/"),
|
||||
specifier,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return missing.sort(
|
||||
(a, b) => a.importer.localeCompare(b.importer) || a.specifier.localeCompare(b.specifier),
|
||||
);
|
||||
return missing.sort(
|
||||
(a, b) =>
|
||||
a.importer.localeCompare(b.importer) ||
|
||||
a.specifier.localeCompare(b.specifier),
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user