























@@ -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 repoRoot = path.resolve(import.meta.dirname, "../..");
67const CODE_EXTENSIONS = new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]);
@@ -13,7 +14,48 @@ type SuppressionEntry = {
1314rule: string;
1415};
151617+function isProductionCodeFile(relativePath: string): boolean {
18+const basename = path.posix.basename(relativePath);
19+if (!CODE_EXTENSIONS.has(path.extname(relativePath))) {
20+return false;
21+}
22+if (basename.startsWith("__rootdir_boundary_canary__.")) {
23+return false;
24+}
25+return !(
26+relativePath.includes("/test/") ||
27+relativePath.endsWith(".test.ts") ||
28+relativePath.endsWith(".test.tsx") ||
29+relativePath.endsWith(".spec.ts") ||
30+relativePath.endsWith(".spec.tsx")
31+);
32+}
33+34+function listGitCodeFiles(root: string): string[] | null {
35+const result = spawnSync("git", ["ls-files", "--", root], {
36+cwd: repoRoot,
37+encoding: "utf8",
38+stdio: ["ignore", "pipe", "ignore"],
39+});
40+if (result.status !== 0) {
41+return null;
42+}
43+return result.stdout
44+.split("\n")
45+.map((line) => line.trim().replaceAll("\\", "/"))
46+.filter((line) => line.length > 0 && isProductionCodeFile(line));
47+}
48+1649function walkCodeFiles(dir: string, files: string[] = []): string[] {
50+const relativeRoot = path.relative(repoRoot, dir).replaceAll(path.sep, "/");
51+if (relativeRoot && !relativeRoot.startsWith("..") && !path.isAbsolute(relativeRoot)) {
52+const gitFiles = listGitCodeFiles(relativeRoot);
53+if (gitFiles) {
54+files.push(...gitFiles);
55+return files;
56+}
57+}
58+1759if (!fs.existsSync(dir)) {
1860return files;
1961}
@@ -26,20 +68,8 @@ function walkCodeFiles(dir: string, files: string[] = []): string[] {
2668walkCodeFiles(fullPath, files);
2769continue;
2870}
29-if (!CODE_EXTENSIONS.has(path.extname(entry.name))) {
30-continue;
31-}
32-if (entry.name.startsWith("__rootdir_boundary_canary__.")) {
33-continue;
34-}
3571const relativePath = path.relative(repoRoot, fullPath).replaceAll(path.sep, "/");
36-if (
37-relativePath.includes("/test/") ||
38-relativePath.endsWith(".test.ts") ||
39-relativePath.endsWith(".test.tsx") ||
40-relativePath.endsWith(".spec.ts") ||
41-relativePath.endsWith(".spec.tsx")
42-) {
72+if (!isProductionCodeFile(relativePath)) {
4373continue;
4474}
4575files.push(relativePath);
@@ -76,6 +106,19 @@ function summarizeSuppressions(entries: readonly SuppressionEntry[]): string[] {
76106}
7710778108describe("production lint suppressions", () => {
109+it("lists production files from git without walking source roots", () => {
110+const readdirSync = vi.spyOn(fs, "readdirSync");
111+try {
112+const files = ROOTS.flatMap((root) => walkCodeFiles(path.join(repoRoot, root))).toSorted();
113+114+expect(files.length).toBeGreaterThan(0);
115+expect(files.some((file) => file.endsWith(".test.ts"))).toBe(false);
116+expect(readdirSync).not.toHaveBeenCalled();
117+} finally {
118+readdirSync.mockRestore();
119+}
120+});
121+79122it("keeps the intentional production suppression tail on an explicit allowlist", () => {
80123expect(summarizeSuppressions(collectProductionLintSuppressions())).toEqual([
81124"extensions/browser/src/browser/pw-tools-core.interactions.ts|@typescript-eslint/no-implied-eval|2",
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。