/** * Self-hosted Inter (variable, upright) from @fontsource-variable/inter. * * src/index.css declares one @font-face per unicode-range subset with a * relative `url(fonts/)`, which resolves to src/fonts/ — a generated, * gitignored copy of the package's files (`ensureFonts()`; Bun's CSS bundler * must be able to resolve every url()). * * - Dev (`bun dev`): the HTML-import bundler inlines them as base64. Fine * locally. * - Production (scripts/build.ts): `external: ["*.woff2"]` keeps them out of * the bundle — Bun inlines every resolvable CSS url() asset, which would put * ~220 KB into the render-blocking stylesheet and defeat unicode-range lazy * loading. The build copies them to dist/fonts/ and precaches them instead. * * Same-origin either way, so COEP `require-corp` (D-023) allows them. */ import { copyFile, mkdir } from "node:fs/promises"; import { join } from "node:path"; const PACKAGE_FILES = join(import.meta.dir, "../node_modules/@fontsource-variable/inter/files"); const CSS_PATH = join(import.meta.dir, "../src/index.css"); export const FONTS_DIR = join(import.meta.dir, "../src/fonts"); /** The font files a stylesheet references via `url(fonts/)`. */ export function fontFilesInCss(css: string): string[] { const files = [...css.matchAll(/url\(["']?fonts\/([^"')]+)["']?\)/g)].map((match) => match[1]!); return [...new Set(files)]; } /** * Copy every font src/index.css references into src/fonts/ (idempotent). * Always overwrites (~220 KB), so a package upgrade can't leave stale copies. */ export async function ensureFonts(): Promise { const files = fontFilesInCss(await Bun.file(CSS_PATH).text()); await mkdir(FONTS_DIR, { recursive: true }); for (const name of files) { await copyFile(join(PACKAGE_FILES, name), join(FONTS_DIR, name)); } return files; }