fix: --lockfile-only flag causes pnpm update to ignore matchers (#9588)

* test: add regression test for `pnpm update --lockfile-only`

* fix: --lockfile-only flag causes pnpm update to ignore matchers
This commit is contained in:
Brandon Cheng
2025-06-01 17:39:54 -04:00
committed by Zoltan Kochan
parent 3387aa95e0
commit 509948d3ae
5 changed files with 68 additions and 1 deletions

View File

@@ -0,0 +1,9 @@
---
"@pnpm/resolve-dependencies": patch
"@pnpm/package-requester": patch
"@pnpm/store-controller-types": patch
"@pnpm/core": patch
pnpm: patch
---
Fix a regression (in v10.9.0) causing the `--lockfile-only` flag on `pnpm update` to produce a different `pnpm-lock.yaml` than an update without the flag.

View File

@@ -211,6 +211,42 @@ test('update only the specified package', async () => {
})
})
test.each([false, true])('update only the specified package with --lockfile-only=%p', async (lockfileOnly) => {
const project = prepareEmpty()
await Promise.all([
addDistTag({ package: '@pnpm.e2e/bar', version: '100.0.0', distTag: 'latest' }),
addDistTag({ package: '@pnpm.e2e/foo', version: '100.0.0', distTag: 'latest' }),
])
const { updatedManifest: manifest } = await addDependenciesToPackage({}, [
'@pnpm.e2e/bar',
'@pnpm.e2e/foo',
// Ensure aliases also stay on the same version.
'bar-alias@npm:@pnpm.e2e/bar',
], testDefaults())
await Promise.all([
addDistTag({ package: '@pnpm.e2e/bar', version: '100.1.0', distTag: 'latest' }),
addDistTag({ package: '@pnpm.e2e/foo', version: '100.1.0', distTag: 'latest' }),
])
await install(manifest, testDefaults({
depth: Infinity,
update: true,
updateMatching: (pkgName: string) => pkgName === '@pnpm.e2e/foo',
// This test specifically tests this flag.
lockfileOnly,
}))
const lockfile = project.readLockfile()
expect(lockfile.snapshots).toStrictEqual({
'@pnpm.e2e/bar@100.0.0': expect.anything(),
'@pnpm.e2e/foo@100.1.0': expect.anything(),
})
})
test('peer dependency is not added to prod deps on update', async () => {
prepareEmpty()
const { updatedManifest: manifest } = await install({

View File

@@ -22,6 +22,7 @@ import { packageIsInstallable } from '@pnpm/package-is-installable'
import { readPackageJson } from '@pnpm/read-package-json'
import {
type DirectoryResolution,
type PreferredVersions,
type Resolution,
type ResolveFunction,
type ResolveResult,
@@ -178,13 +179,30 @@ async function resolveAndFetch (
//
// The resolution step is never skipped for local dependencies.
if (!skipResolution || options.skipFetch === true || Boolean(pkgId?.startsWith('file:')) || wantedDependency.optional === true) {
// When skipResolution is set but a resolution is still performed due to
// options.skipFetch, it's necessary to make sure the resolution doesn't
// accidentally return a newer version of the package. When skipFetch is
// set, the resolved package shouldn't be different. This is done by
// overriding the preferredVersions object to only contain the current
// package's version.
//
// A naive approach would be to change the bare specifier to be the exact
// version of the current pkg if the bare specifier is a range, but this
// would cause the version returned for calcSpecifier to be different.
const preferredVersions: PreferredVersions = (skipResolution && options.currentPkg?.name != null && options.currentPkg?.version != null)
? {
...options.preferredVersions,
[options.currentPkg.name]: { [options.currentPkg.version]: 'version' },
}
: options.preferredVersions
const resolveResult = await ctx.requestsQueue.add<ResolveResult>(async () => ctx.resolve(wantedDependency, {
alwaysTryWorkspacePackages: options.alwaysTryWorkspacePackages,
defaultTag: options.defaultTag,
publishedBy: options.publishedBy,
pickLowestVersion: options.pickLowestVersion,
lockfileDir: options.lockfileDir,
preferredVersions: options.preferredVersions,
preferredVersions,
preferWorkspacePackages: options.preferWorkspacePackages,
projectDir: options.projectDir,
workspacePackages: options.workspacePackages,

View File

@@ -1267,7 +1267,9 @@ async function resolveDependency (
currentPkg: currentPkg
? {
id: currentPkg.pkgId,
name: currentPkg.name,
resolution: currentPkg.resolution,
version: currentPkg.version,
}
: undefined,
expectedPkg: currentPkg,

View File

@@ -110,7 +110,9 @@ export interface RequestPackageOptions {
alwaysTryWorkspacePackages?: boolean
currentPkg?: {
id?: PkgResolutionId
name?: string
resolution?: Resolution
version?: string
}
/**
* Expected package is the package name and version that are found in the lockfile.