import { execFileSync } from "node:child_process"; import { readFileSync } from "node:fs"; import { join } from "node:path"; const ROOT = new URL("..", import.meta.url).pathname.replace(/\/$/, ""); const RUN_LINE_RE = /^\s*run:\s*(.+?)\s*$/; /** @param {string} path */ const read = (path) => readFileSync(join(ROOT, path), "utf-8"); /** * Gates must invoke `pnpm oxlint` with no extra arguments: pnpm forwards them * after `--`, where oxlint reads them as file paths and lints nothing. * @param {Record} scripts */ function findGatesWithOwnFlags(scripts) { const commands = Object.entries(scripts) .filter(([name]) => name !== "oxlint") .flatMap(([name, body]) => body.split("&&").map((c) => [`scripts.${name}`, c.trim()]), ); for (const line of read("lefthook.yml").split("\n")) { const command = RUN_LINE_RE.exec(line)?.[1]; if (command) commands.push(["lefthook.yml", command]); } return commands .filter(([, c]) => c.includes("oxlint") && c !== "pnpm oxlint") .map(([source, c]) => `${source} runs "${c}" instead of "pnpm oxlint"`); } /** * Runs the configured command with one rule forced to warn. A working gate * exits non-zero; exiting 0 means it lints nothing, or does not fail on * warnings. * @param {string} oxlintScript */ function failsOnWarnings(oxlintScript) { const args = [...oxlintScript.split(" ").slice(1), "-W", "no-console"]; try { execFileSync(join(ROOT, "node_modules/.bin/oxlint"), args, { cwd: ROOT, stdio: "ignore", }); return false; } catch { return true; } } /** * Reports ways a quality gate could pass without checking anything, which is * indistinguishable from a clean run. * @returns {string[]} */ export function checkGates() { /** @type {Record} */ const scripts = JSON.parse(read("package.json")).scripts; const violations = findGatesWithOwnFlags(scripts); // Vitest defaults to failing when no test file matches. Setting this puts // the test gate back to exiting 0 if the include globs ever stop matching. if (read("vitest.config.ts").includes("passWithNoTests")) { violations.push("vitest.config.ts sets passWithNoTests"); } // jscpd's pattern is a single glob string. An array matches no files, and // the run then reports success having read nothing. if (Array.isArray(JSON.parse(read(".jscpd.json")).pattern)) { violations.push(".jscpd.json sets pattern to an array, not a glob string"); } // The canary rule below is not type-aware, so the probe alone cannot // detect type-aware rules being switched off. if (!scripts.oxlint.includes("--type-aware")) { violations.push("scripts.oxlint is missing --type-aware"); } if (!failsOnWarnings(scripts.oxlint)) { violations.push( `scripts.oxlint exits 0 despite no-console violations: "${scripts.oxlint}"`, ); } return violations; }