Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions third_party/muya/src/state/__tests__/tableEscapedPipe.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// @vitest-environment happy-dom

import { describe, expect, it, vi } from 'vitest';
import { Muya } from '../../muya';
import { MarkdownToState } from '../markdownToState';
import ExportMarkdown from '../stateToMarkdown';

vi.mock('../../utils/prism/index', () => ({
default: {},
walkTokens: () => null,
loadedLanguages: new Set(),
transformAliasToOrigin: (language: string) => language,
loadLanguage: () => Promise.resolve([]),
search: () => [],
}));

function boot(markdown: string): Muya {
const host = document.createElement('div');
document.body.appendChild(host);
const muya = new Muya(host, { markdown } as ConstructorParameters<typeof Muya>[1]);
muya.init();
return muya;
}

function codeTexts(muya: Muya): string[] {
return [...muya.domNode!.querySelectorAll('td code')].map(code => code.textContent ?? '');
}

function roundTrip(markdown: string): string {
const states = new MarkdownToState().generate(markdown);
return new ExportMarkdown({ listIndentation: 1 }).generate(states);
}

const TABLE = [
'| a | b |',
'| --- | --- |',
'| `\\|` | x |',
'| `\\|\\|` | y |',
'',
].join('\n');

describe('#4849: escaped pipe in a table cell', () => {
it('renders `\\|` inside code as | without the backslash in the editor', () => {
const muya = boot(TABLE);

expect(codeTexts(muya)).toEqual(['|', '||']);
});

it('keeps the table structure', () => {
const muya = boot(TABLE);
const rows = muya.domNode!.querySelectorAll('tr');

expect(rows.length).toBe(3);
expect(rows[2].querySelectorAll('td').length).toBe(2);
});

it('round-trips the escaped pipes back to `\\|` / `\\|\\|`', () => {
const markdown = roundTrip(TABLE);

expect(markdown).toContain('`\\|`');
expect(markdown).toContain('`\\|\\|`');
});

it('round-trips an escaped pipe in plain cell text', () => {
const markdown = roundTrip('| a | b |\n| --- | --- |\n| x \\| y | z |\n');

expect(markdown).toContain('x \\| y');
});
});
9 changes: 2 additions & 7 deletions third_party/muya/src/state/markdownToState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,6 @@ import logger from '../utils/logger';
import { lexBlock } from '../utils/marked';

const debug = logger('import markdown: ');
function restoreTableEscapeCharacters(text: string) {
// NOTE: markedjs replaces all escaped "|" ("\|") characters inside a cell with "|".
// We have to re-escape the character to not break the table.
return text.replace(/\|/g, '\\|');
}

function getOrderedListItemMarker(token: TBlockToken) {
if (token.type !== 'list_item')
Expand Down Expand Up @@ -313,7 +308,7 @@ export class MarkdownToState {
children: header.map((h, i) => ({
name: 'table.cell' as const,
meta: { align: align[i] || 'none' },
text: restoreTableEscapeCharacters(h.text),
text: h.text,
})),
});

Expand All @@ -323,7 +318,7 @@ export class MarkdownToState {
children: row.map((c, i) => ({
name: 'table.cell' as const,
meta: { align: align[i] || 'none' },
text: restoreTableEscapeCharacters(c.text),
text: c.text,
})),
})),
);
Expand Down
Loading