Files
upbeatBytes/frontend/scripts/patch-static-heads.mjs
T
thejayman77 ecfc49eda0 Codex audit fixes: home3 read-time, /art OG/canonical, Bloom medallion centering
- home3 dropped source_read_minutes when mapping the brief item, so the badge
  only ever showed "1 min brief" — include it so "· ~N min full story" appears.
- /art is public but shipped the homepage title/canonical/OG (SSR-off shell, so
  svelte:head can't fix scrapers) — add /art to patch-static-heads.mjs.
- Bloom: center the circular motif in the tile's visual field (~43%) instead of
  the top illustration zone, so it reads as a medallion (per Jay + Codex).
- Polish: prefers-reduced-motion disables the tile hover-lift; fix stale
  patch-play-head.mjs comment reference.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 20:54:57 -04:00

79 lines
3.6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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 <head> — 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 <head> overrides. Keep titles/descriptions in sync with each page's intent.
const PAGES = [
{
file: 'play.html', path: '/play',
title: 'Play · Upbeat Bytes — 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 youre looking at.',
},
];
function subsFor(url, title, desc) {
return [
[/<title>[\s\S]*?<\/title>/, `<title>${title}</title>`],
[/<meta name="description" content="[^"]*"\s*\/>/, `<meta name="description" content="${desc}" />`],
[/<link rel="canonical" href="https:\/\/upbeatbytes\.com\/"\s*\/>/, `<link rel="canonical" href="${url}" />`],
[/<meta property="og:title" content="[^"]*"\s*\/>/, `<meta property="og:title" content="${title}" />`],
[/<meta property="og:description" content="[^"]*"\s*\/>/, `<meta property="og:description" content="${desc}" />`],
[/<meta property="og:url" content="https:\/\/upbeatbytes\.com\/"\s*\/>/, `<meta property="og:url" content="${url}" />`],
[/<meta name="twitter:title" content="[^"]*"\s*\/>/, `<meta name="twitter:title" content="${title}" />`],
[/<meta name="twitter:description" content="[^"]*"\s*\/>/, `<meta name="twitter:description" content="${desc}" />`],
];
}
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}`);
}