@readme/markdown 11.12.0 → 11.13.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.node.js CHANGED
@@ -19028,6 +19028,8 @@ __webpack_require__.d(__webpack_exports__, {
19028
19028
  mdastV6: () => (/* reexport */ lib_mdastV6),
19029
19029
  mdx: () => (/* reexport */ lib_mdx),
19030
19030
  mdxish: () => (/* reexport */ lib_mdxish),
19031
+ mdxishAstProcessor: () => (/* reexport */ mdxishAstProcessor),
19032
+ mdxishMdastToMd: () => (/* reexport */ mdxishMdastToMd),
19031
19033
  mdxishTags: () => (/* reexport */ mdxishTags),
19032
19034
  migrate: () => (/* reexport */ lib_migrate),
19033
19035
  mix: () => (/* reexport */ lib_mix),
@@ -19175,9 +19177,17 @@ function docLink(href) {
19175
19177
  function Anchor(props) {
19176
19178
  const { children, href = '', target = '', title = '', ...attrs } = props;
19177
19179
  const baseUrl = (0,external_react_.useContext)(BaseUrl);
19180
+ // Unwrap any nested anchor elements that GFM's autolinker may have created.
19181
+ // This prevents invalid nested <a> tags when the Anchor's text content looks like a URL.
19182
+ const unwrappedChildren = external_react_default().Children.map(children, child => {
19183
+ if (external_react_default().isValidElement(child) && child.type === 'a') {
19184
+ return child.props.children;
19185
+ }
19186
+ return child;
19187
+ });
19178
19188
  return (
19179
19189
  // eslint-disable-next-line react/jsx-props-no-spreading
19180
- external_react_default().createElement("a", { ...attrs, href: getHref(href, baseUrl), target: target, title: title, ...docLink(href) }, children));
19190
+ external_react_default().createElement("a", { ...attrs, href: getHref(href, baseUrl), target: target, title: title, ...docLink(href) }, unwrappedChildren));
19181
19191
  }
19182
19192
  /* harmony default export */ const components_Anchor = (Anchor);
19183
19193
 
@@ -19315,7 +19325,8 @@ const Code = (props) => {
19315
19325
  const theme = (0,external_react_.useContext)(Theme);
19316
19326
  const copyButtons = (0,external_react_.useContext)(CodeOpts) || props.copyButtons;
19317
19327
  const isHydrated = useHydrated();
19318
- const language = isHydrated ? canonicalLanguage(lang) : '';
19328
+ const language = canonicalLanguage(lang);
19329
+ const isMermaid = language === 'mermaid';
19319
19330
  const codeRef = (0,external_react_.createRef)();
19320
19331
  const codeOpts = {
19321
19332
  inline: !lang,
@@ -19326,7 +19337,7 @@ const Code = (props) => {
19326
19337
  const highlightedCode = syntaxHighlighter && typeof syntaxHighlighter === 'function' && code && isHydrated
19327
19338
  ? syntaxHighlighter(code, language, codeOpts, { mdx: true })
19328
19339
  : code;
19329
- if (language === 'mermaid') {
19340
+ if (isHydrated && isMermaid) {
19330
19341
  return code;
19331
19342
  }
19332
19343
  return (external_react_default().createElement((external_react_default()).Fragment, null,
@@ -19342,15 +19353,28 @@ var dist_utils_default = /*#__PURE__*/__webpack_require__.n(dist_utils);
19342
19353
 
19343
19354
 
19344
19355
 
19356
+
19345
19357
  let mermaid;
19346
19358
  const { uppercase } = (dist_utils_default());
19347
19359
  const CodeTabs = (props) => {
19348
19360
  const { children } = props;
19349
19361
  const theme = (0,external_react_.useContext)(Theme);
19350
- const hasMermaid = !Array.isArray(children) && children.props?.children.props.lang === 'mermaid';
19351
- // render Mermaid diagram
19362
+ const isHydrated = useHydrated();
19363
+ // Handle both array (from rehype-react in rendering mdxish) and single element (MDX/JSX runtime) cases
19364
+ // The children here is the individual code block objects
19365
+ const childrenArray = Array.isArray(children) ? children : [children];
19366
+ // The structure varies depending on rendering context:
19367
+ // - When rendered via rehype-react: pre.props.children is an array where the first element is the Code component
19368
+ // - When rendered via MDX/JSX runtime: pre.props.children is directly the Code component
19369
+ const getCodeComponent = (pre) => {
19370
+ return Array.isArray(pre?.props?.children) ? pre.props.children[0] : pre?.props?.children;
19371
+ };
19372
+ const containAtLeastOneMermaid = childrenArray.some(pre => getCodeComponent(pre)?.props?.lang === 'mermaid');
19373
+ // Render Mermaid diagram
19352
19374
  (0,external_react_.useEffect)(() => {
19353
- if (typeof window !== 'undefined' && hasMermaid) {
19375
+ // Ensure we only render mermaids when frontend is hydrated to avoid hydration errors
19376
+ // because mermaid mutates the DOM before react hydrates
19377
+ if (typeof window !== 'undefined' && containAtLeastOneMermaid && isHydrated) {
19354
19378
  __webpack_require__.e(/* import() */ 486).then(__webpack_require__.bind(__webpack_require__, 5486)).then(module => {
19355
19379
  mermaid = module.default;
19356
19380
  mermaid.initialize({
@@ -19362,7 +19386,7 @@ const CodeTabs = (props) => {
19362
19386
  });
19363
19387
  });
19364
19388
  }
19365
- }, [hasMermaid, theme]);
19389
+ }, [containAtLeastOneMermaid, theme, isHydrated]);
19366
19390
  function handleClick({ target }, index) {
19367
19391
  const $wrap = target.parentElement.parentElement;
19368
19392
  const $open = [].slice.call($wrap.querySelectorAll('.CodeTabs_active'));
@@ -19372,19 +19396,22 @@ const CodeTabs = (props) => {
19372
19396
  codeblocks[index].classList.add('CodeTabs_active');
19373
19397
  target.classList.add('CodeTabs_active');
19374
19398
  }
19375
- // render single Mermaid diagram
19376
- if (hasMermaid) {
19377
- const value = children.props.children.props.value;
19378
- return external_react_default().createElement("pre", { className: "mermaid-render mermaid_single" }, value);
19399
+ // We want to render single mermaid diagrams without the code tabs UI
19400
+ if (childrenArray.length === 1) {
19401
+ const codeComponent = getCodeComponent(childrenArray[0]);
19402
+ if (codeComponent?.props?.lang === 'mermaid') {
19403
+ const value = codeComponent?.props?.value;
19404
+ return external_react_default().createElement("pre", { className: "mermaid-render mermaid_single" }, value);
19405
+ }
19379
19406
  }
19380
19407
  return (external_react_default().createElement("div", { className: `CodeTabs CodeTabs_initial theme-${theme}` },
19381
- external_react_default().createElement("div", { className: "CodeTabs-toolbar" }, (Array.isArray(children) ? children : [children]).map((pre, i) => {
19408
+ external_react_default().createElement("div", { className: "CodeTabs-toolbar" }, childrenArray.map((pre, i) => {
19382
19409
  // the first or only child should be our Code component
19383
- const codeComponent = Array.isArray(pre.props?.children)
19410
+ const tabCodeComponent = Array.isArray(pre.props?.children)
19384
19411
  ? pre.props.children[0]
19385
19412
  : pre.props?.children;
19386
- const lang = codeComponent?.props?.lang;
19387
- const meta = codeComponent?.props?.meta;
19413
+ const lang = tabCodeComponent?.props?.lang;
19414
+ const meta = tabCodeComponent?.props?.meta;
19388
19415
  /* istanbul ignore next */
19389
19416
  return (external_react_default().createElement("button", { key: i, onClick: e => handleClick(e, i), type: "button", value: lang }, meta || `${!lang ? 'Text' : uppercase(lang)}`));
19390
19417
  })),
@@ -90788,77 +90815,197 @@ const mdxToHast = () => tree => {
90788
90815
  ;// ./processor/transform/mdxish/mdxish-component-blocks.ts
90789
90816
 
90790
90817
 
90791
- const tagPattern = /^<([A-Z][A-Za-z0-9_]*)([^>]*?)(\/?)>([\s\S]*)?$/;
90792
- const attributePattern = /([a-zA-Z_:][-a-zA-Z0-9_:.]*)(?:\s*=\s*("[^"]*"|'[^']*'|[^\s"'>]+))?/g;
90818
+ const pascalCaseTagPattern = /^<([A-Z][A-Za-z0-9_]*)([^>]*?)(\/?)>([\s\S]*)?$/;
90819
+ const tagAttributePattern = /([a-zA-Z_:][-a-zA-Z0-9_:.]*)(?:\s*=\s*("[^"]*"|'[^']*'|[^\s"'>]+))?/g;
90820
+ /**
90821
+ * Maximum number of siblings to scan forward when looking for a closing tag
90822
+ * to avoid scanning too far and degrading performance
90823
+ */
90824
+ const MAX_LOOKAHEAD = 30;
90825
+ /**
90826
+ * Tags that have dedicated transformers and should NOT be handled by this plugin.
90827
+ * These components have special parsing requirements that the generic component
90828
+ * block transformer cannot handle correctly.
90829
+ */
90830
+ const EXCLUDED_TAGS = new Set(['HTMLBlock', 'Table']);
90793
90831
  const inlineMdProcessor = unified().use(remarkParse);
90794
90832
  const isClosingTag = (value, tag) => value.trim() === `</${tag}>`;
90795
- // Remove matching closing tag from paragraph children; returns updated paragraph and removal status
90796
- const stripClosingFromParagraph = (node, tag) => {
90797
- if (!Array.isArray(node.children))
90798
- return { paragraph: node, found: false };
90799
- const children = [...node.children];
90800
- const closingIndex = children.findIndex(child => child.type === 'html' && isClosingTag(child.value || '', tag));
90801
- if (closingIndex === -1)
90802
- return { paragraph: node, found: false };
90803
- children.splice(closingIndex, 1);
90804
- return {
90805
- paragraph: { ...node, children },
90806
- found: true,
90807
- };
90808
- };
90809
- // Replace two child nodes (opening HTML tag + paragraph) with a single replacement node
90810
- const replaceChild = (parent, index, replacement) => {
90811
- parent.children.splice(index, 2, replacement);
90812
- };
90813
- // Parse markdown inside a component's inline content into mdast children.
90833
+ /**
90834
+ * Parse markdown content into mdast children nodes.
90835
+ */
90814
90836
  const parseMdChildren = (value) => {
90815
90837
  const parsed = inlineMdProcessor.parse(value);
90816
90838
  return parsed.children || [];
90817
90839
  };
90818
- // Convert raw attribute string into mdxJsxAttribute entries (strings only; no expressions).
90819
- // Handles both key-value attributes (theme="info") and boolean attributes (empty).
90840
+ /**
90841
+ * Convert raw attribute string into mdxJsxAttribute entries.
90842
+ * Handles both key-value attributes (theme="info") and boolean attributes (empty).
90843
+ */
90820
90844
  const parseAttributes = (raw) => {
90821
90845
  const attributes = [];
90822
90846
  const attrString = raw.trim();
90823
90847
  if (!attrString)
90824
90848
  return attributes;
90825
- // Reset regex lastIndex since it's a global regex that maintains state
90826
- attributePattern.lastIndex = 0;
90827
- let match = attributePattern.exec(attrString);
90849
+ tagAttributePattern.lastIndex = 0;
90850
+ let match = tagAttributePattern.exec(attrString);
90828
90851
  while (match !== null) {
90829
90852
  const [, attrName, attrValue] = match;
90830
- // Boolean attribute (no value) -> set to null
90831
- // Attribute with value -> clean and set string value
90832
- // Note: Attribute value types can't directly be numbers & booleans. String, nulls, undefined are supported.
90833
90853
  const value = attrValue ? attrValue.replace(/^['"]|['"]$/g, '') : null;
90834
- attributes.push({
90835
- type: 'mdxJsxAttribute',
90836
- name: attrName,
90837
- value,
90838
- });
90839
- match = attributePattern.exec(attrString);
90854
+ attributes.push({ type: 'mdxJsxAttribute', name: attrName, value });
90855
+ match = tagAttributePattern.exec(attrString);
90840
90856
  }
90841
90857
  return attributes;
90842
90858
  };
90843
- // Parse a single HTML-ish tag string into tag name, attributes, self-closing flag, and inline content.
90859
+ /**
90860
+ * Parse an HTML tag string into structured data.
90861
+ */
90844
90862
  const parseTag = (value) => {
90845
- const match = value.match(tagPattern);
90863
+ const match = value.match(pascalCaseTagPattern);
90846
90864
  if (!match)
90847
90865
  return null;
90848
- const [, tag, attrString = '', selfClosing = '', content = ''] = match;
90849
- const attributes = parseAttributes(attrString);
90866
+ const [, tag, attrString = '', selfClosing = '', contentAfterTag = ''] = match;
90850
90867
  return {
90851
90868
  tag,
90852
- attributes,
90869
+ attributes: parseAttributes(attrString),
90853
90870
  selfClosing: !!selfClosing,
90854
- content,
90871
+ contentAfterTag,
90855
90872
  };
90856
90873
  };
90857
- // Transform PascalCase HTML blocks into mdxJsxFlowElement nodes.
90858
- // Remark parses unknown tags as raw HTML; we rewrite them so MDX/rehype treats them as components.
90874
+ /**
90875
+ * Create an MdxJsxFlowElement node from component data.
90876
+ */
90877
+ const createComponentNode = ({ tag, attributes, children, startPosition, endPosition }) => ({
90878
+ type: 'mdxJsxFlowElement',
90879
+ name: tag,
90880
+ attributes,
90881
+ children,
90882
+ position: {
90883
+ start: startPosition?.start,
90884
+ end: endPosition?.end ?? startPosition?.end,
90885
+ },
90886
+ });
90887
+ /**
90888
+ * Remove a closing tag from a paragraph's children and return the updated paragraph.
90889
+ */
90890
+ const stripClosingTagFromParagraph = (node, tag) => {
90891
+ if (!Array.isArray(node.children))
90892
+ return { paragraph: node, found: false };
90893
+ const children = [...node.children];
90894
+ const closingIndex = children.findIndex(child => child.type === 'html' && isClosingTag(child.value || '', tag));
90895
+ if (closingIndex === -1)
90896
+ return { paragraph: node, found: false };
90897
+ children.splice(closingIndex, 1);
90898
+ return { paragraph: { ...node, children }, found: true };
90899
+ };
90900
+ /**
90901
+ * Scan forward through siblings to find a closing tag.
90902
+ * Handles:
90903
+ * - Exact match HTML siblings (e.g., `</Tag>`)
90904
+ * - HTML siblings with embedded closing tag (e.g., `...\n</Tag>`)
90905
+ * - Paragraph siblings containing the closing tag as a child
90906
+ *
90907
+ * Returns null if not found within MAX_LOOKAHEAD siblings
90908
+ */
90909
+ const scanForClosingTag = (parent, startIndex, tag) => {
90910
+ const closingTagStr = `</${tag}>`;
90911
+ const maxIndex = Math.min(startIndex + MAX_LOOKAHEAD, parent.children.length);
90912
+ let i = startIndex + 1;
90913
+ for (; i < maxIndex; i += 1) {
90914
+ const sibling = parent.children[i];
90915
+ // Check HTML siblings
90916
+ if (sibling.type === 'html') {
90917
+ const siblingValue = sibling.value || '';
90918
+ // Exact match (standalone closing tag)
90919
+ if (isClosingTag(siblingValue, tag)) {
90920
+ return { closingIndex: i, extraClosingChildren: [] };
90921
+ }
90922
+ // Embedded closing tag (closing tag at end of HTML block content)
90923
+ if (siblingValue.includes(closingTagStr)) {
90924
+ const contentBeforeClose = siblingValue.substring(0, siblingValue.lastIndexOf(closingTagStr)).trim();
90925
+ const extraChildren = contentBeforeClose
90926
+ ? parseMdChildren(contentBeforeClose)
90927
+ : [];
90928
+ return { closingIndex: i, extraClosingChildren: extraChildren };
90929
+ }
90930
+ }
90931
+ // Check paragraph siblings
90932
+ if (sibling.type === 'paragraph') {
90933
+ const { paragraph, found } = stripClosingTagFromParagraph(sibling, tag);
90934
+ if (found) {
90935
+ return { closingIndex: i, extraClosingChildren: [], strippedParagraph: paragraph };
90936
+ }
90937
+ }
90938
+ }
90939
+ if (i < parent.children.length) {
90940
+ // eslint-disable-next-line no-console
90941
+ console.warn(`Closing tag </${tag}> not found within ${MAX_LOOKAHEAD} siblings, stopping scan`);
90942
+ }
90943
+ return null;
90944
+ };
90945
+ const substituteNodeWithMdxNode = (parent, index, mdxNode) => {
90946
+ parent.children.splice(index, 1, mdxNode);
90947
+ };
90948
+ /**
90949
+ * Transform PascalCase HTML nodes into mdxJsxFlowElement nodes.
90950
+ *
90951
+ * Remark parses unknown/custom component tags as raw HTML nodes.
90952
+ * These are the custom readme MDX syntax for components.
90953
+ * This transformer identifies these patterns and converts them to proper MDX JSX elements so they
90954
+ * can be accurately recognized and rendered later with their component definition code.
90955
+ * Though for some tags, we need to handle them specially
90956
+ *
90957
+ * ## Supported HTML Structures
90958
+ *
90959
+ * ### 1. Self-closing tags
90960
+ * ```
90961
+ * <Component />
90962
+ * ```
90963
+ * Parsed as: `html: "<Component />"`
90964
+ *
90965
+ * ### 2. Self-contained blocks (entire component in single HTML node)
90966
+ * ```
90967
+ * <Button>Click me</Button>
90968
+ * ```
90969
+ * ```
90970
+ * <Component>
90971
+ * <h2>Title</h2>
90972
+ * <p>Content</p>
90973
+ * </Component>
90974
+ * ```
90975
+ * Parsed as: `html: "<Component>\n <h2>Title</h2>\n <p>Content</p>\n</Component>"`
90976
+ * The opening tag, content, and closing tag are all captured in one HTML node.
90977
+ *
90978
+ * ### 3. Multi-sibling components (closing tag in a following sibling)
90979
+ * Handles various structures where the closing tag is in a later sibling, such as:
90980
+ *
90981
+ * #### 3a. Block components (closing tag in sibling paragraph)
90982
+ * ```
90983
+ * <Callout>
90984
+ * Some **markdown** content
90985
+ * </Callout>
90986
+ * ```
90987
+ *
90988
+ * #### 3b. Multi-paragraph components (closing tag several siblings away)
90989
+ * ```
90990
+ * <Callout>
90991
+ *
90992
+ * First paragraph
90993
+ *
90994
+ * Second paragraph
90995
+ * </Callout>
90996
+ * ```
90997
+ *
90998
+ * #### 3c. Nested components split by blank lines (closing tag embedded in HTML sibling)
90999
+ * ```
91000
+ * <Outer>
91001
+ * <Inner>content</Inner>
91002
+ *
91003
+ * <Inner>content</Inner>
91004
+ * </Outer>
91005
+ * ```
91006
+ */
90859
91007
  const mdxishComponentBlocks = () => tree => {
90860
91008
  const stack = [tree];
90861
- // Process children depth-first, rewriting opening/closing component HTML pairs
90862
91009
  const processChildNode = (parent, index) => {
90863
91010
  const node = parent.children[index];
90864
91011
  if (!node)
@@ -90866,46 +91013,66 @@ const mdxishComponentBlocks = () => tree => {
90866
91013
  if ('children' in node && Array.isArray(node.children)) {
90867
91014
  stack.push(node);
90868
91015
  }
91016
+ // Only visit HTML nodes with an actual html tag
90869
91017
  const value = node.value;
90870
91018
  if (node.type !== 'html' || typeof value !== 'string')
90871
91019
  return;
90872
91020
  const parsed = parseTag(value);
90873
91021
  if (!parsed)
90874
91022
  return;
90875
- const { tag, attributes, selfClosing, content = '' } = parsed;
90876
- const extraChildren = content ? parseMdChildren(content.trimStart()) : [];
91023
+ const { tag, attributes, selfClosing, contentAfterTag = '' } = parsed;
91024
+ // Skip tags that have dedicated transformers
91025
+ if (EXCLUDED_TAGS.has(tag))
91026
+ return;
91027
+ const closingTagStr = `</${tag}>`;
91028
+ // Case 1: Self-closing tag
90877
91029
  if (selfClosing) {
90878
- const componentNode = {
90879
- type: 'mdxJsxFlowElement',
90880
- name: tag,
91030
+ const componentNode = createComponentNode({
91031
+ tag,
90881
91032
  attributes,
90882
91033
  children: [],
90883
- position: node.position,
90884
- };
90885
- parent.children.splice(index, 1, componentNode);
91034
+ startPosition: node.position,
91035
+ });
91036
+ substituteNodeWithMdxNode(parent, index, componentNode);
90886
91037
  return;
90887
91038
  }
90888
- const next = parent.children[index + 1];
90889
- if (!next || next.type !== 'paragraph')
91039
+ // Case 2: Self-contained block (closing tag in content)
91040
+ if (contentAfterTag.includes(closingTagStr)) {
91041
+ const componentInnerContent = contentAfterTag.substring(0, contentAfterTag.lastIndexOf(closingTagStr)).trim();
91042
+ const componentNode = createComponentNode({
91043
+ tag,
91044
+ attributes,
91045
+ children: componentInnerContent ? parseMdChildren(componentInnerContent) : [],
91046
+ startPosition: node.position,
91047
+ });
91048
+ substituteNodeWithMdxNode(parent, index, componentNode);
90890
91049
  return;
90891
- const { paragraph, found } = stripClosingFromParagraph(next, tag);
90892
- if (!found)
91050
+ }
91051
+ // Case 3: Multi-sibling component (closing tag in a following sibling)
91052
+ // Scans forward through siblings to find closing tag in HTML or paragraph nodes
91053
+ const scanResult = scanForClosingTag(parent, index, tag);
91054
+ if (!scanResult)
90893
91055
  return;
90894
- const componentNode = {
90895
- type: 'mdxJsxFlowElement',
90896
- name: tag,
91056
+ const { closingIndex, extraClosingChildren, strippedParagraph } = scanResult;
91057
+ const extraChildren = contentAfterTag ? parseMdChildren(contentAfterTag.trimStart()) : [];
91058
+ // Collect all intermediate siblings between opening tag and closing tag
91059
+ const intermediateChildren = parent.children.slice(index + 1, closingIndex);
91060
+ // For paragraph siblings, include the paragraph's children (with closing tag stripped)
91061
+ // For HTML siblings, include any content parsed from before the closing tag
91062
+ const closingChildren = strippedParagraph
91063
+ ? strippedParagraph.children
91064
+ : extraClosingChildren;
91065
+ const componentNode = createComponentNode({
91066
+ tag,
90897
91067
  attributes,
90898
- children: [
90899
- ...extraChildren,
90900
- ...paragraph.children,
90901
- ],
90902
- position: {
90903
- start: node.position?.start,
90904
- end: next.position?.end,
90905
- },
90906
- };
90907
- replaceChild(parent, index, componentNode);
91068
+ children: [...extraChildren, ...intermediateChildren, ...closingChildren],
91069
+ startPosition: node.position,
91070
+ endPosition: parent.children[closingIndex]?.position,
91071
+ });
91072
+ // Remove all nodes from opening tag to closing tag (inclusive) and replace with component node
91073
+ parent.children.splice(index, closingIndex - index + 1, componentNode);
90908
91074
  };
91075
+ // Travel the tree depth-first
90909
91076
  while (stack.length) {
90910
91077
  const parent = stack.pop();
90911
91078
  if (parent?.children) {
@@ -91278,10 +91445,20 @@ const coerceJsxToMd = ({ components = {}, html = false } = {}) => (node, index,
91278
91445
  hProperties.url = hProperties.href;
91279
91446
  delete hProperties.href;
91280
91447
  }
91448
+ // Unwrap any autolinked children to prevent nested links.
91449
+ // GFM's autolink feature can convert URL-like text inside Anchor children
91450
+ // into link nodes, which would create invalid nested links when Anchor
91451
+ // is converted back to a link node.
91452
+ const children = node.children.flatMap(child => {
91453
+ if (child.type === 'link') {
91454
+ return child.children;
91455
+ }
91456
+ return child;
91457
+ });
91281
91458
  // @ts-expect-error we don't have a mechanism to enforce the URL attribute type right now
91282
91459
  const mdNode = {
91283
91460
  ...hProperties,
91284
- children: node.children,
91461
+ children,
91285
91462
  type: readme_components_types[node.name],
91286
91463
  position: node.position,
91287
91464
  };
@@ -106570,16 +106747,17 @@ const tocHastToMdx = (toc, components) => {
106570
106747
 
106571
106748
 
106572
106749
 
106573
- const { codeTabsTransformer: compile_codeTabsTransformer, ...transforms } = defaultTransforms;
106574
106750
  const sanitizeSchema = cjs_default()(defaultSchema, {
106575
106751
  protocols: ['doc', 'ref', 'blog', 'changelog', 'page'],
106576
106752
  });
106577
106753
  const compile_compile = (text, { components = {}, missingComponents, copyButtons, useTailwind, ...opts } = {}) => {
106754
+ // Destructure at runtime to avoid circular dependency issues
106755
+ const { codeTabsTransformer, ...transforms } = defaultTransforms;
106578
106756
  const remarkPlugins = [
106579
106757
  remarkFrontmatter,
106580
106758
  remarkGfm,
106581
106759
  ...Object.values(transforms),
106582
- [compile_codeTabsTransformer, { copyButtons }],
106760
+ [codeTabsTransformer, { copyButtons }],
106583
106761
  [
106584
106762
  handle_missing_components,
106585
106763
  { components, missingComponents: ['ignore', 'throw'].includes(missingComponents) ? missingComponents : 'ignore' },
@@ -113049,8 +113227,11 @@ function smartCamelCase(str) {
113049
113227
  }
113050
113228
  // Sort by length (longest first) to prevent shorter matches (e.g., "column" in "columns")
113051
113229
  const sortedBoundaries = [...allBoundaries].sort((a, b) => b.length - a.length);
113230
+ // Use case-sensitive matching ('g' not 'gi') so that once a letter is
113231
+ // capitalized by a longer boundary, shorter boundaries won't re-match it.
113232
+ // This prevents issues like 'iconcolor' becoming 'iconColOr' instead of 'iconColor'.
113052
113233
  return sortedBoundaries.reduce((res, word) => {
113053
- const regex = new RegExp(`(${word})([a-z])`, 'gi');
113234
+ const regex = new RegExp(`(${word})([a-z])`, 'g');
113054
113235
  return res.replace(regex, (_, prefix, nextChar) => prefix.toLowerCase() + nextChar.toUpperCase());
113055
113236
  }, str);
113056
113237
  }
@@ -113176,7 +113357,21 @@ const htmlBlockHandler = (_state, node) => {
113176
113357
  children: [],
113177
113358
  };
113178
113359
  };
113360
+ // Convert embed magic blocks to Embed components
113361
+ const embedHandler = (state, node) => {
113362
+ // Assert to get the minimum properties we need
113363
+ const { data } = node;
113364
+ return {
113365
+ type: 'element',
113366
+ // To differentiate between regular embeds and magic block embeds,
113367
+ // magic block embeds have a certain hName
113368
+ tagName: data?.hName === NodeTypes.embedBlock ? 'Embed' : 'embed',
113369
+ properties: data?.hProperties,
113370
+ children: state.all(node),
113371
+ };
113372
+ };
113179
113373
  const mdxComponentHandlers = {
113374
+ embed: embedHandler,
113180
113375
  mdxFlowExpression: mdxExpressionHandler,
113181
113376
  mdxJsxFlowElement: mdxJsxElementHandler,
113182
113377
  mdxJsxTextElement: mdxJsxElementHandler,
@@ -113840,8 +114035,6 @@ const mdxishHtmlBlocks = () => tree => {
113840
114035
  * Taken from the v6 branch
113841
114036
  */
113842
114037
  const RGXP = /^\s*\[block:([^\]]*)\]([^]+?)\[\/block\]/;
113843
- /** Parses markdown in table cells */
113844
- const cellParser = unified().use(remarkParse).use(remarkGfm);
113845
114038
  /**
113846
114039
  * Wraps a node in a "pinned" container if sidebar: true is set in the JSON.
113847
114040
  * Pinned blocks are displayed in a sidebar/floating position in the UI.
@@ -113875,11 +114068,13 @@ const imgWidthBySize = new Proxy(imgSizeValues, {
113875
114068
  const textToInline = (text) => [{ type: 'text', value: text }];
113876
114069
  // Simple text to block nodes (wraps in paragraph)
113877
114070
  const textToBlock = (text) => [{ children: textToInline(text), type: 'paragraph' }];
114071
+ /** Parses markdown and html to markdown nodes */
114072
+ const contentParser = unified().use(remarkParse).use(remarkGfm);
113878
114073
  // Table cells may contain html or markdown content, so we need to parse it accordingly instead of keeping it as raw text
113879
- const parseInline = (text) => {
114074
+ const parseTableCell = (text) => {
113880
114075
  if (!text.trim())
113881
114076
  return [{ type: 'text', value: '' }];
113882
- const tree = cellParser.runSync(cellParser.parse(text));
114077
+ const tree = contentParser.runSync(contentParser.parse(text));
113883
114078
  // If there are multiple block-level nodes, keep them as-is to preserve the document structure and spacing
113884
114079
  if (tree.children.length > 1) {
113885
114080
  return tree.children;
@@ -113888,6 +114083,13 @@ const parseInline = (text) => {
113888
114083
  // This unwraps the extra p node that might appear & wrapping the content
113889
114084
  n.type === 'paragraph' && 'children' in n ? n.children : [n]);
113890
114085
  };
114086
+ // Parse markdown/HTML into block-level nodes (preserves paragraphs, headings, lists, etc.)
114087
+ const parseBlock = (text) => {
114088
+ if (!text.trim())
114089
+ return [{ type: 'paragraph', children: [{ type: 'text', value: '' }] }];
114090
+ const tree = contentParser.runSync(contentParser.parse(text));
114091
+ return tree.children;
114092
+ };
113891
114093
  /**
113892
114094
  * Parse a magic block string and return MDAST nodes.
113893
114095
  *
@@ -113966,7 +114168,7 @@ function parseMagicBlock(raw, options = {}) {
113966
114168
  data: {
113967
114169
  hProperties: {
113968
114170
  ...(imgData.align && { align: imgData.align }),
113969
- className: imgData.border ? 'border' : '',
114171
+ ...(imgData.border && { border: imgData.border.toString() }),
113970
114172
  ...(imgData.sizing && { width: imgWidthBySize[imgData.sizing] }),
113971
114173
  },
113972
114174
  },
@@ -114005,8 +114207,9 @@ function parseMagicBlock(raw, options = {}) {
114005
114207
  const [icon, theme] = Array.isArray(resolvedType) ? resolvedType : ['👍', 'default'];
114006
114208
  if (!(calloutJson.title || calloutJson.body))
114007
114209
  return [];
114008
- const titleBlocks = textToBlock(calloutJson.title || '');
114009
- const bodyBlocks = textToBlock(calloutJson.body || '');
114210
+ // Parses html & markdown content
114211
+ const titleBlocks = parseBlock(calloutJson.title || '');
114212
+ const bodyBlocks = parseBlock(calloutJson.body || '');
114010
114213
  const children = [];
114011
114214
  if (titleBlocks.length > 0 && titleBlocks[0].type === 'paragraph') {
114012
114215
  const firstTitle = titleBlocks[0];
@@ -114021,14 +114224,17 @@ function parseMagicBlock(raw, options = {}) {
114021
114224
  else {
114022
114225
  children.push(...titleBlocks, ...bodyBlocks);
114023
114226
  }
114227
+ // If there is no title or title is empty
114228
+ const empty = !titleBlocks.length || !titleBlocks[0].children[0]?.value;
114024
114229
  // Create mdxJsxFlowElement directly for mdxish
114025
114230
  const calloutElement = {
114026
114231
  type: 'mdxJsxFlowElement',
114027
114232
  name: 'Callout',
114028
- attributes: toAttributes({ icon, theme: theme || 'default', type: theme || 'default' }, [
114233
+ attributes: toAttributes({ icon, theme: theme || 'default', type: theme || 'default', empty }, [
114029
114234
  'icon',
114030
114235
  'theme',
114031
114236
  'type',
114237
+ 'empty',
114032
114238
  ]),
114033
114239
  children: children,
114034
114240
  };
@@ -114057,7 +114263,7 @@ function parseMagicBlock(raw, options = {}) {
114057
114263
  return mapped;
114058
114264
  }, []);
114059
114265
  // In compatibility mode, wrap cell content in paragraphs; otherwise inline text
114060
- const tokenizeCell = compatibilityMode ? textToBlock : parseInline;
114266
+ const tokenizeCell = compatibilityMode ? textToBlock : parseTableCell;
114061
114267
  const children = Array.from({ length: rows + 1 }, (_, y) => ({
114062
114268
  children: Array.from({ length: cols }, (__, x) => ({
114063
114269
  children: sparseData[y]?.[x] ? tokenizeCell(sparseData[y][x]) : [{ type: 'text', value: '' }],
@@ -114086,9 +114292,9 @@ function parseMagicBlock(raw, options = {}) {
114086
114292
  return [
114087
114293
  wrapPinnedBlocks({
114088
114294
  children: [
114089
- { children: [{ type: 'text', value: title || null }], title: embedJson.provider, type: 'link', url },
114295
+ { children: [{ type: 'text', value: title || '' }], title: embedJson.provider, type: 'link', url },
114090
114296
  ],
114091
- data: { hName: 'rdme-embed', hProperties: { ...embedJson, href: url, html, title, url } },
114297
+ data: { hName: 'embed-block', hProperties: { ...embedJson, href: url, html, title, url } },
114092
114298
  type: 'embed',
114093
114299
  }, json),
114094
114300
  ];
@@ -114191,6 +114397,38 @@ const magicBlockRestorer = ({ blocks }) => tree => {
114191
114397
  };
114192
114398
  /* harmony default export */ const mdxish_magic_blocks = (magicBlockRestorer);
114193
114399
 
114400
+ ;// ./processor/transform/mdxish/mdxish-mermaid.ts
114401
+
114402
+ /**
114403
+ * Rehype plugin for mdxish pipeline to add mermaid-render className to mermaid code blocks.
114404
+ * The mermaid-render class is used to identify the mermaid diagrams elements for the
114405
+ * mermaid library to transform. See components/CodeTabs/index.tsx for context
114406
+ */
114407
+ const mdxishMermaidTransformer = () => (tree) => {
114408
+ visit(tree, 'element', (node) => {
114409
+ if (node.tagName !== 'pre' || node.children.length !== 1)
114410
+ return;
114411
+ const [child] = node.children;
114412
+ if (child.type === 'element' &&
114413
+ child.tagName === 'code' &&
114414
+ child.properties?.lang === 'mermaid') {
114415
+ // Combine existing className with the new mermaid-render class
114416
+ const existingClassName = node.properties?.className;
114417
+ const classNameArray = Array.isArray(existingClassName)
114418
+ ? existingClassName.filter(c => typeof c === 'string' || typeof c === 'number')
114419
+ : existingClassName && (typeof existingClassName === 'string' || typeof existingClassName === 'number')
114420
+ ? [existingClassName]
114421
+ : [];
114422
+ node.properties = {
114423
+ ...node.properties,
114424
+ className: ['mermaid-render', ...classNameArray],
114425
+ };
114426
+ }
114427
+ });
114428
+ return tree;
114429
+ };
114430
+ /* harmony default export */ const mdxish_mermaid = (mdxishMermaidTransformer);
114431
+
114194
114432
  ;// ./lib/constants.ts
114195
114433
  /**
114196
114434
  * Pattern to match component tags (PascalCase or snake_case)
@@ -114418,6 +114656,45 @@ const restoreSnakeCaseComponentNames = (options) => {
114418
114656
  };
114419
114657
  /* harmony default export */ const restore_snake_case_component_name = (restoreSnakeCaseComponentNames);
114420
114658
 
114659
+ ;// ./processor/transform/mdxish/retain-boolean-attributes.ts
114660
+
114661
+ // Private Use Area character (U+E000) which is extremely unlikely to appear in real content.
114662
+ const TEMP_TRUE_BOOLEAN_VALUE = 'readme-this-is-a-temporary-boolean-attribute-\uE000';
114663
+ const TEMP_FALSE_BOOLEAN_VALUE = 'readme-this-is-a-temporary-boolean-attribute-\uE001';
114664
+ /**
114665
+ * Preserves boolean properties when passed to rehypeRaw because
114666
+ * rehypeRaw converts boolean properties in nodes to strings (e.g. true -> ""),
114667
+ * which can change the truthiness of the property. Hence we need to preserve the boolean properties.
114668
+ */
114669
+ const preserveBooleanProperties = () => tree => {
114670
+ visit(tree, 'element', (node) => {
114671
+ if (!node.properties)
114672
+ return;
114673
+ Object.entries(node.properties).forEach(([key, value]) => {
114674
+ if (typeof value === 'boolean') {
114675
+ node.properties[key] = value ? TEMP_TRUE_BOOLEAN_VALUE : TEMP_FALSE_BOOLEAN_VALUE;
114676
+ }
114677
+ });
114678
+ });
114679
+ return tree;
114680
+ };
114681
+ const restoreBooleanProperties = () => tree => {
114682
+ visit(tree, 'element', (node) => {
114683
+ if (!node.properties)
114684
+ return;
114685
+ Object.entries(node.properties).forEach(([key, value]) => {
114686
+ if (value === TEMP_TRUE_BOOLEAN_VALUE) {
114687
+ node.properties[key] = true;
114688
+ }
114689
+ else if (value === TEMP_FALSE_BOOLEAN_VALUE) {
114690
+ node.properties[key] = false;
114691
+ }
114692
+ });
114693
+ });
114694
+ return tree;
114695
+ };
114696
+
114697
+
114421
114698
  ;// ./processor/transform/mdxish/variables-text.ts
114422
114699
 
114423
114700
 
@@ -114445,6 +114722,8 @@ const variablesTextTransformer = () => tree => {
114445
114722
  if (parent.type === 'inlineCode')
114446
114723
  return;
114447
114724
  const text = node.value;
114725
+ if (typeof text !== 'string' || !text.trim())
114726
+ return;
114448
114727
  if (!text.includes('{user.') && !text.includes('{user['))
114449
114728
  return;
114450
114729
  const matches = [...text.matchAll(USER_VAR_REGEX)];
@@ -114594,19 +114873,17 @@ function loadComponents() {
114594
114873
 
114595
114874
 
114596
114875
 
114876
+
114877
+
114878
+
114879
+
114597
114880
 
114598
114881
 
114599
114882
 
114600
114883
 
114601
114884
 
114602
114885
  const defaultTransformers = [callouts, code_tabs, gemoji_, transform_embeds];
114603
- /**
114604
- * Process markdown content with MDX syntax support.
114605
- * Detects and renders custom component tags from the components hash.
114606
- *
114607
- * @see {@link https://github.com/readmeio/rmdx/blob/main/docs/mdxish-flow.md}
114608
- */
114609
- function mdxish(mdContent, opts = {}) {
114886
+ function mdxishAstProcessor(mdContent, opts = {}) {
114610
114887
  const { components: userComponents = {}, jsxContext = {}, useTailwind } = opts;
114611
114888
  const components = {
114612
114889
  ...loadComponents(),
@@ -114641,9 +114918,46 @@ function mdxish(mdContent, opts = {}) {
114641
114918
  .use(evaluate_expressions, { context: jsxContext }) // Evaluate MDX expressions using jsxContext
114642
114919
  .use(variables_text) // Parse {user.*} patterns from text (can't rely on remarkMdx)
114643
114920
  .use(useTailwind ? transform_tailwind : undefined, { components: tempComponentsMap })
114644
- .use(remarkGfm)
114921
+ .use(remarkGfm);
114922
+ return {
114923
+ processor,
114924
+ /**
114925
+ * @todo we need to return this transformed content for now
114926
+ * but ultimately need to properly tokenize our special markdown syntax
114927
+ * into hast nodes instead of relying on transformed content
114928
+ */
114929
+ parserReadyContent,
114930
+ };
114931
+ }
114932
+ /**
114933
+ * Converts an Mdast to a Markdown string.
114934
+ */
114935
+ function mdxishMdastToMd(mdast) {
114936
+ const md = unified().use(remarkGfm).use(processor_compile).use(remarkStringify, {
114937
+ bullet: '-',
114938
+ emphasis: '_',
114939
+ }).stringify(mdast);
114940
+ return md;
114941
+ }
114942
+ /**
114943
+ * Processes markdown content with MDX syntax support and returns a HAST.
114944
+ * Detects and renders custom component tags from the components hash.
114945
+ *
114946
+ * @see {@link https://github.com/readmeio/rmdx/blob/main/docs/mdxish-flow.md}
114947
+ */
114948
+ function mdxish(mdContent, opts = {}) {
114949
+ const { components: userComponents = {} } = opts;
114950
+ const components = {
114951
+ ...loadComponents(),
114952
+ ...userComponents,
114953
+ };
114954
+ const { processor, parserReadyContent } = mdxishAstProcessor(mdContent, opts);
114955
+ processor
114645
114956
  .use(remarkRehype, { allowDangerousHtml: true, handlers: mdxComponentHandlers })
114957
+ .use(preserveBooleanProperties) // RehypeRaw converts boolean properties to empty strings
114646
114958
  .use(rehypeRaw, { passThrough: ['html-block'] })
114959
+ .use(restoreBooleanProperties)
114960
+ .use(mdxish_mermaid) // Add mermaid-render className to pre wrappers
114647
114961
  .use(rehypeSlug)
114648
114962
  .use(rehypeMdxishComponents, {
114649
114963
  components,