@readme/markdown 13.7.1 → 13.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/main.js CHANGED
@@ -12309,10 +12309,19 @@ const TailwindStyle = ({ children, darkModeDataAttribute }) => {
12309
12309
  records.forEach(record => {
12310
12310
  if (record.type === 'childList') {
12311
12311
  record.addedNodes.forEach(node => {
12312
- if (!(node instanceof HTMLElement) || !node.classList.contains(tailwindPrefix))
12312
+ if (!(node instanceof HTMLElement))
12313
12313
  return;
12314
- traverse(node, addClasses);
12315
- shouldUpdate = true;
12314
+ // Check the added node itself
12315
+ if (node.classList.contains(tailwindPrefix)) {
12316
+ traverse(node, addClasses);
12317
+ shouldUpdate = true;
12318
+ }
12319
+ // Also check descendants — React may insert a parent node
12320
+ // whose children contain TailwindRoot elements
12321
+ node.querySelectorAll(`.${tailwindPrefix}`).forEach(child => {
12322
+ traverse(child, addClasses);
12323
+ shouldUpdate = true;
12324
+ });
12316
12325
  });
12317
12326
  }
12318
12327
  else if (record.type === 'attributes') {
@@ -70410,9 +70419,416 @@ const mdast = (text, opts = {}) => {
70410
70419
  };
70411
70420
  /* harmony default export */ const lib_mdast = (mdast);
70412
70421
 
70422
+ ;// ./lib/utils/mdxish/protect-code-blocks.ts
70423
+ /**
70424
+ * Replaces code blocks and inline code with placeholders to protect them from preprocessing.
70425
+ *
70426
+ * @param content - The markdown content to process
70427
+ * @returns Object containing protected content and arrays of original code blocks
70428
+ * @example
70429
+ * ```typescript
70430
+ * const input = 'Text with `inline code` and ```fenced block```';
70431
+ * protectCodeBlocks(input)
70432
+ * // Returns: {
70433
+ * // protectedCode: {
70434
+ * // codeBlocks: ['```fenced block```'],
70435
+ * // inlineCode: ['`inline code`']
70436
+ * // },
70437
+ * // protectedContent: 'Text with ___INLINE_CODE_0___ and ___CODE_BLOCK_0___'
70438
+ * // }
70439
+ * ```
70440
+ */
70441
+ function protectCodeBlocks(content) {
70442
+ const codeBlocks = [];
70443
+ const inlineCode = [];
70444
+ let protectedContent = '';
70445
+ let remaining = content;
70446
+ let codeBlockStart = remaining.indexOf('```');
70447
+ while (codeBlockStart !== -1) {
70448
+ protectedContent += remaining.slice(0, codeBlockStart);
70449
+ remaining = remaining.slice(codeBlockStart);
70450
+ const codeBlockEnd = remaining.indexOf('```', 3);
70451
+ if (codeBlockEnd === -1) {
70452
+ break;
70453
+ }
70454
+ const match = remaining.slice(0, codeBlockEnd + 3);
70455
+ const index = codeBlocks.length;
70456
+ codeBlocks.push(match);
70457
+ protectedContent += `___CODE_BLOCK_${index}___`;
70458
+ remaining = remaining.slice(codeBlockEnd + 3);
70459
+ codeBlockStart = remaining.indexOf('```');
70460
+ }
70461
+ protectedContent += remaining;
70462
+ protectedContent = protectedContent.replace(/`([^`\n]+)`/g, match => {
70463
+ const index = inlineCode.length;
70464
+ inlineCode.push(match);
70465
+ return `___INLINE_CODE_${index}___`;
70466
+ });
70467
+ return { protectedCode: { codeBlocks, inlineCode }, protectedContent };
70468
+ }
70469
+ /**
70470
+ * Restores inline code by replacing placeholders with original content.
70471
+ *
70472
+ * @param content - Content with inline code placeholders
70473
+ * @param protectedCode - The protected code arrays
70474
+ * @returns Content with inline code restored
70475
+ */
70476
+ function restoreInlineCode(content, protectedCode) {
70477
+ return content.replace(/___INLINE_CODE_(\d+)___/g, (_m, idx) => {
70478
+ return protectedCode.inlineCode[parseInt(idx, 10)];
70479
+ });
70480
+ }
70481
+ /**
70482
+ * Restores fenced code blocks by replacing placeholders with original content.
70483
+ *
70484
+ * @param content - Content with code block placeholders
70485
+ * @param protectedCode - The protected code arrays
70486
+ * @returns Content with code blocks restored
70487
+ */
70488
+ function restoreFencedCodeBlocks(content, protectedCode) {
70489
+ return content.replace(/___CODE_BLOCK_(\d+)___/g, (_m, idx) => {
70490
+ return protectedCode.codeBlocks[parseInt(idx, 10)];
70491
+ });
70492
+ }
70493
+ /**
70494
+ * Restores all code blocks and inline code by replacing placeholders with original content.
70495
+ *
70496
+ * @param content - Content with code placeholders
70497
+ * @param protectedCode - The protected code arrays
70498
+ * @returns Content with all code blocks and inline code restored
70499
+ * @example
70500
+ * ```typescript
70501
+ * const content = 'Text with ___INLINE_CODE_0___ and ___CODE_BLOCK_0___';
70502
+ * const protectedCode = {
70503
+ * codeBlocks: ['```js\ncode\n```'],
70504
+ * inlineCode: ['`inline`']
70505
+ * };
70506
+ * restoreCodeBlocks(content, protectedCode)
70507
+ * // Returns: 'Text with `inline` and ```js\ncode\n```'
70508
+ * ```
70509
+ */
70510
+ function restoreCodeBlocks(content, protectedCode) {
70511
+ let restored = restoreFencedCodeBlocks(content, protectedCode);
70512
+ restored = restoreInlineCode(restored, protectedCode);
70513
+ return restored;
70514
+ }
70515
+
70516
+ ;// ./processor/transform/mdxish/preprocess-jsx-expressions.ts
70517
+
70518
+ // Base64 encode (Node.js + browser compatible)
70519
+ function base64Encode(str) {
70520
+ if (typeof Buffer !== 'undefined') {
70521
+ return Buffer.from(str, 'utf-8').toString('base64');
70522
+ }
70523
+ return btoa(unescape(encodeURIComponent(str)));
70524
+ }
70525
+ // Base64 decode (Node.js + browser compatible)
70526
+ function base64Decode(str) {
70527
+ if (typeof Buffer !== 'undefined') {
70528
+ return Buffer.from(str, 'base64').toString('utf-8');
70529
+ }
70530
+ return decodeURIComponent(escape(atob(str)));
70531
+ }
70532
+ function escapeHtmlAttribute(value) {
70533
+ return value
70534
+ .replace(/&/g, '&')
70535
+ .replace(/"/g, '"')
70536
+ .replace(/</g, '&lt;')
70537
+ .replace(/>/g, '&gt;')
70538
+ .replace(/\n/g, '&#10;');
70539
+ }
70540
+ // Marker prefix for JSON-serialized complex values (arrays/objects)
70541
+ // Using a prefix that won't conflict with regular string values
70542
+ const JSON_VALUE_MARKER = '__MDXISH_JSON__';
70543
+ // Markers for protected HTMLBlock content (HTML comments avoid markdown parsing issues)
70544
+ const HTML_BLOCK_CONTENT_START = '<!--RDMX_HTMLBLOCK:';
70545
+ const HTML_BLOCK_CONTENT_END = ':RDMX_HTMLBLOCK-->';
70546
+ /**
70547
+ * Evaluates a JavaScript expression using context variables.
70548
+ *
70549
+ * @param expression
70550
+ * @param context
70551
+ * @returns The evaluated result
70552
+ * @example
70553
+ * ```typescript
70554
+ * const context = { baseUrl: 'https://example.com', path: '/api' };
70555
+ * evaluateExpression('baseUrl + path', context)
70556
+ * // Returns: 'https://example.com/api'
70557
+ * ```
70558
+ */
70559
+ function evaluateExpression(expression, context) {
70560
+ const contextKeys = Object.keys(context);
70561
+ const contextValues = Object.values(context);
70562
+ // eslint-disable-next-line no-new-func
70563
+ const func = new Function(...contextKeys, `return ${expression}`);
70564
+ return func(...contextValues);
70565
+ }
70566
+ /**
70567
+ * Base64 encodes HTMLBlock template literal content to prevent markdown parser from consuming <script>/<style> tags.
70568
+ *
70569
+ * @param content
70570
+ * @returns Content with HTMLBlock template literals base64 encoded in HTML comments
70571
+ * @example
70572
+ * ```typescript
70573
+ * const input = '<HTMLBlock>{`<script>alert("xss")</script>`}</HTMLBlock>';
70574
+ * protectHTMLBlockContent(input)
70575
+ * // Returns: '<HTMLBlock><!--RDMX_HTMLBLOCK:PHNjcmlwdD5hbGVydCgieHNzIik8L3NjcmlwdD4=:RDMX_HTMLBLOCK--></HTMLBlock>'
70576
+ * ```
70577
+ */
70578
+ function protectHTMLBlockContent(content) {
70579
+ return content.replace(/(<HTMLBlock[^>]*>)\{\s*`((?:[^`\\]|\\.)*)`\s*\}(<\/HTMLBlock>)/g, (_match, openTag, templateContent, closeTag) => {
70580
+ const encoded = base64Encode(templateContent);
70581
+ return `${openTag}${HTML_BLOCK_CONTENT_START}${encoded}${HTML_BLOCK_CONTENT_END}${closeTag}`;
70582
+ });
70583
+ }
70584
+ /**
70585
+ * Removes JSX-style comments (e.g., { /* comment *\/ }) from content.
70586
+ *
70587
+ * @param content
70588
+ * @returns Content with JSX comments removed
70589
+ * @example
70590
+ * ```typescript
70591
+ * removeJSXComments('Text { /* comment *\/ } more text')
70592
+ * // Returns: 'Text more text'
70593
+ * ```
70594
+ */
70595
+ function removeJSXComments(content) {
70596
+ return content.replace(/\{\s*\/\*[^*]*(?:\*(?!\/)[^*]*)*\*\/\s*\}/g, '');
70597
+ }
70598
+ /**
70599
+ * Extracts content between balanced braces, handling nested braces.
70600
+ *
70601
+ * @param content
70602
+ * @param start
70603
+ * @returns Object with extracted content and end position, or null if braces are unbalanced
70604
+ * @example
70605
+ * ```typescript
70606
+ * const input = 'foo{bar{baz}qux}end';
70607
+ * extractBalancedBraces(input, 3) // start at position 3 (after '{')
70608
+ * // Returns: { content: 'bar{baz}qux', end: 16 }
70609
+ * ```
70610
+ */
70611
+ function extractBalancedBraces(content, start) {
70612
+ let depth = 1;
70613
+ let pos = start;
70614
+ while (pos < content.length && depth > 0) {
70615
+ const char = content[pos];
70616
+ if (char === '{')
70617
+ depth += 1;
70618
+ else if (char === '}')
70619
+ depth -= 1;
70620
+ pos += 1;
70621
+ }
70622
+ if (depth !== 0)
70623
+ return null;
70624
+ return { content: content.slice(start, pos - 1), end: pos };
70625
+ }
70626
+ /**
70627
+ * Escapes problematic braces in content to prevent MDX expression parsing errors.
70628
+ * Handles three cases:
70629
+ * 1. Unbalanced braces (e.g., `{foo` without closing `}`)
70630
+ * 2. Paragraph-spanning expressions (e.g., `{\n\n}` where blank line splits paragraphs)
70631
+ * 3. Skips HTML elements to prevent backslashes appearing in output
70632
+ *
70633
+ */
70634
+ function escapeProblematicBraces(content) {
70635
+ // Skip HTML elements — their content should never be escaped because
70636
+ // rehypeRaw parses them into hast elements, making `\` literal text in output
70637
+ const htmlElements = [];
70638
+ const safe = content.replace(/<([a-z][a-zA-Z0-9]*)(?:\s[^>]*)?>[\s\S]*?<\/\1>/g, match => {
70639
+ const idx = htmlElements.length;
70640
+ htmlElements.push(match);
70641
+ return `___HTML_ELEM_${idx}___`;
70642
+ });
70643
+ const toEscape = new Set();
70644
+ // Convert to array of Unicode code points to handle emojis and multi-byte characters correctly
70645
+ const chars = Array.from(safe);
70646
+ let strDelim = null;
70647
+ let strEscaped = false;
70648
+ // Stack of open braces with their state
70649
+ const openStack = [];
70650
+ // Track position of last newline (outside strings) to detect blank lines
70651
+ let lastNewlinePos = -2; // -2 means no recent newline
70652
+ for (let i = 0; i < chars.length; i += 1) {
70653
+ const ch = chars[i];
70654
+ // Track string delimiters inside expressions to ignore braces within them
70655
+ if (openStack.length > 0) {
70656
+ if (strDelim) {
70657
+ if (strEscaped)
70658
+ strEscaped = false;
70659
+ else if (ch === '\\')
70660
+ strEscaped = true;
70661
+ else if (ch === strDelim)
70662
+ strDelim = null;
70663
+ // eslint-disable-next-line no-continue
70664
+ continue;
70665
+ }
70666
+ if (ch === '"' || ch === "'" || ch === '`') {
70667
+ strDelim = ch;
70668
+ // eslint-disable-next-line no-continue
70669
+ continue;
70670
+ }
70671
+ // Track newlines to detect blank lines (paragraph boundaries)
70672
+ if (ch === '\n') {
70673
+ // Check if this newline creates a blank line (only whitespace since last newline)
70674
+ if (lastNewlinePos >= 0) {
70675
+ const between = chars.slice(lastNewlinePos + 1, i).join('');
70676
+ if (/^[ \t]*$/.test(between)) {
70677
+ // This is a blank line - mark all open expressions as paragraph-spanning
70678
+ openStack.forEach(entry => {
70679
+ entry.hasBlankLine = true;
70680
+ });
70681
+ }
70682
+ }
70683
+ lastNewlinePos = i;
70684
+ }
70685
+ }
70686
+ // Skip already-escaped braces (count preceding backslashes)
70687
+ if (ch === '{' || ch === '}') {
70688
+ let bs = 0;
70689
+ for (let j = i - 1; j >= 0 && chars[j] === '\\'; j -= 1)
70690
+ bs += 1;
70691
+ if (bs % 2 === 1) {
70692
+ // eslint-disable-next-line no-continue
70693
+ continue;
70694
+ }
70695
+ }
70696
+ if (ch === '{') {
70697
+ openStack.push({ pos: i, hasBlankLine: false });
70698
+ lastNewlinePos = -2; // Reset newline tracking for new expression
70699
+ }
70700
+ else if (ch === '}') {
70701
+ if (openStack.length > 0) {
70702
+ const entry = openStack.pop();
70703
+ // If expression spans paragraph boundary, escape both braces
70704
+ if (entry.hasBlankLine) {
70705
+ toEscape.add(entry.pos);
70706
+ toEscape.add(i);
70707
+ }
70708
+ }
70709
+ else {
70710
+ // Unbalanced closing brace (no matching open)
70711
+ toEscape.add(i);
70712
+ }
70713
+ }
70714
+ }
70715
+ // Any remaining open braces are unbalanced
70716
+ openStack.forEach(entry => toEscape.add(entry.pos));
70717
+ // If there are no problematic braces, return safe content as-is;
70718
+ // otherwise, escape each problematic `{` or `}` so MDX doesn't treat them as expressions.
70719
+ let result = toEscape.size === 0
70720
+ ? safe
70721
+ : chars.map((ch, i) => (toEscape.has(i) ? `\\${ch}` : ch)).join('');
70722
+ // Restore HTML elements
70723
+ if (htmlElements.length > 0) {
70724
+ result = result.replace(/___HTML_ELEM_(\d+)___/g, (_m, idx) => htmlElements[parseInt(idx, 10)]);
70725
+ }
70726
+ return result;
70727
+ }
70728
+ /**
70729
+ * Converts JSX attribute expressions (attribute={expression}) to HTML attributes (attribute="value").
70730
+ * Handles style objects (camelCase → kebab-case), className → class, and JSON stringifies objects.
70731
+ *
70732
+ * @param content
70733
+ * @param context
70734
+ * @returns Content with attribute expressions evaluated and converted to HTML attributes
70735
+ * @example
70736
+ * ```typescript
70737
+ * const context = { baseUrl: 'https://example.com' };
70738
+ * const input = '<a href={baseUrl}>Link</a>';
70739
+ * evaluateAttributeExpressions(input, context)
70740
+ * // Returns: '<a href="https://example.com">Link</a>'
70741
+ * ```
70742
+ */
70743
+ function evaluateAttributeExpressions(content, context, protectedCode) {
70744
+ const attrStartRegex = /(\w+)=\{/g;
70745
+ let result = '';
70746
+ let lastEnd = 0;
70747
+ let match = attrStartRegex.exec(content);
70748
+ while (match !== null) {
70749
+ const attributeName = match[1];
70750
+ const braceStart = match.index + match[0].length;
70751
+ const extracted = extractBalancedBraces(content, braceStart);
70752
+ if (extracted) {
70753
+ // The expression might contain template literals in MDX component tag props
70754
+ // E.g. <Component greeting={`Hello World!`} />
70755
+ // that is marked as inline code. So we need to restore the inline codes
70756
+ // in the expression to evaluate it
70757
+ let expression = extracted.content;
70758
+ if (protectedCode) {
70759
+ expression = restoreInlineCode(expression, protectedCode);
70760
+ }
70761
+ const fullMatchEnd = extracted.end;
70762
+ result += content.slice(lastEnd, match.index);
70763
+ try {
70764
+ const evalResult = evaluateExpression(expression, context);
70765
+ if (typeof evalResult === 'object' && evalResult !== null) {
70766
+ if (attributeName === 'style') {
70767
+ const cssString = Object.entries(evalResult)
70768
+ .map(([key, value]) => {
70769
+ const cssKey = key.replace(/([A-Z])/g, '-$1').toLowerCase();
70770
+ return `${cssKey}: ${value}`;
70771
+ })
70772
+ .join('; ');
70773
+ result += `style="${cssString}"`;
70774
+ }
70775
+ else {
70776
+ // These are arrays / objects attribute values
70777
+ // Mark JSON-serialized values with a prefix so they can be parsed back correctly
70778
+ const jsonValue = escapeHtmlAttribute(JSON_VALUE_MARKER + JSON.stringify(evalResult));
70779
+ // Use double quotes so that multi-paragraph values are not split into multiple attributes by the processors
70780
+ result += `${attributeName}="${jsonValue}"`;
70781
+ }
70782
+ }
70783
+ else if (attributeName === 'className') {
70784
+ // Escape special characters so that it doesn't break and split the attribute value to nodes
70785
+ // This will be restored later in the pipeline
70786
+ result += `class="${escapeHtmlAttribute(String(evalResult))}"`;
70787
+ }
70788
+ else {
70789
+ result += `${attributeName}="${escapeHtmlAttribute(String(evalResult))}"`;
70790
+ }
70791
+ }
70792
+ catch (_error) {
70793
+ result += content.slice(match.index, fullMatchEnd);
70794
+ }
70795
+ lastEnd = fullMatchEnd;
70796
+ attrStartRegex.lastIndex = fullMatchEnd;
70797
+ }
70798
+ match = attrStartRegex.exec(content);
70799
+ }
70800
+ result += content.slice(lastEnd);
70801
+ return result;
70802
+ }
70803
+ /**
70804
+ * Preprocesses JSX-like expressions in markdown before parsing.
70805
+ * Inline expressions are handled separately; attribute expressions are processed here.
70806
+ *
70807
+ * @param content
70808
+ * @param context
70809
+ * @returns Preprocessed content ready for markdown parsing
70810
+ */
70811
+ function preprocessJSXExpressions(content, context = {}) {
70812
+ // Step 0: Base64 encode HTMLBlock content
70813
+ let processed = protectHTMLBlockContent(content);
70814
+ // Step 1: Protect code blocks and inline code
70815
+ const { protectedCode, protectedContent } = protectCodeBlocks(processed);
70816
+ // Step 2: Evaluate attribute expressions (JSX attribute syntax: href={baseUrl})
70817
+ // For inline expressions, we use a library to parse the expression & evaluate it later
70818
+ // For attribute expressions, it was difficult to use a library to parse them, so do it manually
70819
+ processed = evaluateAttributeExpressions(protectedContent, context, protectedCode);
70820
+ // Step 3: Escape problematic braces to prevent MDX expression parsing errors
70821
+ // This handles both unbalanced braces and paragraph-spanning expressions in one pass
70822
+ processed = escapeProblematicBraces(processed);
70823
+ // Step 4: Restore protected code blocks
70824
+ processed = restoreCodeBlocks(processed, protectedCode);
70825
+ return processed;
70826
+ }
70827
+
70413
70828
  ;// ./processor/utils.ts
70414
70829
 
70415
70830
 
70831
+
70416
70832
  /**
70417
70833
  * Formats the hProperties of a node as a string, so they can be compiled back into JSX/MDX.
70418
70834
  * This currently sets all the values to a string since we process/compile the MDX on the fly
@@ -70468,7 +70884,17 @@ const getHPropKeys = (node) => {
70468
70884
  const getAttrs = (jsx) => jsx.attributes.reduce((memo, attr) => {
70469
70885
  if ('name' in attr) {
70470
70886
  if (typeof attr.value === 'string') {
70471
- memo[attr.name] = attr.value;
70887
+ if (attr.value.startsWith(JSON_VALUE_MARKER)) {
70888
+ try {
70889
+ memo[attr.name] = JSON.parse(attr.value.slice(JSON_VALUE_MARKER.length));
70890
+ }
70891
+ catch {
70892
+ memo[attr.name] = attr.value;
70893
+ }
70894
+ }
70895
+ else {
70896
+ memo[attr.name] = attr.value;
70897
+ }
70472
70898
  }
70473
70899
  else if (attr.value === null) {
70474
70900
  memo[attr.name] = true;
@@ -71242,10 +71668,10 @@ const hasFlowContent = (nodes) => {
71242
71668
  * Process a Table node: re-parse text-only cell content, then output as
71243
71669
  * a markdown table (phrasing-only) or keep as JSX <Table> (has flow content).
71244
71670
  */
71245
- const processTableNode = (node, index, parent) => {
71671
+ const processTableNode = (node, index, parent, documentPosition) => {
71246
71672
  if (node.name !== 'Table')
71247
71673
  return;
71248
- const { position } = node;
71674
+ const position = documentPosition ?? node.position;
71249
71675
  const { align: alignAttr } = getAttrs(node);
71250
71676
  const align = Array.isArray(alignAttr) ? alignAttr : null;
71251
71677
  let tableHasFlowContent = false;
@@ -71368,7 +71794,7 @@ const mdxishTables = () => tree => {
71368
71794
  const parsed = tableNodeProcessor.runSync(tableNodeProcessor.parse(node.value));
71369
71795
  visit(parsed, isMDXElement, (tableNode) => {
71370
71796
  if (tableNode.name === 'Table') {
71371
- processTableNode(tableNode, index, parent);
71797
+ processTableNode(tableNode, index, parent, node.position);
71372
71798
  // Stop after the outermost Table so nested Tables don't overwrite parent.children[index]
71373
71799
  // we let it get handled naturally
71374
71800
  return EXIT;
@@ -93843,412 +94269,6 @@ const mdxComponentHandlers = {
93843
94269
  [NodeTypes.htmlBlock]: htmlBlockHandler,
93844
94270
  };
93845
94271
 
93846
- ;// ./lib/utils/mdxish/protect-code-blocks.ts
93847
- /**
93848
- * Replaces code blocks and inline code with placeholders to protect them from preprocessing.
93849
- *
93850
- * @param content - The markdown content to process
93851
- * @returns Object containing protected content and arrays of original code blocks
93852
- * @example
93853
- * ```typescript
93854
- * const input = 'Text with `inline code` and ```fenced block```';
93855
- * protectCodeBlocks(input)
93856
- * // Returns: {
93857
- * // protectedCode: {
93858
- * // codeBlocks: ['```fenced block```'],
93859
- * // inlineCode: ['`inline code`']
93860
- * // },
93861
- * // protectedContent: 'Text with ___INLINE_CODE_0___ and ___CODE_BLOCK_0___'
93862
- * // }
93863
- * ```
93864
- */
93865
- function protectCodeBlocks(content) {
93866
- const codeBlocks = [];
93867
- const inlineCode = [];
93868
- let protectedContent = '';
93869
- let remaining = content;
93870
- let codeBlockStart = remaining.indexOf('```');
93871
- while (codeBlockStart !== -1) {
93872
- protectedContent += remaining.slice(0, codeBlockStart);
93873
- remaining = remaining.slice(codeBlockStart);
93874
- const codeBlockEnd = remaining.indexOf('```', 3);
93875
- if (codeBlockEnd === -1) {
93876
- break;
93877
- }
93878
- const match = remaining.slice(0, codeBlockEnd + 3);
93879
- const index = codeBlocks.length;
93880
- codeBlocks.push(match);
93881
- protectedContent += `___CODE_BLOCK_${index}___`;
93882
- remaining = remaining.slice(codeBlockEnd + 3);
93883
- codeBlockStart = remaining.indexOf('```');
93884
- }
93885
- protectedContent += remaining;
93886
- protectedContent = protectedContent.replace(/`([^`\n]+)`/g, match => {
93887
- const index = inlineCode.length;
93888
- inlineCode.push(match);
93889
- return `___INLINE_CODE_${index}___`;
93890
- });
93891
- return { protectedCode: { codeBlocks, inlineCode }, protectedContent };
93892
- }
93893
- /**
93894
- * Restores inline code by replacing placeholders with original content.
93895
- *
93896
- * @param content - Content with inline code placeholders
93897
- * @param protectedCode - The protected code arrays
93898
- * @returns Content with inline code restored
93899
- */
93900
- function restoreInlineCode(content, protectedCode) {
93901
- return content.replace(/___INLINE_CODE_(\d+)___/g, (_m, idx) => {
93902
- return protectedCode.inlineCode[parseInt(idx, 10)];
93903
- });
93904
- }
93905
- /**
93906
- * Restores fenced code blocks by replacing placeholders with original content.
93907
- *
93908
- * @param content - Content with code block placeholders
93909
- * @param protectedCode - The protected code arrays
93910
- * @returns Content with code blocks restored
93911
- */
93912
- function restoreFencedCodeBlocks(content, protectedCode) {
93913
- return content.replace(/___CODE_BLOCK_(\d+)___/g, (_m, idx) => {
93914
- return protectedCode.codeBlocks[parseInt(idx, 10)];
93915
- });
93916
- }
93917
- /**
93918
- * Restores all code blocks and inline code by replacing placeholders with original content.
93919
- *
93920
- * @param content - Content with code placeholders
93921
- * @param protectedCode - The protected code arrays
93922
- * @returns Content with all code blocks and inline code restored
93923
- * @example
93924
- * ```typescript
93925
- * const content = 'Text with ___INLINE_CODE_0___ and ___CODE_BLOCK_0___';
93926
- * const protectedCode = {
93927
- * codeBlocks: ['```js\ncode\n```'],
93928
- * inlineCode: ['`inline`']
93929
- * };
93930
- * restoreCodeBlocks(content, protectedCode)
93931
- * // Returns: 'Text with `inline` and ```js\ncode\n```'
93932
- * ```
93933
- */
93934
- function restoreCodeBlocks(content, protectedCode) {
93935
- let restored = restoreFencedCodeBlocks(content, protectedCode);
93936
- restored = restoreInlineCode(restored, protectedCode);
93937
- return restored;
93938
- }
93939
-
93940
- ;// ./processor/transform/mdxish/preprocess-jsx-expressions.ts
93941
-
93942
- // Base64 encode (Node.js + browser compatible)
93943
- function base64Encode(str) {
93944
- if (typeof Buffer !== 'undefined') {
93945
- return Buffer.from(str, 'utf-8').toString('base64');
93946
- }
93947
- return btoa(unescape(encodeURIComponent(str)));
93948
- }
93949
- // Base64 decode (Node.js + browser compatible)
93950
- function base64Decode(str) {
93951
- if (typeof Buffer !== 'undefined') {
93952
- return Buffer.from(str, 'base64').toString('utf-8');
93953
- }
93954
- return decodeURIComponent(escape(atob(str)));
93955
- }
93956
- function escapeHtmlAttribute(value) {
93957
- return value
93958
- .replace(/&/g, '&amp;')
93959
- .replace(/"/g, '&quot;')
93960
- .replace(/</g, '&lt;')
93961
- .replace(/>/g, '&gt;')
93962
- .replace(/\n/g, '&#10;');
93963
- }
93964
- // Marker prefix for JSON-serialized complex values (arrays/objects)
93965
- // Using a prefix that won't conflict with regular string values
93966
- const JSON_VALUE_MARKER = '__MDXISH_JSON__';
93967
- // Markers for protected HTMLBlock content (HTML comments avoid markdown parsing issues)
93968
- const HTML_BLOCK_CONTENT_START = '<!--RDMX_HTMLBLOCK:';
93969
- const HTML_BLOCK_CONTENT_END = ':RDMX_HTMLBLOCK-->';
93970
- /**
93971
- * Evaluates a JavaScript expression using context variables.
93972
- *
93973
- * @param expression
93974
- * @param context
93975
- * @returns The evaluated result
93976
- * @example
93977
- * ```typescript
93978
- * const context = { baseUrl: 'https://example.com', path: '/api' };
93979
- * evaluateExpression('baseUrl + path', context)
93980
- * // Returns: 'https://example.com/api'
93981
- * ```
93982
- */
93983
- function evaluateExpression(expression, context) {
93984
- const contextKeys = Object.keys(context);
93985
- const contextValues = Object.values(context);
93986
- // eslint-disable-next-line no-new-func
93987
- const func = new Function(...contextKeys, `return ${expression}`);
93988
- return func(...contextValues);
93989
- }
93990
- /**
93991
- * Base64 encodes HTMLBlock template literal content to prevent markdown parser from consuming <script>/<style> tags.
93992
- *
93993
- * @param content
93994
- * @returns Content with HTMLBlock template literals base64 encoded in HTML comments
93995
- * @example
93996
- * ```typescript
93997
- * const input = '<HTMLBlock>{`<script>alert("xss")</script>`}</HTMLBlock>';
93998
- * protectHTMLBlockContent(input)
93999
- * // Returns: '<HTMLBlock><!--RDMX_HTMLBLOCK:PHNjcmlwdD5hbGVydCgieHNzIik8L3NjcmlwdD4=:RDMX_HTMLBLOCK--></HTMLBlock>'
94000
- * ```
94001
- */
94002
- function protectHTMLBlockContent(content) {
94003
- return content.replace(/(<HTMLBlock[^>]*>)\{\s*`((?:[^`\\]|\\.)*)`\s*\}(<\/HTMLBlock>)/g, (_match, openTag, templateContent, closeTag) => {
94004
- const encoded = base64Encode(templateContent);
94005
- return `${openTag}${HTML_BLOCK_CONTENT_START}${encoded}${HTML_BLOCK_CONTENT_END}${closeTag}`;
94006
- });
94007
- }
94008
- /**
94009
- * Removes JSX-style comments (e.g., { /* comment *\/ }) from content.
94010
- *
94011
- * @param content
94012
- * @returns Content with JSX comments removed
94013
- * @example
94014
- * ```typescript
94015
- * removeJSXComments('Text { /* comment *\/ } more text')
94016
- * // Returns: 'Text more text'
94017
- * ```
94018
- */
94019
- function removeJSXComments(content) {
94020
- return content.replace(/\{\s*\/\*[^*]*(?:\*(?!\/)[^*]*)*\*\/\s*\}/g, '');
94021
- }
94022
- /**
94023
- * Extracts content between balanced braces, handling nested braces.
94024
- *
94025
- * @param content
94026
- * @param start
94027
- * @returns Object with extracted content and end position, or null if braces are unbalanced
94028
- * @example
94029
- * ```typescript
94030
- * const input = 'foo{bar{baz}qux}end';
94031
- * extractBalancedBraces(input, 3) // start at position 3 (after '{')
94032
- * // Returns: { content: 'bar{baz}qux', end: 16 }
94033
- * ```
94034
- */
94035
- function extractBalancedBraces(content, start) {
94036
- let depth = 1;
94037
- let pos = start;
94038
- while (pos < content.length && depth > 0) {
94039
- const char = content[pos];
94040
- if (char === '{')
94041
- depth += 1;
94042
- else if (char === '}')
94043
- depth -= 1;
94044
- pos += 1;
94045
- }
94046
- if (depth !== 0)
94047
- return null;
94048
- return { content: content.slice(start, pos - 1), end: pos };
94049
- }
94050
- /**
94051
- * Escapes problematic braces in content to prevent MDX expression parsing errors.
94052
- * Handles three cases:
94053
- * 1. Unbalanced braces (e.g., `{foo` without closing `}`)
94054
- * 2. Paragraph-spanning expressions (e.g., `{\n\n}` where blank line splits paragraphs)
94055
- * 3. Skips HTML elements to prevent backslashes appearing in output
94056
- *
94057
- */
94058
- function escapeProblematicBraces(content) {
94059
- // Skip HTML elements — their content should never be escaped because
94060
- // rehypeRaw parses them into hast elements, making `\` literal text in output
94061
- const htmlElements = [];
94062
- const safe = content.replace(/<([a-z][a-zA-Z0-9]*)(?:\s[^>]*)?>[\s\S]*?<\/\1>/g, match => {
94063
- const idx = htmlElements.length;
94064
- htmlElements.push(match);
94065
- return `___HTML_ELEM_${idx}___`;
94066
- });
94067
- const toEscape = new Set();
94068
- // Convert to array of Unicode code points to handle emojis and multi-byte characters correctly
94069
- const chars = Array.from(safe);
94070
- let strDelim = null;
94071
- let strEscaped = false;
94072
- // Stack of open braces with their state
94073
- const openStack = [];
94074
- // Track position of last newline (outside strings) to detect blank lines
94075
- let lastNewlinePos = -2; // -2 means no recent newline
94076
- for (let i = 0; i < chars.length; i += 1) {
94077
- const ch = chars[i];
94078
- // Track string delimiters inside expressions to ignore braces within them
94079
- if (openStack.length > 0) {
94080
- if (strDelim) {
94081
- if (strEscaped)
94082
- strEscaped = false;
94083
- else if (ch === '\\')
94084
- strEscaped = true;
94085
- else if (ch === strDelim)
94086
- strDelim = null;
94087
- // eslint-disable-next-line no-continue
94088
- continue;
94089
- }
94090
- if (ch === '"' || ch === "'" || ch === '`') {
94091
- strDelim = ch;
94092
- // eslint-disable-next-line no-continue
94093
- continue;
94094
- }
94095
- // Track newlines to detect blank lines (paragraph boundaries)
94096
- if (ch === '\n') {
94097
- // Check if this newline creates a blank line (only whitespace since last newline)
94098
- if (lastNewlinePos >= 0) {
94099
- const between = chars.slice(lastNewlinePos + 1, i).join('');
94100
- if (/^[ \t]*$/.test(between)) {
94101
- // This is a blank line - mark all open expressions as paragraph-spanning
94102
- openStack.forEach(entry => {
94103
- entry.hasBlankLine = true;
94104
- });
94105
- }
94106
- }
94107
- lastNewlinePos = i;
94108
- }
94109
- }
94110
- // Skip already-escaped braces (count preceding backslashes)
94111
- if (ch === '{' || ch === '}') {
94112
- let bs = 0;
94113
- for (let j = i - 1; j >= 0 && chars[j] === '\\'; j -= 1)
94114
- bs += 1;
94115
- if (bs % 2 === 1) {
94116
- // eslint-disable-next-line no-continue
94117
- continue;
94118
- }
94119
- }
94120
- if (ch === '{') {
94121
- openStack.push({ pos: i, hasBlankLine: false });
94122
- lastNewlinePos = -2; // Reset newline tracking for new expression
94123
- }
94124
- else if (ch === '}') {
94125
- if (openStack.length > 0) {
94126
- const entry = openStack.pop();
94127
- // If expression spans paragraph boundary, escape both braces
94128
- if (entry.hasBlankLine) {
94129
- toEscape.add(entry.pos);
94130
- toEscape.add(i);
94131
- }
94132
- }
94133
- else {
94134
- // Unbalanced closing brace (no matching open)
94135
- toEscape.add(i);
94136
- }
94137
- }
94138
- }
94139
- // Any remaining open braces are unbalanced
94140
- openStack.forEach(entry => toEscape.add(entry.pos));
94141
- // If there are no problematic braces, return safe content as-is;
94142
- // otherwise, escape each problematic `{` or `}` so MDX doesn't treat them as expressions.
94143
- let result = toEscape.size === 0
94144
- ? safe
94145
- : chars.map((ch, i) => (toEscape.has(i) ? `\\${ch}` : ch)).join('');
94146
- // Restore HTML elements
94147
- if (htmlElements.length > 0) {
94148
- result = result.replace(/___HTML_ELEM_(\d+)___/g, (_m, idx) => htmlElements[parseInt(idx, 10)]);
94149
- }
94150
- return result;
94151
- }
94152
- /**
94153
- * Converts JSX attribute expressions (attribute={expression}) to HTML attributes (attribute="value").
94154
- * Handles style objects (camelCase → kebab-case), className → class, and JSON stringifies objects.
94155
- *
94156
- * @param content
94157
- * @param context
94158
- * @returns Content with attribute expressions evaluated and converted to HTML attributes
94159
- * @example
94160
- * ```typescript
94161
- * const context = { baseUrl: 'https://example.com' };
94162
- * const input = '<a href={baseUrl}>Link</a>';
94163
- * evaluateAttributeExpressions(input, context)
94164
- * // Returns: '<a href="https://example.com">Link</a>'
94165
- * ```
94166
- */
94167
- function evaluateAttributeExpressions(content, context, protectedCode) {
94168
- const attrStartRegex = /(\w+)=\{/g;
94169
- let result = '';
94170
- let lastEnd = 0;
94171
- let match = attrStartRegex.exec(content);
94172
- while (match !== null) {
94173
- const attributeName = match[1];
94174
- const braceStart = match.index + match[0].length;
94175
- const extracted = extractBalancedBraces(content, braceStart);
94176
- if (extracted) {
94177
- // The expression might contain template literals in MDX component tag props
94178
- // E.g. <Component greeting={`Hello World!`} />
94179
- // that is marked as inline code. So we need to restore the inline codes
94180
- // in the expression to evaluate it
94181
- let expression = extracted.content;
94182
- if (protectedCode) {
94183
- expression = restoreInlineCode(expression, protectedCode);
94184
- }
94185
- const fullMatchEnd = extracted.end;
94186
- result += content.slice(lastEnd, match.index);
94187
- try {
94188
- const evalResult = evaluateExpression(expression, context);
94189
- if (typeof evalResult === 'object' && evalResult !== null) {
94190
- if (attributeName === 'style') {
94191
- const cssString = Object.entries(evalResult)
94192
- .map(([key, value]) => {
94193
- const cssKey = key.replace(/([A-Z])/g, '-$1').toLowerCase();
94194
- return `${cssKey}: ${value}`;
94195
- })
94196
- .join('; ');
94197
- result += `style="${cssString}"`;
94198
- }
94199
- else {
94200
- // These are arrays / objects attribute values
94201
- // Mark JSON-serialized values with a prefix so they can be parsed back correctly
94202
- const jsonValue = escapeHtmlAttribute(JSON_VALUE_MARKER + JSON.stringify(evalResult));
94203
- // Use double quotes so that multi-paragraph values are not split into multiple attributes by the processors
94204
- result += `${attributeName}="${jsonValue}"`;
94205
- }
94206
- }
94207
- else if (attributeName === 'className') {
94208
- // Escape special characters so that it doesn't break and split the attribute value to nodes
94209
- // This will be restored later in the pipeline
94210
- result += `class="${escapeHtmlAttribute(String(evalResult))}"`;
94211
- }
94212
- else {
94213
- result += `${attributeName}="${escapeHtmlAttribute(String(evalResult))}"`;
94214
- }
94215
- }
94216
- catch (_error) {
94217
- result += content.slice(match.index, fullMatchEnd);
94218
- }
94219
- lastEnd = fullMatchEnd;
94220
- attrStartRegex.lastIndex = fullMatchEnd;
94221
- }
94222
- match = attrStartRegex.exec(content);
94223
- }
94224
- result += content.slice(lastEnd);
94225
- return result;
94226
- }
94227
- /**
94228
- * Preprocesses JSX-like expressions in markdown before parsing.
94229
- * Inline expressions are handled separately; attribute expressions are processed here.
94230
- *
94231
- * @param content
94232
- * @param context
94233
- * @returns Preprocessed content ready for markdown parsing
94234
- */
94235
- function preprocessJSXExpressions(content, context = {}) {
94236
- // Step 0: Base64 encode HTMLBlock content
94237
- let processed = protectHTMLBlockContent(content);
94238
- // Step 1: Protect code blocks and inline code
94239
- const { protectedCode, protectedContent } = protectCodeBlocks(processed);
94240
- // Step 2: Evaluate attribute expressions (JSX attribute syntax: href={baseUrl})
94241
- // For inline expressions, we use a library to parse the expression & evaluate it later
94242
- // For attribute expressions, it was difficult to use a library to parse them, so do it manually
94243
- processed = evaluateAttributeExpressions(protectedContent, context, protectedCode);
94244
- // Step 3: Escape problematic braces to prevent MDX expression parsing errors
94245
- // This handles both unbalanced braces and paragraph-spanning expressions in one pass
94246
- processed = escapeProblematicBraces(processed);
94247
- // Step 4: Restore protected code blocks
94248
- processed = restoreCodeBlocks(processed, protectedCode);
94249
- return processed;
94250
- }
94251
-
94252
94272
  ;// ./processor/transform/mdxish/evaluate-expressions.ts
94253
94273
 
94254
94274
 
@@ -97337,11 +97357,66 @@ const transformMagicBlockEmbed = (node) => {
97337
97357
  position,
97338
97358
  };
97339
97359
  };
97360
+ const mdxish_jsx_to_mdast_isTableCell = (node) => isMDXElement(node) && ['th', 'td'].includes(node.name);
97361
+ /**
97362
+ * Converts a JSX <Table> element to an MDAST table node with alignment.
97363
+ * Returns null for header-less tables since MDAST always promotes the first row to <thead>.
97364
+ */
97365
+ const transformTable = (jsx) => {
97366
+ let hasThead = false;
97367
+ visit(jsx, isMDXElement, (child) => {
97368
+ if (child.name === 'thead')
97369
+ hasThead = true;
97370
+ });
97371
+ if (!hasThead)
97372
+ return null;
97373
+ const { align: alignAttr } = getAttrs(jsx);
97374
+ const align = Array.isArray(alignAttr) ? alignAttr : null;
97375
+ const rows = [];
97376
+ visit(jsx, isMDXElement, (child) => {
97377
+ if (child.name !== 'thead' && child.name !== 'tbody')
97378
+ return;
97379
+ visit(child, isMDXElement, (row) => {
97380
+ if (row.name !== 'tr')
97381
+ return;
97382
+ const cells = [];
97383
+ visit(row, mdxish_jsx_to_mdast_isTableCell, (cell) => {
97384
+ const parsedChildren = cell.children.flatMap(parsedNode => {
97385
+ if (parsedNode.type === 'paragraph' && 'children' in parsedNode && parsedNode.children) {
97386
+ return parsedNode.children;
97387
+ }
97388
+ return [parsedNode];
97389
+ });
97390
+ cells.push({
97391
+ type: 'tableCell',
97392
+ children: parsedChildren,
97393
+ position: cell.position,
97394
+ });
97395
+ });
97396
+ rows.push({
97397
+ type: 'tableRow',
97398
+ children: cells,
97399
+ position: row.position,
97400
+ });
97401
+ });
97402
+ });
97403
+ const columnCount = rows[0]?.children?.length || 0;
97404
+ const alignArray = align && columnCount > 0
97405
+ ? align.slice(0, columnCount).concat(Array.from({ length: Math.max(0, columnCount - align.length) }, () => null))
97406
+ : Array.from({ length: columnCount }, () => null);
97407
+ return {
97408
+ type: 'table',
97409
+ align: alignArray,
97410
+ position: jsx.position,
97411
+ children: rows,
97412
+ };
97413
+ };
97340
97414
  const COMPONENT_MAP = {
97341
97415
  Callout: transformCallout,
97342
97416
  Embed: transformEmbed,
97343
97417
  Image: transformImage,
97344
97418
  Recipe: transformRecipe,
97419
+ Table: transformTable,
97345
97420
  };
97346
97421
  /**
97347
97422
  * Transform mdxJsxFlowElement nodes and magic block nodes into proper MDAST node types.
@@ -97364,6 +97439,8 @@ const mdxishJsxToMdast = () => tree => {
97364
97439
  if (!transformer)
97365
97440
  return;
97366
97441
  const newNode = transformer(node);
97442
+ if (!newNode)
97443
+ return;
97367
97444
  // Replace the JSX node with the MDAST node
97368
97445
  parent.children[index] = newNode;
97369
97446
  });
@@ -97644,6 +97721,126 @@ function restoreSnakeCase(placeholderName, mapping) {
97644
97721
  return matchingKey ? mapping[matchingKey] : placeholderName;
97645
97722
  }
97646
97723
 
97724
+ ;// ./processor/transform/mdxish/mdxish-tables-to-jsx.ts
97725
+
97726
+
97727
+ const mdxish_tables_to_jsx_alignToStyle = (align) => {
97728
+ if (!align || align === 'left')
97729
+ return null;
97730
+ return {
97731
+ type: 'mdxJsxAttribute',
97732
+ name: 'style',
97733
+ value: {
97734
+ type: 'mdxJsxAttributeValueExpression',
97735
+ value: `{ textAlign: "${align}" }`,
97736
+ },
97737
+ };
97738
+ };
97739
+ const mdxish_tables_to_jsx_isTableCell = (node) => ['tableHead', 'tableCell'].includes(node.type);
97740
+ const mdxish_tables_to_jsx_isLiteral = (node) => 'value' in node;
97741
+ /**
97742
+ * Mdxish-specific version of `tablesToJsx`. Differs from the shared MDX version:
97743
+ *
97744
+ * - Excludes `html` nodes from triggering JSX conversion because raw HTML
97745
+ * inside JSX `<Table>` breaks remarkMdx parsing on the deserialization roundtrip.
97746
+ * - Skips empty cells instead of aborting the entire visit so that flow content
97747
+ * in later cells is still detected.
97748
+ */
97749
+ const mdxishTablesToJsx = () => tree => {
97750
+ visit(tree, (node) => ['table', 'tableau'].includes(node.type), (table, index, parent) => {
97751
+ let hasFlowContent = false;
97752
+ visit(table, mdxish_tables_to_jsx_isTableCell, (cell) => {
97753
+ if (cell.children.length === 0)
97754
+ return;
97755
+ const content = cell.children.length === 1 && cell.children[0].type === 'paragraph'
97756
+ ? cell.children[0].children[0]
97757
+ : cell.children[0];
97758
+ if (!content)
97759
+ return;
97760
+ visit(cell, 'break', (_, breakIndex, breakParent) => {
97761
+ breakParent.children.splice(breakIndex, 1, { type: 'text', value: '\n' });
97762
+ });
97763
+ if (!(phrasing(content) || content.type === 'plain') && content.type !== 'escape') {
97764
+ if (content.type === 'html')
97765
+ return;
97766
+ hasFlowContent = true;
97767
+ return EXIT;
97768
+ }
97769
+ visit(cell, mdxish_tables_to_jsx_isLiteral, (node) => {
97770
+ if (node.value.match(/\n/)) {
97771
+ hasFlowContent = true;
97772
+ return EXIT;
97773
+ }
97774
+ });
97775
+ });
97776
+ if (!hasFlowContent) {
97777
+ table.type = 'table';
97778
+ return;
97779
+ }
97780
+ const styles = table.align.map(mdxish_tables_to_jsx_alignToStyle);
97781
+ const head = {
97782
+ attributes: [],
97783
+ type: 'mdxJsxFlowElement',
97784
+ name: 'thead',
97785
+ children: [
97786
+ {
97787
+ attributes: [],
97788
+ type: 'mdxJsxFlowElement',
97789
+ name: 'tr',
97790
+ children: table.children[0].children.map((cell, cellIndex) => {
97791
+ return {
97792
+ attributes: [],
97793
+ type: 'mdxJsxFlowElement',
97794
+ name: 'th',
97795
+ children: cell.children,
97796
+ ...(styles[cellIndex] && { attributes: [styles[cellIndex]] }),
97797
+ };
97798
+ }),
97799
+ },
97800
+ ],
97801
+ };
97802
+ const body = {
97803
+ attributes: [],
97804
+ type: 'mdxJsxFlowElement',
97805
+ name: 'tbody',
97806
+ children: table.children.splice(1).map(row => {
97807
+ return {
97808
+ attributes: [],
97809
+ type: 'mdxJsxFlowElement',
97810
+ name: 'tr',
97811
+ children: row.children.map((cell, cellIndex) => {
97812
+ return {
97813
+ type: 'mdxJsxFlowElement',
97814
+ name: 'td',
97815
+ children: cell.children,
97816
+ ...(styles[cellIndex] && { attributes: [styles[cellIndex]] }),
97817
+ };
97818
+ }),
97819
+ };
97820
+ }),
97821
+ };
97822
+ const attributes = [
97823
+ {
97824
+ type: 'mdxJsxAttribute',
97825
+ name: 'align',
97826
+ value: {
97827
+ type: 'mdxJsxAttributeValueExpression',
97828
+ value: JSON.stringify(table.align),
97829
+ },
97830
+ },
97831
+ ];
97832
+ const jsx = {
97833
+ type: 'mdxJsxFlowElement',
97834
+ name: 'Table',
97835
+ attributes: table.align.find(a => a) ? attributes : [],
97836
+ children: [head, body],
97837
+ };
97838
+ parent.children[index] = jsx;
97839
+ });
97840
+ return tree;
97841
+ };
97842
+ /* harmony default export */ const mdxish_tables_to_jsx = (mdxishTablesToJsx);
97843
+
97647
97844
  ;// ./processor/transform/mdxish/normalize-table-separator.ts
97648
97845
  /**
97649
97846
  * Preprocessor to normalize malformed GFM table separator syntax.
@@ -99812,6 +100009,8 @@ function loadComponents() {
99812
100009
 
99813
100010
 
99814
100011
 
100012
+
100013
+
99815
100014
 
99816
100015
 
99817
100016
 
@@ -99909,18 +100108,28 @@ function mdxishAstProcessor(mdContent, opts = {}) {
99909
100108
  };
99910
100109
  }
99911
100110
  /**
99912
- * Converts an Mdast to a Markdown string.
100111
+ * Registers the mdx-jsx serialization extension so remark-stringify
100112
+ * can convert JSX nodes (e.g. `<Table>`) to markdown.
100113
+ */
100114
+ function mdxJsxStringify() {
100115
+ const data = this.data();
100116
+ const extensions = data.toMarkdownExtensions || (data.toMarkdownExtensions = []);
100117
+ extensions.push({ extensions: [mdxJsxToMarkdown()] });
100118
+ }
100119
+ /**
100120
+ * Serializes an Mdast back into a markdown string.
99913
100121
  */
99914
100122
  function mdxishMdastToMd(mdast) {
99915
- const md = unified()
100123
+ const processor = unified()
99916
100124
  .use(remarkGfm)
100125
+ .use(mdxish_tables_to_jsx)
99917
100126
  .use(mdxishCompilers)
100127
+ .use(mdxJsxStringify)
99918
100128
  .use(remarkStringify, {
99919
100129
  bullet: '-',
99920
100130
  emphasis: '_',
99921
- })
99922
- .stringify(mdast);
99923
- return md;
100131
+ });
100132
+ return processor.stringify(processor.runSync(mdast));
99924
100133
  }
99925
100134
  /**
99926
100135
  * Processes markdown content with MDX syntax support and returns a HAST.