963 markdown files reformatted with the repository's pinned prettier so pnpm format:check covers the folded tree like every other repo file. The formatter's embedded-language pass also normalized code fences (TS semicolons, closed HTML tags in examples, lowercased CSS hex colors, one renumbered list that skipped an index). Alphanumeric token deltas vs the fold commit were audited file-by-file; all are formatter-equivalent markup normalizations plus the four sanitized skills.
4.5 KiB
4.5 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
- Ensure file is named
.pnpmfile.cjs(not.js) - Check file is at workspace root
- Run
pnpm installto trigger hooks
Debug hooks
# See hook logs
pnpm install --reporter=append-only