@readme/markdown 11.15.0 → 12.0.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
|
@@ -73147,12 +73147,45 @@ const plain = (node, opts = {}) => {
|
|
|
73147
73147
|
};
|
|
73148
73148
|
/* harmony default export */ const lib_plain = (plain);
|
|
73149
73149
|
|
|
73150
|
+
;// ./processor/transform/extract-text.ts
|
|
73151
|
+
/**
|
|
73152
|
+
* Extracts text content from a single AST node recursively.
|
|
73153
|
+
* Works with both MDAST and HAST-like node structures.
|
|
73154
|
+
*
|
|
73155
|
+
* Placed this outside of the utils.ts file to avoid circular dependencies.
|
|
73156
|
+
*
|
|
73157
|
+
* @param node - The node to extract text from (can be MDAST Node or HAST-like structure)
|
|
73158
|
+
* @returns The concatenated text content
|
|
73159
|
+
*/
|
|
73160
|
+
const extractText = (node) => {
|
|
73161
|
+
if (node.type === 'text' && typeof node.value === 'string') {
|
|
73162
|
+
return node.value;
|
|
73163
|
+
}
|
|
73164
|
+
// When a blockquote contains only an image (no text), treat it as having content
|
|
73165
|
+
// so the blockquote is no longer treated as empty and preserved correctly.
|
|
73166
|
+
if (node.type === 'image') {
|
|
73167
|
+
return typeof node.alt === 'string' && node.alt ? node.alt : '[image]';
|
|
73168
|
+
}
|
|
73169
|
+
if (node.children && Array.isArray(node.children)) {
|
|
73170
|
+
return node.children
|
|
73171
|
+
.map(child => {
|
|
73172
|
+
if (child && typeof child === 'object' && 'type' in child) {
|
|
73173
|
+
return extractText(child);
|
|
73174
|
+
}
|
|
73175
|
+
return '';
|
|
73176
|
+
})
|
|
73177
|
+
.join('');
|
|
73178
|
+
}
|
|
73179
|
+
return '';
|
|
73180
|
+
};
|
|
73181
|
+
|
|
73150
73182
|
;// ./processor/transform/callouts.ts
|
|
73151
73183
|
|
|
73152
73184
|
|
|
73153
73185
|
|
|
73154
73186
|
|
|
73155
73187
|
|
|
73188
|
+
|
|
73156
73189
|
const callouts_regex = `^(${emoji_regex().source}|⚠)(\\s+|$)`;
|
|
73157
73190
|
const findFirst = (node) => {
|
|
73158
73191
|
if ('children' in node)
|
|
@@ -73173,42 +73206,76 @@ const wrapHeading = (node) => {
|
|
|
73173
73206
|
},
|
|
73174
73207
|
};
|
|
73175
73208
|
};
|
|
73209
|
+
/**
|
|
73210
|
+
* Checks if a blockquote matches the expected callout structure:
|
|
73211
|
+
* blockquote > paragraph > text node
|
|
73212
|
+
*/
|
|
73213
|
+
const isCalloutStructure = (node) => {
|
|
73214
|
+
const firstChild = node.children?.[0];
|
|
73215
|
+
if (!firstChild || firstChild.type !== 'paragraph')
|
|
73216
|
+
return false;
|
|
73217
|
+
if (!('children' in firstChild))
|
|
73218
|
+
return false;
|
|
73219
|
+
const firstTextChild = firstChild.children?.[0];
|
|
73220
|
+
return firstTextChild?.type === 'text';
|
|
73221
|
+
};
|
|
73222
|
+
const processBlockquote = (node, index, parent) => {
|
|
73223
|
+
if (!isCalloutStructure(node)) {
|
|
73224
|
+
// Only stringify empty blockquotes (no extractable text content)
|
|
73225
|
+
// Preserve blockquotes with actual content (e.g., headings, lists, etc.)
|
|
73226
|
+
const content = extractText(node);
|
|
73227
|
+
const isEmpty = !content || content.trim() === '';
|
|
73228
|
+
if (isEmpty && index !== undefined && parent) {
|
|
73229
|
+
const textNode = {
|
|
73230
|
+
type: 'text',
|
|
73231
|
+
value: '>',
|
|
73232
|
+
};
|
|
73233
|
+
const paragraphNode = {
|
|
73234
|
+
type: 'paragraph',
|
|
73235
|
+
children: [textNode],
|
|
73236
|
+
position: node.position,
|
|
73237
|
+
};
|
|
73238
|
+
parent.children.splice(index, 1, paragraphNode);
|
|
73239
|
+
}
|
|
73240
|
+
return;
|
|
73241
|
+
}
|
|
73242
|
+
// isCalloutStructure ensures node.children[0] is a Paragraph with children
|
|
73243
|
+
const firstParagraph = node.children[0];
|
|
73244
|
+
const startText = lib_plain(firstParagraph).toString();
|
|
73245
|
+
const [match, icon] = startText.match(callouts_regex) || [];
|
|
73246
|
+
if (icon && match) {
|
|
73247
|
+
const heading = startText.slice(match.length);
|
|
73248
|
+
const empty = !heading.length && firstParagraph.children.length === 1;
|
|
73249
|
+
const theme = themes[icon] || 'default';
|
|
73250
|
+
const firstChild = findFirst(node.children[0]);
|
|
73251
|
+
if (firstChild && 'value' in firstChild && typeof firstChild.value === 'string') {
|
|
73252
|
+
firstChild.value = firstChild.value.slice(match.length);
|
|
73253
|
+
}
|
|
73254
|
+
if (heading) {
|
|
73255
|
+
node.children[0] = wrapHeading(node);
|
|
73256
|
+
// @note: We add to the offset/column the length of the unicode
|
|
73257
|
+
// character that was stripped off, so that the start position of the
|
|
73258
|
+
// heading/text matches where it actually starts.
|
|
73259
|
+
node.children[0].position.start.offset += match.length;
|
|
73260
|
+
node.children[0].position.start.column += match.length;
|
|
73261
|
+
}
|
|
73262
|
+
Object.assign(node, {
|
|
73263
|
+
type: NodeTypes.callout,
|
|
73264
|
+
data: {
|
|
73265
|
+
hName: 'Callout',
|
|
73266
|
+
hProperties: {
|
|
73267
|
+
icon,
|
|
73268
|
+
...(empty && { empty }),
|
|
73269
|
+
theme,
|
|
73270
|
+
},
|
|
73271
|
+
},
|
|
73272
|
+
});
|
|
73273
|
+
}
|
|
73274
|
+
};
|
|
73176
73275
|
const calloutTransformer = () => {
|
|
73177
73276
|
return (tree) => {
|
|
73178
|
-
visit(tree, 'blockquote', (node) => {
|
|
73179
|
-
|
|
73180
|
-
return;
|
|
73181
|
-
// @ts-expect-error -- @todo: update plain to accept mdast
|
|
73182
|
-
const startText = lib_plain(node.children[0]).toString();
|
|
73183
|
-
const [match, icon] = startText.match(callouts_regex) || [];
|
|
73184
|
-
if (icon && match) {
|
|
73185
|
-
const heading = startText.slice(match.length);
|
|
73186
|
-
const empty = !heading.length && node.children[0].children.length === 1;
|
|
73187
|
-
const theme = themes[icon] || 'default';
|
|
73188
|
-
const firstChild = findFirst(node.children[0]);
|
|
73189
|
-
if (firstChild && 'value' in firstChild && typeof firstChild.value === 'string') {
|
|
73190
|
-
firstChild.value = firstChild.value.slice(match.length);
|
|
73191
|
-
}
|
|
73192
|
-
if (heading) {
|
|
73193
|
-
node.children[0] = wrapHeading(node);
|
|
73194
|
-
// @note: We add to the offset/column the length of the unicode
|
|
73195
|
-
// character that was stripped off, so that the start position of the
|
|
73196
|
-
// heading/text matches where it actually starts.
|
|
73197
|
-
node.children[0].position.start.offset += match.length;
|
|
73198
|
-
node.children[0].position.start.column += match.length;
|
|
73199
|
-
}
|
|
73200
|
-
Object.assign(node, {
|
|
73201
|
-
type: NodeTypes.callout,
|
|
73202
|
-
data: {
|
|
73203
|
-
hName: 'Callout',
|
|
73204
|
-
hProperties: {
|
|
73205
|
-
icon,
|
|
73206
|
-
...(empty && { empty }),
|
|
73207
|
-
theme,
|
|
73208
|
-
},
|
|
73209
|
-
},
|
|
73210
|
-
});
|
|
73211
|
-
}
|
|
73277
|
+
visit(tree, 'blockquote', (node, index, parent) => {
|
|
73278
|
+
processBlockquote(node, index, parent);
|
|
73212
73279
|
});
|
|
73213
73280
|
};
|
|
73214
73281
|
};
|
|
@@ -90874,6 +90941,17 @@ const parseTag = (value) => {
|
|
|
90874
90941
|
contentAfterTag,
|
|
90875
90942
|
};
|
|
90876
90943
|
};
|
|
90944
|
+
/**
|
|
90945
|
+
* Parse substring content of a node and update the parent's children to include the new nodes.
|
|
90946
|
+
*/
|
|
90947
|
+
const parseSibling = (stack, parent, index, sibling) => {
|
|
90948
|
+
const siblingNodes = parseMdChildren(sibling);
|
|
90949
|
+
// The new sibling nodes might contain new components to be processed
|
|
90950
|
+
if (siblingNodes.length > 0) {
|
|
90951
|
+
parent.children.splice(index + 1, 0, ...siblingNodes);
|
|
90952
|
+
stack.push(parent);
|
|
90953
|
+
}
|
|
90954
|
+
};
|
|
90877
90955
|
/**
|
|
90878
90956
|
* Create an MdxJsxFlowElement node from component data.
|
|
90879
90957
|
*/
|
|
@@ -91016,11 +91094,12 @@ const mdxishComponentBlocks = () => tree => {
|
|
|
91016
91094
|
if ('children' in node && Array.isArray(node.children)) {
|
|
91017
91095
|
stack.push(node);
|
|
91018
91096
|
}
|
|
91019
|
-
// Only visit HTML nodes with an actual html tag
|
|
91097
|
+
// Only visit HTML nodes with an actual html tag,
|
|
91098
|
+
// which means a potential unparsed MDX component
|
|
91020
91099
|
const value = node.value;
|
|
91021
91100
|
if (node.type !== 'html' || typeof value !== 'string')
|
|
91022
91101
|
return;
|
|
91023
|
-
const parsed = parseTag(value);
|
|
91102
|
+
const parsed = parseTag(value.trim());
|
|
91024
91103
|
if (!parsed)
|
|
91025
91104
|
return;
|
|
91026
91105
|
const { tag, attributes, selfClosing, contentAfterTag = '' } = parsed;
|
|
@@ -91037,11 +91116,19 @@ const mdxishComponentBlocks = () => tree => {
|
|
|
91037
91116
|
startPosition: node.position,
|
|
91038
91117
|
});
|
|
91039
91118
|
substituteNodeWithMdxNode(parent, index, componentNode);
|
|
91119
|
+
// Check and parse if there's relevant content after the current closing tag
|
|
91120
|
+
const remainingContent = contentAfterTag.trim();
|
|
91121
|
+
if (remainingContent) {
|
|
91122
|
+
parseSibling(stack, parent, index, remainingContent);
|
|
91123
|
+
}
|
|
91040
91124
|
return;
|
|
91041
91125
|
}
|
|
91042
91126
|
// Case 2: Self-contained block (closing tag in content)
|
|
91043
91127
|
if (contentAfterTag.includes(closingTagStr)) {
|
|
91044
|
-
|
|
91128
|
+
// Find the first closing tag
|
|
91129
|
+
const closingTagIndex = contentAfterTag.indexOf(closingTagStr);
|
|
91130
|
+
const componentInnerContent = contentAfterTag.substring(0, closingTagIndex).trim();
|
|
91131
|
+
const contentAfterClose = contentAfterTag.substring(closingTagIndex + closingTagStr.length).trim();
|
|
91045
91132
|
const componentNode = createComponentNode({
|
|
91046
91133
|
tag,
|
|
91047
91134
|
attributes,
|
|
@@ -91049,6 +91136,14 @@ const mdxishComponentBlocks = () => tree => {
|
|
|
91049
91136
|
startPosition: node.position,
|
|
91050
91137
|
});
|
|
91051
91138
|
substituteNodeWithMdxNode(parent, index, componentNode);
|
|
91139
|
+
// After the closing tag, there might be more content to be processed
|
|
91140
|
+
if (contentAfterClose) {
|
|
91141
|
+
parseSibling(stack, parent, index, contentAfterClose);
|
|
91142
|
+
}
|
|
91143
|
+
else if (componentNode.children.length > 0) {
|
|
91144
|
+
// The content inside the component block might contain new components to be processed
|
|
91145
|
+
stack.push(componentNode);
|
|
91146
|
+
}
|
|
91052
91147
|
return;
|
|
91053
91148
|
}
|
|
91054
91149
|
// Case 3: Multi-sibling component (closing tag in a following sibling)
|
|
@@ -91074,8 +91169,13 @@ const mdxishComponentBlocks = () => tree => {
|
|
|
91074
91169
|
});
|
|
91075
91170
|
// Remove all nodes from opening tag to closing tag (inclusive) and replace with component node
|
|
91076
91171
|
parent.children.splice(index, closingIndex - index + 1, componentNode);
|
|
91172
|
+
// Since we might be merging sibling nodes together and combining content,
|
|
91173
|
+
// there might be new components to process
|
|
91174
|
+
if (componentNode.children.length > 0) {
|
|
91175
|
+
stack.push(componentNode);
|
|
91176
|
+
}
|
|
91077
91177
|
};
|
|
91078
|
-
//
|
|
91178
|
+
// Process the nodes with the components depth-first to maintain the correct order of the nodes
|
|
91079
91179
|
while (stack.length) {
|
|
91080
91180
|
const parent = stack.pop();
|
|
91081
91181
|
if (parent?.children) {
|
|
@@ -91096,6 +91196,7 @@ const mdxishComponentBlocks = () => tree => {
|
|
|
91096
91196
|
|
|
91097
91197
|
|
|
91098
91198
|
|
|
91199
|
+
|
|
91099
91200
|
const isTableCell = (node) => isMDXElement(node) && ['th', 'td'].includes(node.name);
|
|
91100
91201
|
const tableTypes = {
|
|
91101
91202
|
tr: 'tableRow',
|
|
@@ -91120,18 +91221,13 @@ const isTextOnly = (children) => {
|
|
|
91120
91221
|
});
|
|
91121
91222
|
};
|
|
91122
91223
|
/**
|
|
91123
|
-
*
|
|
91224
|
+
* Convenience wrapper that extracts text content from an array of children nodes.
|
|
91124
91225
|
*/
|
|
91125
|
-
const
|
|
91226
|
+
const extractTextFromChildren = (children) => {
|
|
91126
91227
|
return children
|
|
91127
91228
|
.map(child => {
|
|
91128
91229
|
if (child && typeof child === 'object' && 'type' in child) {
|
|
91129
|
-
|
|
91130
|
-
return child.value;
|
|
91131
|
-
}
|
|
91132
|
-
if (child.type === 'mdxJsxTextElement' && 'children' in child && Array.isArray(child.children)) {
|
|
91133
|
-
return extractText(child.children);
|
|
91134
|
-
}
|
|
91230
|
+
return extractText(child);
|
|
91135
91231
|
}
|
|
91136
91232
|
return '';
|
|
91137
91233
|
})
|
|
@@ -91162,7 +91258,7 @@ const processTableNode = (node, index, parent) => {
|
|
|
91162
91258
|
let parsedChildren = cellChildren;
|
|
91163
91259
|
// If cell contains only text nodes, try to re-parse as markdown
|
|
91164
91260
|
if (isTextOnly(cellChildren)) {
|
|
91165
|
-
const textContent =
|
|
91261
|
+
const textContent = extractTextFromChildren(cellChildren);
|
|
91166
91262
|
if (textContent.trim()) {
|
|
91167
91263
|
try {
|
|
91168
91264
|
const parsed = parseMarkdown(textContent);
|
|
@@ -106577,7 +106673,7 @@ const CUSTOM_PROP_BOUNDARIES = [
|
|
|
106577
106673
|
/**
|
|
106578
106674
|
* Tags that should be passed through and handled at runtime (not by the mdxish plugin)
|
|
106579
106675
|
*/
|
|
106580
|
-
const RUNTIME_COMPONENT_TAGS = new Set(['Variable', 'variable']);
|
|
106676
|
+
const RUNTIME_COMPONENT_TAGS = new Set(['Variable', 'variable', 'rdme-pin']);
|
|
106581
106677
|
/**
|
|
106582
106678
|
* Standard HTML tags that should never be treated as custom components.
|
|
106583
106679
|
* Uses the html-tags package, converted to a Set<string> for efficient lookups.
|
|
@@ -113208,6 +113304,33 @@ const INLINE_COMPONENT_TAGS = new Set(['anchor', 'glossary']);
|
|
|
113208
113304
|
function isElementContentNode(node) {
|
|
113209
113305
|
return node.type === 'element' || node.type === 'text' || node.type === 'comment';
|
|
113210
113306
|
}
|
|
113307
|
+
/**
|
|
113308
|
+
* Components are assumed to be block-level, so whitespace between them can be removed
|
|
113309
|
+
* We want to remove them because it can be treated as actual children of the component
|
|
113310
|
+
* when it doesn't need to.
|
|
113311
|
+
*
|
|
113312
|
+
* It can be a problem because it can be passed as args to the components, like the ones
|
|
113313
|
+
* defined in /components, which can break the type assumptions of the components and cause
|
|
113314
|
+
* type errors, accessing properties that don't exist.
|
|
113315
|
+
*/
|
|
113316
|
+
function areAllChildrenComponents(children) {
|
|
113317
|
+
return children.every(child => {
|
|
113318
|
+
// Whitespace-only text nodes don't affect the check
|
|
113319
|
+
if (child.type === 'text' && !child.value.trim())
|
|
113320
|
+
return true;
|
|
113321
|
+
// Text with actual content means we have mixed content
|
|
113322
|
+
if (child.type === 'text')
|
|
113323
|
+
return false;
|
|
113324
|
+
// Comments don't affect the check
|
|
113325
|
+
if (child.type === 'comment')
|
|
113326
|
+
return true;
|
|
113327
|
+
// Standard HTML tags are not considered components
|
|
113328
|
+
if (child.type === 'element' && 'tagName' in child) {
|
|
113329
|
+
return !STANDARD_HTML_TAGS.has(child.tagName.toLowerCase());
|
|
113330
|
+
}
|
|
113331
|
+
return false;
|
|
113332
|
+
});
|
|
113333
|
+
}
|
|
113211
113334
|
/** Check if nodes represent a single paragraph with only text (no markdown formatting) */
|
|
113212
113335
|
function isSingleParagraphTextNode(nodes) {
|
|
113213
113336
|
return (nodes.length === 1 &&
|
|
@@ -113252,6 +113375,7 @@ function isActualHtmlTag(tagName, originalExcerpt) {
|
|
|
113252
113375
|
function parseTextChildren(node, processMarkdown) {
|
|
113253
113376
|
if (!node.children?.length)
|
|
113254
113377
|
return;
|
|
113378
|
+
// First pass: Recursively process text children as they may contain stringified markdown / mdx content
|
|
113255
113379
|
node.children = node.children.flatMap(child => {
|
|
113256
113380
|
if (child.type !== 'text' || !child.value.trim())
|
|
113257
113381
|
return [child];
|
|
@@ -113263,6 +113387,15 @@ function parseTextChildren(node, processMarkdown) {
|
|
|
113263
113387
|
}
|
|
113264
113388
|
return children;
|
|
113265
113389
|
});
|
|
113390
|
+
// Post-processing: remove whitespace-only text nodes if all siblings are components
|
|
113391
|
+
// This prevents whitespace between component children from being counted as extra children
|
|
113392
|
+
if (areAllChildrenComponents(node.children)) {
|
|
113393
|
+
node.children = node.children.filter(child => {
|
|
113394
|
+
if (child.type === 'text' && !child.value.trim())
|
|
113395
|
+
return false;
|
|
113396
|
+
return true;
|
|
113397
|
+
});
|
|
113398
|
+
}
|
|
113266
113399
|
}
|
|
113267
113400
|
/** Convert node properties from kebab-case/lowercase to camelCase */
|
|
113268
113401
|
function normalizeProperties(node) {
|
|
@@ -113325,6 +113458,15 @@ const mdxExpressionHandler = (_state, node) => ({
|
|
|
113325
113458
|
type: 'text',
|
|
113326
113459
|
value: node.value || '',
|
|
113327
113460
|
});
|
|
113461
|
+
// Since we serialize component / html tag attributes
|
|
113462
|
+
function decodeHtmlEntities(value) {
|
|
113463
|
+
return value
|
|
113464
|
+
.replace(/"/g, '"')
|
|
113465
|
+
.replace(/</g, '<')
|
|
113466
|
+
.replace(/>/g, '>')
|
|
113467
|
+
.replace(/ /g, '\n')
|
|
113468
|
+
.replace(/&/g, '&');
|
|
113469
|
+
}
|
|
113328
113470
|
// Convert MDX JSX elements to HAST elements, preserving attributes and children
|
|
113329
113471
|
const mdxJsxElementHandler = (state, node) => {
|
|
113330
113472
|
const { attributes = [], name } = node;
|
|
@@ -113336,7 +113478,7 @@ const mdxJsxElementHandler = (state, node) => {
|
|
|
113336
113478
|
properties[attribute.name] = true;
|
|
113337
113479
|
}
|
|
113338
113480
|
else if (typeof attribute.value === 'string') {
|
|
113339
|
-
properties[attribute.name] = attribute.value;
|
|
113481
|
+
properties[attribute.name] = decodeHtmlEntities(attribute.value);
|
|
113340
113482
|
}
|
|
113341
113483
|
else {
|
|
113342
113484
|
properties[attribute.name] = attribute.value.value;
|
|
@@ -113398,6 +113540,17 @@ function base64Decode(str) {
|
|
|
113398
113540
|
}
|
|
113399
113541
|
return decodeURIComponent(escape(atob(str)));
|
|
113400
113542
|
}
|
|
113543
|
+
function escapeHtmlAttribute(value) {
|
|
113544
|
+
return value
|
|
113545
|
+
.replace(/&/g, '&')
|
|
113546
|
+
.replace(/"/g, '"')
|
|
113547
|
+
.replace(/</g, '<')
|
|
113548
|
+
.replace(/>/g, '>')
|
|
113549
|
+
.replace(/\n/g, ' ');
|
|
113550
|
+
}
|
|
113551
|
+
// Marker prefix for JSON-serialized complex values (arrays/objects)
|
|
113552
|
+
// Using a prefix that won't conflict with regular string values
|
|
113553
|
+
const JSON_VALUE_MARKER = '__MDXISH_JSON__';
|
|
113401
113554
|
// Markers for protected HTMLBlock content (HTML comments avoid markdown parsing issues)
|
|
113402
113555
|
const HTML_BLOCK_CONTENT_START = '<!--RDMX_HTMLBLOCK:';
|
|
113403
113556
|
const HTML_BLOCK_CONTENT_END = ':RDMX_HTMLBLOCK-->';
|
|
@@ -113527,6 +113680,16 @@ function extractBalancedBraces(content, start) {
|
|
|
113527
113680
|
return null;
|
|
113528
113681
|
return { content: content.slice(start, pos - 1), end: pos };
|
|
113529
113682
|
}
|
|
113683
|
+
function restoreInlineCode(content, protectedCode) {
|
|
113684
|
+
return content.replace(/___INLINE_CODE_(\d+)___/g, (_m, idx) => {
|
|
113685
|
+
return protectedCode.inlineCode[parseInt(idx, 10)];
|
|
113686
|
+
});
|
|
113687
|
+
}
|
|
113688
|
+
function restoreCodeBlocks(content, protectedCode) {
|
|
113689
|
+
return content.replace(/___CODE_BLOCK_(\d+)___/g, (_m, idx) => {
|
|
113690
|
+
return protectedCode.codeBlocks[parseInt(idx, 10)];
|
|
113691
|
+
});
|
|
113692
|
+
}
|
|
113530
113693
|
/**
|
|
113531
113694
|
* Converts JSX attribute expressions (attribute={expression}) to HTML attributes (attribute="value").
|
|
113532
113695
|
* Handles style objects (camelCase → kebab-case), className → class, and JSON stringifies objects.
|
|
@@ -113542,7 +113705,7 @@ function extractBalancedBraces(content, start) {
|
|
|
113542
113705
|
* // Returns: '<a href="https://example.com">Link</a>'
|
|
113543
113706
|
* ```
|
|
113544
113707
|
*/
|
|
113545
|
-
function evaluateAttributeExpressions(content, context) {
|
|
113708
|
+
function evaluateAttributeExpressions(content, context, protectedCode) {
|
|
113546
113709
|
const attrStartRegex = /(\w+)=\{/g;
|
|
113547
113710
|
let result = '';
|
|
113548
113711
|
let lastEnd = 0;
|
|
@@ -113552,7 +113715,14 @@ function evaluateAttributeExpressions(content, context) {
|
|
|
113552
113715
|
const braceStart = match.index + match[0].length;
|
|
113553
113716
|
const extracted = extractBalancedBraces(content, braceStart);
|
|
113554
113717
|
if (extracted) {
|
|
113555
|
-
|
|
113718
|
+
// The expression might contain template literals in MDX component tag props
|
|
113719
|
+
// E.g. <Component greeting={`Hello World!`} />
|
|
113720
|
+
// that is marked as inline code. So we need to restore the inline codes
|
|
113721
|
+
// in the expression to evaluate it
|
|
113722
|
+
let expression = extracted.content;
|
|
113723
|
+
if (protectedCode) {
|
|
113724
|
+
expression = restoreInlineCode(expression, protectedCode);
|
|
113725
|
+
}
|
|
113556
113726
|
const fullMatchEnd = extracted.end;
|
|
113557
113727
|
result += content.slice(lastEnd, match.index);
|
|
113558
113728
|
try {
|
|
@@ -113568,14 +113738,20 @@ function evaluateAttributeExpressions(content, context) {
|
|
|
113568
113738
|
result += `style="${cssString}"`;
|
|
113569
113739
|
}
|
|
113570
113740
|
else {
|
|
113571
|
-
|
|
113741
|
+
// These are arrays / objects attribute values
|
|
113742
|
+
// Mark JSON-serialized values with a prefix so they can be parsed back correctly
|
|
113743
|
+
const jsonValue = escapeHtmlAttribute(JSON_VALUE_MARKER + JSON.stringify(evalResult));
|
|
113744
|
+
// Use double quotes so that multi-paragraph values are not split into multiple attributes by the processors
|
|
113745
|
+
result += `${attributeName}="${jsonValue}"`;
|
|
113572
113746
|
}
|
|
113573
113747
|
}
|
|
113574
113748
|
else if (attributeName === 'className') {
|
|
113575
|
-
|
|
113749
|
+
// Escape special characters so that it doesn't break and split the attribute value to nodes
|
|
113750
|
+
// This will be restored later in the pipeline
|
|
113751
|
+
result += `class="${escapeHtmlAttribute(String(evalResult))}"`;
|
|
113576
113752
|
}
|
|
113577
113753
|
else {
|
|
113578
|
-
result += `${attributeName}="${evalResult}"`;
|
|
113754
|
+
result += `${attributeName}="${escapeHtmlAttribute(String(evalResult))}"`;
|
|
113579
113755
|
}
|
|
113580
113756
|
}
|
|
113581
113757
|
catch (_error) {
|
|
@@ -113606,13 +113782,9 @@ function evaluateAttributeExpressions(content, context) {
|
|
|
113606
113782
|
* // Returns: 'Text with `inline` and ```js\ncode\n```'
|
|
113607
113783
|
* ```
|
|
113608
113784
|
*/
|
|
113609
|
-
function
|
|
113610
|
-
let restored = content
|
|
113611
|
-
|
|
113612
|
-
});
|
|
113613
|
-
restored = restored.replace(/___INLINE_CODE_(\d+)___/g, (_match, index) => {
|
|
113614
|
-
return protectedCode.inlineCode[parseInt(index, 10)];
|
|
113615
|
-
});
|
|
113785
|
+
function restoreProtectedCodes(content, protectedCode) {
|
|
113786
|
+
let restored = restoreCodeBlocks(content, protectedCode);
|
|
113787
|
+
restored = restoreInlineCode(restored, protectedCode);
|
|
113616
113788
|
return restored;
|
|
113617
113789
|
}
|
|
113618
113790
|
/**
|
|
@@ -113633,9 +113805,9 @@ function preprocessJSXExpressions(content, context = {}) {
|
|
|
113633
113805
|
// Step 3: Evaluate attribute expressions (JSX attribute syntax: href={baseUrl})
|
|
113634
113806
|
// For inline expressions, we use a library to parse the expression & evaluate it later
|
|
113635
113807
|
// For attribute expressions, it was difficult to use a library to parse them, so do it manually
|
|
113636
|
-
processed = evaluateAttributeExpressions(processed, context);
|
|
113808
|
+
processed = evaluateAttributeExpressions(processed, context, protectedCode);
|
|
113637
113809
|
// Step 4: Restore protected code blocks
|
|
113638
|
-
processed =
|
|
113810
|
+
processed = restoreProtectedCodes(processed, protectedCode);
|
|
113639
113811
|
return processed;
|
|
113640
113812
|
}
|
|
113641
113813
|
|
|
@@ -114047,7 +114219,7 @@ const wrapPinnedBlocks = (node, json) => {
|
|
|
114047
114219
|
return node;
|
|
114048
114220
|
return {
|
|
114049
114221
|
children: [node],
|
|
114050
|
-
data: {
|
|
114222
|
+
data: { hName: 'rdme-pin', hProperties: { className: 'pin' } },
|
|
114051
114223
|
type: 'rdme-pin',
|
|
114052
114224
|
};
|
|
114053
114225
|
};
|
|
@@ -114135,15 +114307,16 @@ function parseMagicBlock(raw, options = {}) {
|
|
|
114135
114307
|
type: 'code',
|
|
114136
114308
|
value: obj.code.trim(),
|
|
114137
114309
|
}));
|
|
114138
|
-
// Single code block without a tab name renders as a plain code block
|
|
114310
|
+
// Single code block without a tab name (meta or language) renders as a plain code block
|
|
114311
|
+
// Otherwise, we want to render it as a code tabs block
|
|
114139
114312
|
if (children.length === 1) {
|
|
114140
114313
|
if (!children[0].value)
|
|
114141
114314
|
return [];
|
|
114142
|
-
if (children[0].meta)
|
|
114315
|
+
if (!(children[0].meta || children[0].lang))
|
|
114143
114316
|
return [wrapPinnedBlocks(children[0], json)];
|
|
114144
114317
|
}
|
|
114145
|
-
// Multiple code blocks
|
|
114146
|
-
return [wrapPinnedBlocks({ children, className: 'tabs', data: { hName: '
|
|
114318
|
+
// Multiple code blocks or a single code block with a tab name (meta or language) renders as a code tabs block
|
|
114319
|
+
return [wrapPinnedBlocks({ children, className: 'tabs', data: { hName: 'CodeTabs' }, type: 'code-tabs' }, json)];
|
|
114147
114320
|
}
|
|
114148
114321
|
// API header: renders as a heading element (h1-h6)
|
|
114149
114322
|
case 'api-header': {
|
|
@@ -114344,11 +114517,31 @@ function parseMagicBlock(raw, options = {}) {
|
|
|
114344
114517
|
}
|
|
114345
114518
|
}
|
|
114346
114519
|
/**
|
|
114347
|
-
*
|
|
114520
|
+
* Block-level node types that cannot be nested inside paragraphs.
|
|
114348
114521
|
*/
|
|
114349
|
-
const
|
|
114350
|
-
|
|
114351
|
-
|
|
114522
|
+
const blockTypes = [
|
|
114523
|
+
'heading',
|
|
114524
|
+
'code',
|
|
114525
|
+
'code-tabs',
|
|
114526
|
+
'paragraph',
|
|
114527
|
+
'blockquote',
|
|
114528
|
+
'list',
|
|
114529
|
+
'table',
|
|
114530
|
+
'thematicBreak',
|
|
114531
|
+
'html',
|
|
114532
|
+
'yaml',
|
|
114533
|
+
'toml',
|
|
114534
|
+
'rdme-pin',
|
|
114535
|
+
'rdme-callout',
|
|
114536
|
+
'html-block',
|
|
114537
|
+
'embed',
|
|
114538
|
+
'figure',
|
|
114539
|
+
'mdxJsxFlowElement',
|
|
114540
|
+
];
|
|
114541
|
+
/**
|
|
114542
|
+
* Check if a node is a block-level node (cannot be inside a paragraph)
|
|
114543
|
+
*/
|
|
114544
|
+
const isBlockNode = (node) => blockTypes.includes(node.type);
|
|
114352
114545
|
/**
|
|
114353
114546
|
* Unified plugin that restores magic blocks from placeholder tokens.
|
|
114354
114547
|
*
|
|
@@ -114361,8 +114554,9 @@ const magicBlockRestorer = ({ blocks }) => tree => {
|
|
|
114361
114554
|
return;
|
|
114362
114555
|
// Map: key → original raw magic block content
|
|
114363
114556
|
const magicBlockKeys = new Map(blocks.map(({ key, raw }) => [key, raw]));
|
|
114364
|
-
//
|
|
114365
|
-
const
|
|
114557
|
+
// Collect replacements to apply (we need to visit in reverse to maintain indices)
|
|
114558
|
+
const replacements = [];
|
|
114559
|
+
// First pass: collect all replacements
|
|
114366
114560
|
visit(tree, 'inlineCode', (node, index, parent) => {
|
|
114367
114561
|
if (!parent || index == null)
|
|
114368
114562
|
return undefined;
|
|
@@ -114372,31 +114566,73 @@ const magicBlockRestorer = ({ blocks }) => tree => {
|
|
|
114372
114566
|
const children = parseMagicBlock(raw);
|
|
114373
114567
|
if (!children.length)
|
|
114374
114568
|
return undefined;
|
|
114375
|
-
|
|
114376
|
-
|
|
114377
|
-
|
|
114378
|
-
|
|
114379
|
-
|
|
114380
|
-
|
|
114381
|
-
|
|
114569
|
+
// If parent is a paragraph and we're inserting block nodes (which must not be in paragraphs), lift them out
|
|
114570
|
+
if (parent.type === 'paragraph' && children.some(child => isBlockNode(child))) {
|
|
114571
|
+
const blockNodes = [];
|
|
114572
|
+
const inlineNodes = [];
|
|
114573
|
+
// Separate block and inline nodes
|
|
114574
|
+
children.forEach(child => {
|
|
114575
|
+
if (isBlockNode(child)) {
|
|
114576
|
+
blockNodes.push(child);
|
|
114382
114577
|
}
|
|
114383
|
-
|
|
114384
|
-
|
|
114385
|
-
if (paragraphParent) {
|
|
114386
|
-
const paragraphIndex = paragraphParent.children.indexOf(parent);
|
|
114387
|
-
if (paragraphIndex !== -1) {
|
|
114388
|
-
modifications.push({ children, index: paragraphIndex, parent: paragraphParent });
|
|
114578
|
+
else {
|
|
114579
|
+
inlineNodes.push(child);
|
|
114389
114580
|
}
|
|
114390
|
-
}
|
|
114391
|
-
|
|
114581
|
+
});
|
|
114582
|
+
const before = parent.children.slice(0, index);
|
|
114583
|
+
const after = parent.children.slice(index + 1);
|
|
114584
|
+
replacements.push({
|
|
114585
|
+
parent,
|
|
114586
|
+
blockNodes,
|
|
114587
|
+
inlineNodes,
|
|
114588
|
+
before,
|
|
114589
|
+
after,
|
|
114590
|
+
});
|
|
114392
114591
|
}
|
|
114393
|
-
|
|
114394
|
-
|
|
114395
|
-
|
|
114396
|
-
|
|
114397
|
-
|
|
114398
|
-
parent.children.splice(index, 1, ...children);
|
|
114592
|
+
else {
|
|
114593
|
+
// Normal case: just replace the inlineCode with the children
|
|
114594
|
+
parent.children.splice(index, 1, ...children);
|
|
114595
|
+
}
|
|
114596
|
+
return undefined;
|
|
114399
114597
|
});
|
|
114598
|
+
// Second pass: apply replacements that require lifting block nodes out of paragraphs
|
|
114599
|
+
// Process in reverse order to maintain correct indices
|
|
114600
|
+
for (let i = replacements.length - 1; i >= 0; i -= 1) {
|
|
114601
|
+
const { after, before, blockNodes, inlineNodes, parent } = replacements[i];
|
|
114602
|
+
// Find the paragraph's position in the root
|
|
114603
|
+
const rootChildren = tree.children;
|
|
114604
|
+
const paraIndex = rootChildren.findIndex(child => child === parent);
|
|
114605
|
+
if (paraIndex === -1) {
|
|
114606
|
+
// Paragraph not found in root - fall back to normal replacement
|
|
114607
|
+
// This shouldn't happen normally, but handle it gracefully
|
|
114608
|
+
// Reconstruct the original index from before.length
|
|
114609
|
+
const originalIndex = before.length;
|
|
114610
|
+
parent.children.splice(originalIndex, 1, ...blockNodes, ...inlineNodes);
|
|
114611
|
+
// eslint-disable-next-line no-continue
|
|
114612
|
+
continue;
|
|
114613
|
+
}
|
|
114614
|
+
// Update or remove the paragraph
|
|
114615
|
+
if (inlineNodes.length > 0) {
|
|
114616
|
+
// Keep paragraph with inline nodes
|
|
114617
|
+
parent.children = [...before, ...inlineNodes, ...after];
|
|
114618
|
+
// Insert block nodes after the paragraph
|
|
114619
|
+
if (blockNodes.length > 0) {
|
|
114620
|
+
rootChildren.splice(paraIndex + 1, 0, ...blockNodes);
|
|
114621
|
+
}
|
|
114622
|
+
}
|
|
114623
|
+
else if (before.length === 0 && after.length === 0) {
|
|
114624
|
+
// Remove empty paragraph and replace with block nodes
|
|
114625
|
+
rootChildren.splice(paraIndex, 1, ...blockNodes);
|
|
114626
|
+
}
|
|
114627
|
+
else {
|
|
114628
|
+
// Keep paragraph with remaining content
|
|
114629
|
+
parent.children = [...before, ...after];
|
|
114630
|
+
// Insert block nodes after the paragraph
|
|
114631
|
+
if (blockNodes.length > 0) {
|
|
114632
|
+
rootChildren.splice(paraIndex + 1, 0, ...blockNodes);
|
|
114633
|
+
}
|
|
114634
|
+
}
|
|
114635
|
+
}
|
|
114400
114636
|
};
|
|
114401
114637
|
/* harmony default export */ const mdxish_magic_blocks = (magicBlockRestorer);
|
|
114402
114638
|
|
|
@@ -115250,6 +115486,40 @@ const makeUseMDXComponents = (more = {}) => {
|
|
|
115250
115486
|
|
|
115251
115487
|
|
|
115252
115488
|
|
|
115489
|
+
|
|
115490
|
+
/**
|
|
115491
|
+
* Parse JSON-marked string values in props back to their original types.
|
|
115492
|
+
* This handles arrays and objects that were serialized during JSX preprocessing.
|
|
115493
|
+
*/
|
|
115494
|
+
function parseJsonProps(props) {
|
|
115495
|
+
if (!props)
|
|
115496
|
+
return props;
|
|
115497
|
+
const parsed = {};
|
|
115498
|
+
Object.entries(props).forEach(([key, value]) => {
|
|
115499
|
+
if (typeof value === 'string' && value.startsWith(JSON_VALUE_MARKER)) {
|
|
115500
|
+
try {
|
|
115501
|
+
parsed[key] = JSON.parse(value.slice(JSON_VALUE_MARKER.length));
|
|
115502
|
+
}
|
|
115503
|
+
catch {
|
|
115504
|
+
// If parsing fails, use the value without the marker
|
|
115505
|
+
parsed[key] = value.slice(JSON_VALUE_MARKER.length);
|
|
115506
|
+
}
|
|
115507
|
+
}
|
|
115508
|
+
else {
|
|
115509
|
+
parsed[key] = value;
|
|
115510
|
+
}
|
|
115511
|
+
});
|
|
115512
|
+
return parsed;
|
|
115513
|
+
}
|
|
115514
|
+
/**
|
|
115515
|
+
* Custom createElement wrapper that parses JSON-marked string props.
|
|
115516
|
+
* This is needed because rehype-react converts HAST to React, but complex
|
|
115517
|
+
* types (arrays/objects) get serialized to strings during markdown parsing.
|
|
115518
|
+
*/
|
|
115519
|
+
function createElementWithJsonProps(type, props, ...children) {
|
|
115520
|
+
const parsedProps = parseJsonProps(props);
|
|
115521
|
+
return external_react_default().createElement(type, parsedProps, ...children);
|
|
115522
|
+
}
|
|
115253
115523
|
/** Flatten CustomComponents into a component map for rehype-react */
|
|
115254
115524
|
function exportComponentsForRehype(components) {
|
|
115255
115525
|
const exported = Object.entries(components).reduce((memo, [tag, mod]) => {
|
|
@@ -115281,7 +115551,7 @@ function exportComponentsForRehype(components) {
|
|
|
115281
115551
|
function createRehypeReactProcessor(components) {
|
|
115282
115552
|
// @ts-expect-error - rehype-react types are incompatible with React.Fragment return type
|
|
115283
115553
|
return unified().use((rehype_react_default()), {
|
|
115284
|
-
createElement:
|
|
115554
|
+
createElement: createElementWithJsonProps,
|
|
115285
115555
|
Fragment: (external_react_default()).Fragment,
|
|
115286
115556
|
components,
|
|
115287
115557
|
});
|