项目文件夹

文件
2026-07-13 21:35:53 +08:00

234 行
4.3 KiB
Markdown

此文件含有模棱两可的 Unicode 字符
此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。
---
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
-->