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

<!-- 
Source references:
- https://pnpm.io/pnpmfile
-->
