// 静态检查:每份 preset.js 里声明的每一个资源路径是否真实存在。 // 这能抓到"运行时才会暴露"的拼写错误(比如某首备用 BGM 从未被加载过,写错了也不会有人发现)。 // 用法:node tools/check-paths.mjs import { stat } from "node:fs/promises"; import { fileURLToPath, pathToFileURL } from "node:url"; import { glob } from "node:fs/promises"; import { resolve } from "node:path"; const root = resolve("dist"); const presetFiles = []; for await (const f of glob("assets/**/preset.js", { cwd: root })) presetFiles.push(f); let ok = 0; let bad = 0; for (const rel of presetFiles.sort()) { const mod = await import(pathToFileURL(resolve(root, rel)).href); const preset = mod.default; const urls = []; const walk = (value, path) => { if (typeof value === "string" && /^(file|https?):/.test(value)) urls.push([path, value]); else if (value && typeof value === "object") { for (const [k, v] of Object.entries(value)) walk(v, `${path}.${k}`); } else if (Array.isArray(value)) value.forEach((v, i) => walk(v, `${path}[${i}]`)); }; walk(preset, preset.id); console.log(`\n${rel} (id=${preset.id}, name=${preset.name})`); for (const [path, url] of urls) { const u = new URL(url); if (u.protocol !== "file:") { console.log(` ? ${path} -> 非本地路径,跳过: ${url}`); continue; } const file = fileURLToPath(u); const info = await stat(file).catch(() => null); if (info?.isFile()) { console.log(` ok ${path} (${(info.size / 1024).toFixed(1)} KB)`); ok++; } else { console.log(` MISSING ${path} -> ${file}`); bad++; } } } console.log(`\n${ok} 个路径存在,${bad} 个缺失`); process.exit(bad ? 1 : 0);