docs(ai-code-review): 補齊各模組 JSDoc、指令檔逐行註解並重建 README
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
303104bb20
commit
1378f03595
+102
@@ -8,16 +8,47 @@ const LEVEL_EMOJI = { critical: '🔴', warning: '🟡', info: '🔵' };
|
||||
const LEVEL_LABEL = { critical: '嚴重', warning: '警告', info: '建議' };
|
||||
const LEVEL_ORDER = ['critical', 'warning', 'info'];
|
||||
|
||||
/**
|
||||
* 將單一 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 |` 的表格列字串。
|
||||
* @remarks 內部輔助函式,供 {@link buildTable} 逐列組裝表格使用,本身不含換行。
|
||||
*/
|
||||
function findingRow(f) {
|
||||
return `| ${LEVEL_EMOJI[f.level] || ''} ${LEVEL_LABEL[f.level] || f.level} | ${f.role} | ${f.location} | ${f.suggestion} |`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 將多筆 findings 組成完整的 Markdown 表格(含表頭與分隔列)。
|
||||
*
|
||||
* @param {Array<object>} findings 審查問題陣列;空陣列時僅輸出表頭與分隔列。每筆物件格式見 {@link findingRow}。
|
||||
* @returns {string} 完整的 Markdown 表格字串(表頭:等級|審查員|位置|建議)。
|
||||
* @remarks 內部輔助函式,供發布舊問題、新問題(非嚴重)、單筆嚴重問題等 comment 內文使用。
|
||||
*/
|
||||
function buildTable(findings) {
|
||||
const rows = findings.map(findingRow).join('\n');
|
||||
return `| 等級 | 審查員 | 位置 | 建議 |\n|------|--------|------|------|\n${rows}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 取得 finding 等級的人類可讀字串(emoji + 中文標籤),已去除頭尾空白。
|
||||
*
|
||||
* @param {{ level?: string }} f 單筆審查問題物件。`level` 查無對應時 emoji 留空、標籤回退為原始 level 值。
|
||||
* @returns {string} 例如 `🔴 嚴重`;無法對應時回退為原始 level 字串(無 emoji)。
|
||||
* @remarks 內部輔助函式,供 {@link inlineCommentBody} 與 {@link reviewCommentBody} 組裝 comment 內文使用。
|
||||
*/
|
||||
const levelText = f => `${LEVEL_EMOJI[f.level] || ''} ${LEVEL_LABEL[f.level] || f.level}`.trim();
|
||||
/**
|
||||
* findings 排序比較器:先依嚴重等級(critical < warning < info < 其他),同級再依 location 字串排序。
|
||||
*
|
||||
* @param {{ level?: string, location?: string }} a 比較項 A。
|
||||
* @param {{ level?: string, location?: string }} b 比較項 B。
|
||||
* @returns {number} 負值代表 a 排在 b 之前,正值代表之後,0 代表相等(供 Array.prototype.sort 使用)。
|
||||
* @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;
|
||||
@@ -43,10 +74,26 @@ function inlineCommentBody(f) {
|
||||
return `**等級**:${levelText(f)}\n**審查員**:${f.role}\n**建議**:${f.suggestion}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 從 finding 取出問題原因描述,依序嘗試多個可能欄位。
|
||||
*
|
||||
* @param {{ problem?: string, reason?: string, description?: string, detail?: string, title?: string, message?: string }} f
|
||||
* 單筆審查問題物件;依序取第一個有值(truthy)的欄位。所有欄位皆無值時回退為「未提供問題原因」。
|
||||
* @returns {string} 問題原因字串。
|
||||
* @remarks 內部輔助函式,供 {@link reviewCommentBody} 組裝 comment 內文使用,用以容忍不同來源 finding 的欄位命名差異。
|
||||
*/
|
||||
function problemText(f) {
|
||||
return f.problem || f.reason || f.description || f.detail || f.title || f.message || '未提供問題原因';
|
||||
}
|
||||
|
||||
/**
|
||||
* 產生 review comment 內文(嚴重等級/審查員/問題/建議四行)。
|
||||
*
|
||||
* @param {{ level?: string, role?: string, suggestion?: string, problem?: string, reason?: string, description?: string, detail?: string, title?: string, message?: string }} f
|
||||
* 單筆審查問題物件。
|
||||
* @returns {string} 多行 Markdown 字串。
|
||||
* @remarks 內部輔助函式,供 {@link toReviewComment} 產生批次 review comment 內文使用。比 {@link inlineCommentBody} 多了「問題」一行。
|
||||
*/
|
||||
function reviewCommentBody(f) {
|
||||
return [
|
||||
`**嚴重等級**:${levelText(f)}`,
|
||||
@@ -56,17 +103,48 @@ function reviewCommentBody(f) {
|
||||
].join('\n');
|
||||
}
|
||||
|
||||
/**
|
||||
* 計算陣列中符合條件的元素數量。
|
||||
*
|
||||
* @param {Array<T>} findings 待計數的陣列。
|
||||
* @param {(item: T) => boolean} predicate 判斷函式;回傳 true 的元素計入。
|
||||
* @returns {number} 符合條件的元素數量。
|
||||
* @template T
|
||||
* @remarks 內部輔助函式,供 {@link formatFindingsStats} 與 {@link formatFindingsStatsLine} 統計各等級筆數使用。
|
||||
*/
|
||||
function countBy(findings, predicate) {
|
||||
return findings.filter(predicate).length;
|
||||
}
|
||||
|
||||
/**
|
||||
* 過濾出新問題(is_new 不等於 false 者)。
|
||||
*
|
||||
* @param {Array<{ is_new?: boolean }>} findings 審查問題陣列。
|
||||
* @returns {Array<object>} 新問題子集合。
|
||||
* @remarks 內部輔助函式。判定採 `is_new !== false`,因此未設定 is_new(undefined)的 finding 也視為新問題;僅明確 `is_new === false` 會被排除。供統計與 review 發布判斷使用。
|
||||
*/
|
||||
function newFindingsOnly(findings) {
|
||||
return findings.filter(f => f.is_new !== false);
|
||||
}
|
||||
|
||||
// 等級無法歸入 critical/warning/info(例如缺漏或無法辨識)時,歸入「無法標示」
|
||||
/**
|
||||
* 判斷 finding 等級是否無法歸入 critical/warning/info(無法標示)。
|
||||
*
|
||||
* @param {{ level?: string }} f 單筆審查問題物件。
|
||||
* @returns {boolean} 等級不在 LEVEL_ORDER 內時為 true。
|
||||
* @remarks 內部輔助函式,供統計表的「⚪ 無法標示」欄位計數使用。
|
||||
*/
|
||||
const isUnclassified = f => !LEVEL_ORDER.includes(f.level);
|
||||
|
||||
/**
|
||||
* 產生 findings 統計的 Markdown 表格(新問題/舊問題 × 嚴重/警告/建議/無法標示)。
|
||||
*
|
||||
* @param {Array<{ is_new?: boolean, level?: string }>} findings 審查問題陣列;
|
||||
* `is_new === false` 計入舊問題,其餘計入新問題。
|
||||
* @returns {string} 含表頭、分隔列與兩資料列的 Markdown 表格字串。
|
||||
* @remarks 供 {@link buildReviewSummary} 組裝 review 統計本文使用。空陣列時仍輸出表格(各欄為 0 筆)。
|
||||
*/
|
||||
export function formatFindingsStats(findings) {
|
||||
const oldFindings = findings.filter(f => f.is_new === false);
|
||||
const newFindings = newFindingsOnly(findings);
|
||||
@@ -80,6 +158,14 @@ export function formatFindingsStats(findings) {
|
||||
].join('\n');
|
||||
}
|
||||
|
||||
/**
|
||||
* 產生 findings 統計的單行文字摘要(供 log 使用)。
|
||||
*
|
||||
* @param {Array<{ is_new?: boolean, level?: string }>} findings 審查問題陣列;
|
||||
* `is_new === false` 計入舊問題,其餘計入新問題。
|
||||
* @returns {string} 形如 `新: 嚴重1 / 警告0 / 建議2 / 無法標示0;舊: ...` 的單行字串。
|
||||
* @remarks 供 {@link postFindingsReview} 在 log 輸出統計時呼叫。內容與 {@link formatFindingsStats} 一致,僅格式為單行純文字。
|
||||
*/
|
||||
export function formatFindingsStatsLine(findings) {
|
||||
const oldFindings = findings.filter(f => f.is_new === false);
|
||||
const newFindings = newFindingsOnly(findings);
|
||||
@@ -87,6 +173,14 @@ export function formatFindingsStatsLine(findings) {
|
||||
return `新: ${row(newFindings)};舊: ${row(oldFindings)}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 組裝 review 本文:標題 + findings 統計表 +(選擇性)用量區塊。
|
||||
*
|
||||
* @param {Array<object>} findings 用於統計的審查問題陣列。
|
||||
* @param {string} [usageSection=''] 額外附加的用量/token 統計區塊;空字串時不附加。
|
||||
* @returns {string} review 本文(Markdown)。
|
||||
* @remarks 內部輔助函式,供 {@link postFindingsReview} 產生整批 review 的 body。
|
||||
*/
|
||||
function buildReviewSummary(findings, usageSection = '') {
|
||||
const parts = [
|
||||
'## AI Code Review 統計',
|
||||
@@ -97,6 +191,14 @@ function buildReviewSummary(findings, usageSection = '') {
|
||||
return parts.join('\n');
|
||||
}
|
||||
|
||||
/**
|
||||
* 將 finding 轉為 Gitea review comment 物件(含檔案路徑、內文、行號)。
|
||||
*
|
||||
* @param {{ location?: string, level?: string, role?: string, suggestion?: string }} f 單筆審查問題物件。
|
||||
* @returns {{ path: string, body: string, new_position: number } | null}
|
||||
* 可定位時回傳 comment 物件;location 無法解析出行號時回傳 null。
|
||||
* @remarks 內部輔助函式,供 {@link postFindingsReview} 在 map 後以 `filter(Boolean)` 濾除無法定位的項目。
|
||||
*/
|
||||
function toReviewComment(f) {
|
||||
const loc = parseLocation(f.location);
|
||||
if (!loc) return null;
|
||||
|
||||
Reference in New Issue
Block a user