@readme/markdown 14.12.1 → 14.13.1

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
@@ -19523,8 +19523,8 @@ const Callout = (props) => {
19523
19523
 
19524
19524
 
19525
19525
 
19526
- const Card = ({ badge, children, href, kind = 'card', icon, iconColor, target, title }) => {
19527
- const Tag = href ? 'a' : 'div';
19526
+ const Card = ({ badge, children, href, kind = 'card', icon, iconColor, LinkComponent = 'a', target, title, }) => {
19527
+ const Tag = href ? LinkComponent : 'div';
19528
19528
  return (external_react_default().createElement(Tag, { className: `Card Card_${kind}`, href: href, target: target },
19529
19529
  icon && external_react_default().createElement(components_Icon, { className: "Card-icon", icon: icon, iconColor: iconColor }),
19530
19530
  external_react_default().createElement("div", { className: "Card-content" },
@@ -96525,6 +96525,21 @@ const HTML_TABLE_STRUCTURE_TAGS = new Set([
96525
96525
  'caption',
96526
96526
  'colgroup',
96527
96527
  ]);
96528
+ /**
96529
+ * Tags whose bodies a later mdxish transform keeps raw and never re-parses as
96530
+ * markdown. Both layers depend on this property, not on the tags being figures:
96531
+ * the transformer treats them as non-promotable, and the tokenizer must not claim
96532
+ * markdown islands inside them (a claimed island would never be re-parsed and would
96533
+ * leak as literal text). Currently just the figure tags, whose bodies are owned by
96534
+ * the figure reassembly transform (`mdxishJsxToMdast`).
96535
+ */
96536
+ const NON_REPARSED_BODY_TAGS = new Set(['figure', 'figcaption']);
96537
+ /**
96538
+ * The two HTML "foreign content" namespaces: SVG and MathML. Their descendants
96539
+ * (`<path>`, `<mrow>`, …) are namespaced XML, not HTML tags or components. A closed
96540
+ * set per the HTML spec, so transforms can treat these roots as opaque islands.
96541
+ */
96542
+ const FOREIGN_CONTENT_TAGS = ['svg', 'math'];
96528
96543
  /**
96529
96544
  * HTML void elements — elements that have no closing tag and no children.
96530
96545
  *
@@ -119154,6 +119169,9 @@ function getComponentName(componentName, components) {
119154
119169
 
119155
119170
 
119156
119171
 
119172
+ // Foreign-content (SVG/MathML) roots — subtrees the component transform leaves
119173
+ // untouched (their children are namespaced XML, not HTML tags/components).
119174
+ const FOREIGN_CONTENT_ROOTS = new Set(FOREIGN_CONTENT_TAGS);
119157
119175
  function isElementContentNode(node) {
119158
119176
  return node.type === 'element' || node.type === 'text' || node.type === 'comment';
119159
119177
  }
@@ -119329,6 +119347,12 @@ const rehypeMdxishComponents = ({ components, processMarkdown }) => {
119329
119347
  visit(tree, 'element', (node, index, parent) => {
119330
119348
  if (index === undefined || !parent)
119331
119349
  return;
119350
+ // Skip foreign-content subtrees: their namespaced children (<path>, <mrow>, …)
119351
+ // aren't HTML tags or components, so the "unknown component" removal below would
119352
+ // otherwise delete them. (svg/math themselves are standard HTML tags, kept.)
119353
+ // eslint-disable-next-line consistent-return
119354
+ if (FOREIGN_CONTENT_ROOTS.has(node.tagName))
119355
+ return SKIP;
119332
119356
  // Parse Image caption as markdown so it renders formatted (bold, code,
119333
119357
  // decoded entities) in the figcaption instead of as a raw string.
119334
119358
  // rehypeRaw strips children from <img> (void element), so we must
@@ -119640,6 +119664,81 @@ function closeSelfClosingHtmlTags(content) {
119640
119664
  return restoreCodeBlocks(result, protectedCode);
119641
119665
  }
119642
119666
 
119667
+ ;// ./processor/transform/mdxish/collapse-foreign-content-blank-lines.ts
119668
+
119669
+
119670
+ // `svg|math`, from the canonical list so this stays in sync with the component transform.
119671
+ const ROOT_ALT = FOREIGN_CONTENT_TAGS.join('|');
119672
+ // A foreign-content opener. The `[\s/>]` boundary requires a real tag delimiter after the
119673
+ // root name, so lookalikes like `<svgfoo>` and custom elements like `<svg-icon>` are ignored.
119674
+ const ANY_ROOT_RE = new RegExp(`<(?:${ROOT_ALT})(?=[\\s/>])`, 'i');
119675
+ // Tag body: swallows whole quoted attribute values so a `>` inside a quote (e.g.
119676
+ // `<svg data-x="a > b" />`) can't be mistaken for the tag terminator; other non-`>`
119677
+ // chars (including newlines) are consumed one at a time.
119678
+ const TAG_BODY = '(?:"[^"]*"|\'[^\']*\'|[^>\'"])*?';
119679
+ // One whole svg/math tag (opener, self-closer, or closer), matched whole so attributes
119680
+ // and `>` may span lines. The `[\s/>]` boundary after the root name keeps `<svgfoo>`/
119681
+ // `<svg-icon>` out. Group 1 is `/` only for a self-closer.
119682
+ const FOREIGN_TAG_RE = new RegExp(`<(?:${ROOT_ALT})(?=[\\s/>])${TAG_BODY}(/)?>|</(?:${ROOT_ALT})(?=[\\s/>])${TAG_BODY}>`, 'gi');
119683
+ // An HTML comment. Foreign markup inside one is inert and must not open an island.
119684
+ const HTML_COMMENT_RE = /<!--[\s\S]*?-->/g;
119685
+ const withinAny = (spans, offset) => spans.some(([start, end]) => offset >= start && offset < end);
119686
+ /**
119687
+ * `[start, end)` spans of every *matched* SVG/MathML pair — the regions whose blank
119688
+ * lines are known-insignificant XML. Matching whole tags (not per-line open/close
119689
+ * counts) keeps multi-line openers and self-closers balanced, so a wrapped
119690
+ * `<svg … />` can't latch and swallow the doc (#1545).
119691
+ *
119692
+ * Tags are paired innermost-first, as a parser would; an opener still unpaired at EOF
119693
+ * closed nothing and so covers nothing. That keeps a stray `<svg>` in prose from
119694
+ * swallowing every blank line after it, without hiding the real islands that follow.
119695
+ */
119696
+ function findForeignContentSpans(text) {
119697
+ const spans = [];
119698
+ // Foreign tags inside an HTML comment are inert; a stray `<svg>`/`<math>` in a comment
119699
+ // must not open an island and latch onto the rest of the doc.
119700
+ const comments = [...text.matchAll(HTML_COMMENT_RE)].map((m) => [m.index ?? 0, (m.index ?? 0) + m[0].length]);
119701
+ // Offsets of openers still waiting for their closer, innermost last.
119702
+ const openOffsets = [];
119703
+ [...text.matchAll(FOREIGN_TAG_RE)].forEach(match => {
119704
+ const offset = match.index ?? 0;
119705
+ if (withinAny(comments, offset))
119706
+ return; // markup inside an HTML comment is inert
119707
+ if (match[1] === '/')
119708
+ return; // self-closing tag opens no island
119709
+ if (match[0].startsWith('</')) {
119710
+ const start = openOffsets.pop();
119711
+ if (start !== undefined)
119712
+ spans.push([start, offset + match[0].length]); // a closer with no opener closes nothing
119713
+ }
119714
+ else {
119715
+ openOffsets.push(offset);
119716
+ }
119717
+ });
119718
+ return spans;
119719
+ }
119720
+ /**
119721
+ * Drop blank lines inside `<svg>`/`<math>` islands: their whitespace is
119722
+ * insignificant XML, but a blank line ends the CommonMark HTML block and spills the
119723
+ * children out as a code block (#1545). Collapsing keeps the island one block.
119724
+ */
119725
+ function collapseForeignContentBlankLines(content) {
119726
+ // Fast path: nothing to do when the doc has no foreign-content island.
119727
+ if (!ANY_ROOT_RE.test(content))
119728
+ return content;
119729
+ const { protectedContent, protectedCode } = protectCodeBlocks(content);
119730
+ const islands = findForeignContentSpans(protectedContent);
119731
+ // Drop blank lines inside an island; keep everything else verbatim.
119732
+ const lines = [];
119733
+ let lineStart = 0;
119734
+ protectedContent.split('\n').forEach(line => {
119735
+ if (line.trim().length > 0 || !withinAny(islands, lineStart))
119736
+ lines.push(line);
119737
+ lineStart += line.length + 1; // +1 for the '\n' that split() removed
119738
+ });
119739
+ return restoreCodeBlocks(lines.join('\n'), protectedCode);
119740
+ }
119741
+
119643
119742
  ;// ./lib/utils/mdxish/mdxish-component-tag-parser.ts
119644
119743
  // Extract the tag name (PascalCase or lowercase HTML) at the start of a tag string.
119645
119744
  const tagNamePattern = /^<([A-Za-z][A-Za-z0-9_-]*)/;
@@ -121632,12 +121731,95 @@ function tokenizeMagicBlockText(effects, ok, nok) {
121632
121731
  */
121633
121732
 
121634
121733
 
121734
+ ;// ./lib/micromark/mdx-component/continuation-checks.ts
121735
+
121736
+
121737
+ /**
121738
+ * Partial constructs the `mdxComponent` tokenizer runs via `effects.check` to
121739
+ * decide whether the next line continues the block. They live here (not in
121740
+ * `syntax.ts`) because they hold none of `createTokenize`'s closure state — each
121741
+ * is a self-contained, single-line lookahead.
121742
+ */
121743
+ function continuation_checks_tokenizeNonLazyContinuationStart(effects, ok, nok) {
121744
+ // eslint-disable-next-line @typescript-eslint/no-this-alias
121745
+ const self = this;
121746
+ return start;
121747
+ function start(code) {
121748
+ if (markdownLineEnding(code)) {
121749
+ effects.enter(types_types.lineEnding);
121750
+ effects.consume(code);
121751
+ effects.exit(types_types.lineEnding);
121752
+ return after;
121753
+ }
121754
+ return nok(code);
121755
+ }
121756
+ function after(code) {
121757
+ if (self.parser.lazy[self.now().line]) {
121758
+ return nok(code);
121759
+ }
121760
+ return ok(code);
121761
+ }
121762
+ }
121763
+ // A markup-only line opens with a tag (`<x`/`</x`) and ends (ignoring trailing
121764
+ // spaces) at a `>`. That distinguishes a structural continuation like
121765
+ // `<span>b</span></div>` from a paragraph like `<b>Note:</b> read *this*`.
121766
+ function tokenizeMarkupOnlyContinuation(effects, ok, nok) {
121767
+ let lastNonSpace = null;
121768
+ return start;
121769
+ function start(code) {
121770
+ // Caller guarantees we are at `<` at the (already de-indented) line start.
121771
+ effects.enter(types_types.data);
121772
+ effects.consume(code);
121773
+ return afterLessThan;
121774
+ }
121775
+ function afterLessThan(code) {
121776
+ if (code === codes.slash) {
121777
+ effects.consume(code);
121778
+ return afterSlash;
121779
+ }
121780
+ return afterSlash(code);
121781
+ }
121782
+ // The `<` (or `</`) must introduce a real tag, not a stray `<` in prose.
121783
+ function afterSlash(code) {
121784
+ if (asciiAlpha(code)) {
121785
+ lastNonSpace = code;
121786
+ effects.consume(code);
121787
+ return scanToLineEnd;
121788
+ }
121789
+ effects.exit(types_types.data);
121790
+ return nok(code);
121791
+ }
121792
+ function scanToLineEnd(code) {
121793
+ if (code === null || markdownLineEnding(code)) {
121794
+ effects.exit(types_types.data);
121795
+ return lastNonSpace === codes.greaterThan ? ok(code) : nok(code);
121796
+ }
121797
+ if (!markdownSpace(code))
121798
+ lastNonSpace = code;
121799
+ effects.consume(code);
121800
+ return scanToLineEnd;
121801
+ }
121802
+ }
121803
+ // A line ending whose next line isn't a lazy paragraph continuation. Checked so a
121804
+ // component body's blank/continuation lines aren't stolen by an interrupted paragraph.
121805
+ const continuation_checks_nonLazyContinuationStart = {
121806
+ tokenize: continuation_checks_tokenizeNonLazyContinuationStart,
121807
+ partial: true,
121808
+ };
121809
+ // Lookahead for `plainClaimLineStart`: is this line markup-only, or a paragraph
121810
+ // that merely starts with a tag? Run via `effects.check` so it never consumes.
121811
+ const markupOnlyContinuation = {
121812
+ tokenize: tokenizeMarkupOnlyContinuation,
121813
+ partial: true,
121814
+ };
121815
+
121635
121816
  ;// ./lib/micromark/mdx-component/syntax.ts
121636
121817
 
121637
121818
 
121638
121819
 
121639
121820
 
121640
121821
 
121822
+
121641
121823
  // Raw tags (type-1: pre/script/style/textarea) and block tags (type-6: div,
121642
121824
  // section, …) always start a block, so they stay flow even with trailing
121643
121825
  // content. Other lowercase tags (i, span, …) follow the type-7 rule and only
@@ -121647,16 +121829,12 @@ const htmlFlowTagNames = new Set([...htmlRawNames, ...htmlBlockNames]);
121647
121829
  // blank lines between nested JSX siblings don't fragment the block. Excludes table
121648
121830
  // tags (mdxishTables owns their blank lines) and voids (never close).
121649
121831
  const plainBlockClaimTagNames = new Set([...htmlBlockNames].filter(tag => !HTML_TABLE_STRUCTURE_TAGS.has(tag) && !HTML_VOID_ELEMENTS.has(tag)));
121650
- const mdx_component_syntax_nonLazyContinuationStart = {
121651
- tokenize: mdx_component_syntax_tokenizeNonLazyContinuationStart,
121652
- partial: true,
121653
- };
121654
- // Lookahead for `plainClaimLineStart`: is this line markup-only, or a paragraph
121655
- // that merely starts with a tag? Run via `effects.check` so it never consumes.
121656
- const markupOnlyContinuation = {
121657
- tokenize: tokenizeMarkupOnlyContinuation,
121658
- partial: true,
121659
- };
121832
+ // Both are 4 columns per CommonMark, but they mean different things: a tab advances
121833
+ // to the next multiple of TAB_STOP_WIDTH, and INDENTED_CODE_MIN_COLUMNS is the depth
121834
+ // at which a line would fragment into indented code. Named separately so the two
121835
+ // concepts don't read as one incidental literal.
121836
+ const TAB_STOP_WIDTH = 4;
121837
+ const INDENTED_CODE_MIN_COLUMNS = 4;
121660
121838
  function resolveToMdxComponent(events) {
121661
121839
  let index = events.length;
121662
121840
  while (index > 0) {
@@ -121715,6 +121893,18 @@ function createTokenize(mode) {
121715
121893
  // `plainClaimLineStart`: after a blank line it may only continue on a tag line.
121716
121894
  let isPlainBlockClaim = false;
121717
121895
  let pendingBlankLine = false;
121896
+ // Leading indent columns of the current plain-claim line, reset per line; ≥4 is
121897
+ // where CommonMark would fragment the island as indented code. Tabs advance to the
121898
+ // next 4-column stop — the same rule `expandIndentToColumns`
121899
+ // (processor/transform/mdxish/indentation.ts) applies, kept in sync by hand since
121900
+ // this side works on a `Code` stream, not a string. NB: do NOT swap this for
121901
+ // `self.now().column`; micromark bumps the point column by 1 per `horizontalTab`
121902
+ // code (the trailing `virtualSpace` codes don't move it), so it measures a tab as
121903
+ // 1 column, reviving the tab-under-measurement bug this math exists to avoid.
121904
+ let plainClaimIndentColumns = 0;
121905
+ // True once a non-blank line follows the opener: a deep island below it is nested
121906
+ // (cosmetic indent), not top-of-body indented code.
121907
+ let sawPlainBlockBodyContent = false;
121718
121908
  // Code span tracking
121719
121909
  let codeSpanOpenSize = 0;
121720
121910
  let codeSpanCloseSize = 0;
@@ -122014,7 +122204,7 @@ function createTokenize(mode) {
122014
122204
  }
122015
122205
  // Continuation for multi-line opening tags
122016
122206
  function openTagContinuationStart(code) {
122017
- return effects.check(mdx_component_syntax_nonLazyContinuationStart, openTagContinuationNonLazy, continuationAfter)(code);
122207
+ return effects.check(continuation_checks_nonLazyContinuationStart, openTagContinuationNonLazy, continuationAfter)(code);
122018
122208
  }
122019
122209
  function openTagContinuationNonLazy(code) {
122020
122210
  sawLineEnding = true;
@@ -122059,6 +122249,9 @@ function createTokenize(mode) {
122059
122249
  }
122060
122250
  if (code !== codes.space && code !== codes.horizontalTab) {
122061
122251
  openerLineHasContent = true;
122252
+ // Continuation content marks a later deep island as nested, not indented code.
122253
+ if (!onOpenerLine)
122254
+ sawPlainBlockBodyContent = true;
122062
122255
  }
122063
122256
  if (code === codes.backslash) {
122064
122257
  effects.consume(code);
@@ -122139,7 +122332,7 @@ function createTokenize(mode) {
122139
122332
  return inFencedCode;
122140
122333
  }
122141
122334
  function fencedCodeContinuationStart(code) {
122142
- return effects.check(mdx_component_syntax_nonLazyContinuationStart, fencedCodeContinuationNonLazy, continuationAfter)(code);
122335
+ return effects.check(continuation_checks_nonLazyContinuationStart, fencedCodeContinuationNonLazy, continuationAfter)(code);
122143
122336
  }
122144
122337
  function fencedCodeContinuationNonLazy(code) {
122145
122338
  sawLineEnding = true;
@@ -122347,7 +122540,7 @@ function createTokenize(mode) {
122347
122540
  }
122348
122541
  // ── Body continuation (line endings) ───────────────────────────────────
122349
122542
  function bodyContinuationStart(code) {
122350
- return effects.check(mdx_component_syntax_nonLazyContinuationStart, bodyContinuationNonLazy, continuationAfter)(code);
122543
+ return effects.check(continuation_checks_nonLazyContinuationStart, bodyContinuationNonLazy, continuationAfter)(code);
122351
122544
  }
122352
122545
  function bodyContinuationNonLazy(code) {
122353
122546
  sawLineEnding = true;
@@ -122373,8 +122566,10 @@ function createTokenize(mode) {
122373
122566
  return inBodyBraceString(code);
122374
122567
  return inBodyBraceExpr(code);
122375
122568
  }
122376
- if (isPlainBlockClaim)
122569
+ if (isPlainBlockClaim) {
122570
+ plainClaimIndentColumns = 0;
122377
122571
  return plainClaimLineStart(code);
122572
+ }
122378
122573
  return bodyLineStart(code);
122379
122574
  }
122380
122575
  // Dispatch a non-blank continuation line: fenced code at line start, else body.
@@ -122404,6 +122599,8 @@ function createTokenize(mode) {
122404
122599
  function plainClaimLineStart(code) {
122405
122600
  // Leading whitespace only → treat as a blank line, matching CommonMark.
122406
122601
  if (code === codes.space || code === codes.horizontalTab) {
122602
+ plainClaimIndentColumns +=
122603
+ code === codes.horizontalTab ? TAB_STOP_WIDTH - (plainClaimIndentColumns % TAB_STOP_WIDTH) : 1;
122407
122604
  effects.consume(code);
122408
122605
  return plainClaimLineStart;
122409
122606
  }
@@ -122412,10 +122609,18 @@ function createTokenize(mode) {
122412
122609
  effects.exit('mdxComponentData');
122413
122610
  return bodyContinuationStart(code);
122414
122611
  }
122415
- // Across a blank line the block only continues onto a markup-only line; a
122416
- // paragraph that merely starts with a tag must fall back so its markdown
122417
- // parses and rehype-raw re-nests it into the wrapper.
122418
122612
  if (pendingBlankLine) {
122613
+ // A 4+ col island nested under other tags is cosmetic nesting indent, not code:
122614
+ // keep claiming so promotion dedents + re-parses it as markdown (RM-17560).
122615
+ // Tags whose bodies stay raw are excluded — a claimed island there would never
122616
+ // be re-parsed and would leak as literal text.
122617
+ if (plainClaimIndentColumns >= INDENTED_CODE_MIN_COLUMNS &&
122618
+ sawPlainBlockBodyContent &&
122619
+ !NON_REPARSED_BODY_TAGS.has(tagName)) {
122620
+ return plainClaimContinue(code);
122621
+ }
122622
+ // Otherwise only a markup-only tag line continues; markdown/prose falls back to
122623
+ // CommonMark so it parses and rehype-raw re-nests it into the wrapper.
122419
122624
  if (code !== codes.lessThan)
122420
122625
  return nok(code);
122421
122626
  return effects.check(markupOnlyContinuation, plainClaimContinue, nok)(code);
@@ -122436,66 +122641,6 @@ function createTokenize(mode) {
122436
122641
  }
122437
122642
  };
122438
122643
  }
122439
- function mdx_component_syntax_tokenizeNonLazyContinuationStart(effects, ok, nok) {
122440
- // eslint-disable-next-line @typescript-eslint/no-this-alias
122441
- const self = this;
122442
- return start;
122443
- function start(code) {
122444
- if (markdownLineEnding(code)) {
122445
- effects.enter(types_types.lineEnding);
122446
- effects.consume(code);
122447
- effects.exit(types_types.lineEnding);
122448
- return after;
122449
- }
122450
- return nok(code);
122451
- }
122452
- function after(code) {
122453
- if (self.parser.lazy[self.now().line]) {
122454
- return nok(code);
122455
- }
122456
- return ok(code);
122457
- }
122458
- }
122459
- // A markup-only line opens with a tag (`<x`/`</x`) and ends (ignoring trailing
122460
- // spaces) at a `>`. That distinguishes a structural continuation like
122461
- // `<span>b</span></div>` from a paragraph like `<b>Note:</b> read *this*`.
122462
- function tokenizeMarkupOnlyContinuation(effects, ok, nok) {
122463
- let lastNonSpace = null;
122464
- return start;
122465
- function start(code) {
122466
- // Caller guarantees we are at `<` at the (already de-indented) line start.
122467
- effects.enter(types_types.data);
122468
- effects.consume(code);
122469
- return afterLessThan;
122470
- }
122471
- function afterLessThan(code) {
122472
- if (code === codes.slash) {
122473
- effects.consume(code);
122474
- return afterSlash;
122475
- }
122476
- return afterSlash(code);
122477
- }
122478
- // The `<` (or `</`) must introduce a real tag, not a stray `<` in prose.
122479
- function afterSlash(code) {
122480
- if (asciiAlpha(code)) {
122481
- lastNonSpace = code;
122482
- effects.consume(code);
122483
- return scanToLineEnd;
122484
- }
122485
- effects.exit(types_types.data);
122486
- return nok(code);
122487
- }
122488
- function scanToLineEnd(code) {
122489
- if (code === null || markdownLineEnding(code)) {
122490
- effects.exit(types_types.data);
122491
- return lastNonSpace === codes.greaterThan ? ok(code) : nok(code);
122492
- }
122493
- if (!markdownSpace(code))
122494
- lastNonSpace = code;
122495
- effects.consume(code);
122496
- return scanToLineEnd;
122497
- }
122498
- }
122499
122644
  /**
122500
122645
  * Micromark extension that tokenizes MDX-like components.
122501
122646
  *
@@ -122549,6 +122694,7 @@ function mdxComponent() {
122549
122694
 
122550
122695
 
122551
122696
 
122697
+
122552
122698
 
122553
122699
  const buildInlineMdProcessor = (safeMode) => {
122554
122700
  // `jsxTable` must be present so a `<Table>`/`<table>` nested in a component
@@ -122620,7 +122766,7 @@ const toMdxJsxTextElement = (name, attributes, children, position) => ({
122620
122766
  // Raw-body tags (pre/script/style/textarea) must stay byte-exact; table
122621
122767
  // structure (`table` + `tableTags`) is owned by `mdxishTables` and figures by
122622
122768
  // `mdxishJsxToMdast`, both of which run later on raw html nodes.
122623
- const NON_PROMOTABLE_PLAIN_TAGS = new Set([...htmlRawNames, ...tableTags, 'table', 'figure', 'figcaption']);
122769
+ const NON_PROMOTABLE_PLAIN_TAGS = new Set([...htmlRawNames, ...tableTags, 'table', ...NON_REPARSED_BODY_TAGS]);
122624
122770
  const NESTED_TABLE_RE = /<table[\s>]/i;
122625
122771
  const isMarkdownPromotableHtmlTag = (tag) => !NON_PROMOTABLE_PLAIN_TAGS.has(tag);
122626
122772
  // Expression nodes count as plain so `<div>{1+1}</div>` keeps its current
@@ -122979,15 +123125,22 @@ function safeDeindent(text) {
122979
123125
  */
122980
123126
  const parseMdChildren = (value, safeMode) => {
122981
123127
  const parsed = getInlineMdProcessor({ safeMode }).parse(terminateHtmlFlowBlocks(safeDeindent(value).trim()));
123128
+ // Promote nested wrappers bottom-up so an outer wrapper sees markdown buried in a
123129
+ // child claimed whole (e.g. `<li>` in `<ol>`) before its containsMarkdownConstruct check (RM-17560).
123130
+ // eslint-disable-next-line @typescript-eslint/no-use-before-define -- mutually recursive; hoisted decl, safe at runtime
123131
+ promoteComponentBlocks(parsed, safeMode, null);
122982
123132
  return parsed.children || [];
122983
123133
  };
122984
- // Parses trailing content into sibling nodes and re-queues the parent so any
122985
- // components among them get processed.
122986
- const parseSibling = (stack, parent, index, sibling, safeMode) => {
123134
+ // Splices trailing content in as sibling nodes. parseMdChildren has already
123135
+ // promoted any components nested among them (bottom-up); the main loop's
123136
+ // index-based walk then reaches these spliced siblings and the original children
123137
+ // they shift down, so no parent re-queue is needed. Each spliced subtree is marked
123138
+ // `promoted` so the walk doesn't redundantly re-descend into it (its html is gone).
123139
+ const parseSibling = (parent, index, sibling, safeMode, promoted) => {
122987
123140
  const siblingNodes = parseMdChildren(sibling, safeMode);
122988
123141
  if (siblingNodes.length > 0) {
122989
123142
  parent.children.splice(index + 1, 0, ...siblingNodes);
122990
- stack.push(parent);
123143
+ siblingNodes.forEach(siblingNode => promoted.add(siblingNode));
122991
123144
  }
122992
123145
  };
122993
123146
  // Ends the position at `consumedLength` so the component doesn't claim trailing
@@ -123053,16 +123206,19 @@ const substituteNodeWithMdxNode = (parent, index, mdxNode) => {
123053
123206
  * The opening tag, content, and closing tag are all captured in one HTML node
123054
123207
  * (guaranteed by the mdx-component tokenizer).
123055
123208
  */
123056
- const mdxishMdxComponentBlocks = (opts = {}) => (tree, file) => {
123209
+ function promoteComponentBlocks(tree, safeMode, source) {
123057
123210
  const stack = [tree];
123058
- const safeMode = !!opts.safeMode;
123059
- const source = file?.value ? String(file.value) : null;
123060
123211
  const parseOpts = { preserveExpressionsAsText: safeMode };
123212
+ // Subtrees a nested parseMdChildren already promoted wholesale (spliced siblings):
123213
+ // re-descending them finds no html to promote, so skip them.
123214
+ const promoted = new WeakSet();
123061
123215
  const processChildNode = (parent, index) => {
123062
123216
  const node = parent.children[index];
123063
123217
  if (!node)
123064
123218
  return;
123065
- if ('children' in node && Array.isArray(node.children)) {
123219
+ // Descend into container nodes (lists, blockquotes, …) so their html children
123220
+ // are reached — unless the subtree was already promoted upstream.
123221
+ if ('children' in node && Array.isArray(node.children) && !promoted.has(node)) {
123066
123222
  stack.push(node);
123067
123223
  }
123068
123224
  // Only html nodes can be an unparsed MDX component.
@@ -123117,7 +123273,7 @@ const mdxishMdxComponentBlocks = (opts = {}) => (tree, file) => {
123117
123273
  substituteNodeWithMdxNode(parent, index, componentNode);
123118
123274
  const remainingContent = contentAfterTag.trim();
123119
123275
  if (remainingContent) {
123120
- parseSibling(stack, parent, index, remainingContent, safeMode);
123276
+ parseSibling(parent, index, remainingContent, safeMode, promoted);
123121
123277
  }
123122
123278
  return;
123123
123279
  }
@@ -123144,43 +123300,57 @@ const mdxishMdxComponentBlocks = (opts = {}) => (tree, file) => {
123144
123300
  return;
123145
123301
  // Lowercase tags are usually inline; unwrap a sole paragraph so their
123146
123302
  // phrasing content isn't spuriously block-wrapped.
123303
+ let unwrappedSoleParagraph = false;
123147
123304
  if (!isPascal && parsedChildren.length === 1 && parsedChildren[0].type === 'paragraph') {
123148
123305
  parsedChildren = parsedChildren[0].children;
123306
+ unwrappedSoleParagraph = true;
123307
+ }
123308
+ // Without trailing content the whole node position is correct. With it, end
123309
+ // precisely at the closing tag — preferring source offsets when available (the
123310
+ // node's value strips blockquote/list prefixes), else the consumed span.
123311
+ let endPosition = node.position;
123312
+ if (contentAfterClose) {
123313
+ endPosition = source
123314
+ ? positionEndingAtClosingTagInSource(node.position, closingTagStr, source)
123315
+ : positionEndingAtConsumed(node.position, value, leadingWhitespace + openingTagEnd + closingTagIndex + closingTagStr.length);
123149
123316
  }
123150
123317
  const componentNode = createComponentNode({
123151
123318
  tag,
123152
123319
  attributes,
123153
123320
  children: parsedChildren,
123154
123321
  startPosition: node.position,
123155
- // With trailing content, end precisely at the closing tag. Prefer source
123156
- // offsets when available (the node's value strips blockquote/list
123157
- // prefixes); otherwise fall back to the whole node position.
123158
- endPosition: contentAfterClose
123159
- ? source
123160
- ? positionEndingAtClosingTagInSource(node.position, closingTagStr, source)
123161
- : positionEndingAtConsumed(node.position, value, leadingWhitespace + openingTagEnd + closingTagIndex + closingTagStr.length)
123162
- : node.position,
123322
+ endPosition,
123163
123323
  });
123164
123324
  substituteNodeWithMdxNode(parent, index, componentNode);
123165
- // Re-queue whichever side may hold further components.
123166
- if (contentAfterClose) {
123167
- parseSibling(stack, parent, index, contentAfterClose, safeMode);
123168
- }
123169
- else if (componentNode.children.length > 0) {
123325
+ // The unwrap reparented the children out of their paragraph, so re-walk them
123326
+ // since the children HTML may contain promotable syntax (e.g. `{…}`-attr tags)
123327
+ if (unwrappedSoleParagraph) {
123170
123328
  stack.push(componentNode);
123171
123329
  }
123330
+ // Trailing content after the close becomes siblings; parseMdChildren has
123331
+ // already promoted any components nested inside both sides, so the promoted
123332
+ // subtree itself needs no re-queue.
123333
+ if (contentAfterClose) {
123334
+ parseSibling(parent, index, contentAfterClose, safeMode, promoted);
123335
+ }
123172
123336
  }
123173
123337
  };
123174
- // Depth-first so nodes keep their source order.
123338
+ // Depth-first so nodes keep their source order. Index-based (not forEach) and
123339
+ // re-reading length each step: parseSibling splices siblings in mid-iteration, and
123340
+ // those — plus the original children they shift down — must all stay eligible.
123175
123341
  while (stack.length) {
123176
123342
  const parent = stack.pop();
123177
123343
  if (parent?.children) {
123178
- parent.children.forEach((_child, index) => {
123344
+ for (let index = 0; index < parent.children.length; index += 1) {
123179
123345
  processChildNode(parent, index);
123180
- });
123346
+ }
123181
123347
  }
123182
123348
  }
123183
123349
  return tree;
123350
+ }
123351
+ const mdxishMdxComponentBlocks = (opts = {}) => (tree, file) => {
123352
+ const source = file?.value ? String(file.value) : null;
123353
+ return promoteComponentBlocks(tree, !!opts.safeMode, source);
123184
123354
  };
123185
123355
  /* harmony default export */ const mdx_blocks = (mdxishMdxComponentBlocks);
123186
123356
 
@@ -126779,6 +126949,7 @@ function loadComponents() {
126779
126949
 
126780
126950
 
126781
126951
 
126952
+
126782
126953
 
126783
126954
 
126784
126955
  const defaultTransformers = [
@@ -126793,10 +126964,11 @@ const defaultTransformers = [
126793
126964
  * Runs a series of string-level transformations before micromark/remark parsing:
126794
126965
  * 1. Canonicalize closing tags with stray whitespace (e.g., `</ td >` → `</td>`)
126795
126966
  * 2. Normalize malformed table separator syntax (e.g., `|: ---` → `| :---`)
126796
- * 3. Terminate HTML flow blocks so subsequent content isn't swallowed
126797
- * 4. Close invalid "self-closing" HTML tags (e.g., `<i />` → `<i></i>`)
126798
- * 5. Normalize compact ATX headings (e.g., `#Heading``# Heading`)
126799
- * 6. Replace snake_case component names with parser-safe placeholders
126967
+ * 3. Collapse blank lines inside `<svg>`/`<math>` so their children aren't fragmented
126968
+ * 4. Terminate HTML flow blocks so subsequent content isn't swallowed
126969
+ * 5. Close invalid "self-closing" HTML tags (e.g., `<i />` `<i></i>`)
126970
+ * 6. Normalize compact ATX headings (e.g., `#Heading` → `# Heading`)
126971
+ * 7. Replace snake_case component names with parser-safe placeholders
126800
126972
  */
126801
126973
  function preprocessContent(content, opts) {
126802
126974
  const { knownComponents } = opts;
@@ -126804,6 +126976,10 @@ function preprocessContent(content, opts) {
126804
126976
  // classification in `terminateHtmlFlowBlocks` is accurate)
126805
126977
  let result = normalizeClosingTagWhitespace(content);
126806
126978
  result = normalizeTableSeparator(result);
126979
+ // Before terminateHtmlFlowBlocks: a blank line inside an <svg>/<math> island
126980
+ // would otherwise fragment it (children spill out as an indented code block once
126981
+ // a wrapper re-parses its deindented body — #1545).
126982
+ result = collapseForeignContentBlankLines(result);
126807
126983
  result = terminateHtmlFlowBlocks(result);
126808
126984
  result = closeSelfClosingHtmlTags(result);
126809
126985
  result = normalizeCompactHeadings(result);
@@ -127527,6 +127703,8 @@ function restoreMagicBlocks(replaced, blocks) {
127527
127703
 
127528
127704
 
127529
127705
 
127706
+
127707
+
127530
127708
  /**
127531
127709
  * Removes Markdown and MDX comments.
127532
127710
  */
@@ -127537,10 +127715,16 @@ async function stripComments(doc, { mdx, mdxish } = {}) {
127537
127715
  // 1. we can rely on remarkMdx to parse MDXish
127538
127716
  // 2. we need to parse JSX comments into mdxTextExpression nodes so that the transformers can pick them up
127539
127717
  // 3. we need to claim <HTMLBlock> before htmlFlow intercepts its inner HTML tags
127718
+ // 4. we need mdxComponent to parse custom components as one node, and prevent the tag from getting escaped
127540
127719
  if (mdxish) {
127541
127720
  processor
127542
- .data('micromarkExtensions', [htmlBlockComponent(), jsxTable(), mdxExpression({ allowEmpty: true })])
127543
- .data('fromMarkdownExtensions', [htmlBlockComponentFromMarkdown(), jsxTableFromMarkdown(), mdxExpressionFromMarkdown()])
127721
+ .data('micromarkExtensions', [htmlBlockComponent(), jsxTable(), mdxComponent(), mdxExpression({ allowEmpty: true })])
127722
+ .data('fromMarkdownExtensions', [
127723
+ htmlBlockComponentFromMarkdown(),
127724
+ jsxTableFromMarkdown(),
127725
+ mdxComponentFromMarkdown(),
127726
+ mdxExpressionFromMarkdown(),
127727
+ ])
127544
127728
  .data('toMarkdownExtensions', [mdxExpressionToMarkdown()]);
127545
127729
  }
127546
127730
  processor