#!/usr/bin/env node // Verifies the production build: every route has prerendered HTML with its own // canonical and og:url, the CSR shell exists, and no Google font host is left. import { existsSync, readFileSync, readdirSync } from 'node:fs'; import { join, dirname } from 'node:path'; import { fileURLToPath } from 'node:url'; const root = join(dirname(fileURLToPath(import.meta.url)), '..'); const distDir = join(root, 'dist', 'playground-frontend', 'browser'); const ORIGIN = 'https://andreas-dahm.eu'; const FORBIDDEN_HOSTS = ['fonts.googleapis.com', 'fonts.gstatic.com']; function* walk(dir) { for (const entry of readdirSync(dir, { withFileTypes: true })) { const path = join(dir, entry.name); if (entry.isDirectory()) { yield* walk(path); } else { yield path; } } } function routePaths() { const constants = readFileSync(join(root, 'src', 'app', 'constants', 'RouterConstants.ts'), 'utf8'); const paths = [...constants.matchAll(/PATH: '([^']+)'/g)].map(match => match[1]); return ['', ...paths]; } function checkRoute(route, failures) { const file = join(distDir, route, 'index.html'); if (!existsSync(file)) { failures.push(`missing ${file}`); return; } const html = readFileSync(file, 'utf8'); const url = `${ORIGIN}/${route}`; const label = route || '/'; if (!html.includes(`rel="canonical" href="${url}"`) && !html.includes(`href="${url}" rel="canonical"`)) { failures.push(`${label}: canonical is not ${url}`); } if (!html.includes(`property="og:url" content="${url}"`) && !html.includes(`content="${url}" property="og:url"`)) { failures.push(`${label}: og:url is not ${url}`); } if (!/