@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.node.js CHANGED
@@ -24905,10 +24905,19 @@ const TailwindStyle = ({ children, darkModeDataAttribute }) => {
24905
24905
  records.forEach(record => {
24906
24906
  if (record.type === 'childList') {
24907
24907
  record.addedNodes.forEach(node => {
24908
- if (!(node instanceof HTMLElement) || !node.classList.contains(tailwindPrefix))
24908
+ if (!(node instanceof HTMLElement))
24909
24909
  return;
24910
- traverse(node, addClasses);
24911
- shouldUpdate = true;
24910
+ // Check the added node itself
24911
+ if (node.classList.contains(tailwindPrefix)) {
24912
+ traverse(node, addClasses);
24913
+ shouldUpdate = true;
24914
+ }
24915
+ // Also check descendants — React may insert a parent node
24916
+ // whose children contain TailwindRoot elements
24917
+ node.querySelectorAll(`.${tailwindPrefix}`).forEach(child => {
24918
+ traverse(child, addClasses);
24919
+ shouldUpdate = true;
24920
+ });
24912
24921
  });
24913
24922
  }
24914
24923
  else if (record.type === 'attributes') {
@@ -90604,9 +90613,416 @@ const mdast = (text, opts = {}) => {
90604
90613
  };
90605
90614
  /* harmony default export */ const lib_mdast = (mdast);
90606
90615
 
90616
+ ;// ./lib/utils/mdxish/protect-code-blocks.ts
90617
+ /**
90618
+ * Replaces code blocks and inline code with placeholders to protect them from preprocessing.
90619
+ *
90620
+ * @param content - The markdown content to process
90621
+ * @returns Object containing protected content and arrays of original code blocks
90622
+ * @example
90623
+ * ```typescript
90624
+ * const input = 'Text with `inline code` and ```fenced block```';
90625
+ * protectCodeBlocks(input)
90626
+ * // Returns: {
90627
+ * // protectedCode: {
90628
+ * // codeBlocks: ['```fenced block```'],
90629
+ * // inlineCode: ['`inline code`']
90630
+ * // },
90631
+ * // protectedContent: 'Text with ___INLINE_CODE_0___ and ___CODE_BLOCK_0___'
90632
+ * // }
90633
+ * ```
90634
+ */
90635
+ function protectCodeBlocks(content) {
90636
+ const codeBlocks = [];
90637
+ const inlineCode = [];
90638
+ let protectedContent = '';
90639
+ let remaining = content;
90640
+ let codeBlockStart = remaining.indexOf('```');
90641
+ while (codeBlockStart !== -1) {
90642
+ protectedContent += remaining.slice(0, codeBlockStart);
90643
+ remaining = remaining.slice(codeBlockStart);
90644
+ const codeBlockEnd = remaining.indexOf('```', 3);
90645
+ if (codeBlockEnd === -1) {
90646
+ break;
90647
+ }
90648
+ const match = remaining.slice(0, codeBlockEnd + 3);
90649
+ const index = codeBlocks.length;
90650
+ codeBlocks.push(match);
90651
+ protectedContent += `___CODE_BLOCK_${index}___`;
90652
+ remaining = remaining.slice(codeBlockEnd + 3);
90653
+ codeBlockStart = remaining.indexOf('```');
90654
+ }
90655
+ protectedContent += remaining;
90656
+ protectedContent = protectedContent.replace(/`([^`\n]+)`/g, match => {
90657
+ const index = inlineCode.length;
90658
+ inlineCode.push(match);
90659
+ return `___INLINE_CODE_${index}___`;
90660
+ });
90661
+ return { protectedCode: { codeBlocks, inlineCode }, protectedContent };
90662
+ }
90663
+ /**
90664
+ * Restores inline code by replacing placeholders with original content.
90665
+ *
90666
+ * @param content - Content with inline code placeholders
90667
+ * @param protectedCode - The protected code arrays
90668
+ * @returns Content with inline code restored
90669
+ */
90670
+ function restoreInlineCode(content, protectedCode) {
90671
+ return content.replace(/___INLINE_CODE_(\d+)___/g, (_m, idx) => {
90672
+ return protectedCode.inlineCode[parseInt(idx, 10)];
90673
+ });
90674
+ }
90675
+ /**
90676
+ * Restores fenced code blocks by replacing placeholders with original content.
90677
+ *
90678
+ * @param content - Content with code block placeholders
90679
+ * @param protectedCode - The protected code arrays
90680
+ * @returns Content with code blocks restored
90681
+ */
90682
+ function restoreFencedCodeBlocks(content, protectedCode) {
90683
+ return content.replace(/___CODE_BLOCK_(\d+)___/g, (_m, idx) => {
90684
+ return protectedCode.codeBlocks[parseInt(idx, 10)];
90685
+ });
90686
+ }
90687
+ /**
90688
+ * Restores all code blocks and inline code by replacing placeholders with original content.
90689
+ *
90690
+ * @param content - Content with code placeholders
90691
+ * @param protectedCode - The protected code arrays
90692
+ * @returns Content with all code blocks and inline code restored
90693
+ * @example
90694
+ * ```typescript
90695
+ * const content = 'Text with ___INLINE_CODE_0___ and ___CODE_BLOCK_0___';
90696
+ * const protectedCode = {
90697
+ * codeBlocks: ['```js\ncode\n```'],
90698
+ * inlineCode: ['`inline`']
90699
+ * };
90700
+ * restoreCodeBlocks(content, protectedCode)
90701
+ * // Returns: 'Text with `inline` and ```js\ncode\n```'
90702
+ * ```
90703
+ */
90704
+ function restoreCodeBlocks(content, protectedCode) {
90705
+ let restored = restoreFencedCodeBlocks(content, protectedCode);
90706
+ restored = restoreInlineCode(restored, protectedCode);
90707
+ return restored;
90708
+ }
90709
+
90710
+ ;// ./processor/transform/mdxish/preprocess-jsx-expressions.ts
90711
+
90712
+ // Base64 encode (Node.js + browser compatible)
90713
+ function base64Encode(str) {
90714
+ if (typeof Buffer !== 'undefined') {
90715
+ return Buffer.from(str, 'utf-8').toString('base64');
90716
+ }
90717
+ return btoa(unescape(encodeURIComponent(str)));
90718
+ }
90719
+ // Base64 decode (Node.js + browser compatible)
90720
+ function base64Decode(str) {
90721
+ if (typeof Buffer !== 'undefined') {
90722
+ return Buffer.from(str, 'base64').toString('utf-8');
90723
+ }
90724
+ return decodeURIComponent(escape(atob(str)));
90725
+ }
90726
+ function escapeHtmlAttribute(value) {
90727
+ return value
90728
+ .replace(/&/g, '&')
90729
+ .replace(/"/g, '"')
90730
+ .replace(/</g, '&lt;')
90731
+ .replace(/>/g, '&gt;')
90732
+ .replace(/\n/g, '&#10;');
90733
+ }
90734
+ // Marker prefix for JSON-serialized complex values (arrays/objects)
90735
+ // Using a prefix that won't conflict with regular string values
90736
+ const JSON_VALUE_MARKER = '__MDXISH_JSON__';
90737
+ // Markers for protected HTMLBlock content (HTML comments avoid markdown parsing issues)
90738
+ const HTML_BLOCK_CONTENT_START = '<!--RDMX_HTMLBLOCK:';
90739
+ const HTML_BLOCK_CONTENT_END = ':RDMX_HTMLBLOCK-->';
90740
+ /**
90741
+ * Evaluates a JavaScript expression using context variables.
90742
+ *
90743
+ * @param expression
90744
+ * @param context
90745
+ * @returns The evaluated result
90746
+ * @example
90747
+ * ```typescript
90748
+ * const context = { baseUrl: 'https://example.com', path: '/api' };
90749
+ * evaluateExpression('baseUrl + path', context)
90750
+ * // Returns: 'https://example.com/api'
90751
+ * ```
90752
+ */
90753
+ function evaluateExpression(expression, context) {
90754
+ const contextKeys = Object.keys(context);
90755
+ const contextValues = Object.values(context);
90756
+ // eslint-disable-next-line no-new-func
90757
+ const func = new Function(...contextKeys, `return ${expression}`);
90758
+ return func(...contextValues);
90759
+ }
90760
+ /**
90761
+ * Base64 encodes HTMLBlock template literal content to prevent markdown parser from consuming <script>/<style> tags.
90762
+ *
90763
+ * @param content
90764
+ * @returns Content with HTMLBlock template literals base64 encoded in HTML comments
90765
+ * @example
90766
+ * ```typescript
90767
+ * const input = '<HTMLBlock>{`<script>alert("xss")</script>`}</HTMLBlock>';
90768
+ * protectHTMLBlockContent(input)
90769
+ * // Returns: '<HTMLBlock><!--RDMX_HTMLBLOCK:PHNjcmlwdD5hbGVydCgieHNzIik8L3NjcmlwdD4=:RDMX_HTMLBLOCK--></HTMLBlock>'
90770
+ * ```
90771
+ */
90772
+ function protectHTMLBlockContent(content) {
90773
+ return content.replace(/(<HTMLBlock[^>]*>)\{\s*`((?:[^`\\]|\\.)*)`\s*\}(<\/HTMLBlock>)/g, (_match, openTag, templateContent, closeTag) => {
90774
+ const encoded = base64Encode(templateContent);
90775
+ return `${openTag}${HTML_BLOCK_CONTENT_START}${encoded}${HTML_BLOCK_CONTENT_END}${closeTag}`;
90776
+ });
90777
+ }
90778
+ /**
90779
+ * Removes JSX-style comments (e.g., { /* comment *\/ }) from content.
90780
+ *
90781
+ * @param content
90782
+ * @returns Content with JSX comments removed
90783
+ * @example
90784
+ * ```typescript
90785
+ * removeJSXComments('Text { /* comment *\/ } more text')
90786
+ * // Returns: 'Text more text'
90787
+ * ```
90788
+ */
90789
+ function removeJSXComments(content) {
90790
+ return content.replace(/\{\s*\/\*[^*]*(?:\*(?!\/)[^*]*)*\*\/\s*\}/g, '');
90791
+ }
90792
+ /**
90793
+ * Extracts content between balanced braces, handling nested braces.
90794
+ *
90795
+ * @param content
90796
+ * @param start
90797
+ * @returns Object with extracted content and end position, or null if braces are unbalanced
90798
+ * @example
90799
+ * ```typescript
90800
+ * const input = 'foo{bar{baz}qux}end';
90801
+ * extractBalancedBraces(input, 3) // start at position 3 (after '{')
90802
+ * // Returns: { content: 'bar{baz}qux', end: 16 }
90803
+ * ```
90804
+ */
90805
+ function extractBalancedBraces(content, start) {
90806
+ let depth = 1;
90807
+ let pos = start;
90808
+ while (pos < content.length && depth > 0) {
90809
+ const char = content[pos];
90810
+ if (char === '{')
90811
+ depth += 1;
90812
+ else if (char === '}')
90813
+ depth -= 1;
90814
+ pos += 1;
90815
+ }
90816
+ if (depth !== 0)
90817
+ return null;
90818
+ return { content: content.slice(start, pos - 1), end: pos };
90819
+ }
90820
+ /**
90821
+ * Escapes problematic braces in content to prevent MDX expression parsing errors.
90822
+ * Handles three cases:
90823
+ * 1. Unbalanced braces (e.g., `{foo` without closing `}`)
90824
+ * 2. Paragraph-spanning expressions (e.g., `{\n\n}` where blank line splits paragraphs)
90825
+ * 3. Skips HTML elements to prevent backslashes appearing in output
90826
+ *
90827
+ */
90828
+ function escapeProblematicBraces(content) {
90829
+ // Skip HTML elements — their content should never be escaped because
90830
+ // rehypeRaw parses them into hast elements, making `\` literal text in output
90831
+ const htmlElements = [];
90832
+ const safe = content.replace(/<([a-z][a-zA-Z0-9]*)(?:\s[^>]*)?>[\s\S]*?<\/\1>/g, match => {
90833
+ const idx = htmlElements.length;
90834
+ htmlElements.push(match);
90835
+ return `___HTML_ELEM_${idx}___`;
90836
+ });
90837
+ const toEscape = new Set();
90838
+ // Convert to array of Unicode code points to handle emojis and multi-byte characters correctly
90839
+ const chars = Array.from(safe);
90840
+ let strDelim = null;
90841
+ let strEscaped = false;
90842
+ // Stack of open braces with their state
90843
+ const openStack = [];
90844
+ // Track position of last newline (outside strings) to detect blank lines
90845
+ let lastNewlinePos = -2; // -2 means no recent newline
90846
+ for (let i = 0; i < chars.length; i += 1) {
90847
+ const ch = chars[i];
90848
+ // Track string delimiters inside expressions to ignore braces within them
90849
+ if (openStack.length > 0) {
90850
+ if (strDelim) {
90851
+ if (strEscaped)
90852
+ strEscaped = false;
90853
+ else if (ch === '\\')
90854
+ strEscaped = true;
90855
+ else if (ch === strDelim)
90856
+ strDelim = null;
90857
+ // eslint-disable-next-line no-continue
90858
+ continue;
90859
+ }
90860
+ if (ch === '"' || ch === "'" || ch === '`') {
90861
+ strDelim = ch;
90862
+ // eslint-disable-next-line no-continue
90863
+ continue;
90864
+ }
90865
+ // Track newlines to detect blank lines (paragraph boundaries)
90866
+ if (ch === '\n') {
90867
+ // Check if this newline creates a blank line (only whitespace since last newline)
90868
+ if (lastNewlinePos >= 0) {
90869
+ const between = chars.slice(lastNewlinePos + 1, i).join('');
90870
+ if (/^[ \t]*$/.test(between)) {
90871
+ // This is a blank line - mark all open expressions as paragraph-spanning
90872
+ openStack.forEach(entry => {
90873
+ entry.hasBlankLine = true;
90874
+ });
90875
+ }
90876
+ }
90877
+ lastNewlinePos = i;
90878
+ }
90879
+ }
90880
+ // Skip already-escaped braces (count preceding backslashes)
90881
+ if (ch === '{' || ch === '}') {
90882
+ let bs = 0;
90883
+ for (let j = i - 1; j >= 0 && chars[j] === '\\'; j -= 1)
90884
+ bs += 1;
90885
+ if (bs % 2 === 1) {
90886
+ // eslint-disable-next-line no-continue
90887
+ continue;
90888
+ }
90889
+ }
90890
+ if (ch === '{') {
90891
+ openStack.push({ pos: i, hasBlankLine: false });
90892
+ lastNewlinePos = -2; // Reset newline tracking for new expression
90893
+ }
90894
+ else if (ch === '}') {
90895
+ if (openStack.length > 0) {
90896
+ const entry = openStack.pop();
90897
+ // If expression spans paragraph boundary, escape both braces
90898
+ if (entry.hasBlankLine) {
90899
+ toEscape.add(entry.pos);
90900
+ toEscape.add(i);
90901
+ }
90902
+ }
90903
+ else {
90904
+ // Unbalanced closing brace (no matching open)
90905
+ toEscape.add(i);
90906
+ }
90907
+ }
90908
+ }
90909
+ // Any remaining open braces are unbalanced
90910
+ openStack.forEach(entry => toEscape.add(entry.pos));
90911
+ // If there are no problematic braces, return safe content as-is;
90912
+ // otherwise, escape each problematic `{` or `}` so MDX doesn't treat them as expressions.
90913
+ let result = toEscape.size === 0
90914
+ ? safe
90915
+ : chars.map((ch, i) => (toEscape.has(i) ? `\\${ch}` : ch)).join('');
90916
+ // Restore HTML elements
90917
+ if (htmlElements.length > 0) {
90918
+ result = result.replace(/___HTML_ELEM_(\d+)___/g, (_m, idx) => htmlElements[parseInt(idx, 10)]);
90919
+ }
90920
+ return result;
90921
+ }
90922
+ /**
90923
+ * Converts JSX attribute expressions (attribute={expression}) to HTML attributes (attribute="value").
90924
+ * Handles style objects (camelCase → kebab-case), className → class, and JSON stringifies objects.
90925
+ *
90926
+ * @param content
90927
+ * @param context
90928
+ * @returns Content with attribute expressions evaluated and converted to HTML attributes
90929
+ * @example
90930
+ * ```typescript
90931
+ * const context = { baseUrl: 'https://example.com' };
90932
+ * const input = '<a href={baseUrl}>Link</a>';
90933
+ * evaluateAttributeExpressions(input, context)
90934
+ * // Returns: '<a href="https://example.com">Link</a>'
90935
+ * ```
90936
+ */
90937
+ function evaluateAttributeExpressions(content, context, protectedCode) {
90938
+ const attrStartRegex = /(\w+)=\{/g;
90939
+ let result = '';
90940
+ let lastEnd = 0;
90941
+ let match = attrStartRegex.exec(content);
90942
+ while (match !== null) {
90943
+ const attributeName = match[1];
90944
+ const braceStart = match.index + match[0].length;
90945
+ const extracted = extractBalancedBraces(content, braceStart);
90946
+ if (extracted) {
90947
+ // The expression might contain template literals in MDX component tag props
90948
+ // E.g. <Component greeting={`Hello World!`} />
90949
+ // that is marked as inline code. So we need to restore the inline codes
90950
+ // in the expression to evaluate it
90951
+ let expression = extracted.content;
90952
+ if (protectedCode) {
90953
+ expression = restoreInlineCode(expression, protectedCode);
90954
+ }
90955
+ const fullMatchEnd = extracted.end;
90956
+ result += content.slice(lastEnd, match.index);
90957
+ try {
90958
+ const evalResult = evaluateExpression(expression, context);
90959
+ if (typeof evalResult === 'object' && evalResult !== null) {
90960
+ if (attributeName === 'style') {
90961
+ const cssString = Object.entries(evalResult)
90962
+ .map(([key, value]) => {
90963
+ const cssKey = key.replace(/([A-Z])/g, '-$1').toLowerCase();
90964
+ return `${cssKey}: ${value}`;
90965
+ })
90966
+ .join('; ');
90967
+ result += `style="${cssString}"`;
90968
+ }
90969
+ else {
90970
+ // These are arrays / objects attribute values
90971
+ // Mark JSON-serialized values with a prefix so they can be parsed back correctly
90972
+ const jsonValue = escapeHtmlAttribute(JSON_VALUE_MARKER + JSON.stringify(evalResult));
90973
+ // Use double quotes so that multi-paragraph values are not split into multiple attributes by the processors
90974
+ result += `${attributeName}="${jsonValue}"`;
90975
+ }
90976
+ }
90977
+ else if (attributeName === 'className') {
90978
+ // Escape special characters so that it doesn't break and split the attribute value to nodes
90979
+ // This will be restored later in the pipeline
90980
+ result += `class="${escapeHtmlAttribute(String(evalResult))}"`;
90981
+ }
90982
+ else {
90983
+ result += `${attributeName}="${escapeHtmlAttribute(String(evalResult))}"`;
90984
+ }
90985
+ }
90986
+ catch (_error) {
90987
+ result += content.slice(match.index, fullMatchEnd);
90988
+ }
90989
+ lastEnd = fullMatchEnd;
90990
+ attrStartRegex.lastIndex = fullMatchEnd;
90991
+ }
90992
+ match = attrStartRegex.exec(content);
90993
+ }
90994
+ result += content.slice(lastEnd);
90995
+ return result;
90996
+ }
90997
+ /**
90998
+ * Preprocesses JSX-like expressions in markdown before parsing.
90999
+ * Inline expressions are handled separately; attribute expressions are processed here.
91000
+ *
91001
+ * @param content
91002
+ * @param context
91003
+ * @returns Preprocessed content ready for markdown parsing
91004
+ */
91005
+ function preprocessJSXExpressions(content, context = {}) {
91006
+ // Step 0: Base64 encode HTMLBlock content
91007
+ let processed = protectHTMLBlockContent(content);
91008
+ // Step 1: Protect code blocks and inline code
91009
+ const { protectedCode, protectedContent } = protectCodeBlocks(processed);
91010
+ // Step 2: Evaluate attribute expressions (JSX attribute syntax: href={baseUrl})
91011
+ // For inline expressions, we use a library to parse the expression & evaluate it later
91012
+ // For attribute expressions, it was difficult to use a library to parse them, so do it manually
91013
+ processed = evaluateAttributeExpressions(protectedContent, context, protectedCode);
91014
+ // Step 3: Escape problematic braces to prevent MDX expression parsing errors
91015
+ // This handles both unbalanced braces and paragraph-spanning expressions in one pass
91016
+ processed = escapeProblematicBraces(processed);
91017
+ // Step 4: Restore protected code blocks
91018
+ processed = restoreCodeBlocks(processed, protectedCode);
91019
+ return processed;
91020
+ }
91021
+
90607
91022
  ;// ./processor/utils.ts
90608
91023
 
90609
91024
 
91025
+
90610
91026
  /**
90611
91027
  * Formats the hProperties of a node as a string, so they can be compiled back into JSX/MDX.
90612
91028
  * This currently sets all the values to a string since we process/compile the MDX on the fly
@@ -90662,7 +91078,17 @@ const getHPropKeys = (node) => {
90662
91078
  const getAttrs = (jsx) => jsx.attributes.reduce((memo, attr) => {
90663
91079
  if ('name' in attr) {
90664
91080
  if (typeof attr.value === 'string') {
90665
- memo[attr.name] = attr.value;
91081
+ if (attr.value.startsWith(JSON_VALUE_MARKER)) {
91082
+ try {
91083
+ memo[attr.name] = JSON.parse(attr.value.slice(JSON_VALUE_MARKER.length));
91084
+ }
91085
+ catch {
91086
+ memo[attr.name] = attr.value;
91087
+ }
91088
+ }
91089
+ else {
91090
+ memo[attr.name] = attr.value;
91091
+ }
90666
91092
  }
90667
91093
  else if (attr.value === null) {
90668
91094
  memo[attr.name] = true;
@@ -91436,10 +91862,10 @@ const hasFlowContent = (nodes) => {
91436
91862
  * Process a Table node: re-parse text-only cell content, then output as
91437
91863
  * a markdown table (phrasing-only) or keep as JSX <Table> (has flow content).
91438
91864
  */
91439
- const processTableNode = (node, index, parent) => {
91865
+ const processTableNode = (node, index, parent, documentPosition) => {
91440
91866
  if (node.name !== 'Table')
91441
91867
  return;
91442
- const { position } = node;
91868
+ const position = documentPosition ?? node.position;
91443
91869
  const { align: alignAttr } = getAttrs(node);
91444
91870
  const align = Array.isArray(alignAttr) ? alignAttr : null;
91445
91871
  let tableHasFlowContent = false;
@@ -91562,7 +91988,7 @@ const mdxishTables = () => tree => {
91562
91988
  const parsed = tableNodeProcessor.runSync(tableNodeProcessor.parse(node.value));
91563
91989
  visit(parsed, isMDXElement, (tableNode) => {
91564
91990
  if (tableNode.name === 'Table') {
91565
- processTableNode(tableNode, index, parent);
91991
+ processTableNode(tableNode, index, parent, node.position);
91566
91992
  // Stop after the outermost Table so nested Tables don't overwrite parent.children[index]
91567
91993
  // we let it get handled naturally
91568
91994
  return EXIT;
@@ -114037,412 +114463,6 @@ const mdxComponentHandlers = {
114037
114463
  [NodeTypes.htmlBlock]: htmlBlockHandler,
114038
114464
  };
114039
114465
 
114040
- ;// ./lib/utils/mdxish/protect-code-blocks.ts
114041
- /**
114042
- * Replaces code blocks and inline code with placeholders to protect them from preprocessing.
114043
- *
114044
- * @param content - The markdown content to process
114045
- * @returns Object containing protected content and arrays of original code blocks
114046
- * @example
114047
- * ```typescript
114048
- * const input = 'Text with `inline code` and ```fenced block```';
114049
- * protectCodeBlocks(input)
114050
- * // Returns: {
114051
- * // protectedCode: {
114052
- * // codeBlocks: ['```fenced block```'],
114053
- * // inlineCode: ['`inline code`']
114054
- * // },
114055
- * // protectedContent: 'Text with ___INLINE_CODE_0___ and ___CODE_BLOCK_0___'
114056
- * // }
114057
- * ```
114058
- */
114059
- function protectCodeBlocks(content) {
114060
- const codeBlocks = [];
114061
- const inlineCode = [];
114062
- let protectedContent = '';
114063
- let remaining = content;
114064
- let codeBlockStart = remaining.indexOf('```');
114065
- while (codeBlockStart !== -1) {
114066
- protectedContent += remaining.slice(0, codeBlockStart);
114067
- remaining = remaining.slice(codeBlockStart);
114068
- const codeBlockEnd = remaining.indexOf('```', 3);
114069
- if (codeBlockEnd === -1) {
114070
- break;
114071
- }
114072
- const match = remaining.slice(0, codeBlockEnd + 3);
114073
- const index = codeBlocks.length;
114074
- codeBlocks.push(match);
114075
- protectedContent += `___CODE_BLOCK_${index}___`;
114076
- remaining = remaining.slice(codeBlockEnd + 3);
114077
- codeBlockStart = remaining.indexOf('```');
114078
- }
114079
- protectedContent += remaining;
114080
- protectedContent = protectedContent.replace(/`([^`\n]+)`/g, match => {
114081
- const index = inlineCode.length;
114082
- inlineCode.push(match);
114083
- return `___INLINE_CODE_${index}___`;
114084
- });
114085
- return { protectedCode: { codeBlocks, inlineCode }, protectedContent };
114086
- }
114087
- /**
114088
- * Restores inline code by replacing placeholders with original content.
114089
- *
114090
- * @param content - Content with inline code placeholders
114091
- * @param protectedCode - The protected code arrays
114092
- * @returns Content with inline code restored
114093
- */
114094
- function restoreInlineCode(content, protectedCode) {
114095
- return content.replace(/___INLINE_CODE_(\d+)___/g, (_m, idx) => {
114096
- return protectedCode.inlineCode[parseInt(idx, 10)];
114097
- });
114098
- }
114099
- /**
114100
- * Restores fenced code blocks by replacing placeholders with original content.
114101
- *
114102
- * @param content - Content with code block placeholders
114103
- * @param protectedCode - The protected code arrays
114104
- * @returns Content with code blocks restored
114105
- */
114106
- function restoreFencedCodeBlocks(content, protectedCode) {
114107
- return content.replace(/___CODE_BLOCK_(\d+)___/g, (_m, idx) => {
114108
- return protectedCode.codeBlocks[parseInt(idx, 10)];
114109
- });
114110
- }
114111
- /**
114112
- * Restores all code blocks and inline code by replacing placeholders with original content.
114113
- *
114114
- * @param content - Content with code placeholders
114115
- * @param protectedCode - The protected code arrays
114116
- * @returns Content with all code blocks and inline code restored
114117
- * @example
114118
- * ```typescript
114119
- * const content = 'Text with ___INLINE_CODE_0___ and ___CODE_BLOCK_0___';
114120
- * const protectedCode = {
114121
- * codeBlocks: ['```js\ncode\n```'],
114122
- * inlineCode: ['`inline`']
114123
- * };
114124
- * restoreCodeBlocks(content, protectedCode)
114125
- * // Returns: 'Text with `inline` and ```js\ncode\n```'
114126
- * ```
114127
- */
114128
- function restoreCodeBlocks(content, protectedCode) {
114129
- let restored = restoreFencedCodeBlocks(content, protectedCode);
114130
- restored = restoreInlineCode(restored, protectedCode);
114131
- return restored;
114132
- }
114133
-
114134
- ;// ./processor/transform/mdxish/preprocess-jsx-expressions.ts
114135
-
114136
- // Base64 encode (Node.js + browser compatible)
114137
- function base64Encode(str) {
114138
- if (typeof Buffer !== 'undefined') {
114139
- return Buffer.from(str, 'utf-8').toString('base64');
114140
- }
114141
- return btoa(unescape(encodeURIComponent(str)));
114142
- }
114143
- // Base64 decode (Node.js + browser compatible)
114144
- function base64Decode(str) {
114145
- if (typeof Buffer !== 'undefined') {
114146
- return Buffer.from(str, 'base64').toString('utf-8');
114147
- }
114148
- return decodeURIComponent(escape(atob(str)));
114149
- }
114150
- function escapeHtmlAttribute(value) {
114151
- return value
114152
- .replace(/&/g, '&amp;')
114153
- .replace(/"/g, '&quot;')
114154
- .replace(/</g, '&lt;')
114155
- .replace(/>/g, '&gt;')
114156
- .replace(/\n/g, '&#10;');
114157
- }
114158
- // Marker prefix for JSON-serialized complex values (arrays/objects)
114159
- // Using a prefix that won't conflict with regular string values
114160
- const JSON_VALUE_MARKER = '__MDXISH_JSON__';
114161
- // Markers for protected HTMLBlock content (HTML comments avoid markdown parsing issues)
114162
- const HTML_BLOCK_CONTENT_START = '<!--RDMX_HTMLBLOCK:';
114163
- const HTML_BLOCK_CONTENT_END = ':RDMX_HTMLBLOCK-->';
114164
- /**
114165
- * Evaluates a JavaScript expression using context variables.
114166
- *
114167
- * @param expression
114168
- * @param context
114169
- * @returns The evaluated result
114170
- * @example
114171
- * ```typescript
114172
- * const context = { baseUrl: 'https://example.com', path: '/api' };
114173
- * evaluateExpression('baseUrl + path', context)
114174
- * // Returns: 'https://example.com/api'
114175
- * ```
114176
- */
114177
- function evaluateExpression(expression, context) {
114178
- const contextKeys = Object.keys(context);
114179
- const contextValues = Object.values(context);
114180
- // eslint-disable-next-line no-new-func
114181
- const func = new Function(...contextKeys, `return ${expression}`);
114182
- return func(...contextValues);
114183
- }
114184
- /**
114185
- * Base64 encodes HTMLBlock template literal content to prevent markdown parser from consuming <script>/<style> tags.
114186
- *
114187
- * @param content
114188
- * @returns Content with HTMLBlock template literals base64 encoded in HTML comments
114189
- * @example
114190
- * ```typescript
114191
- * const input = '<HTMLBlock>{`<script>alert("xss")</script>`}</HTMLBlock>';
114192
- * protectHTMLBlockContent(input)
114193
- * // Returns: '<HTMLBlock><!--RDMX_HTMLBLOCK:PHNjcmlwdD5hbGVydCgieHNzIik8L3NjcmlwdD4=:RDMX_HTMLBLOCK--></HTMLBlock>'
114194
- * ```
114195
- */
114196
- function protectHTMLBlockContent(content) {
114197
- return content.replace(/(<HTMLBlock[^>]*>)\{\s*`((?:[^`\\]|\\.)*)`\s*\}(<\/HTMLBlock>)/g, (_match, openTag, templateContent, closeTag) => {
114198
- const encoded = base64Encode(templateContent);
114199
- return `${openTag}${HTML_BLOCK_CONTENT_START}${encoded}${HTML_BLOCK_CONTENT_END}${closeTag}`;
114200
- });
114201
- }
114202
- /**
114203
- * Removes JSX-style comments (e.g., { /* comment *\/ }) from content.
114204
- *
114205
- * @param content
114206
- * @returns Content with JSX comments removed
114207
- * @example
114208
- * ```typescript
114209
- * removeJSXComments('Text { /* comment *\/ } more text')
114210
- * // Returns: 'Text more text'
114211
- * ```
114212
- */
114213
- function removeJSXComments(content) {
114214
- return content.replace(/\{\s*\/\*[^*]*(?:\*(?!\/)[^*]*)*\*\/\s*\}/g, '');
114215
- }
114216
- /**
114217
- * Extracts content between balanced braces, handling nested braces.
114218
- *
114219
- * @param content
114220
- * @param start
114221
- * @returns Object with extracted content and end position, or null if braces are unbalanced
114222
- * @example
114223
- * ```typescript
114224
- * const input = 'foo{bar{baz}qux}end';
114225
- * extractBalancedBraces(input, 3) // start at position 3 (after '{')
114226
- * // Returns: { content: 'bar{baz}qux', end: 16 }
114227
- * ```
114228
- */
114229
- function extractBalancedBraces(content, start) {
114230
- let depth = 1;
114231
- let pos = start;
114232
- while (pos < content.length && depth > 0) {
114233
- const char = content[pos];
114234
- if (char === '{')
114235
- depth += 1;
114236
- else if (char === '}')
114237
- depth -= 1;
114238
- pos += 1;
114239
- }
114240
- if (depth !== 0)
114241
- return null;
114242
- return { content: content.slice(start, pos - 1), end: pos };
114243
- }
114244
- /**
114245
- * Escapes problematic braces in content to prevent MDX expression parsing errors.
114246
- * Handles three cases:
114247
- * 1. Unbalanced braces (e.g., `{foo` without closing `}`)
114248
- * 2. Paragraph-spanning expressions (e.g., `{\n\n}` where blank line splits paragraphs)
114249
- * 3. Skips HTML elements to prevent backslashes appearing in output
114250
- *
114251
- */
114252
- function escapeProblematicBraces(content) {
114253
- // Skip HTML elements — their content should never be escaped because
114254
- // rehypeRaw parses them into hast elements, making `\` literal text in output
114255
- const htmlElements = [];
114256
- const safe = content.replace(/<([a-z][a-zA-Z0-9]*)(?:\s[^>]*)?>[\s\S]*?<\/\1>/g, match => {
114257
- const idx = htmlElements.length;
114258
- htmlElements.push(match);
114259
- return `___HTML_ELEM_${idx}___`;
114260
- });
114261
- const toEscape = new Set();
114262
- // Convert to array of Unicode code points to handle emojis and multi-byte characters correctly
114263
- const chars = Array.from(safe);
114264
- let strDelim = null;
114265
- let strEscaped = false;
114266
- // Stack of open braces with their state
114267
- const openStack = [];
114268
- // Track position of last newline (outside strings) to detect blank lines
114269
- let lastNewlinePos = -2; // -2 means no recent newline
114270
- for (let i = 0; i < chars.length; i += 1) {
114271
- const ch = chars[i];
114272
- // Track string delimiters inside expressions to ignore braces within them
114273
- if (openStack.length > 0) {
114274
- if (strDelim) {
114275
- if (strEscaped)
114276
- strEscaped = false;
114277
- else if (ch === '\\')
114278
- strEscaped = true;
114279
- else if (ch === strDelim)
114280
- strDelim = null;
114281
- // eslint-disable-next-line no-continue
114282
- continue;
114283
- }
114284
- if (ch === '"' || ch === "'" || ch === '`') {
114285
- strDelim = ch;
114286
- // eslint-disable-next-line no-continue
114287
- continue;
114288
- }
114289
- // Track newlines to detect blank lines (paragraph boundaries)
114290
- if (ch === '\n') {
114291
- // Check if this newline creates a blank line (only whitespace since last newline)
114292
- if (lastNewlinePos >= 0) {
114293
- const between = chars.slice(lastNewlinePos + 1, i).join('');
114294
- if (/^[ \t]*$/.test(between)) {
114295
- // This is a blank line - mark all open expressions as paragraph-spanning
114296
- openStack.forEach(entry => {
114297
- entry.hasBlankLine = true;
114298
- });
114299
- }
114300
- }
114301
- lastNewlinePos = i;
114302
- }
114303
- }
114304
- // Skip already-escaped braces (count preceding backslashes)
114305
- if (ch === '{' || ch === '}') {
114306
- let bs = 0;
114307
- for (let j = i - 1; j >= 0 && chars[j] === '\\'; j -= 1)
114308
- bs += 1;
114309
- if (bs % 2 === 1) {
114310
- // eslint-disable-next-line no-continue
114311
- continue;
114312
- }
114313
- }
114314
- if (ch === '{') {
114315
- openStack.push({ pos: i, hasBlankLine: false });
114316
- lastNewlinePos = -2; // Reset newline tracking for new expression
114317
- }
114318
- else if (ch === '}') {
114319
- if (openStack.length > 0) {
114320
- const entry = openStack.pop();
114321
- // If expression spans paragraph boundary, escape both braces
114322
- if (entry.hasBlankLine) {
114323
- toEscape.add(entry.pos);
114324
- toEscape.add(i);
114325
- }
114326
- }
114327
- else {
114328
- // Unbalanced closing brace (no matching open)
114329
- toEscape.add(i);
114330
- }
114331
- }
114332
- }
114333
- // Any remaining open braces are unbalanced
114334
- openStack.forEach(entry => toEscape.add(entry.pos));
114335
- // If there are no problematic braces, return safe content as-is;
114336
- // otherwise, escape each problematic `{` or `}` so MDX doesn't treat them as expressions.
114337
- let result = toEscape.size === 0
114338
- ? safe
114339
- : chars.map((ch, i) => (toEscape.has(i) ? `\\${ch}` : ch)).join('');
114340
- // Restore HTML elements
114341
- if (htmlElements.length > 0) {
114342
- result = result.replace(/___HTML_ELEM_(\d+)___/g, (_m, idx) => htmlElements[parseInt(idx, 10)]);
114343
- }
114344
- return result;
114345
- }
114346
- /**
114347
- * Converts JSX attribute expressions (attribute={expression}) to HTML attributes (attribute="value").
114348
- * Handles style objects (camelCase → kebab-case), className → class, and JSON stringifies objects.
114349
- *
114350
- * @param content
114351
- * @param context
114352
- * @returns Content with attribute expressions evaluated and converted to HTML attributes
114353
- * @example
114354
- * ```typescript
114355
- * const context = { baseUrl: 'https://example.com' };
114356
- * const input = '<a href={baseUrl}>Link</a>';
114357
- * evaluateAttributeExpressions(input, context)
114358
- * // Returns: '<a href="https://example.com">Link</a>'
114359
- * ```
114360
- */
114361
- function evaluateAttributeExpressions(content, context, protectedCode) {
114362
- const attrStartRegex = /(\w+)=\{/g;
114363
- let result = '';
114364
- let lastEnd = 0;
114365
- let match = attrStartRegex.exec(content);
114366
- while (match !== null) {
114367
- const attributeName = match[1];
114368
- const braceStart = match.index + match[0].length;
114369
- const extracted = extractBalancedBraces(content, braceStart);
114370
- if (extracted) {
114371
- // The expression might contain template literals in MDX component tag props
114372
- // E.g. <Component greeting={`Hello World!`} />
114373
- // that is marked as inline code. So we need to restore the inline codes
114374
- // in the expression to evaluate it
114375
- let expression = extracted.content;
114376
- if (protectedCode) {
114377
- expression = restoreInlineCode(expression, protectedCode);
114378
- }
114379
- const fullMatchEnd = extracted.end;
114380
- result += content.slice(lastEnd, match.index);
114381
- try {
114382
- const evalResult = evaluateExpression(expression, context);
114383
- if (typeof evalResult === 'object' && evalResult !== null) {
114384
- if (attributeName === 'style') {
114385
- const cssString = Object.entries(evalResult)
114386
- .map(([key, value]) => {
114387
- const cssKey = key.replace(/([A-Z])/g, '-$1').toLowerCase();
114388
- return `${cssKey}: ${value}`;
114389
- })
114390
- .join('; ');
114391
- result += `style="${cssString}"`;
114392
- }
114393
- else {
114394
- // These are arrays / objects attribute values
114395
- // Mark JSON-serialized values with a prefix so they can be parsed back correctly
114396
- const jsonValue = escapeHtmlAttribute(JSON_VALUE_MARKER + JSON.stringify(evalResult));
114397
- // Use double quotes so that multi-paragraph values are not split into multiple attributes by the processors
114398
- result += `${attributeName}="${jsonValue}"`;
114399
- }
114400
- }
114401
- else if (attributeName === 'className') {
114402
- // Escape special characters so that it doesn't break and split the attribute value to nodes
114403
- // This will be restored later in the pipeline
114404
- result += `class="${escapeHtmlAttribute(String(evalResult))}"`;
114405
- }
114406
- else {
114407
- result += `${attributeName}="${escapeHtmlAttribute(String(evalResult))}"`;
114408
- }
114409
- }
114410
- catch (_error) {
114411
- result += content.slice(match.index, fullMatchEnd);
114412
- }
114413
- lastEnd = fullMatchEnd;
114414
- attrStartRegex.lastIndex = fullMatchEnd;
114415
- }
114416
- match = attrStartRegex.exec(content);
114417
- }
114418
- result += content.slice(lastEnd);
114419
- return result;
114420
- }
114421
- /**
114422
- * Preprocesses JSX-like expressions in markdown before parsing.
114423
- * Inline expressions are handled separately; attribute expressions are processed here.
114424
- *
114425
- * @param content
114426
- * @param context
114427
- * @returns Preprocessed content ready for markdown parsing
114428
- */
114429
- function preprocessJSXExpressions(content, context = {}) {
114430
- // Step 0: Base64 encode HTMLBlock content
114431
- let processed = protectHTMLBlockContent(content);
114432
- // Step 1: Protect code blocks and inline code
114433
- const { protectedCode, protectedContent } = protectCodeBlocks(processed);
114434
- // Step 2: Evaluate attribute expressions (JSX attribute syntax: href={baseUrl})
114435
- // For inline expressions, we use a library to parse the expression & evaluate it later
114436
- // For attribute expressions, it was difficult to use a library to parse them, so do it manually
114437
- processed = evaluateAttributeExpressions(protectedContent, context, protectedCode);
114438
- // Step 3: Escape problematic braces to prevent MDX expression parsing errors
114439
- // This handles both unbalanced braces and paragraph-spanning expressions in one pass
114440
- processed = escapeProblematicBraces(processed);
114441
- // Step 4: Restore protected code blocks
114442
- processed = restoreCodeBlocks(processed, protectedCode);
114443
- return processed;
114444
- }
114445
-
114446
114466
  ;// ./processor/transform/mdxish/evaluate-expressions.ts
114447
114467
 
114448
114468
 
@@ -117531,11 +117551,66 @@ const transformMagicBlockEmbed = (node) => {
117531
117551
  position,
117532
117552
  };
117533
117553
  };
117554
+ const mdxish_jsx_to_mdast_isTableCell = (node) => isMDXElement(node) && ['th', 'td'].includes(node.name);
117555
+ /**
117556
+ * Converts a JSX <Table> element to an MDAST table node with alignment.
117557
+ * Returns null for header-less tables since MDAST always promotes the first row to <thead>.
117558
+ */
117559
+ const transformTable = (jsx) => {
117560
+ let hasThead = false;
117561
+ visit(jsx, isMDXElement, (child) => {
117562
+ if (child.name === 'thead')
117563
+ hasThead = true;
117564
+ });
117565
+ if (!hasThead)
117566
+ return null;
117567
+ const { align: alignAttr } = getAttrs(jsx);
117568
+ const align = Array.isArray(alignAttr) ? alignAttr : null;
117569
+ const rows = [];
117570
+ visit(jsx, isMDXElement, (child) => {
117571
+ if (child.name !== 'thead' && child.name !== 'tbody')
117572
+ return;
117573
+ visit(child, isMDXElement, (row) => {
117574
+ if (row.name !== 'tr')
117575
+ return;
117576
+ const cells = [];
117577
+ visit(row, mdxish_jsx_to_mdast_isTableCell, (cell) => {
117578
+ const parsedChildren = cell.children.flatMap(parsedNode => {
117579
+ if (parsedNode.type === 'paragraph' && 'children' in parsedNode && parsedNode.children) {
117580
+ return parsedNode.children;
117581
+ }
117582
+ return [parsedNode];
117583
+ });
117584
+ cells.push({
117585
+ type: 'tableCell',
117586
+ children: parsedChildren,
117587
+ position: cell.position,
117588
+ });
117589
+ });
117590
+ rows.push({
117591
+ type: 'tableRow',
117592
+ children: cells,
117593
+ position: row.position,
117594
+ });
117595
+ });
117596
+ });
117597
+ const columnCount = rows[0]?.children?.length || 0;
117598
+ const alignArray = align && columnCount > 0
117599
+ ? align.slice(0, columnCount).concat(Array.from({ length: Math.max(0, columnCount - align.length) }, () => null))
117600
+ : Array.from({ length: columnCount }, () => null);
117601
+ return {
117602
+ type: 'table',
117603
+ align: alignArray,
117604
+ position: jsx.position,
117605
+ children: rows,
117606
+ };
117607
+ };
117534
117608
  const COMPONENT_MAP = {
117535
117609
  Callout: transformCallout,
117536
117610
  Embed: transformEmbed,
117537
117611
  Image: transformImage,
117538
117612
  Recipe: transformRecipe,
117613
+ Table: transformTable,
117539
117614
  };
117540
117615
  /**
117541
117616
  * Transform mdxJsxFlowElement nodes and magic block nodes into proper MDAST node types.
@@ -117558,6 +117633,8 @@ const mdxishJsxToMdast = () => tree => {
117558
117633
  if (!transformer)
117559
117634
  return;
117560
117635
  const newNode = transformer(node);
117636
+ if (!newNode)
117637
+ return;
117561
117638
  // Replace the JSX node with the MDAST node
117562
117639
  parent.children[index] = newNode;
117563
117640
  });
@@ -117838,6 +117915,126 @@ function restoreSnakeCase(placeholderName, mapping) {
117838
117915
  return matchingKey ? mapping[matchingKey] : placeholderName;
117839
117916
  }
117840
117917
 
117918
+ ;// ./processor/transform/mdxish/mdxish-tables-to-jsx.ts
117919
+
117920
+
117921
+ const mdxish_tables_to_jsx_alignToStyle = (align) => {
117922
+ if (!align || align === 'left')
117923
+ return null;
117924
+ return {
117925
+ type: 'mdxJsxAttribute',
117926
+ name: 'style',
117927
+ value: {
117928
+ type: 'mdxJsxAttributeValueExpression',
117929
+ value: `{ textAlign: "${align}" }`,
117930
+ },
117931
+ };
117932
+ };
117933
+ const mdxish_tables_to_jsx_isTableCell = (node) => ['tableHead', 'tableCell'].includes(node.type);
117934
+ const mdxish_tables_to_jsx_isLiteral = (node) => 'value' in node;
117935
+ /**
117936
+ * Mdxish-specific version of `tablesToJsx`. Differs from the shared MDX version:
117937
+ *
117938
+ * - Excludes `html` nodes from triggering JSX conversion because raw HTML
117939
+ * inside JSX `<Table>` breaks remarkMdx parsing on the deserialization roundtrip.
117940
+ * - Skips empty cells instead of aborting the entire visit so that flow content
117941
+ * in later cells is still detected.
117942
+ */
117943
+ const mdxishTablesToJsx = () => tree => {
117944
+ visit(tree, (node) => ['table', 'tableau'].includes(node.type), (table, index, parent) => {
117945
+ let hasFlowContent = false;
117946
+ visit(table, mdxish_tables_to_jsx_isTableCell, (cell) => {
117947
+ if (cell.children.length === 0)
117948
+ return;
117949
+ const content = cell.children.length === 1 && cell.children[0].type === 'paragraph'
117950
+ ? cell.children[0].children[0]
117951
+ : cell.children[0];
117952
+ if (!content)
117953
+ return;
117954
+ visit(cell, 'break', (_, breakIndex, breakParent) => {
117955
+ breakParent.children.splice(breakIndex, 1, { type: 'text', value: '\n' });
117956
+ });
117957
+ if (!(phrasing(content) || content.type === 'plain') && content.type !== 'escape') {
117958
+ if (content.type === 'html')
117959
+ return;
117960
+ hasFlowContent = true;
117961
+ return EXIT;
117962
+ }
117963
+ visit(cell, mdxish_tables_to_jsx_isLiteral, (node) => {
117964
+ if (node.value.match(/\n/)) {
117965
+ hasFlowContent = true;
117966
+ return EXIT;
117967
+ }
117968
+ });
117969
+ });
117970
+ if (!hasFlowContent) {
117971
+ table.type = 'table';
117972
+ return;
117973
+ }
117974
+ const styles = table.align.map(mdxish_tables_to_jsx_alignToStyle);
117975
+ const head = {
117976
+ attributes: [],
117977
+ type: 'mdxJsxFlowElement',
117978
+ name: 'thead',
117979
+ children: [
117980
+ {
117981
+ attributes: [],
117982
+ type: 'mdxJsxFlowElement',
117983
+ name: 'tr',
117984
+ children: table.children[0].children.map((cell, cellIndex) => {
117985
+ return {
117986
+ attributes: [],
117987
+ type: 'mdxJsxFlowElement',
117988
+ name: 'th',
117989
+ children: cell.children,
117990
+ ...(styles[cellIndex] && { attributes: [styles[cellIndex]] }),
117991
+ };
117992
+ }),
117993
+ },
117994
+ ],
117995
+ };
117996
+ const body = {
117997
+ attributes: [],
117998
+ type: 'mdxJsxFlowElement',
117999
+ name: 'tbody',
118000
+ children: table.children.splice(1).map(row => {
118001
+ return {
118002
+ attributes: [],
118003
+ type: 'mdxJsxFlowElement',
118004
+ name: 'tr',
118005
+ children: row.children.map((cell, cellIndex) => {
118006
+ return {
118007
+ type: 'mdxJsxFlowElement',
118008
+ name: 'td',
118009
+ children: cell.children,
118010
+ ...(styles[cellIndex] && { attributes: [styles[cellIndex]] }),
118011
+ };
118012
+ }),
118013
+ };
118014
+ }),
118015
+ };
118016
+ const attributes = [
118017
+ {
118018
+ type: 'mdxJsxAttribute',
118019
+ name: 'align',
118020
+ value: {
118021
+ type: 'mdxJsxAttributeValueExpression',
118022
+ value: JSON.stringify(table.align),
118023
+ },
118024
+ },
118025
+ ];
118026
+ const jsx = {
118027
+ type: 'mdxJsxFlowElement',
118028
+ name: 'Table',
118029
+ attributes: table.align.find(a => a) ? attributes : [],
118030
+ children: [head, body],
118031
+ };
118032
+ parent.children[index] = jsx;
118033
+ });
118034
+ return tree;
118035
+ };
118036
+ /* harmony default export */ const mdxish_tables_to_jsx = (mdxishTablesToJsx);
118037
+
117841
118038
  ;// ./processor/transform/mdxish/normalize-table-separator.ts
117842
118039
  /**
117843
118040
  * Preprocessor to normalize malformed GFM table separator syntax.
@@ -120006,6 +120203,8 @@ function loadComponents() {
120006
120203
 
120007
120204
 
120008
120205
 
120206
+
120207
+
120009
120208
 
120010
120209
 
120011
120210
 
@@ -120103,18 +120302,28 @@ function mdxishAstProcessor(mdContent, opts = {}) {
120103
120302
  };
120104
120303
  }
120105
120304
  /**
120106
- * Converts an Mdast to a Markdown string.
120305
+ * Registers the mdx-jsx serialization extension so remark-stringify
120306
+ * can convert JSX nodes (e.g. `<Table>`) to markdown.
120307
+ */
120308
+ function mdxJsxStringify() {
120309
+ const data = this.data();
120310
+ const extensions = data.toMarkdownExtensions || (data.toMarkdownExtensions = []);
120311
+ extensions.push({ extensions: [mdxJsxToMarkdown()] });
120312
+ }
120313
+ /**
120314
+ * Serializes an Mdast back into a markdown string.
120107
120315
  */
120108
120316
  function mdxishMdastToMd(mdast) {
120109
- const md = unified()
120317
+ const processor = unified()
120110
120318
  .use(remarkGfm)
120319
+ .use(mdxish_tables_to_jsx)
120111
120320
  .use(mdxishCompilers)
120321
+ .use(mdxJsxStringify)
120112
120322
  .use(remarkStringify, {
120113
120323
  bullet: '-',
120114
120324
  emphasis: '_',
120115
- })
120116
- .stringify(mdast);
120117
- return md;
120325
+ });
120326
+ return processor.stringify(processor.runSync(mdast));
120118
120327
  }
120119
120328
  /**
120120
120329
  * Processes markdown content with MDX syntax support and returns a HAST.