prepare与prepublishOnly钩子
prepare 和 prepublishOnly 是发布流程中最关键的两个钩子,一个负责构建产物,一个负责发布门禁。
prepare钩子详解
触发场景
prepare 在以下场景均会触发:
| 触发时机 | 命令 |
|---|---|
npm install(无参数) | 本地开发安装 |
npm publish | 发布包 |
npm pack | 打包 tarball |
npm link | 本地链接 |
| git 依赖安装 | npm install git+https://... |
prepare 是唯一在安装和发布时都触发的钩子,适合放置编译构建逻辑。
典型应用:自动构建
JSON
// package.json
{
"name": "my-lib",
"main": "dist/index.js",
"module": "dist/index.esm.js",
"scripts": {
"build": "rollup -c",
"prepare": "npm run build"
}
}
- 开发者
npm install后自动构建,确保dist可用 npm publish时自动构建,确保发布的是最新编译产物
条件执行
JSON
{
"scripts": {
"prepare": "husky install"
}
}
husky install在 prepare 中执行,确保 git hooks 在 clone 后自动安装。
JSON
{
"scripts": {
"prepare": "node -e \"if(process.env.NODE_ENV !== 'production'){require('husky').install()}\""
}
}
CI 中跳过 prepare
Bash
# CI 环境无需 prepare(如 husky)
npm ci --ignore-scripts
# 或通过环境变量控制
"prepare": "is-ci || husky install"
npm ci --ignore-scripts会跳过所有脚本,需手动执行构建步骤。
prepare 与 prepublish 的区别
| 钩子 | npm install 时 | npm publish 时 | 状态 |
|---|---|---|---|
prepublish | 触发 | 触发 | 已废弃(语义混乱) |
prepare | 触发 | 触发 | 推荐 |
prepublishOnly | 不触发 | 触发 | 推荐 |
prepublish因在npm install时也触发而被废弃,用prepare+prepublishOnly替代。
prepublishOnly钩子
触发场景
prepublishOnly 仅在 npm publish 时触发,npm install 时不触发:
Bash
# 执行 npm publish 时的顺序
# ... prepare 之后 ...
# prepublishOnly → prepack → pack → ...
prepublishOnly 是发布前的专属门禁,确保仅在发布时执行检查逻辑。
典型应用:发布门禁
JSON
{
"scripts": {
"prepublishOnly": "npm run lint && npm test && npm run build"
}
}
- lint 检查代码规范
- test 确保测试通过
- build 编译产物
三项检查任一失败,发布中止。这是防止低质量代码上线的最后一道防线。
限制发布分支
JSON
{
"scripts": {
"prepublishOnly": "node scripts/check-branch.js && npm run build"
}
}
JavaScript
// scripts/check-branch.js
const { execSync } = require('child_process');
const branch = execSync('git rev-parse --abbrev-ref HEAD').toString().trim();
if (branch !== 'main') {
console.error('只能在 main 分支发布');
process.exit(1);
}
禁止从特性分支直接发布,避免未合并的代码进入 registry。
限制发布源
JSON
{
"scripts": {
"prepublishOnly": "node scripts/check-registry.js"
}
}
JavaScript
// scripts/check-registry.js
const registry = require('child_process')
.execSync('npm config get registry')
.toString()
.trim();
if (!registry.includes('registry.npmjs.org')) {
console.error('禁止发布到非官方 registry');
process.exit(1);
}
防止内部包误发布到公共 registry,或公共包误发布到私有 registry。
prepublishOnly 与 prepack 的选择
| 钩子 | 执行时机 | 适用场景 |
|---|---|---|
prepublishOnly | 打包前 | 代码检查、分支校验 |
prepack | 打包前(更靠后) | 修改打包内容 |
需要修改 tarball 内容用
prepack,仅做门禁检查用prepublishOnly。
组合使用示例
JSON
{
"main": "dist/index.js",
"files": ["dist"],
"scripts": {
"build": "tsc",
"lint": "eslint src/",
"test": "jest",
"prepare": "npm run build",
"prepublishOnly": "npm run lint && npm test"
}
}
prepare:安装和发布时都构建,确保dist始终可用prepublishOnly:发布前单独检查 lint 和 test,install 时不执行
这是库项目最推荐的钩子组合:prepare 负责构建,prepublishOnly 负责门禁。
要点总结
- prepare 在 install/publish/pack/link/git 依赖安装时均触发,适合放置构建逻辑
- prepublishOnly 仅在
npm publish时触发,是发布专属门禁 - 推荐组合:
prepare做构建 +prepublishOnly做检查,替代已废弃的prepublish - prepublishOnly 中可校验分支、registry、lint、test,任一失败中止发布
- CI 中用
--ignore-scripts跳过 prepare,需手动补执行构建步骤