@pnpm/installing.deps-resolver 1100.2.2 → 1100.2.3

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/lib/index.d.ts CHANGED
@@ -9,6 +9,7 @@ import { type DependenciesByProjectId, type GenericDependenciesGraphNodeWithReso
9
9
  export type DependenciesGraph = GenericDependenciesGraphWithResolvedChildren<ResolvedPackage>;
10
10
  export type DependenciesGraphNode = GenericDependenciesGraphNodeWithResolvedChildren & ResolvedPackage;
11
11
  export { getWantedDependencies, type LinkedDependency, type PinnedVersion, type ResolvedPackage, type UpdateMatchingFunction, type WantedDependency, };
12
+ export { assertValidDependencyAliases, isValidDependencyAlias } from './validateDependencyAlias.js';
12
13
  export interface ImporterToResolve extends Importer<{
13
14
  isNew?: boolean;
14
15
  nodeExecPath?: string;
package/lib/index.js CHANGED
@@ -19,6 +19,7 @@ import { toResolveImporter } from './toResolveImporter.js';
19
19
  import { updateLockfile } from './updateLockfile.js';
20
20
  import { updateProjectManifest } from './updateProjectManifest.js';
21
21
  export { getWantedDependencies, };
22
+ export { assertValidDependencyAliases, isValidDependencyAlias } from './validateDependencyAlias.js';
22
23
  export async function resolveDependencies(importers, opts) {
23
24
  const _toResolveImporter = toResolveImporter.bind(null, {
24
25
  defaultUpdateDepth: opts.defaultUpdateDepth,
@@ -87,7 +87,12 @@ export interface ResolutionContext {
87
87
  resolvedPkgsById: ResolvedPkgsById;
88
88
  resolvePeersFromWorkspaceRoot?: boolean;
89
89
  outdatedDependencies: Record<PkgResolutionId, string>;
90
+ packageResolutionBarrier: PackageResolutionBarrier;
90
91
  childrenByParentId: ChildrenByParentId;
92
+ childrenResolutionByPkgId: Record<PkgResolutionId, ChildrenResolution>;
93
+ childrenResolutionId: number;
94
+ importerResolutionOrder: Record<string, number>;
95
+ nodeResolutionContextByNodeId: Map<NodeId, NodeResolutionContext>;
91
96
  patchedDependencies?: PatchGroupRecord;
92
97
  pendingNodes: PendingNode[];
93
98
  wantedLockfile: LockfileObject;
@@ -132,6 +137,26 @@ export interface ResolutionContext {
132
137
  trustPolicyIgnoreAfter?: number;
133
138
  blockExoticSubdeps?: boolean;
134
139
  }
140
+ interface PackageResolutionBarrier {
141
+ activeByDepth: Map<number, number>;
142
+ waiters: Array<() => void>;
143
+ }
144
+ interface ChildrenResolutionOwner {
145
+ depth: number;
146
+ importerOrder: number;
147
+ parentPath: PkgResolutionId[];
148
+ }
149
+ interface ChildrenResolution {
150
+ id: number;
151
+ owner: ChildrenResolutionOwner;
152
+ missingPeersOfChildren?: MissingPeersOfChildren;
153
+ }
154
+ interface NodeResolutionContext {
155
+ depth: number;
156
+ installable: boolean;
157
+ parentIds: PkgResolutionId[];
158
+ pkgId: PkgResolutionId;
159
+ }
135
160
  export interface MissingPeerInfo {
136
161
  range: string;
137
162
  optional: boolean;
@@ -156,6 +181,7 @@ export interface PkgAddress extends PkgAddressOrLinkBase {
156
181
  rootDir: string;
157
182
  missingPeers: MissingPeers;
158
183
  missingPeersOfChildren?: MissingPeersOfChildren;
184
+ childrenResolutionId?: number;
159
185
  publishedAt?: string;
160
186
  saveCatalogName?: string;
161
187
  lockedPeerContext?: LockedPeerContext;
@@ -252,6 +278,15 @@ export declare function resolveDependencies(ctx: ResolutionContext, preferredVer
252
278
  updateDepth?: number;
253
279
  }>, options: ResolvedDependenciesOptions): Promise<ResolvedDependenciesResult>;
254
280
  export declare function createNodeIdForLinkedLocalPkg(lockfileDir: string, pkgDir: string): NodeId;
281
+ export declare function buildTree(ctx: {
282
+ childrenByParentId: ChildrenByParentId;
283
+ dependenciesTree: DependenciesTree<ResolvedPackage>;
284
+ resolvedPkgsById: ResolvedPkgsById;
285
+ skipped: Set<PkgResolutionId>;
286
+ }, parentId: PkgResolutionId, parentIds: PkgResolutionId[], children: Array<{
287
+ alias: string;
288
+ id: PkgResolutionId;
289
+ }>, depth: number, installable: boolean): Record<string, NodeId>;
255
290
  export interface LockedPeerContext {
256
291
  [peerName: string]: DepPath;
257
292
  }
@@ -9,6 +9,7 @@ import { logger } from '@pnpm/logger';
9
9
  import { getPatchInfo } from '@pnpm/patching.config';
10
10
  import { convertEnginesRuntimeToDependencies } from '@pnpm/pkg-manifest.utils';
11
11
  import { DIRECT_DEP_SELECTOR_WEIGHT, } from '@pnpm/resolving.resolver-base';
12
+ import { lexCompare } from '@pnpm/util.lex-comparator';
12
13
  import normalizePath from 'normalize-path';
13
14
  import pDefer from 'p-defer';
14
15
  import { pathExists } from 'path-exists';
@@ -522,6 +523,7 @@ async function resolveDependenciesOfDependency(ctx, preferredVersions, options,
522
523
  }
523
524
  const postponedResolution = resolveChildren.bind(null, ctx, {
524
525
  parentPkg: resolveDependencyResult,
526
+ childrenResolutionId: resolveDependencyResult.childrenResolutionId,
525
527
  dependencyLockfile: extendedWantedDep.infoFromLockfile?.dependencyLockfile,
526
528
  parentDepth: options.currentDepth,
527
529
  parentIds: [...options.parentIds, resolveDependencyResult.pkgId],
@@ -534,7 +536,18 @@ async function resolveDependenciesOfDependency(ctx, preferredVersions, options,
534
536
  return {
535
537
  resolveDependencyResult,
536
538
  postponedResolution: async (postponedResolutionOpts) => {
539
+ if (!isCurrentChildrenResolution(ctx, resolveDependencyResult.pkgId, resolveDependencyResult.childrenResolutionId)) {
540
+ setDependencyTreeNodeWithCurrentChildren(ctx, {
541
+ parentDepth: options.currentDepth,
542
+ parentIds: [...options.parentIds, resolveDependencyResult.pkgId],
543
+ parentPkg: resolveDependencyResult,
544
+ });
545
+ return resolveMissingPeersFromCurrentChildrenResolution(ctx, resolveDependencyResult.pkgId, postponedResolutionOpts.parentPkgAliases);
546
+ }
537
547
  const { missingPeers, resolvedPeers } = await postponedResolution(postponedResolutionOpts);
548
+ if (!isCurrentChildrenResolution(ctx, resolveDependencyResult.pkgId, resolveDependencyResult.childrenResolutionId)) {
549
+ return resolveMissingPeersFromCurrentChildrenResolution(ctx, resolveDependencyResult.pkgId, postponedResolutionOpts.parentPkgAliases);
550
+ }
538
551
  if (resolveDependencyResult.missingPeersOfChildren) {
539
552
  resolveDependencyResult.missingPeersOfChildren.resolved = true;
540
553
  resolveDependencyResult.missingPeersOfChildren.resolve(missingPeers);
@@ -563,7 +576,195 @@ function filterMissingPeers({ missingPeers, resolvedPeers }, parentPkgAliases) {
563
576
  missingPeers: newMissing,
564
577
  };
565
578
  }
566
- async function resolveChildren(ctx, { parentPkg, parentIds, dependencyLockfile, parentDepth, updateDepth, updateMatching, prefix, supportedArchitectures, }, { parentPkgAliases, preferredVersions, publishedBy, }) {
579
+ function startPackageResolution(ctx, depth) {
580
+ const activeCount = ctx.packageResolutionBarrier.activeByDepth.get(depth) ?? 0;
581
+ ctx.packageResolutionBarrier.activeByDepth.set(depth, activeCount + 1);
582
+ let finished = false;
583
+ return () => {
584
+ if (finished)
585
+ return;
586
+ finished = true;
587
+ const nextActiveCount = (ctx.packageResolutionBarrier.activeByDepth.get(depth) ?? 1) - 1;
588
+ if (nextActiveCount > 0) {
589
+ ctx.packageResolutionBarrier.activeByDepth.set(depth, nextActiveCount);
590
+ }
591
+ else {
592
+ ctx.packageResolutionBarrier.activeByDepth.delete(depth);
593
+ }
594
+ const waiters = ctx.packageResolutionBarrier.waiters.splice(0);
595
+ for (const resolve of waiters) {
596
+ resolve();
597
+ }
598
+ };
599
+ }
600
+ async function waitForPackageResolutionTurn(ctx, depth) {
601
+ if (!hasActivePackageResolutionBeforeDepth(ctx, depth))
602
+ return;
603
+ await new Promise((resolve) => ctx.packageResolutionBarrier.waiters.push(resolve));
604
+ return waitForPackageResolutionTurn(ctx, depth);
605
+ }
606
+ function hasActivePackageResolutionBeforeDepth(ctx, depth) {
607
+ for (const [activeDepth, activeCount] of ctx.packageResolutionBarrier.activeByDepth) {
608
+ if (activeDepth < depth && activeCount > 0)
609
+ return true;
610
+ }
611
+ return false;
612
+ }
613
+ function claimChildrenResolution(ctx, opts) {
614
+ const owner = {
615
+ depth: opts.currentDepth,
616
+ importerOrder: ctx.importerResolutionOrder[opts.parentIds[0]] ?? Number.MAX_SAFE_INTEGER,
617
+ parentPath: opts.parentIds,
618
+ };
619
+ const existing = ctx.childrenResolutionByPkgId[opts.pkgId];
620
+ if (existing == null || compareChildrenResolutionOwners(owner, existing.owner) < 0) {
621
+ const previousMissingPeersOfChildren = existing?.missingPeersOfChildren;
622
+ const missingPeersOfChildren = ctx.hoistPeers && !opts.parentIds.includes(opts.pkgId)
623
+ ? createMissingPeersOfChildren()
624
+ : undefined;
625
+ const resolution = {
626
+ id: ++ctx.childrenResolutionId,
627
+ owner,
628
+ missingPeersOfChildren,
629
+ };
630
+ ctx.childrenResolutionByPkgId[opts.pkgId] = resolution;
631
+ if (missingPeersOfChildren) {
632
+ ctx.missingPeersOfChildrenByPkgId[opts.pkgId] = {
633
+ depth: owner.depth,
634
+ missingPeersOfChildren,
635
+ };
636
+ }
637
+ if (previousMissingPeersOfChildren) {
638
+ if (missingPeersOfChildren) {
639
+ missingPeersOfChildren.get().then((missingPeers) => {
640
+ previousMissingPeersOfChildren.resolved = true;
641
+ previousMissingPeersOfChildren.resolve(missingPeers);
642
+ }, previousMissingPeersOfChildren.reject);
643
+ }
644
+ else {
645
+ previousMissingPeersOfChildren.resolved = true;
646
+ previousMissingPeersOfChildren.resolve({});
647
+ }
648
+ }
649
+ return {
650
+ id: resolution.id,
651
+ isOwner: true,
652
+ missingPeersOfChildren,
653
+ };
654
+ }
655
+ let missingPeersOfChildren;
656
+ if (ctx.hoistPeers &&
657
+ !opts.parentIds.includes(opts.pkgId) &&
658
+ existing.missingPeersOfChildren &&
659
+ (existing.owner.depth >= opts.currentDepth ||
660
+ existing.missingPeersOfChildren.resolved)) {
661
+ missingPeersOfChildren = existing.missingPeersOfChildren;
662
+ }
663
+ return {
664
+ id: existing.id,
665
+ isOwner: false,
666
+ missingPeersOfChildren,
667
+ };
668
+ }
669
+ function compareChildrenResolutionOwners(owner1, owner2) {
670
+ if (owner1.depth !== owner2.depth)
671
+ return owner1.depth - owner2.depth;
672
+ if (owner1.importerOrder !== owner2.importerOrder)
673
+ return owner1.importerOrder - owner2.importerOrder;
674
+ const pathLength = Math.min(owner1.parentPath.length, owner2.parentPath.length);
675
+ for (let i = 0; i < pathLength; i++) {
676
+ const result = lexCompare(owner1.parentPath[i], owner2.parentPath[i]);
677
+ if (result !== 0)
678
+ return result;
679
+ }
680
+ return owner1.parentPath.length - owner2.parentPath.length;
681
+ }
682
+ function createMissingPeersOfChildren() {
683
+ const p = pDefer();
684
+ return {
685
+ resolve: p.resolve,
686
+ reject: p.reject,
687
+ get: pShare(p.promise),
688
+ };
689
+ }
690
+ function isCurrentChildrenResolution(ctx, pkgId, childrenResolutionId) {
691
+ return childrenResolutionId != null && ctx.childrenResolutionByPkgId[pkgId]?.id === childrenResolutionId;
692
+ }
693
+ async function resolveMissingPeersFromCurrentChildrenResolution(ctx, pkgId, parentPkgAliases) {
694
+ const missingPeersOfChildren = ctx.childrenResolutionByPkgId[pkgId]?.missingPeersOfChildren;
695
+ if (missingPeersOfChildren == null) {
696
+ return {
697
+ missingPeers: {},
698
+ resolvedPeers: {},
699
+ };
700
+ }
701
+ const missingPeers = await missingPeersOfChildren.get();
702
+ return filterMissingPeers({ missingPeers, resolvedPeers: {} }, parentPkgAliases);
703
+ }
704
+ function setDependencyTreeNodeWithCurrentChildren(ctx, { parentDepth, parentIds, parentPkg, }) {
705
+ ctx.dependenciesTree.set(parentPkg.nodeId, {
706
+ children: () => buildTree(ctx, parentPkg.pkgId, parentIds, ctx.childrenByParentId[parentPkg.pkgId] ?? [], parentDepth + 1, parentPkg.installable),
707
+ depth: parentDepth,
708
+ installable: parentPkg.installable,
709
+ lockedPeerContext: parentPkg.lockedPeerContext,
710
+ previousDepPath: parentPkg.previousDepPath,
711
+ resolvedPackage: ctx.resolvedPkgsById[parentPkg.pkgId],
712
+ });
713
+ ctx.nodeResolutionContextByNodeId.set(parentPkg.nodeId, {
714
+ depth: parentDepth,
715
+ installable: parentPkg.installable,
716
+ parentIds,
717
+ pkgId: parentPkg.pkgId,
718
+ });
719
+ }
720
+ function updateChildrenResolutionNodes(ctx, pkgId, currentNodeId) {
721
+ for (const [nodeId, nodeContext] of ctx.nodeResolutionContextByNodeId) {
722
+ if (nodeId === currentNodeId || nodeContext.pkgId !== pkgId)
723
+ continue;
724
+ const node = ctx.dependenciesTree.get(nodeId);
725
+ if (node == null || node.depth === -1)
726
+ continue;
727
+ node.children = () => buildTree(ctx, pkgId, nodeContext.parentIds, ctx.childrenByParentId[pkgId] ?? [], nodeContext.depth + 1, nodeContext.installable);
728
+ }
729
+ }
730
+ export function buildTree(ctx, parentId, parentIds, children, depth, installable) {
731
+ const childrenNodeIds = {};
732
+ for (const child of children) {
733
+ if (child.id.startsWith('link:')) {
734
+ childrenNodeIds[child.alias] = child.id;
735
+ continue;
736
+ }
737
+ if (parentIdsContainSequence(parentIds, parentId, child.id) || parentId === child.id) {
738
+ continue;
739
+ }
740
+ if (ctx.resolvedPkgsById[child.id].isLeaf) {
741
+ childrenNodeIds[child.alias] = child.id;
742
+ continue;
743
+ }
744
+ const childNodeId = nextNodeId();
745
+ childrenNodeIds[child.alias] = childNodeId;
746
+ installable = installable || !ctx.skipped.has(child.id);
747
+ ctx.dependenciesTree.set(childNodeId, {
748
+ children: () => buildTree(ctx, child.id, [...parentIds, child.id], ctx.childrenByParentId[child.id], depth + 1, installable),
749
+ depth,
750
+ installable,
751
+ resolvedPackage: ctx.resolvedPkgsById[child.id],
752
+ });
753
+ }
754
+ return childrenNodeIds;
755
+ }
756
+ async function resolveChildren(ctx, { parentPkg, childrenResolutionId, parentIds, dependencyLockfile, parentDepth, updateDepth, updateMatching, prefix, supportedArchitectures, }, { parentPkgAliases, preferredVersions, publishedBy, }) {
757
+ if (!isCurrentChildrenResolution(ctx, parentPkg.pkgId, childrenResolutionId)) {
758
+ setDependencyTreeNodeWithCurrentChildren(ctx, {
759
+ parentDepth,
760
+ parentIds,
761
+ parentPkg,
762
+ });
763
+ return {
764
+ missingPeers: {},
765
+ resolvedPeers: {},
766
+ };
767
+ }
567
768
  const currentResolvedDependencies = (dependencyLockfile != null)
568
769
  ? {
569
770
  ...dependencyLockfile.dependencies,
@@ -593,6 +794,14 @@ async function resolveChildren(ctx, { parentPkg, parentIds, dependencyLockfile,
593
794
  supportedArchitectures,
594
795
  parentIds,
595
796
  });
797
+ if (!isCurrentChildrenResolution(ctx, parentPkg.pkgId, childrenResolutionId)) {
798
+ setDependencyTreeNodeWithCurrentChildren(ctx, {
799
+ parentDepth,
800
+ parentIds,
801
+ parentPkg,
802
+ });
803
+ return resolvingPeers;
804
+ }
596
805
  ctx.childrenByParentId[parentPkg.pkgId] = pkgAddresses.map((child) => ({
597
806
  alias: child.alias,
598
807
  id: child.pkgId,
@@ -617,6 +826,13 @@ async function resolveChildren(ctx, { parentPkg, parentIds, dependencyLockfile,
617
826
  previousDepPath: parentPkg.previousDepPath,
618
827
  resolvedPackage: ctx.resolvedPkgsById[parentPkg.pkgId],
619
828
  });
829
+ ctx.nodeResolutionContextByNodeId.set(parentPkg.nodeId, {
830
+ depth: parentDepth,
831
+ installable: parentPkg.installable,
832
+ parentIds,
833
+ pkgId: parentPkg.pkgId,
834
+ });
835
+ updateChildrenResolutionNodes(ctx, parentPkg.pkgId, parentPkg.nodeId);
620
836
  return resolvingPeers;
621
837
  }
622
838
  function getDepsToResolve(wantedDependencies, wantedLockfile, options) {
@@ -775,345 +991,338 @@ async function resolveDependency(wantedDependency, ctx, options) {
775
991
  optional: true,
776
992
  };
777
993
  }
778
- // Normalize the `preferredVersion` (singular) and `preferredVersions`
779
- // (plural) options. If the singular option is passed through, it'll be used
780
- // instead of the plural option.
781
- const preferredVersions = !options.updateRequested && options.preferredVersion != null
782
- ? getExactSinglePreferredVersions(wantedDependency, options.preferredVersion)
783
- : options.preferredVersions;
994
+ const finishPackageResolution = startPackageResolution(ctx, options.currentDepth);
784
995
  try {
785
- const calcSpecifier = options.currentDepth === 0;
786
- if (!options.update && currentPkg.version && currentPkg.pkgId?.endsWith(`@${currentPkg.version}`) && !calcSpecifier) {
787
- wantedDependency.bareSpecifier = replaceVersionInBareSpecifier(wantedDependency.bareSpecifier, currentPkg.version, ctx.namedRegistryPrefixes);
996
+ await waitForPackageResolutionTurn(ctx, options.currentDepth);
997
+ // Normalize the `preferredVersion` (singular) and `preferredVersions`
998
+ // (plural) options. If the singular option is passed through, it'll be used
999
+ // instead of the plural option.
1000
+ const preferredVersions = !options.updateRequested && options.preferredVersion != null
1001
+ ? getExactSinglePreferredVersions(wantedDependency, options.preferredVersion)
1002
+ : options.preferredVersions;
1003
+ try {
1004
+ const calcSpecifier = options.currentDepth === 0;
1005
+ if (!options.update && currentPkg.version && currentPkg.pkgId?.endsWith(`@${currentPkg.version}`) && !calcSpecifier) {
1006
+ wantedDependency.bareSpecifier = replaceVersionInBareSpecifier(wantedDependency.bareSpecifier, currentPkg.version, ctx.namedRegistryPrefixes);
1007
+ }
1008
+ pkgResponse = await ctx.storeController.requestPackage(wantedDependency, {
1009
+ allowBuild: ctx.allowBuild,
1010
+ alwaysTryWorkspacePackages: ctx.linkWorkspacePackagesDepth >= options.currentDepth,
1011
+ currentPkg: currentPkg
1012
+ ? {
1013
+ id: currentPkg.pkgId,
1014
+ name: currentPkg.name,
1015
+ resolution: currentPkg.resolution,
1016
+ version: currentPkg.version,
1017
+ publishedAt: currentPkg.pkgId ? ctx.wantedLockfile.time?.[currentPkg.pkgId] : undefined,
1018
+ }
1019
+ : undefined,
1020
+ expectedPkg: currentPkg,
1021
+ defaultTag: ctx.defaultTag,
1022
+ ignoreScripts: ctx.ignoreScripts,
1023
+ publishedBy: options.publishedBy,
1024
+ publishedByExclude: ctx.publishedByExclude,
1025
+ pickLowestVersion: options.pickLowestVersion,
1026
+ downloadPriority: -options.currentDepth,
1027
+ lockfileDir: ctx.lockfileDir,
1028
+ preferredVersions,
1029
+ preferWorkspacePackages: ctx.preferWorkspacePackages,
1030
+ projectDir: (options.currentDepth > 0 &&
1031
+ !wantedDependency.bareSpecifier.startsWith('file:'))
1032
+ ? ctx.lockfileDir
1033
+ : options.parentPkg.rootDir,
1034
+ skipFetch: ctx.dryRun,
1035
+ trustPolicy: ctx.trustPolicy,
1036
+ trustPolicyExclude: ctx.trustPolicyExclude,
1037
+ trustPolicyIgnoreAfter: ctx.trustPolicyIgnoreAfter,
1038
+ update: options.update,
1039
+ updateChecksums: options.updateChecksums,
1040
+ workspacePackages: ctx.workspacePackages,
1041
+ supportedArchitectures: options.supportedArchitectures,
1042
+ onFetchError: (err) => {
1043
+ err.prefix = options.prefix;
1044
+ err.pkgsStack = getPkgsInfoFromIds(options.parentIds, ctx.resolvedPkgsById);
1045
+ return err;
1046
+ },
1047
+ injectWorkspacePackages: ctx.injectWorkspacePackages,
1048
+ calcSpecifier,
1049
+ pinnedVersion: options.pinnedVersion,
1050
+ });
788
1051
  }
789
- pkgResponse = await ctx.storeController.requestPackage(wantedDependency, {
790
- allowBuild: ctx.allowBuild,
791
- alwaysTryWorkspacePackages: ctx.linkWorkspacePackagesDepth >= options.currentDepth,
792
- currentPkg: currentPkg
793
- ? {
794
- id: currentPkg.pkgId,
795
- name: currentPkg.name,
796
- resolution: currentPkg.resolution,
797
- version: currentPkg.version,
798
- publishedAt: currentPkg.pkgId ? ctx.wantedLockfile.time?.[currentPkg.pkgId] : undefined,
799
- }
800
- : undefined,
801
- expectedPkg: currentPkg,
802
- defaultTag: ctx.defaultTag,
803
- ignoreScripts: ctx.ignoreScripts,
804
- publishedBy: options.publishedBy,
805
- publishedByExclude: ctx.publishedByExclude,
806
- pickLowestVersion: options.pickLowestVersion,
807
- downloadPriority: -options.currentDepth,
808
- lockfileDir: ctx.lockfileDir,
809
- preferredVersions,
810
- preferWorkspacePackages: ctx.preferWorkspacePackages,
811
- projectDir: (options.currentDepth > 0 &&
812
- !wantedDependency.bareSpecifier.startsWith('file:'))
813
- ? ctx.lockfileDir
814
- : options.parentPkg.rootDir,
815
- skipFetch: ctx.dryRun,
816
- trustPolicy: ctx.trustPolicy,
817
- trustPolicyExclude: ctx.trustPolicyExclude,
818
- trustPolicyIgnoreAfter: ctx.trustPolicyIgnoreAfter,
819
- update: options.update,
820
- updateChecksums: options.updateChecksums,
821
- workspacePackages: ctx.workspacePackages,
822
- supportedArchitectures: options.supportedArchitectures,
823
- onFetchError: (err) => {
824
- err.prefix = options.prefix;
825
- err.pkgsStack = getPkgsInfoFromIds(options.parentIds, ctx.resolvedPkgsById);
826
- return err;
1052
+ catch (err) { // eslint-disable-line
1053
+ const wantedDependencyDetails = {
1054
+ name: wantedDependency.alias,
1055
+ bareSpecifier: wantedDependency.bareSpecifier,
1056
+ version: wantedDependency.alias ? wantedDependency.bareSpecifier : undefined,
1057
+ };
1058
+ if (wantedDependency.optional && err.code !== 'ERR_PNPM_TRUST_DOWNGRADE') {
1059
+ skippedOptionalDependencyLogger.debug({
1060
+ details: err.toString(),
1061
+ package: wantedDependencyDetails,
1062
+ parents: getPkgsInfoFromIds(options.parentIds, ctx.resolvedPkgsById),
1063
+ prefix: options.prefix,
1064
+ reason: 'resolution_failure',
1065
+ });
1066
+ return null;
1067
+ }
1068
+ err.package = wantedDependencyDetails;
1069
+ err.prefix = options.prefix;
1070
+ err.pkgsStack = getPkgsInfoFromIds(options.parentIds, ctx.resolvedPkgsById);
1071
+ throw err;
1072
+ }
1073
+ dependencyResolvedLogger.debug({
1074
+ resolution: pkgResponse.body.id,
1075
+ wanted: {
1076
+ dependentId: options.parentPkg.pkgId,
1077
+ name: wantedDependency.alias,
1078
+ rawSpec: wantedDependency.bareSpecifier,
827
1079
  },
828
- injectWorkspacePackages: ctx.injectWorkspacePackages,
829
- calcSpecifier,
830
- pinnedVersion: options.pinnedVersion,
831
1080
  });
832
- }
833
- catch (err) { // eslint-disable-line
834
- const wantedDependencyDetails = {
835
- name: wantedDependency.alias,
836
- bareSpecifier: wantedDependency.bareSpecifier,
837
- version: wantedDependency.alias ? wantedDependency.bareSpecifier : undefined,
838
- };
839
- if (wantedDependency.optional && err.code !== 'ERR_PNPM_TRUST_DOWNGRADE') {
840
- skippedOptionalDependencyLogger.debug({
841
- details: err.toString(),
842
- package: wantedDependencyDetails,
843
- parents: getPkgsInfoFromIds(options.parentIds, ctx.resolvedPkgsById),
844
- prefix: options.prefix,
845
- reason: 'resolution_failure',
846
- });
847
- return null;
1081
+ // Resolver-inline policy violations (e.g. minimumReleaseAge) flow up
1082
+ // here; collect them onto the shared context so resolveDependencyTree
1083
+ // can hand the full set to the install command between
1084
+ // resolveDependencyTree and resolvePeers.
1085
+ if (pkgResponse.body.policyViolation) {
1086
+ ctx.resolutionPolicyViolations.push(pkgResponse.body.policyViolation);
848
1087
  }
849
- err.package = wantedDependencyDetails;
850
- err.prefix = options.prefix;
851
- err.pkgsStack = getPkgsInfoFromIds(options.parentIds, ctx.resolvedPkgsById);
852
- throw err;
853
- }
854
- dependencyResolvedLogger.debug({
855
- resolution: pkgResponse.body.id,
856
- wanted: {
857
- dependentId: options.parentPkg.pkgId,
858
- name: wantedDependency.alias,
859
- rawSpec: wantedDependency.bareSpecifier,
860
- },
861
- });
862
- // Resolver-inline policy violations (e.g. minimumReleaseAge) flow up
863
- // here; collect them onto the shared context so resolveDependencyTree
864
- // can hand the full set to the install command between
865
- // resolveDependencyTree and resolvePeers.
866
- if (pkgResponse.body.policyViolation) {
867
- ctx.resolutionPolicyViolations.push(pkgResponse.body.policyViolation);
868
- }
869
- // Check if exotic dependencies are disallowed in subdependencies
870
- if (ctx.blockExoticSubdeps &&
871
- options.currentDepth > 0 &&
872
- pkgResponse.body.resolvedVia != null && // This is already coming from the lockfile, we skip the check in this case for now. Should be fixed later.
873
- isExoticDep(pkgResponse.body.resolvedVia)) {
874
- 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`);
875
- error.prefix = options.prefix;
876
- error.pkgsStack = getPkgsInfoFromIds(options.parentIds, ctx.resolvedPkgsById);
877
- throw error;
878
- }
879
- if (ctx.allPreferredVersions && pkgResponse.body.manifest?.version) {
880
- if (!ctx.allPreferredVersions[pkgResponse.body.manifest.name]) {
881
- ctx.allPreferredVersions[pkgResponse.body.manifest.name] = {};
1088
+ // Check if exotic dependencies are disallowed in subdependencies
1089
+ if (ctx.blockExoticSubdeps &&
1090
+ options.currentDepth > 0 &&
1091
+ pkgResponse.body.resolvedVia != null && // This is already coming from the lockfile, we skip the check in this case for now. Should be fixed later.
1092
+ isExoticDep(pkgResponse.body.resolvedVia)) {
1093
+ 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`);
1094
+ error.prefix = options.prefix;
1095
+ error.pkgsStack = getPkgsInfoFromIds(options.parentIds, ctx.resolvedPkgsById);
1096
+ throw error;
882
1097
  }
883
- ctx.allPreferredVersions[pkgResponse.body.manifest.name][pkgResponse.body.manifest.version] = 'version';
884
- }
885
- if (!pkgResponse.body.updated &&
886
- options.currentDepth === Math.max(0, options.updateDepth) &&
887
- depIsLinked && !ctx.force && !options.proceed) {
888
- return null;
889
- }
890
- if (pkgResponse.body.isLocal) {
891
- if (!pkgResponse.body.manifest) {
892
- // This should actually never happen because the local-resolver returns a manifest
893
- // even if no real manifest exists in the filesystem.
894
- throw new PnpmError('MISSING_PACKAGE_JSON', `Can't install ${wantedDependency.bareSpecifier}: Missing package.json file`);
1098
+ if (ctx.allPreferredVersions && pkgResponse.body.manifest?.version) {
1099
+ if (!ctx.allPreferredVersions[pkgResponse.body.manifest.name]) {
1100
+ ctx.allPreferredVersions[pkgResponse.body.manifest.name] = {};
1101
+ }
1102
+ ctx.allPreferredVersions[pkgResponse.body.manifest.name][pkgResponse.body.manifest.version] = 'version';
895
1103
  }
896
- return {
897
- alias: wantedDependency.alias ?? pkgResponse.body.alias ?? pkgResponse.body.manifest.name ?? path.basename(pkgResponse.body.resolution.directory),
898
- dev: wantedDependency.dev,
899
- isLinkedDependency: true,
900
- name: pkgResponse.body.manifest.name,
901
- optional: wantedDependency.optional,
902
- pkgId: pkgResponse.body.id,
903
- resolution: pkgResponse.body.resolution,
904
- version: pkgResponse.body.manifest.version,
905
- normalizedBareSpecifier: pkgResponse.body.normalizedBareSpecifier,
906
- pkg: pkgResponse.body.manifest,
907
- };
908
- }
909
- let prepare;
910
- let hasBin;
911
- let pkg = getManifestFromResponse(pkgResponse, wantedDependency, currentPkg);
912
- if (!pkg.dependencies) {
913
- pkg.dependencies = {};
914
- }
915
- if (ctx.readPackageHook != null) {
916
- pkg = await ctx.readPackageHook(pkg);
917
- }
918
- if (pkg.peerDependencies && pkg.dependencies) {
919
- if (ctx.autoInstallPeers) {
920
- pkg = {
921
- ...pkg,
922
- dependencies: omit(Object.keys(pkg.peerDependencies), pkg.dependencies),
923
- };
1104
+ if (!pkgResponse.body.updated &&
1105
+ options.currentDepth === Math.max(0, options.updateDepth) &&
1106
+ depIsLinked && !ctx.force && !options.proceed) {
1107
+ return null;
924
1108
  }
925
- else {
926
- pkg = {
927
- ...pkg,
928
- dependencies: omit(Object.keys(pkg.peerDependencies).filter((peerDep) => options.parentPkgAliases[peerDep]), pkg.dependencies),
1109
+ if (pkgResponse.body.isLocal) {
1110
+ if (!pkgResponse.body.manifest) {
1111
+ // This should actually never happen because the local-resolver returns a manifest
1112
+ // even if no real manifest exists in the filesystem.
1113
+ throw new PnpmError('MISSING_PACKAGE_JSON', `Can't install ${wantedDependency.bareSpecifier}: Missing package.json file`);
1114
+ }
1115
+ return {
1116
+ alias: wantedDependency.alias ?? pkgResponse.body.alias ?? pkgResponse.body.manifest.name ?? path.basename(pkgResponse.body.resolution.directory),
1117
+ dev: wantedDependency.dev,
1118
+ isLinkedDependency: true,
1119
+ name: pkgResponse.body.manifest.name,
1120
+ optional: wantedDependency.optional,
1121
+ pkgId: pkgResponse.body.id,
1122
+ resolution: pkgResponse.body.resolution,
1123
+ version: pkgResponse.body.manifest.version,
1124
+ normalizedBareSpecifier: pkgResponse.body.normalizedBareSpecifier,
1125
+ pkg: pkgResponse.body.manifest,
929
1126
  };
930
1127
  }
931
- }
932
- if (pkg.engines?.runtime != null) {
933
- convertEnginesRuntimeToDependencies(pkg, 'engines', 'dependencies');
934
- }
935
- if (!pkg.name) { // TODO: don't fail on optional dependencies
936
- throw new PnpmError('MISSING_PACKAGE_NAME', `Can't install ${wantedDependency.bareSpecifier}: Missing package name`);
937
- }
938
- let pkgIdWithPatchHash = (pkgResponse.body.id.startsWith(`${pkg.name}@`) ? pkgResponse.body.id : `${pkg.name}@${pkgResponse.body.id}`);
939
- const patch = getPatchInfo(ctx.patchedDependencies, pkg.name, pkg.version);
940
- if (patch) {
941
- ctx.appliedPatches.add(patch.key);
942
- pkgIdWithPatchHash = `${pkgIdWithPatchHash}(patch_hash=${patch.hash})`;
943
- }
944
- // We are building the dependency tree only until there are new packages
945
- // or the packages repeat in a unique order.
946
- // This is needed later during peer dependencies resolution.
947
- //
948
- // So we resolve foo > bar > qar > foo
949
- // But we stop on foo > bar > qar > foo > qar
950
- // In the second example, there's no reason to walk qar again
951
- // when qar is included the first time, the dependencies of foo
952
- // are already resolved and included as parent dependencies of qar.
953
- // So during peers resolution, qar cannot possibly get any new or different
954
- // peers resolved, after the first occurrence.
955
- //
956
- // However, in the next example we would analyze the second qar as well,
957
- // because zoo is a new parent package:
958
- // foo > bar > qar > zoo > qar
959
- if (parentIdsContainSequence(options.parentIds, options.parentPkg.pkgId, pkgResponse.body.id) || pkgResponse.body.id === options.parentPkg.pkgId) {
960
- return null;
961
- }
962
- if (!options.update && (currentPkg.dependencyLockfile != null) && currentPkg.depPath &&
963
- !pkgResponse.body.updated &&
964
- // peerDependencies field is also used for transitive peer dependencies which should not be linked
965
- // That's why we cannot omit reading package.json of such dependencies.
966
- // This can be removed if we implement something like peerDependenciesMeta.transitive: true
967
- (currentPkg.dependencyLockfile.peerDependencies == null)) {
968
- hasBin = currentPkg.dependencyLockfile.hasBin === true;
969
- pkg = {
970
- ...nameVerFromPkgSnapshot(currentPkg.depPath, currentPkg.dependencyLockfile),
971
- ...omitDepsFields(currentPkg.dependencyLockfile),
972
- ...pkg,
973
- };
974
- }
975
- else {
976
- prepare = Boolean(pkgResponse.body.resolvedVia === 'git-repository' &&
977
- typeof pkg.scripts?.prepare === 'string');
978
- if (currentPkg.dependencyLockfile?.deprecated &&
979
- !pkgResponse.body.updated && !pkg.deprecated) {
980
- pkg.deprecated = currentPkg.dependencyLockfile.deprecated;
1128
+ let prepare;
1129
+ let hasBin;
1130
+ let pkg = getManifestFromResponse(pkgResponse, wantedDependency, currentPkg);
1131
+ if (!pkg.dependencies) {
1132
+ pkg.dependencies = {};
981
1133
  }
982
- hasBin = (currentPkg.dependencyLockfile?.hasBin != null && !pkg.bin)
983
- ? currentPkg.dependencyLockfile.hasBin
984
- : Boolean((pkg.bin && !(pkg.bin === '' || Object.keys(pkg.bin).length === 0)) ?? pkg.directories?.bin);
985
- }
986
- if (options.currentDepth === 0 && pkgResponse.body.latest && pkgResponse.body.latest !== pkg.version) {
987
- ctx.outdatedDependencies[pkgResponse.body.id] = pkgResponse.body.latest;
988
- }
989
- if (pkg.peerDependencies != null) {
990
- for (const name in pkg.peerDependencies) {
991
- ctx.allPeerDepNames.add(name);
1134
+ if (ctx.readPackageHook != null) {
1135
+ pkg = await ctx.readPackageHook(pkg);
992
1136
  }
993
- }
994
- if (pkg.peerDependenciesMeta != null) {
995
- for (const name in pkg.peerDependenciesMeta) {
996
- ctx.allPeerDepNames.add(name);
1137
+ if (pkg.peerDependencies && pkg.dependencies) {
1138
+ if (ctx.autoInstallPeers) {
1139
+ pkg = {
1140
+ ...pkg,
1141
+ dependencies: omit(Object.keys(pkg.peerDependencies), pkg.dependencies),
1142
+ };
1143
+ }
1144
+ else {
1145
+ pkg = {
1146
+ ...pkg,
1147
+ dependencies: omit(Object.keys(pkg.peerDependencies).filter((peerDep) => options.parentPkgAliases[peerDep]), pkg.dependencies),
1148
+ };
1149
+ }
997
1150
  }
998
- }
999
- // In case of leaf dependencies (dependencies that have no prod deps or peer deps),
1000
- // we only ever need to analyze one leaf dep in a graph, so the nodeId can be short and stateless.
1001
- const nodeId = pkgIsLeaf(pkg) ? pkgResponse.body.id : nextNodeId();
1002
- const parentIsInstallable = options.parentPkg.installable === undefined || options.parentPkg.installable;
1003
- const installable = parentIsInstallable && pkgResponse.body.isInstallable !== false;
1004
- const isNew = !ctx.resolvedPkgsById[pkgResponse.body.id];
1005
- const parentImporterId = options.parentIds[0];
1006
- const currentIsOptional = wantedDependency.optional || options.parentPkg.optional;
1007
- if (isNew) {
1008
- if (pkg.deprecated &&
1009
- (!ctx.allowedDeprecatedVersions[pkg.name] || !semver.satisfies(pkg.version, ctx.allowedDeprecatedVersions[pkg.name]))) {
1010
- // Report deprecated packages only on first occurrence.
1011
- deprecationLogger.debug({
1012
- deprecated: pkg.deprecated,
1013
- depth: options.currentDepth,
1014
- pkgId: pkgResponse.body.id,
1015
- pkgName: pkg.name,
1016
- pkgVersion: pkg.version,
1017
- prefix: options.prefix,
1018
- });
1151
+ if (pkg.engines?.runtime != null) {
1152
+ convertEnginesRuntimeToDependencies(pkg, 'engines', 'dependencies');
1019
1153
  }
1020
- if (pkgResponse.body.isInstallable === false || !parentIsInstallable) {
1021
- ctx.skipped.add(pkgResponse.body.id);
1154
+ if (!pkg.name) { // TODO: don't fail on optional dependencies
1155
+ throw new PnpmError('MISSING_PACKAGE_NAME', `Can't install ${wantedDependency.bareSpecifier}: Missing package name`);
1022
1156
  }
1023
- progressLogger.debug({
1024
- packageId: pkgResponse.body.id,
1025
- requester: ctx.lockfileDir,
1026
- status: 'resolved',
1027
- });
1028
- // WARN: It is very important to keep this sync
1029
- // Otherwise, deprecation messages for the same package might get written several times
1030
- ctx.resolvedPkgsById[pkgResponse.body.id] = getResolvedPackage({
1031
- dependencyLockfile: currentPkg.dependencyLockfile,
1032
- pkgIdWithPatchHash,
1033
- force: ctx.force,
1034
- hasBin,
1035
- patch,
1036
- pkg,
1037
- pkgResponse,
1038
- prepare,
1039
- wantedDependency,
1040
- parentImporterId,
1041
- optional: currentIsOptional,
1042
- });
1043
- }
1044
- else {
1045
- ctx.resolvedPkgsById[pkgResponse.body.id].prod = ctx.resolvedPkgsById[pkgResponse.body.id].prod || !wantedDependency.dev && !wantedDependency.optional;
1046
- ctx.resolvedPkgsById[pkgResponse.body.id].dev = ctx.resolvedPkgsById[pkgResponse.body.id].dev || wantedDependency.dev;
1047
- ctx.resolvedPkgsById[pkgResponse.body.id].optional = ctx.resolvedPkgsById[pkgResponse.body.id].optional && currentIsOptional;
1048
- if (ctx.resolvedPkgsById[pkgResponse.body.id].fetching == null && pkgResponse.fetching != null) {
1049
- ctx.resolvedPkgsById[pkgResponse.body.id].fetching = pkgResponse.fetching;
1050
- ctx.resolvedPkgsById[pkgResponse.body.id].filesIndexFile = pkgResponse.filesIndexFile;
1157
+ let pkgIdWithPatchHash = (pkgResponse.body.id.startsWith(`${pkg.name}@`) ? pkgResponse.body.id : `${pkg.name}@${pkgResponse.body.id}`);
1158
+ const patch = getPatchInfo(ctx.patchedDependencies, pkg.name, pkg.version);
1159
+ if (patch) {
1160
+ ctx.appliedPatches.add(patch.key);
1161
+ pkgIdWithPatchHash = `${pkgIdWithPatchHash}(patch_hash=${patch.hash})`;
1051
1162
  }
1052
- if (ctx.dependenciesTree.has(nodeId)) {
1053
- ctx.dependenciesTree.get(nodeId).depth = Math.min(ctx.dependenciesTree.get(nodeId).depth, options.currentDepth);
1163
+ // We are building the dependency tree only until there are new packages
1164
+ // or the packages repeat in a unique order.
1165
+ // This is needed later during peer dependencies resolution.
1166
+ //
1167
+ // So we resolve foo > bar > qar > foo
1168
+ // But we stop on foo > bar > qar > foo > qar
1169
+ // In the second example, there's no reason to walk qar again
1170
+ // when qar is included the first time, the dependencies of foo
1171
+ // are already resolved and included as parent dependencies of qar.
1172
+ // So during peers resolution, qar cannot possibly get any new or different
1173
+ // peers resolved, after the first occurrence.
1174
+ //
1175
+ // However, in the next example we would analyze the second qar as well,
1176
+ // because zoo is a new parent package:
1177
+ // foo > bar > qar > zoo > qar
1178
+ if (parentIdsContainSequence(options.parentIds, options.parentPkg.pkgId, pkgResponse.body.id) || pkgResponse.body.id === options.parentPkg.pkgId) {
1179
+ return null;
1180
+ }
1181
+ if (!options.update && (currentPkg.dependencyLockfile != null) && currentPkg.depPath &&
1182
+ !pkgResponse.body.updated &&
1183
+ // peerDependencies field is also used for transitive peer dependencies which should not be linked
1184
+ // That's why we cannot omit reading package.json of such dependencies.
1185
+ // This can be removed if we implement something like peerDependenciesMeta.transitive: true
1186
+ (currentPkg.dependencyLockfile.peerDependencies == null)) {
1187
+ hasBin = currentPkg.dependencyLockfile.hasBin === true;
1188
+ pkg = {
1189
+ ...nameVerFromPkgSnapshot(currentPkg.depPath, currentPkg.dependencyLockfile),
1190
+ ...omitDepsFields(currentPkg.dependencyLockfile),
1191
+ ...pkg,
1192
+ };
1054
1193
  }
1055
1194
  else {
1056
- ctx.pendingNodes.push({
1057
- alias: wantedDependency.alias ?? pkgResponse.body.alias ?? pkg.name,
1058
- depth: options.currentDepth,
1059
- parentIds: options.parentIds,
1060
- installable,
1061
- lockedPeerContext: currentPkg.lockedPeerContext,
1062
- previousDepPath: currentPkg.depPath,
1063
- nodeId,
1064
- resolvedPackage: ctx.resolvedPkgsById[pkgResponse.body.id],
1065
- });
1195
+ prepare = Boolean(pkgResponse.body.resolvedVia === 'git-repository' &&
1196
+ typeof pkg.scripts?.prepare === 'string');
1197
+ if (currentPkg.dependencyLockfile?.deprecated &&
1198
+ !pkgResponse.body.updated && !pkg.deprecated) {
1199
+ pkg.deprecated = currentPkg.dependencyLockfile.deprecated;
1200
+ }
1201
+ hasBin = (currentPkg.dependencyLockfile?.hasBin != null && !pkg.bin)
1202
+ ? currentPkg.dependencyLockfile.hasBin
1203
+ : Boolean((pkg.bin && !(pkg.bin === '' || Object.keys(pkg.bin).length === 0)) ?? pkg.directories?.bin);
1066
1204
  }
1067
- }
1068
- const rootDir = pkgResponse.body.resolution.type === 'directory'
1069
- ? path.resolve(ctx.lockfileDir, pkgResponse.body.resolution.directory)
1070
- : options.prefix;
1071
- let missingPeersOfChildren;
1072
- if (ctx.hoistPeers && !options.parentIds.includes(pkgResponse.body.id)) {
1073
- if (ctx.missingPeersOfChildrenByPkgId[pkgResponse.body.id]) {
1074
- // This if condition is used to avoid a dead lock.
1075
- // There might be a better way to hoist peer dependencies during resolution
1076
- // but it would probably require a big rewrite of the resolution algorithm.
1077
- if (ctx.missingPeersOfChildrenByPkgId[pkgResponse.body.id].depth >= options.currentDepth ||
1078
- ctx.missingPeersOfChildrenByPkgId[pkgResponse.body.id].missingPeersOfChildren.resolved) {
1079
- missingPeersOfChildren = ctx.missingPeersOfChildrenByPkgId[pkgResponse.body.id].missingPeersOfChildren;
1205
+ if (options.currentDepth === 0 && pkgResponse.body.latest && pkgResponse.body.latest !== pkg.version) {
1206
+ ctx.outdatedDependencies[pkgResponse.body.id] = pkgResponse.body.latest;
1207
+ }
1208
+ if (pkg.peerDependencies != null) {
1209
+ for (const name in pkg.peerDependencies) {
1210
+ ctx.allPeerDepNames.add(name);
1211
+ }
1212
+ }
1213
+ if (pkg.peerDependenciesMeta != null) {
1214
+ for (const name in pkg.peerDependenciesMeta) {
1215
+ ctx.allPeerDepNames.add(name);
1216
+ }
1217
+ }
1218
+ // In case of leaf dependencies (dependencies that have no prod deps or peer deps),
1219
+ // we only ever need to analyze one leaf dep in a graph, so the nodeId can be short and stateless.
1220
+ const nodeId = pkgIsLeaf(pkg) ? pkgResponse.body.id : nextNodeId();
1221
+ const parentIsInstallable = options.parentPkg.installable === undefined || options.parentPkg.installable;
1222
+ const installable = parentIsInstallable && pkgResponse.body.isInstallable !== false;
1223
+ const packageIsNew = !ctx.resolvedPkgsById[pkgResponse.body.id];
1224
+ const parentImporterId = options.parentIds[0];
1225
+ const currentIsOptional = wantedDependency.optional || options.parentPkg.optional;
1226
+ const childrenResolution = claimChildrenResolution(ctx, {
1227
+ currentDepth: options.currentDepth,
1228
+ parentIds: options.parentIds,
1229
+ pkgId: pkgResponse.body.id,
1230
+ });
1231
+ const isNew = childrenResolution.isOwner;
1232
+ if (packageIsNew) {
1233
+ if (pkg.deprecated &&
1234
+ (!ctx.allowedDeprecatedVersions[pkg.name] || !semver.satisfies(pkg.version, ctx.allowedDeprecatedVersions[pkg.name]))) {
1235
+ // Report deprecated packages only on first occurrence.
1236
+ deprecationLogger.debug({
1237
+ deprecated: pkg.deprecated,
1238
+ depth: options.currentDepth,
1239
+ pkgId: pkgResponse.body.id,
1240
+ pkgName: pkg.name,
1241
+ pkgVersion: pkg.version,
1242
+ prefix: options.prefix,
1243
+ });
1244
+ }
1245
+ if (pkgResponse.body.isInstallable === false || !parentIsInstallable) {
1246
+ ctx.skipped.add(pkgResponse.body.id);
1080
1247
  }
1248
+ progressLogger.debug({
1249
+ packageId: pkgResponse.body.id,
1250
+ requester: ctx.lockfileDir,
1251
+ status: 'resolved',
1252
+ });
1253
+ // WARN: It is very important to keep this sync
1254
+ // Otherwise, deprecation messages for the same package might get written several times
1255
+ ctx.resolvedPkgsById[pkgResponse.body.id] = getResolvedPackage({
1256
+ dependencyLockfile: currentPkg.dependencyLockfile,
1257
+ pkgIdWithPatchHash,
1258
+ force: ctx.force,
1259
+ hasBin,
1260
+ patch,
1261
+ pkg,
1262
+ pkgResponse,
1263
+ prepare,
1264
+ wantedDependency,
1265
+ parentImporterId,
1266
+ optional: currentIsOptional,
1267
+ });
1081
1268
  }
1082
1269
  else {
1083
- const p = pDefer();
1084
- missingPeersOfChildren = {
1085
- resolve: p.resolve,
1086
- reject: p.reject,
1087
- get: pShare(p.promise),
1088
- };
1089
- ctx.missingPeersOfChildrenByPkgId[pkgResponse.body.id] = {
1090
- depth: options.currentDepth,
1091
- missingPeersOfChildren,
1092
- };
1270
+ ctx.resolvedPkgsById[pkgResponse.body.id].prod = ctx.resolvedPkgsById[pkgResponse.body.id].prod || !wantedDependency.dev && !wantedDependency.optional;
1271
+ ctx.resolvedPkgsById[pkgResponse.body.id].dev = ctx.resolvedPkgsById[pkgResponse.body.id].dev || wantedDependency.dev;
1272
+ ctx.resolvedPkgsById[pkgResponse.body.id].optional = ctx.resolvedPkgsById[pkgResponse.body.id].optional && currentIsOptional;
1273
+ if (ctx.resolvedPkgsById[pkgResponse.body.id].fetching == null && pkgResponse.fetching != null) {
1274
+ ctx.resolvedPkgsById[pkgResponse.body.id].fetching = pkgResponse.fetching;
1275
+ ctx.resolvedPkgsById[pkgResponse.body.id].filesIndexFile = pkgResponse.filesIndexFile;
1276
+ }
1277
+ if (!isNew) {
1278
+ if (ctx.dependenciesTree.has(nodeId)) {
1279
+ ctx.dependenciesTree.get(nodeId).depth = Math.min(ctx.dependenciesTree.get(nodeId).depth, options.currentDepth);
1280
+ }
1281
+ else {
1282
+ ctx.pendingNodes.push({
1283
+ alias: wantedDependency.alias ?? pkgResponse.body.alias ?? pkg.name,
1284
+ depth: options.currentDepth,
1285
+ parentIds: options.parentIds,
1286
+ installable,
1287
+ lockedPeerContext: currentPkg.lockedPeerContext,
1288
+ previousDepPath: currentPkg.depPath,
1289
+ nodeId,
1290
+ resolvedPackage: ctx.resolvedPkgsById[pkgResponse.body.id],
1291
+ });
1292
+ }
1293
+ }
1093
1294
  }
1295
+ const rootDir = pkgResponse.body.resolution.type === 'directory'
1296
+ ? path.resolve(ctx.lockfileDir, pkgResponse.body.resolution.directory)
1297
+ : options.prefix;
1298
+ const missingPeersOfChildren = childrenResolution.missingPeersOfChildren;
1299
+ const resolvedPkg = ctx.resolvedPkgsById[pkgResponse.body.id];
1300
+ return {
1301
+ alias: wantedDependency.alias ?? pkgResponse.body.alias ?? pkg.name,
1302
+ depIsLinked,
1303
+ resolvedVia: pkgResponse.body.resolvedVia,
1304
+ isNew,
1305
+ nodeId,
1306
+ normalizedBareSpecifier: pkgResponse.body.normalizedBareSpecifier,
1307
+ missingPeersOfChildren,
1308
+ childrenResolutionId: childrenResolution.id,
1309
+ pkgId: pkgResponse.body.id,
1310
+ rootDir,
1311
+ missingPeers: getMissingPeers(pkg),
1312
+ optional: resolvedPkg.optional,
1313
+ version: resolvedPkg.version,
1314
+ saveCatalogName: wantedDependency.saveCatalogName,
1315
+ // Next fields are actually only needed when isNew = true
1316
+ installable,
1317
+ isLinkedDependency: undefined,
1318
+ pkg,
1319
+ updated: pkgResponse.body.updated,
1320
+ publishedAt: pkgResponse.body.publishedAt,
1321
+ };
1322
+ }
1323
+ finally {
1324
+ finishPackageResolution();
1094
1325
  }
1095
- const resolvedPkg = ctx.resolvedPkgsById[pkgResponse.body.id];
1096
- return {
1097
- alias: wantedDependency.alias ?? pkgResponse.body.alias ?? pkg.name,
1098
- depIsLinked,
1099
- resolvedVia: pkgResponse.body.resolvedVia,
1100
- isNew,
1101
- nodeId,
1102
- normalizedBareSpecifier: pkgResponse.body.normalizedBareSpecifier,
1103
- missingPeersOfChildren,
1104
- pkgId: pkgResponse.body.id,
1105
- rootDir,
1106
- missingPeers: getMissingPeers(pkg),
1107
- optional: resolvedPkg.optional,
1108
- version: resolvedPkg.version,
1109
- saveCatalogName: wantedDependency.saveCatalogName,
1110
- // Next fields are actually only needed when isNew = true
1111
- installable,
1112
- isLinkedDependency: undefined,
1113
- pkg,
1114
- updated: pkgResponse.body.updated,
1115
- publishedAt: pkgResponse.body.publishedAt,
1116
- };
1117
1326
  }
1118
1327
  export function getManifestFromResponse(pkgResponse, wantedDependency, currentPkg) {
1119
1328
  if (pkgResponse.body.manifest)
@@ -5,7 +5,7 @@ import type { PreferredVersions, Resolution, ResolutionPolicyViolation, Workspac
5
5
  import type { StoreController } from '@pnpm/store.controller-types';
6
6
  import type { AllowBuild, AllowedDeprecatedVersions, PinnedVersion, PkgResolutionId, ProjectId, ProjectManifest, ProjectRootDir, ReadPackageHook, Registries, SupportedArchitectures, TrustPolicy } from '@pnpm/types';
7
7
  import type { WantedDependency } from './getNonDevWantedDependencies.js';
8
- import { type NodeId } from './nextNodeId.js';
8
+ import type { NodeId } from './nextNodeId.js';
9
9
  import { type DependenciesTree, type LinkedDependency, type ResolvedPackage, type ResolvedPkgsById } from './resolveDependencies.js';
10
10
  export type { DependenciesTree, DependenciesTreeNode, LinkedDependency, ResolvedPackage } from './resolveDependencies.js';
11
11
  export interface ResolvedImporters {
@@ -3,9 +3,7 @@ import { createPackageVersionPolicyOrThrow, getPublishedByPolicy } from '@pnpm/c
3
3
  import { globalWarn } from '@pnpm/logger';
4
4
  import { BUILTIN_NAMED_REGISTRIES } from '@pnpm/resolving.npm-resolver';
5
5
  import { partition } from 'ramda';
6
- import { nextNodeId } from './nextNodeId.js';
7
- import { parentIdsContainSequence } from './parentIdsContainSequence.js';
8
- import { resolveRootDependencies, } from './resolveDependencies.js';
6
+ import { buildTree, resolveRootDependencies, } from './resolveDependencies.js';
9
7
  export async function resolveDependencyTree(importers, opts) {
10
8
  const wantedToBeSkippedPackageIds = new Set();
11
9
  const autoInstallPeers = opts.autoInstallPeers === true;
@@ -57,6 +55,14 @@ export async function resolveDependencyTree(importers, opts) {
57
55
  allPeerDepNames: new Set(),
58
56
  maximumPublishedBy: publishedBy,
59
57
  publishedByExclude,
58
+ packageResolutionBarrier: {
59
+ activeByDepth: new Map(),
60
+ waiters: [],
61
+ },
62
+ childrenResolutionByPkgId: {},
63
+ childrenResolutionId: 0,
64
+ importerResolutionOrder: Object.fromEntries(importers.map(({ id }, index) => [id, index])),
65
+ nodeResolutionContextByNodeId: new Map(),
60
66
  trustPolicy: opts.trustPolicy,
61
67
  trustPolicyExclude: opts.trustPolicyExclude ? createPackageVersionPolicyOrThrow(opts.trustPolicyExclude, 'trustPolicyExclude') : undefined,
62
68
  trustPolicyIgnoreAfter: opts.trustPolicyIgnoreAfter,
@@ -176,32 +182,6 @@ export async function resolveDependencyTree(importers, opts) {
176
182
  resolutionPolicyViolations: ctx.resolutionPolicyViolations,
177
183
  };
178
184
  }
179
- function buildTree(ctx, parentId, parentIds, children, depth, installable) {
180
- const childrenNodeIds = {};
181
- for (const child of children) {
182
- if (child.id.startsWith('link:')) {
183
- childrenNodeIds[child.alias] = child.id;
184
- continue;
185
- }
186
- if (parentIdsContainSequence(parentIds, parentId, child.id) || parentId === child.id) {
187
- continue;
188
- }
189
- if (ctx.resolvedPkgsById[child.id].isLeaf) {
190
- childrenNodeIds[child.alias] = child.id;
191
- continue;
192
- }
193
- const childNodeId = nextNodeId();
194
- childrenNodeIds[child.alias] = childNodeId;
195
- installable = installable || !ctx.skipped.has(child.id);
196
- ctx.dependenciesTree.set(childNodeId, {
197
- children: () => buildTree(ctx, child.id, [...parentIds, child.id], ctx.childrenByParentId[child.id], depth + 1, installable),
198
- depth,
199
- installable,
200
- resolvedPackage: ctx.resolvedPkgsById[child.id],
201
- });
202
- }
203
- return childrenNodeIds;
204
- }
205
185
  /**
206
186
  * There may be cases where multiple dependencies have the same alias in the directDeps array.
207
187
  * E.g., when there is "is-negative: github:kevva/is-negative#1.0.0" in the package.json dependencies,
@@ -320,6 +320,15 @@ async function resolvePeersOfNode(currentAlias, nodeId, parentParentPkgs, ctx) {
320
320
  ctx.pathsByNodeIdPromises.set(peerNodeId, peerPathPromise);
321
321
  ctx.pathsByNodeId.set(peerNodeId, previousPeerDepPath);
322
322
  peerPathPromise.resolve(previousPeerDepPath);
323
+ // Pinning writes into parentPkgs, and a childless node shares the object
324
+ // with its parent, so siblings processed later would see the pinned
325
+ // provider too. Copy before the first write to keep the pin scoped to
326
+ // this node — sibling order follows resolution order, so a leak makes the
327
+ // lockfile depend on network timing. Done here, not before the loop, so a
328
+ // pass whose guards skip every entry never allocates.
329
+ if (parentPkgs === parentParentPkgs) {
330
+ parentPkgs = { ...parentParentPkgs };
331
+ }
323
332
  parentPkgs[peerName] = lockedPeer;
324
333
  }
325
334
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pnpm/installing.deps-resolver",
3
- "version": "1100.2.2",
3
+ "version": "1100.2.3",
4
4
  "description": "Resolves dependency graph of a package",
5
5
  "keywords": [
6
6
  "pnpm",
@@ -27,8 +27,8 @@
27
27
  "!*.map"
28
28
  ],
29
29
  "dependencies": {
30
- "@pnpm/util.lex-comparator": "^3.0.2",
31
- "@yarnpkg/core": "4.5.0",
30
+ "@pnpm/util.lex-comparator": "^4.0.1",
31
+ "@yarnpkg/core": "4.8.0",
32
32
  "graph-cycles": "3.0.0",
33
33
  "is-inner-link": "^5.0.0",
34
34
  "is-subdir": "^2.0.0",
@@ -39,46 +39,46 @@
39
39
  "ramda": "npm:@pnpm/ramda@0.28.1",
40
40
  "rename-overwrite": "^7.0.1",
41
41
  "safe-promise-defer": "^2.0.0",
42
- "semver": "^7.8.1",
42
+ "semver": "^7.8.4",
43
43
  "semver-range-intersect": "^0.3.1",
44
44
  "validate-npm-package-name": "7.0.2",
45
45
  "version-selector-type": "^3.0.0",
46
- "@pnpm/catalogs.types": "1100.0.0",
47
46
  "@pnpm/catalogs.resolver": "1100.0.0",
48
- "@pnpm/config.version-policy": "1100.1.4",
49
- "@pnpm/core-loggers": "1100.2.0",
47
+ "@pnpm/catalogs.types": "1100.0.0",
48
+ "@pnpm/config.version-policy": "1100.1.5",
50
49
  "@pnpm/constants": "1100.0.0",
51
- "@pnpm/deps.graph-hasher": "1100.2.4",
52
- "@pnpm/deps.path": "1100.0.7",
53
- "@pnpm/deps.peer-range": "1100.0.1",
50
+ "@pnpm/core-loggers": "1100.2.1",
51
+ "@pnpm/deps.graph-hasher": "1100.2.5",
52
+ "@pnpm/deps.path": "1100.0.8",
54
53
  "@pnpm/error": "1100.0.0",
55
- "@pnpm/fetching.pick-fetcher": "1100.0.11",
56
- "@pnpm/hooks.types": "1100.0.11",
57
- "@pnpm/lockfile.preferred-versions": "1100.0.15",
58
- "@pnpm/lockfile.pruner": "1100.0.10",
59
- "@pnpm/lockfile.utils": "1100.0.12",
60
- "@pnpm/lockfile.types": "1100.0.10",
61
- "@pnpm/patching.config": "1100.0.7",
54
+ "@pnpm/deps.peer-range": "1100.0.2",
55
+ "@pnpm/fetching.pick-fetcher": "1100.0.12",
56
+ "@pnpm/hooks.types": "1100.0.12",
57
+ "@pnpm/lockfile.preferred-versions": "1100.0.16",
58
+ "@pnpm/lockfile.pruner": "1100.0.11",
59
+ "@pnpm/lockfile.types": "1100.0.11",
60
+ "@pnpm/lockfile.utils": "1100.0.13",
61
+ "@pnpm/patching.config": "1100.0.8",
62
62
  "@pnpm/patching.types": "1100.0.0",
63
- "@pnpm/pkg-manifest.reader": "1100.0.7",
64
- "@pnpm/pkg-manifest.utils": "1100.2.4",
65
- "@pnpm/resolving.npm-resolver": "1101.5.2",
66
- "@pnpm/store.controller-types": "1100.1.4",
67
- "@pnpm/resolving.resolver-base": "1100.4.1",
68
- "@pnpm/types": "1101.3.1",
69
- "@pnpm/workspace.spec-parser": "1100.0.0"
63
+ "@pnpm/pkg-manifest.utils": "1100.2.5",
64
+ "@pnpm/pkg-manifest.reader": "1100.0.8",
65
+ "@pnpm/resolving.npm-resolver": "1102.0.0",
66
+ "@pnpm/store.controller-types": "1100.1.5",
67
+ "@pnpm/types": "1101.3.2",
68
+ "@pnpm/workspace.spec-parser": "1100.0.0",
69
+ "@pnpm/resolving.resolver-base": "1100.4.2"
70
70
  },
71
71
  "peerDependencies": {
72
- "@pnpm/logger": "^1001.0.1"
72
+ "@pnpm/logger": "^1100.0.0"
73
73
  },
74
74
  "devDependencies": {
75
- "@jest/globals": "30.3.0",
75
+ "@jest/globals": "30.4.1",
76
76
  "@types/normalize-path": "^3.0.2",
77
77
  "@types/ramda": "0.31.1",
78
78
  "@types/semver": "7.7.1",
79
79
  "@types/validate-npm-package-name": "^4.0.2",
80
- "@pnpm/logger": "1100.0.0",
81
- "@pnpm/installing.deps-resolver": "1100.2.2"
80
+ "@pnpm/installing.deps-resolver": "1100.2.3",
81
+ "@pnpm/logger": "1100.0.0"
82
82
  },
83
83
  "engines": {
84
84
  "node": ">=22.13"