mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-08 20:42:43 +00:00
Add `src/lib/http/cache-headers.ts` exporting two presets and one applier. `NO_STORE_BUT_BFCACHE` (`private, max-age=0, must-revalidate`) replaces the framework's stock `no-store, must-revalidate` on authenticated page responses; Chromium treats the latter as a hard bfcache breaker, while the former keeps shared caches out, forces revalidation on every navigation, and stays bfcache-eligible. `SHORT_LIVED_PUBLIC` covers static-ish one-hour public payloads. `applyAuthedHeaders(res)` stamps the bfcache-friendly directive onto a Response in-place for route handlers that opt into the same posture. Also add `scripts/print-bundle-report.mjs` (wired earlier as the `bundle-report` npm script). Turbopack dropped the webpack "First Load JS" summary line from `next build`; the script reads back the `.next/analyze/client.json` payload that `@next/bundle-analyzer` writes when `pnpm analyze` runs and prints a sorted table of the largest client chunks (parsed, gzip, stat sizes plus totals). Output restores an at-a-glance signal for client-bundle regressions on release rounds without re-running Lighthouse.
119 lines
3.6 KiB
JavaScript
119 lines
3.6 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* Reads `.next/analyze/client.json` (emitted when `pnpm analyze` runs
|
|
* `@next/bundle-analyzer` with `ANALYZE=1`) and prints a sorted table
|
|
* of the largest client chunks. Turbopack dropped the webpack
|
|
* "First Load JS" summary line from `next build`; this script restores
|
|
* an equivalent at-a-glance signal so release rounds can spot
|
|
* client-bundle regressions without re-running Lighthouse.
|
|
*
|
|
* Usage:
|
|
* pnpm analyze # writes .next/analyze/client.json
|
|
* pnpm bundle-report # reads it back, prints top chunks
|
|
*
|
|
* Output columns: rank, chunk label, parsed (post-minify) size, gzip
|
|
* size, stat (pre-minify) size. Sizes report in KiB to two decimals.
|
|
* Exits 0 unless `client.json` is missing or unreadable.
|
|
*/
|
|
|
|
import { readFile } from "node:fs/promises";
|
|
import { resolve } from "node:path";
|
|
|
|
const REPORT_PATH = resolve(process.cwd(), ".next", "analyze", "client.json");
|
|
const DEFAULT_TOP_N = 10;
|
|
|
|
function formatKib(bytes) {
|
|
if (typeof bytes !== "number" || Number.isNaN(bytes)) return "—";
|
|
return `${(bytes / 1024).toFixed(2)} KiB`;
|
|
}
|
|
|
|
function padCell(value, width) {
|
|
const str = String(value ?? "");
|
|
return str.length >= width ? str : str + " ".repeat(width - str.length);
|
|
}
|
|
|
|
async function main() {
|
|
const argTopN = Number(process.argv[2]);
|
|
const topN =
|
|
Number.isFinite(argTopN) && argTopN > 0
|
|
? Math.floor(argTopN)
|
|
: DEFAULT_TOP_N;
|
|
|
|
let raw;
|
|
try {
|
|
raw = await readFile(REPORT_PATH, "utf8");
|
|
} catch (err) {
|
|
if (err && err.code === "ENOENT") {
|
|
console.error(
|
|
`bundle-report: ${REPORT_PATH} not found. Run \`pnpm analyze\` first.`,
|
|
);
|
|
} else {
|
|
console.error(`bundle-report: failed to read ${REPORT_PATH}:`, err);
|
|
}
|
|
process.exit(1);
|
|
}
|
|
|
|
let chunks;
|
|
try {
|
|
chunks = JSON.parse(raw);
|
|
} catch (err) {
|
|
console.error(`bundle-report: failed to parse ${REPORT_PATH}:`, err);
|
|
process.exit(1);
|
|
}
|
|
|
|
if (!Array.isArray(chunks)) {
|
|
console.error(
|
|
"bundle-report: client.json is not an array of chunks — analyzer output shape changed?",
|
|
);
|
|
process.exit(1);
|
|
}
|
|
|
|
const sorted = [...chunks].sort(
|
|
(a, b) => (b.parsedSize ?? 0) - (a.parsedSize ?? 0),
|
|
);
|
|
const top = sorted.slice(0, topN);
|
|
|
|
const totals = chunks.reduce(
|
|
(acc, c) => ({
|
|
parsed: acc.parsed + (c.parsedSize ?? 0),
|
|
gzip: acc.gzip + (c.gzipSize ?? 0),
|
|
stat: acc.stat + (c.statSize ?? 0),
|
|
}),
|
|
{ parsed: 0, gzip: 0, stat: 0 },
|
|
);
|
|
|
|
const labelWidth = Math.max(
|
|
20,
|
|
...top.map((c) => String(c.label ?? "").length),
|
|
);
|
|
const header =
|
|
`${padCell("#", 3)} ${padCell("chunk", labelWidth)} ` +
|
|
`${padCell("parsed", 12)} ${padCell("gzip", 12)} ${padCell("stat", 12)}`;
|
|
|
|
console.log(
|
|
`Top ${top.length} client chunks by parsed size (of ${chunks.length} total):`,
|
|
);
|
|
console.log(header);
|
|
console.log("-".repeat(header.length));
|
|
top.forEach((chunk, idx) => {
|
|
console.log(
|
|
`${padCell(idx + 1, 3)} ${padCell(chunk.label ?? "(unnamed)", labelWidth)} ` +
|
|
`${padCell(formatKib(chunk.parsedSize), 12)} ` +
|
|
`${padCell(formatKib(chunk.gzipSize), 12)} ` +
|
|
`${padCell(formatKib(chunk.statSize), 12)}`,
|
|
);
|
|
});
|
|
console.log("-".repeat(header.length));
|
|
console.log(
|
|
`${padCell("Σ", 3)} ${padCell("all chunks", labelWidth)} ` +
|
|
`${padCell(formatKib(totals.parsed), 12)} ` +
|
|
`${padCell(formatKib(totals.gzip), 12)} ` +
|
|
`${padCell(formatKib(totals.stat), 12)}`,
|
|
);
|
|
}
|
|
|
|
main().catch((err) => {
|
|
console.error("bundle-report: unexpected failure:", err);
|
|
process.exit(1);
|
|
});
|