fix(lockfile): handle non-semver versions in lockfile merger without crashing (#11102)

* fix(lockfile): handle non-semver versions in lockfile merger without crashing

* Apply suggestion from @Copilot

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Zoltan Kochan <z@kochan.io>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Vamsik
2026-03-26 19:51:23 +05:30
committed by GitHub
parent 0e8042e6dc
commit 659e0ea0cc
3 changed files with 47 additions and 2 deletions

View File

@@ -0,0 +1,6 @@
---
"@pnpm/lockfile.merger": patch
"pnpm": patch
---
Fixed a crash in the lockfile merger when merging non-semver version strings (e.g. `link:`, `file:`, git URLs).

View File

@@ -99,8 +99,13 @@ function mergeVersions (ourValue: string, theirValue: string): string {
if (!ourValue) return theirValue
const [ourVersion] = ourValue.split('(')
const [theirVersion] = theirValue.split('(')
if (semver.gt(ourVersion, theirVersion)) {
return ourValue
const validOurVersion = semver.valid(ourVersion)
const validTheirVersion = semver.valid(theirVersion)
if (validOurVersion && validTheirVersion) {
return semver.gt(ourVersion, theirVersion) ? ourValue : theirValue
}
// Non-semver versions (link:, file:, git URLs, etc.) — prefer theirs
return theirValue
}

View File

@@ -353,3 +353,37 @@ test('prefers our lockfile resolutions when it has newer packages #2', () => {
},
})
})
test('does not crash when merging non-semver versions (link: protocol)', () => {
const base: LockfileObject = {
importers: {
['.' as ProjectId]: {
dependencies: { a: '1.0.0' },
specifiers: {},
},
},
lockfileVersion: '5.2',
packages: {
['/a@1.0.0' as DepPath]: {
dependencies: { linked: 'link:../pkg1' },
resolution: { integrity: '' },
},
},
}
const mergedLockfile = mergeLockfileChanges(
base,
{
...base,
packages: {
['/a@1.0.0' as DepPath]: {
dependencies: { linked: 'link:../pkg2' },
resolution: { integrity: '' },
},
},
}
)
// Should not crash and should pick theirs (the incoming change)
expect(mergedLockfile.packages?.['/a@1.0.0' as DepPath].dependencies?.linked).toBe('link:../pkg2')
})