(patch: string)
| 30 | } |
| 31 | |
| 32 | function parsePatch(patch: string): PatchFile[] { |
| 33 | const lines = patch.replace(/\r\n/g, "\n").split("\n"); |
| 34 | const files: PatchFile[] = []; |
| 35 | let current: PatchFile | null = null; |
| 36 | let index = 0; |
| 37 | |
| 38 | while (index < lines.length) { |
| 39 | const line = lines[index] ?? ""; |
| 40 | if (line.startsWith("--- ")) { |
| 41 | const next = lines[index + 1] ?? ""; |
| 42 | if (!next.startsWith("+++ ")) { |
| 43 | throw new Error("Patch is missing +++ header after --- header."); |
| 44 | } |
| 45 | current = { |
| 46 | oldPath: normalizePatchPath(line.slice(4).trim()), |
| 47 | newPath: normalizePatchPath(next.slice(4).trim()), |
| 48 | hunks: [], |
| 49 | }; |
| 50 | files.push(current); |
| 51 | index += 2; |
| 52 | continue; |
| 53 | } |
| 54 | |
| 55 | const header = line.match(HUNK_HEADER); |
| 56 | if (header) { |
| 57 | if (!current) { |
| 58 | throw new Error("Encountered hunk before file header."); |
| 59 | } |
| 60 | const hunk: PatchHunk = { |
| 61 | oldStart: Number(header[1]), |
| 62 | oldCount: Number(header[2] ?? 1), |
| 63 | newStart: Number(header[3]), |
| 64 | newCount: Number(header[4] ?? 1), |
| 65 | lines: [], |
| 66 | }; |
| 67 | index += 1; |
| 68 | while (index < lines.length) { |
| 69 | const hunkLine = lines[index] ?? ""; |
| 70 | if (hunkLine.startsWith("@@ ") || hunkLine.startsWith("--- ")) { |
| 71 | break; |
| 72 | } |
| 73 | if (hunkLine === "\\ No newline at end of file") { |
| 74 | index += 1; |
| 75 | continue; |
| 76 | } |
| 77 | if (hunkLine === "") { |
| 78 | index += 1; |
| 79 | continue; |
| 80 | } |
| 81 | const prefix = hunkLine[0]; |
| 82 | const text = hunkLine.slice(1); |
| 83 | if (prefix === " ") { |
| 84 | hunk.lines.push({ type: "context", text }); |
| 85 | } else if (prefix === "-") { |
| 86 | hunk.lines.push({ type: "delete", text }); |
| 87 | } else if (prefix === "+") { |
| 88 | hunk.lines.push({ type: "add", text }); |
| 89 | } else { |
no test coverage detected