|
| 1 | +import { readdirSync, statSync } from "node:fs"; |
| 2 | +import { join } from "node:path"; |
| 3 | + |
| 4 | +const BUDGET_BYTES: Record<string, number> = { |
| 5 | + ".css": 45_000, |
| 6 | + ".js": 280_000, |
| 7 | +}; |
| 8 | + |
| 9 | +const ASSETS = join("dist", "assets"); |
| 10 | + |
| 11 | +function bundles(): { name: string; ext: string; bytes: number }[] { |
| 12 | + return readdirSync(ASSETS) |
| 13 | + .filter((name) => name.endsWith(".css") || name.endsWith(".js")) |
| 14 | + .map((name) => ({ |
| 15 | + name, |
| 16 | + ext: name.slice(name.lastIndexOf(".")), |
| 17 | + bytes: statSync(join(ASSETS, name)).size, |
| 18 | + })); |
| 19 | +} |
| 20 | + |
| 21 | +const found = bundles(); |
| 22 | +if (found.length === 0) { |
| 23 | + console.error(`No bundles in ${ASSETS}. Run "npm run build" first.`); |
| 24 | + process.exit(1); |
| 25 | +} |
| 26 | + |
| 27 | +const totals = new Map<string, number>(); |
| 28 | +for (const { ext, bytes } of found) totals.set(ext, (totals.get(ext) ?? 0) + bytes); |
| 29 | + |
| 30 | +let failed = false; |
| 31 | +for (const [ext, budget] of Object.entries(BUDGET_BYTES)) { |
| 32 | + const bytes = totals.get(ext) ?? 0; |
| 33 | + const percent = Math.round((bytes / budget) * 100); |
| 34 | + const label = `${ext.slice(1).toUpperCase().padEnd(3)} ${String(bytes).padStart(7)} / ${budget} bytes (${percent}%)`; |
| 35 | + if (bytes > budget) { |
| 36 | + failed = true; |
| 37 | + console.error(`over budget ${label}`); |
| 38 | + } else { |
| 39 | + console.log(`ok ${label}`); |
| 40 | + } |
| 41 | +} |
| 42 | + |
| 43 | +if (failed) { |
| 44 | + console.error(""); |
| 45 | + console.error("A bundle grew past its budget. Justify the growth and raise BUDGET_BYTES,"); |
| 46 | + console.error("or find what was added. Adding a CSS framework once cost 19 kB unnoticed."); |
| 47 | + process.exit(1); |
| 48 | +} |
0 commit comments