@pnpm/installing.deps-resolver 1100.2.3 → 1100.2.4

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.
@@ -27,19 +27,26 @@ function getDedupeMap(injectedDepsByProjects, opts) {
27
27
  for (const [id, deps] of injectedDepsByProjects.entries()) {
28
28
  const dedupedInjectedDeps = new Map();
29
29
  for (const [alias, dep] of deps.entries()) {
30
- // Check for subgroup not equal.
31
- // The injected project in the workspace may have dev deps
32
- const children = Object.entries(opts.depGraph[dep.depPath].children);
30
+ const node = opts.depGraph[dep.depPath];
33
31
  const targetProjectDeps = opts.dependenciesByProjectId[dep.id];
34
- // When the target project wasn't part of the current resolution (e.g. single-project
35
- // operation), its dependencies aren't available. We can only deduplicate safely when the
36
- // injected dep has no children (the empty set is always a subset).
32
+ // In single-project operations (e.g. `pnpm rm` from inside a workspace package) the target
33
+ // workspace project isn't being resolved, so its children aren't in
34
+ // `dependenciesByProjectId`. The injected dep was resolved against the same workspace
35
+ // package source, so dedupe is safe. The exception is peer-suffixed depPaths, whose
36
+ // resolution depends on the importer's peer context. A plain `link:` would lose that, so
37
+ // we skip dedupe for those. A depPath is `${pkgIdWithPatchHash}${peerDepGraphHash}`, so it
38
+ // carries a peer suffix exactly when it differs from its peer-free `pkgIdWithPatchHash`.
37
39
  if (!targetProjectDeps) {
38
- if (children.length > 0)
39
- continue;
40
+ if (node.pkgIdWithPatchHash === dep.depPath) {
41
+ dedupedInjectedDeps.set(alias, dep.id);
42
+ }
43
+ continue;
40
44
  }
45
+ // Check for subgroup not equal.
46
+ // The injected project in the workspace may have dev deps
47
+ const children = Object.entries(node.children);
41
48
  const isSubset = children
42
- .every(([alias, depPath]) => targetProjectDeps?.get(alias) === depPath);
49
+ .every(([alias, depPath]) => targetProjectDeps.get(alias) === depPath);
43
50
  if (isSubset) {
44
51
  dedupedInjectedDeps.set(alias, dep.id);
45
52
  }
@@ -314,6 +314,7 @@ function getPublishedByDate(pkgAddresses, timeFromLockfile = {}) {
314
314
  export async function resolveDependencies(ctx, preferredVersions, wantedDependencies, options) {
315
315
  const extendedWantedDeps = getDepsToResolve(wantedDependencies, ctx.wantedLockfile, {
316
316
  preferredDependencies: options.preferredDependencies,
317
+ preferredVersions,
317
318
  prefix: options.prefix,
318
319
  proceed: options.proceed || ctx.forceFullResolution,
319
320
  registries: ctx.registries,
@@ -849,12 +850,33 @@ function getDepsToResolve(wantedDependencies, wantedLockfile, options) {
849
850
  });
850
851
  for (const wantedDependency of wantedDependencies) {
851
852
  let reference = undefined;
853
+ let preferredVersion = undefined;
852
854
  let proceed = proceedAll;
853
855
  if (wantedDependency.alias) {
854
856
  const satisfiesWanted = satisfiesWanted2Args.bind(null, wantedDependency);
855
857
  if (resolvedDependencies[wantedDependency.alias] &&
856
858
  (satisfiesWanted(resolvedDependencies[wantedDependency.alias]) || resolvedDependencies[wantedDependency.alias].startsWith('file:'))) {
857
- reference = resolvedDependencies[wantedDependency.alias];
859
+ const pinnedRef = resolvedDependencies[wantedDependency.alias];
860
+ // Reusing a lockfile pin verbatim bypasses the preferred-versions
861
+ // walk, so a transitive edge stays on a stale lower version even
862
+ // when a direct dependency resolved to a higher version in range.
863
+ const pinned = pinnedRef.startsWith('file:')
864
+ ? undefined
865
+ : getPinnedNameVer(wantedLockfile, pinnedRef, wantedDependency.alias);
866
+ const higherDirectVersion = pinned == null
867
+ ? undefined
868
+ : findHigherDirectDepVersion(options.preferredVersions, pinned.name, pinned.version, wantedDependency.bareSpecifier);
869
+ if (higherDirectVersion != null) {
870
+ // `preferredVersion` (singular) overrides the
871
+ // EXISTING_VERSION_SELECTOR_WEIGHT stability bias that would
872
+ // otherwise re-pick the lower version. The lower version is then
873
+ // never resolved or fetched.
874
+ proceed = true;
875
+ preferredVersion = higherDirectVersion;
876
+ }
877
+ else {
878
+ reference = pinnedRef;
879
+ }
858
880
  }
859
881
  else if (
860
882
  // If dependencies that were used by the previous version of the package
@@ -885,6 +907,7 @@ function getDepsToResolve(wantedDependencies, wantedLockfile, options) {
885
907
  }
886
908
  extendedWantedDeps.push({
887
909
  infoFromLockfile,
910
+ preferredVersion,
888
911
  proceed,
889
912
  wantedDependency,
890
913
  });
@@ -909,6 +932,44 @@ function referenceSatisfiesWantedSpec(opts, wantedDep, preferredRef) {
909
932
  }
910
933
  return semver.satisfies(version, wantedDep.bareSpecifier, true);
911
934
  }
935
+ function getPinnedNameVer(lockfile, reference, alias) {
936
+ const depPath = dp.refToRelative(reference, alias);
937
+ if (depPath === null)
938
+ return undefined;
939
+ const pkgSnapshot = lockfile.packages?.[depPath];
940
+ if (pkgSnapshot == null)
941
+ return undefined;
942
+ return nameVerFromPkgSnapshot(depPath, pkgSnapshot);
943
+ }
944
+ // The highest version a direct dependency (the only deterministic,
945
+ // resolved-first anchor) resolved to that is higher than the
946
+ // lockfile-pinned `pinnedVersion` and still satisfies the edge's range, or
947
+ // `undefined` when none exists. Direct-dependency versions are marked with
948
+ // DIRECT_DEP_SELECTOR_WEIGHT in preferredVersions.
949
+ function findHigherDirectDepVersion(preferredVersions, pinnedName, pinnedVersion, bareSpecifier) {
950
+ if (preferredVersions == null)
951
+ return undefined;
952
+ if (!semver.valid(pinnedVersion))
953
+ return undefined;
954
+ if (semver.validRange(bareSpecifier) === null)
955
+ return undefined;
956
+ const selectors = preferredVersions[pinnedName];
957
+ if (selectors == null)
958
+ return undefined;
959
+ let best;
960
+ for (const [candidate, selector] of Object.entries(selectors)) {
961
+ if (typeof selector === 'object' &&
962
+ selector.selectorType === 'version' &&
963
+ selector.weight === DIRECT_DEP_SELECTOR_WEIGHT &&
964
+ semver.valid(candidate) &&
965
+ semver.gt(candidate, pinnedVersion) &&
966
+ semver.satisfies(candidate, bareSpecifier, true) &&
967
+ (best == null || semver.gt(candidate, best))) {
968
+ best = candidate;
969
+ }
970
+ }
971
+ return best;
972
+ }
912
973
  function getLockedPeerContext(dependencyLockfile) {
913
974
  if (dependencyLockfile.peerDependencies == null)
914
975
  return undefined;
@@ -259,10 +259,11 @@ async function resolvePeersOfNode(currentAlias, nodeId, parentParentPkgs, ctx) {
259
259
  parentPkgs = { ...parentParentPkgs };
260
260
  const parentPkgNodes = [];
261
261
  for (const [alias, nodeId] of Object.entries(children)) {
262
- if (ctx.allPeerDepNames.has(alias)) {
262
+ const childNode = ctx.dependenciesTree.get(nodeId);
263
+ if (ctx.allPeerDepNames.has(alias) || (alias !== childNode.resolvedPackage.name && ctx.allPeerDepNames.has(childNode.resolvedPackage.name))) {
263
264
  parentPkgNodes.push({
264
265
  alias,
265
- node: ctx.dependenciesTree.get(nodeId),
266
+ node: childNode,
266
267
  nodeId,
267
268
  parentNodeIds,
268
269
  });
@@ -391,7 +392,18 @@ async function resolvePeersOfNode(currentAlias, nodeId, parentParentPkgs, ctx) {
391
392
  }
392
393
  let cache;
393
394
  const isPure = allResolvedPeers.size === 0 && allMissingPeers.size === 0;
394
- if (isPure) {
395
+ // A cycle re-entry resolves against truncated children (the cycle is broken by
396
+ // dropping the repeated package's subtree), so its empty/partial peer sets are
397
+ // not authoritative for the package as a whole. Recording that verdict — as
398
+ // pure or in peersCache — lets it short-circuit other occurrences that *can*
399
+ // see the full subtree, dropping their transitivePeerDependencies depending on
400
+ // traversal order and churning the lockfile (https://github.com/pnpm/pnpm/issues/5108).
401
+ const resolvedThroughCycle = ctx.parentDepPathsChain.includes(resolvedPackage.pkgIdWithPatchHash);
402
+ if (resolvedThroughCycle) {
403
+ // Leave both caches untouched so later occurrences re-resolve (or hit the
404
+ // authoritative entry of the same package) instead of reusing this partial one.
405
+ }
406
+ else if (isPure) {
395
407
  ctx.purePkgs.add(resolvedPackage.pkgIdWithPatchHash);
396
408
  }
397
409
  else {
@@ -48,7 +48,7 @@ export async function toResolveImporter(opts, project) {
48
48
  ...project.wantedDependencies.map(defaultUpdateDepth < 0
49
49
  ? updateLocalTarballs
50
50
  : (dep) => ({ ...dep, updateDepth: defaultUpdateDepth })),
51
- ...existingDeps.map(opts.noDependencySelectors && project.updateMatching != null
51
+ ...existingDeps.map(project.updateMatching != null
52
52
  ? updateLocalTarballs
53
53
  : (dep) => ({ ...dep, updateDepth: -1 })),
54
54
  ];
@@ -10,7 +10,7 @@ export async function updateProjectManifest(importer, opts) {
10
10
  return {
11
11
  alias: rdd.alias,
12
12
  peer: importer.peer,
13
- bareSpecifier: rdd.catalogLookup?.userSpecifiedBareSpecifier ?? rdd.normalizedBareSpecifier ?? wantedDep.bareSpecifier,
13
+ bareSpecifier: getBareSpecifierToSave(wantedDep, rdd, opts.preserveWorkspaceProtocol),
14
14
  resolvedVersion: rdd.version,
15
15
  pinnedVersion: importer.pinnedVersion,
16
16
  saveType: importer.targetDependenciesField,
@@ -31,4 +31,19 @@ export async function updateProjectManifest(importer, opts) {
31
31
  : undefined;
32
32
  return [hookedManifest, originalManifest];
33
33
  }
34
+ function getBareSpecifierToSave(wantedDep, resolvedDep, preserveWorkspaceProtocol) {
35
+ if (resolvedDep.catalogLookup != null) {
36
+ return resolvedDep.catalogLookup.userSpecifiedBareSpecifier;
37
+ }
38
+ if (preserveWorkspaceProtocol && isWorkspaceLocalPathSpecifier(wantedDep.bareSpecifier)) {
39
+ return wantedDep.bareSpecifier;
40
+ }
41
+ return resolvedDep.normalizedBareSpecifier ?? wantedDep.bareSpecifier;
42
+ }
43
+ function isWorkspaceLocalPathSpecifier(bareSpecifier) {
44
+ if (!bareSpecifier.startsWith('workspace:'))
45
+ return false;
46
+ const pref = bareSpecifier.slice('workspace:'.length);
47
+ return pref.startsWith('.') || pref.startsWith('/') || pref.startsWith('~/') || /^[A-Z]:/i.test(pref);
48
+ }
34
49
  //# sourceMappingURL=updateProjectManifest.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pnpm/installing.deps-resolver",
3
- "version": "1100.2.3",
3
+ "version": "1100.2.4",
4
4
  "description": "Resolves dependency graph of a package",
5
5
  "keywords": [
6
6
  "pnpm",
@@ -43,30 +43,30 @@
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.resolver": "1100.0.0",
47
- "@pnpm/catalogs.types": "1100.0.0",
48
- "@pnpm/config.version-policy": "1100.1.5",
49
46
  "@pnpm/constants": "1100.0.0",
50
47
  "@pnpm/core-loggers": "1100.2.1",
51
48
  "@pnpm/deps.graph-hasher": "1100.2.5",
52
- "@pnpm/deps.path": "1100.0.8",
53
49
  "@pnpm/error": "1100.0.0",
54
- "@pnpm/deps.peer-range": "1100.0.2",
55
50
  "@pnpm/fetching.pick-fetcher": "1100.0.12",
51
+ "@pnpm/deps.peer-range": "1100.0.2",
56
52
  "@pnpm/hooks.types": "1100.0.12",
53
+ "@pnpm/deps.path": "1100.0.8",
54
+ "@pnpm/config.version-policy": "1100.1.5",
57
55
  "@pnpm/lockfile.preferred-versions": "1100.0.16",
58
- "@pnpm/lockfile.pruner": "1100.0.11",
59
56
  "@pnpm/lockfile.types": "1100.0.11",
60
57
  "@pnpm/lockfile.utils": "1100.0.13",
61
58
  "@pnpm/patching.config": "1100.0.8",
62
- "@pnpm/patching.types": "1100.0.0",
63
- "@pnpm/pkg-manifest.utils": "1100.2.5",
59
+ "@pnpm/catalogs.types": "1100.0.0",
64
60
  "@pnpm/pkg-manifest.reader": "1100.0.8",
65
- "@pnpm/resolving.npm-resolver": "1102.0.0",
61
+ "@pnpm/pkg-manifest.utils": "1100.2.5",
62
+ "@pnpm/resolving.npm-resolver": "1102.0.1",
63
+ "@pnpm/resolving.resolver-base": "1100.4.2",
66
64
  "@pnpm/store.controller-types": "1100.1.5",
67
65
  "@pnpm/types": "1101.3.2",
68
66
  "@pnpm/workspace.spec-parser": "1100.0.0",
69
- "@pnpm/resolving.resolver-base": "1100.4.2"
67
+ "@pnpm/patching.types": "1100.0.0",
68
+ "@pnpm/catalogs.resolver": "1100.0.0",
69
+ "@pnpm/lockfile.pruner": "1100.0.11"
70
70
  },
71
71
  "peerDependencies": {
72
72
  "@pnpm/logger": "^1100.0.0"
@@ -77,7 +77,7 @@
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/installing.deps-resolver": "1100.2.3",
80
+ "@pnpm/installing.deps-resolver": "1100.2.4",
81
81
  "@pnpm/logger": "1100.0.0"
82
82
  },
83
83
  "engines": {