Skip to content

Commit b20ea9a

Browse files
fix(leaderboard): fallback 非数组内容必须覆盖空,不能 preserve (#328)
Copilot CR (PR #326) 反馈 之前 fallback 只要 JSON.parse 成功就 preserveExisting=true,包括对象、null 等非数组类型也会被"维持原状"。但下游有多处 `import leaderboardData from "@/generated/site-leaderboard.json"` 直接 .filter/.map(如 Hero 的 top3、 /rank 页),一旦内容不是数组,整个 Next build 就会因 "filter is not a function" 直接挂掉。 修法 四档 fallback: 1. 非空数组 → 保留(warn 多少条) 2. 空数组 → 保留空数组(语义合法,下游 .filter 不挂) 3. JSON 损坏 / 非数组 → 兜底覆盖为 [](避免下游 import 后 type error) 4. 文件不存在 → 兜底覆盖为 [] 效果 - 任何已存在的合法数组都不被无故覆盖 - 任何非数组数据都强制规范化为 [],下游 .filter/.map 永远 safe Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
1 parent 1d18dc9 commit b20ea9a

1 file changed

Lines changed: 23 additions & 10 deletions

File tree

scripts/generate-leaderboard.mjs

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -157,25 +157,38 @@ async function main() {
157157
// 一次 fetch 失败(CF 临时挑战 / Vercel runner IP 信誉低 / 后端短暂抖动)
158158
// 不应该把 commit 进 git 的好数据冲成空数组上线。
159159
//
160-
// 三种情况:
161-
// 1. 文件已存在 + 内容是非空数组 → 保留旧数据 exit 0
162-
// 2. 文件已存在但是空数组 / 不是数组 → 维持原状 exit 0(不主动覆盖)
163-
// 3. 文件不存在(首次 build / 干净 cache)→ 写空数组兜底 exit 0
160+
// 四种情况:
161+
// 1. 文件已存在 + 是非空数组 → 保留旧数据 exit 0
162+
// 2. 文件已存在 + 是空数组 → 保留空数组 exit 0(语义合法,下游 .filter 不挂)
163+
// 3. 文件已存在 + JSON 损坏 / 非数组 → 视为无效,走兜底写 [] 覆盖
164+
// 4. 文件不存在(首次 build) → 走兜底写 [] 覆盖
165+
//
166+
// 关键约束:下游有多处 import leaderboard 后直接 .filter/.map,
167+
// 一旦内容是 null/object 等非数组类型,整个 Next build 会因
168+
// "filter is not a function" 挂掉。所以"非数组"必须强制覆盖空数组,
169+
// 不能跟"空数组"一样走 preserve 分支。
164170
let preservedExisting = false;
165171
try {
166172
const existing = await fs.readFile(outputAbs, "utf-8");
167173
try {
168174
const parsed = JSON.parse(existing);
169-
if (Array.isArray(parsed) && parsed.length > 0) {
170-
console.warn(
171-
`[generate-leaderboard] 后端不可用,但保留 ${OUTPUT} 已有 ${parsed.length} 条数据,不覆盖。 | Backend unreachable; keeping existing leaderboard with ${parsed.length} entries.`,
172-
);
175+
if (Array.isArray(parsed)) {
176+
if (parsed.length > 0) {
177+
console.warn(
178+
`[generate-leaderboard] 后端不可用,但保留 ${OUTPUT} 已有 ${parsed.length} 条数据,不覆盖。 | Backend unreachable; keeping existing leaderboard with ${parsed.length} entries.`,
179+
);
180+
} else {
181+
console.warn(
182+
`[generate-leaderboard] 后端不可用,但保留 ${OUTPUT} 的空数组内容。 | Backend unreachable; keeping existing empty leaderboard array.`,
183+
);
184+
}
185+
preservedExisting = true;
173186
} else {
187+
// 非数组(对象、字面量、null 等)→ 不 preserve,走下方兜底覆盖空数组
174188
console.warn(
175-
`[generate-leaderboard] 后端不可用,且 ${OUTPUT} 已有内容非有效非空数组,维持原状。`,
189+
`[generate-leaderboard] ${OUTPUT} 已存在但内容不是数组(typeof=${typeof parsed}),按无效数据处理,兜底覆盖为 []。`,
176190
);
177191
}
178-
preservedExisting = true;
179192
} catch {
180193
// 文件存在但 JSON 损坏:当作没有,走下面写空兜底
181194
console.warn(

0 commit comments

Comments
 (0)