finishSite
uses node:fs
Make a built static site (out, its stylesheet style.css) quick to go through: the stylesheet is named by its
content (style.<digest>.css, which tcm serve --site says to keep for good) in every page's link to it, and
each page gets SPECULATION_RULES. Returns the stylesheet's new name.
import { SPECULATION_RULES } from "#codebase/tcmts/site"; import { createHash } from "node:crypto"; import fs from "node:fs"; import path from "node:path"; export function finishSite(out: string): string { const css = fs.readFileSync(path.join(out, "style.css")); const style = `style.${createHash("sha256").update(css).digest("hex").slice(0, 12)}.css`; fs.renameSync(path.join(out, "style.css"), path.join(out, style)); const walk = (dir: string) => { for (const e of fs.readdirSync(dir, { withFileTypes: true })) { const p = path.join(dir, e.name); if (e.isDirectory()) walk(p); else if (e.name.endsWith(".html")) { const html = fs.readFileSync(p, "utf8"); // (a page of the docs app follows its own links itself, having read what they show: prerendering one would be // for nothing; the others, its code browser's, are prerendered) const rules = /var __TCM_STATIC__/.test(html) ? SPECULATION_RULES.replace( '{"href_matches":"/*"}', '{"selector_matches":"[rel~=external]"},{"href_matches":"/*"}', ) : SPECULATION_RULES; fs.writeFileSync( p, html .replace(/(<link rel="stylesheet" href="(?:\.\.\/)*)style\.css"/, `$1${style}"`) .replace("</head>", () => `${rules}\n</head>`), ); } } }; walk(out); return style; }