Skip to main content

pnpm

pnpm is the repository package manager and workspace dependency manager.

The currently pinned version is:

pnpm 11.21.0

Installation ownership

pnpm is installed by mise through:

"npm:pnpm" = "11.21.0"

This does not mean the repository uses npm as its package manager.

npm is only the mise backend used to install the pnpm CLI.

All repository dependency operations use pnpm.

Package-manager contract

The root package.json declares:

{
"packageManager": "pnpm@11.21.0"
}

This records the package manager and version expected by the repository.

The pnpm project policy also sets:

pmOnFail: error

A package-manager version mismatch therefore fails instead of silently switching package-manager versions.

Workspace definition

The workspace is defined by:

pnpm-workspace.yaml

Applications live under:

apps/*

Shared packages live under:

packages/*

Node.js dependency policy

The workspace defines:

nodeVersion: 24.19.0
engineStrict: true

nodeVersion defines the Node.js target used when pnpm evaluates package engine requirements.

engineStrict prevents dependencies with incompatible engine requirements from being silently accepted.

The actual local Node.js runtime is installed by mise.

Dependency build scripts

pnpm dependency build scripts are reviewed explicitly.

The repository enables:

strictDepBuilds: true

Approved build dependencies are recorded under:

allowBuilds:
"@swc/core": true
core-js: true
esbuild: true
lefthook: true

New dependency lifecycle scripts should be reviewed before being added to this allowlist.

Installing dependencies

Use a frozen lockfile for normal repository bootstrap:

pnpm install --frozen-lockfile

Use a normal install only when intentionally changing dependency resolution:

pnpm install

Adding dependencies

Add dependencies to the workspace that owns them.

pnpm --filter web add <package>
pnpm --filter docs add <package>
pnpm --filter @repo/ui add <package>

Repository-level development tooling should be installed at the workspace root:

pnpm add -Dw <package>

Removing dependencies

pnpm --filter web remove <package>

Lockfile policy

pnpm-lock.yaml is committed to Git.

Do not edit it manually.

Dependency changes should be made with pnpm and the resulting lockfile changes should be reviewed.

pnpm 12

pnpm 12 is intentionally not the repository baseline while it remains prerelease software.

When pnpm 12 becomes stable, evaluate:

  • lockfile migration
  • monorepo compatibility
  • native installation through mise
  • Windows x64 and ARM64
  • Linux glibc and musl
  • macOS ARM64
  • install and startup performance
  • CI behavior

The current npm:pnpm installation decision may be revisited at that time.

pnpr

pnpr is an experimental server-side registry and dependency-resolution project from the pnpm ecosystem.

It is not part of the repository toolchain.

It may be reconsidered if the project later requires:

  • private package hosting
  • registry caching
  • centralized registry credentials
  • large-scale CI dependency acceleration

The current repository uses the standard pnpm registry workflow.

Dependency catalogs

Shared external dependency versions are managed with the pnpm default catalog.

The architectural decision is documented in ADR-012.

The catalog is defined in:

pnpm-workspace.yaml

The current shared catalog contains repository-wide baselines for:

@types/node
@types/react
@types/react-dom
clsx
eslint
globals
react
react-dom
typescript

Consumer packages continue to declare their own dependencies:

{
"devDependencies": {
"eslint": "catalog:",
"typescript": "catalog:"
}
}

This separates two concerns:

dependency ownership
→ package.json of the consumer

shared version policy
→ pnpm-workspace.yaml catalog

Catalog versus workspace protocol

Use:

workspace:*

for packages implemented inside this repository, such as:

@repo/ui
@repo/eslint-config
@repo/typescript-config

Use:

catalog:

for shared external dependencies whose version is intentionally synchronized across multiple consumers.

Do not use workspace:* for registry packages such as ESLint or React.

Adding shared dependencies

When a new external dependency becomes intentionally shared across multiple workspaces:

  1. add its accepted range to the default catalog
  2. change its consumer declarations to catalog:
  3. run pnpm install
  4. run the full repository validation
  5. document the policy if the dependency changes repository architecture

pnpm also supports saving dependencies into a catalog through its catalog-aware add options.

Adding package-local dependencies

A dependency used by only one workspace should normally keep a direct version specifier:

pnpm --filter <workspace> add <package>

Do not move a dependency into the catalog only to eliminate a version string.

Catalog mode

The repository currently uses:

catalogMode: prefer

Existing compatible catalog entries are preferred when adding dependencies.

This gives shared dependencies a stable default without forcing unrelated package-local dependencies into the catalog.

Intentional multiple versions

If a future migration requires multiple versions of the same dependency, use named catalogs rather than allowing consumer manifests to drift silently.

For example:

catalogs:
react20:
react: ^20.0.0
react-dom: ^20.0.0

A selected consumer can then use:

{
"dependencies": {
"react": "catalog:react20",
"react-dom": "catalog:react20"
}
}

Named catalogs should represent an intentional compatibility or migration boundary, not accidental dependency divergence.