Files
2026-07-13 21:35:53 +08:00

234 lines
4.3 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
name: pnpm-hooks
description: 使用 pnpmfile 钩子自定义包解析与依赖行为
---
# pnpm 钩子
pnpm 通过 `.pnpmfile.cjs` 提供钩子,用于自定义包的解析方式及其元数据的处理过程。
## 设置
在 workspace 根目录创建 `.pnpmfile.cjs`
```js
// .pnpmfile.cjs
function readPackage(pkg, context) {
// 修改包元数据
return pkg
}
function afterAllResolved(lockfile, context) {
// 修改 lockfile
return lockfile
}
module.exports = {
hooks: {
readPackage,
afterAllResolved
}
}
```
## readPackage 钩子
在解析前为每个包调用。用于修改依赖、添加缺失的对等依赖或修复有问题的包。
### 添加缺失的对等依赖
```js
function readPackage(pkg, context) {
if (pkg.name === 'some-broken-package') {
pkg.peerDependencies = {
...pkg.peerDependencies,
react: '*'
}
context.log(`已为 ${pkg.name} 添加 react 对等依赖`)
}
return pkg
}
```
### 覆盖依赖版本
```js
function readPackage(pkg, context) {
// 修复所有 lodash 版本
if (pkg.dependencies?.lodash) {
pkg.dependencies.lodash = '^4.17.21'
}
if (pkg.devDependencies?.lodash) {
pkg.devDependencies.lodash = '^4.17.21'
}
return pkg
}
```
### 移除不需要的依赖
```js
function readPackage(pkg, context) {
// 移除会导致问题的可选依赖
if (pkg.optionalDependencies?.fsevents) {
delete pkg.optionalDependencies.fsevents
}
return pkg
}
```
### 替换包
```js
function readPackage(pkg, context) {
// 替换已废弃的包
if (pkg.dependencies?.['old-package']) {
pkg.dependencies['new-package'] = pkg.dependencies['old-package']
delete pkg.dependencies['old-package']
}
return pkg
}
```
### 修复有问题的包
```js
function readPackage(pkg, context) {
// 修复错误的 exports 字段
if (pkg.name === 'broken-esm-package') {
pkg.exports = {
'.': {
import: './dist/index.mjs',
require: './dist/index.cjs'
}
}
}
return pkg
}
```
## afterAllResolved 钩子
在 lockfile 生成后调用。用于解析后的修改。
```js
function afterAllResolved(lockfile, context) {
// 记录所有已解析的包
context.log(`已解析 ${Object.keys(lockfile.packages || {}).length} 个包`)
// 根据需要修改 lockfile
return lockfile
}
```
## context 对象
`context` 对象提供实用工具:
```js
function readPackage(pkg, context) {
// 记录日志消息
context.log('正在处理包...')
return pkg
}
```
## 与 TypeScript 一起使用
如需类型提示,可使用 JSDoc
```js
// .pnpmfile.cjs
/**
* @param {import('type-fest').PackageJson} pkg
* @param {{ log: (msg: string) => void }} context
* @returns {import('type-fest').PackageJson}
*/
function readPackage(pkg, context) {
return pkg
}
module.exports = {
hooks: {
readPackage
}
}
```
## 常见模式
### 按包名条件处理
```js
function readPackage(pkg, context) {
switch (pkg.name) {
case 'package-a':
pkg.dependencies.foo = '^2.0.0'
break
case 'package-b':
delete pkg.optionalDependencies.bar
break
}
return pkg
}
```
### 应用于所有包
```js
function readPackage(pkg, context) {
// 移除所有可选的 fsevents
if (pkg.optionalDependencies) {
delete pkg.optionalDependencies.fsevents
}
return pkg
}
```
### 调试解析过程
```js
function readPackage(pkg, context) {
if (process.env.DEBUG_PNPM) {
context.log(`${pkg.name}@${pkg.version}`)
context.log(` 依赖: ${Object.keys(pkg.dependencies || {}).join(', ')}`)
}
return pkg
}
```
## 钩子 vs overrides
| 特性 | 钩子(.pnpmfile.cjs | overrides |
|---------|----------------------|-----------|
| 复杂度 | 可使用 JavaScript 逻辑 | 仅声明式 |
| 范围 | 任意包元数据 | 仅版本 |
| 适用场景 | 复杂修复、条件逻辑 | 简单版本锁定 |
**简单的版本修复优先使用 overrides**。**在以下场景使用钩子**
- 需要条件逻辑
- 需要非版本类的修改(exports、对等依赖)
- 需要日志记录/调试
## 故障排查
### 钩子未运行
1. 确保文件名为 `.pnpmfile.cjs`(不是 `.js`
2. 确认文件位于 workspace 根目录
3. 运行 `pnpm install` 以触发钩子
### 调试钩子
```bash
# 查看钩子日志
pnpm install --reporter=append-only
```
<!--
Source references:
- https://pnpm.io/pnpmfile
-->