@readme/markdown 13.1.3 → 13.2.0

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
@@ -70954,13 +70954,15 @@ const scanForClosingTag = (parent, startIndex, tag) => {
70954
70954
  if (isClosingTag(siblingValue, tag)) {
70955
70955
  return { closingIndex: i, extraClosingChildren: [] };
70956
70956
  }
70957
- // Embedded closing tag (closing tag at end of HTML block content)
70957
+ // Embedded closing tag (closing tag within HTML block content)
70958
70958
  if (siblingValue.includes(closingTagStr)) {
70959
- const contentBeforeClose = siblingValue.substring(0, siblingValue.lastIndexOf(closingTagStr)).trim();
70959
+ const closeTagPos = siblingValue.indexOf(closingTagStr);
70960
+ const contentBeforeClose = siblingValue.substring(0, closeTagPos).trim();
70961
+ const contentAfterClose = siblingValue.substring(closeTagPos + closingTagStr.length).trim();
70960
70962
  const extraChildren = contentBeforeClose
70961
70963
  ? parseMdChildren(contentBeforeClose)
70962
70964
  : [];
70963
- return { closingIndex: i, extraClosingChildren: extraChildren };
70965
+ return { closingIndex: i, extraClosingChildren: extraChildren, contentAfterClose: contentAfterClose || undefined };
70964
70966
  }
70965
70967
  }
70966
70968
  // Check paragraph siblings
@@ -71105,7 +71107,7 @@ const mdxishComponentBlocks = () => tree => {
71105
71107
  const scanResult = scanForClosingTag(parent, index, tag);
71106
71108
  if (!scanResult)
71107
71109
  return;
71108
- const { closingIndex, extraClosingChildren, strippedParagraph } = scanResult;
71110
+ const { closingIndex, extraClosingChildren, strippedParagraph, contentAfterClose: remainingAfterClose } = scanResult;
71109
71111
  const extraChildren = contentAfterTag ? parseMdChildren(contentAfterTag.trimStart()) : [];
71110
71112
  // Collect all intermediate siblings between opening tag and closing tag
71111
71113
  const intermediateChildren = parent.children.slice(index + 1, closingIndex);
@@ -71128,6 +71130,11 @@ const mdxishComponentBlocks = () => tree => {
71128
71130
  if (componentNode.children.length > 0) {
71129
71131
  stack.push(componentNode);
71130
71132
  }
71133
+ // If the closing tag sibling had content after it (e.g., another component opening tag),
71134
+ // re-insert it as a sibling so it can be processed in subsequent iterations
71135
+ if (remainingAfterClose) {
71136
+ parseSibling(stack, parent, index, remainingAfterClose);
71137
+ }
71131
71138
  };
71132
71139
  // Process the nodes with the components depth-first to maintain the correct order of the nodes
71133
71140
  while (stack.length) {
@@ -93444,7 +93451,7 @@ function isActualHtmlTag(tagName, originalExcerpt) {
93444
93451
  return false;
93445
93452
  }
93446
93453
  /** Parse and replace text children with processed markdown */
93447
- function parseTextChildren(node, processMarkdown) {
93454
+ function parseTextChildren(node, processMarkdown, components) {
93448
93455
  if (!node.children?.length)
93449
93456
  return;
93450
93457
  // First pass: Recursively process text children as they may contain stringified markdown / mdx content
@@ -93459,6 +93466,27 @@ function parseTextChildren(node, processMarkdown) {
93459
93466
  }
93460
93467
  return children;
93461
93468
  });
93469
+ // Unwrap <p> elements whose meaningful children are ALL components.
93470
+ // The markdown parser wraps inline content in <p> tags, but when that content
93471
+ // is actually component children (e.g., <Tab> inside <Tabs>), the wrapper
93472
+ // should be removed so components appear as direct children.
93473
+ // Only unwrap when every non-whitespace, non-br child is a known component
93474
+ // to avoid breaking paragraphs with mixed content (text + inline HTML like <code>).
93475
+ node.children = node.children.flatMap(child => {
93476
+ if (child.type !== 'element' || child.tagName !== 'p')
93477
+ return [child];
93478
+ const meaningfulChildren = child.children.filter(gc => {
93479
+ if (gc.type === 'text' && !gc.value.trim())
93480
+ return false;
93481
+ if (gc.type === 'element' && gc.tagName === 'br')
93482
+ return false;
93483
+ return true;
93484
+ });
93485
+ const allComponents = meaningfulChildren.length > 0 && meaningfulChildren.every(gc => gc.type === 'element' && getComponentName(gc.tagName, components));
93486
+ if (!allComponents)
93487
+ return [child];
93488
+ return meaningfulChildren;
93489
+ });
93462
93490
  // Post-processing: remove whitespace-only text nodes if all siblings are components
93463
93491
  // This prevents whitespace between component children from being counted as extra children
93464
93492
  if (areAllChildrenComponents(node.children)) {
@@ -93513,7 +93541,7 @@ const rehypeMdxishComponents = ({ components, processMarkdown }) => {
93513
93541
  }
93514
93542
  node.tagName = componentName;
93515
93543
  normalizeProperties(node);
93516
- parseTextChildren(node, processMarkdown);
93544
+ parseTextChildren(node, processMarkdown, components);
93517
93545
  });
93518
93546
  // Remove unknown components in reverse order to preserve indices
93519
93547
  for (let i = nodesToRemove.length - 1; i >= 0; i -= 1) {
@@ -93814,8 +93842,10 @@ function escapeUnbalancedBraces(content) {
93814
93842
  const unbalanced = new Set();
93815
93843
  let strDelim = null;
93816
93844
  let strEscaped = false;
93817
- for (let i = 0; i < content.length; i += 1) {
93818
- const ch = content[i];
93845
+ // Convert to array of Unicode code points to handle emojis and multi-byte characters correctly
93846
+ const chars = Array.from(content);
93847
+ for (let i = 0; i < chars.length; i += 1) {
93848
+ const ch = chars[i];
93819
93849
  // Track strings inside expressions to ignore braces within them
93820
93850
  if (opens.length > 0) {
93821
93851
  if (strDelim) {
@@ -93837,7 +93867,7 @@ function escapeUnbalancedBraces(content) {
93837
93867
  // Skip already-escaped braces (count preceding backslashes)
93838
93868
  if (ch === '{' || ch === '}') {
93839
93869
  let bs = 0;
93840
- for (let j = i - 1; j >= 0 && content[j] === '\\'; j -= 1)
93870
+ for (let j = i - 1; j >= 0 && chars[j] === '\\'; j -= 1)
93841
93871
  bs += 1;
93842
93872
  if (bs % 2 === 1) {
93843
93873
  // eslint-disable-next-line no-continue
@@ -93856,7 +93886,7 @@ function escapeUnbalancedBraces(content) {
93856
93886
  opens.forEach(pos => unbalanced.add(pos));
93857
93887
  if (unbalanced.size === 0)
93858
93888
  return content;
93859
- return Array.from(content)
93889
+ return chars
93860
93890
  .map((ch, i) => (unbalanced.has(i) ? `\\${ch}` : ch))
93861
93891
  .join('');
93862
93892
  }
@@ -94001,9 +94031,12 @@ const evaluateExpressions = ({ context = {} } = {}) => tree => {
94001
94031
  }
94002
94032
  catch (_error) {
94003
94033
  // If evaluation fails, leave the expression as-is (fallback to text)
94034
+ // we still need to manually escape escaped characters because the expression
94035
+ // parser treats the contents as code instead of text, skipping the backslash escapes
94036
+ const processed = expression.replace(/\\([!-/:-@[-`{-~])/g, '$1');
94004
94037
  parent.children.splice(index, 1, {
94005
94038
  type: 'text',
94006
- value: `{${expression}}`,
94039
+ value: `{${processed}}`,
94007
94040
  position: node.position,
94008
94041
  });
94009
94042
  }
@@ -94096,11 +94129,11 @@ const MARKER_PATTERNS = [
94096
94129
  // trailingSpace1 is for "** text **" pattern, trailingSpace2 is for "**text **" pattern
94097
94130
  const asteriskBoldRegex = /([^*\s]+)?\s*(\*\*)(?:\s+((?:[^*\n]|\*(?!\*))+?)(\s*)\2|((?:[^*\n]|\*(?!\*))+?)(\s+)\2)(\S|$)?/g;
94098
94131
  // Pattern for __ bold __
94099
- const underscoreBoldRegex = /([^_\s]+)?\s*(__)(?:\s+((?:[^_\n]|_(?!_))+?)(\s*)\2|((?:[^_\n]|_(?!_))+?)(\s+)\2)(\S|$)?/g;
94132
+ const underscoreBoldRegex = /([^_\s]+)?\s*(__)(?:\s+((?:__(?! )|_(?!_)|[^_\n])+?)(\s*)\2|((?:__(?! )|_(?!_)|[^_\n])+?)(\s+)\2)(\S|$)?/g;
94100
94133
  // Pattern for * italic *
94101
94134
  const asteriskItalicRegex = /([^*\s]+)?\s*(\*)(?!\*)(?:\s+([^*\n]+?)(\s*)\2|([^*\n]+?)(\s+)\2)(\S|$)?/g;
94102
94135
  // Pattern for _ italic _
94103
- const underscoreItalicRegex = /([^_\s]+)?\s*(_)(?!_)(?:\s+([^_\n]+?)(\s*)\2|([^_\n]+?)(\s+)\2)(\S|$)?/g;
94136
+ const underscoreItalicRegex = /([^_\s]+)?\s*(_)(?!_)(?:\s+((?:[^_\n]|_(?! ))+?)(\s*)\2|((?:[^_\n]|_(?! ))+?)(\s+)\2)(\S|$)?/g;
94104
94137
  // CommonMark ignores intraword underscores or asteriks, but we want to italicize/bold the inner part
94105
94138
  // Pattern for intraword _word_ in words like hello_world_
94106
94139
  const intrawordUnderscoreItalicRegex = /(\w)_(?!_)([a-zA-Z0-9]+)_(?![\w_])/g;
@@ -95824,6 +95857,9 @@ const restoreBooleanProperties = () => tree => {
95824
95857
 
95825
95858
  const STANDALONE_HTML_LINE_REGEX = /^(<[a-z][^<>]*>|<\/[a-z][^<>]*>)+\s*$/;
95826
95859
  const HTML_LINE_WITH_CONTENT_REGEX = /^<[a-z][^<>]*>.*<\/[a-z][^<>]*>(?:[^<]*)$/;
95860
+ function isLineHtml(line) {
95861
+ return STANDALONE_HTML_LINE_REGEX.test(line) || HTML_LINE_WITH_CONTENT_REGEX.test(line);
95862
+ }
95827
95863
  /**
95828
95864
  * Preprocessor to terminate HTML flow blocks.
95829
95865
  *
@@ -95835,14 +95871,14 @@ const HTML_LINE_WITH_CONTENT_REGEX = /^<[a-z][^<>]*>.*<\/[a-z][^<>]*>(?:[^<]*)$/
95835
95871
  * @link https://spec.commonmark.org/0.29/#html-blocks
95836
95872
  *
95837
95873
  * This preprocessor inserts a blank line after standalone HTML lines when the
95838
- * next line is non-blank, ensuring micromark's HTML flow tokenizer terminates
95839
- * and subsequent content is parsed independently.
95874
+ * next line is non-blank and not an HTML construct (because they still might be part of the HTML flow),
95875
+ * ensuring micromark's HTML flow tokenizer terminates and subsequent content is parsed independently.
95840
95876
  *
95841
- * Only targets non-indented lines with lowercase tag names. Uppercase tags
95877
+ * Conditions:
95878
+ * 1. Only targets non-indented lines with lowercase tag names. Uppercase tags
95842
95879
  * (e.g., `<Table>`, `<MyComponent>`) are JSX custom components and don't
95843
95880
  * trigger CommonMark HTML blocks, so they are left untouched.
95844
- *
95845
- * Lines inside fenced code blocks are skipped entirely.
95881
+ * 2. Lines inside protected blocks (e.g., code blocks) should be left untouched.
95846
95882
  */
95847
95883
  function terminateHtmlFlowBlocks(content) {
95848
95884
  const { protectedContent, protectedCode } = protectCodeBlocks(content);
@@ -95850,9 +95886,14 @@ function terminateHtmlFlowBlocks(content) {
95850
95886
  const result = [];
95851
95887
  for (let i = 0; i < lines.length; i += 1) {
95852
95888
  result.push(lines[i]);
95853
- if (i < lines.length - 1 &&
95854
- (STANDALONE_HTML_LINE_REGEX.test(lines[i]) || HTML_LINE_WITH_CONTENT_REGEX.test(lines[i])) &&
95855
- lines[i + 1].trim().length > 0) {
95889
+ // Skip blank & indented lines
95890
+ if (i >= lines.length - 1 || lines[i + 1].trim().length === 0 || lines[i + 1].startsWith(' ') || lines[i + 1].startsWith('\t')) {
95891
+ // eslint-disable-next-line no-continue
95892
+ continue;
95893
+ }
95894
+ const isCurrentLineHtml = isLineHtml(lines[i]);
95895
+ const isNextLineHtml = isLineHtml(lines[i + 1]);
95896
+ if (isCurrentLineHtml && !isNextLineHtml) {
95856
95897
  result.push('');
95857
95898
  }
95858
95899
  }
@@ -97836,7 +97877,8 @@ async function stripComments(doc, { mdx, mdxish } = {}) {
97836
97877
  if (mdxish) {
97837
97878
  processor
97838
97879
  .data('micromarkExtensions', [mdxExpression({ allowEmpty: true })])
97839
- .data('fromMarkdownExtensions', [mdxExpressionFromMarkdown()]);
97880
+ .data('fromMarkdownExtensions', [mdxExpressionFromMarkdown()])
97881
+ .data('toMarkdownExtensions', [mdxExpressionToMarkdown()]);
97840
97882
  }
97841
97883
  processor
97842
97884
  .use(remarkParse)
@@ -97861,9 +97903,7 @@ async function stripComments(doc, { mdx, mdxish } = {}) {
97861
97903
  // Our markdown renderer uses this to group these code blocks into a tabbed interface.
97862
97904
  (left, right) => {
97863
97905
  if (left.type === 'code' && right.type === 'code') {
97864
- const isTight = left.position &&
97865
- right.position &&
97866
- right.position.start.line - left.position.end.line === 1; // Are the blocks on adjacent lines?
97906
+ const isTight = left.position && right.position && right.position.start.line - left.position.end.line === 1; // Are the blocks on adjacent lines?
97867
97907
  // 0 = no newline between blocks
97868
97908
  return isTight ? 0 : undefined;
97869
97909
  }
package/dist/main.node.js CHANGED
@@ -91158,13 +91158,15 @@ const scanForClosingTag = (parent, startIndex, tag) => {
91158
91158
  if (isClosingTag(siblingValue, tag)) {
91159
91159
  return { closingIndex: i, extraClosingChildren: [] };
91160
91160
  }
91161
- // Embedded closing tag (closing tag at end of HTML block content)
91161
+ // Embedded closing tag (closing tag within HTML block content)
91162
91162
  if (siblingValue.includes(closingTagStr)) {
91163
- const contentBeforeClose = siblingValue.substring(0, siblingValue.lastIndexOf(closingTagStr)).trim();
91163
+ const closeTagPos = siblingValue.indexOf(closingTagStr);
91164
+ const contentBeforeClose = siblingValue.substring(0, closeTagPos).trim();
91165
+ const contentAfterClose = siblingValue.substring(closeTagPos + closingTagStr.length).trim();
91164
91166
  const extraChildren = contentBeforeClose
91165
91167
  ? parseMdChildren(contentBeforeClose)
91166
91168
  : [];
91167
- return { closingIndex: i, extraClosingChildren: extraChildren };
91169
+ return { closingIndex: i, extraClosingChildren: extraChildren, contentAfterClose: contentAfterClose || undefined };
91168
91170
  }
91169
91171
  }
91170
91172
  // Check paragraph siblings
@@ -91309,7 +91311,7 @@ const mdxishComponentBlocks = () => tree => {
91309
91311
  const scanResult = scanForClosingTag(parent, index, tag);
91310
91312
  if (!scanResult)
91311
91313
  return;
91312
- const { closingIndex, extraClosingChildren, strippedParagraph } = scanResult;
91314
+ const { closingIndex, extraClosingChildren, strippedParagraph, contentAfterClose: remainingAfterClose } = scanResult;
91313
91315
  const extraChildren = contentAfterTag ? parseMdChildren(contentAfterTag.trimStart()) : [];
91314
91316
  // Collect all intermediate siblings between opening tag and closing tag
91315
91317
  const intermediateChildren = parent.children.slice(index + 1, closingIndex);
@@ -91332,6 +91334,11 @@ const mdxishComponentBlocks = () => tree => {
91332
91334
  if (componentNode.children.length > 0) {
91333
91335
  stack.push(componentNode);
91334
91336
  }
91337
+ // If the closing tag sibling had content after it (e.g., another component opening tag),
91338
+ // re-insert it as a sibling so it can be processed in subsequent iterations
91339
+ if (remainingAfterClose) {
91340
+ parseSibling(stack, parent, index, remainingAfterClose);
91341
+ }
91335
91342
  };
91336
91343
  // Process the nodes with the components depth-first to maintain the correct order of the nodes
91337
91344
  while (stack.length) {
@@ -113648,7 +113655,7 @@ function isActualHtmlTag(tagName, originalExcerpt) {
113648
113655
  return false;
113649
113656
  }
113650
113657
  /** Parse and replace text children with processed markdown */
113651
- function parseTextChildren(node, processMarkdown) {
113658
+ function parseTextChildren(node, processMarkdown, components) {
113652
113659
  if (!node.children?.length)
113653
113660
  return;
113654
113661
  // First pass: Recursively process text children as they may contain stringified markdown / mdx content
@@ -113663,6 +113670,27 @@ function parseTextChildren(node, processMarkdown) {
113663
113670
  }
113664
113671
  return children;
113665
113672
  });
113673
+ // Unwrap <p> elements whose meaningful children are ALL components.
113674
+ // The markdown parser wraps inline content in <p> tags, but when that content
113675
+ // is actually component children (e.g., <Tab> inside <Tabs>), the wrapper
113676
+ // should be removed so components appear as direct children.
113677
+ // Only unwrap when every non-whitespace, non-br child is a known component
113678
+ // to avoid breaking paragraphs with mixed content (text + inline HTML like <code>).
113679
+ node.children = node.children.flatMap(child => {
113680
+ if (child.type !== 'element' || child.tagName !== 'p')
113681
+ return [child];
113682
+ const meaningfulChildren = child.children.filter(gc => {
113683
+ if (gc.type === 'text' && !gc.value.trim())
113684
+ return false;
113685
+ if (gc.type === 'element' && gc.tagName === 'br')
113686
+ return false;
113687
+ return true;
113688
+ });
113689
+ const allComponents = meaningfulChildren.length > 0 && meaningfulChildren.every(gc => gc.type === 'element' && getComponentName(gc.tagName, components));
113690
+ if (!allComponents)
113691
+ return [child];
113692
+ return meaningfulChildren;
113693
+ });
113666
113694
  // Post-processing: remove whitespace-only text nodes if all siblings are components
113667
113695
  // This prevents whitespace between component children from being counted as extra children
113668
113696
  if (areAllChildrenComponents(node.children)) {
@@ -113717,7 +113745,7 @@ const rehypeMdxishComponents = ({ components, processMarkdown }) => {
113717
113745
  }
113718
113746
  node.tagName = componentName;
113719
113747
  normalizeProperties(node);
113720
- parseTextChildren(node, processMarkdown);
113748
+ parseTextChildren(node, processMarkdown, components);
113721
113749
  });
113722
113750
  // Remove unknown components in reverse order to preserve indices
113723
113751
  for (let i = nodesToRemove.length - 1; i >= 0; i -= 1) {
@@ -114018,8 +114046,10 @@ function escapeUnbalancedBraces(content) {
114018
114046
  const unbalanced = new Set();
114019
114047
  let strDelim = null;
114020
114048
  let strEscaped = false;
114021
- for (let i = 0; i < content.length; i += 1) {
114022
- const ch = content[i];
114049
+ // Convert to array of Unicode code points to handle emojis and multi-byte characters correctly
114050
+ const chars = Array.from(content);
114051
+ for (let i = 0; i < chars.length; i += 1) {
114052
+ const ch = chars[i];
114023
114053
  // Track strings inside expressions to ignore braces within them
114024
114054
  if (opens.length > 0) {
114025
114055
  if (strDelim) {
@@ -114041,7 +114071,7 @@ function escapeUnbalancedBraces(content) {
114041
114071
  // Skip already-escaped braces (count preceding backslashes)
114042
114072
  if (ch === '{' || ch === '}') {
114043
114073
  let bs = 0;
114044
- for (let j = i - 1; j >= 0 && content[j] === '\\'; j -= 1)
114074
+ for (let j = i - 1; j >= 0 && chars[j] === '\\'; j -= 1)
114045
114075
  bs += 1;
114046
114076
  if (bs % 2 === 1) {
114047
114077
  // eslint-disable-next-line no-continue
@@ -114060,7 +114090,7 @@ function escapeUnbalancedBraces(content) {
114060
114090
  opens.forEach(pos => unbalanced.add(pos));
114061
114091
  if (unbalanced.size === 0)
114062
114092
  return content;
114063
- return Array.from(content)
114093
+ return chars
114064
114094
  .map((ch, i) => (unbalanced.has(i) ? `\\${ch}` : ch))
114065
114095
  .join('');
114066
114096
  }
@@ -114205,9 +114235,12 @@ const evaluateExpressions = ({ context = {} } = {}) => tree => {
114205
114235
  }
114206
114236
  catch (_error) {
114207
114237
  // If evaluation fails, leave the expression as-is (fallback to text)
114238
+ // we still need to manually escape escaped characters because the expression
114239
+ // parser treats the contents as code instead of text, skipping the backslash escapes
114240
+ const processed = expression.replace(/\\([!-/:-@[-`{-~])/g, '$1');
114208
114241
  parent.children.splice(index, 1, {
114209
114242
  type: 'text',
114210
- value: `{${expression}}`,
114243
+ value: `{${processed}}`,
114211
114244
  position: node.position,
114212
114245
  });
114213
114246
  }
@@ -114300,11 +114333,11 @@ const MARKER_PATTERNS = [
114300
114333
  // trailingSpace1 is for "** text **" pattern, trailingSpace2 is for "**text **" pattern
114301
114334
  const asteriskBoldRegex = /([^*\s]+)?\s*(\*\*)(?:\s+((?:[^*\n]|\*(?!\*))+?)(\s*)\2|((?:[^*\n]|\*(?!\*))+?)(\s+)\2)(\S|$)?/g;
114302
114335
  // Pattern for __ bold __
114303
- const underscoreBoldRegex = /([^_\s]+)?\s*(__)(?:\s+((?:[^_\n]|_(?!_))+?)(\s*)\2|((?:[^_\n]|_(?!_))+?)(\s+)\2)(\S|$)?/g;
114336
+ const underscoreBoldRegex = /([^_\s]+)?\s*(__)(?:\s+((?:__(?! )|_(?!_)|[^_\n])+?)(\s*)\2|((?:__(?! )|_(?!_)|[^_\n])+?)(\s+)\2)(\S|$)?/g;
114304
114337
  // Pattern for * italic *
114305
114338
  const asteriskItalicRegex = /([^*\s]+)?\s*(\*)(?!\*)(?:\s+([^*\n]+?)(\s*)\2|([^*\n]+?)(\s+)\2)(\S|$)?/g;
114306
114339
  // Pattern for _ italic _
114307
- const underscoreItalicRegex = /([^_\s]+)?\s*(_)(?!_)(?:\s+([^_\n]+?)(\s*)\2|([^_\n]+?)(\s+)\2)(\S|$)?/g;
114340
+ const underscoreItalicRegex = /([^_\s]+)?\s*(_)(?!_)(?:\s+((?:[^_\n]|_(?! ))+?)(\s*)\2|((?:[^_\n]|_(?! ))+?)(\s+)\2)(\S|$)?/g;
114308
114341
  // CommonMark ignores intraword underscores or asteriks, but we want to italicize/bold the inner part
114309
114342
  // Pattern for intraword _word_ in words like hello_world_
114310
114343
  const intrawordUnderscoreItalicRegex = /(\w)_(?!_)([a-zA-Z0-9]+)_(?![\w_])/g;
@@ -116028,6 +116061,9 @@ const restoreBooleanProperties = () => tree => {
116028
116061
 
116029
116062
  const STANDALONE_HTML_LINE_REGEX = /^(<[a-z][^<>]*>|<\/[a-z][^<>]*>)+\s*$/;
116030
116063
  const HTML_LINE_WITH_CONTENT_REGEX = /^<[a-z][^<>]*>.*<\/[a-z][^<>]*>(?:[^<]*)$/;
116064
+ function isLineHtml(line) {
116065
+ return STANDALONE_HTML_LINE_REGEX.test(line) || HTML_LINE_WITH_CONTENT_REGEX.test(line);
116066
+ }
116031
116067
  /**
116032
116068
  * Preprocessor to terminate HTML flow blocks.
116033
116069
  *
@@ -116039,14 +116075,14 @@ const HTML_LINE_WITH_CONTENT_REGEX = /^<[a-z][^<>]*>.*<\/[a-z][^<>]*>(?:[^<]*)$/
116039
116075
  * @link https://spec.commonmark.org/0.29/#html-blocks
116040
116076
  *
116041
116077
  * This preprocessor inserts a blank line after standalone HTML lines when the
116042
- * next line is non-blank, ensuring micromark's HTML flow tokenizer terminates
116043
- * and subsequent content is parsed independently.
116078
+ * next line is non-blank and not an HTML construct (because they still might be part of the HTML flow),
116079
+ * ensuring micromark's HTML flow tokenizer terminates and subsequent content is parsed independently.
116044
116080
  *
116045
- * Only targets non-indented lines with lowercase tag names. Uppercase tags
116081
+ * Conditions:
116082
+ * 1. Only targets non-indented lines with lowercase tag names. Uppercase tags
116046
116083
  * (e.g., `<Table>`, `<MyComponent>`) are JSX custom components and don't
116047
116084
  * trigger CommonMark HTML blocks, so they are left untouched.
116048
- *
116049
- * Lines inside fenced code blocks are skipped entirely.
116085
+ * 2. Lines inside protected blocks (e.g., code blocks) should be left untouched.
116050
116086
  */
116051
116087
  function terminateHtmlFlowBlocks(content) {
116052
116088
  const { protectedContent, protectedCode } = protectCodeBlocks(content);
@@ -116054,9 +116090,14 @@ function terminateHtmlFlowBlocks(content) {
116054
116090
  const result = [];
116055
116091
  for (let i = 0; i < lines.length; i += 1) {
116056
116092
  result.push(lines[i]);
116057
- if (i < lines.length - 1 &&
116058
- (STANDALONE_HTML_LINE_REGEX.test(lines[i]) || HTML_LINE_WITH_CONTENT_REGEX.test(lines[i])) &&
116059
- lines[i + 1].trim().length > 0) {
116093
+ // Skip blank & indented lines
116094
+ if (i >= lines.length - 1 || lines[i + 1].trim().length === 0 || lines[i + 1].startsWith(' ') || lines[i + 1].startsWith('\t')) {
116095
+ // eslint-disable-next-line no-continue
116096
+ continue;
116097
+ }
116098
+ const isCurrentLineHtml = isLineHtml(lines[i]);
116099
+ const isNextLineHtml = isLineHtml(lines[i + 1]);
116100
+ if (isCurrentLineHtml && !isNextLineHtml) {
116060
116101
  result.push('');
116061
116102
  }
116062
116103
  }
@@ -118040,7 +118081,8 @@ async function stripComments(doc, { mdx, mdxish } = {}) {
118040
118081
  if (mdxish) {
118041
118082
  processor
118042
118083
  .data('micromarkExtensions', [mdxExpression({ allowEmpty: true })])
118043
- .data('fromMarkdownExtensions', [mdxExpressionFromMarkdown()]);
118084
+ .data('fromMarkdownExtensions', [mdxExpressionFromMarkdown()])
118085
+ .data('toMarkdownExtensions', [mdxExpressionToMarkdown()]);
118044
118086
  }
118045
118087
  processor
118046
118088
  .use(remarkParse)
@@ -118065,9 +118107,7 @@ async function stripComments(doc, { mdx, mdxish } = {}) {
118065
118107
  // Our markdown renderer uses this to group these code blocks into a tabbed interface.
118066
118108
  (left, right) => {
118067
118109
  if (left.type === 'code' && right.type === 'code') {
118068
- const isTight = left.position &&
118069
- right.position &&
118070
- right.position.start.line - left.position.end.line === 1; // Are the blocks on adjacent lines?
118110
+ const isTight = left.position && right.position && right.position.start.line - left.position.end.line === 1; // Are the blocks on adjacent lines?
118071
118111
  // 0 = no newline between blocks
118072
118112
  return isTight ? 0 : undefined;
118073
118113
  }