




















@@ -1,7 +1,8 @@
1-import { readdirSync, readFileSync, statSync } from "node:fs";
1+import { spawnSync } from "node:child_process";
2+import fs, { readFileSync } from "node:fs";
23import { dirname, relative, resolve } from "node:path";
34import { fileURLToPath } from "node:url";
4-import { describe, expect, it } from "vitest";
5+import { describe, expect, it, vi } from "vitest";
5667const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), "../..");
78const allowedRuntimeResolverRefs = new Set([
@@ -12,26 +13,101 @@ const allowedRuntimeResolverRefs = new Set([
1213]);
13141415function listSourceFiles(dir: string): string[] {
16+const externalFiles = listExternalSourceFiles(dir);
17+if (externalFiles) {
18+return externalFiles;
19+}
20+return listSourceFilesByDirectory(dir);
21+}
22+23+function listExternalSourceFiles(dir: string): string[] | null {
24+return listGitSourceFiles(dir) ?? listFindSourceFiles(dir);
25+}
26+27+function listGitSourceFiles(dir: string): string[] | null {
28+const relativeRoot = toPosix(relative(repoRoot, dir)) || ".";
29+const result = spawnSync("git", ["ls-files", "--", relativeRoot], {
30+cwd: repoRoot,
31+encoding: "utf8",
32+maxBuffer: 1024 * 1024 * 4,
33+stdio: ["ignore", "pipe", "ignore"],
34+});
35+if (result.status !== 0) {
36+return null;
37+}
38+return result.stdout
39+.split("\n")
40+.map((line) => line.trim())
41+.filter((line) => line.length > 0)
42+.map((file) => resolve(repoRoot, file))
43+.filter(isProductionTypeScriptFile)
44+.toSorted();
45+}
46+47+function listFindSourceFiles(dir: string): string[] | null {
48+const result = spawnSync("find", [dir, "-type", "f", "-name", "*.ts"], {
49+cwd: repoRoot,
50+encoding: "utf8",
51+maxBuffer: 1024 * 1024 * 4,
52+stdio: ["ignore", "pipe", "ignore"],
53+});
54+if (result.status !== 0) {
55+return null;
56+}
57+return result.stdout
58+.split("\n")
59+.map((line) => line.trim())
60+.filter((line) => line.length > 0)
61+.map((file) => resolve(repoRoot, file))
62+.filter(isProductionTypeScriptFile)
63+.toSorted();
64+}
65+66+function listSourceFilesByDirectory(dir: string): string[] {
1567const files: string[] = [];
16-for (const entry of readdirSync(dir)) {
68+for (const entry of fs.readdirSync(dir)) {
1769if (entry === "node_modules" || entry === "dist") {
1870continue;
1971}
2072const path = resolve(dir, entry);
21-const stat = statSync(path);
73+const stat = fs.statSync(path);
2274if (stat.isDirectory()) {
23-files.push(...listSourceFiles(path));
75+files.push(...listSourceFilesByDirectory(path));
2476continue;
2577}
26-if (!path.endsWith(".ts") || path.endsWith(".test.ts") || path.endsWith(".test.tsx")) {
78+if (!isProductionTypeScriptFile(path)) {
2779continue;
2880}
2981files.push(path);
3082}
3183return files;
3284}
338586+function isProductionTypeScriptFile(path: string): boolean {
87+return path.endsWith(".ts") && !path.endsWith(".test.ts") && !path.endsWith(".test.tsx");
88+}
89+90+function toPosix(value: string): string {
91+return value.split("\\").join("/");
92+}
93+3494describe("runtime plugin registry boundary", () => {
95+it("lists source files without scanning src in-process", () => {
96+const readDir = vi.spyOn(fs, "readdirSync");
97+const stat = vi.spyOn(fs, "statSync");
98+try {
99+const files = listSourceFiles(resolve(repoRoot, "src"));
100+101+expect(files.length).toBeGreaterThan(0);
102+expect(files.every(isProductionTypeScriptFile)).toBe(true);
103+expect(readDir).not.toHaveBeenCalled();
104+expect(stat).not.toHaveBeenCalled();
105+} finally {
106+readDir.mockRestore();
107+stat.mockRestore();
108+}
109+});
110+35111it("keeps runtime registry resolution behind the loader boundary", () => {
36112const offenders = listSourceFiles(resolve(repoRoot, "src"))
37113.map((path) => ({
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。