You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(resolver): targeted updates resolve exactly like a fresh install (#12558)
Contract: `pnpm up <pkg>` produces the same lockfile a fresh install
of the same manifests would (equivalently: delete the target's
lockfile entries and run `pnpm install`).
pnpm (TypeScript):
- Thread a per-wanted-dependency `updateRequested` flag (true only for
packages matching the user's update target) from the deps-resolver
through requestPackage to the npm picker.
- At the picker, `stripLockfileVersionPins` subtracts
EXISTING_VERSION_SELECTOR_WEIGHT from the target's version-type
selectors (the lockfile seed's contribution, which is added onto
manifest entries pinning the same version) and drops a selector only
when nothing remains. Manifest pins, down-chain propagated versions,
and range/tag selectors (audit-fix penalties) keep applying.
- Always seed `preferredVersions` from the lockfile, also during
update mutations (#10662); caller-supplied preferred
versions (audit-fix penalties) merge over the seed per package name
on a null-prototype target.
- New held-back-update warning at both npm picker entry points: when
`updateRequested` and a kept preferred selector steers the pick
below what the non-pin selectors alone would choose, warn once per
(name, picked, preferred) recommending a pnpm.overrides entry.
- `pnpm update <dep>@<version>` for a transitive-only <dep> warns that
the version part is ignored and recommends an override
(#12744).
pacquet (same behavior):
- The picker strips nothing: UpdateSeedPolicy::DropOnly already
withholds the target's lockfile pins from the preferred-versions
seed. `update_requested` (derived per wanted dependency via
`is_update_target`, matching npm-alias and jsr real names) gates the
held-back-update warning emitted by `warn_once_on_held_back_update`,
with the same message as the TypeScript side.
- `pacquet update <pkg>@<version>` on a transitive-only package warns
that the version part is ignored, mirroring the TypeScript wording.
Coverage: resolver-level tests in both stacks assert that the
lockfile-pin strip lets the target move, that manifest pins,
propagated versions, combined manifest+lockfile entries (manifest
weight preserved), and audit penalties keep steering, and e2e tests
assert the same-chain and importer-pin topologies dedupe under update
exactly as a fresh install does (fresh-install behavior verified
empirically against the registry mock). The transitive-version update
resolves to highest-in-range with the override recommendation, and
crafted package names (`[email protected]`) cannot pollute
Object.prototype.
Closes#10662.
Closes#12744.
Co-authored-by: Minha Kang <[email protected]>
---------
Co-authored-by: Zoltan Kochan <[email protected]>
Co-authored-by: Minha Kang <[email protected]>
Fixed `pnpm up -r <pkg>` bumping unrelated packages that have open semver ranges. Previously, any update mutation nullified the lockfile-derived `preferredVersions` globally, so packages with `^x.y.z` ranges could re-resolve to newer compatible versions even though the user only asked to update a specific package. The install layer now always seeds `preferredVersions` from the lockfile, and caller-supplied preferred versions (such as the vulnerability penalties of `pnpm audit --fix`) layer on top of the seed instead of replacing it. The targeted package still bumps: the per-resolve `updateRequested` flag makes the resolver ignore the target's own lockfile pins.
Fixed a prototype-pollution hazard when seeding preferred versions: a dependency named `__proto__` in a manifest or in `pnpm-lock.yaml` could write through `Object.prototype` (or crash the install) while the preferred-versions map was being built. The maps are now null-prototype objects, so crafted package names land as plain keys.
Fixed `pnpm up <pkg>` producing a different result than a fresh install of the same manifests would. The resolver now distinguishes `updateRequested` (true only for packages that match the user's update target) from the broader `update` flag, and for the targeted package ignores only its own lockfile-derived preferred-version pins — so the target re-resolves exactly as if its lockfile entries were deleted and `pnpm install` ran. Preferred versions a fresh install applies (manifest pins, versions propagated down the dependency chain, and the vulnerability-avoidance penalties of `pnpm audit --fix`) stay in effect, so an update never installs duplicate versions that a reinstall from scratch would not reproduce. When a preferred version holds the update target below the newest version its range admits, pnpm now prints a warning explaining that reaching the newer version everywhere requires an override.
`pnpm update <dep>@<version>` now prints a warning when `<dep>` is only present as a transitive dependency: the requested version cannot be applied there (updates resolve the target the way a fresh install would), and the warning recommends adding the version to `pnpm.overrides` instead, which is the mechanism that does pin transitive dependencies. Closes pnpm/pnpm#12744.
r#""{}" is not a direct dependency, so the requested version "{version}" is ignored — "{}" is updated to what a fresh install would resolve. To force a version of a transitive dependency, add an override scoped to the range its dependents declare to pnpm-workspace.yaml, e.g.: overrides: {{ "{}@<declared range>": "{version}" }}"#,
0 commit comments