import path from 'path'; import { minify } from 'html-minifier'; import UglifyJS from 'uglify-js'; import wasmPlugin from './wasmPlugin'; import imagePlugin from './imagePlugin'; import fontPlugin from './fontPlugin'; import audioPlugin from './audioPlugin'; import filePlugin from './filePlugin'; import { getGames } from './isGame'; interface Args { production?: boolean; portable?: boolean; mobile?: boolean } export async function buildHTML(game: string, { production = false, portable = false, mobile = false }: Args = {}) { const html = await Bun.file(path.resolve(import.meta.dir, 'assets', 'index.html')).text(); const bundle = await Bun.build({ outdir: '/tmp', entrypoints: [path.resolve(import.meta.dir, '..', 'src', 'index.ts')], sourcemap: production ? 'none' : 'inline', define: { global: 'window', GAME: `"${game}"`, GAMES: JSON.stringify(await getGames()), }, plugins: [ imagePlugin, audioPlugin, fontPlugin, wasmPlugin({ production, portable }), filePlugin, ] }); if (bundle.success) { const scriptFile = bundle.outputs.find(a => a.kind === 'entry-point' && a.path.endsWith('.js')); if (!scriptFile) { console.error('No entry point found:', bundle.outputs); return; } const iconFile = Bun.file(path.resolve(import.meta.dir, '..', 'src', 'games', game, 'assets', 'favicon.ico')); let icon = ''; if (await iconFile.exists()) { icon = ``; } let style = ''; let styleFile = bundle.outputs.find(a => a.kind === 'asset' && a.path.endsWith('.css')); if (styleFile) { style = await styleFile.text(); } let script = await scriptFile.text(); const inits = new Set(); script = script.replace(/var (init_[^ ]+) = __esm\(\(\)/g, (_, $1) => { inits.add($1); return `var ${$1} = __esm(async ()`; }); for (const init of inits) { script = script.replaceAll(`${init}()`, `await ${init}()`); } script = script.replaceAll('await Promise.resolve().then(() =>', '('); let scriptPrefix = ''; if (production) { const minifyResult = UglifyJS.minify(script, { module: true, }); if (minifyResult.error) { console.warn(`Minify error: ${minifyResult.error}`); } else { script = minifyResult.code; } } else if (mobile) { const eruda = await Bun.file(path.resolve(import.meta.dir, '..', 'node_modules', 'eruda', 'eruda.js')).text(); scriptPrefix = ``; } const resultHTML = html .replace('', () => `${scriptPrefix}`) .replace('', game[0].toUpperCase() + game.slice(1).toLowerCase()) .replace('', icon) .replace('/*$STYLE$*/', style); return minify(resultHTML, { collapseWhitespace: production, decodeEntities: true, minifyCSS: production, minifyJS: production, }); } else { console.error('Failed: ', !bundle.success, bundle); } }