fix: validate that the version in the lockfile satisfies the range (#9832)

This commit is contained in:
Zoltan Kochan
2025-08-07 01:39:01 +02:00
committed by GitHub
parent af2b391278
commit eac7bab229
4 changed files with 65 additions and 0 deletions

View File

@@ -0,0 +1,5 @@
---
"@pnpm/lockfile.verification": patch
---
satisfiesPackageManifest also checks if the version in the importer satisfied the range in the `package.json`.

View File

@@ -1,3 +1,4 @@
import * as dp from '@pnpm/dependency-path'
import { type ProjectSnapshot } from '@pnpm/lockfile.types'
import {
DEPENDENCIES_FIELDS,
@@ -6,6 +7,7 @@ import {
import equals from 'ramda/src/equals'
import pickBy from 'ramda/src/pickBy'
import omit from 'ramda/src/omit'
import semver from 'semver'
import { type Diff, diffFlatRecords, isEqual } from './diffFlatRecords'
export function satisfiesPackageManifest (
@@ -95,6 +97,14 @@ export function satisfiesPackageManifest (
detailedReason: `importer ${depField}.${depName} specifier ${importer.specifiers[depName]} don't match package manifest specifier (${pkgDeps[depName]})`,
}
}
if (importer?.specifiers[depName] == null || !semver.validRange(importer?.specifiers[depName])) continue
const version = dp.removeSuffix(importerDeps[depName])
if (semver.valid(version) && !semver.satisfies(version, importer.specifiers[depName])) {
return {
satisfies: false,
detailedReason: `The importer resolution is broken at dependency "${depName}": version "${version}" doesn't satisfy range "${importer.specifiers[depName]}"`,
}
}
}
}
return { satisfies: true }

View File

@@ -866,3 +866,37 @@ test('allProjectsAreUpToDate(): returns true if one of the importers is not pres
lockfileDir: '',
})).toBeTruthy()
})
test('allProjectsAreUpToDate(): returns false if the lockfile is broken, the resolved versions do not satisfy the ranges', async () => {
expect(await allProjectsAreUpToDate([
{
id: '.' as ProjectId,
manifest: {
dependencies: {
'@apollo/client': '3.3.7',
},
},
rootDir: '.' as ProjectRootDir,
},
], {
autoInstallPeers: false,
catalogs: {},
excludeLinksFromLockfile: false,
linkWorkspacePackages: true,
wantedLockfile: {
importers: {
['.' as ProjectId]: {
dependencies: {
'@apollo/client': '3.13.8(@types/react@18.3.23)(graphql@15.8.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(subscriptions-transport-ws@0.11.0(graphql@15.8.0))',
},
specifiers: {
'@apollo/client': '3.3.7',
},
},
},
lockfileVersion: LOCKFILE_VERSION,
},
workspacePackages,
lockfileDir: '',
})).toBeFalsy()
})

View File

@@ -378,4 +378,20 @@ test('satisfiesPackageManifest()', () => {
},
}
)).toStrictEqual({ satisfies: true })
expect(satisfiesPackageManifest({}, {
dependencies: {
'@apollo/client': '3.13.8(@types/react@18.3.23)(graphql@15.8.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(subscriptions-transport-ws@0.11.0(graphql@15.8.0))',
},
specifiers: {
'@apollo/client': '3.3.7',
},
}, {
dependencies: {
'@apollo/client': '3.3.7',
},
})).toStrictEqual({
satisfies: false,
detailedReason: 'The importer resolution is broken at dependency "@apollo/client": version "3.13.8" doesn\'t satisfy range "3.3.7"',
})
})