/** * ───────────────────────────────────────────── * SPB Git — Personal Git Platform * ───────────────────────────────────────────── * Author : Simon-Pierre Boucher * Contact : contact@spboucher.ai * File : scripts/fetch-fonts.mjs * Purpose : Download self-hosted Inter / JetBrains Mono fonts (web + OG rendering) * License : MIT © Simon-Pierre Boucher * ───────────────────────────────────────────── */ import { mkdirSync, writeFileSync, existsSync } from 'node:fs'; import { join } from 'node:path'; import process from 'node:process'; const ROOT = new URL('..', import.meta.url).pathname; const FONTS_DIR = join(ROOT, 'src/web/assets/fonts'); const BEST_EFFORT = process.argv.includes('--best-effort'); /** woff2 → served to browsers; woff → consumed by satori for OG images. */ const FONTS = [ ['https://cdn.jsdelivr.net/npm/@fontsource/inter@5.1.0/files/inter-latin-400-normal.woff2', 'inter-400.woff2'], ['https://cdn.jsdelivr.net/npm/@fontsource/inter@5.1.0/files/inter-latin-500-normal.woff2', 'inter-500.woff2'], ['https://cdn.jsdelivr.net/npm/@fontsource/inter@5.1.0/files/inter-latin-600-normal.woff2', 'inter-600.woff2'], ['https://cdn.jsdelivr.net/npm/@fontsource/inter@5.1.0/files/inter-latin-700-normal.woff2', 'inter-700.woff2'], ['https://cdn.jsdelivr.net/npm/@fontsource/jetbrains-mono@5.1.1/files/jetbrains-mono-latin-400-normal.woff2', 'jetbrains-mono-400.woff2'], ['https://cdn.jsdelivr.net/npm/@fontsource/jetbrains-mono@5.1.1/files/jetbrains-mono-latin-600-normal.woff2', 'jetbrains-mono-600.woff2'], ['https://cdn.jsdelivr.net/npm/@fontsource/inter@5.1.0/files/inter-latin-400-normal.woff', 'og/inter-400.woff'], ['https://cdn.jsdelivr.net/npm/@fontsource/inter@5.1.0/files/inter-latin-700-normal.woff', 'og/inter-700.woff'], ['https://cdn.jsdelivr.net/npm/@fontsource/jetbrains-mono@5.1.1/files/jetbrains-mono-latin-400-normal.woff', 'og/jetbrains-mono-400.woff'], ]; mkdirSync(join(FONTS_DIR, 'og'), { recursive: true }); let failures = 0; for (const [url, name] of FONTS) { const dest = join(FONTS_DIR, name); if (existsSync(dest)) continue; try { const res = await fetch(url); if (!res.ok) throw new Error(`HTTP ${res.status}`); writeFileSync(dest, Buffer.from(await res.arrayBuffer())); console.log(`✓ ${name}`); } catch (err) { failures += 1; console.warn(`✗ ${name}: ${err.message}`); } } if (failures > 0 && !BEST_EFFORT) { console.error(`${failures} font(s) failed to download. Re-run: npm run fetch:fonts`); process.exit(1); } if (failures > 0) { console.warn('Continuing despite font download failures (best-effort mode). OG images will use a plain-SVG fallback.'); }