

























@@ -1,6 +1,7 @@
1+import { spawnSync } from "node:child_process";
12import fs from "node:fs";
23import path from "node:path";
3-import { describe, expect, it } from "vitest";
4+import { describe, expect, it, vi } from "vitest";
4556const REPO_ROOT = path.resolve(import.meta.dirname, "../../../..");
67const SRC_ROOT = path.join(REPO_ROOT, "src");
@@ -14,6 +15,77 @@ const LEGACY_MIGRATION_MODULE_RE =
1415/legacy-config-migrate(?:\.js)?|legacy-config-migrations(?:\.[\w-]+)?(?:\.js)?/;
15161617function collectSourceFiles(dir: string, acc: string[] = []): string[] {
18+const externalFiles = listExternalSourceFiles(dir);
19+if (externalFiles) {
20+acc.push(...externalFiles);
21+return acc;
22+}
23+return collectSourceFilesByDirectory(dir, acc);
24+}
25+26+function listExternalSourceFiles(dir: string): string[] | null {
27+return listGitSourceFiles(dir) ?? listFindSourceFiles(dir);
28+}
29+30+function listGitSourceFiles(dir: string): string[] | null {
31+const relativeRoot = path.relative(REPO_ROOT, dir).replaceAll(path.sep, "/");
32+const result = spawnSync("git", ["ls-files", "--", relativeRoot], {
33+cwd: REPO_ROOT,
34+encoding: "utf8",
35+maxBuffer: 1024 * 1024 * 4,
36+stdio: ["ignore", "pipe", "ignore"],
37+});
38+if (result.status !== 0) {
39+return null;
40+}
41+return result.stdout
42+.split("\n")
43+.map((line) => line.trim())
44+.filter((line) => line.length > 0)
45+.map((file) => path.join(REPO_ROOT, file))
46+.filter(isOwnedSourceFile)
47+.toSorted();
48+}
49+50+function listFindSourceFiles(dir: string): string[] | null {
51+const result = spawnSync(
52+"find",
53+[
54+dir,
55+"(",
56+"-name",
57+"dist",
58+"-o",
59+"-name",
60+"node_modules",
61+")",
62+"-prune",
63+"-o",
64+"-type",
65+"f",
66+"-name",
67+"*.ts",
68+"-print",
69+],
70+{
71+cwd: REPO_ROOT,
72+encoding: "utf8",
73+maxBuffer: 1024 * 1024 * 4,
74+stdio: ["ignore", "pipe", "ignore"],
75+},
76+);
77+if (result.status !== 0) {
78+return null;
79+}
80+return result.stdout
81+.split("\n")
82+.map((line) => line.trim())
83+.filter((line) => line.length > 0)
84+.filter(isOwnedSourceFile)
85+.toSorted();
86+}
87+88+function collectSourceFilesByDirectory(dir: string, acc: string[] = []): string[] {
1789for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
1890if (entry.name === "dist" || entry.name === "node_modules") {
1991continue;
@@ -23,17 +95,26 @@ function collectSourceFiles(dir: string, acc: string[] = []): string[] {
2395if (fullPath === DOCTOR_ROOT) {
2496continue;
2597}
26-collectSourceFiles(fullPath, acc);
98+collectSourceFilesByDirectory(fullPath, acc);
2799continue;
28100}
29-if (!entry.isFile() || !entry.name.endsWith(".ts") || entry.name.endsWith(".test.ts")) {
101+if (!entry.isFile() || !isOwnedSourceFile(fullPath)) {
30102continue;
31103}
32104acc.push(fullPath);
33105}
34106return acc;
35107}
36108109+function isOwnedSourceFile(file: string): boolean {
110+return file.endsWith(".ts") && !file.endsWith(".test.ts") && !isUnderDoctorRoot(file);
111+}
112+113+function isUnderDoctorRoot(file: string): boolean {
114+const relativePath = path.relative(DOCTOR_ROOT, file);
115+return relativePath === "" || (!relativePath.startsWith("..") && !path.isAbsolute(relativePath));
116+}
117+37118function collectViolations(files: string[]): string[] {
38119const violations: string[] = [];
39120for (const file of files) {
@@ -58,6 +139,19 @@ function collectViolations(files: string[]): string[] {
58139}
5914060141describe("legacy config write ownership", () => {
142+it("lists ownership scan files without scanning source directories in-process", () => {
143+const readDir = vi.spyOn(fs, "readdirSync");
144+try {
145+const files = collectSourceFiles(SRC_ROOT);
146+147+expect(files.length).toBeGreaterThan(0);
148+expect(files.every(isOwnedSourceFile)).toBe(true);
149+expect(readDir).not.toHaveBeenCalled();
150+} finally {
151+readDir.mockRestore();
152+}
153+});
154+61155it("keeps legacy config repair flags and migration modules under doctor", () => {
62156const files = collectSourceFiles(SRC_ROOT);
63157const violations = collectViolations(files);
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。