budget.ts

2.4 KB · raw

1// §26: fails when any area passes its budget or the TypeScript total passes the 10,000-line ceiling.2const CEILING = 10_000;34// Prefixes are matched in order, so the specific files come before the directories.5const TS_AREAS: [string, string[], number][] = [6	["bucket", ["src/bucket/"], 900],7	["mirror", ["src/mirror/"], 1100],8	["describe", ["src/describe/"], 500],9	["pages", ["src/pages/"], 2600],10	["releases", ["src/releases/"], 900],11	["tarball", ["src/tarball/"], 300],12	["issues", ["src/issues/"], 600],13	["admin", ["src/admin/"], 1100],14	["import-kepr2", ["scripts/import-kepr2.ts"], 100],15	["server", ["src/server/", "src/main.ts", "scripts/budget.ts"], 700],16];17const ASSET_AREAS: [string, string, number][] = [18	["public/js", "public/js/**/*.js", 1200],19	["public/css", "public/css/**/*.css", 2000],20];2122async function lineCount(path: string): Promise<number> {23	const text = await Bun.file(path).text();24	return text.split("\n").length - (text.endsWith("\n") ? 1 : 0);25}2627async function countFiles(pattern: string): Promise<Map<string, number>> {28	const counts = new Map<string, number>();29	for await (const path of new Bun.Glob(pattern).scan(".")) counts.set(path, await lineCount(path));30	return counts;31}3233const tsFiles = await countFiles("{src,scripts}/**/*.ts");34const totals = new Map(TS_AREAS.map(([name]) => [name, 0]));35const unassigned: string[] = [];36for (const [path, lines] of tsFiles) {37	if (path.endsWith(".test.ts")) continue;38	const area = TS_AREAS.find(([, prefixes]) => prefixes.some((prefix) => path.startsWith(prefix)));39	if (!area) unassigned.push(path);40	else totals.set(area[0], (totals.get(area[0]) ?? 0) + lines);41}4243const rows: [string, number, number][] = TS_AREAS.map(([name, , budget]) => [44	name,45	totals.get(name) ?? 0,46	budget,47]);48const total = rows.reduce((sum, [, lines]) => sum + lines, 0);49for (const [name, pattern, budget] of ASSET_AREAS) {50	const files = await countFiles(pattern);51	rows.push([name, [...files.values()].reduce((a, b) => a + b, 0), budget]);52}5354let failed = false;55for (const [name, lines, budget] of rows) {56	const over = lines > budget;57	failed ||= over;58	console.log(`${name.padEnd(14)} ${String(lines).padStart(6)} / ${budget}${over ? "  OVER" : ""}`);59}60console.log(`${"typescript".padEnd(14)} ${String(total).padStart(6)} / ${CEILING}`);61if (total > CEILING) failed = true;62for (const path of unassigned) console.error(`no budget area for ${path}`);63if (failed || unassigned.length > 0) process.exit(1);