From a1e834bfccb8d7f1a26c33ac5d9331b96f7cef51 Mon Sep 17 00:00:00 2001 From: Zoltan Kochan Date: Tue, 20 Sep 2022 03:23:40 +0300 Subject: [PATCH] fix: deduplicate peer deps (#5377) close #5373 --- .changeset/two-cobras-melt.md | 6 +++ .../core/test/install/autoInstallPeers.ts | 17 ++++++++ .../src/resolveDependencies.ts | 42 +++++++++++-------- 3 files changed, 47 insertions(+), 18 deletions(-) create mode 100644 .changeset/two-cobras-melt.md diff --git a/.changeset/two-cobras-melt.md b/.changeset/two-cobras-melt.md new file mode 100644 index 0000000000..e91c45e49a --- /dev/null +++ b/.changeset/two-cobras-melt.md @@ -0,0 +1,6 @@ +--- +"@pnpm/resolve-dependencies": patch +"pnpm": patch +--- + +Deduplicate peer dependencies when automatically installing them [#5373](https://github.com/pnpm/pnpm/issues/5373). diff --git a/packages/core/test/install/autoInstallPeers.ts b/packages/core/test/install/autoInstallPeers.ts index 947d591533..b92df5be05 100644 --- a/packages/core/test/install/autoInstallPeers.ts +++ b/packages/core/test/install/autoInstallPeers.ts @@ -148,6 +148,23 @@ test('don\'t install the same missing peer dependency twice', async () => { ].sort()) }) +// Covers https://github.com/pnpm/pnpm/issues/5373 +test('prefer the peer dependency version already used in the root', async () => { + await addDistTag({ package: '@pnpm/y', version: '2.0.0', distTag: 'latest' }) + const project = prepareEmpty() + await install({ + peerDependencies: { + '@pnpm.e2e/has-y-peer': '1.0.0', + '@pnpm/y': '^1.0.0', + }, + }, await testDefaults({ autoInstallPeers: true })) + const lockfile = await project.readLockfile() + expect(Object.keys(lockfile.packages).sort()).toStrictEqual([ + '/@pnpm/y/1.0.0', + '/@pnpm.e2e/has-y-peer/1.0.0_@pnpm+y@1.0.0', + ].sort()) +}) + test('automatically install root peer dependencies', async () => { const project = prepareEmpty() diff --git a/packages/resolve-dependencies/src/resolveDependencies.ts b/packages/resolve-dependencies/src/resolveDependencies.ts index f610c1eb69..d61faa091e 100644 --- a/packages/resolve-dependencies/src/resolveDependencies.ts +++ b/packages/resolve-dependencies/src/resolveDependencies.ts @@ -387,10 +387,10 @@ async function resolveDependenciesOfImporters ( } const pkgAddressesByImportersWithoutPeers = await Promise.all(zipWith(async (importer, { pkgAddresses, postponedResolutionsQueue, postponedPeersResolutionQueue }) => { const newPreferredVersions = { ...importer.preferredVersions } - const newParentPkgAliases = { ...importer.parentPkgAliases } + const currentParentPkgAliases = {} for (const pkgAddress of pkgAddresses) { - if (newParentPkgAliases[pkgAddress.alias] !== true) { - newParentPkgAliases[pkgAddress.alias] = pkgAddress + if (currentParentPkgAliases[pkgAddress.alias] !== true) { + currentParentPkgAliases[pkgAddress.alias] = pkgAddress } if (pkgAddress.updated) { ctx.updatedSet.add(pkgAddress.alias) @@ -402,6 +402,7 @@ async function resolveDependenciesOfImporters ( } newPreferredVersions[resolvedPackage.name][resolvedPackage.version] = 'version' } + const newParentPkgAliases = { ...importer.parentPkgAliases, ...currentParentPkgAliases } const postponedResolutionOpts = { preferredVersions: newPreferredVersions, parentPkgAliases: newParentPkgAliases, @@ -420,9 +421,10 @@ async function resolveDependenciesOfImporters ( const postponedPeersResolution = await Promise.all( postponedPeersResolutionQueue.map((postponedMissingPeers) => postponedMissingPeers(postponedResolutionOpts.parentPkgAliases)) ) + const resolvedPeers = [...childrenResults, ...postponedPeersResolution].reduce((acc, { resolvedPeers }) => Object.assign(acc, resolvedPeers), {}) const allMissingPeers = mergePkgsDeps( [ - ...pkgAddresses, + ...filterMissingPeersFromPkgAddresses(pkgAddresses, currentParentPkgAliases, resolvedPeers), ...childrenResults, ...postponedPeersResolution, ].map(({ missingPeers }) => missingPeers).filter(Boolean) @@ -430,7 +432,7 @@ async function resolveDependenciesOfImporters ( return { missingPeers: allMissingPeers, pkgAddresses, - resolvedPeers: [...childrenResults, ...postponedPeersResolution].reduce((acc, { resolvedPeers }) => Object.assign(acc, resolvedPeers), {}), + resolvedPeers, } }, importers, resolveResults)) return { @@ -440,6 +442,22 @@ async function resolveDependenciesOfImporters ( } } +function filterMissingPeersFromPkgAddresses (pkgAddresses: PkgAddress[], currentParentPkgAliases: ParentPkgAliases, resolvedPeers: ResolvedPeers) { + return pkgAddresses.map((pkgAddress) => ({ + ...pkgAddress, + missingPeers: fromPairs( + Object.entries(pkgAddress.missingPeers || {}) + .filter(([peerName]) => { + if (!currentParentPkgAliases[peerName]) return true + if (currentParentPkgAliases[peerName] !== true) { + resolvedPeers[peerName] = currentParentPkgAliases[peerName] as PkgAddress + } + return false + }) + ), + })) +} + function getPublishedByDate (pkgAddresses: PkgAddress[], timeFromLockfile: Record = {}): { publishedBy: Date, newTime: Record } { const newTime: Record = {} for (const pkgAddress of pkgAddresses) { @@ -559,19 +577,7 @@ async function startResolvingPeers ( const resolvedPeers = [...childrenResults, ...results].reduce((acc, { resolvedPeers }) => Object.assign(acc, resolvedPeers), {}) const allMissingPeers = mergePkgsDeps( [ - ...pkgAddresses.map((pkgAddress) => ({ - ...pkgAddress, - missingPeers: fromPairs( - Object.entries(pkgAddress.missingPeers || {}) - .filter(([peerName]) => { - if (!currentParentPkgAliases[peerName]) return true - if (currentParentPkgAliases[peerName] !== true) { - resolvedPeers[peerName] = currentParentPkgAliases[peerName] - } - return false - }) - ), - })), + ...filterMissingPeersFromPkgAddresses(pkgAddresses, currentParentPkgAliases, resolvedPeers), ...childrenResults, ...results, ].map(({ missingPeers }) => missingPeers).filter(Boolean)