mirror of
https://github.com/pnpm/pnpm.git
synced 2026-06-01 12:41:16 -04:00
fix(lockfile): support CRLF line endings in env lockfiles (#11654)
* fix(lockfile): support CRLF line endings in env lockfiles Normalize CRLF line endings before parsing YAML document separators in streamed env lockfile reads. Previously the parser assumed LF-only separators (`\n---\n`), which caused pnpm to report ERR_PNPM_BROKEN_LOCKFILE or outdated lockfile errors when configDependencies lockfiles were checked out with CRLF line endings on Windows. Fixes #11612 * test(lockfile): cover CRLF normalization and clean up yamlDocuments Add CRLF-handling tests for streamReadFirstYamlDocument (CRLF and BOM+CRLF) and extractMainDocument (CRLF in combined file and CRLF in content without separator). Hoist the duplicated CRLF replace in Phase 1 out of the if/else, drop two stray semicolons and a couple of blank lines. * chore: include pnpm in changeset --------- Co-authored-by: Zoltan Kochan <z@kochan.io>
This commit is contained in:
committed by
GitHub
parent
b6e2c8c5ac
commit
6e93f350a9
6
.changeset/floppy-parents-teach.md
Normal file
6
.changeset/floppy-parents-teach.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
"@pnpm/lockfile.fs": patch
|
||||
"pnpm": patch
|
||||
---
|
||||
|
||||
Fix lockfile parsing failures when `pnpm-lock.yaml` contains CRLF line endings and multiple YAML documents [#11612](https://github.com/pnpm/pnpm/issues/11612).
|
||||
@@ -27,6 +27,8 @@ export async function streamReadFirstYamlDocument (filePath: string): Promise<st
|
||||
} else {
|
||||
buffer += chunk.value
|
||||
}
|
||||
// Normalize CRLF (Windows) to LF so document separator detection works.
|
||||
buffer = buffer.replace(/\r\n/g, '\n')
|
||||
if (buffer.length >= YAML_DOCUMENT_START.length) break
|
||||
}
|
||||
if (!buffer.startsWith(YAML_DOCUMENT_START)) {
|
||||
@@ -42,7 +44,8 @@ export async function streamReadFirstYamlDocument (filePath: string): Promise<st
|
||||
}
|
||||
const chunk = await chunks.next() // eslint-disable-line no-await-in-loop
|
||||
if (chunk.done) break
|
||||
buffer += chunk.value
|
||||
// Normalize CRLF (Windows) to LF so the separator search matches on Windows-checked-out files.
|
||||
buffer = (buffer + chunk.value).replace(/\r\n/g, '\n')
|
||||
}
|
||||
} catch (err: unknown) {
|
||||
if (util.types.isNativeError(err) && 'code' in err && err.code === 'ENOENT') {
|
||||
@@ -61,6 +64,7 @@ export async function streamReadFirstYamlDocument (filePath: string): Promise<st
|
||||
* Otherwise returns the entire content (no env document present).
|
||||
*/
|
||||
export function extractMainDocument (content: string): string {
|
||||
content = content.replace(/\r\n/g, '\n')
|
||||
if (!content.startsWith(YAML_DOCUMENT_START)) return content
|
||||
const sep = content.indexOf(YAML_DOCUMENT_SEPARATOR, YAML_DOCUMENT_START.length)
|
||||
if (sep === -1) return ''
|
||||
|
||||
@@ -64,6 +64,25 @@ describe('streamReadFirstYamlDocument', () => {
|
||||
const result = await streamReadFirstYamlDocument(filePath)
|
||||
expect(result).toBe(envContent)
|
||||
})
|
||||
|
||||
test('handles CRLF line endings (Windows)', async () => {
|
||||
const dir = temporaryDirectory()
|
||||
const filePath = path.join(dir, 'test.yaml')
|
||||
const envContent = 'lockfileVersion: env-1.0\nimporters:\n .:\n foo: bar'
|
||||
const content = `---\n${envContent}\n---\nlockfileVersion: 9.0\n`.replace(/\n/g, '\r\n')
|
||||
fs.writeFileSync(filePath, content)
|
||||
const result = await streamReadFirstYamlDocument(filePath)
|
||||
expect(result).toBe(envContent)
|
||||
})
|
||||
|
||||
test('handles BOM with CRLF line endings', async () => {
|
||||
const dir = temporaryDirectory()
|
||||
const filePath = path.join(dir, 'test.yaml')
|
||||
const content = '---\r\nfoo: bar\r\n---\r\nlockfileVersion: 9.0\r\n'
|
||||
fs.writeFileSync(filePath, content)
|
||||
const result = await streamReadFirstYamlDocument(filePath)
|
||||
expect(result).toBe('foo: bar')
|
||||
})
|
||||
})
|
||||
|
||||
describe('extractMainDocument', () => {
|
||||
@@ -82,4 +101,15 @@ describe('extractMainDocument', () => {
|
||||
const combined = `---\nfoo: bar\n---\n${mainContent}`
|
||||
expect(extractMainDocument(combined)).toBe(mainContent)
|
||||
})
|
||||
|
||||
test('handles CRLF line endings in combined file', () => {
|
||||
const mainContent = 'lockfileVersion: 9.0\npackages: {}\n'
|
||||
const combined = `---\nfoo: bar\n---\n${mainContent}`.replace(/\n/g, '\r\n')
|
||||
expect(extractMainDocument(combined)).toBe(mainContent)
|
||||
})
|
||||
|
||||
test('normalizes CRLF to LF for content without document separator', () => {
|
||||
const content = 'lockfileVersion: 9.0\r\npackages: {}\r\n'
|
||||
expect(extractMainDocument(content)).toBe('lockfileVersion: 9.0\npackages: {}\n')
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user