Ver Fonte

Merge pull request #7619 from weseek/imprv/gfm-table-performance

imprv: GFM table performance
Yuki Takei há 2 anos atrás
pai
commit
b8e0260cd5
30 ficheiros alterados com 3052 adições e 14 exclusões
  1. 2 1
      apps/app/package.json
  2. 1 0
      packages/micromark-extension-gfm-table/.eslintignore
  3. 5 0
      packages/micromark-extension-gfm-table/.eslintrc.cjs
  4. 3 0
      packages/micromark-extension-gfm-table/.gitignore
  5. 3 0
      packages/micromark-extension-gfm-table/.prettierignore
  6. 1 0
      packages/micromark-extension-gfm-table/.remarkignore
  7. 2 0
      packages/micromark-extension-gfm-table/dev/index.js
  8. 144 0
      packages/micromark-extension-gfm-table/dev/lib/html.js
  9. 682 0
      packages/micromark-extension-gfm-table/dev/lib/syntax.js
  10. 75 0
      packages/micromark-extension-gfm-table/package.json
  11. 390 0
      packages/micromark-extension-gfm-table/readme.md
  12. 104 0
      packages/micromark-extension-gfm-table/test/fixtures/align.html
  13. 77 0
      packages/micromark-extension-gfm-table/test/fixtures/align.md
  14. 40 0
      packages/micromark-extension-gfm-table/test/fixtures/basic.html
  15. 16 0
      packages/micromark-extension-gfm-table/test/fixtures/basic.md
  16. 132 0
      packages/micromark-extension-gfm-table/test/fixtures/containers.html
  17. 53 0
      packages/micromark-extension-gfm-table/test/fixtures/containers.md
  18. 117 0
      packages/micromark-extension-gfm-table/test/fixtures/gfm.html
  19. 54 0
      packages/micromark-extension-gfm-table/test/fixtures/gfm.md
  20. 85 0
      packages/micromark-extension-gfm-table/test/fixtures/grave.html
  21. 32 0
      packages/micromark-extension-gfm-table/test/fixtures/grave.md
  22. 29 0
      packages/micromark-extension-gfm-table/test/fixtures/indent.html
  23. 17 0
      packages/micromark-extension-gfm-table/test/fixtures/indent.md
  24. 31 0
      packages/micromark-extension-gfm-table/test/fixtures/loose.html
  25. 19 0
      packages/micromark-extension-gfm-table/test/fixtures/loose.md
  26. 27 0
      packages/micromark-extension-gfm-table/test/fixtures/some-escapes.html
  27. 12 0
      packages/micromark-extension-gfm-table/test/fixtures/some-escapes.md
  28. 421 0
      packages/micromark-extension-gfm-table/test/index.js
  29. 17 0
      packages/micromark-extension-gfm-table/tsconfig.json
  30. 461 13
      yarn.lock

+ 2 - 1
apps/app/package.json

@@ -61,8 +61,8 @@
     "@google-cloud/storage": "^5.8.5",
     "@growi/core": "^6.1.0-RC.0",
     "@growi/hackmd": "^6.1.0-RC.0",
-    "@growi/remark-attachment-refs": "^6.1.0-RC.0",
     "@growi/preset-themes": "^6.1.0-RC.0",
+    "@growi/remark-attachment-refs": "^6.1.0-RC.0",
     "@growi/remark-drawio": "^6.1.0-RC.0",
     "@growi/remark-growi-directive": "^6.1.0-RC.0",
     "@growi/remark-lsx": "^6.1.0-RC.0",
@@ -120,6 +120,7 @@
     "markdown-table": "^1.1.1",
     "md5": "^2.2.1",
     "method-override": "^3.0.0",
+    "micromark-extension-gfm-table": "link:./packages/micromark-extension-gfm-table",
     "migrate-mongo": "^8.2.3",
     "mkdirp": "^1.0.3",
     "mongoose": "^6.0.13",

+ 1 - 0
packages/micromark-extension-gfm-table/.eslintignore

@@ -0,0 +1 @@
+*.d.ts

+ 5 - 0
packages/micromark-extension-gfm-table/.eslintrc.cjs

@@ -0,0 +1,5 @@
+module.exports = {
+  rules: {
+    '@typescript-eslint/no-use-before-define': 'off',
+  },
+};

+ 3 - 0
packages/micromark-extension-gfm-table/.gitignore

@@ -0,0 +1,3 @@
+*.d.ts
+/lib/
+/index.js

+ 3 - 0
packages/micromark-extension-gfm-table/.prettierignore

@@ -0,0 +1,3 @@
+coverage/
+*.html
+*.md

+ 1 - 0
packages/micromark-extension-gfm-table/.remarkignore

@@ -0,0 +1 @@
+test/

+ 2 - 0
packages/micromark-extension-gfm-table/dev/index.js

@@ -0,0 +1,2 @@
+export {gfmTableHtml} from './lib/html.js'
+export {gfmTable} from './lib/syntax.js'

+ 144 - 0
packages/micromark-extension-gfm-table/dev/lib/html.js

@@ -0,0 +1,144 @@
+/**
+ * @typedef {import('micromark-util-types').HtmlExtension} HtmlExtension
+ * @typedef {import('./syntax.js').Align} Align
+ */
+
+const alignment = {
+  none: '',
+  left: ' align="left"',
+  right: ' align="right"',
+  center: ' align="center"'
+}
+
+/**
+ * HTML extension for micromark (passed in `htmlExtensions`).
+ *
+ * @type {HtmlExtension}
+ */
+export const gfmTableHtml = {
+  enter: {
+    table(token) {
+      /** @type {Array<Align>} */
+      // @ts-expect-error Custom.
+      const tableAlign = token._align
+      this.lineEndingIfNeeded()
+      this.tag('<table>')
+      this.setData('tableAlign', tableAlign)
+    },
+    tableBody() {
+      // Clear slurping line ending from the delimiter row.
+      this.setData('slurpOneLineEnding')
+      this.tag('<tbody>')
+    },
+    tableData() {
+      const tableAlign = /** @type {Array<Align>} */ (
+        this.getData('tableAlign')
+      )
+      const tableColumn = /** @type {number} */ (this.getData('tableColumn'))
+      const align = alignment[tableAlign[tableColumn]]
+
+      if (align === undefined) {
+        // Capture results to ignore them.
+        this.buffer()
+      } else {
+        this.lineEndingIfNeeded()
+        this.tag('<td' + align + '>')
+      }
+    },
+    tableHead() {
+      this.lineEndingIfNeeded()
+      this.tag('<thead>')
+    },
+    tableHeader() {
+      const tableAlign = /** @type {Array<Align>} */ (
+        this.getData('tableAlign')
+      )
+      const tableColumn = /** @type {number} */ (this.getData('tableColumn'))
+      const align = alignment[tableAlign[tableColumn]]
+
+      this.lineEndingIfNeeded()
+      this.tag('<th' + align + '>')
+    },
+    tableRow() {
+      this.setData('tableColumn', 0)
+      this.lineEndingIfNeeded()
+      this.tag('<tr>')
+    }
+  },
+  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')
+      // If there was no table body, make sure the slurping from the delimiter row
+      // is cleared.
+      this.setData('slurpAllLineEndings')
+      this.lineEndingIfNeeded()
+      this.tag('</table>')
+    },
+    tableBody() {
+      this.lineEndingIfNeeded()
+      this.tag('</tbody>')
+    },
+    tableData() {
+      const tableAlign = /** @type {Array<Align>} */ (
+        this.getData('tableAlign')
+      )
+      const tableColumn = /** @type {number} */ (this.getData('tableColumn'))
+
+      if (tableColumn in tableAlign) {
+        this.tag('</td>')
+        this.setData('tableColumn', tableColumn + 1)
+      } else {
+        // Stop capturing.
+        this.resume()
+      }
+    },
+    tableHead() {
+      this.lineEndingIfNeeded()
+      this.tag('</thead>')
+      this.setData('slurpOneLineEnding', true)
+      // Slurp the line ending from the delimiter row.
+    },
+    tableHeader() {
+      const tableColumn = /** @type {number} */ (this.getData('tableColumn'))
+      this.tag('</th>')
+      this.setData('tableColumn', tableColumn + 1)
+    },
+    tableRow() {
+      const tableAlign = /** @type {Array<Align>} */ (
+        this.getData('tableAlign')
+      )
+      let tableColumn = /** @type {number} */ (this.getData('tableColumn'))
+
+      while (tableColumn < tableAlign.length) {
+        this.lineEndingIfNeeded()
+        this.tag('<td' + alignment[tableAlign[tableColumn]] + '></td>')
+        tableColumn++
+      }
+
+      this.setData('tableColumn', tableColumn)
+      this.lineEndingIfNeeded()
+      this.tag('</tr>')
+    }
+  }
+}
+
+/**
+ * @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
+}

+ 682 - 0
packages/micromark-extension-gfm-table/dev/lib/syntax.js

@@ -0,0 +1,682 @@
+/**
+ * @typedef {import('micromark-util-types').Extension} Extension
+ * @typedef {import('micromark-util-types').Resolver} Resolver
+ * @typedef {import('micromark-util-types').Tokenizer} Tokenizer
+ * @typedef {import('micromark-util-types').State} State
+ * @typedef {import('micromark-util-types').Token} Token
+ */
+
+/**
+ * @typedef {'left'|'center'|'right'|'none'} Align
+ */
+
+import { factorySpace } from 'micromark-factory-space';
+import {
+  markdownLineEnding,
+  markdownLineEndingOrSpace,
+  markdownSpace,
+} from 'micromark-util-character';
+import { codes } from 'micromark-util-symbol/codes.js';
+import { constants } from 'micromark-util-symbol/constants.js';
+import { types } from 'micromark-util-symbol/types.js';
+import { ok as assert } from 'uvu/assert';
+
+/**
+ * Syntax extension for micromark (passed in `extensions`).
+ *
+ * @type {Extension}
+ */
+export const gfmTable = {
+  flow: { null: { tokenize: tokenizeTable, resolve: resolveTable } },
+};
+
+const nextPrefixedOrBlank = {
+  tokenize: tokenizeNextPrefixedOrBlank,
+  partial: true,
+};
+
+/** @type {Resolver} */
+// eslint-disable-next-line complexity
+function resolveTable(events, context) {
+  let index = -1;
+  /** @type {boolean|undefined} */
+  let inHead;
+  /** @type {boolean|undefined} */
+  let inDelimiterRow;
+  /** @type {boolean|undefined} */
+  let inRow;
+  /** @type {number|undefined} */
+  let contentStart;
+  /** @type {number|undefined} */
+  let contentEnd;
+  /** @type {number|undefined} */
+  let cellStart;
+  /** @type {boolean|undefined} */
+  let seenCellInRow;
+
+  while (++index < events.length) {
+    const token = events[index][1];
+
+    if (inRow) {
+      if (token.type === 'temporaryTableCellContent') {
+        contentStart = contentStart || index;
+        contentEnd = index;
+      }
+
+      if (
+        // Combine separate content parts into one.
+        (token.type === 'tableCellDivider' || token.type === 'tableRow')
+        && contentEnd
+      ) {
+        assert(
+          contentStart,
+          'expected `contentStart` to be defined if `contentEnd` is',
+        );
+        const content = {
+          type: 'tableContent',
+          start: events[contentStart][1].start,
+          end: events[contentEnd][1].end,
+        };
+        /** @type {Token} */
+        const text = {
+          type: types.chunkText,
+          start: content.start,
+          end: content.end,
+          // @ts-expect-error It’s fine.
+          contentType: constants.contentTypeText,
+        };
+
+        assert(
+          contentStart,
+          'expected `contentStart` to be defined if `contentEnd` is',
+        );
+
+        events.splice(
+          contentStart,
+          contentEnd - contentStart + 1,
+          ['enter', content, context],
+          ['enter', text, context],
+          ['exit', text, context],
+          ['exit', content, context],
+        );
+
+        index -= contentEnd - contentStart - 3;
+        contentStart = undefined;
+        contentEnd = undefined;
+      }
+    }
+
+    if (
+      events[index][0] === 'exit'
+      && cellStart !== undefined
+      && cellStart + (seenCellInRow ? 0 : 1) < index
+      && (token.type === 'tableCellDivider'
+        || (token.type === 'tableRow'
+          && (cellStart + 3 < index
+            || events[cellStart][1].type !== types.whitespace)))
+    ) {
+      const cell = {
+        // eslint-disable-next-line no-nested-ternary
+        type: inDelimiterRow
+          ? 'tableDelimiter'
+          : inHead
+            ? 'tableHeader'
+            : 'tableData',
+        start: events[cellStart][1].start,
+        end: events[index][1].end,
+      };
+      events.splice(index + (token.type === 'tableCellDivider' ? 1 : 0), 0, [
+        'exit',
+        cell,
+        context,
+      ]);
+      events.splice(cellStart, 0, ['enter', cell, context]);
+      index += 2;
+      cellStart = index + 1;
+      seenCellInRow = true;
+    }
+
+    if (token.type === 'tableRow') {
+      inRow = events[index][0] === 'enter';
+
+      if (inRow) {
+        cellStart = index + 1;
+        seenCellInRow = false;
+      }
+    }
+
+    if (token.type === 'tableDelimiterRow') {
+      inDelimiterRow = events[index][0] === 'enter';
+
+      if (inDelimiterRow) {
+        cellStart = index + 1;
+        seenCellInRow = false;
+      }
+    }
+
+    if (token.type === 'tableHead') {
+      inHead = events[index][0] === 'enter';
+    }
+  }
+
+  return events;
+}
+
+/** @type {Tokenizer} */
+function tokenizeTable(effects, ok, nok) {
+  // eslint-disable-next-line @typescript-eslint/no-this-alias
+  const self = this;
+  /** @type {Array<Align>} */
+  const align = [];
+  let tableHeaderCount = 0;
+  /** @type {boolean|undefined} */
+  let seenDelimiter;
+  /** @type {boolean|undefined} */
+  let hasDash;
+
+  return start;
+
+  /** @type {State} */
+  function start(code) {
+    const { containerState } = self;
+    const prevRowCount = containerState.rowCount ?? 0;
+    const hasDelimiterRow = containerState.hasDelimiterRow ?? false;
+
+    // @ts-expect-error Custom.
+    effects.enter('table')._align = align;
+    effects.enter('tableHead');
+    effects.enter('tableRow');
+
+    // increment row count
+    const crrRowCount = prevRowCount + 1;
+    containerState.rowCount = crrRowCount;
+
+    // Max 2 rows processing before delimiter row
+    if (hasDelimiterRow || crrRowCount > 2) {
+      return nok(code);
+    }
+
+    // If we start with a pipe, we open a cell marker.
+    if (code === codes.verticalBar) {
+      return cellDividerHead(code);
+    }
+
+    tableHeaderCount++;
+    effects.enter('temporaryTableCellContent');
+    // Can’t be space or eols at the start of a construct, so we’re in a cell.
+    assert(!markdownLineEndingOrSpace(code), 'expected non-space');
+    return inCellContentHead(code);
+  }
+
+  /** @type {State} */
+  function cellDividerHead(code) {
+    assert(code === codes.verticalBar, 'expected `|`');
+    effects.enter('tableCellDivider');
+    effects.consume(code);
+    effects.exit('tableCellDivider');
+    seenDelimiter = true;
+    return cellBreakHead;
+  }
+
+  /** @type {State} */
+  function cellBreakHead(code) {
+    if (code === codes.eof || markdownLineEnding(code)) {
+      return atRowEndHead(code);
+    }
+
+    if (markdownSpace(code)) {
+      effects.enter(types.whitespace);
+      effects.consume(code);
+      return inWhitespaceHead;
+    }
+
+    if (seenDelimiter) {
+      seenDelimiter = undefined;
+      tableHeaderCount++;
+    }
+
+    if (code === codes.verticalBar) {
+      return cellDividerHead(code);
+    }
+
+    // Anything else is cell content.
+    effects.enter('temporaryTableCellContent');
+    return inCellContentHead(code);
+  }
+
+  /** @type {State} */
+  function inWhitespaceHead(code) {
+    if (markdownSpace(code)) {
+      effects.consume(code);
+      return inWhitespaceHead;
+    }
+
+    effects.exit(types.whitespace);
+    return cellBreakHead(code);
+  }
+
+  /** @type {State} */
+  function inCellContentHead(code) {
+    // EOF, whitespace, pipe
+    if (
+      code === codes.eof
+      || code === codes.verticalBar
+      || markdownLineEndingOrSpace(code)
+    ) {
+      effects.exit('temporaryTableCellContent');
+      return cellBreakHead(code);
+    }
+
+    effects.consume(code);
+    return code === codes.backslash
+      ? inCellContentEscapeHead
+      : inCellContentHead;
+  }
+
+  /** @type {State} */
+  function inCellContentEscapeHead(code) {
+    if (code === codes.backslash || code === codes.verticalBar) {
+      effects.consume(code);
+      return inCellContentHead;
+    }
+
+    // Anything else.
+    return inCellContentHead(code);
+  }
+
+  /** @type {State} */
+  function atRowEndHead(code) {
+    // for debug -- 2023.05.06 Yuki Takei
+    // const { containerState } = self;
+    // let atRowEndHeadCount = containerState.atRowEndHeadCount ?? 0;
+    // atRowEndHeadCount++;
+    // containerState.atRowEndHeadCount = atRowEndHeadCount;
+    // console.log({ atRowEndHeadCount });
+
+    if (code === codes.eof) {
+      return tableExit(code);
+    }
+
+    assert(markdownLineEnding(code), 'expected eol');
+    effects.exit('tableRow');
+    effects.exit('tableHead');
+    const originalInterrupt = self.interrupt;
+    self.interrupt = true;
+    return effects.attempt(
+      { tokenize: tokenizeRowEnd, partial: true },
+      (code) => {
+        self.interrupt = originalInterrupt;
+        effects.enter('tableDelimiterRow');
+        return atDelimiterRowBreak(code);
+      },
+      (code) => {
+        self.interrupt = originalInterrupt;
+        return tableExit(code);
+      },
+    )(code);
+  }
+
+  /** @type {State} */
+  function atDelimiterRowBreak(code) {
+    // persist that the table has a delimiter row
+    self.containerState.hasDelimiterRow = true;
+
+    if (code === codes.eof || markdownLineEnding(code)) {
+      return rowEndDelimiter(code);
+    }
+
+    if (markdownSpace(code)) {
+      effects.enter(types.whitespace);
+      effects.consume(code);
+      return inWhitespaceDelimiter;
+    }
+
+    if (code === codes.dash) {
+      effects.enter('tableDelimiterFiller');
+      effects.consume(code);
+      hasDash = true;
+      align.push('none');
+      return inFillerDelimiter;
+    }
+
+    if (code === codes.colon) {
+      effects.enter('tableDelimiterAlignment');
+      effects.consume(code);
+      effects.exit('tableDelimiterAlignment');
+      align.push('left');
+      return afterLeftAlignment;
+    }
+
+    // If we start with a pipe, we open a cell marker.
+    if (code === codes.verticalBar) {
+      effects.enter('tableCellDivider');
+      effects.consume(code);
+      effects.exit('tableCellDivider');
+      return atDelimiterRowBreak;
+    }
+
+    return tableExit(code);
+  }
+
+  /** @type {State} */
+  function inWhitespaceDelimiter(code) {
+    if (markdownSpace(code)) {
+      effects.consume(code);
+      return inWhitespaceDelimiter;
+    }
+
+    effects.exit(types.whitespace);
+    return atDelimiterRowBreak(code);
+  }
+
+  /** @type {State} */
+  function inFillerDelimiter(code) {
+    if (code === codes.dash) {
+      effects.consume(code);
+      return inFillerDelimiter;
+    }
+
+    effects.exit('tableDelimiterFiller');
+
+    if (code === codes.colon) {
+      effects.enter('tableDelimiterAlignment');
+      effects.consume(code);
+      effects.exit('tableDelimiterAlignment');
+
+      align[align.length - 1] = align[align.length - 1] === 'left' ? 'center' : 'right';
+
+      return afterRightAlignment;
+    }
+
+    return atDelimiterRowBreak(code);
+  }
+
+  /** @type {State} */
+  function afterLeftAlignment(code) {
+    if (code === codes.dash) {
+      effects.enter('tableDelimiterFiller');
+      effects.consume(code);
+      hasDash = true;
+      return inFillerDelimiter;
+    }
+
+    // Anything else is not ok.
+    return tableExit(code);
+  }
+
+  /** @type {State} */
+  function afterRightAlignment(code) {
+    if (code === codes.eof || markdownLineEnding(code)) {
+      return rowEndDelimiter(code);
+    }
+
+    if (markdownSpace(code)) {
+      effects.enter(types.whitespace);
+      effects.consume(code);
+      return inWhitespaceDelimiter;
+    }
+
+    // `|`
+    if (code === codes.verticalBar) {
+      effects.enter('tableCellDivider');
+      effects.consume(code);
+      effects.exit('tableCellDivider');
+      return atDelimiterRowBreak;
+    }
+
+    return tableExit(code);
+  }
+
+  /** @type {State} */
+  function rowEndDelimiter(code) {
+    effects.exit('tableDelimiterRow');
+
+    // Exit if there was no dash at all, or if the header cell count is not the
+    // delimiter cell count.
+    if (!hasDash || tableHeaderCount !== align.length) {
+      return tableExit(code);
+    }
+
+    if (code === codes.eof) {
+      return tableClose(code);
+    }
+
+    assert(markdownLineEnding(code), 'expected eol');
+    return effects.check(
+      nextPrefixedOrBlank,
+      tableClose,
+      effects.attempt(
+        { tokenize: tokenizeRowEnd, partial: true },
+        factorySpace(effects, bodyStart, types.linePrefix, constants.tabSize),
+        tableClose,
+      ),
+    )(code);
+  }
+
+  /** @type {State} */
+  function tableExit(code) {
+    // delete persisted states
+    delete self.containerState.rowCount;
+    delete self.containerState.hasDelimiterRow;
+
+    return nok(code);
+  }
+
+  /** @type {State} */
+  function tableClose(code) {
+    effects.exit('table');
+
+    // delete persisted states
+    delete self.containerState.rowCount;
+    delete self.containerState.hasDelimiterRow;
+
+    return ok(code);
+  }
+
+  /** @type {State} */
+  function bodyStart(code) {
+    effects.enter('tableBody');
+    return rowStartBody(code);
+  }
+
+  /** @type {State} */
+  function rowStartBody(code) {
+    effects.enter('tableRow');
+
+    // If we start with a pipe, we open a cell marker.
+    if (code === codes.verticalBar) {
+      return cellDividerBody(code);
+    }
+
+    effects.enter('temporaryTableCellContent');
+    // Can’t be space or eols at the start of a construct, so we’re in a cell.
+    return inCellContentBody(code);
+  }
+
+  /** @type {State} */
+  function cellDividerBody(code) {
+    assert(code === codes.verticalBar, 'expected `|`');
+    effects.enter('tableCellDivider');
+    effects.consume(code);
+    effects.exit('tableCellDivider');
+    return cellBreakBody;
+  }
+
+  /** @type {State} */
+  function cellBreakBody(code) {
+    if (code === codes.eof || markdownLineEnding(code)) {
+      return atRowEndBody(code);
+    }
+
+    if (markdownSpace(code)) {
+      effects.enter(types.whitespace);
+      effects.consume(code);
+      return inWhitespaceBody;
+    }
+
+    // `|`
+    if (code === codes.verticalBar) {
+      return cellDividerBody(code);
+    }
+
+    // Anything else is cell content.
+    effects.enter('temporaryTableCellContent');
+    return inCellContentBody(code);
+  }
+
+  /** @type {State} */
+  function inWhitespaceBody(code) {
+    if (markdownSpace(code)) {
+      effects.consume(code);
+      return inWhitespaceBody;
+    }
+
+    effects.exit(types.whitespace);
+    return cellBreakBody(code);
+  }
+
+  /** @type {State} */
+  function inCellContentBody(code) {
+    // EOF, whitespace, pipe
+    if (
+      code === codes.eof
+      || code === codes.verticalBar
+      || markdownLineEndingOrSpace(code)
+    ) {
+      effects.exit('temporaryTableCellContent');
+      return cellBreakBody(code);
+    }
+
+    effects.consume(code);
+    return code === codes.backslash
+      ? inCellContentEscapeBody
+      : inCellContentBody;
+  }
+
+  /** @type {State} */
+  function inCellContentEscapeBody(code) {
+    if (code === codes.backslash || code === codes.verticalBar) {
+      effects.consume(code);
+      return inCellContentBody;
+    }
+
+    // Anything else.
+    return inCellContentBody(code);
+  }
+
+  /** @type {State} */
+  function atRowEndBody(code) {
+    effects.exit('tableRow');
+
+    if (code === codes.eof) {
+      return tableBodyClose(code);
+    }
+
+    return effects.check(
+      nextPrefixedOrBlank,
+      tableBodyClose,
+      effects.attempt(
+        { tokenize: tokenizeRowEnd, partial: true },
+        factorySpace(
+          effects,
+          rowStartBody,
+          types.linePrefix,
+          constants.tabSize,
+        ),
+        tableBodyClose,
+      ),
+    )(code);
+  }
+
+  /** @type {State} */
+  function tableBodyClose(code) {
+    effects.exit('tableBody');
+    return tableClose(code);
+  }
+
+  /** @type {Tokenizer} */
+  function tokenizeRowEnd(effects, ok, nok) {
+    return start;
+
+    /** @type {State} */
+    function start(code) {
+      assert(markdownLineEnding(code), 'expected eol');
+      effects.enter(types.lineEnding);
+      effects.consume(code);
+      effects.exit(types.lineEnding);
+      return factorySpace(effects, prefixed, types.linePrefix);
+    }
+
+    /** @type {State} */
+    function prefixed(code) {
+      // Blank or interrupting line.
+      if (
+        self.parser.lazy[self.now().line]
+        || code === codes.eof
+        || markdownLineEnding(code)
+      ) {
+        return nok(code);
+      }
+
+      const tail = self.events[self.events.length - 1];
+
+      // Indented code can interrupt delimiter and body rows.
+      if (
+        !self.parser.constructs.disable.null.includes('codeIndented')
+        && tail
+        && tail[1].type === types.linePrefix
+        && tail[2].sliceSerialize(tail[1], true).length >= constants.tabSize
+      ) {
+        return nok(code);
+      }
+
+      self._gfmTableDynamicInterruptHack = true;
+
+      return effects.check(
+        self.parser.constructs.flow,
+        (code) => {
+          self._gfmTableDynamicInterruptHack = false;
+          return nok(code);
+        },
+        (code) => {
+          self._gfmTableDynamicInterruptHack = false;
+          return ok(code);
+        },
+      )(code);
+    }
+  }
+}
+
+/** @type {Tokenizer} */
+function tokenizeNextPrefixedOrBlank(effects, ok, nok) {
+  let size = 0;
+
+  return start;
+
+  /** @type {State} */
+  function start(code) {
+    // This is a check, so we don’t care about tokens, but we open a bogus one
+    // so we’re valid.
+    effects.enter('check');
+    // EOL.
+    effects.consume(code);
+    return whitespace;
+  }
+
+  /** @type {State} */
+  function whitespace(code) {
+    if (code === codes.virtualSpace || code === codes.space) {
+      effects.consume(code);
+      size++;
+      return size === constants.tabSize ? ok : whitespace;
+    }
+
+    // EOF or whitespace
+    if (code === codes.eof || markdownLineEndingOrSpace(code)) {
+      return ok(code);
+    }
+
+    // Anything else.
+    return nok(code);
+  }
+}

+ 75 - 0
packages/micromark-extension-gfm-table/package.json

@@ -0,0 +1,75 @@
+{
+  "name": "micromark-extension-gfm-table",
+  "version": "1.0.5",
+  "description": "micromark extension to support GFM tables",
+  "license": "MIT",
+  "keywords": [
+    "micromark",
+    "micromark-extension",
+    "table",
+    "row",
+    "column",
+    "cell",
+    "tabular",
+    "gfm",
+    "markdown",
+    "unified"
+  ],
+  "repository": "micromark/micromark-extension-gfm-table",
+  "bugs": "https://github.com/micromark/micromark-extension-gfm-table/issues",
+  "funding": {
+    "type": "opencollective",
+    "url": "https://opencollective.com/unified"
+  },
+  "author": "Titus Wormer <tituswormer@gmail.com> (https://wooorm.com)",
+  "contributors": [
+    "Titus Wormer <tituswormer@gmail.com> (https://wooorm.com)"
+  ],
+  "sideEffects": false,
+  "type": "module",
+  "main": "index.js",
+  "types": "index.d.ts",
+  "files": [
+    "dev/",
+    "lib/",
+    "index.d.ts",
+    "index.js"
+  ],
+  "exports": {
+    "development": "./dev/index.js",
+    "default": "./index.js"
+  },
+  "dependencies": {
+    "micromark-factory-space": "^1.0.0",
+    "micromark-util-character": "^1.0.0",
+    "micromark-util-symbol": "^1.0.0",
+    "micromark-util-types": "^1.0.0",
+    "uvu": "^0.5.0"
+  },
+  "devDependencies": {
+    "@types/tape": "^4.0.0",
+    "c8": "^7.0.0",
+    "create-gfm-fixtures": "^1.0.0",
+    "micromark": "^3.0.0",
+    "micromark-build": "^1.0.0",
+    "rimraf": "^3.0.0",
+    "tape": "^5.0.0",
+    "type-coverage": "^2.0.0"
+  },
+  "scripts": {
+    "build": "rimraf \"dev/**/*.d.ts\" \"test/**/*.d.ts\" && tsc && type-coverage && micromark-build",
+    "clean": "npx -y shx rm -rf lib index.js index.d.ts \"dev/**/*.d.ts\" \"test/**/*.d.ts\"",
+    "dev": "yarn build",
+    "test-api": "node --conditions development test/index.js",
+    "test-coverage": "c8 --check-coverage --branches 99.45 --functions 100 --lines 99.63 --statements 99.63 --reporter lcov node --conditions development test/index.js",
+    "test": "npm run build && npm run test-coverage",
+    "lint": "yarn eslint \"**/*.{cjs, js,jsx,ts,tsx}\"",
+    "lint:fix": "yarn eslint \"**/*.{cjs, js,jsx,ts,tsx}\" --fix"
+  },
+  "typeCoverage": {
+    "atLeast": 94.42,
+    "detail": true,
+    "strict": true,
+    "ignoreCatch": true
+  }
+}

+ 390 - 0
packages/micromark-extension-gfm-table/readme.md

@@ -0,0 +1,390 @@
+# micromark-extension-gfm-table
+
+[![Build][build-badge]][build]
+[![Coverage][coverage-badge]][coverage]
+[![Downloads][downloads-badge]][downloads]
+[![Size][size-badge]][size]
+[![Sponsors][sponsors-badge]][collective]
+[![Backers][backers-badge]][collective]
+[![Chat][chat-badge]][chat]
+
+[micromark][] extension to support GFM [tables][].
+
+## Contents
+
+*   [What is this?](#what-is-this)
+*   [When to use this](#when-to-use-this)
+*   [Install](#install)
+*   [Use](#use)
+*   [API](#api)
+    *   [`gfmTable`](#gfmtable)
+    *   [`gfmTableHtml`](#gfmtablehtml)
+*   [Authoring](#authoring)
+*   [HTML](#html)
+*   [CSS](#css)
+*   [Syntax](#syntax)
+*   [Types](#types)
+*   [Compatibility](#compatibility)
+*   [Security](#security)
+*   [Related](#related)
+*   [Contribute](#contribute)
+*   [License](#license)
+
+## What is this?
+
+This package contains extensions that add support for tables enabled by
+GFM to [`micromark`][micromark].
+It matches how tables work on `github.com`.
+
+## When to use this
+
+These tools are all low-level.
+In many cases, you want to use [`remark-gfm`][plugin] with remark instead.
+
+Even when you want to use `micromark`, you likely want to use
+[`micromark-extension-gfm`][micromark-extension-gfm] to support all GFM
+features.
+That extension includes this extension.
+
+When working with `mdast-util-from-markdown`, you must combine this package with
+[`mdast-util-gfm-table`][util].
+
+## Install
+
+This package is [ESM only][esm].
+In Node.js (version 12.20+, 14.14+, 16.0+, or 18.0+), install with [npm][]:
+
+```sh
+npm install micromark-extension-gfm-table
+```
+
+In Deno with [`esm.sh`][esmsh]:
+
+```js
+import {gfmTable, gfmTableHtml} from 'https://esm.sh/micromark-extension-gfm-table@1'
+```
+
+In browsers with [`esm.sh`][esmsh]:
+
+```html
+<script type="module">
+  import {gfmTable, gfmTableHtml} from 'https://esm.sh/micromark-extension-gfm-table@1?bundle'
+</script>
+```
+
+## Use
+
+```js
+import {micromark} from 'micromark'
+import {gfmTable, gfmTableHtml} from 'micromark-extension-gfm-table'
+
+const output = micromark('| a |\n| - |', {
+  extensions: [gfmTable],
+  htmlExtensions: [gfmTableHtml]
+})
+
+console.log(output)
+```
+
+Yields:
+
+```html
+<table>
+<thead>
+<tr>
+<th>a</th>
+</tr>
+</thead>
+</table>
+```
+
+## API
+
+This package exports the identifiers `gfmTable` and `gfmTableHtml`.
+There is no default export.
+
+The export map supports the endorsed [`development` condition][condition].
+Run `node --conditions development module.js` to get instrumented dev code.
+Without this condition, production code is loaded.
+
+### `gfmTable`
+
+Syntax extension for micromark (passed in `extensions`).
+
+### `gfmTableHtml`
+
+HTML extension for micromark (passed in `htmlExtensions`).
+
+## Authoring
+
+###### Align
+
+When authoring markdown with tables, it can get a bit hard to make them look
+well.
+You can align the pipes (`|`) in rows nicely, which makes it easier to spot
+problems, but aligning gets cumbersome for tables with many rows or columns,
+or when they change frequently, especially if data in cells have varying
+lengths.
+To illustrate, when some cell increases in size which makes it longer than all
+other cells in that column, you’d have to pad every other cell as well, which
+can be a lot of work, and will introduce a giant diff in Git.
+
+###### Initial and final pipes
+
+In some cases, GFM tables can be written without initial or final pipes:
+
+```markdown
+name  | letter
+----- | ------
+alpha | a
+bravo | b
+```
+
+These tables do not parse in certain other cases, making them fragile and hard
+to get right.
+Due to this, it’s recommended to always include initial and final pipes.
+
+###### Escaped pipes in code
+
+GitHub applies one weird, special thing in tables that markdown otherwise never
+does: it allows character escapes (not character references) of pipes (not other
+characters) in code in cells.
+It’s weird, because markdown, per CommonMark, does not allow character escapes
+in code.
+GitHub only applies this change in code in tables:
+
+```markdown
+| `a\|b\-` |
+| - |
+
+`a\|b\-`
+```
+
+Yields:
+
+```html
+<table>
+<thead>
+<tr>
+<th><code>a|b\-</code></th>
+</tr>
+</thead>
+</table>
+<p><code>a\|b\-</code></p>
+```
+
+> 👉 **Note**: observe that the escaped pipe in the table does not result in
+> another column, and is not present in the resulting code.
+> Other escapes, and pipe escapes outside tables, do nothing.
+
+This behavior solves a real problem, so you might resort to using it.
+It might not work in other markdown parsers though.
+
+## HTML
+
+GFM tables relate to several tabular data HTML elements:
+See [*§ 4.9.1 The `table` element*][html-table],
+[*§ 4.9.5 The `tbody` element*][html-tbody],
+[*§ 4.9.6 The `thead` element*][html-thead],
+[*§ 4.9.8 The `tr` element*][html-tr],
+[*§ 4.9.9 The `td` element*][html-td], and
+[*§ 4.9.10 The `th` element*][html-th]
+in the HTML spec for more info.
+
+GitHub provides the alignment information from the delimiter row on each `<td>`
+and `<th>` element with an `align` attribute.
+This feature stems from ancient times in HTML, and still works, but is
+considered a [non-conforming feature][html-non-conform], which must not be used
+by authors.
+
+## CSS
+
+The following CSS is needed to make tables look a bit like GitHub.
+For the complete actual CSS see
+[`sindresorhus/github-markdown-css`][github-markdown-css]
+
+```css
+/* Light theme. */
+:root {
+  --color-canvas-default: #ffffff;
+  --color-canvas-subtle: #f6f8fa;
+  --color-border-default: #d0d7de;
+  --color-border-muted: hsla(210, 18%, 87%, 1);
+}
+
+/* Dark theme. */
+@media (prefers-color-scheme: dark) {
+  :root {
+    --color-canvas-default: #0d1117;
+    --color-canvas-subtle: #161b22;
+    --color-border-default: #30363d;
+    --color-border-muted: #21262d;
+  }
+}
+
+table {
+  border-spacing: 0;
+  border-collapse: collapse;
+  display: block;
+  margin-top: 0;
+  margin-bottom: 16px;
+  width: max-content;
+  max-width: 100%;
+  overflow: auto;
+}
+
+tr {
+  background-color: var(--color-canvas-default);
+  border-top: 1px solid var(--color-border-muted);
+}
+
+tr:nth-child(2n) {
+  background-color: var(--color-canvas-subtle);
+}
+
+td,
+th {
+  padding: 6px 13px;
+  border: 1px solid var(--color-border-default);
+}
+
+th {
+  font-weight: 600;
+}
+
+table img {
+  background-color: transparent;
+}
+```
+
+## Syntax
+
+Tables form with, roughly, the following BNF:
+
+```bnf
+; Restriction: number of cells in first row must match number of cells in delimiter row.
+table ::= row eol delimiter_row 0.*( eol row )
+
+; Restriction: Line cannot be blank.
+row ::= [ '|' ] cell 0.*( '|' cell ) [ '|' ]
+delimiter_row ::= [ '|' ] delimiter_cell 0.*( '|' delimiter_cell ) [ '|' ]
+
+cell ::= 0.*space_or_tab 0.*( cell_text | cell_escape ) 0.*space_or_tab
+cell_text ::= code - eol - '|' - '\\' - ''
+cell_escape ::= '\\' ( '|' | '\\' )
+delimiter_cell ::= 0.*space_or_tab [ ':' ] 1*'-' [ ':' ] 0.*space_or_tab
+```
+
+## Types
+
+This package is fully typed with [TypeScript][].
+There are no additional exported types.
+
+## Compatibility
+
+This package is at least compatible with all maintained versions of Node.js.
+As of now, that is Node.js 12.20+, 14.14+, 16.0+, and 18.0+.
+It also works in Deno and modern browsers.
+
+## Security
+
+This package is safe.
+
+## Related
+
+*   [`syntax-tree/mdast-util-gfm-table`][util]
+    — support GFM tables in mdast
+*   [`syntax-tree/mdast-util-gfm`][mdast-util-gfm]
+    — support GFM in mdast
+*   [`remarkjs/remark-gfm`][plugin]
+    — support GFM in remark
+
+## Contribute
+
+See [`contributing.md` in `micromark/.github`][contributing] for ways to get
+started.
+See [`support.md`][support] for ways to get help.
+
+This project has a [code of conduct][coc].
+By interacting with this repository, organization, or community you agree to
+abide by its terms.
+
+## License
+
+[MIT][license] © [Titus Wormer][author]
+
+<!-- Definitions -->
+
+[build-badge]: https://github.com/micromark/micromark-extension-gfm-table/workflows/main/badge.svg
+
+[build]: https://github.com/micromark/micromark-extension-gfm-table/actions
+
+[coverage-badge]: https://img.shields.io/codecov/c/github/micromark/micromark-extension-gfm-table.svg
+
+[coverage]: https://codecov.io/github/micromark/micromark-extension-gfm-table
+
+[downloads-badge]: https://img.shields.io/npm/dm/micromark-extension-gfm-table.svg
+
+[downloads]: https://www.npmjs.com/package/micromark-extension-gfm-table
+
+[size-badge]: https://img.shields.io/bundlephobia/minzip/micromark-extension-gfm-table.svg
+
+[size]: https://bundlephobia.com/result?p=micromark-extension-gfm-table
+
+[sponsors-badge]: https://opencollective.com/unified/sponsors/badge.svg
+
+[backers-badge]: https://opencollective.com/unified/backers/badge.svg
+
+[collective]: https://opencollective.com/unified
+
+[chat-badge]: https://img.shields.io/badge/chat-discussions-success.svg
+
+[chat]: https://github.com/micromark/micromark/discussions
+
+[npm]: https://docs.npmjs.com/cli/install
+
+[esmsh]: https://esm.sh
+
+[license]: license
+
+[author]: https://wooorm.com
+
+[contributing]: https://github.com/micromark/.github/blob/main/contributing.md
+
+[support]: https://github.com/micromark/.github/blob/main/support.md
+
+[coc]: https://github.com/micromark/.github/blob/main/code-of-conduct.md
+
+[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c
+
+[typescript]: https://www.typescriptlang.org
+
+[condition]: https://nodejs.org/api/packages.html#packages_resolving_user_conditions
+
+[micromark]: https://github.com/micromark/micromark
+
+[micromark-extension-gfm]: https://github.com/micromark/micromark-extension-gfm
+
+[util]: https://github.com/syntax-tree/mdast-util-gfm-table
+
+[mdast-util-gfm]: https://github.com/syntax-tree/mdast-util-gfm
+
+[plugin]: https://github.com/remarkjs/remark-gfm
+
+[tables]: https://github.github.com/gfm/#tables-extension-
+
+[html-table]: https://html.spec.whatwg.org/multipage/tables.html#the-table-element
+
+[html-tbody]: https://html.spec.whatwg.org/multipage/tables.html#the-tbody-element
+
+[html-thead]: https://html.spec.whatwg.org/multipage/tables.html#the-thead-element
+
+[html-tr]: https://html.spec.whatwg.org/multipage/tables.html#the-tr-element
+
+[html-td]: https://html.spec.whatwg.org/multipage/tables.html#the-td-element
+
+[html-th]: https://html.spec.whatwg.org/multipage/tables.html#the-th-element
+
+[html-non-conform]: https://html.spec.whatwg.org/multipage/obsolete.html#non-conforming-features
+
+[github-markdown-css]: https://github.com/sindresorhus/github-markdown-css

+ 104 - 0
packages/micromark-extension-gfm-table/test/fixtures/align.html

@@ -0,0 +1,104 @@
+<h1>Align</h1>
+<h2>An empty initial cell</h2>
+<table>
+<thead>
+<tr>
+<th></th>
+<th align="center">a</th>
+<th align="left">c</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td>a</td>
+<td align="center">b</td>
+<td align="left">c</td>
+</tr>
+<tr>
+<td>a</td>
+<td align="center">b</td>
+<td align="left">c</td>
+</tr>
+</tbody>
+</table>
+<h2>Missing alignment characters</h2>
+<p>| a | b | c |
+|   |---|---|
+| d | e | f |</p>
+<hr />
+<p>| a | b | c |
+|---|---|   |
+| d | e | f |</p>
+<h2>Incorrect characters</h2>
+<p>| a | b | c |
+|---|-*-|---|
+| d | e | f |</p>
+<h2>Two alignments</h2>
+<p>|a|
+|::|</p>
+<table>
+<thead>
+<tr>
+<th align="center">a</th>
+</tr>
+</thead>
+</table>
+<h2>Two at the start or end</h2>
+<p>|a|
+|::-|</p>
+<p>|a|
+|-::|</p>
+<h2>In the middle</h2>
+<p>|a|
+|-:-|</p>
+<h2>A space in the middle</h2>
+<p>|a|
+|- -|</p>
+<h2>No pipe</h2>
+<table>
+<thead>
+<tr>
+<th align="center">a</th>
+</tr>
+</thead>
+</table>
+<table>
+<thead>
+<tr>
+<th align="left">a</th>
+</tr>
+</thead>
+</table>
+<table>
+<thead>
+<tr>
+<th align="right">a</th>
+</tr>
+</thead>
+</table>
+<h2>A single colon</h2>
+<p>|a|
+|:|</p>
+<p>a
+:</p>
+<h2>Alignment on empty cells</h2>
+<table>
+<thead>
+<tr>
+<th>a</th>
+<th>b</th>
+<th align="left">c</th>
+<th align="right">d</th>
+<th align="center">e</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td>f</td>
+<td></td>
+<td align="left"></td>
+<td align="right"></td>
+<td align="center"></td>
+</tr>
+</tbody>
+</table>

+ 77 - 0
packages/micromark-extension-gfm-table/test/fixtures/align.md

@@ -0,0 +1,77 @@
+# Align
+
+## An empty initial cell
+
+| | a|c|
+|--|:----:|:---|
+|a|b|c|
+|a|b|c|
+
+## Missing alignment characters
+
+| a | b | c |
+|   |---|---|
+| d | e | f |
+
+* * *
+
+| a | b | c |
+|---|---|   |
+| d | e | f |
+
+## Incorrect characters
+
+| a | b | c |
+|---|-*-|---|
+| d | e | f |
+
+## Two alignments
+
+|a|
+|::|
+
+|a|
+|:-:|
+
+## Two at the start or end
+
+|a|
+|::-|
+
+|a|
+|-::|
+
+## In the middle
+
+|a|
+|-:-|
+
+## A space in the middle
+
+|a|
+|- -|
+
+## No pipe
+
+a
+:-:
+
+a
+:-
+
+a
+-:
+
+## A single colon
+
+|a|
+|:|
+
+a
+:
+
+## Alignment on empty cells
+
+| a | b | c | d | e |
+| - | - | :- | -: | :-: |
+| f |

+ 40 - 0
packages/micromark-extension-gfm-table/test/fixtures/basic.html

@@ -0,0 +1,40 @@
+<h1>Tables</h1>
+<table>
+<thead>
+<tr>
+<th>a</th>
+<th>b</th>
+<th>c</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td>d</td>
+<td>e</td>
+<td>f</td>
+</tr>
+</tbody>
+</table>
+<h2>No body</h2>
+<table>
+<thead>
+<tr>
+<th>a</th>
+<th>b</th>
+<th>c</th>
+</tr>
+</thead>
+</table>
+<h2>One column</h2>
+<table>
+<thead>
+<tr>
+<th>a</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td>b</td>
+</tr>
+</tbody>
+</table>

+ 16 - 0
packages/micromark-extension-gfm-table/test/fixtures/basic.md

@@ -0,0 +1,16 @@
+# Tables
+
+| a | b | c |
+| - | - | - |
+| d | e | f |
+
+## No body
+
+| a | b | c |
+| - | - | - |
+
+## One column
+
+| a |
+| - |
+| b |

+ 132 - 0
packages/micromark-extension-gfm-table/test/fixtures/containers.html

@@ -0,0 +1,132 @@
+<h1>Tables in things</h1>
+<h2>In lists</h2>
+<ul>
+<li>
+<p>Unordered:</p>
+<table>
+<thead>
+<tr>
+<th>A</th>
+<th>B</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td>1</td>
+<td>2</td>
+</tr>
+</tbody>
+</table>
+</li>
+</ul>
+<ol>
+<li>
+<p>Ordered:</p>
+<table>
+<thead>
+<tr>
+<th>A</th>
+<th>B</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td>1</td>
+<td>2</td>
+</tr>
+</tbody>
+</table>
+</li>
+</ol>
+<ul>
+<li>Lazy?
+<table>
+<thead>
+<tr>
+<th>A</th>
+<th>B</th>
+</tr>
+</thead>
+</table>
+</li>
+</ul>
+<p>| 1 | 2 |
+| 3 | 4 |
+| 5 | 6 |
+| 7 | 8 |</p>
+<h2>In block quotes</h2>
+<blockquote>
+<p>W/ space:</p>
+<table>
+<thead>
+<tr>
+<th>A</th>
+<th>B</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td>1</td>
+<td>2</td>
+</tr>
+</tbody>
+</table>
+</blockquote>
+<blockquote>
+<p>W/o space:</p>
+<table>
+<thead>
+<tr>
+<th>A</th>
+<th>B</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td>1</td>
+<td>2</td>
+</tr>
+</tbody>
+</table>
+</blockquote>
+<blockquote>
+<p>Lazy?</p>
+<table>
+<thead>
+<tr>
+<th>A</th>
+<th>B</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td>1</td>
+<td>2</td>
+</tr>
+<tr>
+<td>3</td>
+<td>4</td>
+</tr>
+</tbody>
+</table>
+</blockquote>
+<p>| 5 | 6 |</p>
+<h3>List interrupting delimiters</h3>
+<p>a |</p>
+<ul>
+<li>|</li>
+</ul>
+<table>
+<thead>
+<tr>
+<th>a</th>
+</tr>
+</thead>
+</table>
+<table>
+<thead>
+<tr>
+<th>a</th>
+</tr>
+</thead>
+</table>

+ 53 - 0
packages/micromark-extension-gfm-table/test/fixtures/containers.md

@@ -0,0 +1,53 @@
+# Tables in things
+
+## In lists
+
+*   Unordered:
+
+    | A | B |
+    | - | - |
+    | 1 | 2 |
+
+1.  Ordered:
+
+    | A | B |
+    | - | - |
+    | 1 | 2 |
+
+*   Lazy?
+    | A | B |
+    | - | - |
+   | 1 | 2 |
+  | 3 | 4 |
+ | 5 | 6 |
+| 7 | 8 |
+
+## In block quotes
+
+> W/ space:
+> | A | B |
+> | - | - |
+> | 1 | 2 |
+
+>W/o space:
+>| A | B |
+>| - | - |
+>| 1 | 2 |
+
+> Lazy?
+> | A | B |
+> | - | - |
+> | 1 | 2 |
+>| 3 | 4 |
+| 5 | 6 |
+
+### List interrupting delimiters
+
+a |
+- |
+
+a
+-|
+
+a
+|-

+ 117 - 0
packages/micromark-extension-gfm-table/test/fixtures/gfm.html

@@ -0,0 +1,117 @@
+<h1>Examples from GFM</h1>
+<h2>A</h2>
+<table>
+<thead>
+<tr>
+<th>foo</th>
+<th>bar</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td>baz</td>
+<td>bim</td>
+</tr>
+</tbody>
+</table>
+<h2>B</h2>
+<table>
+<thead>
+<tr>
+<th align="center">abc</th>
+<th align="right">defghi</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td align="center">bar</td>
+<td align="right">baz</td>
+</tr>
+</tbody>
+</table>
+<h2>C</h2>
+<table>
+<thead>
+<tr>
+<th>f|oo</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td>b <code>|</code> az</td>
+</tr>
+<tr>
+<td>b <strong>|</strong> im</td>
+</tr>
+</tbody>
+</table>
+<h2>D</h2>
+<table>
+<thead>
+<tr>
+<th>abc</th>
+<th>def</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td>bar</td>
+<td>baz</td>
+</tr>
+</tbody>
+</table>
+<blockquote>
+<p>bar</p>
+</blockquote>
+<h2>E</h2>
+<table>
+<thead>
+<tr>
+<th>abc</th>
+<th>def</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td>bar</td>
+<td>baz</td>
+</tr>
+<tr>
+<td>bar</td>
+<td></td>
+</tr>
+</tbody>
+</table>
+<p>bar</p>
+<h2>F</h2>
+<p>| abc | def |
+| --- |
+| bar |</p>
+<h2>G</h2>
+<table>
+<thead>
+<tr>
+<th>abc</th>
+<th>def</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td>bar</td>
+<td></td>
+</tr>
+<tr>
+<td>bar</td>
+<td>baz</td>
+</tr>
+</tbody>
+</table>
+<h2>H</h2>
+<table>
+<thead>
+<tr>
+<th>abc</th>
+<th>def</th>
+</tr>
+</thead>
+</table>

+ 54 - 0
packages/micromark-extension-gfm-table/test/fixtures/gfm.md

@@ -0,0 +1,54 @@
+# Examples from GFM
+
+## A
+
+| foo | bar |
+| --- | --- |
+| baz | bim |
+
+## B
+
+| abc | defghi |
+:-: | -----------:
+bar | baz
+
+## C
+
+| f\|oo  |
+| ------ |
+| b `\|` az |
+| b **\|** im |
+
+## D
+
+| abc | def |
+| --- | --- |
+| bar | baz |
+> bar
+
+## E
+
+| abc | def |
+| --- | --- |
+| bar | baz |
+bar
+
+bar
+
+## F
+
+| abc | def |
+| --- |
+| bar |
+
+## G
+
+| abc | def |
+| --- | --- |
+| bar |
+| bar | baz | boo |
+
+## H
+
+| abc | def |
+| --- | --- |

+ 85 - 0
packages/micromark-extension-gfm-table/test/fixtures/grave.html

@@ -0,0 +1,85 @@
+<h1>Grave accents</h1>
+<h2>Grave accent in cell</h2>
+<table>
+<thead>
+<tr>
+<th>A</th>
+<th>B</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td><kbd>`</kbd></td>
+<td>C</td>
+</tr>
+</tbody>
+</table>
+<h2>Escaped grave accent in “inline code” in cell</h2>
+<table>
+<thead>
+<tr>
+<th>A</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td><code>\</code></td>
+</tr>
+</tbody>
+</table>
+<h2>“Empty” inline code</h2>
+<table>
+<thead>
+<tr>
+<th>1</th>
+<th>2</th>
+<th>3</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td>a</td>
+<td>``</td>
+<td></td>
+</tr>
+<tr>
+<td>b</td>
+<td>``</td>
+<td>``</td>
+</tr>
+<tr>
+<td>c</td>
+<td>`</td>
+<td>`</td>
+</tr>
+<tr>
+<td>d</td>
+<td>`</td>
+<td>`</td>
+</tr>
+<tr>
+<td>e</td>
+<td><code>|</code></td>
+<td></td>
+</tr>
+<tr>
+<td>f</td>
+<td>|</td>
+<td></td>
+</tr>
+</tbody>
+</table>
+<h2>Escaped pipes in code in cells</h2>
+<table>
+<thead>
+<tr>
+<th><code>|\\</code></th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td><code>|\\</code></td>
+</tr>
+</tbody>
+</table>
+<p><code>\|\\</code></p>

+ 32 - 0
packages/micromark-extension-gfm-table/test/fixtures/grave.md

@@ -0,0 +1,32 @@
+# Grave accents
+
+## Grave accent in cell
+
+| A            | B |
+|--------------|---|
+| <kbd>`</kbd> | C |
+
+## Escaped grave accent in “inline code” in cell
+
+| A   |
+|-----|
+| `\` |
+
+## “Empty” inline code
+
+| 1 | 2    | 3  |
+|---|------|----|
+| a |   `` |    |
+| b |   `` | `` |
+| c |    ` | `  |
+| d |     `|`   |
+| e | `\|` |    |
+| f |   \| |    |
+
+## Escaped pipes in code in cells
+
+| `\|\\` |
+| --- |
+| `\|\\` |
+
+`\|\\`

+ 29 - 0
packages/micromark-extension-gfm-table/test/fixtures/indent.html

@@ -0,0 +1,29 @@
+<h1>Code</h1>
+<h2>Indented delimiter row</h2>
+<table>
+<thead>
+<tr>
+<th>a</th>
+</tr>
+</thead>
+</table>
+<p>a
+|-</p>
+<h2>Indented body</h2>
+<table>
+<thead>
+<tr>
+<th>a</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td>C</td>
+</tr>
+<tr>
+<td>D</td>
+</tr>
+</tbody>
+</table>
+<pre><code>| E |
+</code></pre>

+ 17 - 0
packages/micromark-extension-gfm-table/test/fixtures/indent.md

@@ -0,0 +1,17 @@
+# Code
+
+## Indented delimiter row
+
+a
+   |-
+
+a
+    |-
+
+## Indented body
+
+| a |
+ | - |
+  | C |
+   | D |
+    | E |

+ 31 - 0
packages/micromark-extension-gfm-table/test/fixtures/loose.html

@@ -0,0 +1,31 @@
+<h1>Loose</h1>
+<h2>Loose</h2>
+<table>
+<thead>
+<tr>
+<th>Header 1</th>
+<th>Header 2</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td>Cell 1</td>
+<td>Cell 2</td>
+</tr>
+<tr>
+<td>Cell 3</td>
+<td>Cell 4</td>
+</tr>
+</tbody>
+</table>
+<h2>One “column”, loose</h2>
+<h2>a</h2>
+<p>b</p>
+<h2>No pipe in first row</h2>
+<table>
+<thead>
+<tr>
+<th>a</th>
+</tr>
+</thead>
+</table>

+ 19 - 0
packages/micromark-extension-gfm-table/test/fixtures/loose.md

@@ -0,0 +1,19 @@
+# Loose
+
+## Loose
+
+Header 1 | Header 2
+-------- | --------
+Cell 1   | Cell 2
+Cell 3   | Cell 4
+
+## One “column”, loose
+
+a
+-
+b
+
+## No pipe in first row
+
+a
+| - |

+ 27 - 0
packages/micromark-extension-gfm-table/test/fixtures/some-escapes.html

@@ -0,0 +1,27 @@
+<h1>Some more escapes</h1>
+<table>
+<thead>
+<tr>
+<th>Head</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td>A</td>
+</tr>
+<tr>
+<td>B | Bravo</td>
+</tr>
+<tr>
+<td>C | Charlie</td>
+</tr>
+<tr>
+<td>D \| Delta</td>
+</tr>
+<tr>
+<td>E \| Echo</td>
+</tr>
+</tbody>
+</table>
+<p>Note: GH has a bug where in case C and E, the escaped escape is treated as a
+normal escape.</p>

+ 12 - 0
packages/micromark-extension-gfm-table/test/fixtures/some-escapes.md

@@ -0,0 +1,12 @@
+# Some more escapes
+
+| Head          |
+| ------------- |
+| A | Alpha     |
+| B \| Bravo    |
+| C \\| Charlie |
+| D \\\| Delta  |
+| E \\\\| Echo  |
+
+Note: GH has a bug where in case C and E, the escaped escape is treated as a
+normal escape.

+ 421 - 0
packages/micromark-extension-gfm-table/test/index.js

@@ -0,0 +1,421 @@
+import {URL} from 'node:url'
+import fs from 'node:fs'
+import path from 'node:path'
+import test from 'tape'
+import {micromark} from 'micromark'
+import {createGfmFixtures} from 'create-gfm-fixtures'
+import {gfmTable as syntax, gfmTableHtml as html} from '../dev/index.js'
+
+test('markdown -> html (micromark)', (t) => {
+  t.deepEqual(
+    micromark('| a |', {
+      extensions: [syntax],
+      htmlExtensions: [html]
+    }),
+    '<p>| a |</p>',
+    'should not support a table w/ the head row ending in an eof (1)'
+  )
+
+  t.deepEqual(
+    micromark('| a', {
+      extensions: [syntax],
+      htmlExtensions: [html]
+    }),
+    '<p>| a</p>',
+    'should not support a table w/ the head row ending in an eof (2)'
+  )
+
+  t.deepEqual(
+    micromark('a |', {
+      extensions: [syntax],
+      htmlExtensions: [html]
+    }),
+    '<p>a |</p>',
+    'should not support a table w/ the head row ending in an eof (3)'
+  )
+
+  t.deepEqual(
+    micromark('| a |\n| - |', {
+      extensions: [syntax],
+      htmlExtensions: [html]
+    }),
+    '<table>\n<thead>\n<tr>\n<th>a</th>\n</tr>\n</thead>\n</table>',
+    'should support a table w/ a delimiter row ending in an eof (1)'
+  )
+
+  t.deepEqual(
+    micromark('| a\n| -', {
+      extensions: [syntax],
+      htmlExtensions: [html]
+    }),
+    '<table>\n<thead>\n<tr>\n<th>a</th>\n</tr>\n</thead>\n</table>',
+    'should support a table w/ a delimiter row ending in an eof (2)'
+  )
+
+  t.deepEqual(
+    micromark('| a |\n| - |\n| b |', {
+      extensions: [syntax],
+      htmlExtensions: [html]
+    }),
+    '<table>\n<thead>\n<tr>\n<th>a</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>b</td>\n</tr>\n</tbody>\n</table>',
+    'should support a table w/ a body row ending in an eof (1)'
+  )
+
+  t.deepEqual(
+    micromark('| a\n| -\n| b', {
+      extensions: [syntax],
+      htmlExtensions: [html]
+    }),
+    '<table>\n<thead>\n<tr>\n<th>a</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>b</td>\n</tr>\n</tbody>\n</table>',
+    'should support a table w/ a body row ending in an eof (2)'
+  )
+
+  t.deepEqual(
+    micromark('a|b\n-|-\nc|d', {
+      extensions: [syntax],
+      htmlExtensions: [html]
+    }),
+    '<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>c</td>\n<td>d</td>\n</tr>\n</tbody>\n</table>',
+    'should support a table w/ a body row ending in an eof (3)'
+  )
+
+  t.deepEqual(
+    micromark('| a  \n| -\t\n| b |     ', {
+      extensions: [syntax],
+      htmlExtensions: [html]
+    }),
+    '<table>\n<thead>\n<tr>\n<th>a</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>b</td>\n</tr>\n</tbody>\n</table>',
+    'should support rows w/ trailing whitespace (1)'
+  )
+
+  t.deepEqual(
+    micromark('| a | \n| - |', {extensions: [syntax], htmlExtensions: [html]}),
+    '<table>\n<thead>\n<tr>\n<th>a</th>\n</tr>\n</thead>\n</table>',
+    'should support rows w/ trailing whitespace (2)'
+  )
+
+  t.deepEqual(
+    micromark('| a |\n| - | ', {extensions: [syntax], htmlExtensions: [html]}),
+    '<table>\n<thead>\n<tr>\n<th>a</th>\n</tr>\n</thead>\n</table>',
+    'should support rows w/ trailing whitespace (3)'
+  )
+
+  t.deepEqual(
+    micromark('| a |\n| - |\n| b | ', {
+      extensions: [syntax],
+      htmlExtensions: [html]
+    }),
+    '<table>\n<thead>\n<tr>\n<th>a</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>b</td>\n</tr>\n</tbody>\n</table>',
+    'should support rows w/ trailing whitespace (4)'
+  )
+
+  t.deepEqual(
+    micromark('||a|\n|-|-|', {
+      extensions: [syntax],
+      htmlExtensions: [html]
+    }),
+    '<table>\n<thead>\n<tr>\n<th></th>\n<th>a</th>\n</tr>\n</thead>\n</table>',
+    'should support empty first header cells'
+  )
+
+  t.deepEqual(
+    micromark('|a||\n|-|-|', {
+      extensions: [syntax],
+      htmlExtensions: [html]
+    }),
+    '<table>\n<thead>\n<tr>\n<th>a</th>\n<th></th>\n</tr>\n</thead>\n</table>',
+    'should support empty last header cells'
+  )
+
+  t.deepEqual(
+    micromark('a||b\n-|-|-', {
+      extensions: [syntax],
+      htmlExtensions: [html]
+    }),
+    '<table>\n<thead>\n<tr>\n<th>a</th>\n<th></th>\n<th>b</th>\n</tr>\n</thead>\n</table>',
+    'should support empty header cells'
+  )
+
+  t.deepEqual(
+    micromark('|a|b|\n|-|-|\n||c|', {
+      extensions: [syntax],
+      htmlExtensions: [html]
+    }),
+    '<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td></td>\n<td>c</td>\n</tr>\n</tbody>\n</table>',
+    'should support empty first body cells'
+  )
+
+  t.deepEqual(
+    micromark('|a|b|\n|-|-|\n|c||', {
+      extensions: [syntax],
+      htmlExtensions: [html]
+    }),
+    '<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>c</td>\n<td></td>\n</tr>\n</tbody>\n</table>',
+    'should support empty last body cells'
+  )
+
+  t.deepEqual(
+    micromark('a|b|c\n-|-|-\nd||e', {
+      extensions: [syntax],
+      htmlExtensions: [html]
+    }),
+    '<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n<th>c</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>d</td>\n<td></td>\n<td>e</td>\n</tr>\n</tbody>\n</table>',
+    'should support empty body cells'
+  )
+
+  t.deepEqual(
+    micromark('| a |\n| - |\n- b', {
+      extensions: [syntax],
+      htmlExtensions: [html]
+    }),
+    '<table>\n<thead>\n<tr>\n<th>a</th>\n</tr>\n</thead>\n</table>\n<ul>\n<li>b</li>\n</ul>',
+    'should support a list after a table'
+  )
+
+  t.deepEqual(
+    micromark('> | a |\n| - |', {
+      extensions: [syntax],
+      htmlExtensions: [html]
+    }),
+    '<blockquote>\n<p>| a |\n| - |</p>\n</blockquote>',
+    'should not support a lazy delimiter row (1)'
+  )
+
+  t.deepEqual(
+    micromark('> a\n> | b |\n| - |', {
+      extensions: [syntax],
+      htmlExtensions: [html]
+    }),
+    '<blockquote>\n<p>a\n| b |\n| - |</p>\n</blockquote>',
+    'should not support a lazy delimiter row (2)'
+  )
+
+  t.deepEqual(
+    micromark('| a |\n> | - |', {
+      extensions: [syntax],
+      htmlExtensions: [html]
+    }),
+    '<p>| a |</p>\n<blockquote>\n<p>| - |</p>\n</blockquote>',
+    'should not support a lazy delimiter row (3)'
+  )
+
+  t.deepEqual(
+    micromark('> a\n> | b |\n|-', {
+      extensions: [syntax],
+      htmlExtensions: [html]
+    }),
+    '<blockquote>\n<p>a\n| b |\n|-</p>\n</blockquote>',
+    'should not support a lazy delimiter row (4)'
+  )
+
+  t.deepEqual(
+    micromark('> | a |\n> | - |\n| b |', {
+      extensions: [syntax],
+      htmlExtensions: [html]
+    }),
+    '<blockquote>\n<table>\n<thead>\n<tr>\n<th>a</th>\n</tr>\n</thead>\n</table>\n</blockquote>\n<p>| b |</p>',
+    'should not support a lazy body row (1)'
+  )
+
+  t.deepEqual(
+    micromark('> a\n> | b |\n> | - |\n| c |', {
+      extensions: [syntax],
+      htmlExtensions: [html]
+    }),
+    '<blockquote>\n<p>a</p>\n<table>\n<thead>\n<tr>\n<th>b</th>\n</tr>\n</thead>\n</table>\n</blockquote>\n<p>| c |</p>',
+    'should not support a lazy body row (2)'
+  )
+
+  t.deepEqual(
+    micromark('> | A |\n> | - |\n> | 1 |\n| 2 |', {
+      extensions: [syntax],
+      htmlExtensions: [html]
+    }),
+    '<blockquote>\n<table>\n<thead>\n<tr>\n<th>A</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>1</td>\n</tr>\n</tbody>\n</table>\n</blockquote>\n<p>| 2 |</p>',
+    'should not support a lazy body row (3)'
+  )
+
+  const doc = '   - d\n    - e'
+
+  t.deepEqual(
+    micromark(doc, {extensions: [syntax], htmlExtensions: [html]}),
+    micromark(doc),
+    'should not change how lists and lazyness work'
+  )
+
+  t.deepEqual(
+    micromark('| a |\n   | - |', {
+      extensions: [syntax],
+      htmlExtensions: [html]
+    }),
+    '<table>\n<thead>\n<tr>\n<th>a</th>\n</tr>\n</thead>\n</table>',
+    'should form a table if the delimiter row is indented w/ 3 spaces'
+  )
+
+  t.deepEqual(
+    micromark('| a |\n    | - |', {
+      extensions: [syntax],
+      htmlExtensions: [html]
+    }),
+    '<p>| a |\n| - |</p>',
+    'should not form a table if the delimiter row is indented w/ 4 spaces'
+  )
+
+  t.deepEqual(
+    micromark('| a |\n    | - |', {
+      extensions: [syntax, {disable: {null: ['codeIndented']}}],
+      htmlExtensions: [html]
+    }),
+    '<table>\n<thead>\n<tr>\n<th>a</th>\n</tr>\n</thead>\n</table>',
+    'should form a table if the delimiter row is indented w/ 4 spaces and indented code is turned off'
+  )
+
+  t.deepEqual(
+    micromark('| a |\n| - |\n> block quote?', {
+      extensions: [syntax],
+      htmlExtensions: [html]
+    }),
+    '<table>\n<thead>\n<tr>\n<th>a</th>\n</tr>\n</thead>\n</table>\n<blockquote>\n<p>block quote?</p>\n</blockquote>',
+    'should be interrupted by a block quote'
+  )
+
+  t.deepEqual(
+    micromark('| a |\n| - |\n>', {
+      extensions: [syntax],
+      htmlExtensions: [html]
+    }),
+    '<table>\n<thead>\n<tr>\n<th>a</th>\n</tr>\n</thead>\n</table>\n<blockquote>\n</blockquote>',
+    'should be interrupted by a block quote (empty)'
+  )
+
+  t.deepEqual(
+    micromark('| a |\n| - |\n- list?', {
+      extensions: [syntax],
+      htmlExtensions: [html]
+    }),
+    '<table>\n<thead>\n<tr>\n<th>a</th>\n</tr>\n</thead>\n</table>\n<ul>\n<li>list?</li>\n</ul>',
+    'should be interrupted by a list'
+  )
+
+  t.deepEqual(
+    micromark('| a |\n| - |\n-', {
+      extensions: [syntax],
+      htmlExtensions: [html]
+    }),
+    '<table>\n<thead>\n<tr>\n<th>a</th>\n</tr>\n</thead>\n</table>\n<ul>\n<li></li>\n</ul>',
+    'should be interrupted by a list (empty)'
+  )
+
+  t.deepEqual(
+    micromark('| a |\n| - |\n<!-- HTML? -->', {
+      allowDangerousHtml: true,
+      extensions: [syntax],
+      htmlExtensions: [html]
+    }),
+    '<table>\n<thead>\n<tr>\n<th>a</th>\n</tr>\n</thead>\n</table>\n<!-- HTML? -->',
+    'should be interrupted by HTML (flow)'
+  )
+
+  t.deepEqual(
+    micromark('| a |\n| - |\n\tcode?', {
+      allowDangerousHtml: true,
+      extensions: [syntax],
+      htmlExtensions: [html]
+    }),
+    '<table>\n<thead>\n<tr>\n<th>a</th>\n</tr>\n</thead>\n</table>\n<pre><code>code?\n</code></pre>',
+    'should be interrupted by code (indented)'
+  )
+
+  t.deepEqual(
+    micromark('| a |\n| - |\n```js\ncode?', {
+      allowDangerousHtml: true,
+      extensions: [syntax],
+      htmlExtensions: [html]
+    }),
+    '<table>\n<thead>\n<tr>\n<th>a</th>\n</tr>\n</thead>\n</table>\n<pre><code class="language-js">code?\n</code></pre>\n',
+    'should be interrupted by code (fenced)'
+  )
+
+  t.deepEqual(
+    micromark('| a |\n| - |\n***', {
+      allowDangerousHtml: true,
+      extensions: [syntax],
+      htmlExtensions: [html]
+    }),
+    '<table>\n<thead>\n<tr>\n<th>a</th>\n</tr>\n</thead>\n</table>\n<hr />',
+    'should be interrupted by a thematic break'
+  )
+
+  t.deepEqual(
+    micromark('| a |\n| - |\n# heading?', {
+      extensions: [syntax],
+      htmlExtensions: [html]
+    }),
+    '<table>\n<thead>\n<tr>\n<th>a</th>\n</tr>\n</thead>\n</table>\n<h1>heading?</h1>',
+    'should be interrupted by a heading (ATX)'
+  )
+
+  t.deepEqual(
+    micromark('| a |\n| - |\nheading\n=', {
+      extensions: [syntax],
+      htmlExtensions: [html]
+    }),
+    '<table>\n<thead>\n<tr>\n<th>a</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>heading</td>\n</tr>\n<tr>\n<td>=</td>\n</tr>\n</tbody>\n</table>',
+    'should *not* be interrupted by a heading (setext)'
+  )
+
+  t.deepEqual(
+    micromark('| a |\n| - |\nheading\n---', {
+      extensions: [syntax],
+      htmlExtensions: [html]
+    }),
+    '<table>\n<thead>\n<tr>\n<th>a</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>heading</td>\n</tr>\n</tbody>\n</table>\n<hr />',
+    'should *not* be interrupted by a heading (setext), but interrupt if the underline is also a thematic break'
+  )
+
+  // t.deepEqual(
+  //   micromark('| a |\n| - |\nheading\n-', {
+  //     extensions: [syntax],
+  //     htmlExtensions: [html]
+  //   }),
+  //   '<table>\n<thead>\n<tr>\n<th>a</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>heading</td>\n</tr>\n</tbody>\n</table>\n<ul>\n<li></li>\n</ul>',
+  //   'should *not* be interrupted by a heading (setext), but interrupt if the underline is also an empty list item bullet'
+  // )
+
+  t.end()
+})
+
+test('fixtures', async (t) => {
+  const base = new URL('fixtures/', import.meta.url)
+
+  await createGfmFixtures(base, {rehypeStringify: {closeSelfClosing: true}})
+
+  const files = fs.readdirSync(base).filter((d) => /\.md$/.test(d))
+  let index = -1
+
+  while (++index < files.length) {
+    const name = path.basename(files[index], '.md')
+    const input = fs.readFileSync(new URL(name + '.md', base))
+    let expected = String(fs.readFileSync(new URL(name + '.html', base)))
+    let actual = micromark(input, {
+      allowDangerousHtml: true,
+      allowDangerousProtocol: true,
+      extensions: [syntax],
+      htmlExtensions: [html]
+    })
+
+    if (actual && !/\n$/.test(actual)) {
+      actual += '\n'
+    }
+
+    if (name === 'some-escapes') {
+      expected = expected
+        .replace(/C \| Charlie/, 'C \\')
+        .replace(/E \\\| Echo/, 'E \\\\')
+    }
+
+    t.deepEqual(actual, expected, name)
+  }
+
+  t.end()
+})

+ 17 - 0
packages/micromark-extension-gfm-table/tsconfig.json

@@ -0,0 +1,17 @@
+{
+  "include": ["dev/**/*.js", "test/**/*.js"],
+  "compilerOptions": {
+    "target": "ES2020",
+    "lib": ["ES2020"],
+    "module": "ES2020",
+    "moduleResolution": "node",
+    "allowJs": true,
+    "checkJs": true,
+    "declaration": true,
+    "emitDeclarationOnly": true,
+    "allowSyntheticDefaultImports": true,
+    "skipLibCheck": true,
+    "strict": true,
+    "noImplicitThis": false
+  }
+}

+ 461 - 13
yarn.lock

@@ -18,6 +18,14 @@
     "@jridgewell/gen-mapping" "^0.1.0"
     "@jridgewell/trace-mapping" "^0.3.9"
 
+"@ampproject/remapping@^2.2.0":
+  version "2.2.1"
+  resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.1.tgz#99e8e11851128b8702cd57c33684f1d0f260b630"
+  integrity sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==
+  dependencies:
+    "@jridgewell/gen-mapping" "^0.3.0"
+    "@jridgewell/trace-mapping" "^0.3.9"
+
 "@apidevtools/json-schema-ref-parser@^9.0.6":
   version "9.0.9"
   resolved "https://registry.yarnpkg.com/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-9.0.9.tgz#d720f9256e3609621280584f2b47ae165359268b"
@@ -955,11 +963,44 @@
   dependencies:
     "@babel/highlight" "^7.18.6"
 
+"@babel/code-frame@^7.21.4":
+  version "7.21.4"
+  resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.21.4.tgz#d0fa9e4413aca81f2b23b9442797bda1826edb39"
+  integrity sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g==
+  dependencies:
+    "@babel/highlight" "^7.18.6"
+
 "@babel/compat-data@^7.20.5":
   version "7.20.14"
   resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.20.14.tgz#4106fc8b755f3e3ee0a0a7c27dde5de1d2b2baf8"
   integrity sha512-0YpKHD6ImkWMEINCyDAD0HLLUH/lPCefG8ld9it8DJB2wnApraKuhgYTvTY1z7UFIfBTGy5LwncZ+5HWWGbhFw==
 
+"@babel/compat-data@^7.21.4":
+  version "7.21.4"
+  resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.21.4.tgz#457ffe647c480dff59c2be092fc3acf71195c87f"
+  integrity sha512-/DYyDpeCfaVinT40FPGdkkb+lYSKvsVuMjDAG7jPOWWiM1ibOaB9CXJAlc4d1QpP/U2q2P9jbrSlClKSErd55g==
+
+"@babel/core@^7.0.0":
+  version "7.21.4"
+  resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.21.4.tgz#c6dc73242507b8e2a27fd13a9c1814f9fa34a659"
+  integrity sha512-qt/YV149Jman/6AfmlxJ04LMIu8bMoyl3RB91yTFrxQmgbrSvQMy7cI8Q62FHx1t8wJ8B5fu0UDoLwHAhUo1QA==
+  dependencies:
+    "@ampproject/remapping" "^2.2.0"
+    "@babel/code-frame" "^7.21.4"
+    "@babel/generator" "^7.21.4"
+    "@babel/helper-compilation-targets" "^7.21.4"
+    "@babel/helper-module-transforms" "^7.21.2"
+    "@babel/helpers" "^7.21.0"
+    "@babel/parser" "^7.21.4"
+    "@babel/template" "^7.20.7"
+    "@babel/traverse" "^7.21.4"
+    "@babel/types" "^7.21.4"
+    convert-source-map "^1.7.0"
+    debug "^4.1.0"
+    gensync "^1.0.0-beta.2"
+    json5 "^2.2.2"
+    semver "^6.3.0"
+
 "@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.13.15", "@babel/core@^7.20.12":
   version "7.20.12"
   resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.20.12.tgz#7930db57443c6714ad216953d1356dac0eb8496d"
@@ -990,6 +1031,16 @@
     "@jridgewell/gen-mapping" "^0.3.2"
     jsesc "^2.5.1"
 
+"@babel/generator@^7.21.4":
+  version "7.21.4"
+  resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.21.4.tgz#64a94b7448989f421f919d5239ef553b37bb26bc"
+  integrity sha512-NieM3pVIYW2SwGzKoqfPrQsf4xGs9M9AIG3ThppsSRmO+m7eQhmI6amajKMUeIO37wFfsvnvcxQFx6x6iqxDnA==
+  dependencies:
+    "@babel/types" "^7.21.4"
+    "@jridgewell/gen-mapping" "^0.3.2"
+    "@jridgewell/trace-mapping" "^0.3.17"
+    jsesc "^2.5.1"
+
 "@babel/helper-compilation-targets@^7.20.7":
   version "7.20.7"
   resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz#a6cd33e93629f5eb473b021aac05df62c4cd09bb"
@@ -1001,6 +1052,17 @@
     lru-cache "^5.1.1"
     semver "^6.3.0"
 
+"@babel/helper-compilation-targets@^7.21.4":
+  version "7.21.4"
+  resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.21.4.tgz#770cd1ce0889097ceacb99418ee6934ef0572656"
+  integrity sha512-Fa0tTuOXZ1iL8IeDFUWCzjZcn+sJGd9RZdH9esYVjEejGmzf+FFYQpMi/kZUk2kPy/q1H3/GPw7np8qar/stfg==
+  dependencies:
+    "@babel/compat-data" "^7.21.4"
+    "@babel/helper-validator-option" "^7.21.0"
+    browserslist "^4.21.3"
+    lru-cache "^5.1.1"
+    semver "^6.3.0"
+
 "@babel/helper-environment-visitor@^7.18.9":
   version "7.18.9"
   resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz#0c0cee9b35d2ca190478756865bb3528422f51be"
@@ -1014,6 +1076,14 @@
     "@babel/template" "^7.18.10"
     "@babel/types" "^7.19.0"
 
+"@babel/helper-function-name@^7.21.0":
+  version "7.21.0"
+  resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz#d552829b10ea9f120969304023cd0645fa00b1b4"
+  integrity sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==
+  dependencies:
+    "@babel/template" "^7.20.7"
+    "@babel/types" "^7.21.0"
+
 "@babel/helper-hoist-variables@^7.18.6":
   version "7.18.6"
   resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz#d4d2c8fb4baeaa5c68b99cc8245c56554f926678"
@@ -1042,6 +1112,20 @@
     "@babel/traverse" "^7.20.10"
     "@babel/types" "^7.20.7"
 
+"@babel/helper-module-transforms@^7.21.2":
+  version "7.21.2"
+  resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz#160caafa4978ac8c00ac66636cb0fa37b024e2d2"
+  integrity sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ==
+  dependencies:
+    "@babel/helper-environment-visitor" "^7.18.9"
+    "@babel/helper-module-imports" "^7.18.6"
+    "@babel/helper-simple-access" "^7.20.2"
+    "@babel/helper-split-export-declaration" "^7.18.6"
+    "@babel/helper-validator-identifier" "^7.19.1"
+    "@babel/template" "^7.20.7"
+    "@babel/traverse" "^7.21.2"
+    "@babel/types" "^7.21.2"
+
 "@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.19.0", "@babel/helper-plugin-utils@^7.8.0":
   version "7.20.2"
   resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz#d1b9000752b18d0877cff85a5c376ce5c3121629"
@@ -1076,6 +1160,11 @@
   resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz#bf0d2b5a509b1f336099e4ff36e1a63aa5db4db8"
   integrity sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==
 
+"@babel/helper-validator-option@^7.21.0":
+  version "7.21.0"
+  resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz#8224c7e13ace4bafdc4004da2cf064ef42673180"
+  integrity sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==
+
 "@babel/helpers@^7.20.7":
   version "7.20.13"
   resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.20.13.tgz#e3cb731fb70dc5337134cadc24cbbad31cc87ad2"
@@ -1085,6 +1174,15 @@
     "@babel/traverse" "^7.20.13"
     "@babel/types" "^7.20.7"
 
+"@babel/helpers@^7.21.0":
+  version "7.21.0"
+  resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.21.0.tgz#9dd184fb5599862037917cdc9eecb84577dc4e7e"
+  integrity sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA==
+  dependencies:
+    "@babel/template" "^7.20.7"
+    "@babel/traverse" "^7.21.0"
+    "@babel/types" "^7.21.0"
+
 "@babel/highlight@^7.18.6":
   version "7.18.6"
   resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf"
@@ -1099,6 +1197,11 @@
   resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.20.15.tgz#eec9f36d8eaf0948bb88c87a46784b5ee9fd0c89"
   integrity sha512-DI4a1oZuf8wC+oAJA9RW6ga3Zbe8RZFt7kD9i4qAspz3I/yHet1VvC3DiSy/fsUvv5pvJuNPh0LPOdCcqinDPg==
 
+"@babel/parser@^7.21.4":
+  version "7.21.4"
+  resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.21.4.tgz#94003fdfc520bbe2875d4ae557b43ddb6d880f17"
+  integrity sha512-alVJj7k7zIxqBZ7BTRhz0IqJFxW1VJbm6N8JbcYhQ186df9ZBPbZBmWSqAMXwHGsCJdYks7z/voa3ibiS5bCIw==
+
 "@babel/plugin-syntax-async-generators@^7.8.4":
   version "7.8.4"
   resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d"
@@ -1251,6 +1354,22 @@
     debug "^4.1.0"
     globals "^11.1.0"
 
+"@babel/traverse@^7.21.0", "@babel/traverse@^7.21.2", "@babel/traverse@^7.21.4":
+  version "7.21.4"
+  resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.21.4.tgz#a836aca7b116634e97a6ed99976236b3282c9d36"
+  integrity sha512-eyKrRHKdyZxqDm+fV1iqL9UAHMoIg0nDaGqfIOd8rKH17m5snv7Gn4qgjBoFfLz9APvjFU/ICT00NVCv1Epp8Q==
+  dependencies:
+    "@babel/code-frame" "^7.21.4"
+    "@babel/generator" "^7.21.4"
+    "@babel/helper-environment-visitor" "^7.18.9"
+    "@babel/helper-function-name" "^7.21.0"
+    "@babel/helper-hoist-variables" "^7.18.6"
+    "@babel/helper-split-export-declaration" "^7.18.6"
+    "@babel/parser" "^7.21.4"
+    "@babel/types" "^7.21.4"
+    debug "^4.1.0"
+    globals "^11.1.0"
+
 "@babel/types@^7.0.0", "@babel/types@^7.13.17", "@babel/types@^7.18.6", "@babel/types@^7.19.0", "@babel/types@^7.20.2", "@babel/types@^7.20.7", "@babel/types@^7.3.0", "@babel/types@^7.3.3":
   version "7.20.7"
   resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.20.7.tgz#54ec75e252318423fc07fb644dc6a58a64c09b7f"
@@ -1260,6 +1379,15 @@
     "@babel/helper-validator-identifier" "^7.19.1"
     to-fast-properties "^2.0.0"
 
+"@babel/types@^7.21.0", "@babel/types@^7.21.2", "@babel/types@^7.21.4":
+  version "7.21.4"
+  resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.21.4.tgz#2d5d6bb7908699b3b416409ffd3b5daa25b030d4"
+  integrity sha512-rU2oY501qDxE8Pyo7i/Orqma4ziCOrby0/9mvbDUGEfvZjb279Nk9k19e2fiCxHbRRpY2ZyrgW1eq22mvmOIzA==
+  dependencies:
+    "@babel/helper-string-parser" "^7.19.4"
+    "@babel/helper-validator-identifier" "^7.19.1"
+    to-fast-properties "^2.0.0"
+
 "@bcoe/v8-coverage@^0.2.3":
   version "0.2.3"
   resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
@@ -1856,6 +1984,15 @@
     "@jridgewell/set-array" "^1.0.0"
     "@jridgewell/sourcemap-codec" "^1.4.10"
 
+"@jridgewell/gen-mapping@^0.3.0":
+  version "0.3.3"
+  resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz#7e02e6eb5df901aaedb08514203b096614024098"
+  integrity sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==
+  dependencies:
+    "@jridgewell/set-array" "^1.0.1"
+    "@jridgewell/sourcemap-codec" "^1.4.10"
+    "@jridgewell/trace-mapping" "^0.3.9"
+
 "@jridgewell/gen-mapping@^0.3.2":
   version "0.3.2"
   resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz#c1aedc61e853f2bb9f5dfe6d4442d3b565b253b9"
@@ -1865,6 +2002,11 @@
     "@jridgewell/sourcemap-codec" "^1.4.10"
     "@jridgewell/trace-mapping" "^0.3.9"
 
+"@jridgewell/resolve-uri@3.1.0":
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78"
+  integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==
+
 "@jridgewell/resolve-uri@^3.0.3":
   version "3.0.8"
   resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.0.8.tgz#687cc2bbf243f4e9a868ecf2262318e2658873a1"
@@ -1875,7 +2017,7 @@
   resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72"
   integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==
 
-"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.13":
+"@jridgewell/sourcemap-codec@1.4.14", "@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.13":
   version "1.4.14"
   resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24"
   integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==
@@ -1896,6 +2038,14 @@
     "@jridgewell/resolve-uri" "^3.0.3"
     "@jridgewell/sourcemap-codec" "^1.4.10"
 
+"@jridgewell/trace-mapping@^0.3.17":
+  version "0.3.18"
+  resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz#25783b2086daf6ff1dcb53c9249ae480e4dd4cd6"
+  integrity sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==
+  dependencies:
+    "@jridgewell/resolve-uri" "3.1.0"
+    "@jridgewell/sourcemap-codec" "1.4.14"
+
 "@jsdevtools/ono@^7.1.3":
   version "7.1.3"
   resolved "https://registry.yarnpkg.com/@jsdevtools/ono/-/ono-7.1.3.tgz#9df03bbd7c696a5c58885c34aa06da41c8543796"
@@ -2231,6 +2381,13 @@
   dependencies:
     "@octokit/types" "^6.0.3"
 
+"@octokit/auth-token@^3.0.0":
+  version "3.0.3"
+  resolved "https://registry.yarnpkg.com/@octokit/auth-token/-/auth-token-3.0.3.tgz#ce7e48a3166731f26068d7a7a7996b5da58cbe0c"
+  integrity sha512-/aFM2M4HVDBT/jjDBa84sJniv1t9Gm/rLkalaz9htOm+L+8JMj1k9w0CkUdcxNyNxZPlTxKPVko+m1VlM58ZVA==
+  dependencies:
+    "@octokit/types" "^9.0.0"
+
 "@octokit/core@^2.4.3":
   version "2.5.4"
   resolved "https://registry.yarnpkg.com/@octokit/core/-/core-2.5.4.tgz#f7fbf8e4f86c5cc2497a8887ba2561ec8d358054"
@@ -2243,6 +2400,19 @@
     before-after-hook "^2.1.0"
     universal-user-agent "^5.0.0"
 
+"@octokit/core@^4.1.0":
+  version "4.2.0"
+  resolved "https://registry.yarnpkg.com/@octokit/core/-/core-4.2.0.tgz#8c253ba9605aca605bc46187c34fcccae6a96648"
+  integrity sha512-AgvDRUg3COpR82P7PBdGZF/NNqGmtMq2NiPqeSsDIeCfYFOZ9gddqWNQHnFdEUf+YwOj4aZYmJnlPp7OXmDIDg==
+  dependencies:
+    "@octokit/auth-token" "^3.0.0"
+    "@octokit/graphql" "^5.0.0"
+    "@octokit/request" "^6.0.0"
+    "@octokit/request-error" "^3.0.0"
+    "@octokit/types" "^9.0.0"
+    before-after-hook "^2.2.0"
+    universal-user-agent "^6.0.0"
+
 "@octokit/endpoint@^6.0.1":
   version "6.0.11"
   resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-6.0.11.tgz#082adc2aebca6dcefa1fb383f5efb3ed081949d1"
@@ -2252,6 +2422,15 @@
     is-plain-object "^5.0.0"
     universal-user-agent "^6.0.0"
 
+"@octokit/endpoint@^7.0.0":
+  version "7.0.5"
+  resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-7.0.5.tgz#2bb2a911c12c50f10014183f5d596ce30ac67dd1"
+  integrity sha512-LG4o4HMY1Xoaec87IqQ41TQ+glvIeTKqfjkCEmt5AIwDZJwQeVZFIEYXrYY6yLwK+pAScb9Gj4q+Nz2qSw1roA==
+  dependencies:
+    "@octokit/types" "^9.0.0"
+    is-plain-object "^5.0.0"
+    universal-user-agent "^6.0.0"
+
 "@octokit/graphql@^4.3.1":
   version "4.8.0"
   resolved "https://registry.yarnpkg.com/@octokit/graphql/-/graphql-4.8.0.tgz#664d9b11c0e12112cbf78e10f49a05959aa22cc3"
@@ -2261,11 +2440,25 @@
     "@octokit/types" "^6.0.3"
     universal-user-agent "^6.0.0"
 
+"@octokit/graphql@^5.0.0":
+  version "5.0.5"
+  resolved "https://registry.yarnpkg.com/@octokit/graphql/-/graphql-5.0.5.tgz#a4cb3ea73f83b861893a6370ee82abb36e81afd2"
+  integrity sha512-Qwfvh3xdqKtIznjX9lz2D458r7dJPP8l6r4GQkIdWQouZwHQK0mVT88uwiU2bdTU2OtT1uOlKpRciUWldpG0yQ==
+  dependencies:
+    "@octokit/request" "^6.0.0"
+    "@octokit/types" "^9.0.0"
+    universal-user-agent "^6.0.0"
+
 "@octokit/openapi-types@^10.0.0":
   version "10.0.0"
   resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-10.0.0.tgz#db4335de99509021f501fc4e026e6ff495fe1e62"
   integrity sha512-k1iO2zKuEjjRS1EJb4FwSLk+iF6EGp+ZV0OMRViQoWhQ1fZTk9hg1xccZII5uyYoiqcbC73MRBmT45y1vp2PPg==
 
+"@octokit/openapi-types@^17.0.0":
+  version "17.0.0"
+  resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-17.0.0.tgz#7356d287f48b20e9a1f497ef8dfaabdff9cf8622"
+  integrity sha512-V8BVJGN0ZmMlURF55VFHFd/L92XQQ43KvFjNmY1IYbCN3V/h/uUFV6iQi19WEHM395Nn+1qhUbViCAD/1czzog==
+
 "@octokit/plugin-paginate-rest@^2.2.0":
   version "2.16.0"
   resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.16.0.tgz#09dbda2e5fbca022e3cdf76b63618f7b357c9f0c"
@@ -2273,7 +2466,14 @@
   dependencies:
     "@octokit/types" "^6.26.0"
 
-"@octokit/plugin-request-log@^1.0.0":
+"@octokit/plugin-paginate-rest@^6.0.0":
+  version "6.0.0"
+  resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-6.0.0.tgz#f34b5a7d9416019126042cd7d7b811e006c0d561"
+  integrity sha512-Sq5VU1PfT6/JyuXPyt04KZNVsFOSBaYOAq2QRZUwzVlI10KFvcbUo8lR258AAQL1Et60b0WuVik+zOWKLuDZxw==
+  dependencies:
+    "@octokit/types" "^9.0.0"
+
+"@octokit/plugin-request-log@^1.0.0", "@octokit/plugin-request-log@^1.0.4":
   version "1.0.4"
   resolved "https://registry.yarnpkg.com/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz#5e50ed7083a613816b1e4a28aeec5fb7f1462e85"
   integrity sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA==
@@ -2286,6 +2486,14 @@
     "@octokit/types" "^4.1.6"
     deprecation "^2.3.1"
 
+"@octokit/plugin-rest-endpoint-methods@^7.0.0":
+  version "7.0.1"
+  resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-7.0.1.tgz#f7ebe18144fd89460f98f35a587b056646e84502"
+  integrity sha512-pnCaLwZBudK5xCdrR823xHGNgqOzRnJ/mpC/76YPpNP7DybdsJtP7mdOwh+wYZxK5jqeQuhu59ogMI4NRlBUvA==
+  dependencies:
+    "@octokit/types" "^9.0.0"
+    deprecation "^2.3.1"
+
 "@octokit/request-error@^2.1.0":
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-2.1.0.tgz#9e150357831bfc788d13a4fd4b1913d60c74d677"
@@ -2295,6 +2503,15 @@
     deprecation "^2.0.0"
     once "^1.4.0"
 
+"@octokit/request-error@^3.0.0":
+  version "3.0.3"
+  resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-3.0.3.tgz#ef3dd08b8e964e53e55d471acfe00baa892b9c69"
+  integrity sha512-crqw3V5Iy2uOU5Np+8M/YexTlT8zxCfI+qu+LxUB7SZpje4Qmx3mub5DfEKSO8Ylyk0aogi6TYdf6kxzh2BguQ==
+  dependencies:
+    "@octokit/types" "^9.0.0"
+    deprecation "^2.0.0"
+    once "^1.4.0"
+
 "@octokit/request@^5.4.0", "@octokit/request@^5.6.0":
   version "5.6.1"
   resolved "https://registry.yarnpkg.com/@octokit/request/-/request-5.6.1.tgz#f97aff075c37ab1d427c49082fefeef0dba2d8ce"
@@ -2307,6 +2524,18 @@
     node-fetch "^2.6.1"
     universal-user-agent "^6.0.0"
 
+"@octokit/request@^6.0.0":
+  version "6.2.3"
+  resolved "https://registry.yarnpkg.com/@octokit/request/-/request-6.2.3.tgz#76d5d6d44da5c8d406620a4c285d280ae310bdb4"
+  integrity sha512-TNAodj5yNzrrZ/VxP+H5HiYaZep0H3GU0O7PaF+fhDrt8FPrnkei9Aal/txsN/1P7V3CPiThG0tIvpPDYUsyAA==
+  dependencies:
+    "@octokit/endpoint" "^7.0.0"
+    "@octokit/request-error" "^3.0.0"
+    "@octokit/types" "^9.0.0"
+    is-plain-object "^5.0.0"
+    node-fetch "^2.6.7"
+    universal-user-agent "^6.0.0"
+
 "@octokit/rest@^17.0.0":
   version "17.11.2"
   resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-17.11.2.tgz#f3dbd46f9f06361c646230fd0ef8598e59183ead"
@@ -2317,6 +2546,16 @@
     "@octokit/plugin-request-log" "^1.0.0"
     "@octokit/plugin-rest-endpoint-methods" "3.17.0"
 
+"@octokit/rest@^19.0.0":
+  version "19.0.7"
+  resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-19.0.7.tgz#d2e21b4995ab96ae5bfae50b4969da7e04e0bb70"
+  integrity sha512-HRtSfjrWmWVNp2uAkEpQnuGMJsu/+dBr47dRc5QVgsCbnIc1+GFEaoKBWkYG+zjrsHpSqcAElMio+n10c0b5JA==
+  dependencies:
+    "@octokit/core" "^4.1.0"
+    "@octokit/plugin-paginate-rest" "^6.0.0"
+    "@octokit/plugin-request-log" "^1.0.4"
+    "@octokit/plugin-rest-endpoint-methods" "^7.0.0"
+
 "@octokit/types@^4.1.6":
   version "4.1.10"
   resolved "https://registry.yarnpkg.com/@octokit/types/-/types-4.1.10.tgz#e4029c11e2cc1335051775bc1600e7e740e4aca4"
@@ -2338,6 +2577,13 @@
   dependencies:
     "@octokit/openapi-types" "^10.0.0"
 
+"@octokit/types@^9.0.0":
+  version "9.1.2"
+  resolved "https://registry.yarnpkg.com/@octokit/types/-/types-9.1.2.tgz#1a8d35b1f4a3d2ad386e223f249dd5f7506979c1"
+  integrity sha512-LPbJIuu1WNoRHbN4UMysEdlissRFpTCWyoKT7kHPufI8T+XX33/qilfMWJo3mCOjNIKu0+43oSQPf+HJa0+TTQ==
+  dependencies:
+    "@octokit/openapi-types" "^17.0.0"
+
 "@pkgr/utils@^2.2.0":
   version "2.3.0"
   resolved "https://registry.yarnpkg.com/@pkgr/utils/-/utils-2.3.0.tgz#3b8491f112a80839450498816767eb03b7db6139"
@@ -2882,6 +3128,17 @@
   resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-4.2.2.tgz#ed4e0ad92306a704f9fb132a0cfcf77486dbe2bc"
   integrity sha512-HnYpAE1Y6kRyKM/XkEuiRQhTHvkzMBurTHnpFLYLBGPIylZNPs9jJcuOOYWxPLJCSEtmZT0Y8rHDokKN7rRTig==
 
+"@types/babel__core@^7.0.0":
+  version "7.20.0"
+  resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.0.tgz#61bc5a4cae505ce98e1e36c5445e4bee060d8891"
+  integrity sha512-+n8dL/9GWblDO0iU6eZAwEIJVr5DWigtle+Q6HLOrh/pdbXOhOtqzq8VPPE2zvNJzSKY4vH/z3iT3tn0A3ypiQ==
+  dependencies:
+    "@babel/parser" "^7.20.7"
+    "@babel/types" "^7.20.7"
+    "@types/babel__generator" "*"
+    "@types/babel__template" "*"
+    "@types/babel__traverse" "*"
+
 "@types/babel__core@^7.1.14":
   version "7.1.15"
   resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.15.tgz#2ccfb1ad55a02c83f8e0ad327cbc332f55eb1024"
@@ -3224,6 +3481,11 @@
     "@types/scheduler" "*"
     csstype "^3.0.2"
 
+"@types/resolve@^1.0.0":
+  version "1.20.2"
+  resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-1.20.2.tgz#97d26e00cd4a0423b4af620abecf3e6f442b7975"
+  integrity sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==
+
 "@types/retry@^0.12.0":
   version "0.12.0"
   resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d"
@@ -4029,6 +4291,17 @@ babel-loader@^8.2.2, babel-loader@^8.2.5:
     make-dir "^3.1.0"
     schema-utils "^2.6.5"
 
+babel-plugin-inline-constants@^4.0.0:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-inline-constants/-/babel-plugin-inline-constants-4.1.0.tgz#5c57e8086f8fbac94021a1aad9a91feb213b8c20"
+  integrity sha512-4sMw2JqBsQ9J4dxZBgEvfqS/PhsvbhVNFYBe66VraFLVuc4TaST1X4qZjGceBQsh9M6Ilx74GrXSolCtYVftDA==
+  dependencies:
+    "@babel/types" "^7.0.0"
+    "@types/babel__core" "^7.0.0"
+    "@types/resolve" "^1.0.0"
+    import-meta-resolve "^2.0.0"
+    resolve "^1.0.0"
+
 babel-plugin-istanbul@^6.1.1:
   version "6.1.1"
   resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz#fa88ec59232fd9b4e36dbbc540a8ec9a9b47da73"
@@ -4059,6 +4332,18 @@ babel-plugin-superjson-next@^0.4.2:
     "@babel/types" "^7.13.17"
     hoist-non-react-statics "^3.3.2"
 
+babel-plugin-unassert@^3.2.0:
+  version "3.2.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-unassert/-/babel-plugin-unassert-3.2.0.tgz#4ea8f65709905cc540627baf4ce4c837281a317d"
+  integrity sha512-dNeuFtaJ1zNDr59r24NjjIm4SsXXm409iNOVMIERp6ePciII+rTrdwsWcHDqDFUKpOoBNT4ZS63nPEbrANW7DQ==
+
+babel-plugin-undebug@^2.0.0:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/babel-plugin-undebug/-/babel-plugin-undebug-2.0.1.tgz#48dc76e5f7c51ade771cef1f08c2a05498a6b349"
+  integrity sha512-QXStD4aw+RMICmjHkJo4GHXivHpy8LI6T8vuD1AWF044ld6/Gb0ozE5FSBLjhWUGgqNHr0fnnmMc1+RlJgp50w==
+  dependencies:
+    "@types/babel__core" "^7.0.0"
+
 babel-preset-current-node-syntax@^1.0.0:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz#b4399239b89b2a011f9ddbe3e4f401fc40cff73b"
@@ -4178,6 +4463,11 @@ before-after-hook@^2.1.0:
   resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.2.2.tgz#a6e8ca41028d90ee2c24222f201c90956091613e"
   integrity sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ==
 
+before-after-hook@^2.2.0:
+  version "2.2.3"
+  resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.2.3.tgz#c51e809c81a4e354084422b9b26bad88249c517c"
+  integrity sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==
+
 better-ajv-errors@^0.5.2:
   version "0.5.7"
   resolved "https://registry.yarnpkg.com/better-ajv-errors/-/better-ajv-errors-0.5.7.tgz#246123954161cc0ef124761c55a121c96b0cdce0"
@@ -5265,6 +5555,11 @@ content-type@~1.0.4:
   version "1.0.4"
   resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b"
 
+control-pictures@^2.0.0:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/control-pictures/-/control-pictures-2.0.1.tgz#00a74b6e008618413ef86c73aeeb606152b6ff74"
+  integrity sha512-pJEWxGxTWzMLBeErdOZHno1029Yn+BSzirlyku5JZ60+X+h4Z7XxQdhDLgN+7DF1hEdkhSFEmLJtkpsS6arnPQ==
+
 conventional-changelog-angular@^5.0.6:
   version "5.0.12"
   resolved "https://registry.yarnpkg.com/conventional-changelog-angular/-/conventional-changelog-angular-5.0.12.tgz#c979b8b921cbfe26402eb3da5bbfda02d865a2b9"
@@ -5478,6 +5773,25 @@ crc32-stream@^4.0.2:
     crc-32 "^1.2.0"
     readable-stream "^3.4.0"
 
+create-gfm-fixtures@^1.0.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/create-gfm-fixtures/-/create-gfm-fixtures-1.1.0.tgz#93dc4001d1a1af5a26b32cfdd1c1d2d5f8bdf9b9"
+  integrity sha512-aXuki5L0vIbhlZ+NwSivXA0VJiyyQlnC8UgcfEuJNHK/aQmXzoXUmw9+yBrmcEzB5hK5+kA/qg1u1emucgth2A==
+  dependencies:
+    "@octokit/rest" "^19.0.0"
+    control-pictures "^2.0.0"
+    globby "^13.0.0"
+    hast-util-heading-rank "^2.0.0"
+    hast-util-select "^5.0.0"
+    hast-util-to-string "^2.0.0"
+    hast-util-whitespace "^2.0.0"
+    node-fetch "^3.0.0"
+    rehype-parse "^8.0.0"
+    rehype-stringify "^9.0.0"
+    replace-ext "^2.0.0"
+    unified "^10.0.0"
+    unist-util-visit "^4.0.0"
+
 create-react-context@^0.1.5:
   version "0.1.6"
   resolved "https://registry.yarnpkg.com/create-react-context/-/create-react-context-0.1.6.tgz#0f425931d907741127acc6e31acb4f9015dd9fdc"
@@ -5649,6 +5963,11 @@ dashdash@^1.12.0:
   dependencies:
     assert-plus "^1.0.0"
 
+data-uri-to-buffer@^4.0.0:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz#d8feb2b2881e6a4f58c2e08acfd0e2834e26222e"
+  integrity sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==
+
 date-and-time@^2.0.0:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/date-and-time/-/date-and-time-2.0.1.tgz#bc8b72704980e8a0979bb186118d30d02059ef04"
@@ -7036,6 +7355,14 @@ fd-slicer@~1.1.0:
   dependencies:
     pend "~1.2.0"
 
+fetch-blob@^3.1.2, fetch-blob@^3.1.4:
+  version "3.2.0"
+  resolved "https://registry.yarnpkg.com/fetch-blob/-/fetch-blob-3.2.0.tgz#f09b8d4bbd45adc6f0c20b7e787e793e309dcce9"
+  integrity sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==
+  dependencies:
+    node-domexception "^1.0.0"
+    web-streams-polyfill "^3.0.3"
+
 figlet@^1.1.1:
   version "1.5.0"
   resolved "https://registry.yarnpkg.com/figlet/-/figlet-1.5.0.tgz#2db4d00a584e5155a96080632db919213c3e003c"
@@ -7290,6 +7617,13 @@ format@^0.2.0:
   resolved "https://registry.yarnpkg.com/format/-/format-0.2.2.tgz#d6170107e9efdc4ed30c9dc39016df942b5cb58b"
   integrity sha1-1hcBB+nv3E7TDJ3DkBbflCtctYs=
 
+formdata-polyfill@^4.0.10:
+  version "4.0.10"
+  resolved "https://registry.yarnpkg.com/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz#24807c31c9d402e002ab3d8c720144ceb8848423"
+  integrity sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==
+  dependencies:
+    fetch-blob "^3.1.2"
+
 forwarded@~0.1.2:
   version "0.1.2"
   resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84"
@@ -7692,7 +8026,7 @@ glob@^7.0.0, glob@^7.0.5, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@^7.2.0:
     once "^1.3.0"
     path-is-absolute "^1.0.0"
 
-glob@^8.1.0:
+glob@^8.0.0, glob@^8.1.0:
   version "8.1.0"
   resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e"
   integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==
@@ -7799,6 +8133,17 @@ globby@^11.0.1, globby@^11.0.2, globby@^11.0.4, globby@^11.1.0:
     merge2 "^1.4.1"
     slash "^3.0.0"
 
+globby@^13.0.0:
+  version "13.1.4"
+  resolved "https://registry.yarnpkg.com/globby/-/globby-13.1.4.tgz#2f91c116066bcec152465ba36e5caa4a13c01317"
+  integrity sha512-iui/IiiW+QrJ1X1hKH5qwlMQyv34wJAYwH1vrf8b9kBA4sNiif3gKsMHa+BrdnOpEudWjpotfa7LrTzB1ERS/g==
+  dependencies:
+    dir-glob "^3.0.1"
+    fast-glob "^3.2.11"
+    ignore "^5.2.0"
+    merge2 "^1.4.1"
+    slash "^4.0.0"
+
 globby@^13.1.2:
   version "13.1.2"
   resolved "https://registry.yarnpkg.com/globby/-/globby-13.1.2.tgz#29047105582427ab6eca4f905200667b056da515"
@@ -8075,6 +8420,23 @@ hast-util-parse-selector@^3.0.0:
   dependencies:
     "@types/hast" "^2.0.0"
 
+hast-util-raw@^7.0.0:
+  version "7.2.3"
+  resolved "https://registry.yarnpkg.com/hast-util-raw/-/hast-util-raw-7.2.3.tgz#dcb5b22a22073436dbdc4aa09660a644f4991d99"
+  integrity sha512-RujVQfVsOrxzPOPSzZFiwofMArbQke6DJjnFfceiEbFh7S05CbPt0cYN+A5YeD3pso0JQk6O1aHBnx9+Pm2uqg==
+  dependencies:
+    "@types/hast" "^2.0.0"
+    "@types/parse5" "^6.0.0"
+    hast-util-from-parse5 "^7.0.0"
+    hast-util-to-parse5 "^7.0.0"
+    html-void-elements "^2.0.0"
+    parse5 "^6.0.0"
+    unist-util-position "^4.0.0"
+    unist-util-visit "^4.0.0"
+    vfile "^5.0.0"
+    web-namespaces "^2.0.0"
+    zwitch "^2.0.0"
+
 hast-util-raw@^7.2.0:
   version "7.2.1"
   resolved "https://registry.yarnpkg.com/hast-util-raw/-/hast-util-raw-7.2.1.tgz#6e964cee098dbdd93d1b77cf180b5827d48048ab"
@@ -8099,6 +8461,27 @@ hast-util-sanitize@^4.0.0:
   dependencies:
     "@types/hast" "^2.0.0"
 
+hast-util-select@^5.0.0:
+  version "5.0.5"
+  resolved "https://registry.yarnpkg.com/hast-util-select/-/hast-util-select-5.0.5.tgz#be9ccb71d2278681ca024727f12abd4f93b3e9bc"
+  integrity sha512-QQhWMhgTFRhCaQdgTKzZ5g31GLQ9qRb1hZtDPMqQaOhpLBziWcshUS0uCR5IJ0U1jrK/mxg35fmcq+Dp/Cy2Aw==
+  dependencies:
+    "@types/hast" "^2.0.0"
+    "@types/unist" "^2.0.0"
+    bcp-47-match "^2.0.0"
+    comma-separated-tokens "^2.0.0"
+    css-selector-parser "^1.0.0"
+    direction "^2.0.0"
+    hast-util-has-property "^2.0.0"
+    hast-util-to-string "^2.0.0"
+    hast-util-whitespace "^2.0.0"
+    not "^0.1.0"
+    nth-check "^2.0.0"
+    property-information "^6.0.0"
+    space-separated-tokens "^2.0.0"
+    unist-util-visit "^4.0.0"
+    zwitch "^2.0.0"
+
 hast-util-select@^5.0.2, hast-util-select@~5.0.1:
   version "5.0.2"
   resolved "https://registry.yarnpkg.com/hast-util-select/-/hast-util-select-5.0.2.tgz#8c603ebacf0f47e154c5fa2e5b7efc520813866b"
@@ -8121,6 +8504,23 @@ hast-util-select@^5.0.2, hast-util-select@~5.0.1:
     unist-util-visit "^4.0.0"
     zwitch "^2.0.0"
 
+hast-util-to-html@^8.0.0:
+  version "8.0.4"
+  resolved "https://registry.yarnpkg.com/hast-util-to-html/-/hast-util-to-html-8.0.4.tgz#0269ef33fa3f6599b260a8dc94f733b8e39e41fc"
+  integrity sha512-4tpQTUOr9BMjtYyNlt0P50mH7xj0Ks2xpo8M943Vykljf99HW6EzulIoJP1N3eKOSScEHzyzi9dm7/cn0RfGwA==
+  dependencies:
+    "@types/hast" "^2.0.0"
+    "@types/unist" "^2.0.0"
+    ccount "^2.0.0"
+    comma-separated-tokens "^2.0.0"
+    hast-util-raw "^7.0.0"
+    hast-util-whitespace "^2.0.0"
+    html-void-elements "^2.0.0"
+    property-information "^6.0.0"
+    space-separated-tokens "^2.0.0"
+    stringify-entities "^4.0.0"
+    zwitch "^2.0.4"
+
 hast-util-to-parse5@^7.0.0:
   version "7.0.0"
   resolved "https://registry.yarnpkg.com/hast-util-to-parse5/-/hast-util-to-parse5-7.0.0.tgz#a39808e69005d10afeed1866029a1fb137df3f7c"
@@ -8464,6 +8864,11 @@ import-local@^3.0.2:
     pkg-dir "^4.2.0"
     resolve-cwd "^3.0.0"
 
+import-meta-resolve@^2.0.0:
+  version "2.2.2"
+  resolved "https://registry.yarnpkg.com/import-meta-resolve/-/import-meta-resolve-2.2.2.tgz#75237301e72d1f0fbd74dbc6cca9324b164c2cc9"
+  integrity sha512-f8KcQ1D80V7RnqVm+/lirO9zkOxjGxhaTC1IPrBGd3MEfNgmNG67tSUO9gTi2F3Blr2Az6g1vocaxzkVnWl9MA==
+
 imurmurhash@^0.1.4:
   version "0.1.4"
   resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
@@ -10735,6 +11140,18 @@ mhchemparser@^4.1.0:
   resolved "https://registry.yarnpkg.com/mhchemparser/-/mhchemparser-4.1.1.tgz#a2142fdab37a02ec8d1b48a445059287790becd5"
   integrity sha512-R75CUN6O6e1t8bgailrF1qPq+HhVeFTM3XQ0uzI+mXTybmphy3b6h4NbLOYhemViQ3lUs+6CKRkC3Ws1TlYREA==
 
+micromark-build@^1.0.0:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/micromark-build/-/micromark-build-1.3.0.tgz#29d78fe3d2b61e86c2e9ca1272b67adc1e337e11"
+  integrity sha512-5ou2lgc/x79OfAce8yANDiBTXoeFEalRKJFtJPXLkX+Ua31jf1rzF0K/+9FS7u5mT+qL7z0fadia8Im6Sj/hxg==
+  dependencies:
+    "@babel/core" "^7.0.0"
+    babel-plugin-inline-constants "^4.0.0"
+    babel-plugin-unassert "^3.2.0"
+    babel-plugin-undebug "^2.0.0"
+    glob "^8.0.0"
+    import-meta-resolve "^2.0.0"
+
 micromark-core-commonmark@^1.0.0, micromark-core-commonmark@^1.0.1:
   version "1.0.6"
   resolved "https://registry.yarnpkg.com/micromark-core-commonmark/-/micromark-core-commonmark-1.0.6.tgz#edff4c72e5993d93724a3c206970f5a15b0585ad"
@@ -10794,16 +11211,9 @@ micromark-extension-gfm-strikethrough@^1.0.0:
     micromark-util-types "^1.0.0"
     uvu "^0.5.0"
 
-micromark-extension-gfm-table@^1.0.0:
-  version "1.0.5"
-  resolved "https://registry.yarnpkg.com/micromark-extension-gfm-table/-/micromark-extension-gfm-table-1.0.5.tgz#7b708b728f8dc4d95d486b9e7a2262f9cddbcbb4"
-  integrity sha512-xAZ8J1X9W9K3JTJTUL7G6wSKhp2ZYHrFk5qJgY/4B33scJzE2kpfRL6oiw/veJTbt7jiM/1rngLlOKPWr1G+vg==
-  dependencies:
-    micromark-factory-space "^1.0.0"
-    micromark-util-character "^1.0.0"
-    micromark-util-symbol "^1.0.0"
-    micromark-util-types "^1.0.0"
-    uvu "^0.5.0"
+"micromark-extension-gfm-table@link:./apps/app/packages/micromark-extension-gfm-table":
+  version "0.0.0"
+  uid ""
 
 micromark-extension-gfm-tagfilter@^1.0.0:
   version "1.0.1"
@@ -11561,6 +11971,11 @@ nocache@^3.0.1:
   resolved "https://registry.yarnpkg.com/nocache/-/nocache-3.0.1.tgz#54d8b53a7e0a0aa1a288cfceab8a3cefbcde67d4"
   integrity sha512-Gh39xwJwBKy0OvFmWfBs/vDO4Nl7JhnJtkqNP76OUinQz7BiMoszHYrIDHHAaqVl/QKVxCEy4ZxC/XZninu7nQ==
 
+node-domexception@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5"
+  integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==
+
 node-emoji@^1.11.0:
   version "1.11.0"
   resolved "https://registry.yarnpkg.com/node-emoji/-/node-emoji-1.11.0.tgz#69a0150e6946e2f115e9d7ea4df7971e2628301c"
@@ -11582,6 +11997,15 @@ node-fetch@2.6.7, node-fetch@^2.3.0, node-fetch@^2.6.1, node-fetch@^2.6.7:
   dependencies:
     whatwg-url "^5.0.0"
 
+node-fetch@^3.0.0:
+  version "3.3.1"
+  resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-3.3.1.tgz#b3eea7b54b3a48020e46f4f88b9c5a7430d20b2e"
+  integrity sha512-cRVc/kyto/7E5shrWca1Wsea4y6tL9iYJE5FBCius3JQfb/4P4I295PfhgbJQBLTx6lATE4z+wK0rPM4VS2uow==
+  dependencies:
+    data-uri-to-buffer "^4.0.0"
+    fetch-blob "^3.1.4"
+    formdata-polyfill "^4.0.10"
+
 node-forge@^0.10.0:
   version "0.10.0"
   resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.10.0.tgz#32dea2afb3e9926f02ee5ce8794902691a676bf3"
@@ -13557,6 +13981,15 @@ rehype-slug@^5.0.1:
     unified "^10.0.0"
     unist-util-visit "^4.0.0"
 
+rehype-stringify@^9.0.0:
+  version "9.0.3"
+  resolved "https://registry.yarnpkg.com/rehype-stringify/-/rehype-stringify-9.0.3.tgz#70e3bd6d4d29e7acf36b802deed350305d2c3c17"
+  integrity sha512-kWiZ1bgyWlgOxpqD5HnxShKAdXtb2IUljn3hQAhySeak6IOQPPt6DeGnsIh4ixm7yKJWzm8TXFuC/lPfcWHJqw==
+  dependencies:
+    "@types/hast" "^2.0.0"
+    hast-util-to-html "^8.0.0"
+    unified "^10.0.0"
+
 rehype-toc@^3.0.2:
   version "3.0.2"
   resolved "https://registry.yarnpkg.com/rehype-toc/-/rehype-toc-3.0.2.tgz#0373e2abafddeb0606ee38229ff6714da6d86d68"
@@ -13677,6 +14110,11 @@ repeating@^2.0.0:
   dependencies:
     is-finite "^1.0.0"
 
+replace-ext@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-2.0.0.tgz#9471c213d22e1bcc26717cd6e50881d88f812b06"
+  integrity sha512-UszKE5KVK6JvyD92nzMn9cDapSk6w/CaFZ96CnmDMUqH9oowfxF/ZjRITD25H4DnOQClLA4/j7jLGXXLVKxAug==
+
 replacestream@^4.0.3:
   version "4.0.3"
   resolved "https://registry.yarnpkg.com/replacestream/-/replacestream-4.0.3.tgz#3ee5798092be364b1cdb1484308492cb3dff2f36"
@@ -16184,6 +16622,11 @@ web-namespaces@^2.0.0:
   resolved "https://registry.yarnpkg.com/web-namespaces/-/web-namespaces-2.0.1.tgz#1010ff7c650eccb2592cebeeaf9a1b253fd40692"
   integrity sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==
 
+web-streams-polyfill@^3.0.3:
+  version "3.2.1"
+  resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz#71c2718c52b45fd49dbeee88634b3a60ceab42a6"
+  integrity sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==
+
 webidl-conversions@^3.0.0:
   version "3.0.1"
   resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"
@@ -16671,3 +17114,8 @@ zwitch@^2.0.0:
   version "2.0.2"
   resolved "https://registry.yarnpkg.com/zwitch/-/zwitch-2.0.2.tgz#91f8d0e901ffa3d66599756dde7f57b17c95dce1"
   integrity sha512-JZxotl7SxAJH0j7dN4pxsTV6ZLXoLdGME+PsjkL/DaBrVryK9kTGq06GfKrwcSOqypP+fdXGoCHE36b99fWVoA==
+
+zwitch@^2.0.4:
+  version "2.0.4"
+  resolved "https://registry.yarnpkg.com/zwitch/-/zwitch-2.0.4.tgz#c827d4b0acb76fc3e685a4c6ec2902d51070e9d7"
+  integrity sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==