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:
- add its accepted range to the default catalog
- change its consumer declarations to
catalog: - run
pnpm install - run the full repository validation
- 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.