// White-labeling glue: each client under sites// ships its own icon.ico. // Next.js's App Router picks up the favicon via the file convention at // src/app/(home)/icon.ico, which can only point to one file. This script runs // before `next dev` / `next build` (see predev/prebuild in package.json) and // copies the right per-site icon into that location based on NEXT_PUBLIC_SITE_ID. // The destination is gitignored so it's treated as a build artifact. import { copyFileSync, existsSync, readdirSync, statSync } from 'node:fs' import { dirname, join, resolve } from 'node:path' import { fileURLToPath } from 'node:url' const __dirname = dirname(fileURLToPath(import.meta.url)) const repoRoot = resolve(__dirname, '..') // Mirror the default in src/config/site.ts so an unset env var produces the // same site here as it does at runtime. const siteId = process.env.NEXT_PUBLIC_SITE_ID || 'dreikoenige' const sitesDir = join(repoRoot, 'sites') const source = join(sitesDir, siteId, 'icon.ico') const destination = join(repoRoot, 'src', 'app', '(home)', 'icon.ico') // Fail loudly with the list of valid site ids — same UX as the runtime check // in src/config/site.ts when an unknown NEXT_PUBLIC_SITE_ID is supplied. if (!existsSync(source)) { const available = readdirSync(sitesDir) .filter((entry) => statSync(join(sitesDir, entry)).isDirectory()) .join(', ') throw new Error( `[copy-favicon] No icon.ico for site "${siteId}" at ${source}. Available sites: ${available}`, ) } copyFileSync(source, destination) console.log(`[copy-favicon] ${siteId} → src/app/(home)/icon.ico`)