// Turns the built web app (dist/) into a C file the payload compiles in. // Every file is stored gzip-compressed and served that way. // // node scripts/embed-assets.mjs import fs from 'node:fs'; import path from 'node:path'; import zlib from 'node:zlib'; import { fileURLToPath } from 'node:url'; const [distDir, outFile] = process.argv.slice(2); if (!distDir || !outFile) { console.error('usage: embed-assets.mjs '); process.exit(1); } const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..'); const version = JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf8')).version; const MIME = { '.html': 'text/html; charset=utf-8', '.js': 'text/javascript; charset=utf-8', '.css': 'text/css; charset=utf-8', '.svg': 'image/svg+xml', '.json': 'application/json; charset=utf-8', '.png': 'image/png', '.ico': 'image/x-icon', '.wasm': 'application/wasm', }; function walk(dir, prefix = '') { const out = []; for (const entry of fs.readdirSync(dir, { withFileTypes: true })) { const rel = `${prefix}/${entry.name}`; if (entry.isDirectory()) out.push(...walk(path.join(dir, entry.name), rel)); else out.push(rel); } return out.sort(); } const files = walk(distDir); if (!files.includes('/index.html')) { console.error(`${distDir} has no index.html; run "npm run build" first`); process.exit(1); } let c = '/* Generated by scripts/embed-assets.mjs. Do not edit. */\n#include "assets.h"\n\n'; c += `const char CS_VERSION[] = ${JSON.stringify(version)};\n\n`; const rows = []; let total = 0; files.forEach((rel, i) => { const gz = zlib.gzipSync(fs.readFileSync(path.join(distDir, rel)), { level: 9 }); total += gz.length; const bytes = []; for (let o = 0; o < gz.length; o += 20) bytes.push(' ' + [...gz.subarray(o, o + 20)].join(',')); c += `static const unsigned char asset_${i}[] = {\n${bytes.join(',\n')}\n};\n\n`; const mime = MIME[path.extname(rel).toLowerCase()] ?? 'application/octet-stream'; rows.push(` {${JSON.stringify(rel)}, ${JSON.stringify(mime)}, asset_${i}, sizeof asset_${i}, ${rel.startsWith('/assets/') ? 1 : 0}}`); }); c += `const struct asset CS_ASSETS[] = {\n${rows.join(',\n')}\n};\n\nconst int CS_ASSET_COUNT = ${files.length};\n`; fs.mkdirSync(path.dirname(outFile), { recursive: true }); fs.writeFileSync(outFile, c); console.log(`Embedded ${files.length} files, ${(total / 1024).toFixed(0)} KB compressed -> ${outFile}`);