diff --git a/.changeset/slow-panthers-refuse.md b/.changeset/slow-panthers-refuse.md new file mode 100644 index 00000000000..7c28bf041df --- /dev/null +++ b/.changeset/slow-panthers-refuse.md @@ -0,0 +1,6 @@ +--- +"@pnpm/resolve-dependencies": patch +"pnpm": patch +--- + +Don't override the root dependency when auto installing peer dependencies [#5412](https://github.com/pnpm/pnpm/issues/5412). diff --git a/packages/core/test/install/autoInstallPeers.ts b/packages/core/test/install/autoInstallPeers.ts index 855f71b2ff2..158655696ce 100644 --- a/packages/core/test/install/autoInstallPeers.ts +++ b/packages/core/test/install/autoInstallPeers.ts @@ -1,6 +1,6 @@ import path from 'path' import assertProject from '@pnpm/assert-project' -import { addDependenciesToPackage, install, mutateModules } from '@pnpm/core' +import { addDependenciesToPackage, install, mutateModules, PackageManifest } from '@pnpm/core' import { prepareEmpty, preparePackages } from '@pnpm/prepare' import { addDistTag, REGISTRY_MOCK_PORT } from '@pnpm/registry-mock' import rimraf from '@zkochan/rimraf' @@ -347,3 +347,48 @@ test('installation on a package with many complex circular dependencies does not prepareEmpty() await addDependenciesToPackage({}, ['webpack@4.46.0'], await testDefaults({ autoInstallPeers: true })) }) + +test('do not override the direct dependency with an auto installed peer dependency', async () => { + const includedDeps = new Set([ + '@angular-devkit/build-angular', + '@angular/platform-browser-dynamic', + 'inquirer', + 'rxjs', + '@angular/common', + 'rxjs', + ]) + const project = prepareEmpty() + await install({ + dependencies: { + rxjs: '6.6.7', + }, + devDependencies: { + 'jest-preset-angular': '12.0.1', + }, + }, await testDefaults({ + autoInstallPeers: true, + hooks: { + // This hook may be removed and the test will still be valid. + // The only reason the hook was added to remove the packages that aren't needed for the tests and make the test faster. + readPackage: [ + (pkg: PackageManifest) => { + for (const depType of ['dependencies', 'optionalDependencies', 'peerDependencies', 'peerDependenciesMeta']) { + if (pkg[depType]) { + for (const depName of Object.keys(pkg[depType])) { + if (!includedDeps.has(depName)) { + delete pkg[depType][depName] + } + } + } + } + if (pkg.name === '@angular-devkit/build-angular' && pkg.dependencies) { + delete pkg.dependencies.rxjs + } + return pkg + }, + ], + }, + })) + const lockfile = await project.readLockfile() + expect(lockfile.dependencies.rxjs).toStrictEqual('6.6.7') +}) diff --git a/packages/resolve-dependencies/src/resolveDependencies.ts b/packages/resolve-dependencies/src/resolveDependencies.ts index 3b82b5719b9..96aa421d466 100644 --- a/packages/resolve-dependencies/src/resolveDependencies.ts +++ b/packages/resolve-dependencies/src/resolveDependencies.ts @@ -302,7 +302,7 @@ export async function resolveRootDependencies ( }) importerResolutionResult = { pkgAddresses: resolveDependenciesResult.pkgAddresses, - ...(await resolveDependenciesResult.resolvingPeers), + ...filterMissingPeers(await resolveDependenciesResult.resolvingPeers, parentPkgAliases), } pkgAddresses.push(...importerResolutionResult.pkgAddresses) } @@ -680,7 +680,7 @@ async function resolveDependenciesOfDependency ( postponedPeersResolution: resolveDependencyResult.missingPeersOfChildren != null ? async (parentPkgAliases) => { const missingPeers = await resolveDependencyResult.missingPeersOfChildren!.get() - return filterMissingPeers(missingPeers, {}, parentPkgAliases) + return filterMissingPeers({ missingPeers, resolvedPeers: {} }, parentPkgAliases) } : undefined, } @@ -698,12 +698,15 @@ async function resolveDependenciesOfDependency ( postponedResolution: async (postponedResolutionOpts) => { const { missingPeers, resolvedPeers } = await postponedResolution(postponedResolutionOpts) resolveDependencyResult.missingPeersOfChildren!.resolve(missingPeers) - return filterMissingPeers(missingPeers, resolvedPeers, postponedResolutionOpts.parentPkgAliases) + return filterMissingPeers({ missingPeers, resolvedPeers }, postponedResolutionOpts.parentPkgAliases) }, } } -function filterMissingPeers (missingPeers: MissingPeers, resolvedPeers: ResolvedPeers, parentPkgAliases: ParentPkgAliases): PeersResolutionResult { +function filterMissingPeers ( + { missingPeers, resolvedPeers }: PeersResolutionResult, + parentPkgAliases: ParentPkgAliases +): PeersResolutionResult { const newMissing = {} as MissingPeers for (const [peerName, peerVersion] of Object.entries(missingPeers)) { if (parentPkgAliases[peerName]) {