@pnpm/deps.graph-hasher 1100.2.12 → 1100.2.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # @pnpm/calc-dep-state
2
2
 
3
+ ## 1100.2.13
4
+
5
+ ### Patch Changes
6
+
7
+ - Installing a local `file:` directory dependency with the global virtual store enabled no longer fails with `TypeError: Cannot read properties of undefined (reading 'split')` [#13335](https://github.com/pnpm/pnpm/issues/13335).
8
+
9
+ Local directory dependencies — `file:` directories and injected workspace packages — now get a global-virtual-store slot of their own per project. They used to share one slot across every project that depended on a directory of the same name, so a project could end up linked to another project's copy of the dependency.
10
+
11
+ - Updated dependencies:
12
+ - @pnpm/deps.path@1100.0.12
13
+ - @pnpm/engine.runtime.system-version@1100.0.7
14
+ - @pnpm/lockfile.types@1100.0.17
15
+ - @pnpm/lockfile.utils@1100.1.6
16
+ - @pnpm/resolving.resolver-base@1100.5.5
17
+ - @pnpm/types@1101.7.0
18
+
3
19
  ## 1100.2.12
4
20
 
5
21
  ### Patch Changes
package/lib/index.d.ts CHANGED
@@ -77,28 +77,42 @@ export interface HashedDepPath<T extends PkgMeta> {
77
77
  pkgMeta: T;
78
78
  hash: string;
79
79
  }
80
- export declare function iterateHashedGraphNodes<T extends PkgMeta>(graph: DepsGraph<DepPath>, pkgMetaIterator: PkgMetaIterator<T>, allowBuild?: AllowBuild, supportedArchitectures?: SupportedArchitectures,
81
- /**
82
- * Install-wide fallback `engines.runtime` / `devEngines.runtime`
83
- * Node version. Used only for snapshots that don't pin their own
84
- * Node; pinning snapshots get resolved per-snapshot via
85
- * {@link readSnapshotRuntimePin} so the GVS engine hash matches
86
- * the Node the bin linker would actually spawn for each package
87
- * (see [`bins/linker/src/index.ts`](https://github.com/pnpm/pnpm/blob/29a42efc3b/bins/linker/src/index.ts)).
88
- * Typically obtained via {@link findRuntimeNodeVersion} over the
89
- * lockfile's snapshot keys. `undefined` falls back to
90
- * {@link engineName}'s default (system `node --version`, with
91
- * `process.version` as a last resort).
92
- */
93
- nodeVersion?: string): IterableIterator<HashedDepPath<T>>;
94
- export declare function calcGraphNodeHash<T extends PkgMeta>({ graph, cache, builtDepPaths, buildRequiredCache, supportedArchitectures, nodeVersion }: {
80
+ export interface GraphNodeHashOptions {
81
+ allowBuild?: AllowBuild;
82
+ supportedArchitectures?: SupportedArchitectures;
83
+ /**
84
+ * Install-wide fallback `engines.runtime` / `devEngines.runtime`
85
+ * Node version. Used only for snapshots that don't pin their own
86
+ * Node; pinning snapshots get resolved per-snapshot via
87
+ * {@link readSnapshotRuntimePin} so the GVS engine hash matches
88
+ * the Node the bin linker would actually spawn for each package
89
+ * (see [`bins/linker/src/index.ts`](https://github.com/pnpm/pnpm/blob/29a42efc3b/bins/linker/src/index.ts)).
90
+ * Typically obtained via {@link findRuntimeNodeVersion} over the
91
+ * lockfile's snapshot keys. `undefined` falls back to
92
+ * {@link engineName}'s default (system `node --version`, with
93
+ * `process.version` as a last resort).
94
+ */
95
+ nodeVersion?: string;
96
+ /**
97
+ * Directory the lockfile lives in. Scopes the slots of local directory
98
+ * dependencies to the project that owns them — see
99
+ * {@link isLocalDirectoryResolution}. Omitting it leaves those packages on a
100
+ * shared slot, which is only safe for a lockfile known to have no directory
101
+ * dependencies.
102
+ */
103
+ lockfileDir?: string;
104
+ }
105
+ export declare function iterateHashedGraphNodes<T extends PkgMeta>(graph: DepsGraph<DepPath>, pkgMetaIterator: PkgMetaIterator<T>, opts?: GraphNodeHashOptions): IterableIterator<HashedDepPath<T>>;
106
+ export declare function calcGraphNodeHash<T extends PkgMeta>({ graph, cache, builtDepPaths, buildRequiredCache, supportedArchitectures, nodeVersion, lockfileDir }: {
95
107
  graph: DepsGraph<DepPath>;
96
108
  cache: DepsStateCache;
97
109
  builtDepPaths?: Set<DepPath>;
98
110
  buildRequiredCache?: Record<string, boolean>;
99
111
  supportedArchitectures?: SupportedArchitectures;
100
- /** See [`iterateHashedGraphNodes`]'s `nodeVersion` parameter. */
112
+ /** See {@link GraphNodeHashOptions.nodeVersion}. */
101
113
  nodeVersion?: string;
114
+ /** See {@link GraphNodeHashOptions.lockfileDir}. */
115
+ lockfileDir?: string;
102
116
  }, pkgMeta: T): string;
103
117
  export declare function calcLeafGlobalVirtualStorePath(fullPkgId: string, name: string, version: string): string;
104
118
  /**
package/lib/index.js CHANGED
@@ -113,25 +113,12 @@ function calcDepGraphHash(depsGraph, cache, parents, depPath, supportedArchitect
113
113
  });
114
114
  return cache[depPath];
115
115
  }
116
- export function* iterateHashedGraphNodes(graph, pkgMetaIterator, allowBuild, supportedArchitectures,
117
- /**
118
- * Install-wide fallback `engines.runtime` / `devEngines.runtime`
119
- * Node version. Used only for snapshots that don't pin their own
120
- * Node; pinning snapshots get resolved per-snapshot via
121
- * {@link readSnapshotRuntimePin} so the GVS engine hash matches
122
- * the Node the bin linker would actually spawn for each package
123
- * (see [`bins/linker/src/index.ts`](https://github.com/pnpm/pnpm/blob/29a42efc3b/bins/linker/src/index.ts)).
124
- * Typically obtained via {@link findRuntimeNodeVersion} over the
125
- * lockfile's snapshot keys. `undefined` falls back to
126
- * {@link engineName}'s default (system `node --version`, with
127
- * `process.version` as a last resort).
128
- */
129
- nodeVersion) {
116
+ export function* iterateHashedGraphNodes(graph, pkgMetaIterator, opts = {}) {
130
117
  let builtDepPaths;
131
118
  let entries;
132
- if (allowBuild != null) {
119
+ if (opts.allowBuild != null) {
133
120
  const pkgMetaList = Array.from(pkgMetaIterator);
134
- builtDepPaths = computeBuiltDepPaths(pkgMetaList, allowBuild);
121
+ builtDepPaths = computeBuiltDepPaths(pkgMetaList, opts.allowBuild);
135
122
  entries = pkgMetaList;
136
123
  }
137
124
  else {
@@ -142,8 +129,9 @@ nodeVersion) {
142
129
  cache: {},
143
130
  builtDepPaths,
144
131
  buildRequiredCache: builtDepPaths !== undefined ? {} : undefined,
145
- supportedArchitectures,
146
- nodeVersion,
132
+ supportedArchitectures: opts.supportedArchitectures,
133
+ nodeVersion: opts.nodeVersion,
134
+ lockfileDir: opts.lockfileDir,
147
135
  };
148
136
  for (const pkgMeta of entries) {
149
137
  yield {
@@ -152,7 +140,7 @@ nodeVersion) {
152
140
  };
153
141
  }
154
142
  }
155
- export function calcGraphNodeHash({ graph, cache, builtDepPaths, buildRequiredCache, supportedArchitectures, nodeVersion }, pkgMeta) {
143
+ export function calcGraphNodeHash({ graph, cache, builtDepPaths, buildRequiredCache, supportedArchitectures, nodeVersion, lockfileDir }, pkgMeta) {
156
144
  const { name, version, depPath } = pkgMeta;
157
145
  // When builtDepPaths is provided (derived from the allowBuilds config),
158
146
  // we only include the engine name for packages that are allowed to build
@@ -169,8 +157,39 @@ export function calcGraphNodeHash({ graph, cache, builtDepPaths, buildRequiredCa
169
157
  const ownPin = readSnapshotRuntimePin(graph[depPath]?.children);
170
158
  const engine = includeEngine ? engineName(ownPin ?? nodeVersion) : null;
171
159
  const deps = calcDepGraphHash(graph, cache, new Set(), depPath, supportedArchitectures);
172
- const hexDigest = hashObjectWithoutSorting({ engine, deps }, { encoding: 'hex' });
173
- return formatGlobalVirtualStorePath(name, version, hexDigest);
160
+ const isLocalDirectory = isLocalDirectoryResolution(graph[depPath]?.resolution);
161
+ // Scoping the slot needs the project's identity; the segment only needs to
162
+ // know that the package is a local directory, so a caller that leaves
163
+ // `lockfileDir` out still gets a well-formed path.
164
+ const project = isLocalDirectory ? lockfileDir : undefined;
165
+ const hexDigest = project == null
166
+ ? hashObjectWithoutSorting({ engine, deps }, { encoding: 'hex' })
167
+ : hashObjectWithoutSorting({ engine, deps, project }, { encoding: 'hex' });
168
+ return formatGlobalVirtualStorePath(name, isLocalDirectory ? LOCAL_DIRECTORY_SEGMENT : version, hexDigest);
169
+ }
170
+ /**
171
+ * Slot segment that stands in for the version of a package resolved from a
172
+ * local directory. pnpm omits the version from a directory snapshot, so the
173
+ * lockfile has none to offer — and the resolver, which does know it from the
174
+ * manifest, must agree with the lockfile or a re-install would relocate the
175
+ * package. The segment is decoration in a store listing; the digest that
176
+ * follows it is what identifies the slot.
177
+ */
178
+ const LOCAL_DIRECTORY_SEGMENT = 'directory';
179
+ /**
180
+ * Whether the package came from a local directory — a `file:` directory
181
+ * dependency or an injected workspace package.
182
+ *
183
+ * Such a package needs a slot of its own per project. A directory resolution is
184
+ * the one resolution with no integrity: it is a path relative to the lockfile,
185
+ * so `file:dep` hashes identically in every project that happens to depend on a
186
+ * directory of that name. Sharing the slot would hand one project the files of
187
+ * whichever project installed first, and because the source directory is
188
+ * mutable pnpm re-imports it on every install — so the projects would go on
189
+ * overwriting each other's dependency.
190
+ */
191
+ function isLocalDirectoryResolution(resolution) {
192
+ return resolution != null && 'type' in resolution && resolution.type === 'directory';
174
193
  }
175
194
  export function calcLeafGlobalVirtualStorePath(fullPkgId, name, version) {
176
195
  const depsHash = hashObject({ id: fullPkgId, deps: {} });
@@ -244,6 +263,7 @@ export function lockfileToDepGraph(lockfile, supportedArchitectures) {
244
263
  });
245
264
  graph[depPath] = {
246
265
  children,
266
+ resolution: pkgSnapshot.resolution,
247
267
  fullPkgId: createFullPkgId(getPkgIdWithPatchHash(depPath), pkgSnapshot.resolution, supportedArchitectures),
248
268
  };
249
269
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pnpm/deps.graph-hasher",
3
- "version": "1100.2.12",
3
+ "version": "1100.2.13",
4
4
  "description": "Calculates the state of a dependency",
5
5
  "keywords": [
6
6
  "pnpm",
@@ -28,17 +28,17 @@
28
28
  ],
29
29
  "dependencies": {
30
30
  "@pnpm/crypto.object-hasher": "1100.0.1",
31
- "@pnpm/deps.path": "1100.0.11",
32
- "@pnpm/engine.runtime.system-version": "1100.0.6",
33
- "@pnpm/lockfile.types": "1100.0.16",
34
- "@pnpm/lockfile.utils": "1100.1.5",
35
- "@pnpm/resolving.resolver-base": "1100.5.4",
36
- "@pnpm/types": "1101.6.0",
31
+ "@pnpm/deps.path": "1100.0.12",
32
+ "@pnpm/engine.runtime.system-version": "1100.0.7",
33
+ "@pnpm/lockfile.types": "1100.0.17",
34
+ "@pnpm/lockfile.utils": "1100.1.6",
35
+ "@pnpm/resolving.resolver-base": "1100.5.5",
36
+ "@pnpm/types": "1101.7.0",
37
37
  "detect-libc": "^2.1.2"
38
38
  },
39
39
  "devDependencies": {
40
40
  "@jest/globals": "30.4.1",
41
- "@pnpm/deps.graph-hasher": "1100.2.12"
41
+ "@pnpm/deps.graph-hasher": "1100.2.13"
42
42
  },
43
43
  "engines": {
44
44
  "node": ">=22.13"