// Builds the deck page from src/deck.html plus every src/slides/*.html fragment // (in file-name order), then inlines it and everything under src/c/ into // app/assets.generated.ts so the package app serves with no external requests. import { readdirSync, readFileSync, statSync, writeFileSync } from 'node:fs' import { join, relative, extname } from 'node:path' const root = new URL('..', import.meta.url).pathname const srcDir = join(root, 'src') const types = { '.html': 'text/html; charset=utf-8', '.css': 'text/css; charset=utf-8', '.js': 'text/javascript; charset=utf-8', '.svg': 'image/svg+xml', '.png': 'image/png', '.ico': 'image/x-icon', '.jpg': 'image/jpeg', '.webp': 'image/webp', '.woff2': 'font/woff2', '.txt': 'text/plain; charset=utf-8', } const textTypes = new Set(['.html', '.css', '.js', '.svg', '.txt']) function walk(dir) { return readdirSync(dir).flatMap((name) => { const p = join(dir, name) return statSync(p).isDirectory() ? walk(p) : [p] }) } const slidesDir = join(srcDir, 'slides') const slideFiles = readdirSync(slidesDir).filter((f) => f.endsWith('.html')).sort((a, b) => (a < b ? -1 : 1)) const deck = readFileSync(join(srcDir, 'deck.html'), 'utf8') if (!deck.includes('')) throw new Error('src/deck.html needs a marker') // Shared CSS and JS are inlined: Chrome can fetch resources before the page's // runtime tag exists, which would resolve them against the wrong directory. const inlined = new Set() const inline = (f) => (inlined.add(`c/${f}`), readFileSync(join(srcDir, 'c', f), 'utf8').trim()) const page = deck .replace(//g, (_, f) => ``) .replace(/`) .replace('', () => slideFiles.map((f) => readFileSync(join(slidesDir, f), 'utf8').trim()).join('\n')) const entries = walk(join(srcDir, 'c')).sort().filter((file) => !inlined.has(relative(srcDir, file).split('\\').join('/'))).map((file) => { const ext = extname(file) const type = types[ext] if (!type) throw new Error(`No content type for ${file}`) const path = relative(srcDir, file).split('\\').join('/') const buf = readFileSync(file) return textTypes.has(ext) ? { path, type, text: buf.toString('utf8') } : { path, type, base64: buf.toString('base64') } }) entries.unshift({ path: 'index.html', type: types['.html'], text: page }) // Landing page and the benchmark graph (data inlined from src/data-trend.json, built from data/*.csv). entries.unshift({ path: 'home.html', type: types['.html'], text: readFileSync(join(srcDir, 'pages/home.html'), 'utf8') }) entries.unshift({ path: 'graph.html', type: types['.html'], text: readFileSync(join(srcDir, 'pages/graph.html'), 'utf8').replace('/* data */null', () => readFileSync(join(srcDir, 'data-trend.json'), 'utf8').trim()) }) const out = `// Generated by scripts/build-assets.mjs. Do not edit by hand. export type Asset = { type: string; text?: string; base64?: string } export const assets: Record = ${JSON.stringify( Object.fromEntries(entries.map(({ path, ...rest }) => [path, rest])), null, '\t', )} ` writeFileSync(join(root, 'app/assets.generated.ts'), out) const bytes = entries.reduce((n, e) => n + (e.text?.length ?? e.base64.length), 0) console.log(`Composed ${slideFiles.length} slides. Wrote ${entries.length} assets (${(bytes / 1024).toFixed(0)} KiB) to app/assets.generated.ts`)