fix: upgrade peer dependencies with pnpm upgrade --latest (#9928)

The `--latest` flag triggers the `installSome` code path, which built
`currentBareSpecifiers` via `getAllDependenciesFromManifest()` — a
function that excluded peer dependencies. The non-`--latest` path uses
`getWantedDependencies()`, which honors `autoInstallPeers` and includes
them. Pass `autoInstallPeers` through `getAllDependenciesFromManifest`
so both paths agree.

Closes #9900

Co-authored-by: Zoltan Kochan <z@kochan.io>
This commit is contained in:
Minha Kang
2026-04-24 12:27:53 +09:00
committed by Zoltan Kochan
parent 55e35d0491
commit 005eeb776b
5 changed files with 48 additions and 3 deletions

View File

@@ -0,0 +1,6 @@
---
"@pnpm/installing.deps-installer": patch
"pnpm": patch
---
Fix peer dependencies not being upgraded with `pnpm upgrade --latest` [#9900](https://github.com/pnpm/pnpm/issues/9900).

View File

@@ -681,7 +681,9 @@ export async function mutateModules (
>
async function installSome (project: InstallSomeProject) {
const currentBareSpecifiers = opts.ignoreCurrentSpecifiers ? {} : getAllDependenciesFromManifest(project.manifest)
const currentBareSpecifiers = opts.ignoreCurrentSpecifiers
? {}
: getAllDependenciesFromManifest(project.manifest, { autoInstallPeers: opts.autoInstallPeers })
const optionalDependencies = project.targetDependenciesField ? {} : project.manifest.optionalDependencies ?? {}
const devDependencies = project.targetDependenciesField ? {} : project.manifest.devDependencies ?? {}
if (preferredSpecs == null) {

View File

@@ -263,3 +263,36 @@ test('peer dependency is not added to prod deps on update', async () => {
},
})
})
// Covers https://github.com/pnpm/pnpm/issues/9900
test('peer dependencies are updated with pnpm upgrade --latest when autoInstallPeers is true', async () => {
await addDistTag({ package: '@pnpm.e2e/foo', version: '1.0.0', distTag: 'latest' })
const project = prepareEmpty()
const manifest = {
name: 'test-pkg',
version: '1.0.0',
peerDependencies: {
'@pnpm.e2e/foo': '^1.0.0',
},
}
await install(manifest, testDefaults({ autoInstallPeers: true }))
let lockfile = project.readLockfile()
expect(lockfile.importers?.['.']?.dependencies?.['@pnpm.e2e/foo'].version).toBe('1.0.0')
await addDistTag({ package: '@pnpm.e2e/foo', version: '1.3.0', distTag: 'latest' })
await addDependenciesToPackage(manifest, ['@pnpm.e2e/foo'], testDefaults({
allowNew: false,
autoInstallPeers: true,
update: true,
updateToLatest: true,
}))
lockfile = project.readLockfile()
expect(lockfile.importers?.['.']?.dependencies?.['@pnpm.e2e/foo'].version).toBe('1.3.0')
})

View File

@@ -1,11 +1,13 @@
import type { Dependencies, DependenciesField, ProjectManifest } from '@pnpm/types'
export function getAllDependenciesFromManifest (
pkg: Pick<ProjectManifest, DependenciesField>
pkg: Pick<ProjectManifest, DependenciesField | 'peerDependencies'>,
opts?: { autoInstallPeers?: boolean }
): Dependencies {
return {
...pkg.devDependencies,
...pkg.dependencies,
...pkg.optionalDependencies,
...(opts?.autoInstallPeers ? pkg.peerDependencies : {}),
} as Dependencies
}

View File

@@ -25,11 +25,13 @@ export function filterDependenciesByType (
}
export function getAllDependenciesFromManifest (
manifest: Pick<ProjectManifest, 'devDependencies' | 'dependencies' | 'optionalDependencies'>
manifest: Pick<ProjectManifest, 'devDependencies' | 'dependencies' | 'optionalDependencies' | 'peerDependencies'>,
opts?: { autoInstallPeers?: boolean }
): Dependencies {
return {
...manifest.devDependencies,
...manifest.dependencies,
...manifest.optionalDependencies,
...(opts?.autoInstallPeers ? manifest.peerDependencies : {}),
}
}