Files
SpineWallpaper/tools/checks/README.md
T
Shuery 3f11426964 feat: TS 构建管线、Spine 抓取器与 wallpapers/ 唯一真相来源
把项目从「手写 dist/」改成「wallpapers/ 是唯一真相来源,dist/ 由 pnpm build 生成」,
并补上配套的类型、门禁与抓取器。一次提交落地整条管线,因为拆开会留下不能构建的中间态。

- src/:运行时与模拟器源码(TS,strict),编译到 build/ 再拷进各分发
- tools/:build / dev / check-{syntax,paths,dist},以及抓取器与回归门禁 tools/checks/
  (.scratch/ 下那批一次性脚本移入 tools/checks/ 并入库为长期门禁)
- wallpapers/:七档壁纸的源数据 + README.md(id/音频/预设的完整规范)
- docs/adr/0005-0008:构建管线与分发拓扑、模拟器契约、自包含 sim、每骨架资源布局
- .gitignore:排除 .scratch/ 的参考资料副本(上游 spine 整仓克隆 ~1.2 GB、
  抓取侦查数据 ~680 MB)与调试转储;这些是本地调查材料,补偿会让仓库无法克隆
- 归一化 .gitignore/CONTEXT.md 行尾(工作区 CRLF、索引 LF 造成的整文件假 diff)

同时修掉三档卡住构建的未完工壁纸:
- kv45 的 meta.json 里 id 还是抓取期场景名 scene_main,经 downloader promote 正名为 kv45
- shajin / zhigengniao_juheye 的 meta.json 误用了骨架描述文件(name/spine/animations/pages)、
  且都缺 preset.template.json;现按规范重建:骨架沉到 spines/<名>/(spine-ts 按 atlas 所在
  目录解析贴图页)、补上元数据与单骨架预设,并清掉 zhigengniao 骨架里指向作者机的绝对路径
- 顺带 promote 已在 sources.yml 里的 kv46(月升之前,与兽共舞)

pnpm check 五道门全绿:10 个分发 / 7 档壁纸 / 141 处引用自包含。
2026-10-02 01:27:02 +08:00

5.9 KiB
Raw Blame History

tools/checks/ — 验收套件

跑一遍就相当于"这次改动没把东西弄坏"的全部证据。它原来是散在 .scratch/ 里的, 而 .scratch/ 按 AGENTS.md 是问题与规格的存放处——这些脚本不是某条 issue 的产物, issue 关掉之后还要一直跑,所以搬到了这里。


怎么跑

它们的前置条件不一样,不能一条命令全跑完。按下面三组来:

① 不需要服务器

pnpm check                                     # 类型 + 构建 + 语法 + 路径 + 产物自包含 + 游戏合集文案归属
pnpm build sim                                 # 下面几项要 sim 产物
node tools/checks/test-sim-gate.mts            # 门禁真的会拦(往页面注入四种外部依赖)
node tools/checks/test-inline-guard.mts        # 内联 </script 守卫真的会拦
node tools/checks/test-transform-module.mts    # 模块合成的 export 改写
node tools/checks/diff-project.mts             # 产物 project.json vs 已发布基线
node tools/checks/check-collection-copy.mts    # 游戏合集的文案是"它自己那个游戏"的(先 pnpm build)
node tools/checks/verify-sim-page.mts <分发名>  # 自包含包 file:// 可用(含"零 http 请求")
node tools/checks/verify-static-preview.mts <分发名>  # 静态托管子路径可用(自带服务器)
node tools/checks/read-audio-tags.mts          # 读音频文件自带标签(核对歌名用)
node tools/checks/check-sim-scripts.mts [页面]  # 抽出自包含页的每个内联 script 单独语法检查
node tools/checks/check-downloader.mts         # 抓取器:能编译 + 夹具绿 + 四处破坏必红(不联网)
node tools/checks/verify-scene-player.mts      # 场景播放器:多骨架+平面合成、画面非空、atlas 指错必红

verify-scene-player 自己跑 tsc -p tsconfig.runtime.json 并自造夹具(用抓取器 staged 的 kv45/scene_main 与 nico-tea/scene_main——staging 是"一个场景一个壁纸目录"), 不碰 wallpapers/、不需要 pnpm build;前置只有 python -m tools.downloader fetch --page kv45。

verify-static-preview 需要 pnpm build --with-sim(面板要随产物走)。

② 需要 serve.mjs(对拍驱动 window.__test 只有它注入)

⚠ 跑 test-inline-guard 之前必须先停掉 pnpm dev。 它会故意把 src/vendor/spine-player.js 改坏来验证守卫,而调试服的热更新正在监听 src/——两者会抢构建,结果随机失败。 同理,任何会写 dist/ 的构建都别和 watcher 同时跑(pnpm build sim 与 watcher 也会抢 dist/)。

node tools/serve.mjs 8190 --root dist/releases/collection-all   # 另开一个终端
node tools/checks/test-props.mjs        # bgm 选择/回落 + fps 限流
node tools/checks/test-resize.mjs       # 改窗口尺寸不泄漏、不跑偏
node tools/checks/test-acceptance.mjs   # 体积/文案/音源/构图总验收
node tools/checks/verify-fitter.mjs     # viewport-fitter 的纯函数

③ 需要 pnpm dev(模拟器面板)

node tools/dev.ts --port 5173 --no-build                        # 另开一个终端
node tools/checks/smoke-dev.mts 5173          # 路由、注入、越界 404
node tools/checks/smoke-sim.mts http://127.0.0.1:5173           # 面板真的挂上、属性真的下发
node tools/checks/verify-panel.mts http://127.0.0.1:5173       # 面板还原度(12 组)
node tools/checks/verify-hot-reload.mts http://127.0.0.1:5173  # 改 CSS 只换样式表、改 runtime 整页刷新
node tools/checks/check-single-bgm.mts http://127.0.0.1:5173   # 单档分发里切歌真的换音源
node tools/checks/shot-panel.mts http://127.0.0.1:5173 [输出目录]  # 给面板出图(不是断言)

verify-dev-flags.mts 要另一个模式:先 node tools/build.ts --with-sim, 再 node tools/dev.ts --port 5173 --no-build --with-sim,然后 node tools/checks/verify-dev-flags.mts。它验的是"热更新重建后 --with-sim 还在"。

verify-visual-equivalence.mts 要两步:先用 tools/capture.mjs 拍到 tools/shots/visual-current/,再跑它。基线在 tools/shots/baseline-pre-refactor/, 不能重新生成。


两层,以及为什么

后缀 是否被 pnpm typecheck 检查 说明
.mts ✅ 走 tsconfig.checks.json 后来写的验收脚本
.mjs ❌ 四个早期的运行时测试:test-acceptance / test-props / test-resize / verify-fitter

tsconfig.checks.json 只关掉 noImplicitAny 一项,其余严格模式照旧—— 正是严格模式翻出了 28 处真问题(可能 undefined、[] 推成 never[]、catch 里 error 是 unknown、 参数个数不符),其中一条是"文件头声明了 ⑤ 但从未实现的断言"。

往 .mjs 里写类型注解会让它运行时崩。 .mjs 是纯 JS,(x: string) => … 是 SyntaxError, 而 tsc 不管 .mjs,所以这个错在类型检查里完全看不见——踩过一次。 要写类型就先改成 .mts。

把那四个 .mjs 也并进受检层是一件独立的、该单独做的事:实测会立刻多出约 80 处既有问题。 别在别的改动的尾巴上顺手做。


约定

  • 一个从不失败的检查等于没有检查。 门禁类的脚本(test-sim-gate / test-inline-guard) 都要"先证明它会红"——注入一个坏东西,确认它报错,再还原。
  • 验证要先检查前置条件。 verify-dev-flags.mts 第一件事是 fetch("/") 断言服务活着; 否则"服务没起来"会被当成"验证通过"——这是真实发生过的一次空验证。
  • 区分"选项在"和"能生效"。 verify-panel.mts 断言面板上有那个下拉; check-single-bgm.mts 断言选了之后 <audio>.src 真的变了。两者缺一不可。