fix(table): escape pipe chars in cells on serialize, unescape on parse

- escapeCell() replaces unescaped | with \| before writing each cell,
  applied in both width calculation and formatRow so padding is accurate
- parseRow unescapes \| back to | after splitting on unescaped pipes
- Adds round-trip regression test for cells containing pipe characters

Addresses coderabbitai review comment on PR #5680.
This commit is contained in:
milvasic 2026-03-23 19:35:22 +01:00
parent 314ab03715
commit 004e667b7a
2 changed files with 18 additions and 4 deletions

View File

@ -138,6 +138,18 @@ describe("serializeMarkdownTable", () => {
expect(new Set(lines.map((l) => l.length)).size).toBe(1);
});
it("round-trips a cell containing a pipe character", () => {
const data: TableData = {
headers: ["A", "B"],
rows: [["foo|bar", "baz"]],
alignments: ["none", "none"],
};
const md = serializeMarkdownTable(data);
const parsed = parseMarkdownTable(md);
expect(parsed?.rows[0][0]).toBe("foo|bar");
expect(parsed?.rows[0][1]).toBe("baz");
});
it("round-trips through parse and serialize", () => {
const original = `| Name | Age |
| ----- | --- |

View File

@ -41,7 +41,7 @@ export function parseMarkdownTable(md: string): TableData | null {
let trimmed = line;
if (trimmed.startsWith("|")) trimmed = trimmed.slice(1);
if (trimmed.endsWith("|")) trimmed = trimmed.slice(0, -1);
return trimmed.split(/(?<!\\)\|/).map((cell) => cell.trim());
return trimmed.split(/(?<!\\)\|/).map((cell) => cell.trim().replace(/\\\|/g, "|"));
};
const headers = parseRow(lines[0]);
@ -84,12 +84,14 @@ export function serializeMarkdownTable(data: TableData): string {
const { headers, rows, alignments } = data;
const colCount = headers.length;
const escapeCell = (text: string): string => text.replace(/(?<!\\)\|/g, "\\|");
// Calculate maximum width per column (minimum 3 for the separator).
const widths: number[] = [];
for (let c = 0; c < colCount; c++) {
let max = Math.max(3, headers[c].length);
let max = Math.max(3, escapeCell(headers[c]).length);
for (const row of rows) {
max = Math.max(max, (row[c] || "").length);
max = Math.max(max, escapeCell(row[c] || "").length);
}
widths.push(max);
}
@ -110,7 +112,7 @@ export function serializeMarkdownTable(data: TableData): string {
const formatRow = (cells: string[]): string => {
const formatted = cells.map((cell, i) => {
const align = alignments[i] || "none";
return padCell(cell, widths[i], align);
return padCell(escapeCell(cell), widths[i], align);
});
return "| " + formatted.join(" | ") + " |";
};