Initial Commit
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
/**
|
||||
* Grep guard — fail if `src/` contains any disallowed construct:
|
||||
* `any`, `ts-ignore`, `ts-expect-error`, `TODO`, `FIXME`, `@deprecated`,
|
||||
* `.skip(`, `.only(`. Test fixtures and benchmarks are excluded.
|
||||
*/
|
||||
import { readdir, readFile } from "node:fs/promises";
|
||||
import { dirname, join, relative, resolve } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { describe, expect, it } from "../src/runner/index.js";
|
||||
|
||||
const here = dirname(fileURLToPath(import.meta.url));
|
||||
const srcRoot = resolve(here, "..", "src");
|
||||
|
||||
const PATTERNS: Array<{ name: string; regex: RegExp }> = [
|
||||
{ name: "any", regex: /(?<![a-zA-Z0-9_])any(?![a-zA-Z0-9_])/ },
|
||||
{ name: "ts-ignore", regex: /@ts-ignore/ },
|
||||
{ name: "ts-expect-error", regex: /@ts-expect-error/ },
|
||||
{ name: "TODO", regex: /\bTODO\b/ },
|
||||
{ name: "FIXME", regex: /\bFIXME\b/ },
|
||||
{ name: "@deprecated", regex: /@deprecated/ },
|
||||
{ name: ".skip(", regex: /\.skip\s*\(/ },
|
||||
{ name: ".only(", regex: /\.only\s*\(/ },
|
||||
];
|
||||
|
||||
const EXCLUDED_FILE_SUFFIXES = [".test.ts", ".test.tsx", ".bench.ts"];
|
||||
|
||||
const EXCLUDED_FROM_ANY_CHECK: readonly string[] = [
|
||||
// Mock helpers necessarily reach into untyped runner internals.
|
||||
"runner/mocks.ts",
|
||||
"runner/bun-test.ts",
|
||||
];
|
||||
|
||||
async function walk(dir: string, out: string[]): Promise<void> {
|
||||
const entries = await readdir(dir, { withFileTypes: true });
|
||||
for (const entry of entries) {
|
||||
const full = join(dir, entry.name);
|
||||
if (entry.isDirectory()) {
|
||||
await walk(full, out);
|
||||
} else if (
|
||||
entry.isFile() &&
|
||||
(entry.name.endsWith(".ts") || entry.name.endsWith(".tsx"))
|
||||
) {
|
||||
out.push(full);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
describe("no-disallowed source guard", () => {
|
||||
it("src/ contains no disallowed constructs", {
|
||||
timeout: 10000,
|
||||
}, async () => {
|
||||
const all: string[] = [];
|
||||
await walk(srcRoot, all);
|
||||
const files = all.filter((file) => {
|
||||
const rel = relative(srcRoot, file).replace(/\\/g, "/");
|
||||
return !EXCLUDED_FILE_SUFFIXES.some((s) => rel.endsWith(s));
|
||||
});
|
||||
const violations: string[] = [];
|
||||
for (const file of files) {
|
||||
const text = await readFile(file, "utf8");
|
||||
const lines = text.split("\n");
|
||||
const rel = relative(srcRoot, file).replace(/\\/g, "/");
|
||||
for (const { name, regex } of PATTERNS) {
|
||||
if (name === "any" && EXCLUDED_FROM_ANY_CHECK.includes(rel)) {
|
||||
continue;
|
||||
}
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
const line = lines[i] ?? "";
|
||||
const trimmed = line.trim();
|
||||
if (
|
||||
trimmed.startsWith("//") ||
|
||||
trimmed.startsWith("*") ||
|
||||
trimmed.startsWith("/*")
|
||||
)
|
||||
continue;
|
||||
if (regex.test(line)) {
|
||||
violations.push(
|
||||
`${rel}:${i + 1} contains "${name}": ${trimmed}`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
expect(violations).toEqual([]);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user