From 659e0ea0cc30a92a7546337bdd1f519898db36c3 Mon Sep 17 00:00:00 2001 From: Vamsik Date: Thu, 26 Mar 2026 19:51:23 +0530 Subject: [PATCH] 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 Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .changeset/fix-lockfile-merger-non-semver.md | 6 ++++ lockfile/merger/src/index.ts | 9 ++++-- lockfile/merger/test/index.ts | 34 ++++++++++++++++++++ 3 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 .changeset/fix-lockfile-merger-non-semver.md diff --git a/.changeset/fix-lockfile-merger-non-semver.md b/.changeset/fix-lockfile-merger-non-semver.md new file mode 100644 index 0000000000..8d7b3d510b --- /dev/null +++ b/.changeset/fix-lockfile-merger-non-semver.md @@ -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). diff --git a/lockfile/merger/src/index.ts b/lockfile/merger/src/index.ts index 7eff82c9c2..1db82f5b6c 100644 --- a/lockfile/merger/src/index.ts +++ b/lockfile/merger/src/index.ts @@ -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 } diff --git a/lockfile/merger/test/index.ts b/lockfile/merger/test/index.ts index ca99b99023..531dd1a675 100644 --- a/lockfile/merger/test/index.ts +++ b/lockfile/merger/test/index.ts @@ -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') +})