/** * scripts/build.mjs — LittleJS 增强发行版打包脚本(esbuild 锁参,core-protocol-v0) * * ⚠️ 运行环境纪律(执行版 §1/§5): * **本脚本仅在「集成段(mini-desktop)」运行**,不在 lane 本机跑。lane 阶段一切测试 = * 零依赖 `node --test`;esbuild 是工具链依赖(需 `npm i -D esbuild`),只在 mini-desktop 装与跑。 * 在无 esbuild 的本机执行本脚本会**显式报错并给出安装指引**(不静默失败)。 * * 锁定参数(§5,不得擅改 —— 改动影响 SIZES 口径与 channel-spike 对照): * bundle + minify + format=iife + target=es2019 + sourcemap=false * * 用法(集成段): * node scripts/build.mjs <入口.js> <输出.js> [--global-name=NAME] * 例:node scripts/build.mjs src/core/plugin.js dist/core.iife.js --global-name=GameRuntimeCore */ 'use strict'; import { argv, exit } from 'node:process'; import { writeFileSync } from 'node:fs'; /** 锁定的 esbuild 构建参数(§5 写死,集成段与 size.mjs 必须同口径)。 */ export const LOCKED_BUILD_OPTIONS = { bundle: true, minify: true, format: 'iife', target: 'es2019', sourcemap: false, // legalComments:'none' 去掉许可证注释,保证 minify 后字节口径稳定(不影响合规,许可证在仓内 LICENSE 留存)。 legalComments: 'none', }; /** * 用锁定参数构建单个入口。集成段调用;本机无 esbuild 时抛错给指引。 * @param {string} entry 入口文件路径 * @param {string} outfile 输出文件路径 * @param {string} [globalName] iife 全局变量名(可选) * @returns {Promise} */ export async function build(entry, outfile, globalName) { let esbuild; try { // 动态 import:仅集成段装了 esbuild 才解析得到;本机解析失败走下方指引。 esbuild = await import('esbuild'); } catch { console.error( '[build] 未找到 esbuild —— 本脚本仅在集成段(mini-desktop)运行。\n' + ' 请在集成段执行:npm i -D esbuild,再重跑本脚本。\n' + ' lane 本机请勿运行构建(纪律:本机零依赖、只跑 node --test)。' ); throw new Error('esbuild 不可用(非集成段环境)'); } // 【A0/门1① additive】metafile:true 落 .meta.json,供 grep 核验「引擎真入包 + 被哪些 chunk 引用」。 // additive(不改 LOCKED_BUILD_OPTIONS 锁参口径):移除 metafile 不影响产物字节,仅少一份诊断文件。 const result = await esbuild.build({ ...LOCKED_BUILD_OPTIONS, entryPoints: [entry], outfile, metafile: true, ...(globalName ? { globalName } : {}), }); if (result && result.metafile) { writeFileSync(outfile + '.meta.json', JSON.stringify(result.metafile)); console.log(`[build] metafile → ${outfile}.meta.json`); } console.log(`[build] OK ${entry} → ${outfile} (iife/es2019/min/no-sourcemap)`); } // ── CLI 入口(仅集成段使用)────────────────────────────────────────────────── // 仅当作为脚本直接运行时执行 CLI;被 import(如 size.mjs 复用 LOCKED_BUILD_OPTIONS)时不触发。 if (import.meta.url === `file://${argv[1]}`) { const [entry, outfile, ...rest] = argv.slice(2); if (!entry || !outfile) { console.error('用法:node scripts/build.mjs <入口.js> <输出.js> [--global-name=NAME]'); exit(1); } const gn = (rest.find((a) => a.startsWith('--global-name=')) || '').split('=')[1]; build(entry, outfile, gn).catch((e) => { console.error('[build] 失败:' + (e && e.message ? e.message : e)); exit(1); }); }