The build report
What build prints when it's done. Each page's row has its HTML, gzipped, as it's sent, the time
it took to compile, and the elements that give it behavior. So a page that grows heavy or slow
shows where the cost is.
What pages share is one common row, the CSS and JavaScript Vite bundled, since the browser
downloads it once. The last line is the whole build and the version of Pagez that ran.
page html time
/ 1.2 KB 12 ms
/essays/ 1.4 KB 9 ms fold-out
common 0.8 KB css, 1.1 KB js
2 pages and 1 redirect in 240 ms → dist · pagez 0.1.0
import { createLogger, type Logger } from "vite-plus";
/** A page as the report shows it. */
export interface Reported {
/** Its path from the root, `/about/`, or `/404.html`. */
path: string;
/** Its HTML's size, gzipped, in bytes. */
html: number;
/** How long it took to compile, in milliseconds. */
ms: number;
behavior: string[];
}
export interface Report {
pages: Reported[];
/** The bundled CSS and JavaScript, gzipped, in bytes. */
css: number;
js: number;
redirects: number;
ms: number;
out: string;
version: string;
}A logger that prints the report and Vite's warnings and errors, and none of Vite's other lines.
Vite lists each file it wrote, which the report says page by page, so pagez build prints the
report alone. The build also runs at logLevel: "warn", which stops Vite's progress lines.
export function reportOnly(): Logger {
const logger = createLogger();
const info = logger.info.bind(logger);
logger.info = (message, options) => {
if (/ · pagez \S+$/.test(message)) info(message, options);
};
return logger;
}
export function report({ pages, css, js, redirects, ms, out, version }: Report): string {
const rows = [
["page", "html", "time", ""],
...pages.map((page) => [page.path, size(page.html), time(page.ms), page.behavior.join(", ")]),
];
const widths = [0, 1, 2].map((i) => Math.max(...rows.map((row) => row[i]!.length)));
const lines = rows.map((row) =>
[row[0]!.padEnd(widths[0]!), row[1]!.padStart(widths[1]!), row[2]!.padStart(widths[2]!), row[3]]
.join(" ")
.trimEnd(),
);
const common = [...(css > 0 ? [`${size(css)} css`] : []), ...(js > 0 ? [`${size(js)} js`] : [])];
if (common.length > 0) lines.push(`${"common".padEnd(widths[0]!)} ${common.join(", ")}`);
const counted = [
plural(pages.length, "page"),
...(redirects > 0 ? [plural(redirects, "redirect")] : []),
];
lines.push(`${counted.join(" and ")} in ${time(ms)} → ${out} · pagez ${version}`);
return lines.join("\n");
}
const plural = (n: number, word: string) => `${n} ${word}${n === 1 ? "" : "s"}`;
/** Kilobytes, to one decimal below 10 KB, where a tenth still means something. */
export function size(bytes: number): string {
const kb = bytes / 1024;
return `${kb < 10 ? kb.toFixed(1) : Math.round(kb)} KB`;
}
export function time(ms: number): string {
return ms < 1000 ? `${Math.round(ms)} ms` : `${(ms / 1000).toFixed(1)} s`;
}