@readme/markdown 11.14.1 → 12.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +1 -1
- package/dist/lib/index.d.ts +0 -1
- package/dist/main.js +459 -187
- package/dist/main.node.js +459 -187
- package/dist/main.node.js.map +1 -1
- package/dist/processor/transform/extract-text.d.ts +14 -0
- package/dist/processor/transform/mdxish/normalize-table-separator.d.ts +23 -0
- package/dist/processor/transform/mdxish/preprocess-jsx-expressions.d.ts +1 -0
- package/package.json +2 -2
- package/dist/lib/utils/isPlainText.d.ts +0 -9
package/dist/main.node.js
CHANGED
|
@@ -19024,7 +19024,6 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
19024
19024
|
exports: () => (/* reexport */ lib_exports),
|
|
19025
19025
|
gemojiRegex: () => (/* reexport */ gemoji_regex),
|
|
19026
19026
|
hast: () => (/* reexport */ lib_hast),
|
|
19027
|
-
isPlainText: () => (/* reexport */ isPlainText),
|
|
19028
19027
|
mdast: () => (/* reexport */ lib_mdast),
|
|
19029
19028
|
mdastV6: () => (/* reexport */ lib_mdastV6),
|
|
19030
19029
|
mdx: () => (/* reexport */ lib_mdx),
|
|
@@ -73148,12 +73147,40 @@ const plain = (node, opts = {}) => {
|
|
|
73148
73147
|
};
|
|
73149
73148
|
/* harmony default export */ const lib_plain = (plain);
|
|
73150
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
|
+
if (node.children && Array.isArray(node.children)) {
|
|
73165
|
+
return node.children
|
|
73166
|
+
.map(child => {
|
|
73167
|
+
if (child && typeof child === 'object' && 'type' in child) {
|
|
73168
|
+
return extractText(child);
|
|
73169
|
+
}
|
|
73170
|
+
return '';
|
|
73171
|
+
})
|
|
73172
|
+
.join('');
|
|
73173
|
+
}
|
|
73174
|
+
return '';
|
|
73175
|
+
};
|
|
73176
|
+
|
|
73151
73177
|
;// ./processor/transform/callouts.ts
|
|
73152
73178
|
|
|
73153
73179
|
|
|
73154
73180
|
|
|
73155
73181
|
|
|
73156
73182
|
|
|
73183
|
+
|
|
73157
73184
|
const callouts_regex = `^(${emoji_regex().source}|⚠)(\\s+|$)`;
|
|
73158
73185
|
const findFirst = (node) => {
|
|
73159
73186
|
if ('children' in node)
|
|
@@ -73174,42 +73201,76 @@ const wrapHeading = (node) => {
|
|
|
73174
73201
|
},
|
|
73175
73202
|
};
|
|
73176
73203
|
};
|
|
73204
|
+
/**
|
|
73205
|
+
* Checks if a blockquote matches the expected callout structure:
|
|
73206
|
+
* blockquote > paragraph > text node
|
|
73207
|
+
*/
|
|
73208
|
+
const isCalloutStructure = (node) => {
|
|
73209
|
+
const firstChild = node.children?.[0];
|
|
73210
|
+
if (!firstChild || firstChild.type !== 'paragraph')
|
|
73211
|
+
return false;
|
|
73212
|
+
if (!('children' in firstChild))
|
|
73213
|
+
return false;
|
|
73214
|
+
const firstTextChild = firstChild.children?.[0];
|
|
73215
|
+
return firstTextChild?.type === 'text';
|
|
73216
|
+
};
|
|
73217
|
+
const processBlockquote = (node, index, parent) => {
|
|
73218
|
+
if (!isCalloutStructure(node)) {
|
|
73219
|
+
// Only stringify empty blockquotes (no extractable text content)
|
|
73220
|
+
// Preserve blockquotes with actual content (e.g., headings, lists, etc.)
|
|
73221
|
+
const content = extractText(node);
|
|
73222
|
+
const isEmpty = !content || content.trim() === '';
|
|
73223
|
+
if (isEmpty && index !== undefined && parent) {
|
|
73224
|
+
const textNode = {
|
|
73225
|
+
type: 'text',
|
|
73226
|
+
value: '>',
|
|
73227
|
+
};
|
|
73228
|
+
const paragraphNode = {
|
|
73229
|
+
type: 'paragraph',
|
|
73230
|
+
children: [textNode],
|
|
73231
|
+
position: node.position,
|
|
73232
|
+
};
|
|
73233
|
+
parent.children.splice(index, 1, paragraphNode);
|
|
73234
|
+
}
|
|
73235
|
+
return;
|
|
73236
|
+
}
|
|
73237
|
+
// isCalloutStructure ensures node.children[0] is a Paragraph with children
|
|
73238
|
+
const firstParagraph = node.children[0];
|
|
73239
|
+
const startText = lib_plain(firstParagraph).toString();
|
|
73240
|
+
const [match, icon] = startText.match(callouts_regex) || [];
|
|
73241
|
+
if (icon && match) {
|
|
73242
|
+
const heading = startText.slice(match.length);
|
|
73243
|
+
const empty = !heading.length && firstParagraph.children.length === 1;
|
|
73244
|
+
const theme = themes[icon] || 'default';
|
|
73245
|
+
const firstChild = findFirst(node.children[0]);
|
|
73246
|
+
if (firstChild && 'value' in firstChild && typeof firstChild.value === 'string') {
|
|
73247
|
+
firstChild.value = firstChild.value.slice(match.length);
|
|
73248
|
+
}
|
|
73249
|
+
if (heading) {
|
|
73250
|
+
node.children[0] = wrapHeading(node);
|
|
73251
|
+
// @note: We add to the offset/column the length of the unicode
|
|
73252
|
+
// character that was stripped off, so that the start position of the
|
|
73253
|
+
// heading/text matches where it actually starts.
|
|
73254
|
+
node.children[0].position.start.offset += match.length;
|
|
73255
|
+
node.children[0].position.start.column += match.length;
|
|
73256
|
+
}
|
|
73257
|
+
Object.assign(node, {
|
|
73258
|
+
type: NodeTypes.callout,
|
|
73259
|
+
data: {
|
|
73260
|
+
hName: 'Callout',
|
|
73261
|
+
hProperties: {
|
|
73262
|
+
icon,
|
|
73263
|
+
...(empty && { empty }),
|
|
73264
|
+
theme,
|
|
73265
|
+
},
|
|
73266
|
+
},
|
|
73267
|
+
});
|
|
73268
|
+
}
|
|
73269
|
+
};
|
|
73177
73270
|
const calloutTransformer = () => {
|
|
73178
73271
|
return (tree) => {
|
|
73179
|
-
visit(tree, 'blockquote', (node) => {
|
|
73180
|
-
|
|
73181
|
-
return;
|
|
73182
|
-
// @ts-expect-error -- @todo: update plain to accept mdast
|
|
73183
|
-
const startText = lib_plain(node.children[0]).toString();
|
|
73184
|
-
const [match, icon] = startText.match(callouts_regex) || [];
|
|
73185
|
-
if (icon && match) {
|
|
73186
|
-
const heading = startText.slice(match.length);
|
|
73187
|
-
const empty = !heading.length && node.children[0].children.length === 1;
|
|
73188
|
-
const theme = themes[icon] || 'default';
|
|
73189
|
-
const firstChild = findFirst(node.children[0]);
|
|
73190
|
-
if (firstChild && 'value' in firstChild && typeof firstChild.value === 'string') {
|
|
73191
|
-
firstChild.value = firstChild.value.slice(match.length);
|
|
73192
|
-
}
|
|
73193
|
-
if (heading) {
|
|
73194
|
-
node.children[0] = wrapHeading(node);
|
|
73195
|
-
// @note: We add to the offset/column the length of the unicode
|
|
73196
|
-
// character that was stripped off, so that the start position of the
|
|
73197
|
-
// heading/text matches where it actually starts.
|
|
73198
|
-
node.children[0].position.start.offset += match.length;
|
|
73199
|
-
node.children[0].position.start.column += match.length;
|
|
73200
|
-
}
|
|
73201
|
-
Object.assign(node, {
|
|
73202
|
-
type: NodeTypes.callout,
|
|
73203
|
-
data: {
|
|
73204
|
-
hName: 'Callout',
|
|
73205
|
-
hProperties: {
|
|
73206
|
-
icon,
|
|
73207
|
-
...(empty && { empty }),
|
|
73208
|
-
theme,
|
|
73209
|
-
},
|
|
73210
|
-
},
|
|
73211
|
-
});
|
|
73212
|
-
}
|
|
73272
|
+
visit(tree, 'blockquote', (node, index, parent) => {
|
|
73273
|
+
processBlockquote(node, index, parent);
|
|
73213
73274
|
});
|
|
73214
73275
|
};
|
|
73215
73276
|
};
|
|
@@ -90825,10 +90886,13 @@ const tagAttributePattern = /([a-zA-Z_:][-a-zA-Z0-9_:.]*)(?:\s*=\s*("[^"]*"|'[^'
|
|
|
90825
90886
|
const MAX_LOOKAHEAD = 30;
|
|
90826
90887
|
/**
|
|
90827
90888
|
* Tags that have dedicated transformers and should NOT be handled by this plugin.
|
|
90828
|
-
* These components have special parsing requirements that the generic component
|
|
90829
|
-
* block transformer cannot handle correctly
|
|
90889
|
+
* These components either have special parsing requirements that the generic component
|
|
90890
|
+
* block transformer cannot handle correctly, or are inline components that we don't
|
|
90891
|
+
* want to convert to mdxJsxFlowElement which is a block level element.
|
|
90892
|
+
*
|
|
90893
|
+
* Glossary and Anchor are inline components.
|
|
90830
90894
|
*/
|
|
90831
|
-
const EXCLUDED_TAGS = new Set(['HTMLBlock', 'Table']);
|
|
90895
|
+
const EXCLUDED_TAGS = new Set(['HTMLBlock', 'Table', 'Glossary', 'Anchor']);
|
|
90832
90896
|
const inlineMdProcessor = unified().use(remarkParse);
|
|
90833
90897
|
const isClosingTag = (value, tag) => value.trim() === `</${tag}>`;
|
|
90834
90898
|
/**
|
|
@@ -90872,6 +90936,17 @@ const parseTag = (value) => {
|
|
|
90872
90936
|
contentAfterTag,
|
|
90873
90937
|
};
|
|
90874
90938
|
};
|
|
90939
|
+
/**
|
|
90940
|
+
* Parse substring content of a node and update the parent's children to include the new nodes.
|
|
90941
|
+
*/
|
|
90942
|
+
const parseSibling = (stack, parent, index, sibling) => {
|
|
90943
|
+
const siblingNodes = parseMdChildren(sibling);
|
|
90944
|
+
// The new sibling nodes might contain new components to be processed
|
|
90945
|
+
if (siblingNodes.length > 0) {
|
|
90946
|
+
parent.children.splice(index + 1, 0, ...siblingNodes);
|
|
90947
|
+
stack.push(parent);
|
|
90948
|
+
}
|
|
90949
|
+
};
|
|
90875
90950
|
/**
|
|
90876
90951
|
* Create an MdxJsxFlowElement node from component data.
|
|
90877
90952
|
*/
|
|
@@ -91014,11 +91089,12 @@ const mdxishComponentBlocks = () => tree => {
|
|
|
91014
91089
|
if ('children' in node && Array.isArray(node.children)) {
|
|
91015
91090
|
stack.push(node);
|
|
91016
91091
|
}
|
|
91017
|
-
// Only visit HTML nodes with an actual html tag
|
|
91092
|
+
// Only visit HTML nodes with an actual html tag,
|
|
91093
|
+
// which means a potential unparsed MDX component
|
|
91018
91094
|
const value = node.value;
|
|
91019
91095
|
if (node.type !== 'html' || typeof value !== 'string')
|
|
91020
91096
|
return;
|
|
91021
|
-
const parsed = parseTag(value);
|
|
91097
|
+
const parsed = parseTag(value.trim());
|
|
91022
91098
|
if (!parsed)
|
|
91023
91099
|
return;
|
|
91024
91100
|
const { tag, attributes, selfClosing, contentAfterTag = '' } = parsed;
|
|
@@ -91035,11 +91111,19 @@ const mdxishComponentBlocks = () => tree => {
|
|
|
91035
91111
|
startPosition: node.position,
|
|
91036
91112
|
});
|
|
91037
91113
|
substituteNodeWithMdxNode(parent, index, componentNode);
|
|
91114
|
+
// Check and parse if there's relevant content after the current closing tag
|
|
91115
|
+
const remainingContent = contentAfterTag.trim();
|
|
91116
|
+
if (remainingContent) {
|
|
91117
|
+
parseSibling(stack, parent, index, remainingContent);
|
|
91118
|
+
}
|
|
91038
91119
|
return;
|
|
91039
91120
|
}
|
|
91040
91121
|
// Case 2: Self-contained block (closing tag in content)
|
|
91041
91122
|
if (contentAfterTag.includes(closingTagStr)) {
|
|
91042
|
-
|
|
91123
|
+
// Find the first closing tag
|
|
91124
|
+
const closingTagIndex = contentAfterTag.indexOf(closingTagStr);
|
|
91125
|
+
const componentInnerContent = contentAfterTag.substring(0, closingTagIndex).trim();
|
|
91126
|
+
const contentAfterClose = contentAfterTag.substring(closingTagIndex + closingTagStr.length).trim();
|
|
91043
91127
|
const componentNode = createComponentNode({
|
|
91044
91128
|
tag,
|
|
91045
91129
|
attributes,
|
|
@@ -91047,6 +91131,14 @@ const mdxishComponentBlocks = () => tree => {
|
|
|
91047
91131
|
startPosition: node.position,
|
|
91048
91132
|
});
|
|
91049
91133
|
substituteNodeWithMdxNode(parent, index, componentNode);
|
|
91134
|
+
// After the closing tag, there might be more content to be processed
|
|
91135
|
+
if (contentAfterClose) {
|
|
91136
|
+
parseSibling(stack, parent, index, contentAfterClose);
|
|
91137
|
+
}
|
|
91138
|
+
else if (componentNode.children.length > 0) {
|
|
91139
|
+
// The content inside the component block might contain new components to be processed
|
|
91140
|
+
stack.push(componentNode);
|
|
91141
|
+
}
|
|
91050
91142
|
return;
|
|
91051
91143
|
}
|
|
91052
91144
|
// Case 3: Multi-sibling component (closing tag in a following sibling)
|
|
@@ -91072,8 +91164,13 @@ const mdxishComponentBlocks = () => tree => {
|
|
|
91072
91164
|
});
|
|
91073
91165
|
// Remove all nodes from opening tag to closing tag (inclusive) and replace with component node
|
|
91074
91166
|
parent.children.splice(index, closingIndex - index + 1, componentNode);
|
|
91167
|
+
// Since we might be merging sibling nodes together and combining content,
|
|
91168
|
+
// there might be new components to process
|
|
91169
|
+
if (componentNode.children.length > 0) {
|
|
91170
|
+
stack.push(componentNode);
|
|
91171
|
+
}
|
|
91075
91172
|
};
|
|
91076
|
-
//
|
|
91173
|
+
// Process the nodes with the components depth-first to maintain the correct order of the nodes
|
|
91077
91174
|
while (stack.length) {
|
|
91078
91175
|
const parent = stack.pop();
|
|
91079
91176
|
if (parent?.children) {
|
|
@@ -91094,6 +91191,7 @@ const mdxishComponentBlocks = () => tree => {
|
|
|
91094
91191
|
|
|
91095
91192
|
|
|
91096
91193
|
|
|
91194
|
+
|
|
91097
91195
|
const isTableCell = (node) => isMDXElement(node) && ['th', 'td'].includes(node.name);
|
|
91098
91196
|
const tableTypes = {
|
|
91099
91197
|
tr: 'tableRow',
|
|
@@ -91118,18 +91216,13 @@ const isTextOnly = (children) => {
|
|
|
91118
91216
|
});
|
|
91119
91217
|
};
|
|
91120
91218
|
/**
|
|
91121
|
-
*
|
|
91219
|
+
* Convenience wrapper that extracts text content from an array of children nodes.
|
|
91122
91220
|
*/
|
|
91123
|
-
const
|
|
91221
|
+
const extractTextFromChildren = (children) => {
|
|
91124
91222
|
return children
|
|
91125
91223
|
.map(child => {
|
|
91126
91224
|
if (child && typeof child === 'object' && 'type' in child) {
|
|
91127
|
-
|
|
91128
|
-
return child.value;
|
|
91129
|
-
}
|
|
91130
|
-
if (child.type === 'mdxJsxTextElement' && 'children' in child && Array.isArray(child.children)) {
|
|
91131
|
-
return extractText(child.children);
|
|
91132
|
-
}
|
|
91225
|
+
return extractText(child);
|
|
91133
91226
|
}
|
|
91134
91227
|
return '';
|
|
91135
91228
|
})
|
|
@@ -91160,7 +91253,7 @@ const processTableNode = (node, index, parent) => {
|
|
|
91160
91253
|
let parsedChildren = cellChildren;
|
|
91161
91254
|
// If cell contains only text nodes, try to re-parse as markdown
|
|
91162
91255
|
if (isTextOnly(cellChildren)) {
|
|
91163
|
-
const textContent =
|
|
91256
|
+
const textContent = extractTextFromChildren(cellChildren);
|
|
91164
91257
|
if (textContent.trim()) {
|
|
91165
91258
|
try {
|
|
91166
91259
|
const parsed = parseMarkdown(textContent);
|
|
@@ -106575,7 +106668,7 @@ const CUSTOM_PROP_BOUNDARIES = [
|
|
|
106575
106668
|
/**
|
|
106576
106669
|
* Tags that should be passed through and handled at runtime (not by the mdxish plugin)
|
|
106577
106670
|
*/
|
|
106578
|
-
const RUNTIME_COMPONENT_TAGS = new Set(['Variable', 'variable']);
|
|
106671
|
+
const RUNTIME_COMPONENT_TAGS = new Set(['Variable', 'variable', 'rdme-pin']);
|
|
106579
106672
|
/**
|
|
106580
106673
|
* Standard HTML tags that should never be treated as custom components.
|
|
106581
106674
|
* Uses the html-tags package, converted to a Set<string> for efficient lookups.
|
|
@@ -113206,6 +113299,33 @@ const INLINE_COMPONENT_TAGS = new Set(['anchor', 'glossary']);
|
|
|
113206
113299
|
function isElementContentNode(node) {
|
|
113207
113300
|
return node.type === 'element' || node.type === 'text' || node.type === 'comment';
|
|
113208
113301
|
}
|
|
113302
|
+
/**
|
|
113303
|
+
* Components are assumed to be block-level, so whitespace between them can be removed
|
|
113304
|
+
* We want to remove them because it can be treated as actual children of the component
|
|
113305
|
+
* when it doesn't need to.
|
|
113306
|
+
*
|
|
113307
|
+
* It can be a problem because it can be passed as args to the components, like the ones
|
|
113308
|
+
* defined in /components, which can break the type assumptions of the components and cause
|
|
113309
|
+
* type errors, accessing properties that don't exist.
|
|
113310
|
+
*/
|
|
113311
|
+
function areAllChildrenComponents(children) {
|
|
113312
|
+
return children.every(child => {
|
|
113313
|
+
// Whitespace-only text nodes don't affect the check
|
|
113314
|
+
if (child.type === 'text' && !child.value.trim())
|
|
113315
|
+
return true;
|
|
113316
|
+
// Text with actual content means we have mixed content
|
|
113317
|
+
if (child.type === 'text')
|
|
113318
|
+
return false;
|
|
113319
|
+
// Comments don't affect the check
|
|
113320
|
+
if (child.type === 'comment')
|
|
113321
|
+
return true;
|
|
113322
|
+
// Standard HTML tags are not considered components
|
|
113323
|
+
if (child.type === 'element' && 'tagName' in child) {
|
|
113324
|
+
return !STANDARD_HTML_TAGS.has(child.tagName.toLowerCase());
|
|
113325
|
+
}
|
|
113326
|
+
return false;
|
|
113327
|
+
});
|
|
113328
|
+
}
|
|
113209
113329
|
/** Check if nodes represent a single paragraph with only text (no markdown formatting) */
|
|
113210
113330
|
function isSingleParagraphTextNode(nodes) {
|
|
113211
113331
|
return (nodes.length === 1 &&
|
|
@@ -113250,6 +113370,7 @@ function isActualHtmlTag(tagName, originalExcerpt) {
|
|
|
113250
113370
|
function parseTextChildren(node, processMarkdown) {
|
|
113251
113371
|
if (!node.children?.length)
|
|
113252
113372
|
return;
|
|
113373
|
+
// First pass: Recursively process text children as they may contain stringified markdown / mdx content
|
|
113253
113374
|
node.children = node.children.flatMap(child => {
|
|
113254
113375
|
if (child.type !== 'text' || !child.value.trim())
|
|
113255
113376
|
return [child];
|
|
@@ -113261,6 +113382,15 @@ function parseTextChildren(node, processMarkdown) {
|
|
|
113261
113382
|
}
|
|
113262
113383
|
return children;
|
|
113263
113384
|
});
|
|
113385
|
+
// Post-processing: remove whitespace-only text nodes if all siblings are components
|
|
113386
|
+
// This prevents whitespace between component children from being counted as extra children
|
|
113387
|
+
if (areAllChildrenComponents(node.children)) {
|
|
113388
|
+
node.children = node.children.filter(child => {
|
|
113389
|
+
if (child.type === 'text' && !child.value.trim())
|
|
113390
|
+
return false;
|
|
113391
|
+
return true;
|
|
113392
|
+
});
|
|
113393
|
+
}
|
|
113264
113394
|
}
|
|
113265
113395
|
/** Convert node properties from kebab-case/lowercase to camelCase */
|
|
113266
113396
|
function normalizeProperties(node) {
|
|
@@ -113323,6 +113453,15 @@ const mdxExpressionHandler = (_state, node) => ({
|
|
|
113323
113453
|
type: 'text',
|
|
113324
113454
|
value: node.value || '',
|
|
113325
113455
|
});
|
|
113456
|
+
// Since we serialize component / html tag attributes
|
|
113457
|
+
function decodeHtmlEntities(value) {
|
|
113458
|
+
return value
|
|
113459
|
+
.replace(/"/g, '"')
|
|
113460
|
+
.replace(/</g, '<')
|
|
113461
|
+
.replace(/>/g, '>')
|
|
113462
|
+
.replace(/ /g, '\n')
|
|
113463
|
+
.replace(/&/g, '&');
|
|
113464
|
+
}
|
|
113326
113465
|
// Convert MDX JSX elements to HAST elements, preserving attributes and children
|
|
113327
113466
|
const mdxJsxElementHandler = (state, node) => {
|
|
113328
113467
|
const { attributes = [], name } = node;
|
|
@@ -113334,7 +113473,7 @@ const mdxJsxElementHandler = (state, node) => {
|
|
|
113334
113473
|
properties[attribute.name] = true;
|
|
113335
113474
|
}
|
|
113336
113475
|
else if (typeof attribute.value === 'string') {
|
|
113337
|
-
properties[attribute.name] = attribute.value;
|
|
113476
|
+
properties[attribute.name] = decodeHtmlEntities(attribute.value);
|
|
113338
113477
|
}
|
|
113339
113478
|
else {
|
|
113340
113479
|
properties[attribute.name] = attribute.value.value;
|
|
@@ -113396,6 +113535,17 @@ function base64Decode(str) {
|
|
|
113396
113535
|
}
|
|
113397
113536
|
return decodeURIComponent(escape(atob(str)));
|
|
113398
113537
|
}
|
|
113538
|
+
function escapeHtmlAttribute(value) {
|
|
113539
|
+
return value
|
|
113540
|
+
.replace(/&/g, '&')
|
|
113541
|
+
.replace(/"/g, '"')
|
|
113542
|
+
.replace(/</g, '<')
|
|
113543
|
+
.replace(/>/g, '>')
|
|
113544
|
+
.replace(/\n/g, ' ');
|
|
113545
|
+
}
|
|
113546
|
+
// Marker prefix for JSON-serialized complex values (arrays/objects)
|
|
113547
|
+
// Using a prefix that won't conflict with regular string values
|
|
113548
|
+
const JSON_VALUE_MARKER = '__MDXISH_JSON__';
|
|
113399
113549
|
// Markers for protected HTMLBlock content (HTML comments avoid markdown parsing issues)
|
|
113400
113550
|
const HTML_BLOCK_CONTENT_START = '<!--RDMX_HTMLBLOCK:';
|
|
113401
113551
|
const HTML_BLOCK_CONTENT_END = ':RDMX_HTMLBLOCK-->';
|
|
@@ -113525,6 +113675,16 @@ function extractBalancedBraces(content, start) {
|
|
|
113525
113675
|
return null;
|
|
113526
113676
|
return { content: content.slice(start, pos - 1), end: pos };
|
|
113527
113677
|
}
|
|
113678
|
+
function restoreInlineCode(content, protectedCode) {
|
|
113679
|
+
return content.replace(/___INLINE_CODE_(\d+)___/g, (_m, idx) => {
|
|
113680
|
+
return protectedCode.inlineCode[parseInt(idx, 10)];
|
|
113681
|
+
});
|
|
113682
|
+
}
|
|
113683
|
+
function restoreCodeBlocks(content, protectedCode) {
|
|
113684
|
+
return content.replace(/___CODE_BLOCK_(\d+)___/g, (_m, idx) => {
|
|
113685
|
+
return protectedCode.codeBlocks[parseInt(idx, 10)];
|
|
113686
|
+
});
|
|
113687
|
+
}
|
|
113528
113688
|
/**
|
|
113529
113689
|
* Converts JSX attribute expressions (attribute={expression}) to HTML attributes (attribute="value").
|
|
113530
113690
|
* Handles style objects (camelCase → kebab-case), className → class, and JSON stringifies objects.
|
|
@@ -113540,7 +113700,7 @@ function extractBalancedBraces(content, start) {
|
|
|
113540
113700
|
* // Returns: '<a href="https://example.com">Link</a>'
|
|
113541
113701
|
* ```
|
|
113542
113702
|
*/
|
|
113543
|
-
function evaluateAttributeExpressions(content, context) {
|
|
113703
|
+
function evaluateAttributeExpressions(content, context, protectedCode) {
|
|
113544
113704
|
const attrStartRegex = /(\w+)=\{/g;
|
|
113545
113705
|
let result = '';
|
|
113546
113706
|
let lastEnd = 0;
|
|
@@ -113550,7 +113710,14 @@ function evaluateAttributeExpressions(content, context) {
|
|
|
113550
113710
|
const braceStart = match.index + match[0].length;
|
|
113551
113711
|
const extracted = extractBalancedBraces(content, braceStart);
|
|
113552
113712
|
if (extracted) {
|
|
113553
|
-
|
|
113713
|
+
// The expression might contain template literals in MDX component tag props
|
|
113714
|
+
// E.g. <Component greeting={`Hello World!`} />
|
|
113715
|
+
// that is marked as inline code. So we need to restore the inline codes
|
|
113716
|
+
// in the expression to evaluate it
|
|
113717
|
+
let expression = extracted.content;
|
|
113718
|
+
if (protectedCode) {
|
|
113719
|
+
expression = restoreInlineCode(expression, protectedCode);
|
|
113720
|
+
}
|
|
113554
113721
|
const fullMatchEnd = extracted.end;
|
|
113555
113722
|
result += content.slice(lastEnd, match.index);
|
|
113556
113723
|
try {
|
|
@@ -113566,14 +113733,20 @@ function evaluateAttributeExpressions(content, context) {
|
|
|
113566
113733
|
result += `style="${cssString}"`;
|
|
113567
113734
|
}
|
|
113568
113735
|
else {
|
|
113569
|
-
|
|
113736
|
+
// These are arrays / objects attribute values
|
|
113737
|
+
// Mark JSON-serialized values with a prefix so they can be parsed back correctly
|
|
113738
|
+
const jsonValue = escapeHtmlAttribute(JSON_VALUE_MARKER + JSON.stringify(evalResult));
|
|
113739
|
+
// Use double quotes so that multi-paragraph values are not split into multiple attributes by the processors
|
|
113740
|
+
result += `${attributeName}="${jsonValue}"`;
|
|
113570
113741
|
}
|
|
113571
113742
|
}
|
|
113572
113743
|
else if (attributeName === 'className') {
|
|
113573
|
-
|
|
113744
|
+
// Escape special characters so that it doesn't break and split the attribute value to nodes
|
|
113745
|
+
// This will be restored later in the pipeline
|
|
113746
|
+
result += `class="${escapeHtmlAttribute(String(evalResult))}"`;
|
|
113574
113747
|
}
|
|
113575
113748
|
else {
|
|
113576
|
-
result += `${attributeName}="${evalResult}"`;
|
|
113749
|
+
result += `${attributeName}="${escapeHtmlAttribute(String(evalResult))}"`;
|
|
113577
113750
|
}
|
|
113578
113751
|
}
|
|
113579
113752
|
catch (_error) {
|
|
@@ -113604,13 +113777,9 @@ function evaluateAttributeExpressions(content, context) {
|
|
|
113604
113777
|
* // Returns: 'Text with `inline` and ```js\ncode\n```'
|
|
113605
113778
|
* ```
|
|
113606
113779
|
*/
|
|
113607
|
-
function
|
|
113608
|
-
let restored = content
|
|
113609
|
-
|
|
113610
|
-
});
|
|
113611
|
-
restored = restored.replace(/___INLINE_CODE_(\d+)___/g, (_match, index) => {
|
|
113612
|
-
return protectedCode.inlineCode[parseInt(index, 10)];
|
|
113613
|
-
});
|
|
113780
|
+
function restoreProtectedCodes(content, protectedCode) {
|
|
113781
|
+
let restored = restoreCodeBlocks(content, protectedCode);
|
|
113782
|
+
restored = restoreInlineCode(restored, protectedCode);
|
|
113614
113783
|
return restored;
|
|
113615
113784
|
}
|
|
113616
113785
|
/**
|
|
@@ -113631,9 +113800,9 @@ function preprocessJSXExpressions(content, context = {}) {
|
|
|
113631
113800
|
// Step 3: Evaluate attribute expressions (JSX attribute syntax: href={baseUrl})
|
|
113632
113801
|
// For inline expressions, we use a library to parse the expression & evaluate it later
|
|
113633
113802
|
// For attribute expressions, it was difficult to use a library to parse them, so do it manually
|
|
113634
|
-
processed = evaluateAttributeExpressions(processed, context);
|
|
113803
|
+
processed = evaluateAttributeExpressions(processed, context, protectedCode);
|
|
113635
113804
|
// Step 4: Restore protected code blocks
|
|
113636
|
-
processed =
|
|
113805
|
+
processed = restoreProtectedCodes(processed, protectedCode);
|
|
113637
113806
|
return processed;
|
|
113638
113807
|
}
|
|
113639
113808
|
|
|
@@ -114045,7 +114214,7 @@ const wrapPinnedBlocks = (node, json) => {
|
|
|
114045
114214
|
return node;
|
|
114046
114215
|
return {
|
|
114047
114216
|
children: [node],
|
|
114048
|
-
data: {
|
|
114217
|
+
data: { hName: 'rdme-pin', hProperties: { className: 'pin' } },
|
|
114049
114218
|
type: 'rdme-pin',
|
|
114050
114219
|
};
|
|
114051
114220
|
};
|
|
@@ -114133,15 +114302,16 @@ function parseMagicBlock(raw, options = {}) {
|
|
|
114133
114302
|
type: 'code',
|
|
114134
114303
|
value: obj.code.trim(),
|
|
114135
114304
|
}));
|
|
114136
|
-
// Single code block without a tab name renders as a plain code block
|
|
114305
|
+
// Single code block without a tab name (meta or language) renders as a plain code block
|
|
114306
|
+
// Otherwise, we want to render it as a code tabs block
|
|
114137
114307
|
if (children.length === 1) {
|
|
114138
114308
|
if (!children[0].value)
|
|
114139
114309
|
return [];
|
|
114140
|
-
if (children[0].meta)
|
|
114310
|
+
if (!(children[0].meta || children[0].lang))
|
|
114141
114311
|
return [wrapPinnedBlocks(children[0], json)];
|
|
114142
114312
|
}
|
|
114143
|
-
// Multiple code blocks
|
|
114144
|
-
return [wrapPinnedBlocks({ children, className: 'tabs', data: { hName: '
|
|
114313
|
+
// Multiple code blocks or a single code block with a tab name (meta or language) renders as a code tabs block
|
|
114314
|
+
return [wrapPinnedBlocks({ children, className: 'tabs', data: { hName: 'CodeTabs' }, type: 'code-tabs' }, json)];
|
|
114145
114315
|
}
|
|
114146
114316
|
// API header: renders as a heading element (h1-h6)
|
|
114147
114317
|
case 'api-header': {
|
|
@@ -114342,11 +114512,31 @@ function parseMagicBlock(raw, options = {}) {
|
|
|
114342
114512
|
}
|
|
114343
114513
|
}
|
|
114344
114514
|
/**
|
|
114345
|
-
*
|
|
114515
|
+
* Block-level node types that cannot be nested inside paragraphs.
|
|
114346
114516
|
*/
|
|
114347
|
-
const
|
|
114348
|
-
|
|
114349
|
-
|
|
114517
|
+
const blockTypes = [
|
|
114518
|
+
'heading',
|
|
114519
|
+
'code',
|
|
114520
|
+
'code-tabs',
|
|
114521
|
+
'paragraph',
|
|
114522
|
+
'blockquote',
|
|
114523
|
+
'list',
|
|
114524
|
+
'table',
|
|
114525
|
+
'thematicBreak',
|
|
114526
|
+
'html',
|
|
114527
|
+
'yaml',
|
|
114528
|
+
'toml',
|
|
114529
|
+
'rdme-pin',
|
|
114530
|
+
'rdme-callout',
|
|
114531
|
+
'html-block',
|
|
114532
|
+
'embed',
|
|
114533
|
+
'figure',
|
|
114534
|
+
'mdxJsxFlowElement',
|
|
114535
|
+
];
|
|
114536
|
+
/**
|
|
114537
|
+
* Check if a node is a block-level node (cannot be inside a paragraph)
|
|
114538
|
+
*/
|
|
114539
|
+
const isBlockNode = (node) => blockTypes.includes(node.type);
|
|
114350
114540
|
/**
|
|
114351
114541
|
* Unified plugin that restores magic blocks from placeholder tokens.
|
|
114352
114542
|
*
|
|
@@ -114359,8 +114549,9 @@ const magicBlockRestorer = ({ blocks }) => tree => {
|
|
|
114359
114549
|
return;
|
|
114360
114550
|
// Map: key → original raw magic block content
|
|
114361
114551
|
const magicBlockKeys = new Map(blocks.map(({ key, raw }) => [key, raw]));
|
|
114362
|
-
//
|
|
114363
|
-
const
|
|
114552
|
+
// Collect replacements to apply (we need to visit in reverse to maintain indices)
|
|
114553
|
+
const replacements = [];
|
|
114554
|
+
// First pass: collect all replacements
|
|
114364
114555
|
visit(tree, 'inlineCode', (node, index, parent) => {
|
|
114365
114556
|
if (!parent || index == null)
|
|
114366
114557
|
return undefined;
|
|
@@ -114370,31 +114561,73 @@ const magicBlockRestorer = ({ blocks }) => tree => {
|
|
|
114370
114561
|
const children = parseMagicBlock(raw);
|
|
114371
114562
|
if (!children.length)
|
|
114372
114563
|
return undefined;
|
|
114373
|
-
|
|
114374
|
-
|
|
114375
|
-
|
|
114376
|
-
|
|
114377
|
-
|
|
114378
|
-
|
|
114379
|
-
|
|
114564
|
+
// If parent is a paragraph and we're inserting block nodes (which must not be in paragraphs), lift them out
|
|
114565
|
+
if (parent.type === 'paragraph' && children.some(child => isBlockNode(child))) {
|
|
114566
|
+
const blockNodes = [];
|
|
114567
|
+
const inlineNodes = [];
|
|
114568
|
+
// Separate block and inline nodes
|
|
114569
|
+
children.forEach(child => {
|
|
114570
|
+
if (isBlockNode(child)) {
|
|
114571
|
+
blockNodes.push(child);
|
|
114380
114572
|
}
|
|
114381
|
-
|
|
114382
|
-
|
|
114383
|
-
if (paragraphParent) {
|
|
114384
|
-
const paragraphIndex = paragraphParent.children.indexOf(parent);
|
|
114385
|
-
if (paragraphIndex !== -1) {
|
|
114386
|
-
modifications.push({ children, index: paragraphIndex, parent: paragraphParent });
|
|
114573
|
+
else {
|
|
114574
|
+
inlineNodes.push(child);
|
|
114387
114575
|
}
|
|
114388
|
-
}
|
|
114389
|
-
|
|
114576
|
+
});
|
|
114577
|
+
const before = parent.children.slice(0, index);
|
|
114578
|
+
const after = parent.children.slice(index + 1);
|
|
114579
|
+
replacements.push({
|
|
114580
|
+
parent,
|
|
114581
|
+
blockNodes,
|
|
114582
|
+
inlineNodes,
|
|
114583
|
+
before,
|
|
114584
|
+
after,
|
|
114585
|
+
});
|
|
114390
114586
|
}
|
|
114391
|
-
|
|
114392
|
-
|
|
114393
|
-
|
|
114394
|
-
|
|
114395
|
-
|
|
114396
|
-
parent.children.splice(index, 1, ...children);
|
|
114587
|
+
else {
|
|
114588
|
+
// Normal case: just replace the inlineCode with the children
|
|
114589
|
+
parent.children.splice(index, 1, ...children);
|
|
114590
|
+
}
|
|
114591
|
+
return undefined;
|
|
114397
114592
|
});
|
|
114593
|
+
// Second pass: apply replacements that require lifting block nodes out of paragraphs
|
|
114594
|
+
// Process in reverse order to maintain correct indices
|
|
114595
|
+
for (let i = replacements.length - 1; i >= 0; i -= 1) {
|
|
114596
|
+
const { after, before, blockNodes, inlineNodes, parent } = replacements[i];
|
|
114597
|
+
// Find the paragraph's position in the root
|
|
114598
|
+
const rootChildren = tree.children;
|
|
114599
|
+
const paraIndex = rootChildren.findIndex(child => child === parent);
|
|
114600
|
+
if (paraIndex === -1) {
|
|
114601
|
+
// Paragraph not found in root - fall back to normal replacement
|
|
114602
|
+
// This shouldn't happen normally, but handle it gracefully
|
|
114603
|
+
// Reconstruct the original index from before.length
|
|
114604
|
+
const originalIndex = before.length;
|
|
114605
|
+
parent.children.splice(originalIndex, 1, ...blockNodes, ...inlineNodes);
|
|
114606
|
+
// eslint-disable-next-line no-continue
|
|
114607
|
+
continue;
|
|
114608
|
+
}
|
|
114609
|
+
// Update or remove the paragraph
|
|
114610
|
+
if (inlineNodes.length > 0) {
|
|
114611
|
+
// Keep paragraph with inline nodes
|
|
114612
|
+
parent.children = [...before, ...inlineNodes, ...after];
|
|
114613
|
+
// Insert block nodes after the paragraph
|
|
114614
|
+
if (blockNodes.length > 0) {
|
|
114615
|
+
rootChildren.splice(paraIndex + 1, 0, ...blockNodes);
|
|
114616
|
+
}
|
|
114617
|
+
}
|
|
114618
|
+
else if (before.length === 0 && after.length === 0) {
|
|
114619
|
+
// Remove empty paragraph and replace with block nodes
|
|
114620
|
+
rootChildren.splice(paraIndex, 1, ...blockNodes);
|
|
114621
|
+
}
|
|
114622
|
+
else {
|
|
114623
|
+
// Keep paragraph with remaining content
|
|
114624
|
+
parent.children = [...before, ...after];
|
|
114625
|
+
// Insert block nodes after the paragraph
|
|
114626
|
+
if (blockNodes.length > 0) {
|
|
114627
|
+
rootChildren.splice(paraIndex + 1, 0, ...blockNodes);
|
|
114628
|
+
}
|
|
114629
|
+
}
|
|
114630
|
+
}
|
|
114398
114631
|
};
|
|
114399
114632
|
/* harmony default export */ const mdxish_magic_blocks = (magicBlockRestorer);
|
|
114400
114633
|
|
|
@@ -114618,6 +114851,92 @@ const normalizeEmphasisAST = () => (tree) => {
|
|
|
114618
114851
|
};
|
|
114619
114852
|
/* harmony default export */ const normalize_malformed_md_syntax = (normalizeEmphasisAST);
|
|
114620
114853
|
|
|
114854
|
+
;// ./processor/transform/mdxish/normalize-table-separator.ts
|
|
114855
|
+
/**
|
|
114856
|
+
* Preprocessor to normalize malformed GFM table separator syntax.
|
|
114857
|
+
*
|
|
114858
|
+
* Fixes the common mistake where the alignment colon is placed after the pipe
|
|
114859
|
+
* instead of before the dashes:
|
|
114860
|
+
*
|
|
114861
|
+
* Invalid: `|: ---` or `|:---` (colon after pipe)
|
|
114862
|
+
* Valid: `| :---` (colon before dashes)
|
|
114863
|
+
*
|
|
114864
|
+
* Also handles right alignment:
|
|
114865
|
+
* Invalid: `| ---:| ` with space before pipe
|
|
114866
|
+
* Valid: `| ---:|` (no space before closing pipe)
|
|
114867
|
+
*
|
|
114868
|
+
* This runs before remark-parse to ensure the table is recognized as a valid GFM table.
|
|
114869
|
+
*/
|
|
114870
|
+
/**
|
|
114871
|
+
* Pattern to match a table separator row.
|
|
114872
|
+
* A separator row consists of cells that contain only dashes, colons (for alignment), and spaces.
|
|
114873
|
+
*
|
|
114874
|
+
* Valid GFM separator formats:
|
|
114875
|
+
* - `---` or `----` (no alignment, defaults to left)
|
|
114876
|
+
* - `:---` (left aligned)
|
|
114877
|
+
* - `---:` (right aligned)
|
|
114878
|
+
* - `:---:` (center aligned)
|
|
114879
|
+
*
|
|
114880
|
+
* Invalid formats this fixes:
|
|
114881
|
+
* - `|: ---` → `| :---` (colon wrongly placed after pipe)
|
|
114882
|
+
* - `|:---` → `| :---` (colon directly after pipe, missing space)
|
|
114883
|
+
* - `|::---` or `| ::---` → `| :---` (double colon typo)
|
|
114884
|
+
*/
|
|
114885
|
+
// Match a line that looks like a table separator row
|
|
114886
|
+
// This regex captures the whole line if it contains only pipe-separated cells with dashes/colons
|
|
114887
|
+
const TABLE_SEPARATOR_LINE_REGEX = /^(\|[:\s-]+)+\|?\s*$/;
|
|
114888
|
+
// Match malformed left-alignment: `|: ` or `|:` followed by dashes
|
|
114889
|
+
// Captures: group 1 = pipe, group 2 = spaces after colon, group 3 = dashes
|
|
114890
|
+
const MALFORMED_LEFT_ALIGN_REGEX = /\|:(\s*)(-+)/g;
|
|
114891
|
+
// Match malformed double colon: `|::---` or `| ::---` → `| :---`
|
|
114892
|
+
const MALFORMED_DOUBLE_COLON_REGEX = /\|\s*::(\s*)(-+)/g;
|
|
114893
|
+
// Match malformed patterns with spaces before closing colons: `| --- : |` → `| ---: |`
|
|
114894
|
+
const MALFORMED_RIGHT_ALIGN_SPACE_REGEX = /(-+)\s+:(\s*\|)/g;
|
|
114895
|
+
// Match malformed center alignment with spaces: `| : --- : |` → `| :---: |`
|
|
114896
|
+
const MALFORMED_CENTER_ALIGN_REGEX = /\|:(\s+)(-+)(\s+):/g;
|
|
114897
|
+
/**
|
|
114898
|
+
* Normalizes a single table separator line.
|
|
114899
|
+
*/
|
|
114900
|
+
function normalizeTableSeparatorLine(line) {
|
|
114901
|
+
// Check if this line looks like a table separator
|
|
114902
|
+
if (!TABLE_SEPARATOR_LINE_REGEX.test(line)) {
|
|
114903
|
+
return line;
|
|
114904
|
+
}
|
|
114905
|
+
let normalized = line;
|
|
114906
|
+
// Fix `|::---` → `| :---` (double colon typo)
|
|
114907
|
+
// Must run before single colon fix to avoid partial replacement
|
|
114908
|
+
normalized = normalized.replace(MALFORMED_DOUBLE_COLON_REGEX, '| :$2');
|
|
114909
|
+
// Fix `|: ---` or `|:---` → `| :---`
|
|
114910
|
+
// The colon should be adjacent to the dashes, not the pipe
|
|
114911
|
+
normalized = normalized.replace(MALFORMED_LEFT_ALIGN_REGEX, '| :$2');
|
|
114912
|
+
// Fix `| --- : |` → `| ---: |`
|
|
114913
|
+
// Remove space before right-alignment colon
|
|
114914
|
+
normalized = normalized.replace(MALFORMED_RIGHT_ALIGN_SPACE_REGEX, '$1:$2');
|
|
114915
|
+
// Fix `| : --- : |` → `| :---: |`
|
|
114916
|
+
// Remove spaces around center-aligned dashes
|
|
114917
|
+
normalized = normalized.replace(MALFORMED_CENTER_ALIGN_REGEX, '| :$2:');
|
|
114918
|
+
return normalized;
|
|
114919
|
+
}
|
|
114920
|
+
/**
|
|
114921
|
+
* Preprocesses markdown content to normalize malformed table separator syntax.
|
|
114922
|
+
*
|
|
114923
|
+
* @param content - The raw markdown content
|
|
114924
|
+
* @returns The content with normalized table separators
|
|
114925
|
+
*/
|
|
114926
|
+
function normalizeTableSeparator(content) {
|
|
114927
|
+
const lines = content.split('\n');
|
|
114928
|
+
const normalizedLines = lines.map((line, index) => {
|
|
114929
|
+
const prevLine = index > 0 ? lines[index - 1] : '';
|
|
114930
|
+
const isPrevLineTableRow = prevLine.trim().startsWith('|');
|
|
114931
|
+
if (isPrevLineTableRow) {
|
|
114932
|
+
return normalizeTableSeparatorLine(line);
|
|
114933
|
+
}
|
|
114934
|
+
return line;
|
|
114935
|
+
});
|
|
114936
|
+
return normalizedLines.join('\n');
|
|
114937
|
+
}
|
|
114938
|
+
/* harmony default export */ const normalize_table_separator = ((/* unused pure expression or super */ null && (normalizeTableSeparator)));
|
|
114939
|
+
|
|
114621
114940
|
;// ./processor/transform/mdxish/restore-snake-case-component-name.ts
|
|
114622
114941
|
|
|
114623
114942
|
|
|
@@ -114881,6 +115200,7 @@ function loadComponents() {
|
|
|
114881
115200
|
|
|
114882
115201
|
|
|
114883
115202
|
|
|
115203
|
+
|
|
114884
115204
|
|
|
114885
115205
|
|
|
114886
115206
|
const defaultTransformers = [callouts, code_tabs, gemoji_, transform_embeds];
|
|
@@ -114893,9 +115213,11 @@ function mdxishAstProcessor(mdContent, opts = {}) {
|
|
|
114893
115213
|
// Preprocessing pipeline: Transform content to be parser-ready
|
|
114894
115214
|
// Step 1: Extract legacy magic blocks
|
|
114895
115215
|
const { replaced: contentAfterMagicBlocks, blocks } = extractMagicBlocks(mdContent);
|
|
114896
|
-
// Step 2:
|
|
114897
|
-
const
|
|
114898
|
-
// Step 3:
|
|
115216
|
+
// Step 2: Normalize malformed table separator syntax (e.g., `|: ---` → `| :---`)
|
|
115217
|
+
const contentAfterTableNormalization = normalizeTableSeparator(contentAfterMagicBlocks);
|
|
115218
|
+
// Step 3: Evaluate JSX expressions in attributes
|
|
115219
|
+
const contentAfterJSXEvaluation = preprocessJSXExpressions(contentAfterTableNormalization, jsxContext);
|
|
115220
|
+
// Step 4: Replace snake_case component names with parser-safe placeholders
|
|
114899
115221
|
// (e.g., <Snake_case /> → <MDXishSnakeCase0 /> which will be restored after parsing)
|
|
114900
115222
|
const { content: parserReadyContent, mapping: snakeCaseMapping } = processSnakeCaseComponent(contentAfterJSXEvaluation);
|
|
114901
115223
|
// Create string map for tailwind transformer
|
|
@@ -115159,6 +115481,40 @@ const makeUseMDXComponents = (more = {}) => {
|
|
|
115159
115481
|
|
|
115160
115482
|
|
|
115161
115483
|
|
|
115484
|
+
|
|
115485
|
+
/**
|
|
115486
|
+
* Parse JSON-marked string values in props back to their original types.
|
|
115487
|
+
* This handles arrays and objects that were serialized during JSX preprocessing.
|
|
115488
|
+
*/
|
|
115489
|
+
function parseJsonProps(props) {
|
|
115490
|
+
if (!props)
|
|
115491
|
+
return props;
|
|
115492
|
+
const parsed = {};
|
|
115493
|
+
Object.entries(props).forEach(([key, value]) => {
|
|
115494
|
+
if (typeof value === 'string' && value.startsWith(JSON_VALUE_MARKER)) {
|
|
115495
|
+
try {
|
|
115496
|
+
parsed[key] = JSON.parse(value.slice(JSON_VALUE_MARKER.length));
|
|
115497
|
+
}
|
|
115498
|
+
catch {
|
|
115499
|
+
// If parsing fails, use the value without the marker
|
|
115500
|
+
parsed[key] = value.slice(JSON_VALUE_MARKER.length);
|
|
115501
|
+
}
|
|
115502
|
+
}
|
|
115503
|
+
else {
|
|
115504
|
+
parsed[key] = value;
|
|
115505
|
+
}
|
|
115506
|
+
});
|
|
115507
|
+
return parsed;
|
|
115508
|
+
}
|
|
115509
|
+
/**
|
|
115510
|
+
* Custom createElement wrapper that parses JSON-marked string props.
|
|
115511
|
+
* This is needed because rehype-react converts HAST to React, but complex
|
|
115512
|
+
* types (arrays/objects) get serialized to strings during markdown parsing.
|
|
115513
|
+
*/
|
|
115514
|
+
function createElementWithJsonProps(type, props, ...children) {
|
|
115515
|
+
const parsedProps = parseJsonProps(props);
|
|
115516
|
+
return external_react_default().createElement(type, parsedProps, ...children);
|
|
115517
|
+
}
|
|
115162
115518
|
/** Flatten CustomComponents into a component map for rehype-react */
|
|
115163
115519
|
function exportComponentsForRehype(components) {
|
|
115164
115520
|
const exported = Object.entries(components).reduce((memo, [tag, mod]) => {
|
|
@@ -115190,7 +115546,7 @@ function exportComponentsForRehype(components) {
|
|
|
115190
115546
|
function createRehypeReactProcessor(components) {
|
|
115191
115547
|
// @ts-expect-error - rehype-react types are incompatible with React.Fragment return type
|
|
115192
115548
|
return unified().use((rehype_react_default()), {
|
|
115193
|
-
createElement:
|
|
115549
|
+
createElement: createElementWithJsonProps,
|
|
115194
115550
|
Fragment: (external_react_default()).Fragment,
|
|
115195
115551
|
components,
|
|
115196
115552
|
});
|
|
@@ -115499,89 +115855,6 @@ async function stripComments(doc, { mdx } = {}) {
|
|
|
115499
115855
|
}
|
|
115500
115856
|
/* harmony default export */ const lib_stripComments = (stripComments);
|
|
115501
115857
|
|
|
115502
|
-
;// ./lib/utils/isPlainText.ts
|
|
115503
|
-
|
|
115504
|
-
/**
|
|
115505
|
-
* Detects if content contains HTML, magic blocks, or MDX syntax.
|
|
115506
|
-
*
|
|
115507
|
-
* We can use this in some pipelines to determine if we should have to parse content through
|
|
115508
|
-
* `.plain() or if it is already plain text and it should be able to detect everything that would
|
|
115509
|
-
* be stripped or sanitized by `.plain()`.
|
|
115510
|
-
*
|
|
115511
|
-
*/
|
|
115512
|
-
function isPlainText(content) {
|
|
115513
|
-
if (!content || typeof content !== 'string') {
|
|
115514
|
-
return true;
|
|
115515
|
-
}
|
|
115516
|
-
// Exclude markdown code blocks and inline code to avoid false positives
|
|
115517
|
-
// Match code blocks with optional language identifier: ```lang\n...\n```
|
|
115518
|
-
const codeBlockRegex = /```[^\n]*\n[\s\S]*?```/g;
|
|
115519
|
-
// Match inline code: `code` (but not escaped backticks)
|
|
115520
|
-
const inlineCodeRegex = /`[^`\n]+`/g;
|
|
115521
|
-
// Remove code blocks and inline code to avoid false positives
|
|
115522
|
-
let contentWithoutCode = structuredClone(content);
|
|
115523
|
-
contentWithoutCode = contentWithoutCode.replace(codeBlockRegex, '');
|
|
115524
|
-
contentWithoutCode = contentWithoutCode.replace(inlineCodeRegex, '');
|
|
115525
|
-
// Check for magic blocks: `[block:TYPE]...[/block]`
|
|
115526
|
-
// Only check after removing code blocks to avoid detecting magic blocks in code
|
|
115527
|
-
if (contentWithoutCode.match(MAGIC_BLOCK_REGEX) !== null) {
|
|
115528
|
-
return false;
|
|
115529
|
-
}
|
|
115530
|
-
// Check for markdown links: [text](url) or [text][reference]
|
|
115531
|
-
// Pattern matches inline links and reference-style links
|
|
115532
|
-
// Exclude images which start with ! before the bracket
|
|
115533
|
-
// Only check after removing code blocks
|
|
115534
|
-
const markdownLinkPattern = /(?<!!)\[([^\]]+)\]\(([^)]+)\)|(?<!!)\[([^\]]+)\]\[([^\]]*)\]/;
|
|
115535
|
-
if (markdownLinkPattern.test(contentWithoutCode)) {
|
|
115536
|
-
return false;
|
|
115537
|
-
}
|
|
115538
|
-
// Check for JSX elements (PascalCase components) in the original content
|
|
115539
|
-
// This includes code blocks since JSX code examples should be detected
|
|
115540
|
-
// Pattern matches:
|
|
115541
|
-
// - Self-closing: <Component /> or <Component/>
|
|
115542
|
-
// - With attributes: <Component prop="value" />
|
|
115543
|
-
// - With children: <Component>...</Component>
|
|
115544
|
-
// Use simpler, safer patterns to avoid ReDoS from backtracking
|
|
115545
|
-
// Match self-closing tags with bounded attribute length to prevent excessive backtracking
|
|
115546
|
-
const jsxSelfClosingPattern = /<[A-Z][a-zA-Z0-9]*(?:\s[^>]{0,50})?\/>/;
|
|
115547
|
-
if (jsxSelfClosingPattern.test(content)) {
|
|
115548
|
-
return false;
|
|
115549
|
-
}
|
|
115550
|
-
// For components with children, use a safer pattern that limits backtracking
|
|
115551
|
-
// Match opening tag with bounded attributes, then look for closing tag with same name
|
|
115552
|
-
const jsxWithChildrenPattern = /<([A-Z][a-zA-Z0-9]*)(?:\s[^>]{0,50})?>[\s\S]{0,50}<\/\1>/;
|
|
115553
|
-
if (jsxWithChildrenPattern.test(content)) {
|
|
115554
|
-
return false;
|
|
115555
|
-
}
|
|
115556
|
-
// Check for MDX expressions and HTML tags in the original content
|
|
115557
|
-
// HTML/JSX/MDX in code blocks should be detected (as per test requirements)
|
|
115558
|
-
// But exclude inline code that contains magic block patterns to avoid false positives
|
|
115559
|
-
let contentForHtmlMdx = content;
|
|
115560
|
-
// Find inline code blocks and check if they contain [block: pattern
|
|
115561
|
-
// Exclude these from HTML/MDX detection to avoid false positives
|
|
115562
|
-
const inlineCodePattern = /`[^`\n]+`/g;
|
|
115563
|
-
let inlineCodeMatch;
|
|
115564
|
-
inlineCodePattern.lastIndex = 0;
|
|
115565
|
-
while ((inlineCodeMatch = inlineCodePattern.exec(content)) !== null) {
|
|
115566
|
-
if (inlineCodeMatch[0].includes('[block:')) {
|
|
115567
|
-
contentForHtmlMdx = contentForHtmlMdx.replace(inlineCodeMatch[0], '');
|
|
115568
|
-
}
|
|
115569
|
-
}
|
|
115570
|
-
// Match simple MDX variable expressions like {variable}, {user.name}, {getValue()}, {}
|
|
115571
|
-
// Use bounded quantifier to prevent ReDoS - limit to reasonable variable name length
|
|
115572
|
-
// Allow empty braces {} to be detected as well
|
|
115573
|
-
const jsxExpressionPattern = /\{[^}"]{0,50}\}/;
|
|
115574
|
-
if (jsxExpressionPattern.test(contentForHtmlMdx)) {
|
|
115575
|
-
return false;
|
|
115576
|
-
}
|
|
115577
|
-
// Match HTML tags with bounded attribute length to prevent ReDoS
|
|
115578
|
-
const htmlTagPattern = /<[a-z][a-z0-9]*(?:\s[^>]{0,50})?(?:\/>|>)/i;
|
|
115579
|
-
if (htmlTagPattern.test(contentForHtmlMdx)) {
|
|
115580
|
-
return false;
|
|
115581
|
-
}
|
|
115582
|
-
return true;
|
|
115583
|
-
}
|
|
115584
|
-
|
|
115585
115858
|
;// ./lib/index.ts
|
|
115586
115859
|
|
|
115587
115860
|
|
|
@@ -115600,7 +115873,6 @@ function isPlainText(content) {
|
|
|
115600
115873
|
|
|
115601
115874
|
|
|
115602
115875
|
|
|
115603
|
-
|
|
115604
115876
|
;// ./index.tsx
|
|
115605
115877
|
|
|
115606
115878
|
|