@pnpm/installing.deps-resolver 1008.3.1

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.
Files changed (47) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +17 -0
  3. package/lib/dedupeInjectedDeps.d.ts +14 -0
  4. package/lib/dedupeInjectedDeps.js +72 -0
  5. package/lib/depPathToRef.d.ts +4 -0
  6. package/lib/depPathToRef.js +7 -0
  7. package/lib/getCatalogSnapshots.d.ts +4 -0
  8. package/lib/getCatalogSnapshots.js +21 -0
  9. package/lib/getExactSinglePreferredVersions.d.ts +6 -0
  10. package/lib/getExactSinglePreferredVersions.js +11 -0
  11. package/lib/getNonDevWantedDependencies.d.ts +12 -0
  12. package/lib/getNonDevWantedDependencies.js +29 -0
  13. package/lib/getWantedDependencies.d.ts +14 -0
  14. package/lib/getWantedDependencies.js +45 -0
  15. package/lib/hoistPeers.d.ts +10 -0
  16. package/lib/hoistPeers.js +75 -0
  17. package/lib/index.d.ts +53 -0
  18. package/lib/index.js +335 -0
  19. package/lib/mergePeers.d.ts +7 -0
  20. package/lib/mergePeers.js +30 -0
  21. package/lib/nextNodeId.d.ts +6 -0
  22. package/lib/nextNodeId.js +5 -0
  23. package/lib/parentIdsContainSequence.d.ts +2 -0
  24. package/lib/parentIdsContainSequence.js +9 -0
  25. package/lib/replaceVersionInBareSpecifier.d.ts +1 -0
  26. package/lib/replaceVersionInBareSpecifier.js +15 -0
  27. package/lib/resolveDependencies.d.ts +248 -0
  28. package/lib/resolveDependencies.js +1182 -0
  29. package/lib/resolveDependencyTree.d.ts +122 -0
  30. package/lib/resolveDependencyTree.js +228 -0
  31. package/lib/resolvePeers.d.ts +59 -0
  32. package/lib/resolvePeers.js +702 -0
  33. package/lib/safeIsInnerLink.d.ts +6 -0
  34. package/lib/safeIsInnerLink.js +31 -0
  35. package/lib/toResolveImporter.d.ts +22 -0
  36. package/lib/toResolveImporter.js +117 -0
  37. package/lib/unwrapPackageName.d.ts +23 -0
  38. package/lib/unwrapPackageName.js +39 -0
  39. package/lib/updateLockfile.d.ts +10 -0
  40. package/lib/updateLockfile.js +121 -0
  41. package/lib/updateProjectManifest.d.ts +8 -0
  42. package/lib/updateProjectManifest.js +34 -0
  43. package/lib/validatePeerDependencies.d.ts +6 -0
  44. package/lib/validatePeerDependencies.js +15 -0
  45. package/lib/wantedDepIsLocallyAvailable.d.ts +6 -0
  46. package/lib/wantedDepIsLocallyAvailable.js +23 -0
  47. package/package.json +91 -0
@@ -0,0 +1,1182 @@
1
+ import path from 'node:path';
2
+ import { matchCatalogResolveResult } from '@pnpm/catalogs.resolver';
3
+ import { deprecationLogger, progressLogger, skippedOptionalDependencyLogger, } from '@pnpm/core-loggers';
4
+ import * as dp from '@pnpm/deps.path';
5
+ import { PnpmError } from '@pnpm/error';
6
+ import { getPreferredVersionsFromLockfileAndManifests } from '@pnpm/lockfile.preferred-versions';
7
+ import { nameVerFromPkgSnapshot, pkgSnapshotToResolution, } from '@pnpm/lockfile.utils';
8
+ import { logger } from '@pnpm/logger';
9
+ import { getPatchInfo } from '@pnpm/patching.config';
10
+ import { convertEnginesRuntimeToDependencies } from '@pnpm/pkg-manifest.utils';
11
+ import { DIRECT_DEP_SELECTOR_WEIGHT, } from '@pnpm/resolving.resolver-base';
12
+ import normalizePath from 'normalize-path';
13
+ import pDefer from 'p-defer';
14
+ import { pathExists } from 'path-exists';
15
+ import { pShare } from 'promise-share';
16
+ import { omit, pickBy, zipWith } from 'ramda';
17
+ import semver from 'semver';
18
+ import { getExactSinglePreferredVersions } from './getExactSinglePreferredVersions.js';
19
+ import { getNonDevWantedDependencies } from './getNonDevWantedDependencies.js';
20
+ import { getHoistableOptionalPeers, hoistPeers } from './hoistPeers.js';
21
+ import { safeIntersect } from './mergePeers.js';
22
+ import { nextNodeId } from './nextNodeId.js';
23
+ import { parentIdsContainSequence } from './parentIdsContainSequence.js';
24
+ import { replaceVersionInBareSpecifier } from './replaceVersionInBareSpecifier.js';
25
+ import { wantedDepIsLocallyAvailable } from './wantedDepIsLocallyAvailable.js';
26
+ const dependencyResolvedLogger = logger('_dependency_resolved');
27
+ const omitDepsFields = omit(['dependencies', 'optionalDependencies', 'peerDependencies', 'peerDependenciesMeta']);
28
+ export function getPkgsInfoFromIds(ids, resolvedPkgsById) {
29
+ return ids
30
+ .slice(1)
31
+ .map((id) => {
32
+ const { name, version } = resolvedPkgsById[id];
33
+ return { id, name, version };
34
+ });
35
+ }
36
+ export async function resolveRootDependencies(ctx, importers) {
37
+ if (ctx.autoInstallPeers) {
38
+ ctx.allPreferredVersions = getPreferredVersionsFromLockfileAndManifests(ctx.wantedLockfile.packages, []);
39
+ }
40
+ else if (ctx.hoistPeers) {
41
+ ctx.allPreferredVersions = {};
42
+ }
43
+ const { pkgAddressesByImportersWithoutPeers, publishedBy, time } = await resolveDependenciesOfImporters(ctx, importers);
44
+ if (!ctx.hoistPeers) {
45
+ return {
46
+ pkgAddressesByImporters: pkgAddressesByImportersWithoutPeers.map(({ pkgAddresses }) => pkgAddresses),
47
+ time,
48
+ };
49
+ }
50
+ let workspaceRootDeps;
51
+ if (ctx.resolvePeersFromWorkspaceRoot) {
52
+ const rootImporterIndex = importers.findIndex(({ options }) => options.parentIds[0] === '.');
53
+ workspaceRootDeps = pkgAddressesByImportersWithoutPeers[rootImporterIndex]?.pkgAddresses ?? [];
54
+ }
55
+ else {
56
+ workspaceRootDeps = [];
57
+ }
58
+ const _hoistPeers = hoistPeers.bind(null, {
59
+ autoInstallPeers: ctx.autoInstallPeers,
60
+ allPreferredVersions: ctx.allPreferredVersions,
61
+ workspaceRootDeps,
62
+ });
63
+ /* eslint-disable no-await-in-loop */
64
+ while (true) {
65
+ const allMissingOptionalPeersByImporters = await Promise.all(pkgAddressesByImportersWithoutPeers.map(async (importerResolutionResult, index) => {
66
+ const { parentPkgAliases, preferredVersions, options } = importers[index];
67
+ const allMissingOptionalPeers = {};
68
+ while (true) {
69
+ for (const pkgAddress of importerResolutionResult.pkgAddresses) {
70
+ parentPkgAliases[pkgAddress.alias] = true;
71
+ }
72
+ const missingOptionalPeers = [];
73
+ const missingRequiredPeers = [];
74
+ for (const [peerName, peerInfo] of Object.entries(importerResolutionResult.missingPeers ?? {})) {
75
+ if (peerInfo.optional) {
76
+ missingOptionalPeers.push([peerName, peerInfo]);
77
+ }
78
+ else {
79
+ missingRequiredPeers.push([peerName, peerInfo]);
80
+ parentPkgAliases[peerName] = true;
81
+ }
82
+ }
83
+ if (ctx.autoInstallPeers) {
84
+ // All the missing peers should get installed in the root.
85
+ // Otherwise, pending nodes will not work.
86
+ // even those peers should be hoisted that are not autoinstalled
87
+ for (const [resolvedPeerName, resolvedPeerAddress] of Object.entries(importerResolutionResult.resolvedPeers ?? {})) {
88
+ if (!parentPkgAliases[resolvedPeerName]) {
89
+ importerResolutionResult.pkgAddresses.push(resolvedPeerAddress);
90
+ }
91
+ }
92
+ }
93
+ for (const [missingOptionalPeerName, { range: missingOptionalPeerRange }] of missingOptionalPeers) {
94
+ if (!allMissingOptionalPeers[missingOptionalPeerName]) {
95
+ allMissingOptionalPeers[missingOptionalPeerName] = [missingOptionalPeerRange];
96
+ }
97
+ else if (!allMissingOptionalPeers[missingOptionalPeerName].includes(missingOptionalPeerRange)) {
98
+ allMissingOptionalPeers[missingOptionalPeerName].push(missingOptionalPeerRange);
99
+ }
100
+ }
101
+ if (!missingRequiredPeers.length)
102
+ break;
103
+ const dependencies = _hoistPeers(missingRequiredPeers);
104
+ if (!Object.keys(dependencies).length)
105
+ break;
106
+ const wantedDependencies = getNonDevWantedDependencies({ dependencies });
107
+ const resolveDependenciesResult = await resolveDependencies(ctx, preferredVersions, wantedDependencies, {
108
+ ...options,
109
+ parentPkgAliases,
110
+ publishedBy,
111
+ updateToLatest: false,
112
+ });
113
+ importerResolutionResult.pkgAddresses.push(...resolveDependenciesResult.pkgAddresses);
114
+ Object.assign(importerResolutionResult, filterMissingPeers(await resolveDependenciesResult.resolvingPeers, parentPkgAliases));
115
+ }
116
+ return allMissingOptionalPeers;
117
+ }));
118
+ let hasNewMissingPeers = false;
119
+ await Promise.all(allMissingOptionalPeersByImporters.map(async (allMissingOptionalPeers, index) => {
120
+ const { preferredVersions, parentPkgAliases, options } = importers[index];
121
+ if (Object.keys(allMissingOptionalPeers).length && ctx.allPreferredVersions) {
122
+ const optionalDependencies = getHoistableOptionalPeers(allMissingOptionalPeers, ctx.allPreferredVersions);
123
+ if (Object.keys(optionalDependencies).length) {
124
+ hasNewMissingPeers = true;
125
+ const wantedDependencies = getNonDevWantedDependencies({ optionalDependencies });
126
+ const resolveDependenciesResult = await resolveDependencies(ctx, preferredVersions, wantedDependencies, {
127
+ ...options,
128
+ parentPkgAliases,
129
+ publishedBy,
130
+ updateToLatest: false,
131
+ });
132
+ pkgAddressesByImportersWithoutPeers[index].pkgAddresses.push(...resolveDependenciesResult.pkgAddresses);
133
+ Object.assign(pkgAddressesByImportersWithoutPeers[index], filterMissingPeers(await resolveDependenciesResult.resolvingPeers, parentPkgAliases));
134
+ }
135
+ }
136
+ }));
137
+ if (!hasNewMissingPeers)
138
+ break;
139
+ }
140
+ /* eslint-enable no-await-in-loop */
141
+ return {
142
+ pkgAddressesByImporters: pkgAddressesByImportersWithoutPeers.map(({ pkgAddresses }) => pkgAddresses),
143
+ time,
144
+ };
145
+ }
146
+ async function resolveDependenciesOfImporters(ctx, importers) {
147
+ const pickLowestVersion = ctx.resolutionMode === 'time-based' || ctx.resolutionMode === 'lowest-direct';
148
+ const resolveResults = await Promise.all(importers.map(async (importer) => {
149
+ const extendedWantedDeps = getDepsToResolve(importer.wantedDependencies, ctx.wantedLockfile, {
150
+ preferredDependencies: importer.options.preferredDependencies,
151
+ prefix: importer.options.prefix,
152
+ proceed: importer.options.proceed || ctx.forceFullResolution,
153
+ registries: ctx.registries,
154
+ resolvedDependencies: importer.options.resolvedDependencies,
155
+ });
156
+ const postponedResolutionsQueue = [];
157
+ const postponedPeersResolutionQueue = [];
158
+ const pkgAddresses = [];
159
+ const resolveDependenciesOfImporterWantedDep = resolveDependenciesOfImporterDependency.bind(null, {
160
+ ctx,
161
+ importer,
162
+ pickLowestVersion,
163
+ });
164
+ const resolvedDependenciesOfImporter = await Promise.all(extendedWantedDeps.map(resolveDependenciesOfImporterWantedDep));
165
+ for (const { resolveDependencyResult, postponedPeersResolution, postponedResolution } of resolvedDependenciesOfImporter) {
166
+ if (resolveDependencyResult) {
167
+ pkgAddresses.push(resolveDependencyResult);
168
+ }
169
+ if (postponedResolution) {
170
+ postponedResolutionsQueue.push(postponedResolution);
171
+ }
172
+ if (postponedPeersResolution) {
173
+ postponedPeersResolutionQueue.push(postponedPeersResolution);
174
+ }
175
+ }
176
+ return { pkgAddresses, postponedResolutionsQueue, postponedPeersResolutionQueue };
177
+ }));
178
+ let publishedBy;
179
+ let time;
180
+ if (ctx.resolutionMode === 'time-based') {
181
+ const result = getPublishedByDate(resolveResults.map(({ pkgAddresses }) => pkgAddresses).flat(), ctx.wantedLockfile.time);
182
+ if (result.publishedBy) {
183
+ publishedBy = new Date(result.publishedBy.getTime() + 60 * 60 * 1000); // adding 1 hour delta
184
+ time = result.newTime;
185
+ }
186
+ }
187
+ if (ctx.maximumPublishedBy && (publishedBy == null || publishedBy > ctx.maximumPublishedBy)) {
188
+ publishedBy = ctx.maximumPublishedBy;
189
+ }
190
+ const pkgAddressesByImportersWithoutPeers = await Promise.all(zipWith(async (importer, { pkgAddresses, postponedResolutionsQueue, postponedPeersResolutionQueue }) => {
191
+ const newPreferredVersions = Object.create(importer.preferredVersions);
192
+ const currentParentPkgAliases = {};
193
+ for (const pkgAddress of pkgAddresses) {
194
+ if (currentParentPkgAliases[pkgAddress.alias] !== true) {
195
+ currentParentPkgAliases[pkgAddress.alias] = pkgAddress;
196
+ }
197
+ if (pkgAddress.updated) {
198
+ ctx.updatedSet.add(pkgAddress.alias);
199
+ }
200
+ const resolvedPackage = ctx.resolvedPkgsById[pkgAddress.pkgId];
201
+ if (!resolvedPackage)
202
+ continue; // This will happen only with linked dependencies
203
+ if (!Object.hasOwn(newPreferredVersions, resolvedPackage.name)) {
204
+ newPreferredVersions[resolvedPackage.name] = { ...importer.preferredVersions[resolvedPackage.name] };
205
+ }
206
+ if (!newPreferredVersions[resolvedPackage.name][resolvedPackage.version]) {
207
+ newPreferredVersions[resolvedPackage.name][resolvedPackage.version] = {
208
+ selectorType: 'version',
209
+ weight: DIRECT_DEP_SELECTOR_WEIGHT,
210
+ };
211
+ }
212
+ }
213
+ const newParentPkgAliases = { ...importer.parentPkgAliases, ...currentParentPkgAliases };
214
+ const postponedResolutionOpts = {
215
+ preferredVersions: newPreferredVersions,
216
+ parentPkgAliases: newParentPkgAliases,
217
+ publishedBy,
218
+ };
219
+ const childrenResults = await Promise.all(postponedResolutionsQueue.map((postponedResolution) => postponedResolution(postponedResolutionOpts)));
220
+ if (!ctx.hoistPeers) {
221
+ return {
222
+ missingPeers: {},
223
+ pkgAddresses,
224
+ resolvedPeers: {},
225
+ };
226
+ }
227
+ const postponedPeersResolution = await Promise.all(postponedPeersResolutionQueue.map((postponedMissingPeers) => postponedMissingPeers(postponedResolutionOpts.parentPkgAliases)));
228
+ const resolvedPeers = [...childrenResults, ...postponedPeersResolution].reduce((acc, { resolvedPeers }) => Object.assign(acc, resolvedPeers), {});
229
+ const allMissingPeers = mergePkgsDeps([
230
+ ...filterMissingPeersFromPkgAddresses(pkgAddresses, currentParentPkgAliases, resolvedPeers),
231
+ ...childrenResults,
232
+ ...postponedPeersResolution,
233
+ ].map(({ missingPeers }) => missingPeers).filter(Boolean), {
234
+ autoInstallPeersFromHighestMatch: ctx.autoInstallPeersFromHighestMatch,
235
+ });
236
+ return {
237
+ missingPeers: allMissingPeers,
238
+ pkgAddresses,
239
+ resolvedPeers,
240
+ };
241
+ }, importers, resolveResults));
242
+ return {
243
+ pkgAddressesByImportersWithoutPeers,
244
+ publishedBy,
245
+ time,
246
+ };
247
+ }
248
+ async function resolveDependenciesOfImporterDependency({ ctx, importer, pickLowestVersion }, extendedWantedDep) {
249
+ // The catalog protocol is only usable in importers (i.e. packages in the
250
+ // workspace. Replacing catalog protocol while resolving importers here before
251
+ // resolving dependencies of packages outside of the workspace/monorepo.
252
+ const catalogLookup = matchCatalogResolveResult(ctx.catalogResolver(extendedWantedDep.wantedDependency), {
253
+ found: (result) => result.resolution,
254
+ unused: () => undefined,
255
+ misconfiguration: (result) => {
256
+ throw result.error;
257
+ },
258
+ });
259
+ const originalBareSpecifier = extendedWantedDep.wantedDependency.bareSpecifier;
260
+ // The lockfile from a previous installation may have already resolved this
261
+ // cataloged dependency. Reuse the exact version in the lockfile catalog
262
+ // snapshot to ensure all projects using the same cataloged dependency get the
263
+ // same version.
264
+ if (catalogLookup != null) {
265
+ extendedWantedDep.wantedDependency.bareSpecifier = catalogLookup.specifier;
266
+ extendedWantedDep.preferredVersion = getCatalogExistingVersionFromSnapshot(catalogLookup, ctx.wantedLockfile, extendedWantedDep.wantedDependency);
267
+ }
268
+ const result = await resolveDependenciesOfDependency(ctx, importer.preferredVersions, {
269
+ ...importer.options,
270
+ parentPkgAliases: importer.parentPkgAliases,
271
+ pickLowestVersion: pickLowestVersion && !importer.updatePackageManifest,
272
+ pinnedVersion: importer.pinnedVersion,
273
+ publishedBy: ctx.maximumPublishedBy,
274
+ }, extendedWantedDep);
275
+ // If the catalog protocol was used, store metadata about the catalog
276
+ // lookup to use in the lockfile.
277
+ if (result.resolveDependencyResult != null && catalogLookup != null) {
278
+ result.resolveDependencyResult.catalogLookup = {
279
+ ...catalogLookup,
280
+ userSpecifiedBareSpecifier: originalBareSpecifier,
281
+ };
282
+ }
283
+ return result;
284
+ }
285
+ function filterMissingPeersFromPkgAddresses(pkgAddresses, currentParentPkgAliases, resolvedPeers) {
286
+ return pkgAddresses.map((pkgAddress) => ({
287
+ ...pkgAddress,
288
+ missingPeers: pickBy((_, peerName) => {
289
+ if (!currentParentPkgAliases[peerName])
290
+ return true;
291
+ if (currentParentPkgAliases[peerName] !== true) {
292
+ resolvedPeers[peerName] = currentParentPkgAliases[peerName];
293
+ }
294
+ return false;
295
+ }, pkgAddress.missingPeers ?? {}),
296
+ }));
297
+ }
298
+ function getPublishedByDate(pkgAddresses, timeFromLockfile = {}) {
299
+ const newTime = {};
300
+ for (const pkgAddress of pkgAddresses) {
301
+ if (pkgAddress.publishedAt) {
302
+ newTime[pkgAddress.pkgId] = pkgAddress.publishedAt;
303
+ }
304
+ else if (timeFromLockfile[pkgAddress.pkgId]) {
305
+ newTime[pkgAddress.pkgId] = timeFromLockfile[pkgAddress.pkgId];
306
+ }
307
+ }
308
+ const sortedDates = Object.values(newTime)
309
+ .map((publishedAt) => new Date(publishedAt))
310
+ .sort((d1, d2) => d1.getTime() - d2.getTime());
311
+ return { publishedBy: sortedDates[sortedDates.length - 1], newTime };
312
+ }
313
+ export async function resolveDependencies(ctx, preferredVersions, wantedDependencies, options) {
314
+ const extendedWantedDeps = getDepsToResolve(wantedDependencies, ctx.wantedLockfile, {
315
+ preferredDependencies: options.preferredDependencies,
316
+ prefix: options.prefix,
317
+ proceed: options.proceed || ctx.forceFullResolution,
318
+ registries: ctx.registries,
319
+ resolvedDependencies: options.resolvedDependencies,
320
+ });
321
+ const postponedResolutionsQueue = [];
322
+ const postponedPeersResolutionQueue = [];
323
+ const pkgAddresses = [];
324
+ await Promise.all(extendedWantedDeps.map(async (extendedWantedDep) => {
325
+ const { resolveDependencyResult, postponedResolution, postponedPeersResolution, } = await resolveDependenciesOfDependency(ctx, preferredVersions, options, extendedWantedDep);
326
+ if (resolveDependencyResult) {
327
+ pkgAddresses.push(resolveDependencyResult);
328
+ }
329
+ if (postponedResolution) {
330
+ postponedResolutionsQueue.push(postponedResolution);
331
+ }
332
+ if (postponedPeersResolution) {
333
+ postponedPeersResolutionQueue.push(postponedPeersResolution);
334
+ }
335
+ }));
336
+ const newPreferredVersions = Object.create(preferredVersions);
337
+ const currentParentPkgAliases = {};
338
+ for (const pkgAddress of pkgAddresses) {
339
+ if (currentParentPkgAliases[pkgAddress.alias] !== true) {
340
+ currentParentPkgAliases[pkgAddress.alias] = pkgAddress;
341
+ }
342
+ if (pkgAddress.updated) {
343
+ ctx.updatedSet.add(pkgAddress.alias);
344
+ }
345
+ const resolvedPackage = ctx.resolvedPkgsById[pkgAddress.pkgId];
346
+ if (!resolvedPackage)
347
+ continue; // This will happen only with linked dependencies
348
+ if (!Object.hasOwn(newPreferredVersions, resolvedPackage.name)) {
349
+ newPreferredVersions[resolvedPackage.name] = { ...preferredVersions[resolvedPackage.name] };
350
+ }
351
+ if (!newPreferredVersions[resolvedPackage.name][resolvedPackage.version]) {
352
+ newPreferredVersions[resolvedPackage.name][resolvedPackage.version] = 'version';
353
+ }
354
+ }
355
+ const newParentPkgAliases = {
356
+ ...options.parentPkgAliases,
357
+ ...currentParentPkgAliases,
358
+ };
359
+ const postponedResolutionOpts = {
360
+ preferredVersions: newPreferredVersions,
361
+ parentPkgAliases: newParentPkgAliases,
362
+ publishedBy: options.publishedBy,
363
+ };
364
+ const childrenResults = await Promise.all(postponedResolutionsQueue.map((postponedResolution) => postponedResolution(postponedResolutionOpts)));
365
+ if (!ctx.hoistPeers) {
366
+ return {
367
+ resolvingPeers: Promise.resolve({
368
+ missingPeers: {},
369
+ resolvedPeers: {},
370
+ }),
371
+ pkgAddresses,
372
+ };
373
+ }
374
+ return {
375
+ pkgAddresses,
376
+ resolvingPeers: startResolvingPeers({
377
+ childrenResults,
378
+ pkgAddresses,
379
+ parentPkgAliases: options.parentPkgAliases,
380
+ currentParentPkgAliases,
381
+ postponedPeersResolutionQueue,
382
+ autoInstallPeersFromHighestMatch: ctx.autoInstallPeersFromHighestMatch,
383
+ }),
384
+ };
385
+ }
386
+ async function startResolvingPeers({ childrenResults, currentParentPkgAliases, parentPkgAliases, pkgAddresses, postponedPeersResolutionQueue, autoInstallPeersFromHighestMatch, }) {
387
+ const results = await Promise.all(postponedPeersResolutionQueue.map((postponedPeersResolution) => postponedPeersResolution(parentPkgAliases)));
388
+ const resolvedPeers = [...childrenResults, ...results].reduce((acc, { resolvedPeers }) => Object.assign(acc, resolvedPeers), {});
389
+ const allMissingPeers = mergePkgsDeps([
390
+ ...filterMissingPeersFromPkgAddresses(pkgAddresses, currentParentPkgAliases, resolvedPeers),
391
+ ...childrenResults,
392
+ ...results,
393
+ ].map(({ missingPeers }) => missingPeers).filter(Boolean), { autoInstallPeersFromHighestMatch });
394
+ return {
395
+ missingPeers: allMissingPeers,
396
+ resolvedPeers,
397
+ };
398
+ }
399
+ function mergePkgsDeps(pkgsDeps, opts) {
400
+ const groupedRanges = {};
401
+ for (const deps of pkgsDeps) {
402
+ for (const [name, { range, optional }] of Object.entries(deps)) {
403
+ if (!groupedRanges[name]) {
404
+ groupedRanges[name] = { ranges: [], optional };
405
+ }
406
+ else {
407
+ groupedRanges[name].optional &&= optional;
408
+ }
409
+ groupedRanges[name].ranges.push(range);
410
+ }
411
+ }
412
+ const mergedPkgDeps = {};
413
+ for (const [name, { ranges, optional }] of Object.entries(groupedRanges)) {
414
+ const intersection = safeIntersect(ranges);
415
+ if (intersection) {
416
+ mergedPkgDeps[name] = { range: intersection, optional };
417
+ }
418
+ else if (opts.autoInstallPeersFromHighestMatch) {
419
+ mergedPkgDeps[name] = { range: ranges.join(' || '), optional };
420
+ }
421
+ }
422
+ return mergedPkgDeps;
423
+ }
424
+ async function resolveDependenciesOfDependency(ctx, preferredVersions, options, extendedWantedDep) {
425
+ const updateDepth = typeof extendedWantedDep.wantedDependency.updateDepth === 'number'
426
+ ? extendedWantedDep.wantedDependency.updateDepth
427
+ : options.updateDepth;
428
+ const updateShouldContinue = options.currentDepth <= updateDepth;
429
+ const updateRequested = updateShouldContinue &&
430
+ ((options.updateMatching == null) ||
431
+ (extendedWantedDep.infoFromLockfile?.name != null &&
432
+ options.updateMatching(extendedWantedDep.infoFromLockfile.name, extendedWantedDep.infoFromLockfile.version)));
433
+ const update = updateRequested ||
434
+ ((extendedWantedDep.infoFromLockfile?.dependencyLockfile) == null) || Boolean((ctx.workspacePackages != null) &&
435
+ ctx.linkWorkspacePackagesDepth !== -1 &&
436
+ wantedDepIsLocallyAvailable(ctx.workspacePackages, extendedWantedDep.wantedDependency, { defaultTag: ctx.defaultTag, registry: ctx.registries.default })) || ctx.updatedSet.has(extendedWantedDep.infoFromLockfile.name);
437
+ const resolveDependencyOpts = {
438
+ currentDepth: options.currentDepth,
439
+ parentPkg: options.parentPkg,
440
+ parentPkgAliases: options.parentPkgAliases,
441
+ preferredVersions,
442
+ currentPkg: extendedWantedDep.infoFromLockfile ?? undefined,
443
+ preferredVersion: extendedWantedDep.preferredVersion,
444
+ pickLowestVersion: options.pickLowestVersion,
445
+ prefix: options.prefix,
446
+ proceed: extendedWantedDep.proceed || updateShouldContinue || ctx.updatedSet.size > 0,
447
+ publishedBy: options.publishedBy,
448
+ update: update ? options.updateToLatest ? 'latest' : 'compatible' : false,
449
+ updateDepth,
450
+ updateRequested,
451
+ supportedArchitectures: options.supportedArchitectures,
452
+ parentIds: options.parentIds,
453
+ pinnedVersion: options.pinnedVersion,
454
+ };
455
+ // The catalog protocol is normally replaced when resolving the dependencies
456
+ // of importers. However, when a workspace package is "injected", it becomes a
457
+ // "file:" dependency and is no longer an "importer" from the perspective of
458
+ // pnpm.
459
+ //
460
+ // To allow the catalog protocol to still be used for injected workspace
461
+ // packages, it's necessary to check if the parent package was an injected
462
+ // workspace package and replace the catalog: protocol for the current package.
463
+ const isInjectedWorkspacePackage = options.parentPkg.resolvedVia === 'workspace' &&
464
+ options.parentPkg.pkgId.startsWith('file:');
465
+ if (isInjectedWorkspacePackage) {
466
+ const catalogLookup = matchCatalogResolveResult(ctx.catalogResolver(extendedWantedDep.wantedDependency), {
467
+ found: (result) => result.resolution,
468
+ unused: () => undefined,
469
+ misconfiguration: (result) => {
470
+ throw result.error;
471
+ },
472
+ });
473
+ // The standard process for replacing the catalog protocol when resolving
474
+ // the dependencies of "importers" stores the catalog lookup in the
475
+ // dependency resolution result. This allows the catalogs snapshot section
476
+ // of the wanted lockfile to be kept up to date.
477
+ //
478
+ // We can do a simple replacement here instead and discard the catalog
479
+ // lookup object. It's not necessary to store this information for injected
480
+ // workspace packages. The injected workspace package will still be resolved
481
+ // as an importer separately, and we can rely on that process keeping the
482
+ // importers lockfile catalog snapshots up to date.
483
+ if (catalogLookup != null) {
484
+ extendedWantedDep.wantedDependency.bareSpecifier = catalogLookup.specifier;
485
+ extendedWantedDep.preferredVersion = getCatalogExistingVersionFromSnapshot(catalogLookup, ctx.wantedLockfile, extendedWantedDep.wantedDependency);
486
+ }
487
+ }
488
+ const resolveDependencyResult = await resolveDependency(extendedWantedDep.wantedDependency, ctx, resolveDependencyOpts);
489
+ if (resolveDependencyResult == null)
490
+ return { resolveDependencyResult: null };
491
+ if (resolveDependencyResult.isLinkedDependency) {
492
+ ctx.dependenciesTree.set(createNodeIdForLinkedLocalPkg(ctx.lockfileDir, resolveDependencyResult.resolution.directory), {
493
+ children: {},
494
+ depth: -1,
495
+ installable: true,
496
+ resolvedPackage: {
497
+ name: resolveDependencyResult.name,
498
+ version: resolveDependencyResult.version,
499
+ },
500
+ });
501
+ return { resolveDependencyResult };
502
+ }
503
+ if (!resolveDependencyResult.isNew) {
504
+ return {
505
+ resolveDependencyResult,
506
+ postponedPeersResolution: resolveDependencyResult.missingPeersOfChildren != null
507
+ ? async (parentPkgAliases) => {
508
+ const missingPeers = await resolveDependencyResult.missingPeersOfChildren.get();
509
+ return filterMissingPeers({ missingPeers, resolvedPeers: {} }, parentPkgAliases);
510
+ }
511
+ : undefined,
512
+ };
513
+ }
514
+ const postponedResolution = resolveChildren.bind(null, ctx, {
515
+ parentPkg: resolveDependencyResult,
516
+ dependencyLockfile: extendedWantedDep.infoFromLockfile?.dependencyLockfile,
517
+ parentDepth: options.currentDepth,
518
+ parentIds: [...options.parentIds, resolveDependencyResult.pkgId],
519
+ updateDepth,
520
+ prefix: options.prefix,
521
+ updateMatching: options.updateMatching,
522
+ supportedArchitectures: options.supportedArchitectures,
523
+ updateToLatest: options.updateToLatest,
524
+ });
525
+ return {
526
+ resolveDependencyResult,
527
+ postponedResolution: async (postponedResolutionOpts) => {
528
+ const { missingPeers, resolvedPeers } = await postponedResolution(postponedResolutionOpts);
529
+ if (resolveDependencyResult.missingPeersOfChildren) {
530
+ resolveDependencyResult.missingPeersOfChildren.resolved = true;
531
+ resolveDependencyResult.missingPeersOfChildren.resolve(missingPeers);
532
+ }
533
+ return filterMissingPeers({ missingPeers, resolvedPeers }, postponedResolutionOpts.parentPkgAliases);
534
+ },
535
+ };
536
+ }
537
+ export function createNodeIdForLinkedLocalPkg(lockfileDir, pkgDir) {
538
+ return `link:${normalizePath(path.relative(lockfileDir, pkgDir))}`;
539
+ }
540
+ function filterMissingPeers({ missingPeers, resolvedPeers }, parentPkgAliases) {
541
+ const newMissing = {};
542
+ for (const [peerName, peerVersion] of Object.entries(missingPeers)) {
543
+ if (parentPkgAliases[peerName]) {
544
+ if (parentPkgAliases[peerName] !== true) {
545
+ resolvedPeers[peerName] = parentPkgAliases[peerName];
546
+ }
547
+ }
548
+ else {
549
+ newMissing[peerName] = peerVersion;
550
+ }
551
+ }
552
+ return {
553
+ resolvedPeers,
554
+ missingPeers: newMissing,
555
+ };
556
+ }
557
+ async function resolveChildren(ctx, { parentPkg, parentIds, dependencyLockfile, parentDepth, updateDepth, updateMatching, prefix, supportedArchitectures, }, { parentPkgAliases, preferredVersions, publishedBy, }) {
558
+ const currentResolvedDependencies = (dependencyLockfile != null)
559
+ ? {
560
+ ...dependencyLockfile.dependencies,
561
+ ...dependencyLockfile.optionalDependencies,
562
+ }
563
+ : undefined;
564
+ const resolvedDependencies = parentPkg.updated
565
+ ? undefined
566
+ : currentResolvedDependencies;
567
+ const parentDependsOnPeer = Boolean(Object.keys(dependencyLockfile?.peerDependencies ??
568
+ parentPkg.pkg.peerDependencies ??
569
+ {}).length);
570
+ const wantedDependencies = getNonDevWantedDependencies(parentPkg.pkg);
571
+ const { pkgAddresses, resolvingPeers, } = await resolveDependencies(ctx, preferredVersions, wantedDependencies, {
572
+ currentDepth: parentDepth + 1,
573
+ parentPkg,
574
+ parentPkgAliases,
575
+ preferredDependencies: currentResolvedDependencies,
576
+ prefix,
577
+ // If the package is not linked, we should also gather information about its dependencies.
578
+ // After linking the package we'll need to symlink its dependencies.
579
+ proceed: !parentPkg.depIsLinked || parentDependsOnPeer,
580
+ publishedBy,
581
+ resolvedDependencies,
582
+ updateDepth,
583
+ updateMatching,
584
+ supportedArchitectures,
585
+ parentIds,
586
+ });
587
+ ctx.childrenByParentId[parentPkg.pkgId] = pkgAddresses.map((child) => ({
588
+ alias: child.alias,
589
+ id: child.pkgId,
590
+ }));
591
+ ctx.dependenciesTree.set(parentPkg.nodeId, {
592
+ children: pkgAddresses.reduce((chn, child) => {
593
+ chn[child.alias] = child.nodeId ?? child.pkgId;
594
+ return chn;
595
+ }, {}),
596
+ depth: parentDepth,
597
+ installable: parentPkg.installable,
598
+ resolvedPackage: ctx.resolvedPkgsById[parentPkg.pkgId],
599
+ });
600
+ return resolvingPeers;
601
+ }
602
+ function getDepsToResolve(wantedDependencies, wantedLockfile, options) {
603
+ const resolvedDependencies = options.resolvedDependencies ?? {};
604
+ const preferredDependencies = options.preferredDependencies ?? {};
605
+ const extendedWantedDeps = [];
606
+ // The only reason we resolve children in case the package depends on peers
607
+ // is to get information about the existing dependencies, so that they can
608
+ // be merged with the resolved peers.
609
+ let proceedAll = options.proceed;
610
+ const satisfiesWanted2Args = referenceSatisfiesWantedSpec.bind(null, {
611
+ lockfile: wantedLockfile,
612
+ prefix: options.prefix,
613
+ });
614
+ for (const wantedDependency of wantedDependencies) {
615
+ let reference = undefined;
616
+ let proceed = proceedAll;
617
+ if (wantedDependency.alias) {
618
+ const satisfiesWanted = satisfiesWanted2Args.bind(null, wantedDependency);
619
+ if (resolvedDependencies[wantedDependency.alias] &&
620
+ (satisfiesWanted(resolvedDependencies[wantedDependency.alias]) || resolvedDependencies[wantedDependency.alias].startsWith('file:'))) {
621
+ reference = resolvedDependencies[wantedDependency.alias];
622
+ }
623
+ else if (
624
+ // If dependencies that were used by the previous version of the package
625
+ // satisfy the newer version's requirements, then pnpm tries to keep
626
+ // the previous dependency.
627
+ // So for example, if foo@1.0.0 had bar@1.0.0 as a dependency
628
+ // and foo was updated to 1.1.0 which depends on bar ^1.0.0
629
+ // then bar@1.0.0 can be reused for foo@1.1.0
630
+ semver.validRange(wantedDependency.bareSpecifier) !== null &&
631
+ preferredDependencies[wantedDependency.alias] &&
632
+ satisfiesWanted(preferredDependencies[wantedDependency.alias])) {
633
+ proceed = true;
634
+ reference = preferredDependencies[wantedDependency.alias];
635
+ }
636
+ }
637
+ const infoFromLockfile = getInfoFromLockfile(wantedLockfile, options.registries, reference, wantedDependency.alias);
638
+ if (!proceedAll &&
639
+ ((infoFromLockfile == null) ||
640
+ infoFromLockfile.dependencyLockfile != null && (infoFromLockfile.dependencyLockfile.peerDependencies != null ||
641
+ infoFromLockfile.dependencyLockfile.transitivePeerDependencies?.length))) {
642
+ proceed = true;
643
+ proceedAll = true;
644
+ for (const extendedWantedDep of extendedWantedDeps) {
645
+ if (!extendedWantedDep.proceed) {
646
+ extendedWantedDep.proceed = true;
647
+ }
648
+ }
649
+ }
650
+ extendedWantedDeps.push({
651
+ infoFromLockfile,
652
+ proceed,
653
+ wantedDependency,
654
+ });
655
+ }
656
+ return extendedWantedDeps;
657
+ }
658
+ function referenceSatisfiesWantedSpec(opts, wantedDep, preferredRef) {
659
+ const depPath = dp.refToRelative(preferredRef, wantedDep.alias);
660
+ if (depPath === null)
661
+ return false;
662
+ const pkgSnapshot = opts.lockfile.packages?.[depPath];
663
+ if (pkgSnapshot == null) {
664
+ logger.warn({
665
+ message: `Could not find preferred package ${depPath} in lockfile`,
666
+ prefix: opts.prefix,
667
+ });
668
+ return false;
669
+ }
670
+ const { version } = nameVerFromPkgSnapshot(depPath, pkgSnapshot);
671
+ if (!semver.validRange(wantedDep.bareSpecifier) && Object.values(opts.lockfile.importers).filter(importer => importer.specifiers[wantedDep.alias] === wantedDep.bareSpecifier).length) {
672
+ return true;
673
+ }
674
+ return semver.satisfies(version, wantedDep.bareSpecifier, true);
675
+ }
676
+ function getInfoFromLockfile(lockfile, registries, reference, alias) {
677
+ if (!reference || !alias) {
678
+ return undefined;
679
+ }
680
+ const depPath = dp.refToRelative(reference, alias);
681
+ if (!depPath) {
682
+ return undefined;
683
+ }
684
+ let dependencyLockfile = lockfile.packages?.[depPath];
685
+ if (dependencyLockfile != null) {
686
+ if ((dependencyLockfile.peerDependencies != null) && (dependencyLockfile.dependencies != null)) {
687
+ // This is done to guarantee that the dependency will be relinked with the
688
+ // up-to-date peer dependencies
689
+ // Covered by test: "peer dependency is grouped with dependency when peer is resolved not from a top dependency"
690
+ const dependencies = {};
691
+ for (const [depName, ref] of Object.entries(dependencyLockfile.dependencies ?? {})) {
692
+ if (dependencyLockfile.peerDependencies[depName])
693
+ continue;
694
+ dependencies[depName] = ref;
695
+ }
696
+ dependencyLockfile = {
697
+ ...dependencyLockfile,
698
+ dependencies,
699
+ };
700
+ }
701
+ const { name, version, nonSemverVersion } = nameVerFromPkgSnapshot(depPath, dependencyLockfile);
702
+ return {
703
+ name,
704
+ version,
705
+ dependencyLockfile,
706
+ pkgId: nonSemverVersion ?? `${name}@${version}`,
707
+ // resolution may not exist if lockfile is broken, and an unexpected error will be thrown
708
+ // if resolution does not exist, return undefined so it can be autofixed later
709
+ resolution: dependencyLockfile.resolution && pkgSnapshotToResolution(depPath, dependencyLockfile, registries),
710
+ };
711
+ }
712
+ else {
713
+ const parsed = dp.parse(depPath);
714
+ return {
715
+ pkgId: parsed.nonSemverVersion ?? (parsed.name && parsed.version ? `${parsed.name}@${parsed.version}` : depPath), // Does it make sense to set pkgId when we're not sure?
716
+ };
717
+ }
718
+ }
719
+ async function resolveDependency(wantedDependency, ctx, options) {
720
+ const currentPkg = options.currentPkg ?? {};
721
+ const currentLockfileContainsTheDep = currentPkg.depPath
722
+ ? Boolean(ctx.currentLockfile.packages?.[currentPkg.depPath])
723
+ : undefined;
724
+ const depIsLinked = Boolean(
725
+ // if package is not in `node_modules/.pnpm-lock.yaml`
726
+ // we can safely assume that it doesn't exist in `node_modules`
727
+ currentLockfileContainsTheDep &&
728
+ currentPkg.depPath &&
729
+ currentPkg.dependencyLockfile &&
730
+ currentPkg.name &&
731
+ await pathExists(path.join(ctx.virtualStoreDir, dp.depPathToFilename(currentPkg.depPath, ctx.virtualStoreDirMaxLength), 'node_modules', currentPkg.name, 'package.json')));
732
+ if (!options.update && !options.proceed && (currentPkg.resolution != null) && depIsLinked) {
733
+ return null;
734
+ }
735
+ let pkgResponse;
736
+ if (!options.parentPkg.installable) {
737
+ wantedDependency = {
738
+ ...wantedDependency,
739
+ optional: true,
740
+ };
741
+ }
742
+ // Normalize the `preferredVersion` (singular) and `preferredVersions`
743
+ // (plural) options. If the singular option is passed through, it'll be used
744
+ // instead of the plural option.
745
+ const preferredVersions = !options.updateRequested && options.preferredVersion != null
746
+ ? getExactSinglePreferredVersions(wantedDependency, options.preferredVersion)
747
+ : options.preferredVersions;
748
+ try {
749
+ const calcSpecifier = options.currentDepth === 0;
750
+ if (!options.update && currentPkg.version && currentPkg.pkgId?.endsWith(`@${currentPkg.version}`) && !calcSpecifier) {
751
+ wantedDependency.bareSpecifier = replaceVersionInBareSpecifier(wantedDependency.bareSpecifier, currentPkg.version);
752
+ }
753
+ pkgResponse = await ctx.storeController.requestPackage(wantedDependency, {
754
+ allowBuild: ctx.allowBuild,
755
+ alwaysTryWorkspacePackages: ctx.linkWorkspacePackagesDepth >= options.currentDepth,
756
+ currentPkg: currentPkg
757
+ ? {
758
+ id: currentPkg.pkgId,
759
+ name: currentPkg.name,
760
+ resolution: currentPkg.resolution,
761
+ version: currentPkg.version,
762
+ }
763
+ : undefined,
764
+ expectedPkg: currentPkg,
765
+ defaultTag: ctx.defaultTag,
766
+ ignoreScripts: ctx.ignoreScripts,
767
+ publishedBy: options.publishedBy,
768
+ publishedByExclude: ctx.publishedByExclude,
769
+ pickLowestVersion: options.pickLowestVersion,
770
+ downloadPriority: -options.currentDepth,
771
+ lockfileDir: ctx.lockfileDir,
772
+ preferredVersions,
773
+ preferWorkspacePackages: ctx.preferWorkspacePackages,
774
+ projectDir: (options.currentDepth > 0 &&
775
+ !wantedDependency.bareSpecifier.startsWith('file:'))
776
+ ? ctx.lockfileDir
777
+ : options.parentPkg.rootDir,
778
+ skipFetch: ctx.dryRun,
779
+ trustPolicy: ctx.trustPolicy,
780
+ trustPolicyExclude: ctx.trustPolicyExclude,
781
+ trustPolicyIgnoreAfter: ctx.trustPolicyIgnoreAfter,
782
+ update: options.update,
783
+ workspacePackages: ctx.workspacePackages,
784
+ supportedArchitectures: options.supportedArchitectures,
785
+ onFetchError: (err) => {
786
+ err.prefix = options.prefix;
787
+ err.pkgsStack = getPkgsInfoFromIds(options.parentIds, ctx.resolvedPkgsById);
788
+ return err;
789
+ },
790
+ injectWorkspacePackages: ctx.injectWorkspacePackages,
791
+ calcSpecifier,
792
+ pinnedVersion: options.pinnedVersion,
793
+ });
794
+ }
795
+ catch (err) { // eslint-disable-line
796
+ const wantedDependencyDetails = {
797
+ name: wantedDependency.alias,
798
+ bareSpecifier: wantedDependency.bareSpecifier,
799
+ version: wantedDependency.alias ? wantedDependency.bareSpecifier : undefined,
800
+ };
801
+ if (wantedDependency.optional && err.code !== 'ERR_PNPM_TRUST_DOWNGRADE' && err.code !== 'ERR_PNPM_NO_MATURE_MATCHING_VERSION') {
802
+ skippedOptionalDependencyLogger.debug({
803
+ details: err.toString(),
804
+ package: wantedDependencyDetails,
805
+ parents: getPkgsInfoFromIds(options.parentIds, ctx.resolvedPkgsById),
806
+ prefix: options.prefix,
807
+ reason: 'resolution_failure',
808
+ });
809
+ return null;
810
+ }
811
+ err.package = wantedDependencyDetails;
812
+ err.prefix = options.prefix;
813
+ err.pkgsStack = getPkgsInfoFromIds(options.parentIds, ctx.resolvedPkgsById);
814
+ throw err;
815
+ }
816
+ dependencyResolvedLogger.debug({
817
+ resolution: pkgResponse.body.id,
818
+ wanted: {
819
+ dependentId: options.parentPkg.pkgId,
820
+ name: wantedDependency.alias,
821
+ rawSpec: wantedDependency.bareSpecifier,
822
+ },
823
+ });
824
+ // Check if exotic dependencies are disallowed in subdependencies
825
+ if (ctx.blockExoticSubdeps &&
826
+ options.currentDepth > 0 &&
827
+ pkgResponse.body.resolvedVia != null && // This is already coming from the lockfile, we skip the check in this case for now. Should be fixed later.
828
+ isExoticDep(pkgResponse.body.resolvedVia)) {
829
+ const error = new PnpmError('EXOTIC_SUBDEP', `Exotic dependency "${wantedDependency.alias ?? wantedDependency.bareSpecifier}" (resolved via ${pkgResponse.body.resolvedVia}) is not allowed in subdependencies when blockExoticSubdeps is enabled`);
830
+ error.prefix = options.prefix;
831
+ error.pkgsStack = getPkgsInfoFromIds(options.parentIds, ctx.resolvedPkgsById);
832
+ throw error;
833
+ }
834
+ if (ctx.allPreferredVersions && pkgResponse.body.manifest?.version) {
835
+ if (!ctx.allPreferredVersions[pkgResponse.body.manifest.name]) {
836
+ ctx.allPreferredVersions[pkgResponse.body.manifest.name] = {};
837
+ }
838
+ ctx.allPreferredVersions[pkgResponse.body.manifest.name][pkgResponse.body.manifest.version] = 'version';
839
+ }
840
+ if (!pkgResponse.body.updated &&
841
+ options.currentDepth === Math.max(0, options.updateDepth) &&
842
+ depIsLinked && !ctx.force && !options.proceed) {
843
+ return null;
844
+ }
845
+ if (pkgResponse.body.isLocal) {
846
+ if (!pkgResponse.body.manifest) {
847
+ // This should actually never happen because the local-resolver returns a manifest
848
+ // even if no real manifest exists in the filesystem.
849
+ throw new PnpmError('MISSING_PACKAGE_JSON', `Can't install ${wantedDependency.bareSpecifier}: Missing package.json file`);
850
+ }
851
+ return {
852
+ alias: wantedDependency.alias ?? pkgResponse.body.alias ?? pkgResponse.body.manifest.name ?? path.basename(pkgResponse.body.resolution.directory),
853
+ dev: wantedDependency.dev,
854
+ isLinkedDependency: true,
855
+ name: pkgResponse.body.manifest.name,
856
+ optional: wantedDependency.optional,
857
+ pkgId: pkgResponse.body.id,
858
+ resolution: pkgResponse.body.resolution,
859
+ version: pkgResponse.body.manifest.version,
860
+ normalizedBareSpecifier: pkgResponse.body.normalizedBareSpecifier,
861
+ pkg: pkgResponse.body.manifest,
862
+ };
863
+ }
864
+ let prepare;
865
+ let hasBin;
866
+ let pkg = getManifestFromResponse(pkgResponse, wantedDependency, currentPkg);
867
+ if (!pkg.dependencies) {
868
+ pkg.dependencies = {};
869
+ }
870
+ if (ctx.readPackageHook != null) {
871
+ pkg = await ctx.readPackageHook(pkg);
872
+ }
873
+ if (pkg.peerDependencies && pkg.dependencies) {
874
+ if (ctx.autoInstallPeers) {
875
+ pkg = {
876
+ ...pkg,
877
+ dependencies: omit(Object.keys(pkg.peerDependencies), pkg.dependencies),
878
+ };
879
+ }
880
+ else {
881
+ pkg = {
882
+ ...pkg,
883
+ dependencies: omit(Object.keys(pkg.peerDependencies).filter((peerDep) => options.parentPkgAliases[peerDep]), pkg.dependencies),
884
+ };
885
+ }
886
+ }
887
+ if (pkg.engines?.runtime != null) {
888
+ convertEnginesRuntimeToDependencies(pkg, 'engines', 'dependencies');
889
+ }
890
+ if (!pkg.name) { // TODO: don't fail on optional dependencies
891
+ throw new PnpmError('MISSING_PACKAGE_NAME', `Can't install ${wantedDependency.bareSpecifier}: Missing package name`);
892
+ }
893
+ let pkgIdWithPatchHash = (pkgResponse.body.id.startsWith(`${pkg.name}@`) ? pkgResponse.body.id : `${pkg.name}@${pkgResponse.body.id}`);
894
+ const patch = getPatchInfo(ctx.patchedDependencies, pkg.name, pkg.version);
895
+ if (patch) {
896
+ ctx.appliedPatches.add(patch.key);
897
+ pkgIdWithPatchHash = `${pkgIdWithPatchHash}(patch_hash=${patch.hash})`;
898
+ }
899
+ // We are building the dependency tree only until there are new packages
900
+ // or the packages repeat in a unique order.
901
+ // This is needed later during peer dependencies resolution.
902
+ //
903
+ // So we resolve foo > bar > qar > foo
904
+ // But we stop on foo > bar > qar > foo > qar
905
+ // In the second example, there's no reason to walk qar again
906
+ // when qar is included the first time, the dependencies of foo
907
+ // are already resolved and included as parent dependencies of qar.
908
+ // So during peers resolution, qar cannot possibly get any new or different
909
+ // peers resolved, after the first occurrence.
910
+ //
911
+ // However, in the next example we would analyze the second qar as well,
912
+ // because zoo is a new parent package:
913
+ // foo > bar > qar > zoo > qar
914
+ if (parentIdsContainSequence(options.parentIds, options.parentPkg.pkgId, pkgResponse.body.id) || pkgResponse.body.id === options.parentPkg.pkgId) {
915
+ return null;
916
+ }
917
+ if (!options.update && (currentPkg.dependencyLockfile != null) && currentPkg.depPath &&
918
+ !pkgResponse.body.updated &&
919
+ // peerDependencies field is also used for transitive peer dependencies which should not be linked
920
+ // That's why we cannot omit reading package.json of such dependencies.
921
+ // This can be removed if we implement something like peerDependenciesMeta.transitive: true
922
+ (currentPkg.dependencyLockfile.peerDependencies == null)) {
923
+ hasBin = currentPkg.dependencyLockfile.hasBin === true;
924
+ pkg = {
925
+ ...nameVerFromPkgSnapshot(currentPkg.depPath, currentPkg.dependencyLockfile),
926
+ ...omitDepsFields(currentPkg.dependencyLockfile),
927
+ ...pkg,
928
+ };
929
+ }
930
+ else {
931
+ prepare = Boolean(pkgResponse.body.resolvedVia === 'git-repository' &&
932
+ typeof pkg.scripts?.prepare === 'string');
933
+ if (currentPkg.dependencyLockfile?.deprecated &&
934
+ !pkgResponse.body.updated && !pkg.deprecated) {
935
+ pkg.deprecated = currentPkg.dependencyLockfile.deprecated;
936
+ }
937
+ hasBin = (currentPkg.dependencyLockfile?.hasBin != null && !pkg.bin)
938
+ ? currentPkg.dependencyLockfile.hasBin
939
+ : Boolean((pkg.bin && !(pkg.bin === '' || Object.keys(pkg.bin).length === 0)) ?? pkg.directories?.bin);
940
+ }
941
+ if (options.currentDepth === 0 && pkgResponse.body.latest && pkgResponse.body.latest !== pkg.version) {
942
+ ctx.outdatedDependencies[pkgResponse.body.id] = pkgResponse.body.latest;
943
+ }
944
+ if (pkg.peerDependencies != null) {
945
+ for (const name in pkg.peerDependencies) {
946
+ ctx.allPeerDepNames.add(name);
947
+ }
948
+ }
949
+ if (pkg.peerDependenciesMeta != null) {
950
+ for (const name in pkg.peerDependenciesMeta) {
951
+ ctx.allPeerDepNames.add(name);
952
+ }
953
+ }
954
+ // In case of leaf dependencies (dependencies that have no prod deps or peer deps),
955
+ // we only ever need to analyze one leaf dep in a graph, so the nodeId can be short and stateless.
956
+ const nodeId = pkgIsLeaf(pkg) ? pkgResponse.body.id : nextNodeId();
957
+ const parentIsInstallable = options.parentPkg.installable === undefined || options.parentPkg.installable;
958
+ const installable = parentIsInstallable && pkgResponse.body.isInstallable !== false;
959
+ const isNew = !ctx.resolvedPkgsById[pkgResponse.body.id];
960
+ const parentImporterId = options.parentIds[0];
961
+ const currentIsOptional = wantedDependency.optional || options.parentPkg.optional;
962
+ if (isNew) {
963
+ if (pkg.deprecated &&
964
+ (!ctx.allowedDeprecatedVersions[pkg.name] || !semver.satisfies(pkg.version, ctx.allowedDeprecatedVersions[pkg.name]))) {
965
+ // Report deprecated packages only on first occurrence.
966
+ deprecationLogger.debug({
967
+ deprecated: pkg.deprecated,
968
+ depth: options.currentDepth,
969
+ pkgId: pkgResponse.body.id,
970
+ pkgName: pkg.name,
971
+ pkgVersion: pkg.version,
972
+ prefix: options.prefix,
973
+ });
974
+ }
975
+ if (pkgResponse.body.isInstallable === false || !parentIsInstallable) {
976
+ ctx.skipped.add(pkgResponse.body.id);
977
+ }
978
+ progressLogger.debug({
979
+ packageId: pkgResponse.body.id,
980
+ requester: ctx.lockfileDir,
981
+ status: 'resolved',
982
+ });
983
+ // WARN: It is very important to keep this sync
984
+ // Otherwise, deprecation messages for the same package might get written several times
985
+ ctx.resolvedPkgsById[pkgResponse.body.id] = getResolvedPackage({
986
+ dependencyLockfile: currentPkg.dependencyLockfile,
987
+ pkgIdWithPatchHash,
988
+ force: ctx.force,
989
+ hasBin,
990
+ patch,
991
+ pkg,
992
+ pkgResponse,
993
+ prepare,
994
+ wantedDependency,
995
+ parentImporterId,
996
+ optional: currentIsOptional,
997
+ });
998
+ }
999
+ else {
1000
+ ctx.resolvedPkgsById[pkgResponse.body.id].prod = ctx.resolvedPkgsById[pkgResponse.body.id].prod || !wantedDependency.dev && !wantedDependency.optional;
1001
+ ctx.resolvedPkgsById[pkgResponse.body.id].dev = ctx.resolvedPkgsById[pkgResponse.body.id].dev || wantedDependency.dev;
1002
+ ctx.resolvedPkgsById[pkgResponse.body.id].optional = ctx.resolvedPkgsById[pkgResponse.body.id].optional && currentIsOptional;
1003
+ if (ctx.resolvedPkgsById[pkgResponse.body.id].fetching == null && pkgResponse.fetching != null) {
1004
+ ctx.resolvedPkgsById[pkgResponse.body.id].fetching = pkgResponse.fetching;
1005
+ ctx.resolvedPkgsById[pkgResponse.body.id].filesIndexFile = pkgResponse.filesIndexFile;
1006
+ }
1007
+ if (ctx.dependenciesTree.has(nodeId)) {
1008
+ ctx.dependenciesTree.get(nodeId).depth = Math.min(ctx.dependenciesTree.get(nodeId).depth, options.currentDepth);
1009
+ }
1010
+ else {
1011
+ ctx.pendingNodes.push({
1012
+ alias: wantedDependency.alias ?? pkgResponse.body.alias ?? pkg.name,
1013
+ depth: options.currentDepth,
1014
+ parentIds: options.parentIds,
1015
+ installable,
1016
+ nodeId,
1017
+ resolvedPackage: ctx.resolvedPkgsById[pkgResponse.body.id],
1018
+ });
1019
+ }
1020
+ }
1021
+ const rootDir = pkgResponse.body.resolution.type === 'directory'
1022
+ ? path.resolve(ctx.lockfileDir, pkgResponse.body.resolution.directory)
1023
+ : options.prefix;
1024
+ let missingPeersOfChildren;
1025
+ if (ctx.hoistPeers && !options.parentIds.includes(pkgResponse.body.id)) {
1026
+ if (ctx.missingPeersOfChildrenByPkgId[pkgResponse.body.id]) {
1027
+ // This if condition is used to avoid a dead lock.
1028
+ // There might be a better way to hoist peer dependencies during resolution
1029
+ // but it would probably require a big rewrite of the resolution algorithm.
1030
+ if (ctx.missingPeersOfChildrenByPkgId[pkgResponse.body.id].depth >= options.currentDepth ||
1031
+ ctx.missingPeersOfChildrenByPkgId[pkgResponse.body.id].missingPeersOfChildren.resolved) {
1032
+ missingPeersOfChildren = ctx.missingPeersOfChildrenByPkgId[pkgResponse.body.id].missingPeersOfChildren;
1033
+ }
1034
+ }
1035
+ else {
1036
+ const p = pDefer();
1037
+ missingPeersOfChildren = {
1038
+ resolve: p.resolve,
1039
+ reject: p.reject,
1040
+ get: pShare(p.promise),
1041
+ };
1042
+ ctx.missingPeersOfChildrenByPkgId[pkgResponse.body.id] = {
1043
+ depth: options.currentDepth,
1044
+ missingPeersOfChildren,
1045
+ };
1046
+ }
1047
+ }
1048
+ const resolvedPkg = ctx.resolvedPkgsById[pkgResponse.body.id];
1049
+ return {
1050
+ alias: wantedDependency.alias ?? pkgResponse.body.alias ?? pkg.name,
1051
+ depIsLinked,
1052
+ resolvedVia: pkgResponse.body.resolvedVia,
1053
+ isNew,
1054
+ nodeId,
1055
+ normalizedBareSpecifier: pkgResponse.body.normalizedBareSpecifier,
1056
+ missingPeersOfChildren,
1057
+ pkgId: pkgResponse.body.id,
1058
+ rootDir,
1059
+ missingPeers: getMissingPeers(pkg),
1060
+ optional: resolvedPkg.optional,
1061
+ version: resolvedPkg.version,
1062
+ saveCatalogName: wantedDependency.saveCatalogName,
1063
+ // Next fields are actually only needed when isNew = true
1064
+ installable,
1065
+ isLinkedDependency: undefined,
1066
+ pkg,
1067
+ updated: pkgResponse.body.updated,
1068
+ publishedAt: pkgResponse.body.publishedAt,
1069
+ };
1070
+ }
1071
+ export function getManifestFromResponse(pkgResponse, wantedDependency, currentPkg) {
1072
+ if (pkgResponse.body.manifest)
1073
+ return pkgResponse.body.manifest;
1074
+ if (currentPkg?.name && currentPkg?.version) {
1075
+ return {
1076
+ name: currentPkg.name,
1077
+ version: currentPkg.version,
1078
+ };
1079
+ }
1080
+ return {
1081
+ name: wantedDependency.alias ? wantedDependency.alias : wantedDependency.bareSpecifier.split('/').pop(),
1082
+ version: '0.0.0',
1083
+ };
1084
+ }
1085
+ function getMissingPeers(pkg) {
1086
+ const missingPeers = {};
1087
+ for (const [peerName, peerVersion] of Object.entries(pkg.peerDependencies ?? {})) {
1088
+ missingPeers[peerName] = {
1089
+ range: peerVersion,
1090
+ optional: pkg.peerDependenciesMeta?.[peerName]?.optional === true,
1091
+ };
1092
+ }
1093
+ return missingPeers;
1094
+ }
1095
+ function pkgIsLeaf(pkg) {
1096
+ return Object.keys(pkg.dependencies ?? {}).length === 0 &&
1097
+ Object.keys(pkg.optionalDependencies ?? {}).length === 0 &&
1098
+ Object.keys(pkg.peerDependencies ?? {}).length === 0 &&
1099
+ // Package manifests can declare peerDependenciesMeta without declaring
1100
+ // peerDependencies. peerDependenciesMeta implies the later.
1101
+ Object.keys(pkg.peerDependenciesMeta ?? {}).length === 0;
1102
+ }
1103
+ function getResolvedPackage(options) {
1104
+ const peerDependencies = peerDependenciesWithoutOwn(options.pkg);
1105
+ return {
1106
+ additionalInfo: {
1107
+ bundledDependencies: options.pkg.bundledDependencies,
1108
+ bundleDependencies: options.pkg.bundleDependencies,
1109
+ cpu: options.pkg.cpu,
1110
+ deprecated: options.pkg.deprecated,
1111
+ engines: options.pkg.engines,
1112
+ os: options.pkg.os,
1113
+ libc: options.pkg.libc,
1114
+ },
1115
+ isLeaf: pkgIsLeaf(options.pkg),
1116
+ pkgIdWithPatchHash: options.pkgIdWithPatchHash,
1117
+ dev: options.wantedDependency.dev,
1118
+ fetching: options.pkgResponse.fetching,
1119
+ filesIndexFile: options.pkgResponse.filesIndexFile,
1120
+ hasBin: options.hasBin,
1121
+ hasBundledDependencies: !((options.pkg.bundledDependencies ?? options.pkg.bundleDependencies) == null),
1122
+ id: options.pkgResponse.body.id,
1123
+ name: options.pkg.name,
1124
+ optional: options.optional,
1125
+ optionalDependencies: new Set(Object.keys(options.pkg.optionalDependencies ?? {})),
1126
+ patch: options.patch,
1127
+ peerDependencies,
1128
+ prepare: options.prepare,
1129
+ prod: !options.wantedDependency.dev && !options.wantedDependency.optional,
1130
+ resolution: options.pkgResponse.body.resolution,
1131
+ version: options.pkg.version,
1132
+ };
1133
+ }
1134
+ function peerDependenciesWithoutOwn(pkg) {
1135
+ if ((pkg.peerDependencies == null) && (pkg.peerDependenciesMeta == null))
1136
+ return {};
1137
+ const ownDeps = new Set([
1138
+ pkg.name,
1139
+ ...Object.keys(pkg.dependencies ?? {}),
1140
+ ...Object.keys(pkg.optionalDependencies ?? {}),
1141
+ ]);
1142
+ const result = {};
1143
+ if (pkg.peerDependencies != null) {
1144
+ for (const [peerName, peerRange] of Object.entries(pkg.peerDependencies)) {
1145
+ if (ownDeps.has(peerName))
1146
+ continue;
1147
+ result[peerName] = {
1148
+ version: peerRange,
1149
+ };
1150
+ }
1151
+ }
1152
+ if (pkg.peerDependenciesMeta != null) {
1153
+ for (const [peerName, peerMeta] of Object.entries(pkg.peerDependenciesMeta)) {
1154
+ if (ownDeps.has(peerName) || peerMeta.optional !== true)
1155
+ continue;
1156
+ if (!result[peerName])
1157
+ result[peerName] = { version: '*' };
1158
+ result[peerName].optional = true;
1159
+ }
1160
+ }
1161
+ return result;
1162
+ }
1163
+ function getCatalogExistingVersionFromSnapshot(catalogLookup, wantedLockfile, wantedDependency) {
1164
+ const existingCatalogResolution = wantedLockfile.catalogs?.[catalogLookup.catalogName]?.[wantedDependency.alias];
1165
+ return existingCatalogResolution?.specifier === catalogLookup.specifier
1166
+ ? existingCatalogResolution.version
1167
+ : undefined;
1168
+ }
1169
+ const NON_EXOTIC_RESOLVED_VIA = new Set([
1170
+ 'custom-resolver',
1171
+ 'github.com/denoland/deno',
1172
+ 'github.com/oven-sh/bun',
1173
+ 'jsr-registry',
1174
+ 'local-filesystem',
1175
+ 'nodejs.org',
1176
+ 'npm-registry',
1177
+ 'workspace',
1178
+ ]);
1179
+ function isExoticDep(resolvedVia) {
1180
+ return !NON_EXOTIC_RESOLVED_VIA.has(resolvedVia);
1181
+ }
1182
+ //# sourceMappingURL=resolveDependencies.js.map