@readme/markdown 14.11.4 → 14.11.5

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
@@ -117546,6 +117546,19 @@ const compile_list_item_listItem = (node, parent, state, info) => {
117546
117546
  const plain_plain = (node) => node.value;
117547
117547
  /* harmony default export */ const compile_plain = (plain_plain);
117548
117548
 
117549
+ ;// ./processor/compile/text.ts
117550
+
117551
+ // A `_` flanked by word characters can never open or close emphasis under
117552
+ // CommonMark's flanking rules, so the escape mdast-util-to-markdown adds to
117553
+ // intraword underscores is unnecessary and only produces noisy `\_` diffs.
117554
+ // Uses Unicode letter/number classes so non-ASCII words (e.g. `é_pay`) match.
117555
+ const INTRAWORD_UNDERSCORE_ESCAPE = /(?<=[\p{L}\p{N}_])\\_(?=[\p{L}\p{N}_]|\\_)/gu;
117556
+ const compile_text_text = (node, parent, state, info) => {
117557
+ const serialized = handle.text(node, parent, state, info);
117558
+ return serialized.replace(INTRAWORD_UNDERSCORE_ESCAPE, '_');
117559
+ };
117560
+ /* harmony default export */ const compile_text = (compile_text_text);
117561
+
117549
117562
  ;// ./processor/compile/index.ts
117550
117563
 
117551
117564
 
@@ -117558,11 +117571,11 @@ const plain_plain = (node) => node.value;
117558
117571
 
117559
117572
 
117560
117573
 
117574
+
117561
117575
  function compilers(mdxish = false) {
117562
117576
  const data = this.data();
117563
117577
  const toMarkdownExtensions = data.toMarkdownExtensions || (data.toMarkdownExtensions = []);
117564
117578
  const handlers = {
117565
- ...(mdxish && { [NodeTypes.anchor]: compile_anchor }),
117566
117579
  [NodeTypes.callout]: compile_callout,
117567
117580
  [NodeTypes.codeTabs]: compile_code_tabs,
117568
117581
  [NodeTypes.embedBlock]: compile_embed,
@@ -117570,15 +117583,18 @@ function compilers(mdxish = false) {
117570
117583
  [NodeTypes.glossary]: compile_compatibility,
117571
117584
  [NodeTypes.htmlBlock]: html_block,
117572
117585
  [NodeTypes.reusableContent]: compile_compatibility,
117573
- ...(mdxish && { [NodeTypes.variable]: compile_variable }),
117574
117586
  embed: compile_compatibility,
117575
117587
  escape: compile_compatibility,
117576
117588
  figure: compile_compatibility,
117577
117589
  html: compile_compatibility,
117578
117590
  i: compile_compatibility,
117579
- ...(mdxish && { listItem: list_item }),
117580
117591
  plain: compile_plain,
117581
117592
  yaml: compile_compatibility,
117593
+ // needed only for mdxish
117594
+ ...(mdxish && { [NodeTypes.anchor]: compile_anchor }),
117595
+ ...(mdxish && { listItem: list_item }),
117596
+ ...(mdxish && { text: compile_text }),
117597
+ ...(mdxish && { [NodeTypes.variable]: compile_variable }),
117582
117598
  };
117583
117599
  toMarkdownExtensions.push({ extensions: [{ handlers }] });
117584
117600
  }
@@ -122440,70 +122456,168 @@ const mdxishInlineMdxComponents = () => tree => {
122440
122456
  };
122441
122457
  /* harmony default export */ const inline_mdx_blocks = (mdxishInlineMdxComponents);
122442
122458
 
122459
+ ;// ./processor/transform/mdxish/terminate-html-flow-blocks.ts
122460
+
122461
+
122462
+
122463
+ const STANDALONE_HTML_LINE_REGEX = /^(<[a-z][^<>]*>|<\/[a-z][^<>]*>)+\s*$/;
122464
+ const HTML_LINE_WITH_CONTENT_REGEX = /^<[a-z][^<>]*>.*<\/[a-z][^<>]*>(?:[^<]*)$/;
122465
+ // A line that is exactly one lowercase opening (or self-closing) tag — the shape
122466
+ // that opens a CommonMark type-6/7 HTML block and swallows the lines below it.
122467
+ const SINGLE_OPENING_TAG_REGEX = /^<[a-z][^<>]*>$/;
122468
+ // Tags whose contents must be preserved as is, inserting a blank line after the
122469
+ // opener corrupts the payload.
122470
+ // htmlRawNames here refer to <pre>, <textarea>, <script>, <style>
122471
+ const RAW_CONTENT_TAGS = [...htmlRawNames, ...HTML_TABLE_STRUCTURE_TAGS];
122472
+ // The `(?=[\s/>])` lookahead avoids false matches on lookalike names like `<script-foo>`.
122473
+ const RAW_CONTENT_TAG_MATCHERS = RAW_CONTENT_TAGS.map(tag => ({
122474
+ open: new RegExp(`<${tag}(?=[\\s/>])[^>]*?(?<!/)>`, 'gi'),
122475
+ close: new RegExp(`</${tag}(?=[\\s>])[^>]*>`, 'gi'),
122476
+ }));
122477
+ function isLineHtml(line) {
122478
+ return STANDALONE_HTML_LINE_REGEX.test(line) || HTML_LINE_WITH_CONTENT_REGEX.test(line);
122479
+ }
122480
+ // Per-tag {opens, closes} counts of RAW_CONTENT_TAGS on one line — feeds both the
122481
+ // per-line net-open check and the cumulative still-open depth tracking.
122482
+ function countRawContentTags(line) {
122483
+ return RAW_CONTENT_TAG_MATCHERS.map(({ open, close }) => ({
122484
+ opens: (line.match(open) ?? []).length,
122485
+ closes: (line.match(close) ?? []).length,
122486
+ }));
122487
+ }
122488
+ // Indentation width in columns, counting a tab as 4 per CommonMark.
122489
+ function indentWidth(line) {
122490
+ const leading = line.match(/^[ \t]*/)?.[0] ?? '';
122491
+ return leading.replace(/\t/g, ' ').length;
122492
+ }
122493
+ /**
122494
+ * Decides whether a blank line belongs between `line` and `next` so the HTML
122495
+ * flow block opened on `line` terminates and `next` parses as markdown.
122496
+ */
122497
+ function shouldTerminateBetween(line, next, { insideRawContent, lineLeavesRawOpen }) {
122498
+ if (next.trim().length === 0)
122499
+ return false;
122500
+ const currentIndent = indentWidth(line);
122501
+ const nextIndent = indentWidth(next);
122502
+ // 4+ columns is CommonMark indented-code territory: an inserted blank line
122503
+ // would turn the next line into a code block instead of freeing it (#1344).
122504
+ if (currentIndent > 3 || nextIndent > 3)
122505
+ return false;
122506
+ const currentTrimmed = line.trim();
122507
+ const nextTrimmed = next.trim();
122508
+ if (!isLineHtml(currentTrimmed) || isLineHtml(nextTrimmed) || lineLeavesRawOpen) {
122509
+ return false;
122510
+ }
122511
+ // Column-0 pairs keep the original (pre-relaxation) rule, deliberately ignoring
122512
+ // `insideRawContent`: one unclosed <pre>/<table> typo would otherwise suppress
122513
+ // termination for the whole rest of the document.
122514
+ if (currentIndent === 0 && nextIndent === 0)
122515
+ return true;
122516
+ // Indented shapes (either line at 1–3 columns) are stricter: only a lone opening
122517
+ // tag followed by markdown. A next line opening a tag or expression must stay
122518
+ // glued for the mdxComponent tokenizer; raw-content payloads stay byte-exact.
122519
+ return (!insideRawContent &&
122520
+ SINGLE_OPENING_TAG_REGEX.test(currentTrimmed) &&
122521
+ !nextTrimmed.startsWith('<') &&
122522
+ !nextTrimmed.startsWith('{'));
122523
+ }
122524
+ /**
122525
+ * CommonMark HTML blocks (types 6/7) end only at a blank line, so markdown on the
122526
+ * next line is swallowed as HTML (`## heading` after `<div>` renders as literal text).
122527
+ * Inserts the missing blank line; the gating rules live in `shouldTerminateBetween`.
122528
+ *
122529
+ * @link https://spec.commonmark.org/0.29/#html-blocks
122530
+ */
122531
+ function terminateHtmlFlowBlocks(content) {
122532
+ const { protectedContent, protectedCode } = protectCodeBlocks(content);
122533
+ const lines = protectedContent.split('\n');
122534
+ const result = [];
122535
+ // Per-tag count of still-open raw-content elements at the current boundary.
122536
+ const rawContentDepths = RAW_CONTENT_TAG_MATCHERS.map(() => 0);
122537
+ for (let i = 0; i < lines.length; i += 1) {
122538
+ const line = lines[i];
122539
+ result.push(line);
122540
+ const tagCounts = countRawContentTags(line);
122541
+ tagCounts.forEach(({ opens, closes }, tagIndex) => {
122542
+ rawContentDepths[tagIndex] = Math.max(0, rawContentDepths[tagIndex] + opens - closes);
122543
+ });
122544
+ const next = lines[i + 1];
122545
+ const rawContentFacts = {
122546
+ insideRawContent: rawContentDepths.some(depth => depth > 0),
122547
+ lineLeavesRawOpen: tagCounts.some(({ opens, closes }) => opens > closes),
122548
+ };
122549
+ if (next !== undefined && shouldTerminateBetween(line, next, rawContentFacts)) {
122550
+ result.push('');
122551
+ }
122552
+ }
122553
+ return restoreCodeBlocks(result.join('\n'), protectedCode);
122554
+ }
122555
+
122443
122556
  ;// ./processor/transform/mdxish/components/mdx-blocks.ts
122444
122557
 
122445
122558
 
122446
122559
 
122447
122560
 
122448
122561
 
122449
- /** Matches a JSX attribute expression (e.g. `key={i}`) anywhere in a string. */
122562
+
122563
+
122564
+ // Matches a JSX attribute expression (e.g. `key={i}`) anywhere in a string. */
122450
122565
  const NESTED_ATTR_EXPRESSION_RE = /[\w-]+\s*=\s*\{/;
122451
- /**
122452
- * Reduce leading whitespace on all lines just enough to prevent
122453
- * remark from treating indented content as code blocks (4+ spaces).
122454
- * Preserves relative indentation so whitespace text nodes are
122455
- * maintained in the HAST output.
122566
+ // Name shape mirrors `componentTagPattern`; the lookbehind skips the inner tag
122567
+ // of a legacy `<<VARIABLE>>`.
122568
+ const NESTED_COMPONENT_TAG_RE = /(?<!<)<([A-Z][A-Za-z0-9_]*)[\s/>]/g;
122569
+ // Excludes tags with dedicated transformers (`Table`, `HTMLBlock`, inline
122570
+ // components), which expect their wrapper to stay raw.
122571
+ const hasNestedGenericComponentTag = (content) => [...content.matchAll(NESTED_COMPONENT_TAG_RE)].some(match => !GENERIC_MDX_COMPONENT_EXCLUDED_TAGS.has(match[1]));
122572
+ /**
122573
+ * Strip the shared leading indentation from a component body so readability indentation
122574
+ * isn't parsed as indented code (4+ spaces), e.g. ` <p>` / ` text` -> `<p>` / ` text`.
122575
+ * Relative indentation is kept, so content genuinely 4+ columns deeper stays code. We
122576
+ * only strip when a line actually reaches 4 columns; otherwise the body is left as-is so
122577
+ * its leading whitespace survives as text nodes (mixed component + HTML content needs it).
122456
122578
  */
122457
122579
  function safeDeindent(text) {
122458
122580
  const lines = text.split('\n');
122459
122581
  const nonEmptyLines = lines.filter(line => line.trim().length > 0);
122460
122582
  if (nonEmptyLines.length === 0)
122461
122583
  return text;
122462
- const minIndent = Math.min(...nonEmptyLines.map(line => {
122463
- const match = line.match(/^(\s*)/);
122464
- return match ? match[1].length : 0;
122465
- }));
122466
- // Only strip enough indent to keep all lines below the 4-space code threshold
122467
- const stripAmount = Math.max(0, minIndent - 3);
122584
+ // Indent counts characters (tab = 1), unlike indentWidth's CommonMark columns
122585
+ // (tab = 4) in terminate-html-flow-blocks.
122586
+ const indents = nonEmptyLines.map(line => line.match(/^(\s*)/)?.[1].length ?? 0);
122587
+ const minIndent = Math.min(...indents);
122588
+ const maxIndent = Math.max(...indents);
122589
+ const stripAmount = maxIndent > 3 ? minIndent : 0;
122468
122590
  if (stripAmount === 0)
122469
122591
  return text;
122470
122592
  return lines.map(line => line.slice(stripAmount)).join('\n');
122471
122593
  }
122472
122594
  /**
122473
- * Parse markdown content into mdast children nodes.
122474
- * Dedents the content first to prevent indented component content
122475
- * (from nested components) from being treated as code blocks.
122595
+ * Parse component-body markdown into mdast children. Dedenting shifts columns and
122596
+ * stales the top-level `terminateHtmlFlowBlocks` decisions, so that one preprocessor
122597
+ * re-runs here; other column-anchored fixups (compact headings, tables) do not.
122476
122598
  */
122477
122599
  const parseMdChildren = (value, safeMode) => {
122478
- const parsed = getInlineMdProcessor({ safeMode }).parse(safeDeindent(value).trim());
122600
+ const parsed = getInlineMdProcessor({ safeMode }).parse(terminateHtmlFlowBlocks(safeDeindent(value).trim()));
122479
122601
  return parsed.children || [];
122480
122602
  };
122481
- /**
122482
- * Parse substring content of a node and update the parent's children to include the new nodes.
122483
- */
122603
+ // Parses trailing content into sibling nodes and re-queues the parent so any
122604
+ // components among them get processed.
122484
122605
  const parseSibling = (stack, parent, index, sibling, safeMode) => {
122485
122606
  const siblingNodes = parseMdChildren(sibling, safeMode);
122486
- // The new sibling nodes might contain new components to be processed
122487
122607
  if (siblingNodes.length > 0) {
122488
122608
  parent.children.splice(index + 1, 0, ...siblingNodes);
122489
122609
  stack.push(parent);
122490
122610
  }
122491
122611
  };
122492
- /**
122493
- * Build a position ending at `consumedLength` into the html node's value, so the
122494
- * component doesn't claim trailing content the tokenizer swallowed into one node.
122495
- */
122612
+ // Ends the position at `consumedLength` so the component doesn't claim trailing
122613
+ // content the tokenizer swallowed into the same html node.
122496
122614
  const positionEndingAtConsumed = (nodePosition, value, consumedLength) => {
122497
122615
  if (!nodePosition?.start)
122498
122616
  return nodePosition;
122499
122617
  return { start: nodePosition.start, end: pointAfter(nodePosition.start, value.slice(0, consumedLength)) };
122500
122618
  };
122501
- /**
122502
- * Build a position ending right after the last occurrence of `closingTag` within
122503
- * this node's span in the original source. Used in the trailing-content path so
122504
- * the offset is computed against the real source bytes (including blockquote/list
122505
- * prefixes that were stripped from the html node's value).
122506
- */
122619
+ // Like `positionEndingAtConsumed`, but measures against the original source so
122620
+ // blockquote/list prefixes stripped from the html node's value are counted.
122507
122621
  const positionEndingAtClosingTagInSource = (nodePosition, closingTag, source) => {
122508
122622
  if (!nodePosition?.start || !nodePosition.end)
122509
122623
  return nodePosition;
@@ -122514,9 +122628,6 @@ const positionEndingAtClosingTagInSource = (nodePosition, closingTag, source) =>
122514
122628
  const consumed = nodeSource.slice(0, closingTagOffset + closingTag.length);
122515
122629
  return { start: nodePosition.start, end: pointAfter(nodePosition.start, consumed) };
122516
122630
  };
122517
- /**
122518
- * Create an MdxJsxFlowElement node from component data.
122519
- */
122520
122631
  const createComponentNode = ({ tag, attributes, children, startPosition, endPosition }) => ({
122521
122632
  type: 'mdxJsxFlowElement',
122522
122633
  name: tag,
@@ -122569,8 +122680,7 @@ const mdxishMdxComponentBlocks = (opts = {}) => (tree, file) => {
122569
122680
  if ('children' in node && Array.isArray(node.children)) {
122570
122681
  stack.push(node);
122571
122682
  }
122572
- // Only visit HTML nodes with an actual html tag,
122573
- // which means a potential unparsed MDX component
122683
+ // Only html nodes can be an unparsed MDX component.
122574
122684
  const value = node.value;
122575
122685
  if (node.type !== 'html' || typeof value !== 'string')
122576
122686
  return;
@@ -122579,32 +122689,25 @@ const mdxishMdxComponentBlocks = (opts = {}) => (tree, file) => {
122579
122689
  if (!parsed)
122580
122690
  return;
122581
122691
  const { tag, attributes, selfClosing, contentAfterTag = '' } = parsed;
122582
- // Offset of `trimmed` within the (possibly whitespace-padded) html node value,
122583
- // so consumed-length math maps back onto the node's real source offsets.
122692
+ // Offsets so consumed-length math maps back onto the node's real source.
122584
122693
  const leadingWhitespace = value.length - value.trimStart().length;
122585
- // Index right after the opening tag's `>` within `trimmed`.
122586
122694
  const openingTagEnd = trimmed.length - contentAfterTag.length;
122587
- // Skip tags that have dedicated transformers
122588
122695
  if (GENERIC_MDX_COMPONENT_EXCLUDED_TAGS.has(tag))
122589
- return;
122696
+ return; // owned by dedicated transformers
122590
122697
  const isPascal = isPascalCase(tag);
122591
- // Lowercase inline tags (inside a paragraph) with `{…}` attributes are
122592
- // promoted to `mdxJsxTextElement` by mdxishInlineComponentBlocks. Skip
122593
- // them here so they stay as html for that pass; PascalCase components
122594
- // keep going through this transformer (they stay flow-level even when
122595
- // inline, which is how ReadMe's custom components are modeled).
122698
+ // Lowercase inline tags with `{…}` attributes belong to
122699
+ // mdxishInlineComponentBlocks; leave them as html for that pass. PascalCase
122700
+ // components stay flow-level even when inline (ReadMe's component model).
122596
122701
  if (!isPascal && parent.type === 'paragraph')
122597
122702
  return;
122598
- // Lowercase HTML tags are eligible when they (or a descendant tag in their
122599
- // content, e.g. a `.map()` body returning JSX with `key={i}`) carry a
122600
- // JSX-expression attribute. Without this, a wrapper `<div>` with only plain
122601
- // attributes (or none) swallows its whole nested block — including any
122602
- // `style={{...}}`/`.map()` JSX inside it — as literal HTML text, which
122603
- // rehype-raw's parse5 pass then garbles (it has no notion of JS expressions).
122604
- // Plain HTML with no expressions anywhere stays as an html node so
122605
- // rehype-raw handles it as normal.
122703
+ // A lowercase wrapper is only promoted when it (or a descendant) carries a
122704
+ // JSX expression or nests a component; otherwise it would swallow that inner
122705
+ // JSX/component as literal text that rehype-raw's parse5 pass can't handle.
122706
+ // Table-structural wrappers are excluded `mdxishTables` re-parses those.
122606
122707
  const hasNestedExpressionAttr = !selfClosing && NESTED_ATTR_EXPRESSION_RE.test(contentAfterTag);
122607
- if (!isPascal && !hasExpressionAttr(attributes) && !hasNestedExpressionAttr)
122708
+ const isTableStructuralTag = tag === 'table' || tableTags.has(tag);
122709
+ const hasNestedComponentTag = !selfClosing && !isTableStructuralTag && hasNestedGenericComponentTag(contentAfterTag);
122710
+ if (!isPascal && !hasExpressionAttr(attributes) && !hasNestedExpressionAttr && !hasNestedComponentTag)
122608
122711
  return;
122609
122712
  const closingTagStr = `</${tag}>`;
122610
122713
  // Case 1: Self-closing tag
@@ -122618,7 +122721,6 @@ const mdxishMdxComponentBlocks = (opts = {}) => (tree, file) => {
122618
122721
  endPosition: positionEndingAtConsumed(node.position, value, leadingWhitespace + openingTagEnd),
122619
122722
  });
122620
122723
  substituteNodeWithMdxNode(parent, index, componentNode);
122621
- // Check and parse if there's relevant content after the current closing tag
122622
122724
  const remainingContent = contentAfterTag.trim();
122623
122725
  if (remainingContent) {
122624
122726
  parseSibling(stack, parent, index, remainingContent, safeMode);
@@ -122627,17 +122729,14 @@ const mdxishMdxComponentBlocks = (opts = {}) => (tree, file) => {
122627
122729
  }
122628
122730
  // Case 2: Self-contained block (closing tag in content)
122629
122731
  if (contentAfterTag.includes(closingTagStr)) {
122630
- // Find the first closing tag
122631
122732
  const closingTagIndex = contentAfterTag.lastIndexOf(closingTagStr);
122632
- // Pass raw (untrimmed) content so dedent in parseMdChildren can
122633
- // normalize indentation before trimming
122733
+ // Untrimmed so parseMdChildren can dedent before trimming.
122634
122734
  const componentInnerContent = contentAfterTag.substring(0, closingTagIndex);
122635
122735
  const contentAfterClose = contentAfterTag.substring(closingTagIndex + closingTagStr.length).trim();
122636
122736
  let parsedChildren = componentInnerContent.trim()
122637
122737
  ? parseMdChildren(componentInnerContent, safeMode)
122638
122738
  : [];
122639
- // Lowercase HTML tags are usually inline (e.g. <a>, <span>). Remark wraps
122640
- // bare text in a paragraph; unwrap when there's exactly one paragraph so
122739
+ // Lowercase tags are usually inline; unwrap a sole paragraph so their
122641
122740
  // phrasing content isn't spuriously block-wrapped.
122642
122741
  if (!isPascal && parsedChildren.length === 1 && parsedChildren[0].type === 'paragraph') {
122643
122742
  parsedChildren = parsedChildren[0].children;
@@ -122647,12 +122746,9 @@ const mdxishMdxComponentBlocks = (opts = {}) => (tree, file) => {
122647
122746
  attributes,
122648
122747
  children: parsedChildren,
122649
122748
  startPosition: node.position,
122650
- // When trailing content follows the closing tag, compute the end position precisely
122651
- // within the html node's value so the component doesn't claim that content.
122652
- // Prefer source-based positioning when the original source is available: the html
122653
- // node's value has '> '/space prefixes stripped for blockquotes/list items, so
122654
- // positionEndingAtConsumed would undercount source offsets. When the entire node
122655
- // is consumed, use the original node position directly.
122749
+ // With trailing content, end precisely at the closing tag. Prefer source
122750
+ // offsets when available (the node's value strips blockquote/list
122751
+ // prefixes); otherwise fall back to the whole node position.
122656
122752
  endPosition: contentAfterClose
122657
122753
  ? source
122658
122754
  ? positionEndingAtClosingTagInSource(node.position, closingTagStr, source)
@@ -122660,17 +122756,16 @@ const mdxishMdxComponentBlocks = (opts = {}) => (tree, file) => {
122660
122756
  : node.position,
122661
122757
  });
122662
122758
  substituteNodeWithMdxNode(parent, index, componentNode);
122663
- // After the closing tag, there might be more content to be processed
122759
+ // Re-queue whichever side may hold further components.
122664
122760
  if (contentAfterClose) {
122665
122761
  parseSibling(stack, parent, index, contentAfterClose, safeMode);
122666
122762
  }
122667
122763
  else if (componentNode.children.length > 0) {
122668
- // The content inside the component block might contain new components to be processed
122669
122764
  stack.push(componentNode);
122670
122765
  }
122671
122766
  }
122672
122767
  };
122673
- // Process the nodes with the components depth-first to maintain the correct order of the nodes
122768
+ // Depth-first so nodes keep their source order.
122674
122769
  while (stack.length) {
122675
122770
  const parent = stack.pop();
122676
122771
  if (parent?.children) {
@@ -125503,73 +125598,6 @@ function normalizeTableSeparator(content) {
125503
125598
  }
125504
125599
  /* harmony default export */ const normalize_table_separator = ((/* unused pure expression or super */ null && (normalizeTableSeparator)));
125505
125600
 
125506
- ;// ./processor/transform/mdxish/terminate-html-flow-blocks.ts
125507
-
125508
-
125509
-
125510
- const STANDALONE_HTML_LINE_REGEX = /^(<[a-z][^<>]*>|<\/[a-z][^<>]*>)+\s*$/;
125511
- const HTML_LINE_WITH_CONTENT_REGEX = /^<[a-z][^<>]*>.*<\/[a-z][^<>]*>(?:[^<]*)$/;
125512
- // Tags whose contents must be preserved as is, inserting a blank line after the
125513
- // opener corrupts the payload.
125514
- // htmlRawNames here refer to <pre>, <textarea>, <script>, <style>
125515
- const RAW_CONTENT_TAGS = [...htmlRawNames, ...HTML_TABLE_STRUCTURE_TAGS];
125516
- // The `(?=[\s/>])` lookahead avoids false matches on lookalike names like `<script-foo>`.
125517
- const RAW_CONTENT_TAG_MATCHERS = RAW_CONTENT_TAGS.map(tag => ({
125518
- open: new RegExp(`<${tag}(?=[\\s/>])[^>]*?(?<!/)>`, 'gi'),
125519
- close: new RegExp(`</${tag}(?=[\\s>])[^>]*>`, 'gi'),
125520
- }));
125521
- function isLineHtml(line) {
125522
- return STANDALONE_HTML_LINE_REGEX.test(line) || HTML_LINE_WITH_CONTENT_REGEX.test(line);
125523
- }
125524
- // True if any RAW_CONTENT_TAGS opener on this line is not closed on the same line.
125525
- function hasUnclosedRawContentOpener(line) {
125526
- return RAW_CONTENT_TAG_MATCHERS.some(({ open, close }) => {
125527
- const opens = (line.match(open) ?? []).length;
125528
- const closes = (line.match(close) ?? []).length;
125529
- return opens > closes;
125530
- });
125531
- }
125532
- /**
125533
- * Preprocessor to terminate HTML flow blocks.
125534
- *
125535
- * In CommonMark, HTML blocks (types 6 and 7) only terminate on a blank line.
125536
- * Without one, any content on the next line is consumed as part of the HTML block
125537
- * and never parsed as its own construct. For example, a `[block:callout]` immediately
125538
- * following `<div><p></p></div>` gets swallowed into the HTML flow token.
125539
- *
125540
- * @link https://spec.commonmark.org/0.29/#html-blocks
125541
- *
125542
- * This preprocessor inserts a blank line after standalone HTML lines when the next
125543
- * line is non-blank and not an HTML construct, ensuring micromark's HTML flow
125544
- * tokenizer terminates and subsequent content is parsed independently.
125545
- *
125546
- * Conditions:
125547
- * 1. Only non-indented lines with lowercase tag names are considered. Uppercase tags
125548
- * (e.g. `<Table>`, `<MyComponent>`) are JSX custom components and don't trigger
125549
- * CommonMark HTML blocks.
125550
- * 2. Lines inside protected blocks (e.g. fenced code) are left untouched.
125551
- * 3. Lines with an unclosed RAW_CONTENT_TAGS opener are exempted.
125552
- */
125553
- function terminateHtmlFlowBlocks(content) {
125554
- const { protectedContent, protectedCode } = protectCodeBlocks(content);
125555
- const lines = protectedContent.split('\n');
125556
- const result = [];
125557
- for (let i = 0; i < lines.length; i += 1) {
125558
- result.push(lines[i]);
125559
- // Skip blank & indented lines
125560
- if (i >= lines.length - 1 || lines[i + 1].trim().length === 0 || lines[i + 1].startsWith(' ') || lines[i + 1].startsWith('\t')) {
125561
- // eslint-disable-next-line no-continue
125562
- continue;
125563
- }
125564
- const isCurrentLineHtml = isLineHtml(lines[i]);
125565
- const isNextLineHtml = isLineHtml(lines[i + 1]);
125566
- if (isCurrentLineHtml && !isNextLineHtml && !hasUnclosedRawContentOpener(lines[i])) {
125567
- result.push('');
125568
- }
125569
- }
125570
- return restoreCodeBlocks(result.join('\n'), protectedCode);
125571
- }
125572
-
125573
125601
  ;// ./processor/transform/mdxish/variables-code.ts
125574
125602
 
125575
125603
 
@@ -126437,6 +126465,12 @@ function mdxishMdastToMd(mdast) {
126437
126465
  .use(remarkStringify, {
126438
126466
  bullet: '-',
126439
126467
  emphasis: '_',
126468
+ // Escape literal braces in text so they don't parse as (often
126469
+ // unterminated) MDX expressions on the next round trip.
126470
+ unsafe: [
126471
+ { character: '{', inConstruct: 'phrasing' },
126472
+ { character: '}', inConstruct: 'phrasing' },
126473
+ ],
126440
126474
  });
126441
126475
  return processor.stringify(processor.runSync(mdast));
126442
126476
  }