@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/components/Anchor.tsx +10 -1
- package/components/Code/index.tsx +3 -2
- package/components/CodeTabs/index.tsx +31 -12
- package/dist/index.d.ts +1 -1
- package/dist/lib/index.d.ts +1 -1
- package/dist/lib/mdxish.d.ts +15 -1
- package/dist/main.js +421 -107
- package/dist/main.node.js +421 -107
- package/dist/main.node.js.map +1 -1
- package/dist/processor/transform/mdxish/mdxish-component-blocks.d.ts +63 -0
- package/dist/processor/transform/mdxish/mdxish-mermaid.d.ts +8 -0
- package/dist/processor/transform/mdxish/retain-boolean-attributes.d.ts +9 -0
- package/package.json +1 -1
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) },
|
|
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 =
|
|
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 (
|
|
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
|
|
19351
|
-
//
|
|
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
|
-
|
|
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
|
-
}, [
|
|
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
|
|
19376
|
-
if (
|
|
19377
|
-
const
|
|
19378
|
-
|
|
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" },
|
|
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
|
|
19410
|
+
const tabCodeComponent = Array.isArray(pre.props?.children)
|
|
19384
19411
|
? pre.props.children[0]
|
|
19385
19412
|
: pre.props?.children;
|
|
19386
|
-
const lang =
|
|
19387
|
-
const 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
|
|
90792
|
-
const
|
|
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
|
-
|
|
90796
|
-
|
|
90797
|
-
|
|
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
|
-
|
|
90819
|
-
|
|
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
|
-
|
|
90826
|
-
|
|
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
|
-
|
|
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
|
-
|
|
90859
|
+
/**
|
|
90860
|
+
* Parse an HTML tag string into structured data.
|
|
90861
|
+
*/
|
|
90844
90862
|
const parseTag = (value) => {
|
|
90845
|
-
const match = value.match(
|
|
90863
|
+
const match = value.match(pascalCaseTagPattern);
|
|
90846
90864
|
if (!match)
|
|
90847
90865
|
return null;
|
|
90848
|
-
const [, tag, attrString = '', selfClosing = '',
|
|
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
|
-
|
|
90871
|
+
contentAfterTag,
|
|
90855
90872
|
};
|
|
90856
90873
|
};
|
|
90857
|
-
|
|
90858
|
-
|
|
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,
|
|
90876
|
-
|
|
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
|
-
|
|
90880
|
-
name: tag,
|
|
91030
|
+
const componentNode = createComponentNode({
|
|
91031
|
+
tag,
|
|
90881
91032
|
attributes,
|
|
90882
91033
|
children: [],
|
|
90883
|
-
|
|
90884
|
-
};
|
|
90885
|
-
parent
|
|
91034
|
+
startPosition: node.position,
|
|
91035
|
+
});
|
|
91036
|
+
substituteNodeWithMdxNode(parent, index, componentNode);
|
|
90886
91037
|
return;
|
|
90887
91038
|
}
|
|
90888
|
-
|
|
90889
|
-
if (
|
|
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
|
-
|
|
90892
|
-
|
|
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
|
|
90895
|
-
|
|
90896
|
-
|
|
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
|
-
|
|
90900
|
-
|
|
90901
|
-
|
|
90902
|
-
|
|
90903
|
-
|
|
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
|
|
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
|
-
[
|
|
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])`, '
|
|
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
|
|
114074
|
+
const parseTableCell = (text) => {
|
|
113880
114075
|
if (!text.trim())
|
|
113881
114076
|
return [{ type: 'text', value: '' }];
|
|
113882
|
-
const tree =
|
|
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
|
-
|
|
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
|
-
|
|
114009
|
-
const
|
|
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 :
|
|
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 ||
|
|
114295
|
+
{ children: [{ type: 'text', value: title || '' }], title: embedJson.provider, type: 'link', url },
|
|
114090
114296
|
],
|
|
114091
|
-
data: { hName: '
|
|
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,
|