Files
agent-skills/skills/pnpm/references/features-hooks.md
Jason Woltje f5792c40be feat: Complete fleet — 94 skills across 10+ domains
Pulled ALL skills from 15 source repositories:
- anthropics/skills: 16 (docs, design, MCP, testing)
- obra/superpowers: 14 (TDD, debugging, agents, planning)
- coreyhaines31/marketingskills: 25 (marketing, CRO, SEO, growth)
- better-auth/skills: 5 (auth patterns)
- vercel-labs/agent-skills: 5 (React, design, Vercel)
- antfu/skills: 16 (Vue, Vite, Vitest, pnpm, Turborepo)
- Plus 13 individual skills from various repos

Mosaic Stack is not limited to coding — the Orchestrator and
subagents serve coding, business, design, marketing, writing,
logistics, analysis, and more.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 16:27:42 -06:00

234 lines
4.3 KiB
Markdown

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