#!/usr/bin/env node // Enforces a hard-zero policy for Knip's unused exports. import { fileURLToPath } from "node:url"; import { isLikelyRepoFilePath, runKnip, uniqueSorted } from "./deadcode-knip-runner.mts"; type KnipResult = Awaited>; const KNIP_ISSUES = "exports,nsExports,types,nsTypes,enumMembers,namespaceMembers"; const KNIP_SCANS = [ { name: "production unused-export scan", args: ["--config", "config/knip.config.ts", "--production"], }, { name: "full-tree unused-export scan", args: ["--config", "config/knip.all-exports.config.ts"], }, { name: "script unused-export scan", args: ["--config", "config/knip.scripts-exports.config.ts", "--include-entry-exports"], }, ]; const KNIP_COMMON_ARGS = [ "--no-progress", "--reporter", "compact", "--include", KNIP_ISSUES, "--no-config-hints", ]; /** Parses compact Knip export sections into one path-and-symbol entry per finding. */ export function parseKnipCompactUnusedExportsResult(output: string) { const entries: string[] = []; let inExportSection = false; let sawExportSection = false; for (const line of output.split(/\r?\n/u)) { const sectionMatch = /^(Unused exports|Exports in used namespace|Unused exported types|Exported types in used namespace|Unused exported enum members|Unused exported namespace members) \(\d+\)$/u.exec( line, ); if (sectionMatch) { inExportSection = true; sawExportSection = true; continue; } if (/^Unused .+ \(\d+\)$/u.test(line)) { inExportSection = false; continue; } if (!inExportSection) { continue; } const separatorIndex = line.indexOf(": "); if (separatorIndex === -1) { continue; } const file = line.slice(0, separatorIndex).trim(); if (!isLikelyRepoFilePath(file)) { continue; } const symbols = line.slice(separatorIndex + 2).split(", "); for (const symbol of symbols) { const trimmedSymbol = symbol.trim(); if (trimmedSymbol) { entries.push(`${file}: ${trimmedSymbol}`); } } } return { entries: uniqueSorted(entries), sawExportSection }; } /** Parses compact Knip export sections into one path-and-symbol entry per finding. */ export function parseKnipCompactUnusedExports(output: string) { return parseKnipCompactUnusedExportsResult(output).entries; } /** Rejects every unused export reported by Knip. */ export function checkUnusedExports(output: string) { const entries = parseKnipCompactUnusedExports(output); return { ok: entries.length === 0, entries, message: entries.length === 0 ? "" : [ "Unused exports are not allowed:", ...entries.map((entry) => ` ${entry}`), "Delete the exports or model their real production consumers in Knip.", ].join("\n"), }; } /** Classifies Knip export output, rejecting findings after resolution failures. */ export function checkExportScan(scanName: string, output: string) { const resolutionErrors = output .split(/\r?\n/u) .filter((line) => line.startsWith("ERROR: Error loading ")); if (resolutionErrors.length > 0) { return { ok: false, entries: [], message: [ `deadcode ${scanName} could not resolve workspace modules; export findings would be unreliable and are discarded.`, ...resolutionErrors, "Install workspace dependencies in-tree (pnpm install) — on delegated boxes the crabbox wrapper links the hydrated modules dir into the workdir — and rerun.", ].join("\n"), }; } const check = checkUnusedExports(output); return { ...check, message: check.ok ? "" : `${scanName}:\n${check.message}`, }; } async function main() { // The scans are independent Knip child processes over separate configs; // running them concurrently cuts the lane's serial wall clock roughly 2x. const results = await Promise.all( KNIP_SCANS.map(async (scan) => ({ scan, result: await runKnip([...scan.args, ...KNIP_COMMON_ARGS], { scanName: scan.name }), })), ); for (const { scan, result } of results) { if (!reportUnusedExportScan(scan, result)) { process.exitCode = 1; return; } } console.log( "[deadcode] Knip production and full-tree unused-export checks passed with 0 entries.", ); } function reportUnusedExportScan(scan: (typeof KNIP_SCANS)[number], result: KnipResult) { if (result.errorCode || result.status === null) { console.error( `deadcode ${scan.name} failed: ${result.errorCode ?? result.signal ?? "unknown"}${ result.errorMessage ? `: ${result.errorMessage}` : "" }`, ); if (result.output) { console.error(result.output); } return false; } const check = checkExportScan(scan.name, result.output); if (!check.ok) { console.error(check.message); return false; } const parsed = parseKnipCompactUnusedExportsResult(result.output); // Knip's compact reporter omits empty sections, so a clean scan (exit 0) // legitimately prints no export sections; sectionless output is only a // failure signal when Knip also exited nonzero (crash/config error). if (!parsed.sawExportSection && result.status !== 0) { console.error(`deadcode ${scan.name} produced no export sections.`); if (result.output) { console.error(result.output); } return false; } if (result.status !== 0) { console.error(`deadcode ${scan.name} exited with status ${result.status}.`); if (result.output) { console.error(result.output); } return false; } console.log(`[deadcode] Knip ${scan.name} passed with 0 entries.`); return true; } if (process.argv[1] === fileURLToPath(import.meta.url)) { await main(); }