--- 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 ```