From abaf12e8cf82e7df342a35dc520fcfa64c9dd344 Mon Sep 17 00:00:00 2001 From: Zoltan Kochan Date: Sun, 21 Apr 2024 14:31:58 +0200 Subject: [PATCH] fix: resolving peer deps with prerelease versions (#7981) close #7977 --- .changeset/four-adults-burn.md | 6 ++++ .../resolve-dependencies/src/hoistPeers.ts | 29 +++++++++++++++++++ .../src/resolveDependencies.ts | 19 ++---------- .../test/hoistPeers.test.ts | 14 +++++++++ 4 files changed, 51 insertions(+), 17 deletions(-) create mode 100644 .changeset/four-adults-burn.md create mode 100644 pkg-manager/resolve-dependencies/src/hoistPeers.ts create mode 100644 pkg-manager/resolve-dependencies/test/hoistPeers.test.ts diff --git a/.changeset/four-adults-burn.md b/.changeset/four-adults-burn.md new file mode 100644 index 00000000000..a55c32ed62e --- /dev/null +++ b/.changeset/four-adults-burn.md @@ -0,0 +1,6 @@ +--- +"@pnpm/resolve-dependencies": patch +"pnpm": patch +--- + +Resolve peer dependencies correctly, when they have prerelease versions [#7977](https://github.com/pnpm/pnpm/issues/7977). diff --git a/pkg-manager/resolve-dependencies/src/hoistPeers.ts b/pkg-manager/resolve-dependencies/src/hoistPeers.ts new file mode 100644 index 00000000000..a1976ebf18c --- /dev/null +++ b/pkg-manager/resolve-dependencies/src/hoistPeers.ts @@ -0,0 +1,29 @@ +import { type PreferredVersions } from '@pnpm/resolver-base' +import semver from 'semver' + +export function hoistPeers ( + missingRequiredPeers: Array<[string, { range: string }]>, + opts: { + autoInstallPeers: boolean + allPreferredVersions?: PreferredVersions + } +): Record { + const dependencies: Record = {} + for (const [peerName, { range }] of missingRequiredPeers) { + if (opts.allPreferredVersions![peerName]) { + const versions: string[] = [] + const nonVersions: string[] = [] + for (const [spec, specType] of Object.entries(opts.allPreferredVersions![peerName])) { + if (specType === 'version') { + versions.push(spec) + } else { + nonVersions.push(spec) + } + } + dependencies[peerName] = [semver.maxSatisfying(versions, '*', { includePrerelease: true }), ...nonVersions].join(' || ') + } else if (opts.autoInstallPeers) { + dependencies[peerName] = range + } + } + return dependencies +} diff --git a/pkg-manager/resolve-dependencies/src/resolveDependencies.ts b/pkg-manager/resolve-dependencies/src/resolveDependencies.ts index 86c5ed97041..ed2e409c69f 100644 --- a/pkg-manager/resolve-dependencies/src/resolveDependencies.ts +++ b/pkg-manager/resolve-dependencies/src/resolveDependencies.ts @@ -57,6 +57,7 @@ import { nodeIdContains, splitNodeId, } from './nodeIdUtils' +import { hoistPeers } from './hoistPeers' import { wantedDepIsLocallyAvailable } from './wantedDepIsLocallyAvailable' import { replaceVersionInPref } from './replaceVersionInPref' @@ -329,23 +330,7 @@ export async function resolveRootDependencies ( ) ) if (!missingRequiredPeers.length && !missingOptionalPeerNames.length) break - const dependencies: Record = {} - for (const [peerName, { range }] of missingRequiredPeers) { - if (ctx.allPreferredVersions![peerName]) { - const versions: string[] = [] - const nonVersions: string[] = [] - for (const [spec, specType] of Object.entries(ctx.allPreferredVersions![peerName])) { - if (specType === 'version') { - versions.push(spec) - } else { - nonVersions.push(spec) - } - } - dependencies[peerName] = [semver.maxSatisfying(versions, '*'), ...nonVersions].join(' || ') - } else if (ctx.autoInstallPeers) { - dependencies[peerName] = range - } - } + const dependencies = hoistPeers(missingRequiredPeers, ctx) const nextMissingOptionalPeers: string[] = [] const optionalDependencies: Record = {} for (const missingOptionalPeerName of missingOptionalPeerNames) { diff --git a/pkg-manager/resolve-dependencies/test/hoistPeers.test.ts b/pkg-manager/resolve-dependencies/test/hoistPeers.test.ts new file mode 100644 index 00000000000..119d86c346b --- /dev/null +++ b/pkg-manager/resolve-dependencies/test/hoistPeers.test.ts @@ -0,0 +1,14 @@ +import { hoistPeers } from '../lib/hoistPeers' + +test('hoistPeers picks an already available prerelease version', () => { + expect(hoistPeers([['foo', { range: '*' }]], { + autoInstallPeers: false, + allPreferredVersions: { + foo: { + '1.0.0-beta.0': 'version', + }, + }, + })).toStrictEqual({ + foo: '1.0.0-beta.0', + }) +})