





















@@ -1,7 +1,8 @@
1-import { existsSync, readdirSync, readFileSync } from "node:fs";
1+import { spawnSync } from "node:child_process";
2+import fs from "node:fs";
23import { basename, dirname, relative, resolve, sep } from "node:path";
34import { fileURLToPath } from "node:url";
4-import { describe, expect, it } from "vitest";
5+import { describe, expect, it, vi } from "vitest";
56import { loadPluginManifestRegistry } from "../manifest-registry.js";
6778type SharedFamilyHookKind = "replay" | "stream" | "tool-compat";
@@ -49,10 +50,40 @@ function toRepoRelative(path: string): string {
4950return relative(REPO_ROOT, path).split(sep).join("/");
5051}
515253+function shouldSkipScannedPath(relativePath: string): boolean {
54+return relativePath.split("/").some((part) => part === "dist" || part === "node_modules");
55+}
56+57+function listGitFiles(dir: string): string[] | null {
58+const relativeDir = toRepoRelative(dir);
59+if (!relativeDir || relativeDir.startsWith("..")) {
60+return null;
61+}
62+const result = spawnSync("git", ["ls-files", "--", relativeDir], {
63+cwd: REPO_ROOT,
64+encoding: "utf8",
65+stdio: ["ignore", "pipe", "ignore"],
66+});
67+if (result.status !== 0) {
68+return null;
69+}
70+return result.stdout
71+.split("\n")
72+.map((line) => line.trim().replaceAll("\\", "/"))
73+.filter((line) => line.length > 0 && !shouldSkipScannedPath(line))
74+.map((line) => resolve(REPO_ROOT, line))
75+.toSorted();
76+}
77+5278function listFiles(dir: string): string[] {
79+const gitFiles = listGitFiles(dir);
80+if (gitFiles) {
81+return gitFiles;
82+}
83+5384const files: string[] = [];
548555-for (const entry of readdirSync(dir, { withFileTypes: true })) {
86+for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
5687if (entry.name === "dist" || entry.name === "node_modules") {
5788continue;
5889}
@@ -82,7 +113,7 @@ function resolveBundledPluginSourceRoot(rootDir: string, workspaceDir?: string):
82113return workspaceDir;
83114}
84115const sourceRoot = resolve(REPO_ROOT, "extensions", basename(rootDir));
85-return existsSync(sourceRoot) ? sourceRoot : rootDir;
116+return fs.existsSync(sourceRoot) ? sourceRoot : rootDir;
86117}
8711888119function collectSharedFamilyProviders(): Map<string, SharedFamilyProviderInventory> {
@@ -93,7 +124,7 @@ function collectSharedFamilyProviders(): Map<string, SharedFamilyProviderInvento
93124if (!filePath.endsWith(".ts") || filePath.endsWith(".test.ts")) {
94125continue;
95126}
96-const source = readFileSync(filePath, "utf8");
127+const source = fs.readFileSync(filePath, "utf8");
97128const matchedKinds = SHARED_FAMILY_HOOK_PATTERNS.filter(({ regex }) => regex.test(source));
98129if (matchedKinds.length === 0) {
99130continue;
@@ -121,7 +152,7 @@ function collectProviderBoundaryTests(): Map<string, Set<string>> {
121152if (!filePath.endsWith(".test.ts")) {
122153continue;
123154}
124-const source = readFileSync(filePath, "utf8");
155+const source = fs.readFileSync(filePath, "utf8");
125156if (!PROVIDER_BOUNDARY_TEST_SIGNALS.some((signal) => signal.test(source))) {
126157continue;
127158}
@@ -149,7 +180,7 @@ function collectSharedFamilyAssignments(): Map<string, ExpectedSharedFamilyContr
149180if (!filePath.endsWith(".ts") || filePath.endsWith(".test.ts")) {
150181continue;
151182}
152-const source = readFileSync(filePath, "utf8");
183+const source = fs.readFileSync(filePath, "utf8");
153184const replayFamilies = listMatchingFamilies(source, replayPattern);
154185const streamFamilies = listMatchingFamilies(source, streamPattern);
155186const toolCompatFamilies = listMatchingFamilies(source, toolCompatPattern);
@@ -184,6 +215,20 @@ function collectSharedFamilyAssignments(): Map<string, ExpectedSharedFamilyContr
184215}
185216186217describe("provider family plugin-boundary inventory", () => {
218+it("lists bundled plugin files from git without walking plugin roots", () => {
219+const bundledRoots = listBundledPluginRoots();
220+const readDir = vi.spyOn(fs, "readdirSync");
221+try {
222+const files = bundledRoots.flatMap((plugin) => listFiles(plugin.rootDir));
223+224+expect(files.length).toBeGreaterThan(0);
225+expect(files.some((file) => toRepoRelative(file).startsWith("extensions/"))).toBe(true);
226+expect(readDir).not.toHaveBeenCalled();
227+} finally {
228+readDir.mockRestore();
229+}
230+});
231+187232it("keeps shared-family provider hooks covered by at least one plugin-boundary test", () => {
188233const sharedFamilyProviders = collectSharedFamilyProviders();
189234const providerBoundaryTests = collectProviderBoundaryTests();
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。