pagez

Size

pnpm size measures what Pagez adds to a page, gzipped, as a browser pays for it, and fails past a budget, so growth shows in the pull request that causes it. verify runs it. It builds two test pages with the built plugin, dist/:

  • the theme, the <style> a page with no CSS of its own gets
  • the head, what Pagez writes around a plain page's text, without the theme
  • the runtime, the module a page with one element's behavior ships, with that element's setup

Each budget is a little over what it measures now. Raising one is a choice the pull request makes, and says why.

import { cpSync, mkdtempSync, readdirSync, readFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { gzipSync } from "node:zlib";
import { build } from "vite-plus";

const root = join(import.meta.dirname, "../..");
const { default: pagez } = (await import(join(root, "dist/vite.js"))) as {
  default: () => import("vite-plus").Plugin[];
};

/** Each part's budget, in gzipped bytes. */
const BUDGET = { theme: 900, head: 300, runtime: 400 };

/** A test page, built with the plugin in a folder of its own, as its `dist/`. */
async function built(page: string): Promise<string> {
  const dir = mkdtempSync(join(tmpdir(), "pagez-size-"));
  cpSync(join(root, "tests/pages", page), dir, { recursive: true });
  await build({ root: dir, configFile: false, logLevel: "silent", plugins: [pagez()] });
  return join(dir, "dist");
}

const gzipped = (text: string) => gzipSync(text, { level: 9 }).length;

const plain = readFileSync(join(await built("plain"), "index.html"), "utf8");
const style = /<style>([\s\S]*?)<\/style>/.exec(plain)?.[1] ?? "";
const head = plain.replace(/<style>[\s\S]*?<\/style>\s*/, "").replace(/<main>[\s\S]*<\/main>/, "");
const assets = join(await built("element-behavior"), "assets");
const runtime = readdirSync(assets)
  .filter((file) => file.endsWith(".js"))
  .map((file) => readFileSync(join(assets, file), "utf8"))
  .join("");

const measured = { theme: gzipped(style), head: gzipped(head), runtime: gzipped(runtime) };
let over = false;
for (const [part, bytes] of Object.entries(measured)) {
  const budget = BUDGET[part as keyof typeof BUDGET];
  const mark = bytes > budget ? "  over budget" : "";
  over ||= bytes > budget;
  console.log(`${part.padEnd(8)} ${String(bytes).padStart(5)} B gzip, of ${budget} B${mark}`);
}
if (over) process.exitCode = 1;