| name | init-prettier-git-hooks |
| description | 初始化或补强基于 lint-staged + simple-git-hooks + prettier 的 git 提交前代码 格式化流程,并统一 `.gitattributes`、`.editorconfig`、`prettier.config.mjs` 的 LF 行尾策略,解决 Windows 上常见的 git 幽灵修改、CRLF 漂移、 已有配置缺项或冲突配置未收敛的问题。只要用户提到 prettier、git hooks、 lint-staged、simple-git-hooks、行尾统一、EOL、CRLF/LF、gitattributes、 editorconfig、ghost modified、幽灵修改,都应该使用本技能, 而不是把它当成"只能无脑覆盖模板"的初始化脚本。 |
| user-invocable | true |
| metadata | {"version":"1.1.4"} |
初始化 Prettier + Git Hooks 格式化流程
本技能用于在任何 Node.js 项目中初始化或补强基于 lint-staged + simple-git-hooks + prettier 的 git 提交前格式化流程。重点不是“把模板复制进去”,而是对目标项目做检查、合并、补全、精确覆盖。
目标结果
当用户要求处理本技能时,最终应让项目具备这一整条 LF 统一链路:
.gitattributes:* text=auto eol=lf
.editorconfig:[*] 区块内存在 end_of_line = lf
prettier.config.mjs:endOfLine: "lf",禁止保留 "auto"
package.json、lint-staged.config.js、simple-git-hooks.mjs 的格式化链路可正常工作
- 执行
git add --renormalize . 后,git 行尾归一化完成
- 带注释的
.json 配置文件具备精确的 Prettier parser: "jsonc" override,不会在 pnpm format 或 lint-staged 中失败
1. 定位根 package.json 并检查依赖
在安装任何依赖之前,必须先定位到项目的根 package.json 文件所在位置,并检查必需依赖是否已经安装。
1.1. 定位根 package.json
| 项目类型 | 定位方式 | 识别标志 |
|---|
| Monorepo(pnpm workspace) | 从当前目录向上查找 pnpm-workspace.yaml 文件,其所在目录即为 monorepo 根目录 | 存在 pnpm-workspace.yaml |
| Monorepo(npm/yarn workspace) | 从当前目录向上查找含有 "workspaces" 字段的 package.json | package.json 含 workspaces 字段 |
| 普通项目 | 当前项目目录的 package.json 即为根包 | 无上述标志 |
操作步骤:
- 读取当前项目的
package.json,检查是否存在 workspaces 字段
- 检查当前目录及其上级目录是否存在
pnpm-workspace.yaml
- 如果发现 monorepo 标志,切换到 monorepo 根目录操作;否则在当前目录操作
- 确认根
package.json 的绝对路径,后续所有依赖安装和检查都针对该文件
1.2. 检查必需依赖
必须在根 package.json 的 devDependencies 中逐一检查以下 6 个必需依赖包是否已安装:
| # | 依赖名称 | 说明 | 必需 |
|---|
| 1 | prettier | 核心代码格式化工具 | ✅ |
| 2 | @prettier/plugin-oxc | 使用 oxc 引擎解析 JS/TS,更快速 | ✅ |
| 3 | prettier-plugin-lint-md | Markdown 文件格式化插件 | ✅ |
| 4 | lint-staged | 只对 git 暂存区文件执行 lint/format | ✅ |
| 5 | simple-git-hooks | 轻量级 git hooks 管理工具 | ✅ |
| 6 | commitlint | git 提交信息规范校验 | ✅ |
检查流程:
- 读取根
package.json 的 devDependencies 字段
- 逐一检查上表中的 6 个依赖名称是否存在于
devDependencies 键列表中
- 记录检查结果,标记哪些依赖已安装、哪些缺失
- 输出检查报告(示例见下方)
检查报告格式:
### 依赖检查结果
根 package.json 路径:`/path/to/root/package.json`
项目类型:Monorepo(pnpm workspace)
| 依赖名称 | 状态 |
| :-----------------------: | :-----------------: |
| `prettier` | ✅ 已安装 (^3.7.4) |
| `@prettier/plugin-oxc` | ✅ 已安装 (^0.1.3) |
| `prettier-plugin-lint-md` | ✅ 已安装 (^1.0.1) |
| `lint-staged` | ✅ 已安装 (^16.2.6) |
| `simple-git-hooks` | ✅ 已安装 (^2.13.1) |
| `commitlint` | ❌ 缺失 |
缺失依赖数量:1
1.3. 安装缺失依赖
根据检查结果决定后续操作:
- 全部已安装:跳过安装步骤,直接进入步骤 2(配置文件创建)
- 存在缺失:仅安装缺失的依赖,不重复安装已有的
pnpm add -D commitlint
如果是全新项目(所有依赖都缺失),执行完整安装命令:
pnpm add -D prettier @prettier/plugin-oxc prettier-plugin-lint-md lint-staged simple-git-hooks commitlint
注意:在 monorepo 项目中,务必在根目录执行安装命令(使用 -w 标志或确保 cwd 是根目录),以将这些开发依赖安装到根 package.json。
2. 必须创建或修改的配置文件
2.0. 合并总原则
本技能必须默认采用合并策略,而不是无条件覆盖:
- 文件不存在:直接从模板创建。
- 文件已存在:以模板为基线,对目标文件做定向补全和冲突覆盖。
- 用户已有的项目特化配置应尽量保留,例如额外的二进制后缀、额外的 EditorConfig 区块、额外的 Prettier 插件配置。
- 对于本技能明确要求的策略键,必须覆盖到位,不允许保留冲突值。
本技能本轮最重要的策略键只有三个:
.gitattributes 的全局文本规则必须收敛为 * text=auto eol=lf
.editorconfig 的通用区块必须收敛为 end_of_line = lf
prettier.config.mjs 的 endOfLine 必须收敛为 "lf"
2.1. 创建 prettier.config.mjs
模板:参见 templates/prettier.config.mjs
如果文件不存在:
如果文件已存在:
- 优先保留用户已有的插件、overrides、printWidth、单双引号、缩进等项目风格
- 只针对
endOfLine 做精确处理:
- 如果已存在且不是
"lf",必须改成 "lf"
- 如果不存在,则补写
endOfLine: "lf"
- 明确禁止保留
endOfLine: "auto",因为它在 Windows 上会继续保留或引入 CRLF
2.1.1. JSONC 配置文件 override
本技能负责建立格式化链路,因此不能只添加通用 format 命令。只要项目中存在带注释的 .json 配置文件,就必须检查它们是否需要 Prettier jsonc parser override。
优先检查这些文件:
.vscode/extensions.json
.vscode/settings.json
tsconfig.json
tsconfig.*.json
jsconfig.json
.devcontainer/devcontainer.json
.hintrc
- 任何本次技能模板中带注释、块注释或尾逗号的
*.json
判断流程:
-
扫描本次将创建或更新的 *.json 配置文件。
-
判断文件是否包含 JSONC 语法:// 行注释、块注释、尾逗号,或目标工具明确支持 JSONC。
-
检查 package.json 的 format 脚本、lint-staged.config.*、simple-git-hooks.mjs 是否会格式化 *.json 或所有文件。
-
如果 JSONC 文件会进入 Prettier,合并精确 override;若该文件需要保持无尾逗号风格,同步写入 trailingComma: "none":
/** @type {import("prettier").Config} */
const config = {
overrides: [
{
files: [".vscode/extensions.json", ".vscode/settings.json"],
parser: "jsonc",
trailingComma: "none",
},
],
};
export default config;
-
如果已有 overrides,追加缺失项;如果同一文件已有 override 但 parser 不是 jsonc,除非项目有明确理由,否则改为 jsonc 并补齐所需的 trailingComma 策略。
-
不要把 **/*.json 全部设置为 JSONC。package.json、lockfile、以及第三方严格 JSON parser 读取的配置文件仍应保持严格 JSON。
-
不要通过删除注释来绕过 parser 问题。模板注释是维护信息,正确处理是保留注释并让格式化器使用正确 parser。
2.2. 创建或更新 .gitattributes
模板:参见 templates/.gitattributes
如果文件不存在:
如果文件已存在:
- 把全局文本规则收敛为单条:
* text=auto eol=lf
- 如果已有以下这类宽泛规则,必须精准替换,而不是继续并存:
* text=auto
*.* text eol=lf
- 任何把全局文本规则指向
crlf 的写法
- 保留已有的项目特化二进制规则,例如额外的媒体、办公文档、引擎资源、字体后缀
- 如果现有文件缺少模板中的常见二进制后缀,可以按模板补齐
- 不要删除用户手写注释,除非这些注释只是在说明旧的冲突规则
2.3. 创建或更新 .editorconfig
模板:参见 templates/.editorconfig
如果文件不存在:
如果文件已存在:
- 优先保留现有的缩进风格、每种语言的局部区块、项目特化规则
- 检查通用区块
[*]
- 如果不存在
[*] 区块,创建一个最小通用区块并补上 end_of_line = lf
- 如果已存在
[*] 区块但 end_of_line 不是 lf,必须改成 lf
- 如果
[*] 区块缺少 end_of_line,则补上 end_of_line = lf
- 不要因为补 EOL 而抹掉现有的
[*.yaml]、[*.md]、[Makefile] 等局部区块
2.4. 创建 lint-staged.config.js
模板:参见 templates/lint-staged.config.js
如果文件已存在,确认它仍会对暂存区文件执行 prettier --experimental-cli --write --no-parallel 或等价命令。
关于 --no-parallel 的说明:
- Prettier 3.6+ 的
--experimental-cli 默认会启动并行 worker pool,默认 worker 数为 CPU 核心数 - 1。
- 在 lint-staged / pre-commit 场景中,某些文件(如大型 markdown、特殊字符、或特定插件处理的文件)可能导致 worker 线程崩溃,抛出
WorkTankWorkerError,从而阻塞提交。
- 提交钩子的第一优先级是稳定性,因此模板中关闭并行;
package.json 中的 format 脚本等全量格式化场景可保留并行以换取速度。
2.5. 创建 simple-git-hooks.mjs
模板:参见 templates/simple-git-hooks.mjs
如果文件已存在,确认:
pre-commit 仍然会执行 lint-staged
commit-msg 仍然会执行 commitlint
2.6. 更新 package.json
需要在 package.json 中添加/修改以下内容:
scripts 部分新增命令:
{
"scripts": {
"format": "prettier --experimental-cli --write .",
"prepare": "<原有的 prepare 命令> && simple-git-hooks"
}
}
说明:
| 命令 | 说明 |
|---|
format | 手动格式化整个项目的所有文件 |
prepare | 在 npm install 后自动初始化 git hooks |
注意: 如果项目原本没有 prepare 命令,则直接设置为 "prepare": "simple-git-hooks"。
3. 初始化 Git Hooks
配置文件创建完成后,需要执行以下命令来初始化 git hooks:
pnpm approve-builds simple-git-hooks
npx simple-git-hooks
成功后会看到类似输出:
[INFO] Successfully set the pre-commit with command: npx lint-staged
[INFO] Successfully set the commit-msg with command: npx --no-install commitlint --edit ${1}
[INFO] Successfully set all git hooks
4. Git 行尾归一化
完成配置文件修改后,必须执行一次:
git add --renormalize .
这一步是 LF 统一链路的一部分,不要省略。它负责把已追踪文件重新按 .gitattributes 规则归一化并刷新索引中的 blob,否则历史对象里仍是 CRLF、工作区却是 LF 时,会出现「内容看似未改但 git status 永远脏」的幽灵差异。只改 .gitattributes 而不执行 renormalize 不会自动重写已入库的历史行尾。
若 git diff --cached 出现预期内的批量行尾变更,应单独提交规范化(例如 chore: normalize line endings per .gitattributes)。每个仍受影响的分支都需要各自执行并提交一次 renormalize,不能指望只在主干做一次就消除所有分支的脏状态。
复盘与团队案例:加了 .gitattributes 就万事大吉?必须 git add --renormalize。
5. 自检清单
完成初始化后,请逐项检查以下内容:
6. 工作流程说明
当你执行 git commit 时,会自动触发以下流程:
- pre-commit 钩子触发 → 执行
npx lint-staged
- lint-staged 对暂存区的文件执行
prettier --experimental-cli --write --no-parallel
- commit-msg 钩子触发 → 执行
npx --no-install commitlint --edit ${1} 校验提交信息格式
为什么 lint-staged 要加 --no-parallel?
--experimental-cli 默认启用并行 worker pool,但 worker 崩溃会导致 WorkTankWorkerError 并阻塞提交。提交钩子场景优先保证稳定性,因此关闭并行。
7. 模板清单
本技能应优先复用这些模板:
templates/.gitattributes
templates/.editorconfig
templates/prettier.config.mjs
templates/lint-staged.config.js
templates/simple-git-hooks.mjs
8. 注意事项
- 修改
simple-git-hooks.mjs 后,务必重新执行 npx simple-git-hooks 命令来更新 git hooks。
- 首次安装依赖时,如果 pnpm 提示
simple-git-hooks 的 postinstall 脚本被忽略,需要执行 pnpm approve-builds simple-git-hooks 来允许其运行。
- Prettier 使用
--experimental-cli 参数,这是启用实验性 CLI 功能的必要参数。
- lint-staged 中必须带
--no-parallel:--experimental-cli 默认会启动并行 worker pool,worker 崩溃时会抛出 WorkTankWorkerError 阻塞提交。提交钩子场景优先稳定性,因此关闭并行;package.json 的 format 脚本等全量格式化场景可保留并行。
- 不要把补 EOL 配置理解成“整文件覆盖”。这个技能的职责就是在保留项目现状的前提下,收敛冲突键,补齐缺失键。