chore(.agents/skills): update existing skills
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
# Generation Info
|
||||
|
||||
- **Source:** `sources/pnpm`
|
||||
- **Git SHA:** `a1d6d5aef9d5f369fa2f0d8a54f1edbaff8b23b3`
|
||||
- **Generated:** 2026-01-28
|
||||
- **Git SHA:** `5cd19942ee75cda8ed299233c486a67d95bb38ec`
|
||||
- **Generated:** 2026-06-22
|
||||
|
||||
@@ -1,42 +1,45 @@
|
||||
---
|
||||
name: pnpm
|
||||
description: Node.js package manager with strict dependency resolution. Use when running pnpm specific commands, configuring workspaces, or managing dependencies with catalogs, patches, or overrides.
|
||||
description: Node.js package manager with strict dependency resolution. Use when running pnpm specific commands, configuring workspaces via pnpm-workspace.yaml, or managing dependencies with catalogs, patches, overrides, config dependencies, or the global virtual store.
|
||||
metadata:
|
||||
author: Anthony Fu
|
||||
version: "2026.1.28"
|
||||
version: "2026.6.22"
|
||||
source: Generated from https://github.com/pnpm/pnpm, scripts located at https://github.com/antfu/skills
|
||||
---
|
||||
|
||||
pnpm is a fast, disk space efficient package manager. It uses a content-addressable store to deduplicate packages across all projects on a machine, saving significant disk space. pnpm enforces strict dependency resolution by default, preventing phantom dependencies. Configuration should preferably be placed in `pnpm-workspace.yaml` for pnpm-specific settings.
|
||||
pnpm is a fast, disk space efficient package manager. It uses a content-addressable store to deduplicate packages across all projects on a machine, and enforces strict dependency resolution by default, preventing phantom dependencies.
|
||||
|
||||
**Important:** When working with pnpm projects, agents should check for `pnpm-workspace.yaml` and `.npmrc` files to understand workspace structure and configuration. Always use `--frozen-lockfile` in CI environments.
|
||||
**Configuration model (important):** pnpm settings now live in `pnpm-workspace.yaml` (and the global `config.yaml`) using **camelCase** keys. `.npmrc` is used **only** for authentication/registry credentials, and the `pnpm` field of `package.json` is no longer read. When working in a pnpm project, check `pnpm-workspace.yaml` for settings/workspace structure and `.npmrc` only for auth. Always use `--frozen-lockfile` (or `pnpm ci`) in CI.
|
||||
|
||||
> The skill is based on pnpm 10.x, generated at 2026-01-28.
|
||||
> The skill is based on pnpm 10.x, generated at 2026-06-22. It also covers v11 behavior changes (config split, isolated global packages, `allowBuilds`, `pmOnFail`, global virtual store) where current docs describe them.
|
||||
|
||||
## Core
|
||||
|
||||
| Topic | Description | Reference |
|
||||
|-------|-------------|-----------|
|
||||
| CLI Commands | Install, add, remove, update, run, exec, dlx, and workspace commands | [core-cli](references/core-cli.md) |
|
||||
| Configuration | pnpm-workspace.yaml, .npmrc settings, and package.json fields | [core-config](references/core-config.md) |
|
||||
| Workspaces | Monorepo support with filtering, workspace protocol, and shared lockfile | [core-workspaces](references/core-workspaces.md) |
|
||||
| Store | Content-addressable storage, hard links, and disk efficiency | [core-store](references/core-store.md) |
|
||||
| CLI Commands | install/add/remove/update, run, dlx/pnx, workspace, runtime, publishing (version, view, sbom, stage) | [core-cli](references/core-cli.md) |
|
||||
| Configuration | pnpm-workspace.yaml settings (camelCase), global config.yaml, packageConfigs, .npmrc auth | [core-config](references/core-config.md) |
|
||||
| Workspaces | Monorepo support: filtering, workspace protocol, shared lockfile, packageConfigs | [core-workspaces](references/core-workspaces.md) |
|
||||
| Store | Content-addressable store, virtual store, node linker modes, frozen/read-only store | [core-store](references/core-store.md) |
|
||||
|
||||
## Features
|
||||
|
||||
| Topic | Description | Reference |
|
||||
|-------|-------------|-----------|
|
||||
| Catalogs | Centralized dependency version management for workspaces | [features-catalogs](references/features-catalogs.md) |
|
||||
| Overrides | Force specific versions of dependencies including transitive | [features-overrides](references/features-overrides.md) |
|
||||
| Patches | Modify third-party packages with custom fixes | [features-patches](references/features-patches.md) |
|
||||
| Aliases | Install packages under custom names using npm: protocol | [features-aliases](references/features-aliases.md) |
|
||||
| Hooks | Customize resolution with .pnpmfile.cjs hooks | [features-hooks](references/features-hooks.md) |
|
||||
| Peer Dependencies | Auto-install, strict mode, and dependency rules | [features-peer-deps](references/features-peer-deps.md) |
|
||||
| Catalogs | Centralized dependency versions; catalogMode, catalog: in overrides | [features-catalogs](references/features-catalogs.md) |
|
||||
| Overrides | Force versions (incl. transitive & peer deps); packageExtensions | [features-overrides](references/features-overrides.md) |
|
||||
| Patches | Modify third-party packages; patchedDependencies in pnpm-workspace.yaml | [features-patches](references/features-patches.md) |
|
||||
| Aliases | Install under custom names (npm:) and registry aliases (namedRegistries) | [features-aliases](references/features-aliases.md) |
|
||||
| Hooks | .pnpmfile.mjs hooks (readPackage, updateConfig, beforePacking), finders, resolvers/fetchers | [features-hooks](references/features-hooks.md) |
|
||||
| Peer Dependencies | Auto-install, strict mode, rules, dedupePeers, peers check | [features-peer-deps](references/features-peer-deps.md) |
|
||||
| Config Dependencies | Share hooks/settings/catalogs/patches across repos via configDependencies | [features-config-dependencies](references/features-config-dependencies.md) |
|
||||
| Global Virtual Store | Shared node_modules, git-worktree multi-agent setups, isolated global packages | [features-global-virtual-store](references/features-global-virtual-store.md) |
|
||||
| Supply-Chain Security | Build approval (allowBuilds), minimumReleaseAge, trustPolicy, lockfile integrity | [features-supply-chain-security](references/features-supply-chain-security.md) |
|
||||
|
||||
## Best Practices
|
||||
|
||||
| Topic | Description | Reference |
|
||||
|-------|-------------|-----------|
|
||||
| CI/CD Setup | GitHub Actions, GitLab CI, Docker, and caching strategies | [best-practices-ci](references/best-practices-ci.md) |
|
||||
| Migration | Migrating from npm/Yarn, handling phantom deps, monorepo migration | [best-practices-migration](references/best-practices-migration.md) |
|
||||
| Performance | Install optimizations, store caching, workspace parallelization | [best-practices-performance](references/best-practices-performance.md) |
|
||||
| CI/CD Setup | GitHub Actions, GitLab, Docker, pnpm ci, store caching, frozen lockfiles | [best-practices-ci](references/best-practices-ci.md) |
|
||||
| Migration | npm/Yarn → pnpm, phantom deps, and pnpm v10 → v11 config migration | [best-practices-migration](references/best-practices-migration.md) |
|
||||
| Performance | Install optimizations, allowBuilds, global virtual store, workspace parallelization | [best-practices-performance](references/best-practices-performance.md) |
|
||||
|
||||
@@ -7,6 +7,8 @@ description: Optimizing pnpm for continuous integration and deployment workflows
|
||||
|
||||
Best practices for using pnpm in CI/CD environments for fast, reliable builds.
|
||||
|
||||
> **CI auto-behaviors:** When pnpm detects a CI environment it switches to **frozen-lockfile** mode automatically and (since v11) **fails on an incompatible lockfile** written by a newer pnpm major instead of rewriting it — keep the CI pnpm version in sync with the one that generated the lockfile. The global virtual store is auto-disabled in CI (no warm cache).
|
||||
|
||||
## GitHub Actions
|
||||
|
||||
### Basic Setup
|
||||
@@ -24,18 +26,20 @@ jobs:
|
||||
|
||||
- uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9
|
||||
version: 10
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
node-version: 22
|
||||
cache: 'pnpm'
|
||||
|
||||
- run: pnpm install --frozen-lockfile
|
||||
- run: pnpm install --frozen-lockfile # or: pnpm ci
|
||||
- run: pnpm test
|
||||
- run: pnpm build
|
||||
```
|
||||
|
||||
> `pnpm ci` (aliases `clean-install`, `install-clean`) = `pnpm clean` + `pnpm install --frozen-lockfile`, ideal for fully reproducible CI builds.
|
||||
|
||||
### With Store Caching
|
||||
|
||||
For larger projects, cache the pnpm store:
|
||||
@@ -43,7 +47,7 @@ For larger projects, cache the pnpm store:
|
||||
```yaml
|
||||
- uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9
|
||||
version: 10
|
||||
|
||||
- name: Get pnpm store directory
|
||||
shell: bash
|
||||
@@ -61,6 +65,8 @@ For larger projects, cache the pnpm store:
|
||||
- run: pnpm install --frozen-lockfile
|
||||
```
|
||||
|
||||
> **Trust:** only cache/restore the pnpm store and cache dir between *trusted* jobs. A store an untrusted job can write to must not be reused by trusted jobs — it is part of pnpm's trust domain.
|
||||
|
||||
### Matrix Testing
|
||||
|
||||
```yaml
|
||||
@@ -124,11 +130,13 @@ build:
|
||||
|
||||
## Docker
|
||||
|
||||
> **PATH change (v11):** global pnpm binaries now live in `$PNPM_HOME/bin`. In Docker set `ENV PATH="$PNPM_HOME/bin:$PATH"` (not `$PNPM_HOME`). There is also an official image `ghcr.io/pnpm/pnpm:<version>` (Debian slim, pnpm only — choose Node yourself via `pnpm runtime set node <ver> -g` or `devEngines.runtime`).
|
||||
|
||||
### Multi-Stage Build
|
||||
|
||||
```dockerfile
|
||||
# Build stage
|
||||
FROM node:20-slim AS builder
|
||||
FROM node:24-slim AS builder
|
||||
|
||||
# Enable corepack for pnpm
|
||||
RUN corepack enable
|
||||
@@ -214,12 +222,12 @@ pnpm install --frozen-lockfile --ignore-scripts
|
||||
|
||||
## Corepack Integration
|
||||
|
||||
Use Corepack to manage pnpm version:
|
||||
Use Corepack to pin the pnpm version:
|
||||
|
||||
```json
|
||||
// package.json
|
||||
{
|
||||
"packageManager": "pnpm@9.0.0"
|
||||
"packageManager": "pnpm@10.0.0"
|
||||
}
|
||||
```
|
||||
|
||||
@@ -229,6 +237,8 @@ Use Corepack to manage pnpm version:
|
||||
- run: pnpm install --frozen-lockfile
|
||||
```
|
||||
|
||||
For range-based pinning use `devEngines.packageManager` (resolved version stored in the lockfile). To skip the pin check when version management is external (asdf/mise/Volta), set `pmOnFail: ignore` in `pnpm-workspace.yaml`, or run a one-off with `pnpm with current <cmd>`.
|
||||
|
||||
## Monorepo CI Strategies
|
||||
|
||||
### Build Changed Packages Only
|
||||
@@ -271,15 +281,18 @@ jobs:
|
||||
|
||||
## Best Practices Summary
|
||||
|
||||
1. **Always use `--frozen-lockfile`** in CI
|
||||
2. **Cache the pnpm store** for faster installs
|
||||
3. **Use Corepack** for consistent pnpm versions
|
||||
4. **Specify `packageManager`** in package.json
|
||||
1. **Use `pnpm ci` or `--frozen-lockfile`** in CI
|
||||
2. **Cache the pnpm store** (only across trusted jobs)
|
||||
3. **Match the CI pnpm major** to the one that wrote the lockfile (CI fails on incompatible lockfiles)
|
||||
4. **Pin `packageManager`** (or `devEngines.packageManager`) in package.json
|
||||
5. **Use `--filter`** in monorepos to build only what changed
|
||||
6. **Multi-stage Docker builds** for smaller images
|
||||
6. **Multi-stage Docker builds**; set `PATH=$PNPM_HOME/bin:$PATH`
|
||||
|
||||
<!--
|
||||
Source references:
|
||||
- https://pnpm.io/continuous-integration
|
||||
- https://pnpm.io/docker
|
||||
- https://pnpm.io/cli/ci
|
||||
- https://github.com/pnpm/action-setup
|
||||
-->
|
||||
|
||||
|
||||
@@ -5,7 +5,36 @@ description: Migrating from npm or Yarn to pnpm with minimal friction
|
||||
|
||||
# Migration to pnpm
|
||||
|
||||
Guide for migrating existing projects from npm or Yarn to pnpm.
|
||||
Guide for migrating existing projects from npm or Yarn to pnpm, plus upgrading pnpm v10 → v11.
|
||||
|
||||
## Upgrading pnpm v10 → v11
|
||||
|
||||
v11 changes how configuration is read. Most of it is mechanical — run the codemod:
|
||||
|
||||
```bash
|
||||
cd /path/to/project
|
||||
pnpx codemod run pnpm-v10-to-v11
|
||||
```
|
||||
|
||||
The codemod automatically:
|
||||
|
||||
- **Moves `package.json#pnpm` settings into `pnpm-workspace.yaml`** (the `pnpm` field is no longer read).
|
||||
- **Splits `.npmrc`**: only auth/registry settings stay in `.npmrc`; every other key moves to `pnpm-workspace.yaml` as **camelCase** (e.g. `node-linker` → `nodeLinker`). Per-subproject `.npmrc` files become `packageConfigs["<name>"]`.
|
||||
- **Consolidates build settings** (`onlyBuiltDependencies`, `neverBuiltDependencies`, `ignoredBuiltDependencies`, `onlyBuiltDependenciesFile`) into one `allowBuilds: { name: true|false }` map.
|
||||
- **Replaces** `managePackageManagerVersions`/`packageManagerStrict`/`packageManagerStrictVersion` with `pmOnFail: download|ignore|warn|error`.
|
||||
- **Renames** `allowNonAppliedPatches` → `allowUnusedPatches`, `auditConfig.ignoreCves` → `auditConfig.ignoreGhsas`.
|
||||
- **Converts** `useNodeVersion` → `devEngines.runtime`, and bumps `packageManager`.
|
||||
|
||||
Manual follow-ups (not automatable):
|
||||
|
||||
- Convert `CVE-…` IDs to `GHSA-…` in `auditConfig.ignoreGhsas`.
|
||||
- `ignorePatchFailures` removed — failed patches now always throw.
|
||||
- `npm_config_*` env vars → `pnpm_config_*` (CI, shell profiles, Docker).
|
||||
- `pnpm link <name>` → use a path (`pnpm link ./foo`); `pnpm link --global` → `pnpm add -g .`.
|
||||
- `pnpm install -g` (no args) and `pnpm server` removed.
|
||||
- A `package.json` script named `clean`/`setup`/`deploy`/`rebuild` now shadows the built-in — use `pnpm pm <name>` for the built-in.
|
||||
|
||||
## Migrating from npm / Yarn
|
||||
|
||||
## Quick Migration
|
||||
|
||||
@@ -64,10 +93,9 @@ pnpm add lodash
|
||||
|
||||
pnpm reports peer dependency issues by default.
|
||||
|
||||
**Option 1:** Let pnpm auto-install:
|
||||
```ini
|
||||
# .npmrc (default in pnpm v8+)
|
||||
auto-install-peers=true
|
||||
**Option 1:** Let pnpm auto-install (default in v8+):
|
||||
```yaml title="pnpm-workspace.yaml"
|
||||
autoInstallPeers: true
|
||||
```
|
||||
|
||||
**Option 2:** Install manually:
|
||||
@@ -76,30 +104,26 @@ pnpm add react react-dom
|
||||
```
|
||||
|
||||
**Option 3:** Suppress warnings if acceptable:
|
||||
```json
|
||||
{
|
||||
"pnpm": {
|
||||
"peerDependencyRules": {
|
||||
"ignoreMissing": ["react"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```yaml title="pnpm-workspace.yaml"
|
||||
peerDependencyRules:
|
||||
ignoreMissing:
|
||||
- react
|
||||
```
|
||||
|
||||
### Symlink Issues
|
||||
|
||||
Some tools don't work with symlinks. Use hoisted mode:
|
||||
|
||||
```ini
|
||||
# .npmrc
|
||||
node-linker=hoisted
|
||||
```yaml title="pnpm-workspace.yaml"
|
||||
nodeLinker: hoisted
|
||||
```
|
||||
|
||||
Or hoist specific packages:
|
||||
|
||||
```ini
|
||||
public-hoist-pattern[]=*eslint*
|
||||
public-hoist-pattern[]=*babel*
|
||||
```yaml title="pnpm-workspace.yaml"
|
||||
publicHoistPattern:
|
||||
- '*eslint*'
|
||||
- '*babel*'
|
||||
```
|
||||
|
||||
### Native Module Rebuilds
|
||||
@@ -165,7 +189,7 @@ pnpm install
|
||||
```json
|
||||
// From Yarn
|
||||
"@myorg/utils": "*"
|
||||
|
||||
|
||||
// To pnpm
|
||||
"@myorg/utils": "workspace:*"
|
||||
```
|
||||
@@ -184,7 +208,7 @@ pnpm -r run build
|
||||
# Lerna: run in specific package
|
||||
lerna run build --scope=@myorg/app
|
||||
|
||||
# pnpm equivalent
|
||||
# pnpm equivalent
|
||||
pnpm --filter @myorg/app run build
|
||||
|
||||
# Lerna: publish
|
||||
@@ -199,21 +223,19 @@ pnpm publish -r
|
||||
|
||||
## Configuration Migration
|
||||
|
||||
### .npmrc Settings
|
||||
Keep only **auth/registry** in `.npmrc`; put everything else in `pnpm-workspace.yaml` (camelCase).
|
||||
|
||||
Most npm/Yarn settings work in pnpm's `.npmrc`:
|
||||
|
||||
```ini
|
||||
# Registry settings (same as npm)
|
||||
registry=https://registry.npmjs.org/
|
||||
@myorg:registry=https://npm.myorg.com/
|
||||
|
||||
# Auth tokens (same as npm)
|
||||
```ini title=".npmrc (auth only, gitignored)"
|
||||
//registry.npmjs.org/:_authToken=${NPM_TOKEN}
|
||||
//npm.myorg.com/:_authToken=${MYORG_TOKEN}
|
||||
```
|
||||
|
||||
# pnpm-specific additions
|
||||
auto-install-peers=true
|
||||
strict-peer-dependencies=false
|
||||
```yaml title="pnpm-workspace.yaml"
|
||||
registries:
|
||||
default: https://registry.npmjs.org/
|
||||
'@myorg': https://npm.myorg.com/
|
||||
autoInstallPeers: true
|
||||
strictPeerDependencies: false
|
||||
```
|
||||
|
||||
### Scripts Migration
|
||||
@@ -227,8 +249,8 @@ Most scripts work unchanged. Update pnpm-specific patterns:
|
||||
"build:all": "npm run build --workspaces",
|
||||
// pnpm: use -r flag
|
||||
"build:all": "pnpm -r run build",
|
||||
|
||||
// npm: run in specific workspace
|
||||
|
||||
// npm: run in specific workspace
|
||||
"dev:app": "npm run dev -w packages/app",
|
||||
// pnpm: use --filter
|
||||
"dev:app": "pnpm --filter @myorg/app run dev"
|
||||
@@ -246,13 +268,13 @@ Update CI configuration:
|
||||
|
||||
# After (pnpm)
|
||||
- uses: pnpm/action-setup@v4
|
||||
- run: pnpm install --frozen-lockfile
|
||||
- run: pnpm install --frozen-lockfile # or: pnpm ci
|
||||
```
|
||||
|
||||
Add to `package.json` for Corepack:
|
||||
```json
|
||||
{
|
||||
"packageManager": "pnpm@9.0.0"
|
||||
"packageManager": "pnpm@10.0.0"
|
||||
}
|
||||
```
|
||||
|
||||
@@ -285,7 +307,8 @@ Keep old lockfile in git history for easy rollback.
|
||||
|
||||
<!--
|
||||
Source references:
|
||||
- https://pnpm.io/installation
|
||||
- https://pnpm.io/migration
|
||||
- https://pnpm.io/cli/import
|
||||
- https://pnpm.io/limitations
|
||||
- https://pnpm.io/configuring
|
||||
-->
|
||||
|
||||
|
||||
@@ -27,12 +27,6 @@ Use cached packages when available:
|
||||
pnpm install --prefer-offline
|
||||
```
|
||||
|
||||
Or configure globally:
|
||||
```ini
|
||||
# .npmrc
|
||||
prefer-offline=true
|
||||
```
|
||||
|
||||
### Skip Optional Dependencies
|
||||
|
||||
If you don't need optional deps:
|
||||
@@ -53,51 +47,46 @@ pnpm install --ignore-scripts
|
||||
|
||||
### Only Build Specific Dependencies
|
||||
|
||||
Only run build scripts for specific packages:
|
||||
Build-script approval is a single `allowBuilds` map (replaces `onlyBuiltDependencies`/`neverBuiltDependencies`). Only allowed packages run install scripts:
|
||||
|
||||
```ini
|
||||
# .npmrc
|
||||
onlyBuiltDependencies[]=esbuild
|
||||
onlyBuiltDependencies[]=sharp
|
||||
onlyBuiltDependencies[]=@swc/core
|
||||
```yaml title="pnpm-workspace.yaml"
|
||||
allowBuilds:
|
||||
esbuild: true
|
||||
'@swc/core': true
|
||||
core-js: false # explicitly skip
|
||||
```
|
||||
|
||||
Or skip builds entirely for deps that don't need them:
|
||||
|
||||
```json
|
||||
{
|
||||
"pnpm": {
|
||||
"neverBuiltDependencies": ["fsevents", "cpu-features"]
|
||||
}
|
||||
}
|
||||
```
|
||||
Packages not listed are treated as unreviewed (blocked by default). See `features-supply-chain-security` for the full build-approval workflow.
|
||||
|
||||
## Store Optimizations
|
||||
|
||||
### Side Effects Cache
|
||||
|
||||
Cache native module build results:
|
||||
Cache native module build results (enabled by default):
|
||||
|
||||
```ini
|
||||
# .npmrc
|
||||
side-effects-cache=true
|
||||
```yaml title="pnpm-workspace.yaml"
|
||||
sideEffectsCache: true
|
||||
```
|
||||
|
||||
This caches the results of postinstall scripts, speeding up subsequent installs.
|
||||
|
||||
### Shared Store
|
||||
### Global Virtual Store
|
||||
|
||||
Use a single store for all projects (default behavior):
|
||||
For many checkouts of the same repo (e.g. git worktrees / multiple agents), enable the global virtual store so each project's `node_modules` is just symlinks into one shared store — near-zero per-checkout cost. Auto-disabled in CI.
|
||||
|
||||
```ini
|
||||
# .npmrc
|
||||
store-dir=~/.pnpm-store
|
||||
```yaml title="pnpm-workspace.yaml"
|
||||
enableGlobalVirtualStore: true
|
||||
```
|
||||
|
||||
Benefits:
|
||||
- Packages downloaded once for all projects
|
||||
- Hard links save disk space
|
||||
- Faster installs from cache
|
||||
### Shared Store
|
||||
|
||||
A single content-addressable store is used for all projects by default:
|
||||
|
||||
```yaml title="pnpm-workspace.yaml"
|
||||
storeDir: ~/.local/share/pnpm/store
|
||||
```
|
||||
|
||||
Benefits: packages downloaded once, hard links save disk space, faster cached installs.
|
||||
|
||||
### Store Maintenance
|
||||
|
||||
@@ -122,9 +111,8 @@ pnpm -r --parallel run build
|
||||
```
|
||||
|
||||
Control concurrency:
|
||||
```ini
|
||||
# .npmrc
|
||||
workspace-concurrency=8
|
||||
```yaml title="pnpm-workspace.yaml"
|
||||
workspaceConcurrency: 8
|
||||
```
|
||||
|
||||
### Stream Output
|
||||
@@ -160,33 +148,17 @@ pnpm -r --workspace-concurrency=1 run build
|
||||
|
||||
## Network Optimizations
|
||||
|
||||
### Configure Registry
|
||||
Network/registry settings are camelCase in `pnpm-workspace.yaml` (registry URLs may also go in `registries`):
|
||||
|
||||
Use closest/fastest registry:
|
||||
|
||||
```ini
|
||||
# .npmrc
|
||||
registry=https://registry.npmmirror.com/
|
||||
```
|
||||
|
||||
### HTTP Settings
|
||||
|
||||
Tune network settings:
|
||||
|
||||
```ini
|
||||
# .npmrc
|
||||
fetch-retries=3
|
||||
fetch-retry-mintimeout=10000
|
||||
fetch-retry-maxtimeout=60000
|
||||
network-concurrency=16
|
||||
```
|
||||
|
||||
### Proxy Configuration
|
||||
|
||||
```ini
|
||||
# .npmrc
|
||||
proxy=http://proxy.company.com:8080
|
||||
https-proxy=http://proxy.company.com:8080
|
||||
```yaml title="pnpm-workspace.yaml"
|
||||
registries:
|
||||
default: https://registry.npmmirror.com/
|
||||
fetchRetries: 3
|
||||
fetchRetryMintimeout: 10000
|
||||
fetchRetryMaxtimeout: 60000
|
||||
networkConcurrency: 16 # auto: clamp(workers x 3, 16, 64)
|
||||
httpProxy: http://proxy.company.com:8080
|
||||
httpsProxy: http://proxy.company.com:8080
|
||||
```
|
||||
|
||||
## Lockfile Optimization
|
||||
@@ -195,9 +167,8 @@ https-proxy=http://proxy.company.com:8080
|
||||
|
||||
Use shared lockfile for all packages (default):
|
||||
|
||||
```ini
|
||||
# .npmrc
|
||||
shared-workspace-lockfile=true
|
||||
```yaml title="pnpm-workspace.yaml"
|
||||
sharedWorkspaceLockfile: true
|
||||
```
|
||||
|
||||
Benefits:
|
||||
@@ -244,41 +215,47 @@ DEBUG=pnpm:* pnpm install
|
||||
|
||||
## Configuration Summary
|
||||
|
||||
Optimized `.npmrc` for performance:
|
||||
Optimized `pnpm-workspace.yaml` for performance:
|
||||
|
||||
```ini
|
||||
```yaml title="pnpm-workspace.yaml"
|
||||
# Install behavior
|
||||
prefer-offline=true
|
||||
auto-install-peers=true
|
||||
autoInstallPeers: true
|
||||
sideEffectsCache: true
|
||||
optimisticRepeatInstall: true
|
||||
|
||||
# Build optimization
|
||||
side-effects-cache=true
|
||||
# Only build what's necessary
|
||||
onlyBuiltDependencies[]=esbuild
|
||||
onlyBuiltDependencies[]=@swc/core
|
||||
# Build approval (only what's necessary)
|
||||
allowBuilds:
|
||||
esbuild: true
|
||||
'@swc/core': true
|
||||
|
||||
# Network
|
||||
fetch-retries=3
|
||||
network-concurrency=16
|
||||
fetchRetries: 3
|
||||
networkConcurrency: 16
|
||||
|
||||
# Workspace
|
||||
workspace-concurrency=4
|
||||
workspaceConcurrency: 4
|
||||
|
||||
# Many checkouts of the same repo
|
||||
enableGlobalVirtualStore: true
|
||||
```
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Scenario | Command/Setting |
|
||||
|----------|-----------------|
|
||||
| CI installs | `pnpm install --frozen-lockfile` |
|
||||
| CI installs | `pnpm ci` / `pnpm install --frozen-lockfile` |
|
||||
| Offline development | `--prefer-offline` |
|
||||
| Skip native builds | `neverBuiltDependencies` |
|
||||
| Control native builds | `allowBuilds` map |
|
||||
| Parallel workspace | `pnpm -r --parallel run build` |
|
||||
| Build changed only | `pnpm --filter "...[origin/main]" build` |
|
||||
| Clean store | `pnpm store prune` |
|
||||
| Many worktrees/agents | `enableGlobalVirtualStore: true` |
|
||||
|
||||
<!--
|
||||
Source references:
|
||||
- https://pnpm.io/npmrc
|
||||
- https://pnpm.io/settings
|
||||
- https://pnpm.io/cli/install
|
||||
- https://pnpm.io/filtering
|
||||
- https://pnpm.io/global-virtual-store
|
||||
-->
|
||||
|
||||
|
||||
@@ -1,229 +1,196 @@
|
||||
---
|
||||
name: pnpm-cli-commands
|
||||
description: Essential pnpm commands for package management, running scripts, and workspace operations
|
||||
description: Essential pnpm commands for package management, running scripts, workspaces, publishing, and runtimes
|
||||
---
|
||||
|
||||
# pnpm CLI Commands
|
||||
|
||||
pnpm provides a comprehensive CLI for package management with commands similar to npm/yarn but with unique features.
|
||||
pnpm provides a comprehensive CLI. Commands resemble npm/yarn but with unique features.
|
||||
|
||||
## Installation Commands
|
||||
|
||||
### Install all dependencies
|
||||
```bash
|
||||
pnpm install
|
||||
# or
|
||||
pnpm i
|
||||
```
|
||||
|
||||
### Add a dependency
|
||||
```bash
|
||||
# Production dependency
|
||||
pnpm add <pkg>
|
||||
|
||||
# Dev dependency
|
||||
pnpm add -D <pkg>
|
||||
pnpm add --save-dev <pkg>
|
||||
|
||||
# Optional dependency
|
||||
pnpm add -O <pkg>
|
||||
|
||||
# Global package
|
||||
pnpm add -g <pkg>
|
||||
|
||||
# Specific version
|
||||
pnpm install # install all deps (alias: pnpm i)
|
||||
pnpm add <pkg> # production dependency
|
||||
pnpm add -D <pkg> # devDependency (also -d)
|
||||
pnpm add -O <pkg> # optionalDependency (also -o)
|
||||
pnpm add -E <pkg> # exact version (also -e)
|
||||
pnpm add <pkg>@<version>
|
||||
pnpm add <pkg>@next
|
||||
pnpm add <pkg>@^1.0.0
|
||||
pnpm remove <pkg> # aliases: rm, uninstall, un
|
||||
pnpm update # alias: up
|
||||
pnpm update --latest # ignore semver ranges (-L)
|
||||
pnpm update -i # interactive
|
||||
```
|
||||
|
||||
### Remove a dependency
|
||||
### Clean / reproducible installs
|
||||
|
||||
```bash
|
||||
pnpm remove <pkg>
|
||||
pnpm rm <pkg>
|
||||
pnpm uninstall <pkg>
|
||||
pnpm un <pkg>
|
||||
pnpm install --frozen-lockfile # fail if lockfile would change (auto in CI)
|
||||
pnpm ci # clean install = pnpm clean + install --frozen-lockfile
|
||||
pnpm clean # remove node_modules in all workspace projects (alias: purge)
|
||||
pnpm clean --lockfile # also delete pnpm-lock.yaml
|
||||
```
|
||||
|
||||
### Update dependencies
|
||||
```bash
|
||||
# Update all
|
||||
pnpm update
|
||||
pnpm up
|
||||
|
||||
# Update specific package
|
||||
pnpm update <pkg>
|
||||
|
||||
# Update to latest (ignore semver)
|
||||
pnpm update --latest
|
||||
pnpm up -L
|
||||
|
||||
# Interactive update
|
||||
pnpm update --interactive
|
||||
pnpm up -i
|
||||
```
|
||||
> Since v11, an integrity mismatch against the lockfile is a hard error (`ERR_PNPM_TARBALL_INTEGRITY`). Use `pnpm install --update-checksums` only after verifying the new bytes. In CI, pnpm also fails on lockfiles written by a newer pnpm major.
|
||||
|
||||
## Script Commands
|
||||
|
||||
### Run scripts
|
||||
```bash
|
||||
pnpm run <script>
|
||||
# or shorthand
|
||||
pnpm <script>
|
||||
|
||||
# Pass arguments to script
|
||||
pnpm run <script> # or just: pnpm <script>
|
||||
pnpm run build -- --watch
|
||||
|
||||
# Run script if exists (no error if missing)
|
||||
pnpm run --if-present build
|
||||
pnpm set-script test "vitest run" # add/update a scripts entry (alias: ss)
|
||||
pnpm exec <cmd> # run a local binary, e.g. pnpm exec eslint .
|
||||
```
|
||||
|
||||
### Execute binaries
|
||||
- **Hidden scripts:** names starting with `.` (e.g. `.helper`) can't be run directly, only called from other scripts.
|
||||
- **Built-in vs script conflict:** `clean`, `setup`, `deploy`, `rebuild` prefer a same-named `package.json` script. Force the built-in with `pnpm pm <name>` (e.g. `pnpm pm clean`).
|
||||
|
||||
### dlx / pnx — run without installing
|
||||
|
||||
```bash
|
||||
# Run local binary
|
||||
pnpm exec <command>
|
||||
|
||||
# Example
|
||||
pnpm exec eslint .
|
||||
pnx create-vite my-app # pnx == pnpm dlx == pnpx
|
||||
pnpm dlx degit user/repo dest
|
||||
pnx shx@catalog: # catalog: protocol supported
|
||||
pnx --package=@scope/tool tool --help
|
||||
```
|
||||
|
||||
### dlx - Run without installing
|
||||
```bash
|
||||
# Like npx but for pnpm
|
||||
pnpm dlx <pkg>
|
||||
|
||||
# Examples
|
||||
pnpm dlx create-vite my-app
|
||||
pnpm dlx degit user/repo my-project
|
||||
```
|
||||
> `dlx`/`pnx` honor supply-chain settings (`minimumReleaseAge`, `trustPolicy`) and use the global virtual store by default in v11.
|
||||
|
||||
## Workspace Commands
|
||||
|
||||
### Run in all packages
|
||||
```bash
|
||||
# Run script in all workspace packages
|
||||
pnpm -r run <script>
|
||||
pnpm --recursive run <script>
|
||||
|
||||
# Run in specific packages
|
||||
pnpm -r run <script> # run in all packages (alias: --recursive)
|
||||
pnpm --filter <pattern> run <script>
|
||||
|
||||
# Examples
|
||||
pnpm --filter "./packages/**" run build
|
||||
pnpm --filter "!./packages/internal/**" run test
|
||||
pnpm --filter "@myorg/*" run lint
|
||||
pnpm -r --parallel run dev
|
||||
```
|
||||
|
||||
### Filter patterns
|
||||
```bash
|
||||
# By package name
|
||||
pnpm --filter <pkg-name> <command>
|
||||
pnpm --filter "@scope/pkg" build
|
||||
|
||||
# By directory
|
||||
```bash
|
||||
pnpm --filter <pkg-name> <cmd> # by name (-F shorthand)
|
||||
pnpm --filter "./packages/core" test
|
||||
|
||||
# Dependencies of a package
|
||||
pnpm --filter "...@scope/app" build
|
||||
|
||||
# Dependents of a package
|
||||
pnpm --filter "@scope/core..." test
|
||||
|
||||
# Changed packages since commit/branch
|
||||
pnpm --filter "...[origin/main]" build
|
||||
pnpm --filter "...@scope/app" build # package + its dependencies
|
||||
pnpm --filter "@scope/core..." test # package + its dependents
|
||||
pnpm --filter "...[origin/main]" build # changed since git ref
|
||||
```
|
||||
|
||||
## Other Useful Commands
|
||||
## Patches
|
||||
|
||||
### Link packages
|
||||
```bash
|
||||
# Link global package
|
||||
pnpm link --global
|
||||
pnpm link -g
|
||||
|
||||
# Use linked package
|
||||
pnpm link --global <pkg>
|
||||
pnpm patch <pkg>@<version> # opens an editable copy, prints a path
|
||||
pnpm patch-commit <path> # writes patches/*.patch and records it
|
||||
pnpm patch-remove <pkg>@<version>
|
||||
```
|
||||
|
||||
### Patch packages
|
||||
## Linking local packages
|
||||
|
||||
```bash
|
||||
# Create patch for a package
|
||||
pnpm patch <pkg>@<version>
|
||||
|
||||
# After editing, commit the patch
|
||||
pnpm patch-commit <path>
|
||||
|
||||
# Remove a patch
|
||||
pnpm patch-remove <pkg>
|
||||
pnpm link <dir> # link a path into this project's node_modules (path only!)
|
||||
pnpm add -g . # register the current package's bins globally
|
||||
```
|
||||
|
||||
### Store management
|
||||
> Breaking in v11: `pnpm link` accepts **only relative/absolute paths** (no global store resolution, no `--global`, no bare `pnpm link`). Use `pnpm add -g .` to expose bins system-wide.
|
||||
|
||||
## Global packages (v11 isolated installs)
|
||||
|
||||
```bash
|
||||
# Show store path
|
||||
pnpm store path
|
||||
pnpm add -g typescript prettier # each gets its own isolated install dir
|
||||
pnpm add -g eslint,prettier # comma = ONE shared install group
|
||||
pnpm add -g --allow-build=esbuild esbuild
|
||||
pnpm remove -g <pkg>
|
||||
pnpm list -g
|
||||
pnpm bin -g # show global bin dir ($PNPM_HOME/bin)
|
||||
```
|
||||
|
||||
# Remove unreferenced packages
|
||||
pnpm store prune
|
||||
> `pnpm install -g` (no args) is not supported. After upgrading to v11 run `pnpm setup` so `$PNPM_HOME/bin` is on PATH.
|
||||
|
||||
# Check store integrity
|
||||
## Runtimes (Node/Deno/Bun)
|
||||
|
||||
```bash
|
||||
pnpm runtime set node 22 -g # install & expose node (alias: rt)
|
||||
pnpm runtime set node lts -g
|
||||
pnpm runtime set deno 2 -g
|
||||
pnpm install --no-runtime # skip installing devEngines.runtime entries
|
||||
```
|
||||
|
||||
## Store management
|
||||
|
||||
```bash
|
||||
pnpm store path # store location (prints removed size after prune)
|
||||
pnpm store prune # GC unreferenced packages (+ global virtual store links)
|
||||
pnpm store status
|
||||
```
|
||||
|
||||
### Other commands
|
||||
## Inspection / registry
|
||||
|
||||
```bash
|
||||
# Clean install (like npm ci)
|
||||
pnpm install --frozen-lockfile
|
||||
|
||||
# List installed packages
|
||||
pnpm list
|
||||
pnpm ls
|
||||
|
||||
# Why is package installed?
|
||||
pnpm why <pkg>
|
||||
|
||||
# Outdated packages
|
||||
pnpm list # alias: ls
|
||||
pnpm why <pkg> # reverse-dependency tree (dedupes subtrees)
|
||||
pnpm why --find-by=<finder> # custom finder from .pnpmfile.mjs
|
||||
pnpm outdated
|
||||
|
||||
# Audit for vulnerabilities
|
||||
pnpm audit
|
||||
|
||||
# Rebuild native modules
|
||||
pnpm peers check # report unmet/missing peers from the lockfile
|
||||
pnpm view <pkg> [field] # registry metadata (aliases: info, show)
|
||||
pnpm whoami
|
||||
pnpm rebuild
|
||||
pnpm import # create pnpm-lock.yaml from npm/yarn lockfile
|
||||
pnpm dedupe
|
||||
```
|
||||
|
||||
# Import from npm/yarn lockfile
|
||||
pnpm import
|
||||
## Publishing
|
||||
|
||||
# Create tarball
|
||||
```bash
|
||||
pnpm pack
|
||||
pnpm publish -r --no-git-checks
|
||||
pnpm version patch|minor|major|2.0.0 # bump version, commit + tag (v11)
|
||||
pnpm version prerelease --preid beta
|
||||
pnpm deprecate <pkg>@<range> "message"
|
||||
pnpm dist-tag add <pkg>@<version> <tag>
|
||||
pnpm unpublish <pkg>@<version> # discouraged; prefer deprecate
|
||||
pnpm sbom --sbom-format cyclonedx # SBOM: cyclonedx (1.7) | spdx (2.3)
|
||||
pnpm stage publish ... # staged publishing (defer 2FA)
|
||||
```
|
||||
|
||||
# Publish package
|
||||
pnpm publish
|
||||
## Maintenance & version management
|
||||
|
||||
```bash
|
||||
pnpm self-update [<version>] # updates the packageManager pin, or installs globally
|
||||
pnpm with current install # run a specific pnpm version for one command
|
||||
pnpm with 11.0.0 install
|
||||
pnpm approve-builds [--all] # review dependency build scripts (writes allowBuilds)
|
||||
```
|
||||
|
||||
## Useful Flags
|
||||
|
||||
```bash
|
||||
# Ignore scripts
|
||||
pnpm install --ignore-scripts
|
||||
|
||||
# Prefer offline (use cache)
|
||||
pnpm install --prefer-offline
|
||||
|
||||
# Strict peer dependencies
|
||||
pnpm install --strict-peer-dependencies
|
||||
|
||||
# Production only
|
||||
pnpm install --prod
|
||||
pnpm install -P
|
||||
|
||||
# No optional dependencies
|
||||
pnpm install --prod # -P, omit devDependencies
|
||||
pnpm install --no-optional
|
||||
pnpm install --strict-peer-dependencies
|
||||
```
|
||||
|
||||
## Key Points
|
||||
|
||||
- `pnpm ci` = clean + frozen install; CI auto-enables frozen-lockfile.
|
||||
- `dlx`/`pnpx` are aliases of `pnx`; global installs are now isolated per package (comma-list to share).
|
||||
- `pnpm link` only takes paths; use `pnpm add -g .` for global bins.
|
||||
- Manage Node/Deno/Bun with `pnpm runtime set`; skip them at install with `--no-runtime`.
|
||||
- New publishing/registry commands: `version`, `view`, `whoami`, `deprecate`, `dist-tag`, `unpublish`, `sbom`, `stage`.
|
||||
|
||||
<!--
|
||||
Source references:
|
||||
- https://pnpm.io/cli/install
|
||||
- https://pnpm.io/cli/add
|
||||
- https://pnpm.io/cli/run
|
||||
- https://pnpm.io/filtering
|
||||
- https://pnpm.io/cli/link
|
||||
- https://pnpm.io/global-packages
|
||||
- https://pnpm.io/cli/runtime
|
||||
- https://pnpm.io/cli/version
|
||||
- https://pnpm.io/cli/with
|
||||
- https://pnpm.io/cli/sbom
|
||||
-->
|
||||
|
||||
@@ -1,188 +1,185 @@
|
||||
---
|
||||
name: pnpm-configuration
|
||||
description: Configuration options via pnpm-workspace.yaml and .npmrc settings
|
||||
description: Configuring pnpm via pnpm-workspace.yaml (settings), the global config.yaml, and .npmrc (auth only)
|
||||
---
|
||||
|
||||
# pnpm Configuration
|
||||
|
||||
pnpm uses two main configuration files: `pnpm-workspace.yaml` for workspace and pnpm-specific settings, and `.npmrc` for npm-compatible and pnpm-specific settings.
|
||||
pnpm settings are split into **two** categories. Knowing where each goes is the single most important config concept in current pnpm:
|
||||
|
||||
## pnpm-workspace.yaml
|
||||
| Category | Stored in | Format |
|
||||
|----------|-----------|--------|
|
||||
| **All pnpm/install settings** (`nodeLinker`, `hoistPattern`, `autoInstallPeers`, `overrides`, `catalog`, …) | `pnpm-workspace.yaml` (project) and `config.yaml` (global) | YAML, **camelCase** keys |
|
||||
| **Auth & registry credentials** (`_authToken`, `cert`, `key`, …) | `.npmrc` (project, gitignored) and global `rc` | INI |
|
||||
|
||||
The recommended location for pnpm-specific configurations. Place at project root.
|
||||
> **Important changes:** pnpm no longer reads settings from the `pnpm` field of `package.json`, and `.npmrc` is now used **only** for authentication/registry credentials. Everything else belongs in `pnpm-workspace.yaml`. Keys in YAML are **camelCase** (e.g. `nodeLinker`), not the kebab-case used by old `.npmrc` files.
|
||||
|
||||
```yaml
|
||||
# Define workspace packages
|
||||
## pnpm-workspace.yaml (primary config)
|
||||
|
||||
Place at the workspace/project root. Even a single-package project uses this file for pnpm settings.
|
||||
|
||||
```yaml title="pnpm-workspace.yaml"
|
||||
# Workspace packages (omit for a single-package repo)
|
||||
packages:
|
||||
- 'packages/*'
|
||||
- 'apps/*'
|
||||
- '!**/test/**' # Exclude pattern
|
||||
- '!**/test/**'
|
||||
|
||||
# Catalog for shared dependency versions
|
||||
# Common install settings (camelCase)
|
||||
nodeLinker: isolated # isolated (default) | hoisted | pnp
|
||||
autoInstallPeers: true
|
||||
strictPeerDependencies: false
|
||||
savePrefix: '^'
|
||||
saveExact: false
|
||||
hoistPattern:
|
||||
- '*eslint*'
|
||||
- '*babel*'
|
||||
publicHoistPattern: []
|
||||
shamefullyHoist: false
|
||||
dedupeDirectDeps: false
|
||||
resolutionMode: highest # highest | time-based | lowest-direct
|
||||
|
||||
# Centralized version management
|
||||
catalog:
|
||||
react: ^18.2.0
|
||||
typescript: ~5.3.0
|
||||
|
||||
# Named catalogs for different dependency groups
|
||||
catalogs:
|
||||
react17:
|
||||
react: ^17.0.2
|
||||
react-dom: ^17.0.2
|
||||
react18:
|
||||
react: ^18.2.0
|
||||
react-dom: ^18.2.0
|
||||
|
||||
# Override resolutions (preferred location)
|
||||
# Force dependency versions (root only)
|
||||
overrides:
|
||||
lodash: ^4.17.21
|
||||
'foo@^1.0.0>bar': ^2.0.0
|
||||
|
||||
# pnpm settings (alternative to .npmrc)
|
||||
settings:
|
||||
auto-install-peers: true
|
||||
strict-peer-dependencies: false
|
||||
link-workspace-packages: true
|
||||
prefer-workspace-packages: true
|
||||
shared-workspace-lockfile: true
|
||||
# Extend/patch broken package manifests
|
||||
packageExtensions:
|
||||
react-redux:
|
||||
peerDependencies:
|
||||
react-dom: '*'
|
||||
|
||||
# Peer dependency rules
|
||||
peerDependencyRules:
|
||||
ignoreMissing:
|
||||
- '@babel/*'
|
||||
allowedVersions:
|
||||
react: '17 || 18'
|
||||
```
|
||||
|
||||
## .npmrc Settings
|
||||
## Global configuration (config.yaml)
|
||||
|
||||
pnpm reads settings from `.npmrc` files. Create at project root or user home.
|
||||
User-level non-auth settings live in a global YAML `config.yaml`:
|
||||
|
||||
### Common pnpm Settings
|
||||
- `$XDG_CONFIG_HOME/pnpm/config.yaml` (if set)
|
||||
- Linux: `~/.config/pnpm/config.yaml`
|
||||
- macOS: `~/Library/Preferences/pnpm/config.yaml`
|
||||
- Windows: `~/AppData/Local/pnpm/config/config.yaml`
|
||||
|
||||
```ini
|
||||
# Automatically install peer dependencies
|
||||
auto-install-peers=true
|
||||
The companion global `rc` file (same directory, named `rc`) holds only registry/auth settings.
|
||||
|
||||
# Fail on peer dependency issues
|
||||
strict-peer-dependencies=false
|
||||
## Per-project settings in a workspace (packageConfigs)
|
||||
|
||||
# Hoist patterns for dependencies
|
||||
public-hoist-pattern[]=*types*
|
||||
public-hoist-pattern[]=*eslint*
|
||||
shamefully-hoist=false
|
||||
There are no per-subproject `.npmrc` files anymore. Set per-package config via `packageConfigs` in the root `pnpm-workspace.yaml`:
|
||||
|
||||
# Store location
|
||||
store-dir=~/.pnpm-store
|
||||
```yaml title="pnpm-workspace.yaml"
|
||||
packageConfigs:
|
||||
# Map form: keyed by package name
|
||||
project-1:
|
||||
saveExact: true
|
||||
project-2:
|
||||
savePrefix: '~'
|
||||
# Array form: pattern-matched rules
|
||||
# - match: ['project-1', 'project-2']
|
||||
# modulesDir: node_modules
|
||||
# saveExact: true
|
||||
```
|
||||
|
||||
# Virtual store location
|
||||
virtual-store-dir=node_modules/.pnpm
|
||||
## .npmrc — authentication only
|
||||
|
||||
# Lockfile settings
|
||||
lockfile=true
|
||||
prefer-frozen-lockfile=true
|
||||
Keep auth tokens out of the repo (gitignore the project `.npmrc`). Auth files, highest priority first:
|
||||
|
||||
# Side effects cache (speeds up rebuilds)
|
||||
side-effects-cache=true
|
||||
1. `<workspace root>/.npmrc` (project, gitignored)
|
||||
2. `<pnpm config>/auth.ini` (written by `pnpm login`)
|
||||
3. `~/.npmrc` (fallback for npm compatibility)
|
||||
|
||||
# Registry settings
|
||||
registry=https://registry.npmjs.org/
|
||||
```ini title=".npmrc"
|
||||
//registry.npmjs.org/:_authToken=${NPM_TOKEN}
|
||||
@myorg:registry=https://npm.myorg.com/
|
||||
//npm.myorg.com/:_authToken=${MYORG_TOKEN}
|
||||
```
|
||||
|
||||
### Workspace Settings
|
||||
Configure registries themselves (non-secret) in `pnpm-workspace.yaml`:
|
||||
|
||||
```ini
|
||||
# Link workspace packages
|
||||
link-workspace-packages=true
|
||||
|
||||
# Prefer workspace packages over registry
|
||||
prefer-workspace-packages=true
|
||||
|
||||
# Single lockfile for all packages
|
||||
shared-workspace-lockfile=true
|
||||
|
||||
# Save prefix for workspace dependencies
|
||||
save-workspace-protocol=rolling
|
||||
```yaml title="pnpm-workspace.yaml"
|
||||
registries:
|
||||
default: https://registry.npmjs.org/
|
||||
'@my-org': https://private.example.com/
|
||||
# Named registry aliases usable as a prefix, e.g. `pnpm add work:@corp/lib`
|
||||
namedRegistries:
|
||||
work: https://npm.work.example.com/
|
||||
```
|
||||
|
||||
### Node.js Settings
|
||||
> Security: since v11, env-variable expansion is disabled for registry/proxy URLs and credential keys in the **project** `.npmrc` (to stop a malicious repo from leaking secrets). Put dynamic-token lines in the user-level auth file instead.
|
||||
|
||||
```ini
|
||||
# Use specific Node.js version
|
||||
use-node-version=20.10.0
|
||||
|
||||
# Node.js version file
|
||||
node-version-file=.nvmrc
|
||||
|
||||
# Manage Node.js versions
|
||||
manage-package-manager-versions=true
|
||||
```
|
||||
|
||||
### Security Settings
|
||||
|
||||
```ini
|
||||
# Ignore specific scripts
|
||||
ignore-scripts=false
|
||||
|
||||
# Allow specific build scripts
|
||||
onlyBuiltDependencies[]=esbuild
|
||||
onlyBuiltDependencies[]=sharp
|
||||
|
||||
# Package extensions for missing peer deps
|
||||
package-extensions[foo@1].peerDependencies.bar=*
|
||||
```
|
||||
|
||||
## Configuration Hierarchy
|
||||
|
||||
Settings are read in order (later overrides earlier):
|
||||
|
||||
1. `/etc/npmrc` - Global config
|
||||
2. `~/.npmrc` - User config
|
||||
3. `<project>/.npmrc` - Project config
|
||||
4. Environment variables: `npm_config_<key>=<value>`
|
||||
5. `pnpm-workspace.yaml` settings field
|
||||
|
||||
## Environment Variables
|
||||
## The `pnpm config` command
|
||||
|
||||
```bash
|
||||
# Set config via env
|
||||
npm_config_registry=https://registry.npmjs.org/
|
||||
# Writes to global config.yaml / rc by default
|
||||
pnpm config set nodeVersion 22.0.0
|
||||
pnpm config set --location=project nodeVersion 22.0.0 # writes pnpm-workspace.yaml
|
||||
|
||||
# pnpm-specific env vars
|
||||
PNPM_HOME=~/.local/share/pnpm
|
||||
# JSON values create arrays/objects
|
||||
pnpm config set --location=project --json allowBuilds '{"react": true}'
|
||||
|
||||
# get/list print JSON (no longer INI) since v11
|
||||
pnpm config get nodeLinker
|
||||
pnpm config get 'allowBuilds.react'
|
||||
pnpm config list
|
||||
```
|
||||
|
||||
## Package.json Fields
|
||||
## Environment variables
|
||||
|
||||
pnpm reads specific fields from `package.json`:
|
||||
Use `pnpm_config_*` (or `PNPM_CONFIG_*`). pnpm **no longer reads `npm_config_*`**.
|
||||
|
||||
```bash
|
||||
pnpm_config_save_exact=true pnpm add foo
|
||||
```
|
||||
|
||||
## Notable settings that changed names
|
||||
|
||||
| Old (removed) | Replacement | Notes |
|
||||
|---------------|-------------|-------|
|
||||
| `onlyBuiltDependencies`, `neverBuiltDependencies`, `ignoredBuiltDependencies`, `onlyBuiltDependenciesFile` | `allowBuilds: { name: true\|false }` | Single map controlling build-script approval. See supply-chain-security. |
|
||||
| `managePackageManagerVersions`, `packageManagerStrict`, `packageManagerStrictVersion`, `COREPACK_ENABLE_STRICT` | `pmOnFail: download\|ignore\|warn\|error` | Behavior when running pnpm version ≠ declared one. |
|
||||
| `useNodeVersion` | `devEngines.runtime` (in `package.json`) | Runtime pinning. |
|
||||
| `auditConfig.ignoreCves` | `auditConfig.ignoreGhsas` | Use GHSA IDs. |
|
||||
| `allowNonAppliedPatches` | `allowUnusedPatches` | `ignorePatchFailures` removed (patches now always throw). |
|
||||
| `package.json#pnpm` field | `pnpm-workspace.yaml` | No longer read at all. |
|
||||
|
||||
## Package Manager / Runtime pinning (package.json)
|
||||
|
||||
```json
|
||||
{
|
||||
"pnpm": {
|
||||
"overrides": {
|
||||
"lodash": "^4.17.21"
|
||||
},
|
||||
"peerDependencyRules": {
|
||||
"ignoreMissing": ["@babel/*"],
|
||||
"allowedVersions": {
|
||||
"react": "17 || 18"
|
||||
}
|
||||
},
|
||||
"neverBuiltDependencies": ["fsevents"],
|
||||
"onlyBuiltDependencies": ["esbuild"],
|
||||
"allowedDeprecatedVersions": {
|
||||
"request": "*"
|
||||
},
|
||||
"patchedDependencies": {
|
||||
"express@4.18.2": "patches/express@4.18.2.patch"
|
||||
}
|
||||
"packageManager": "pnpm@10.0.0",
|
||||
"devEngines": {
|
||||
"packageManager": { "name": "pnpm", "version": ">=11.0.0 <12.0.0", "onFail": "download" },
|
||||
"runtime": { "name": "node", "version": "22.x", "onFail": "download" }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Key Differences from npm/yarn
|
||||
`devEngines.packageManager` supports ranges (resolved version stored in lockfile); `packageManager` requires an exact version. Override `onFail` without editing the manifest via `pmOnFail` / `runtimeOnFail` settings.
|
||||
|
||||
1. **Strict by default**: No phantom dependencies
|
||||
2. **Workspace protocol**: `workspace:*` for local packages
|
||||
3. **Catalogs**: Centralized version management
|
||||
4. **Content-addressable store**: Shared across projects
|
||||
## Key Points
|
||||
|
||||
- All pnpm settings go in `pnpm-workspace.yaml` (camelCase) or global `config.yaml`; `.npmrc` is auth/registry only.
|
||||
- `package.json#pnpm` and `npm_config_*` env vars are no longer read.
|
||||
- Use `packageConfigs` for per-package settings inside a workspace.
|
||||
- Build-script approval is now one `allowBuilds` map; package-manager strictness is one `pmOnFail` setting.
|
||||
- `pnpm config get`/`list` output JSON, and `--location=project` writes to `pnpm-workspace.yaml`.
|
||||
|
||||
<!--
|
||||
Source references:
|
||||
- https://pnpm.io/pnpm-workspace_yaml
|
||||
- https://pnpm.io/settings
|
||||
- https://pnpm.io/configuring
|
||||
- https://pnpm.io/npmrc
|
||||
- https://pnpm.io/pnpm-workspace_yaml
|
||||
- https://pnpm.io/package_json
|
||||
- https://pnpm.io/cli/config
|
||||
-->
|
||||
|
||||
@@ -16,10 +16,9 @@ pnpm uses a content-addressable store to save disk space and speed up installati
|
||||
### Storage Layout
|
||||
|
||||
```
|
||||
~/.pnpm-store/ # Global store (default location)
|
||||
└── v3/
|
||||
└── files/
|
||||
└── <hash>/ # Files stored by content hash
|
||||
<store-dir>/ # Global content-addressable store (pnpm store path)
|
||||
└── files/
|
||||
└── <hash>/ # Files stored by content hash
|
||||
|
||||
project/
|
||||
└── node_modules/
|
||||
@@ -53,26 +52,24 @@ pnpm store add <pkg>
|
||||
|
||||
## Configuration
|
||||
|
||||
Store/linker settings live in `pnpm-workspace.yaml` (camelCase), not `.npmrc`.
|
||||
|
||||
### Store Location
|
||||
|
||||
```ini
|
||||
# .npmrc
|
||||
store-dir=~/.pnpm-store
|
||||
|
||||
# Or use environment variable
|
||||
PNPM_HOME=~/.local/share/pnpm
|
||||
```yaml title="pnpm-workspace.yaml"
|
||||
storeDir: ~/.local/share/pnpm/store
|
||||
```
|
||||
|
||||
The default store path is OS-specific (e.g. `~/.local/share/pnpm/store` on Linux, `~/Library/pnpm/store` on macOS). Find it with `pnpm store path`.
|
||||
|
||||
### Virtual Store
|
||||
|
||||
The virtual store (`.pnpm` in `node_modules`) contains symlinks to the global store:
|
||||
The virtual store (`.pnpm` in `node_modules`) contains hard links to the global store:
|
||||
|
||||
```ini
|
||||
# Customize virtual store location
|
||||
virtual-store-dir=node_modules/.pnpm
|
||||
|
||||
# Alternative flat layout
|
||||
node-linker=hoisted
|
||||
```yaml title="pnpm-workspace.yaml"
|
||||
virtualStoreDir: node_modules/.pnpm
|
||||
virtualStoreDirMaxLength: 60 # lower this for long-path issues on Windows
|
||||
nodeLinker: hoisted # alternative flat layout
|
||||
```
|
||||
|
||||
## Disk Space Benefits
|
||||
@@ -91,19 +88,22 @@ du -sh node_modules # Apparent size
|
||||
du -sh --apparent-size node_modules # With hard links counted
|
||||
```
|
||||
|
||||
## Global Virtual Store
|
||||
|
||||
With `enableGlobalVirtualStore: true`, projects skip the per-project `node_modules/.pnpm` directory entirely; their `node_modules` contains only symlinks into one shared virtual store at `<store-path>/links/`, keyed by dependency-graph hash. In pnpm v11 it is the default for `pnpm dlx`/`pnx` and global installs; for project installs it is still opt-in. See `features-global-virtual-store` for details and the git-worktrees multi-agent workflow.
|
||||
|
||||
```yaml title="pnpm-workspace.yaml"
|
||||
enableGlobalVirtualStore: true
|
||||
```
|
||||
|
||||
## Node Linker Modes
|
||||
|
||||
Configure how `node_modules` is structured:
|
||||
Configure how `node_modules` is structured (`nodeLinker` in `pnpm-workspace.yaml`):
|
||||
|
||||
```ini
|
||||
# Default: Symlinked structure (recommended)
|
||||
node-linker=isolated
|
||||
|
||||
# Flat node_modules (npm-like, for compatibility)
|
||||
node-linker=hoisted
|
||||
|
||||
# PnP mode (experimental, like Yarn PnP)
|
||||
node-linker=pnp
|
||||
```yaml title="pnpm-workspace.yaml"
|
||||
nodeLinker: isolated # default: symlinked virtual store (strict, no phantom deps)
|
||||
# nodeLinker: hoisted # flat node_modules (npm-like) for tools that dislike symlinks
|
||||
# nodeLinker: pnp # Plug'n'Play, no node_modules (set `symlink: false` too)
|
||||
```
|
||||
|
||||
### Isolated Mode (Default)
|
||||
@@ -120,14 +120,19 @@ node-linker=pnp
|
||||
|
||||
## Side Effects Cache
|
||||
|
||||
Cache build outputs for native modules:
|
||||
Cache build outputs for native modules (enabled by default):
|
||||
|
||||
```ini
|
||||
# Enable side effects caching
|
||||
side-effects-cache=true
|
||||
```yaml title="pnpm-workspace.yaml"
|
||||
sideEffectsCache: true
|
||||
sideEffectsCacheReadonly: false # only read the cache, don't create it
|
||||
```
|
||||
|
||||
# Store side effects in project (instead of global store)
|
||||
side-effects-cache-readonly=true
|
||||
## Read-only / Frozen Store
|
||||
|
||||
`frozenStore: true` (v11.7+) lets `pnpm install` run against a read-only store (Nix store, read-only bind mount, OCI layer). Pair with `--offline --frozen-lockfile`; the store must already contain everything, including approved build outputs.
|
||||
|
||||
```bash
|
||||
pnpm install --frozen-store --offline --frozen-lockfile
|
||||
```
|
||||
|
||||
## Shared Store Across Machines
|
||||
@@ -160,20 +165,21 @@ pnpm store prune
|
||||
```
|
||||
|
||||
### Hard link issues (network drives, Docker)
|
||||
```ini
|
||||
# Use copying instead of hard links
|
||||
package-import-method=copy
|
||||
```yaml title="pnpm-workspace.yaml"
|
||||
# auto (default) tries clone -> hardlink -> copy
|
||||
packageImportMethod: copy
|
||||
```
|
||||
|
||||
### Permission issues
|
||||
```bash
|
||||
# Fix store permissions
|
||||
chmod -R u+w ~/.pnpm-store
|
||||
# Fix store permissions (find the path with `pnpm store path`)
|
||||
chmod -R u+w "$(pnpm store path)"
|
||||
```
|
||||
|
||||
<!--
|
||||
Source references:
|
||||
- https://pnpm.io/symlinked-node-modules-structure
|
||||
- https://pnpm.io/cli/store
|
||||
- https://pnpm.io/npmrc#store-dir
|
||||
- https://pnpm.io/settings#storedir
|
||||
- https://pnpm.io/global-virtual-store
|
||||
-->
|
||||
|
||||
@@ -124,23 +124,39 @@ pnpm --filter "./packages/**" exec rm -rf dist
|
||||
|
||||
## Workspace Settings
|
||||
|
||||
Configure in `.npmrc` or `pnpm-workspace.yaml`:
|
||||
Configure in `pnpm-workspace.yaml` using **camelCase** keys (these settings no longer belong in `.npmrc`):
|
||||
|
||||
```yaml title="pnpm-workspace.yaml"
|
||||
packages:
|
||||
- 'packages/*'
|
||||
|
||||
```ini
|
||||
# Link workspace packages automatically
|
||||
link-workspace-packages=true
|
||||
|
||||
linkWorkspacePackages: true
|
||||
# Prefer workspace packages over registry
|
||||
prefer-workspace-packages=true
|
||||
|
||||
# Single lockfile (recommended)
|
||||
shared-workspace-lockfile=true
|
||||
|
||||
# Workspace protocol handling
|
||||
save-workspace-protocol=rolling
|
||||
|
||||
preferWorkspacePackages: true
|
||||
# Single lockfile for the whole workspace (recommended)
|
||||
sharedWorkspaceLockfile: true
|
||||
# Workspace protocol handling on publish
|
||||
saveWorkspaceProtocol: rolling
|
||||
# Concurrent workspace scripts
|
||||
workspace-concurrency=4
|
||||
workspaceConcurrency: 4
|
||||
# Use root deps to resolve peers of all projects
|
||||
resolvePeersFromWorkspaceRoot: true
|
||||
# Scripts required in every project (else `pnpm -r run <name>` fails)
|
||||
requiredScripts:
|
||||
- build
|
||||
```
|
||||
|
||||
### Per-package configuration (packageConfigs)
|
||||
|
||||
There are no per-subproject `.npmrc` files. Set package-specific settings from the root file:
|
||||
|
||||
```yaml title="pnpm-workspace.yaml"
|
||||
packageConfigs:
|
||||
project-1:
|
||||
saveExact: true
|
||||
project-2:
|
||||
savePrefix: '~'
|
||||
```
|
||||
|
||||
## Publishing Workspaces
|
||||
@@ -171,10 +187,11 @@ pnpm publish -r --no-git-checks
|
||||
## Best Practices
|
||||
|
||||
1. **Use workspace protocol** for internal dependencies
|
||||
2. **Enable `link-workspace-packages`** for automatic linking
|
||||
2. **Enable `linkWorkspacePackages`** for automatic linking
|
||||
3. **Use shared lockfile** for consistency
|
||||
4. **Filter by dependencies** when building to ensure correct order
|
||||
5. **Use catalogs** for shared external dependency versions
|
||||
5. **Use catalogs** for shared external dependency versions (defined in this same file)
|
||||
6. **Keep all pnpm settings in `pnpm-workspace.yaml`** (camelCase), not `.npmrc`
|
||||
|
||||
## Example Project Structure
|
||||
|
||||
@@ -197,7 +214,7 @@ my-monorepo/
|
||||
└── package.json
|
||||
```
|
||||
|
||||
<!--
|
||||
<!--
|
||||
Source references:
|
||||
- https://pnpm.io/workspaces
|
||||
- https://pnpm.io/filtering
|
||||
|
||||
@@ -130,7 +130,7 @@ Force all transitive dependencies to use an alias:
|
||||
```yaml
|
||||
# pnpm-workspace.yaml
|
||||
overrides:
|
||||
'underscore': 'npm:lodash@^4.17.21'
|
||||
"underscore": "npm:lodash@^4.17.21"
|
||||
```
|
||||
|
||||
This replaces all `underscore` imports (including in dependencies) with lodash.
|
||||
@@ -148,6 +148,21 @@ Aliases work with any valid pnpm specifier:
|
||||
}
|
||||
```
|
||||
|
||||
## Registry Aliases (namedRegistries)
|
||||
|
||||
Distinct from package aliases: a `namedRegistries` prefix selects *which registry* a package is fetched from.
|
||||
|
||||
```yaml title="pnpm-workspace.yaml"
|
||||
namedRegistries:
|
||||
work: https://npm.work.example.com/
|
||||
```
|
||||
|
||||
```bash
|
||||
pnpm add work:@corp/lib@^2.0.0 # resolves @corp/lib against the work registry
|
||||
```
|
||||
|
||||
The built-in `gh:` alias points at GitHub Packages. Auth is reused from per-URL `.npmrc` entries.
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Clear naming**: Use descriptive alias names that indicate purpose
|
||||
@@ -156,13 +171,15 @@ Aliases work with any valid pnpm specifier:
|
||||
"lodash-modern": "npm:lodash@4"
|
||||
```
|
||||
|
||||
2. **Document aliases**: Add comments or documentation explaining why aliases exist
|
||||
2. **Document aliases**: explain why aliases exist
|
||||
|
||||
3. **Prefer overrides for global replacement**: If you want to replace a package everywhere, use overrides instead of aliases
|
||||
3. **Prefer overrides for global replacement**: to replace a package everywhere, use `overrides` (in `pnpm-workspace.yaml`) instead of aliases
|
||||
|
||||
4. **Test thoroughly**: Aliased packages may have subtle differences in behavior
|
||||
|
||||
<!--
|
||||
Source references:
|
||||
- https://pnpm.io/aliases
|
||||
- https://pnpm.io/settings#namedregistries
|
||||
-->
|
||||
|
||||
|
||||
@@ -37,6 +37,8 @@ Reference in `package.json` with `catalog:`:
|
||||
}
|
||||
```
|
||||
|
||||
`catalog:` is shorthand for `catalog:default`. The `catalog:` protocol is valid in `package.json` `dependencies`, `devDependencies`, `peerDependencies`, and `optionalDependencies`, plus in `overrides` inside `pnpm-workspace.yaml`. It also works on the CLI: `pnpm add react@catalog:` and `pnx shx@catalog:`.
|
||||
|
||||
## Named Catalogs
|
||||
|
||||
Create multiple catalogs for different scenarios:
|
||||
@@ -54,14 +56,14 @@ catalogs:
|
||||
react17:
|
||||
react: ^17.0.2
|
||||
react-dom: ^17.0.2
|
||||
|
||||
|
||||
react18:
|
||||
react: ^18.2.0
|
||||
react-dom: ^18.2.0
|
||||
|
||||
|
||||
testing:
|
||||
vitest: ^1.0.0
|
||||
'@testing-library/react': ^14.0.0
|
||||
"@testing-library/react": ^14.0.0
|
||||
```
|
||||
|
||||
Reference named catalogs:
|
||||
@@ -78,12 +80,33 @@ Reference named catalogs:
|
||||
}
|
||||
```
|
||||
|
||||
## Keeping overrides in sync with a catalog
|
||||
|
||||
Reference a catalog from `overrides` so the version lives in exactly one place:
|
||||
|
||||
```yaml title="pnpm-workspace.yaml"
|
||||
catalog:
|
||||
foo: ^1.0.0
|
||||
|
||||
overrides:
|
||||
foo: 'catalog:' # or catalog:<name>
|
||||
```
|
||||
|
||||
## Settings
|
||||
|
||||
```yaml title="pnpm-workspace.yaml"
|
||||
# How `pnpm add` interacts with the default catalog (v10.12+)
|
||||
catalogMode: manual # manual (default) | prefer | strict
|
||||
# strict: only catalog versions allowed; prefer: fall back if no match
|
||||
cleanupUnusedCatalogs: true # remove unused catalog entries on install (v10.15+)
|
||||
```
|
||||
|
||||
## Benefits
|
||||
|
||||
1. **Single source of truth**: Update version in one place
|
||||
2. **Consistency**: All packages use the same version
|
||||
3. **Easy upgrades**: Change version once, affects entire workspace
|
||||
4. **Type-safe**: TypeScript support in pnpm-workspace.yaml
|
||||
4. **Fewer merge conflicts**: package.json files stay untouched on upgrades
|
||||
|
||||
## Catalog vs Overrides
|
||||
|
||||
@@ -134,7 +157,11 @@ catalog:
|
||||
react-dom: ^18.2.0
|
||||
```
|
||||
|
||||
Then update package.json files to use `catalog:`.
|
||||
Then update package.json files to use `catalog:`. To migrate an existing workspace automatically:
|
||||
|
||||
```bash
|
||||
pnpx codemod pnpm/catalog
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
@@ -153,7 +180,7 @@ catalog:
|
||||
# "dependencies": { "@myorg/utils": "workspace:^" }
|
||||
```
|
||||
|
||||
<!--
|
||||
<!--
|
||||
Source references:
|
||||
- https://pnpm.io/catalogs
|
||||
-->
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
---
|
||||
name: pnpm-config-dependencies
|
||||
description: Share and centralize pnpm hooks, settings, patches, catalogs, and overrides across repos via config dependencies
|
||||
---
|
||||
|
||||
# pnpm Config Dependencies
|
||||
|
||||
Config dependencies are npm packages that pnpm installs **before** all regular dependencies, so they can supply hooks, settings, patches, catalogs, and overrides that are reused across many repositories. They let you keep one shared "pnpm config" package and consume it everywhere.
|
||||
|
||||
## Declaring config dependencies
|
||||
|
||||
They live in `pnpm-workspace.yaml`; their integrity is recorded in a dedicated env-lockfile document inside `pnpm-lock.yaml`.
|
||||
|
||||
```yaml title="pnpm-workspace.yaml"
|
||||
configDependencies:
|
||||
my-configs: "1.0.0"
|
||||
```
|
||||
|
||||
Add one with the `--config` flag:
|
||||
|
||||
```bash
|
||||
pnpm add --config my-configs
|
||||
pnpm add --config @myorg/pnpm-plugin-my-catalogs
|
||||
```
|
||||
|
||||
## Constraints
|
||||
|
||||
- **No regular `dependencies`.** They may declare `optionalDependencies`, but only one level deep.
|
||||
- **No lifecycle scripts** (`preinstall`, `postinstall`, …).
|
||||
- `optionalDependencies` (used for platform-specific binaries, esbuild-style) must use **exact** versions — ranges/tags are rejected, keeping installs reproducible.
|
||||
|
||||
## Auto-loaded plugins
|
||||
|
||||
A config dependency named `pnpm-plugin-*`, `@*/pnpm-plugin-*`, or `@pnpm/plugin-*` has its `pnpmfile.mjs` (or `.cjs`) loaded automatically from the package root.
|
||||
|
||||
## Use cases
|
||||
|
||||
### Import hook logic from a shared package
|
||||
|
||||
Because config deps install before the pnpmfile loads, you can import from them:
|
||||
|
||||
```js title=".pnpmfile.mjs"
|
||||
import { readPackage } from '.pnpm-config/my-hooks'
|
||||
|
||||
export const hooks = { readPackage }
|
||||
```
|
||||
|
||||
### Share settings & catalogs via updateConfig
|
||||
|
||||
A plugin can inject settings/catalog entries through the `updateConfig` hook:
|
||||
|
||||
```js title="@myorg/pnpm-plugin-my-catalogs/pnpmfile.mjs"
|
||||
export const hooks = {
|
||||
updateConfig(config) {
|
||||
config.catalogs.default ??= {}
|
||||
config.catalogs.default['is-odd'] = '1.0.0'
|
||||
return config
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
After installing it as a config dependency, consumers can use the catalog:
|
||||
|
||||
```bash
|
||||
pnpm add is-odd@catalog: # installs is-odd@1.0.0, writes "is-odd": "catalog:"
|
||||
```
|
||||
|
||||
### Share patch files
|
||||
|
||||
Reference patches stored inside a config dependency:
|
||||
|
||||
```yaml title="pnpm-workspace.yaml"
|
||||
configDependencies:
|
||||
my-patches: "1.0.0"
|
||||
patchedDependencies:
|
||||
react: "node_modules/.pnpm-config/my-patches/react.patch"
|
||||
```
|
||||
|
||||
## Key Points
|
||||
|
||||
- Centralize hooks, settings, catalogs, overrides, and patches in one package, consumed across repos.
|
||||
- Declared via `configDependencies` in `pnpm-workspace.yaml`; installed before regular deps.
|
||||
- No regular dependencies and no lifecycle scripts; `optionalDependencies` need exact versions.
|
||||
- `pnpm-plugin-*` / `@pnpm/plugin-*` packages auto-load their pnpmfile.
|
||||
- Pair with the `updateConfig` hook to push settings/catalogs into consuming projects.
|
||||
|
||||
<!--
|
||||
Source references:
|
||||
- https://pnpm.io/config-dependencies
|
||||
- https://pnpm.io/pnpmfile#hooksupdateconfigconfig-config--promiseconfig
|
||||
-->
|
||||
@@ -0,0 +1,94 @@
|
||||
---
|
||||
name: pnpm-global-virtual-store
|
||||
description: Global virtual store for shared node_modules across checkouts, git-worktree multi-agent setups, and isolated global packages
|
||||
---
|
||||
|
||||
# Global Virtual Store, Git Worktrees & Global Packages
|
||||
|
||||
## Global virtual store
|
||||
|
||||
By default each project has its own `node_modules/.pnpm` virtual store containing hard links to the content-addressable store. With the **global virtual store** enabled, pnpm keeps one shared virtual store at `<store-path>/links/` (find it via `pnpm store path`), and each project's `node_modules` contains only **symlinks** into it.
|
||||
|
||||
```yaml title="pnpm-workspace.yaml"
|
||||
enableGlobalVirtualStore: true
|
||||
```
|
||||
|
||||
```
|
||||
# Default (per-project .pnpm with hard links)
|
||||
project-a/node_modules/lodash -> .pnpm/lodash@4.17.21/node_modules/lodash
|
||||
|
||||
# Global virtual store (symlink to shared location)
|
||||
project-a/node_modules/lodash -> <store>/links/@/lodash/4.17.21/<hash>/node_modules/lodash
|
||||
project-b/node_modules/lodash -> <store>/links/@/lodash/4.17.21/<hash>/node_modules/lodash # same target
|
||||
```
|
||||
|
||||
- **Package identity = hash of the dependency graph.** Two projects with the same `lodash@4.17.21` and the same transitive tree point at the exact same directory (NixOS-style). Different peers ⇒ separate entries.
|
||||
- **Near-zero per-project cost** and **instant installs** once a version is in the store.
|
||||
- In **pnpm v11** it is the default for `pnpm dlx`/`pnx` and global installs; for **project** installs it is still **opt-in/experimental**.
|
||||
|
||||
### Limitations
|
||||
|
||||
- **CI:** auto-disabled (no warm cache to benefit from).
|
||||
- **Trust:** the store is shared writable state — only for mutually trusting projects/users/jobs; protect the path with filesystem permissions.
|
||||
- **ESM hoisting:** relies on `NODE_PATH`, which Node ignores for ESM imports. If ESM deps import undeclared packages, resolution fails. Fix with `packageExtensions` or the `@pnpm/plugin-esm-node-path` config dependency.
|
||||
|
||||
## Git worktrees for multi-agent development
|
||||
|
||||
Git worktrees let you check out many branches simultaneously, each in its own directory, sharing one `.git` object store. Combined with the global virtual store, every worktree gets a fully functional `node_modules` that is almost free on disk — ideal for running multiple AI agents in parallel.
|
||||
|
||||
```sh
|
||||
# Bare repo as the hub, one worktree per branch/agent
|
||||
git clone --bare https://github.com/your-org/your-monorepo.git your-monorepo
|
||||
cd your-monorepo
|
||||
git worktree add ./main main
|
||||
git worktree add ./feature-auth feat/auth
|
||||
git worktree add ./fix-api fix/api-error
|
||||
```
|
||||
|
||||
```yaml title="pnpm-workspace.yaml"
|
||||
packages:
|
||||
- 'packages/*'
|
||||
enableGlobalVirtualStore: true
|
||||
```
|
||||
|
||||
```sh
|
||||
cd main && pnpm install # first install fills the global store
|
||||
cd ../feature-auth && pnpm install # subsequent worktrees: nearly instant, just symlinks
|
||||
```
|
||||
|
||||
Each worktree has its own `node_modules` tree (so agents can install different versions on different branches without conflict), but all package contents come from the one shared store. Remove a worktree with `git worktree remove ./feature-auth`.
|
||||
|
||||
> The pnpm repo itself uses this setup and ships helper scripts (`pnpm worktree:new <branch|pr>`). Assumes all worktrees/agents share the same trust boundary.
|
||||
|
||||
## Global packages (v11 isolated installs)
|
||||
|
||||
`pnpm add -g` was redesigned in v11 for isolation. Each globally installed package (or group) gets its own install directory with its own `package.json`, `node_modules/`, and lockfile, so global tools can't break each other via peer/hoisting conflicts. Installs are stored at `{pnpmHomeDir}/global/v11/{hash}/` and share the global virtual store.
|
||||
|
||||
```sh
|
||||
pnpm add -g typescript prettier # space-separated = separate isolated installs each
|
||||
pnpm add -g eslint,prettier # comma-separated = ONE shared install group
|
||||
pnpm remove -g eslint # removes only eslint's group
|
||||
pnpm add -g --allow-build=esbuild esbuild # pre-approve build scripts
|
||||
pnpm list -g # always works at depth 0
|
||||
pnpm bin -g # global bin dir = $PNPM_HOME/bin
|
||||
```
|
||||
|
||||
- `pnpm install -g` (no args) is **not** supported — use `pnpm add -g <pkg>`.
|
||||
- Binaries live in `$PNPM_HOME/bin` (not `$PNPM_HOME` directly). Run `pnpm setup` after upgrading to put it on PATH.
|
||||
- Register a local package's bins globally with `pnpm add -g .` (replaces `pnpm link --global`).
|
||||
- `pnpm list -g --depth=<n>` (n>0) only works for a single install group.
|
||||
|
||||
## Key Points
|
||||
|
||||
- `enableGlobalVirtualStore: true` ⇒ `node_modules` is symlinks into one shared, hash-addressed store.
|
||||
- Best for many checkouts of the same repo (git worktrees, parallel agents); auto-disabled in CI.
|
||||
- Watch out for ESM packages importing undeclared deps (NODE_PATH limitation).
|
||||
- v11 global installs are isolated per package; comma-list to share a group; bins live in `$PNPM_HOME/bin`.
|
||||
|
||||
<!--
|
||||
Source references:
|
||||
- https://pnpm.io/global-virtual-store
|
||||
- https://pnpm.io/git-worktrees
|
||||
- https://pnpm.io/global-packages
|
||||
- https://pnpm.io/settings#enableglobalvirtualstore
|
||||
-->
|
||||
@@ -1,233 +1,180 @@
|
||||
---
|
||||
name: pnpm-hooks
|
||||
description: Customize package resolution and dependency behavior with pnpmfile hooks
|
||||
description: Customize resolution, config, packing, and fetching with .pnpmfile.mjs hooks, finders, and custom resolvers/fetchers
|
||||
---
|
||||
|
||||
# pnpm Hooks
|
||||
# pnpm Hooks (.pnpmfile.mjs)
|
||||
|
||||
pnpm provides hooks via `.pnpmfile.cjs` to customize how packages are resolved and their metadata is processed.
|
||||
pnpm hooks customize installation. Declare them in `.pnpmfile.mjs` (ESM, preferred) or `.pnpmfile.cjs` (CommonJS), located next to the lockfile (workspace root for a monorepo).
|
||||
|
||||
> The modern format uses ESM `export const hooks = { ... }`. The old CommonJS `module.exports = { hooks }` still works in `.pnpmfile.cjs`.
|
||||
|
||||
## 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
|
||||
}
|
||||
```js title=".pnpmfile.mjs"
|
||||
export const hooks = {
|
||||
readPackage,
|
||||
afterAllResolved,
|
||||
updateConfig,
|
||||
beforePacking,
|
||||
}
|
||||
```
|
||||
|
||||
## readPackage Hook
|
||||
## Hook reference
|
||||
|
||||
Called for every package before resolution. Use to modify dependencies, add missing peer deps, or fix broken packages.
|
||||
| Hook | When | Use |
|
||||
|------|------|-----|
|
||||
| `readPackage(pkg, ctx)` | after a dependency manifest is parsed | mutate a dependency's `package.json` (affects resolution) |
|
||||
| `afterAllResolved(lockfile, ctx)` | after resolution | mutate the lockfile before it's written |
|
||||
| `updateConfig(config)` | before install | mutate pnpm's settings (great with config dependencies) |
|
||||
| `beforePacking(pkg)` | before `pnpm pack`/`publish` tarball | customize the **published** manifest only |
|
||||
| `preResolution(opts)` | after reading lockfiles, before resolution | inspect/modify lockfile objects |
|
||||
| `importPackage(dir, opts)` | when writing to node_modules | change how packages are linked |
|
||||
|
||||
### Add Missing Peer Dependency
|
||||
## readPackage
|
||||
|
||||
```js
|
||||
Called for every package before resolution. Common uses:
|
||||
|
||||
```js title=".pnpmfile.mjs"
|
||||
function readPackage(pkg, context) {
|
||||
// Add a missing peer dependency
|
||||
if (pkg.name === 'some-broken-package') {
|
||||
pkg.peerDependencies = {
|
||||
...pkg.peerDependencies,
|
||||
react: '*'
|
||||
}
|
||||
context.log(`Added react peer dep to ${pkg.name}`)
|
||||
pkg.peerDependencies = { ...pkg.peerDependencies, react: '*' }
|
||||
}
|
||||
// Pin a transitive version
|
||||
if (pkg.dependencies?.lodash) pkg.dependencies.lodash = '^4.17.21'
|
||||
// Drop a problematic optional dep
|
||||
delete pkg.optionalDependencies?.fsevents
|
||||
// Replace a deprecated dep
|
||||
if (pkg.dependencies?.['old-pkg']) {
|
||||
pkg.dependencies['new-pkg'] = pkg.dependencies['old-pkg']
|
||||
delete pkg.dependencies['old-pkg']
|
||||
}
|
||||
return pkg
|
||||
}
|
||||
|
||||
export const hooks = { readPackage }
|
||||
```
|
||||
|
||||
### Override Dependency Version
|
||||
> Mutations are not written to disk; they only affect resolution. Delete `pnpm-lock.yaml` to re-resolve an already-locked dependency. Removing `scripts` here does **not** stop a build — use the `allowBuilds` setting instead. To persist a change to a dependency's files, use `pnpm patch`.
|
||||
|
||||
```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
|
||||
}
|
||||
```
|
||||
## updateConfig
|
||||
|
||||
### Remove Unwanted Dependency
|
||||
Modify pnpm's own settings programmatically — most powerful when shipped in a config dependency so settings are shared across repos.
|
||||
|
||||
```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
|
||||
```js title=".pnpmfile.mjs"
|
||||
export const hooks = {
|
||||
updateConfig(config) {
|
||||
return Object.assign(config, {
|
||||
enablePrePostScripts: false,
|
||||
optimisticRepeatInstall: true,
|
||||
resolutionMode: 'lowest-direct',
|
||||
verifyDepsBeforeRun: 'install',
|
||||
})
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 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
|
||||
// Add a catalog entry from a plugin
|
||||
export const hooks = {
|
||||
updateConfig(config) {
|
||||
config.catalogs.default ??= {}
|
||||
config.catalogs.default['is-odd'] = '1.0.0'
|
||||
return config
|
||||
}
|
||||
return pkg
|
||||
}
|
||||
```
|
||||
|
||||
### Apply to All Packages
|
||||
## beforePacking
|
||||
|
||||
```js
|
||||
function readPackage(pkg, context) {
|
||||
// Remove all optional fsevents
|
||||
if (pkg.optionalDependencies) {
|
||||
delete pkg.optionalDependencies.fsevents
|
||||
Customize the manifest that ends up in the published tarball without touching your local `package.json`.
|
||||
|
||||
```js title=".pnpmfile.mjs"
|
||||
export const hooks = {
|
||||
beforePacking(pkg) {
|
||||
delete pkg.devDependencies
|
||||
pkg.main = './dist/index.js'
|
||||
return pkg
|
||||
}
|
||||
return pkg
|
||||
}
|
||||
```
|
||||
|
||||
### Debug Resolution
|
||||
## afterAllResolved
|
||||
|
||||
```js
|
||||
function readPackage(pkg, context) {
|
||||
if (process.env.DEBUG_PNPM) {
|
||||
context.log(`${pkg.name}@${pkg.version}`)
|
||||
context.log(` deps: ${Object.keys(pkg.dependencies || {}).join(', ')}`)
|
||||
```js title=".pnpmfile.mjs"
|
||||
export const hooks = {
|
||||
afterAllResolved(lockfile, context) {
|
||||
context.log(`Resolved ${Object.keys(lockfile.packages || {}).length} packages`)
|
||||
return lockfile
|
||||
}
|
||||
return pkg
|
||||
}
|
||||
```
|
||||
|
||||
## Finders (pnpm list / why)
|
||||
|
||||
Custom predicates used via `--find-by`:
|
||||
|
||||
```js title=".pnpmfile.mjs"
|
||||
export const finders = {
|
||||
react17: (ctx) => ctx.readManifest().peerDependencies?.react === '^17.0.0'
|
||||
}
|
||||
```
|
||||
|
||||
```bash
|
||||
pnpm why --find-by=react17
|
||||
```
|
||||
|
||||
## Custom resolvers & fetchers (advanced)
|
||||
|
||||
Register top-level `resolvers`/`fetchers` to support new package schemes (e.g. `my-protocol:pkg`). Each is an object with cheap `canResolve`/`canFetch` guards plus `resolve`/`fetch`. Custom resolvers run before built-ins; custom resolution `type` fields must use the `custom:` prefix.
|
||||
|
||||
```js title=".pnpmfile.cjs"
|
||||
const resolver = {
|
||||
canResolve: (dep) => dep.alias.startsWith('@company/'),
|
||||
resolve: async (dep) => ({
|
||||
id: `${dep.alias}@${dep.bareSpecifier}`,
|
||||
resolution: { type: 'custom:cdn', cdnUrl: '...' },
|
||||
}),
|
||||
}
|
||||
const fetcher = {
|
||||
canFetch: (id, res) => res.type === 'custom:cdn',
|
||||
fetch: (cafs, res, opts, fetchers) =>
|
||||
fetchers.remoteTarball(cafs, { tarball: res.cdnUrl, integrity: res.integrity }, opts),
|
||||
}
|
||||
module.exports = { resolvers: [resolver], fetchers: [fetcher] }
|
||||
```
|
||||
|
||||
> `hooks.fetchers` was removed in v11 — use the top-level `fetchers` export instead.
|
||||
|
||||
## Related settings
|
||||
|
||||
```yaml title="pnpm-workspace.yaml"
|
||||
ignorePnpmfile: false # ignore the pnpmfile entirely
|
||||
pnpmfile: ['.pnpmfile.mjs'] # local pnpmfile location(s)
|
||||
globalPnpmfile: ~/.pnpm/global_pnpmfile.mjs
|
||||
```
|
||||
|
||||
## 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 |
|
||||
| | Hooks (.pnpmfile) | Overrides (pnpm-workspace.yaml) |
|
||||
|--|-------------------|---------------------------------|
|
||||
| Logic | JavaScript | declarative |
|
||||
| Scope | any manifest field, config, lockfile, packing | versions |
|
||||
| Use when | conditional/complex fixes | simple version pins |
|
||||
|
||||
**Prefer overrides** for simple version fixes. **Use hooks** when you need:
|
||||
- Conditional logic
|
||||
- Non-version modifications (exports, peer deps)
|
||||
- Logging/debugging
|
||||
Prefer `overrides`/`packageExtensions` for simple cases; use hooks for conditional logic, config sharing, or packing tweaks.
|
||||
|
||||
## Troubleshooting
|
||||
## Key Points
|
||||
|
||||
### 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
|
||||
```
|
||||
- Prefer `.pnpmfile.mjs` with `export const hooks`/`finders`/`resolvers`/`fetchers`.
|
||||
- New hooks: `updateConfig` (mutate settings), `beforePacking` (published manifest), `preResolution`, `importPackage`.
|
||||
- Pair `updateConfig` with config dependencies to share settings/catalogs across repos.
|
||||
- `--ignore-scripts` does **not** disable the pnpmfile; use `ignorePnpmfile`.
|
||||
|
||||
<!--
|
||||
Source references:
|
||||
- https://pnpm.io/pnpmfile
|
||||
- https://pnpm.io/finders
|
||||
- https://pnpm.io/config-dependencies
|
||||
-->
|
||||
|
||||
@@ -9,11 +9,11 @@ Overrides let you force specific versions of packages, including transitive depe
|
||||
|
||||
## Basic Syntax
|
||||
|
||||
Define overrides in `pnpm-workspace.yaml` (recommended) or `package.json`:
|
||||
Define overrides in `pnpm-workspace.yaml`. They can only be set at the **root** of the project.
|
||||
|
||||
### In pnpm-workspace.yaml (Recommended)
|
||||
> The `pnpm.overrides` field in `package.json` is **no longer read** (pnpm no longer reads any settings from `package.json#pnpm`). Move overrides to `pnpm-workspace.yaml`.
|
||||
|
||||
```yaml
|
||||
```yaml title="pnpm-workspace.yaml"
|
||||
packages:
|
||||
- 'packages/*'
|
||||
|
||||
@@ -22,27 +22,16 @@ overrides:
|
||||
lodash: ^4.17.21
|
||||
|
||||
# Override specific version range
|
||||
'foo@^1.0.0': ^1.2.3
|
||||
"foo@^1.0.0": ^1.2.3
|
||||
|
||||
# Override nested dependency
|
||||
'express>cookie': ^0.6.0
|
||||
# Override nested dependency (only zoo inside qar@1)
|
||||
"qar@1>zoo": "2"
|
||||
|
||||
# Override to different package
|
||||
'underscore': 'npm:lodash@^4.17.21'
|
||||
```
|
||||
"underscore": "npm:lodash@^4.17.21"
|
||||
|
||||
### In package.json
|
||||
|
||||
```json
|
||||
{
|
||||
"pnpm": {
|
||||
"overrides": {
|
||||
"lodash": "^4.17.21",
|
||||
"foo@^1.0.0": "^1.2.3",
|
||||
"bar@^2.0.0>qux": "^1.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
# Reference a catalog so the version stays in sync
|
||||
"react": "catalog:"
|
||||
```
|
||||
|
||||
## Override Patterns
|
||||
@@ -57,15 +46,15 @@ Forces all lodash installations to use ^4.17.21.
|
||||
### Override specific parent version
|
||||
```yaml
|
||||
overrides:
|
||||
'foo@^1.0.0': ^1.2.3
|
||||
"foo@^1.0.0": ^1.2.3
|
||||
```
|
||||
Only override foo when the requested version matches ^1.0.0.
|
||||
|
||||
### Override nested dependency
|
||||
```yaml
|
||||
overrides:
|
||||
'express>cookie': ^0.6.0
|
||||
'foo@1.x>bar@^2.0.0>qux': ^1.0.0
|
||||
"express>cookie": ^0.6.0
|
||||
"foo@1.x>bar@^2.0.0>qux": ^1.0.0
|
||||
```
|
||||
Override cookie only when it's a dependency of express.
|
||||
|
||||
@@ -74,10 +63,10 @@ Override cookie only when it's a dependency of express.
|
||||
overrides:
|
||||
# Replace underscore with lodash
|
||||
"underscore": "npm:lodash@^4.17.21"
|
||||
|
||||
|
||||
# Use local file
|
||||
"some-pkg": "file:./local-pkg"
|
||||
|
||||
|
||||
# Use git
|
||||
"some-pkg": "github:user/repo#commit"
|
||||
```
|
||||
@@ -85,10 +74,24 @@ overrides:
|
||||
### Remove a dependency
|
||||
```yaml
|
||||
overrides:
|
||||
'unwanted-pkg': '-'
|
||||
"unwanted-pkg": "-"
|
||||
"foo@1.0.0>bar": "-" # great for skipping unused optionalDependencies
|
||||
```
|
||||
The `-` removes the package entirely.
|
||||
|
||||
### Override peer dependencies
|
||||
|
||||
Overrides also apply to `peerDependencies`:
|
||||
|
||||
```yaml title="pnpm-workspace.yaml"
|
||||
overrides:
|
||||
"react-dom>react": "18.1.0"
|
||||
```
|
||||
|
||||
- Semver ranges, `workspace:`, and `catalog:` keep the entry as a peer dependency.
|
||||
- Non-range specifiers (`link:`, `file:`) move it into `dependencies`.
|
||||
- `-` removes the peer dependency entirely.
|
||||
|
||||
## Common Use Cases
|
||||
|
||||
### Security Fix
|
||||
@@ -98,8 +101,8 @@ Force patched version of vulnerable package:
|
||||
```yaml
|
||||
overrides:
|
||||
# Fix CVE in transitive dependency
|
||||
'minimist': '^1.2.6'
|
||||
'json5': '^2.2.3'
|
||||
"minimist": "^1.2.6"
|
||||
"json5": "^2.2.3"
|
||||
```
|
||||
|
||||
### Deduplicate Dependencies
|
||||
@@ -108,30 +111,29 @@ Force single version when multiple are installed:
|
||||
|
||||
```yaml
|
||||
overrides:
|
||||
'react': '^18.2.0'
|
||||
'react-dom': '^18.2.0'
|
||||
"react": "^18.2.0"
|
||||
"react-dom": "^18.2.0"
|
||||
```
|
||||
|
||||
### Fix Peer Dependency Issues
|
||||
|
||||
```yaml
|
||||
overrides:
|
||||
'@types/react': '^18.2.0'
|
||||
"@types/react": "^18.2.0"
|
||||
```
|
||||
|
||||
### Replace Deprecated Package
|
||||
|
||||
```yaml
|
||||
overrides:
|
||||
'request': 'npm:@cypress/request@^3.0.0'
|
||||
"request": "npm:@cypress/request@^3.0.0"
|
||||
```
|
||||
|
||||
## Hooks Alternative
|
||||
|
||||
For more complex scenarios, use `.pnpmfile.cjs`:
|
||||
For more complex scenarios, use `.pnpmfile.mjs`:
|
||||
|
||||
```js
|
||||
// .pnpmfile.cjs
|
||||
```js title=".pnpmfile.mjs"
|
||||
function readPackage(pkg, context) {
|
||||
// Override dependency version
|
||||
if (pkg.dependencies?.lodash) {
|
||||
@@ -149,13 +151,20 @@ function readPackage(pkg, context) {
|
||||
return pkg
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
hooks: {
|
||||
readPackage
|
||||
}
|
||||
export const hooks = {
|
||||
readPackage
|
||||
}
|
||||
```
|
||||
|
||||
Or extend a manifest declaratively with `packageExtensions` (no JS needed):
|
||||
|
||||
```yaml title="pnpm-workspace.yaml"
|
||||
packageExtensions:
|
||||
react-redux:
|
||||
peerDependencies:
|
||||
react-dom: '*'
|
||||
```
|
||||
|
||||
## Overrides vs Catalogs
|
||||
|
||||
| Feature | Overrides | Catalogs |
|
||||
@@ -179,6 +188,7 @@ pnpm list lodash --depth=Infinity
|
||||
|
||||
<!--
|
||||
Source references:
|
||||
- https://pnpm.io/package_json#pnpmoverrides
|
||||
- https://pnpm.io/settings#overrides
|
||||
- https://pnpm.io/settings#packageextensions
|
||||
- https://pnpm.io/pnpmfile
|
||||
-->
|
||||
|
||||
@@ -42,23 +42,20 @@ pnpm patch-commit <path-from-step-1>
|
||||
pnpm patch-commit /tmp/abc123...
|
||||
```
|
||||
|
||||
This creates a `.patch` file in `patches/` and updates `package.json`:
|
||||
This creates a `.patch` file in `patches/` and records it in `pnpm-workspace.yaml`:
|
||||
|
||||
```
|
||||
patches/
|
||||
└── express@4.18.2.patch
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"pnpm": {
|
||||
"patchedDependencies": {
|
||||
"express@4.18.2": "patches/express@4.18.2.patch"
|
||||
}
|
||||
}
|
||||
}
|
||||
```yaml title="pnpm-workspace.yaml"
|
||||
patchedDependencies:
|
||||
express@4.18.2: patches/express@4.18.2.patch
|
||||
```
|
||||
|
||||
> `patchedDependencies` (like all pnpm settings) now lives in `pnpm-workspace.yaml`, not the `package.json#pnpm` field.
|
||||
|
||||
## Patch File Format
|
||||
|
||||
Patches use standard unified diff format:
|
||||
@@ -100,59 +97,48 @@ pnpm patch-commit <path>
|
||||
```bash
|
||||
pnpm patch-remove <pkg>@<version>
|
||||
|
||||
# Example
|
||||
# Example
|
||||
pnpm patch-remove express@4.18.2
|
||||
```
|
||||
|
||||
Or manually:
|
||||
1. Delete the patch file from `patches/`
|
||||
2. Remove entry from `patchedDependencies` in `package.json`
|
||||
2. Remove the entry from `patchedDependencies` in `pnpm-workspace.yaml`
|
||||
3. Run `pnpm install`
|
||||
|
||||
## Patch Configuration
|
||||
|
||||
### Custom Patches Directory
|
||||
### Multiple Packages / Workspaces
|
||||
|
||||
```json
|
||||
{
|
||||
"pnpm": {
|
||||
"patchedDependencies": {
|
||||
"express@4.18.2": "custom-patches/my-express-fix.patch"
|
||||
}
|
||||
}
|
||||
}
|
||||
Patches are shared across the whole workspace from the root `pnpm-workspace.yaml`:
|
||||
|
||||
```yaml title="pnpm-workspace.yaml"
|
||||
patchedDependencies:
|
||||
express@4.18.2: patches/express@4.18.2.patch
|
||||
lodash@4.17.21: patches/lodash@4.17.21.patch
|
||||
'@types/node@20.10.0': patches/@types__node@20.10.0.patch
|
||||
```
|
||||
|
||||
### Multiple Packages
|
||||
A version-less key (`express:`) patches every installed version. All workspace packages using a matching version get the patch.
|
||||
|
||||
```json
|
||||
{
|
||||
"pnpm": {
|
||||
"patchedDependencies": {
|
||||
"express@4.18.2": "patches/express@4.18.2.patch",
|
||||
"lodash@4.17.21": "patches/lodash@4.17.21.patch",
|
||||
"@types/node@20.10.0": "patches/@types__node@20.10.0.patch"
|
||||
}
|
||||
}
|
||||
}
|
||||
### Patches from a config dependency
|
||||
|
||||
Patch files can live inside a shared config dependency and be referenced by path:
|
||||
|
||||
```yaml title="pnpm-workspace.yaml"
|
||||
configDependencies:
|
||||
my-patches: '1.0.0'
|
||||
patchedDependencies:
|
||||
react: node_modules/.pnpm-config/my-patches/react.patch
|
||||
```
|
||||
|
||||
## Workspaces
|
||||
### allowUnusedPatches
|
||||
|
||||
Patches are shared across the workspace. Define in the root `package.json`:
|
||||
|
||||
```json
|
||||
// Root package.json
|
||||
{
|
||||
"pnpm": {
|
||||
"patchedDependencies": {
|
||||
"express@4.18.2": "patches/express@4.18.2.patch"
|
||||
}
|
||||
}
|
||||
}
|
||||
```yaml title="pnpm-workspace.yaml"
|
||||
allowUnusedPatches: true # don't fail when a listed patch wasn't applied
|
||||
```
|
||||
|
||||
All workspace packages using `express@4.18.2` will have the patch applied.
|
||||
> `ignorePatchFailures` was **removed** in v11. A patch that fails to apply now always throws. When several patches are grouped, all errors are reported together at the end.
|
||||
|
||||
## Best Practices
|
||||
|
||||
@@ -197,5 +183,5 @@ Ensure:
|
||||
Source references:
|
||||
- https://pnpm.io/cli/patch
|
||||
- https://pnpm.io/cli/patch-commit
|
||||
- https://pnpm.io/package_json#pnpmpatcheddependencies
|
||||
- https://pnpm.io/config-dependencies
|
||||
-->
|
||||
|
||||
@@ -7,127 +7,99 @@ description: Handling peer dependencies with auto-install and resolution rules
|
||||
|
||||
pnpm has strict peer dependency handling by default. It provides configuration options to control how peer dependencies are resolved and reported.
|
||||
|
||||
All peer-dependency settings live in `pnpm-workspace.yaml` (camelCase). The `package.json#pnpm` field is no longer read.
|
||||
|
||||
## Auto-Install Peer Dependencies
|
||||
|
||||
By default, pnpm automatically installs peer dependencies:
|
||||
By default (since v8), pnpm automatically installs missing non-optional peer dependencies:
|
||||
|
||||
```ini
|
||||
# .npmrc (default is true since pnpm v8)
|
||||
auto-install-peers=true
|
||||
```yaml title="pnpm-workspace.yaml"
|
||||
autoInstallPeers: true
|
||||
```
|
||||
|
||||
When enabled, pnpm automatically adds missing peer dependencies based on the best matching version.
|
||||
On conflicting requirements (e.g. one dep needs `react@^16`, another `react@^17`), pnpm installs nothing and prints a warning — resolve it manually.
|
||||
|
||||
## Strict Peer Dependencies
|
||||
|
||||
Control whether peer dependency issues cause errors:
|
||||
|
||||
```ini
|
||||
# Fail on peer dependency issues (default: false)
|
||||
strict-peer-dependencies=true
|
||||
```yaml title="pnpm-workspace.yaml"
|
||||
strictPeerDependencies: true # default false
|
||||
```
|
||||
|
||||
When strict, pnpm will fail if:
|
||||
- Peer dependency is missing
|
||||
- Installed version doesn't match required range
|
||||
When strict, commands fail on a missing or invalid peer dependency in the tree.
|
||||
|
||||
## Resolve from workspace root
|
||||
|
||||
```yaml title="pnpm-workspace.yaml"
|
||||
resolvePeersFromWorkspaceRoot: true # default; install shared peers once at the root
|
||||
```
|
||||
|
||||
## Deduplicate peers
|
||||
|
||||
```yaml title="pnpm-workspace.yaml"
|
||||
dedupePeerDependents: true # default; share package instances across projects when peers match
|
||||
dedupePeers: false # v10.33+: version-only peer suffixes (name@version), fewer instances
|
||||
```
|
||||
|
||||
## Peer Dependency Rules
|
||||
|
||||
Configure peer dependency behavior in `package.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"pnpm": {
|
||||
"peerDependencyRules": {
|
||||
"ignoreMissing": ["@babel/*", "eslint"],
|
||||
"allowedVersions": {
|
||||
"react": "17 || 18"
|
||||
},
|
||||
"allowAny": ["@types/*"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```yaml title="pnpm-workspace.yaml"
|
||||
peerDependencyRules:
|
||||
ignoreMissing:
|
||||
- '@babel/*'
|
||||
- eslint
|
||||
allowedVersions:
|
||||
react: '17 || 18'
|
||||
allowAny:
|
||||
- '@types/*'
|
||||
```
|
||||
|
||||
### ignoreMissing
|
||||
|
||||
Suppress warnings for missing peer dependencies:
|
||||
Suppress warnings for missing peer dependencies. Patterns: exact name (`react`), scope (`@babel/*`), or `*` (not recommended).
|
||||
|
||||
```json
|
||||
{
|
||||
"pnpm": {
|
||||
"peerDependencyRules": {
|
||||
"ignoreMissing": [
|
||||
"@babel/*",
|
||||
"eslint",
|
||||
"webpack"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```yaml title="pnpm-workspace.yaml"
|
||||
peerDependencyRules:
|
||||
ignoreMissing:
|
||||
- '@babel/*'
|
||||
- eslint
|
||||
- webpack
|
||||
```
|
||||
|
||||
Use patterns:
|
||||
- `"react"` - exact package name
|
||||
- `"@babel/*"` - all packages in scope
|
||||
- `"*"` - all packages (not recommended)
|
||||
|
||||
### allowedVersions
|
||||
|
||||
Allow specific versions that would otherwise cause warnings:
|
||||
Allow specific versions that would otherwise warn. Target a specific parent with `parent>peer`.
|
||||
|
||||
```json
|
||||
{
|
||||
"pnpm": {
|
||||
"peerDependencyRules": {
|
||||
"allowedVersions": {
|
||||
"react": "17 || 18",
|
||||
"webpack": "4 || 5",
|
||||
"@types/react": "*"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```yaml title="pnpm-workspace.yaml"
|
||||
peerDependencyRules:
|
||||
allowedVersions:
|
||||
react: '17'
|
||||
'button@2>react': '17' # only when react is a peer of button@2
|
||||
```
|
||||
|
||||
### allowAny
|
||||
|
||||
Allow any version for specified peer dependencies:
|
||||
Resolve matching peers from any version, ignoring the declared range.
|
||||
|
||||
```json
|
||||
{
|
||||
"pnpm": {
|
||||
"peerDependencyRules": {
|
||||
"allowAny": ["@types/*", "eslint"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```yaml title="pnpm-workspace.yaml"
|
||||
peerDependencyRules:
|
||||
allowAny:
|
||||
- '@types/*'
|
||||
- eslint
|
||||
```
|
||||
|
||||
## Adding Peer Dependencies via Hooks
|
||||
## Adding Peer Dependencies via packageExtensions
|
||||
|
||||
Use `.pnpmfile.cjs` to add missing peer dependencies:
|
||||
Declaratively add a missing peer dependency without JS:
|
||||
|
||||
```js
|
||||
// .pnpmfile.cjs
|
||||
function readPackage(pkg, context) {
|
||||
// Add missing peer dependency
|
||||
if (pkg.name === 'problematic-package') {
|
||||
pkg.peerDependencies = {
|
||||
...pkg.peerDependencies,
|
||||
```yaml title="pnpm-workspace.yaml"
|
||||
packageExtensions:
|
||||
problematic-package:
|
||||
peerDependencies:
|
||||
react: '*'
|
||||
}
|
||||
}
|
||||
return pkg
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
hooks: {
|
||||
readPackage
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
For conditional logic, use a `readPackage` hook in `.pnpmfile.mjs` instead.
|
||||
|
||||
## Peer Dependencies in Workspaces
|
||||
|
||||
Workspace packages can satisfy peer dependencies:
|
||||
@@ -141,7 +113,7 @@ Workspace packages can satisfy peer dependencies:
|
||||
}
|
||||
}
|
||||
|
||||
// packages/components/package.json
|
||||
// packages/components/package.json
|
||||
{
|
||||
"peerDependencies": {
|
||||
"react": "^17.0.0 || ^18.0.0"
|
||||
@@ -183,68 +155,47 @@ catalog:
|
||||
|
||||
### Suppress ESLint Plugin Warnings
|
||||
|
||||
```json
|
||||
{
|
||||
"pnpm": {
|
||||
"peerDependencyRules": {
|
||||
"ignoreMissing": [
|
||||
"eslint",
|
||||
"@typescript-eslint/parser"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```yaml title="pnpm-workspace.yaml"
|
||||
peerDependencyRules:
|
||||
ignoreMissing:
|
||||
- eslint
|
||||
- '@typescript-eslint/parser'
|
||||
```
|
||||
|
||||
### Allow Multiple Major Versions
|
||||
|
||||
```json
|
||||
{
|
||||
"pnpm": {
|
||||
"peerDependencyRules": {
|
||||
"allowedVersions": {
|
||||
"webpack": "4 || 5",
|
||||
"postcss": "7 || 8"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```yaml title="pnpm-workspace.yaml"
|
||||
peerDependencyRules:
|
||||
allowedVersions:
|
||||
webpack: '4 || 5'
|
||||
postcss: '7 || 8'
|
||||
```
|
||||
|
||||
## Debugging Peer Dependencies
|
||||
|
||||
```bash
|
||||
# Report unmet/missing peers straight from the lockfile (v11)
|
||||
pnpm peers check
|
||||
|
||||
# See why a package is installed
|
||||
pnpm why <package>
|
||||
|
||||
# List all peer dependency warnings
|
||||
pnpm install --reporter=append-only 2>&1 | grep -i peer
|
||||
|
||||
# Check dependency tree
|
||||
pnpm list --depth=Infinity
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Enable auto-install-peers** for convenience (default in pnpm v8+)
|
||||
|
||||
2. **Use peerDependencyRules** instead of ignoring all warnings
|
||||
|
||||
1. **Keep `autoInstallPeers` on** for convenience (default in v8+)
|
||||
2. **Use `peerDependencyRules`** instead of blanket-ignoring warnings
|
||||
3. **Document suppressed warnings** explaining why they're safe
|
||||
|
||||
4. **Keep peer deps ranges wide** in libraries:
|
||||
```json
|
||||
{
|
||||
"peerDependencies": {
|
||||
"react": "^17.0.0 || ^18.0.0"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
5. **Test with different peer versions** if you support multiple majors
|
||||
4. **Keep peer ranges wide** in libraries (e.g. `"react": "^17 || ^18"`)
|
||||
5. **Run `pnpm peers check`** in CI to catch peer regressions
|
||||
|
||||
<!--
|
||||
Source references:
|
||||
- https://pnpm.io/package_json#pnpmpeerdependencyrules
|
||||
- https://pnpm.io/npmrc#auto-install-peers
|
||||
- https://pnpm.io/settings#peerdependencyrules
|
||||
- https://pnpm.io/settings#autoinstallpeers
|
||||
- https://pnpm.io/cli/peers
|
||||
-->
|
||||
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
---
|
||||
name: pnpm-supply-chain-security
|
||||
description: Build-script approval (allowBuilds), minimum release age, trust policy, and exotic-subdep blocking for safer installs
|
||||
---
|
||||
|
||||
# pnpm Supply-Chain Security
|
||||
|
||||
pnpm blocks several attack vectors by default. Agents installing dependencies must understand these, since installs can fail or prompt on them.
|
||||
|
||||
## Build-script approval (allowBuilds)
|
||||
|
||||
By default pnpm does **not** run dependency lifecycle scripts (`preinstall`/`install`/`postinstall`). Packages must be explicitly approved. Approval lives in one `allowBuilds` map in `pnpm-workspace.yaml`.
|
||||
|
||||
```yaml title="pnpm-workspace.yaml"
|
||||
allowBuilds:
|
||||
esbuild: true
|
||||
core-js: false
|
||||
# version selectors are supported
|
||||
nx@21.6.4 || 21.6.5: true
|
||||
```
|
||||
|
||||
- Packages **not listed** are unreviewed and blocked by default.
|
||||
- `strictDepBuilds: true` (default) ⇒ unreviewed builds make install exit non-zero (`ERR_PNPM_IGNORED_BUILDS`). Set `false` to warn instead.
|
||||
- During install, unreviewed packages with build scripts are auto-added to `pnpm-workspace.yaml` with a placeholder so you can set `true`/`false`.
|
||||
|
||||
> `allowBuilds` replaces the removed `onlyBuiltDependencies`, `neverBuiltDependencies`, `ignoredBuiltDependencies`, `onlyBuiltDependenciesFile`, and `ignoreDepScripts`.
|
||||
|
||||
### Approving builds
|
||||
|
||||
```bash
|
||||
pnpm approve-builds # interactive prompt
|
||||
pnpm approve-builds --all # approve all pending
|
||||
pnpm approve-builds esbuild fsevents !core-js # ! = deny
|
||||
pnpm add --allow-build=esbuild my-bundler # approve while adding
|
||||
pnpm add -g --allow-build=esbuild esbuild # global (replaces approve-builds -g)
|
||||
```
|
||||
|
||||
### Escape hatch (dangerous)
|
||||
|
||||
```yaml title="pnpm-workspace.yaml"
|
||||
dangerouslyAllowAllBuilds: true # runs ALL build scripts now and in the future — avoid
|
||||
```
|
||||
|
||||
## Minimum release age
|
||||
|
||||
Delay installing freshly published versions so malicious releases (usually pulled within an hour) are avoided. Applies to **all** deps, including transitive.
|
||||
|
||||
```yaml title="pnpm-workspace.yaml"
|
||||
minimumReleaseAge: 1440 # minutes; default 1440 (1 day) since v11
|
||||
minimumReleaseAgeExclude: # always install newest of these immediately
|
||||
- webpack
|
||||
- '@myorg/*'
|
||||
- nx@21.6.5 # exempt a specific version
|
||||
```
|
||||
|
||||
- `minimumReleaseAgeStrict` — when no in-range version satisfies the age, fail (default when you set `minimumReleaseAge` yourself) vs. fall back.
|
||||
- `minimumReleaseAgeIgnoreMissingTime` — skip the check for registries that omit the `time` field (default `true`).
|
||||
|
||||
## Trust policy
|
||||
|
||||
Fail if a package's trust level **decreased** vs earlier releases (e.g. was published by a trusted publisher, now only has provenance or nothing).
|
||||
|
||||
```yaml title="pnpm-workspace.yaml"
|
||||
trustPolicy: no-downgrade # off (default) | no-downgrade
|
||||
trustPolicyExclude:
|
||||
- 'chokidar@4.0.3'
|
||||
trustPolicyIgnoreAfter: 525600 # ignore the check for pkgs published > N minutes ago
|
||||
```
|
||||
|
||||
## Block exotic transitive sources
|
||||
|
||||
```yaml title="pnpm-workspace.yaml"
|
||||
blockExoticSubdeps: true # default
|
||||
```
|
||||
|
||||
When `true`, only **direct** dependencies may use exotic sources (git repos, direct tarball URLs); all transitive deps must come from a trusted source (registry, local path, workspace link, or trusted GitHub repos).
|
||||
|
||||
## Lockfile integrity
|
||||
|
||||
Since v11, a downloaded tarball whose hash doesn't match `pnpm-lock.yaml` is a hard error (`ERR_PNPM_TARBALL_INTEGRITY`) — protecting committed lockfiles from a compromised registry/proxy. `--force` and `pnpm update` do **not** bypass it.
|
||||
|
||||
```bash
|
||||
pnpm install --update-checksums # narrow opt-in after verifying the new bytes
|
||||
```
|
||||
|
||||
## Trusted store/cache
|
||||
|
||||
The content-addressable store, global virtual store, and metadata cache are part of pnpm's trust domain. Share them only between mutually trusting users/jobs and protect with filesystem permissions. `verifyStoreIntegrity` (default `true`) detects accidental corruption but does not make a writable-by-untrusted store safe.
|
||||
|
||||
## Key Points
|
||||
|
||||
- Dependency build scripts are blocked until approved via `allowBuilds` / `pnpm approve-builds`; unreviewed builds fail by default (`strictDepBuilds`).
|
||||
- `minimumReleaseAge` (default 1 day in v11) delays new releases; `trustPolicy: no-downgrade` blocks trust regressions; `blockExoticSubdeps` limits transitive git/tarball sources.
|
||||
- Tarball integrity mismatches are fatal; use `--update-checksums` only after verification.
|
||||
- Treat the store/cache as trusted shared state.
|
||||
|
||||
<!--
|
||||
Source references:
|
||||
- https://pnpm.io/settings#allowbuilds
|
||||
- https://pnpm.io/cli/approve-builds
|
||||
- https://pnpm.io/settings#minimumreleaseage
|
||||
- https://pnpm.io/settings#trustpolicy
|
||||
- https://pnpm.io/settings#blockexoticsubdeps
|
||||
- https://pnpm.io/supply-chain-security
|
||||
-->
|
||||
Reference in New Issue
Block a user