// Post-build: give specific prerendered shells their OWN social/canonical metadata. // // The app is a static SPA (ssr=false), so every prerendered shell ships app.html's // HOMEPAGE — meaning a shared /play or /word link previews as the news homepage, // and its canonical points at "/". Client svelte:head can't fix that for non-JS scrapers // (Twitter/Slack/iMessage) or for canonical dedup. So we rewrite each page's static head // here, at build time. Deep-linked variants (e.g. /play?game=…) inherit the same file. import { readFile, writeFile } from 'node:fs/promises'; const BASE = 'https://upbeatbytes.com'; // Per-page overrides. Keep titles/descriptions in sync with each page's intent. const PAGES = [ { file: 'news.html', path: '/news', title: 'News · upbeatBytes — calm, constructive news', desc: 'Calm, constructive news, newest first — and a daily Highlights brief. ' + 'No ads, no paywalls, no doomscrolling.', }, { file: 'play.html', path: '/play', title: 'Play · upbeatBytes — calm daily games', desc: 'A calm set of daily games — Daily Word, Word Search, Bloom, and Memory Match. ' + 'A friendly little break from the doomscroll.', }, { file: 'word.html', path: '/word', title: 'Word of the Day · upbeatBytes', desc: 'A new uplifting word every day — its meaning in plain language, how to say it, and how to use it.', }, { file: 'quote.html', path: '/quote', title: 'Quote of the Day · upbeatBytes', desc: 'A hopeful, hand-picked quote each day, with a short note on what it means.', }, { file: 'onthisday.html', path: '/onthisday', title: 'On This Day · upbeatBytes', desc: 'One genuinely good thing that happened on this day in history.', }, { file: 'art.html', path: '/art', title: 'Daily Art · upbeatBytes', desc: "A masterwork a day from the world's open museum collections — beautifully framed, " + 'with a short note on what you’re looking at.', }, ]; function subsFor(url, title, desc) { return [ [/[\s\S]*?<\/title>/, `<title>${title}`], [//, ``], [//, ``], [//, ``], [//, ``], [//, ``], [//, ``], [//, ``], ]; } for (const { file, path, title, desc } of PAGES) { const fileUrl = new URL(`../build/${file}`, import.meta.url); let html; try { html = await readFile(fileUrl, 'utf8'); } catch { console.error(`patch-static-heads: build/${file} is missing (route renamed/removed?)`); process.exit(1); } const missed = []; for (const [re, repl] of subsFor(BASE + path, title, desc)) { if (!re.test(html)) { missed.push(re.source.slice(0, 40)); continue; } html = html.replace(re, repl); } // Fail loudly if the homepage head drifted — better a broken build than silently // shipping the wrong preview/canonical again. if (missed.length) { console.error(`patch-static-heads: ${file} — these head tags were not found (app.html changed?):\n ` + missed.join('\n ')); process.exit(1); } await writeFile(fileUrl, html); console.log(`patch-static-heads: rewrote build/${file} head → ${path}`); }