mirror of
https://github.com/motajs/template.git
synced 2026-10-22 15:20:17 +08:00
docs(09-21): revision 5 — explicit process.exit, cmd /c commands, classifier tokens, D-97 style gap
This commit is contained in:
parent
2863183170
commit
e449579f25
@ -34,7 +34,7 @@ must_haves:
|
||||
- "**D-99 / D-100:** 构建仍前置 `vue-tsc --noEmit`(保留于 `build:game`);移除 `main.js` 二次压缩(babel)步骤与相关依赖引用;`manualChunks` 按实际依赖微调——`audio` chunk(`codec-parser` / `opus-decoder` / `ogg-opus-decoder` / `@wasm-audio-decoders/ogg-vorbis`)保留,`antdv` chunk 移除(全仓源码无 `ant-design-vue` / `@ant-design/icons-vue` 导入);`@vitejs/plugin-legacy` 维持现状(目标浏览器列表不变)。"
|
||||
- "**写入边界(D-56 / AGENTS.md / 用户裁定「你改 src,我做脚本」):** 只允许写 `files_modified` 所列路径;**完全不修改 `src/**`**(含 `src/bootstrap.ts`、`src/data/**`、`src/client/**`,以及用户待修复的两处 stale import);不回退用户既有改动(`index.html`、`packages-user/client-modules/src/**`、`src/**` 等 WIP);不改数据端/渲染端源码、`public/**`、`vite.config.ts`、lockfile、`.planning/ROADMAP.md` / `STATE.md` / `09-CONTEXT.md` / 既有 PLAN 与 SUMMARY。`dev.md` 仅按 W5 更新构建命令表中的 `build:packages` / `build:lib` / `build:game` 三行。**范围门禁以 Task 0 的 baseline 快照为基线:任何相对 baseline 新增或被二次修改的 `src/**` / `09-CONTEXT.md` 路径一律判负,不得整体白名单放行 `src/`。**"
|
||||
- "**命名须先获批(AGENTS.md / D-04):** 新增文件 `script/vite-plugin-data-isolation.ts`、导出 `dataIsolationPlugin`、插件 `name: 'mota-data-isolation'`、测试文件 `script/vite-plugin-data-isolation.test.ts`(OI-1);`script/build-resource.ts` 新导出 `buildContent` / `minifyJSONC` / `IContentBuildOptions` / `IContentBuildStats` 与测试文件 `script/build-resource.test.ts`(OI-2)。**类型一致性(W2):`IContentBuildOptions.chars` 为 `string`,`getAllChars` 相应返回 `Promise<string>`(`[...set].join('')`),`Fontmin.glyph({ text })` 收到的是字符串。** 未获批前不得落码(测试文件按 AGENTS.md 免审批,但仍列入报备)。"
|
||||
- "**验证可运行性(Nyquist)与门禁分层:** Task 1 以**真实 Vite `build()`** 驱动插件单测(渲染端 specifier(**未设 alias 的 `vue`**)被拒 / 渲染端解析路径(`packages-user/client-*`、`packages/render*`、`packages/client*`、`packages/system`、`packages/audio`、`packages/legacy-*`、`src/client`)被拒**且错误含命中 token** / 纯数据端通过并输出 ESM / 数据端路径不误判);Task 2 以 `minifyJSONC` / `buildContent` 单测 + 构建脚本结构断言(含**数据先于渲染的调用顺序**、**`dist` 只清空一次**、**两处 `emptyOutDir: false`**)+ 产物门禁 + **baseline 范围门禁**兜底;`pnpm exec tsx script/build-game.ts` **必须实跑**。**数据端 ESM 是独立硬门禁**:数据端先构建,`dist/data/data.js` 必须存在且含 `export`,否则 `bad.push` + 非零;**不因渲染端失败而跳过**。**渲染端产物门禁是条件分支**:`dist/index.html` 存在 → 断言不含 `styles.css` link;不存在 → 打印 BLOCKED 说明(渲染端因用户 `src/**` stale import 失败),而 `dist/data/data.js`、`dist/content/**`、`dist/LICENSE` 仍照常断言。**不存在任何无条件通过承诺。**"
|
||||
- "**验证可运行性(Nyquist)与门禁分层:** Task 1 以**真实 Vite `build()`** 驱动插件单测(渲染端 specifier(**未设 alias 的 `vue`**)被拒 / 渲染端解析路径(`packages-user/client-*`、`packages/render*`、`packages/client*`、`packages/system`、`packages/audio`、`packages/legacy-*`、`src/client`)被拒**且错误含命中 token** / 纯数据端通过并输出 ESM / 数据端路径不误判);Task 2 以 `minifyJSONC` / `buildContent` 单测 + 构建脚本结构断言(含**数据先于渲染的调用顺序**、**`dist` 只清空一次**、**两处 `emptyOutDir: false`**)+ 产物门禁 + **baseline 范围门禁**兜底;`cmd /c \"pnpm exec tsx script/build-game.ts\"` **必须实跑**(本仓 PowerShell 5.1 下裸 `pnpm` 会因 `pnpm.ps1` 的 `PSSecurityException` 失败,故一律经 `cmd /c` 调用),且**构建进程必须在有界时间内显式退出**(见 Task 2 的显式 `process.exit(...)` 要求)。**数据端 ESM 是独立硬门禁**:数据端先构建,`dist/data/data.js` 必须存在且含 `export`,否则 `bad.push` + 非零;**不因渲染端失败而跳过**。**渲染端产物门禁是条件分支**:`dist/index.html` 存在 → 断言不含 `styles.css` link;不存在 → 打印 BLOCKED 说明(渲染端因用户 `src/**` stale import 失败),而 `dist/data/data.js`、`dist/content/**`、`dist/LICENSE` 仍照常断言。**不存在任何无条件通过承诺。**"
|
||||
artifacts:
|
||||
- path: script/vite-plugin-data-isolation.ts
|
||||
provides: "新增 Vite 插件 `dataIsolationPlugin`:`enforce: 'pre'` + `resolveId` 拦渲染端 specifier,`moduleParsed` 按解析后绝对路径拦渲染端包(含传递依赖与 `src/client/**`),命中即 `this.error(...)` 终止数据端构建;错误消息含 `[mota-data-isolation]` + 命中模块 + **命中 token(`matched: <token>`)** + importers(命名待批准,OI-1)"
|
||||
@ -57,10 +57,10 @@ must_haves:
|
||||
- path: dev.md
|
||||
provides: "构建命令表同步:删除 `build:packages` / `build:lib` 两行,更新 `build:game` 说明(类型检查 + 渲染端游戏 + 数据端 ESM + content);不改其它章节"
|
||||
- path: .planning/phases/09-minimum-browser-runnable/09-21-SUMMARY.md
|
||||
provides: "本计划执行摘要:范围、插件扫描口径与错误行为(含命中 token)、content 压缩口径(含标记保留这一兼容性约束)、dist 产物清单与排除项、删除清单、`dev.md` 同步、`open_items` 最终处置、门禁结果(渲染端 BLOCKED 分支或成功分支)、数据端 ESM 硬门禁结果、渲染端两处 stale import 的当前状态、baseline 范围门禁结果"
|
||||
provides: "本计划执行摘要:范围、插件扫描口径与错误行为(含命中 token)、content 压缩口径(含标记保留这一兼容性约束)、dist 产物清单与排除项、删除清单、`dev.md` 同步、`open_items` 最终处置、门禁结果(渲染端 BLOCKED 分支或成功分支)、数据端 ESM 硬门禁结果、渲染端两处 stale import 的当前状态、**D-97「样式经入口 JS 引入」不由本计划交付的显式声明(`src/styles.less` 全仓无 import 点;移除 link 后、用户接线前 `dist` 缺 `#game-draw` 居中样式,OI-5;不静默降级)**、baseline 范围门禁结果"
|
||||
key_links:
|
||||
- "`dataIsolationPlugin`(`script/vite-plugin-data-isolation.ts`)↔ `buildData` 的 `plugins` 数组(`script/build-game.ts`)—— D-94 的唯一执行点;取代 `checkClientData` 体积启发式"
|
||||
- "插件 `this.error` 错误文本(含 `mota-data-isolation`、命中模块与**命中 token**)↔ `buildGame` 的数据端 `.catch` 分类 —— 归一化后(`String(reason)` 上把 Windows 反斜杠替换为 `/`,**源码单反斜杠正则**)同时匹配插件标识与已知渲染端 token(`@motajs/render` / `packages/render` / `packages/render-vue` / `packages/client` / `packages/system` / `packages/audio` / `packages/legacy-` / `packages-user/client-` / `src/client`)才标记为 **OI-11 回归诊断**;其它数据端错误标记为普通数据端失败;两条路径都使最终退出码非零(数据端 ESM 是硬门禁)"
|
||||
- "插件 `this.error` 错误文本(含 `mota-data-isolation`、命中模块与**命中 token**)↔ `buildGame` 的数据端 `.catch` 分类 —— 归一化后(`String(reason)` 上把 Windows 反斜杠替换为 `/`,**源码单反斜杠正则**)同时匹配插件标识与已知渲染端 token(`@motajs/render` / `@motajs/client` / `@motajs/system` / `@motajs/audio` / `@motajs/legacy-` / `@user/client-` / `packages/render` / `packages/render-vue` / `packages/client` / `packages/system` / `packages/audio` / `packages/legacy-` / `packages-user/client-` / `src/client`)才标记为 **OI-11 回归诊断**;其它数据端错误标记为普通数据端失败;两条路径都使最终退出码非零(数据端 ESM 是硬门禁)"
|
||||
- "`buildGame` 的调用顺序 `await buildData(` 先于 `await buildClient(` ↔ 数据端产物与渲染端解耦(D-95 / B1)—— 渲染端失败不会抹掉 `dist/data/data.js`;清空 `dist` 只在准备步骤执行一次,两次构建均 `emptyOutDir: false`"
|
||||
- "`buildData` 的 `build.lib.fileName: 'data'` + `formats: ['es']` ↔ `dist/data/data.js` —— D-95 产物契约(硬门禁)"
|
||||
- "`minifyJSONC` 的标记保留 ↔ `PrefixedJSONCProcessor.process`(`packages-user/data-state/src/loader/jsoncProcessor.ts:14-18`,`split(prefixSplit)` 要求恰好 2 段)—— 丢失标记会导致运行期 error 66 / `{}`"
|
||||
@ -90,7 +90,7 @@ must_haves:
|
||||
- "**未经批准的命名一律不得使用:** `script/vite-plugin-data-isolation.ts` / `dataIsolationPlugin` / `mota-data-isolation` / `script/vite-plugin-data-isolation.test.ts`(OI-1)与 `buildContent` / `minifyJSONC` / `IContentBuildOptions` / `IContentBuildStats` / `script/build-resource.test.ts`(OI-2)必须先经 Task 0 报备并获批准;未获批前不得落码。"
|
||||
- "**content 压缩必须保留标记行:** 不得只输出 `JSON.stringify` 结果而丢掉 `// --- SYSTEM PREFIX END --- //`;不得改后缀(仍为 `.jsonc`)。此为本计划对 D-96 的**兼容性补充**(OI-3),不是范围扩张。"
|
||||
- "**构建顺序不得倒置:** 不得先构建渲染端再构建数据端;不得在 `buildClient` 或 `buildData` 内使用 `emptyOutDir: true`(会删除已产出的 `dist/data`);不得让渲染端失败以任何方式跳过后面的 content / `LICENSE` / 产物断言。"
|
||||
- "**数据端门禁不得软化:** 不得为 `dist/data/data.js` 缺失写 expected-pending 软通过、不得写 sentinel 豁免、不得以「渲染端失败」为由跳过数据端 ESM 断言。严格签名只用于把数据端错误标记为 OI-11 回归诊断,最终退出码仍为非零。"
|
||||
- "**数据端门禁不得软化:** 不得为 `dist/data/data.js` 缺失写 expected-pending 软通过、不得写 sentinel 豁免、不得以「渲染端失败」为由跳过数据端 ESM 断言。严格签名只用于把数据端错误标记为 OI-11 回归诊断,最终退出码仍为非零。**`buildGame` 必须在每个分支显式 `process.exit(...)` 终止进程**(先 flush stdout;不得依赖事件循环自然退出——Vite/esbuild 的 handle 会让进程在渲染端构建失败后存活至超时)。"
|
||||
- "**不得删除 `public/**`:** D-97 的「整体删除」由用户执行;本计划只通过 `copyPublicDir: false` 阻止其进入 `dist`。"
|
||||
- "**写入边界(D-56 + 用户裁定「你改 src,我做脚本」):** 只允许写 `files_modified`;**不得修改 `src/**` 的任何文件**(包括 `src/bootstrap.ts`、`src/data/**`、`src/client/**`、两处 stale import);不得改 `vite.config.ts`、`packages/**`、`packages-user/**`、`public/**`、`script/dev.ts`、`script/declare.ts`、`script/types.ts`、`script/utils.ts`、lockfile;不得回退用户在工作树中的任何既有改动。`dev.md` 只允许改构建命令表三行。"
|
||||
- "**`.planning/**` 边界:** 不得改 `ROADMAP.md` / `STATE.md` / `09-CONTEXT.md` / 任何既有 PLAN 或 SUMMARY;只允许新增 `09-21-SUMMARY.md`(与 09-19 / 09-20 的既有做法一致)。范围门禁以 baseline 快照为准,**不得整体白名单放行 `src/` 或 `09-CONTEXT.md`**。"
|
||||
@ -100,7 +100,7 @@ must_haves:
|
||||
- "**不得为通过门禁而修改范围外文件:** 数据端若出现严格签名,只能记录 OI-11 回归诊断;渲染端若因用户两处 stale import 或其它 WIP 失败,也只能记录完整错误与归因;不得修 `src/**` 或包源码。"
|
||||
- "**不得伪造条件门禁:** 渲染端失败时不得手写 `dist/index.html` 以骗过产物门禁;不得把 BLOCKED 分支描述为「通过」;不得给出任何无条件通过承诺。注意:content 与 `LICENSE` 是构建脚本**正常产出**的,不是手写补的。"
|
||||
- "**不得使用双反斜杠正则做路径归一化:** 源码必须是单反斜杠正则(`String(reason)` 上把 Windows 反斜杠替换为 `/`),不得写成四反斜杠(在 Windows 上会把两个反斜杠误当一次匹配,导致分类失效)。"
|
||||
- "**不得把被删除的旧 pipeline 标识符写进注释或字符串:** Task 2 的结构门禁对 `transformAsync` / `archiver` / `compressing` / `checkClientData` / `ClientDataLevel` / `getFileName` / `getFileSize` / `generateResourceJSON` / `splitResource` / `source-code.zip` / `loadList.json` / `dist.zip` / `data.iife.js` / `_temp` 做负向检查,残留会被判负。"
|
||||
- "**不得把被删除的旧 pipeline 标识符写进注释或字符串:** Task 2 的结构门禁对 `transformAsync` / `archiver` / `compressing` / `checkClientData` / `ClientDataLevel` / `getFileName` / `getFileSize` / `generateResourceJSON` / `splitResource` / `source-code.zip` / `loadList.json` / `dist.zip` / `data.iife.js` / `_temp` 做负向检查,残留会被判负;并对**裸 `iife`** 做负向检查——`formats: ['iife']`、`data.iife.js`、甚至注释里解释「不用 iife」的说明都不得出现在 `script/build-game.ts` 中(要与该门禁口径一致,改写说明请放在本 PLAN / SUMMARY,不写进源码注释)。"
|
||||
open_items:
|
||||
- "**OI-1(插件文件与导出命名):** 提议新增 `script/vite-plugin-data-isolation.ts`,导出 `dataIsolationPlugin(): Plugin`,插件 `name: 'mota-data-isolation'`;测试文件 `script/vite-plugin-data-isolation.test.ts`。备选命名:`script/build-plugin-data-isolation.ts` / `forbidRenderDependencies` / `renderIsolationPlugin`。**待用户报备批准**(D-04;测试文件按 AGENTS.md 免审批,但仍一并报备)。"
|
||||
- "**OI-2(content 模块命名与类型):** 提议 `script/build-resource.ts` 导出 `buildContent(options: IContentBuildOptions): Promise<IContentBuildStats>`、`minifyJSONC(text: string): string`,接口 `IContentBuildOptions`(`sourceDir` / `outputDir` / **`chars: string`**)与 `IContentBuildStats`(`files` / `jsonc` / `fonts`);测试文件 `script/build-resource.test.ts`。**W2 类型一致性:`getAllChars` 返回 `Promise<string>`,`chars` 不做 `Set`。** 备选:`processContent` / `compressJSONC`。**待批准**(dev.md 要求对象入参声明 interface)。"
|
||||
@ -209,7 +209,7 @@ Phase 9 · 任务 11 · 第 21 个计划 —— **构建重构(适配新引擎
|
||||
|
||||
⑤ **D-96 content:** `src/content` → `dist/content`;`.jsonc` = **保留一行系统标记 + `JSON.stringify(JSON.parse(body))`**(去注释、后缀仍 `.jsonc`;标记必须保留,否则 `PrefixedJSONCProcessor` 运行期 error 66);字体用 fontmin glyph 子集压缩;**不分块、不 zip**。`build-resource` 改为从 `src/content` 读取素材。实测 `src/content/**` 共 **23** 个 jsonc,每个恰好一个标记。
|
||||
|
||||
⑥ **D-97 产物:** 不复制 `public/**`(`copyPublicDir: false`;整体删除由用户执行);dist 不携带旧模板内容(`editor.html` / `_server` / `_docs` / `libs` / `logo.png` / `styles.css` / `main.js` / `启动服务.exe`);不打包源码 zip;不生成资源地图 JSON;不生成 `dist.zip`;**复制 `LICENSE` 到 `dist/LICENSE`**;`index.html` 去掉旧样式表 link。样式 import 由用户后续在 `src/**` 侧添加(OI-5),本计划不改 `src/**`。
|
||||
⑥ **D-97 产物:** 不复制 `public/**`(`copyPublicDir: false`;整体删除由用户执行);dist 不携带旧模板内容(`editor.html` / `_server` / `_docs` / `libs` / `logo.png` / `styles.css` / `main.js` / `启动服务.exe`);不打包源码 zip;不生成资源地图 JSON;不生成 `dist.zip`;**复制 `LICENSE` 到 `dist/LICENSE`**;`index.html` 去掉旧样式表 link。**D-97 的「样式经入口 JS 引入」不由本计划交付:** `src/styles.less` 当前全仓无 import 点(仅 `index.html` 的旧 link 引用),按用户裁定「你改 src,我做脚本」本计划不改 `src/**`;因此移除 link 后、用户接线前 `dist` 缺 `#game-draw` 居中样式(OI-5),该缺口须**显式记入 SUMMARY**,不得描述为已交付。
|
||||
|
||||
⑦ **D-98 / D-99 / D-100:** 删除 `build:packages` / `build:lib`;`build:game` 调整(OI-6:移除读取已删 `public/project` 的 `declare.ts` 前置;**保留 `vue-tsc --noEmit`**);移除 babel 二次压缩;`manualChunks` 移除 `antdv`(源码已无引用)、保留 `audio`;legacy 插件维持现状;**`dev.md` 构建表删除 `build:packages` / `build:lib` 两行并更新 `build:game` 说明**。
|
||||
|
||||
@ -229,9 +229,9 @@ Phase 9 · 任务 11 · 第 21 个计划 —— **构建重构(适配新引擎
|
||||
|
||||
⑮ **不在本增量:** 不删 `public/**`;**不改 `src/**`(零改动,含两处 stale import)**;不改数据端/渲染端源码、`vite.config.ts`、`script/dev.ts`、`script/declare.ts`、`script/types.ts`、lockfile;不新增依赖;不改 `ROADMAP.md` / `STATE.md` / `09-CONTEXT.md` / 既有 PLAN 与 SUMMARY。
|
||||
|
||||
⑯ **已知阻断(须一并告知并记入 SUMMARY):** ① 数据端**当前无已知阻断**(历史 OI-11 链路已解决);② 渲染端可能因两处 stale import 失败(用户 WIP,用户修复);③ 若渲染端仍因用户其它 WIP 失败,记录完整错误与归因,**不得**修改范围外文件。
|
||||
⑯ **已知阻断(须一并告知并记入 SUMMARY):** ① 数据端**当前无已知阻断**(历史 OI-11 链路已解决);② 渲染端可能因两处 stale import 失败(用户 WIP,用户修复);③ 若渲染端仍因用户其它 WIP 失败,记录完整错误与归因,**不得**修改范围外文件;④ **样式缺口(D-97 未尽项)**:`src/styles.less` 全仓无 import 点,移除 `index.html` 的 `styles.css` link 后,用户接线前 `dist` 缺 `#game-draw` 居中样式(OI-5),本计划**不交付**「样式经入口 JS 引入」,须显式写明。
|
||||
|
||||
⑰ **验证方式:** Task 1 = `pnpm exec vitest run script/vite-plugin-data-isolation.test.ts`(真实 `vite.build()`:未设 alias 的 `vue` 被拒 / 拒渲染端解析路径(含 token 断言:`packages-user/client-`、`packages/render`、`packages/render-vue`、`packages/system`、`packages/audio`、`packages/legacy-`、`src/client`)/ 通过纯数据端并输出 ESM / 不误判数据端路径)+ 结构断言;Task 2 = `pnpm exec vitest run script/build-resource.test.ts` + 构建脚本结构断言(含 **数据先于渲染的调用顺序**、**`dist` 只清空一次**、**两处 `emptyOutDir: false`**、`getAllChars` 返回 `Promise<string>`、`dev.md` 构建表)+ 产物门禁(**数据端 ESM 硬断言** / 渲染端条件分支)+ **baseline 范围门禁** + `pnpm exec tsx script/build-game.ts` 实跑;**不设全仓 `check:type` / `build` / 全量 `test:ci` 门禁**。
|
||||
⑰ **验证方式(PowerShell 5.1 口径:一律经 `cmd /c` 调用,裸 `pnpm` 会因 `pnpm.ps1` 的 `PSSecurityException` 失败):** Task 1 = `cmd /c "pnpm exec vitest run script/vite-plugin-data-isolation.test.ts"`(真实 `vite.build()`:未设 alias 的 `vue` 被拒 / 拒渲染端解析路径(含 token 断言:`packages-user/client-`、`packages/render`、`packages/render-vue`、`packages/system`、`packages/audio`、`packages/legacy-`、`src/client`)/ 通过纯数据端并输出 ESM / 不误判数据端路径)+ 结构断言;Task 2 = `cmd /c "pnpm exec vitest run script/build-resource.test.ts"` + 构建脚本结构断言(含 **数据先于渲染的调用顺序**、**`dist` 只清空一次**、**两处 `emptyOutDir: false`**、**显式 `process.exit(...)` 终止**、`getAllChars` 返回 `Promise<string>`、`dev.md` 构建表)+ 产物门禁(**数据端 ESM 硬断言** / 渲染端条件分支)+ **baseline 范围门禁** + `cmd /c "pnpm exec tsx script/build-game.ts"` 实跑(必须在有界时间内退出);**不设全仓 `check:type` / `build` / 全量 `test:ci` 门禁**。
|
||||
|
||||
⑱ **修改方式声明(AGENTS.md):** 本计划执行含文件写入与删除,由**派发子代理执行**(`gsd-executor`),非内联执行。
|
||||
|
||||
@ -239,7 +239,7 @@ Phase 9 · 任务 11 · 第 21 个计划 —— **构建重构(适配新引擎
|
||||
</action>
|
||||
<decision>是否按本计划(09-21)执行「任务 11 · 构建重构」——新增数据端/渲染端分离 Vite 插件(错误含命中 token)并注入数据端库模式 ESM 构建(**数据端先构建、`dist/data/data.js` 为硬门禁**)、重写 `build-resource` 把 `src/content`(23 个 jsonc)压缩拷贝到 `dist/content`(jsonc 保留系统标记、fontmin、不分块不 zip)、重写 `build-game`(清空一次 → 数据端 → 渲染端条件 BLOCKED → content → LICENSE;去掉旧模板/babel/资源分块/源码 zip/资源地图/dist.zip)、删除 `build-packages.ts` / `build-lib.ts` 与对应 npm script、同步 `dev.md` 构建表、`index.html` 去旧样式表 link、保留 `vue-tsc --noEmit`、微调 `manualChunks`;**不删 `public/**`、不改 `src/**`(含用户待修复的两处渲染端 stale import)、不动用户其它并发 WIP**;**数据端 ESM 硬失败、渲染端产物条件分支**。</decision>
|
||||
<context>
|
||||
AGENTS.md / D-03 / D-04 要求执行前与命名先获批;`.planning/config.json` 为 `mode: yolo`,故本关卡显式 `blocking-human` 且 `autonomous: false`。执行期含写操作与**删除两个脚本**。`src/content` 共 **23** 个 jsonc,均为「前缀注释 + 严格 JSON」且每个恰好一个标记,加载器 `PrefixedJSONCProcessor` 依赖 `// --- SYSTEM PREFIX END --- //` 的 split(必须恰好 2 段),故压缩保留标记是运行期兼容硬约束(去的是标记**之外**的注释)。`src/content/font` 无字体二进制、`src/content/item.ts` 与 `core.jsonc` 的 `itemEffectDir` 不匹配——属既有缺口,本计划只记录不改。**revision 4 的关键事实:** 用户的 `src` 重构已删除 `src/constant.ts`,数据端闭包(`src/data/**`)**不含任何渲染端导入**,因此 `dist/data/data.js` 必须产出并断言 ESM(硬门禁);渲染端仍有两处指向已删除文件的 stale import(`src/client/initialize.ts:14` / `src/client/plugin/shop/shop.tsx:9`),属用户 WIP,本计划**不改 `src/**`**,故渲染端产物门禁是条件分支,但**绝不能拖累数据端硬门禁**。工作树含用户并发改动(`index.html`、`packages-user/client-modules/src/**`、`09-CONTEXT.md`、`pnpm-lock.yaml` 等),提交时须逐条核对暂存面、只暂存本计划 `files_modified`。行号为规划日实测、可能漂移,门禁以结构/行为断言为准。
|
||||
AGENTS.md / D-03 / D-04 要求执行前与命名先获批;`.planning/config.json` 为 `mode: yolo`,故本关卡显式 `blocking-human` 且 `autonomous: false`。执行期含写操作与**删除两个脚本**。`src/content` 共 **23** 个 jsonc,均为「前缀注释 + 严格 JSON」且每个恰好一个标记,加载器 `PrefixedJSONCProcessor` 依赖 `// --- SYSTEM PREFIX END --- //` 的 split(必须恰好 2 段),故压缩保留标记是运行期兼容硬约束(去的是标记**之外**的注释)。`src/content/font` 无字体二进制、`src/content/item.ts` 与 `core.jsonc` 的 `itemEffectDir` 不匹配——属既有缺口,本计划只记录不改。**revision 4 的关键事实:** 用户的 `src` 重构已删除 `src/constant.ts`,数据端闭包(`src/data/**`)**不含任何渲染端导入**,因此 `dist/data/data.js` 必须产出并断言 ESM(硬门禁);渲染端仍有两处指向已删除文件的 stale import(`src/client/initialize.ts:14` / `src/client/plugin/shop/shop.tsx:9`),属用户 WIP,本计划**不改 `src/**`**,故渲染端产物门禁是条件分支,但**绝不能拖累数据端硬门禁**。**baseline 由 Task 0 在执行时捕获,不预置并发文件清单:** 规划日实测 `git status --porcelain -uall` 仅含 `.planning/phases/09-minimum-browser-runnable/09-CONTEXT.md`、`.planning/phases/10-docs-restructure/10-CONTEXT.md` 与 `10-13..10-20-PLAN.md`、`pnpm-lock.yaml` 的既有改动(`index.html` / `packages-user/**` / `src/**` 当前无改动);执行时以实际快照为准,提交时须逐条核对暂存面、只暂存本计划 `files_modified`。行号为规划日实测、可能漂移,门禁以结构/行为断言为准。
|
||||
</context>
|
||||
<options>
|
||||
<option id="execute-default">
|
||||
@ -267,6 +267,8 @@ Phase 9 · 任务 11 · 第 21 个计划 —— **构建重构(适配新引擎
|
||||
- 汇报中已原样告知**门禁分层**:**数据端 ESM 硬门禁(缺失 / 非 ESM 即非零,无 pending 软通过)**;渲染端成功 → 追加渲染产物断言;渲染端因用户 stale import 失败 → **BLOCKED**(打印说明,不出 `ARTIFACT GATE FAILED`,不影响数据端断言);**无任何无条件通过承诺**
|
||||
- 汇报中已原样告知 **baseline 范围门禁口径**:Task 0 捕获 `git status --porcelain -uall` + `git diff --name-only -- src` + 路径 SHA-256 到系统临时目录;Task 2 对任何相对 baseline 新增或被二次修改的路径判负(白名单仅 `files_modified`),**`src/**` / `09-CONTEXT.md` 不再整体白名单放行**
|
||||
- 汇报中已原样告知:**删除 `build-packages.ts` / `build-lib.ts` 与 `build:packages` / `build:lib` 属不可逆的仓库内删除(可经 git 恢复)**、**content 压缩保留标记是对 D-96 的兼容性补充**、**`dev.md` 构建表将同步删除这两个命令**
|
||||
- 汇报中已**显式说明 D-97 的未尽项**:「样式经入口 JS 引入」**不由本计划交付**(`src/styles.less` 全仓无 import 点;移除 link 后、用户于 `src/**` 接线前,`dist` 缺 `#game-draw` 居中样式,OI-5),并承诺写入 SUMMARY,不静默降级
|
||||
- 汇报中已说明**构建必须在每个分支显式 `process.exit(...)` 终止**(先 flush stdout),不得依赖事件循环自然退出(渲染端失败时 Vite/esbuild 残留 handle 会使进程挂到超时),且所有验证命令一律经 `cmd /c "pnpm exec ..."` 调用(PowerShell 5.1 下裸 `pnpm` 触发 `PSSecurityException`)
|
||||
- 汇报中已原样告知**关卡性质**(必须停下等待;不因触发方式或 `yolo` / `auto_advance` 自动放行;回复「可以执行」即通过)
|
||||
</acceptance_criteria>
|
||||
<done>用户明确回复「可以执行」;baseline 快照已写入系统临时目录(含路径哈希与 `src` diff 清单);在此之前未执行任何任务、未修改或删除任何仓库文件、未派发任何执行子代理</done>
|
||||
@ -320,7 +322,7 @@ Phase 9 · 任务 11 · 第 21 个计划 —— **构建重构(适配新引擎
|
||||
</behavior>
|
||||
<reversibility rating="reversible">仅新增一个插件文件与一个测试文件;删除这两文件即可整体回退,未接线到构建脚本、未改任何既有文件。</reversibility>
|
||||
<verify>
|
||||
<automated>pnpm exec vitest run script/vite-plugin-data-isolation.test.ts</automated>
|
||||
<automated>cmd /c "pnpm exec vitest run script/vite-plugin-data-isolation.test.ts"</automated>
|
||||
<fails_when>non-zero exit, 或任一断言失败(渲染端 specifier 未被拒 / 渲染端解析路径未被拒 / `src/client` 目录未被拒 / 渲染端别名包未被拒或缺对应 `matched:` token / 纯数据端构建失败 / 数据端路径被误判 / 错误消息缺少插件标识或导入者)</fails_when>
|
||||
<automated>node -e "const fs=require('fs');const bad=[];const p='script/vite-plugin-data-isolation.ts';if(!fs.existsSync(p))bad.push('plugin file missing');else{const t=fs.readFileSync(p,'utf8');if(!/export\s+function\s+dataIsolationPlugin\s*\(/.test(t))bad.push('dataIsolationPlugin not exported');if(!/enforce\s*:\s*'pre'/.test(t))bad.push('plugin is not enforce:pre');if(t.indexOf('moduleParsed')<0)bad.push('plugin lacks moduleParsed resolved-path check');if(t.indexOf('this.error(')<0)bad.push('plugin does not abort via this.error');if(t.indexOf('mota-data-isolation')<0)bad.push('plugin error tag missing');if(t.indexOf('matched:')<0)bad.push('plugin error message lacks matched token');for(const tok of ['@user/client-','@user/entry-client','@motajs/render','@motajs/system','@motajs/audio','@motajs/legacy-','vue','@vue/','ant-design-vue','packages-user/client-','packages/render','packages/system','packages/audio','packages/legacy-','packages/client']){if(t.indexOf(tok)<0)bad.push('plugin forbidden set missing token: '+tok)}}const tp='script/vite-plugin-data-isolation.test.ts';if(!fs.existsSync(tp))bad.push('plugin test file missing');else{const t=fs.readFileSync(tp,'utf8');if(t.indexOf('build(')<0)bad.push('test does not call real vite build');if(t.indexOf('dataIsolationPlugin')<0)bad.push('test does not import the plugin');if(t.indexOf('matched:')<0)bad.push('test does not assert matched token');if(t.indexOf('src/client')<0)bad.push('test does not cover src/client')}if(bad.length>0){console.error('PLUGIN GATE FAILED :: '+bad.join(' ; '));process.exit(1)}console.log('OK plugin structure: enforce pre + specifier + resolved path + abort + matched token')"</automated>
|
||||
<fails_when>non-zero exit, 或 stderr 含 "PLUGIN GATE FAILED"(插件文件缺失 / 未导出 `dataIsolationPlugin` / 非 `enforce: 'pre'` / 缺 `moduleParsed` 路径检查 / 缺 `this.error` 终止 / 缺错误标签 / 错误消息缺 `matched:` / 禁令缺少关键 token / 测试文件缺失或未用真实 `vite.build()` / 测试未断言 `matched:` 或未覆盖 `src/client`)</fails_when>
|
||||
@ -357,7 +359,7 @@ Phase 9 · 任务 11 · 第 21 个计划 —— **构建重构(适配新引擎
|
||||
- 导出 `minifyJSONC(text: string): string`:以系统标记 `'// --- SYSTEM PREFIX END --- //'` 定位;`split` 后:**1 段** = 无标记,对整体 `JSON.stringify(JSON.parse(text))`;**2 段** = 标记存在,输出 `标记 + '\n' + JSON.stringify(JSON.parse(parts[1]))`;**其它段数** = 标记异常,抛出带文件语义的错误。**严禁**丢掉标记行;**不得**改调用方后缀。
|
||||
- 导出 `buildContent(options: IContentBuildOptions): Promise<IContentBuildStats>`:递归遍历 `sourceDir`(`readdir` with `withFileTypes`)并在 `outputDir` 内镜像目录结构:
|
||||
- `*.jsonc` → `minifyJSONC` 后写入同名路径;
|
||||
- 字体扩展(`.ttf` / `.otf` / `.woff` / `.woff2`)→ 用 `Fontmin` 的 `glyph({ text: options.chars })` 插件压缩到 `outputDir` 对应子目录(保持文件名);
|
||||
- 字体扩展(`.ttf` / `.otf` / `.woff` / `.woff2`)→ 沿用旧写法(`script/build-game.ts:475-482`):`const fontmin = new Fontmin();` + **`Fontmin.glyph({ text: options.chars })`** 插件,`fontmin.src(...).dest(...).use(plugin).runAsync()` 压缩到 `outputDir` 对应子目录(保持文件名)。**结构门禁按 `Fontmin` 与 `glyph` 两个独立字面量检查(不要求连写成 `Fontmin.glyph`),但 `.glyph({ text: options.chars })` 必须收到 `options.chars` 字符串**;
|
||||
- 其它文件(PNG / `.ts` 等)→ `fs-extra.copy` 原样拷贝;
|
||||
- `sourceDir/font` 下若**无**字体二进制:跳过 fontmin 并 `process.stdout.write` 告警(含目录),不抛错(OI-4)。
|
||||
- 按 dev.md 写 jsDoc;模块无顶层副作用;非空判断字面量用 `isNil`、对象用 `!`。
|
||||
@ -365,16 +367,16 @@ Phase 9 · 任务 11 · 第 21 个计划 —— **构建重构(适配新引擎
|
||||
|
||||
(2) **重写 `script/build-game.ts`:**
|
||||
- **保留**:`DEBUG_BUILD` / `DEBUG_REPLAY`、`ansi`、`logProgress`(外部行为)、`buildClient` / `buildData` 名称、`getAllChars` 名称、`distDir`、错误处理与 `process.exit(1)` 风格、`formatSize` 报告。
|
||||
- **删除**:`tempDir` / `_temp` 相关、`RESOUCE_SIZE`、`checkClientData` / `ClientDataLevel` / `getFileName` / `getFileSize`、`generateResourceJSON` / `CompressedLoadList*`、`@babel/core` 与其变换函数、`archiver` / `createWriteStream` / `compressing`、`RequiredData` / `RequiredIconsData` 及对 `public/project/{data,icons}.js` 的读取、`toCopy` 旧模板清单、源码压缩包、资源地图 JSON、资源分块拷贝、服务器 exe 拷贝、`dist.zip` 压缩与相关体积告警分支、`Fontmin` 直接引用。**删除后不得把上述标识符留在注释或字符串里**(结构门禁做负向检查)。
|
||||
- **删除**:`tempDir` / `_temp` 相关、`RESOUCE_SIZE`、`checkClientData` / `ClientDataLevel` / `getFileName` / `getFileSize`、`generateResourceJSON` / `CompressedLoadList*`、`@babel/core` 与其变换函数、`archiver` / `createWriteStream` / `compressing`、`RequiredData` / `RequiredIconsData` 及对 `public/project/{data,icons}.js` 的读取、`toCopy` 旧模板清单、源码压缩包、资源地图 JSON、资源分块拷贝、服务器 exe 拷贝、`dist.zip` 压缩与相关体积告警分支、`Fontmin` 直接引用(移至 `build-resource.ts`)、**`formats: ['iife']` 与任何 `iife` 字样**。**删除后不得把上述标识符留在注释或字符串里**(结构门禁对以上 token **及裸 `iife`** 做负向检查;说明性文字放本 PLAN / SUMMARY,不写进源码)。
|
||||
- **`buildClient(outDir)`**:复用 `loadConfigFromFile` + `mergeConfig`;注入 `legacy(...)`(目标浏览器列表与 `renderModernChunks:false` 维持现状,OI-7);`build.outDir = outDir`(即 `distDir`)、`copyPublicDir: false`、`minify: !DEBUG_BUILD`、**`emptyOutDir: false`**;`rollupOptions.external` 维持 `['@wasm-audio-decoders/opus-ml']`;`output` 维持 `format:'es'` / `entryFileNames` / `chunkFileNames` / `assetFileNames`;`manualChunks` 仅保留 `audio`(移除 `antdv`)。
|
||||
- **`buildData(outDir, entry)`**:`build.outDir = outDir`(调用方传 `resolve(distDir, 'data')`)、`copyPublicDir:false`、**`emptyOutDir: false`**、`minify: !DEBUG_REPLAY`、`lib: { entry, fileName: 'data', formats: ['es'] }`(移除 iife 与 `name`)、`plugins: [dataIsolationPlugin()]`。
|
||||
- **`buildData(outDir, entry)`**:`build.outDir = outDir`(调用方传 `resolve(distDir, 'data')`)、`copyPublicDir:false`、**`emptyOutDir: false`**、`minify: !DEBUG_REPLAY`、`lib: { entry, fileName: 'data', formats: ['es'] }`(`formats` 仅 `['es']`,源码中不得残留任何 `iife` 字样或 `name`)、`plugins: [dataIsolationPlugin()]`。
|
||||
- **`getAllChars()`:返回 `Promise<string>`**:递归读取渲染端产物中的 `.js`(**排除 `dist/data` 与 `dist/content`**,若 `dist` 下无 js 则跳过、不抛错)与 `src/content/**/*.jsonc` 的字符集合(`Set<string>`),最终以 `[...chars].join('')` 返回**字符串**;供 fontmin 使用。此函数不依赖数据端产物。
|
||||
- **`buildGame()` 编排(新进度表 5 步:构建前准备 → 构建数据端 → 构建渲染端 → 处理 content → 收尾):**
|
||||
1. `await ensureDir(distDir)` + **`await emptyDir(distDir)`(整个流程只执行一次)**。
|
||||
2. **先构建数据端(独立、硬门禁):** `await buildData(resolve(distDir, 'data'), resolve(process.cwd(), 'src/data.ts')).catch(reason => { ... })`;失败时把 `String(reason)` 中的 Windows 反斜杠归一化为 `/`(**源码单反斜杠正则**),若同时含 `mota-data-isolation` 与任一渲染端 token(`@motajs/render` / `packages/render` / `packages/render-vue` / `packages/client` / `packages/system` / `packages/audio` / `packages/legacy-` / `packages-user/client-` / `src/client`)→ 打印 `OI-11 回归诊断`;否则打印普通数据端失败;**两种情况都置 `dataFailed = true`**(不得吞掉、不得软通过、不得 `process.exit(0)`)。
|
||||
2. **先构建数据端(独立、硬门禁):** `await buildData(resolve(distDir, 'data'), resolve(process.cwd(), 'src/data.ts')).catch(reason => { ... })`;失败时把 `String(reason)` 中的 Windows 反斜杠归一化为 `/`(**源码单反斜杠正则**),若同时含 `mota-data-isolation` 与任一渲染端 token(`@motajs/render` / `@motajs/client` / `@motajs/system` / `@motajs/audio` / `@motajs/legacy-` / `@user/client-` / `packages/render` / `packages/render-vue` / `packages/client` / `packages/system` / `packages/audio` / `packages/legacy-` / `packages-user/client-` / `src/client`)→ 打印 `OI-11 回归诊断`;否则打印普通数据端失败;**两种情况都置 `dataFailed = true`**(不得吞掉、不得软通过、不得 `process.exit(0)`)。
|
||||
3. **后构建渲染端(条件分支):** `await buildClient(distDir).catch(reason => { ... })`;失败时打印 `BLOCKED`(说明错误来自用户 `src/**`,含两处 stale import),置 `renderBlocked = true`,**不覆盖** `dist/data`、**不退出**。
|
||||
4. `const chars = await getAllChars()`(`string`)→ `await buildContent({ sourceDir: resolve(process.cwd(), 'src/content'), outputDir: resolve(distDir, 'content'), chars })`(失败置 `contentFailed`)→ `await copy(resolve(process.cwd(), 'LICENSE'), resolve(distDir, 'LICENSE'))`(失败置 `licenseFailed`)。
|
||||
5. 输出:渲染端状态(BUILT / BLOCKED)、数据端状态(`dist/data/data.js`(ESM)或 FAILED + 归因)、content 统计(`files` / `jsonc` / `fonts`)、dist 体积;**`dataFailed || contentFailed || licenseFailed` → `process.exit(1)`**;仅 `renderBlocked` 时退出码保持 0(用户 WIP,非本计划失败)。**不生成任何压缩包。**
|
||||
5. 输出:渲染端状态(BUILT / BLOCKED)、数据端状态(`dist/data/data.js`(ESM)或 FAILED + 归因)、content 统计(`files` / `jsonc` / `fonts`)、dist 体积;随后**先 flush stdout**(`await new Promise(resolve => process.stdout.write('', resolve))`),再**无条件显式终止进程**:`process.exit(dataFailed || contentFailed || licenseFailed ? 1 : 0)`。**每个分支都必须到达这条显式退出**(数据端 / content / LICENSE 任一失败 → 1;仅 `renderBlocked` → 0),**不得依赖事件循环自然退出**——实测渲染端构建失败时 node/tsx 会因 Vite/esbuild 残留 handle 存活 7s+,构建会挂到超时。**不生成任何压缩包。**
|
||||
- `logProgress` 步骤表同步改为上述 5 项;`ansi.clear` 后的状态行不得被进度勾选覆盖。
|
||||
- 保留模块底部执行块(`buildGame()`);无需导出。
|
||||
|
||||
@ -386,7 +388,7 @@ Phase 9 · 任务 11 · 第 21 个计划 —— **构建重构(适配新引擎
|
||||
|
||||
(6) **`dev.md` 构建表(W5):** 删除 `build:packages` / `build:lib` 两行;`build:game` 行说明更新为「类型检查 + 构建渲染端游戏包与数据端 ESM 包 + content 处理」;**只改这三行,不动其它章节**。
|
||||
|
||||
(7) **`src/**`(零改动):** 按用户裁定「你改 src,我做脚本」,**不修改 `src/bootstrap.ts` 或任何 `src/**` 文件**。D-97 的「样式经入口 JS 引入」由用户后续在 `src/**` 侧完成(OI-5);本任务只在 SUMMARY 记录「用户接线前 `#game-draw` 居中样式可能缺失」的已知缺口。
|
||||
(7) **`src/**`(零改动):** 按用户裁定「你改 src,我做脚本」,**不修改 `src/bootstrap.ts` 或任何 `src/**` 文件**。**D-97 的「样式经入口 JS 引入」本计划不交付**——`src/styles.less` 当前全仓无 import 点,本计划只移除 `index.html` 的 link;在用户于 `src/**` 侧接线(OI-5)之前,`dist` **缺少 `#game-draw` 居中样式**,该缺口必须**显式写入 SUMMARY**,不得描述为已交付或静默降级。
|
||||
|
||||
(8) **新增 `script/build-resource.test.ts`(OI-2):** 使用临时目录测试 `minifyJSONC` 与 `buildContent`。
|
||||
- `minifyJSONC`:输入「`// Type: X` + 标记 + 多行 JSON」→ 输出以标记开头、正文为单行压缩 JSON、`split` 后恰好 2 段、`JSON.parse` 成功、不再含 `Type:` 注释。
|
||||
@ -395,22 +397,22 @@ Phase 9 · 任务 11 · 第 21 个计划 —— **构建重构(适配新引擎
|
||||
- `buildContent`:临时 `source/content` 放一个 jsonc、一个 PNG(任意字节)与一个空 `font/` 目录 → 输出 jsonc 已压缩、PNG 字节一致、`fonts === 0` 且不抛错(`chars` 以 `string` 传入)。
|
||||
- 每个 `it` 前一行注释。
|
||||
|
||||
(9) **实跑与分层门禁:** 先跑单测;再实跑 `pnpm exec tsx script/build-game.ts`。
|
||||
(9) **实跑与分层门禁:** 先跑单测;再实跑 `cmd /c "pnpm exec tsx script/build-game.ts"`(该进程必须经显式 `process.exit(...)` 在**有界时间内**返回;挂起到超时即判负)。
|
||||
- **数据端:** `dist/data/data.js` 必须存在且为 ESM(含 `export`);缺失 / 非 ESM → 硬失败(`bad.push` + 非零),**无 pending 软通过**。若失败文本含 `mota-data-isolation` 与渲染端 token,按 OI-11 回归诊断记录,但**仍然判负**。
|
||||
- **渲染端:** 若 `dist/index.html` 产出 → 断言其不含 `styles.css` link;若缺失 → 记为 **BLOCKED**(用户两处 stale import),不判本计划失败。
|
||||
- **content / LICENSE:** 无论两端成败都必须尝试产出并在产物门禁中断言。
|
||||
- **本计划自身错误**(插件 / `build-resource.ts` / `build-game.ts` / 测试)→ 必须修复后重跑;**不得**为通过门禁修改 `src/**` 或包源码。
|
||||
|
||||
(10) **`09-21-SUMMARY.md`:** 记录范围、插件扫描口径与错误行为(含 `matched:` token)、**数据端 ESM 硬门禁结果**、渲染端 BLOCKED 或成功分支与两处 stale import 当前状态、content 压缩口径(23 个 jsonc、标记保留理由、`Type:` 注释丢失)、dist 产物清单与 D-97 排除项、删除清单、`manualChunks` / legacy 调整、`index.html` 与样式落点(用户侧 OI-5)、命名获批情形、门禁结果(插件单测 / content 单测 / 结构断言 / 数据硬门禁 / 条件渲染门禁 / baseline 范围门禁)、`open_items` 最终处置、遗留缺口(`item.js`、字体缺失、OI-11、OI-12、样式 import)。
|
||||
(10) **`09-21-SUMMARY.md`:** 记录范围、插件扫描口径与错误行为(含 `matched:` token)、**数据端 ESM 硬门禁结果**、渲染端 BLOCKED 或成功分支与两处 stale import 当前状态、content 压缩口径(23 个 jsonc、标记保留理由、`Type:` 注释丢失)、dist 产物清单与 D-97 排除项、删除清单、`manualChunks` / legacy 调整、`index.html` 去 link、**D-97「样式经入口 JS 引入」不由本计划交付的显式声明**(`src/styles.less` 全仓无 import 点,用户接线前 `dist` 缺 `#game-draw` 居中样式,OI-5)、命名获批情形、门禁结果(插件单测 / content 单测 / 结构断言 / 数据硬门禁 / 条件渲染门禁 / baseline 范围门禁 / **构建进程显式退出**)、`open_items` 最终处置、遗留缺口(`item.js`、字体缺失、OI-11、OI-12、样式 import)。
|
||||
</action>
|
||||
<reversibility rating="costly">删除 `build-packages.ts` / `build-lib.ts` 与修改 `package.json` scripts 属仓库内结构性删除(可经 git 恢复);其余为文件重写,`git checkout` 可回退。执行后 `dist/` 布局改变,旧产物形态(资源分块 / `dist.zip`)不再可用。</reversibility>
|
||||
<verify>
|
||||
<automated>pnpm exec vitest run script/build-resource.test.ts</automated>
|
||||
<automated>cmd /c "pnpm exec vitest run script/build-resource.test.ts"</automated>
|
||||
<fails_when>non-zero exit, 或任一断言失败(标记未保留 / 注释未去除 / 非法 JSON 未抛错 / 无标记输入处理错误 / PNG 拷贝字节不一致 / 无字体时抛错 / `chars` 未以字符串传入)</fails_when>
|
||||
<automated>node -e "const fs=require('fs');const bad=[];const bg=fs.readFileSync('script/build-game.ts','utf8');for(const t of ['transformAsync','archiver','compressing','checkClientData','ClientDataLevel','getFileName','getFileSize','generateResourceJSON','splitResource','source-code.zip','loadList.json','dist.zip','data.iife.js','_temp']){if(bg.indexOf(t)>=0)bad.push('legacy token still present: '+t)}for(const t of ['copyPublicDir: false','emptyOutDir: false','dataIsolationPlugin','buildContent','LICENSE','Promise<string>','getAllChars','mota-data-isolation','@motajs/render','packages/render','packages/render-vue','packages/client','packages/system','packages/audio','packages/legacy-','src/client','String(reason)']){if(bg.indexOf(t)<0)bad.push('missing required token: '+t)}if(!/formats\s*:\s*\['es'\]/.test(bg))bad.push('data build is not ESM-only');if(bg.indexOf('iife')>=0)bad.push('build-game.ts still mentions iife');if(!/manualChunks\s*:\s*\{[\s\S]*audio/.test(bg))bad.push('audio manualChunk missing');if(/antdv\s*:/.test(bg))bad.push('antdv manualChunk must be removed');if(bg.split('emptyOutDir: false').length-1<2)bad.push('both buildClient and buildData must set emptyOutDir:false');const start=bg.indexOf('async function buildGame(');const body=start>=0?bg.slice(start):'';if(start<0)bad.push('buildGame function missing');const di=body.indexOf('await buildData(');const ci=body.indexOf('await buildClient(');if(di<0||ci<0)bad.push('buildGame must await buildData() and await buildClient()');else if(di>ci)bad.push('buildGame must build data BEFORE render');if(body.indexOf('emptyDir(')<0)bad.push('buildGame must clear dist in the prep step');else if(di>=0&&body.indexOf('emptyDir(')>di)bad.push('dist must be cleared before the data build');const br=fs.readFileSync('script/build-resource.ts','utf8');if(br.indexOf('splitResource')>=0||br.indexOf('JSZip')>=0)bad.push('build-resource.ts still has split/zip logic');for(const t of ['minifyJSONC','buildContent','SYSTEM PREFIX END','chars: string','Fontmin.glyph','Promise<IContentBuildStats>']){if(br.indexOf(t)<0)bad.push('build-resource.ts missing '+t)}if(!fs.existsSync('script/build-resource.test.ts'))bad.push('build-resource test missing');for(const p of ['script/build-packages.ts','script/build-lib.ts']){if(fs.existsSync(p))bad.push('legacy script not deleted: '+p)}const pkg=JSON.parse(fs.readFileSync('package.json','utf8'));for(const s of ['build:packages','build:lib']){if(s in pkg.scripts)bad.push('package.json still has script: '+s)}if(!pkg.scripts['build:game'].includes('vue-tsc --noEmit'))bad.push('build:game must keep vue-tsc --noEmit');if(pkg.scripts['build:game'].includes('declare.ts'))bad.push('build:game must drop declare.ts pre-step');const html=fs.readFileSync('index.html','utf8');if(html.indexOf('styles.css')>=0)bad.push('index.html still links the legacy stylesheet');if(html.indexOf('/src/bootstrap.ts')<0)bad.push('index.html entry script missing');const dev=fs.readFileSync('dev.md','utf8');for(const s of ['build:packages','build:lib']){if(dev.indexOf(s)>=0)bad.push('dev.md still lists '+s)}if(dev.indexOf('build:game')<0)bad.push('dev.md must keep the build:game row');if(bad.length>0){console.error('BUILD STRUCTURE GATE FAILED :: '+bad.join(' ; '));process.exit(1)}console.log('OK build structure: data-first order + single dist clear + double emptyOutDir:false + ESM data lib wired to isolation plugin + content/LICENSE + legacy scripts and doc rows removed')"</automated>
|
||||
<fails_when>non-zero exit, 或 stderr 含 "BUILD STRUCTURE GATE FAILED"(`build-game.ts` 仍含 babel / zip / 资源分块 / 旧模板 token;缺 `copyPublicDir: false`、`emptyOutDir: false`(两处)、`dataIsolationPlugin`、`buildContent`、`LICENSE`、`Promise<string>`、`getAllChars`、`mota-data-isolation` 或分类 token;数据端非 ESM 或仍含 iife;`audio` chunk 缺失或 `antdv` 未移除;**数据端未先于渲染端构建**;**`dist` 未在数据端构建前清空**;`build-resource.ts` 仍含分块/zip 或缺压缩/标记/`chars: string`;测试文件缺失;旧脚本未删除;`package.json` 仍含被删 script、`build:game` 丢了 `vue-tsc --noEmit` 或仍含 `declare.ts`;`index.html` 仍有旧样式表 link 或缺入口;`dev.md` 仍列出被删命令或缺 `build:game` 行)</fails_when>
|
||||
<automated>pnpm exec tsx script/build-game.ts</automated>
|
||||
<fails_when>non-zero exit(数据端硬门禁失败、content 或 LICENSE 失败、或本计划文件自身出错)——**渲染端因用户两处 stale import 失败并打印 BLOCKED 但退出码为 0 时不算失败**;但若构建脚本吞掉数据端失败并以 0 退出,则视为本计划错误;若错误来自用户 WIP 的范围外文件(渲染端),记录完整错误与归因、不修改范围外文件,BLOCKED 写入 SUMMARY</fails_when>
|
||||
<automated>node -e "const fs=require('fs');const bad=[];const bg=fs.readFileSync('script/build-game.ts','utf8');for(const t of ['transformAsync','archiver','compressing','checkClientData','ClientDataLevel','getFileName','getFileSize','generateResourceJSON','splitResource','source-code.zip','loadList.json','dist.zip','data.iife.js','_temp']){if(bg.indexOf(t)>=0)bad.push('legacy token still present: '+t)}for(const t of ['copyPublicDir: false','emptyOutDir: false','dataIsolationPlugin','buildContent','LICENSE','Promise<string>','getAllChars','mota-data-isolation','@motajs/render','@motajs/client','@motajs/system','@motajs/audio','@motajs/legacy-','@user/client-','packages/render','packages/render-vue','packages/client','packages/system','packages/audio','packages/legacy-','packages-user/client-','src/client','String(reason)']){if(bg.indexOf(t)<0)bad.push('missing required token: '+t)}if(!/formats\s*:\s*\['es'\]/.test(bg))bad.push('data build is not ESM-only');if(bg.indexOf('iife')>=0)bad.push('build-game.ts must not mention iife at all (code, strings or comments; D-95 requires ESM-only)');if(!/manualChunks\s*:\s*\{[\s\S]*audio/.test(bg))bad.push('audio manualChunk missing');if(/antdv\s*:/.test(bg))bad.push('antdv manualChunk must be removed');if(bg.split('emptyOutDir: false').length-1<2)bad.push('both buildClient and buildData must set emptyOutDir:false');const start=bg.indexOf('async function buildGame(');const body=start>=0?bg.slice(start):'';if(start<0)bad.push('buildGame function missing');const di=body.indexOf('await buildData(');const ci=body.indexOf('await buildClient(');if(di<0||ci<0)bad.push('buildGame must await buildData() and await buildClient()');else if(di>ci)bad.push('buildGame must build data BEFORE render');if(body.indexOf('emptyDir(')<0)bad.push('buildGame must clear dist in the prep step');else if(di>=0&&body.indexOf('emptyDir(')>di)bad.push('dist must be cleared before the data build');if(body.indexOf('dataFailed')<0)bad.push('buildGame must track dataFailed');if(body.indexOf('process.exit(')<0)bad.push('buildGame must end with an explicit process.exit(...)');const br=fs.readFileSync('script/build-resource.ts','utf8');if(br.indexOf('splitResource')>=0||br.indexOf('JSZip')>=0)bad.push('build-resource.ts still has split/zip logic');for(const t of ['minifyJSONC','buildContent','SYSTEM PREFIX END','chars: string','Fontmin','.glyph(','Promise<IContentBuildStats>']){if(br.indexOf(t)<0)bad.push('build-resource.ts missing '+t)}if(!fs.existsSync('script/build-resource.test.ts'))bad.push('build-resource test missing');for(const p of ['script/build-packages.ts','script/build-lib.ts']){if(fs.existsSync(p))bad.push('legacy script not deleted: '+p)}const pkg=JSON.parse(fs.readFileSync('package.json','utf8'));for(const s of ['build:packages','build:lib']){if(s in pkg.scripts)bad.push('package.json still has script: '+s)}if(!pkg.scripts['build:game'].includes('vue-tsc --noEmit'))bad.push('build:game must keep vue-tsc --noEmit');if(pkg.scripts['build:game'].includes('declare.ts'))bad.push('build:game must drop declare.ts pre-step');const html=fs.readFileSync('index.html','utf8');if(html.indexOf('styles.css')>=0)bad.push('index.html still links the legacy stylesheet');if(html.indexOf('/src/bootstrap.ts')<0)bad.push('index.html entry script missing');const dev=fs.readFileSync('dev.md','utf8');for(const s of ['build:packages','build:lib']){if(dev.indexOf(s)>=0)bad.push('dev.md still lists '+s)}if(dev.indexOf('build:game')<0)bad.push('dev.md must keep the build:game row');if(bad.length>0){console.error('BUILD STRUCTURE GATE FAILED :: '+bad.join(' ; '));process.exit(1)}console.log('OK build structure: data-first order + single dist clear + double emptyOutDir:false + ESM data lib wired to isolation plugin + content/LICENSE + legacy scripts and doc rows removed')"</automated>
|
||||
<fails_when>non-zero exit, 或 stderr 含 "BUILD STRUCTURE GATE FAILED"(`build-game.ts` 仍含 babel / zip / 资源分块 / 旧模板 token;缺 `copyPublicDir: false`、`emptyOutDir: false`(两处)、`dataIsolationPlugin`、`buildContent`、`LICENSE`、`Promise<string>`、`getAllChars`、`mota-data-isolation` 或分类 token;数据端非 ESM 或仍含 iife;`audio` chunk 缺失或 `antdv` 未移除;**数据端未先于渲染端构建**;**`dist` 未在数据端构建前清空**;`build-resource.ts` 仍含分块/zip 或缺压缩/标记/`chars: string`;测试文件缺失;旧脚本未删除;`package.json` 仍含被删 script、`build:game` 丢了 `vue-tsc --noEmit` 或仍含 `declare.ts`;`index.html` 仍有旧样式表 link 或缺入口;`dev.md` 仍列出被删命令或缺 `build:game` 行;**`buildGame` 未以显式 `process.exit(...)` 终止(或未跟踪 `dataFailed`)**)</fails_when>
|
||||
<automated>cmd /c "pnpm exec tsx script/build-game.ts"</automated>
|
||||
<fails_when>non-zero exit(数据端硬门禁失败、content 或 LICENSE 失败、或本计划文件自身出错)——**渲染端因用户两处 stale import 失败并打印 BLOCKED 但退出码为 0 时不算失败**;但若构建脚本吞掉数据端失败并以 0 退出,则视为本计划错误;若错误来自用户 WIP 的范围外文件(渲染端),记录完整错误与归因、不修改范围外文件,BLOCKED 写入 SUMMARY;**构建进程未在有界时间内返回(挂起 / 超时)一律判负**——`buildGame` 必须在每个分支显式 `process.exit(dataFailed || contentFailed || licenseFailed ? 1 : 0)`(先 flush stdout),不得依赖事件循环自然退出</fails_when>
|
||||
<automated>node -e "const fs=require('fs');const bad=[];const dataPath='dist/data/data.js';if(!fs.existsSync(dataPath))bad.push('HARD GATE: dist/data/data.js missing');else{const d=fs.readFileSync(dataPath,'utf8');if(!/export\s*\{/.test(d))bad.push('HARD GATE: dist/data/data.js is not ESM (no export statement)')}for(const p of ['dist/content/core.jsonc','dist/content/client.jsonc','dist/content/enemy.jsonc','dist/content/item.jsonc','dist/content/tile.jsonc','dist/content/maps/.list.jsonc','dist/content/maps/sample0.jsonc','dist/content/defaults/tile.jsonc','dist/content/texture/.list.jsonc','dist/content/texture/terrains0.png','dist/content/animation/.list.jsonc','dist/content/audio/.list.jsonc','dist/content/font/.list.jsonc','dist/LICENSE']){if(!fs.existsSync(p))bad.push('missing '+p)}const M='// --- SYSTEM PREFIX END --- //';for(const p of ['dist/content/core.jsonc','dist/content/client.jsonc']){if(!fs.existsSync(p))continue;const t=fs.readFileSync(p,'utf8');const parts=t.split(M);if(parts.length!==2)bad.push(p+' marker must appear exactly once');else{try{JSON.parse(parts[1])}catch(e){bad.push(p+' body is not valid JSON')}}if(t.indexOf('// Type:')>=0)bad.push(p+' still contains the Type comment')}for(const p of ['dist/project','dist/main.js','dist/source-code.zip','dist/loadList.json','dist/styles.css','dist/logo.png','dist/editor.html','dist/_docs','dist/_server','dist/libs','dist/data.process.js','dist/data/data.iife.js','dist.zip']){if(fs.existsSync(p))bad.push('unexpected '+p)}if(fs.existsSync('dist/data')){for(const f of fs.readdirSync('dist/data')){if(f.endsWith('.iife.js')||f.endsWith('.process.js'))bad.push('unexpected dist/data/'+f)}}if(fs.existsSync('dist/index.html')){const h=fs.readFileSync('dist/index.html','utf8');if(h.indexOf('styles.css')>=0)bad.push('dist/index.html still links styles.css')}else{process.stdout.write('BLOCKED: dist/index.html absent (render build failed on user src stale imports: src/client/initialize.ts ../constant, src/client/plugin/shop/shop.tsx @/constant). Render artifact gate skipped; data/content/LICENSE gates still enforced.\n')}if(bad.length>0){console.error('ARTIFACT GATE FAILED :: '+bad.join(' ; '));process.exit(1)}console.log('OK artifacts: data.js ESM + content/** compressed (marker kept, Type comment removed) + LICENSE'+(fs.existsSync('dist/index.html')?' + index.html without styles.css':' (render BLOCKED)'))"</automated>
|
||||
<fails_when>non-zero exit, 或 stderr 含 "ARTIFACT GATE FAILED"(`dist/data/data.js` 缺失或非 ESM / content 必需产物缺失 / `core.jsonc` 或 `client.jsonc` 标记未恰好保留一次或正文非合法 JSON / `// Type:` 注释仍在 / D-97 所列旧产物仍存在 / `dist/data` 含 iife 或 process 产物 / `dist/index.html` 存在却仍链 `styles.css`)。`dist/index.html` 缺失只打印 BLOCKED,**不判负**</fails_when>
|
||||
<!-- plan-criteria-allow: R4 - 本门禁内的 git 由 node execSync 调用并以 try/catch 捕获(失败即非零退出),命令中的 | 属于 JS 正则/字符串,不是 shell 管道阶段 -->
|
||||
@ -418,15 +420,15 @@ Phase 9 · 任务 11 · 第 21 个计划 —— **构建重构(适配新引擎
|
||||
<fails_when>non-zero exit, 或 stderr 含 "SCOPE GATE FAILED"(baseline 快照缺失 / git 捕获失败 / 出现相对 baseline 的**新增**路径且不在 `files_modified` 白名单 / 出现相对 baseline 被**二次修改**的路径(哈希变化)/ `git diff --name-only -- src` 出现 baseline 之外的新条目)</fails_when>
|
||||
</verify>
|
||||
<acceptance_criteria>
|
||||
- **数据端硬门禁(必过):** `dist/data/data.js` 存在且为 ESM(含 `export`);数据端构建在渲染端之前完成;`dist` 只在准备步骤清空一次;`buildClient` 与 `buildData` 均为 `emptyOutDir: false`;数据端失败(含 OI-11 回归签名)→ 退出码非零,**不存在 pending 软通过或 sentinel 豁免**
|
||||
- **数据端硬门禁(必过):** `dist/data/data.js` 存在且为 ESM(含 `export`);数据端构建在渲染端之前完成;`dist` 只在准备步骤清空一次;`buildClient` 与 `buildData` 均为 `emptyOutDir: false`;数据端失败(含 OI-11 回归签名)→ 退出码非零,**不存在 pending 软通过或 sentinel 豁免**;`buildGame` 在**每个分支**显式 `process.exit(...)` 后返回,构建进程**不得挂起到超时**
|
||||
- **渲染端条件分支:** `dist/index.html` 存在时断言不含 `styles.css` link;缺失时打印 BLOCKED(用户两处 stale import:`src/client/initialize.ts:14` / `src/client/plugin/shop/shop.tsx:9`)并跳过渲染产物断言,**不判本计划失败**
|
||||
- **content / LICENSE:** `dist/content/**`(jsonc 压缩且保留系统标记恰好一次、PNG 原样、字体按 OI-4 处理)与 `dist/LICENSE` 在两种分支下均被断言
|
||||
- `script/build-resource.ts` 已重写为 content 处理模块(`minifyJSONC` / `buildContent` / `chars: string`,按 OI-2 命名);不再含分块 / zip / `JSZip`;`script/build-resource.test.ts` 全绿
|
||||
- `script/build-game.ts` 已重写:删除全部旧 pipeline 标识符(不得残留在注释/字符串中),`manualChunks` 仅保留 `audio`、legacy 维持现状、`getAllChars` 返回 `Promise<string>`
|
||||
- `script/build-packages.ts` / `script/build-lib.ts` 已删除;`package.json` 不再含 `build:packages` / `build:lib`,`build:game` 保留 `vue-tsc --noEmit` 且去掉 `declare.ts` 前置
|
||||
- `index.html` 仅移除旧样式表 link;**`src/**` 零改动**(样式 import 由用户侧完成,OI-5);`dev.md` 构建表同步(删除两行、保留并更新 `build:game`)
|
||||
- `index.html` 仅移除旧样式表 link;**`src/**` 零改动**;`dev.md` 构建表同步(删除两行、保留并更新 `build:game`);**D-97「样式经入口 JS 引入」本计划不交付**(`src/styles.less` 全仓无 import 点,OI-5;用户接线前 `dist` 缺 `#game-draw` 居中样式,须显式写入 SUMMARY,不得静默降级)
|
||||
- **baseline 范围门禁:** 相对 Task 0 快照无新增、无二次修改的 `src/**` 与 `09-CONTEXT.md` 路径(白名单仅 `files_modified`;不整体放行 `src/`)
|
||||
- `09-21-SUMMARY.md` 已写(范围、插件口径、数据端硬门禁结果、渲染端分支与 stale import 状态、content 压缩与标记理由、产物/排除清单、删除清单、命名获批、门禁结果、`open_items` 处置、遗留缺口)
|
||||
- `09-21-SUMMARY.md` 已写(范围、插件口径、数据端硬门禁结果、渲染端分支与 stale import 状态、content 压缩与标记理由、产物/排除清单、删除清单、命名获批、门禁结果、**构建进程显式退出**、**D-97「样式经入口 JS 引入」不由本计划交付的显式声明(OI-5 缺口)**、`open_items` 处置、遗留缺口)
|
||||
</acceptance_criteria>
|
||||
<done>构建流程完成新引擎适配:数据端先构建且 `dist/data/data.js`(ESM)为硬门禁、content 以压缩形态(保留标记)落到 `dist/content`、`LICENSE` 保留、渲染端按条件分支产出或打印 BLOCKED、旧脚本与旧 pipeline 全部移除、`src/**` 零改动;插件单测 / content 单测 / 结构断言 / 数据硬门禁 / baseline 范围门禁全绿,结果与分支处置写入 `09-21-SUMMARY.md`</done>
|
||||
</task>
|
||||
@ -458,7 +460,7 @@ Phase 9 · 任务 11 · 第 21 个计划 —— **构建重构(适配新引擎
|
||||
| T-09-21-04 | Tampering | 越界写入 / 回退用户 WIP / 误改 `src/**` | medium | mitigate | `files_modified` 白名单 + prohibitions(`src/**` 零改动)+ **baseline 快照范围门禁**(新增或哈希变化的路径一律判负,不整体放行 `src/`) |
|
||||
| T-09-21-05 | Tampering | 误删用户仍需要的脚本或 npm script(不可逆结构删除) | medium | mitigate | 删除范围仅 `build-packages.ts` / `build-lib.ts` 与对应两个 script(D-93 / D-98);Task 0 明示可经 git 恢复;结构门禁只允许这两个文件缺失 |
|
||||
| T-09-21-06 | Tampering | 未经批准使用新命名(插件 / 接口 / 成员) | medium | mitigate | Task 0 报备 OI-1 / OI-2;prohibitions 明列「未获批前不得落码」 |
|
||||
| T-09-21-07 | DoS | 数据端硬门禁被软通过、或 OI-11 签名吞掉真实数据端错误 | high | mitigate | 分类必须同时匹配 `mota-data-isolation` 与(`@motajs/render` / `packages/render` / `packages/render-vue` / `packages/client` / `packages/system` / `packages/audio` / `packages/legacy-` / `packages-user/client-` / `src/client`);**两条路径都 `dataFailed = true` 并最终非零**;产物门禁对 `dist/data/data.js` 缺失 / 非 ESM 直接 `bad.push`,无 pending 豁免 |
|
||||
| T-09-21-07 | DoS | 数据端硬门禁被软通过、或 OI-11 签名吞掉真实数据端错误 | high | mitigate | 分类必须同时匹配 `mota-data-isolation` 与(`@motajs/render` / `@motajs/client` / `@motajs/system` / `@motajs/audio` / `@motajs/legacy-` / `@user/client-` / `packages/render` / `packages/render-vue` / `packages/client` / `packages/system` / `packages/audio` / `packages/legacy-` / `packages-user/client-` / `src/client`);**两条路径都 `dataFailed = true` 并最终非零**;产物门禁对 `dist/data/data.js` 缺失 / 非 ESM 直接 `bad.push`,无 pending 豁免;`buildGame` 每个分支显式 `process.exit(...)`,避免渲染端失败后被残留 handle 挂到超时 |
|
||||
| T-09-21-08 | Tampering | 字体缺失或格式非法导致 fontmin 抛错阻断构建 | medium | mitigate | 无字体二进制时跳过 fontmin 并告警(OI-4);`buildContent` 单测覆盖空 `font/` 目录 |
|
||||
| T-09-21-09 | Tampering | `manualChunks` 引用不存在的 `ant-design-vue` 造成分块噪声/构建告警 | low | mitigate | 移除 `antdv` chunk、保留 `audio`;结构门禁断言 |
|
||||
| T-09-21-10 | Information Disclosure | 改动既有注释 / jsDoc(AGENTS.md 禁改) | low | mitigate | prohibitions 明列;仅重写两个构建脚本自身,其它文件保持原注释 |
|
||||
@ -469,7 +471,7 @@ Phase 9 · 任务 11 · 第 21 个计划 —— **构建重构(适配新引擎
|
||||
<verification>
|
||||
- **Task 0:** 预执行状态(旧构建脚本 / `package.json` scripts / `index.html` 旧 link / `dev.md` 三行构建表 / 加载器标记逻辑就位;`src/content` 共 23 个 jsonc 且标记各恰好一次;插件与测试文件不存在;数据端链路为 data-only);**baseline 快照**(`git status --porcelain -uall` 路径哈希 + `git diff --name-only -- src`)写入系统临时目录;人工关卡(用户回复「可以执行」;OI-1..OI-12 若有指定按其指定)。
|
||||
- **Task 1:** `dataIsolationPlugin` 结构(`enforce: 'pre'` + `resolveId` + `moduleParsed` + `this.error` + 禁令集合 + `packages/client*` / `src/client` 守卫 + `matched:` token);运行期单测以真实 `vite.build()` 断言「拒渲染端 specifier / 拒渲染端解析路径(含别名包与 token)/ 拒 `src/client` / 纯数据端通过并产出 ESM / 数据端不误判」。
|
||||
- **Task 2:** `build-resource` 单测(标记保留、去注释、非法 JSON 抛错、PNG 原样、无字体跳过);`build-game` / `package.json` / `index.html` / `build-resource` / `dev.md` 结构断言(旧 pipeline 移除、**数据端先于渲染端**、**`dist` 只清空一次**、两处 `emptyOutDir: false`、ESM 数据端 + 插件注入、content + LICENSE、旧脚本删除、scripts 调整、样式 link 移除、`chars: string` / `Promise<string>`);`pnpm exec tsx script/build-game.ts` 实跑——数据端 ESM **硬门禁**(缺失 / 非 ESM 即非零)、渲染端**条件分支**(存在则断言、缺失则 BLOCKED)、content 与 LICENSE 必过;baseline 范围门禁。
|
||||
- **Task 2:** `build-resource` 单测(标记保留、去注释、非法 JSON 抛错、PNG 原样、无字体跳过);`build-game` / `package.json` / `index.html` / `build-resource` / `dev.md` 结构断言(旧 pipeline 移除、**数据端先于渲染端**、**`dist` 只清空一次**、两处 `emptyOutDir: false`、ESM 数据端 + 插件注入、content + LICENSE、旧脚本删除、scripts 调整、样式 link 移除、`chars: string` / `Promise<string>`);`cmd /c "pnpm exec tsx script/build-game.ts"` 实跑(**必须经显式 `process.exit(...)` 在有界时间内退出**)——数据端 ESM **硬门禁**(缺失 / 非 ESM 即非零)、渲染端**条件分支**(存在则断言、缺失则 BLOCKED)、content 与 LICENSE 必过;baseline 范围门禁。
|
||||
- **一致性:** `dist/content/**` 落点与 `MotaDataLoader.resolveURL` 生产态一致;jsonc 标记与 `PrefixedJSONCProcessor` 契约一致;`build:game` 与 `script/build-game.ts` 入口一致;`index.html` 去 link 与 OI-5(用户侧样式接线)一致。
|
||||
- **无全仓门禁:** 全程不使用 `pnpm check:type` / `pnpm build` / 全量 `pnpm test:ci` / 全仓 TS 诊断数(用户 WIP 存在历史类型错误)。
|
||||
- **人工复核:** 结论写入 `09-21-SUMMARY.md`(范围、插件口径、数据端硬门禁结果、渲染端分支与两处 stale import 状态、content 压缩与标记理由(23 个 jsonc)、产物/排除清单、删除清单、命名获批、门禁结果、`open_items` 最终处置)。
|
||||
@ -479,7 +481,7 @@ Phase 9 · 任务 11 · 第 21 个计划 —— **构建重构(适配新引擎
|
||||
- **D-94:** 数据端构建由模块图插件把关(specifier + 解析路径双路,含 `packages/client*` / `src/client/**`),命中渲染端模块即 `this.error` 终止且错误含 `matched:` token;`checkClientData` 及体积启发式已删除;数据端依赖不误判。
|
||||
- **D-95:** 数据端库模式 `formats: ['es']`,产物 `dist/data/data.js`;无 iife 产物;**数据端先于渲染端构建**,两者均 `emptyOutDir: false`;`dist/data/data.js` 缺失或非 ESM **一律判负**(无预期放行、无 sentinel 豁免)。
|
||||
- **D-96:** `src/content`(23 个 jsonc)→ `dist/content`;jsonc 压缩**保留系统标记**、后缀仍 `.jsonc`;fontmin 压缩(无字体时跳过告警);不分块、不 zip;`build-resource` 从 `src/content` 读取。
|
||||
- **D-97:** `copyPublicDir: false`;dist 不含 D-97 所列旧模板产物;`LICENSE` 复制到 `dist/LICENSE`;`index.html` 旧样式表 link 移除;样式 import 由用户侧 `src/**` 完成(OI-5,本计划不改 `src/**`)。
|
||||
- **D-97:** `copyPublicDir: false`;dist 不含 D-97 所列旧模板产物;`LICENSE` 复制到 `dist/LICENSE`;`index.html` 旧样式表 link 移除;**「样式经入口 JS 引入」不由本计划交付**——`src/styles.less` 全仓无 import 点,本计划不改 `src/**`(OI-5),用户接线前 `dist` 缺 `#game-draw` 居中样式,缺口须显式记录。
|
||||
- **D-93 / D-98 / D-99 / D-100:** 两个旧脚本删除、两个 npm script 删除、`dev.md` 构建表同步、`build:game` 保留 `vue-tsc --noEmit`;babel 二次压缩移除;`manualChunks` 去 `antdv` 保 `audio`;legacy 插件维持现状。
|
||||
- **边界:** 未删 `public/**`;**未改 `src/**`(零改动,含两处 stale import)**;未改数据端/渲染端源码、`vite.config.ts`、`script/dev.ts`、lockfile、`ROADMAP.md` / `STATE.md` / `09-CONTEXT.md` / 既有 PLAN 与 SUMMARY;未回退用户 WIP。
|
||||
- **门禁分层:** 数据端 ESM 硬门禁必须通过;渲染端成功 → 追加渲染产物断言;渲染端因用户 stale import 失败 → 打印 BLOCKED 并跳过渲染断言(数据端、content、LICENSE 仍断言);**不作出任何无条件通过承诺**。
|
||||
|
||||
Loading…
Reference in New Issue
Block a user