Summary
pnpm up -r <transitive-pattern> <direct-dependency> can skip recursive transitive updates for the transitive pattern.
In a real workspace, pnpm up -r "@babel/core" updates transitive @babel/core to 7.29.7, but pnpm up -r "@babel/core" uuid does not when uuid is a direct dependency selector.
Minimal Reproduction
Verified with pnpm v11.5.0 in a 27-project Angular monorepo (~/fathom/frontend/fathom-frontend-bench1, lockfileVersion 9.0).
git checkout HEAD -- pnpm-lock.yaml
pnpm up -r "@babel/core"
rg '@babel/core@' pnpm-lock.yaml
# => '@babel/core@7.29.7' is present
git checkout HEAD -- pnpm-lock.yaml
pnpm up -r "@babel/core" uuid
rg '@babel/core@' pnpm-lock.yaml
# => '@babel/core@7.29.7' is missing
The original long command failed for the same reason because it mixed transitive patterns with uuid, which is a direct dependency in the root manifest:
pnpm up -r fast-uri "@babel/*" brace-expansion webpack-dev-server hono \
"@hono/*" fast-xml-builder fast-xml-parser ip-address ws uuid qs
Removing uuid from that command makes the @babel/* recursive transitive update work.
Root Cause
The resolver assigns update depth for existing dependencies in installing/deps-resolver/src/toResolveImporter.ts.
noDependencySelectors is computed globally in installing/deps-resolver/src/index.ts:
noDependencySelectors: importers.every(({ wantedDependencies }) => wantedDependencies.length === 0)
When any direct selector exists (for example uuid), noDependencySelectors becomes false. Then existing transitive dependencies are assigned updateDepth: -1 even when project.updateMatching exists.
Debugging showed this for @babel/core in the mixed command:
updateMatchingResult: true
updateDepth: -1
updateShouldContinue: false
updateRequested: false
So the matcher correctly identifies @babel/core, but the depth check prevents the update. Since updateRequested=false, resolveDependency rewrites ranges like ^7.23.2 to the current exact lockfile version, keeping the old resolved version.
Expected Behavior
Mixing direct dependency selectors and transitive update patterns should not disable recursive updates for matching transitive packages.
pnpm up -r "@babel/core" uuid should update transitive @babel/core just like pnpm up -r "@babel/core" does.
Actual Behavior
The direct selector causes existing transitive dependencies to get updateDepth: -1, so matching transitive packages are not updated.
Fix Direction
When project.updateMatching exists, existing dependencies should keep defaultUpdateDepth. updateMatching already filters package-by-package, so applying the depth does not mean every transitive package is updated.
Related
Written by an agent (opencode, gpt-5.5).
Summary
pnpm up -r <transitive-pattern> <direct-dependency>can skip recursive transitive updates for the transitive pattern.In a real workspace,
pnpm up -r "@babel/core"updates transitive@babel/coreto7.29.7, butpnpm up -r "@babel/core" uuiddoes not whenuuidis a direct dependency selector.Minimal Reproduction
Verified with pnpm
v11.5.0in a 27-project Angular monorepo (~/fathom/frontend/fathom-frontend-bench1, lockfileVersion 9.0).The original long command failed for the same reason because it mixed transitive patterns with
uuid, which is a direct dependency in the root manifest:Removing
uuidfrom that command makes the@babel/*recursive transitive update work.Root Cause
The resolver assigns update depth for existing dependencies in
installing/deps-resolver/src/toResolveImporter.ts.noDependencySelectorsis computed globally ininstalling/deps-resolver/src/index.ts:When any direct selector exists (for example
uuid),noDependencySelectorsbecomesfalse. Then existing transitive dependencies are assignedupdateDepth: -1even whenproject.updateMatchingexists.Debugging showed this for
@babel/corein the mixed command:So the matcher correctly identifies
@babel/core, but the depth check prevents the update. SinceupdateRequested=false,resolveDependencyrewrites ranges like^7.23.2to the current exact lockfile version, keeping the old resolved version.Expected Behavior
Mixing direct dependency selectors and transitive update patterns should not disable recursive updates for matching transitive packages.
pnpm up -r "@babel/core" uuidshould update transitive@babel/corejust likepnpm up -r "@babel/core"does.Actual Behavior
The direct selector causes existing transitive dependencies to get
updateDepth: -1, so matching transitive packages are not updated.Fix Direction
When
project.updateMatchingexists, existing dependencies should keepdefaultUpdateDepth.updateMatchingalready filters package-by-package, so applying the depth does not mean every transitive package is updated.Related
Written by an agent (opencode, gpt-5.5).