项目背景:
公司里的某个代码仓库配置的提交前校验 lint-staged 由于文件路径匹配有问题,导致过去几年曾经出现很多“漏网之鱼”,这些代码没有被 prettier 格式化,看上去有些杂乱。
我发现这个问题后,及时进行修复。但没想到过了几天,同事找我说她每次提交代码时,prettier 不仅会格式化她变更的代码,还会调整以前别人写的代码,导致她需要补写的单元测试巨多……(因为我司规定变更代码的单元测试覆盖率检测需大于 80% 才能发布)所以她希望只格式化自己变更的代码行,而不是整个文件
方案分析:
我的内心当时有两个小人:
激进派:将 master 分支代码全部用 prettier 格式化一遍,修改文件大约 2000+ 个,测试回归后发布。
保守派:按同事妹子说的那样,每次只格式化变更的代码行,而不是整个文件。
我打算先看看保守派的这个做法技术上是否可行,再拿着方案和仓库 Owner 沟通。我上网查了一下,发现 prettier 可以指定格式化的范围,貌似可行。
一顿折腾+自测,在 copilot 的帮助下,我实现了这个功能,
#!/bin/bash
# 获取暂存的更改
diff_output=$(git diff --cached --unified=0)
# 初始化数组
blocks=()
# 使用 grep 和 awk 提取以 diff --git 开头的所有片段,并存储到数组中
current_block=""
while IFS= read -r line; do
if [[ "$line" =~ ^diff\ --git ]]; then
if [[ -n "$current_block" ]]; then
blocks+=("$current_block")
fi
current_block="$line"
else
current_block="$current_block"$'\n'"$line"
fi
done < <(echo "$diff_output")
# 添加最后一个块
if [[ -n "$current_block" ]]; then
blocks+=("$current_block")
fi
# 遍历数组
for block in "${blocks[@]}"; do
# echo block: $block
#提取文件名和行号
echo "$block" | grep -E '^\+\+\+' | while read -r line; do
# 提取文件名并去掉前缀路径
file=$(echo "$line" | awk '{print $2}' | sed 's|^b/resibuflightsenglish/EnglishSite/||')
# 跳过删除的文件
if [ "$file" == "/dev/null" ]; then
continue
fi
# 只处理特定后缀的文件
if [[ ! "$file" =~ \.(ts|tsx|js|jsx)$ ]]; then
continue
fi
# 提取行号
echo "$block" | grep -E "^@@.*@@" | while read -r hunk; do
# 提取行号范围
start_line=$(echo "$hunk" | sed -n 's/^@@ -[0-9,]* \+\([0-9]*\).*/\1/p')
# echo $start_line
line_count=$(echo "$hunk" | sed -n 's/^@@ -[0-9,]* \+\([0-9]*\),\([0-9]*\).*/\2/p')
# 如果没有匹配到行号,line_count 可能为空,设置默认值为 1
if [ -z "$line_count" ]; then
line_count=1
fi
# 如果 line_count 为 0,直接跳过
if [ "$line_count" -eq 0 ]; then
continue
fi
# 计算结束行号
end_line=$((start_line + line_count - 1))
# 确保行号范围有效
if [ "$start_line" -le 0 ] || [ "$end_line" -le 0 ] || [ "$start_line" -gt "$end_line" ]; then
echo "Invalid line range: $start_line to $end_line"
continue
fi
# 计算字符位置
start_char=$(awk -v start=$start_line 'NR<start {total += length($0) + 1} NR==start {total += 1; print total}' $file)
end_char=$(awk -v end=$end_line 'NR<=end {total += length($0) + 1} END {print total}' $file)
# # 使用 Prettier 格式化特定字符范围
# echo "Formatting characters $start_char to $end_char in file $file $start_line to $end_line"
npx prettier --write --range-start $start_char --range-end $end_char $file
done
done
done
解决方案:
最终我拿着这两个方案和仓库 Owner 沟通时,她选择了方案一,直接把所有代码格式化一遍,手动加白名单跳过这次覆盖率卡点,然后发布。虽然方案二最终没有派上用场,这里记录一下,因为觉得写的过程还是比较有收获的。如果其他人有类似需求也可以做个参考~