pnpm
Nub mirrors pnpm most closely — native version-9 lockfile read and write, the full dependency-verb surface, workspace selectors, an isolated dependency tree, and pnpm-owned config, all gated on pnpm being the incumbent.
Nub mirrors pnpm most closely: the CLI surface, the isolated node_modules layout, and the lockfile. When pnpm is the incumbent — a packageManager: "pnpm@…" field or an existing pnpm-lock.yaml — Nub reads and writes pnpm's own pnpm-lock.yaml in its native format, and honors pnpm's config. Everything below is gated on that.
Commands
| Feature | pnpm | nub | Notes |
|---|---|---|---|
install / i / ci | Supported | Supported | |
add / remove / update | Supported | Supported | |
import | Supported | Supported | |
dedupe / prune | Supported | Supported | |
rebuild / fetch | Supported | Supported | |
link / unlink | Supported | Supported | |
patch / patch-commit / patch-remove | Supported | Supported | |
approve-builds / ignored-builds | Supported | Supported | |
dlx / create | Supported | Supported | |
list / why / outdated | Supported | Supported | |
audit / licenses / peers | Supported | Supported | |
publish / pack / version | Supported | Supported | |
store / config | Supported | Supported | |
recursive meta-verb | Supported | Supported | |
deploy | Supported | Not supported. Not wired; pnpm deploy stays the path for now.Not wired; pnpm deploy stays the path for now. | Not wired; pnpm deploy stays the path for now. |
Configuration
| Feature | pnpm | nub | Notes |
|---|---|---|---|
dependencies / devDependencies | Supported | Supported | |
optionalDependencies | Supported | Supported | |
peerDependencies / peerDependenciesMeta | Supported | Supported | |
dependenciesMeta.injected | Supported | Supported | |
pnpm.overrides | Supported | Supported | |
pnpm.packageExtensions | Supported | Supported | |
pnpm.patchedDependencies | Supported | Supported | |
pnpm.supportedArchitectures | Supported | Supported. Also settable per-invocation with --os / --cpu / --libc.Also settable per-invocation with --os / --cpu / --libc. | Also settable per-invocation with --os / --cpu / --libc. |
resolutions | Supported | Supported | |
onlyBuiltDependencies / neverBuiltDependencies | Supported | Supported | |
packageManager / engines | Supported | Supported | |
workspaces / pnpm-workspace.yaml | Supported | Supported. Workspace and resolution settings; layout keys are not read.Workspace and resolution settings; layout keys are not read. | Workspace and resolution settings; layout keys are not read. |
catalog: / catalogs: | Supported | Supported | |
workspace: protocol | Supported | Supported | |
namedRegistries | Supported | Supported. Alias-to-registry map; built-in gh: alias for GitHub Packages.Alias-to-registry map; built-in gh: alias for GitHub Packages. | Alias-to-registry map; built-in gh: alias for GitHub Packages. |
| workspace selectors | Supported | Partially supported. No git-since ([ref]) selector.No git-since ([ref]) selector. | No git-since ([ref]) selector. |
.npmrc | Supported | Supported | |
.pnpmfile.cjs / .pnpmfile.mjs | Supported | Supported | |
npm_config_* | Supported | Supported | |
pnpm_config_* | Supported | Supported | |
pnpm-lock.yaml v9 | Supported | Supported. v6/v5.4 declined — re-lock under pnpm 9+.v6/v5.4 declined — re-lock under pnpm 9+. | v6/v5.4 declined — re-lock under pnpm 9+. |
nodeLinker | Supported | Partially supported. Set in nub.jsonc or .npmrc under every pnpm major. No PnP.Set in nub.jsonc or .npmrc under every pnpm major. No PnP. | Set in nub.jsonc or .npmrc under every pnpm major. No PnP. |
pnpm-lock.yaml
Nub reads and writes pnpm lockfile v9 (lockfileVersion: '9.0') — what pnpm 9+ emits. A lockfile Nub writes installs cleanly under pnpm:
$ nub install
dependencies:
+ is-odd@3.0.1
$ grep lockfileVersion pnpm-lock.yaml
lockfileVersion: '9.0'
$ pnpm install --frozen-lockfile
Lockfile is up to date, resolution step is skippedThe round-trip holds both directions, for single packages and for workspaces with catalog: and workspace: entries (verified against pnpm 10.15.1).
Older formats — v6 (pnpm 8) and v5.4 (pnpm 7) — keep root dependencies under a top-level dependencies: map instead of v9's importers:. Nub declines them up front and leaves your node_modules and lockfile untouched:
# captured: nub 0.0.44 on a lockfileVersion: '6.0' lockfile
$ nub install
Error: ERR_NUB_LOCKFILE_UNSUPPORTED_FORMAT
× pnpm-lock.yaml is lockfileVersion 6.0 (pnpm 8); nub reads v9 (pnpm 9+).
help: Re-lock under pnpm 9+ (`pnpm install`), then `nub install`.The refusal names the detected version (v5.4 reads (pnpm 7)). Migrate by running a one-time pnpm install under pnpm 9+ to upgrade the lockfile to v9, then nub install.
Settings
When pnpm is the incumbent, Nub honors pnpm's own settings:
pnpm-workspace.yaml—packages:globs, thecatalog:/catalogs:maps, and pnpm 11 resolution settings. Layout keys are not read.namedRegistries— an alias-to-registry-URL map frompnpm-workspace.yamlor the global pnpmconfig.yaml; a dependency spec prefixed<alias>:(e.g."@work/constants": "work:1.x.x") resolves from that registry, the built-ingh:alias points at GitHub Packages, and auth rides existing//host/:_authToken=entries in.npmrc.pnpm.overrides— version pins applied during resolution, written into the lockfile'soverrides:block.pnpm.packageExtensions— extra dependency/peer metadata merged onto resolved packages, hashed into the lockfile'spackageExtensionsChecksum.pnpm.patchedDependencies— patch files applied to dependency contents during linking, wired tonub patch/patch-commit/patch-remove.pnpm.onlyBuiltDependencies/pnpm.neverBuiltDependencies/pnpm.allowBuilds— feed Nub's lifecycle-script policy.pnpm.supportedArchitectures— which platforms' optional dependencies get installed. Also readable from.npmrcandpnpm-workspace.yaml, and overridable per axis for one run with--os/--cpu/--libc..pnpmfile.cjs/.pnpmfile.mjs— thereadPackage,preResolution, andafterAllResolvedhooks run. Dependency-map edits fromreadPackageapply; package-map edits fromafterAllResolvedapply. New importers/packages, identity rewrites,updateConfighooks, and config-dependency pnpmfiles are not supported.
Read the full docs on pnpm.io.
packages:
- 'packages/*'
catalog:
lodash.merge: 4.6.2
namedRegistries:
work: https://npm.example.com/{
"pnpm": { "overrides": { "is-number": "7.0.0" } }
}Nub reads overrides, packageExtensions, and patchedDependencies from either home pnpm accepts: the pnpm.* namespace in package.json, or the matching key in pnpm-workspace.yaml. The pnpm resolver prefers pnpm.overrides; top-level resolutions is still honored (pnpm supports it for Yarn compatibility), but top-level overrides is npm and Bun's field and is ignored under pnpm.
Adopting Nub into an existing pnpm repo leaves the project pnpm-owned, so all of this keeps working untouched. Switching it to Nub's own identity with nub pm use nub migrates the pins to the top-level overrides and patchedDependencies fields in package.json.
None of this is read under an npm, Yarn, or Bun incumbent, or in a Nub-identity project. Under those three incumbents, a stray .pnpmfile.cjs is ignored with a warning rather than applied silently:
$ nub install
nub: `.pnpmfile.cjs` ignored — this project uses npm, which doesn't
apply pnpmfile hooks. Remove it, name it explicitly with
`--pnpmfile`, or switch to pnpm (`nub pm use pnpm`).Under Nub identity, a default .pnpmfile.cjs / .pnpmfile.mjs is ignored silently. An explicit --pnpmfile <path> always loads.
Install behavior
The default node_modules layout is isolated — pnpm's symlink-into-a-virtual-store scheme. The --node-linker hoisted flag gives the flat npm-style layout.
Nub does not read nodeLinker or the hoisting keys from pnpm-workspace.yaml or pnpm's global config.yaml. Set layout with install.linker and install.publicHoist in nub.jsonc, their .npmrc spellings, or command-line flags. All three sources work under every pnpm major.
Read the full docs on pnpm.io.
There is no pnp linker. A pnp value in .npmrc or nub.jsonc, and a --node-linker pnp flag, are refused rather than silently downgraded. A branded nodeLinker: pnp in pnpm YAML is ignored with the other branded layout settings.
$ nub install
× node-linker=pnp is not supported by nub; use `isolated` (default) or # ❌
│ `hoisted`Lifecycle scripts for dependencies are skipped by default, exactly as pnpm 10 does. A package runs install scripts only when allowlisted via pnpm.onlyBuiltDependencies (or pnpm.allowBuilds), or when Nub's gated default-trust floor vouches for it (see the default-trust floor); nub approve-builds adds an entry and builds the approved packages in the same invocation. pnpm.neverBuiltDependencies is a denylist that wins over any allow and over the floor.
Unsupported
Two pnpm commands produce an unsupported-command error rather than a silent fallback:
sbom # CycloneDX/SPDX bill of materials — not yet wired
deploy # not yet wired; the operation stays manualThe node-linker=pnp install mode is also declined; see the table above.
Package manager
Nub's install engine — the package.json fields and config keys that shape dependency resolution, plus lockfile compatibility with npm, pnpm, Bun, and Yarn.
npm
Nub speaks npm's on-disk formats — the version-2 and version-3 lockfile round-trips byte-for-byte, npm workspaces and overrides are honored, and npm config is read across the builtin, global, user, and project scopes. The CLI is pnpm-shaped; the files are npm's.