fix(skin): 修复最终评审发现的 6 处缺陷(junction 排除失效/meta 静默丢失等)

跨任务视角发现的问题:Critical1 materialize.mjs 的 junction 排除比较在
Windows 上因长路径前缀恒假、从未生效,且测试夹具的 junction 指向真源自身,
掩盖了此问题;Critical2 composeSkin 例外通道在 uuid 缺失/框架侧无 meta 时
静默失败,违反第二准则;Important3 可覆盖范围误把 theme/ 下的框架 TS 代码
也纳入;Important5 cli-entry 测试漏注册 check-skin/build-game;Important6
composeSkin 与 check-skin 各自拼路径,未共用 paths.mjs 权威推导;Important7
--platform 缺值时静默落到默认平台。逐条修复并补测试,Critical1 用诱饵框架
目录验证过能真正杀掉该 bug(变异推演见 fix report)。

spec §5 step5 的 dist/<name>/ 拷贝按要求标注为本期未实现,不实现。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-27 23:15:28 +08:00
co-authored by Claude Opus 5
parent 88deab3533
commit 5708926711
12 changed files with 209 additions and 24 deletions
+19 -1
View File
@@ -5,6 +5,15 @@ const IMAGE_EXTS = new Set(['.png', '.jpg', '.jpeg', '.webp']);
const SPINE_ROOT = 'spine'; // framework/ui/spine/<资源名>/ 为一套 Spine
const SKIPPED = new Set(['.gitkeep']);
/**
* 可被子游戏覆盖的框架资源目录(spec §2 规则 3 枚举的 8 个目录)。
* `theme/` 不在其中——那是 §4.2 规定的框架 TS 代码位置,不是资源,不可覆盖。
*/
const COVERABLE_DIRS = new Set([
'atlas-common', 'atlas-login', 'atlas-hall', 'atlas-room',
'standalone', 'spine', 'audio', 'font',
]);
/** 列出目录下所有资源的相对路径(正斜杠),排除 .meta 与 .gitkeep。 */
export function listAssets(dir) {
if (!existsSync(dir)) return [];
@@ -20,6 +29,15 @@ export function listAssets(dir) {
return out;
}
/**
* 列出框架 UI 目录下**可被覆盖**的资源(白名单目录,见 COVERABLE_DIRS)。
* `frameworkUiDir` 下还有 `theme/`、`README.md` 等非资源内容,
* 不属于可覆盖范围,也不计入"框架可覆盖资源共 N 个"的统计。
*/
export function listCoverableAssets(frameworkUiDir) {
return listAssets(frameworkUiDir).filter((rel) => COVERABLE_DIRS.has(rel.split(posix.sep)[0]));
}
/** 读 PNG 宽高;非 PNG 或读取失败返回 null。 */
export function readPngSize(file) {
try {
@@ -39,7 +57,7 @@ const spineGroup = (rel) => rel.split(posix.sep).slice(0, 2).join(posix.sep);
* 返回 { matched: 相对路径[], errors: {path, code, message}[] }。
*/
export function validateOverrides(frameworkUiDir, overrideDir) {
const fwList = listAssets(frameworkUiDir);
const fwList = listCoverableAssets(frameworkUiDir);
const fwSet = new Set(fwList);
const ovList = listAssets(overrideDir);
const ovSet = new Set(ovList);