Files
pnpm/installing/deps-resolver/package.json
Zoltan Kochan 5dc8be8a42 fix(graph-hasher): resolve GVS engine per-snapshot for runtime-pinned deps (#11693)
Closes #11690.

A dependency that declares `engines.runtime` in its manifest carries the desugared `dependencies.node: 'runtime:<version>'` pin in the lockfile, and pnpm's bin linker spawns that dep's lifecycle scripts through the pinned Node downloaded into `<pkgDir>/node_modules/node/`. The GVS hash and the side-effects-cache key prefix were still anchored to the install-wide runtime — so the pinning snapshot's slot encoded the wrong Node major, and a reinstall on the same host could read the cached side-effects under a key whose `<platform>;<arch>;node<major>` triple disagreed with the Node the build actually ran on.

Per-snapshot resolution now matches what `bins/linker` already does on a per-package basis: a snapshot's own pin wins; the install-wide value (from #11689's `findRuntimeNodeVersion`) is the fallback.

### TypeScript

- `deps/graph-hasher/src/index.ts:72-77` — adds `readSnapshotRuntimePin(children)`: pulls the bare Node version from a graph node's `children.node` entry when that points at a `node@runtime:<version>` snapshot. Factors out a small `extractRuntimeNodeVersion(snapshotKey)` parser shared with `findRuntimeNodeVersion`.
- `deps/graph-hasher/src/index.ts:115-116,245-246` — `calcDepState` and `calcGraphNodeHash` consult `readSnapshotRuntimePin(graph[depPath].children)` first and only fall back to the install-wide `nodeVersion` parameter when the snapshot doesn't pin its own Node. No caller changes required — install-wide fallback continues to be computed via `findRuntimeNodeVersion(Object.keys(graph))` at each call site.
- **Refactor (separate commit):** `findRuntimeNodeVersion` moved from `@pnpm/engine.runtime.system-node-version` to `@pnpm/deps.graph-hasher` (along with the new `readSnapshotRuntimePin`). `system-node-version` is about probing the *host* Node — `getSystemNodeVersion`, `engineName`. The lockfile-shape parsers fit better next to the package that actually composes the engine string. Every caller already depended on graph-hasher, so no new deps; six packages drop the now-unused dependency on `system-node-version`.

### Pacquet

- `pacquet/crates/package-manager/src/install_frozen_lockfile.rs:1309-1345` — new `find_own_runtime_node_major(snapshot)` reads a snapshot's `dependencies` for a `node` entry with `Prefix::Runtime`, returning the bare major.
- `pacquet/crates/package-manager/src/virtual_store_layout.rs:178-205` — `VirtualStoreLayout::new` resolves engine per-snapshot inside the hash loop via `engine_name(own_major, None, None)` when the snapshot pins, otherwise inherits the install-wide `engine` argument.

### Migration

Snapshots of dependencies that declare their own `engines.runtime` re-hash under that dep's pinned Node instead of the install-wide value. Old slots become prune-eligible on next install.
2026-05-17 13:25:05 +02:00

93 lines
3.0 KiB
JSON

{
"name": "@pnpm/installing.deps-resolver",
"version": "1100.0.10",
"description": "Resolves dependency graph of a package",
"keywords": [
"pnpm",
"pnpm11"
],
"license": "MIT",
"funding": "https://opencollective.com/pnpm",
"repository": "https://github.com/pnpm/pnpm/tree/main/installing/deps-resolver",
"homepage": "https://github.com/pnpm/pnpm/tree/main/installing/deps-resolver#readme",
"bugs": {
"url": "https://github.com/pnpm/pnpm/issues"
},
"type": "module",
"main": "lib/index.js",
"types": "lib/index.d.ts",
"exports": {
".": "./lib/index.js"
},
"files": [
"lib",
"!*.map"
],
"scripts": {
"start": "tsgo --watch",
"test": "pn compile && pn .test",
"lint": "eslint \"src/**/*.ts\" \"test/**/*.ts\"",
"prepublishOnly": "tsgo --build",
"compile": "tsgo --build && pn lint --fix",
".test": "cross-env NODE_OPTIONS=\"$NODE_OPTIONS --experimental-vm-modules --disable-warning=ExperimentalWarning --disable-warning=DEP0169\" jest"
},
"dependencies": {
"@pnpm/catalogs.resolver": "workspace:*",
"@pnpm/catalogs.types": "workspace:*",
"@pnpm/config.version-policy": "workspace:*",
"@pnpm/constants": "workspace:*",
"@pnpm/core-loggers": "workspace:*",
"@pnpm/deps.graph-hasher": "workspace:*",
"@pnpm/deps.path": "workspace:*",
"@pnpm/deps.peer-range": "workspace:*",
"@pnpm/error": "workspace:*",
"@pnpm/fetching.pick-fetcher": "workspace:*",
"@pnpm/hooks.types": "workspace:*",
"@pnpm/lockfile.preferred-versions": "workspace:*",
"@pnpm/lockfile.pruner": "workspace:*",
"@pnpm/lockfile.types": "workspace:*",
"@pnpm/lockfile.utils": "workspace:*",
"@pnpm/patching.config": "workspace:*",
"@pnpm/patching.types": "workspace:*",
"@pnpm/pkg-manifest.reader": "workspace:*",
"@pnpm/pkg-manifest.utils": "workspace:*",
"@pnpm/resolving.npm-resolver": "workspace:*",
"@pnpm/resolving.resolver-base": "workspace:*",
"@pnpm/store.controller-types": "workspace:*",
"@pnpm/types": "workspace:*",
"@pnpm/util.lex-comparator": "catalog:",
"@pnpm/workspace.spec-parser": "workspace:*",
"@yarnpkg/core": "catalog:",
"graph-cycles": "catalog:",
"is-inner-link": "catalog:",
"is-subdir": "catalog:",
"normalize-path": "catalog:",
"p-defer": "catalog:",
"path-exists": "catalog:",
"promise-share": "catalog:",
"ramda": "catalog:",
"rename-overwrite": "catalog:",
"safe-promise-defer": "catalog:",
"semver": "catalog:",
"semver-range-intersect": "catalog:",
"version-selector-type": "catalog:"
},
"peerDependencies": {
"@pnpm/logger": "catalog:"
},
"devDependencies": {
"@jest/globals": "catalog:",
"@pnpm/installing.deps-resolver": "workspace:*",
"@pnpm/logger": "workspace:*",
"@types/normalize-path": "catalog:",
"@types/ramda": "catalog:",
"@types/semver": "catalog:"
},
"engines": {
"node": ">=22.13"
},
"jest": {
"preset": "@pnpm/jest-config"
}
}