@readme/markdown 12.2.0 → 13.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/components/Glossary/style.scss +1 -1
- package/components/Image/index.tsx +6 -2
- package/dist/components/Image/index.d.ts +1 -1
- package/dist/lib/mdast-util/magic-block/index.d.ts +19 -0
- package/dist/lib/mdast-util/magic-block/types.d.ts +30 -0
- package/dist/lib/mdxish.d.ts +11 -0
- package/dist/lib/micromark/magic-block/index.d.ts +12 -0
- package/dist/lib/micromark/magic-block/syntax.d.ts +35 -0
- package/dist/lib/utils/mdxish/protect-code-blocks.d.ts +61 -0
- package/dist/main.css +1 -1
- package/dist/main.css.map +1 -1
- package/dist/main.js +2441 -736
- package/dist/main.node.js +2441 -736
- package/dist/main.node.js.map +1 -1
- package/dist/processor/compile/index.d.ts +3 -1
- package/dist/processor/compile/variable.d.ts +10 -0
- package/dist/processor/transform/mdxish/magic-blocks/magic-block-transformer.d.ts +15 -0
- package/dist/processor/transform/mdxish/magic-blocks/placeholder.d.ts +72 -0
- package/dist/processor/transform/mdxish/magic-blocks/types.d.ts +120 -0
- package/dist/processor/transform/mdxish/mdxish-jsx-to-mdast.d.ts +15 -0
- package/dist/processor/transform/mdxish/mdxish-snake-case-components.d.ts +13 -1
- package/package.json +4 -2
- package/styles/gfm.scss +4 -0
- package/dist/lib/utils/extractMagicBlocks.d.ts +0 -25
- package/dist/processor/transform/mdxish/mdxish-magic-blocks.d.ts +0 -25
package/dist/main.js
CHANGED
|
@@ -11908,7 +11908,9 @@ const CreateHeading = (depth) => {
|
|
|
11908
11908
|
;// ./components/Image/index.tsx
|
|
11909
11909
|
|
|
11910
11910
|
const Image = (Props) => {
|
|
11911
|
-
const { align = '', alt = '', border = false, caption, className = '', height = 'auto', src, title = '', width = 'auto', lazy = true, children, } = Props;
|
|
11911
|
+
const { align = '', alt = '', border: borderProp = false, caption, className = '', height = 'auto', src, title = '', width = 'auto', lazy = true, children, } = Props;
|
|
11912
|
+
// Normalize border: MDXish passes {false} as the string "false", not a boolean
|
|
11913
|
+
const border = borderProp === true || borderProp === 'true';
|
|
11912
11914
|
const [lightbox, setLightbox] = external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_.useState(false);
|
|
11913
11915
|
if (className === 'emoji') {
|
|
11914
11916
|
return external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_.createElement("img", { alt: alt, height: height, loading: lazy ? 'lazy' : 'eager', src: src, title: title, width: width });
|
|
@@ -14345,7 +14347,7 @@ function htmlExtension(all, extension) {
|
|
|
14345
14347
|
|
|
14346
14348
|
;// ./node_modules/micromark-util-character/index.js
|
|
14347
14349
|
/**
|
|
14348
|
-
* @
|
|
14350
|
+
* @import {Code} from 'micromark-util-types'
|
|
14349
14351
|
*/
|
|
14350
14352
|
|
|
14351
14353
|
/**
|
|
@@ -14571,7 +14573,9 @@ const unicodeWhitespace = regexCheck(/\s/);
|
|
|
14571
14573
|
* Create a code check from a regex.
|
|
14572
14574
|
*
|
|
14573
14575
|
* @param {RegExp} regex
|
|
14576
|
+
* Expression.
|
|
14574
14577
|
* @returns {(code: Code) => boolean}
|
|
14578
|
+
* Check.
|
|
14575
14579
|
*/
|
|
14576
14580
|
function regexCheck(regex) {
|
|
14577
14581
|
return check;
|
|
@@ -53098,6 +53102,59 @@ const isCalloutStructure = (node) => {
|
|
|
53098
53102
|
const firstTextChild = firstChild.children?.[0];
|
|
53099
53103
|
return firstTextChild?.type === 'text';
|
|
53100
53104
|
};
|
|
53105
|
+
/**
|
|
53106
|
+
* Finds the first text node containing a newline in a paragraph's children.
|
|
53107
|
+
* Returns the index and the newline position within that text node.
|
|
53108
|
+
*/
|
|
53109
|
+
const findNewlineInParagraph = (paragraph) => {
|
|
53110
|
+
for (let i = 0; i < paragraph.children.length; i += 1) {
|
|
53111
|
+
const child = paragraph.children[i];
|
|
53112
|
+
if (child.type === 'text' && typeof child.value === 'string') {
|
|
53113
|
+
const newlineIndex = child.value.indexOf('\n');
|
|
53114
|
+
if (newlineIndex !== -1) {
|
|
53115
|
+
return { index: i, newlineIndex };
|
|
53116
|
+
}
|
|
53117
|
+
}
|
|
53118
|
+
}
|
|
53119
|
+
return null;
|
|
53120
|
+
};
|
|
53121
|
+
/**
|
|
53122
|
+
* Splits a paragraph at the first newline, separating heading content (before \n)
|
|
53123
|
+
* from body content (after \n). Mutates the paragraph to contain only heading children.
|
|
53124
|
+
*/
|
|
53125
|
+
const splitParagraphAtNewline = (paragraph) => {
|
|
53126
|
+
const splitPoint = findNewlineInParagraph(paragraph);
|
|
53127
|
+
if (!splitPoint)
|
|
53128
|
+
return null;
|
|
53129
|
+
const { index, newlineIndex } = splitPoint;
|
|
53130
|
+
const originalChildren = paragraph.children;
|
|
53131
|
+
const textNode = originalChildren[index];
|
|
53132
|
+
const beforeNewline = textNode.value.slice(0, newlineIndex);
|
|
53133
|
+
const afterNewline = textNode.value.slice(newlineIndex + 1);
|
|
53134
|
+
// Split paragraph: heading = children[0..index-1] + text before newline
|
|
53135
|
+
const headingChildren = originalChildren.slice(0, index);
|
|
53136
|
+
if (beforeNewline.length > 0 || headingChildren.length === 0) {
|
|
53137
|
+
headingChildren.push({ type: 'text', value: beforeNewline });
|
|
53138
|
+
}
|
|
53139
|
+
paragraph.children = headingChildren;
|
|
53140
|
+
// Body = text after newline + remaining children from original array
|
|
53141
|
+
const bodyChildren = [];
|
|
53142
|
+
if (afterNewline.length > 0) {
|
|
53143
|
+
bodyChildren.push({ type: 'text', value: afterNewline });
|
|
53144
|
+
}
|
|
53145
|
+
bodyChildren.push(...originalChildren.slice(index + 1));
|
|
53146
|
+
return bodyChildren.length > 0 ? bodyChildren : null;
|
|
53147
|
+
};
|
|
53148
|
+
/**
|
|
53149
|
+
* Removes the icon/match prefix from the first text node in a paragraph.
|
|
53150
|
+
* This is needed to clean up the raw AST after we've extracted the icon.
|
|
53151
|
+
*/
|
|
53152
|
+
const removeIconPrefix = (paragraph, prefixLength) => {
|
|
53153
|
+
const firstTextNode = findFirst(paragraph);
|
|
53154
|
+
if (firstTextNode && 'value' in firstTextNode && typeof firstTextNode.value === 'string') {
|
|
53155
|
+
firstTextNode.value = firstTextNode.value.slice(prefixLength);
|
|
53156
|
+
}
|
|
53157
|
+
};
|
|
53101
53158
|
const processBlockquote = (node, index, parent) => {
|
|
53102
53159
|
if (!isCalloutStructure(node)) {
|
|
53103
53160
|
// Only stringify empty blockquotes (no extractable text content)
|
|
@@ -53122,22 +53179,39 @@ const processBlockquote = (node, index, parent) => {
|
|
|
53122
53179
|
const firstParagraph = node.children[0];
|
|
53123
53180
|
const startText = lib_plain(firstParagraph).toString();
|
|
53124
53181
|
const [match, icon] = startText.match(callouts_regex) || [];
|
|
53182
|
+
const firstParagraphOriginalEnd = firstParagraph.position.end;
|
|
53125
53183
|
if (icon && match) {
|
|
53126
|
-
|
|
53127
|
-
|
|
53184
|
+
// Handle cases where heading and body are on the same line separated by a newline.
|
|
53185
|
+
// Example: "> ⚠️ **Bold heading**\nBody text here"
|
|
53186
|
+
const bodyChildren = splitParagraphAtNewline(firstParagraph);
|
|
53187
|
+
const didSplit = bodyChildren !== null;
|
|
53188
|
+
// Extract heading text after removing the icon prefix.
|
|
53189
|
+
// Use `plain()` to handle complex markdown structures (bold, inline code, etc.)
|
|
53190
|
+
const headingText = lib_plain(firstParagraph)
|
|
53191
|
+
.toString()
|
|
53192
|
+
.slice(match.length);
|
|
53193
|
+
// Clean up the raw AST by removing the icon prefix from the first text node
|
|
53194
|
+
removeIconPrefix(firstParagraph, match.length);
|
|
53195
|
+
const empty = !headingText.length && firstParagraph.children.length === 1;
|
|
53128
53196
|
const theme = themes[icon] || 'default';
|
|
53129
|
-
|
|
53130
|
-
if (
|
|
53131
|
-
firstChild.value = firstChild.value.slice(match.length);
|
|
53132
|
-
}
|
|
53133
|
-
if (heading) {
|
|
53197
|
+
// Convert the first paragraph (first children of node) to a heading if it has content or was split
|
|
53198
|
+
if (headingText || didSplit) {
|
|
53134
53199
|
node.children[0] = wrapHeading(node);
|
|
53135
|
-
//
|
|
53136
|
-
// character that was stripped off, so that the start position of the
|
|
53137
|
-
// heading/text matches where it actually starts.
|
|
53200
|
+
// Adjust position to account for the stripped icon prefix
|
|
53138
53201
|
node.children[0].position.start.offset += match.length;
|
|
53139
53202
|
node.children[0].position.start.column += match.length;
|
|
53140
53203
|
}
|
|
53204
|
+
// Insert body content as a separate paragraph after the heading
|
|
53205
|
+
if (bodyChildren) {
|
|
53206
|
+
node.children.splice(1, 0, {
|
|
53207
|
+
type: 'paragraph',
|
|
53208
|
+
children: bodyChildren,
|
|
53209
|
+
position: {
|
|
53210
|
+
start: node.children[0].position.end,
|
|
53211
|
+
end: firstParagraphOriginalEnd,
|
|
53212
|
+
},
|
|
53213
|
+
});
|
|
53214
|
+
}
|
|
53141
53215
|
Object.assign(node, {
|
|
53142
53216
|
type: NodeTypes.callout,
|
|
53143
53217
|
data: {
|
|
@@ -70517,7 +70591,7 @@ const toAttributes = (object, keys = []) => {
|
|
|
70517
70591
|
if (keys.length > 0 && !keys.includes(name))
|
|
70518
70592
|
return;
|
|
70519
70593
|
let value;
|
|
70520
|
-
if (typeof v === 'undefined' || v === null || v === '') {
|
|
70594
|
+
if (typeof v === 'undefined' || v === null || v === '' || v === false) {
|
|
70521
70595
|
return;
|
|
70522
70596
|
}
|
|
70523
70597
|
else if (typeof v === 'string') {
|
|
@@ -86552,7 +86626,7 @@ const CUSTOM_PROP_BOUNDARIES = [
|
|
|
86552
86626
|
/**
|
|
86553
86627
|
* Tags that should be passed through and handled at runtime (not by the mdxish plugin)
|
|
86554
86628
|
*/
|
|
86555
|
-
const RUNTIME_COMPONENT_TAGS = new Set(['Variable', 'variable', 'rdme-pin']);
|
|
86629
|
+
const RUNTIME_COMPONENT_TAGS = new Set(['Variable', 'variable', 'html-block', 'rdme-pin']);
|
|
86556
86630
|
/**
|
|
86557
86631
|
* Standard HTML tags that should never be treated as custom components.
|
|
86558
86632
|
* Uses the html-tags package, converted to a Set<string> for efficient lookups.
|
|
@@ -91665,6 +91739,10 @@ ${reformatHTML(html)}
|
|
|
91665
91739
|
const plain_plain = (node) => node.value;
|
|
91666
91740
|
/* harmony default export */ const compile_plain = (plain_plain);
|
|
91667
91741
|
|
|
91742
|
+
;// ./processor/compile/variable.ts
|
|
91743
|
+
const variable = (node) => `{user.${node.data?.hProperties?.name || ''}}`;
|
|
91744
|
+
/* harmony default export */ const compile_variable = (variable);
|
|
91745
|
+
|
|
91668
91746
|
;// ./processor/compile/index.ts
|
|
91669
91747
|
|
|
91670
91748
|
|
|
@@ -91674,7 +91752,8 @@ const plain_plain = (node) => node.value;
|
|
|
91674
91752
|
|
|
91675
91753
|
|
|
91676
91754
|
|
|
91677
|
-
|
|
91755
|
+
|
|
91756
|
+
function compilers(mdxish = false) {
|
|
91678
91757
|
const data = this.data();
|
|
91679
91758
|
const toMarkdownExtensions = data.toMarkdownExtensions || (data.toMarkdownExtensions = []);
|
|
91680
91759
|
const handlers = {
|
|
@@ -91685,6 +91764,7 @@ function compilers() {
|
|
|
91685
91764
|
[NodeTypes.glossary]: compile_compatibility,
|
|
91686
91765
|
[NodeTypes.htmlBlock]: html_block,
|
|
91687
91766
|
[NodeTypes.reusableContent]: compile_compatibility,
|
|
91767
|
+
...(mdxish && { [NodeTypes.variable]: compile_variable }),
|
|
91688
91768
|
embed: compile_compatibility,
|
|
91689
91769
|
escape: compile_compatibility,
|
|
91690
91770
|
figure: compile_compatibility,
|
|
@@ -91695,6 +91775,9 @@ function compilers() {
|
|
|
91695
91775
|
};
|
|
91696
91776
|
toMarkdownExtensions.push({ extensions: [{ handlers }] });
|
|
91697
91777
|
}
|
|
91778
|
+
function mdxishCompilers() {
|
|
91779
|
+
return compilers.call(this, true);
|
|
91780
|
+
}
|
|
91698
91781
|
/* harmony default export */ const processor_compile = (compilers);
|
|
91699
91782
|
|
|
91700
91783
|
;// ./processor/transform/escape-pipes-in-tables.ts
|
|
@@ -93405,13 +93488,17 @@ const htmlBlockHandler = (_state, node) => {
|
|
|
93405
93488
|
const embedHandler = (state, node) => {
|
|
93406
93489
|
// Assert to get the minimum properties we need
|
|
93407
93490
|
const { data } = node;
|
|
93491
|
+
// Magic block embeds (hName === 'embed-block') render as Embed component
|
|
93492
|
+
// which doesn't use children - it renders based on props only
|
|
93493
|
+
const isMagicBlockEmbed = data?.hName === NodeTypes.embedBlock;
|
|
93408
93494
|
return {
|
|
93409
93495
|
type: 'element',
|
|
93410
93496
|
// To differentiate between regular embeds and magic block embeds,
|
|
93411
93497
|
// magic block embeds have a certain hName
|
|
93412
|
-
tagName:
|
|
93498
|
+
tagName: isMagicBlockEmbed ? 'Embed' : 'embed',
|
|
93413
93499
|
properties: data?.hProperties,
|
|
93414
|
-
children
|
|
93500
|
+
// Don't include children for magic block embeds - Embed component renders based on props
|
|
93501
|
+
children: isMagicBlockEmbed ? [] : state.all(node),
|
|
93415
93502
|
};
|
|
93416
93503
|
};
|
|
93417
93504
|
const mdxComponentHandlers = {
|
|
@@ -93424,7 +93511,102 @@ const mdxComponentHandlers = {
|
|
|
93424
93511
|
[NodeTypes.htmlBlock]: htmlBlockHandler,
|
|
93425
93512
|
};
|
|
93426
93513
|
|
|
93514
|
+
;// ./lib/utils/mdxish/protect-code-blocks.ts
|
|
93515
|
+
/**
|
|
93516
|
+
* Replaces code blocks and inline code with placeholders to protect them from preprocessing.
|
|
93517
|
+
*
|
|
93518
|
+
* @param content - The markdown content to process
|
|
93519
|
+
* @returns Object containing protected content and arrays of original code blocks
|
|
93520
|
+
* @example
|
|
93521
|
+
* ```typescript
|
|
93522
|
+
* const input = 'Text with `inline code` and ```fenced block```';
|
|
93523
|
+
* protectCodeBlocks(input)
|
|
93524
|
+
* // Returns: {
|
|
93525
|
+
* // protectedCode: {
|
|
93526
|
+
* // codeBlocks: ['```fenced block```'],
|
|
93527
|
+
* // inlineCode: ['`inline code`']
|
|
93528
|
+
* // },
|
|
93529
|
+
* // protectedContent: 'Text with ___INLINE_CODE_0___ and ___CODE_BLOCK_0___'
|
|
93530
|
+
* // }
|
|
93531
|
+
* ```
|
|
93532
|
+
*/
|
|
93533
|
+
function protectCodeBlocks(content) {
|
|
93534
|
+
const codeBlocks = [];
|
|
93535
|
+
const inlineCode = [];
|
|
93536
|
+
let protectedContent = '';
|
|
93537
|
+
let remaining = content;
|
|
93538
|
+
let codeBlockStart = remaining.indexOf('```');
|
|
93539
|
+
while (codeBlockStart !== -1) {
|
|
93540
|
+
protectedContent += remaining.slice(0, codeBlockStart);
|
|
93541
|
+
remaining = remaining.slice(codeBlockStart);
|
|
93542
|
+
const codeBlockEnd = remaining.indexOf('```', 3);
|
|
93543
|
+
if (codeBlockEnd === -1) {
|
|
93544
|
+
break;
|
|
93545
|
+
}
|
|
93546
|
+
const match = remaining.slice(0, codeBlockEnd + 3);
|
|
93547
|
+
const index = codeBlocks.length;
|
|
93548
|
+
codeBlocks.push(match);
|
|
93549
|
+
protectedContent += `___CODE_BLOCK_${index}___`;
|
|
93550
|
+
remaining = remaining.slice(codeBlockEnd + 3);
|
|
93551
|
+
codeBlockStart = remaining.indexOf('```');
|
|
93552
|
+
}
|
|
93553
|
+
protectedContent += remaining;
|
|
93554
|
+
protectedContent = protectedContent.replace(/`([^`\n]+)`/g, match => {
|
|
93555
|
+
const index = inlineCode.length;
|
|
93556
|
+
inlineCode.push(match);
|
|
93557
|
+
return `___INLINE_CODE_${index}___`;
|
|
93558
|
+
});
|
|
93559
|
+
return { protectedCode: { codeBlocks, inlineCode }, protectedContent };
|
|
93560
|
+
}
|
|
93561
|
+
/**
|
|
93562
|
+
* Restores inline code by replacing placeholders with original content.
|
|
93563
|
+
*
|
|
93564
|
+
* @param content - Content with inline code placeholders
|
|
93565
|
+
* @param protectedCode - The protected code arrays
|
|
93566
|
+
* @returns Content with inline code restored
|
|
93567
|
+
*/
|
|
93568
|
+
function restoreInlineCode(content, protectedCode) {
|
|
93569
|
+
return content.replace(/___INLINE_CODE_(\d+)___/g, (_m, idx) => {
|
|
93570
|
+
return protectedCode.inlineCode[parseInt(idx, 10)];
|
|
93571
|
+
});
|
|
93572
|
+
}
|
|
93573
|
+
/**
|
|
93574
|
+
* Restores fenced code blocks by replacing placeholders with original content.
|
|
93575
|
+
*
|
|
93576
|
+
* @param content - Content with code block placeholders
|
|
93577
|
+
* @param protectedCode - The protected code arrays
|
|
93578
|
+
* @returns Content with code blocks restored
|
|
93579
|
+
*/
|
|
93580
|
+
function restoreFencedCodeBlocks(content, protectedCode) {
|
|
93581
|
+
return content.replace(/___CODE_BLOCK_(\d+)___/g, (_m, idx) => {
|
|
93582
|
+
return protectedCode.codeBlocks[parseInt(idx, 10)];
|
|
93583
|
+
});
|
|
93584
|
+
}
|
|
93585
|
+
/**
|
|
93586
|
+
* Restores all code blocks and inline code by replacing placeholders with original content.
|
|
93587
|
+
*
|
|
93588
|
+
* @param content - Content with code placeholders
|
|
93589
|
+
* @param protectedCode - The protected code arrays
|
|
93590
|
+
* @returns Content with all code blocks and inline code restored
|
|
93591
|
+
* @example
|
|
93592
|
+
* ```typescript
|
|
93593
|
+
* const content = 'Text with ___INLINE_CODE_0___ and ___CODE_BLOCK_0___';
|
|
93594
|
+
* const protectedCode = {
|
|
93595
|
+
* codeBlocks: ['```js\ncode\n```'],
|
|
93596
|
+
* inlineCode: ['`inline`']
|
|
93597
|
+
* };
|
|
93598
|
+
* restoreCodeBlocks(content, protectedCode)
|
|
93599
|
+
* // Returns: 'Text with `inline` and ```js\ncode\n```'
|
|
93600
|
+
* ```
|
|
93601
|
+
*/
|
|
93602
|
+
function restoreCodeBlocks(content, protectedCode) {
|
|
93603
|
+
let restored = restoreFencedCodeBlocks(content, protectedCode);
|
|
93604
|
+
restored = restoreInlineCode(restored, protectedCode);
|
|
93605
|
+
return restored;
|
|
93606
|
+
}
|
|
93607
|
+
|
|
93427
93608
|
;// ./processor/transform/mdxish/preprocess-jsx-expressions.ts
|
|
93609
|
+
|
|
93428
93610
|
// Base64 encode (Node.js + browser compatible)
|
|
93429
93611
|
function base64Encode(str) {
|
|
93430
93612
|
if (typeof Buffer !== 'undefined') {
|
|
@@ -93491,52 +93673,6 @@ function protectHTMLBlockContent(content) {
|
|
|
93491
93673
|
return `${openTag}${HTML_BLOCK_CONTENT_START}${encoded}${HTML_BLOCK_CONTENT_END}${closeTag}`;
|
|
93492
93674
|
});
|
|
93493
93675
|
}
|
|
93494
|
-
/**
|
|
93495
|
-
* Replaces code blocks and inline code with placeholders to protect them from JSX processing.
|
|
93496
|
-
*
|
|
93497
|
-
* @param content
|
|
93498
|
-
* @returns Object containing protected content and arrays of original code blocks
|
|
93499
|
-
* @example
|
|
93500
|
-
* ```typescript
|
|
93501
|
-
* const input = 'Text with `inline code` and ```fenced block```';
|
|
93502
|
-
* protectCodeBlocks(input)
|
|
93503
|
-
* // Returns: {
|
|
93504
|
-
* // protectedCode: {
|
|
93505
|
-
* // codeBlocks: ['```fenced block```'],
|
|
93506
|
-
* // inlineCode: ['`inline code`']
|
|
93507
|
-
* // },
|
|
93508
|
-
* // protectedContent: 'Text with ___INLINE_CODE_0___ and ___CODE_BLOCK_0___'
|
|
93509
|
-
* // }
|
|
93510
|
-
* ```
|
|
93511
|
-
*/
|
|
93512
|
-
function protectCodeBlocks(content) {
|
|
93513
|
-
const codeBlocks = [];
|
|
93514
|
-
const inlineCode = [];
|
|
93515
|
-
let protectedContent = '';
|
|
93516
|
-
let remaining = content;
|
|
93517
|
-
let codeBlockStart = remaining.indexOf('```');
|
|
93518
|
-
while (codeBlockStart !== -1) {
|
|
93519
|
-
protectedContent += remaining.slice(0, codeBlockStart);
|
|
93520
|
-
remaining = remaining.slice(codeBlockStart);
|
|
93521
|
-
const codeBlockEnd = remaining.indexOf('```', 3);
|
|
93522
|
-
if (codeBlockEnd === -1) {
|
|
93523
|
-
break;
|
|
93524
|
-
}
|
|
93525
|
-
const match = remaining.slice(0, codeBlockEnd + 3);
|
|
93526
|
-
const index = codeBlocks.length;
|
|
93527
|
-
codeBlocks.push(match);
|
|
93528
|
-
protectedContent += `___CODE_BLOCK_${index}___`;
|
|
93529
|
-
remaining = remaining.slice(codeBlockEnd + 3);
|
|
93530
|
-
codeBlockStart = remaining.indexOf('```');
|
|
93531
|
-
}
|
|
93532
|
-
protectedContent += remaining;
|
|
93533
|
-
protectedContent = protectedContent.replace(/`[^`]+`/g, match => {
|
|
93534
|
-
const index = inlineCode.length;
|
|
93535
|
-
inlineCode.push(match);
|
|
93536
|
-
return `___INLINE_CODE_${index}___`;
|
|
93537
|
-
});
|
|
93538
|
-
return { protectedCode: { codeBlocks, inlineCode }, protectedContent };
|
|
93539
|
-
}
|
|
93540
93676
|
/**
|
|
93541
93677
|
* Removes JSX-style comments (e.g., { /* comment *\/ }) from content.
|
|
93542
93678
|
*
|
|
@@ -93579,16 +93715,6 @@ function extractBalancedBraces(content, start) {
|
|
|
93579
93715
|
return null;
|
|
93580
93716
|
return { content: content.slice(start, pos - 1), end: pos };
|
|
93581
93717
|
}
|
|
93582
|
-
function restoreInlineCode(content, protectedCode) {
|
|
93583
|
-
return content.replace(/___INLINE_CODE_(\d+)___/g, (_m, idx) => {
|
|
93584
|
-
return protectedCode.inlineCode[parseInt(idx, 10)];
|
|
93585
|
-
});
|
|
93586
|
-
}
|
|
93587
|
-
function restoreCodeBlocks(content, protectedCode) {
|
|
93588
|
-
return content.replace(/___CODE_BLOCK_(\d+)___/g, (_m, idx) => {
|
|
93589
|
-
return protectedCode.codeBlocks[parseInt(idx, 10)];
|
|
93590
|
-
});
|
|
93591
|
-
}
|
|
93592
93718
|
/**
|
|
93593
93719
|
* Escapes unbalanced braces in content to prevent MDX expression parsing errors.
|
|
93594
93720
|
* Handles: already-escaped braces, string literals inside expressions, nested balanced braces.
|
|
@@ -93719,28 +93845,6 @@ function evaluateAttributeExpressions(content, context, protectedCode) {
|
|
|
93719
93845
|
result += content.slice(lastEnd);
|
|
93720
93846
|
return result;
|
|
93721
93847
|
}
|
|
93722
|
-
/**
|
|
93723
|
-
* Restores code blocks and inline code by replacing placeholders with original content.
|
|
93724
|
-
*
|
|
93725
|
-
* @param content
|
|
93726
|
-
* @param protectedCode
|
|
93727
|
-
* @returns Content with all code blocks and inline code restored
|
|
93728
|
-
* @example
|
|
93729
|
-
* ```typescript
|
|
93730
|
-
* const content = 'Text with ___INLINE_CODE_0___ and ___CODE_BLOCK_0___';
|
|
93731
|
-
* const protectedCode = {
|
|
93732
|
-
* codeBlocks: ['```js\ncode\n```'],
|
|
93733
|
-
* inlineCode: ['`inline`']
|
|
93734
|
-
* };
|
|
93735
|
-
* restoreCodeBlocks(content, protectedCode)
|
|
93736
|
-
* // Returns: 'Text with `inline` and ```js\ncode\n```'
|
|
93737
|
-
* ```
|
|
93738
|
-
*/
|
|
93739
|
-
function restoreProtectedCodes(content, protectedCode) {
|
|
93740
|
-
let restored = restoreCodeBlocks(content, protectedCode);
|
|
93741
|
-
restored = restoreInlineCode(restored, protectedCode);
|
|
93742
|
-
return restored;
|
|
93743
|
-
}
|
|
93744
93848
|
/**
|
|
93745
93849
|
* Preprocesses JSX-like expressions in markdown before parsing.
|
|
93746
93850
|
* Inline expressions are handled separately; attribute expressions are processed here.
|
|
@@ -93763,7 +93867,7 @@ function preprocessJSXExpressions(content, context = {}) {
|
|
|
93763
93867
|
// Step 4: Escape unbalanced braces to prevent MDX expression parsing errors
|
|
93764
93868
|
processed = escapeUnbalancedBraces(processed);
|
|
93765
93869
|
// Step 5: Restore protected code blocks
|
|
93766
|
-
processed =
|
|
93870
|
+
processed = restoreCodeBlocks(processed, protectedCode);
|
|
93767
93871
|
return processed;
|
|
93768
93872
|
}
|
|
93769
93873
|
|
|
@@ -93818,360 +93922,407 @@ const evaluateExpressions = ({ context = {} } = {}) => tree => {
|
|
|
93818
93922
|
};
|
|
93819
93923
|
/* harmony default export */ const evaluate_expressions = (evaluateExpressions);
|
|
93820
93924
|
|
|
93821
|
-
;// ./processor/transform/mdxish/
|
|
93822
|
-
|
|
93823
|
-
|
|
93824
|
-
|
|
93925
|
+
;// ./processor/transform/mdxish/normalize-malformed-md-syntax.ts
|
|
93825
93926
|
|
|
93826
|
-
|
|
93827
|
-
|
|
93828
|
-
|
|
93829
|
-
|
|
93830
|
-
|
|
93831
|
-
|
|
93832
|
-
|
|
93833
|
-
|
|
93834
|
-
|
|
93835
|
-
|
|
93836
|
-
|
|
93837
|
-
|
|
93927
|
+
// Marker patterns for multi-node emphasis detection
|
|
93928
|
+
const MARKER_PATTERNS = [
|
|
93929
|
+
{ isBold: true, marker: '**' },
|
|
93930
|
+
{ isBold: true, marker: '__' },
|
|
93931
|
+
{ isBold: false, marker: '*' },
|
|
93932
|
+
{ isBold: false, marker: '_' },
|
|
93933
|
+
];
|
|
93934
|
+
// Patterns to detect for bold (** and __) and italic (* and _) syntax:
|
|
93935
|
+
// Bold: ** text**, **text **, word** text**, ** text **
|
|
93936
|
+
// Italic: * text*, *text *, word* text*, * text *
|
|
93937
|
+
// Same patterns for underscore variants
|
|
93938
|
+
// We use separate patterns for each marker type to allow this flexibility.
|
|
93939
|
+
// Pattern for ** bold **
|
|
93940
|
+
// Groups: 1=wordBefore, 2=marker, 3=contentWithSpaceAfter, 4=trailingSpace1, 5=contentWithSpaceBefore, 6=trailingSpace2, 7=afterChar
|
|
93941
|
+
// trailingSpace1 is for "** text **" pattern, trailingSpace2 is for "**text **" pattern
|
|
93942
|
+
const asteriskBoldRegex = /([^*\s]+)?\s*(\*\*)(?:\s+((?:[^*\n]|\*(?!\*))+?)(\s*)\2|((?:[^*\n]|\*(?!\*))+?)(\s+)\2)(\S|$)?/g;
|
|
93943
|
+
// Pattern for __ bold __
|
|
93944
|
+
const underscoreBoldRegex = /([^_\s]+)?\s*(__)(?:\s+((?:[^_\n]|_(?!_))+?)(\s*)\2|((?:[^_\n]|_(?!_))+?)(\s+)\2)(\S|$)?/g;
|
|
93945
|
+
// Pattern for * italic *
|
|
93946
|
+
const asteriskItalicRegex = /([^*\s]+)?\s*(\*)(?!\*)(?:\s+([^*\n]+?)(\s*)\2|([^*\n]+?)(\s+)\2)(\S|$)?/g;
|
|
93947
|
+
// Pattern for _ italic _
|
|
93948
|
+
const underscoreItalicRegex = /([^_\s]+)?\s*(_)(?!_)(?:\s+([^_\n]+?)(\s*)\2|([^_\n]+?)(\s+)\2)(\S|$)?/g;
|
|
93949
|
+
// CommonMark ignores intraword underscores or asteriks, but we want to italicize/bold the inner part
|
|
93950
|
+
// Pattern for intraword _word_ in words like hello_world_
|
|
93951
|
+
const intrawordUnderscoreItalicRegex = /(\w)_(?!_)([a-zA-Z0-9]+)_(?![\w_])/g;
|
|
93952
|
+
// Pattern for intraword __word__ in words like hello__world__
|
|
93953
|
+
const intrawordUnderscoreBoldRegex = /(\w)__([a-zA-Z0-9]+)__(?![\w_])/g;
|
|
93954
|
+
// Pattern for intraword *word* in words like hello*world*
|
|
93955
|
+
const intrawordAsteriskItalicRegex = /(\w)\*(?!\*)([a-zA-Z0-9]+)\*(?![\w*])/g;
|
|
93956
|
+
// Pattern for intraword **word** in words like hello**world**
|
|
93957
|
+
const intrawordAsteriskBoldRegex = /(\w)\*\*([a-zA-Z0-9]+)\*\*(?![\w*])/g;
|
|
93958
|
+
/**
|
|
93959
|
+
* Finds opening emphasis marker in a text value.
|
|
93960
|
+
* Returns marker info if found, null otherwise.
|
|
93961
|
+
*/
|
|
93962
|
+
function findOpeningMarker(text) {
|
|
93963
|
+
const results = MARKER_PATTERNS.map(({ isBold, marker }) => {
|
|
93964
|
+
if (marker === '*' && text.startsWith('**'))
|
|
93965
|
+
return null;
|
|
93966
|
+
if (marker === '_' && text.startsWith('__'))
|
|
93967
|
+
return null;
|
|
93968
|
+
if (text.startsWith(marker) && text.length > marker.length) {
|
|
93969
|
+
return { isBold, marker, textAfter: text.slice(marker.length), textBefore: '' };
|
|
93838
93970
|
}
|
|
93839
|
-
|
|
93840
|
-
|
|
93971
|
+
const idx = text.indexOf(marker);
|
|
93972
|
+
if (idx > 0 && !/\s/.test(text[idx - 1])) {
|
|
93973
|
+
if (marker === '*' && text.slice(idx).startsWith('**'))
|
|
93974
|
+
return null;
|
|
93975
|
+
if (marker === '_' && text.slice(idx).startsWith('__'))
|
|
93976
|
+
return null;
|
|
93977
|
+
const after = text.slice(idx + marker.length);
|
|
93978
|
+
if (after.length > 0) {
|
|
93979
|
+
return { isBold, marker, textAfter: after, textBefore: text.slice(0, idx) };
|
|
93980
|
+
}
|
|
93841
93981
|
}
|
|
93982
|
+
return null;
|
|
93842
93983
|
});
|
|
93984
|
+
return results.find(r => r !== null) ?? null;
|
|
93843
93985
|
}
|
|
93844
93986
|
/**
|
|
93845
|
-
*
|
|
93987
|
+
* Finds the end/closing marker in a text node for multi-node emphasis.
|
|
93846
93988
|
*/
|
|
93847
|
-
function
|
|
93848
|
-
const
|
|
93849
|
-
|
|
93850
|
-
|
|
93851
|
-
|
|
93852
|
-
|
|
93853
|
-
|
|
93854
|
-
|
|
93855
|
-
|
|
93856
|
-
|
|
93857
|
-
|
|
93858
|
-
|
|
93859
|
-
// Reconstruct code fence syntax (markdown parser consumes opening ```)
|
|
93860
|
-
const lang = node.lang || '';
|
|
93861
|
-
const fence = `\`\`\`${lang ? `${lang}\n` : ''}`;
|
|
93862
|
-
parts.push(fence);
|
|
93863
|
-
parts.push(node.value);
|
|
93864
|
-
// Add newline before closing fence if missing
|
|
93865
|
-
const closingFence = node.value.endsWith('\n') ? '```' : '\n```';
|
|
93866
|
-
parts.push(closingFence);
|
|
93989
|
+
function findEndMarker(text, marker) {
|
|
93990
|
+
const spacePattern = ` ${marker}`;
|
|
93991
|
+
const spaceIdx = text.indexOf(spacePattern);
|
|
93992
|
+
if (spaceIdx >= 0) {
|
|
93993
|
+
if (marker === '*' && text.slice(spaceIdx + 1).startsWith('**'))
|
|
93994
|
+
return null;
|
|
93995
|
+
if (marker === '_' && text.slice(spaceIdx + 1).startsWith('__'))
|
|
93996
|
+
return null;
|
|
93997
|
+
return {
|
|
93998
|
+
textAfter: text.slice(spaceIdx + spacePattern.length),
|
|
93999
|
+
textBefore: text.slice(0, spaceIdx),
|
|
94000
|
+
};
|
|
93867
94001
|
}
|
|
93868
|
-
|
|
93869
|
-
|
|
93870
|
-
|
|
93871
|
-
|
|
93872
|
-
|
|
93873
|
-
|
|
94002
|
+
if (text.startsWith(marker)) {
|
|
94003
|
+
if (marker === '*' && text.startsWith('**'))
|
|
94004
|
+
return null;
|
|
94005
|
+
if (marker === '_' && text.startsWith('__'))
|
|
94006
|
+
return null;
|
|
94007
|
+
return {
|
|
94008
|
+
textAfter: text.slice(marker.length),
|
|
94009
|
+
textBefore: '',
|
|
94010
|
+
};
|
|
93874
94011
|
}
|
|
93875
|
-
return
|
|
94012
|
+
return null;
|
|
93876
94013
|
}
|
|
93877
94014
|
/**
|
|
93878
|
-
*
|
|
93879
|
-
* Returns "true"/"false" string to survive rehypeRaw serialization.
|
|
94015
|
+
* Scan children for an opening emphasis marker in a text node.
|
|
93880
94016
|
*/
|
|
93881
|
-
function
|
|
93882
|
-
|
|
93883
|
-
|
|
93884
|
-
|
|
93885
|
-
|
|
93886
|
-
|
|
93887
|
-
|
|
93888
|
-
|
|
93889
|
-
|
|
93890
|
-
|
|
93891
|
-
|
|
93892
|
-
|
|
94017
|
+
function findOpeningInChildren(children) {
|
|
94018
|
+
let result = null;
|
|
94019
|
+
children.some((child, idx) => {
|
|
94020
|
+
if (child.type !== 'text')
|
|
94021
|
+
return false;
|
|
94022
|
+
const found = findOpeningMarker(child.value);
|
|
94023
|
+
if (found) {
|
|
94024
|
+
result = { idx, opening: found };
|
|
94025
|
+
return true;
|
|
94026
|
+
}
|
|
94027
|
+
return false;
|
|
94028
|
+
});
|
|
94029
|
+
return result;
|
|
93893
94030
|
}
|
|
93894
94031
|
/**
|
|
93895
|
-
*
|
|
94032
|
+
* Scan children (after openingIdx) for a closing emphasis marker.
|
|
93896
94033
|
*/
|
|
93897
|
-
function
|
|
93898
|
-
|
|
93899
|
-
|
|
93900
|
-
|
|
93901
|
-
|
|
93902
|
-
|
|
93903
|
-
|
|
93904
|
-
|
|
93905
|
-
|
|
93906
|
-
|
|
94034
|
+
function findClosingInChildren(children, openingIdx, marker) {
|
|
94035
|
+
let result = null;
|
|
94036
|
+
children.slice(openingIdx + 1).some((child, relativeIdx) => {
|
|
94037
|
+
if (child.type !== 'text')
|
|
94038
|
+
return false;
|
|
94039
|
+
const found = findEndMarker(child.value, marker);
|
|
94040
|
+
if (found) {
|
|
94041
|
+
result = { closingIdx: openingIdx + 1 + relativeIdx, closing: found };
|
|
94042
|
+
return true;
|
|
94043
|
+
}
|
|
93907
94044
|
return false;
|
|
93908
|
-
}
|
|
93909
|
-
return
|
|
94045
|
+
});
|
|
94046
|
+
return result;
|
|
93910
94047
|
}
|
|
93911
94048
|
/**
|
|
93912
|
-
*
|
|
94049
|
+
* Build the replacement nodes for a matched emphasis pair.
|
|
93913
94050
|
*/
|
|
93914
|
-
function
|
|
93915
|
-
|
|
93916
|
-
|
|
93917
|
-
|
|
93918
|
-
|
|
93919
|
-
|
|
93920
|
-
|
|
93921
|
-
|
|
93922
|
-
|
|
93923
|
-
|
|
93924
|
-
|
|
93925
|
-
|
|
93926
|
-
|
|
93927
|
-
|
|
94051
|
+
function buildReplacementNodes(container, { opening, openingIdx, closing, closingIdx }) {
|
|
94052
|
+
const newNodes = [];
|
|
94053
|
+
if (opening.textBefore) {
|
|
94054
|
+
newNodes.push({ type: 'text', value: `${opening.textBefore} ` });
|
|
94055
|
+
}
|
|
94056
|
+
const emphasisChildren = [];
|
|
94057
|
+
const openingText = opening.textAfter.replace(/^\s+/, '');
|
|
94058
|
+
if (openingText) {
|
|
94059
|
+
emphasisChildren.push({ type: 'text', value: openingText });
|
|
94060
|
+
}
|
|
94061
|
+
container.children.slice(openingIdx + 1, closingIdx).forEach(child => {
|
|
94062
|
+
emphasisChildren.push(child);
|
|
94063
|
+
});
|
|
94064
|
+
const closingText = closing.textBefore.replace(/\s+$/, '');
|
|
94065
|
+
if (closingText) {
|
|
94066
|
+
emphasisChildren.push({ type: 'text', value: closingText });
|
|
94067
|
+
}
|
|
94068
|
+
if (emphasisChildren.length > 0) {
|
|
94069
|
+
const emphasisNode = opening.isBold
|
|
94070
|
+
? { type: 'strong', children: emphasisChildren }
|
|
94071
|
+
: { type: 'emphasis', children: emphasisChildren };
|
|
94072
|
+
newNodes.push(emphasisNode);
|
|
94073
|
+
}
|
|
94074
|
+
if (closing.textAfter) {
|
|
94075
|
+
newNodes.push({ type: 'text', value: closing.textAfter });
|
|
94076
|
+
}
|
|
94077
|
+
return newNodes;
|
|
93928
94078
|
}
|
|
93929
94079
|
/**
|
|
93930
|
-
*
|
|
94080
|
+
* Find and transform one multi-node emphasis pair in the container.
|
|
94081
|
+
* Returns true if a pair was found and transformed, false otherwise.
|
|
93931
94082
|
*/
|
|
93932
|
-
function
|
|
93933
|
-
|
|
93934
|
-
|
|
93935
|
-
|
|
93936
|
-
const
|
|
93937
|
-
|
|
93938
|
-
|
|
93939
|
-
|
|
93940
|
-
|
|
93941
|
-
|
|
93942
|
-
|
|
93943
|
-
|
|
93944
|
-
|
|
93945
|
-
attrs = match[1] || '';
|
|
93946
|
-
}
|
|
93947
|
-
}
|
|
93948
|
-
if (n.value === '</HTMLBlock>' || n.value.includes('</HTMLBlock>')) {
|
|
93949
|
-
hasClosed = true;
|
|
93950
|
-
}
|
|
93951
|
-
}
|
|
93952
|
-
if (n.children && Array.isArray(n.children)) {
|
|
93953
|
-
n.children.forEach(child => {
|
|
93954
|
-
check(child);
|
|
93955
|
-
});
|
|
93956
|
-
}
|
|
93957
|
-
};
|
|
93958
|
-
check(node);
|
|
93959
|
-
// Return true only if opening without closing (split case)
|
|
93960
|
-
return { attrs, found: hasOpening && !hasClosed };
|
|
94083
|
+
function processOneEmphasisPair(container) {
|
|
94084
|
+
const openingResult = findOpeningInChildren(container.children);
|
|
94085
|
+
if (!openingResult)
|
|
94086
|
+
return false;
|
|
94087
|
+
const { idx: openingIdx, opening } = openingResult;
|
|
94088
|
+
const closingResult = findClosingInChildren(container.children, openingIdx, opening.marker);
|
|
94089
|
+
if (!closingResult)
|
|
94090
|
+
return false;
|
|
94091
|
+
const { closingIdx, closing } = closingResult;
|
|
94092
|
+
const newNodes = buildReplacementNodes(container, { opening, openingIdx, closing, closingIdx });
|
|
94093
|
+
const deleteCount = closingIdx - openingIdx + 1;
|
|
94094
|
+
container.children.splice(openingIdx, deleteCount, ...newNodes);
|
|
94095
|
+
return true;
|
|
93961
94096
|
}
|
|
93962
94097
|
/**
|
|
93963
|
-
*
|
|
94098
|
+
* Handle malformed emphasis that spans multiple AST nodes.
|
|
94099
|
+
* E.g., "**bold [link](url)**" where markers are in different text nodes.
|
|
93964
94100
|
*/
|
|
93965
|
-
function
|
|
93966
|
-
|
|
93967
|
-
|
|
93968
|
-
|
|
93969
|
-
|
|
93970
|
-
|
|
93971
|
-
|
|
93972
|
-
|
|
93973
|
-
|
|
94101
|
+
function visitMultiNodeEmphasis(tree) {
|
|
94102
|
+
const containerTypes = ['paragraph', 'heading', 'tableCell', 'listItem', 'blockquote'];
|
|
94103
|
+
visit(tree, node => {
|
|
94104
|
+
if (!containerTypes.includes(node.type))
|
|
94105
|
+
return;
|
|
94106
|
+
if (!('children' in node) || !Array.isArray(node.children))
|
|
94107
|
+
return;
|
|
94108
|
+
const container = node;
|
|
94109
|
+
let foundPair = true;
|
|
94110
|
+
while (foundPair) {
|
|
94111
|
+
foundPair = processOneEmphasisPair(container);
|
|
94112
|
+
}
|
|
94113
|
+
});
|
|
93974
94114
|
}
|
|
93975
94115
|
/**
|
|
93976
|
-
*
|
|
94116
|
+
* A remark plugin that normalizes malformed bold and italic markers in text nodes.
|
|
94117
|
+
* Detects patterns like `** bold**`, `Hello** Wrong Bold**`, `__ bold__`, `Hello__ Wrong Bold__`,
|
|
94118
|
+
* `* italic*`, `Hello* Wrong Italic*`, `_ italic_`, or `Hello_ Wrong Italic_`
|
|
94119
|
+
* and converts them to proper strong/emphasis nodes, matching the behavior of the legacy rdmd engine.
|
|
94120
|
+
*
|
|
94121
|
+
* Supports both asterisk (`**bold**`, `*italic*`) and underscore (`__bold__`, `_italic_`) syntax.
|
|
94122
|
+
* Also supports snake_case content like `** some_snake_case**`.
|
|
94123
|
+
*
|
|
94124
|
+
* This runs after remark-parse, which (in v11+) is strict and doesn't parse
|
|
94125
|
+
* malformed emphasis syntax. This plugin post-processes the AST to handle these cases.
|
|
93977
94126
|
*/
|
|
93978
|
-
const
|
|
93979
|
-
|
|
93980
|
-
|
|
93981
|
-
|
|
93982
|
-
|
|
93983
|
-
|
|
93984
|
-
|
|
93985
|
-
|
|
93986
|
-
|
|
93987
|
-
|
|
93988
|
-
|
|
93989
|
-
|
|
93990
|
-
|
|
93991
|
-
|
|
93992
|
-
|
|
93993
|
-
|
|
94127
|
+
const normalizeEmphasisAST = () => (tree) => {
|
|
94128
|
+
visit(tree, 'text', function visitor(node, index, parent) {
|
|
94129
|
+
if (index === undefined || !parent)
|
|
94130
|
+
return undefined;
|
|
94131
|
+
// Skip if inside code blocks or inline code
|
|
94132
|
+
if (parent.type === 'inlineCode' || parent.type === 'code') {
|
|
94133
|
+
return undefined;
|
|
94134
|
+
}
|
|
94135
|
+
const text = node.value;
|
|
94136
|
+
const allMatches = [];
|
|
94137
|
+
[...text.matchAll(asteriskBoldRegex)].forEach(match => {
|
|
94138
|
+
allMatches.push({ isBold: true, marker: '**', match });
|
|
94139
|
+
});
|
|
94140
|
+
[...text.matchAll(underscoreBoldRegex)].forEach(match => {
|
|
94141
|
+
allMatches.push({ isBold: true, marker: '__', match });
|
|
94142
|
+
});
|
|
94143
|
+
[...text.matchAll(asteriskItalicRegex)].forEach(match => {
|
|
94144
|
+
allMatches.push({ isBold: false, marker: '*', match });
|
|
94145
|
+
});
|
|
94146
|
+
[...text.matchAll(underscoreItalicRegex)].forEach(match => {
|
|
94147
|
+
allMatches.push({ isBold: false, marker: '_', match });
|
|
94148
|
+
});
|
|
94149
|
+
[...text.matchAll(intrawordUnderscoreItalicRegex)].forEach(match => {
|
|
94150
|
+
allMatches.push({ isBold: false, isIntraword: true, marker: '_', match });
|
|
94151
|
+
});
|
|
94152
|
+
[...text.matchAll(intrawordUnderscoreBoldRegex)].forEach(match => {
|
|
94153
|
+
allMatches.push({ isBold: true, isIntraword: true, marker: '__', match });
|
|
94154
|
+
});
|
|
94155
|
+
[...text.matchAll(intrawordAsteriskItalicRegex)].forEach(match => {
|
|
94156
|
+
allMatches.push({ isBold: false, isIntraword: true, marker: '*', match });
|
|
94157
|
+
});
|
|
94158
|
+
[...text.matchAll(intrawordAsteriskBoldRegex)].forEach(match => {
|
|
94159
|
+
allMatches.push({ isBold: true, isIntraword: true, marker: '**', match });
|
|
94160
|
+
});
|
|
94161
|
+
if (allMatches.length === 0)
|
|
94162
|
+
return undefined;
|
|
94163
|
+
allMatches.sort((a, b) => (a.match.index ?? 0) - (b.match.index ?? 0));
|
|
94164
|
+
const filteredMatches = [];
|
|
94165
|
+
let lastEnd = 0;
|
|
94166
|
+
allMatches.forEach(info => {
|
|
94167
|
+
const start = info.match.index ?? 0;
|
|
94168
|
+
const end = start + info.match[0].length;
|
|
94169
|
+
if (start >= lastEnd) {
|
|
94170
|
+
filteredMatches.push(info);
|
|
94171
|
+
lastEnd = end;
|
|
94172
|
+
}
|
|
94173
|
+
});
|
|
94174
|
+
if (filteredMatches.length === 0)
|
|
94175
|
+
return undefined;
|
|
94176
|
+
const parts = [];
|
|
94177
|
+
let lastIndex = 0;
|
|
94178
|
+
filteredMatches.forEach(({ isBold, isIntraword, marker, match }) => {
|
|
94179
|
+
const matchIndex = match.index ?? 0;
|
|
94180
|
+
const fullMatch = match[0];
|
|
94181
|
+
if (isIntraword) {
|
|
94182
|
+
// handles cases like hello_world_ where we only want to italicize 'world'
|
|
94183
|
+
const charBefore = match[1] || ''; // e.g., "l" in "hello_world_"
|
|
94184
|
+
const content = match[2]; // e.g., "world"
|
|
94185
|
+
const combinedBefore = text.slice(lastIndex, matchIndex) + charBefore;
|
|
94186
|
+
if (combinedBefore) {
|
|
94187
|
+
parts.push({ type: 'text', value: combinedBefore });
|
|
93994
94188
|
}
|
|
93995
|
-
if (
|
|
93996
|
-
|
|
93997
|
-
|
|
93998
|
-
|
|
93999
|
-
|
|
94000
|
-
|
|
94001
|
-
|
|
94002
|
-
|
|
94003
|
-
|
|
94004
|
-
|
|
94005
|
-
|
|
94006
|
-
content = decodeProtectedContent(content);
|
|
94007
|
-
const htmlString = formatHtmlForMdxish(content);
|
|
94008
|
-
const runScripts = extractRunScriptsAttr(attrs);
|
|
94009
|
-
const safeMode = extractBooleanAttr(attrs, 'safeMode');
|
|
94010
|
-
// Replace range with single HTMLBlock node
|
|
94011
|
-
const mdNode = createHTMLBlockNode(htmlString, children[i].position, runScripts, safeMode);
|
|
94012
|
-
root.children.splice(i, closingIdx - i + 1, mdNode);
|
|
94189
|
+
if (isBold) {
|
|
94190
|
+
parts.push({
|
|
94191
|
+
type: 'strong',
|
|
94192
|
+
children: [{ type: 'text', value: content }],
|
|
94193
|
+
});
|
|
94194
|
+
}
|
|
94195
|
+
else {
|
|
94196
|
+
parts.push({
|
|
94197
|
+
type: 'emphasis',
|
|
94198
|
+
children: [{ type: 'text', value: content }],
|
|
94199
|
+
});
|
|
94013
94200
|
}
|
|
94201
|
+
lastIndex = matchIndex + fullMatch.length;
|
|
94202
|
+
return;
|
|
94014
94203
|
}
|
|
94015
|
-
|
|
94016
|
-
|
|
94017
|
-
|
|
94018
|
-
|
|
94019
|
-
visit(tree, 'html', (node, index, parent) => {
|
|
94020
|
-
if (!parent || index === undefined)
|
|
94021
|
-
return;
|
|
94022
|
-
const value = node.value;
|
|
94023
|
-
if (!value)
|
|
94024
|
-
return;
|
|
94025
|
-
// Case 1: Full HTMLBlock in single node
|
|
94026
|
-
const fullMatch = value.match(/^<HTMLBlock(\s[^>]*)?>([\s\S]*)<\/HTMLBlock>$/);
|
|
94027
|
-
if (fullMatch) {
|
|
94028
|
-
const attrs = fullMatch[1] || '';
|
|
94029
|
-
let content = fullMatch[2] || '';
|
|
94030
|
-
// Remove template literal syntax if present: {`...`}
|
|
94031
|
-
content = content.replace(/^\s*\{\s*`/, '').replace(/`\s*\}\s*$/, '');
|
|
94032
|
-
// Decode protected content that was base64 encoded during preprocessing
|
|
94033
|
-
content = decodeProtectedContent(content);
|
|
94034
|
-
const htmlString = formatHtmlForMdxish(content);
|
|
94035
|
-
const runScripts = extractRunScriptsAttr(attrs);
|
|
94036
|
-
const safeMode = extractBooleanAttr(attrs, 'safeMode');
|
|
94037
|
-
parent.children[index] = createHTMLBlockNode(htmlString, node.position, runScripts, safeMode);
|
|
94038
|
-
return;
|
|
94039
|
-
}
|
|
94040
|
-
// Case 2: Opening tag only (split by blank lines)
|
|
94041
|
-
if (value === '<HTMLBlock>' || value.match(/^<HTMLBlock\s[^>]*>$/)) {
|
|
94042
|
-
const siblings = parent.children;
|
|
94043
|
-
let closingIdx = -1;
|
|
94044
|
-
// Find closing tag in siblings
|
|
94045
|
-
for (let i = index + 1; i < siblings.length; i += 1) {
|
|
94046
|
-
const sibling = siblings[i];
|
|
94047
|
-
if (sibling.type === 'html') {
|
|
94048
|
-
const sibVal = sibling.value;
|
|
94049
|
-
if (sibVal === '</HTMLBlock>' || sibVal?.includes('</HTMLBlock>')) {
|
|
94050
|
-
closingIdx = i;
|
|
94051
|
-
break;
|
|
94052
|
-
}
|
|
94204
|
+
if (matchIndex > lastIndex) {
|
|
94205
|
+
const beforeText = text.slice(lastIndex, matchIndex);
|
|
94206
|
+
if (beforeText) {
|
|
94207
|
+
parts.push({ type: 'text', value: beforeText });
|
|
94053
94208
|
}
|
|
94054
94209
|
}
|
|
94055
|
-
|
|
94056
|
-
|
|
94057
|
-
//
|
|
94058
|
-
const
|
|
94059
|
-
|
|
94060
|
-
|
|
94061
|
-
|
|
94062
|
-
|
|
94063
|
-
|
|
94064
|
-
|
|
94065
|
-
|
|
94066
|
-
|
|
94067
|
-
|
|
94068
|
-
|
|
94069
|
-
|
|
94210
|
+
const wordBefore = match[1]; // e.g., "Hello" in "Hello** Wrong Bold**"
|
|
94211
|
+
const contentWithSpaceAfter = match[3]; // Content when there's a space after opening markers
|
|
94212
|
+
const trailingSpace1 = match[4] || ''; // Space before closing markers (for "** text **" pattern)
|
|
94213
|
+
const contentWithSpaceBefore = match[5]; // Content when there's only a space before closing markers
|
|
94214
|
+
const trailingSpace2 = match[6] || ''; // Space before closing markers (for "**text **" pattern)
|
|
94215
|
+
const trailingSpace = trailingSpace1 || trailingSpace2; // Combined trailing space
|
|
94216
|
+
const content = (contentWithSpaceAfter || contentWithSpaceBefore || '').trim();
|
|
94217
|
+
const afterChar = match[7]; // Character after closing markers (if any)
|
|
94218
|
+
const markerPos = fullMatch.indexOf(marker);
|
|
94219
|
+
const spacesBeforeMarkers = wordBefore
|
|
94220
|
+
? fullMatch.slice(wordBefore.length, markerPos)
|
|
94221
|
+
: fullMatch.slice(0, markerPos);
|
|
94222
|
+
const shouldAddSpace = !!contentWithSpaceAfter && !!wordBefore && !spacesBeforeMarkers;
|
|
94223
|
+
if (wordBefore) {
|
|
94224
|
+
const spacing = spacesBeforeMarkers + (shouldAddSpace ? ' ' : '');
|
|
94225
|
+
parts.push({ type: 'text', value: wordBefore + spacing });
|
|
94070
94226
|
}
|
|
94071
|
-
|
|
94072
|
-
|
|
94073
|
-
const htmlString = formatHtmlForMdxish(decodedContent);
|
|
94074
|
-
const runScripts = extractRunScriptsAttr(value);
|
|
94075
|
-
const safeMode = extractBooleanAttr(value, 'safeMode');
|
|
94076
|
-
// Replace opening tag with HTMLBlock node, remove consumed siblings
|
|
94077
|
-
parent.children[index] = createHTMLBlockNode(htmlString, node.position, runScripts, safeMode);
|
|
94078
|
-
parent.children.splice(index + 1, closingIdx - index);
|
|
94079
|
-
}
|
|
94080
|
-
});
|
|
94081
|
-
// Handle HTMLBlock inside paragraphs (parsed as inline elements)
|
|
94082
|
-
visit(tree, 'paragraph', (node, index, parent) => {
|
|
94083
|
-
if (!parent || index === undefined)
|
|
94084
|
-
return;
|
|
94085
|
-
const children = node.children || [];
|
|
94086
|
-
let htmlBlockStartIdx = -1;
|
|
94087
|
-
let htmlBlockEndIdx = -1;
|
|
94088
|
-
let templateLiteralStartIdx = -1;
|
|
94089
|
-
let templateLiteralEndIdx = -1;
|
|
94090
|
-
for (let i = 0; i < children.length; i += 1) {
|
|
94091
|
-
const child = children[i];
|
|
94092
|
-
if (child.type === 'html' && typeof child.value === 'string') {
|
|
94093
|
-
const value = child.value;
|
|
94094
|
-
if (value === '<HTMLBlock>' || value.match(/^<HTMLBlock\s[^>]*>$/)) {
|
|
94095
|
-
htmlBlockStartIdx = i;
|
|
94096
|
-
}
|
|
94097
|
-
else if (value === '</HTMLBlock>') {
|
|
94098
|
-
htmlBlockEndIdx = i;
|
|
94099
|
-
}
|
|
94227
|
+
else if (spacesBeforeMarkers) {
|
|
94228
|
+
parts.push({ type: 'text', value: spacesBeforeMarkers });
|
|
94100
94229
|
}
|
|
94101
|
-
|
|
94102
|
-
|
|
94103
|
-
|
|
94104
|
-
|
|
94105
|
-
|
|
94230
|
+
if (content) {
|
|
94231
|
+
if (isBold) {
|
|
94232
|
+
parts.push({
|
|
94233
|
+
type: 'strong',
|
|
94234
|
+
children: [{ type: 'text', value: content }],
|
|
94235
|
+
});
|
|
94106
94236
|
}
|
|
94107
|
-
|
|
94108
|
-
|
|
94109
|
-
|
|
94110
|
-
|
|
94111
|
-
|
|
94112
|
-
templateLiteralEndIdx = i;
|
|
94237
|
+
else {
|
|
94238
|
+
parts.push({
|
|
94239
|
+
type: 'emphasis',
|
|
94240
|
+
children: [{ type: 'text', value: content }],
|
|
94241
|
+
});
|
|
94113
94242
|
}
|
|
94114
94243
|
}
|
|
94115
|
-
|
|
94116
|
-
|
|
94117
|
-
|
|
94118
|
-
|
|
94119
|
-
|
|
94120
|
-
|
|
94121
|
-
|
|
94122
|
-
|
|
94123
|
-
|
|
94124
|
-
|
|
94125
|
-
const child = children[i];
|
|
94126
|
-
templateContent.push(collectTextContent(child));
|
|
94244
|
+
if (afterChar) {
|
|
94245
|
+
const prefix = trailingSpace ? ' ' : '';
|
|
94246
|
+
parts.push({ type: 'text', value: prefix + afterChar });
|
|
94247
|
+
}
|
|
94248
|
+
lastIndex = matchIndex + fullMatch.length;
|
|
94249
|
+
});
|
|
94250
|
+
if (lastIndex < text.length) {
|
|
94251
|
+
const remainingText = text.slice(lastIndex);
|
|
94252
|
+
if (remainingText) {
|
|
94253
|
+
parts.push({ type: 'text', value: remainingText });
|
|
94127
94254
|
}
|
|
94128
|
-
// Decode protected content that was base64 encoded during preprocessing
|
|
94129
|
-
const decodedContent = decodeProtectedContent(templateContent.join(''));
|
|
94130
|
-
const htmlString = formatHtmlForMdxish(decodedContent);
|
|
94131
|
-
const runScripts = openingTag.value ? extractRunScriptsAttr(openingTag.value) : undefined;
|
|
94132
|
-
const safeMode = openingTag.value ? extractBooleanAttr(openingTag.value, 'safeMode') : undefined;
|
|
94133
|
-
const mdNode = createHTMLBlockNode(htmlString, node.position, runScripts, safeMode);
|
|
94134
|
-
parent.children[index] = mdNode;
|
|
94135
94255
|
}
|
|
94136
|
-
|
|
94137
|
-
|
|
94138
|
-
|
|
94139
|
-
const html = node.data?.hProperties?.html;
|
|
94140
|
-
if (html &&
|
|
94141
|
-
(!node.children ||
|
|
94142
|
-
node.children.length === 0 ||
|
|
94143
|
-
(node.children.length === 1 && node.children[0].type === 'text' && node.children[0].value !== html))) {
|
|
94144
|
-
node.children = [
|
|
94145
|
-
{
|
|
94146
|
-
type: 'text',
|
|
94147
|
-
value: html,
|
|
94148
|
-
},
|
|
94149
|
-
];
|
|
94256
|
+
if (parts.length > 0) {
|
|
94257
|
+
parent.children.splice(index, 1, ...parts);
|
|
94258
|
+
return [SKIP, index + parts.length];
|
|
94150
94259
|
}
|
|
94260
|
+
return undefined;
|
|
94151
94261
|
});
|
|
94262
|
+
// Handle malformed emphasis spanning multiple nodes (e.g., **text [link](url) **)
|
|
94263
|
+
visitMultiNodeEmphasis(tree);
|
|
94152
94264
|
return tree;
|
|
94153
94265
|
};
|
|
94154
|
-
/* harmony default export */ const
|
|
94266
|
+
/* harmony default export */ const normalize_malformed_md_syntax = (normalizeEmphasisAST);
|
|
94267
|
+
|
|
94268
|
+
;// ./processor/transform/mdxish/magic-blocks/placeholder.ts
|
|
94269
|
+
const EMPTY_IMAGE_PLACEHOLDER = {
|
|
94270
|
+
type: 'image',
|
|
94271
|
+
url: '',
|
|
94272
|
+
alt: '',
|
|
94273
|
+
title: '',
|
|
94274
|
+
data: { hProperties: {} },
|
|
94275
|
+
};
|
|
94276
|
+
const EMPTY_EMBED_PLACEHOLDER = {
|
|
94277
|
+
type: 'embed',
|
|
94278
|
+
children: [{ type: 'link', url: '', title: '', children: [{ type: 'text', value: '' }] }],
|
|
94279
|
+
data: { hName: 'embed-block', hProperties: { url: '', href: '', title: '' } },
|
|
94280
|
+
};
|
|
94281
|
+
const EMPTY_RECIPE_PLACEHOLDER = {
|
|
94282
|
+
type: 'mdxJsxFlowElement',
|
|
94283
|
+
name: 'Recipe',
|
|
94284
|
+
attributes: [],
|
|
94285
|
+
children: [],
|
|
94286
|
+
};
|
|
94287
|
+
const EMPTY_CALLOUT_PLACEHOLDER = {
|
|
94288
|
+
type: 'mdxJsxFlowElement',
|
|
94289
|
+
name: 'Callout',
|
|
94290
|
+
attributes: [
|
|
94291
|
+
{ type: 'mdxJsxAttribute', name: 'icon', value: '📘' },
|
|
94292
|
+
{ type: 'mdxJsxAttribute', name: 'theme', value: 'info' },
|
|
94293
|
+
{ type: 'mdxJsxAttribute', name: 'type', value: 'info' },
|
|
94294
|
+
{ type: 'mdxJsxAttribute', name: 'empty', value: 'true' },
|
|
94295
|
+
],
|
|
94296
|
+
children: [{ type: 'heading', depth: 3, children: [{ type: 'text', value: '' }] }],
|
|
94297
|
+
};
|
|
94298
|
+
const EMPTY_TABLE_PLACEHOLDER = {
|
|
94299
|
+
type: 'table',
|
|
94300
|
+
align: ['left', 'left'],
|
|
94301
|
+
children: [
|
|
94302
|
+
{ type: 'tableRow', children: [{ type: 'tableCell', children: [{ type: 'text', value: '' }] }] },
|
|
94303
|
+
{ type: 'tableRow', children: [{ type: 'tableCell', children: [{ type: 'text', value: '' }] }] },
|
|
94304
|
+
],
|
|
94305
|
+
};
|
|
94306
|
+
const EMPTY_CODE_PLACEHOLDER = {
|
|
94307
|
+
type: 'code',
|
|
94308
|
+
value: '',
|
|
94309
|
+
lang: null,
|
|
94310
|
+
meta: null,
|
|
94311
|
+
};
|
|
94312
|
+
|
|
94313
|
+
;// ./processor/transform/mdxish/magic-blocks/magic-block-transformer.ts
|
|
94314
|
+
|
|
94155
94315
|
|
|
94156
|
-
;// ./processor/transform/mdxish/mdxish-magic-blocks.ts
|
|
94157
94316
|
|
|
94158
94317
|
|
|
94159
94318
|
|
|
94160
94319
|
|
|
94161
94320
|
|
|
94162
94321
|
/**
|
|
94163
|
-
*
|
|
94164
|
-
* Group 1: block type (e.g., "image", "code", "callout")
|
|
94165
|
-
* Group 2: JSON content between the tags
|
|
94166
|
-
* Taken from the v6 branch
|
|
94167
|
-
*/
|
|
94168
|
-
const RGXP = /^\s*\[block:([^\]]*)\]([^]+?)\[\/block\]/;
|
|
94169
|
-
/**
|
|
94170
|
-
* Wraps a node in a "pinned" container if sidebar: true is set in the JSON.
|
|
94171
|
-
* Pinned blocks are displayed in a sidebar/floating position in the UI.
|
|
94322
|
+
* Wraps a node in a "pinned" container if sidebar: true is set.
|
|
94172
94323
|
*/
|
|
94173
|
-
const wrapPinnedBlocks = (node,
|
|
94174
|
-
if (!
|
|
94324
|
+
const wrapPinnedBlocks = (node, data) => {
|
|
94325
|
+
if (!data.sidebar)
|
|
94175
94326
|
return node;
|
|
94176
94327
|
return {
|
|
94177
94328
|
children: [node],
|
|
@@ -94187,34 +94338,24 @@ const imgSizeValues = {
|
|
|
94187
94338
|
original: 'auto',
|
|
94188
94339
|
};
|
|
94189
94340
|
/**
|
|
94190
|
-
* Proxy that resolves image sizing values
|
|
94191
|
-
* - "full" → "100%", "original" → "auto" (from imgSizeValues)
|
|
94192
|
-
* - Pure numbers like "50" → "50%" (percentage)
|
|
94193
|
-
* - Anything else passes through as-is (e.g., "200px")
|
|
94341
|
+
* Proxy that resolves image sizing values.
|
|
94194
94342
|
*/
|
|
94195
94343
|
const imgWidthBySize = new Proxy(imgSizeValues, {
|
|
94196
94344
|
get: (widths, size) => (size?.match(/^\d+$/) ? `${size}%` : size in widths ? widths[size] : size),
|
|
94197
94345
|
});
|
|
94198
|
-
// Simple text to inline nodes (just returns text node - no markdown parsing)
|
|
94199
94346
|
const textToInline = (text) => [{ type: 'text', value: text }];
|
|
94200
|
-
// Simple text to block nodes (wraps in paragraph)
|
|
94201
94347
|
const textToBlock = (text) => [{ children: textToInline(text), type: 'paragraph' }];
|
|
94202
94348
|
/** Parses markdown and html to markdown nodes */
|
|
94203
|
-
const contentParser = unified().use(remarkParse).use(remarkGfm);
|
|
94204
|
-
// Table cells may contain html or markdown content, so we need to parse it accordingly instead of keeping it as raw text
|
|
94349
|
+
const contentParser = unified().use(remarkParse).use(remarkGfm).use(normalize_malformed_md_syntax);
|
|
94205
94350
|
const parseTableCell = (text) => {
|
|
94206
94351
|
if (!text.trim())
|
|
94207
94352
|
return [{ type: 'text', value: '' }];
|
|
94208
94353
|
const tree = contentParser.runSync(contentParser.parse(text));
|
|
94209
|
-
// If there are multiple block-level nodes, keep them as-is to preserve the document structure and spacing
|
|
94210
94354
|
if (tree.children.length > 1) {
|
|
94211
94355
|
return tree.children;
|
|
94212
94356
|
}
|
|
94213
|
-
return tree.children.flatMap(n =>
|
|
94214
|
-
// This unwraps the extra p node that might appear & wrapping the content
|
|
94215
|
-
n.type === 'paragraph' && 'children' in n ? n.children : [n]);
|
|
94357
|
+
return tree.children.flatMap(n => n.type === 'paragraph' && 'children' in n ? n.children : [n]);
|
|
94216
94358
|
};
|
|
94217
|
-
// Parse markdown/HTML into block-level nodes (preserves paragraphs, headings, lists, etc.)
|
|
94218
94359
|
const parseBlock = (text) => {
|
|
94219
94360
|
if (!text.trim())
|
|
94220
94361
|
return [{ type: 'paragraph', children: [{ type: 'text', value: '' }] }];
|
|
@@ -94222,44 +94363,43 @@ const parseBlock = (text) => {
|
|
|
94222
94363
|
return tree.children;
|
|
94223
94364
|
};
|
|
94224
94365
|
/**
|
|
94225
|
-
*
|
|
94226
|
-
|
|
94227
|
-
|
|
94228
|
-
|
|
94229
|
-
|
|
94230
|
-
|
|
94231
|
-
|
|
94232
|
-
|
|
94233
|
-
|
|
94234
|
-
|
|
94235
|
-
|
|
94236
|
-
|
|
94237
|
-
|
|
94238
|
-
|
|
94239
|
-
|
|
94240
|
-
|
|
94241
|
-
|
|
94242
|
-
|
|
94366
|
+
* Transform a magicBlock node into final MDAST nodes.
|
|
94367
|
+
*/
|
|
94368
|
+
function transformMagicBlock(blockType, data, rawValue, options = {}) {
|
|
94369
|
+
const { compatibilityMode = false, safeMode = false } = options;
|
|
94370
|
+
// Handle empty data by returning placeholder nodes for known block types
|
|
94371
|
+
// This allows the editor to show appropriate placeholder UI instead of nothing
|
|
94372
|
+
if (Object.keys(data).length < 1) {
|
|
94373
|
+
switch (blockType) {
|
|
94374
|
+
case 'image':
|
|
94375
|
+
return [EMPTY_IMAGE_PLACEHOLDER];
|
|
94376
|
+
case 'embed':
|
|
94377
|
+
return [EMPTY_EMBED_PLACEHOLDER];
|
|
94378
|
+
case 'code':
|
|
94379
|
+
return [EMPTY_CODE_PLACEHOLDER];
|
|
94380
|
+
case 'callout':
|
|
94381
|
+
return [EMPTY_CALLOUT_PLACEHOLDER];
|
|
94382
|
+
case 'parameters':
|
|
94383
|
+
case 'table':
|
|
94384
|
+
return [EMPTY_TABLE_PLACEHOLDER];
|
|
94385
|
+
case 'recipe':
|
|
94386
|
+
case 'tutorial-tile':
|
|
94387
|
+
return [EMPTY_RECIPE_PLACEHOLDER];
|
|
94388
|
+
default:
|
|
94389
|
+
return [{ type: 'paragraph', children: [{ type: 'text', value: rawValue }] }];
|
|
94390
|
+
}
|
|
94243
94391
|
}
|
|
94244
|
-
|
|
94245
|
-
// eslint-disable-next-line no-console
|
|
94246
|
-
console.error('Invalid Magic Block JSON:', err);
|
|
94247
|
-
if (alwaysThrow)
|
|
94248
|
-
throw new Error('Invalid Magic Block JSON');
|
|
94249
|
-
return [];
|
|
94250
|
-
}
|
|
94251
|
-
if (Object.keys(json).length < 1)
|
|
94252
|
-
return [];
|
|
94253
|
-
// Each case handles a different magic block type and returns appropriate MDAST nodes
|
|
94254
|
-
switch (type) {
|
|
94255
|
-
// Code blocks: single code block or tabbed code blocks (multiple languages)
|
|
94392
|
+
switch (blockType) {
|
|
94256
94393
|
case 'code': {
|
|
94257
|
-
const codeJson =
|
|
94394
|
+
const codeJson = data;
|
|
94395
|
+
if (!codeJson.codes || !Array.isArray(codeJson.codes)) {
|
|
94396
|
+
return [wrapPinnedBlocks(EMPTY_CODE_PLACEHOLDER, data)];
|
|
94397
|
+
}
|
|
94258
94398
|
const children = codeJson.codes.map(obj => ({
|
|
94259
94399
|
className: 'tab-panel',
|
|
94260
94400
|
data: { hName: 'code', hProperties: { lang: obj.language, meta: obj.name || null } },
|
|
94261
94401
|
lang: obj.language,
|
|
94262
|
-
meta: obj.name || null,
|
|
94402
|
+
meta: obj.name || null,
|
|
94263
94403
|
type: 'code',
|
|
94264
94404
|
value: obj.code.trim(),
|
|
94265
94405
|
}));
|
|
@@ -94269,31 +94409,31 @@ function parseMagicBlock(raw, options = {}) {
|
|
|
94269
94409
|
if (!children[0].value)
|
|
94270
94410
|
return [];
|
|
94271
94411
|
if (!(children[0].meta || children[0].lang))
|
|
94272
|
-
return [wrapPinnedBlocks(children[0],
|
|
94412
|
+
return [wrapPinnedBlocks(children[0], data)];
|
|
94273
94413
|
}
|
|
94274
94414
|
// Multiple code blocks or a single code block with a tab name (meta or language) renders as a code tabs block
|
|
94275
|
-
return [wrapPinnedBlocks({ children, className: 'tabs', data: { hName: 'CodeTabs' }, type: 'code-tabs' },
|
|
94415
|
+
return [wrapPinnedBlocks({ children, className: 'tabs', data: { hName: 'CodeTabs' }, type: 'code-tabs' }, data)];
|
|
94276
94416
|
}
|
|
94277
|
-
// API header: renders as a heading element (h1-h6)
|
|
94278
94417
|
case 'api-header': {
|
|
94279
|
-
const headerJson =
|
|
94280
|
-
// In compatibility mode, default to h1; otherwise h2
|
|
94418
|
+
const headerJson = data;
|
|
94281
94419
|
const depth = headerJson.level || (compatibilityMode ? 1 : 2);
|
|
94282
94420
|
return [
|
|
94283
94421
|
wrapPinnedBlocks({
|
|
94284
94422
|
children: 'title' in headerJson ? textToInline(headerJson.title || '') : [],
|
|
94285
94423
|
depth,
|
|
94286
94424
|
type: 'heading',
|
|
94287
|
-
},
|
|
94425
|
+
}, data),
|
|
94288
94426
|
];
|
|
94289
94427
|
}
|
|
94290
|
-
// Image block: renders as <img> or <figure> with caption
|
|
94291
94428
|
case 'image': {
|
|
94292
|
-
const imageJson =
|
|
94429
|
+
const imageJson = data;
|
|
94430
|
+
if (!imageJson.images || !Array.isArray(imageJson.images)) {
|
|
94431
|
+
return [wrapPinnedBlocks(EMPTY_IMAGE_PLACEHOLDER, data)];
|
|
94432
|
+
}
|
|
94293
94433
|
const imgData = imageJson.images.find(i => i.image);
|
|
94294
|
-
if (!imgData?.image)
|
|
94295
|
-
return [];
|
|
94296
|
-
|
|
94434
|
+
if (!imgData?.image) {
|
|
94435
|
+
return [wrapPinnedBlocks(EMPTY_IMAGE_PLACEHOLDER, data)];
|
|
94436
|
+
}
|
|
94297
94437
|
const [url, title, alt] = imgData.image;
|
|
94298
94438
|
const block = {
|
|
94299
94439
|
alt: alt || imgData.caption || '',
|
|
@@ -94308,57 +94448,66 @@ function parseMagicBlock(raw, options = {}) {
|
|
|
94308
94448
|
type: 'image',
|
|
94309
94449
|
url,
|
|
94310
94450
|
};
|
|
94311
|
-
// Wrap in <figure> if caption is present
|
|
94312
94451
|
const img = imgData.caption
|
|
94313
94452
|
? {
|
|
94314
94453
|
children: [
|
|
94315
94454
|
block,
|
|
94316
|
-
{ children:
|
|
94455
|
+
{ children: parseBlock(imgData.caption), data: { hName: 'figcaption' }, type: 'figcaption' },
|
|
94317
94456
|
],
|
|
94318
94457
|
data: { hName: 'figure' },
|
|
94319
94458
|
type: 'figure',
|
|
94320
94459
|
url,
|
|
94321
94460
|
}
|
|
94322
94461
|
: block;
|
|
94323
|
-
return [wrapPinnedBlocks(img,
|
|
94462
|
+
return [wrapPinnedBlocks(img, data)];
|
|
94324
94463
|
}
|
|
94325
|
-
// Callout: info/warning/error boxes with icon and theme
|
|
94326
94464
|
case 'callout': {
|
|
94327
|
-
const calloutJson =
|
|
94328
|
-
// Preset callout types map to [icon, theme] tuples
|
|
94465
|
+
const calloutJson = data;
|
|
94329
94466
|
const types = {
|
|
94330
94467
|
danger: ['❗️', 'error'],
|
|
94331
94468
|
info: ['📘', 'info'],
|
|
94332
94469
|
success: ['👍', 'okay'],
|
|
94333
94470
|
warning: ['🚧', 'warn'],
|
|
94334
94471
|
};
|
|
94335
|
-
// Resolve type to [icon, theme] - use preset if available, otherwise custom
|
|
94336
94472
|
const resolvedType = typeof calloutJson.type === 'string' && calloutJson.type in types
|
|
94337
94473
|
? types[calloutJson.type]
|
|
94338
94474
|
: [calloutJson.icon || '👍', typeof calloutJson.type === 'string' ? calloutJson.type : 'default'];
|
|
94339
94475
|
const [icon, theme] = Array.isArray(resolvedType) ? resolvedType : ['👍', 'default'];
|
|
94340
94476
|
if (!(calloutJson.title || calloutJson.body))
|
|
94341
94477
|
return [];
|
|
94342
|
-
|
|
94343
|
-
const
|
|
94344
|
-
const
|
|
94478
|
+
const hasTitle = !!calloutJson.title?.trim();
|
|
94479
|
+
const hasBody = !!calloutJson.body?.trim();
|
|
94480
|
+
const empty = !hasTitle;
|
|
94345
94481
|
const children = [];
|
|
94346
|
-
if (
|
|
94347
|
-
const
|
|
94348
|
-
|
|
94482
|
+
if (hasTitle) {
|
|
94483
|
+
const titleBlocks = parseBlock(calloutJson.title || '');
|
|
94484
|
+
if (titleBlocks.length > 0 && titleBlocks[0].type === 'paragraph') {
|
|
94485
|
+
const firstTitle = titleBlocks[0];
|
|
94486
|
+
const heading = {
|
|
94487
|
+
type: 'heading',
|
|
94488
|
+
depth: 3,
|
|
94489
|
+
children: (firstTitle.children || []),
|
|
94490
|
+
};
|
|
94491
|
+
children.push(heading);
|
|
94492
|
+
children.push(...titleBlocks.slice(1));
|
|
94493
|
+
}
|
|
94494
|
+
else {
|
|
94495
|
+
children.push(...titleBlocks);
|
|
94496
|
+
}
|
|
94497
|
+
}
|
|
94498
|
+
else {
|
|
94499
|
+
// Add empty heading placeholder so body goes to children.slice(1)
|
|
94500
|
+
// The Callout component expects children[0] to be the heading
|
|
94501
|
+
children.push({
|
|
94349
94502
|
type: 'heading',
|
|
94350
94503
|
depth: 3,
|
|
94351
|
-
children:
|
|
94352
|
-
};
|
|
94353
|
-
children.push(heading);
|
|
94354
|
-
children.push(...titleBlocks.slice(1), ...bodyBlocks);
|
|
94504
|
+
children: [{ type: 'text', value: '' }],
|
|
94505
|
+
});
|
|
94355
94506
|
}
|
|
94356
|
-
|
|
94357
|
-
|
|
94507
|
+
if (hasBody) {
|
|
94508
|
+
const bodyBlocks = parseBlock(calloutJson.body || '');
|
|
94509
|
+
children.push(...bodyBlocks);
|
|
94358
94510
|
}
|
|
94359
|
-
// If there is no title or title is empty
|
|
94360
|
-
const empty = !titleBlocks.length || !titleBlocks[0].children[0]?.value;
|
|
94361
|
-
// Create mdxJsxFlowElement directly for mdxish
|
|
94362
94511
|
const calloutElement = {
|
|
94363
94512
|
type: 'mdxJsxFlowElement',
|
|
94364
94513
|
name: 'Callout',
|
|
@@ -94370,23 +94519,17 @@ function parseMagicBlock(raw, options = {}) {
|
|
|
94370
94519
|
]),
|
|
94371
94520
|
children: children,
|
|
94372
94521
|
};
|
|
94373
|
-
return [wrapPinnedBlocks(calloutElement,
|
|
94522
|
+
return [wrapPinnedBlocks(calloutElement, data)];
|
|
94374
94523
|
}
|
|
94375
|
-
// Parameters: renders as a table (used for API parameters, etc.)
|
|
94376
94524
|
case 'parameters': {
|
|
94377
|
-
const paramsJson =
|
|
94378
|
-
const { cols, data, rows } = paramsJson;
|
|
94379
|
-
if (!Object.keys(
|
|
94525
|
+
const paramsJson = data;
|
|
94526
|
+
const { cols, data: tableData, rows } = paramsJson;
|
|
94527
|
+
if (!tableData || !Object.keys(tableData).length)
|
|
94380
94528
|
return [];
|
|
94381
|
-
|
|
94382
|
-
|
|
94383
|
-
|
|
94384
|
-
* Example: { "h-0": "Name", "h-1": "Type", "0-0": "id", "0-1": "string" }
|
|
94385
|
-
* Becomes: [["Name", "Type"], ["id", "string"]]
|
|
94386
|
-
*/
|
|
94387
|
-
const sparseData = Object.entries(data).reduce((mapped, [key, v]) => {
|
|
94529
|
+
if (typeof cols !== 'number' || typeof rows !== 'number' || cols < 1 || rows < 0)
|
|
94530
|
+
return [];
|
|
94531
|
+
const sparseData = Object.entries(tableData).reduce((mapped, [key, v]) => {
|
|
94388
94532
|
const [row, col] = key.split('-');
|
|
94389
|
-
// Header row ("h") becomes index 0, data rows are offset by 1
|
|
94390
94533
|
const rowIndex = row === 'h' ? 0 : parseInt(row, 10) + 1;
|
|
94391
94534
|
const colIndex = parseInt(col, 10);
|
|
94392
94535
|
if (!mapped[rowIndex])
|
|
@@ -94394,9 +94537,8 @@ function parseMagicBlock(raw, options = {}) {
|
|
|
94394
94537
|
mapped[rowIndex][colIndex] = v;
|
|
94395
94538
|
return mapped;
|
|
94396
94539
|
}, []);
|
|
94397
|
-
// In compatibility mode, wrap cell content in paragraphs; otherwise inline text
|
|
94398
94540
|
const tokenizeCell = compatibilityMode ? textToBlock : parseTableCell;
|
|
94399
|
-
const
|
|
94541
|
+
const tableChildren = Array.from({ length: rows + 1 }, (_, y) => ({
|
|
94400
94542
|
children: Array.from({ length: cols }, (__, x) => ({
|
|
94401
94543
|
children: sparseData[y]?.[x] ? tokenizeCell(sparseData[y][x]) : [{ type: 'text', value: '' }],
|
|
94402
94544
|
type: y === 0 ? 'tableHead' : 'tableCell',
|
|
@@ -94404,14 +94546,15 @@ function parseMagicBlock(raw, options = {}) {
|
|
|
94404
94546
|
type: 'tableRow',
|
|
94405
94547
|
}));
|
|
94406
94548
|
return [
|
|
94407
|
-
wrapPinnedBlocks({ align: paramsJson.align ?? new Array(cols).fill('left'), children, type: 'table' },
|
|
94549
|
+
wrapPinnedBlocks({ align: paramsJson.align ?? new Array(cols).fill('left'), children: tableChildren, type: 'table' }, data),
|
|
94408
94550
|
];
|
|
94409
94551
|
}
|
|
94410
|
-
// Embed: external content (YouTube, etc.) with provider detection
|
|
94411
94552
|
case 'embed': {
|
|
94412
|
-
const embedJson =
|
|
94553
|
+
const embedJson = data;
|
|
94554
|
+
if (!embedJson.url) {
|
|
94555
|
+
return [wrapPinnedBlocks(EMPTY_EMBED_PLACEHOLDER, data)];
|
|
94556
|
+
}
|
|
94413
94557
|
const { html, title, url } = embedJson;
|
|
94414
|
-
// Extract provider name from URL hostname (e.g., "youtube.com" → "youtube.com")
|
|
94415
94558
|
try {
|
|
94416
94559
|
embedJson.provider = new URL(url).hostname
|
|
94417
94560
|
.split(/(?:www)?\./)
|
|
@@ -94428,12 +94571,13 @@ function parseMagicBlock(raw, options = {}) {
|
|
|
94428
94571
|
],
|
|
94429
94572
|
data: { hName: 'embed-block', hProperties: { ...embedJson, href: url, html, title, url } },
|
|
94430
94573
|
type: 'embed',
|
|
94431
|
-
},
|
|
94574
|
+
}, data),
|
|
94432
94575
|
];
|
|
94433
94576
|
}
|
|
94434
|
-
// HTML block: raw HTML content (scripts enabled only in compatibility mode)
|
|
94435
94577
|
case 'html': {
|
|
94436
|
-
const htmlJson =
|
|
94578
|
+
const htmlJson = data;
|
|
94579
|
+
if (typeof htmlJson.html !== 'string')
|
|
94580
|
+
return [];
|
|
94437
94581
|
return [
|
|
94438
94582
|
wrapPinnedBlocks({
|
|
94439
94583
|
data: {
|
|
@@ -94441,39 +94585,33 @@ function parseMagicBlock(raw, options = {}) {
|
|
|
94441
94585
|
hProperties: { html: htmlJson.html, runScripts: compatibilityMode, safeMode },
|
|
94442
94586
|
},
|
|
94443
94587
|
type: 'html-block',
|
|
94444
|
-
},
|
|
94588
|
+
}, data),
|
|
94445
94589
|
];
|
|
94446
94590
|
}
|
|
94447
|
-
// Recipe/TutorialTile: renders as Recipe component
|
|
94448
94591
|
case 'recipe':
|
|
94449
94592
|
case 'tutorial-tile': {
|
|
94450
|
-
const recipeJson =
|
|
94593
|
+
const recipeJson = data;
|
|
94451
94594
|
if (!recipeJson.slug || !recipeJson.title)
|
|
94452
94595
|
return [];
|
|
94453
|
-
// Create mdxJsxFlowElement directly for mdxish flow
|
|
94454
|
-
// Note: Don't wrap in pinned blocks for mdxish - rehypeMdxishComponents handles component resolution
|
|
94455
|
-
// The node structure matches what mdxishComponentBlocks creates for JSX tags
|
|
94456
94596
|
const recipeNode = {
|
|
94457
94597
|
type: 'mdxJsxFlowElement',
|
|
94458
94598
|
name: 'Recipe',
|
|
94459
94599
|
attributes: toAttributes(recipeJson, ['slug', 'title']),
|
|
94460
94600
|
children: [],
|
|
94461
|
-
// Position is optional but helps with debugging
|
|
94462
94601
|
position: undefined,
|
|
94463
94602
|
};
|
|
94464
94603
|
return [recipeNode];
|
|
94465
94604
|
}
|
|
94466
|
-
// Unknown block types: render as generic div with JSON properties
|
|
94467
94605
|
default: {
|
|
94468
|
-
const text =
|
|
94606
|
+
const text = data.text || data.html || '';
|
|
94469
94607
|
return [
|
|
94470
|
-
wrapPinnedBlocks({ children: textToBlock(text), data: { hName:
|
|
94608
|
+
wrapPinnedBlocks({ children: textToBlock(text), data: { hName: blockType || 'div', hProperties: data, ...data }, type: 'div' }, data),
|
|
94471
94609
|
];
|
|
94472
94610
|
}
|
|
94473
94611
|
}
|
|
94474
94612
|
}
|
|
94475
94613
|
/**
|
|
94476
|
-
*
|
|
94614
|
+
* Check if a child node is a flow element that needs unwrapping.
|
|
94477
94615
|
*/
|
|
94478
94616
|
const blockTypes = [
|
|
94479
94617
|
'heading',
|
|
@@ -94499,29 +94637,19 @@ const blockTypes = [
|
|
|
94499
94637
|
*/
|
|
94500
94638
|
const isBlockNode = (node) => blockTypes.includes(node.type);
|
|
94501
94639
|
/**
|
|
94502
|
-
* Unified plugin that
|
|
94503
|
-
*
|
|
94504
|
-
* During preprocessing, extractMagicBlocks replaces [block:TYPE]...[/block]
|
|
94505
|
-
* with inline code tokens like `__MAGIC_BLOCK_0__`. This plugin finds those
|
|
94506
|
-
* tokens in the parsed MDAST and replaces them with the parsed block content.
|
|
94640
|
+
* Unified plugin that transforms magicBlock nodes into final MDAST nodes.
|
|
94507
94641
|
*/
|
|
94508
|
-
const
|
|
94509
|
-
if (!blocks.length)
|
|
94510
|
-
return;
|
|
94511
|
-
// Map: key → original raw magic block content
|
|
94512
|
-
const magicBlockKeys = new Map(blocks.map(({ key, raw }) => [key, raw]));
|
|
94513
|
-
// Collect replacements to apply (we need to visit in reverse to maintain indices)
|
|
94642
|
+
const magicBlockTransformer = (options = {}) => tree => {
|
|
94514
94643
|
const replacements = [];
|
|
94515
|
-
|
|
94516
|
-
|
|
94517
|
-
if (!parent || index == null)
|
|
94518
|
-
return undefined;
|
|
94519
|
-
const raw = magicBlockKeys.get(node.value);
|
|
94520
|
-
if (!raw)
|
|
94521
|
-
return undefined;
|
|
94522
|
-
const children = parseMagicBlock(raw);
|
|
94523
|
-
if (!children.length)
|
|
94644
|
+
visit(tree, 'magicBlock', (node, index, parent) => {
|
|
94645
|
+
if (!parent || index === undefined)
|
|
94524
94646
|
return undefined;
|
|
94647
|
+
const children = transformMagicBlock(node.blockType, node.data, node.value, options);
|
|
94648
|
+
if (!children.length) {
|
|
94649
|
+
// Remove the node if transformation returns nothing
|
|
94650
|
+
parent.children.splice(index, 1);
|
|
94651
|
+
return [SKIP, index];
|
|
94652
|
+
}
|
|
94525
94653
|
// If parent is a paragraph and we're inserting block nodes (which must not be in paragraphs), lift them out
|
|
94526
94654
|
if (parent.type === 'paragraph' && children.some(child => isBlockNode(child))) {
|
|
94527
94655
|
const blockNodes = [];
|
|
@@ -94590,7 +94718,557 @@ const magicBlockRestorer = ({ blocks }) => tree => {
|
|
|
94590
94718
|
}
|
|
94591
94719
|
}
|
|
94592
94720
|
};
|
|
94593
|
-
/* harmony default export */ const
|
|
94721
|
+
/* harmony default export */ const magic_block_transformer = (magicBlockTransformer);
|
|
94722
|
+
|
|
94723
|
+
;// ./processor/transform/mdxish/mdxish-html-blocks.ts
|
|
94724
|
+
|
|
94725
|
+
|
|
94726
|
+
|
|
94727
|
+
|
|
94728
|
+
/**
|
|
94729
|
+
* Decodes HTMLBlock content that was protected during preprocessing.
|
|
94730
|
+
* Content is wrapped in <!--RDMX_HTMLBLOCK:base64:RDMX_HTMLBLOCK-->
|
|
94731
|
+
*/
|
|
94732
|
+
function decodeProtectedContent(content) {
|
|
94733
|
+
// Escape special regex characters in the markers
|
|
94734
|
+
const startEscaped = HTML_BLOCK_CONTENT_START.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
94735
|
+
const endEscaped = HTML_BLOCK_CONTENT_END.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
94736
|
+
const markerRegex = new RegExp(`${startEscaped}([A-Za-z0-9+/=]+)${endEscaped}`, 'g');
|
|
94737
|
+
return content.replace(markerRegex, (_match, encoded) => {
|
|
94738
|
+
try {
|
|
94739
|
+
return base64Decode(encoded);
|
|
94740
|
+
}
|
|
94741
|
+
catch {
|
|
94742
|
+
return encoded;
|
|
94743
|
+
}
|
|
94744
|
+
});
|
|
94745
|
+
}
|
|
94746
|
+
/**
|
|
94747
|
+
* Collects text content from a node and its children recursively
|
|
94748
|
+
*/
|
|
94749
|
+
function collectTextContent(node) {
|
|
94750
|
+
const parts = [];
|
|
94751
|
+
if (node.type === 'text' && node.value) {
|
|
94752
|
+
parts.push(node.value);
|
|
94753
|
+
}
|
|
94754
|
+
else if (node.type === 'html' && node.value) {
|
|
94755
|
+
parts.push(node.value);
|
|
94756
|
+
}
|
|
94757
|
+
else if (node.type === 'inlineCode' && node.value) {
|
|
94758
|
+
parts.push(node.value);
|
|
94759
|
+
}
|
|
94760
|
+
else if (node.type === 'code' && node.value) {
|
|
94761
|
+
// Reconstruct code fence syntax (markdown parser consumes opening ```)
|
|
94762
|
+
const lang = node.lang || '';
|
|
94763
|
+
const fence = `\`\`\`${lang ? `${lang}\n` : ''}`;
|
|
94764
|
+
parts.push(fence);
|
|
94765
|
+
parts.push(node.value);
|
|
94766
|
+
// Add newline before closing fence if missing
|
|
94767
|
+
const closingFence = node.value.endsWith('\n') ? '```' : '\n```';
|
|
94768
|
+
parts.push(closingFence);
|
|
94769
|
+
}
|
|
94770
|
+
else if (node.children && Array.isArray(node.children)) {
|
|
94771
|
+
node.children.forEach(child => {
|
|
94772
|
+
if (typeof child === 'object' && child !== null) {
|
|
94773
|
+
parts.push(collectTextContent(child));
|
|
94774
|
+
}
|
|
94775
|
+
});
|
|
94776
|
+
}
|
|
94777
|
+
return parts.join('');
|
|
94778
|
+
}
|
|
94779
|
+
/**
|
|
94780
|
+
* Extracts boolean attribute from HTML tag. Handles JSX (safeMode={true}) and string (safeMode="true") syntax.
|
|
94781
|
+
* Returns "true"/"false" string to survive rehypeRaw serialization.
|
|
94782
|
+
*/
|
|
94783
|
+
function extractBooleanAttr(attrs, name) {
|
|
94784
|
+
// Try JSX syntax: name={true|false}
|
|
94785
|
+
const jsxMatch = attrs.match(new RegExp(`${name}=\\{(true|false)\\}`));
|
|
94786
|
+
if (jsxMatch) {
|
|
94787
|
+
return jsxMatch[1];
|
|
94788
|
+
}
|
|
94789
|
+
// Try string syntax: name="true"|true
|
|
94790
|
+
const stringMatch = attrs.match(new RegExp(`${name}="?(true|false)"?`));
|
|
94791
|
+
if (stringMatch) {
|
|
94792
|
+
return stringMatch[1];
|
|
94793
|
+
}
|
|
94794
|
+
return undefined;
|
|
94795
|
+
}
|
|
94796
|
+
/**
|
|
94797
|
+
* Extracts runScripts attribute from HTML tag. Returns boolean for "true"/"false", string for other values, or undefined if not found.
|
|
94798
|
+
*/
|
|
94799
|
+
function extractRunScriptsAttr(attrs) {
|
|
94800
|
+
const runScriptsMatch = attrs.match(/runScripts="?([^">\s]+)"?/);
|
|
94801
|
+
if (!runScriptsMatch) {
|
|
94802
|
+
return undefined;
|
|
94803
|
+
}
|
|
94804
|
+
const value = runScriptsMatch[1];
|
|
94805
|
+
if (value === 'true') {
|
|
94806
|
+
return true;
|
|
94807
|
+
}
|
|
94808
|
+
if (value === 'false') {
|
|
94809
|
+
return false;
|
|
94810
|
+
}
|
|
94811
|
+
return value;
|
|
94812
|
+
}
|
|
94813
|
+
/**
|
|
94814
|
+
* Creates an HTMLBlock node from HTML string and optional attributes
|
|
94815
|
+
*/
|
|
94816
|
+
function createHTMLBlockNode(htmlString, position, runScripts, safeMode) {
|
|
94817
|
+
return {
|
|
94818
|
+
position,
|
|
94819
|
+
children: [{ type: 'text', value: htmlString }],
|
|
94820
|
+
type: NodeTypes.htmlBlock,
|
|
94821
|
+
data: {
|
|
94822
|
+
hName: 'html-block',
|
|
94823
|
+
hProperties: {
|
|
94824
|
+
html: htmlString,
|
|
94825
|
+
...(runScripts !== undefined && { runScripts }),
|
|
94826
|
+
...(safeMode !== undefined && { safeMode }),
|
|
94827
|
+
},
|
|
94828
|
+
},
|
|
94829
|
+
};
|
|
94830
|
+
}
|
|
94831
|
+
/**
|
|
94832
|
+
* Checks for opening tag only (for split detection)
|
|
94833
|
+
*/
|
|
94834
|
+
function hasOpeningTagOnly(node) {
|
|
94835
|
+
let hasOpening = false;
|
|
94836
|
+
let hasClosed = false;
|
|
94837
|
+
let attrs = '';
|
|
94838
|
+
const check = (n) => {
|
|
94839
|
+
if (n.type === 'html' && n.value) {
|
|
94840
|
+
if (n.value === '<HTMLBlock>') {
|
|
94841
|
+
hasOpening = true;
|
|
94842
|
+
}
|
|
94843
|
+
else {
|
|
94844
|
+
const match = n.value.match(/^<HTMLBlock(\s[^>]*)?>$/);
|
|
94845
|
+
if (match) {
|
|
94846
|
+
hasOpening = true;
|
|
94847
|
+
attrs = match[1] || '';
|
|
94848
|
+
}
|
|
94849
|
+
}
|
|
94850
|
+
if (n.value === '</HTMLBlock>' || n.value.includes('</HTMLBlock>')) {
|
|
94851
|
+
hasClosed = true;
|
|
94852
|
+
}
|
|
94853
|
+
}
|
|
94854
|
+
if (n.children && Array.isArray(n.children)) {
|
|
94855
|
+
n.children.forEach(child => {
|
|
94856
|
+
check(child);
|
|
94857
|
+
});
|
|
94858
|
+
}
|
|
94859
|
+
};
|
|
94860
|
+
check(node);
|
|
94861
|
+
// Return true only if opening without closing (split case)
|
|
94862
|
+
return { attrs, found: hasOpening && !hasClosed };
|
|
94863
|
+
}
|
|
94864
|
+
/**
|
|
94865
|
+
* Checks if a node contains an HTMLBlock closing tag
|
|
94866
|
+
*/
|
|
94867
|
+
function hasClosingTag(node) {
|
|
94868
|
+
if (node.type === 'html' && node.value) {
|
|
94869
|
+
if (node.value === '</HTMLBlock>' || node.value.includes('</HTMLBlock>'))
|
|
94870
|
+
return true;
|
|
94871
|
+
}
|
|
94872
|
+
if (node.children && Array.isArray(node.children)) {
|
|
94873
|
+
return node.children.some(child => hasClosingTag(child));
|
|
94874
|
+
}
|
|
94875
|
+
return false;
|
|
94876
|
+
}
|
|
94877
|
+
/**
|
|
94878
|
+
* Transforms HTMLBlock MDX JSX to html-block nodes. Handles <HTMLBlock>{`...`}</HTMLBlock> syntax.
|
|
94879
|
+
*/
|
|
94880
|
+
const mdxishHtmlBlocks = () => tree => {
|
|
94881
|
+
// Handle HTMLBlock split across root children (caused by newlines)
|
|
94882
|
+
visit(tree, 'root', (root) => {
|
|
94883
|
+
const children = root.children;
|
|
94884
|
+
let i = 0;
|
|
94885
|
+
while (i < children.length) {
|
|
94886
|
+
const child = children[i];
|
|
94887
|
+
const { attrs, found: hasOpening } = hasOpeningTagOnly(child);
|
|
94888
|
+
if (hasOpening) {
|
|
94889
|
+
// Find closing tag in subsequent siblings
|
|
94890
|
+
let closingIdx = -1;
|
|
94891
|
+
for (let j = i + 1; j < children.length; j += 1) {
|
|
94892
|
+
if (hasClosingTag(children[j])) {
|
|
94893
|
+
closingIdx = j;
|
|
94894
|
+
break;
|
|
94895
|
+
}
|
|
94896
|
+
}
|
|
94897
|
+
if (closingIdx !== -1) {
|
|
94898
|
+
// Collect inner content between tags
|
|
94899
|
+
const contentParts = [];
|
|
94900
|
+
for (let j = i; j <= closingIdx; j += 1) {
|
|
94901
|
+
const node = children[j];
|
|
94902
|
+
contentParts.push(collectTextContent(node));
|
|
94903
|
+
}
|
|
94904
|
+
// Remove the opening/closing tags and template literal syntax from content
|
|
94905
|
+
let content = contentParts.join('');
|
|
94906
|
+
content = content.replace(/^<HTMLBlock[^>]*>\s*\{?\s*`?/, '').replace(/`?\s*\}?\s*<\/HTMLBlock>$/, '');
|
|
94907
|
+
// Decode protected content that was base64 encoded during preprocessing
|
|
94908
|
+
content = decodeProtectedContent(content);
|
|
94909
|
+
const htmlString = formatHtmlForMdxish(content);
|
|
94910
|
+
const runScripts = extractRunScriptsAttr(attrs);
|
|
94911
|
+
const safeMode = extractBooleanAttr(attrs, 'safeMode');
|
|
94912
|
+
// Replace range with single HTMLBlock node
|
|
94913
|
+
const mdNode = createHTMLBlockNode(htmlString, children[i].position, runScripts, safeMode);
|
|
94914
|
+
root.children.splice(i, closingIdx - i + 1, mdNode);
|
|
94915
|
+
}
|
|
94916
|
+
}
|
|
94917
|
+
i += 1;
|
|
94918
|
+
}
|
|
94919
|
+
});
|
|
94920
|
+
// Handle HTMLBlock parsed as HTML elements (when template literal contains block-level HTML tags)
|
|
94921
|
+
visit(tree, 'html', (node, index, parent) => {
|
|
94922
|
+
if (!parent || index === undefined)
|
|
94923
|
+
return;
|
|
94924
|
+
const value = node.value;
|
|
94925
|
+
if (!value)
|
|
94926
|
+
return;
|
|
94927
|
+
// Case 1: Full HTMLBlock in single node
|
|
94928
|
+
const fullMatch = value.match(/^<HTMLBlock(\s[^>]*)?>([\s\S]*)<\/HTMLBlock>$/);
|
|
94929
|
+
if (fullMatch) {
|
|
94930
|
+
const attrs = fullMatch[1] || '';
|
|
94931
|
+
let content = fullMatch[2] || '';
|
|
94932
|
+
// Remove template literal syntax if present: {`...`}
|
|
94933
|
+
content = content.replace(/^\s*\{\s*`/, '').replace(/`\s*\}\s*$/, '');
|
|
94934
|
+
// Decode protected content that was base64 encoded during preprocessing
|
|
94935
|
+
content = decodeProtectedContent(content);
|
|
94936
|
+
const htmlString = formatHtmlForMdxish(content);
|
|
94937
|
+
const runScripts = extractRunScriptsAttr(attrs);
|
|
94938
|
+
const safeMode = extractBooleanAttr(attrs, 'safeMode');
|
|
94939
|
+
parent.children[index] = createHTMLBlockNode(htmlString, node.position, runScripts, safeMode);
|
|
94940
|
+
return;
|
|
94941
|
+
}
|
|
94942
|
+
// Case 2: Opening tag only (split by blank lines)
|
|
94943
|
+
if (value === '<HTMLBlock>' || value.match(/^<HTMLBlock\s[^>]*>$/)) {
|
|
94944
|
+
const siblings = parent.children;
|
|
94945
|
+
let closingIdx = -1;
|
|
94946
|
+
// Find closing tag in siblings
|
|
94947
|
+
for (let i = index + 1; i < siblings.length; i += 1) {
|
|
94948
|
+
const sibling = siblings[i];
|
|
94949
|
+
if (sibling.type === 'html') {
|
|
94950
|
+
const sibVal = sibling.value;
|
|
94951
|
+
if (sibVal === '</HTMLBlock>' || sibVal?.includes('</HTMLBlock>')) {
|
|
94952
|
+
closingIdx = i;
|
|
94953
|
+
break;
|
|
94954
|
+
}
|
|
94955
|
+
}
|
|
94956
|
+
}
|
|
94957
|
+
if (closingIdx === -1)
|
|
94958
|
+
return;
|
|
94959
|
+
// Collect content between tags, skipping template literal delimiters
|
|
94960
|
+
const contentParts = [];
|
|
94961
|
+
for (let i = index + 1; i < closingIdx; i += 1) {
|
|
94962
|
+
const sibling = siblings[i];
|
|
94963
|
+
// Skip template literal delimiters
|
|
94964
|
+
if (sibling.type === 'text') {
|
|
94965
|
+
const textVal = sibling.value;
|
|
94966
|
+
if (textVal === '{' || textVal === '}' || textVal === '{`' || textVal === '`}') {
|
|
94967
|
+
// eslint-disable-next-line no-continue
|
|
94968
|
+
continue;
|
|
94969
|
+
}
|
|
94970
|
+
}
|
|
94971
|
+
contentParts.push(collectTextContent(sibling));
|
|
94972
|
+
}
|
|
94973
|
+
// Decode protected content that was base64 encoded during preprocessing
|
|
94974
|
+
const decodedContent = decodeProtectedContent(contentParts.join(''));
|
|
94975
|
+
const htmlString = formatHtmlForMdxish(decodedContent);
|
|
94976
|
+
const runScripts = extractRunScriptsAttr(value);
|
|
94977
|
+
const safeMode = extractBooleanAttr(value, 'safeMode');
|
|
94978
|
+
// Replace opening tag with HTMLBlock node, remove consumed siblings
|
|
94979
|
+
parent.children[index] = createHTMLBlockNode(htmlString, node.position, runScripts, safeMode);
|
|
94980
|
+
parent.children.splice(index + 1, closingIdx - index);
|
|
94981
|
+
}
|
|
94982
|
+
});
|
|
94983
|
+
// Handle HTMLBlock inside paragraphs (parsed as inline elements)
|
|
94984
|
+
visit(tree, 'paragraph', (node, index, parent) => {
|
|
94985
|
+
if (!parent || index === undefined)
|
|
94986
|
+
return;
|
|
94987
|
+
const children = node.children || [];
|
|
94988
|
+
let htmlBlockStartIdx = -1;
|
|
94989
|
+
let htmlBlockEndIdx = -1;
|
|
94990
|
+
let templateLiteralStartIdx = -1;
|
|
94991
|
+
let templateLiteralEndIdx = -1;
|
|
94992
|
+
for (let i = 0; i < children.length; i += 1) {
|
|
94993
|
+
const child = children[i];
|
|
94994
|
+
if (child.type === 'html' && typeof child.value === 'string') {
|
|
94995
|
+
const value = child.value;
|
|
94996
|
+
if (value === '<HTMLBlock>' || value.match(/^<HTMLBlock\s[^>]*>$/)) {
|
|
94997
|
+
htmlBlockStartIdx = i;
|
|
94998
|
+
}
|
|
94999
|
+
else if (value === '</HTMLBlock>') {
|
|
95000
|
+
htmlBlockEndIdx = i;
|
|
95001
|
+
}
|
|
95002
|
+
}
|
|
95003
|
+
// Find opening brace after HTMLBlock start
|
|
95004
|
+
if (htmlBlockStartIdx !== -1 && templateLiteralStartIdx === -1 && child.type === 'text') {
|
|
95005
|
+
const value = child.value;
|
|
95006
|
+
if (value === '{') {
|
|
95007
|
+
templateLiteralStartIdx = i;
|
|
95008
|
+
}
|
|
95009
|
+
}
|
|
95010
|
+
// Find closing brace before HTMLBlock end
|
|
95011
|
+
if (htmlBlockStartIdx !== -1 && htmlBlockEndIdx === -1 && child.type === 'text') {
|
|
95012
|
+
const value = child.value;
|
|
95013
|
+
if (value === '}') {
|
|
95014
|
+
templateLiteralEndIdx = i;
|
|
95015
|
+
}
|
|
95016
|
+
}
|
|
95017
|
+
}
|
|
95018
|
+
if (htmlBlockStartIdx !== -1 &&
|
|
95019
|
+
htmlBlockEndIdx !== -1 &&
|
|
95020
|
+
templateLiteralStartIdx !== -1 &&
|
|
95021
|
+
templateLiteralEndIdx !== -1 &&
|
|
95022
|
+
templateLiteralStartIdx < templateLiteralEndIdx) {
|
|
95023
|
+
const openingTag = children[htmlBlockStartIdx];
|
|
95024
|
+
// Collect content between braces (handles code blocks)
|
|
95025
|
+
const templateContent = [];
|
|
95026
|
+
for (let i = templateLiteralStartIdx + 1; i < templateLiteralEndIdx; i += 1) {
|
|
95027
|
+
const child = children[i];
|
|
95028
|
+
templateContent.push(collectTextContent(child));
|
|
95029
|
+
}
|
|
95030
|
+
// Decode protected content that was base64 encoded during preprocessing
|
|
95031
|
+
const decodedContent = decodeProtectedContent(templateContent.join(''));
|
|
95032
|
+
const htmlString = formatHtmlForMdxish(decodedContent);
|
|
95033
|
+
const runScripts = openingTag.value ? extractRunScriptsAttr(openingTag.value) : undefined;
|
|
95034
|
+
const safeMode = openingTag.value ? extractBooleanAttr(openingTag.value, 'safeMode') : undefined;
|
|
95035
|
+
const mdNode = createHTMLBlockNode(htmlString, node.position, runScripts, safeMode);
|
|
95036
|
+
parent.children[index] = mdNode;
|
|
95037
|
+
}
|
|
95038
|
+
});
|
|
95039
|
+
// Ensure html-block nodes have HTML in children as text node
|
|
95040
|
+
visit(tree, 'html-block', (node) => {
|
|
95041
|
+
const html = node.data?.hProperties?.html;
|
|
95042
|
+
if (html &&
|
|
95043
|
+
(!node.children ||
|
|
95044
|
+
node.children.length === 0 ||
|
|
95045
|
+
(node.children.length === 1 && node.children[0].type === 'text' && node.children[0].value !== html))) {
|
|
95046
|
+
node.children = [
|
|
95047
|
+
{
|
|
95048
|
+
type: 'text',
|
|
95049
|
+
value: html,
|
|
95050
|
+
},
|
|
95051
|
+
];
|
|
95052
|
+
}
|
|
95053
|
+
});
|
|
95054
|
+
return tree;
|
|
95055
|
+
};
|
|
95056
|
+
/* harmony default export */ const mdxish_html_blocks = (mdxishHtmlBlocks);
|
|
95057
|
+
|
|
95058
|
+
;// ./processor/transform/mdxish/mdxish-jsx-to-mdast.ts
|
|
95059
|
+
|
|
95060
|
+
|
|
95061
|
+
|
|
95062
|
+
const transformImage = (jsx) => {
|
|
95063
|
+
const attrs = getAttrs(jsx);
|
|
95064
|
+
const { align, alt = '', border, caption, className, height, lazy, src = '', title = '', width } = attrs;
|
|
95065
|
+
const hProperties = {
|
|
95066
|
+
alt,
|
|
95067
|
+
src,
|
|
95068
|
+
title,
|
|
95069
|
+
...(align && { align }),
|
|
95070
|
+
...(border !== undefined && { border: String(border) }),
|
|
95071
|
+
...(caption && { caption }),
|
|
95072
|
+
...(className && { className }),
|
|
95073
|
+
...(height !== undefined && { height: String(height) }),
|
|
95074
|
+
...(lazy !== undefined && { lazy }),
|
|
95075
|
+
...(width !== undefined && { width: String(width) }),
|
|
95076
|
+
};
|
|
95077
|
+
return {
|
|
95078
|
+
type: NodeTypes.imageBlock,
|
|
95079
|
+
align,
|
|
95080
|
+
alt,
|
|
95081
|
+
border: border !== undefined ? String(border) : undefined,
|
|
95082
|
+
caption,
|
|
95083
|
+
className,
|
|
95084
|
+
height: height !== undefined ? String(height) : undefined,
|
|
95085
|
+
lazy,
|
|
95086
|
+
src,
|
|
95087
|
+
title,
|
|
95088
|
+
width: width !== undefined ? String(width) : undefined,
|
|
95089
|
+
data: {
|
|
95090
|
+
hName: 'img',
|
|
95091
|
+
hProperties,
|
|
95092
|
+
},
|
|
95093
|
+
position: jsx.position,
|
|
95094
|
+
};
|
|
95095
|
+
};
|
|
95096
|
+
const transformCallout = (jsx) => {
|
|
95097
|
+
const attrs = getAttrs(jsx);
|
|
95098
|
+
const { empty = false, icon = '', theme = '' } = attrs;
|
|
95099
|
+
return {
|
|
95100
|
+
type: NodeTypes.callout,
|
|
95101
|
+
children: jsx.children,
|
|
95102
|
+
data: {
|
|
95103
|
+
hName: 'Callout',
|
|
95104
|
+
hProperties: {
|
|
95105
|
+
empty,
|
|
95106
|
+
icon,
|
|
95107
|
+
theme,
|
|
95108
|
+
},
|
|
95109
|
+
},
|
|
95110
|
+
position: jsx.position,
|
|
95111
|
+
};
|
|
95112
|
+
};
|
|
95113
|
+
const transformEmbed = (jsx) => {
|
|
95114
|
+
const attrs = getAttrs(jsx);
|
|
95115
|
+
const { favicon, html, iframe, image, providerName, providerUrl, title = '', url = '' } = attrs;
|
|
95116
|
+
return {
|
|
95117
|
+
type: NodeTypes.embedBlock,
|
|
95118
|
+
title,
|
|
95119
|
+
url,
|
|
95120
|
+
data: {
|
|
95121
|
+
hName: 'embed',
|
|
95122
|
+
hProperties: {
|
|
95123
|
+
title,
|
|
95124
|
+
url,
|
|
95125
|
+
...(favicon && { favicon }),
|
|
95126
|
+
...(html && { html }),
|
|
95127
|
+
...(iframe !== undefined && { iframe }),
|
|
95128
|
+
...(image && { image }),
|
|
95129
|
+
...(providerName && { providerName }),
|
|
95130
|
+
...(providerUrl && { providerUrl }),
|
|
95131
|
+
},
|
|
95132
|
+
},
|
|
95133
|
+
position: jsx.position,
|
|
95134
|
+
};
|
|
95135
|
+
};
|
|
95136
|
+
const transformRecipe = (jsx) => {
|
|
95137
|
+
const attrs = getAttrs(jsx);
|
|
95138
|
+
const { backgroundColor = '', emoji = '', id = '', link = '', slug = '', title = '' } = attrs;
|
|
95139
|
+
return {
|
|
95140
|
+
type: NodeTypes.recipe,
|
|
95141
|
+
backgroundColor,
|
|
95142
|
+
emoji,
|
|
95143
|
+
id,
|
|
95144
|
+
link,
|
|
95145
|
+
slug,
|
|
95146
|
+
title,
|
|
95147
|
+
position: jsx.position,
|
|
95148
|
+
};
|
|
95149
|
+
};
|
|
95150
|
+
/**
|
|
95151
|
+
* Transform a magic block image node into an ImageBlock.
|
|
95152
|
+
* Magic block images have structure: { type: 'image', url, title, alt, data.hProperties }
|
|
95153
|
+
*/
|
|
95154
|
+
const transformMagicBlockImage = (node) => {
|
|
95155
|
+
const { alt = '', data, position, title = '', url = '' } = node;
|
|
95156
|
+
const hProps = data?.hProperties || {};
|
|
95157
|
+
const { align, border, width } = hProps;
|
|
95158
|
+
const hProperties = {
|
|
95159
|
+
alt,
|
|
95160
|
+
src: url,
|
|
95161
|
+
title,
|
|
95162
|
+
...(align && { align }),
|
|
95163
|
+
...(border && { border }),
|
|
95164
|
+
...(width && { width }),
|
|
95165
|
+
};
|
|
95166
|
+
return {
|
|
95167
|
+
type: NodeTypes.imageBlock,
|
|
95168
|
+
align,
|
|
95169
|
+
alt,
|
|
95170
|
+
border,
|
|
95171
|
+
src: url,
|
|
95172
|
+
title,
|
|
95173
|
+
width,
|
|
95174
|
+
data: {
|
|
95175
|
+
hName: 'img',
|
|
95176
|
+
hProperties,
|
|
95177
|
+
},
|
|
95178
|
+
position,
|
|
95179
|
+
};
|
|
95180
|
+
};
|
|
95181
|
+
/**
|
|
95182
|
+
* Transform a magic block embed node into an EmbedBlock.
|
|
95183
|
+
* Magic block embeds have structure: { type: 'embed', children, data.hProperties }
|
|
95184
|
+
*/
|
|
95185
|
+
const transformMagicBlockEmbed = (node) => {
|
|
95186
|
+
const { data, position } = node;
|
|
95187
|
+
const hProps = data?.hProperties || {};
|
|
95188
|
+
const { favicon, html, image, providerName, providerUrl, title = '', url = '' } = hProps;
|
|
95189
|
+
return {
|
|
95190
|
+
type: NodeTypes.embedBlock,
|
|
95191
|
+
title,
|
|
95192
|
+
url,
|
|
95193
|
+
data: {
|
|
95194
|
+
hName: 'embed',
|
|
95195
|
+
hProperties: {
|
|
95196
|
+
title,
|
|
95197
|
+
url,
|
|
95198
|
+
...(favicon && { favicon }),
|
|
95199
|
+
...(html && { html }),
|
|
95200
|
+
...(image && { image }),
|
|
95201
|
+
...(providerName && { providerName }),
|
|
95202
|
+
...(providerUrl && { providerUrl }),
|
|
95203
|
+
},
|
|
95204
|
+
},
|
|
95205
|
+
position,
|
|
95206
|
+
};
|
|
95207
|
+
};
|
|
95208
|
+
const COMPONENT_MAP = {
|
|
95209
|
+
Callout: transformCallout,
|
|
95210
|
+
Embed: transformEmbed,
|
|
95211
|
+
Image: transformImage,
|
|
95212
|
+
Recipe: transformRecipe,
|
|
95213
|
+
};
|
|
95214
|
+
/**
|
|
95215
|
+
* Transform mdxJsxFlowElement nodes and magic block nodes into proper MDAST node types.
|
|
95216
|
+
*
|
|
95217
|
+
* This transformer runs after mdxishComponentBlocks and converts:
|
|
95218
|
+
* - JSX component elements (Image, Callout, Embed, Recipe) into their corresponding MDAST types
|
|
95219
|
+
* - Magic block image nodes (type: 'image') into image-block
|
|
95220
|
+
* - Magic block embed nodes (type: 'embed') into embed-block
|
|
95221
|
+
* - Figure nodes containing images (from magic blocks with captions) - transforms the inner image
|
|
95222
|
+
*
|
|
95223
|
+
* This is controlled by the `newEditorTypes` flag to maintain backwards compatibility.
|
|
95224
|
+
*/
|
|
95225
|
+
const mdxishJsxToMdast = () => tree => {
|
|
95226
|
+
// Transform JSX components (Image, Callout, Embed, Recipe)
|
|
95227
|
+
visit(tree, 'mdxJsxFlowElement', (node, index, parent) => {
|
|
95228
|
+
if (!parent || index === undefined || !node.name)
|
|
95229
|
+
return;
|
|
95230
|
+
const transformer = COMPONENT_MAP[node.name];
|
|
95231
|
+
if (!transformer)
|
|
95232
|
+
return;
|
|
95233
|
+
const newNode = transformer(node);
|
|
95234
|
+
// Replace the JSX node with the MDAST node
|
|
95235
|
+
parent.children[index] = newNode;
|
|
95236
|
+
});
|
|
95237
|
+
// Transform magic block images (type: 'image') to image-block
|
|
95238
|
+
// Note: Standard markdown images are wrapped in paragraphs and handled by imageTransformer
|
|
95239
|
+
// Magic block images are direct children of root, so we handle them here
|
|
95240
|
+
visit(tree, 'image', (node, index, parent) => {
|
|
95241
|
+
if (!parent || index === undefined)
|
|
95242
|
+
return SKIP;
|
|
95243
|
+
// Skip images inside paragraphs (those are standard markdown images handled by imageTransformer)
|
|
95244
|
+
if (parent.type === 'paragraph')
|
|
95245
|
+
return SKIP;
|
|
95246
|
+
const newNode = transformMagicBlockImage(node);
|
|
95247
|
+
parent.children[index] = newNode;
|
|
95248
|
+
return SKIP;
|
|
95249
|
+
});
|
|
95250
|
+
// Transform magic block embeds (type: 'embed') to embed-block
|
|
95251
|
+
visit(tree, 'embed', (node, index, parent) => {
|
|
95252
|
+
if (!parent || index === undefined)
|
|
95253
|
+
return SKIP;
|
|
95254
|
+
const newNode = transformMagicBlockEmbed(node);
|
|
95255
|
+
parent.children[index] = newNode;
|
|
95256
|
+
return SKIP;
|
|
95257
|
+
});
|
|
95258
|
+
// Transform images inside figure nodes (magic blocks with captions)
|
|
95259
|
+
const isFigure = (node) => node.type === 'figure';
|
|
95260
|
+
visit(tree, isFigure, node => {
|
|
95261
|
+
// Find and transform the image child
|
|
95262
|
+
node.children = node.children.map(child => {
|
|
95263
|
+
if (child.type === 'image') {
|
|
95264
|
+
return transformMagicBlockImage(child);
|
|
95265
|
+
}
|
|
95266
|
+
return child;
|
|
95267
|
+
});
|
|
95268
|
+
});
|
|
95269
|
+
return tree;
|
|
95270
|
+
};
|
|
95271
|
+
/* harmony default export */ const mdxish_jsx_to_mdast = (mdxishJsxToMdast);
|
|
94594
95272
|
|
|
94595
95273
|
;// ./processor/transform/mdxish/mdxish-mermaid.ts
|
|
94596
95274
|
|
|
@@ -94632,25 +95310,50 @@ const componentTagPattern = /<(\/?[A-Z][A-Za-z0-9_]*)([^>]*?)(\/?)>/g;
|
|
|
94632
95310
|
|
|
94633
95311
|
;// ./processor/transform/mdxish/mdxish-snake-case-components.ts
|
|
94634
95312
|
|
|
95313
|
+
|
|
94635
95314
|
/**
|
|
94636
95315
|
* Replaces snake_case component names with valid HTML placeholders.
|
|
94637
95316
|
* Required because remark-parse rejects tags with underscores.
|
|
94638
95317
|
* Example: `<Snake_case />` → `<MDXishSnakeCase0 />`
|
|
95318
|
+
*
|
|
95319
|
+
* Code blocks and inline code are protected and will not be transformed.
|
|
95320
|
+
*
|
|
95321
|
+
* @param content - The markdown content to process
|
|
95322
|
+
* @param options - Options including knownComponents to filter by
|
|
94639
95323
|
*/
|
|
94640
|
-
function processSnakeCaseComponent(content) {
|
|
95324
|
+
function processSnakeCaseComponent(content, options = {}) {
|
|
95325
|
+
const { knownComponents } = options;
|
|
94641
95326
|
// Early exit if no potential snake_case components
|
|
94642
95327
|
if (!/[A-Z][A-Za-z0-9]*_[A-Za-z0-9_]*/.test(content)) {
|
|
94643
95328
|
return { content, mapping: {} };
|
|
94644
95329
|
}
|
|
95330
|
+
// Step 1: Extract code blocks to protect them from transformation
|
|
95331
|
+
const { protectedCode, protectedContent } = protectCodeBlocks(content);
|
|
95332
|
+
// Find the highest existing placeholder number to avoid collisions
|
|
95333
|
+
// e.g., if content has <MDXishSnakeCase0 />, start counter from 1
|
|
95334
|
+
const placeholderPattern = /MDXishSnakeCase(\d+)/g;
|
|
95335
|
+
let startCounter = 0;
|
|
95336
|
+
let placeholderMatch;
|
|
95337
|
+
while ((placeholderMatch = placeholderPattern.exec(content)) !== null) {
|
|
95338
|
+
const num = parseInt(placeholderMatch[1], 10);
|
|
95339
|
+
if (num >= startCounter) {
|
|
95340
|
+
startCounter = num + 1;
|
|
95341
|
+
}
|
|
95342
|
+
}
|
|
94645
95343
|
const mapping = {};
|
|
94646
95344
|
const reverseMap = new Map();
|
|
94647
|
-
let counter =
|
|
94648
|
-
|
|
95345
|
+
let counter = startCounter;
|
|
95346
|
+
// Step 2: Transform snake_case components in non-code content
|
|
95347
|
+
const processedContent = protectedContent.replace(componentTagPattern, (match, tagName, attrs, selfClosing) => {
|
|
94649
95348
|
if (!tagName.includes('_')) {
|
|
94650
95349
|
return match;
|
|
94651
95350
|
}
|
|
94652
95351
|
const isClosing = tagName.startsWith('/');
|
|
94653
95352
|
const cleanTagName = isClosing ? tagName.slice(1) : tagName;
|
|
95353
|
+
// Only transform if it's a known component (or if no filter is provided)
|
|
95354
|
+
if (knownComponents && !knownComponents.has(cleanTagName)) {
|
|
95355
|
+
return match;
|
|
95356
|
+
}
|
|
94654
95357
|
let placeholder = reverseMap.get(cleanTagName);
|
|
94655
95358
|
if (!placeholder) {
|
|
94656
95359
|
// eslint-disable-next-line no-plusplus
|
|
@@ -94661,8 +95364,10 @@ function processSnakeCaseComponent(content) {
|
|
|
94661
95364
|
const processedTagName = isClosing ? `/${placeholder}` : placeholder;
|
|
94662
95365
|
return `<${processedTagName}${attrs}${selfClosing}>`;
|
|
94663
95366
|
});
|
|
95367
|
+
// Step 3: Restore code blocks (untouched)
|
|
95368
|
+
const finalContent = restoreCodeBlocks(processedContent, protectedCode);
|
|
94664
95369
|
return {
|
|
94665
|
-
content:
|
|
95370
|
+
content: finalContent,
|
|
94666
95371
|
mapping,
|
|
94667
95372
|
};
|
|
94668
95373
|
}
|
|
@@ -94679,139 +95384,6 @@ function restoreSnakeCase(placeholderName, mapping) {
|
|
|
94679
95384
|
return matchingKey ? mapping[matchingKey] : placeholderName;
|
|
94680
95385
|
}
|
|
94681
95386
|
|
|
94682
|
-
;// ./processor/transform/mdxish/normalize-malformed-md-syntax.ts
|
|
94683
|
-
|
|
94684
|
-
// Patterns to detect for bold (** and __) and italic (* and _) syntax:
|
|
94685
|
-
// Bold: ** text**, **text **, word** text**, ** text **
|
|
94686
|
-
// Italic: * text*, *text *, word* text*, * text *
|
|
94687
|
-
// Same patterns for underscore variants
|
|
94688
|
-
// We use separate patterns for each marker type to allow this flexibility.
|
|
94689
|
-
// Pattern for ** bold **
|
|
94690
|
-
// Groups: 1=wordBefore, 2=marker, 3=contentWithSpaceAfter, 4=trailingSpace1, 5=contentWithSpaceBefore, 6=trailingSpace2, 7=afterChar
|
|
94691
|
-
// trailingSpace1 is for "** text **" pattern, trailingSpace2 is for "**text **" pattern
|
|
94692
|
-
const asteriskBoldRegex = /([^*\s]+)?\s*(\*\*)(?:\s+((?:[^*\n]|\*(?!\*))+?)(\s*)\2|((?:[^*\n]|\*(?!\*))+?)(\s+)\2)(\S|$)?/g;
|
|
94693
|
-
// Pattern for __ bold __
|
|
94694
|
-
const underscoreBoldRegex = /([^_\s]+)?\s*(__)(?:\s+((?:[^_\n]|_(?!_))+?)(\s*)\2|((?:[^_\n]|_(?!_))+?)(\s+)\2)(\S|$)?/g;
|
|
94695
|
-
// Pattern for * italic *
|
|
94696
|
-
const asteriskItalicRegex = /([^*\s]+)?\s*(\*)(?!\*)(?:\s+([^*\n]+?)(\s*)\2|([^*\n]+?)(\s+)\2)(\S|$)?/g;
|
|
94697
|
-
// Pattern for _ italic _
|
|
94698
|
-
const underscoreItalicRegex = /([^_\s]+)?\s*(_)(?!_)(?:\s+([^_\n]+?)(\s*)\2|([^_\n]+?)(\s+)\2)(\S|$)?/g;
|
|
94699
|
-
/**
|
|
94700
|
-
* A remark plugin that normalizes malformed bold and italic markers in text nodes.
|
|
94701
|
-
* Detects patterns like `** bold**`, `Hello** Wrong Bold**`, `__ bold__`, `Hello__ Wrong Bold__`,
|
|
94702
|
-
* `* italic*`, `Hello* Wrong Italic*`, `_ italic_`, or `Hello_ Wrong Italic_`
|
|
94703
|
-
* and converts them to proper strong/emphasis nodes, matching the behavior of the legacy rdmd engine.
|
|
94704
|
-
*
|
|
94705
|
-
* Supports both asterisk (`**bold**`, `*italic*`) and underscore (`__bold__`, `_italic_`) syntax.
|
|
94706
|
-
* Also supports snake_case content like `** some_snake_case**`.
|
|
94707
|
-
*
|
|
94708
|
-
* This runs after remark-parse, which (in v11+) is strict and doesn't parse
|
|
94709
|
-
* malformed emphasis syntax. This plugin post-processes the AST to handle these cases.
|
|
94710
|
-
*/
|
|
94711
|
-
const normalizeEmphasisAST = () => (tree) => {
|
|
94712
|
-
visit(tree, 'text', function visitor(node, index, parent) {
|
|
94713
|
-
if (index === undefined || !parent)
|
|
94714
|
-
return undefined;
|
|
94715
|
-
// Skip if inside code blocks or inline code
|
|
94716
|
-
if (parent.type === 'inlineCode' || parent.type === 'code') {
|
|
94717
|
-
return undefined;
|
|
94718
|
-
}
|
|
94719
|
-
const text = node.value;
|
|
94720
|
-
const allMatches = [];
|
|
94721
|
-
[...text.matchAll(asteriskBoldRegex)].forEach(match => {
|
|
94722
|
-
allMatches.push({ isBold: true, marker: '**', match });
|
|
94723
|
-
});
|
|
94724
|
-
[...text.matchAll(underscoreBoldRegex)].forEach(match => {
|
|
94725
|
-
allMatches.push({ isBold: true, marker: '__', match });
|
|
94726
|
-
});
|
|
94727
|
-
[...text.matchAll(asteriskItalicRegex)].forEach(match => {
|
|
94728
|
-
allMatches.push({ isBold: false, marker: '*', match });
|
|
94729
|
-
});
|
|
94730
|
-
[...text.matchAll(underscoreItalicRegex)].forEach(match => {
|
|
94731
|
-
allMatches.push({ isBold: false, marker: '_', match });
|
|
94732
|
-
});
|
|
94733
|
-
if (allMatches.length === 0)
|
|
94734
|
-
return undefined;
|
|
94735
|
-
allMatches.sort((a, b) => (a.match.index ?? 0) - (b.match.index ?? 0));
|
|
94736
|
-
const filteredMatches = [];
|
|
94737
|
-
let lastEnd = 0;
|
|
94738
|
-
allMatches.forEach(info => {
|
|
94739
|
-
const start = info.match.index ?? 0;
|
|
94740
|
-
const end = start + info.match[0].length;
|
|
94741
|
-
if (start >= lastEnd) {
|
|
94742
|
-
filteredMatches.push(info);
|
|
94743
|
-
lastEnd = end;
|
|
94744
|
-
}
|
|
94745
|
-
});
|
|
94746
|
-
if (filteredMatches.length === 0)
|
|
94747
|
-
return undefined;
|
|
94748
|
-
const parts = [];
|
|
94749
|
-
let lastIndex = 0;
|
|
94750
|
-
filteredMatches.forEach(({ match, marker, isBold }) => {
|
|
94751
|
-
const matchIndex = match.index ?? 0;
|
|
94752
|
-
const fullMatch = match[0];
|
|
94753
|
-
if (matchIndex > lastIndex) {
|
|
94754
|
-
const beforeText = text.slice(lastIndex, matchIndex);
|
|
94755
|
-
if (beforeText) {
|
|
94756
|
-
parts.push({ type: 'text', value: beforeText });
|
|
94757
|
-
}
|
|
94758
|
-
}
|
|
94759
|
-
const wordBefore = match[1]; // e.g., "Hello" in "Hello** Wrong Bold**"
|
|
94760
|
-
const contentWithSpaceAfter = match[3]; // Content when there's a space after opening markers
|
|
94761
|
-
const trailingSpace1 = match[4] || ''; // Space before closing markers (for "** text **" pattern)
|
|
94762
|
-
const contentWithSpaceBefore = match[5]; // Content when there's only a space before closing markers
|
|
94763
|
-
const trailingSpace2 = match[6] || ''; // Space before closing markers (for "**text **" pattern)
|
|
94764
|
-
const trailingSpace = trailingSpace1 || trailingSpace2; // Combined trailing space
|
|
94765
|
-
const content = (contentWithSpaceAfter || contentWithSpaceBefore || '').trim();
|
|
94766
|
-
const afterChar = match[7]; // Character after closing markers (if any)
|
|
94767
|
-
const markerPos = fullMatch.indexOf(marker);
|
|
94768
|
-
const spacesBeforeMarkers = wordBefore
|
|
94769
|
-
? fullMatch.slice(wordBefore.length, markerPos)
|
|
94770
|
-
: fullMatch.slice(0, markerPos);
|
|
94771
|
-
const shouldAddSpace = !!contentWithSpaceAfter && !!wordBefore && !spacesBeforeMarkers;
|
|
94772
|
-
if (wordBefore) {
|
|
94773
|
-
const spacing = spacesBeforeMarkers + (shouldAddSpace ? ' ' : '');
|
|
94774
|
-
parts.push({ type: 'text', value: wordBefore + spacing });
|
|
94775
|
-
}
|
|
94776
|
-
else if (spacesBeforeMarkers) {
|
|
94777
|
-
parts.push({ type: 'text', value: spacesBeforeMarkers });
|
|
94778
|
-
}
|
|
94779
|
-
if (content) {
|
|
94780
|
-
if (isBold) {
|
|
94781
|
-
parts.push({
|
|
94782
|
-
type: 'strong',
|
|
94783
|
-
children: [{ type: 'text', value: content }],
|
|
94784
|
-
});
|
|
94785
|
-
}
|
|
94786
|
-
else {
|
|
94787
|
-
parts.push({
|
|
94788
|
-
type: 'emphasis',
|
|
94789
|
-
children: [{ type: 'text', value: content }],
|
|
94790
|
-
});
|
|
94791
|
-
}
|
|
94792
|
-
}
|
|
94793
|
-
if (afterChar) {
|
|
94794
|
-
const prefix = trailingSpace ? ' ' : '';
|
|
94795
|
-
parts.push({ type: 'text', value: prefix + afterChar });
|
|
94796
|
-
}
|
|
94797
|
-
lastIndex = matchIndex + fullMatch.length;
|
|
94798
|
-
});
|
|
94799
|
-
if (lastIndex < text.length) {
|
|
94800
|
-
const remainingText = text.slice(lastIndex);
|
|
94801
|
-
if (remainingText) {
|
|
94802
|
-
parts.push({ type: 'text', value: remainingText });
|
|
94803
|
-
}
|
|
94804
|
-
}
|
|
94805
|
-
if (parts.length > 0) {
|
|
94806
|
-
parent.children.splice(index, 1, ...parts);
|
|
94807
|
-
return [SKIP, index + parts.length];
|
|
94808
|
-
}
|
|
94809
|
-
return undefined;
|
|
94810
|
-
});
|
|
94811
|
-
return tree;
|
|
94812
|
-
};
|
|
94813
|
-
/* harmony default export */ const normalize_malformed_md_syntax = (normalizeEmphasisAST);
|
|
94814
|
-
|
|
94815
95387
|
;// ./processor/transform/mdxish/normalize-table-separator.ts
|
|
94816
95388
|
/**
|
|
94817
95389
|
* Preprocessor to normalize malformed GFM table separator syntax.
|
|
@@ -95044,57 +95616,1172 @@ const variablesTextTransformer = () => tree => {
|
|
|
95044
95616
|
};
|
|
95045
95617
|
/* harmony default export */ const variables_text = (variablesTextTransformer);
|
|
95046
95618
|
|
|
95047
|
-
;// ./lib/
|
|
95619
|
+
;// ./lib/mdast-util/magic-block/index.ts
|
|
95620
|
+
const contextMap = new WeakMap();
|
|
95048
95621
|
/**
|
|
95049
|
-
*
|
|
95050
|
-
* and `[/block]`, including new lines. Negative lookahead for the closing
|
|
95051
|
-
* `[/block]` tag is required to prevent greedy matching to ensure it stops at
|
|
95052
|
-
* the first closing tag it encounters preventing vulnerability to polynomial
|
|
95053
|
-
* backtracking issues.
|
|
95622
|
+
* Find the magicBlock token in the token ancestry.
|
|
95054
95623
|
*/
|
|
95055
|
-
|
|
95624
|
+
function findMagicBlockToken() {
|
|
95625
|
+
// Walk up the token stack to find the magicBlock token
|
|
95626
|
+
const events = this.tokenStack;
|
|
95627
|
+
for (let i = events.length - 1; i >= 0; i -= 1) {
|
|
95628
|
+
const token = events[i][0];
|
|
95629
|
+
if (token.type === 'magicBlock') {
|
|
95630
|
+
return token;
|
|
95631
|
+
}
|
|
95632
|
+
}
|
|
95633
|
+
return undefined;
|
|
95634
|
+
}
|
|
95056
95635
|
/**
|
|
95057
|
-
*
|
|
95058
|
-
* Returns the modified markdown and an array of extracted blocks.
|
|
95636
|
+
* Enter handler: Create a new magicBlock node.
|
|
95059
95637
|
*/
|
|
95060
|
-
function
|
|
95061
|
-
|
|
95062
|
-
|
|
95063
|
-
|
|
95064
|
-
|
|
95065
|
-
|
|
95066
|
-
|
|
95067
|
-
|
|
95068
|
-
|
|
95069
|
-
* Token is a wrapper around the `key` to serialize & influence how the
|
|
95070
|
-
* magic block is parsed in the remark pipeline.
|
|
95071
|
-
* - Use backticks so it becomes a code span, preventing `remarkParse` from
|
|
95072
|
-
* parsing special characters in the token as markdown syntax
|
|
95073
|
-
* - Prepend a newline to ensure it is parsed as a block level node
|
|
95074
|
-
* - Append a newline to ensure it is separated from following content
|
|
95075
|
-
*/
|
|
95076
|
-
const token = `\n\`${key}\`\n`;
|
|
95077
|
-
blocks.push({ key, raw: match, token });
|
|
95078
|
-
index += 1;
|
|
95079
|
-
return token;
|
|
95080
|
-
});
|
|
95081
|
-
return { replaced, blocks };
|
|
95638
|
+
function enterMagicBlock(token) {
|
|
95639
|
+
// Initialize context for this magic block
|
|
95640
|
+
contextMap.set(token, { blockType: '', dataChunks: [] });
|
|
95641
|
+
this.enter({
|
|
95642
|
+
type: 'magicBlock',
|
|
95643
|
+
blockType: '',
|
|
95644
|
+
data: {},
|
|
95645
|
+
value: '',
|
|
95646
|
+
}, token);
|
|
95082
95647
|
}
|
|
95083
95648
|
/**
|
|
95084
|
-
*
|
|
95649
|
+
* Exit handler for block type: Extract the block type from the token.
|
|
95085
95650
|
*/
|
|
95086
|
-
function
|
|
95087
|
-
|
|
95088
|
-
|
|
95089
|
-
|
|
95090
|
-
|
|
95091
|
-
|
|
95092
|
-
|
|
95093
|
-
|
|
95094
|
-
|
|
95095
|
-
|
|
95096
|
-
|
|
95651
|
+
function exitMagicBlockType(token) {
|
|
95652
|
+
const blockToken = findMagicBlockToken.call(this);
|
|
95653
|
+
if (!blockToken)
|
|
95654
|
+
return;
|
|
95655
|
+
const context = contextMap.get(blockToken);
|
|
95656
|
+
if (context) {
|
|
95657
|
+
context.blockType = this.sliceSerialize(token);
|
|
95658
|
+
}
|
|
95659
|
+
}
|
|
95660
|
+
/**
|
|
95661
|
+
* Exit handler for block data: Accumulate JSON content chunks.
|
|
95662
|
+
*/
|
|
95663
|
+
function exitMagicBlockData(token) {
|
|
95664
|
+
const blockToken = findMagicBlockToken.call(this);
|
|
95665
|
+
if (!blockToken)
|
|
95666
|
+
return;
|
|
95667
|
+
const context = contextMap.get(blockToken);
|
|
95668
|
+
if (context) {
|
|
95669
|
+
context.dataChunks.push(this.sliceSerialize(token));
|
|
95670
|
+
}
|
|
95671
|
+
}
|
|
95672
|
+
/**
|
|
95673
|
+
* Exit handler for line endings: Preserve newlines in multiline blocks.
|
|
95674
|
+
*/
|
|
95675
|
+
function exitMagicBlockLineEnding(token) {
|
|
95676
|
+
const blockToken = findMagicBlockToken.call(this);
|
|
95677
|
+
if (!blockToken)
|
|
95678
|
+
return;
|
|
95679
|
+
const context = contextMap.get(blockToken);
|
|
95680
|
+
if (context) {
|
|
95681
|
+
context.dataChunks.push(this.sliceSerialize(token));
|
|
95682
|
+
}
|
|
95097
95683
|
}
|
|
95684
|
+
/**
|
|
95685
|
+
* Exit handler for end marker: If this is a failed end marker check (not the final marker),
|
|
95686
|
+
* add its content to the data chunks so we don't lose characters like '['.
|
|
95687
|
+
*/
|
|
95688
|
+
function exitMagicBlockMarkerEnd(token) {
|
|
95689
|
+
const blockToken = findMagicBlockToken.call(this);
|
|
95690
|
+
if (!blockToken)
|
|
95691
|
+
return;
|
|
95692
|
+
// Get the content of the marker
|
|
95693
|
+
const markerContent = this.sliceSerialize(token);
|
|
95694
|
+
// If this marker doesn't end with ']', it's a failed check and content belongs to data
|
|
95695
|
+
// The successful end marker would be "[/block]"
|
|
95696
|
+
if (!markerContent.endsWith(']') || markerContent !== '[/block]') {
|
|
95697
|
+
const context = contextMap.get(blockToken);
|
|
95698
|
+
if (context) {
|
|
95699
|
+
context.dataChunks.push(markerContent);
|
|
95700
|
+
}
|
|
95701
|
+
}
|
|
95702
|
+
}
|
|
95703
|
+
/**
|
|
95704
|
+
* Exit handler: Finalize the magicBlock node with parsed JSON data.
|
|
95705
|
+
*/
|
|
95706
|
+
function exitMagicBlock(token) {
|
|
95707
|
+
const context = contextMap.get(token);
|
|
95708
|
+
const node = this.stack[this.stack.length - 1];
|
|
95709
|
+
if (context) {
|
|
95710
|
+
const rawJson = context.dataChunks.join('');
|
|
95711
|
+
node.blockType = context.blockType;
|
|
95712
|
+
node.value = `[block:${context.blockType}]${rawJson}[/block]`;
|
|
95713
|
+
// Parse JSON data
|
|
95714
|
+
try {
|
|
95715
|
+
node.data = JSON.parse(rawJson.trim());
|
|
95716
|
+
}
|
|
95717
|
+
catch {
|
|
95718
|
+
// Invalid JSON - store empty object but keep the raw value
|
|
95719
|
+
node.data = {};
|
|
95720
|
+
}
|
|
95721
|
+
// Clean up context
|
|
95722
|
+
contextMap.delete(token);
|
|
95723
|
+
}
|
|
95724
|
+
this.exit(token);
|
|
95725
|
+
}
|
|
95726
|
+
/**
|
|
95727
|
+
* Handler to serialize magicBlock nodes back to markdown.
|
|
95728
|
+
*/
|
|
95729
|
+
const handleMagicBlock = (node) => {
|
|
95730
|
+
const magicNode = node;
|
|
95731
|
+
// If we have the original raw value, use it
|
|
95732
|
+
if (magicNode.value) {
|
|
95733
|
+
return magicNode.value;
|
|
95734
|
+
}
|
|
95735
|
+
// Otherwise reconstruct from parsed data
|
|
95736
|
+
const json = JSON.stringify(magicNode.data, null, 2);
|
|
95737
|
+
return `[block:${magicNode.blockType}]\n${json}\n[/block]`;
|
|
95738
|
+
};
|
|
95739
|
+
/**
|
|
95740
|
+
* Create an extension for `mdast-util-from-markdown` to enable magic blocks.
|
|
95741
|
+
*
|
|
95742
|
+
* Converts micromark magic block tokens into `magicBlock` MDAST nodes.
|
|
95743
|
+
*
|
|
95744
|
+
* @returns Extension for `mdast-util-from-markdown`
|
|
95745
|
+
*/
|
|
95746
|
+
function magicBlockFromMarkdown() {
|
|
95747
|
+
return {
|
|
95748
|
+
enter: {
|
|
95749
|
+
magicBlock: enterMagicBlock,
|
|
95750
|
+
},
|
|
95751
|
+
exit: {
|
|
95752
|
+
magicBlockType: exitMagicBlockType,
|
|
95753
|
+
magicBlockData: exitMagicBlockData,
|
|
95754
|
+
magicBlockLineEnding: exitMagicBlockLineEnding,
|
|
95755
|
+
magicBlockMarkerEnd: exitMagicBlockMarkerEnd,
|
|
95756
|
+
magicBlock: exitMagicBlock,
|
|
95757
|
+
},
|
|
95758
|
+
};
|
|
95759
|
+
}
|
|
95760
|
+
/**
|
|
95761
|
+
* Create an extension for `mdast-util-to-markdown` to serialize magic blocks.
|
|
95762
|
+
*
|
|
95763
|
+
* Converts `magicBlock` MDAST nodes back to `[block:TYPE]JSON[/block]` syntax.
|
|
95764
|
+
*
|
|
95765
|
+
* @returns Extension for `mdast-util-to-markdown`
|
|
95766
|
+
*/
|
|
95767
|
+
function magicBlockToMarkdown() {
|
|
95768
|
+
return {
|
|
95769
|
+
handlers: {
|
|
95770
|
+
magicBlock: handleMagicBlock,
|
|
95771
|
+
},
|
|
95772
|
+
};
|
|
95773
|
+
}
|
|
95774
|
+
|
|
95775
|
+
;// ./node_modules/micromark-util-symbol/lib/codes.js
|
|
95776
|
+
/**
|
|
95777
|
+
* Character codes.
|
|
95778
|
+
*
|
|
95779
|
+
* This module is compiled away!
|
|
95780
|
+
*
|
|
95781
|
+
* micromark works based on character codes.
|
|
95782
|
+
* This module contains constants for the ASCII block and the replacement
|
|
95783
|
+
* character.
|
|
95784
|
+
* A couple of them are handled in a special way, such as the line endings
|
|
95785
|
+
* (CR, LF, and CR+LF, commonly known as end-of-line: EOLs), the tab (horizontal
|
|
95786
|
+
* tab) and its expansion based on what column it’s at (virtual space),
|
|
95787
|
+
* and the end-of-file (eof) character.
|
|
95788
|
+
* As values are preprocessed before handling them, the actual characters LF,
|
|
95789
|
+
* CR, HT, and NUL (which is present as the replacement character), are
|
|
95790
|
+
* guaranteed to not exist.
|
|
95791
|
+
*
|
|
95792
|
+
* Unicode basic latin block.
|
|
95793
|
+
*/
|
|
95794
|
+
const codes = /** @type {const} */ ({
|
|
95795
|
+
carriageReturn: -5,
|
|
95796
|
+
lineFeed: -4,
|
|
95797
|
+
carriageReturnLineFeed: -3,
|
|
95798
|
+
horizontalTab: -2,
|
|
95799
|
+
virtualSpace: -1,
|
|
95800
|
+
eof: null,
|
|
95801
|
+
nul: 0,
|
|
95802
|
+
soh: 1,
|
|
95803
|
+
stx: 2,
|
|
95804
|
+
etx: 3,
|
|
95805
|
+
eot: 4,
|
|
95806
|
+
enq: 5,
|
|
95807
|
+
ack: 6,
|
|
95808
|
+
bel: 7,
|
|
95809
|
+
bs: 8,
|
|
95810
|
+
ht: 9, // `\t`
|
|
95811
|
+
lf: 10, // `\n`
|
|
95812
|
+
vt: 11, // `\v`
|
|
95813
|
+
ff: 12, // `\f`
|
|
95814
|
+
cr: 13, // `\r`
|
|
95815
|
+
so: 14,
|
|
95816
|
+
si: 15,
|
|
95817
|
+
dle: 16,
|
|
95818
|
+
dc1: 17,
|
|
95819
|
+
dc2: 18,
|
|
95820
|
+
dc3: 19,
|
|
95821
|
+
dc4: 20,
|
|
95822
|
+
nak: 21,
|
|
95823
|
+
syn: 22,
|
|
95824
|
+
etb: 23,
|
|
95825
|
+
can: 24,
|
|
95826
|
+
em: 25,
|
|
95827
|
+
sub: 26,
|
|
95828
|
+
esc: 27,
|
|
95829
|
+
fs: 28,
|
|
95830
|
+
gs: 29,
|
|
95831
|
+
rs: 30,
|
|
95832
|
+
us: 31,
|
|
95833
|
+
space: 32,
|
|
95834
|
+
exclamationMark: 33, // `!`
|
|
95835
|
+
quotationMark: 34, // `"`
|
|
95836
|
+
numberSign: 35, // `#`
|
|
95837
|
+
dollarSign: 36, // `$`
|
|
95838
|
+
percentSign: 37, // `%`
|
|
95839
|
+
ampersand: 38, // `&`
|
|
95840
|
+
apostrophe: 39, // `'`
|
|
95841
|
+
leftParenthesis: 40, // `(`
|
|
95842
|
+
rightParenthesis: 41, // `)`
|
|
95843
|
+
asterisk: 42, // `*`
|
|
95844
|
+
plusSign: 43, // `+`
|
|
95845
|
+
comma: 44, // `,`
|
|
95846
|
+
dash: 45, // `-`
|
|
95847
|
+
dot: 46, // `.`
|
|
95848
|
+
slash: 47, // `/`
|
|
95849
|
+
digit0: 48, // `0`
|
|
95850
|
+
digit1: 49, // `1`
|
|
95851
|
+
digit2: 50, // `2`
|
|
95852
|
+
digit3: 51, // `3`
|
|
95853
|
+
digit4: 52, // `4`
|
|
95854
|
+
digit5: 53, // `5`
|
|
95855
|
+
digit6: 54, // `6`
|
|
95856
|
+
digit7: 55, // `7`
|
|
95857
|
+
digit8: 56, // `8`
|
|
95858
|
+
digit9: 57, // `9`
|
|
95859
|
+
colon: 58, // `:`
|
|
95860
|
+
semicolon: 59, // `;`
|
|
95861
|
+
lessThan: 60, // `<`
|
|
95862
|
+
equalsTo: 61, // `=`
|
|
95863
|
+
greaterThan: 62, // `>`
|
|
95864
|
+
questionMark: 63, // `?`
|
|
95865
|
+
atSign: 64, // `@`
|
|
95866
|
+
uppercaseA: 65, // `A`
|
|
95867
|
+
uppercaseB: 66, // `B`
|
|
95868
|
+
uppercaseC: 67, // `C`
|
|
95869
|
+
uppercaseD: 68, // `D`
|
|
95870
|
+
uppercaseE: 69, // `E`
|
|
95871
|
+
uppercaseF: 70, // `F`
|
|
95872
|
+
uppercaseG: 71, // `G`
|
|
95873
|
+
uppercaseH: 72, // `H`
|
|
95874
|
+
uppercaseI: 73, // `I`
|
|
95875
|
+
uppercaseJ: 74, // `J`
|
|
95876
|
+
uppercaseK: 75, // `K`
|
|
95877
|
+
uppercaseL: 76, // `L`
|
|
95878
|
+
uppercaseM: 77, // `M`
|
|
95879
|
+
uppercaseN: 78, // `N`
|
|
95880
|
+
uppercaseO: 79, // `O`
|
|
95881
|
+
uppercaseP: 80, // `P`
|
|
95882
|
+
uppercaseQ: 81, // `Q`
|
|
95883
|
+
uppercaseR: 82, // `R`
|
|
95884
|
+
uppercaseS: 83, // `S`
|
|
95885
|
+
uppercaseT: 84, // `T`
|
|
95886
|
+
uppercaseU: 85, // `U`
|
|
95887
|
+
uppercaseV: 86, // `V`
|
|
95888
|
+
uppercaseW: 87, // `W`
|
|
95889
|
+
uppercaseX: 88, // `X`
|
|
95890
|
+
uppercaseY: 89, // `Y`
|
|
95891
|
+
uppercaseZ: 90, // `Z`
|
|
95892
|
+
leftSquareBracket: 91, // `[`
|
|
95893
|
+
backslash: 92, // `\`
|
|
95894
|
+
rightSquareBracket: 93, // `]`
|
|
95895
|
+
caret: 94, // `^`
|
|
95896
|
+
underscore: 95, // `_`
|
|
95897
|
+
graveAccent: 96, // `` ` ``
|
|
95898
|
+
lowercaseA: 97, // `a`
|
|
95899
|
+
lowercaseB: 98, // `b`
|
|
95900
|
+
lowercaseC: 99, // `c`
|
|
95901
|
+
lowercaseD: 100, // `d`
|
|
95902
|
+
lowercaseE: 101, // `e`
|
|
95903
|
+
lowercaseF: 102, // `f`
|
|
95904
|
+
lowercaseG: 103, // `g`
|
|
95905
|
+
lowercaseH: 104, // `h`
|
|
95906
|
+
lowercaseI: 105, // `i`
|
|
95907
|
+
lowercaseJ: 106, // `j`
|
|
95908
|
+
lowercaseK: 107, // `k`
|
|
95909
|
+
lowercaseL: 108, // `l`
|
|
95910
|
+
lowercaseM: 109, // `m`
|
|
95911
|
+
lowercaseN: 110, // `n`
|
|
95912
|
+
lowercaseO: 111, // `o`
|
|
95913
|
+
lowercaseP: 112, // `p`
|
|
95914
|
+
lowercaseQ: 113, // `q`
|
|
95915
|
+
lowercaseR: 114, // `r`
|
|
95916
|
+
lowercaseS: 115, // `s`
|
|
95917
|
+
lowercaseT: 116, // `t`
|
|
95918
|
+
lowercaseU: 117, // `u`
|
|
95919
|
+
lowercaseV: 118, // `v`
|
|
95920
|
+
lowercaseW: 119, // `w`
|
|
95921
|
+
lowercaseX: 120, // `x`
|
|
95922
|
+
lowercaseY: 121, // `y`
|
|
95923
|
+
lowercaseZ: 122, // `z`
|
|
95924
|
+
leftCurlyBrace: 123, // `{`
|
|
95925
|
+
verticalBar: 124, // `|`
|
|
95926
|
+
rightCurlyBrace: 125, // `}`
|
|
95927
|
+
tilde: 126, // `~`
|
|
95928
|
+
del: 127,
|
|
95929
|
+
// Unicode Specials block.
|
|
95930
|
+
byteOrderMarker: 65_279,
|
|
95931
|
+
// Unicode Specials block.
|
|
95932
|
+
replacementCharacter: 65_533 // `�`
|
|
95933
|
+
})
|
|
95934
|
+
|
|
95935
|
+
;// ./lib/micromark/magic-block/syntax.ts
|
|
95936
|
+
|
|
95937
|
+
|
|
95938
|
+
/**
|
|
95939
|
+
* Known magic block types that the tokenizer will recognize.
|
|
95940
|
+
* Unknown types will not be tokenized as magic blocks.
|
|
95941
|
+
*/
|
|
95942
|
+
const KNOWN_BLOCK_TYPES = new Set([
|
|
95943
|
+
'code',
|
|
95944
|
+
'api-header',
|
|
95945
|
+
'image',
|
|
95946
|
+
'callout',
|
|
95947
|
+
'parameters',
|
|
95948
|
+
'table',
|
|
95949
|
+
'embed',
|
|
95950
|
+
'html',
|
|
95951
|
+
'recipe',
|
|
95952
|
+
'tutorial-tile',
|
|
95953
|
+
]);
|
|
95954
|
+
/**
|
|
95955
|
+
* Check if a character is valid for a magic block type identifier.
|
|
95956
|
+
* Types can contain alphanumeric characters and hyphens (e.g., "api-header", "tutorial-tile")
|
|
95957
|
+
*/
|
|
95958
|
+
function isTypeChar(code) {
|
|
95959
|
+
return asciiAlphanumeric(code) || code === codes.dash;
|
|
95960
|
+
}
|
|
95961
|
+
/**
|
|
95962
|
+
* Creates the opening marker state machine: [block:
|
|
95963
|
+
* Returns the first state function to start parsing.
|
|
95964
|
+
*/
|
|
95965
|
+
function createOpeningMarkerParser(effects, nok, onComplete) {
|
|
95966
|
+
const expectB = (code) => {
|
|
95967
|
+
if (code !== codes.lowercaseB)
|
|
95968
|
+
return nok(code);
|
|
95969
|
+
effects.consume(code);
|
|
95970
|
+
return expectL;
|
|
95971
|
+
};
|
|
95972
|
+
const expectL = (code) => {
|
|
95973
|
+
if (code !== codes.lowercaseL)
|
|
95974
|
+
return nok(code);
|
|
95975
|
+
effects.consume(code);
|
|
95976
|
+
return expectO;
|
|
95977
|
+
};
|
|
95978
|
+
const expectO = (code) => {
|
|
95979
|
+
if (code !== codes.lowercaseO)
|
|
95980
|
+
return nok(code);
|
|
95981
|
+
effects.consume(code);
|
|
95982
|
+
return expectC;
|
|
95983
|
+
};
|
|
95984
|
+
const expectC = (code) => {
|
|
95985
|
+
if (code !== codes.lowercaseC)
|
|
95986
|
+
return nok(code);
|
|
95987
|
+
effects.consume(code);
|
|
95988
|
+
return expectK;
|
|
95989
|
+
};
|
|
95990
|
+
const expectK = (code) => {
|
|
95991
|
+
if (code !== codes.lowercaseK)
|
|
95992
|
+
return nok(code);
|
|
95993
|
+
effects.consume(code);
|
|
95994
|
+
return expectColon;
|
|
95995
|
+
};
|
|
95996
|
+
const expectColon = (code) => {
|
|
95997
|
+
if (code !== codes.colon)
|
|
95998
|
+
return nok(code);
|
|
95999
|
+
effects.consume(code);
|
|
96000
|
+
effects.exit('magicBlockMarkerStart');
|
|
96001
|
+
effects.enter('magicBlockType');
|
|
96002
|
+
return onComplete;
|
|
96003
|
+
};
|
|
96004
|
+
return expectB;
|
|
96005
|
+
}
|
|
96006
|
+
/**
|
|
96007
|
+
* Creates the type capture state machine.
|
|
96008
|
+
* Captures type characters until ] and validates against known types.
|
|
96009
|
+
*/
|
|
96010
|
+
function createTypeCaptureParser(effects, nok, onComplete, blockTypeRef) {
|
|
96011
|
+
const captureTypeFirst = (code) => {
|
|
96012
|
+
// Reject empty type name [block:]
|
|
96013
|
+
if (code === codes.rightSquareBracket) {
|
|
96014
|
+
return nok(code);
|
|
96015
|
+
}
|
|
96016
|
+
if (isTypeChar(code)) {
|
|
96017
|
+
blockTypeRef.value += String.fromCharCode(code);
|
|
96018
|
+
effects.consume(code);
|
|
96019
|
+
return captureType;
|
|
96020
|
+
}
|
|
96021
|
+
return nok(code);
|
|
96022
|
+
};
|
|
96023
|
+
const captureType = (code) => {
|
|
96024
|
+
if (code === codes.rightSquareBracket) {
|
|
96025
|
+
if (!KNOWN_BLOCK_TYPES.has(blockTypeRef.value)) {
|
|
96026
|
+
return nok(code);
|
|
96027
|
+
}
|
|
96028
|
+
effects.exit('magicBlockType');
|
|
96029
|
+
effects.enter('magicBlockMarkerTypeEnd');
|
|
96030
|
+
effects.consume(code);
|
|
96031
|
+
effects.exit('magicBlockMarkerTypeEnd');
|
|
96032
|
+
return onComplete;
|
|
96033
|
+
}
|
|
96034
|
+
if (isTypeChar(code)) {
|
|
96035
|
+
blockTypeRef.value += String.fromCharCode(code);
|
|
96036
|
+
effects.consume(code);
|
|
96037
|
+
return captureType;
|
|
96038
|
+
}
|
|
96039
|
+
return nok(code);
|
|
96040
|
+
};
|
|
96041
|
+
return { first: captureTypeFirst, remaining: captureType };
|
|
96042
|
+
}
|
|
96043
|
+
/**
|
|
96044
|
+
* Creates the closing marker state machine: /block]
|
|
96045
|
+
* Handles partial matches by calling onMismatch to fall back to data capture.
|
|
96046
|
+
*/
|
|
96047
|
+
function createClosingMarkerParser(effects, onSuccess, onMismatch, onEof, jsonState) {
|
|
96048
|
+
const handleMismatch = (code) => {
|
|
96049
|
+
if (jsonState && code === codes.quotationMark) {
|
|
96050
|
+
jsonState.inString = true;
|
|
96051
|
+
}
|
|
96052
|
+
return onMismatch(code);
|
|
96053
|
+
};
|
|
96054
|
+
const expectSlash = (code) => {
|
|
96055
|
+
if (code === null)
|
|
96056
|
+
return onEof(code);
|
|
96057
|
+
if (code !== codes.slash)
|
|
96058
|
+
return handleMismatch(code);
|
|
96059
|
+
effects.consume(code);
|
|
96060
|
+
return expectB;
|
|
96061
|
+
};
|
|
96062
|
+
const expectB = (code) => {
|
|
96063
|
+
if (code === null)
|
|
96064
|
+
return onEof(code);
|
|
96065
|
+
if (code !== codes.lowercaseB)
|
|
96066
|
+
return handleMismatch(code);
|
|
96067
|
+
effects.consume(code);
|
|
96068
|
+
return expectL;
|
|
96069
|
+
};
|
|
96070
|
+
const expectL = (code) => {
|
|
96071
|
+
if (code === null)
|
|
96072
|
+
return onEof(code);
|
|
96073
|
+
if (code !== codes.lowercaseL)
|
|
96074
|
+
return handleMismatch(code);
|
|
96075
|
+
effects.consume(code);
|
|
96076
|
+
return expectO;
|
|
96077
|
+
};
|
|
96078
|
+
const expectO = (code) => {
|
|
96079
|
+
if (code === null)
|
|
96080
|
+
return onEof(code);
|
|
96081
|
+
if (code !== codes.lowercaseO)
|
|
96082
|
+
return handleMismatch(code);
|
|
96083
|
+
effects.consume(code);
|
|
96084
|
+
return expectC;
|
|
96085
|
+
};
|
|
96086
|
+
const expectC = (code) => {
|
|
96087
|
+
if (code === null)
|
|
96088
|
+
return onEof(code);
|
|
96089
|
+
if (code !== codes.lowercaseC)
|
|
96090
|
+
return handleMismatch(code);
|
|
96091
|
+
effects.consume(code);
|
|
96092
|
+
return expectK;
|
|
96093
|
+
};
|
|
96094
|
+
const expectK = (code) => {
|
|
96095
|
+
if (code === null)
|
|
96096
|
+
return onEof(code);
|
|
96097
|
+
if (code !== codes.lowercaseK)
|
|
96098
|
+
return handleMismatch(code);
|
|
96099
|
+
effects.consume(code);
|
|
96100
|
+
return expectBracket;
|
|
96101
|
+
};
|
|
96102
|
+
const expectBracket = (code) => {
|
|
96103
|
+
if (code === null)
|
|
96104
|
+
return onEof(code);
|
|
96105
|
+
if (code !== codes.rightSquareBracket)
|
|
96106
|
+
return handleMismatch(code);
|
|
96107
|
+
effects.consume(code);
|
|
96108
|
+
return onSuccess;
|
|
96109
|
+
};
|
|
96110
|
+
return { expectSlash };
|
|
96111
|
+
}
|
|
96112
|
+
/**
|
|
96113
|
+
* Partial construct for checking non-lazy continuation.
|
|
96114
|
+
* This is used by the flow tokenizer to check if we can continue
|
|
96115
|
+
* parsing on the next line.
|
|
96116
|
+
*/
|
|
96117
|
+
const syntax_nonLazyContinuation = {
|
|
96118
|
+
partial: true,
|
|
96119
|
+
tokenize: syntax_tokenizeNonLazyContinuation,
|
|
96120
|
+
};
|
|
96121
|
+
/**
|
|
96122
|
+
* Tokenizer for non-lazy continuation checking.
|
|
96123
|
+
* Returns ok if the next line is non-lazy (can continue), nok if lazy.
|
|
96124
|
+
*/
|
|
96125
|
+
function syntax_tokenizeNonLazyContinuation(effects, ok, nok) {
|
|
96126
|
+
const lineStart = (code) => {
|
|
96127
|
+
// `this` here refers to the micromark parser
|
|
96128
|
+
// since we are just passing functions as references and not actually calling them
|
|
96129
|
+
// micromarks's internal parser will call this automatically with appropriate arguments
|
|
96130
|
+
return this.parser.lazy[this.now().line] ? nok(code) : ok(code);
|
|
96131
|
+
};
|
|
96132
|
+
const start = (code) => {
|
|
96133
|
+
if (code === null) {
|
|
96134
|
+
return nok(code);
|
|
96135
|
+
}
|
|
96136
|
+
if (!markdownLineEnding(code)) {
|
|
96137
|
+
return nok(code);
|
|
96138
|
+
}
|
|
96139
|
+
effects.enter('magicBlockLineEnding');
|
|
96140
|
+
effects.consume(code);
|
|
96141
|
+
effects.exit('magicBlockLineEnding');
|
|
96142
|
+
return lineStart;
|
|
96143
|
+
};
|
|
96144
|
+
return start;
|
|
96145
|
+
}
|
|
96146
|
+
/**
|
|
96147
|
+
* Create a micromark extension for magic block syntax.
|
|
96148
|
+
*
|
|
96149
|
+
* This extension handles both single-line and multiline magic blocks:
|
|
96150
|
+
* - Flow construct (concrete): Handles block-level multiline magic blocks at document level
|
|
96151
|
+
* - Text construct: Handles inline magic blocks in lists, paragraphs, etc.
|
|
96152
|
+
*
|
|
96153
|
+
* The flow construct is marked as "concrete" which prevents it from being
|
|
96154
|
+
* interrupted by container markers (like `>` for blockquotes or `-` for lists).
|
|
96155
|
+
*/
|
|
96156
|
+
function magicBlock() {
|
|
96157
|
+
return {
|
|
96158
|
+
// Flow construct - handles block-level magic blocks at document root
|
|
96159
|
+
// Marked as concrete to prevent interruption by containers
|
|
96160
|
+
flow: {
|
|
96161
|
+
[codes.leftSquareBracket]: {
|
|
96162
|
+
name: 'magicBlock',
|
|
96163
|
+
concrete: true,
|
|
96164
|
+
tokenize: tokenizeMagicBlockFlow,
|
|
96165
|
+
},
|
|
96166
|
+
},
|
|
96167
|
+
// Text construct - handles magic blocks in inline contexts (lists, paragraphs)
|
|
96168
|
+
text: {
|
|
96169
|
+
[codes.leftSquareBracket]: {
|
|
96170
|
+
name: 'magicBlock',
|
|
96171
|
+
tokenize: tokenizeMagicBlockText,
|
|
96172
|
+
},
|
|
96173
|
+
},
|
|
96174
|
+
};
|
|
96175
|
+
}
|
|
96176
|
+
/**
|
|
96177
|
+
* Flow tokenizer for block-level magic blocks (multiline).
|
|
96178
|
+
* Uses the continuation checking pattern from code fences.
|
|
96179
|
+
*/
|
|
96180
|
+
function tokenizeMagicBlockFlow(effects, ok, nok) {
|
|
96181
|
+
// State for tracking JSON content
|
|
96182
|
+
const jsonState = { escapeNext: false, inString: false };
|
|
96183
|
+
const blockTypeRef = { value: '' };
|
|
96184
|
+
let seenOpenBrace = false;
|
|
96185
|
+
// Create shared parsers for opening marker and type capture
|
|
96186
|
+
const typeParser = createTypeCaptureParser(effects, nok, beforeData, blockTypeRef);
|
|
96187
|
+
const openingMarkerParser = createOpeningMarkerParser(effects, nok, typeParser.first);
|
|
96188
|
+
return start;
|
|
96189
|
+
function start(code) {
|
|
96190
|
+
if (code !== codes.leftSquareBracket)
|
|
96191
|
+
return nok(code);
|
|
96192
|
+
effects.enter('magicBlock');
|
|
96193
|
+
effects.enter('magicBlockMarkerStart');
|
|
96194
|
+
effects.consume(code);
|
|
96195
|
+
return openingMarkerParser;
|
|
96196
|
+
}
|
|
96197
|
+
/**
|
|
96198
|
+
* State before data content - handles line endings or starts data capture.
|
|
96199
|
+
*/
|
|
96200
|
+
function beforeData(code) {
|
|
96201
|
+
// EOF - magic block must be closed
|
|
96202
|
+
if (code === null) {
|
|
96203
|
+
effects.exit('magicBlock');
|
|
96204
|
+
return nok(code);
|
|
96205
|
+
}
|
|
96206
|
+
// Line ending before any data - check continuation
|
|
96207
|
+
if (markdownLineEnding(code)) {
|
|
96208
|
+
return effects.check(syntax_nonLazyContinuation, continuationOkBeforeData, after)(code);
|
|
96209
|
+
}
|
|
96210
|
+
// Check for closing marker directly (without entering data)
|
|
96211
|
+
// This handles cases like [block:type]\n{}\n\n[/block] where there are
|
|
96212
|
+
// newlines after the data object
|
|
96213
|
+
if (code === codes.leftSquareBracket) {
|
|
96214
|
+
effects.enter('magicBlockMarkerEnd');
|
|
96215
|
+
effects.consume(code);
|
|
96216
|
+
return expectSlashFromContinuation;
|
|
96217
|
+
}
|
|
96218
|
+
// If we've already seen the opening brace, just continue capturing data
|
|
96219
|
+
if (seenOpenBrace) {
|
|
96220
|
+
effects.enter('magicBlockData');
|
|
96221
|
+
return captureData(code);
|
|
96222
|
+
}
|
|
96223
|
+
// Skip whitespace (spaces/tabs) before the data - stay in beforeData
|
|
96224
|
+
// Don't enter magicBlockData token yet until we confirm there's a '{'
|
|
96225
|
+
if (code === codes.space || code === codes.horizontalTab) {
|
|
96226
|
+
return beforeDataWhitespace(code);
|
|
96227
|
+
}
|
|
96228
|
+
// Data must start with '{' for valid JSON
|
|
96229
|
+
if (code !== codes.leftCurlyBrace) {
|
|
96230
|
+
effects.exit('magicBlock');
|
|
96231
|
+
return nok(code);
|
|
96232
|
+
}
|
|
96233
|
+
// We have '{' - enter data token and start capturing
|
|
96234
|
+
seenOpenBrace = true;
|
|
96235
|
+
effects.enter('magicBlockData');
|
|
96236
|
+
return captureData(code);
|
|
96237
|
+
}
|
|
96238
|
+
/**
|
|
96239
|
+
* Consume whitespace before the data without creating a token.
|
|
96240
|
+
* Uses a temporary token to satisfy micromark's requirement.
|
|
96241
|
+
*/
|
|
96242
|
+
function beforeDataWhitespace(code) {
|
|
96243
|
+
if (code === null) {
|
|
96244
|
+
effects.exit('magicBlock');
|
|
96245
|
+
return nok(code);
|
|
96246
|
+
}
|
|
96247
|
+
if (markdownLineEnding(code)) {
|
|
96248
|
+
return effects.check(syntax_nonLazyContinuation, continuationOkBeforeData, after)(code);
|
|
96249
|
+
}
|
|
96250
|
+
if (code === codes.space || code === codes.horizontalTab) {
|
|
96251
|
+
// We need to consume this but can't without a token - use magicBlockData
|
|
96252
|
+
// and track that we haven't seen '{' yet
|
|
96253
|
+
effects.enter('magicBlockData');
|
|
96254
|
+
effects.consume(code);
|
|
96255
|
+
return beforeDataWhitespaceContinue;
|
|
96256
|
+
}
|
|
96257
|
+
if (code === codes.leftCurlyBrace) {
|
|
96258
|
+
seenOpenBrace = true;
|
|
96259
|
+
effects.enter('magicBlockData');
|
|
96260
|
+
return captureData(code);
|
|
96261
|
+
}
|
|
96262
|
+
effects.exit('magicBlock');
|
|
96263
|
+
return nok(code);
|
|
96264
|
+
}
|
|
96265
|
+
/**
|
|
96266
|
+
* Continue consuming whitespace or validate the opening brace.
|
|
96267
|
+
*/
|
|
96268
|
+
function beforeDataWhitespaceContinue(code) {
|
|
96269
|
+
if (code === null) {
|
|
96270
|
+
effects.exit('magicBlockData');
|
|
96271
|
+
effects.exit('magicBlock');
|
|
96272
|
+
return nok(code);
|
|
96273
|
+
}
|
|
96274
|
+
if (markdownLineEnding(code)) {
|
|
96275
|
+
effects.exit('magicBlockData');
|
|
96276
|
+
return effects.check(syntax_nonLazyContinuation, continuationOk, after)(code);
|
|
96277
|
+
}
|
|
96278
|
+
if (code === codes.space || code === codes.horizontalTab) {
|
|
96279
|
+
effects.consume(code);
|
|
96280
|
+
return beforeDataWhitespaceContinue;
|
|
96281
|
+
}
|
|
96282
|
+
if (code === codes.leftCurlyBrace) {
|
|
96283
|
+
seenOpenBrace = true;
|
|
96284
|
+
return captureData(code);
|
|
96285
|
+
}
|
|
96286
|
+
effects.exit('magicBlockData');
|
|
96287
|
+
effects.exit('magicBlock');
|
|
96288
|
+
return nok(code);
|
|
96289
|
+
}
|
|
96290
|
+
/**
|
|
96291
|
+
* Continuation OK before we've entered data token.
|
|
96292
|
+
*/
|
|
96293
|
+
function continuationOkBeforeData(code) {
|
|
96294
|
+
effects.enter('magicBlockLineEnding');
|
|
96295
|
+
effects.consume(code);
|
|
96296
|
+
effects.exit('magicBlockLineEnding');
|
|
96297
|
+
return beforeData; // Stay in beforeData state - don't enter magicBlockData yet
|
|
96298
|
+
}
|
|
96299
|
+
function captureData(code) {
|
|
96300
|
+
// EOF - magic block must be closed
|
|
96301
|
+
if (code === null) {
|
|
96302
|
+
effects.exit('magicBlockData');
|
|
96303
|
+
effects.exit('magicBlock');
|
|
96304
|
+
return nok(code);
|
|
96305
|
+
}
|
|
96306
|
+
// At line ending, check if we can continue on the next line
|
|
96307
|
+
if (markdownLineEnding(code)) {
|
|
96308
|
+
effects.exit('magicBlockData');
|
|
96309
|
+
return effects.check(syntax_nonLazyContinuation, continuationOk, after)(code);
|
|
96310
|
+
}
|
|
96311
|
+
if (jsonState.escapeNext) {
|
|
96312
|
+
jsonState.escapeNext = false;
|
|
96313
|
+
effects.consume(code);
|
|
96314
|
+
return captureData;
|
|
96315
|
+
}
|
|
96316
|
+
if (jsonState.inString) {
|
|
96317
|
+
if (code === codes.backslash) {
|
|
96318
|
+
jsonState.escapeNext = true;
|
|
96319
|
+
effects.consume(code);
|
|
96320
|
+
return captureData;
|
|
96321
|
+
}
|
|
96322
|
+
if (code === codes.quotationMark) {
|
|
96323
|
+
jsonState.inString = false;
|
|
96324
|
+
}
|
|
96325
|
+
effects.consume(code);
|
|
96326
|
+
return captureData;
|
|
96327
|
+
}
|
|
96328
|
+
if (code === codes.quotationMark) {
|
|
96329
|
+
jsonState.inString = true;
|
|
96330
|
+
effects.consume(code);
|
|
96331
|
+
return captureData;
|
|
96332
|
+
}
|
|
96333
|
+
if (code === codes.leftSquareBracket) {
|
|
96334
|
+
effects.exit('magicBlockData');
|
|
96335
|
+
effects.enter('magicBlockMarkerEnd');
|
|
96336
|
+
effects.consume(code);
|
|
96337
|
+
return expectSlash;
|
|
96338
|
+
}
|
|
96339
|
+
effects.consume(code);
|
|
96340
|
+
return captureData;
|
|
96341
|
+
}
|
|
96342
|
+
/**
|
|
96343
|
+
* Called when non-lazy continuation check passes - we can continue parsing.
|
|
96344
|
+
*/
|
|
96345
|
+
function continuationOk(code) {
|
|
96346
|
+
// Consume the line ending
|
|
96347
|
+
effects.enter('magicBlockLineEnding');
|
|
96348
|
+
effects.consume(code);
|
|
96349
|
+
effects.exit('magicBlockLineEnding');
|
|
96350
|
+
return continuationStart;
|
|
96351
|
+
}
|
|
96352
|
+
/**
|
|
96353
|
+
* Start of continuation line - check for more line endings, closing marker, or start capturing data.
|
|
96354
|
+
*/
|
|
96355
|
+
function continuationStart(code) {
|
|
96356
|
+
// Handle consecutive line endings
|
|
96357
|
+
if (code === null) {
|
|
96358
|
+
effects.exit('magicBlock');
|
|
96359
|
+
return nok(code);
|
|
96360
|
+
}
|
|
96361
|
+
if (markdownLineEnding(code)) {
|
|
96362
|
+
return effects.check(syntax_nonLazyContinuation, continuationOkBeforeData, after)(code);
|
|
96363
|
+
}
|
|
96364
|
+
// Check if this is the start of the closing marker [/block]
|
|
96365
|
+
// If so, handle it directly without entering magicBlockData
|
|
96366
|
+
if (code === codes.leftSquareBracket) {
|
|
96367
|
+
effects.enter('magicBlockMarkerEnd');
|
|
96368
|
+
effects.consume(code);
|
|
96369
|
+
return expectSlashFromContinuation;
|
|
96370
|
+
}
|
|
96371
|
+
effects.enter('magicBlockData');
|
|
96372
|
+
return captureData(code);
|
|
96373
|
+
}
|
|
96374
|
+
/**
|
|
96375
|
+
* Check for closing marker slash when coming from continuation.
|
|
96376
|
+
* If not a closing marker, create an empty data token and continue.
|
|
96377
|
+
*/
|
|
96378
|
+
function expectSlashFromContinuation(code) {
|
|
96379
|
+
if (code === null) {
|
|
96380
|
+
effects.exit('magicBlockMarkerEnd');
|
|
96381
|
+
effects.exit('magicBlock');
|
|
96382
|
+
return nok(code);
|
|
96383
|
+
}
|
|
96384
|
+
if (markdownLineEnding(code)) {
|
|
96385
|
+
effects.exit('magicBlockMarkerEnd');
|
|
96386
|
+
return effects.check(syntax_nonLazyContinuation, continuationOkBeforeData, after)(code);
|
|
96387
|
+
}
|
|
96388
|
+
if (code === codes.slash) {
|
|
96389
|
+
effects.consume(code);
|
|
96390
|
+
return expectClosingB;
|
|
96391
|
+
}
|
|
96392
|
+
// Not a closing marker - this is data content
|
|
96393
|
+
// Exit marker and enter data
|
|
96394
|
+
effects.exit('magicBlockMarkerEnd');
|
|
96395
|
+
effects.enter('magicBlockData');
|
|
96396
|
+
// The [ was consumed by the marker, so we need to conceptually "have it" in our data
|
|
96397
|
+
// But since we already consumed it into the marker, we need a different approach
|
|
96398
|
+
// Re-classify: consume this character as data and continue
|
|
96399
|
+
if (code === codes.quotationMark)
|
|
96400
|
+
jsonState.inString = true;
|
|
96401
|
+
effects.consume(code);
|
|
96402
|
+
return captureData;
|
|
96403
|
+
}
|
|
96404
|
+
function expectSlash(code) {
|
|
96405
|
+
if (code === null) {
|
|
96406
|
+
effects.exit('magicBlockMarkerEnd');
|
|
96407
|
+
effects.exit('magicBlock');
|
|
96408
|
+
return nok(code);
|
|
96409
|
+
}
|
|
96410
|
+
// At line ending during marker parsing
|
|
96411
|
+
if (markdownLineEnding(code)) {
|
|
96412
|
+
effects.exit('magicBlockMarkerEnd');
|
|
96413
|
+
return effects.check(syntax_nonLazyContinuation, continuationOk, after)(code);
|
|
96414
|
+
}
|
|
96415
|
+
if (code !== codes.slash) {
|
|
96416
|
+
effects.exit('magicBlockMarkerEnd');
|
|
96417
|
+
effects.enter('magicBlockData');
|
|
96418
|
+
if (code === codes.quotationMark)
|
|
96419
|
+
jsonState.inString = true;
|
|
96420
|
+
effects.consume(code);
|
|
96421
|
+
return captureData;
|
|
96422
|
+
}
|
|
96423
|
+
effects.consume(code);
|
|
96424
|
+
return expectClosingB;
|
|
96425
|
+
}
|
|
96426
|
+
function expectClosingB(code) {
|
|
96427
|
+
if (code === null) {
|
|
96428
|
+
effects.exit('magicBlockMarkerEnd');
|
|
96429
|
+
effects.exit('magicBlock');
|
|
96430
|
+
return nok(code);
|
|
96431
|
+
}
|
|
96432
|
+
if (code !== codes.lowercaseB) {
|
|
96433
|
+
effects.exit('magicBlockMarkerEnd');
|
|
96434
|
+
effects.enter('magicBlockData');
|
|
96435
|
+
if (code === codes.quotationMark)
|
|
96436
|
+
jsonState.inString = true;
|
|
96437
|
+
effects.consume(code);
|
|
96438
|
+
return captureData;
|
|
96439
|
+
}
|
|
96440
|
+
effects.consume(code);
|
|
96441
|
+
return expectClosingL;
|
|
96442
|
+
}
|
|
96443
|
+
function expectClosingL(code) {
|
|
96444
|
+
if (code === null) {
|
|
96445
|
+
effects.exit('magicBlockMarkerEnd');
|
|
96446
|
+
effects.exit('magicBlock');
|
|
96447
|
+
return nok(code);
|
|
96448
|
+
}
|
|
96449
|
+
if (code !== codes.lowercaseL) {
|
|
96450
|
+
effects.exit('magicBlockMarkerEnd');
|
|
96451
|
+
effects.enter('magicBlockData');
|
|
96452
|
+
if (code === codes.quotationMark)
|
|
96453
|
+
jsonState.inString = true;
|
|
96454
|
+
effects.consume(code);
|
|
96455
|
+
return captureData;
|
|
96456
|
+
}
|
|
96457
|
+
effects.consume(code);
|
|
96458
|
+
return expectClosingO;
|
|
96459
|
+
}
|
|
96460
|
+
function expectClosingO(code) {
|
|
96461
|
+
if (code === null) {
|
|
96462
|
+
effects.exit('magicBlockMarkerEnd');
|
|
96463
|
+
effects.exit('magicBlock');
|
|
96464
|
+
return nok(code);
|
|
96465
|
+
}
|
|
96466
|
+
if (code !== codes.lowercaseO) {
|
|
96467
|
+
effects.exit('magicBlockMarkerEnd');
|
|
96468
|
+
effects.enter('magicBlockData');
|
|
96469
|
+
if (code === codes.quotationMark)
|
|
96470
|
+
jsonState.inString = true;
|
|
96471
|
+
effects.consume(code);
|
|
96472
|
+
return captureData;
|
|
96473
|
+
}
|
|
96474
|
+
effects.consume(code);
|
|
96475
|
+
return expectClosingC;
|
|
96476
|
+
}
|
|
96477
|
+
function expectClosingC(code) {
|
|
96478
|
+
if (code === null) {
|
|
96479
|
+
effects.exit('magicBlockMarkerEnd');
|
|
96480
|
+
effects.exit('magicBlock');
|
|
96481
|
+
return nok(code);
|
|
96482
|
+
}
|
|
96483
|
+
if (code !== codes.lowercaseC) {
|
|
96484
|
+
effects.exit('magicBlockMarkerEnd');
|
|
96485
|
+
effects.enter('magicBlockData');
|
|
96486
|
+
if (code === codes.quotationMark)
|
|
96487
|
+
jsonState.inString = true;
|
|
96488
|
+
effects.consume(code);
|
|
96489
|
+
return captureData;
|
|
96490
|
+
}
|
|
96491
|
+
effects.consume(code);
|
|
96492
|
+
return expectClosingK;
|
|
96493
|
+
}
|
|
96494
|
+
function expectClosingK(code) {
|
|
96495
|
+
if (code === null) {
|
|
96496
|
+
effects.exit('magicBlockMarkerEnd');
|
|
96497
|
+
effects.exit('magicBlock');
|
|
96498
|
+
return nok(code);
|
|
96499
|
+
}
|
|
96500
|
+
if (code !== codes.lowercaseK) {
|
|
96501
|
+
effects.exit('magicBlockMarkerEnd');
|
|
96502
|
+
effects.enter('magicBlockData');
|
|
96503
|
+
if (code === codes.quotationMark)
|
|
96504
|
+
jsonState.inString = true;
|
|
96505
|
+
effects.consume(code);
|
|
96506
|
+
return captureData;
|
|
96507
|
+
}
|
|
96508
|
+
effects.consume(code);
|
|
96509
|
+
return expectClosingBracket;
|
|
96510
|
+
}
|
|
96511
|
+
function expectClosingBracket(code) {
|
|
96512
|
+
if (code === null) {
|
|
96513
|
+
effects.exit('magicBlockMarkerEnd');
|
|
96514
|
+
effects.exit('magicBlock');
|
|
96515
|
+
return nok(code);
|
|
96516
|
+
}
|
|
96517
|
+
if (code !== codes.rightSquareBracket) {
|
|
96518
|
+
effects.exit('magicBlockMarkerEnd');
|
|
96519
|
+
effects.enter('magicBlockData');
|
|
96520
|
+
if (code === codes.quotationMark)
|
|
96521
|
+
jsonState.inString = true;
|
|
96522
|
+
effects.consume(code);
|
|
96523
|
+
return captureData;
|
|
96524
|
+
}
|
|
96525
|
+
effects.consume(code);
|
|
96526
|
+
effects.exit('magicBlockMarkerEnd');
|
|
96527
|
+
// Check for trailing whitespace on the same line
|
|
96528
|
+
return consumeTrailing;
|
|
96529
|
+
}
|
|
96530
|
+
/**
|
|
96531
|
+
* Consume trailing whitespace (spaces/tabs) on the same line after [/block].
|
|
96532
|
+
* For concrete flow constructs, we must end at eol/eof or fail.
|
|
96533
|
+
* Trailing whitespace is consumed; other content causes nok (text tokenizer handles it).
|
|
96534
|
+
*/
|
|
96535
|
+
function consumeTrailing(code) {
|
|
96536
|
+
// End of file - done
|
|
96537
|
+
if (code === null) {
|
|
96538
|
+
effects.exit('magicBlock');
|
|
96539
|
+
return ok(code);
|
|
96540
|
+
}
|
|
96541
|
+
// Line ending - done
|
|
96542
|
+
if (markdownLineEnding(code)) {
|
|
96543
|
+
effects.exit('magicBlock');
|
|
96544
|
+
return ok(code);
|
|
96545
|
+
}
|
|
96546
|
+
// Space or tab - consume as trailing whitespace
|
|
96547
|
+
if (code === codes.space || code === codes.horizontalTab) {
|
|
96548
|
+
effects.enter('magicBlockTrailing');
|
|
96549
|
+
effects.consume(code);
|
|
96550
|
+
return consumeTrailingContinue;
|
|
96551
|
+
}
|
|
96552
|
+
// Any other character - fail flow tokenizer, let text tokenizer handle it
|
|
96553
|
+
effects.exit('magicBlock');
|
|
96554
|
+
return nok(code);
|
|
96555
|
+
}
|
|
96556
|
+
/**
|
|
96557
|
+
* Continue consuming trailing whitespace.
|
|
96558
|
+
*/
|
|
96559
|
+
function consumeTrailingContinue(code) {
|
|
96560
|
+
// End of file - done
|
|
96561
|
+
if (code === null) {
|
|
96562
|
+
effects.exit('magicBlockTrailing');
|
|
96563
|
+
effects.exit('magicBlock');
|
|
96564
|
+
return ok(code);
|
|
96565
|
+
}
|
|
96566
|
+
// Line ending - done
|
|
96567
|
+
if (markdownLineEnding(code)) {
|
|
96568
|
+
effects.exit('magicBlockTrailing');
|
|
96569
|
+
effects.exit('magicBlock');
|
|
96570
|
+
return ok(code);
|
|
96571
|
+
}
|
|
96572
|
+
// More space or tab - keep consuming
|
|
96573
|
+
if (code === codes.space || code === codes.horizontalTab) {
|
|
96574
|
+
effects.consume(code);
|
|
96575
|
+
return consumeTrailingContinue;
|
|
96576
|
+
}
|
|
96577
|
+
// Non-whitespace after whitespace - fail flow tokenizer
|
|
96578
|
+
effects.exit('magicBlockTrailing');
|
|
96579
|
+
effects.exit('magicBlock');
|
|
96580
|
+
return nok(code);
|
|
96581
|
+
}
|
|
96582
|
+
/**
|
|
96583
|
+
* Called when we can't continue (lazy line or EOF) - exit the construct.
|
|
96584
|
+
*/
|
|
96585
|
+
function after(code) {
|
|
96586
|
+
effects.exit('magicBlock');
|
|
96587
|
+
return nok(code);
|
|
96588
|
+
}
|
|
96589
|
+
}
|
|
96590
|
+
/**
|
|
96591
|
+
* Text tokenizer for single-line magic blocks only.
|
|
96592
|
+
* Used in inline contexts like list items and paragraphs.
|
|
96593
|
+
* Multiline blocks are handled by the flow tokenizer.
|
|
96594
|
+
*/
|
|
96595
|
+
function tokenizeMagicBlockText(effects, ok, nok) {
|
|
96596
|
+
// State for tracking JSON content
|
|
96597
|
+
const jsonState = { escapeNext: false, inString: false };
|
|
96598
|
+
const blockTypeRef = { value: '' };
|
|
96599
|
+
let seenOpenBrace = false;
|
|
96600
|
+
// Create shared parsers for opening marker and type capture
|
|
96601
|
+
const typeParser = createTypeCaptureParser(effects, nok, beforeData, blockTypeRef);
|
|
96602
|
+
const openingMarkerParser = createOpeningMarkerParser(effects, nok, typeParser.first);
|
|
96603
|
+
// Success handler for closing marker - exits tokens and returns ok
|
|
96604
|
+
const closingSuccess = (code) => {
|
|
96605
|
+
effects.exit('magicBlockMarkerEnd');
|
|
96606
|
+
effects.exit('magicBlock');
|
|
96607
|
+
return ok(code);
|
|
96608
|
+
};
|
|
96609
|
+
// Mismatch handler - falls back to data capture
|
|
96610
|
+
const closingMismatch = (code) => {
|
|
96611
|
+
effects.exit('magicBlockMarkerEnd');
|
|
96612
|
+
effects.enter('magicBlockData');
|
|
96613
|
+
if (code === codes.quotationMark)
|
|
96614
|
+
jsonState.inString = true;
|
|
96615
|
+
effects.consume(code);
|
|
96616
|
+
return captureData;
|
|
96617
|
+
};
|
|
96618
|
+
// EOF handler
|
|
96619
|
+
const closingEof = (_code) => nok(_code);
|
|
96620
|
+
// Create closing marker parsers
|
|
96621
|
+
const closingFromBeforeData = createClosingMarkerParser(effects, closingSuccess, closingMismatch, closingEof, jsonState);
|
|
96622
|
+
const closingFromData = createClosingMarkerParser(effects, closingSuccess, closingMismatch, closingEof, jsonState);
|
|
96623
|
+
return start;
|
|
96624
|
+
function start(code) {
|
|
96625
|
+
if (code !== codes.leftSquareBracket)
|
|
96626
|
+
return nok(code);
|
|
96627
|
+
effects.enter('magicBlock');
|
|
96628
|
+
effects.enter('magicBlockMarkerStart');
|
|
96629
|
+
effects.consume(code);
|
|
96630
|
+
return openingMarkerParser;
|
|
96631
|
+
}
|
|
96632
|
+
/**
|
|
96633
|
+
* State before data content - handles line endings before entering data token.
|
|
96634
|
+
* Whitespace before '{' is allowed.
|
|
96635
|
+
*/
|
|
96636
|
+
function beforeData(code) {
|
|
96637
|
+
// Fail on EOF - magic block must be closed
|
|
96638
|
+
if (code === null) {
|
|
96639
|
+
return nok(code);
|
|
96640
|
+
}
|
|
96641
|
+
// Handle line endings before any data - consume them without entering data token
|
|
96642
|
+
if (markdownLineEnding(code)) {
|
|
96643
|
+
effects.enter('magicBlockLineEnding');
|
|
96644
|
+
effects.consume(code);
|
|
96645
|
+
effects.exit('magicBlockLineEnding');
|
|
96646
|
+
return beforeData;
|
|
96647
|
+
}
|
|
96648
|
+
// Check for closing marker directly (without entering data)
|
|
96649
|
+
if (code === codes.leftSquareBracket) {
|
|
96650
|
+
effects.enter('magicBlockMarkerEnd');
|
|
96651
|
+
effects.consume(code);
|
|
96652
|
+
return closingFromBeforeData.expectSlash;
|
|
96653
|
+
}
|
|
96654
|
+
// If we've already seen the opening brace, just continue capturing data
|
|
96655
|
+
if (seenOpenBrace) {
|
|
96656
|
+
effects.enter('magicBlockData');
|
|
96657
|
+
return captureData(code);
|
|
96658
|
+
}
|
|
96659
|
+
// Skip whitespace (spaces/tabs) before the data
|
|
96660
|
+
if (code === codes.space || code === codes.horizontalTab) {
|
|
96661
|
+
return beforeDataWhitespace(code);
|
|
96662
|
+
}
|
|
96663
|
+
// Data must start with '{' for valid JSON
|
|
96664
|
+
if (code !== codes.leftCurlyBrace) {
|
|
96665
|
+
return nok(code);
|
|
96666
|
+
}
|
|
96667
|
+
// We have '{' - enter data token and start capturing
|
|
96668
|
+
seenOpenBrace = true;
|
|
96669
|
+
effects.enter('magicBlockData');
|
|
96670
|
+
return captureData(code);
|
|
96671
|
+
}
|
|
96672
|
+
/**
|
|
96673
|
+
* Consume whitespace before the data.
|
|
96674
|
+
*/
|
|
96675
|
+
function beforeDataWhitespace(code) {
|
|
96676
|
+
if (code === null) {
|
|
96677
|
+
return nok(code);
|
|
96678
|
+
}
|
|
96679
|
+
if (markdownLineEnding(code)) {
|
|
96680
|
+
effects.enter('magicBlockLineEnding');
|
|
96681
|
+
effects.consume(code);
|
|
96682
|
+
effects.exit('magicBlockLineEnding');
|
|
96683
|
+
return beforeData;
|
|
96684
|
+
}
|
|
96685
|
+
if (code === codes.space || code === codes.horizontalTab) {
|
|
96686
|
+
effects.enter('magicBlockData');
|
|
96687
|
+
effects.consume(code);
|
|
96688
|
+
return beforeDataWhitespaceContinue;
|
|
96689
|
+
}
|
|
96690
|
+
if (code === codes.leftCurlyBrace) {
|
|
96691
|
+
seenOpenBrace = true;
|
|
96692
|
+
effects.enter('magicBlockData');
|
|
96693
|
+
return captureData(code);
|
|
96694
|
+
}
|
|
96695
|
+
return nok(code);
|
|
96696
|
+
}
|
|
96697
|
+
/**
|
|
96698
|
+
* Continue consuming whitespace or validate the opening brace.
|
|
96699
|
+
*/
|
|
96700
|
+
function beforeDataWhitespaceContinue(code) {
|
|
96701
|
+
if (code === null) {
|
|
96702
|
+
effects.exit('magicBlockData');
|
|
96703
|
+
return nok(code);
|
|
96704
|
+
}
|
|
96705
|
+
if (markdownLineEnding(code)) {
|
|
96706
|
+
effects.exit('magicBlockData');
|
|
96707
|
+
effects.enter('magicBlockLineEnding');
|
|
96708
|
+
effects.consume(code);
|
|
96709
|
+
effects.exit('magicBlockLineEnding');
|
|
96710
|
+
return beforeData;
|
|
96711
|
+
}
|
|
96712
|
+
if (code === codes.space || code === codes.horizontalTab) {
|
|
96713
|
+
effects.consume(code);
|
|
96714
|
+
return beforeDataWhitespaceContinue;
|
|
96715
|
+
}
|
|
96716
|
+
if (code === codes.leftCurlyBrace) {
|
|
96717
|
+
seenOpenBrace = true;
|
|
96718
|
+
return captureData(code);
|
|
96719
|
+
}
|
|
96720
|
+
effects.exit('magicBlockData');
|
|
96721
|
+
return nok(code);
|
|
96722
|
+
}
|
|
96723
|
+
function captureData(code) {
|
|
96724
|
+
// Fail on EOF - magic block must be closed
|
|
96725
|
+
if (code === null) {
|
|
96726
|
+
effects.exit('magicBlockData');
|
|
96727
|
+
return nok(code);
|
|
96728
|
+
}
|
|
96729
|
+
// Handle multiline magic blocks within text/paragraphs
|
|
96730
|
+
// Exit data, consume line ending with proper token, then re-enter data
|
|
96731
|
+
if (markdownLineEnding(code)) {
|
|
96732
|
+
effects.exit('magicBlockData');
|
|
96733
|
+
effects.enter('magicBlockLineEnding');
|
|
96734
|
+
effects.consume(code);
|
|
96735
|
+
effects.exit('magicBlockLineEnding');
|
|
96736
|
+
return beforeData; // Go back to beforeData to handle potential empty lines
|
|
96737
|
+
}
|
|
96738
|
+
if (jsonState.escapeNext) {
|
|
96739
|
+
jsonState.escapeNext = false;
|
|
96740
|
+
effects.consume(code);
|
|
96741
|
+
return captureData;
|
|
96742
|
+
}
|
|
96743
|
+
if (jsonState.inString) {
|
|
96744
|
+
if (code === codes.backslash) {
|
|
96745
|
+
jsonState.escapeNext = true;
|
|
96746
|
+
effects.consume(code);
|
|
96747
|
+
return captureData;
|
|
96748
|
+
}
|
|
96749
|
+
if (code === codes.quotationMark) {
|
|
96750
|
+
jsonState.inString = false;
|
|
96751
|
+
}
|
|
96752
|
+
effects.consume(code);
|
|
96753
|
+
return captureData;
|
|
96754
|
+
}
|
|
96755
|
+
if (code === codes.quotationMark) {
|
|
96756
|
+
jsonState.inString = true;
|
|
96757
|
+
effects.consume(code);
|
|
96758
|
+
return captureData;
|
|
96759
|
+
}
|
|
96760
|
+
if (code === codes.leftSquareBracket) {
|
|
96761
|
+
effects.exit('magicBlockData');
|
|
96762
|
+
effects.enter('magicBlockMarkerEnd');
|
|
96763
|
+
effects.consume(code);
|
|
96764
|
+
return closingFromData.expectSlash;
|
|
96765
|
+
}
|
|
96766
|
+
effects.consume(code);
|
|
96767
|
+
return captureData;
|
|
96768
|
+
}
|
|
96769
|
+
}
|
|
96770
|
+
/* harmony default export */ const syntax = ((/* unused pure expression or super */ null && (magicBlock)));
|
|
96771
|
+
|
|
96772
|
+
;// ./lib/micromark/magic-block/index.ts
|
|
96773
|
+
/**
|
|
96774
|
+
* Micromark extension for magic block syntax.
|
|
96775
|
+
*
|
|
96776
|
+
* Usage:
|
|
96777
|
+
* ```ts
|
|
96778
|
+
* import { magicBlock } from './lib/micromark/magic-block';
|
|
96779
|
+
*
|
|
96780
|
+
* const processor = unified()
|
|
96781
|
+
* .data('micromarkExtensions', [magicBlock()])
|
|
96782
|
+
* ```
|
|
96783
|
+
*/
|
|
96784
|
+
|
|
95098
96785
|
|
|
95099
96786
|
;// ./lib/utils/mdxish/mdxish-load-components.ts
|
|
95100
96787
|
|
|
@@ -95161,46 +96848,56 @@ function loadComponents() {
|
|
|
95161
96848
|
|
|
95162
96849
|
|
|
95163
96850
|
|
|
96851
|
+
|
|
96852
|
+
|
|
95164
96853
|
|
|
95165
96854
|
|
|
95166
96855
|
|
|
95167
96856
|
const defaultTransformers = [callouts, code_tabs, gemoji_, transform_embeds];
|
|
95168
96857
|
function mdxishAstProcessor(mdContent, opts = {}) {
|
|
95169
|
-
const { components: userComponents = {}, jsxContext = {}, useTailwind } = opts;
|
|
96858
|
+
const { components: userComponents = {}, jsxContext = {}, newEditorTypes = false, safeMode = false, useTailwind, } = opts;
|
|
95170
96859
|
const components = {
|
|
95171
96860
|
...loadComponents(),
|
|
95172
96861
|
...userComponents,
|
|
95173
96862
|
};
|
|
96863
|
+
// Build set of known component names for snake_case filtering
|
|
96864
|
+
const knownComponents = new Set(Object.keys(components));
|
|
95174
96865
|
// Preprocessing pipeline: Transform content to be parser-ready
|
|
95175
|
-
// Step 1:
|
|
95176
|
-
const
|
|
95177
|
-
// Step 2:
|
|
95178
|
-
const
|
|
95179
|
-
|
|
95180
|
-
|
|
95181
|
-
// Step
|
|
95182
|
-
|
|
95183
|
-
const { content: parserReadyContent, mapping: snakeCaseMapping } = processSnakeCaseComponent(contentAfterJSXEvaluation);
|
|
96866
|
+
// Step 1: Normalize malformed table separator syntax (e.g., `|: ---` → `| :---`)
|
|
96867
|
+
const contentAfterTableNormalization = normalizeTableSeparator(mdContent);
|
|
96868
|
+
// Step 2: Evaluate JSX expressions in attributes
|
|
96869
|
+
const contentAfterJSXEvaluation = safeMode
|
|
96870
|
+
? contentAfterTableNormalization
|
|
96871
|
+
: preprocessJSXExpressions(contentAfterTableNormalization, jsxContext);
|
|
96872
|
+
// Step 3: Replace snake_case component names with parser-safe placeholders
|
|
96873
|
+
const { content: parserReadyContent, mapping: snakeCaseMapping } = processSnakeCaseComponent(contentAfterJSXEvaluation, { knownComponents });
|
|
95184
96874
|
// Create string map for tailwind transformer
|
|
95185
96875
|
const tempComponentsMap = Object.entries(components).reduce((acc, [key, value]) => {
|
|
95186
96876
|
acc[key] = String(value);
|
|
95187
96877
|
return acc;
|
|
95188
96878
|
}, {});
|
|
96879
|
+
// Get mdxExpression extension and remove its flow construct to prevent
|
|
96880
|
+
// `{...}` from interrupting paragraphs (which breaks multiline magic blocks)
|
|
96881
|
+
const mdxExprExt = mdxExpression({ allowEmpty: true });
|
|
96882
|
+
const mdxExprTextOnly = {
|
|
96883
|
+
text: mdxExprExt.text,
|
|
96884
|
+
};
|
|
95189
96885
|
const processor = unified()
|
|
95190
|
-
.data('micromarkExtensions', [
|
|
95191
|
-
.data('fromMarkdownExtensions', [mdxExpressionFromMarkdown()])
|
|
96886
|
+
.data('micromarkExtensions', safeMode ? [magicBlock()] : [magicBlock(), mdxExprTextOnly])
|
|
96887
|
+
.data('fromMarkdownExtensions', safeMode ? [magicBlockFromMarkdown()] : [magicBlockFromMarkdown(), mdxExpressionFromMarkdown()])
|
|
95192
96888
|
.use(remarkParse)
|
|
95193
96889
|
.use(remarkFrontmatter)
|
|
95194
96890
|
.use(normalize_malformed_md_syntax)
|
|
95195
|
-
.use(
|
|
96891
|
+
.use(magic_block_transformer)
|
|
95196
96892
|
.use(transform_images, { isMdxish: true })
|
|
95197
96893
|
.use(defaultTransformers)
|
|
95198
96894
|
.use(mdxish_component_blocks)
|
|
95199
96895
|
.use(restore_snake_case_component_name, { mapping: snakeCaseMapping })
|
|
95200
96896
|
.use(mdxish_tables)
|
|
95201
96897
|
.use(mdxish_html_blocks)
|
|
95202
|
-
.use(
|
|
95203
|
-
.use(
|
|
96898
|
+
.use(newEditorTypes ? mdxish_jsx_to_mdast : undefined) // Convert JSX elements to MDAST types
|
|
96899
|
+
.use(safeMode ? undefined : evaluate_expressions, { context: jsxContext }) // Evaluate MDX expressions using jsxContext
|
|
96900
|
+
.use(variables_text) // Parse {user.*} patterns from text
|
|
95204
96901
|
.use(useTailwind ? transform_tailwind : undefined, { components: tempComponentsMap })
|
|
95205
96902
|
.use(remarkGfm);
|
|
95206
96903
|
return {
|
|
@@ -95217,10 +96914,14 @@ function mdxishAstProcessor(mdContent, opts = {}) {
|
|
|
95217
96914
|
* Converts an Mdast to a Markdown string.
|
|
95218
96915
|
*/
|
|
95219
96916
|
function mdxishMdastToMd(mdast) {
|
|
95220
|
-
const md = unified()
|
|
96917
|
+
const md = unified()
|
|
96918
|
+
.use(remarkGfm)
|
|
96919
|
+
.use(mdxishCompilers)
|
|
96920
|
+
.use(remarkStringify, {
|
|
95221
96921
|
bullet: '-',
|
|
95222
96922
|
emphasis: '_',
|
|
95223
|
-
})
|
|
96923
|
+
})
|
|
96924
|
+
.stringify(mdast);
|
|
95224
96925
|
return md;
|
|
95225
96926
|
}
|
|
95226
96927
|
/**
|
|
@@ -95716,11 +97417,14 @@ const tags = (doc) => {
|
|
|
95716
97417
|
|
|
95717
97418
|
|
|
95718
97419
|
|
|
97420
|
+
|
|
95719
97421
|
const mdxishTags_tags = (doc) => {
|
|
95720
|
-
const { replaced: sanitizedDoc } = extractMagicBlocks(doc);
|
|
95721
97422
|
const set = new Set();
|
|
95722
|
-
const processor = remark()
|
|
95723
|
-
|
|
97423
|
+
const processor = remark()
|
|
97424
|
+
.data('micromarkExtensions', [magicBlock()])
|
|
97425
|
+
.data('fromMarkdownExtensions', [magicBlockFromMarkdown()])
|
|
97426
|
+
.use(mdxish_component_blocks);
|
|
97427
|
+
const tree = processor.parse(doc);
|
|
95724
97428
|
visit(processor.runSync(tree), isMDXElement, (node) => {
|
|
95725
97429
|
if (node.name?.match(/^[A-Z]/)) {
|
|
95726
97430
|
set.add(node.name);
|
|
@@ -95741,12 +97445,15 @@ const mdxishTags_tags = (doc) => {
|
|
|
95741
97445
|
|
|
95742
97446
|
|
|
95743
97447
|
|
|
97448
|
+
|
|
95744
97449
|
/**
|
|
95745
97450
|
* Removes Markdown and MDX comments.
|
|
95746
97451
|
*/
|
|
95747
97452
|
async function stripComments(doc, { mdx, mdxish } = {}) {
|
|
95748
|
-
const
|
|
95749
|
-
|
|
97453
|
+
const processor = unified()
|
|
97454
|
+
.data('micromarkExtensions', [magicBlock()])
|
|
97455
|
+
.data('fromMarkdownExtensions', [magicBlockFromMarkdown()])
|
|
97456
|
+
.data('toMarkdownExtensions', [magicBlockToMarkdown()]);
|
|
95750
97457
|
// we still require these two extensions because:
|
|
95751
97458
|
// 1. we can rely on remarkMdx to parse MDXish
|
|
95752
97459
|
// 2. we need to parse JSX comments into mdxTextExpression nodes so that the transformers can pick them up
|
|
@@ -95788,10 +97495,8 @@ async function stripComments(doc, { mdx, mdxish } = {}) {
|
|
|
95788
97495
|
},
|
|
95789
97496
|
],
|
|
95790
97497
|
});
|
|
95791
|
-
const file = await processor.process(
|
|
95792
|
-
|
|
95793
|
-
const restored = restoreMagicBlocks(stringified, blocks);
|
|
95794
|
-
return restored;
|
|
97498
|
+
const file = await processor.process(doc);
|
|
97499
|
+
return String(file).trim();
|
|
95795
97500
|
}
|
|
95796
97501
|
/* harmony default export */ const lib_stripComments = (stripComments);
|
|
95797
97502
|
|