Files
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

78 lines
2.9 KiB
TypeScript

// 验收「构建开关在热更新重建里保持」。
//
// 上一版这个验证是**空的**:调试服因为参数不认识根本没起来,于是"没重建"被当成了"重建后开关还在"。
// 所以这里第一件事就是断言服务真的活着——一个不检查前置条件的验证等于没有验证。
//
// 用法:node tools/checks/verify-dev-flags.mts [base]
// 前提:调试服以 --with-sim 启动,且产物里本来就带模拟器。
import { appendFileSync, readdirSync, existsSync, readFileSync, writeFileSync } from "node:fs";
import { join } from "node:path";
import { sleep } from "../cdp.mjs";
const BASE = process.argv[2] ?? "http://127.0.0.1:5173";
const CSS = "src/styles/index.css";
const RELEASES = "dist/releases";
let failures = 0;
const check = (ok: unknown, label: string, detail?: unknown) => {
if (!ok) failures++;
console.log(` ${ok ? "✓" : "✗"} ${label}${detail !== undefined ? ` → ${String(detail)}` : ""}`);
};
const simulatorCount = () => {
let n = 0;
for (const dir of readdirSync(RELEASES)) {
if (existsSync(join(RELEASES, dir, "scripts", "wallpaper-engine.js"))) n += 1;
}
return n;
};
const original = readFileSync(CSS, "utf8");
try {
console.log("=== ① 前置条件:调试服真的活着 ===");
let alive = false;
try {
const res = await fetch(`${BASE}/`);
alive = res.status === 200;
} catch {
alive = false;
}
check(alive, "调试服响应 GET /", alive ? BASE : "连不上——后面所有结论都不成立");
if (!alive) throw new Error("调试服没起来,验证无意义");
const before = simulatorCount();
check(before > 0, "重建前产物里带模拟器", `${before} 个分发`);
if (before === 0) throw new Error("产物里没有模拟器,请先 node tools/build.ts --with-sim");
console.log("\n=== ② 保存一次源文件,等热更新重建 ===");
appendFileSync(CSS, `\n/* dev-flags probe ${Date.now()} */\n`);
// 用"被改的 CSS 已经能被取到新内容"作为重建完成的判据,而不是干等固定秒数。
let rebuilt = false;
const deadline = Date.now() + 30000;
while (Date.now() < deadline) {
await sleep(400);
try {
const text = await (await fetch(`${BASE}/release/collection-all/styles/index.css`)).text();
if (/dev-flags probe/.test(text)) {
rebuilt = true;
break;
}
} catch {
// 重建中偶尔取不到,继续等
}
}
check(rebuilt, "重建完成(调试服已能取到新的 CSS)");
console.log("\n=== ③ 开关是否保持 ===");
const after = simulatorCount();
check(after === before, "--with-sim 在热更新重建后仍然生效", `${before} → ${after}`);
} finally {
writeFileSync(CSS, original);
await sleep(3000);
console.log("\n源文件已还原");
}
console.log(failures === 0 ? "构建开关保持:通过" : `构建开关保持:失败 ${failures} 处`);
process.exit(failures === 0 ? 0 : 1);