/** * @import {HtmlExtension} from 'micromark-util-types' */ const alignment = { none: '', left: ' align="left"', right: ' align="right"', center: ' align="center"' }; // To do: micromark@5: use `infer` here, when all events are exposed. /** * Create an HTML extension for `micromark` to support GitHub tables when * serializing to HTML. * * @returns {HtmlExtension} * Extension for `micromark` that can be passed in `htmlExtensions` to * support GitHub tables when serializing to HTML. */ export function gfmTableHtml() { return { enter: { table(token) { const tableAlign = token._align; this.lineEndingIfNeeded(); this.tag(''); this.setData('tableAlign', tableAlign); }, tableBody() { this.tag(''); }, tableData() { const tableAlign = this.getData('tableAlign'); const tableColumn = this.getData('tableColumn'); const align = alignment[tableAlign[tableColumn]]; if (align === undefined) { // Capture results to ignore them. this.buffer(); } else { this.lineEndingIfNeeded(); this.tag(''); } }, tableHead() { this.lineEndingIfNeeded(); this.tag(''); }, tableHeader() { const tableAlign = this.getData('tableAlign'); const tableColumn = this.getData('tableColumn'); const align = alignment[tableAlign[tableColumn]]; this.lineEndingIfNeeded(); this.tag(''); }, tableRow() { this.setData('tableColumn', 0); this.lineEndingIfNeeded(); this.tag(''); } }, exit: { // Overwrite the default code text data handler to unescape escaped pipes when // they are in tables. codeTextData(token) { let value = this.sliceSerialize(token); if (this.getData('tableAlign')) { value = value.replace(/\\([\\|])/g, replace); } this.raw(this.encode(value)); }, table() { this.setData('tableAlign'); // Note: we don’t set `slurpAllLineEndings` anymore, in delimiter rows, // but we do need to reset it to match a funky newline GH generates for // list items combined with tables. this.setData('slurpAllLineEndings'); this.lineEndingIfNeeded(); this.tag('
'); }, tableBody() { this.lineEndingIfNeeded(); this.tag(''); }, tableData() { const tableAlign = this.getData('tableAlign'); const tableColumn = this.getData('tableColumn'); if (tableColumn in tableAlign) { this.tag(''); this.setData('tableColumn', tableColumn + 1); } else { // Stop capturing. this.resume(); } }, tableHead() { this.lineEndingIfNeeded(); this.tag(''); }, tableHeader() { const tableColumn = this.getData('tableColumn'); this.tag(''); this.setData('tableColumn', tableColumn + 1); }, tableRow() { const tableAlign = this.getData('tableAlign'); let tableColumn = this.getData('tableColumn'); while (tableColumn < tableAlign.length) { this.lineEndingIfNeeded(); this.tag(''); tableColumn++; } this.setData('tableColumn', tableColumn); this.lineEndingIfNeeded(); this.tag(''); } } }; } /** * @param {string} $0 * @param {string} $1 * @returns {string} */ function replace($0, $1) { // Pipes work, backslashes don’t (but can’t escape pipes). return $1 === '|' ? $1 : $0; }