全部学科
Python全栈
python
NodeJS全栈
nodejs
📅 2026-05-25 7 分钟 ✍️ juanwangdev

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,需手动补执行构建步骤
想在手机上练习这篇文章的配套题目?
使用微信卷王开发者小程序,打开首页顶部扫码功能识别二维码
← 上一篇 npm完整生命周期流程
下一篇 → Verdaccio私有仓库搭建
扫码体验小程序
加载中
想在手机上刷题学习?
使用微信卷王开发者小程序,打开首页顶部扫码功能识别二维码