@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.
@@ -86449,8 +86449,8 @@ function Anchor(props) {
86449
86449
 
86450
86450
 
86451
86451
 
86452
- const Card = ({ badge, children, href, kind = 'card', icon, iconColor, target, title }) => {
86453
- const Tag = href ? 'a' : 'div';
86452
+ const Card = ({ badge, children, href, kind = 'card', icon, iconColor, LinkComponent = 'a', target, title, }) => {
86453
+ const Tag = href ? LinkComponent : 'div';
86454
86454
  return (external_react_default().createElement(Tag, { className: `Card Card_${kind}`, href: href, target: target },
86455
86455
  icon && external_react_default().createElement(components_Icon, { className: "Card-icon", icon: icon, iconColor: iconColor }),
86456
86456
  external_react_default().createElement("div", { className: "Card-content" },
@@ -96476,6 +96476,21 @@ const HTML_TABLE_STRUCTURE_TAGS = new Set([
96476
96476
  'caption',
96477
96477
  'colgroup',
96478
96478
  ]);
96479
+ /**
96480
+ * Tags whose bodies a later mdxish transform keeps raw and never re-parses as
96481
+ * markdown. Both layers depend on this property, not on the tags being figures:
96482
+ * the transformer treats them as non-promotable, and the tokenizer must not claim
96483
+ * markdown islands inside them (a claimed island would never be re-parsed and would
96484
+ * leak as literal text). Currently just the figure tags, whose bodies are owned by
96485
+ * the figure reassembly transform (`mdxishJsxToMdast`).
96486
+ */
96487
+ const NON_REPARSED_BODY_TAGS = new Set(['figure', 'figcaption']);
96488
+ /**
96489
+ * The two HTML "foreign content" namespaces: SVG and MathML. Their descendants
96490
+ * (`<path>`, `<mrow>`, …) are namespaced XML, not HTML tags or components. A closed
96491
+ * set per the HTML spec, so transforms can treat these roots as opaque islands.
96492
+ */
96493
+ const FOREIGN_CONTENT_TAGS = ['svg', 'math'];
96479
96494
  /**
96480
96495
  * HTML void elements — elements that have no closing tag and no children.
96481
96496
  *
@@ -117685,6 +117700,9 @@ function getComponentName(componentName, components) {
117685
117700
 
117686
117701
 
117687
117702
 
117703
+ // Foreign-content (SVG/MathML) roots — subtrees the component transform leaves
117704
+ // untouched (their children are namespaced XML, not HTML tags/components).
117705
+ const FOREIGN_CONTENT_ROOTS = new Set(FOREIGN_CONTENT_TAGS);
117688
117706
  function isElementContentNode(node) {
117689
117707
  return node.type === 'element' || node.type === 'text' || node.type === 'comment';
117690
117708
  }
@@ -117860,6 +117878,12 @@ const rehypeMdxishComponents = ({ components, processMarkdown }) => {
117860
117878
  lib_visit(tree, 'element', (node, index, parent) => {
117861
117879
  if (index === undefined || !parent)
117862
117880
  return;
117881
+ // Skip foreign-content subtrees: their namespaced children (<path>, <mrow>, …)
117882
+ // aren't HTML tags or components, so the "unknown component" removal below would
117883
+ // otherwise delete them. (svg/math themselves are standard HTML tags, kept.)
117884
+ // eslint-disable-next-line consistent-return
117885
+ if (FOREIGN_CONTENT_ROOTS.has(node.tagName))
117886
+ return lib_SKIP;
117863
117887
  // Parse Image caption as markdown so it renders formatted (bold, code,
117864
117888
  // decoded entities) in the figcaption instead of as a raw string.
117865
117889
  // rehypeRaw strips children from <img> (void element), so we must
@@ -118171,6 +118195,81 @@ function closeSelfClosingHtmlTags(content) {
118171
118195
  return restoreCodeBlocks(result, protectedCode);
118172
118196
  }
118173
118197
 
118198
+ ;// ./processor/transform/mdxish/collapse-foreign-content-blank-lines.ts
118199
+
118200
+
118201
+ // `svg|math`, from the canonical list so this stays in sync with the component transform.
118202
+ const ROOT_ALT = FOREIGN_CONTENT_TAGS.join('|');
118203
+ // A foreign-content opener. The `[\s/>]` boundary requires a real tag delimiter after the
118204
+ // root name, so lookalikes like `<svgfoo>` and custom elements like `<svg-icon>` are ignored.
118205
+ const ANY_ROOT_RE = new RegExp(`<(?:${ROOT_ALT})(?=[\\s/>])`, 'i');
118206
+ // Tag body: swallows whole quoted attribute values so a `>` inside a quote (e.g.
118207
+ // `<svg data-x="a > b" />`) can't be mistaken for the tag terminator; other non-`>`
118208
+ // chars (including newlines) are consumed one at a time.
118209
+ const TAG_BODY = '(?:"[^"]*"|\'[^\']*\'|[^>\'"])*?';
118210
+ // One whole svg/math tag (opener, self-closer, or closer), matched whole so attributes
118211
+ // and `>` may span lines. The `[\s/>]` boundary after the root name keeps `<svgfoo>`/
118212
+ // `<svg-icon>` out. Group 1 is `/` only for a self-closer.
118213
+ const FOREIGN_TAG_RE = new RegExp(`<(?:${ROOT_ALT})(?=[\\s/>])${TAG_BODY}(/)?>|</(?:${ROOT_ALT})(?=[\\s/>])${TAG_BODY}>`, 'gi');
118214
+ // An HTML comment. Foreign markup inside one is inert and must not open an island.
118215
+ const HTML_COMMENT_RE = /<!--[\s\S]*?-->/g;
118216
+ const withinAny = (spans, offset) => spans.some(([start, end]) => offset >= start && offset < end);
118217
+ /**
118218
+ * `[start, end)` spans of every *matched* SVG/MathML pair — the regions whose blank
118219
+ * lines are known-insignificant XML. Matching whole tags (not per-line open/close
118220
+ * counts) keeps multi-line openers and self-closers balanced, so a wrapped
118221
+ * `<svg … />` can't latch and swallow the doc (#1545).
118222
+ *
118223
+ * Tags are paired innermost-first, as a parser would; an opener still unpaired at EOF
118224
+ * closed nothing and so covers nothing. That keeps a stray `<svg>` in prose from
118225
+ * swallowing every blank line after it, without hiding the real islands that follow.
118226
+ */
118227
+ function findForeignContentSpans(text) {
118228
+ const spans = [];
118229
+ // Foreign tags inside an HTML comment are inert; a stray `<svg>`/`<math>` in a comment
118230
+ // must not open an island and latch onto the rest of the doc.
118231
+ const comments = [...text.matchAll(HTML_COMMENT_RE)].map((m) => [m.index ?? 0, (m.index ?? 0) + m[0].length]);
118232
+ // Offsets of openers still waiting for their closer, innermost last.
118233
+ const openOffsets = [];
118234
+ [...text.matchAll(FOREIGN_TAG_RE)].forEach(match => {
118235
+ const offset = match.index ?? 0;
118236
+ if (withinAny(comments, offset))
118237
+ return; // markup inside an HTML comment is inert
118238
+ if (match[1] === '/')
118239
+ return; // self-closing tag opens no island
118240
+ if (match[0].startsWith('</')) {
118241
+ const start = openOffsets.pop();
118242
+ if (start !== undefined)
118243
+ spans.push([start, offset + match[0].length]); // a closer with no opener closes nothing
118244
+ }
118245
+ else {
118246
+ openOffsets.push(offset);
118247
+ }
118248
+ });
118249
+ return spans;
118250
+ }
118251
+ /**
118252
+ * Drop blank lines inside `<svg>`/`<math>` islands: their whitespace is
118253
+ * insignificant XML, but a blank line ends the CommonMark HTML block and spills the
118254
+ * children out as a code block (#1545). Collapsing keeps the island one block.
118255
+ */
118256
+ function collapseForeignContentBlankLines(content) {
118257
+ // Fast path: nothing to do when the doc has no foreign-content island.
118258
+ if (!ANY_ROOT_RE.test(content))
118259
+ return content;
118260
+ const { protectedContent, protectedCode } = protectCodeBlocks(content);
118261
+ const islands = findForeignContentSpans(protectedContent);
118262
+ // Drop blank lines inside an island; keep everything else verbatim.
118263
+ const lines = [];
118264
+ let lineStart = 0;
118265
+ protectedContent.split('\n').forEach(line => {
118266
+ if (line.trim().length > 0 || !withinAny(islands, lineStart))
118267
+ lines.push(line);
118268
+ lineStart += line.length + 1; // +1 for the '\n' that split() removed
118269
+ });
118270
+ return restoreCodeBlocks(lines.join('\n'), protectedCode);
118271
+ }
118272
+
118174
118273
  ;// ./lib/utils/mdxish/mdxish-component-tag-parser.ts
118175
118274
  // Extract the tag name (PascalCase or lowercase HTML) at the start of a tag string.
118176
118275
  const tagNamePattern = /^<([A-Za-z][A-Za-z0-9_-]*)/;
@@ -120163,12 +120262,95 @@ function tokenizeMagicBlockText(effects, ok, nok) {
120163
120262
  */
120164
120263
 
120165
120264
 
120265
+ ;// ./lib/micromark/mdx-component/continuation-checks.ts
120266
+
120267
+
120268
+ /**
120269
+ * Partial constructs the `mdxComponent` tokenizer runs via `effects.check` to
120270
+ * decide whether the next line continues the block. They live here (not in
120271
+ * `syntax.ts`) because they hold none of `createTokenize`'s closure state — each
120272
+ * is a self-contained, single-line lookahead.
120273
+ */
120274
+ function continuation_checks_tokenizeNonLazyContinuationStart(effects, ok, nok) {
120275
+ // eslint-disable-next-line @typescript-eslint/no-this-alias
120276
+ const self = this;
120277
+ return start;
120278
+ function start(code) {
120279
+ if (markdownLineEnding(code)) {
120280
+ effects.enter(types_types.lineEnding);
120281
+ effects.consume(code);
120282
+ effects.exit(types_types.lineEnding);
120283
+ return after;
120284
+ }
120285
+ return nok(code);
120286
+ }
120287
+ function after(code) {
120288
+ if (self.parser.lazy[self.now().line]) {
120289
+ return nok(code);
120290
+ }
120291
+ return ok(code);
120292
+ }
120293
+ }
120294
+ // A markup-only line opens with a tag (`<x`/`</x`) and ends (ignoring trailing
120295
+ // spaces) at a `>`. That distinguishes a structural continuation like
120296
+ // `<span>b</span></div>` from a paragraph like `<b>Note:</b> read *this*`.
120297
+ function tokenizeMarkupOnlyContinuation(effects, ok, nok) {
120298
+ let lastNonSpace = null;
120299
+ return start;
120300
+ function start(code) {
120301
+ // Caller guarantees we are at `<` at the (already de-indented) line start.
120302
+ effects.enter(types_types.data);
120303
+ effects.consume(code);
120304
+ return afterLessThan;
120305
+ }
120306
+ function afterLessThan(code) {
120307
+ if (code === codes.slash) {
120308
+ effects.consume(code);
120309
+ return afterSlash;
120310
+ }
120311
+ return afterSlash(code);
120312
+ }
120313
+ // The `<` (or `</`) must introduce a real tag, not a stray `<` in prose.
120314
+ function afterSlash(code) {
120315
+ if (asciiAlpha(code)) {
120316
+ lastNonSpace = code;
120317
+ effects.consume(code);
120318
+ return scanToLineEnd;
120319
+ }
120320
+ effects.exit(types_types.data);
120321
+ return nok(code);
120322
+ }
120323
+ function scanToLineEnd(code) {
120324
+ if (code === null || markdownLineEnding(code)) {
120325
+ effects.exit(types_types.data);
120326
+ return lastNonSpace === codes.greaterThan ? ok(code) : nok(code);
120327
+ }
120328
+ if (!markdownSpace(code))
120329
+ lastNonSpace = code;
120330
+ effects.consume(code);
120331
+ return scanToLineEnd;
120332
+ }
120333
+ }
120334
+ // A line ending whose next line isn't a lazy paragraph continuation. Checked so a
120335
+ // component body's blank/continuation lines aren't stolen by an interrupted paragraph.
120336
+ const continuation_checks_nonLazyContinuationStart = {
120337
+ tokenize: continuation_checks_tokenizeNonLazyContinuationStart,
120338
+ partial: true,
120339
+ };
120340
+ // Lookahead for `plainClaimLineStart`: is this line markup-only, or a paragraph
120341
+ // that merely starts with a tag? Run via `effects.check` so it never consumes.
120342
+ const markupOnlyContinuation = {
120343
+ tokenize: tokenizeMarkupOnlyContinuation,
120344
+ partial: true,
120345
+ };
120346
+
120166
120347
  ;// ./lib/micromark/mdx-component/syntax.ts
120167
120348
 
120168
120349
 
120169
120350
 
120170
120351
 
120171
120352
 
120353
+
120172
120354
  // Raw tags (type-1: pre/script/style/textarea) and block tags (type-6: div,
120173
120355
  // section, …) always start a block, so they stay flow even with trailing
120174
120356
  // content. Other lowercase tags (i, span, …) follow the type-7 rule and only
@@ -120178,16 +120360,12 @@ const htmlFlowTagNames = new Set([...htmlRawNames, ...htmlBlockNames]);
120178
120360
  // blank lines between nested JSX siblings don't fragment the block. Excludes table
120179
120361
  // tags (mdxishTables owns their blank lines) and voids (never close).
120180
120362
  const plainBlockClaimTagNames = new Set([...htmlBlockNames].filter(tag => !HTML_TABLE_STRUCTURE_TAGS.has(tag) && !HTML_VOID_ELEMENTS.has(tag)));
120181
- const mdx_component_syntax_nonLazyContinuationStart = {
120182
- tokenize: mdx_component_syntax_tokenizeNonLazyContinuationStart,
120183
- partial: true,
120184
- };
120185
- // Lookahead for `plainClaimLineStart`: is this line markup-only, or a paragraph
120186
- // that merely starts with a tag? Run via `effects.check` so it never consumes.
120187
- const markupOnlyContinuation = {
120188
- tokenize: tokenizeMarkupOnlyContinuation,
120189
- partial: true,
120190
- };
120363
+ // Both are 4 columns per CommonMark, but they mean different things: a tab advances
120364
+ // to the next multiple of TAB_STOP_WIDTH, and INDENTED_CODE_MIN_COLUMNS is the depth
120365
+ // at which a line would fragment into indented code. Named separately so the two
120366
+ // concepts don't read as one incidental literal.
120367
+ const TAB_STOP_WIDTH = 4;
120368
+ const INDENTED_CODE_MIN_COLUMNS = 4;
120191
120369
  function resolveToMdxComponent(events) {
120192
120370
  let index = events.length;
120193
120371
  while (index > 0) {
@@ -120246,6 +120424,18 @@ function createTokenize(mode) {
120246
120424
  // `plainClaimLineStart`: after a blank line it may only continue on a tag line.
120247
120425
  let isPlainBlockClaim = false;
120248
120426
  let pendingBlankLine = false;
120427
+ // Leading indent columns of the current plain-claim line, reset per line; ≥4 is
120428
+ // where CommonMark would fragment the island as indented code. Tabs advance to the
120429
+ // next 4-column stop — the same rule `expandIndentToColumns`
120430
+ // (processor/transform/mdxish/indentation.ts) applies, kept in sync by hand since
120431
+ // this side works on a `Code` stream, not a string. NB: do NOT swap this for
120432
+ // `self.now().column`; micromark bumps the point column by 1 per `horizontalTab`
120433
+ // code (the trailing `virtualSpace` codes don't move it), so it measures a tab as
120434
+ // 1 column, reviving the tab-under-measurement bug this math exists to avoid.
120435
+ let plainClaimIndentColumns = 0;
120436
+ // True once a non-blank line follows the opener: a deep island below it is nested
120437
+ // (cosmetic indent), not top-of-body indented code.
120438
+ let sawPlainBlockBodyContent = false;
120249
120439
  // Code span tracking
120250
120440
  let codeSpanOpenSize = 0;
120251
120441
  let codeSpanCloseSize = 0;
@@ -120545,7 +120735,7 @@ function createTokenize(mode) {
120545
120735
  }
120546
120736
  // Continuation for multi-line opening tags
120547
120737
  function openTagContinuationStart(code) {
120548
- return effects.check(mdx_component_syntax_nonLazyContinuationStart, openTagContinuationNonLazy, continuationAfter)(code);
120738
+ return effects.check(continuation_checks_nonLazyContinuationStart, openTagContinuationNonLazy, continuationAfter)(code);
120549
120739
  }
120550
120740
  function openTagContinuationNonLazy(code) {
120551
120741
  sawLineEnding = true;
@@ -120590,6 +120780,9 @@ function createTokenize(mode) {
120590
120780
  }
120591
120781
  if (code !== codes.space && code !== codes.horizontalTab) {
120592
120782
  openerLineHasContent = true;
120783
+ // Continuation content marks a later deep island as nested, not indented code.
120784
+ if (!onOpenerLine)
120785
+ sawPlainBlockBodyContent = true;
120593
120786
  }
120594
120787
  if (code === codes.backslash) {
120595
120788
  effects.consume(code);
@@ -120670,7 +120863,7 @@ function createTokenize(mode) {
120670
120863
  return inFencedCode;
120671
120864
  }
120672
120865
  function fencedCodeContinuationStart(code) {
120673
- return effects.check(mdx_component_syntax_nonLazyContinuationStart, fencedCodeContinuationNonLazy, continuationAfter)(code);
120866
+ return effects.check(continuation_checks_nonLazyContinuationStart, fencedCodeContinuationNonLazy, continuationAfter)(code);
120674
120867
  }
120675
120868
  function fencedCodeContinuationNonLazy(code) {
120676
120869
  sawLineEnding = true;
@@ -120878,7 +121071,7 @@ function createTokenize(mode) {
120878
121071
  }
120879
121072
  // ── Body continuation (line endings) ───────────────────────────────────
120880
121073
  function bodyContinuationStart(code) {
120881
- return effects.check(mdx_component_syntax_nonLazyContinuationStart, bodyContinuationNonLazy, continuationAfter)(code);
121074
+ return effects.check(continuation_checks_nonLazyContinuationStart, bodyContinuationNonLazy, continuationAfter)(code);
120882
121075
  }
120883
121076
  function bodyContinuationNonLazy(code) {
120884
121077
  sawLineEnding = true;
@@ -120904,8 +121097,10 @@ function createTokenize(mode) {
120904
121097
  return inBodyBraceString(code);
120905
121098
  return inBodyBraceExpr(code);
120906
121099
  }
120907
- if (isPlainBlockClaim)
121100
+ if (isPlainBlockClaim) {
121101
+ plainClaimIndentColumns = 0;
120908
121102
  return plainClaimLineStart(code);
121103
+ }
120909
121104
  return bodyLineStart(code);
120910
121105
  }
120911
121106
  // Dispatch a non-blank continuation line: fenced code at line start, else body.
@@ -120935,6 +121130,8 @@ function createTokenize(mode) {
120935
121130
  function plainClaimLineStart(code) {
120936
121131
  // Leading whitespace only → treat as a blank line, matching CommonMark.
120937
121132
  if (code === codes.space || code === codes.horizontalTab) {
121133
+ plainClaimIndentColumns +=
121134
+ code === codes.horizontalTab ? TAB_STOP_WIDTH - (plainClaimIndentColumns % TAB_STOP_WIDTH) : 1;
120938
121135
  effects.consume(code);
120939
121136
  return plainClaimLineStart;
120940
121137
  }
@@ -120943,10 +121140,18 @@ function createTokenize(mode) {
120943
121140
  effects.exit('mdxComponentData');
120944
121141
  return bodyContinuationStart(code);
120945
121142
  }
120946
- // Across a blank line the block only continues onto a markup-only line; a
120947
- // paragraph that merely starts with a tag must fall back so its markdown
120948
- // parses and rehype-raw re-nests it into the wrapper.
120949
121143
  if (pendingBlankLine) {
121144
+ // A 4+ col island nested under other tags is cosmetic nesting indent, not code:
121145
+ // keep claiming so promotion dedents + re-parses it as markdown (RM-17560).
121146
+ // Tags whose bodies stay raw are excluded — a claimed island there would never
121147
+ // be re-parsed and would leak as literal text.
121148
+ if (plainClaimIndentColumns >= INDENTED_CODE_MIN_COLUMNS &&
121149
+ sawPlainBlockBodyContent &&
121150
+ !NON_REPARSED_BODY_TAGS.has(tagName)) {
121151
+ return plainClaimContinue(code);
121152
+ }
121153
+ // Otherwise only a markup-only tag line continues; markdown/prose falls back to
121154
+ // CommonMark so it parses and rehype-raw re-nests it into the wrapper.
120950
121155
  if (code !== codes.lessThan)
120951
121156
  return nok(code);
120952
121157
  return effects.check(markupOnlyContinuation, plainClaimContinue, nok)(code);
@@ -120967,66 +121172,6 @@ function createTokenize(mode) {
120967
121172
  }
120968
121173
  };
120969
121174
  }
120970
- function mdx_component_syntax_tokenizeNonLazyContinuationStart(effects, ok, nok) {
120971
- // eslint-disable-next-line @typescript-eslint/no-this-alias
120972
- const self = this;
120973
- return start;
120974
- function start(code) {
120975
- if (markdownLineEnding(code)) {
120976
- effects.enter(types_types.lineEnding);
120977
- effects.consume(code);
120978
- effects.exit(types_types.lineEnding);
120979
- return after;
120980
- }
120981
- return nok(code);
120982
- }
120983
- function after(code) {
120984
- if (self.parser.lazy[self.now().line]) {
120985
- return nok(code);
120986
- }
120987
- return ok(code);
120988
- }
120989
- }
120990
- // A markup-only line opens with a tag (`<x`/`</x`) and ends (ignoring trailing
120991
- // spaces) at a `>`. That distinguishes a structural continuation like
120992
- // `<span>b</span></div>` from a paragraph like `<b>Note:</b> read *this*`.
120993
- function tokenizeMarkupOnlyContinuation(effects, ok, nok) {
120994
- let lastNonSpace = null;
120995
- return start;
120996
- function start(code) {
120997
- // Caller guarantees we are at `<` at the (already de-indented) line start.
120998
- effects.enter(types_types.data);
120999
- effects.consume(code);
121000
- return afterLessThan;
121001
- }
121002
- function afterLessThan(code) {
121003
- if (code === codes.slash) {
121004
- effects.consume(code);
121005
- return afterSlash;
121006
- }
121007
- return afterSlash(code);
121008
- }
121009
- // The `<` (or `</`) must introduce a real tag, not a stray `<` in prose.
121010
- function afterSlash(code) {
121011
- if (asciiAlpha(code)) {
121012
- lastNonSpace = code;
121013
- effects.consume(code);
121014
- return scanToLineEnd;
121015
- }
121016
- effects.exit(types_types.data);
121017
- return nok(code);
121018
- }
121019
- function scanToLineEnd(code) {
121020
- if (code === null || markdownLineEnding(code)) {
121021
- effects.exit(types_types.data);
121022
- return lastNonSpace === codes.greaterThan ? ok(code) : nok(code);
121023
- }
121024
- if (!markdownSpace(code))
121025
- lastNonSpace = code;
121026
- effects.consume(code);
121027
- return scanToLineEnd;
121028
- }
121029
- }
121030
121175
  /**
121031
121176
  * Micromark extension that tokenizes MDX-like components.
121032
121177
  *
@@ -121080,6 +121225,7 @@ function syntax_mdxComponent() {
121080
121225
 
121081
121226
 
121082
121227
 
121228
+
121083
121229
 
121084
121230
  const buildInlineMdProcessor = (safeMode) => {
121085
121231
  // `jsxTable` must be present so a `<Table>`/`<table>` nested in a component
@@ -121151,7 +121297,7 @@ const toMdxJsxTextElement = (name, attributes, children, position) => ({
121151
121297
  // Raw-body tags (pre/script/style/textarea) must stay byte-exact; table
121152
121298
  // structure (`table` + `tableTags`) is owned by `mdxishTables` and figures by
121153
121299
  // `mdxishJsxToMdast`, both of which run later on raw html nodes.
121154
- const NON_PROMOTABLE_PLAIN_TAGS = new Set([...htmlRawNames, ...tableTags, 'table', 'figure', 'figcaption']);
121300
+ const NON_PROMOTABLE_PLAIN_TAGS = new Set([...htmlRawNames, ...tableTags, 'table', ...NON_REPARSED_BODY_TAGS]);
121155
121301
  const NESTED_TABLE_RE = /<table[\s>]/i;
121156
121302
  const isMarkdownPromotableHtmlTag = (tag) => !NON_PROMOTABLE_PLAIN_TAGS.has(tag);
121157
121303
  // Expression nodes count as plain so `<div>{1+1}</div>` keeps its current
@@ -121510,15 +121656,22 @@ function safeDeindent(text) {
121510
121656
  */
121511
121657
  const parseMdChildren = (value, safeMode) => {
121512
121658
  const parsed = getInlineMdProcessor({ safeMode }).parse(terminateHtmlFlowBlocks(safeDeindent(value).trim()));
121659
+ // Promote nested wrappers bottom-up so an outer wrapper sees markdown buried in a
121660
+ // child claimed whole (e.g. `<li>` in `<ol>`) before its containsMarkdownConstruct check (RM-17560).
121661
+ // eslint-disable-next-line @typescript-eslint/no-use-before-define -- mutually recursive; hoisted decl, safe at runtime
121662
+ promoteComponentBlocks(parsed, safeMode, null);
121513
121663
  return parsed.children || [];
121514
121664
  };
121515
- // Parses trailing content into sibling nodes and re-queues the parent so any
121516
- // components among them get processed.
121517
- const parseSibling = (stack, parent, index, sibling, safeMode) => {
121665
+ // Splices trailing content in as sibling nodes. parseMdChildren has already
121666
+ // promoted any components nested among them (bottom-up); the main loop's
121667
+ // index-based walk then reaches these spliced siblings and the original children
121668
+ // they shift down, so no parent re-queue is needed. Each spliced subtree is marked
121669
+ // `promoted` so the walk doesn't redundantly re-descend into it (its html is gone).
121670
+ const parseSibling = (parent, index, sibling, safeMode, promoted) => {
121518
121671
  const siblingNodes = parseMdChildren(sibling, safeMode);
121519
121672
  if (siblingNodes.length > 0) {
121520
121673
  parent.children.splice(index + 1, 0, ...siblingNodes);
121521
- stack.push(parent);
121674
+ siblingNodes.forEach(siblingNode => promoted.add(siblingNode));
121522
121675
  }
121523
121676
  };
121524
121677
  // Ends the position at `consumedLength` so the component doesn't claim trailing
@@ -121584,16 +121737,19 @@ const substituteNodeWithMdxNode = (parent, index, mdxNode) => {
121584
121737
  * The opening tag, content, and closing tag are all captured in one HTML node
121585
121738
  * (guaranteed by the mdx-component tokenizer).
121586
121739
  */
121587
- const mdx_blocks_mdxishMdxComponentBlocks = (opts = {}) => (tree, file) => {
121740
+ function promoteComponentBlocks(tree, safeMode, source) {
121588
121741
  const stack = [tree];
121589
- const safeMode = !!opts.safeMode;
121590
- const source = file?.value ? String(file.value) : null;
121591
121742
  const parseOpts = { preserveExpressionsAsText: safeMode };
121743
+ // Subtrees a nested parseMdChildren already promoted wholesale (spliced siblings):
121744
+ // re-descending them finds no html to promote, so skip them.
121745
+ const promoted = new WeakSet();
121592
121746
  const processChildNode = (parent, index) => {
121593
121747
  const node = parent.children[index];
121594
121748
  if (!node)
121595
121749
  return;
121596
- if ('children' in node && Array.isArray(node.children)) {
121750
+ // Descend into container nodes (lists, blockquotes, …) so their html children
121751
+ // are reached — unless the subtree was already promoted upstream.
121752
+ if ('children' in node && Array.isArray(node.children) && !promoted.has(node)) {
121597
121753
  stack.push(node);
121598
121754
  }
121599
121755
  // Only html nodes can be an unparsed MDX component.
@@ -121648,7 +121804,7 @@ const mdx_blocks_mdxishMdxComponentBlocks = (opts = {}) => (tree, file) => {
121648
121804
  substituteNodeWithMdxNode(parent, index, componentNode);
121649
121805
  const remainingContent = contentAfterTag.trim();
121650
121806
  if (remainingContent) {
121651
- parseSibling(stack, parent, index, remainingContent, safeMode);
121807
+ parseSibling(parent, index, remainingContent, safeMode, promoted);
121652
121808
  }
121653
121809
  return;
121654
121810
  }
@@ -121675,43 +121831,57 @@ const mdx_blocks_mdxishMdxComponentBlocks = (opts = {}) => (tree, file) => {
121675
121831
  return;
121676
121832
  // Lowercase tags are usually inline; unwrap a sole paragraph so their
121677
121833
  // phrasing content isn't spuriously block-wrapped.
121834
+ let unwrappedSoleParagraph = false;
121678
121835
  if (!isPascal && parsedChildren.length === 1 && parsedChildren[0].type === 'paragraph') {
121679
121836
  parsedChildren = parsedChildren[0].children;
121837
+ unwrappedSoleParagraph = true;
121838
+ }
121839
+ // Without trailing content the whole node position is correct. With it, end
121840
+ // precisely at the closing tag — preferring source offsets when available (the
121841
+ // node's value strips blockquote/list prefixes), else the consumed span.
121842
+ let endPosition = node.position;
121843
+ if (contentAfterClose) {
121844
+ endPosition = source
121845
+ ? positionEndingAtClosingTagInSource(node.position, closingTagStr, source)
121846
+ : positionEndingAtConsumed(node.position, value, leadingWhitespace + openingTagEnd + closingTagIndex + closingTagStr.length);
121680
121847
  }
121681
121848
  const componentNode = createComponentNode({
121682
121849
  tag,
121683
121850
  attributes,
121684
121851
  children: parsedChildren,
121685
121852
  startPosition: node.position,
121686
- // With trailing content, end precisely at the closing tag. Prefer source
121687
- // offsets when available (the node's value strips blockquote/list
121688
- // prefixes); otherwise fall back to the whole node position.
121689
- endPosition: contentAfterClose
121690
- ? source
121691
- ? positionEndingAtClosingTagInSource(node.position, closingTagStr, source)
121692
- : positionEndingAtConsumed(node.position, value, leadingWhitespace + openingTagEnd + closingTagIndex + closingTagStr.length)
121693
- : node.position,
121853
+ endPosition,
121694
121854
  });
121695
121855
  substituteNodeWithMdxNode(parent, index, componentNode);
121696
- // Re-queue whichever side may hold further components.
121697
- if (contentAfterClose) {
121698
- parseSibling(stack, parent, index, contentAfterClose, safeMode);
121699
- }
121700
- else if (componentNode.children.length > 0) {
121856
+ // The unwrap reparented the children out of their paragraph, so re-walk them
121857
+ // since the children HTML may contain promotable syntax (e.g. `{…}`-attr tags)
121858
+ if (unwrappedSoleParagraph) {
121701
121859
  stack.push(componentNode);
121702
121860
  }
121861
+ // Trailing content after the close becomes siblings; parseMdChildren has
121862
+ // already promoted any components nested inside both sides, so the promoted
121863
+ // subtree itself needs no re-queue.
121864
+ if (contentAfterClose) {
121865
+ parseSibling(parent, index, contentAfterClose, safeMode, promoted);
121866
+ }
121703
121867
  }
121704
121868
  };
121705
- // Depth-first so nodes keep their source order.
121869
+ // Depth-first so nodes keep their source order. Index-based (not forEach) and
121870
+ // re-reading length each step: parseSibling splices siblings in mid-iteration, and
121871
+ // those — plus the original children they shift down — must all stay eligible.
121706
121872
  while (stack.length) {
121707
121873
  const parent = stack.pop();
121708
121874
  if (parent?.children) {
121709
- parent.children.forEach((_child, index) => {
121875
+ for (let index = 0; index < parent.children.length; index += 1) {
121710
121876
  processChildNode(parent, index);
121711
- });
121877
+ }
121712
121878
  }
121713
121879
  }
121714
121880
  return tree;
121881
+ }
121882
+ const mdx_blocks_mdxishMdxComponentBlocks = (opts = {}) => (tree, file) => {
121883
+ const source = file?.value ? String(file.value) : null;
121884
+ return promoteComponentBlocks(tree, !!opts.safeMode, source);
121715
121885
  };
121716
121886
  /* harmony default export */ const mdx_blocks = (mdx_blocks_mdxishMdxComponentBlocks);
121717
121887
 
@@ -126728,6 +126898,7 @@ function loadComponents() {
126728
126898
 
126729
126899
 
126730
126900
 
126901
+
126731
126902
 
126732
126903
 
126733
126904
  const defaultTransformers = [
@@ -126742,10 +126913,11 @@ const defaultTransformers = [
126742
126913
  * Runs a series of string-level transformations before micromark/remark parsing:
126743
126914
  * 1. Canonicalize closing tags with stray whitespace (e.g., `</ td >` → `</td>`)
126744
126915
  * 2. Normalize malformed table separator syntax (e.g., `|: ---` → `| :---`)
126745
- * 3. Terminate HTML flow blocks so subsequent content isn't swallowed
126746
- * 4. Close invalid "self-closing" HTML tags (e.g., `<i />` → `<i></i>`)
126747
- * 5. Normalize compact ATX headings (e.g., `#Heading``# Heading`)
126748
- * 6. Replace snake_case component names with parser-safe placeholders
126916
+ * 3. Collapse blank lines inside `<svg>`/`<math>` so their children aren't fragmented
126917
+ * 4. Terminate HTML flow blocks so subsequent content isn't swallowed
126918
+ * 5. Close invalid "self-closing" HTML tags (e.g., `<i />` `<i></i>`)
126919
+ * 6. Normalize compact ATX headings (e.g., `#Heading` → `# Heading`)
126920
+ * 7. Replace snake_case component names with parser-safe placeholders
126749
126921
  */
126750
126922
  function preprocessContent(content, opts) {
126751
126923
  const { knownComponents } = opts;
@@ -126753,6 +126925,10 @@ function preprocessContent(content, opts) {
126753
126925
  // classification in `terminateHtmlFlowBlocks` is accurate)
126754
126926
  let result = normalizeClosingTagWhitespace(content);
126755
126927
  result = normalizeTableSeparator(result);
126928
+ // Before terminateHtmlFlowBlocks: a blank line inside an <svg>/<math> island
126929
+ // would otherwise fragment it (children spill out as an indented code block once
126930
+ // a wrapper re-parses its deindented body — #1545).
126931
+ result = collapseForeignContentBlankLines(result);
126756
126932
  result = terminateHtmlFlowBlocks(result);
126757
126933
  result = closeSelfClosingHtmlTags(result);
126758
126934
  result = normalizeCompactHeadings(result);
@@ -127368,6 +127544,8 @@ const mdxishTags_tags = (doc) => {
127368
127544
 
127369
127545
 
127370
127546
 
127547
+
127548
+
127371
127549
  /**
127372
127550
  * Removes Markdown and MDX comments.
127373
127551
  */
@@ -127378,10 +127556,16 @@ async function stripComments(doc, { mdx, mdxish } = {}) {
127378
127556
  // 1. we can rely on remarkMdx to parse MDXish
127379
127557
  // 2. we need to parse JSX comments into mdxTextExpression nodes so that the transformers can pick them up
127380
127558
  // 3. we need to claim <HTMLBlock> before htmlFlow intercepts its inner HTML tags
127559
+ // 4. we need mdxComponent to parse custom components as one node, and prevent the tag from getting escaped
127381
127560
  if (mdxish) {
127382
127561
  processor
127383
- .data('micromarkExtensions', [htmlBlockComponent(), jsxTable(), mdxExpression({ allowEmpty: true })])
127384
- .data('fromMarkdownExtensions', [htmlBlockComponentFromMarkdown(), jsxTableFromMarkdown(), mdxExpressionFromMarkdown()])
127562
+ .data('micromarkExtensions', [htmlBlockComponent(), jsxTable(), mdxComponent(), mdxExpression({ allowEmpty: true })])
127563
+ .data('fromMarkdownExtensions', [
127564
+ htmlBlockComponentFromMarkdown(),
127565
+ jsxTableFromMarkdown(),
127566
+ mdxComponentFromMarkdown(),
127567
+ mdxExpressionFromMarkdown(),
127568
+ ])
127385
127569
  .data('toMarkdownExtensions', [mdxExpressionToMarkdown()]);
127386
127570
  }
127387
127571
  processor