fix(json/comments): validateJSONArrayFile 先驗證再寫入避免毀損原檔、findingRow 對空值防呆

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jeffery
2026-06-26 15:26:08 +08:00
co-authored by Claude Opus 4.8
parent fbb74092cb
commit 402630fa69
3 changed files with 90 additions and 6 deletions
+7 -4
View File
@@ -7,17 +7,20 @@ import { ok, line, warn } from './log.js';
const LEVEL_EMOJI = { critical: '🔴', warning: '🟡', info: '🔵' };
const LEVEL_LABEL = { critical: '嚴重', warning: '警告', info: '建議' };
const LEVEL_ORDER = ['critical', 'warning', 'info'];
// 預先把等級對應到排序索引,bySeverity 排序時直接 O(1) 取值,省去每次比較的 includes + indexOf 掃描。
const LEVEL_RANK = new Map(LEVEL_ORDER.map((level, index) => [level, index]));
/**
* 將單一 finding 格式化為 Markdown 表格的一列(等級|審查員|位置|建議)。
*
* @param {{ level?: string, role?: string, location?: string, suggestion?: string }} f
* 單筆審查問題物件。`level` 若不在 critical/warning/info 之內,emoji 留空、標籤回退為原始 level 值;
* `role`、`location`、`suggestion` 直接內嵌字串(未定義時會輸出 undefined 字樣)。傳入 null/undefined 會拋 TypeError(需人工確認是否需防呆)。
* @returns {string} 形如 `| 🔴 嚴重 | role | location | suggestion |` 的表格列字串。
* `role`、`location`、`suggestion` 直接內嵌字串(未定義時會輸出 undefined 字樣)。傳入 null/undefined 時回傳空字串(已防呆,不會拋例外)。
* @returns {string} 形如 `| 🔴 嚴重 | role | location | suggestion |` 的表格列字串`f` 為空值時回傳空字串
* @remarks 內部輔助函式,供 {@link buildTable} 逐列組裝表格使用,本身不含換行。
*/
function findingRow(f) {
if (!f) return '';
return `| ${LEVEL_EMOJI[f.level] || ''} ${LEVEL_LABEL[f.level] || f.level} | ${f.role} | ${f.location} | ${f.suggestion} |`;
}
@@ -50,8 +53,8 @@ const levelText = f => `${LEVEL_EMOJI[f.level] || ''} ${LEVEL_LABEL[f.level] ||
* @remarks 不在 LEVEL_ORDER 內的等級一律視為最低優先(排在最後);location 未定義時以空字串參與比較,因此排序穩定不會丟例外。
*/
const bySeverity = (a, b) => {
const aLevel = LEVEL_ORDER.includes(a.level) ? LEVEL_ORDER.indexOf(a.level) : LEVEL_ORDER.length;
const bLevel = LEVEL_ORDER.includes(b.level) ? LEVEL_ORDER.indexOf(b.level) : LEVEL_ORDER.length;
const aLevel = LEVEL_RANK.has(a.level) ? LEVEL_RANK.get(a.level) : LEVEL_ORDER.length;
const bLevel = LEVEL_RANK.has(b.level) ? LEVEL_RANK.get(b.level) : LEVEL_ORDER.length;
if (aLevel !== bLevel) return aLevel - bLevel;
return String(a.location || '').localeCompare(String(b.location || ''));
};