@pnpm/deps.graph-builder 1100.0.21 → 1100.0.23

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,32 @@
1
1
  # @pnpm/deps.graph-builder
2
2
 
3
+ ## 1100.0.23
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies:
8
+ - @pnpm/config.package-is-installable@1100.0.14
9
+ - @pnpm/core-loggers@1100.2.3
10
+ - @pnpm/fs.symlink-dependency@1100.0.12
11
+
12
+ ## 1100.0.22
13
+
14
+ ### Patch Changes
15
+
16
+ - Updated dependencies:
17
+ - @pnpm/config.package-is-installable@1100.0.13
18
+ - @pnpm/core-loggers@1100.2.2
19
+ - @pnpm/deps.graph-hasher@1100.2.10
20
+ - @pnpm/deps.path@1100.0.9
21
+ - @pnpm/fs.symlink-dependency@1100.0.11
22
+ - @pnpm/hooks.types@1100.2.1
23
+ - @pnpm/installing.modules-yaml@1100.0.10
24
+ - @pnpm/lockfile.fs@1100.1.11
25
+ - @pnpm/lockfile.utils@1100.1.3
26
+ - @pnpm/patching.config@1100.0.10
27
+ - @pnpm/store.controller-types@1100.1.8
28
+ - @pnpm/types@1101.4.0
29
+
3
30
  ## 1100.0.21
4
31
 
5
32
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pnpm/deps.graph-builder",
3
- "version": "1100.0.21",
3
+ "version": "1100.0.23",
4
4
  "description": "A package for building a dependency graph from a lockfile",
5
5
  "keywords": [
6
6
  "pnpm",
@@ -27,20 +27,20 @@
27
27
  "!*.map"
28
28
  ],
29
29
  "dependencies": {
30
- "@pnpm/config.package-is-installable": "1100.0.12",
30
+ "@pnpm/config.package-is-installable": "1100.0.14",
31
31
  "@pnpm/constants": "1100.0.0",
32
- "@pnpm/core-loggers": "1100.2.1",
33
- "@pnpm/deps.graph-hasher": "1100.2.9",
34
- "@pnpm/deps.path": "1100.0.8",
35
- "@pnpm/fs.symlink-dependency": "1100.0.10",
36
- "@pnpm/hooks.types": "1100.2.0",
37
- "@pnpm/installing.modules-yaml": "1100.0.9",
38
- "@pnpm/lockfile.fs": "1100.1.10",
39
- "@pnpm/lockfile.utils": "1100.1.2",
40
- "@pnpm/patching.config": "1100.0.9",
32
+ "@pnpm/core-loggers": "1100.2.3",
33
+ "@pnpm/deps.graph-hasher": "1100.2.10",
34
+ "@pnpm/deps.path": "1100.0.9",
35
+ "@pnpm/fs.symlink-dependency": "1100.0.12",
36
+ "@pnpm/hooks.types": "1100.2.1",
37
+ "@pnpm/installing.modules-yaml": "1100.0.10",
38
+ "@pnpm/lockfile.fs": "1100.1.11",
39
+ "@pnpm/lockfile.utils": "1100.1.3",
40
+ "@pnpm/patching.config": "1100.0.10",
41
41
  "@pnpm/patching.types": "1100.0.0",
42
- "@pnpm/store.controller-types": "1100.1.7",
43
- "@pnpm/types": "1101.3.2",
42
+ "@pnpm/store.controller-types": "1100.1.8",
43
+ "@pnpm/types": "1101.4.0",
44
44
  "path-exists": "^5.0.0",
45
45
  "ramda": "npm:@pnpm/ramda@0.28.1"
46
46
  },
@@ -49,7 +49,7 @@
49
49
  },
50
50
  "devDependencies": {
51
51
  "@jest/globals": "30.4.1",
52
- "@pnpm/deps.graph-builder": "1100.0.21",
52
+ "@pnpm/deps.graph-builder": "1100.0.23",
53
53
  "@pnpm/logger": "1100.0.0",
54
54
  "@types/ramda": "0.31.1"
55
55
  },
package/lib/index.d.ts DELETED
@@ -1 +0,0 @@
1
- export * from './lockfileToDepGraph.js';
@@ -1,16 +0,0 @@
1
- import { type PkgMetaAndSnapshot } from '@pnpm/deps.graph-hasher';
2
- import type { LockfileObject } from '@pnpm/lockfile.fs';
3
- import type { AllowBuild, SupportedArchitectures } from '@pnpm/types';
4
- interface PkgSnapshotWithLocation {
5
- pkgMeta: PkgMetaAndSnapshot;
6
- dirInVirtualStore: string;
7
- }
8
- export declare function iteratePkgsForVirtualStore(lockfile: LockfileObject, opts: {
9
- allowBuild?: AllowBuild;
10
- enableGlobalVirtualStore?: boolean;
11
- virtualStoreDirMaxLength: number;
12
- virtualStoreDir: string;
13
- globalVirtualStoreDir: string;
14
- supportedArchitectures?: SupportedArchitectures;
15
- }): IterableIterator<PkgSnapshotWithLocation>;
16
- export {};
@@ -1,65 +0,0 @@
1
- import path from 'node:path';
2
- import { calcGraphNodeHash, findRuntimeNodeVersion, iterateHashedGraphNodes, iteratePkgMeta, lockfileToDepGraph, } from '@pnpm/deps.graph-hasher';
3
- import * as dp from '@pnpm/deps.path';
4
- import { nameVerFromPkgSnapshot, } from '@pnpm/lockfile.utils';
5
- export function* iteratePkgsForVirtualStore(lockfile, opts) {
6
- // Resolve the project's pinned runtime Node version once per
7
- // invocation — the result drives every snapshot's GVS hash (or
8
- // the side-effects-cache key prefix in the non-GVS runtime
9
- // branch). `undefined` when no `engines.runtime` / `devEngines.runtime`
10
- // pin reached the lockfile, in which case the hasher falls through
11
- // to the host-detected Node.
12
- const nodeVersion = findRuntimeNodeVersion(Object.keys(lockfile.packages ?? {}));
13
- if (opts.enableGlobalVirtualStore) {
14
- for (const { hash, pkgMeta } of hashDependencyPaths(lockfile, {
15
- allowBuild: opts.allowBuild,
16
- supportedArchitectures: opts.supportedArchitectures,
17
- nodeVersion,
18
- })) {
19
- yield {
20
- dirInVirtualStore: path.join(opts.globalVirtualStoreDir, hash),
21
- pkgMeta,
22
- };
23
- }
24
- }
25
- else if (lockfile.packages) {
26
- let graphNodeHashOpts;
27
- for (const depPath in lockfile.packages) {
28
- if (!Object.hasOwn(lockfile.packages, depPath)) {
29
- continue;
30
- }
31
- const pkgSnapshot = lockfile.packages[depPath];
32
- const { name, version } = nameVerFromPkgSnapshot(depPath, pkgSnapshot);
33
- const pkgMeta = {
34
- depPath: depPath,
35
- pkgIdWithPatchHash: dp.getPkgIdWithPatchHash(depPath),
36
- name,
37
- version,
38
- pkgSnapshot,
39
- };
40
- let dirInVirtualStore;
41
- if (dp.isRuntimeDepPath(depPath)) {
42
- graphNodeHashOpts ??= {
43
- cache: {},
44
- graph: lockfileToDepGraph(lockfile, opts.supportedArchitectures),
45
- supportedArchitectures: opts.supportedArchitectures,
46
- nodeVersion,
47
- };
48
- const hash = calcGraphNodeHash(graphNodeHashOpts, pkgMeta);
49
- dirInVirtualStore = path.join(opts.globalVirtualStoreDir, hash);
50
- }
51
- else {
52
- dirInVirtualStore = path.join(opts.virtualStoreDir, dp.depPathToFilename(depPath, opts.virtualStoreDirMaxLength));
53
- }
54
- yield {
55
- dirInVirtualStore,
56
- pkgMeta,
57
- };
58
- }
59
- }
60
- }
61
- function hashDependencyPaths(lockfile, { allowBuild, supportedArchitectures, nodeVersion, }) {
62
- const graph = lockfileToDepGraph(lockfile, supportedArchitectures);
63
- return iterateHashedGraphNodes(graph, iteratePkgMeta(lockfile, graph), allowBuild, supportedArchitectures, nodeVersion);
64
- }
65
- //# sourceMappingURL=iteratePkgsForVirtualStore.js.map
@@ -1,87 +0,0 @@
1
- import type { IncludedDependencies } from '@pnpm/installing.modules-yaml';
2
- import type { LockfileObject, LockfileResolution } from '@pnpm/lockfile.fs';
3
- import { type PatchGroupRecord } from '@pnpm/patching.config';
4
- import type { PatchInfo } from '@pnpm/patching.types';
5
- import type { PkgRequestFetchResult, StoreController } from '@pnpm/store.controller-types';
6
- import type { AllowBuild, DepPath, PkgIdWithPatchHash, ProjectId, Registries, SupportedArchitectures } from '@pnpm/types';
7
- export interface DependenciesGraphNode {
8
- alias?: string;
9
- hasBundledDependencies: boolean;
10
- modules: string;
11
- name: string;
12
- version: string;
13
- fetching?: () => Promise<PkgRequestFetchResult>;
14
- forceImportPackage?: boolean;
15
- dir: string;
16
- children: Record<string, string>;
17
- optionalDependencies: Set<string>;
18
- optional: boolean;
19
- depPath: DepPath;
20
- pkgIdWithPatchHash: PkgIdWithPatchHash;
21
- isBuilt?: boolean;
22
- requiresBuild?: boolean;
23
- hasBin: boolean;
24
- filesIndexFile?: string;
25
- patch?: PatchInfo;
26
- resolution: LockfileResolution;
27
- }
28
- export interface DependenciesGraph {
29
- [depPath: string]: DependenciesGraphNode;
30
- }
31
- export interface LockfileToDepGraphOptions {
32
- allowBuild?: AllowBuild;
33
- autoInstallPeers: boolean;
34
- enableGlobalVirtualStore?: boolean;
35
- engineStrict: boolean;
36
- force: boolean;
37
- importerIds: ProjectId[];
38
- include: IncludedDependencies;
39
- includeUnchangedDeps?: boolean;
40
- ignoreScripts: boolean;
41
- /**
42
- * When true, skip fetching local dependencies (file: protocol pointing to directories).
43
- * This is useful for `pnpm fetch` which only downloads packages from the registry
44
- * and doesn't need local packages that won't be available (e.g., in Docker builds).
45
- */
46
- ignoreLocalPackages?: boolean;
47
- lockfileDir: string;
48
- nodeVersion: string;
49
- pnpmVersion: string;
50
- patchedDependencies?: PatchGroupRecord;
51
- registries: Registries;
52
- sideEffectsCacheRead: boolean;
53
- skipped: Set<DepPath>;
54
- storeController: StoreController;
55
- storeDir: string;
56
- globalVirtualStoreDir: string;
57
- virtualStoreDir: string;
58
- supportedArchitectures?: SupportedArchitectures;
59
- virtualStoreDirMaxLength: number;
60
- }
61
- export interface DirectDependenciesByImporterId {
62
- [importerId: string]: {
63
- [alias: string]: string;
64
- };
65
- }
66
- export interface DepHierarchy {
67
- [depPath: string]: Record<string, DepHierarchy>;
68
- }
69
- export interface LockfileToDepGraphResult {
70
- directDependenciesByImporterId: DirectDependenciesByImporterId;
71
- graph: DependenciesGraph;
72
- hierarchy?: DepHierarchy;
73
- hoistedLocations?: Record<string, string[]>;
74
- symlinkedDirectDependenciesByImporterId?: DirectDependenciesByImporterId;
75
- prevGraph?: DependenciesGraph;
76
- injectionTargetsByDepPath: Map<string, string[]>;
77
- }
78
- /**
79
- * Generate a dependency graph from lockfiles.
80
- *
81
- * If a current lockfile is provided, this function only includes new or changed
82
- * packages in the graph. In other words, the graph returned will be a set
83
- * subtraction of the packages in the wanted lockfile minus the current
84
- * lockfile. This behavior can be configured with the `includeUnchangedDeps`
85
- * option.
86
- */
87
- export declare function lockfileToDepGraph(lockfile: LockfileObject, currentLockfile: LockfileObject | null, opts: LockfileToDepGraphOptions): Promise<LockfileToDepGraphResult>;
@@ -1,229 +0,0 @@
1
- import fs from 'node:fs';
2
- import path from 'node:path';
3
- import { packageIsInstallable } from '@pnpm/config.package-is-installable';
4
- import { WANTED_LOCKFILE } from '@pnpm/constants';
5
- import { progressLogger, } from '@pnpm/core-loggers';
6
- import * as dp from '@pnpm/deps.path';
7
- import { safeJoinModulesDir } from '@pnpm/fs.symlink-dependency';
8
- import { packageIdFromSnapshot, pkgSnapshotToResolution, } from '@pnpm/lockfile.utils';
9
- import { logger } from '@pnpm/logger';
10
- import { getPatchInfo } from '@pnpm/patching.config';
11
- import { pathExists } from 'path-exists';
12
- import { equals, isEmpty } from 'ramda';
13
- import { iteratePkgsForVirtualStore } from './iteratePkgsForVirtualStore.js';
14
- const brokenModulesLogger = logger('_broken_node_modules');
15
- /**
16
- * Generate a dependency graph from lockfiles.
17
- *
18
- * If a current lockfile is provided, this function only includes new or changed
19
- * packages in the graph. In other words, the graph returned will be a set
20
- * subtraction of the packages in the wanted lockfile minus the current
21
- * lockfile. This behavior can be configured with the `includeUnchangedDeps`
22
- * option.
23
- */
24
- export async function lockfileToDepGraph(lockfile, currentLockfile, opts) {
25
- const { graph, locationByDepPath, injectionTargetsByDepPath, } = await buildGraphFromPackages(lockfile, currentLockfile, opts);
26
- const _getChildrenPaths = getChildrenPaths.bind(null, {
27
- force: opts.force,
28
- graph,
29
- lockfileDir: opts.lockfileDir,
30
- registries: opts.registries,
31
- sideEffectsCacheRead: opts.sideEffectsCacheRead,
32
- skipped: opts.skipped,
33
- storeController: opts.storeController,
34
- storeDir: opts.storeDir,
35
- virtualStoreDir: opts.virtualStoreDir,
36
- virtualStoreDirMaxLength: opts.virtualStoreDirMaxLength,
37
- locationByDepPath,
38
- });
39
- for (const node of Object.values(graph)) {
40
- const pkgSnapshot = lockfile.packages[node.depPath];
41
- const allDeps = {
42
- ...pkgSnapshot.dependencies,
43
- ...(opts.include.optionalDependencies ? pkgSnapshot.optionalDependencies : {}),
44
- };
45
- const peerDeps = pkgSnapshot.peerDependencies ? new Set(Object.keys(pkgSnapshot.peerDependencies)) : null;
46
- node.children = _getChildrenPaths(allDeps, peerDeps, '.');
47
- }
48
- const directDependenciesByImporterId = {};
49
- for (const importerId of opts.importerIds) {
50
- const projectSnapshot = lockfile.importers[importerId];
51
- const rootDeps = {
52
- ...(opts.include.devDependencies ? projectSnapshot.devDependencies : {}),
53
- ...(opts.include.dependencies ? projectSnapshot.dependencies : {}),
54
- ...(opts.include.optionalDependencies ? projectSnapshot.optionalDependencies : {}),
55
- };
56
- directDependenciesByImporterId[importerId] = _getChildrenPaths(rootDeps, null, importerId);
57
- }
58
- return { graph, directDependenciesByImporterId, injectionTargetsByDepPath };
59
- }
60
- async function buildGraphFromPackages(lockfile, currentLockfile, opts) {
61
- const currentPackages = currentLockfile?.packages ?? {};
62
- const graph = {};
63
- const locationByDepPath = {};
64
- // Only populated for directory deps (injected workspace packages)
65
- const injectionTargetsByDepPath = new Map();
66
- const _getPatchInfo = getPatchInfo.bind(null, opts.patchedDependencies);
67
- const promises = [];
68
- const pkgSnapshotsWithLocations = iteratePkgsForVirtualStore(lockfile, opts);
69
- for (const { dirInVirtualStore, pkgMeta } of pkgSnapshotsWithLocations) {
70
- promises.push((async () => {
71
- const { pkgIdWithPatchHash, name: pkgName, version: pkgVersion, depPath, pkgSnapshot } = pkgMeta;
72
- if (opts.skipped.has(depPath))
73
- return;
74
- const pkg = {
75
- name: pkgName,
76
- version: pkgVersion,
77
- engines: pkgSnapshot.engines,
78
- cpu: pkgSnapshot.cpu,
79
- os: pkgSnapshot.os,
80
- libc: pkgSnapshot.libc,
81
- };
82
- const packageId = packageIdFromSnapshot(depPath, pkgSnapshot);
83
- if (!opts.force && packageIsInstallable(packageId, pkg, {
84
- engineStrict: opts.engineStrict,
85
- lockfileDir: opts.lockfileDir,
86
- nodeVersion: opts.nodeVersion,
87
- optional: pkgSnapshot.optional === true,
88
- supportedArchitectures: opts.supportedArchitectures,
89
- }) === false) {
90
- opts.skipped.add(depPath);
91
- return;
92
- }
93
- const isDirectoryDep = 'directory' in pkgSnapshot.resolution && pkgSnapshot.resolution.directory != null;
94
- if (isDirectoryDep && opts.ignoreLocalPackages) {
95
- logger.info({
96
- message: `Skipping local dependency ${pkgName}@${pkgVersion} (file: protocol)`,
97
- prefix: opts.lockfileDir,
98
- });
99
- return;
100
- }
101
- const depIsPresent = !isDirectoryDep &&
102
- currentPackages[depPath] &&
103
- equals(currentPackages[depPath].dependencies, pkgSnapshot.dependencies);
104
- const depIntegrityIsUnchanged = isIntegrityEqual(pkgSnapshot.resolution, currentPackages[depPath]?.resolution);
105
- const modules = path.join(dirInVirtualStore, 'node_modules');
106
- // `pkgName` is reconstructed from the (attacker-controllable) lockfile
107
- // depPath key via `dp.parse`, which does no validation. Contain it here so
108
- // a traversal name (e.g. `../../../tmp/x`) can't make the package import
109
- // escape the virtual store. Mirrors the guard on the hoisted linker.
110
- const dir = safeJoinModulesDir(modules, pkgName);
111
- locationByDepPath[depPath] = dir;
112
- // Track directory deps for injected workspace packages
113
- if (isDirectoryDep) {
114
- injectionTargetsByDepPath.set(depPath, [dir]);
115
- }
116
- // In GVS mode, packages that are allowed to build may have a .pnpm-needs-build
117
- // marker indicating a previous build failed or was interrupted. When the
118
- // marker is present, skip the fast path to force a re-fetch/re-import/re-build.
119
- const mightNeedBuild = opts.enableGlobalVirtualStore &&
120
- opts.allowBuild?.(depPath) === true;
121
- let dirExists;
122
- if (depIsPresent &&
123
- depIntegrityIsUnchanged &&
124
- isEmpty(currentPackages[depPath].optionalDependencies ?? {}) &&
125
- isEmpty(pkgSnapshot.optionalDependencies ?? {}) &&
126
- !opts.includeUnchangedDeps) {
127
- dirExists = await pathExists(dir);
128
- if (dirExists) {
129
- if (!(mightNeedBuild && fs.existsSync(path.join(dir, '.pnpm-needs-build'))))
130
- return;
131
- }
132
- else {
133
- brokenModulesLogger.debug({ missing: dir });
134
- }
135
- }
136
- let fetchResponse;
137
- if (depIsPresent && depIntegrityIsUnchanged && equals(currentPackages[depPath].optionalDependencies, pkgSnapshot.optionalDependencies)) {
138
- if (dirExists ?? await pathExists(dir)) {
139
- if (!(mightNeedBuild && fs.existsSync(path.join(dir, '.pnpm-needs-build')))) {
140
- fetchResponse = {};
141
- }
142
- }
143
- else {
144
- brokenModulesLogger.debug({ missing: dir });
145
- }
146
- }
147
- if (!fetchResponse && opts.enableGlobalVirtualStore && !isDirectoryDep
148
- && !opts.force) {
149
- if (dirExists ?? await pathExists(dir)) {
150
- if (!(mightNeedBuild && fs.existsSync(path.join(dir, '.pnpm-needs-build')))) {
151
- fetchResponse = {};
152
- }
153
- }
154
- }
155
- if (!fetchResponse) {
156
- const resolution = pkgSnapshotToResolution(depPath, pkgSnapshot, opts.registries);
157
- progressLogger.debug({ packageId, requester: opts.lockfileDir, status: 'resolved' });
158
- try {
159
- fetchResponse = await opts.storeController.fetchPackage({
160
- allowBuild: opts.allowBuild,
161
- force: false,
162
- lockfileDir: opts.lockfileDir,
163
- ignoreScripts: opts.ignoreScripts,
164
- pkg: { name: pkgName, version: pkgVersion, id: packageId, resolution },
165
- supportedArchitectures: opts.supportedArchitectures,
166
- });
167
- }
168
- catch (err) {
169
- if (pkgSnapshot.optional)
170
- return;
171
- throw err;
172
- }
173
- }
174
- graph[dir] = {
175
- children: {},
176
- pkgIdWithPatchHash,
177
- resolution: pkgSnapshot.resolution,
178
- depPath,
179
- dir,
180
- fetching: fetchResponse.fetching,
181
- filesIndexFile: fetchResponse.filesIndexFile,
182
- forceImportPackage: !depIntegrityIsUnchanged,
183
- hasBin: pkgSnapshot.hasBin === true,
184
- hasBundledDependencies: pkgSnapshot.bundledDependencies != null,
185
- modules,
186
- name: pkgName,
187
- version: pkgVersion,
188
- optional: !!pkgSnapshot.optional,
189
- optionalDependencies: new Set(Object.keys(pkgSnapshot.optionalDependencies ?? {})),
190
- patch: _getPatchInfo(pkgName, pkgVersion),
191
- };
192
- })());
193
- }
194
- await Promise.all(promises);
195
- return { graph, locationByDepPath, injectionTargetsByDepPath };
196
- }
197
- function getChildrenPaths(ctx, allDeps, peerDeps, importerId) {
198
- const children = {};
199
- for (const [alias, ref] of Object.entries(allDeps)) {
200
- const childDepPath = dp.refToRelative(ref, alias);
201
- if (childDepPath === null) {
202
- children[alias] = path.resolve(ctx.lockfileDir, importerId, ref.slice(5));
203
- continue;
204
- }
205
- const childRelDepPath = dp.refToRelative(ref, alias);
206
- if (ctx.locationByDepPath[childRelDepPath]) {
207
- children[alias] = ctx.locationByDepPath[childRelDepPath];
208
- }
209
- else if (ctx.graph[childRelDepPath]) {
210
- children[alias] = ctx.graph[childRelDepPath].dir;
211
- }
212
- else if (ref.startsWith('file:')) {
213
- children[alias] = path.resolve(ctx.lockfileDir, ref.slice(5));
214
- }
215
- else if (!ctx.skipped.has(childRelDepPath) && ((peerDeps == null) || !peerDeps.has(alias))) {
216
- throw new Error(`${childRelDepPath} not found in ${WANTED_LOCKFILE}`);
217
- }
218
- }
219
- return children;
220
- }
221
- function isIntegrityEqual(resolutionA, resolutionB) {
222
- // The LockfileResolution type is a union, but it doesn't have a "tag"
223
- // field to perform a discriminant match on. Using a type assertion is
224
- // required to get the integrity field.
225
- const integrityA = resolutionA?.integrity;
226
- const integrityB = resolutionB?.integrity;
227
- return integrityA === integrityB;
228
- }
229
- //# sourceMappingURL=lockfileToDepGraph.js.map