pagez

A large site

What pnpm speed and pnpm hotspots build: a site of a few hundred pages, written into a folder, since the test sites' handful of pages never showed a slow build. It has what a real site's build pays for:

  • three folders of posts, each with an index that lists its posts from pages
  • shared elements, one with behavior
  • a template that imports a large package, happy-dom
  • data an element imports
  • a frame and a style.css
  • links between pages
  • redirects
  • a 404.md

The folder gets a node_modules linked to Pagez's own, so the package resolves beside it.

import { mkdirSync, rmSync, symlinkSync, writeFileSync } from "node:fs";
import { dirname, join } from "node:path";

const FOLDERS = ["essays", "talks", "notes"];

const write = (file: string, text: string) => {
  mkdirSync(dirname(file), { recursive: true });
  writeFileSync(file, text);
};

/** Words for a paragraph, the same for the same `n`, so every run builds the same site. */
function words(n: number, count: number): string {
  const all =
    "the a page site build folder element link data frame style talk note essay idea time shape draft".split(
      " ",
    );
  return Array.from({ length: count }, (_, i) => all[(n * 7 + i * 3) % all.length]).join(" ");
}

/** Writes the site into `dir`, emptied first, with `pages` posts across its folders. */
export function writeSite(dir: string, pages: number = 300): void {
  rmSync(dir, { recursive: true, force: true });
  mkdirSync(dir, { recursive: true });
  symlinkSync(join(import.meta.dirname, "../../node_modules"), join(dir, "node_modules"), "dir");
  write(join(dir, "pagez.md"), "---\nurl: https://example.com/\nauthor: A. Writer\n---\n");
  write(
    join(dir, "frame.html"),
    `<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>A large site</title>
  </head>
  <body>
    <nav>${FOLDERS.map((folder) => `<a href="/${folder}/">${folder}</a>`).join(" · ")}</nav>
    <main><slot></slot></main>
    <footer><site-author></site-author></footer>
  </body>
</html>
`,
  );
  write(
    join(dir, "style.css"),
    "body { margin: 0 auto; max-width: 60ch; }\nfold-out { display: block; }\n",
  );
  write(join(dir, "@site-author.html"), "<template>${site.author}</template>\n");
  write(
    join(dir, "@post-list.html"),
    `<template>
<script>
const posts = (pages, under) =>
  pages.filter((page) => page.path.startsWith(under) && page.path !== under);
</script>
<ul>\${posts(pages, attrs.under).map((post) => html\`<li><a href="\${post.path}">\${post.title}</a></li>\`)}</ul>
</template>
`,
  );
  write(
    join(dir, "@word-count.html"),
    `<template>
<script>
import { Window } from "happy-dom";
const count = (text) => {
  const window = new Window();
  window.document.body.innerHTML = text;
  const words = window.document.body.textContent.split(/\\s+/).filter(Boolean).length;
  window.close();
  return words;
};
</script>
<span>\${count(page.description ?? "")} words</span>
</template>
`,
  );
  const rows = Array.from({ length: 20 }, (_, i) => ({ name: words(i, 2), value: i * 3 }));
  write(join(dir, "rows.json"), JSON.stringify(rows));
  write(
    join(dir, "@data-table.html"),
    `<template>
<script>
import rows from "./rows.json" with { type: "json" };
</script>
<table>\${rows.map((row) => html\`<tr><td>\${row.name}</td><td>\${row.value}</td></tr>\`)}</table>
</template>
`,
  );
  write(
    join(dir, "@fold-out.html"),
    `<template>
  <button aria-expanded="false">\${attrs.label ?? "More"}</button>
  <div hidden><slot></slot></div>
</template>

<style>
  fold-out button { font: inherit; }
</style>

<script>
  export default (el, { signal }) => {
    const button = el.querySelector("button");
    const text = el.querySelector("div");
    button.addEventListener("click", () => (text.hidden = !text.hidden), { signal });
  };
</script>
`,
  );
  write(
    join(dir, "index.md"),
    `---\ntitle: Home\n---\n\n# Home\n\n${FOLDERS.map((folder) => `- [${folder}](${folder}/index.md)`).join("\n")}\n`,
  );
  write(
    join(dir, "404.md"),
    "---\ntitle: Not found\n---\n\n# Not found\n\nTry [the home page](index.md).\n",
  );
  for (const folder of FOLDERS) {
    write(
      join(dir, folder, "index.md"),
      `---\ntitle: ${folder}\n---\n\n# ${folder}\n\n{@post-list under="/${folder}/" /}\n`,
    );
  }
  for (let n = 0; n < pages; n++) {
    const folder = FOLDERS[n % FOLDERS.length]!;
    const next = FOLDERS[(n + 1) % FOLDERS.length]!;
    const moved = n % 10 === 0 ? `redirects: [/old-${n}/]\n` : "";
    write(
      join(dir, folder, `post-${n}.md`),
      `---
title: Post ${n}
description: ${words(n, 12)}
${moved}---

# Post ${n}

{@word-count /}

${words(n, 40)}. See [post ${n + 1}](../${next}/post-${(n + 1) % pages}.md).

## Details

{@fold-out label="More"}

${words(n + 1, 30)}.

{/fold-out}

{@data-table /}

${words(n + 2, 40)}.
`,
    );
  }
}