// 单文件离线版:直接从源码打包,不依赖开发服务器或现有 web/dist。 // npm run build:standalone [-- /绝对路径/试玩版.html] import { mkdir, readFile, writeFile } from 'node:fs/promises'; import { dirname, resolve } from 'node:path'; import { fileURLToPath } from 'node:url'; import { build, transform } from 'esbuild'; const root = fileURLToPath(new URL('../', import.meta.url)); const out = process.argv[2] ? resolve(process.argv[2]) : resolve(root, '../../releases/数字一笔-试玩版.html'); const [template, css, bundle] = await Promise.all([ readFile(resolve(root, 'web/index.html'), 'utf8'), readFile(resolve(root, 'web/style.css'), 'utf8'), build({ absWorkingDir: root, entryPoints: ['web/main.ts'], bundle: true, format: 'iife', target: ['es2020'], minify: true, write: false, legalComments: 'none', loader: { '.json': 'json' }, }), ]); const style = (await transform(css, { loader: 'css', minify: true })).code; const script = bundle.outputFiles[0].text; function inlineOnce(html, needle, replacement) { if (html.split(needle).length !== 2) throw new Error(`HTML 入口已变动,需要更新单文件构建脚本:${needle}`); // 回调替换,避免 JS/CSS 内的 $& 等字符被解释为替换指令。 return html.replace(needle, () => replacement); } let html = inlineOnce(template, '', ``); html = inlineOnce(html, '', ''); html = inlineOnce(html, '按数字顺序,一笔走遍每一格', '全部关卡开放,自由选关试玩'); html = inlineOnce(html, '', ``); html = html.replace('