Adds scripts/check-lint-ignores.mjs with four enforcement mechanisms: ratcheting count cap (12 source / 3 test), banned rule prefixes, required justification, and separate test thresholds. Wired into pnpm check. Converts player-management and create-player-modal from div-based modals to native <dialog> with showModal()/close(), removing 8 biome-ignore comments. Remaining ignores are legitimate (Biome false positives or stopPropagation wrappers with no fitting role). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
109 lines
2.9 KiB
JavaScript
109 lines
2.9 KiB
JavaScript
/**
|
|
* Backpressure check for biome-ignore comments.
|
|
*
|
|
* 1. Ratcheting cap — source and test files have separate max counts.
|
|
* Lower these numbers as you fix ignores; they can never go up silently.
|
|
* 2. Banned rules — ignoring certain rule categories is never allowed.
|
|
* 3. Justification — every ignore must have a non-empty explanation after
|
|
* the rule name.
|
|
*/
|
|
|
|
import { execSync } from "node:child_process";
|
|
import { readFileSync } from "node:fs";
|
|
|
|
// ── Configuration ──────────────────────────────────────────────────────
|
|
const MAX_SOURCE_IGNORES = 12;
|
|
const MAX_TEST_IGNORES = 3;
|
|
|
|
/** Rule prefixes that must never be suppressed. */
|
|
const BANNED_PREFIXES = [
|
|
"lint/security/",
|
|
"lint/correctness/noGlobalObjectCalls",
|
|
"lint/correctness/noUnsafeFinally",
|
|
];
|
|
// ───────────────────────────────────────────────────────────────────────
|
|
|
|
const IGNORE_PATTERN = /biome-ignore\s+([\w/]+)(?::\s*(.*))?/;
|
|
|
|
function findFiles() {
|
|
return execSync("git ls-files -- '*.ts' '*.tsx'", { encoding: "utf-8" })
|
|
.trim()
|
|
.split("\n")
|
|
.filter(Boolean);
|
|
}
|
|
|
|
function isTestFile(path) {
|
|
return (
|
|
path.includes("__tests__/") ||
|
|
path.endsWith(".test.ts") ||
|
|
path.endsWith(".test.tsx")
|
|
);
|
|
}
|
|
|
|
let errors = 0;
|
|
let sourceCount = 0;
|
|
let testCount = 0;
|
|
|
|
for (const file of findFiles()) {
|
|
const lines = readFileSync(file, "utf-8").split("\n");
|
|
|
|
for (let i = 0; i < lines.length; i++) {
|
|
const match = lines[i].match(IGNORE_PATTERN);
|
|
if (!match) continue;
|
|
|
|
const rule = match[1];
|
|
const justification = (match[2] ?? "").trim();
|
|
const loc = `${file}:${i + 1}`;
|
|
|
|
// Count by category
|
|
if (isTestFile(file)) {
|
|
testCount++;
|
|
} else {
|
|
sourceCount++;
|
|
}
|
|
|
|
// Banned rules
|
|
for (const prefix of BANNED_PREFIXES) {
|
|
if (rule.startsWith(prefix)) {
|
|
console.error(`BANNED: ${loc} — ${rule} must not be suppressed`);
|
|
errors++;
|
|
}
|
|
}
|
|
|
|
// Justification required
|
|
if (!justification) {
|
|
console.error(
|
|
`MISSING JUSTIFICATION: ${loc} — biome-ignore ${rule} needs an explanation after the colon`,
|
|
);
|
|
errors++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Ratcheting caps
|
|
if (sourceCount > MAX_SOURCE_IGNORES) {
|
|
console.error(
|
|
`SOURCE CAP EXCEEDED: ${sourceCount} biome-ignore comments in source (max ${MAX_SOURCE_IGNORES}). Fix issues and lower the cap.`,
|
|
);
|
|
errors++;
|
|
}
|
|
|
|
if (testCount > MAX_TEST_IGNORES) {
|
|
console.error(
|
|
`TEST CAP EXCEEDED: ${testCount} biome-ignore comments in tests (max ${MAX_TEST_IGNORES}). Fix issues and lower the cap.`,
|
|
);
|
|
errors++;
|
|
}
|
|
|
|
// Summary
|
|
console.log(
|
|
`biome-ignore: ${sourceCount} source (max ${MAX_SOURCE_IGNORES}), ${testCount} test (max ${MAX_TEST_IGNORES})`,
|
|
);
|
|
|
|
if (errors > 0) {
|
|
console.error(`\n${errors} problem(s) found.`);
|
|
process.exit(1);
|
|
} else {
|
|
console.log("All checks passed.");
|
|
}
|