diff --git a/index.compiler.spec.js b/index.compiler.spec.js
index bd1b3125..cbbf7888 100644
--- a/index.compiler.spec.js
+++ b/index.compiler.spec.js
@@ -1698,6 +1698,89 @@ describe('GFM tables', () => {
`);
});
+
+ it('should handle escaped pipes inside a table', () => {
+ render(
+ compiler(
+ [
+ '| \\|Attribute\\| | \\|Type\\| |',
+ '| --------------- | ------------------ |',
+ '| pos\\|position | "left" \\| "right" |',
+ ].join('\n')
+ )
+ );
+
+ expect(root.innerHTML).toMatchInlineSnapshot(`
+
+
+
+
+
+ |Attribute|
+ |
+
+ |Type|
+ |
+
+
+
+
+
+ pos|position
+ |
+
+ "left" | "right"
+ |
+
+
+
+
+`);
+ });
+
+ it('should handle pipes in code inside a table', () => {
+ render(
+ compiler(
+ [
+ '| Attribute | Type |',
+ '| ------------ | --------------------- |',
+ '| `position` | `"left" | "right"` |',
+ ].join('\n')
+ )
+ );
+
+ expect(root.innerHTML).toMatchInlineSnapshot(`
+
+
+
+
+
+ Attribute
+ |
+
+ Type
+ |
+
+
+
+
+
+
+ position
+
+ |
+
+
+ "left" | "right"
+
+ |
+
+
+
+
+`);
+ });
+
});
describe('arbitrary HTML', () => {
diff --git a/index.js b/index.js
index 7d1bc53b..6761a9b0 100644
--- a/index.js
+++ b/index.js
@@ -167,11 +167,12 @@ const REFERENCE_LINK_R = /^\[([^\]]*)\] ?\[([^\]]*)\]/;
const SQUARE_BRACKETS_R = /(\[|\])/g;
const SHOULD_RENDER_AS_BLOCK_R = /(\n|^[-*]\s|^#|^ {2,}|^-{2,}|^>\s)/;
const TAB_R = /\t/g;
+const TABLE_SEPARATOR_R = /^ *\| */;
const TABLE_TRIM_PIPES = /(^ *\||\| *$)/g;
+const TABLE_CELL_END_TRIM = / *$/;
const TABLE_CENTER_ALIGN = /^ *:-+: *$/;
const TABLE_LEFT_ALIGN = /^ *:-+ *$/;
const TABLE_RIGHT_ALIGN = /^ *-+: *$/;
-const TABLE_ROW_SPLIT = / *\| */;
const TEXT_BOLD_R = /^([*_])\1((?:\[.*?\][([].*?[)\]]|<.*?>(?:.*?<.*?>)?|`.*?`|~+.*?~+|.)*?)\1\1(?!\1)/;
const TEXT_EMPHASIZED_R = /^([*_])((?:\[.*?\][([].*?[)\]]|<.*?>(?:.*?<.*?>)?|`.*?`|~+.*?~+|.)*?)\1(?!\1)/;
@@ -287,46 +288,56 @@ function parseTableAlignCapture(alignCapture) {
return null;
}
-function parseTableHeader(capture, parse, state) {
- const headerText = capture[1]
- .replace(TABLE_TRIM_PIPES, '')
- .trim()
- .split(TABLE_ROW_SPLIT);
-
- return headerText.map(function(text) {
- return parse(text, state);
+function parseTableRow(source, parse, state) {
+ const prevInTable = state.inTable;
+ state.inTable = true;
+ const tableRow = parse(source.trim(), state);
+ state.inTable = prevInTable;
+
+ let cells = [[]];
+ tableRow.forEach(function(node, i) {
+ if (node.type === 'tableSeparator') {
+ // Filter out empty table separators at the start/end:
+ if (i !== 0 && i !== tableRow.length - 1) {
+ // Split the current row:
+ cells.push([]);
+ }
+ } else {
+ if (node.type === 'text' && (
+ tableRow[i + 1] == null ||
+ tableRow[i + 1].type === 'tableSeparator'
+ )) {
+ node.content = node.content.replace(TABLE_CELL_END_TRIM, "");
+ }
+ cells[cells.length - 1].push(node);
+ }
});
+ return cells;
}
-function parseTableAlign(capture /*, parse, state*/) {
- const alignText = capture[2]
+function parseTableAlign(source /*, parse, state*/) {
+ const alignText = source
.replace(TABLE_TRIM_PIPES, '')
- .trim()
- .split(TABLE_ROW_SPLIT);
+ .split('|');
return alignText.map(parseTableAlignCapture);
}
-function parseTableCells(capture, parse, state) {
- const rowsText = capture[3]
+function parseTableCells(source, parse, state) {
+ const rowsText = source
.trim()
.split('\n');
return rowsText.map(function(rowText) {
- return rowText
- .replace(TABLE_TRIM_PIPES, '')
- .split(TABLE_ROW_SPLIT)
- .map(function(text) {
- return parse(text.trim(), state);
- });
+ return parseTableRow(rowText, parse, state);
});
}
function parseTable(capture, parse, state) {
state.inline = true;
- const header = parseTableHeader(capture, parse, state);
- const align = parseTableAlign(capture, parse, state);
- const cells = parseTableCells(capture, parse, state);
+ const header = parseTableRow(capture[1], parse, state);
+ const align = parseTableAlign(capture[2], parse, state);
+ const cells = parseTableCells(capture[3], parse, state);
state.inline = false;
return {
@@ -1413,6 +1424,21 @@ export function compiler(markdown, options) {
},
},
+ tableSeparator: {
+ match: function(source, state) {
+ if (!state.inTable) {
+ return null;
+ }
+ return TABLE_SEPARATOR_R.exec(source);
+ },
+ order: PARSE_PRIORITY_HIGH,
+ parse: function() {
+ return { type: 'tableSeparator' };
+ },
+ // These shouldn't be reached, but in case they are, be reasonable:
+ react() { return ' | '; }
+ },
+
text: {
// Here we look for anything followed by non-symbols,
// double newlines, or double-space-newlines