@readme/markdown 14.4.0 → 14.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/main.js CHANGED
@@ -11594,6 +11594,7 @@ __webpack_require__.r(__webpack_exports__);
11594
11594
  __webpack_require__.d(__webpack_exports__, {
11595
11595
  Components: () => (/* reexport */ components_namespaceObject),
11596
11596
  FLOW_TYPES: () => (/* reexport */ FLOW_TYPES),
11597
+ INLINE_ONLY_PARENT_TYPES: () => (/* reexport */ INLINE_ONLY_PARENT_TYPES),
11597
11598
  NodeTypes: () => (/* reexport */ NodeTypes),
11598
11599
  Owlmoji: () => (/* reexport */ Owlmoji),
11599
11600
  compile: () => (/* reexport */ lib_compile),
@@ -12254,13 +12255,17 @@ const Image = (Props) => {
12254
12255
  external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_.createElement("i", { "aria-hidden": "true", className: "fa-solid fa-xmark" }))))) : null;
12255
12256
  if (framed) {
12256
12257
  const frameClass = `img-frame img-frame-${align || 'center'}`;
12258
+ // Left/right frames shrink to fit, so percentage widths can't resolve
12259
+ // against the parent, hoist onto the wrapper. Center frames are full-width.
12260
+ const isClamped = align === 'left' || align === 'right';
12261
+ const frameStyle = isClamped && typeof width === 'string' && width.endsWith('%') ? { width } : undefined;
12257
12262
  if (children || caption) {
12258
- return (external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_.createElement("figure", { className: frameClass },
12263
+ return (external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_.createElement("figure", { className: frameClass, style: frameStyle },
12259
12264
  closedLightbox(alt || 'Expand image', imgElement),
12260
12265
  lightboxOverlay,
12261
12266
  external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_.createElement("figcaption", null, children || caption)));
12262
12267
  }
12263
- return (external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_.createElement("div", { className: frameClass },
12268
+ return (external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_.createElement("div", { className: frameClass, style: frameStyle },
12264
12269
  closedLightbox(alt || 'Expand image', imgElement),
12265
12270
  lightboxOverlay));
12266
12271
  }
@@ -13061,6 +13066,34 @@ const FLOW_TYPES = new Set([
13061
13066
  'table',
13062
13067
  'thematicBreak',
13063
13068
  ]);
13069
+ /**
13070
+ * MDAST parent types whose children are restricted to inline content. A block-level
13071
+ * node placed inside any of these violates the parent's content model and breaks
13072
+ * downstream consumers
13073
+ *
13074
+ * Derived from the mdast spec. Seven of these are direct "phrasing content" parents:
13075
+ * - paragraph, heading, link, linkReference, emphasis, strong
13076
+ * - tableCell (phrasing minus Break)
13077
+ *
13078
+ * @link {https://github.com/syntax-tree/mdast}
13079
+ *
13080
+ * `delete` is GFM and has *transparent* content, see:
13081
+ * @link {https://github.com/syntax-tree/mdast-util-gfm-strikethrough}
13082
+ *
13083
+ * its children must also be phrasing, so it belongs here too.
13084
+ *
13085
+ * NOTE: exported so downstream consumers can reuse and not drift away
13086
+ */
13087
+ const INLINE_ONLY_PARENT_TYPES = new Set([
13088
+ 'paragraph',
13089
+ 'heading',
13090
+ 'tableCell',
13091
+ 'link',
13092
+ 'linkReference',
13093
+ 'emphasis',
13094
+ 'strong',
13095
+ 'delete',
13096
+ ]);
13064
13097
  // HELPER CONSTANTS FOR MDXISH RENDERING
13065
13098
  /**
13066
13099
  * Inline-only custom components that appear as phrasing content within
@@ -75543,7 +75576,15 @@ const walkTags = (html, handlers) => {
75543
75576
  const tagEnd = (parser) => (parser.endIndex ?? parser.startIndex) + 1;
75544
75577
  const parser = new Parser_Parser({
75545
75578
  onopentag(name) {
75546
- handlers.onOpen?.({ name, start: parser.startIndex, end: tagEnd(parser) });
75579
+ const start = parser.startIndex;
75580
+ const end = tagEnd(parser);
75581
+ handlers.onOpen?.({
75582
+ name,
75583
+ start,
75584
+ end,
75585
+ isSelfClosing: html[end - 2] === '/',
75586
+ isStrayCloser: html[start + 1] === '/',
75587
+ });
75547
75588
  },
75548
75589
  onclosetag(name, implicit) {
75549
75590
  handlers.onClose?.({ name, start: parser.startIndex, end: tagEnd(parser), implicit });
@@ -75786,6 +75827,57 @@ const remapPositionsToOriginal = (tree, originalSource, inserts) => {
75786
75827
  });
75787
75828
  };
75788
75829
 
75830
+ ;// ./processor/transform/mdxish/tables/repair-expression-escapes.ts
75831
+
75832
+ /**
75833
+ * mdxjs hands the contents of every `{…}` to acorn as a JavaScript expression.
75834
+ * A bare backslash is only legal inside a string/template literal in JS, so a
75835
+ * markdown-style escape such as `{customer\_id}` — common when authors escape an
75836
+ * underscore out of habit — makes acorn throw "Could not parse expression with
75837
+ * acorn", which drops parsing for the whole surrounding `<Table>`.
75838
+ *
75839
+ * This pass deletes backslashes that sit in JS *code* position inside a `{…}`
75840
+ * expression. Backslashes within a '…', "…" or `…` literal are valid escapes
75841
+ * and are left untouched. Scoped to the malformed-retry path; the happy path
75842
+ * never runs it.
75843
+ */
75844
+ const repairExpressionEscapes = (html) => {
75845
+ const inserts = [];
75846
+ let braceDepth = 0;
75847
+ // Active string/template delimiter while scanning inside an expression, or null.
75848
+ let stringChar = null;
75849
+ for (let i = 0; i < html.length; i += 1) {
75850
+ const ch = html[i];
75851
+ if (stringChar) {
75852
+ // Inside a JS string/template literal a backslash escapes the next char
75853
+ // and is valid, so skip the pair untouched (this also prevents an escaped
75854
+ // quote from prematurely closing the literal).
75855
+ if (ch === '\\') {
75856
+ i += 1;
75857
+ }
75858
+ else if (ch === stringChar) {
75859
+ stringChar = null;
75860
+ }
75861
+ }
75862
+ else if (braceDepth > 0 && (ch === '"' || ch === "'" || ch === '`')) {
75863
+ stringChar = ch;
75864
+ }
75865
+ else if (ch === '{') {
75866
+ braceDepth += 1;
75867
+ }
75868
+ else if (ch === '}') {
75869
+ if (braceDepth > 0)
75870
+ braceDepth -= 1;
75871
+ }
75872
+ else if (braceDepth > 0 && ch === '\\') {
75873
+ // A backslash in code position inside an expression is always a syntax
75874
+ // error; delete it so acorn can parse the remaining expression.
75875
+ inserts.push({ offset: i, text: '', consumes: 1 });
75876
+ }
75877
+ }
75878
+ return applyInserts(html, inserts);
75879
+ };
75880
+
75789
75881
  ;// ./node_modules/html-tags/html-tags.json
75790
75882
  const html_tags_namespaceObject = /*#__PURE__*/JSON.parse('["a","abbr","address","area","article","aside","audio","b","base","bdi","bdo","blockquote","body","br","button","canvas","caption","cite","code","col","colgroup","data","datalist","dd","del","details","dfn","dialog","div","dl","dt","em","embed","fieldset","figcaption","figure","footer","form","h1","h2","h3","h4","h5","h6","head","header","hgroup","hr","html","i","iframe","img","input","ins","kbd","label","legend","li","link","main","map","mark","math","menu","meta","meter","nav","noscript","object","ol","optgroup","option","output","p","picture","pre","progress","q","rp","rt","ruby","s","samp","script","search","section","select","selectedcontent","slot","small","source","span","strong","style","sub","summary","sup","svg","table","tbody","td","template","textarea","tfoot","th","thead","time","title","tr","track","u","ul","var","video","wbr"]');
75791
75883
  // EXTERNAL MODULE: ./node_modules/react-html-attributes/dist/index.js
@@ -75912,13 +76004,6 @@ const isStandardHtmlTag = (name) => STANDARD_HTML_TAGS.has(name.toLowerCase());
75912
76004
  // (htmlparser2) handles attribute parsing; this scan only needs to locate
75913
76005
  // orphan closers, which can't appear inside an attribute value anyway.
75914
76006
  const HTML_TAG_TOKEN_RE = /<(\/)?([a-zA-Z][a-zA-Z0-9-]*)\b[^>]*>/g;
75915
- /**
75916
- * htmlparser2 silently drops closing tags that have no matching opener
75917
- * (e.g. the trailing `</li>` in `<ul><li>x</ul></li>`), leaving them in the
75918
- * source makes mdxjs choke on the dangling closer. Scan the masked
75919
- * source for `</name>` tokens that don't pair with any prior unmatched
75920
- * `<name>` and return their spans so the caller can drop them.
75921
- */
75922
76007
  const findOrphanClosers = (html) => {
75923
76008
  const masked = maskNonTagRegions(html);
75924
76009
  const stack = [];
@@ -75927,18 +76012,30 @@ const findOrphanClosers = (html) => {
75927
76012
  let match;
75928
76013
  while ((match = HTML_TAG_TOKEN_RE.exec(masked)) !== null) {
75929
76014
  const name = match[2].toLowerCase();
75930
- // Non-HTML names and void elements are handled by the main walker.
75931
76015
  // eslint-disable-next-line no-continue
75932
- if (!isStandardHtmlTag(name) || HTML_VOID_ELEMENTS.has(name))
76016
+ if (!isStandardHtmlTag(name))
76017
+ continue;
76018
+ // Special case for <br>: htmlparser2 will normalize orphan </br> to <br>
76019
+ // so we don't need to handle it here.
76020
+ // eslint-disable-next-line no-continue
76021
+ if (name === 'br' && match[1] === '/')
75933
76022
  continue;
76023
+ const isVoid = HTML_VOID_ELEMENTS.has(name);
75934
76024
  if (match[1] === '/') {
76025
+ // Other void closers (`</hr>`, `</img>`, ...) have no HTML5 rewrite rule
76026
+ // and must be stripped before the strict mdxjs parse runs.
76027
+ if (isVoid) {
76028
+ orphans.push({ offset: match.index, length: match[0].length });
76029
+ // eslint-disable-next-line no-continue
76030
+ continue;
76031
+ }
75935
76032
  const idx = stack.lastIndexOf(name);
75936
76033
  if (idx === -1)
75937
76034
  orphans.push({ offset: match.index, length: match[0].length });
75938
76035
  else
75939
76036
  stack.length = idx;
75940
76037
  }
75941
- else if (!match[0].endsWith('/>')) {
76038
+ else if (!isVoid && !match[0].endsWith('/>')) {
75942
76039
  stack.push(name);
75943
76040
  }
75944
76041
  }
@@ -75971,7 +76068,7 @@ const repairUnclosedTags = (html) => {
75971
76068
  const inserts = [];
75972
76069
  const openTags = [];
75973
76070
  walkTags(html, {
75974
- onOpen({ name, start, end }) {
76071
+ onOpen({ name, start, end, isSelfClosing, isStrayCloser }) {
75975
76072
  // Escape non-HTML names (custom components, typos, `<arbitrary-tag>`)
75976
76073
  // so MDX treats them as literal text instead of expecting a closer
75977
76074
  if (!isStandardHtmlTag(name)) {
@@ -75979,12 +76076,19 @@ const repairUnclosedTags = (html) => {
75979
76076
  return;
75980
76077
  }
75981
76078
  if (HTML_VOID_ELEMENTS.has(name.toLowerCase())) {
75982
- // MDX requires void elements to be self-closing (`<br/>`, not `<br>`).
75983
- // If the source open tag doesn't end with `/`, inject one before the
75984
- // `>` so it parses. `end` is one past `>`, so `end - 2` is the char
75985
- // immediately before `>`.
75986
- if (html[end - 2] !== '/')
76079
+ // MDX requires void elements to be self-closing (`<br/>`, not `<br>`)
76080
+ // so we need to rewrite them to self closing tags.
76081
+ if (isStrayCloser) {
76082
+ // htmlparser2 may normalize some stray closers like `</br>` into opener
76083
+ // events <br> per the HTML5 spec. In this case remove the closing /
76084
+ // and fully rewrite the tag to self closing.
76085
+ inserts.push({ offset: start, text: `<${name}/>`, consumes: end - start });
76086
+ return;
76087
+ }
76088
+ else if (!isSelfClosing) {
76089
+ // Slots in the / right before the closing >
75987
76090
  inserts.push({ offset: end - 1, text: '/' });
76091
+ }
75988
76092
  return;
75989
76093
  }
75990
76094
  openTags.push({ name, start, end });
@@ -76024,6 +76128,7 @@ const repairUnclosedTags = (html) => {
76024
76128
 
76025
76129
 
76026
76130
 
76131
+
76027
76132
 
76028
76133
  const isTableCell = (node) => isMDXElement(node) && ['th', 'td'].includes(node.name);
76029
76134
  const tableTypes = {
@@ -76035,6 +76140,9 @@ const tableTypes = {
76035
76140
  // register them manually so we control ordering against our other tokenizers.
76036
76141
  // The fallback omits these so blank-line-separated markdown inside cells still
76037
76142
  // parses when mdxjs throws on malformed JSX.
76143
+ //
76144
+ // mdx parsing is used because it heavily simplifies the parsing of the table structure;
76145
+ // it can identify the rows and cells. The heavy lifting is done by it
76038
76146
  const buildTableNodeProcessor = (withMdx) => unified()
76039
76147
  .data('micromarkExtensions', [...(withMdx ? [mdxjs()] : []), syntax_gemoji(), legacyVariable()])
76040
76148
  .data('fromMarkdownExtensions', [
@@ -76269,22 +76377,29 @@ const mdxishTables = () => tree => {
76269
76377
  // Main logic to transform table node to its parts
76270
76378
  // Because the processor uses remarkMdx, it is stricter in what it accepts
76271
76379
  // and only accepts valid MDX syntax. in the table node.
76272
- // To get around that, we have some fallback logics after trying to repair the table content
76380
+ // To get around that, we have some fallback logics after trying to repair the table content.
76273
76381
  let parsed = parseTableNode(tableNodeProcessor, node);
76274
76382
  if (!parsed) {
76275
- // First common error is unclosed HTML tags
76276
- const repaired = repairUnclosedTags(node.value);
76277
- if (repaired.value !== node.value) {
76278
- parsed = parseTableNode(tableNodeProcessor, { ...node, value: repaired.value }, { inserts: repaired.inserts, originalSource: node.value });
76279
- }
76280
- if (!parsed) {
76281
- // Second common error is having a line with text and an opening tag
76282
- // E.g. text <div> \n <div> text
76283
- const normalized = normalizeTagSpacing(node.value);
76284
- if (normalized.value !== node.value) {
76285
- parsed = parseTableNode(tableNodeProcessor, { ...node, value: normalized.value }, { inserts: normalized.inserts, originalSource: node.value });
76383
+ // Try a sequence of targeted repairs and re-parse
76384
+ // after each, stopping at the first that yields a parseable tree:
76385
+ // - repairUnclosedTags: unclosed/orphan HTML tags
76386
+ // - normalizeTagSpacing: a line mixing text and an opening tag
76387
+ // (e.g. `text <div> \n <div> text`)
76388
+ // - repairExpressionEscapes: backslash escapes inside a `{…}` expression
76389
+ // These repairs are created after seeing real customer content that has failed to parse
76390
+ const repairs = [
76391
+ repairUnclosedTags,
76392
+ normalizeTagSpacing,
76393
+ repairExpressionEscapes,
76394
+ ];
76395
+ // Stops at the first repair that yields a parseable tree
76396
+ repairs.some(repair => {
76397
+ const { value, inserts } = repair(node.value);
76398
+ if (value !== node.value) {
76399
+ parsed = parseTableNode(tableNodeProcessor, { ...node, value }, { inserts, originalSource: node.value });
76286
76400
  }
76287
- }
76401
+ return Boolean(parsed);
76402
+ });
76288
76403
  }
76289
76404
  if (parsed) {
76290
76405
  // If the table is parsed successfully, we can now process it further
@@ -76305,6 +76420,8 @@ const mdxishTables = () => tree => {
76305
76420
  return;
76306
76421
  parent.children.splice(index, 1, ...fallback.children);
76307
76422
  }
76423
+ // Otherwise, there's no point in trying to parse the table content further
76424
+ // More repairs are needed in that case
76308
76425
  });
76309
76426
  return tree;
76310
76427
  };
@@ -103402,6 +103519,7 @@ const mdxishHtmlBlocks = () => tree => {
103402
103519
 
103403
103520
 
103404
103521
 
103522
+
103405
103523
  function toImageAlign(value) {
103406
103524
  if (value === 'left' || value === 'center' || value === 'right') {
103407
103525
  return value;
@@ -103954,16 +104072,12 @@ const mdxishJsxToMdast = () => tree => {
103954
104072
  parent.children[index] = newNode;
103955
104073
  }
103956
104074
  });
103957
- // Transform magic block images (type: 'image') to image-block
103958
- // Images inside paragraphs are standard markdown handled by imageTransformer, normalized below
104075
+ // Promote magic-block images (type: 'image') to image-block, except inside inline-only
104076
+ // parents where the image must stay inline (authors use `<Image caption="…" />` instead).
103959
104077
  visit(tree, 'image', (node, index, parent) => {
103960
104078
  if (!parent || index === undefined)
103961
104079
  return SKIP;
103962
- if (parent.type === 'paragraph')
103963
- return SKIP;
103964
- // `![](url)` in any tableCell stays inline. Authors who want a captioned figure use
103965
- // `<Image caption="…" />` JSX, which becomes `image-block` via `COMPONENT_MAP` above.
103966
- if (parent.type === 'tableCell')
104080
+ if (INLINE_ONLY_PARENT_TYPES.has(parent.type))
103967
104081
  return SKIP;
103968
104082
  const newNode = transformMagicBlockImage(node);
103969
104083
  parent.children[index] = newNode;
package/dist/main.node.js CHANGED
@@ -19278,6 +19278,7 @@ __webpack_require__.r(__webpack_exports__);
19278
19278
  __webpack_require__.d(__webpack_exports__, {
19279
19279
  Components: () => (/* reexport */ components_namespaceObject),
19280
19280
  FLOW_TYPES: () => (/* reexport */ FLOW_TYPES),
19281
+ INLINE_ONLY_PARENT_TYPES: () => (/* reexport */ INLINE_ONLY_PARENT_TYPES),
19281
19282
  NodeTypes: () => (/* reexport */ NodeTypes),
19282
19283
  Owlmoji: () => (/* reexport */ Owlmoji),
19283
19284
  compile: () => (/* reexport */ lib_compile),
@@ -24880,13 +24881,17 @@ const Image = (Props) => {
24880
24881
  external_react_.createElement("i", { "aria-hidden": "true", className: "fa-solid fa-xmark" }))))) : null;
24881
24882
  if (framed) {
24882
24883
  const frameClass = `img-frame img-frame-${align || 'center'}`;
24884
+ // Left/right frames shrink to fit, so percentage widths can't resolve
24885
+ // against the parent, hoist onto the wrapper. Center frames are full-width.
24886
+ const isClamped = align === 'left' || align === 'right';
24887
+ const frameStyle = isClamped && typeof width === 'string' && width.endsWith('%') ? { width } : undefined;
24883
24888
  if (children || caption) {
24884
- return (external_react_.createElement("figure", { className: frameClass },
24889
+ return (external_react_.createElement("figure", { className: frameClass, style: frameStyle },
24885
24890
  closedLightbox(alt || 'Expand image', imgElement),
24886
24891
  lightboxOverlay,
24887
24892
  external_react_.createElement("figcaption", null, children || caption)));
24888
24893
  }
24889
- return (external_react_.createElement("div", { className: frameClass },
24894
+ return (external_react_.createElement("div", { className: frameClass, style: frameStyle },
24890
24895
  closedLightbox(alt || 'Expand image', imgElement),
24891
24896
  lightboxOverlay));
24892
24897
  }
@@ -25634,6 +25639,34 @@ const FLOW_TYPES = new Set([
25634
25639
  'table',
25635
25640
  'thematicBreak',
25636
25641
  ]);
25642
+ /**
25643
+ * MDAST parent types whose children are restricted to inline content. A block-level
25644
+ * node placed inside any of these violates the parent's content model and breaks
25645
+ * downstream consumers
25646
+ *
25647
+ * Derived from the mdast spec. Seven of these are direct "phrasing content" parents:
25648
+ * - paragraph, heading, link, linkReference, emphasis, strong
25649
+ * - tableCell (phrasing minus Break)
25650
+ *
25651
+ * @link {https://github.com/syntax-tree/mdast}
25652
+ *
25653
+ * `delete` is GFM and has *transparent* content, see:
25654
+ * @link {https://github.com/syntax-tree/mdast-util-gfm-strikethrough}
25655
+ *
25656
+ * its children must also be phrasing, so it belongs here too.
25657
+ *
25658
+ * NOTE: exported so downstream consumers can reuse and not drift away
25659
+ */
25660
+ const INLINE_ONLY_PARENT_TYPES = new Set([
25661
+ 'paragraph',
25662
+ 'heading',
25663
+ 'tableCell',
25664
+ 'link',
25665
+ 'linkReference',
25666
+ 'emphasis',
25667
+ 'strong',
25668
+ 'delete',
25669
+ ]);
25637
25670
  // HELPER CONSTANTS FOR MDXISH RENDERING
25638
25671
  /**
25639
25672
  * Inline-only custom components that appear as phrasing content within
@@ -95767,7 +95800,15 @@ const walkTags = (html, handlers) => {
95767
95800
  const tagEnd = (parser) => (parser.endIndex ?? parser.startIndex) + 1;
95768
95801
  const parser = new Parser_Parser({
95769
95802
  onopentag(name) {
95770
- handlers.onOpen?.({ name, start: parser.startIndex, end: tagEnd(parser) });
95803
+ const start = parser.startIndex;
95804
+ const end = tagEnd(parser);
95805
+ handlers.onOpen?.({
95806
+ name,
95807
+ start,
95808
+ end,
95809
+ isSelfClosing: html[end - 2] === '/',
95810
+ isStrayCloser: html[start + 1] === '/',
95811
+ });
95771
95812
  },
95772
95813
  onclosetag(name, implicit) {
95773
95814
  handlers.onClose?.({ name, start: parser.startIndex, end: tagEnd(parser), implicit });
@@ -96010,6 +96051,57 @@ const remapPositionsToOriginal = (tree, originalSource, inserts) => {
96010
96051
  });
96011
96052
  };
96012
96053
 
96054
+ ;// ./processor/transform/mdxish/tables/repair-expression-escapes.ts
96055
+
96056
+ /**
96057
+ * mdxjs hands the contents of every `{…}` to acorn as a JavaScript expression.
96058
+ * A bare backslash is only legal inside a string/template literal in JS, so a
96059
+ * markdown-style escape such as `{customer\_id}` — common when authors escape an
96060
+ * underscore out of habit — makes acorn throw "Could not parse expression with
96061
+ * acorn", which drops parsing for the whole surrounding `<Table>`.
96062
+ *
96063
+ * This pass deletes backslashes that sit in JS *code* position inside a `{…}`
96064
+ * expression. Backslashes within a '…', "…" or `…` literal are valid escapes
96065
+ * and are left untouched. Scoped to the malformed-retry path; the happy path
96066
+ * never runs it.
96067
+ */
96068
+ const repairExpressionEscapes = (html) => {
96069
+ const inserts = [];
96070
+ let braceDepth = 0;
96071
+ // Active string/template delimiter while scanning inside an expression, or null.
96072
+ let stringChar = null;
96073
+ for (let i = 0; i < html.length; i += 1) {
96074
+ const ch = html[i];
96075
+ if (stringChar) {
96076
+ // Inside a JS string/template literal a backslash escapes the next char
96077
+ // and is valid, so skip the pair untouched (this also prevents an escaped
96078
+ // quote from prematurely closing the literal).
96079
+ if (ch === '\\') {
96080
+ i += 1;
96081
+ }
96082
+ else if (ch === stringChar) {
96083
+ stringChar = null;
96084
+ }
96085
+ }
96086
+ else if (braceDepth > 0 && (ch === '"' || ch === "'" || ch === '`')) {
96087
+ stringChar = ch;
96088
+ }
96089
+ else if (ch === '{') {
96090
+ braceDepth += 1;
96091
+ }
96092
+ else if (ch === '}') {
96093
+ if (braceDepth > 0)
96094
+ braceDepth -= 1;
96095
+ }
96096
+ else if (braceDepth > 0 && ch === '\\') {
96097
+ // A backslash in code position inside an expression is always a syntax
96098
+ // error; delete it so acorn can parse the remaining expression.
96099
+ inserts.push({ offset: i, text: '', consumes: 1 });
96100
+ }
96101
+ }
96102
+ return applyInserts(html, inserts);
96103
+ };
96104
+
96013
96105
  ;// ./node_modules/html-tags/html-tags.json
96014
96106
  const html_tags_namespaceObject = /*#__PURE__*/JSON.parse('["a","abbr","address","area","article","aside","audio","b","base","bdi","bdo","blockquote","body","br","button","canvas","caption","cite","code","col","colgroup","data","datalist","dd","del","details","dfn","dialog","div","dl","dt","em","embed","fieldset","figcaption","figure","footer","form","h1","h2","h3","h4","h5","h6","head","header","hgroup","hr","html","i","iframe","img","input","ins","kbd","label","legend","li","link","main","map","mark","math","menu","meta","meter","nav","noscript","object","ol","optgroup","option","output","p","picture","pre","progress","q","rp","rt","ruby","s","samp","script","search","section","select","selectedcontent","slot","small","source","span","strong","style","sub","summary","sup","svg","table","tbody","td","template","textarea","tfoot","th","thead","time","title","tr","track","u","ul","var","video","wbr"]');
96015
96107
  // EXTERNAL MODULE: ./node_modules/react-html-attributes/dist/index.js
@@ -96136,13 +96228,6 @@ const isStandardHtmlTag = (name) => STANDARD_HTML_TAGS.has(name.toLowerCase());
96136
96228
  // (htmlparser2) handles attribute parsing; this scan only needs to locate
96137
96229
  // orphan closers, which can't appear inside an attribute value anyway.
96138
96230
  const HTML_TAG_TOKEN_RE = /<(\/)?([a-zA-Z][a-zA-Z0-9-]*)\b[^>]*>/g;
96139
- /**
96140
- * htmlparser2 silently drops closing tags that have no matching opener
96141
- * (e.g. the trailing `</li>` in `<ul><li>x</ul></li>`), leaving them in the
96142
- * source makes mdxjs choke on the dangling closer. Scan the masked
96143
- * source for `</name>` tokens that don't pair with any prior unmatched
96144
- * `<name>` and return their spans so the caller can drop them.
96145
- */
96146
96231
  const findOrphanClosers = (html) => {
96147
96232
  const masked = maskNonTagRegions(html);
96148
96233
  const stack = [];
@@ -96151,18 +96236,30 @@ const findOrphanClosers = (html) => {
96151
96236
  let match;
96152
96237
  while ((match = HTML_TAG_TOKEN_RE.exec(masked)) !== null) {
96153
96238
  const name = match[2].toLowerCase();
96154
- // Non-HTML names and void elements are handled by the main walker.
96155
96239
  // eslint-disable-next-line no-continue
96156
- if (!isStandardHtmlTag(name) || HTML_VOID_ELEMENTS.has(name))
96240
+ if (!isStandardHtmlTag(name))
96241
+ continue;
96242
+ // Special case for <br>: htmlparser2 will normalize orphan </br> to <br>
96243
+ // so we don't need to handle it here.
96244
+ // eslint-disable-next-line no-continue
96245
+ if (name === 'br' && match[1] === '/')
96157
96246
  continue;
96247
+ const isVoid = HTML_VOID_ELEMENTS.has(name);
96158
96248
  if (match[1] === '/') {
96249
+ // Other void closers (`</hr>`, `</img>`, ...) have no HTML5 rewrite rule
96250
+ // and must be stripped before the strict mdxjs parse runs.
96251
+ if (isVoid) {
96252
+ orphans.push({ offset: match.index, length: match[0].length });
96253
+ // eslint-disable-next-line no-continue
96254
+ continue;
96255
+ }
96159
96256
  const idx = stack.lastIndexOf(name);
96160
96257
  if (idx === -1)
96161
96258
  orphans.push({ offset: match.index, length: match[0].length });
96162
96259
  else
96163
96260
  stack.length = idx;
96164
96261
  }
96165
- else if (!match[0].endsWith('/>')) {
96262
+ else if (!isVoid && !match[0].endsWith('/>')) {
96166
96263
  stack.push(name);
96167
96264
  }
96168
96265
  }
@@ -96195,7 +96292,7 @@ const repairUnclosedTags = (html) => {
96195
96292
  const inserts = [];
96196
96293
  const openTags = [];
96197
96294
  walkTags(html, {
96198
- onOpen({ name, start, end }) {
96295
+ onOpen({ name, start, end, isSelfClosing, isStrayCloser }) {
96199
96296
  // Escape non-HTML names (custom components, typos, `<arbitrary-tag>`)
96200
96297
  // so MDX treats them as literal text instead of expecting a closer
96201
96298
  if (!isStandardHtmlTag(name)) {
@@ -96203,12 +96300,19 @@ const repairUnclosedTags = (html) => {
96203
96300
  return;
96204
96301
  }
96205
96302
  if (HTML_VOID_ELEMENTS.has(name.toLowerCase())) {
96206
- // MDX requires void elements to be self-closing (`<br/>`, not `<br>`).
96207
- // If the source open tag doesn't end with `/`, inject one before the
96208
- // `>` so it parses. `end` is one past `>`, so `end - 2` is the char
96209
- // immediately before `>`.
96210
- if (html[end - 2] !== '/')
96303
+ // MDX requires void elements to be self-closing (`<br/>`, not `<br>`)
96304
+ // so we need to rewrite them to self closing tags.
96305
+ if (isStrayCloser) {
96306
+ // htmlparser2 may normalize some stray closers like `</br>` into opener
96307
+ // events <br> per the HTML5 spec. In this case remove the closing /
96308
+ // and fully rewrite the tag to self closing.
96309
+ inserts.push({ offset: start, text: `<${name}/>`, consumes: end - start });
96310
+ return;
96311
+ }
96312
+ else if (!isSelfClosing) {
96313
+ // Slots in the / right before the closing >
96211
96314
  inserts.push({ offset: end - 1, text: '/' });
96315
+ }
96212
96316
  return;
96213
96317
  }
96214
96318
  openTags.push({ name, start, end });
@@ -96248,6 +96352,7 @@ const repairUnclosedTags = (html) => {
96248
96352
 
96249
96353
 
96250
96354
 
96355
+
96251
96356
 
96252
96357
  const isTableCell = (node) => isMDXElement(node) && ['th', 'td'].includes(node.name);
96253
96358
  const tableTypes = {
@@ -96259,6 +96364,9 @@ const tableTypes = {
96259
96364
  // register them manually so we control ordering against our other tokenizers.
96260
96365
  // The fallback omits these so blank-line-separated markdown inside cells still
96261
96366
  // parses when mdxjs throws on malformed JSX.
96367
+ //
96368
+ // mdx parsing is used because it heavily simplifies the parsing of the table structure;
96369
+ // it can identify the rows and cells. The heavy lifting is done by it
96262
96370
  const buildTableNodeProcessor = (withMdx) => unified()
96263
96371
  .data('micromarkExtensions', [...(withMdx ? [mdxjs()] : []), syntax_gemoji(), legacyVariable()])
96264
96372
  .data('fromMarkdownExtensions', [
@@ -96493,22 +96601,29 @@ const mdxishTables = () => tree => {
96493
96601
  // Main logic to transform table node to its parts
96494
96602
  // Because the processor uses remarkMdx, it is stricter in what it accepts
96495
96603
  // and only accepts valid MDX syntax. in the table node.
96496
- // To get around that, we have some fallback logics after trying to repair the table content
96604
+ // To get around that, we have some fallback logics after trying to repair the table content.
96497
96605
  let parsed = parseTableNode(tableNodeProcessor, node);
96498
96606
  if (!parsed) {
96499
- // First common error is unclosed HTML tags
96500
- const repaired = repairUnclosedTags(node.value);
96501
- if (repaired.value !== node.value) {
96502
- parsed = parseTableNode(tableNodeProcessor, { ...node, value: repaired.value }, { inserts: repaired.inserts, originalSource: node.value });
96503
- }
96504
- if (!parsed) {
96505
- // Second common error is having a line with text and an opening tag
96506
- // E.g. text <div> \n <div> text
96507
- const normalized = normalizeTagSpacing(node.value);
96508
- if (normalized.value !== node.value) {
96509
- parsed = parseTableNode(tableNodeProcessor, { ...node, value: normalized.value }, { inserts: normalized.inserts, originalSource: node.value });
96607
+ // Try a sequence of targeted repairs and re-parse
96608
+ // after each, stopping at the first that yields a parseable tree:
96609
+ // - repairUnclosedTags: unclosed/orphan HTML tags
96610
+ // - normalizeTagSpacing: a line mixing text and an opening tag
96611
+ // (e.g. `text <div> \n <div> text`)
96612
+ // - repairExpressionEscapes: backslash escapes inside a `{…}` expression
96613
+ // These repairs are created after seeing real customer content that has failed to parse
96614
+ const repairs = [
96615
+ repairUnclosedTags,
96616
+ normalizeTagSpacing,
96617
+ repairExpressionEscapes,
96618
+ ];
96619
+ // Stops at the first repair that yields a parseable tree
96620
+ repairs.some(repair => {
96621
+ const { value, inserts } = repair(node.value);
96622
+ if (value !== node.value) {
96623
+ parsed = parseTableNode(tableNodeProcessor, { ...node, value }, { inserts, originalSource: node.value });
96510
96624
  }
96511
- }
96625
+ return Boolean(parsed);
96626
+ });
96512
96627
  }
96513
96628
  if (parsed) {
96514
96629
  // If the table is parsed successfully, we can now process it further
@@ -96529,6 +96644,8 @@ const mdxishTables = () => tree => {
96529
96644
  return;
96530
96645
  parent.children.splice(index, 1, ...fallback.children);
96531
96646
  }
96647
+ // Otherwise, there's no point in trying to parse the table content further
96648
+ // More repairs are needed in that case
96532
96649
  });
96533
96650
  return tree;
96534
96651
  };
@@ -123626,6 +123743,7 @@ const mdxishHtmlBlocks = () => tree => {
123626
123743
 
123627
123744
 
123628
123745
 
123746
+
123629
123747
  function toImageAlign(value) {
123630
123748
  if (value === 'left' || value === 'center' || value === 'right') {
123631
123749
  return value;
@@ -124178,16 +124296,12 @@ const mdxishJsxToMdast = () => tree => {
124178
124296
  parent.children[index] = newNode;
124179
124297
  }
124180
124298
  });
124181
- // Transform magic block images (type: 'image') to image-block
124182
- // Images inside paragraphs are standard markdown handled by imageTransformer, normalized below
124299
+ // Promote magic-block images (type: 'image') to image-block, except inside inline-only
124300
+ // parents where the image must stay inline (authors use `<Image caption="…" />` instead).
124183
124301
  visit(tree, 'image', (node, index, parent) => {
124184
124302
  if (!parent || index === undefined)
124185
124303
  return SKIP;
124186
- if (parent.type === 'paragraph')
124187
- return SKIP;
124188
- // `![](url)` in any tableCell stays inline. Authors who want a captioned figure use
124189
- // `<Image caption="…" />` JSX, which becomes `image-block` via `COMPONENT_MAP` above.
124190
- if (parent.type === 'tableCell')
124304
+ if (INLINE_ONLY_PARENT_TYPES.has(parent.type))
124191
124305
  return SKIP;
124192
124306
  const newNode = transformMagicBlockImage(node);
124193
124307
  parent.children[index] = newNode;