// Build-time prerenderer.
//
// Renders every route to static HTML so crawlers that do not execute JavaScript
// (Meta, LinkedIn, Slack, Bing) receive real content, correct per-route
,
// meta description, canonical, and Open Graph tags. Google also benefits: pages
// no longer sit in its deferred JS-rendering queue.
//
// Output layout (consumed by server/index.js):
// dist/index.html -> /
// dist/about/index.html -> /about
// dist/services//index.html
// dist/404.html -> served with a real 404 status
//
// Run automatically as part of `npm run build`.
import { execFileSync } from 'child_process'
import { mkdirSync, readFileSync, writeFileSync } from 'fs'
import path from 'path'
import { fileURLToPath } from 'url'
import { render } from '../dist-ssr/entry-server.js'
import { services } from '../src/data/services.js'
import { industries } from '../src/data/industries.js'
const __dirname = path.dirname(fileURLToPath(import.meta.url))
const distDir = path.join(__dirname, '../dist')
const STATIC_ROUTES = [
'/',
'/about',
'/services',
'/industries',
'/contact',
'/support',
'/privacy-policy',
]
const routes = [
...STATIC_ROUTES,
...services.map((s) => `/services/${s.id}`),
...industries.map((i) => `/industries/${i.id}`),
]
// Tags the SEO component owns per-route. They are stripped from the template so
// Helmet's values replace them instead of duplicating them.
const TEMPLATE_TAGS_TO_STRIP = [
/[\s\S]*?<\/title>\s*/,
/]*>\s*/,
/]*>\s*/g,
/]*>\s*/g,
/\s*/,
/\s*/,
]
// Metadata React renders inside the component tree. React 19 hoists these into
// in the browser and in its streaming renderer, but renderToString leaves
// them inline, so the prerenderer performs the same hoist. Leaving them in
// would put every title, canonical, and og: tag somewhere crawlers ignore.
const HOISTABLE_TAGS =
/]*>[\s\S]*?<\/title>|]*?\/?>|]*?\/?>|