(patch: string)
| 295 | * @throws ParseError if the patch is invalid |
| 296 | */ |
| 297 | export function parsePatch(patch: string): ApplyPatchArgs { |
| 298 | const trimmedPatch = patch.trim() |
| 299 | const lines = trimmedPatch.split("\n") |
| 300 | |
| 301 | // Handle heredoc-wrapped patches (lenient mode) |
| 302 | let effectiveLines = lines |
| 303 | if (lines.length >= 4) { |
| 304 | const firstLine = lines[0] |
| 305 | const lastLine = lines[lines.length - 1] |
| 306 | if ( |
| 307 | (firstLine === "<<EOF" || firstLine === "<<'EOF'" || firstLine === '<<"EOF"') && |
| 308 | lastLine?.endsWith("EOF") |
| 309 | ) { |
| 310 | effectiveLines = lines.slice(1, lines.length - 1) |
| 311 | } |
| 312 | } |
| 313 | |
| 314 | checkPatchBoundaries(effectiveLines) |
| 315 | |
| 316 | const hunks: Hunk[] = [] |
| 317 | const lastLineIndex = effectiveLines.length - 1 |
| 318 | let remainingLines = effectiveLines.slice(1, lastLineIndex) // Skip Begin and End markers |
| 319 | let lineNumber = 2 // Start at line 2 (after Begin Patch) |
| 320 | |
| 321 | while (remainingLines.length > 0) { |
| 322 | const { hunk, linesConsumed } = parseOneHunk(remainingLines, lineNumber) |
| 323 | hunks.push(hunk) |
| 324 | lineNumber += linesConsumed |
| 325 | remainingLines = remainingLines.slice(linesConsumed) |
| 326 | } |
| 327 | |
| 328 | return { |
| 329 | hunks, |
| 330 | patch: effectiveLines.join("\n"), |
| 331 | } |
| 332 | } |
no test coverage detected