批次改多檔最危險的不是語法錯誤,是「能編譯的壞掉」。腳本吃掉一整行、內容插進註解裡,編譯器全部放行。要怎麼獨立驗證覆蓋率。
發布於 2026-09-06・約 4 分鐘
我要在 29 個結構相似的元件裡各加一段程式碼。手改太慢,寫了個腳本:找到某個特徵行,往下 N 行找插入點,插進去。
跑完,npm run build 通過。看起來成功了。
npm run build
實際上 29 個裡有 8 個是壞的,而 build 只抓到其中 1 個。
五個檔案各掉了一整行。
腳本裡「找不到插入點」的分支寫成 i = j + 1,本意是跳過繼續找,實際效果是把第 j 行整行吃掉。掉的四行是 <div> 開標籤。
i = j + 1
<div>
有一個掉了開標籤造成標籤不配對,build 抓到了。另外四個掉的行剛好在不影響語法的位置——JSX 少一層 div,編譯完全沒問題,只是版面結構變了。
三個檔案把內容插到註解行上。
{/* 這裡是結果卡片區塊 新插入的程式碼 */}
整段被吞進 JSX 註解裡。語法完全合法,編譯器沒話說,那段程式碼就是不存在。
npm run build 回答的問題是「這份程式碼能不能編譯」。
它不回答「我想改的 29 個地方,有幾個真的改到了」。
這兩個問題的答案可以毫無關係。「能編譯的壞掉」是批次編輯最典型的失效方式,因為腳本的錯誤通常是「位置算錯」,而位置算錯出來的結果多半仍然是合法的程式碼。
console 沒有錯誤也不算驗證,理由一樣。
改完之後要跑一支跟改動腳本無關的檢查,回答三個數字:
目標 29 個 → 幾個真的改到 / 幾個漏掉 / 有沒有改到不該改的
「跟改動腳本無關」是重點。用同一份判斷邏輯去驗證,等於問腳本「你覺得你做對了嗎」。
# 最土但最有效的版本 TARGETS=$(ls src/components/tools/*.jsx) echo "目標檔案數: $(echo "$TARGETS" | wc -l)" echo "含有新程式碼的檔案數: $(grep -l 'NEW_MARKER' $TARGETS | wc -l)" echo "--- 漏掉的 ---" grep -L 'NEW_MARKER' $TARGETS echo "--- 出現超過一次的(可能重複插入)---" grep -c 'NEW_MARKER' $TARGETS | grep -v ':1$'
三個數字都要看。只看「有幾個改到」會漏掉重複插入的情況。
掉行這種錯誤,用內容比對抓不到(剩下的內容都是對的),但行數會露餡:
# 改動前先記 wc -l src/components/tools/*.jsx > /tmp/before.txt # 改動後 wc -l src/components/tools/*.jsx > /tmp/after.txt paste /tmp/before.txt /tmp/after.txt | awk '{d=$3-$1; if (d != EXPECTED) print $2, "行數變化", d}'
EXPECTED 是你預期每個檔案應該增加的行數。不等於它的就是有問題——多了代表重複插入,少了代表吃掉東西。
EXPECTED
這一招很簡單但抓到了我全部五個掉行的檔案。
發現壞掉之後,怎麼知道掉的是哪一行?
同一個檔案裡通常有結構相同的兄弟區塊。 我那 29 個元件內部都有好幾個長得一樣的卡片區塊,拿沒被改到的那個當範本,就知道被改的那個少了什麼。
如果沒有兄弟區塊,就跟另一個「同樣該改、而且改對了」的檔案比對。
diff <(sed -n '40,70p' 壞掉的.jsx) <(sed -n '40,70p' 正確的.jsx)
回頭看,最大的問題在於改動腳本本身的設計。「找到 A 行,往下 N 行」是脆弱的定位方式——只要有一個檔案的縮排、註解、空行不一樣,就會插到錯的地方。
比較穩的定位方式,由好到壞:
一、用解析器改,不要用行操作。 JSX 用 jscodeshift 或 Babel 的 AST,CSS 用 PostCSS。它們知道什麼是節點、什麼是註解,不會把東西插進註解裡。學習成本高一點,但正確性是本質上的。
jscodeshift
二、用唯一且穩定的錨點。 在要插入的位置先放一個標記註解 {/* @slot:footer */},腳本只找那個標記。標記是你自己放的,保證每個檔案都在對的位置。
{/* @slot:footer */}
三、行操作。 只在前兩種都做不到的時候用,而且一定要配覆蓋率檢查。
我當時選了第三種,因為「只是加一行,用 sed 很快」。省下的半小時後來花了兩小時修。
1. 先列出目標清單,存成檔案。這是之後所有驗證的分母。 2. 記錄改動前的狀態(行數、關鍵字出現次數)。 3. 在一個檔案上先試,人工看過結果對不對。 4. 跑全部。 5. 跑獨立的覆蓋率檢查:改到幾個 / 漏幾個 / 重複幾個 / 行數變化對不對。 6. 隨機抽三個檔案人工看。 7. 最後才 build。
第 5 步是這整條流程裡唯一不能省的。第 1 步決定了第 5 步能不能做——沒有明確的分母,覆蓋率就無從算起,而「我覺得應該都改到了」是這類事故的標準開場白。
標籤:重構、自動化、批次編輯、驗證、JavaScript