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 处引用自包含。
This commit is contained in:
1 parent
b8eee05d78
commit
3f11426964
297 files changed
+216627
-1926
No files matched your search
@@ -0,0 +1,75 @@
|
||||
// 游戏合集的文案必须是**它自己那个游戏**的——产物里不许出现别家的标题。
|
||||
//
|
||||
// 这条门禁来自一个真实缺陷:addGame 曾经把 title 取全局 meta(那一层是给「全部合集」写的),
|
||||
// 于是 collection-ys 顶着《崩坏:星穹铁道》昔涟。三条断言都只看产物,不看实现:
|
||||
// ① 前置条件:dist/releases 必须存在。否则"一个分发都没有"会被当成通过(空验证比不验证更危险)。
|
||||
// ② 游戏合集的 title 必须提到本游戏的显示名,且逐字等于 wallpapers/<游戏id>/meta.json 的 title。
|
||||
// ③ 全部合集 collection-all 的 title 必须来自 wallpapers/meta.json。
|
||||
//
|
||||
// 前置:先 `pnpm build`(只用产物与源数据,不需要服务器)。
|
||||
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
|
||||
const RELEASES = "dist/releases";
|
||||
|
||||
if (!existsSync(RELEASES)) {
|
||||
console.error(`前置条件不满足:${RELEASES} 不存在。先跑 pnpm build。`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
let failures = 0;
|
||||
const check = (ok: boolean, label: string, detail?: string): void => {
|
||||
if (!ok) failures++;
|
||||
console.log(` ${ok ? "✓" : "✗"} ${label}${detail !== undefined ? ` → ${detail}` : ""}`);
|
||||
};
|
||||
|
||||
const readJson = (path: string): Record<string, unknown> =>
|
||||
JSON.parse(readFileSync(path, "utf8")) as Record<string, unknown>;
|
||||
const str = (value: unknown): string => (typeof value === "string" ? value : "");
|
||||
|
||||
const globalMeta = readJson("wallpapers/meta.json");
|
||||
const releases = readdirSync(RELEASES).filter((name) => name.startsWith("collection-"));
|
||||
|
||||
let checkedGames = 0;
|
||||
for (const dir of releases) {
|
||||
const projectPath = join(RELEASES, dir, "project.json");
|
||||
if (!existsSync(projectPath)) {
|
||||
check(false, `${dir} 缺少 project.json`);
|
||||
continue;
|
||||
}
|
||||
const project = readJson(projectPath);
|
||||
const gameId = dir.slice("collection-".length);
|
||||
|
||||
if (gameId === "all") {
|
||||
check(str(project.title) === str(globalMeta.title), "collection-all 的 title 来自全局 meta", str(project.title));
|
||||
continue;
|
||||
}
|
||||
|
||||
const gameMetaPath = `wallpapers/${gameId}/meta.json`;
|
||||
if (!existsSync(gameMetaPath)) {
|
||||
check(false, `${dir} 找不到对应的 ${gameMetaPath}`);
|
||||
continue;
|
||||
}
|
||||
const gameMeta = readJson(gameMetaPath);
|
||||
checkedGames++;
|
||||
|
||||
check(
|
||||
str(project.title) === str(gameMeta.title),
|
||||
`${dir} 的 title 逐字等于它自己游戏的 title`,
|
||||
`产物 ${str(project.title) || "(空)"} / 源 ${str(gameMeta.title) || "(未声明)"}`,
|
||||
);
|
||||
check(
|
||||
str(project.title).includes(str(gameMeta.name)),
|
||||
`${dir} 的 title 提到了本游戏「${str(gameMeta.name)}」`,
|
||||
str(project.title),
|
||||
);
|
||||
if (gameMeta.description !== undefined) {
|
||||
check(str(project.description) === str(gameMeta.description), `${dir} 的 description 用的是游戏级声明`);
|
||||
}
|
||||
}
|
||||
|
||||
// 数量断言:目录命名一变(比如以后换成别的前缀),上面整个循环会静默变成空验证。
|
||||
check(checkedGames > 0, "至少检查到一个游戏合集", `实际 ${checkedGames} 个`);
|
||||
|
||||
console.log(failures === 0 ? "\n游戏合集文案归属:通过" : `\n游戏合集文案归属:失败 ${failures} 处`);
|
||||
process.exit(failures === 0 ? 0 : 1);
|
||||
Reference in new issue
Block a user