@readme/markdown 14.3.0 → 14.4.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
@@ -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),
@@ -24834,9 +24835,10 @@ const LightboxPortal = ({ children }) => {
24834
24835
  return (0,external_react_dom_namespaceObject.createPortal)(children, document.body);
24835
24836
  };
24836
24837
  const Image = (Props) => {
24837
- const { align = '', alt = '', border: borderProp = false, caption, className = '', height = 'auto', src, title = '', width = 'auto', lazy = true, children, } = Props;
24838
- // Normalize border: MDXish passes {false} as the string "false", not a boolean
24838
+ const { align = '', alt = '', border: borderProp = false, caption, className = '', framed: framedProp = false, height = 'auto', src, title = '', width = 'auto', lazy = true, children, } = Props;
24839
+ // Normalize border/framed: MDXish passes {false} as the string "false", not a boolean
24839
24840
  const border = borderProp === true || borderProp === 'true';
24841
+ const framed = framedProp === true || framedProp === 'true';
24840
24842
  const [lightbox, setLightbox] = external_react_.useState(false);
24841
24843
  if (className === 'emoji') {
24842
24844
  return external_react_.createElement("img", { alt: alt, height: height, loading: lazy ? 'lazy' : 'eager', src: src, title: title, width: width });
@@ -24865,7 +24867,10 @@ const Image = (Props) => {
24865
24867
  return;
24866
24868
  setLightbox(!lightbox);
24867
24869
  };
24868
- const imgElement = (external_react_.createElement("img", { alt: alt, className: `img ${caption || children ? 'img-align-center' : align ? `img-align-${align}` : ''} ${border ? 'border' : ''}`, height: height, loading: lazy ? 'lazy' : 'eager', src: src, title: title, width: width }));
24870
+ // Framed images center the <img> itself; outer wrapper handles left/right alignment via text-align.
24871
+ const imgElement = (external_react_.createElement("img", { alt: alt, className: `img ${caption || children || framed ? 'img-align-center' : align ? `img-align-${align}` : ''} ${border ? 'border' : ''}`, height: height, loading: lazy ? 'lazy' : 'eager', src: src, title: title, width: width }));
24872
+ const closedLightbox = (ariaLabel, content) => (external_react_.createElement("span", { "aria-label": ariaLabel, className: "img lightbox closed", onClick: toggle, onKeyDown: handleKeyDown, role: 'button', tabIndex: 0 },
24873
+ external_react_.createElement("span", { className: "lightbox-inner" }, content)));
24869
24874
  const lightboxOverlay = lightbox ? (external_react_.createElement(LightboxPortal, null,
24870
24875
  external_react_.createElement("div", { className: "markdown-body" },
24871
24876
  external_react_.createElement("span", { "aria-label": alt || 'Collapse image', className: "img lightbox open", onClick: toggle, onKeyDown: handleKeyDown, role: 'button', tabIndex: 0 },
@@ -24874,17 +24879,27 @@ const Image = (Props) => {
24874
24879
  (children || caption) && external_react_.createElement("figcaption", null, children || caption))),
24875
24880
  external_react_.createElement("button", { "aria-label": "Minimize image", className: "lightbox-close", onClick: toggle, type: "button" },
24876
24881
  external_react_.createElement("i", { "aria-hidden": "true", className: "fa-solid fa-xmark" }))))) : null;
24882
+ if (framed) {
24883
+ const frameClass = `img-frame img-frame-${align || 'center'}`;
24884
+ if (children || caption) {
24885
+ return (external_react_.createElement("figure", { className: frameClass },
24886
+ closedLightbox(alt || 'Expand image', imgElement),
24887
+ lightboxOverlay,
24888
+ external_react_.createElement("figcaption", null, children || caption)));
24889
+ }
24890
+ return (external_react_.createElement("div", { className: frameClass },
24891
+ closedLightbox(alt || 'Expand image', imgElement),
24892
+ lightboxOverlay));
24893
+ }
24877
24894
  if (children || caption) {
24878
24895
  return (external_react_.createElement("figure", null,
24879
- external_react_.createElement("span", { "aria-label": alt, className: "img lightbox closed", onClick: toggle, onKeyDown: handleKeyDown, role: 'button', tabIndex: 0 },
24880
- external_react_.createElement("span", { className: "lightbox-inner" },
24881
- imgElement,
24882
- external_react_.createElement("figcaption", null, children || caption))),
24896
+ closedLightbox(alt || 'Expand image', external_react_.createElement(external_react_.Fragment, null,
24897
+ imgElement,
24898
+ external_react_.createElement("figcaption", null, children || caption))),
24883
24899
  lightboxOverlay));
24884
24900
  }
24885
24901
  return (external_react_.createElement(external_react_.Fragment, null,
24886
- external_react_.createElement("span", { "aria-label": "Expand image", className: "img lightbox closed", onClick: toggle, onKeyDown: handleKeyDown, role: 'button', tabIndex: 0 },
24887
- external_react_.createElement("span", { className: "lightbox-inner" }, imgElement)),
24902
+ closedLightbox('Expand image', imgElement),
24888
24903
  lightboxOverlay));
24889
24904
  };
24890
24905
  /* harmony default export */ const components_Image = (Image);
@@ -25122,12 +25137,14 @@ const Tab = ({ children }) => {
25122
25137
  };
25123
25138
  const Tabs = ({ children }) => {
25124
25139
  const [activeTab, setActiveTab] = (0,external_react_.useState)(0);
25140
+ // React passes `children` as a single element when there's only one child, so normalize.
25141
+ const tabs = external_react_default().Children.toArray(children);
25125
25142
  return (external_react_default().createElement("div", { className: "TabGroup" },
25126
25143
  external_react_default().createElement("header", null,
25127
- external_react_default().createElement("nav", { className: "TabGroup-nav" }, children?.map((tab, index) => (external_react_default().createElement("button", { key: tab.props.title, className: `TabGroup-tab${activeTab === index ? '_active' : ''}`, onClick: () => setActiveTab(index) },
25144
+ external_react_default().createElement("nav", { className: "TabGroup-nav" }, tabs.map((tab, index) => (external_react_default().createElement("button", { key: tab.props.title, className: `TabGroup-tab${activeTab === index ? '_active' : ''}`, onClick: () => setActiveTab(index) },
25128
25145
  tab.props.icon && (external_react_default().createElement("i", { className: `TabGroup-icon fa-duotone fa-solid ${tab.props.icon}`, style: { color: `${tab.props.iconColor}` } })),
25129
25146
  tab.props.title))))),
25130
- external_react_default().createElement("section", null, children && children[activeTab])));
25147
+ external_react_default().createElement("section", null, tabs[activeTab])));
25131
25148
  };
25132
25149
  /* harmony default export */ const components_Tabs = (Tabs);
25133
25150
 
@@ -25618,6 +25635,34 @@ const FLOW_TYPES = new Set([
25618
25635
  'table',
25619
25636
  'thematicBreak',
25620
25637
  ]);
25638
+ /**
25639
+ * MDAST parent types whose children are restricted to inline content. A block-level
25640
+ * node placed inside any of these violates the parent's content model and breaks
25641
+ * downstream consumers
25642
+ *
25643
+ * Derived from the mdast spec. Seven of these are direct "phrasing content" parents:
25644
+ * - paragraph, heading, link, linkReference, emphasis, strong
25645
+ * - tableCell (phrasing minus Break)
25646
+ *
25647
+ * @link {https://github.com/syntax-tree/mdast}
25648
+ *
25649
+ * `delete` is GFM and has *transparent* content, see:
25650
+ * @link {https://github.com/syntax-tree/mdast-util-gfm-strikethrough}
25651
+ *
25652
+ * its children must also be phrasing, so it belongs here too.
25653
+ *
25654
+ * NOTE: exported so downstream consumers can reuse and not drift away
25655
+ */
25656
+ const INLINE_ONLY_PARENT_TYPES = new Set([
25657
+ 'paragraph',
25658
+ 'heading',
25659
+ 'tableCell',
25660
+ 'link',
25661
+ 'linkReference',
25662
+ 'emphasis',
25663
+ 'strong',
25664
+ 'delete',
25665
+ ]);
25621
25666
  // HELPER CONSTANTS FOR MDXISH RENDERING
25622
25667
  /**
25623
25668
  * Inline-only custom components that appear as phrasing content within
@@ -95751,7 +95796,15 @@ const walkTags = (html, handlers) => {
95751
95796
  const tagEnd = (parser) => (parser.endIndex ?? parser.startIndex) + 1;
95752
95797
  const parser = new Parser_Parser({
95753
95798
  onopentag(name) {
95754
- handlers.onOpen?.({ name, start: parser.startIndex, end: tagEnd(parser) });
95799
+ const start = parser.startIndex;
95800
+ const end = tagEnd(parser);
95801
+ handlers.onOpen?.({
95802
+ name,
95803
+ start,
95804
+ end,
95805
+ isSelfClosing: html[end - 2] === '/',
95806
+ isStrayCloser: html[start + 1] === '/',
95807
+ });
95755
95808
  },
95756
95809
  onclosetag(name, implicit) {
95757
95810
  handlers.onClose?.({ name, start: parser.startIndex, end: tagEnd(parser), implicit });
@@ -95806,13 +95859,14 @@ const applyInserts = (html, inserts) => {
95806
95859
  const sorted = [...inserts].sort((a, b) => a.offset - b.offset);
95807
95860
  let out = '';
95808
95861
  let cursor = 0;
95809
- sorted.forEach(({ offset, text }) => {
95862
+ sorted.forEach(({ offset, text, consumes = 0 }) => {
95810
95863
  const clamped = Math.min(Math.max(offset, cursor), html.length);
95811
95864
  if (clamped > cursor) {
95812
95865
  out += html.slice(cursor, clamped);
95813
95866
  cursor = clamped;
95814
95867
  }
95815
95868
  out += text;
95869
+ cursor = Math.min(cursor + consumes, html.length);
95816
95870
  });
95817
95871
  return { value: out + html.slice(cursor), inserts: sorted };
95818
95872
  };
@@ -95924,9 +95978,10 @@ const buildOffsetMapper = (inserts) => {
95924
95978
  // Pre-compute each insert's start in repaired-space.
95925
95979
  let acc = 0;
95926
95980
  const segments = inserts.map(ins => {
95981
+ const consumes = ins.consumes ?? 0;
95927
95982
  const repairedStart = ins.offset + acc;
95928
- acc += ins.text.length;
95929
- return { origOffset: ins.offset, len: ins.text.length, repairedStart };
95983
+ acc += ins.text.length - consumes;
95984
+ return { origOffset: ins.offset, consumes, len: ins.text.length, repairedStart };
95930
95985
  });
95931
95986
  return (repaired) => {
95932
95987
  // Offsets inside an insert's synthetic span have no original counterpart;
@@ -95934,7 +95989,7 @@ const buildOffsetMapper = (inserts) => {
95934
95989
  const hit = segments.find(seg => seg.repairedStart < repaired && repaired < seg.repairedStart + seg.len);
95935
95990
  if (hit)
95936
95991
  return hit.origOffset;
95937
- const shift = segments.reduce((acc2, seg) => (seg.repairedStart < repaired ? acc2 + seg.len : acc2), 0);
95992
+ const shift = segments.reduce((acc2, seg) => (seg.repairedStart < repaired ? acc2 + seg.len - seg.consumes : acc2), 0);
95938
95993
  return repaired - shift;
95939
95994
  };
95940
95995
  };
@@ -96113,6 +96168,48 @@ const HTML_VOID_ELEMENTS = new Set([
96113
96168
 
96114
96169
 
96115
96170
  const isStandardHtmlTag = (name) => STANDARD_HTML_TAGS.has(name.toLowerCase());
96171
+ // Intentionally simpler than htmlparser2: `[^>]*` does not honor `>` inside
96172
+ // quoted attribute values. That's acceptable here because the main walker
96173
+ // (htmlparser2) handles attribute parsing; this scan only needs to locate
96174
+ // orphan closers, which can't appear inside an attribute value anyway.
96175
+ const HTML_TAG_TOKEN_RE = /<(\/)?([a-zA-Z][a-zA-Z0-9-]*)\b[^>]*>/g;
96176
+ const findOrphanClosers = (html) => {
96177
+ const masked = maskNonTagRegions(html);
96178
+ const stack = [];
96179
+ const orphans = [];
96180
+ HTML_TAG_TOKEN_RE.lastIndex = 0;
96181
+ let match;
96182
+ while ((match = HTML_TAG_TOKEN_RE.exec(masked)) !== null) {
96183
+ const name = match[2].toLowerCase();
96184
+ // eslint-disable-next-line no-continue
96185
+ if (!isStandardHtmlTag(name))
96186
+ continue;
96187
+ // Special case for <br>: htmlparser2 will normalize orphan </br> to <br>
96188
+ // so we don't need to handle it here.
96189
+ // eslint-disable-next-line no-continue
96190
+ if (name === 'br' && match[1] === '/')
96191
+ continue;
96192
+ const isVoid = HTML_VOID_ELEMENTS.has(name);
96193
+ if (match[1] === '/') {
96194
+ // Other void closers (`</hr>`, `</img>`, ...) have no HTML5 rewrite rule
96195
+ // and must be stripped before the strict mdxjs parse runs.
96196
+ if (isVoid) {
96197
+ orphans.push({ offset: match.index, length: match[0].length });
96198
+ // eslint-disable-next-line no-continue
96199
+ continue;
96200
+ }
96201
+ const idx = stack.lastIndexOf(name);
96202
+ if (idx === -1)
96203
+ orphans.push({ offset: match.index, length: match[0].length });
96204
+ else
96205
+ stack.length = idx;
96206
+ }
96207
+ else if (!isVoid && !match[0].endsWith('/>')) {
96208
+ stack.push(name);
96209
+ }
96210
+ }
96211
+ return orphans;
96212
+ };
96116
96213
  /**
96117
96214
  * MDX requires a JSX inline tag and its closer to live on the same line — not
96118
96215
  * just the same paragraph. (`<td>\nArray <object>\n</object></td>` still
@@ -96140,7 +96237,7 @@ const repairUnclosedTags = (html) => {
96140
96237
  const inserts = [];
96141
96238
  const openTags = [];
96142
96239
  walkTags(html, {
96143
- onOpen({ name, start, end }) {
96240
+ onOpen({ name, start, end, isSelfClosing, isStrayCloser }) {
96144
96241
  // Escape non-HTML names (custom components, typos, `<arbitrary-tag>`)
96145
96242
  // so MDX treats them as literal text instead of expecting a closer
96146
96243
  if (!isStandardHtmlTag(name)) {
@@ -96148,12 +96245,19 @@ const repairUnclosedTags = (html) => {
96148
96245
  return;
96149
96246
  }
96150
96247
  if (HTML_VOID_ELEMENTS.has(name.toLowerCase())) {
96151
- // MDX requires void elements to be self-closing (`<br/>`, not `<br>`).
96152
- // If the source open tag doesn't end with `/`, inject one before the
96153
- // `>` so it parses. `end` is one past `>`, so `end - 2` is the char
96154
- // immediately before `>`.
96155
- if (html[end - 2] !== '/')
96248
+ // MDX requires void elements to be self-closing (`<br/>`, not `<br>`)
96249
+ // so we need to rewrite them to self closing tags.
96250
+ if (isStrayCloser) {
96251
+ // htmlparser2 may normalize some stray closers like `</br>` into opener
96252
+ // events <br> per the HTML5 spec. In this case remove the closing /
96253
+ // and fully rewrite the tag to self closing.
96254
+ inserts.push({ offset: start, text: `<${name}/>`, consumes: end - start });
96255
+ return;
96256
+ }
96257
+ else if (!isSelfClosing) {
96258
+ // Slots in the / right before the closing >
96156
96259
  inserts.push({ offset: end - 1, text: '/' });
96260
+ }
96157
96261
  return;
96158
96262
  }
96159
96263
  openTags.push({ name, start, end });
@@ -96169,6 +96273,7 @@ const repairUnclosedTags = (html) => {
96169
96273
  inserts.push({ offset: findOffsetToPlaceCloser(html, openTag.end, start), text: `</${name}>` });
96170
96274
  },
96171
96275
  });
96276
+ findOrphanClosers(html).forEach(({ offset, length }) => inserts.push({ offset, text: '', consumes: length }));
96172
96277
  return applyInserts(html, inserts);
96173
96278
  };
96174
96279
 
@@ -118598,6 +118703,12 @@ function parseTextChildren(node, processMarkdown, components) {
118598
118703
  node.children = node.children.flatMap(child => {
118599
118704
  if (child.type !== 'text' || !child.value.trim())
118600
118705
  return [child];
118706
+ // Store multiline template-literal children (e.g. {`...\n...`}) as a bare string prop
118707
+ // via node.properties.children so hast-to-hyperscript doesn't wrap them in an array.
118708
+ if (child.value.includes('\n')) {
118709
+ node.properties = { ...node.properties, children: child.value };
118710
+ return [];
118711
+ }
118601
118712
  const hast = processMarkdown(child.value.trim());
118602
118713
  const children = (hast.children ?? []).filter(isElementContentNode);
118603
118714
  // For inline components, preserve plain text instead of wrapping in <p>
@@ -120749,6 +120860,123 @@ function createTokenize(mode) {
120749
120860
  const isSameCaseAsTag = (code) => isLowercaseTag
120750
120861
  ? code >= codes.lowercaseA && code <= codes.lowercaseZ
120751
120862
  : code >= codes.uppercaseA && code <= codes.uppercaseZ;
120863
+ // Shared brace-expression state machine. The two call sites differ only in where
120864
+ // to continue after a line ending and where to return when braceDepth reaches zero.
120865
+ function createBraceExprStates(continuationStart, afterBraceClose) {
120866
+ function braceExpr(code) {
120867
+ if (code === null)
120868
+ return nok(code);
120869
+ if (markdownLineEnding(code)) {
120870
+ if (!isFlow)
120871
+ return nok(code);
120872
+ effects.exit('mdxComponentData');
120873
+ return continuationStart(code);
120874
+ }
120875
+ if (code === codes.quotationMark || code === codes.apostrophe) {
120876
+ quoteChar = code;
120877
+ effects.consume(code);
120878
+ return braceString;
120879
+ }
120880
+ if (code === codes.graveAccent) {
120881
+ inTemplateLit = true;
120882
+ effects.consume(code);
120883
+ return braceTemplateLiteral;
120884
+ }
120885
+ if (code === codes.leftCurlyBrace) {
120886
+ braceDepth += 1;
120887
+ effects.consume(code);
120888
+ return braceExpr;
120889
+ }
120890
+ if (code === codes.rightCurlyBrace) {
120891
+ braceDepth -= 1;
120892
+ effects.consume(code);
120893
+ if (templateStack.length > 0 && braceDepth === templateStack[templateStack.length - 1]) {
120894
+ templateStack.pop();
120895
+ inTemplateLit = true;
120896
+ return braceTemplateLiteral;
120897
+ }
120898
+ if (braceDepth === 0) {
120899
+ return afterBraceClose;
120900
+ }
120901
+ return braceExpr;
120902
+ }
120903
+ effects.consume(code);
120904
+ return braceExpr;
120905
+ }
120906
+ function braceString(code) {
120907
+ if (code === null)
120908
+ return nok(code);
120909
+ if (markdownLineEnding(code)) {
120910
+ if (!isFlow)
120911
+ return nok(code);
120912
+ effects.exit('mdxComponentData');
120913
+ return continuationStart(code);
120914
+ }
120915
+ if (code === codes.backslash) {
120916
+ effects.consume(code);
120917
+ return braceStringEscape;
120918
+ }
120919
+ if (code === quoteChar) {
120920
+ effects.consume(code);
120921
+ quoteChar = null;
120922
+ return braceExpr;
120923
+ }
120924
+ effects.consume(code);
120925
+ return braceString;
120926
+ }
120927
+ function braceStringEscape(code) {
120928
+ if (code === null || markdownLineEnding(code)) {
120929
+ return braceString(code);
120930
+ }
120931
+ effects.consume(code);
120932
+ return braceString;
120933
+ }
120934
+ function braceTemplateLiteral(code) {
120935
+ if (code === null)
120936
+ return nok(code);
120937
+ if (markdownLineEnding(code)) {
120938
+ if (!isFlow)
120939
+ return nok(code);
120940
+ effects.exit('mdxComponentData');
120941
+ return continuationStart(code);
120942
+ }
120943
+ if (code === codes.graveAccent) {
120944
+ inTemplateLit = false;
120945
+ effects.consume(code);
120946
+ return braceExpr;
120947
+ }
120948
+ if (code === codes.backslash) {
120949
+ effects.consume(code);
120950
+ return braceTemplateLiteralEscape;
120951
+ }
120952
+ if (code === codes.dollarSign) {
120953
+ effects.consume(code);
120954
+ return braceTemplateLiteralDollar;
120955
+ }
120956
+ effects.consume(code);
120957
+ return braceTemplateLiteral;
120958
+ }
120959
+ function braceTemplateLiteralEscape(code) {
120960
+ if (code === null || markdownLineEnding(code)) {
120961
+ return braceTemplateLiteral(code);
120962
+ }
120963
+ effects.consume(code);
120964
+ return braceTemplateLiteral;
120965
+ }
120966
+ function braceTemplateLiteralDollar(code) {
120967
+ if (code === codes.leftCurlyBrace) {
120968
+ templateStack.push(braceDepth);
120969
+ braceDepth += 1;
120970
+ inTemplateLit = false;
120971
+ effects.consume(code);
120972
+ return braceExpr;
120973
+ }
120974
+ return braceTemplateLiteral(code);
120975
+ }
120976
+ return { braceExpr, braceString, braceTemplateLiteral };
120977
+ }
120978
+ const { braceExpr: inBraceExpr, braceString: inBraceString, braceTemplateLiteral: inBraceTemplateLiteral, } = createBraceExprStates(openTagContinuationStart, afterOpenTagName);
120979
+ const { braceExpr: inBodyBraceExpr, braceString: inBodyBraceString, braceTemplateLiteral: inBodyBraceTemplateLiteral, } = createBraceExprStates(bodyContinuationStart, body);
120752
120980
  return start;
120753
120981
  // ── Start ──────────────────────────────────────────────────────────────
120754
120982
  function start(code) {
@@ -120871,126 +121099,6 @@ function createTokenize(mode) {
120871
121099
  effects.consume(code);
120872
121100
  return inQuotedAttr;
120873
121101
  }
120874
- function inBraceExpr(code) {
120875
- if (code === null)
120876
- return nok(code);
120877
- if (markdownLineEnding(code)) {
120878
- if (!isFlow)
120879
- return nok(code);
120880
- effects.exit('mdxComponentData');
120881
- return openTagContinuationStart(code);
120882
- }
120883
- // Handle strings inside braces
120884
- if (code === codes.quotationMark || code === codes.apostrophe) {
120885
- quoteChar = code;
120886
- effects.consume(code);
120887
- return inBraceString;
120888
- }
120889
- // Handle template literals inside braces
120890
- if (code === codes.graveAccent) {
120891
- inTemplateLit = true;
120892
- effects.consume(code);
120893
- return inBraceTemplateLiteral;
120894
- }
120895
- if (code === codes.leftCurlyBrace) {
120896
- braceDepth += 1;
120897
- effects.consume(code);
120898
- return inBraceExpr;
120899
- }
120900
- if (code === codes.rightCurlyBrace) {
120901
- braceDepth -= 1;
120902
- effects.consume(code);
120903
- // Check if this } closes a ${...} interpolation
120904
- if (templateStack.length > 0 && braceDepth === templateStack[templateStack.length - 1]) {
120905
- templateStack.pop();
120906
- inTemplateLit = true; // back inside the template literal
120907
- return inBraceTemplateLiteral;
120908
- }
120909
- if (braceDepth === 0) {
120910
- return afterOpenTagName;
120911
- }
120912
- return inBraceExpr;
120913
- }
120914
- effects.consume(code);
120915
- return inBraceExpr;
120916
- }
120917
- function inBraceString(code) {
120918
- if (code === null)
120919
- return nok(code);
120920
- if (markdownLineEnding(code)) {
120921
- if (!isFlow)
120922
- return nok(code);
120923
- effects.exit('mdxComponentData');
120924
- return openTagContinuationStart(code);
120925
- }
120926
- if (code === codes.backslash) {
120927
- effects.consume(code);
120928
- return inBraceStringEscape;
120929
- }
120930
- if (code === quoteChar) {
120931
- effects.consume(code);
120932
- quoteChar = null;
120933
- return inBraceExpr;
120934
- }
120935
- effects.consume(code);
120936
- return inBraceString;
120937
- }
120938
- function inBraceStringEscape(code) {
120939
- if (code === null || markdownLineEnding(code)) {
120940
- return inBraceString(code);
120941
- }
120942
- effects.consume(code);
120943
- return inBraceString;
120944
- }
120945
- // ── Template literal handling inside brace expressions ─────────────────
120946
- function inBraceTemplateLiteral(code) {
120947
- if (code === null)
120948
- return nok(code);
120949
- if (markdownLineEnding(code)) {
120950
- if (!isFlow)
120951
- return nok(code);
120952
- effects.exit('mdxComponentData');
120953
- return openTagContinuationStart(code);
120954
- }
120955
- // Closing backtick ends the template literal
120956
- if (code === codes.graveAccent) {
120957
- inTemplateLit = false;
120958
- effects.consume(code);
120959
- return inBraceExpr;
120960
- }
120961
- // Backslash escape (e.g., \` or \$)
120962
- if (code === codes.backslash) {
120963
- effects.consume(code);
120964
- return inBraceTemplateLiteralEscape;
120965
- }
120966
- // ${ starts an interpolation
120967
- if (code === codes.dollarSign) {
120968
- effects.consume(code);
120969
- return inBraceTemplateLiteralDollar;
120970
- }
120971
- effects.consume(code);
120972
- return inBraceTemplateLiteral;
120973
- }
120974
- function inBraceTemplateLiteralEscape(code) {
120975
- if (code === null || markdownLineEnding(code)) {
120976
- return inBraceTemplateLiteral(code);
120977
- }
120978
- effects.consume(code);
120979
- return inBraceTemplateLiteral;
120980
- }
120981
- function inBraceTemplateLiteralDollar(code) {
120982
- if (code === codes.leftCurlyBrace) {
120983
- // Enter ${...} interpolation. Save current braceDepth so we know
120984
- // when the matching } returns us to this template literal.
120985
- templateStack.push(braceDepth);
120986
- braceDepth += 1;
120987
- inTemplateLit = false; // now inside interpolation expression
120988
- effects.consume(code);
120989
- return inBraceExpr;
120990
- }
120991
- // Just a $ not followed by { — back to template literal
120992
- return inBraceTemplateLiteral(code);
120993
- }
120994
121102
  function selfCloseGt(code) {
120995
121103
  if (code === codes.greaterThan) {
120996
121104
  effects.consume(code);
@@ -121060,6 +121168,15 @@ function createTokenize(mode) {
121060
121168
  codeSpanOpenSize = 0;
121061
121169
  return countOpenTicks(code);
121062
121170
  }
121171
+ // JSX expression child — track braces/template literals so the closing
121172
+ // backtick of `{`...`}` is not misread as a code span opener
121173
+ if (code === codes.leftCurlyBrace) {
121174
+ braceDepth = 1;
121175
+ inTemplateLit = false;
121176
+ effects.consume(code);
121177
+ atLineStart = false;
121178
+ return inBodyBraceExpr;
121179
+ }
121063
121180
  effects.consume(code);
121064
121181
  atLineStart = false;
121065
121182
  return body;
@@ -121300,6 +121417,14 @@ function createTokenize(mode) {
121300
121417
  return bodyContinuationStart(code);
121301
121418
  }
121302
121419
  effects.enter('mdxComponentData');
121420
+ // Resume inside a body brace expression if a line ending interrupted one
121421
+ if (braceDepth > 0) {
121422
+ if (inTemplateLit)
121423
+ return inBodyBraceTemplateLiteral(code);
121424
+ if (quoteChar !== null)
121425
+ return inBodyBraceString(code);
121426
+ return inBodyBraceExpr(code);
121427
+ }
121303
121428
  // Detect tilde fences at line start
121304
121429
  if (atLineStart && code === codes.tilde) {
121305
121430
  return bodyAfterLineStart(code);
@@ -121402,7 +121527,8 @@ const buildInlineMdProcessor = (safeMode) => {
121402
121527
  // Since evaluating expressions can be dangerous, do so only when safeMode is off
121403
121528
  if (!safeMode) {
121404
121529
  const mdxExprExt = mdxExpression({ allowEmpty: true });
121405
- micromarkExts.push({ text: mdxExprExt.text });
121530
+ // We include both flow and text extensions to support both single-line and multi-line expressions
121531
+ micromarkExts.push({ flow: mdxExprExt.flow, text: mdxExprExt.text });
121406
121532
  fromMarkdownExts.push(mdxExpressionFromMarkdown());
121407
121533
  }
121408
121534
  return unified()
@@ -122060,7 +122186,7 @@ const createEvaluatedNode = (result, position) => {
122060
122186
  else if (typeof result === 'object') {
122061
122187
  return { type: 'text', value: JSON.stringify(result), position };
122062
122188
  }
122063
- return { type: 'text', value: String(result).replace(/\s+/g, ' ').trim(), position };
122189
+ return { type: 'text', value: String(result), position };
122064
122190
  };
122065
122191
  /**
122066
122192
  * AST transformer to evaluate MDX expressions.
@@ -123549,6 +123675,7 @@ const mdxishHtmlBlocks = () => tree => {
123549
123675
 
123550
123676
 
123551
123677
 
123678
+
123552
123679
  function toImageAlign(value) {
123553
123680
  if (value === 'left' || value === 'center' || value === 'right') {
123554
123681
  return value;
@@ -123770,7 +123897,7 @@ const transformAnchor = (jsx) => {
123770
123897
  */
123771
123898
  const transformImage = (jsx) => {
123772
123899
  const attrs = getAttrs(jsx);
123773
- const { align, alt = '', border, caption, className, height, lazy, src = '', title = '', width } = attrs;
123900
+ const { align, alt = '', border, caption, className, framed, height, lazy, src = '', title = '', width } = attrs;
123774
123901
  const validAlign = toImageAlign(align);
123775
123902
  const sizing = width !== undefined ? String(width) : undefined;
123776
123903
  const hProperties = {
@@ -123781,6 +123908,7 @@ const transformImage = (jsx) => {
123781
123908
  ...(border !== undefined && { border: toBool(border) }),
123782
123909
  ...(caption && { caption }),
123783
123910
  ...(className && { className }),
123911
+ ...(framed !== undefined && { framed: toBool(framed) }),
123784
123912
  ...(height !== undefined && { height: String(height) }),
123785
123913
  ...(lazy !== undefined && { lazy: toBool(lazy) }),
123786
123914
  ...(sizing && { sizing }),
@@ -123794,6 +123922,7 @@ const transformImage = (jsx) => {
123794
123922
  caption,
123795
123923
  children: caption ? lib_mdast(caption).children : [],
123796
123924
  className,
123925
+ framed: toBool(framed),
123797
123926
  height: height !== undefined ? String(height) : undefined,
123798
123927
  lazy: toBool(lazy),
123799
123928
  sizing,
@@ -124099,16 +124228,12 @@ const mdxishJsxToMdast = () => tree => {
124099
124228
  parent.children[index] = newNode;
124100
124229
  }
124101
124230
  });
124102
- // Transform magic block images (type: 'image') to image-block
124103
- // Images inside paragraphs are standard markdown handled by imageTransformer, normalized below
124231
+ // Promote magic-block images (type: 'image') to image-block, except inside inline-only
124232
+ // parents where the image must stay inline (authors use `<Image caption="…" />` instead).
124104
124233
  visit(tree, 'image', (node, index, parent) => {
124105
124234
  if (!parent || index === undefined)
124106
124235
  return SKIP;
124107
- if (parent.type === 'paragraph')
124108
- return SKIP;
124109
- // `![](url)` in any tableCell stays inline. Authors who want a captioned figure use
124110
- // `<Image caption="…" />` JSX, which becomes `image-block` via `COMPONENT_MAP` above.
124111
- if (parent.type === 'tableCell')
124236
+ if (INLINE_ONLY_PARENT_TYPES.has(parent.type))
124112
124237
  return SKIP;
124113
124238
  const newNode = transformMagicBlockImage(node);
124114
124239
  parent.children[index] = newNode;
@@ -124162,6 +124287,14 @@ const mdxishJsxToMdast = () => tree => {
124162
124287
  else if (node.border !== undefined) {
124163
124288
  node.border = toBool(node.border);
124164
124289
  }
124290
+ if (hProps.framed !== undefined) {
124291
+ const val = toBool(hProps.framed);
124292
+ node.framed = val;
124293
+ hProps.framed = val;
124294
+ }
124295
+ else if (node.framed !== undefined) {
124296
+ node.framed = toBool(node.framed);
124297
+ }
124165
124298
  // Validate align
124166
124299
  const validAlign = toImageAlign(hProps.align) || toImageAlign(node.align);
124167
124300
  node.align = validAlign;
@@ -124616,11 +124749,30 @@ function normalizeTableSeparator(content) {
124616
124749
 
124617
124750
  ;// ./processor/transform/mdxish/terminate-html-flow-blocks.ts
124618
124751
 
124752
+
124619
124753
  const STANDALONE_HTML_LINE_REGEX = /^(<[a-z][^<>]*>|<\/[a-z][^<>]*>)+\s*$/;
124620
124754
  const HTML_LINE_WITH_CONTENT_REGEX = /^<[a-z][^<>]*>.*<\/[a-z][^<>]*>(?:[^<]*)$/;
124755
+ const terminate_html_flow_blocks_TABLE_STRUCTURE_TAGS = ['table', 'thead', 'tbody', 'tfoot', 'tr', 'td', 'th', 'caption', 'colgroup'];
124756
+ // Tags whose contents must be preserved as is, inserting a blank line after the
124757
+ // opener corrupts the payload.
124758
+ // htmlRawNames here refer to <pre>, <textarea>, <script>, <style>
124759
+ const RAW_CONTENT_TAGS = [...htmlRawNames, ...terminate_html_flow_blocks_TABLE_STRUCTURE_TAGS];
124760
+ // The `(?=[\s/>])` lookahead avoids false matches on lookalike names like `<script-foo>`.
124761
+ const RAW_CONTENT_TAG_MATCHERS = RAW_CONTENT_TAGS.map(tag => ({
124762
+ open: new RegExp(`<${tag}(?=[\\s/>])[^>]*?(?<!/)>`, 'gi'),
124763
+ close: new RegExp(`</${tag}(?=[\\s>])[^>]*>`, 'gi'),
124764
+ }));
124621
124765
  function isLineHtml(line) {
124622
124766
  return STANDALONE_HTML_LINE_REGEX.test(line) || HTML_LINE_WITH_CONTENT_REGEX.test(line);
124623
124767
  }
124768
+ // True if any RAW_CONTENT_TAGS opener on this line is not closed on the same line.
124769
+ function hasUnclosedRawContentOpener(line) {
124770
+ return RAW_CONTENT_TAG_MATCHERS.some(({ open, close }) => {
124771
+ const opens = (line.match(open) ?? []).length;
124772
+ const closes = (line.match(close) ?? []).length;
124773
+ return opens > closes;
124774
+ });
124775
+ }
124624
124776
  /**
124625
124777
  * Preprocessor to terminate HTML flow blocks.
124626
124778
  *
@@ -124631,15 +124783,16 @@ function isLineHtml(line) {
124631
124783
  *
124632
124784
  * @link https://spec.commonmark.org/0.29/#html-blocks
124633
124785
  *
124634
- * This preprocessor inserts a blank line after standalone HTML lines when the
124635
- * next line is non-blank and not an HTML construct (because they still might be part of the HTML flow),
124636
- * ensuring micromark's HTML flow tokenizer terminates and subsequent content is parsed independently.
124786
+ * This preprocessor inserts a blank line after standalone HTML lines when the next
124787
+ * line is non-blank and not an HTML construct, ensuring micromark's HTML flow
124788
+ * tokenizer terminates and subsequent content is parsed independently.
124637
124789
  *
124638
124790
  * Conditions:
124639
- * 1. Only targets non-indented lines with lowercase tag names. Uppercase tags
124640
- * (e.g., `<Table>`, `<MyComponent>`) are JSX custom components and don't
124641
- * trigger CommonMark HTML blocks, so they are left untouched.
124642
- * 2. Lines inside protected blocks (e.g., code blocks) should be left untouched.
124791
+ * 1. Only non-indented lines with lowercase tag names are considered. Uppercase tags
124792
+ * (e.g. `<Table>`, `<MyComponent>`) are JSX custom components and don't trigger
124793
+ * CommonMark HTML blocks.
124794
+ * 2. Lines inside protected blocks (e.g. fenced code) are left untouched.
124795
+ * 3. Lines with an unclosed RAW_CONTENT_TAGS opener are exempted.
124643
124796
  */
124644
124797
  function terminateHtmlFlowBlocks(content) {
124645
124798
  const { protectedContent, protectedCode } = protectCodeBlocks(content);
@@ -124654,7 +124807,7 @@ function terminateHtmlFlowBlocks(content) {
124654
124807
  }
124655
124808
  const isCurrentLineHtml = isLineHtml(lines[i]);
124656
124809
  const isNextLineHtml = isLineHtml(lines[i + 1]);
124657
- if (isCurrentLineHtml && !isNextLineHtml) {
124810
+ if (isCurrentLineHtml && !isNextLineHtml && !hasUnclosedRawContentOpener(lines[i])) {
124658
124811
  result.push('');
124659
124812
  }
124660
124813
  }
@@ -125472,8 +125625,8 @@ function mdxish(mdContent, opts = {}) {
125472
125625
  const { processor, parserReadyContent } = mdxishAstProcessor(contentWithoutComments, opts);
125473
125626
  processor
125474
125627
  .use(safeMode ? undefined : evaluate_exports) // Evaluate `export const/function` and stash scope on file.data.mdxishScope
125628
+ .use(remarkBreaks) // Must precede evaluateExpressions to avoid splitting the \n in an evaluated template literal into a <br> node
125475
125629
  .use(safeMode ? undefined : evaluate_expressions) // Evaluate self-contained MDX expressions (e.g. `{1+1}`)
125476
- .use(remarkBreaks)
125477
125630
  .use(variables_code, { variables }) // Resolve <<...>> and {user.*} inside code and inline code nodes
125478
125631
  .use(remarkRehype, { allowDangerousHtml: true, handlers: mdxComponentHandlers })
125479
125632
  .use(preserveBooleanProperties) // RehypeRaw converts boolean properties to empty strings
@@ -125725,7 +125878,10 @@ function exportComponentsForRehype(components) {
125725
125878
  function createElementPreservingHastProps(type, props, ...children) {
125726
125879
  if (props?.node?.properties) {
125727
125880
  const { node, ...rest } = props;
125728
- return external_react_default().createElement(type, { ...rest, ...node.properties }, ...children);
125881
+ const mergedProps = { ...rest, ...node.properties };
125882
+ // Strip undefined so positional args don't shadow node.properties.children
125883
+ const definedChildren = children.filter(c => c !== undefined);
125884
+ return external_react_default().createElement(type, mergedProps, ...definedChildren);
125729
125885
  }
125730
125886
  return external_react_default().createElement(type, props, ...children);
125731
125887
  }