@readme/markdown 14.1.1 → 14.1.3
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.js +168 -109
- package/dist/main.node.js +168 -109
- package/dist/main.node.js.map +1 -1
- package/dist/processor/transform/mdxish/components/utils.d.ts +7 -5
- package/package.json +1 -1
package/dist/main.node.js
CHANGED
|
@@ -107805,7 +107805,12 @@ const tocHastToMdx = (toc, components, variables) => {
|
|
|
107805
107805
|
|
|
107806
107806
|
|
|
107807
107807
|
const sanitizeSchema = cjs_default()(defaultSchema, {
|
|
107808
|
-
|
|
107808
|
+
attributes: {
|
|
107809
|
+
a: ['target'],
|
|
107810
|
+
},
|
|
107811
|
+
protocols: {
|
|
107812
|
+
href: ['doc', 'ref', 'blog', 'changelog', 'page'],
|
|
107813
|
+
},
|
|
107809
107814
|
});
|
|
107810
107815
|
const compile_compile = (text, { components = {}, missingComponents, copyButtons, useTailwind, ...opts } = {}) => {
|
|
107811
107816
|
// Destructure at runtime to avoid circular dependency issues
|
|
@@ -117396,17 +117401,46 @@ function mdxComponent() {
|
|
|
117396
117401
|
|
|
117397
117402
|
|
|
117398
117403
|
|
|
117404
|
+
|
|
117405
|
+
|
|
117406
|
+
|
|
117407
|
+
|
|
117408
|
+
const buildInlineMdProcessor = (safeMode) => {
|
|
117409
|
+
const micromarkExts = [mdxComponent(), syntax_gemoji(), legacyVariable(), magicBlock()];
|
|
117410
|
+
const fromMarkdownExts = [
|
|
117411
|
+
mdxComponentFromMarkdown(),
|
|
117412
|
+
gemojiFromMarkdown(),
|
|
117413
|
+
legacyVariableFromMarkdown(),
|
|
117414
|
+
emptyTaskListItemFromMarkdown(),
|
|
117415
|
+
magicBlockFromMarkdown(),
|
|
117416
|
+
];
|
|
117417
|
+
// Since evaluating expressions can be dangerous, do so only when safeMode is off
|
|
117418
|
+
if (!safeMode) {
|
|
117419
|
+
const mdxExprExt = mdxExpression({ allowEmpty: true });
|
|
117420
|
+
micromarkExts.push({ text: mdxExprExt.text });
|
|
117421
|
+
fromMarkdownExts.push(mdxExpressionFromMarkdown());
|
|
117422
|
+
}
|
|
117423
|
+
return unified()
|
|
117424
|
+
.data('micromarkExtensions', micromarkExts)
|
|
117425
|
+
.data('fromMarkdownExtensions', fromMarkdownExts)
|
|
117426
|
+
.use(remarkParse)
|
|
117427
|
+
.use(remarkGfm);
|
|
117428
|
+
};
|
|
117429
|
+
const processorCache = new Map();
|
|
117399
117430
|
/**
|
|
117400
|
-
*
|
|
117401
|
-
*
|
|
117402
|
-
*
|
|
117403
|
-
*
|
|
117431
|
+
* Unified processor for re-parsing the body of an MDX component
|
|
117432
|
+
* Memoized based on the argument value so we don't pay the construction cost on every parse
|
|
117433
|
+
* Currently the argument is only safeMode, but we could add more arguments in the future,
|
|
117434
|
+
* in which case the key would need to be extend to include the new arguments.
|
|
117404
117435
|
*/
|
|
117405
|
-
const
|
|
117406
|
-
|
|
117407
|
-
|
|
117408
|
-
|
|
117409
|
-
|
|
117436
|
+
const getInlineMdProcessor = ({ safeMode = false } = {}) => {
|
|
117437
|
+
let processor = processorCache.get(safeMode);
|
|
117438
|
+
if (!processor) {
|
|
117439
|
+
processor = buildInlineMdProcessor(safeMode);
|
|
117440
|
+
processorCache.set(safeMode, processor);
|
|
117441
|
+
}
|
|
117442
|
+
return processor;
|
|
117443
|
+
};
|
|
117410
117444
|
/**
|
|
117411
117445
|
* True when a tag name starts with an uppercase letter — ReadMe's marker for
|
|
117412
117446
|
* a custom MDX component (vs a lowercase HTML tag).
|
|
@@ -117447,11 +117481,11 @@ const toMdxJsxTextElement = (name, attributes, children, position) => ({
|
|
|
117447
117481
|
* rejects line endings), so the paragraph-flatten path covers every case we
|
|
117448
117482
|
* actually produce; other shapes fall back to empty children.
|
|
117449
117483
|
*/
|
|
117450
|
-
const parsePhrasingChildren = (value) => {
|
|
117484
|
+
const parsePhrasingChildren = (value, safeMode) => {
|
|
117451
117485
|
const trimmed = value.trim();
|
|
117452
117486
|
if (!trimmed)
|
|
117453
117487
|
return [];
|
|
117454
|
-
const [first] =
|
|
117488
|
+
const [first] = getInlineMdProcessor({ safeMode }).parse(trimmed).children;
|
|
117455
117489
|
return first?.type === 'paragraph' ? first.children : [];
|
|
117456
117490
|
};
|
|
117457
117491
|
/**
|
|
@@ -117464,7 +117498,7 @@ const parsePhrasingChildren = (value) => {
|
|
|
117464
117498
|
*
|
|
117465
117499
|
* Returns the original node unchanged when it doesn't qualify.
|
|
117466
117500
|
*/
|
|
117467
|
-
const promoteInlineHtml = (node, parseOpts) => {
|
|
117501
|
+
const promoteInlineHtml = (node, parseOpts, safeMode) => {
|
|
117468
117502
|
const parsed = parseTag((node.value ?? '').trim(), parseOpts);
|
|
117469
117503
|
if (!parsed)
|
|
117470
117504
|
return node;
|
|
@@ -117489,7 +117523,7 @@ const promoteInlineHtml = (node, parseOpts) => {
|
|
|
117489
117523
|
if (closeIdx < 0)
|
|
117490
117524
|
return node;
|
|
117491
117525
|
const inner = contentAfterTag.slice(0, closeIdx);
|
|
117492
|
-
return toMdxJsxTextElement(tag, attributes, parsePhrasingChildren(inner), node.position);
|
|
117526
|
+
return toMdxJsxTextElement(tag, attributes, parsePhrasingChildren(inner, safeMode), node.position);
|
|
117493
117527
|
};
|
|
117494
117528
|
/**
|
|
117495
117529
|
* Transform inline html nodes with expression attributes
|
|
@@ -117508,9 +117542,10 @@ const promoteInlineHtml = (node, parseOpts) => {
|
|
|
117508
117542
|
* `<a href="x">` stays as an html node for rehype-raw.
|
|
117509
117543
|
*/
|
|
117510
117544
|
const mdxishInlineMdxHtmlBlocks = (opts = {}) => tree => {
|
|
117511
|
-
const
|
|
117545
|
+
const safeMode = !!opts.safeMode;
|
|
117546
|
+
const parseOpts = { preserveExpressionsAsText: safeMode };
|
|
117512
117547
|
visit(tree, 'paragraph', (paragraph) => {
|
|
117513
|
-
paragraph.children = paragraph.children.map(child => child.type === 'html' ? promoteInlineHtml(child, parseOpts) : child);
|
|
117548
|
+
paragraph.children = paragraph.children.map(child => child.type === 'html' ? promoteInlineHtml(child, parseOpts, safeMode) : child);
|
|
117514
117549
|
});
|
|
117515
117550
|
};
|
|
117516
117551
|
/* harmony default export */ const inline_html = (mdxishInlineMdxHtmlBlocks);
|
|
@@ -117593,15 +117628,15 @@ function safeDeindent(text) {
|
|
|
117593
117628
|
* Dedents the content first to prevent indented component content
|
|
117594
117629
|
* (from nested components) from being treated as code blocks.
|
|
117595
117630
|
*/
|
|
117596
|
-
const parseMdChildren = (value) => {
|
|
117597
|
-
const parsed =
|
|
117631
|
+
const parseMdChildren = (value, safeMode) => {
|
|
117632
|
+
const parsed = getInlineMdProcessor({ safeMode }).parse(safeDeindent(value).trim());
|
|
117598
117633
|
return parsed.children || [];
|
|
117599
117634
|
};
|
|
117600
117635
|
/**
|
|
117601
117636
|
* Parse substring content of a node and update the parent's children to include the new nodes.
|
|
117602
117637
|
*/
|
|
117603
|
-
const parseSibling = (stack, parent, index, sibling) => {
|
|
117604
|
-
const siblingNodes = parseMdChildren(sibling);
|
|
117638
|
+
const parseSibling = (stack, parent, index, sibling, safeMode) => {
|
|
117639
|
+
const siblingNodes = parseMdChildren(sibling, safeMode);
|
|
117605
117640
|
// The new sibling nodes might contain new components to be processed
|
|
117606
117641
|
if (siblingNodes.length > 0) {
|
|
117607
117642
|
parent.children.splice(index + 1, 0, ...siblingNodes);
|
|
@@ -117653,7 +117688,8 @@ const substituteNodeWithMdxNode = (parent, index, mdxNode) => {
|
|
|
117653
117688
|
*/
|
|
117654
117689
|
const mdxishMdxComponentBlocks = (opts = {}) => tree => {
|
|
117655
117690
|
const stack = [tree];
|
|
117656
|
-
const
|
|
117691
|
+
const safeMode = !!opts.safeMode;
|
|
117692
|
+
const parseOpts = { preserveExpressionsAsText: safeMode };
|
|
117657
117693
|
const processChildNode = (parent, index) => {
|
|
117658
117694
|
const node = parent.children[index];
|
|
117659
117695
|
if (!node)
|
|
@@ -117699,7 +117735,7 @@ const mdxishMdxComponentBlocks = (opts = {}) => tree => {
|
|
|
117699
117735
|
// Check and parse if there's relevant content after the current closing tag
|
|
117700
117736
|
const remainingContent = contentAfterTag.trim();
|
|
117701
117737
|
if (remainingContent) {
|
|
117702
|
-
parseSibling(stack, parent, index, remainingContent);
|
|
117738
|
+
parseSibling(stack, parent, index, remainingContent, safeMode);
|
|
117703
117739
|
}
|
|
117704
117740
|
return;
|
|
117705
117741
|
}
|
|
@@ -117712,7 +117748,7 @@ const mdxishMdxComponentBlocks = (opts = {}) => tree => {
|
|
|
117712
117748
|
const componentInnerContent = contentAfterTag.substring(0, closingTagIndex);
|
|
117713
117749
|
const contentAfterClose = contentAfterTag.substring(closingTagIndex + closingTagStr.length).trim();
|
|
117714
117750
|
let parsedChildren = componentInnerContent.trim()
|
|
117715
|
-
? parseMdChildren(componentInnerContent)
|
|
117751
|
+
? parseMdChildren(componentInnerContent, safeMode)
|
|
117716
117752
|
: [];
|
|
117717
117753
|
// Lowercase HTML tags are usually inline (e.g. <a>, <span>). Remark wraps
|
|
117718
117754
|
// bare text in a paragraph; unwrap when there's exactly one paragraph so
|
|
@@ -117729,7 +117765,7 @@ const mdxishMdxComponentBlocks = (opts = {}) => tree => {
|
|
|
117729
117765
|
substituteNodeWithMdxNode(parent, index, componentNode);
|
|
117730
117766
|
// After the closing tag, there might be more content to be processed
|
|
117731
117767
|
if (contentAfterClose) {
|
|
117732
|
-
parseSibling(stack, parent, index, contentAfterClose);
|
|
117768
|
+
parseSibling(stack, parent, index, contentAfterClose, safeMode);
|
|
117733
117769
|
}
|
|
117734
117770
|
else if (componentNode.children.length > 0) {
|
|
117735
117771
|
// The content inside the component block might contain new components to be processed
|
|
@@ -119639,11 +119675,17 @@ const blockTypes = [
|
|
|
119639
119675
|
* Check if a node is a block-level node (cannot be inside a paragraph)
|
|
119640
119676
|
*/
|
|
119641
119677
|
const isBlockNode = (node) => blockTypes.includes(node.type);
|
|
119678
|
+
const isParagraph = (node) => node.type === 'paragraph';
|
|
119679
|
+
/**
|
|
119680
|
+
* True for phrasing content that contributes only whitespace at render time
|
|
119681
|
+
* (a soft `break` node or a text node with no non-whitespace characters).
|
|
119682
|
+
*/
|
|
119683
|
+
const isWhitespacePhrasing = (node) => node.type === 'break' || (node.type === 'text' && !node.value.trim());
|
|
119642
119684
|
/**
|
|
119643
119685
|
* Unified plugin that transforms magicBlock nodes into final MDAST nodes.
|
|
119644
119686
|
*/
|
|
119645
119687
|
const magicBlockTransformer = (options = {}) => tree => {
|
|
119646
|
-
const
|
|
119688
|
+
const lifts = [];
|
|
119647
119689
|
visitParents(tree, 'magicBlock', (node, ancestors) => {
|
|
119648
119690
|
const parent = ancestors[ancestors.length - 1]; // direct parent of the current node
|
|
119649
119691
|
const index = parent.children.indexOf(node);
|
|
@@ -119657,51 +119699,60 @@ const magicBlockTransformer = (options = {}) => tree => {
|
|
|
119657
119699
|
parent.children.splice(index, 1);
|
|
119658
119700
|
return;
|
|
119659
119701
|
}
|
|
119660
|
-
// If parent is a paragraph and we're inserting block nodes (which must not be in paragraphs)
|
|
119661
|
-
|
|
119702
|
+
// If parent is a paragraph and we're inserting block nodes (which must not be in paragraphs)
|
|
119703
|
+
// it means we need to lift them out
|
|
119704
|
+
if (isParagraph(parent) && children.some(isBlockNode)) {
|
|
119662
119705
|
const blockNodes = [];
|
|
119663
|
-
const inlineNodes = [];
|
|
119664
119706
|
children.forEach(child => {
|
|
119665
|
-
(isBlockNode(child)
|
|
119707
|
+
if (isBlockNode(child)) {
|
|
119708
|
+
blockNodes.push(child);
|
|
119709
|
+
}
|
|
119666
119710
|
});
|
|
119667
|
-
|
|
119668
|
-
|
|
119711
|
+
lifts.push({
|
|
119712
|
+
childrenBlockNodes: blockNodes,
|
|
119713
|
+
grandparent: ancestors[ancestors.length - 2] || tree, // grandparent of the current node
|
|
119714
|
+
node,
|
|
119669
119715
|
parent,
|
|
119670
|
-
blockNodes,
|
|
119671
|
-
inlineNodes,
|
|
119672
|
-
before: parent.children.slice(0, index),
|
|
119673
|
-
after: parent.children.slice(index + 1),
|
|
119674
119716
|
});
|
|
119675
119717
|
}
|
|
119676
119718
|
else {
|
|
119677
119719
|
parent.children.splice(index, 1, ...children);
|
|
119678
119720
|
}
|
|
119679
119721
|
});
|
|
119680
|
-
// Second pass: apply
|
|
119681
|
-
//
|
|
119682
|
-
|
|
119683
|
-
|
|
119684
|
-
const
|
|
119685
|
-
const
|
|
119686
|
-
|
|
119687
|
-
|
|
119722
|
+
// Second pass: apply lifts that move block nodes, and the content after them, out of paragraphs.
|
|
119723
|
+
// Operate on live state (find each node's current index now) and process in reverse so
|
|
119724
|
+
// that container insertions don't disturb earlier paragraphs' positions.
|
|
119725
|
+
for (let i = lifts.length - 1; i >= 0; i -= 1) {
|
|
119726
|
+
const { childrenBlockNodes: blockNodes, grandparent, node, parent: parentParagraph } = lifts[i];
|
|
119727
|
+
const nodePosition = parentParagraph.children.indexOf(node);
|
|
119728
|
+
const parentPosition = grandparent.children.indexOf(parentParagraph);
|
|
119729
|
+
if (nodePosition === -1 || parentPosition === -1) {
|
|
119688
119730
|
// eslint-disable-next-line no-continue
|
|
119689
119731
|
continue;
|
|
119690
119732
|
}
|
|
119691
|
-
|
|
119692
|
-
|
|
119693
|
-
|
|
119694
|
-
|
|
119695
|
-
|
|
119696
|
-
|
|
119697
|
-
|
|
119698
|
-
|
|
119733
|
+
// Snapshot live siblings to reconstruct the parent paragraph around the lifted node.
|
|
119734
|
+
const parentSiblingsBefore = parentParagraph.children.slice(0, nodePosition);
|
|
119735
|
+
const parentSiblingsAfter = parentParagraph.children.slice(nodePosition + 1);
|
|
119736
|
+
// Remove split-edge whitespace so lifted blocks do not render with extra blank lines.
|
|
119737
|
+
while (parentSiblingsBefore.length > 0 && isWhitespacePhrasing(parentSiblingsBefore[parentSiblingsBefore.length - 1]))
|
|
119738
|
+
parentSiblingsBefore.pop();
|
|
119739
|
+
while (parentSiblingsAfter.length > 0 && isWhitespacePhrasing(parentSiblingsAfter[0]))
|
|
119740
|
+
parentSiblingsAfter.shift();
|
|
119741
|
+
const splitOffContentFromParent = [...blockNodes];
|
|
119742
|
+
if (parentSiblingsAfter.length > 0) {
|
|
119743
|
+
// Keep trailing inline content grouped under a paragraph sibling as it might be an inline node
|
|
119744
|
+
// Even if it contains a block node, it will be hoisted out during its turn in the loop
|
|
119745
|
+
const trailingParagraph = { type: 'paragraph', children: parentSiblingsAfter };
|
|
119746
|
+
splitOffContentFromParent.push(trailingParagraph);
|
|
119747
|
+
}
|
|
119748
|
+
parentParagraph.children = [...parentSiblingsBefore];
|
|
119749
|
+
// If the parent paragraph is empty, just replace it with the lifted block nodes
|
|
119750
|
+
const shouldReplaceParent = parentParagraph.children.length === 0;
|
|
119751
|
+
if (shouldReplaceParent) {
|
|
119752
|
+
grandparent.children.splice(parentPosition, 1, ...splitOffContentFromParent);
|
|
119699
119753
|
}
|
|
119700
119754
|
else {
|
|
119701
|
-
|
|
119702
|
-
if (blockNodes.length > 0) {
|
|
119703
|
-
containerChildren.splice(paraIndex + 1, 0, ...blockNodes);
|
|
119704
|
-
}
|
|
119755
|
+
grandparent.children.splice(parentPosition + 1, 0, ...splitOffContentFromParent);
|
|
119705
119756
|
}
|
|
119706
119757
|
}
|
|
119707
119758
|
};
|
|
@@ -119771,29 +119822,56 @@ function protectHTMLBlockContent(content) {
|
|
|
119771
119822
|
function removeJSXComments(content) {
|
|
119772
119823
|
return content.replace(JSX_COMMENT_REGEX, '');
|
|
119773
119824
|
}
|
|
119825
|
+
const HTML_ELEM_PLACEHOLDER_PREFIX = '___MDXISH_HTML_ELEM_';
|
|
119826
|
+
const HTML_ELEM_PLACEHOLDER = new RegExp(`${HTML_ELEM_PLACEHOLDER_PREFIX}(\\d+)___`, 'g');
|
|
119827
|
+
// Matches an HTML element that starts at a line boundary and ends at a line boundary.
|
|
119828
|
+
// Allows optional leading indentation and lazily matches until the same closing tag.
|
|
119829
|
+
const BLOCK_HTML_RE = /(?<=^|\n)[ \t]*<([a-z][a-zA-Z0-9]*)(?:\s[^>]*)?>[\s\S]*?<\/\1>[ \t]*(?=\n|$)/g;
|
|
119774
119830
|
/**
|
|
119775
|
-
*
|
|
119776
|
-
*
|
|
119777
|
-
*
|
|
119831
|
+
* Hides line-anchored HTML elements from the brace-escaping pass so we don't leak `\{`
|
|
119832
|
+
* into rendered output (rehypeRaw renders the `\` literally, e.g. `<div>{foo</div>`).
|
|
119833
|
+
*
|
|
119834
|
+
* One carve-out: if an interior line at column 0 has bare text containing `{`, mdxish
|
|
119835
|
+
* parses that line as a paragraph and the mdxExpression step would throw without an
|
|
119836
|
+
* escape — so we leave that case to the brace balancer.
|
|
119778
119837
|
*/
|
|
119779
|
-
function
|
|
119780
|
-
// Skip HTML elements — their content should never be escaped because
|
|
119781
|
-
// rehypeRaw parses them into hast elements, making `\` literal text in output
|
|
119838
|
+
function protectHTMLElements(content) {
|
|
119782
119839
|
const htmlElements = [];
|
|
119783
|
-
const
|
|
119784
|
-
|
|
119840
|
+
const protectedContent = content.replace(BLOCK_HTML_RE, match => {
|
|
119841
|
+
// Look at the lines between the open and close tags. If any of them starts
|
|
119842
|
+
// at column 0 with bare text (not whitespace, not another tag) and contains
|
|
119843
|
+
// `{`, mdxish will parse that line as a paragraph and the brace as an MDX
|
|
119844
|
+
// expression, which would throw an error. So we let the brace balancer escape it.
|
|
119845
|
+
// Otherwise, we need to extract the sequence to protect it from the brace escaping.
|
|
119846
|
+
const interior = match.split('\n').slice(1, -1);
|
|
119847
|
+
const hazard = interior.some(line => line.length > 0 && line[0] !== ' ' && line[0] !== '\t' && line[0] !== '<' && line.includes('{'));
|
|
119848
|
+
if (hazard)
|
|
119849
|
+
return match;
|
|
119785
119850
|
htmlElements.push(match);
|
|
119786
|
-
return
|
|
119851
|
+
return `${HTML_ELEM_PLACEHOLDER_PREFIX}${htmlElements.length - 1}___`;
|
|
119787
119852
|
});
|
|
119788
|
-
|
|
119789
|
-
|
|
119790
|
-
|
|
119853
|
+
return { htmlElements, protectedContent };
|
|
119854
|
+
}
|
|
119855
|
+
function restoreHTMLElements(content, htmlElements) {
|
|
119856
|
+
if (htmlElements.length === 0)
|
|
119857
|
+
return content;
|
|
119858
|
+
return content.replace(HTML_ELEM_PLACEHOLDER, (_m, idx) => htmlElements[parseInt(idx, 10)]);
|
|
119859
|
+
}
|
|
119860
|
+
/**
|
|
119861
|
+
* Escapes unbalanced and paragraph-spanning braces so MDX doesn't trip on them.
|
|
119862
|
+
*/
|
|
119863
|
+
function escapeProblematicBraces(content) {
|
|
119864
|
+
const { htmlElements, protectedContent } = protectHTMLElements(content);
|
|
119791
119865
|
let strDelim = null;
|
|
119792
119866
|
let strEscaped = false;
|
|
119793
|
-
// Stack of open braces with their state
|
|
119794
|
-
const openStack = [];
|
|
119795
119867
|
// Track position of last newline (outside strings) to detect blank lines
|
|
119796
|
-
|
|
119868
|
+
// -2 means no recent newline
|
|
119869
|
+
let lastNewlinePos = -2;
|
|
119870
|
+
// Character state machine trackers
|
|
119871
|
+
const toEscape = new Set();
|
|
119872
|
+
// Convert to array of Unicode code points so that emojis and multi-byte characters are correctly tracked
|
|
119873
|
+
const chars = Array.from(protectedContent);
|
|
119874
|
+
const openStack = [];
|
|
119797
119875
|
for (let i = 0; i < chars.length; i += 1) {
|
|
119798
119876
|
const ch = chars[i];
|
|
119799
119877
|
// Track string delimiters inside expressions to ignore braces within them
|
|
@@ -119813,22 +119891,17 @@ function escapeProblematicBraces(content) {
|
|
|
119813
119891
|
// eslint-disable-next-line no-continue
|
|
119814
119892
|
continue;
|
|
119815
119893
|
}
|
|
119816
|
-
// Track newlines to detect blank lines (paragraph boundaries)
|
|
119817
119894
|
if (ch === '\n') {
|
|
119818
|
-
// Check if this newline creates a blank line (only whitespace since last newline)
|
|
119819
119895
|
if (lastNewlinePos >= 0) {
|
|
119820
119896
|
const between = chars.slice(lastNewlinePos + 1, i).join('');
|
|
119821
119897
|
if (/^[ \t]*$/.test(between)) {
|
|
119822
|
-
|
|
119823
|
-
openStack.forEach(entry => {
|
|
119824
|
-
entry.hasBlankLine = true;
|
|
119825
|
-
});
|
|
119898
|
+
openStack.forEach(entry => { entry.hasBlankLine = true; });
|
|
119826
119899
|
}
|
|
119827
119900
|
}
|
|
119828
119901
|
lastNewlinePos = i;
|
|
119829
119902
|
}
|
|
119830
119903
|
}
|
|
119831
|
-
// Skip already-escaped braces (
|
|
119904
|
+
// Skip already-escaped braces (odd run of preceding backslashes).
|
|
119832
119905
|
if (ch === '{' || ch === '}') {
|
|
119833
119906
|
let bs = 0;
|
|
119834
119907
|
for (let j = i - 1; j >= 0 && chars[j] === '\\'; j -= 1)
|
|
@@ -119839,10 +119912,9 @@ function escapeProblematicBraces(content) {
|
|
|
119839
119912
|
}
|
|
119840
119913
|
}
|
|
119841
119914
|
if (ch === '{') {
|
|
119842
|
-
//
|
|
119843
|
-
//
|
|
119844
|
-
//
|
|
119845
|
-
// won't split paragraphs — skip the blank-line check for these.
|
|
119915
|
+
// `=` (after whitespace) before `{` ⇒ JSX attribute expression. The
|
|
119916
|
+
// mdxComponent tokenizer captures the whole component, so blank lines
|
|
119917
|
+
// inside attribute values are harmless. Nested `{` inherits the flag.
|
|
119846
119918
|
let isAttrExpr = false;
|
|
119847
119919
|
for (let j = i - 1; j >= 0; j -= 1) {
|
|
119848
119920
|
const pc = chars[j];
|
|
@@ -119856,27 +119928,17 @@ function escapeProblematicBraces(content) {
|
|
|
119856
119928
|
// Nested `{ ... }` inside an attribute value (e.g. `data={[{ ... }]}` or
|
|
119857
119929
|
// `data={{ a: { b: 1 } }}`) must inherit the same exemption; only the
|
|
119858
119930
|
// outer `{` is directly after `=`.
|
|
119859
|
-
if (!isAttrExpr && openStack.length > 0) {
|
|
119860
|
-
|
|
119861
|
-
if (parent.isAttrExpr) {
|
|
119862
|
-
isAttrExpr = true;
|
|
119863
|
-
}
|
|
119931
|
+
if (!isAttrExpr && openStack.length > 0 && openStack[openStack.length - 1].isAttrExpr) {
|
|
119932
|
+
isAttrExpr = true;
|
|
119864
119933
|
}
|
|
119865
119934
|
openStack.push({ pos: i, hasBlankLine: false, isAttrExpr });
|
|
119866
|
-
lastNewlinePos = -2;
|
|
119935
|
+
lastNewlinePos = -2;
|
|
119867
119936
|
}
|
|
119868
119937
|
else if (ch === '}') {
|
|
119869
119938
|
if (openStack.length > 0) {
|
|
119870
119939
|
const entry = openStack.pop();
|
|
119871
|
-
//
|
|
119872
|
-
//
|
|
119873
|
-
// even if the body has blank lines in it. If we escape the braces here
|
|
119874
|
-
// the tokenizer never gets a shot at it.
|
|
119875
|
-
//
|
|
119876
|
-
// "Pure" means the braces open with `{/*` and close with `*/}` right
|
|
119877
|
-
// next to each other. Something like `{/* c */ expr\n\nmore}` is just
|
|
119878
|
-
// a regular expression that happens to start with a comment, so it
|
|
119879
|
-
// still needs the normal blank-line protection.
|
|
119940
|
+
// Pure `{/* ... */}` comments are handled downstream by the jsxComment
|
|
119941
|
+
// tokenizer — escaping their braces would prevent it from running.
|
|
119880
119942
|
const isPureJsxComment = chars[entry.pos + 1] === '/' &&
|
|
119881
119943
|
chars[entry.pos + 2] === '*' &&
|
|
119882
119944
|
chars[i - 1] === '/' &&
|
|
@@ -119887,21 +119949,15 @@ function escapeProblematicBraces(content) {
|
|
|
119887
119949
|
}
|
|
119888
119950
|
}
|
|
119889
119951
|
else {
|
|
119890
|
-
// Unbalanced closing brace (no matching open)
|
|
119891
119952
|
toEscape.add(i);
|
|
119892
119953
|
}
|
|
119893
119954
|
}
|
|
119894
119955
|
}
|
|
119895
|
-
//
|
|
119956
|
+
// Anything still open is unbalanced.
|
|
119896
119957
|
openStack.forEach(entry => toEscape.add(entry.pos));
|
|
119897
|
-
//
|
|
119898
|
-
|
|
119899
|
-
|
|
119900
|
-
// Restore HTML elements
|
|
119901
|
-
if (htmlElements.length > 0) {
|
|
119902
|
-
result = result.replace(/___HTML_ELEM_(\d+)___/g, (_m, idx) => htmlElements[parseInt(idx, 10)]);
|
|
119903
|
-
}
|
|
119904
|
-
return result;
|
|
119958
|
+
// Reconstruct the content with the escaped braces.
|
|
119959
|
+
const escapedContent = toEscape.size === 0 ? protectedContent : chars.map((ch, i) => (toEscape.has(i) ? `\\${ch}` : ch)).join('');
|
|
119960
|
+
return restoreHTMLElements(escapedContent, htmlElements);
|
|
119905
119961
|
}
|
|
119906
119962
|
/**
|
|
119907
119963
|
* Preprocesses JSX-like markdown content before parsing.
|
|
@@ -122047,15 +122103,18 @@ function mdxishAstProcessor(mdContent, opts = {}) {
|
|
|
122047
122103
|
.use(remarkParse)
|
|
122048
122104
|
.use(remarkFrontmatter)
|
|
122049
122105
|
.use(normalize_malformed_md_syntax)
|
|
122050
|
-
.use(magic_block_transformer)
|
|
122051
|
-
.use(transform_images, { isMdxish: true })
|
|
122052
|
-
.use(defaultTransformers)
|
|
122053
122106
|
.use(self_closing_blocks)
|
|
122054
122107
|
.use(mdx_blocks, { safeMode })
|
|
122055
122108
|
.use(inline_html, { safeMode })
|
|
122056
122109
|
.use(restore_snake_case_component_name, { mapping: snakeCaseMapping })
|
|
122057
122110
|
.use(mdxish_tables)
|
|
122058
122111
|
.use(mdxish_html_blocks)
|
|
122112
|
+
// The next few transformers must appear after mdxishMdxComponentBlocks
|
|
122113
|
+
// so nodes produced by the inline re-parse of component bodies
|
|
122114
|
+
// (e.g. code/image/embed inside <Tabs>) get visited too
|
|
122115
|
+
.use(magic_block_transformer)
|
|
122116
|
+
.use(transform_images, { isMdxish: true })
|
|
122117
|
+
.use(defaultTransformers)
|
|
122059
122118
|
.use(newEditorTypes ? inline_mdx_blocks : undefined) // Merge inline html components (e.g. <Anchor>) into MDAST nodes
|
|
122060
122119
|
.use(newEditorTypes ? mdxish_jsx_to_mdast : undefined) // Convert block JSX elements to MDAST types
|
|
122061
122120
|
.use(variables_text) // Parse {user.*} patterns from text nodes
|