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

4.3 KiB

name, description
name description
pnpm-hooks 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:

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

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

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

function readPackage(pkg, context) {
  // Remove optional dependency that causes issues
  if (pkg.optionalDependencies?.fsevents) {
    delete pkg.optionalDependencies.fsevents
  }
  return pkg
}

Replace Package

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

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.

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:

function readPackage(pkg, context) {
  // Log messages
  context.log('Processing package...')
  
  return pkg
}

Use with TypeScript

For type hints, use JSDoc:

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

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

function readPackage(pkg, context) {
  // Remove all optional fsevents
  if (pkg.optionalDependencies) {
    delete pkg.optionalDependencies.fsevents
  }
  return pkg
}

Debug Resolution

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

# See hook logs
pnpm install --reporter=append-only