--- name: pnpm-hooks description: Customize package resolution and dependency behavior with pnpmfile hooks --- # pnpm Hooks pnpm provides hooks via `.pnpmfile.cjs` to customize how packages are resolved and their metadata is processed. ## Setup Create `.pnpmfile.cjs` at workspace root: ```js // .pnpmfile.cjs function readPackage(pkg, context) { // Modify package metadata return pkg } function afterAllResolved(lockfile, context) { // Modify lockfile return lockfile } module.exports = { hooks: { readPackage, afterAllResolved } } ``` ## readPackage Hook Called for every package before resolution. Use to modify dependencies, add missing peer deps, or fix broken packages. ### Add Missing Peer Dependency ```js function readPackage(pkg, context) { if (pkg.name === 'some-broken-package') { pkg.peerDependencies = { ...pkg.peerDependencies, react: '*' } context.log(`Added react peer dep to ${pkg.name}`) } return pkg } ``` ### Override Dependency Version ```js function readPackage(pkg, context) { // Fix all lodash versions if (pkg.dependencies?.lodash) { pkg.dependencies.lodash = '^4.17.21' } if (pkg.devDependencies?.lodash) { pkg.devDependencies.lodash = '^4.17.21' } return pkg } ``` ### Remove Unwanted Dependency ```js function readPackage(pkg, context) { // Remove optional dependency that causes issues if (pkg.optionalDependencies?.fsevents) { delete pkg.optionalDependencies.fsevents } return pkg } ``` ### Replace Package ```js function readPackage(pkg, context) { // Replace deprecated package if (pkg.dependencies?.['old-package']) { pkg.dependencies['new-package'] = pkg.dependencies['old-package'] delete pkg.dependencies['old-package'] } return pkg } ``` ### Fix Broken Package ```js function readPackage(pkg, context) { // Fix incorrect exports field if (pkg.name === 'broken-esm-package') { pkg.exports = { '.': { import: './dist/index.mjs', require: './dist/index.cjs' } } } return pkg } ``` ## afterAllResolved Hook Called after the lockfile is generated. Use for post-resolution modifications. ```js function afterAllResolved(lockfile, context) { // Log all resolved packages context.log(`Resolved ${Object.keys(lockfile.packages || {}).length} packages`) // Modify lockfile if needed return lockfile } ``` ## Context Object The `context` object provides utilities: ```js function readPackage(pkg, context) { // Log messages context.log('Processing package...') return pkg } ``` ## Use with TypeScript For type hints, use 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 } } ``` ## Common Patterns ### Conditional by Package Name ```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 } ``` ### Apply to All Packages ```js function readPackage(pkg, context) { // Remove all optional fsevents if (pkg.optionalDependencies) { delete pkg.optionalDependencies.fsevents } return pkg } ``` ### Debug Resolution ```js function readPackage(pkg, context) { if (process.env.DEBUG_PNPM) { context.log(`${pkg.name}@${pkg.version}`) context.log(` deps: ${Object.keys(pkg.dependencies || {}).join(', ')}`) } return pkg } ``` ## Hooks vs Overrides | Feature | Hooks (.pnpmfile.cjs) | Overrides | |---------|----------------------|-----------| | Complexity | Can use JavaScript logic | Declarative only | | Scope | Any package metadata | Version only | | Use case | Complex fixes, conditional logic | Simple version pins | **Prefer overrides** for simple version fixes. **Use hooks** when you need: - Conditional logic - Non-version modifications (exports, peer deps) - Logging/debugging ## Troubleshooting ### Hook not running 1. Ensure file is named `.pnpmfile.cjs` (not `.js`) 2. Check file is at workspace root 3. Run `pnpm install` to trigger hooks ### Debug hooks ```bash # See hook logs pnpm install --reporter=append-only ```