@readme/markdown 11.11.0 → 11.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/main.node.js
CHANGED
|
@@ -90788,77 +90788,197 @@ const mdxToHast = () => tree => {
|
|
|
90788
90788
|
;// ./processor/transform/mdxish/mdxish-component-blocks.ts
|
|
90789
90789
|
|
|
90790
90790
|
|
|
90791
|
-
const
|
|
90792
|
-
const
|
|
90791
|
+
const pascalCaseTagPattern = /^<([A-Z][A-Za-z0-9_]*)([^>]*?)(\/?)>([\s\S]*)?$/;
|
|
90792
|
+
const tagAttributePattern = /([a-zA-Z_:][-a-zA-Z0-9_:.]*)(?:\s*=\s*("[^"]*"|'[^']*'|[^\s"'>]+))?/g;
|
|
90793
|
+
/**
|
|
90794
|
+
* Maximum number of siblings to scan forward when looking for a closing tag
|
|
90795
|
+
* to avoid scanning too far and degrading performance
|
|
90796
|
+
*/
|
|
90797
|
+
const MAX_LOOKAHEAD = 30;
|
|
90798
|
+
/**
|
|
90799
|
+
* Tags that have dedicated transformers and should NOT be handled by this plugin.
|
|
90800
|
+
* These components have special parsing requirements that the generic component
|
|
90801
|
+
* block transformer cannot handle correctly.
|
|
90802
|
+
*/
|
|
90803
|
+
const EXCLUDED_TAGS = new Set(['HTMLBlock', 'Table']);
|
|
90793
90804
|
const inlineMdProcessor = unified().use(remarkParse);
|
|
90794
90805
|
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.
|
|
90806
|
+
/**
|
|
90807
|
+
* Parse markdown content into mdast children nodes.
|
|
90808
|
+
*/
|
|
90814
90809
|
const parseMdChildren = (value) => {
|
|
90815
90810
|
const parsed = inlineMdProcessor.parse(value);
|
|
90816
90811
|
return parsed.children || [];
|
|
90817
90812
|
};
|
|
90818
|
-
|
|
90819
|
-
|
|
90813
|
+
/**
|
|
90814
|
+
* Convert raw attribute string into mdxJsxAttribute entries.
|
|
90815
|
+
* Handles both key-value attributes (theme="info") and boolean attributes (empty).
|
|
90816
|
+
*/
|
|
90820
90817
|
const parseAttributes = (raw) => {
|
|
90821
90818
|
const attributes = [];
|
|
90822
90819
|
const attrString = raw.trim();
|
|
90823
90820
|
if (!attrString)
|
|
90824
90821
|
return attributes;
|
|
90825
|
-
|
|
90826
|
-
|
|
90827
|
-
let match = attributePattern.exec(attrString);
|
|
90822
|
+
tagAttributePattern.lastIndex = 0;
|
|
90823
|
+
let match = tagAttributePattern.exec(attrString);
|
|
90828
90824
|
while (match !== null) {
|
|
90829
90825
|
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
90826
|
const value = attrValue ? attrValue.replace(/^['"]|['"]$/g, '') : null;
|
|
90834
|
-
attributes.push({
|
|
90835
|
-
|
|
90836
|
-
name: attrName,
|
|
90837
|
-
value,
|
|
90838
|
-
});
|
|
90839
|
-
match = attributePattern.exec(attrString);
|
|
90827
|
+
attributes.push({ type: 'mdxJsxAttribute', name: attrName, value });
|
|
90828
|
+
match = tagAttributePattern.exec(attrString);
|
|
90840
90829
|
}
|
|
90841
90830
|
return attributes;
|
|
90842
90831
|
};
|
|
90843
|
-
|
|
90832
|
+
/**
|
|
90833
|
+
* Parse an HTML tag string into structured data.
|
|
90834
|
+
*/
|
|
90844
90835
|
const parseTag = (value) => {
|
|
90845
|
-
const match = value.match(
|
|
90836
|
+
const match = value.match(pascalCaseTagPattern);
|
|
90846
90837
|
if (!match)
|
|
90847
90838
|
return null;
|
|
90848
|
-
const [, tag, attrString = '', selfClosing = '',
|
|
90849
|
-
const attributes = parseAttributes(attrString);
|
|
90839
|
+
const [, tag, attrString = '', selfClosing = '', contentAfterTag = ''] = match;
|
|
90850
90840
|
return {
|
|
90851
90841
|
tag,
|
|
90852
|
-
attributes,
|
|
90842
|
+
attributes: parseAttributes(attrString),
|
|
90853
90843
|
selfClosing: !!selfClosing,
|
|
90854
|
-
|
|
90844
|
+
contentAfterTag,
|
|
90855
90845
|
};
|
|
90856
90846
|
};
|
|
90857
|
-
|
|
90858
|
-
|
|
90847
|
+
/**
|
|
90848
|
+
* Create an MdxJsxFlowElement node from component data.
|
|
90849
|
+
*/
|
|
90850
|
+
const createComponentNode = ({ tag, attributes, children, startPosition, endPosition }) => ({
|
|
90851
|
+
type: 'mdxJsxFlowElement',
|
|
90852
|
+
name: tag,
|
|
90853
|
+
attributes,
|
|
90854
|
+
children,
|
|
90855
|
+
position: {
|
|
90856
|
+
start: startPosition?.start,
|
|
90857
|
+
end: endPosition?.end ?? startPosition?.end,
|
|
90858
|
+
},
|
|
90859
|
+
});
|
|
90860
|
+
/**
|
|
90861
|
+
* Remove a closing tag from a paragraph's children and return the updated paragraph.
|
|
90862
|
+
*/
|
|
90863
|
+
const stripClosingTagFromParagraph = (node, tag) => {
|
|
90864
|
+
if (!Array.isArray(node.children))
|
|
90865
|
+
return { paragraph: node, found: false };
|
|
90866
|
+
const children = [...node.children];
|
|
90867
|
+
const closingIndex = children.findIndex(child => child.type === 'html' && isClosingTag(child.value || '', tag));
|
|
90868
|
+
if (closingIndex === -1)
|
|
90869
|
+
return { paragraph: node, found: false };
|
|
90870
|
+
children.splice(closingIndex, 1);
|
|
90871
|
+
return { paragraph: { ...node, children }, found: true };
|
|
90872
|
+
};
|
|
90873
|
+
/**
|
|
90874
|
+
* Scan forward through siblings to find a closing tag.
|
|
90875
|
+
* Handles:
|
|
90876
|
+
* - Exact match HTML siblings (e.g., `</Tag>`)
|
|
90877
|
+
* - HTML siblings with embedded closing tag (e.g., `...\n</Tag>`)
|
|
90878
|
+
* - Paragraph siblings containing the closing tag as a child
|
|
90879
|
+
*
|
|
90880
|
+
* Returns null if not found within MAX_LOOKAHEAD siblings
|
|
90881
|
+
*/
|
|
90882
|
+
const scanForClosingTag = (parent, startIndex, tag) => {
|
|
90883
|
+
const closingTagStr = `</${tag}>`;
|
|
90884
|
+
const maxIndex = Math.min(startIndex + MAX_LOOKAHEAD, parent.children.length);
|
|
90885
|
+
let i = startIndex + 1;
|
|
90886
|
+
for (; i < maxIndex; i += 1) {
|
|
90887
|
+
const sibling = parent.children[i];
|
|
90888
|
+
// Check HTML siblings
|
|
90889
|
+
if (sibling.type === 'html') {
|
|
90890
|
+
const siblingValue = sibling.value || '';
|
|
90891
|
+
// Exact match (standalone closing tag)
|
|
90892
|
+
if (isClosingTag(siblingValue, tag)) {
|
|
90893
|
+
return { closingIndex: i, extraClosingChildren: [] };
|
|
90894
|
+
}
|
|
90895
|
+
// Embedded closing tag (closing tag at end of HTML block content)
|
|
90896
|
+
if (siblingValue.includes(closingTagStr)) {
|
|
90897
|
+
const contentBeforeClose = siblingValue.substring(0, siblingValue.lastIndexOf(closingTagStr)).trim();
|
|
90898
|
+
const extraChildren = contentBeforeClose
|
|
90899
|
+
? parseMdChildren(contentBeforeClose)
|
|
90900
|
+
: [];
|
|
90901
|
+
return { closingIndex: i, extraClosingChildren: extraChildren };
|
|
90902
|
+
}
|
|
90903
|
+
}
|
|
90904
|
+
// Check paragraph siblings
|
|
90905
|
+
if (sibling.type === 'paragraph') {
|
|
90906
|
+
const { paragraph, found } = stripClosingTagFromParagraph(sibling, tag);
|
|
90907
|
+
if (found) {
|
|
90908
|
+
return { closingIndex: i, extraClosingChildren: [], strippedParagraph: paragraph };
|
|
90909
|
+
}
|
|
90910
|
+
}
|
|
90911
|
+
}
|
|
90912
|
+
if (i < parent.children.length) {
|
|
90913
|
+
// eslint-disable-next-line no-console
|
|
90914
|
+
console.warn(`Closing tag </${tag}> not found within ${MAX_LOOKAHEAD} siblings, stopping scan`);
|
|
90915
|
+
}
|
|
90916
|
+
return null;
|
|
90917
|
+
};
|
|
90918
|
+
const substituteNodeWithMdxNode = (parent, index, mdxNode) => {
|
|
90919
|
+
parent.children.splice(index, 1, mdxNode);
|
|
90920
|
+
};
|
|
90921
|
+
/**
|
|
90922
|
+
* Transform PascalCase HTML nodes into mdxJsxFlowElement nodes.
|
|
90923
|
+
*
|
|
90924
|
+
* Remark parses unknown/custom component tags as raw HTML nodes.
|
|
90925
|
+
* These are the custom readme MDX syntax for components.
|
|
90926
|
+
* This transformer identifies these patterns and converts them to proper MDX JSX elements so they
|
|
90927
|
+
* can be accurately recognized and rendered later with their component definition code.
|
|
90928
|
+
* Though for some tags, we need to handle them specially
|
|
90929
|
+
*
|
|
90930
|
+
* ## Supported HTML Structures
|
|
90931
|
+
*
|
|
90932
|
+
* ### 1. Self-closing tags
|
|
90933
|
+
* ```
|
|
90934
|
+
* <Component />
|
|
90935
|
+
* ```
|
|
90936
|
+
* Parsed as: `html: "<Component />"`
|
|
90937
|
+
*
|
|
90938
|
+
* ### 2. Self-contained blocks (entire component in single HTML node)
|
|
90939
|
+
* ```
|
|
90940
|
+
* <Button>Click me</Button>
|
|
90941
|
+
* ```
|
|
90942
|
+
* ```
|
|
90943
|
+
* <Component>
|
|
90944
|
+
* <h2>Title</h2>
|
|
90945
|
+
* <p>Content</p>
|
|
90946
|
+
* </Component>
|
|
90947
|
+
* ```
|
|
90948
|
+
* Parsed as: `html: "<Component>\n <h2>Title</h2>\n <p>Content</p>\n</Component>"`
|
|
90949
|
+
* The opening tag, content, and closing tag are all captured in one HTML node.
|
|
90950
|
+
*
|
|
90951
|
+
* ### 3. Multi-sibling components (closing tag in a following sibling)
|
|
90952
|
+
* Handles various structures where the closing tag is in a later sibling, such as:
|
|
90953
|
+
*
|
|
90954
|
+
* #### 3a. Block components (closing tag in sibling paragraph)
|
|
90955
|
+
* ```
|
|
90956
|
+
* <Callout>
|
|
90957
|
+
* Some **markdown** content
|
|
90958
|
+
* </Callout>
|
|
90959
|
+
* ```
|
|
90960
|
+
*
|
|
90961
|
+
* #### 3b. Multi-paragraph components (closing tag several siblings away)
|
|
90962
|
+
* ```
|
|
90963
|
+
* <Callout>
|
|
90964
|
+
*
|
|
90965
|
+
* First paragraph
|
|
90966
|
+
*
|
|
90967
|
+
* Second paragraph
|
|
90968
|
+
* </Callout>
|
|
90969
|
+
* ```
|
|
90970
|
+
*
|
|
90971
|
+
* #### 3c. Nested components split by blank lines (closing tag embedded in HTML sibling)
|
|
90972
|
+
* ```
|
|
90973
|
+
* <Outer>
|
|
90974
|
+
* <Inner>content</Inner>
|
|
90975
|
+
*
|
|
90976
|
+
* <Inner>content</Inner>
|
|
90977
|
+
* </Outer>
|
|
90978
|
+
* ```
|
|
90979
|
+
*/
|
|
90859
90980
|
const mdxishComponentBlocks = () => tree => {
|
|
90860
90981
|
const stack = [tree];
|
|
90861
|
-
// Process children depth-first, rewriting opening/closing component HTML pairs
|
|
90862
90982
|
const processChildNode = (parent, index) => {
|
|
90863
90983
|
const node = parent.children[index];
|
|
90864
90984
|
if (!node)
|
|
@@ -90866,46 +90986,66 @@ const mdxishComponentBlocks = () => tree => {
|
|
|
90866
90986
|
if ('children' in node && Array.isArray(node.children)) {
|
|
90867
90987
|
stack.push(node);
|
|
90868
90988
|
}
|
|
90989
|
+
// Only visit HTML nodes with an actual html tag
|
|
90869
90990
|
const value = node.value;
|
|
90870
90991
|
if (node.type !== 'html' || typeof value !== 'string')
|
|
90871
90992
|
return;
|
|
90872
90993
|
const parsed = parseTag(value);
|
|
90873
90994
|
if (!parsed)
|
|
90874
90995
|
return;
|
|
90875
|
-
const { tag, attributes, selfClosing,
|
|
90876
|
-
|
|
90996
|
+
const { tag, attributes, selfClosing, contentAfterTag = '' } = parsed;
|
|
90997
|
+
// Skip tags that have dedicated transformers
|
|
90998
|
+
if (EXCLUDED_TAGS.has(tag))
|
|
90999
|
+
return;
|
|
91000
|
+
const closingTagStr = `</${tag}>`;
|
|
91001
|
+
// Case 1: Self-closing tag
|
|
90877
91002
|
if (selfClosing) {
|
|
90878
|
-
const componentNode = {
|
|
90879
|
-
|
|
90880
|
-
name: tag,
|
|
91003
|
+
const componentNode = createComponentNode({
|
|
91004
|
+
tag,
|
|
90881
91005
|
attributes,
|
|
90882
91006
|
children: [],
|
|
90883
|
-
|
|
90884
|
-
};
|
|
90885
|
-
parent
|
|
91007
|
+
startPosition: node.position,
|
|
91008
|
+
});
|
|
91009
|
+
substituteNodeWithMdxNode(parent, index, componentNode);
|
|
90886
91010
|
return;
|
|
90887
91011
|
}
|
|
90888
|
-
|
|
90889
|
-
if (
|
|
91012
|
+
// Case 2: Self-contained block (closing tag in content)
|
|
91013
|
+
if (contentAfterTag.includes(closingTagStr)) {
|
|
91014
|
+
const componentInnerContent = contentAfterTag.substring(0, contentAfterTag.lastIndexOf(closingTagStr)).trim();
|
|
91015
|
+
const componentNode = createComponentNode({
|
|
91016
|
+
tag,
|
|
91017
|
+
attributes,
|
|
91018
|
+
children: componentInnerContent ? parseMdChildren(componentInnerContent) : [],
|
|
91019
|
+
startPosition: node.position,
|
|
91020
|
+
});
|
|
91021
|
+
substituteNodeWithMdxNode(parent, index, componentNode);
|
|
90890
91022
|
return;
|
|
90891
|
-
|
|
90892
|
-
|
|
91023
|
+
}
|
|
91024
|
+
// Case 3: Multi-sibling component (closing tag in a following sibling)
|
|
91025
|
+
// Scans forward through siblings to find closing tag in HTML or paragraph nodes
|
|
91026
|
+
const scanResult = scanForClosingTag(parent, index, tag);
|
|
91027
|
+
if (!scanResult)
|
|
90893
91028
|
return;
|
|
90894
|
-
const
|
|
90895
|
-
|
|
90896
|
-
|
|
91029
|
+
const { closingIndex, extraClosingChildren, strippedParagraph } = scanResult;
|
|
91030
|
+
const extraChildren = contentAfterTag ? parseMdChildren(contentAfterTag.trimStart()) : [];
|
|
91031
|
+
// Collect all intermediate siblings between opening tag and closing tag
|
|
91032
|
+
const intermediateChildren = parent.children.slice(index + 1, closingIndex);
|
|
91033
|
+
// For paragraph siblings, include the paragraph's children (with closing tag stripped)
|
|
91034
|
+
// For HTML siblings, include any content parsed from before the closing tag
|
|
91035
|
+
const closingChildren = strippedParagraph
|
|
91036
|
+
? strippedParagraph.children
|
|
91037
|
+
: extraClosingChildren;
|
|
91038
|
+
const componentNode = createComponentNode({
|
|
91039
|
+
tag,
|
|
90897
91040
|
attributes,
|
|
90898
|
-
children: [
|
|
90899
|
-
|
|
90900
|
-
|
|
90901
|
-
|
|
90902
|
-
|
|
90903
|
-
|
|
90904
|
-
end: next.position?.end,
|
|
90905
|
-
},
|
|
90906
|
-
};
|
|
90907
|
-
replaceChild(parent, index, componentNode);
|
|
91041
|
+
children: [...extraChildren, ...intermediateChildren, ...closingChildren],
|
|
91042
|
+
startPosition: node.position,
|
|
91043
|
+
endPosition: parent.children[closingIndex]?.position,
|
|
91044
|
+
});
|
|
91045
|
+
// Remove all nodes from opening tag to closing tag (inclusive) and replace with component node
|
|
91046
|
+
parent.children.splice(index, closingIndex - index + 1, componentNode);
|
|
90908
91047
|
};
|
|
91048
|
+
// Travel the tree depth-first
|
|
90909
91049
|
while (stack.length) {
|
|
90910
91050
|
const parent = stack.pop();
|
|
90911
91051
|
if (parent?.children) {
|
|
@@ -113049,8 +113189,11 @@ function smartCamelCase(str) {
|
|
|
113049
113189
|
}
|
|
113050
113190
|
// Sort by length (longest first) to prevent shorter matches (e.g., "column" in "columns")
|
|
113051
113191
|
const sortedBoundaries = [...allBoundaries].sort((a, b) => b.length - a.length);
|
|
113192
|
+
// Use case-sensitive matching ('g' not 'gi') so that once a letter is
|
|
113193
|
+
// capitalized by a longer boundary, shorter boundaries won't re-match it.
|
|
113194
|
+
// This prevents issues like 'iconcolor' becoming 'iconColOr' instead of 'iconColor'.
|
|
113052
113195
|
return sortedBoundaries.reduce((res, word) => {
|
|
113053
|
-
const regex = new RegExp(`(${word})([a-z])`, '
|
|
113196
|
+
const regex = new RegExp(`(${word})([a-z])`, 'g');
|
|
113054
113197
|
return res.replace(regex, (_, prefix, nextChar) => prefix.toLowerCase() + nextChar.toUpperCase());
|
|
113055
113198
|
}, str);
|
|
113056
113199
|
}
|
|
@@ -113176,7 +113319,21 @@ const htmlBlockHandler = (_state, node) => {
|
|
|
113176
113319
|
children: [],
|
|
113177
113320
|
};
|
|
113178
113321
|
};
|
|
113322
|
+
// Convert embed magic blocks to Embed components
|
|
113323
|
+
const embedHandler = (state, node) => {
|
|
113324
|
+
// Assert to get the minimum properties we need
|
|
113325
|
+
const { data } = node;
|
|
113326
|
+
return {
|
|
113327
|
+
type: 'element',
|
|
113328
|
+
// To differentiate between regular embeds and magic block embeds,
|
|
113329
|
+
// magic block embeds have a certain hName
|
|
113330
|
+
tagName: data?.hName === NodeTypes.embedBlock ? 'Embed' : 'embed',
|
|
113331
|
+
properties: data?.hProperties,
|
|
113332
|
+
children: state.all(node),
|
|
113333
|
+
};
|
|
113334
|
+
};
|
|
113179
113335
|
const mdxComponentHandlers = {
|
|
113336
|
+
embed: embedHandler,
|
|
113180
113337
|
mdxFlowExpression: mdxExpressionHandler,
|
|
113181
113338
|
mdxJsxFlowElement: mdxJsxElementHandler,
|
|
113182
113339
|
mdxJsxTextElement: mdxJsxElementHandler,
|
|
@@ -113880,6 +114037,10 @@ const parseInline = (text) => {
|
|
|
113880
114037
|
if (!text.trim())
|
|
113881
114038
|
return [{ type: 'text', value: '' }];
|
|
113882
114039
|
const tree = cellParser.runSync(cellParser.parse(text));
|
|
114040
|
+
// If there are multiple block-level nodes, keep them as-is to preserve the document structure and spacing
|
|
114041
|
+
if (tree.children.length > 1) {
|
|
114042
|
+
return tree.children;
|
|
114043
|
+
}
|
|
113883
114044
|
return tree.children.flatMap(n =>
|
|
113884
114045
|
// This unwraps the extra p node that might appear & wrapping the content
|
|
113885
114046
|
n.type === 'paragraph' && 'children' in n ? n.children : [n]);
|
|
@@ -114017,14 +114178,17 @@ function parseMagicBlock(raw, options = {}) {
|
|
|
114017
114178
|
else {
|
|
114018
114179
|
children.push(...titleBlocks, ...bodyBlocks);
|
|
114019
114180
|
}
|
|
114181
|
+
// If there is no title or title is empty
|
|
114182
|
+
const empty = !titleBlocks.length || !titleBlocks[0].children[0]?.value;
|
|
114020
114183
|
// Create mdxJsxFlowElement directly for mdxish
|
|
114021
114184
|
const calloutElement = {
|
|
114022
114185
|
type: 'mdxJsxFlowElement',
|
|
114023
114186
|
name: 'Callout',
|
|
114024
|
-
attributes: toAttributes({ icon, theme: theme || 'default', type: theme || 'default' }, [
|
|
114187
|
+
attributes: toAttributes({ icon, theme: theme || 'default', type: theme || 'default', empty }, [
|
|
114025
114188
|
'icon',
|
|
114026
114189
|
'theme',
|
|
114027
114190
|
'type',
|
|
114191
|
+
'empty',
|
|
114028
114192
|
]),
|
|
114029
114193
|
children: children,
|
|
114030
114194
|
};
|
|
@@ -114082,9 +114246,9 @@ function parseMagicBlock(raw, options = {}) {
|
|
|
114082
114246
|
return [
|
|
114083
114247
|
wrapPinnedBlocks({
|
|
114084
114248
|
children: [
|
|
114085
|
-
{ children: [{ type: 'text', value: title ||
|
|
114249
|
+
{ children: [{ type: 'text', value: title || '' }], title: embedJson.provider, type: 'link', url },
|
|
114086
114250
|
],
|
|
114087
|
-
data: { hName: '
|
|
114251
|
+
data: { hName: 'embed-block', hProperties: { ...embedJson, href: url, html, title, url } },
|
|
114088
114252
|
type: 'embed',
|
|
114089
114253
|
}, json),
|
|
114090
114254
|
];
|
|
@@ -114102,6 +114266,25 @@ function parseMagicBlock(raw, options = {}) {
|
|
|
114102
114266
|
}, json),
|
|
114103
114267
|
];
|
|
114104
114268
|
}
|
|
114269
|
+
// Recipe/TutorialTile: renders as Recipe component
|
|
114270
|
+
case 'recipe':
|
|
114271
|
+
case 'tutorial-tile': {
|
|
114272
|
+
const recipeJson = json;
|
|
114273
|
+
if (!recipeJson.slug || !recipeJson.title)
|
|
114274
|
+
return [];
|
|
114275
|
+
// Create mdxJsxFlowElement directly for mdxish flow
|
|
114276
|
+
// Note: Don't wrap in pinned blocks for mdxish - rehypeMdxishComponents handles component resolution
|
|
114277
|
+
// The node structure matches what mdxishComponentBlocks creates for JSX tags
|
|
114278
|
+
const recipeNode = {
|
|
114279
|
+
type: 'mdxJsxFlowElement',
|
|
114280
|
+
name: 'Recipe',
|
|
114281
|
+
attributes: toAttributes(recipeJson, ['slug', 'title']),
|
|
114282
|
+
children: [],
|
|
114283
|
+
// Position is optional but helps with debugging
|
|
114284
|
+
position: undefined,
|
|
114285
|
+
};
|
|
114286
|
+
return [recipeNode];
|
|
114287
|
+
}
|
|
114105
114288
|
// Unknown block types: render as generic div with JSON properties
|
|
114106
114289
|
default: {
|
|
114107
114290
|
const text = json.text || json.html || '';
|
|
@@ -114395,6 +114578,45 @@ const restoreSnakeCaseComponentNames = (options) => {
|
|
|
114395
114578
|
};
|
|
114396
114579
|
/* harmony default export */ const restore_snake_case_component_name = (restoreSnakeCaseComponentNames);
|
|
114397
114580
|
|
|
114581
|
+
;// ./processor/transform/mdxish/retain-boolean-attributes.ts
|
|
114582
|
+
|
|
114583
|
+
// Private Use Area character (U+E000) which is extremely unlikely to appear in real content.
|
|
114584
|
+
const TEMP_TRUE_BOOLEAN_VALUE = 'readme-this-is-a-temporary-boolean-attribute-\uE000';
|
|
114585
|
+
const TEMP_FALSE_BOOLEAN_VALUE = 'readme-this-is-a-temporary-boolean-attribute-\uE001';
|
|
114586
|
+
/**
|
|
114587
|
+
* Preserves boolean properties when passed to rehypeRaw because
|
|
114588
|
+
* rehypeRaw converts boolean properties in nodes to strings (e.g. true -> ""),
|
|
114589
|
+
* which can change the truthiness of the property. Hence we need to preserve the boolean properties.
|
|
114590
|
+
*/
|
|
114591
|
+
const preserveBooleanProperties = () => tree => {
|
|
114592
|
+
visit(tree, 'element', (node) => {
|
|
114593
|
+
if (!node.properties)
|
|
114594
|
+
return;
|
|
114595
|
+
Object.entries(node.properties).forEach(([key, value]) => {
|
|
114596
|
+
if (typeof value === 'boolean') {
|
|
114597
|
+
node.properties[key] = value ? TEMP_TRUE_BOOLEAN_VALUE : TEMP_FALSE_BOOLEAN_VALUE;
|
|
114598
|
+
}
|
|
114599
|
+
});
|
|
114600
|
+
});
|
|
114601
|
+
return tree;
|
|
114602
|
+
};
|
|
114603
|
+
const restoreBooleanProperties = () => tree => {
|
|
114604
|
+
visit(tree, 'element', (node) => {
|
|
114605
|
+
if (!node.properties)
|
|
114606
|
+
return;
|
|
114607
|
+
Object.entries(node.properties).forEach(([key, value]) => {
|
|
114608
|
+
if (value === TEMP_TRUE_BOOLEAN_VALUE) {
|
|
114609
|
+
node.properties[key] = true;
|
|
114610
|
+
}
|
|
114611
|
+
else if (value === TEMP_FALSE_BOOLEAN_VALUE) {
|
|
114612
|
+
node.properties[key] = false;
|
|
114613
|
+
}
|
|
114614
|
+
});
|
|
114615
|
+
});
|
|
114616
|
+
return tree;
|
|
114617
|
+
};
|
|
114618
|
+
|
|
114619
|
+
|
|
114398
114620
|
;// ./processor/transform/mdxish/variables-text.ts
|
|
114399
114621
|
|
|
114400
114622
|
|
|
@@ -114422,6 +114644,8 @@ const variablesTextTransformer = () => tree => {
|
|
|
114422
114644
|
if (parent.type === 'inlineCode')
|
|
114423
114645
|
return;
|
|
114424
114646
|
const text = node.value;
|
|
114647
|
+
if (typeof text !== 'string' || !text.trim())
|
|
114648
|
+
return;
|
|
114425
114649
|
if (!text.includes('{user.') && !text.includes('{user['))
|
|
114426
114650
|
return;
|
|
114427
114651
|
const matches = [...text.matchAll(USER_VAR_REGEX)];
|
|
@@ -114574,6 +114798,7 @@ function loadComponents() {
|
|
|
114574
114798
|
|
|
114575
114799
|
|
|
114576
114800
|
|
|
114801
|
+
|
|
114577
114802
|
|
|
114578
114803
|
|
|
114579
114804
|
const defaultTransformers = [callouts, code_tabs, gemoji_, transform_embeds];
|
|
@@ -114620,7 +114845,9 @@ function mdxish(mdContent, opts = {}) {
|
|
|
114620
114845
|
.use(useTailwind ? transform_tailwind : undefined, { components: tempComponentsMap })
|
|
114621
114846
|
.use(remarkGfm)
|
|
114622
114847
|
.use(remarkRehype, { allowDangerousHtml: true, handlers: mdxComponentHandlers })
|
|
114848
|
+
.use(preserveBooleanProperties) // RehypeRaw converts boolean properties to empty strings
|
|
114623
114849
|
.use(rehypeRaw, { passThrough: ['html-block'] })
|
|
114850
|
+
.use(restoreBooleanProperties)
|
|
114624
114851
|
.use(rehypeSlug)
|
|
114625
114852
|
.use(rehypeMdxishComponents, {
|
|
114626
114853
|
components,
|