/* Generates src/icons.generated.ts from the simple-icons npm package — build-time only, so the site never phones a CDN (v1 loaded brand logos from cdn.simpleicons.org at runtime: a third-party request on a privacy guide). The slug list is DERIVED from the data files (tools.json + directory.json iconSlug fields) so it can never drift from the content. Slugs missing from the installed set fall back to the letter tile at render time. Run: pnpm icons */ import { readFileSync, writeFileSync } from 'node:fs' import * as icons from 'simple-icons' const dataFiles = ['../src/data/tools.json', '../src/data/directory.json'] const slugs = new Set(['github']) // footer link icon for (const file of dataFiles) { const entries = JSON.parse(readFileSync(new URL(file, import.meta.url), 'utf8')) for (const entry of entries) { if (entry.iconSlug) slugs.add(entry.iconSlug) } } const bySlug = new Map(Object.values(icons).map((i) => [i.slug, i])) const found = [] const missing = [] for (const slug of [...slugs].sort()) { const icon = bySlug.get(slug) if (icon) found.push(icon) else missing.push(slug) } const lines = found.map((i) => ` ${JSON.stringify(i.slug)}: ${JSON.stringify(i.path)},`) const out = `/* AUTO-GENERATED by scripts/gen-icons.mjs — do not edit. SVG path data from the simple-icons npm package (CC0), inlined at build time. Zero runtime CDN request. Missing slugs at generation time: ${ missing.length ? missing.join(', ') : 'none' }. */ export const BRAND_ICONS: Record = { ${lines.join('\n')} } ` writeFileSync(new URL('../src/icons.generated.ts', import.meta.url), out) console.log( `icons: ${found.length} embedded, ${missing.length} missing${missing.length ? ` (${missing.join(', ')})` : ''}`, )