@readme/markdown 15.3.0 → 15.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
@@ -11423,7 +11423,7 @@ module.exports = function () {
11423
11423
 
11424
11424
  /***/ },
11425
11425
 
11426
- /***/ 5168
11426
+ /***/ 912
11427
11427
  (module, __webpack_exports__, __webpack_require__) {
11428
11428
 
11429
11429
  "use strict";
@@ -11564,6 +11564,63 @@ __webpack_require__.d(util_types_namespaceObject, {
11564
11564
  // EXTERNAL MODULE: external {"amd":"react","commonjs":"react","commonjs2":"react","root":"React","umd":"react"}
11565
11565
  var external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_ = __webpack_require__(1307);
11566
11566
  var external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_default = /*#__PURE__*/__webpack_require__.n(external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_);
11567
+ ;// ./hooks/useRestartAnimatedImages/index.tsx
11568
+
11569
+ /**
11570
+ * GIF is the one extension that implies animation. `.webp` and `.png` (the extension an APNG
11571
+ * ships under) are overwhelmingly static, so matching them would reload far more images than
11572
+ * it rewinds; telling those apart needs the bytes, not the URL.
11573
+ */
11574
+ const ANIMATED_IMAGE_SRC = /\.gif(\?|#|$)/i;
11575
+ const restartAnimatedImages = (root) => {
11576
+ root?.querySelectorAll('img').forEach(img => {
11577
+ if (!ANIMATED_IMAGE_SRC.test(img.src))
11578
+ return;
11579
+ const { src } = img;
11580
+ // There's no seek API for animated images; reassigning `src` is the only reset
11581
+ img.src = '';
11582
+ img.src = src;
11583
+ });
11584
+ };
11585
+ const useRestartAfterFirstPaint = (revealKey, restart) => {
11586
+ const isFirstRender = (0,external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_.useRef)(true);
11587
+ const latestRestart = (0,external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_.useRef)(restart);
11588
+ latestRestart.current = restart;
11589
+ (0,external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_.useEffect)(() => {
11590
+ if (isFirstRender.current) {
11591
+ isFirstRender.current = false;
11592
+ return;
11593
+ }
11594
+ latestRestart.current();
11595
+ }, [revealKey]);
11596
+ };
11597
+ /**
11598
+ * Restarts GIF playback inside the panel that just became active.
11599
+ *
11600
+ * Tabs keep every panel mounted, so an <img> is never re-created and a GIF is
11601
+ * shown mid-loop (or frozen on its last frame) when its tab is re-selected.
11602
+ *
11603
+ * @returns a ref to attach to each panel element, indexed by tab.
11604
+ */
11605
+ function useRestartAnimatedImages(activeIndex) {
11606
+ const panelRefs = (0,external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_.useRef)([]);
11607
+ useRestartAfterFirstPaint(activeIndex, () => restartAnimatedImages(panelRefs.current[activeIndex]));
11608
+ return panelRefs;
11609
+ }
11610
+ /**
11611
+ * Restarts GIF playback inside an element that has just been revealed after being hidden —
11612
+ * a collapsed <details>, whose images have been animating out of sight since page load.
11613
+ */
11614
+ function useRestartAnimatedImagesOnReveal(isRevealed) {
11615
+ const contentRef = (0,external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_.useRef)(null);
11616
+ useRestartAfterFirstPaint(isRevealed, () => {
11617
+ // Rewinding content the reader just hid would be wasted work
11618
+ if (isRevealed)
11619
+ restartAnimatedImages(contentRef.current);
11620
+ });
11621
+ return contentRef;
11622
+ }
11623
+
11567
11624
  ;// ./components/Icon/index.tsx
11568
11625
 
11569
11626
  /** @see https://docs-v5.fontawesome.com/web/reference-icons */
@@ -11588,14 +11645,16 @@ const Icon = ({ className, faClassName, icon, iconColor }) => {
11588
11645
 
11589
11646
 
11590
11647
 
11648
+
11591
11649
  const Accordion = ({ children, icon, iconColor, title }) => {
11592
11650
  const [isOpen, setIsOpen] = (0,external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_.useState)(false);
11651
+ const contentRef = useRestartAnimatedImagesOnReveal(isOpen);
11593
11652
  return (external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_default().createElement("details", { className: "Accordion", onToggle: () => setIsOpen(!isOpen) },
11594
11653
  external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_default().createElement("summary", { className: "Accordion-title" },
11595
11654
  external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_default().createElement("i", { className: `Accordion-toggleIcon${isOpen ? '_opened' : ''} fa fa-regular fa-chevron-right` }),
11596
11655
  icon && external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_default().createElement(components_Icon, { className: "Accordion-icon", icon: icon, iconColor: iconColor }),
11597
11656
  title),
11598
- external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_default().createElement("div", { className: "Accordion-content" }, children)));
11657
+ external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_default().createElement("div", { ref: contentRef, className: "Accordion-content" }, children)));
11599
11658
  };
11600
11659
  /* harmony default export */ const components_Accordion = (Accordion);
11601
11660
 
@@ -12495,11 +12554,13 @@ function TableOfContents({ children }) {
12495
12554
 
12496
12555
 
12497
12556
 
12557
+
12498
12558
  const Tab = ({ children }) => {
12499
12559
  return external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_default().createElement("div", { className: "TabContent" }, children);
12500
12560
  };
12501
12561
  const Tabs = ({ children }) => {
12502
12562
  const [activeTab, setActiveTab] = (0,external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_.useState)(0);
12563
+ const panelRefs = useRestartAnimatedImages(activeTab);
12503
12564
  // React passes `children` as a single element when there's only one child, so normalize.
12504
12565
  const tabs = external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_default().Children.toArray(children);
12505
12566
  return (external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_default().createElement("div", { className: "TabGroup" },
@@ -12507,7 +12568,9 @@ const Tabs = ({ children }) => {
12507
12568
  external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_default().createElement("nav", { className: "TabGroup-nav" }, tabs.map((tab, index) => (external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_default().createElement("button", { key: tab.key, className: `TabGroup-tab${activeTab === index ? '_active' : ''}`, onClick: () => setActiveTab(index) },
12508
12569
  tab.props.icon && (external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_default().createElement(components_Icon, { className: "TabGroup-icon", icon: tab.props.icon, iconColor: tab.props.iconColor })),
12509
12570
  tab.props.title))))),
12510
- external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_default().createElement("section", null, tabs.map((tab, index) => (external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_default().createElement("div", { key: tab.key, hidden: index !== activeTab }, tab))))));
12571
+ external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_default().createElement("section", null, tabs.map((tab, index) => (external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_default().createElement("div", { key: tab.key, ref: el => {
12572
+ panelRefs.current[index] = el;
12573
+ }, hidden: index !== activeTab }, tab))))));
12511
12574
  };
12512
12575
  /* harmony default export */ const components_Tabs = (Tabs);
12513
12576
 
@@ -12675,7 +12738,7 @@ async function loadStylesheet(id, base) {
12675
12738
  async function loadModule() {
12676
12739
  throw new Error('The browser build does not support plugins or config files.');
12677
12740
  }
12678
- async function createCompiler({ darkModeDataAttribute }) {
12741
+ async function createCompiler({ darkModeDataAttribute, darkModeRootSelector, }) {
12679
12742
  let css = `
12680
12743
  @layer theme, base, components, utilities;
12681
12744
 
@@ -12683,9 +12746,12 @@ async function createCompiler({ darkModeDataAttribute }) {
12683
12746
  @import "tailwindcss/utilities.css" layer(utilities);
12684
12747
  `;
12685
12748
  if (darkModeDataAttribute) {
12749
+ // Anchors `dark:` to `darkModeRootSelector`'s own attribute instead of any ancestor's,
12750
+ // when supplied — see the `darkModeRootSelector` param doc below for why there's no default.
12751
+ const root = darkModeRootSelector || '';
12686
12752
  css += `
12687
12753
 
12688
- @custom-variant dark (&:where([${darkModeDataAttribute}=dark], [${darkModeDataAttribute}=dark] *));`;
12754
+ @custom-variant dark (&:where(${root}[${darkModeDataAttribute}=dark], ${root}[${darkModeDataAttribute}=dark] *));`;
12689
12755
  }
12690
12756
  return Hu(css, {
12691
12757
  base: '/',
@@ -12693,8 +12759,8 @@ async function createCompiler({ darkModeDataAttribute }) {
12693
12759
  loadModule,
12694
12760
  });
12695
12761
  }
12696
- async function tailwindCompiler(classes, { prefix, darkModeDataAttribute }) {
12697
- const compiler = await createCompiler({ darkModeDataAttribute });
12762
+ async function tailwindCompiler(classes, { prefix, darkModeDataAttribute, darkModeRootSelector, }) {
12763
+ const compiler = await createCompiler({ darkModeDataAttribute, darkModeRootSelector });
12698
12764
  const css = compiler.build(Array.from(classes));
12699
12765
  return lib_postcss([postcss_prefix_selector_default()({ prefix })]).process(css, { from: undefined });
12700
12766
  }
@@ -12709,7 +12775,7 @@ const traverse = (node, callback) => {
12709
12775
  traverse(child, callback);
12710
12776
  });
12711
12777
  };
12712
- const TailwindStyle = ({ children, darkModeDataAttribute }) => {
12778
+ const TailwindStyle = ({ children, darkModeDataAttribute, darkModeRootSelector }) => {
12713
12779
  const [stylesheet, setStylesheet] = (0,external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_.useState)('');
12714
12780
  const classesSet = (0,external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_.useRef)(new Set());
12715
12781
  const ref = (0,external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_.useRef)(null);
@@ -12733,6 +12799,7 @@ const TailwindStyle = ({ children, darkModeDataAttribute }) => {
12733
12799
  const sheet = await tailwindCompiler(classes, {
12734
12800
  prefix: `.${tailwindPrefix}`,
12735
12801
  darkModeDataAttribute,
12802
+ darkModeRootSelector,
12736
12803
  });
12737
12804
  /* @note: don't insert an empty stylesheet */
12738
12805
  if (sheet.css.match(/^@layer utilities;/m))
@@ -12740,7 +12807,7 @@ const TailwindStyle = ({ children, darkModeDataAttribute }) => {
12740
12807
  setStylesheet(sheet.css);
12741
12808
  };
12742
12809
  run();
12743
- }, [classes, darkModeDataAttribute]);
12810
+ }, [classes, darkModeDataAttribute, darkModeRootSelector]);
12744
12811
  /*
12745
12812
  * @note: execute once on load
12746
12813
  */
@@ -76386,6 +76453,10 @@ function syntax_createTokenize(mode) {
76386
76453
  effects.consume(code);
76387
76454
  return inBraceExpr;
76388
76455
  }
76456
+ // A raw `<` can't sit in an opening tag (quotes/braces are handled above);
76457
+ // bailing bounds each attempt to the next `<` instead of rescanning the line.
76458
+ if (code === codes.lessThan)
76459
+ return nok(code);
76389
76460
  effects.consume(code);
76390
76461
  return afterOpenTagName;
76391
76462
  }
@@ -101592,7 +101663,7 @@ const listMarkerRegex = /^(?:[*+-]|\d+[.)])(?:([\r\n]| {1,3})|$)/;
101592
101663
  * with their checkbox intact (for example, `- [ ]`) instead of dropping it
101593
101664
  * We can add more adjustments if needed
101594
101665
  */
101595
- const compile_list_item_listItem = (node, parent, state, info) => {
101666
+ const compile_list_item_listItem = ((node, parent, state, info) => {
101596
101667
  const head = node.children[0];
101597
101668
  const isCheckbox = typeof node.checked === 'boolean' && head && head.type === 'paragraph';
101598
101669
  if (!isCheckbox) {
@@ -101616,15 +101687,47 @@ const compile_list_item_listItem = (node, parent, state, info) => {
101616
101687
  return `${marker}${actualSeparator}${checkbox}`;
101617
101688
  });
101618
101689
  return value;
101619
- };
101690
+ });
101620
101691
  /* harmony default export */ const list_item = (compile_list_item_listItem);
101621
101692
 
101622
101693
  ;// ./processor/compile/plain.ts
101623
101694
  const plain_plain = (node) => node.value;
101624
101695
  /* harmony default export */ const compile_plain = (plain_plain);
101625
101696
 
101697
+ ;// ./processor/compile/table-cell-block-markers.ts
101698
+ const CELL_START_BLOCK_MARKER = /^(?:[-+](?=\s|$)|#{1,6}(?=\s|$)|>)/;
101699
+ const BR_HTML = /^\s*<br\s*\/?>\s*$/i;
101700
+ const isLineBreak = (node) => {
101701
+ if (node.type === 'break')
101702
+ return true;
101703
+ if (node.type === 'html')
101704
+ return BR_HTML.test(node.value);
101705
+ return node.type === 'mdxJsxTextElement' && 'name' in node && node.name?.toLowerCase() === 'br';
101706
+ };
101707
+ const followsLineBreak = (node, parent) => {
101708
+ const siblings = (parent?.children ?? []);
101709
+ const previous = siblings[siblings.indexOf(node) - 1];
101710
+ return !!previous && isLineBreak(previous);
101711
+ };
101712
+ /**
101713
+ * Re-escapes a leading block marker (`-`, `+`, `#`, `>`) at the start of a table cell, or
101714
+ * after a `<br />` within one.
101715
+ *
101716
+ * A cell is serialized mid-line, so the `atBreak` patterns that escape these characters
101717
+ * never fire and an author's `| \- one |` round trips to `| - one |` (RM-17203). Cells are
101718
+ * serialized as `containerPhrasing(cell, { before: '|' })`, so `|` marks the cell start.
101719
+ */
101720
+ const escapeCellStartBlockMarker = (serialized, node, parent, state, info) => {
101721
+ if (serialized.startsWith('\\') || !CELL_START_BLOCK_MARKER.test(serialized))
101722
+ return serialized;
101723
+ if (!state.stack.includes('tableCell') || !(info.before === '|' || followsLineBreak(node, parent)))
101724
+ return serialized;
101725
+ return `\\${serialized}`;
101726
+ };
101727
+
101626
101728
  ;// ./processor/compile/text.ts
101627
101729
 
101730
+
101628
101731
  // A `_` flanked by word characters can never open or close emphasis under
101629
101732
  // CommonMark's flanking rules, so the escape mdast-util-to-markdown adds to
101630
101733
  // intraword underscores is unnecessary and only produces noisy `\_` diffs.
@@ -101632,9 +101735,10 @@ const plain_plain = (node) => node.value;
101632
101735
  const INTRAWORD_UNDERSCORE_ESCAPE = /(?<=[\p{L}\p{N}_])\\_(?=[\p{L}\p{N}_]|\\_)/gu;
101633
101736
  const compile_text_text = (node, parent, state, info) => {
101634
101737
  const serialized = handle.text(node, parent, state, info);
101635
- return serialized.replace(INTRAWORD_UNDERSCORE_ESCAPE, '_');
101738
+ return escapeCellStartBlockMarker(serialized, node, parent, state, info);
101636
101739
  };
101637
- /* harmony default export */ const compile_text = (compile_text_text);
101740
+ const mdxishText = (node, parent, state, info) => compile_text_text(node, parent, state, info).replace(INTRAWORD_UNDERSCORE_ESCAPE, '_');
101741
+ /* harmony default export */ const compile_text = (mdxishText);
101638
101742
 
101639
101743
  ;// ./processor/compile/index.ts
101640
101744
 
@@ -101666,6 +101770,7 @@ function compilers(mdxish = false) {
101666
101770
  html: compile_compatibility,
101667
101771
  i: compile_compatibility,
101668
101772
  plain: compile_plain,
101773
+ text: compile_text_text,
101669
101774
  yaml: compile_compatibility,
101670
101775
  // needed only for mdxish
101671
101776
  ...(mdxish && { list: compile_list }),
@@ -103734,13 +103839,14 @@ function restoreCodeBlocks(content, protectedCode) {
103734
103839
  *
103735
103840
  * The attribute portion skips over quoted strings (`"..."` and `'...'`) so that
103736
103841
  * a `/>` inside an attribute value (e.g. `title="use /> here"`) does not cause
103737
- * a premature match.
103842
+ * a premature match, and stops at an unquoted `<` so a stray `<x` in prose
103843
+ * doesn't scan to the end of the document (quadratic).
103738
103844
  *
103739
103845
  * Only matches lowercase tag names to avoid interfering with PascalCase
103740
103846
  * JSX custom components (e.g. `<MyComponent />`), which are handled
103741
103847
  * separately by components/mdx-blocks.
103742
103848
  */
103743
- const SELF_CLOSING_TAG_RE = /<([a-z][a-z0-9-]*)((?:\s+(?:[^>"']*(?:"[^"]*"|'[^']*'))*[^>"']*)?)?\s*\/>/g;
103849
+ const SELF_CLOSING_TAG_RE = /<([a-z][a-z0-9-]*)((?:\s+(?:[^<>"']*(?:"[^"]*"|'[^']*'))*[^<>"']*)?)?\s*\/>/g;
103744
103850
  /**
103745
103851
  * String-level preprocessor that converts self-closing non-void HTML tags
103746
103852
  * into explicitly closed tags.
@@ -104405,8 +104511,9 @@ function terminateHtmlFlowBlocks(content) {
104405
104511
 
104406
104512
 
104407
104513
 
104408
- // Matches a JSX attribute expression (e.g. `key={i}`) anywhere in a string. */
104409
- const NESTED_ATTR_EXPRESSION_RE = /[\w-]+\s*=\s*\{/;
104514
+ // Matches a JSX attribute expression (e.g. `key={i}`) anywhere in a string. One name
104515
+ // char suffices for existence — `[\w-]+` backtracked quadratically over huge attributes.
104516
+ const NESTED_ATTR_EXPRESSION_RE = /[\w-]\s*=\s*\{/;
104410
104517
  // Name shape mirrors `componentTagPattern`; the lookbehind skips the inner tag
104411
104518
  // of a legacy `<<VARIABLE>>`.
104412
104519
  const NESTED_COMPONENT_TAG_RE = /(?<!<)<([A-Z][A-Za-z0-9_]*)[\s/>]/g;
@@ -104705,150 +104812,6 @@ const mdxishMdxComponentBlocks = (opts = {}) => (tree, file) => {
104705
104812
  };
104706
104813
  /* harmony default export */ const mdx_blocks = (mdxishMdxComponentBlocks);
104707
104814
 
104708
- ;// ./processor/transform/mdxish/components/self-closing-blocks.ts
104709
-
104710
-
104711
- /**
104712
- * Tags to process as self-closing blocks.
104713
- * These components use simple string attributes (no JSX expressions like `data={[...]}`).
104714
- * Components with JSX expression attributes should NOT be added here as parseAttributes
104715
- * cannot handle them correctly.
104716
- */
104717
- const SELF_CLOSING_BLOCK_TAGS = new Set(['Embed', 'Recipe']);
104718
- // Regex to match self-closing PascalCase tags (handles multi-line)
104719
- const selfClosingTagPattern = /^<([A-Z][A-Za-z0-9_]*)([\s\S]*?)\/>$/;
104720
- /**
104721
- * Try to convert a paragraph node containing a self-closing JSX component into an mdxJsxFlowElement.
104722
- * Returns the new node if conversion succeeded, or null if the node doesn't match.
104723
- */
104724
- const tryConvertToMdxNode = (node) => {
104725
- if (node.children.length !== 1)
104726
- return null;
104727
- const child = node.children[0];
104728
- if (child.type !== 'html')
104729
- return null;
104730
- const value = child.value?.trim();
104731
- if (!value)
104732
- return null;
104733
- const match = value.match(selfClosingTagPattern);
104734
- if (!match)
104735
- return null;
104736
- const [, tag, attrString] = match;
104737
- if (!SELF_CLOSING_BLOCK_TAGS.has(tag))
104738
- return null;
104739
- return {
104740
- type: 'mdxJsxFlowElement',
104741
- name: tag,
104742
- attributes: parseAttributes(attrString),
104743
- children: [],
104744
- position: node.position,
104745
- };
104746
- };
104747
- /**
104748
- * Transform paragraph-wrapped self-closing JSX components into mdxJsxFlowElement nodes.
104749
- *
104750
- * CommonMark wraps multi-line JSX in paragraphs when the opening tag isn't complete
104751
- * on one line. This plugin detects these structures and unwraps them for components
104752
- * in the SELF_CLOSING_BLOCK_TAGS allowlist.
104753
- *
104754
- * Input structure:
104755
- * ```
104756
- * paragraph > html: "<Embed\n typeOfEmbed=\"youtube\"\n/>"
104757
- * ```
104758
- *
104759
- * Output structure:
104760
- * ```
104761
- * mdxJsxFlowElement: { name: "Embed", attributes: [...], children: [] }
104762
- * ```
104763
- */
104764
- const mdxishSelfClosingBlocks = () => tree => {
104765
- visit(tree, 'paragraph', (node, index, parent) => {
104766
- if (index === undefined || !parent)
104767
- return;
104768
- const mdxNode = tryConvertToMdxNode(node);
104769
- if (mdxNode) {
104770
- parent.children.splice(index, 1, mdxNode);
104771
- }
104772
- });
104773
- };
104774
- /* harmony default export */ const self_closing_blocks = (mdxishSelfClosingBlocks);
104775
-
104776
- ;// ./processor/transform/mdxish/components/snake-case-components.ts
104777
-
104778
-
104779
- /**
104780
- * Replaces snake_case component names with valid HTML placeholders.
104781
- * Required because remark-parse rejects tags with underscores.
104782
- * Example: `<Snake_case />` → `<MDXishSnakeCase0 />`
104783
- *
104784
- * Code blocks and inline code are protected and will not be transformed.
104785
- *
104786
- * @param content - The markdown content to process
104787
- * @param options - Options including knownComponents to filter by
104788
- */
104789
- function processSnakeCaseComponent(content, options = {}) {
104790
- const { knownComponents } = options;
104791
- // Early exit if no potential snake_case components
104792
- if (!/[A-Z][A-Za-z0-9]*_[A-Za-z0-9_]*/.test(content)) {
104793
- return { content, mapping: {} };
104794
- }
104795
- // Step 1: Extract code blocks to protect them from transformation
104796
- const { protectedCode, protectedContent } = protectCodeBlocks(content);
104797
- // Find the highest existing placeholder number to avoid collisions
104798
- // e.g., if content has <MDXishSnakeCase0 />, start counter from 1
104799
- const placeholderPattern = /MDXishSnakeCase(\d+)/g;
104800
- let startCounter = 0;
104801
- let placeholderMatch;
104802
- while ((placeholderMatch = placeholderPattern.exec(content)) !== null) {
104803
- const num = parseInt(placeholderMatch[1], 10);
104804
- if (num >= startCounter) {
104805
- startCounter = num + 1;
104806
- }
104807
- }
104808
- const mapping = {};
104809
- const reverseMap = new Map();
104810
- let counter = startCounter;
104811
- // Step 2: Transform snake_case components in non-code content
104812
- const processedContent = protectedContent.replace(componentTagPattern, (match, tagName, attrs, selfClosing) => {
104813
- if (!tagName.includes('_')) {
104814
- return match;
104815
- }
104816
- const isClosing = tagName.startsWith('/');
104817
- const cleanTagName = isClosing ? tagName.slice(1) : tagName;
104818
- // Only transform if it's a known component (or if no filter is provided)
104819
- if (knownComponents && !knownComponents.has(cleanTagName)) {
104820
- return match;
104821
- }
104822
- let placeholder = reverseMap.get(cleanTagName);
104823
- if (!placeholder) {
104824
- // eslint-disable-next-line no-plusplus
104825
- placeholder = `MDXishSnakeCase${counter++}`;
104826
- mapping[placeholder] = cleanTagName;
104827
- reverseMap.set(cleanTagName, placeholder);
104828
- }
104829
- const processedTagName = isClosing ? `/${placeholder}` : placeholder;
104830
- return `<${processedTagName}${attrs}${selfClosing}>`;
104831
- });
104832
- // Step 3: Restore code blocks (untouched)
104833
- const finalContent = restoreCodeBlocks(processedContent, protectedCode);
104834
- return {
104835
- content: finalContent,
104836
- mapping,
104837
- };
104838
- }
104839
- /**
104840
- * Restores placeholder name to original snake_case name.
104841
- * Uses case-insensitive matching since HTML parsers normalize to lowercase.
104842
- */
104843
- function restoreSnakeCase(placeholderName, mapping) {
104844
- if (mapping[placeholderName]) {
104845
- return mapping[placeholderName];
104846
- }
104847
- const lowerName = placeholderName.toLowerCase();
104848
- const matchingKey = Object.keys(mapping).find(key => key.toLowerCase() === lowerName);
104849
- return matchingKey ? mapping[matchingKey] : placeholderName;
104850
- }
104851
-
104852
104815
  ;// ./processor/transform/mdxish/resolve-esm-imports.ts
104853
104816
 
104854
104817
  // We provide React as a default module so that components can use hooks
@@ -105033,6 +104996,54 @@ const containsJsxNode = (value) => {
105033
104996
  return true;
105034
104997
  return Object.values(value).some(containsJsxNode);
105035
104998
  };
104999
+ /** Read the component name off a JSX element name node (`Foo`, `Foo.Bar`, `foo:Bar`). */
105000
+ const jsxElementName = (name) => {
105001
+ if (name === null || typeof name !== 'object')
105002
+ return undefined;
105003
+ const node = name;
105004
+ if (node.type === 'JSXIdentifier')
105005
+ return typeof node.name === 'string' ? node.name : undefined;
105006
+ // `<Foo.Bar/>` and `<foo:Bar/>` resolve through their leftmost part.
105007
+ if (node.type === 'JSXMemberExpression')
105008
+ return jsxElementName(node.object);
105009
+ if (node.type === 'JSXNamespacedName')
105010
+ return jsxElementName(node.namespace);
105011
+ return undefined;
105012
+ };
105013
+ /**
105014
+ * Collect the capitalized names an expression uses as JSX tags. Parsed rather than pattern
105015
+ * matched: `{count < Max ? <Foo/> : <Bar/>}` puts a capitalized name straight after a `<` without
105016
+ * it being a tag, and only the parser can tell the two apart. Unparseable input yields nothing —
105017
+ * evaluation is about to throw on it anyway.
105018
+ */
105019
+ const jsxComponentNames = (expression) => {
105020
+ let program;
105021
+ try {
105022
+ program = parseExpression(expression);
105023
+ }
105024
+ catch {
105025
+ return [];
105026
+ }
105027
+ const names = new Set();
105028
+ const walk = (value) => {
105029
+ if (Array.isArray(value)) {
105030
+ value.forEach(walk);
105031
+ return;
105032
+ }
105033
+ if (value === null || typeof value !== 'object')
105034
+ return;
105035
+ const node = value;
105036
+ if (node.type === 'JSXOpeningElement') {
105037
+ const name = jsxElementName(node.name);
105038
+ // Lowercase tags compile to a string type, never a variable reference.
105039
+ if (name && /^[A-Z]/.test(name))
105040
+ names.add(name);
105041
+ }
105042
+ Object.values(node).forEach(walk);
105043
+ };
105044
+ walk(program);
105045
+ return Array.from(names);
105046
+ };
105036
105047
  /** Convert a program's JSX into `React.createElement` calls and evaluate it. `scope` must provide `React`. */
105037
105048
  const evalJsxProgram = (program, scope) => {
105038
105049
  buildJsx(program, { runtime: 'classic', pragma: 'React.createElement', pragmaFrag: 'React.Fragment' });
@@ -105238,6 +105249,28 @@ function reactElementToHast(node) {
105238
105249
 
105239
105250
 
105240
105251
 
105252
+
105253
+
105254
+
105255
+
105256
+ /**
105257
+ * Bind the components a given expression actually uses as tags. Scoped per expression on purpose:
105258
+ * binding the whole hash would shadow same-named globals (a `math` component would break
105259
+ * `{Math.max(1, 2)}`) and pad `evaluate`'s `new Function` parameter list. Resolution defers to
105260
+ * `getComponentName` so an expression and a plain tag always reach the same component.
105261
+ */
105262
+ const componentScope = (expression, components) => {
105263
+ const scope = {};
105264
+ jsxComponentNames(expression).forEach(name => {
105265
+ // `getComponentName` normalizes the tag, never the key, so it can't match `<MyBlock/>` to a
105266
+ // `my_block` entry; compare the key's PascalCase form for that direction.
105267
+ const tagName = getComponentName(name, components) ?? Object.keys(components).find(k => toPascalCase(k) === name);
105268
+ if (!tagName)
105269
+ return;
105270
+ scope[name] = (props) => external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_default().createElement(tagName, props);
105271
+ });
105272
+ return scope;
105273
+ };
105241
105274
  /**
105242
105275
  * We divide the result of an expression into two categories:
105243
105276
  * 1. Renderable values: HTML, JSX, e.g. .map() returning JSX
@@ -105248,6 +105281,17 @@ const isRenderable = (value) => {
105248
105281
  return true;
105249
105282
  return Array.isArray(value) && value.some(isRenderable);
105250
105283
  };
105284
+ /**
105285
+ * Whether an expression evaluated to block-level content. Components count as block-level
105286
+ * unless they're on the inline list, matching the assumption the rest of the component
105287
+ * pipeline makes.
105288
+ */
105289
+ const isBlockResult = (children) => children.some(child => {
105290
+ if (child.type !== 'element' && child.type !== 'mdx-jsx')
105291
+ return false;
105292
+ const { tagName } = child;
105293
+ return !STANDARD_HTML_TAGS.has(tagName.toLowerCase()) && !INLINE_COMPONENT_TAGS.has(tagName);
105294
+ });
105251
105295
  /** Turn a non-renderable evaluation result into a text node. */
105252
105296
  const createTextNode = (result, position) => {
105253
105297
  if (result === null || result === undefined)
@@ -105259,13 +105303,22 @@ const createTextNode = (result, position) => {
105259
105303
  /**
105260
105304
  * AST transformer to evaluate MDX expressions.
105261
105305
  * Replaces mdxFlowExpression and mdxTextExpression nodes with their evaluated values.
105262
- * Self-contained expressions resolve directly (e.g. `{1+1}`); expressions that
105263
- * reference identifiers can resolve if those identifiers were introduced by an
105264
- * earlier `export const/function` (collected onto `file.data.mdxishScope`).
105265
- * Anything else falls through to the error branch and is kept as literal `{...}` text.
105266
- */
105267
- const evaluateExpressions = () => (tree, file) => {
105268
- const scope = { ...(file.data.mdxishScope), React: (external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_default()) };
105306
+ * Self-contained expressions resolve directly (e.g. `{1+1}`); expressions that reference
105307
+ * identifiers can resolve if those identifiers are a custom component, the `user` variables
105308
+ * object, or were introduced by an earlier `export const/function` (collected onto
105309
+ * `file.data.mdxishScope`). Anything else falls through to the error branch and is kept as
105310
+ * literal `{...}` text.
105311
+ */
105312
+ const evaluateExpressions = ({ components, variables } = {}) => (tree, file) => {
105313
+ const baseScope = {
105314
+ // `User` matches the fallback the MDX path binds in `run.tsx`. Only when variables were
105315
+ // supplied: the proxy never throws, so an unconditional bind would resolve `user.*` on
105316
+ // surfaces that render without them instead of leaving literal text.
105317
+ ...(variables ? { user: user(variables) } : {}),
105318
+ // In-document exports win, matching `renderMdxish`.
105319
+ ...file.data.mdxishScope,
105320
+ React: (external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_default()),
105321
+ };
105269
105322
  visit(tree, ['mdxFlowExpression', 'mdxTextExpression'], (node, index, parent) => {
105270
105323
  if (!parent || index === null || index === undefined)
105271
105324
  return;
@@ -105275,6 +105328,7 @@ const evaluateExpressions = () => (tree, file) => {
105275
105328
  if (!expression)
105276
105329
  return;
105277
105330
  try {
105331
+ const scope = { ...componentScope(expression, components ?? {}), ...baseScope };
105278
105332
  const result = evalExpression(expression, scope);
105279
105333
  if (isRenderable(result)) {
105280
105334
  // Stash hast built straight from the React tree; `mdxExpressionHandler` emits it and it
@@ -105295,6 +105349,22 @@ const evaluateExpressions = () => (tree, file) => {
105295
105349
  parent.children.splice(index, 1, { type: 'text', value: `{${processed}}`, position });
105296
105350
  }
105297
105351
  });
105352
+ // A text expression is parsed inside a paragraph, but its result can be block content: a
105353
+ // `<Tabs>` renders a `<div>`, and a browser closes the `<p>` before it, so the DOM it builds
105354
+ // no longer matches what was rendered and hydration fails. Lift the expression out when
105355
+ // that's all the paragraph holds.
105356
+ visit(tree, 'paragraph', (node, index, parent) => {
105357
+ if (!parent || index === null || index === undefined)
105358
+ return;
105359
+ const meaningful = node.children.filter(child => !(child.type === 'text' && !child.value.trim()));
105360
+ const [only] = meaningful;
105361
+ if (meaningful.length !== 1 || only.type !== 'mdxTextExpression')
105362
+ return;
105363
+ const hChildren = only.data?.hChildren;
105364
+ if (!hChildren || !isBlockResult(hChildren))
105365
+ return;
105366
+ parent.children.splice(index, 1, only);
105367
+ });
105298
105368
  return tree;
105299
105369
  };
105300
105370
  /* harmony default export */ const evaluate_expressions = (evaluateExpressions);
@@ -107102,45 +107172,6 @@ const resolveDeferredAttributeExpressionProps = () => (tree, file) => {
107102
107172
  };
107103
107173
  /* harmony default export */ const resolve_deferred_attribute_expression_props = (resolveDeferredAttributeExpressionProps);
107104
107174
 
107105
- ;// ./processor/transform/mdxish/restore-snake-case-component-name.ts
107106
-
107107
-
107108
- /**
107109
- * Restores snake_case component names from placeholders after parsing.
107110
- * Runs after mdxishComponentBlocks converts HTML nodes to mdxJsxFlowElement.
107111
- */
107112
- const restoreSnakeCaseComponentNames = (options) => {
107113
- const { mapping } = options;
107114
- return tree => {
107115
- if (!mapping || Object.keys(mapping).length === 0) {
107116
- return tree;
107117
- }
107118
- visit(tree, 'mdxJsxFlowElement', (node) => {
107119
- if (node.name) {
107120
- node.name = restoreSnakeCase(node.name, mapping);
107121
- }
107122
- });
107123
- // Pre-compile regex patterns for better performance
107124
- const regexPatterns = Object.entries(mapping).map(([placeholder, original]) => ({
107125
- regex: new RegExp(`(<\\/?)(${placeholder})(\\s|\\/?>)`, 'gi'),
107126
- original,
107127
- }));
107128
- visit(tree, 'html', (node) => {
107129
- if (node.value) {
107130
- let newValue = node.value;
107131
- regexPatterns.forEach(({ regex, original }) => {
107132
- newValue = newValue.replace(regex, `$1${original}$3`);
107133
- });
107134
- if (newValue !== node.value) {
107135
- node.value = newValue;
107136
- }
107137
- }
107138
- });
107139
- return tree;
107140
- };
107141
- };
107142
- /* harmony default export */ const restore_snake_case_component_name = (restoreSnakeCaseComponentNames);
107143
-
107144
107175
  ;// ./processor/transform/mdxish/retain-boolean-attributes.ts
107145
107176
 
107146
107177
  // Private Use Area character (U+E000) which is extremely unlikely to appear in real content.
@@ -107682,9 +107713,6 @@ function loadComponents() {
107682
107713
 
107683
107714
 
107684
107715
 
107685
-
107686
-
107687
-
107688
107716
 
107689
107717
 
107690
107718
 
@@ -107706,10 +107734,8 @@ const defaultTransformers = [
107706
107734
  * 5. Terminate HTML flow blocks so subsequent content isn't swallowed
107707
107735
  * 6. Close invalid "self-closing" HTML tags (e.g., `<i />` → `<i></i>`)
107708
107736
  * 7. Normalize compact ATX headings (e.g., `#Heading` → `# Heading`)
107709
- * 8. Replace snake_case component names with parser-safe placeholders
107710
107737
  */
107711
- function preprocessContent(content, opts) {
107712
- const { knownComponents } = opts;
107738
+ function preprocessContent(content) {
107713
107739
  // Runs first so `jsxTable` sees a literal `</table>` (and the HTML-line
107714
107740
  // classification in `terminateHtmlFlowBlocks` is accurate)
107715
107741
  let result = normalizeClosingTagWhitespace(content);
@@ -107722,7 +107748,7 @@ function preprocessContent(content, opts) {
107722
107748
  result = terminateHtmlFlowBlocks(result);
107723
107749
  result = closeSelfClosingHtmlTags(result);
107724
107750
  result = normalizeCompactHeadings(result);
107725
- return processSnakeCaseComponent(result, { knownComponents });
107751
+ return result;
107726
107752
  }
107727
107753
  function mdxishAstProcessor(mdContent, opts = {}) {
107728
107754
  const { components: userComponents = {}, hardBreaks: enableHardBreaks = true, newEditorTypes = false, safeMode = false, useTailwind, } = opts;
@@ -107730,9 +107756,7 @@ function mdxishAstProcessor(mdContent, opts = {}) {
107730
107756
  ...loadComponents(),
107731
107757
  ...userComponents,
107732
107758
  };
107733
- // Build set of known component names for snake_case filtering
107734
- const knownComponents = new Set(Object.keys(components));
107735
- const { content: parserReadyContent, mapping: snakeCaseMapping } = preprocessContent(mdContent, { knownComponents });
107759
+ const parserReadyContent = preprocessContent(mdContent);
107736
107760
  // Create string map for tailwind transformer
107737
107761
  const tempComponentsMap = Object.entries(components).reduce((acc, [key, value]) => {
107738
107762
  acc[key] = String(value);
@@ -107745,10 +107769,8 @@ function mdxishAstProcessor(mdContent, opts = {}) {
107745
107769
  .use(remarkParse)
107746
107770
  .use(remarkFrontmatter)
107747
107771
  .use(normalize_malformed_md_syntax)
107748
- .use(self_closing_blocks)
107749
107772
  .use(mdx_blocks, { safeMode })
107750
107773
  .use(inline_html, { safeMode })
107751
- .use(restore_snake_case_component_name, { mapping: snakeCaseMapping })
107752
107774
  .use(mdxish_tables)
107753
107775
  .use(mdxish_html_blocks) // Convert every <HTMLBlock> shape → html-block
107754
107776
  // The next few transformers must appear after mdxishMdxComponentBlocks
@@ -107824,7 +107846,7 @@ function mdxish(mdContent, opts = {}) {
107824
107846
  processor
107825
107847
  .use(safeMode ? undefined : evaluate_exports) // Evaluate `export const/function` and stash scope on file.data.mdxishScope
107826
107848
  .use(enableHardBreaks ? hard_breaks : undefined) // Must precede evaluateExpressions to avoid splitting the \n in an evaluated template literal into a <br> node
107827
- .use(safeMode ? undefined : evaluate_expressions) // Evaluate self-contained MDX expressions (e.g. `{1+1}`)
107849
+ .use(safeMode ? undefined : evaluate_expressions, { components, variables }) // Evaluate self-contained MDX expressions (e.g. `{1+1}`)
107828
107850
  .use(safeMode ? undefined : evaluate_style_block_expressions) // Evaluate `<style>{`...`}</style>` template literals into plain CSS
107829
107851
  .use(variables_code, { variables }) // Resolve <<...>> and {user.*} inside code and inline code nodes
107830
107852
  .use(remarkRehype, { allowDangerousHtml: true, handlers: mdxComponentHandlers })
@@ -108811,7 +108833,7 @@ module.exports = /*#__PURE__*/JSON.parse('{"*":["about","acceptCharset","accessK
108811
108833
  /******/ // startup
108812
108834
  /******/ // Load entry module and return exports
108813
108835
  /******/ // This entry module used 'module' so it can't be inlined
108814
- /******/ let __webpack_exports__ = __webpack_require__(5168);
108836
+ /******/ let __webpack_exports__ = __webpack_require__(912);
108815
108837
  /******/
108816
108838
  /******/ return __webpack_exports__;
108817
108839
  /******/ })()