fix: ignore first YAML document when reading two-document lockfiles

v11 lockfiles may contain two YAML documents separated by `---`. The
first document is the env lockfile metadata; the second is the actual
lockfile data. The reader now strips the first document before parsing.
This commit is contained in:
Zoltan Kochan
2026-03-24 14:11:58 +01:00
parent 28204a4c9a
commit 883d9f8ab1
4 changed files with 52 additions and 0 deletions

View File

@@ -0,0 +1,6 @@
---
"@pnpm/lockfile.fs": patch
"pnpm": patch
---
When a pnpm-lock.yaml contains two documents, ignore the first one. pnpm v11 will write two lockfile documents into pnpm-lock.yaml in order to store pnpm version integrities and config dependency resolutions.

View File

@@ -85,6 +85,9 @@ async function _read (
hadConflicts: false,
}
}
// Skip the first YAML document if the file contains two documents (v11 format).
// The first document is the env lockfile; the second is the actual lockfile.
lockfileRawContent = extractMainDocument(lockfileRawContent)
let lockfile: LockfileObject
let hadConflicts!: boolean
try {
@@ -237,3 +240,10 @@ async function _readGitBranchLockfiles (
return Promise.all(files.map((file) => _read(path.join(lockfileDir, file), prefix, opts)))
}
function extractMainDocument (content: string): string {
if (!content.startsWith('---\n')) return content
const sep = content.indexOf('\n---\n', '---\n'.length)
if (sep === -1) return content
return content.slice(sep + '\n---\n'.length)
}

14
lockfile/fs/test/fixtures/8/pnpm-lock.yaml generated vendored Normal file
View File

@@ -0,0 +1,14 @@
---
lockfileVersion: '9.0'
---
lockfileVersion: '9.0'
importers:
.:
dependencies:
foo:
version: '1.0.0'
specifier: '1'
dependenciesMeta:
foo:
injected: true

View File

@@ -278,6 +278,28 @@ test('readWantedLockfile() when useGitBranchLockfile and mergeGitBranchLockfiles
})
})
test('readWantedLockfile() ignores the first YAML document in a two-document lockfile', async () => {
const lockfile = await readWantedLockfile(path.join('fixtures', '8'), {
ignoreIncompatible: false,
})
expect(lockfile?.lockfileVersion).toEqual('9.0')
expect(lockfile?.importers).toStrictEqual({
'.': {
dependencies: {
foo: '1.0.0',
},
devDependencies: undefined,
optionalDependencies: undefined,
specifiers: {
foo: '1',
},
dependenciesMeta: {
foo: { injected: true },
},
},
})
})
test('readWantedLockfile() with inlineSpecifiersFormat', async () => {
const wantedLockfile = {
importers: {