@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.node.js
CHANGED
|
@@ -24514,7 +24514,9 @@ const CreateHeading = (depth) => {
|
|
|
24514
24514
|
;// ./components/Image/index.tsx
|
|
24515
24515
|
|
|
24516
24516
|
const Image = (Props) => {
|
|
24517
|
-
const { align = '', alt = '', border = false, caption, className = '', height = 'auto', src, title = '', width = 'auto', lazy = true, children, } = Props;
|
|
24517
|
+
const { align = '', alt = '', border: borderProp = false, caption, className = '', height = 'auto', src, title = '', width = 'auto', lazy = true, children, } = Props;
|
|
24518
|
+
// Normalize border: MDXish passes {false} as the string "false", not a boolean
|
|
24519
|
+
const border = borderProp === true || borderProp === 'true';
|
|
24518
24520
|
const [lightbox, setLightbox] = external_react_.useState(false);
|
|
24519
24521
|
if (className === 'emoji') {
|
|
24520
24522
|
return external_react_.createElement("img", { alt: alt, height: height, loading: lazy ? 'lazy' : 'eager', src: src, title: title, width: width });
|
|
@@ -26898,7 +26900,7 @@ function htmlExtension(all, extension) {
|
|
|
26898
26900
|
|
|
26899
26901
|
;// ./node_modules/micromark-util-character/index.js
|
|
26900
26902
|
/**
|
|
26901
|
-
* @
|
|
26903
|
+
* @import {Code} from 'micromark-util-types'
|
|
26902
26904
|
*/
|
|
26903
26905
|
|
|
26904
26906
|
/**
|
|
@@ -27124,7 +27126,9 @@ const unicodeWhitespace = regexCheck(/\s/);
|
|
|
27124
27126
|
* Create a code check from a regex.
|
|
27125
27127
|
*
|
|
27126
27128
|
* @param {RegExp} regex
|
|
27129
|
+
* Expression.
|
|
27127
27130
|
* @returns {(code: Code) => boolean}
|
|
27131
|
+
* Check.
|
|
27128
27132
|
*/
|
|
27129
27133
|
function regexCheck(regex) {
|
|
27130
27134
|
return check;
|
|
@@ -73302,6 +73306,59 @@ const isCalloutStructure = (node) => {
|
|
|
73302
73306
|
const firstTextChild = firstChild.children?.[0];
|
|
73303
73307
|
return firstTextChild?.type === 'text';
|
|
73304
73308
|
};
|
|
73309
|
+
/**
|
|
73310
|
+
* Finds the first text node containing a newline in a paragraph's children.
|
|
73311
|
+
* Returns the index and the newline position within that text node.
|
|
73312
|
+
*/
|
|
73313
|
+
const findNewlineInParagraph = (paragraph) => {
|
|
73314
|
+
for (let i = 0; i < paragraph.children.length; i += 1) {
|
|
73315
|
+
const child = paragraph.children[i];
|
|
73316
|
+
if (child.type === 'text' && typeof child.value === 'string') {
|
|
73317
|
+
const newlineIndex = child.value.indexOf('\n');
|
|
73318
|
+
if (newlineIndex !== -1) {
|
|
73319
|
+
return { index: i, newlineIndex };
|
|
73320
|
+
}
|
|
73321
|
+
}
|
|
73322
|
+
}
|
|
73323
|
+
return null;
|
|
73324
|
+
};
|
|
73325
|
+
/**
|
|
73326
|
+
* Splits a paragraph at the first newline, separating heading content (before \n)
|
|
73327
|
+
* from body content (after \n). Mutates the paragraph to contain only heading children.
|
|
73328
|
+
*/
|
|
73329
|
+
const splitParagraphAtNewline = (paragraph) => {
|
|
73330
|
+
const splitPoint = findNewlineInParagraph(paragraph);
|
|
73331
|
+
if (!splitPoint)
|
|
73332
|
+
return null;
|
|
73333
|
+
const { index, newlineIndex } = splitPoint;
|
|
73334
|
+
const originalChildren = paragraph.children;
|
|
73335
|
+
const textNode = originalChildren[index];
|
|
73336
|
+
const beforeNewline = textNode.value.slice(0, newlineIndex);
|
|
73337
|
+
const afterNewline = textNode.value.slice(newlineIndex + 1);
|
|
73338
|
+
// Split paragraph: heading = children[0..index-1] + text before newline
|
|
73339
|
+
const headingChildren = originalChildren.slice(0, index);
|
|
73340
|
+
if (beforeNewline.length > 0 || headingChildren.length === 0) {
|
|
73341
|
+
headingChildren.push({ type: 'text', value: beforeNewline });
|
|
73342
|
+
}
|
|
73343
|
+
paragraph.children = headingChildren;
|
|
73344
|
+
// Body = text after newline + remaining children from original array
|
|
73345
|
+
const bodyChildren = [];
|
|
73346
|
+
if (afterNewline.length > 0) {
|
|
73347
|
+
bodyChildren.push({ type: 'text', value: afterNewline });
|
|
73348
|
+
}
|
|
73349
|
+
bodyChildren.push(...originalChildren.slice(index + 1));
|
|
73350
|
+
return bodyChildren.length > 0 ? bodyChildren : null;
|
|
73351
|
+
};
|
|
73352
|
+
/**
|
|
73353
|
+
* Removes the icon/match prefix from the first text node in a paragraph.
|
|
73354
|
+
* This is needed to clean up the raw AST after we've extracted the icon.
|
|
73355
|
+
*/
|
|
73356
|
+
const removeIconPrefix = (paragraph, prefixLength) => {
|
|
73357
|
+
const firstTextNode = findFirst(paragraph);
|
|
73358
|
+
if (firstTextNode && 'value' in firstTextNode && typeof firstTextNode.value === 'string') {
|
|
73359
|
+
firstTextNode.value = firstTextNode.value.slice(prefixLength);
|
|
73360
|
+
}
|
|
73361
|
+
};
|
|
73305
73362
|
const processBlockquote = (node, index, parent) => {
|
|
73306
73363
|
if (!isCalloutStructure(node)) {
|
|
73307
73364
|
// Only stringify empty blockquotes (no extractable text content)
|
|
@@ -73326,22 +73383,39 @@ const processBlockquote = (node, index, parent) => {
|
|
|
73326
73383
|
const firstParagraph = node.children[0];
|
|
73327
73384
|
const startText = lib_plain(firstParagraph).toString();
|
|
73328
73385
|
const [match, icon] = startText.match(callouts_regex) || [];
|
|
73386
|
+
const firstParagraphOriginalEnd = firstParagraph.position.end;
|
|
73329
73387
|
if (icon && match) {
|
|
73330
|
-
|
|
73331
|
-
|
|
73388
|
+
// Handle cases where heading and body are on the same line separated by a newline.
|
|
73389
|
+
// Example: "> ⚠️ **Bold heading**\nBody text here"
|
|
73390
|
+
const bodyChildren = splitParagraphAtNewline(firstParagraph);
|
|
73391
|
+
const didSplit = bodyChildren !== null;
|
|
73392
|
+
// Extract heading text after removing the icon prefix.
|
|
73393
|
+
// Use `plain()` to handle complex markdown structures (bold, inline code, etc.)
|
|
73394
|
+
const headingText = lib_plain(firstParagraph)
|
|
73395
|
+
.toString()
|
|
73396
|
+
.slice(match.length);
|
|
73397
|
+
// Clean up the raw AST by removing the icon prefix from the first text node
|
|
73398
|
+
removeIconPrefix(firstParagraph, match.length);
|
|
73399
|
+
const empty = !headingText.length && firstParagraph.children.length === 1;
|
|
73332
73400
|
const theme = themes[icon] || 'default';
|
|
73333
|
-
|
|
73334
|
-
if (
|
|
73335
|
-
firstChild.value = firstChild.value.slice(match.length);
|
|
73336
|
-
}
|
|
73337
|
-
if (heading) {
|
|
73401
|
+
// Convert the first paragraph (first children of node) to a heading if it has content or was split
|
|
73402
|
+
if (headingText || didSplit) {
|
|
73338
73403
|
node.children[0] = wrapHeading(node);
|
|
73339
|
-
//
|
|
73340
|
-
// character that was stripped off, so that the start position of the
|
|
73341
|
-
// heading/text matches where it actually starts.
|
|
73404
|
+
// Adjust position to account for the stripped icon prefix
|
|
73342
73405
|
node.children[0].position.start.offset += match.length;
|
|
73343
73406
|
node.children[0].position.start.column += match.length;
|
|
73344
73407
|
}
|
|
73408
|
+
// Insert body content as a separate paragraph after the heading
|
|
73409
|
+
if (bodyChildren) {
|
|
73410
|
+
node.children.splice(1, 0, {
|
|
73411
|
+
type: 'paragraph',
|
|
73412
|
+
children: bodyChildren,
|
|
73413
|
+
position: {
|
|
73414
|
+
start: node.children[0].position.end,
|
|
73415
|
+
end: firstParagraphOriginalEnd,
|
|
73416
|
+
},
|
|
73417
|
+
});
|
|
73418
|
+
}
|
|
73345
73419
|
Object.assign(node, {
|
|
73346
73420
|
type: NodeTypes.callout,
|
|
73347
73421
|
data: {
|
|
@@ -90721,7 +90795,7 @@ const toAttributes = (object, keys = []) => {
|
|
|
90721
90795
|
if (keys.length > 0 && !keys.includes(name))
|
|
90722
90796
|
return;
|
|
90723
90797
|
let value;
|
|
90724
|
-
if (typeof v === 'undefined' || v === null || v === '') {
|
|
90798
|
+
if (typeof v === 'undefined' || v === null || v === '' || v === false) {
|
|
90725
90799
|
return;
|
|
90726
90800
|
}
|
|
90727
90801
|
else if (typeof v === 'string') {
|
|
@@ -106756,7 +106830,7 @@ const CUSTOM_PROP_BOUNDARIES = [
|
|
|
106756
106830
|
/**
|
|
106757
106831
|
* Tags that should be passed through and handled at runtime (not by the mdxish plugin)
|
|
106758
106832
|
*/
|
|
106759
|
-
const RUNTIME_COMPONENT_TAGS = new Set(['Variable', 'variable', 'rdme-pin']);
|
|
106833
|
+
const RUNTIME_COMPONENT_TAGS = new Set(['Variable', 'variable', 'html-block', 'rdme-pin']);
|
|
106760
106834
|
/**
|
|
106761
106835
|
* Standard HTML tags that should never be treated as custom components.
|
|
106762
106836
|
* Uses the html-tags package, converted to a Set<string> for efficient lookups.
|
|
@@ -111869,6 +111943,10 @@ ${reformatHTML(html)}
|
|
|
111869
111943
|
const plain_plain = (node) => node.value;
|
|
111870
111944
|
/* harmony default export */ const compile_plain = (plain_plain);
|
|
111871
111945
|
|
|
111946
|
+
;// ./processor/compile/variable.ts
|
|
111947
|
+
const variable = (node) => `{user.${node.data?.hProperties?.name || ''}}`;
|
|
111948
|
+
/* harmony default export */ const compile_variable = (variable);
|
|
111949
|
+
|
|
111872
111950
|
;// ./processor/compile/index.ts
|
|
111873
111951
|
|
|
111874
111952
|
|
|
@@ -111878,7 +111956,8 @@ const plain_plain = (node) => node.value;
|
|
|
111878
111956
|
|
|
111879
111957
|
|
|
111880
111958
|
|
|
111881
|
-
|
|
111959
|
+
|
|
111960
|
+
function compilers(mdxish = false) {
|
|
111882
111961
|
const data = this.data();
|
|
111883
111962
|
const toMarkdownExtensions = data.toMarkdownExtensions || (data.toMarkdownExtensions = []);
|
|
111884
111963
|
const handlers = {
|
|
@@ -111889,6 +111968,7 @@ function compilers() {
|
|
|
111889
111968
|
[NodeTypes.glossary]: compile_compatibility,
|
|
111890
111969
|
[NodeTypes.htmlBlock]: html_block,
|
|
111891
111970
|
[NodeTypes.reusableContent]: compile_compatibility,
|
|
111971
|
+
...(mdxish && { [NodeTypes.variable]: compile_variable }),
|
|
111892
111972
|
embed: compile_compatibility,
|
|
111893
111973
|
escape: compile_compatibility,
|
|
111894
111974
|
figure: compile_compatibility,
|
|
@@ -111899,6 +111979,9 @@ function compilers() {
|
|
|
111899
111979
|
};
|
|
111900
111980
|
toMarkdownExtensions.push({ extensions: [{ handlers }] });
|
|
111901
111981
|
}
|
|
111982
|
+
function mdxishCompilers() {
|
|
111983
|
+
return compilers.call(this, true);
|
|
111984
|
+
}
|
|
111902
111985
|
/* harmony default export */ const processor_compile = (compilers);
|
|
111903
111986
|
|
|
111904
111987
|
;// ./processor/transform/escape-pipes-in-tables.ts
|
|
@@ -113609,13 +113692,17 @@ const htmlBlockHandler = (_state, node) => {
|
|
|
113609
113692
|
const embedHandler = (state, node) => {
|
|
113610
113693
|
// Assert to get the minimum properties we need
|
|
113611
113694
|
const { data } = node;
|
|
113695
|
+
// Magic block embeds (hName === 'embed-block') render as Embed component
|
|
113696
|
+
// which doesn't use children - it renders based on props only
|
|
113697
|
+
const isMagicBlockEmbed = data?.hName === NodeTypes.embedBlock;
|
|
113612
113698
|
return {
|
|
113613
113699
|
type: 'element',
|
|
113614
113700
|
// To differentiate between regular embeds and magic block embeds,
|
|
113615
113701
|
// magic block embeds have a certain hName
|
|
113616
|
-
tagName:
|
|
113702
|
+
tagName: isMagicBlockEmbed ? 'Embed' : 'embed',
|
|
113617
113703
|
properties: data?.hProperties,
|
|
113618
|
-
children
|
|
113704
|
+
// Don't include children for magic block embeds - Embed component renders based on props
|
|
113705
|
+
children: isMagicBlockEmbed ? [] : state.all(node),
|
|
113619
113706
|
};
|
|
113620
113707
|
};
|
|
113621
113708
|
const mdxComponentHandlers = {
|
|
@@ -113628,7 +113715,102 @@ const mdxComponentHandlers = {
|
|
|
113628
113715
|
[NodeTypes.htmlBlock]: htmlBlockHandler,
|
|
113629
113716
|
};
|
|
113630
113717
|
|
|
113718
|
+
;// ./lib/utils/mdxish/protect-code-blocks.ts
|
|
113719
|
+
/**
|
|
113720
|
+
* Replaces code blocks and inline code with placeholders to protect them from preprocessing.
|
|
113721
|
+
*
|
|
113722
|
+
* @param content - The markdown content to process
|
|
113723
|
+
* @returns Object containing protected content and arrays of original code blocks
|
|
113724
|
+
* @example
|
|
113725
|
+
* ```typescript
|
|
113726
|
+
* const input = 'Text with `inline code` and ```fenced block```';
|
|
113727
|
+
* protectCodeBlocks(input)
|
|
113728
|
+
* // Returns: {
|
|
113729
|
+
* // protectedCode: {
|
|
113730
|
+
* // codeBlocks: ['```fenced block```'],
|
|
113731
|
+
* // inlineCode: ['`inline code`']
|
|
113732
|
+
* // },
|
|
113733
|
+
* // protectedContent: 'Text with ___INLINE_CODE_0___ and ___CODE_BLOCK_0___'
|
|
113734
|
+
* // }
|
|
113735
|
+
* ```
|
|
113736
|
+
*/
|
|
113737
|
+
function protectCodeBlocks(content) {
|
|
113738
|
+
const codeBlocks = [];
|
|
113739
|
+
const inlineCode = [];
|
|
113740
|
+
let protectedContent = '';
|
|
113741
|
+
let remaining = content;
|
|
113742
|
+
let codeBlockStart = remaining.indexOf('```');
|
|
113743
|
+
while (codeBlockStart !== -1) {
|
|
113744
|
+
protectedContent += remaining.slice(0, codeBlockStart);
|
|
113745
|
+
remaining = remaining.slice(codeBlockStart);
|
|
113746
|
+
const codeBlockEnd = remaining.indexOf('```', 3);
|
|
113747
|
+
if (codeBlockEnd === -1) {
|
|
113748
|
+
break;
|
|
113749
|
+
}
|
|
113750
|
+
const match = remaining.slice(0, codeBlockEnd + 3);
|
|
113751
|
+
const index = codeBlocks.length;
|
|
113752
|
+
codeBlocks.push(match);
|
|
113753
|
+
protectedContent += `___CODE_BLOCK_${index}___`;
|
|
113754
|
+
remaining = remaining.slice(codeBlockEnd + 3);
|
|
113755
|
+
codeBlockStart = remaining.indexOf('```');
|
|
113756
|
+
}
|
|
113757
|
+
protectedContent += remaining;
|
|
113758
|
+
protectedContent = protectedContent.replace(/`([^`\n]+)`/g, match => {
|
|
113759
|
+
const index = inlineCode.length;
|
|
113760
|
+
inlineCode.push(match);
|
|
113761
|
+
return `___INLINE_CODE_${index}___`;
|
|
113762
|
+
});
|
|
113763
|
+
return { protectedCode: { codeBlocks, inlineCode }, protectedContent };
|
|
113764
|
+
}
|
|
113765
|
+
/**
|
|
113766
|
+
* Restores inline code by replacing placeholders with original content.
|
|
113767
|
+
*
|
|
113768
|
+
* @param content - Content with inline code placeholders
|
|
113769
|
+
* @param protectedCode - The protected code arrays
|
|
113770
|
+
* @returns Content with inline code restored
|
|
113771
|
+
*/
|
|
113772
|
+
function restoreInlineCode(content, protectedCode) {
|
|
113773
|
+
return content.replace(/___INLINE_CODE_(\d+)___/g, (_m, idx) => {
|
|
113774
|
+
return protectedCode.inlineCode[parseInt(idx, 10)];
|
|
113775
|
+
});
|
|
113776
|
+
}
|
|
113777
|
+
/**
|
|
113778
|
+
* Restores fenced code blocks by replacing placeholders with original content.
|
|
113779
|
+
*
|
|
113780
|
+
* @param content - Content with code block placeholders
|
|
113781
|
+
* @param protectedCode - The protected code arrays
|
|
113782
|
+
* @returns Content with code blocks restored
|
|
113783
|
+
*/
|
|
113784
|
+
function restoreFencedCodeBlocks(content, protectedCode) {
|
|
113785
|
+
return content.replace(/___CODE_BLOCK_(\d+)___/g, (_m, idx) => {
|
|
113786
|
+
return protectedCode.codeBlocks[parseInt(idx, 10)];
|
|
113787
|
+
});
|
|
113788
|
+
}
|
|
113789
|
+
/**
|
|
113790
|
+
* Restores all code blocks and inline code by replacing placeholders with original content.
|
|
113791
|
+
*
|
|
113792
|
+
* @param content - Content with code placeholders
|
|
113793
|
+
* @param protectedCode - The protected code arrays
|
|
113794
|
+
* @returns Content with all code blocks and inline code restored
|
|
113795
|
+
* @example
|
|
113796
|
+
* ```typescript
|
|
113797
|
+
* const content = 'Text with ___INLINE_CODE_0___ and ___CODE_BLOCK_0___';
|
|
113798
|
+
* const protectedCode = {
|
|
113799
|
+
* codeBlocks: ['```js\ncode\n```'],
|
|
113800
|
+
* inlineCode: ['`inline`']
|
|
113801
|
+
* };
|
|
113802
|
+
* restoreCodeBlocks(content, protectedCode)
|
|
113803
|
+
* // Returns: 'Text with `inline` and ```js\ncode\n```'
|
|
113804
|
+
* ```
|
|
113805
|
+
*/
|
|
113806
|
+
function restoreCodeBlocks(content, protectedCode) {
|
|
113807
|
+
let restored = restoreFencedCodeBlocks(content, protectedCode);
|
|
113808
|
+
restored = restoreInlineCode(restored, protectedCode);
|
|
113809
|
+
return restored;
|
|
113810
|
+
}
|
|
113811
|
+
|
|
113631
113812
|
;// ./processor/transform/mdxish/preprocess-jsx-expressions.ts
|
|
113813
|
+
|
|
113632
113814
|
// Base64 encode (Node.js + browser compatible)
|
|
113633
113815
|
function base64Encode(str) {
|
|
113634
113816
|
if (typeof Buffer !== 'undefined') {
|
|
@@ -113695,52 +113877,6 @@ function protectHTMLBlockContent(content) {
|
|
|
113695
113877
|
return `${openTag}${HTML_BLOCK_CONTENT_START}${encoded}${HTML_BLOCK_CONTENT_END}${closeTag}`;
|
|
113696
113878
|
});
|
|
113697
113879
|
}
|
|
113698
|
-
/**
|
|
113699
|
-
* Replaces code blocks and inline code with placeholders to protect them from JSX processing.
|
|
113700
|
-
*
|
|
113701
|
-
* @param content
|
|
113702
|
-
* @returns Object containing protected content and arrays of original code blocks
|
|
113703
|
-
* @example
|
|
113704
|
-
* ```typescript
|
|
113705
|
-
* const input = 'Text with `inline code` and ```fenced block```';
|
|
113706
|
-
* protectCodeBlocks(input)
|
|
113707
|
-
* // Returns: {
|
|
113708
|
-
* // protectedCode: {
|
|
113709
|
-
* // codeBlocks: ['```fenced block```'],
|
|
113710
|
-
* // inlineCode: ['`inline code`']
|
|
113711
|
-
* // },
|
|
113712
|
-
* // protectedContent: 'Text with ___INLINE_CODE_0___ and ___CODE_BLOCK_0___'
|
|
113713
|
-
* // }
|
|
113714
|
-
* ```
|
|
113715
|
-
*/
|
|
113716
|
-
function protectCodeBlocks(content) {
|
|
113717
|
-
const codeBlocks = [];
|
|
113718
|
-
const inlineCode = [];
|
|
113719
|
-
let protectedContent = '';
|
|
113720
|
-
let remaining = content;
|
|
113721
|
-
let codeBlockStart = remaining.indexOf('```');
|
|
113722
|
-
while (codeBlockStart !== -1) {
|
|
113723
|
-
protectedContent += remaining.slice(0, codeBlockStart);
|
|
113724
|
-
remaining = remaining.slice(codeBlockStart);
|
|
113725
|
-
const codeBlockEnd = remaining.indexOf('```', 3);
|
|
113726
|
-
if (codeBlockEnd === -1) {
|
|
113727
|
-
break;
|
|
113728
|
-
}
|
|
113729
|
-
const match = remaining.slice(0, codeBlockEnd + 3);
|
|
113730
|
-
const index = codeBlocks.length;
|
|
113731
|
-
codeBlocks.push(match);
|
|
113732
|
-
protectedContent += `___CODE_BLOCK_${index}___`;
|
|
113733
|
-
remaining = remaining.slice(codeBlockEnd + 3);
|
|
113734
|
-
codeBlockStart = remaining.indexOf('```');
|
|
113735
|
-
}
|
|
113736
|
-
protectedContent += remaining;
|
|
113737
|
-
protectedContent = protectedContent.replace(/`[^`]+`/g, match => {
|
|
113738
|
-
const index = inlineCode.length;
|
|
113739
|
-
inlineCode.push(match);
|
|
113740
|
-
return `___INLINE_CODE_${index}___`;
|
|
113741
|
-
});
|
|
113742
|
-
return { protectedCode: { codeBlocks, inlineCode }, protectedContent };
|
|
113743
|
-
}
|
|
113744
113880
|
/**
|
|
113745
113881
|
* Removes JSX-style comments (e.g., { /* comment *\/ }) from content.
|
|
113746
113882
|
*
|
|
@@ -113783,16 +113919,6 @@ function extractBalancedBraces(content, start) {
|
|
|
113783
113919
|
return null;
|
|
113784
113920
|
return { content: content.slice(start, pos - 1), end: pos };
|
|
113785
113921
|
}
|
|
113786
|
-
function restoreInlineCode(content, protectedCode) {
|
|
113787
|
-
return content.replace(/___INLINE_CODE_(\d+)___/g, (_m, idx) => {
|
|
113788
|
-
return protectedCode.inlineCode[parseInt(idx, 10)];
|
|
113789
|
-
});
|
|
113790
|
-
}
|
|
113791
|
-
function restoreCodeBlocks(content, protectedCode) {
|
|
113792
|
-
return content.replace(/___CODE_BLOCK_(\d+)___/g, (_m, idx) => {
|
|
113793
|
-
return protectedCode.codeBlocks[parseInt(idx, 10)];
|
|
113794
|
-
});
|
|
113795
|
-
}
|
|
113796
113922
|
/**
|
|
113797
113923
|
* Escapes unbalanced braces in content to prevent MDX expression parsing errors.
|
|
113798
113924
|
* Handles: already-escaped braces, string literals inside expressions, nested balanced braces.
|
|
@@ -113923,28 +114049,6 @@ function evaluateAttributeExpressions(content, context, protectedCode) {
|
|
|
113923
114049
|
result += content.slice(lastEnd);
|
|
113924
114050
|
return result;
|
|
113925
114051
|
}
|
|
113926
|
-
/**
|
|
113927
|
-
* Restores code blocks and inline code by replacing placeholders with original content.
|
|
113928
|
-
*
|
|
113929
|
-
* @param content
|
|
113930
|
-
* @param protectedCode
|
|
113931
|
-
* @returns Content with all code blocks and inline code restored
|
|
113932
|
-
* @example
|
|
113933
|
-
* ```typescript
|
|
113934
|
-
* const content = 'Text with ___INLINE_CODE_0___ and ___CODE_BLOCK_0___';
|
|
113935
|
-
* const protectedCode = {
|
|
113936
|
-
* codeBlocks: ['```js\ncode\n```'],
|
|
113937
|
-
* inlineCode: ['`inline`']
|
|
113938
|
-
* };
|
|
113939
|
-
* restoreCodeBlocks(content, protectedCode)
|
|
113940
|
-
* // Returns: 'Text with `inline` and ```js\ncode\n```'
|
|
113941
|
-
* ```
|
|
113942
|
-
*/
|
|
113943
|
-
function restoreProtectedCodes(content, protectedCode) {
|
|
113944
|
-
let restored = restoreCodeBlocks(content, protectedCode);
|
|
113945
|
-
restored = restoreInlineCode(restored, protectedCode);
|
|
113946
|
-
return restored;
|
|
113947
|
-
}
|
|
113948
114052
|
/**
|
|
113949
114053
|
* Preprocesses JSX-like expressions in markdown before parsing.
|
|
113950
114054
|
* Inline expressions are handled separately; attribute expressions are processed here.
|
|
@@ -113967,7 +114071,7 @@ function preprocessJSXExpressions(content, context = {}) {
|
|
|
113967
114071
|
// Step 4: Escape unbalanced braces to prevent MDX expression parsing errors
|
|
113968
114072
|
processed = escapeUnbalancedBraces(processed);
|
|
113969
114073
|
// Step 5: Restore protected code blocks
|
|
113970
|
-
processed =
|
|
114074
|
+
processed = restoreCodeBlocks(processed, protectedCode);
|
|
113971
114075
|
return processed;
|
|
113972
114076
|
}
|
|
113973
114077
|
|
|
@@ -114022,360 +114126,407 @@ const evaluateExpressions = ({ context = {} } = {}) => tree => {
|
|
|
114022
114126
|
};
|
|
114023
114127
|
/* harmony default export */ const evaluate_expressions = (evaluateExpressions);
|
|
114024
114128
|
|
|
114025
|
-
;// ./processor/transform/mdxish/
|
|
114026
|
-
|
|
114027
|
-
|
|
114028
|
-
|
|
114129
|
+
;// ./processor/transform/mdxish/normalize-malformed-md-syntax.ts
|
|
114029
114130
|
|
|
114030
|
-
|
|
114031
|
-
|
|
114032
|
-
|
|
114033
|
-
|
|
114034
|
-
|
|
114035
|
-
|
|
114036
|
-
|
|
114037
|
-
|
|
114038
|
-
|
|
114039
|
-
|
|
114040
|
-
|
|
114041
|
-
|
|
114131
|
+
// Marker patterns for multi-node emphasis detection
|
|
114132
|
+
const MARKER_PATTERNS = [
|
|
114133
|
+
{ isBold: true, marker: '**' },
|
|
114134
|
+
{ isBold: true, marker: '__' },
|
|
114135
|
+
{ isBold: false, marker: '*' },
|
|
114136
|
+
{ isBold: false, marker: '_' },
|
|
114137
|
+
];
|
|
114138
|
+
// Patterns to detect for bold (** and __) and italic (* and _) syntax:
|
|
114139
|
+
// Bold: ** text**, **text **, word** text**, ** text **
|
|
114140
|
+
// Italic: * text*, *text *, word* text*, * text *
|
|
114141
|
+
// Same patterns for underscore variants
|
|
114142
|
+
// We use separate patterns for each marker type to allow this flexibility.
|
|
114143
|
+
// Pattern for ** bold **
|
|
114144
|
+
// Groups: 1=wordBefore, 2=marker, 3=contentWithSpaceAfter, 4=trailingSpace1, 5=contentWithSpaceBefore, 6=trailingSpace2, 7=afterChar
|
|
114145
|
+
// trailingSpace1 is for "** text **" pattern, trailingSpace2 is for "**text **" pattern
|
|
114146
|
+
const asteriskBoldRegex = /([^*\s]+)?\s*(\*\*)(?:\s+((?:[^*\n]|\*(?!\*))+?)(\s*)\2|((?:[^*\n]|\*(?!\*))+?)(\s+)\2)(\S|$)?/g;
|
|
114147
|
+
// Pattern for __ bold __
|
|
114148
|
+
const underscoreBoldRegex = /([^_\s]+)?\s*(__)(?:\s+((?:[^_\n]|_(?!_))+?)(\s*)\2|((?:[^_\n]|_(?!_))+?)(\s+)\2)(\S|$)?/g;
|
|
114149
|
+
// Pattern for * italic *
|
|
114150
|
+
const asteriskItalicRegex = /([^*\s]+)?\s*(\*)(?!\*)(?:\s+([^*\n]+?)(\s*)\2|([^*\n]+?)(\s+)\2)(\S|$)?/g;
|
|
114151
|
+
// Pattern for _ italic _
|
|
114152
|
+
const underscoreItalicRegex = /([^_\s]+)?\s*(_)(?!_)(?:\s+([^_\n]+?)(\s*)\2|([^_\n]+?)(\s+)\2)(\S|$)?/g;
|
|
114153
|
+
// CommonMark ignores intraword underscores or asteriks, but we want to italicize/bold the inner part
|
|
114154
|
+
// Pattern for intraword _word_ in words like hello_world_
|
|
114155
|
+
const intrawordUnderscoreItalicRegex = /(\w)_(?!_)([a-zA-Z0-9]+)_(?![\w_])/g;
|
|
114156
|
+
// Pattern for intraword __word__ in words like hello__world__
|
|
114157
|
+
const intrawordUnderscoreBoldRegex = /(\w)__([a-zA-Z0-9]+)__(?![\w_])/g;
|
|
114158
|
+
// Pattern for intraword *word* in words like hello*world*
|
|
114159
|
+
const intrawordAsteriskItalicRegex = /(\w)\*(?!\*)([a-zA-Z0-9]+)\*(?![\w*])/g;
|
|
114160
|
+
// Pattern for intraword **word** in words like hello**world**
|
|
114161
|
+
const intrawordAsteriskBoldRegex = /(\w)\*\*([a-zA-Z0-9]+)\*\*(?![\w*])/g;
|
|
114162
|
+
/**
|
|
114163
|
+
* Finds opening emphasis marker in a text value.
|
|
114164
|
+
* Returns marker info if found, null otherwise.
|
|
114165
|
+
*/
|
|
114166
|
+
function findOpeningMarker(text) {
|
|
114167
|
+
const results = MARKER_PATTERNS.map(({ isBold, marker }) => {
|
|
114168
|
+
if (marker === '*' && text.startsWith('**'))
|
|
114169
|
+
return null;
|
|
114170
|
+
if (marker === '_' && text.startsWith('__'))
|
|
114171
|
+
return null;
|
|
114172
|
+
if (text.startsWith(marker) && text.length > marker.length) {
|
|
114173
|
+
return { isBold, marker, textAfter: text.slice(marker.length), textBefore: '' };
|
|
114042
114174
|
}
|
|
114043
|
-
|
|
114044
|
-
|
|
114175
|
+
const idx = text.indexOf(marker);
|
|
114176
|
+
if (idx > 0 && !/\s/.test(text[idx - 1])) {
|
|
114177
|
+
if (marker === '*' && text.slice(idx).startsWith('**'))
|
|
114178
|
+
return null;
|
|
114179
|
+
if (marker === '_' && text.slice(idx).startsWith('__'))
|
|
114180
|
+
return null;
|
|
114181
|
+
const after = text.slice(idx + marker.length);
|
|
114182
|
+
if (after.length > 0) {
|
|
114183
|
+
return { isBold, marker, textAfter: after, textBefore: text.slice(0, idx) };
|
|
114184
|
+
}
|
|
114045
114185
|
}
|
|
114186
|
+
return null;
|
|
114046
114187
|
});
|
|
114188
|
+
return results.find(r => r !== null) ?? null;
|
|
114047
114189
|
}
|
|
114048
114190
|
/**
|
|
114049
|
-
*
|
|
114191
|
+
* Finds the end/closing marker in a text node for multi-node emphasis.
|
|
114050
114192
|
*/
|
|
114051
|
-
function
|
|
114052
|
-
const
|
|
114053
|
-
|
|
114054
|
-
|
|
114055
|
-
|
|
114056
|
-
|
|
114057
|
-
|
|
114058
|
-
|
|
114059
|
-
|
|
114060
|
-
|
|
114061
|
-
|
|
114062
|
-
|
|
114063
|
-
// Reconstruct code fence syntax (markdown parser consumes opening ```)
|
|
114064
|
-
const lang = node.lang || '';
|
|
114065
|
-
const fence = `\`\`\`${lang ? `${lang}\n` : ''}`;
|
|
114066
|
-
parts.push(fence);
|
|
114067
|
-
parts.push(node.value);
|
|
114068
|
-
// Add newline before closing fence if missing
|
|
114069
|
-
const closingFence = node.value.endsWith('\n') ? '```' : '\n```';
|
|
114070
|
-
parts.push(closingFence);
|
|
114193
|
+
function findEndMarker(text, marker) {
|
|
114194
|
+
const spacePattern = ` ${marker}`;
|
|
114195
|
+
const spaceIdx = text.indexOf(spacePattern);
|
|
114196
|
+
if (spaceIdx >= 0) {
|
|
114197
|
+
if (marker === '*' && text.slice(spaceIdx + 1).startsWith('**'))
|
|
114198
|
+
return null;
|
|
114199
|
+
if (marker === '_' && text.slice(spaceIdx + 1).startsWith('__'))
|
|
114200
|
+
return null;
|
|
114201
|
+
return {
|
|
114202
|
+
textAfter: text.slice(spaceIdx + spacePattern.length),
|
|
114203
|
+
textBefore: text.slice(0, spaceIdx),
|
|
114204
|
+
};
|
|
114071
114205
|
}
|
|
114072
|
-
|
|
114073
|
-
|
|
114074
|
-
|
|
114075
|
-
|
|
114076
|
-
|
|
114077
|
-
|
|
114206
|
+
if (text.startsWith(marker)) {
|
|
114207
|
+
if (marker === '*' && text.startsWith('**'))
|
|
114208
|
+
return null;
|
|
114209
|
+
if (marker === '_' && text.startsWith('__'))
|
|
114210
|
+
return null;
|
|
114211
|
+
return {
|
|
114212
|
+
textAfter: text.slice(marker.length),
|
|
114213
|
+
textBefore: '',
|
|
114214
|
+
};
|
|
114078
114215
|
}
|
|
114079
|
-
return
|
|
114216
|
+
return null;
|
|
114080
114217
|
}
|
|
114081
114218
|
/**
|
|
114082
|
-
*
|
|
114083
|
-
* Returns "true"/"false" string to survive rehypeRaw serialization.
|
|
114219
|
+
* Scan children for an opening emphasis marker in a text node.
|
|
114084
114220
|
*/
|
|
114085
|
-
function
|
|
114086
|
-
|
|
114087
|
-
|
|
114088
|
-
|
|
114089
|
-
|
|
114090
|
-
|
|
114091
|
-
|
|
114092
|
-
|
|
114093
|
-
|
|
114094
|
-
|
|
114095
|
-
|
|
114096
|
-
|
|
114221
|
+
function findOpeningInChildren(children) {
|
|
114222
|
+
let result = null;
|
|
114223
|
+
children.some((child, idx) => {
|
|
114224
|
+
if (child.type !== 'text')
|
|
114225
|
+
return false;
|
|
114226
|
+
const found = findOpeningMarker(child.value);
|
|
114227
|
+
if (found) {
|
|
114228
|
+
result = { idx, opening: found };
|
|
114229
|
+
return true;
|
|
114230
|
+
}
|
|
114231
|
+
return false;
|
|
114232
|
+
});
|
|
114233
|
+
return result;
|
|
114097
114234
|
}
|
|
114098
114235
|
/**
|
|
114099
|
-
*
|
|
114236
|
+
* Scan children (after openingIdx) for a closing emphasis marker.
|
|
114100
114237
|
*/
|
|
114101
|
-
function
|
|
114102
|
-
|
|
114103
|
-
|
|
114104
|
-
|
|
114105
|
-
|
|
114106
|
-
|
|
114107
|
-
|
|
114108
|
-
|
|
114109
|
-
|
|
114110
|
-
|
|
114238
|
+
function findClosingInChildren(children, openingIdx, marker) {
|
|
114239
|
+
let result = null;
|
|
114240
|
+
children.slice(openingIdx + 1).some((child, relativeIdx) => {
|
|
114241
|
+
if (child.type !== 'text')
|
|
114242
|
+
return false;
|
|
114243
|
+
const found = findEndMarker(child.value, marker);
|
|
114244
|
+
if (found) {
|
|
114245
|
+
result = { closingIdx: openingIdx + 1 + relativeIdx, closing: found };
|
|
114246
|
+
return true;
|
|
114247
|
+
}
|
|
114111
114248
|
return false;
|
|
114112
|
-
}
|
|
114113
|
-
return
|
|
114249
|
+
});
|
|
114250
|
+
return result;
|
|
114114
114251
|
}
|
|
114115
114252
|
/**
|
|
114116
|
-
*
|
|
114253
|
+
* Build the replacement nodes for a matched emphasis pair.
|
|
114117
114254
|
*/
|
|
114118
|
-
function
|
|
114119
|
-
|
|
114120
|
-
|
|
114121
|
-
|
|
114122
|
-
|
|
114123
|
-
|
|
114124
|
-
|
|
114125
|
-
|
|
114126
|
-
|
|
114127
|
-
|
|
114128
|
-
|
|
114129
|
-
|
|
114130
|
-
|
|
114131
|
-
|
|
114255
|
+
function buildReplacementNodes(container, { opening, openingIdx, closing, closingIdx }) {
|
|
114256
|
+
const newNodes = [];
|
|
114257
|
+
if (opening.textBefore) {
|
|
114258
|
+
newNodes.push({ type: 'text', value: `${opening.textBefore} ` });
|
|
114259
|
+
}
|
|
114260
|
+
const emphasisChildren = [];
|
|
114261
|
+
const openingText = opening.textAfter.replace(/^\s+/, '');
|
|
114262
|
+
if (openingText) {
|
|
114263
|
+
emphasisChildren.push({ type: 'text', value: openingText });
|
|
114264
|
+
}
|
|
114265
|
+
container.children.slice(openingIdx + 1, closingIdx).forEach(child => {
|
|
114266
|
+
emphasisChildren.push(child);
|
|
114267
|
+
});
|
|
114268
|
+
const closingText = closing.textBefore.replace(/\s+$/, '');
|
|
114269
|
+
if (closingText) {
|
|
114270
|
+
emphasisChildren.push({ type: 'text', value: closingText });
|
|
114271
|
+
}
|
|
114272
|
+
if (emphasisChildren.length > 0) {
|
|
114273
|
+
const emphasisNode = opening.isBold
|
|
114274
|
+
? { type: 'strong', children: emphasisChildren }
|
|
114275
|
+
: { type: 'emphasis', children: emphasisChildren };
|
|
114276
|
+
newNodes.push(emphasisNode);
|
|
114277
|
+
}
|
|
114278
|
+
if (closing.textAfter) {
|
|
114279
|
+
newNodes.push({ type: 'text', value: closing.textAfter });
|
|
114280
|
+
}
|
|
114281
|
+
return newNodes;
|
|
114132
114282
|
}
|
|
114133
114283
|
/**
|
|
114134
|
-
*
|
|
114284
|
+
* Find and transform one multi-node emphasis pair in the container.
|
|
114285
|
+
* Returns true if a pair was found and transformed, false otherwise.
|
|
114135
114286
|
*/
|
|
114136
|
-
function
|
|
114137
|
-
|
|
114138
|
-
|
|
114139
|
-
|
|
114140
|
-
const
|
|
114141
|
-
|
|
114142
|
-
|
|
114143
|
-
|
|
114144
|
-
|
|
114145
|
-
|
|
114146
|
-
|
|
114147
|
-
|
|
114148
|
-
|
|
114149
|
-
attrs = match[1] || '';
|
|
114150
|
-
}
|
|
114151
|
-
}
|
|
114152
|
-
if (n.value === '</HTMLBlock>' || n.value.includes('</HTMLBlock>')) {
|
|
114153
|
-
hasClosed = true;
|
|
114154
|
-
}
|
|
114155
|
-
}
|
|
114156
|
-
if (n.children && Array.isArray(n.children)) {
|
|
114157
|
-
n.children.forEach(child => {
|
|
114158
|
-
check(child);
|
|
114159
|
-
});
|
|
114160
|
-
}
|
|
114161
|
-
};
|
|
114162
|
-
check(node);
|
|
114163
|
-
// Return true only if opening without closing (split case)
|
|
114164
|
-
return { attrs, found: hasOpening && !hasClosed };
|
|
114287
|
+
function processOneEmphasisPair(container) {
|
|
114288
|
+
const openingResult = findOpeningInChildren(container.children);
|
|
114289
|
+
if (!openingResult)
|
|
114290
|
+
return false;
|
|
114291
|
+
const { idx: openingIdx, opening } = openingResult;
|
|
114292
|
+
const closingResult = findClosingInChildren(container.children, openingIdx, opening.marker);
|
|
114293
|
+
if (!closingResult)
|
|
114294
|
+
return false;
|
|
114295
|
+
const { closingIdx, closing } = closingResult;
|
|
114296
|
+
const newNodes = buildReplacementNodes(container, { opening, openingIdx, closing, closingIdx });
|
|
114297
|
+
const deleteCount = closingIdx - openingIdx + 1;
|
|
114298
|
+
container.children.splice(openingIdx, deleteCount, ...newNodes);
|
|
114299
|
+
return true;
|
|
114165
114300
|
}
|
|
114166
114301
|
/**
|
|
114167
|
-
*
|
|
114302
|
+
* Handle malformed emphasis that spans multiple AST nodes.
|
|
114303
|
+
* E.g., "**bold [link](url)**" where markers are in different text nodes.
|
|
114168
114304
|
*/
|
|
114169
|
-
function
|
|
114170
|
-
|
|
114171
|
-
|
|
114172
|
-
|
|
114173
|
-
|
|
114174
|
-
|
|
114175
|
-
|
|
114176
|
-
|
|
114177
|
-
|
|
114305
|
+
function visitMultiNodeEmphasis(tree) {
|
|
114306
|
+
const containerTypes = ['paragraph', 'heading', 'tableCell', 'listItem', 'blockquote'];
|
|
114307
|
+
visit(tree, node => {
|
|
114308
|
+
if (!containerTypes.includes(node.type))
|
|
114309
|
+
return;
|
|
114310
|
+
if (!('children' in node) || !Array.isArray(node.children))
|
|
114311
|
+
return;
|
|
114312
|
+
const container = node;
|
|
114313
|
+
let foundPair = true;
|
|
114314
|
+
while (foundPair) {
|
|
114315
|
+
foundPair = processOneEmphasisPair(container);
|
|
114316
|
+
}
|
|
114317
|
+
});
|
|
114178
114318
|
}
|
|
114179
114319
|
/**
|
|
114180
|
-
*
|
|
114320
|
+
* A remark plugin that normalizes malformed bold and italic markers in text nodes.
|
|
114321
|
+
* Detects patterns like `** bold**`, `Hello** Wrong Bold**`, `__ bold__`, `Hello__ Wrong Bold__`,
|
|
114322
|
+
* `* italic*`, `Hello* Wrong Italic*`, `_ italic_`, or `Hello_ Wrong Italic_`
|
|
114323
|
+
* and converts them to proper strong/emphasis nodes, matching the behavior of the legacy rdmd engine.
|
|
114324
|
+
*
|
|
114325
|
+
* Supports both asterisk (`**bold**`, `*italic*`) and underscore (`__bold__`, `_italic_`) syntax.
|
|
114326
|
+
* Also supports snake_case content like `** some_snake_case**`.
|
|
114327
|
+
*
|
|
114328
|
+
* This runs after remark-parse, which (in v11+) is strict and doesn't parse
|
|
114329
|
+
* malformed emphasis syntax. This plugin post-processes the AST to handle these cases.
|
|
114181
114330
|
*/
|
|
114182
|
-
const
|
|
114183
|
-
|
|
114184
|
-
|
|
114185
|
-
|
|
114186
|
-
|
|
114187
|
-
|
|
114188
|
-
|
|
114189
|
-
|
|
114190
|
-
|
|
114191
|
-
|
|
114192
|
-
|
|
114193
|
-
|
|
114194
|
-
|
|
114195
|
-
|
|
114196
|
-
|
|
114197
|
-
|
|
114331
|
+
const normalizeEmphasisAST = () => (tree) => {
|
|
114332
|
+
visit(tree, 'text', function visitor(node, index, parent) {
|
|
114333
|
+
if (index === undefined || !parent)
|
|
114334
|
+
return undefined;
|
|
114335
|
+
// Skip if inside code blocks or inline code
|
|
114336
|
+
if (parent.type === 'inlineCode' || parent.type === 'code') {
|
|
114337
|
+
return undefined;
|
|
114338
|
+
}
|
|
114339
|
+
const text = node.value;
|
|
114340
|
+
const allMatches = [];
|
|
114341
|
+
[...text.matchAll(asteriskBoldRegex)].forEach(match => {
|
|
114342
|
+
allMatches.push({ isBold: true, marker: '**', match });
|
|
114343
|
+
});
|
|
114344
|
+
[...text.matchAll(underscoreBoldRegex)].forEach(match => {
|
|
114345
|
+
allMatches.push({ isBold: true, marker: '__', match });
|
|
114346
|
+
});
|
|
114347
|
+
[...text.matchAll(asteriskItalicRegex)].forEach(match => {
|
|
114348
|
+
allMatches.push({ isBold: false, marker: '*', match });
|
|
114349
|
+
});
|
|
114350
|
+
[...text.matchAll(underscoreItalicRegex)].forEach(match => {
|
|
114351
|
+
allMatches.push({ isBold: false, marker: '_', match });
|
|
114352
|
+
});
|
|
114353
|
+
[...text.matchAll(intrawordUnderscoreItalicRegex)].forEach(match => {
|
|
114354
|
+
allMatches.push({ isBold: false, isIntraword: true, marker: '_', match });
|
|
114355
|
+
});
|
|
114356
|
+
[...text.matchAll(intrawordUnderscoreBoldRegex)].forEach(match => {
|
|
114357
|
+
allMatches.push({ isBold: true, isIntraword: true, marker: '__', match });
|
|
114358
|
+
});
|
|
114359
|
+
[...text.matchAll(intrawordAsteriskItalicRegex)].forEach(match => {
|
|
114360
|
+
allMatches.push({ isBold: false, isIntraword: true, marker: '*', match });
|
|
114361
|
+
});
|
|
114362
|
+
[...text.matchAll(intrawordAsteriskBoldRegex)].forEach(match => {
|
|
114363
|
+
allMatches.push({ isBold: true, isIntraword: true, marker: '**', match });
|
|
114364
|
+
});
|
|
114365
|
+
if (allMatches.length === 0)
|
|
114366
|
+
return undefined;
|
|
114367
|
+
allMatches.sort((a, b) => (a.match.index ?? 0) - (b.match.index ?? 0));
|
|
114368
|
+
const filteredMatches = [];
|
|
114369
|
+
let lastEnd = 0;
|
|
114370
|
+
allMatches.forEach(info => {
|
|
114371
|
+
const start = info.match.index ?? 0;
|
|
114372
|
+
const end = start + info.match[0].length;
|
|
114373
|
+
if (start >= lastEnd) {
|
|
114374
|
+
filteredMatches.push(info);
|
|
114375
|
+
lastEnd = end;
|
|
114376
|
+
}
|
|
114377
|
+
});
|
|
114378
|
+
if (filteredMatches.length === 0)
|
|
114379
|
+
return undefined;
|
|
114380
|
+
const parts = [];
|
|
114381
|
+
let lastIndex = 0;
|
|
114382
|
+
filteredMatches.forEach(({ isBold, isIntraword, marker, match }) => {
|
|
114383
|
+
const matchIndex = match.index ?? 0;
|
|
114384
|
+
const fullMatch = match[0];
|
|
114385
|
+
if (isIntraword) {
|
|
114386
|
+
// handles cases like hello_world_ where we only want to italicize 'world'
|
|
114387
|
+
const charBefore = match[1] || ''; // e.g., "l" in "hello_world_"
|
|
114388
|
+
const content = match[2]; // e.g., "world"
|
|
114389
|
+
const combinedBefore = text.slice(lastIndex, matchIndex) + charBefore;
|
|
114390
|
+
if (combinedBefore) {
|
|
114391
|
+
parts.push({ type: 'text', value: combinedBefore });
|
|
114198
114392
|
}
|
|
114199
|
-
if (
|
|
114200
|
-
|
|
114201
|
-
|
|
114202
|
-
|
|
114203
|
-
|
|
114204
|
-
contentParts.push(collectTextContent(node));
|
|
114205
|
-
}
|
|
114206
|
-
// Remove the opening/closing tags and template literal syntax from content
|
|
114207
|
-
let content = contentParts.join('');
|
|
114208
|
-
content = content.replace(/^<HTMLBlock[^>]*>\s*\{?\s*`?/, '').replace(/`?\s*\}?\s*<\/HTMLBlock>$/, '');
|
|
114209
|
-
// Decode protected content that was base64 encoded during preprocessing
|
|
114210
|
-
content = decodeProtectedContent(content);
|
|
114211
|
-
const htmlString = formatHtmlForMdxish(content);
|
|
114212
|
-
const runScripts = extractRunScriptsAttr(attrs);
|
|
114213
|
-
const safeMode = extractBooleanAttr(attrs, 'safeMode');
|
|
114214
|
-
// Replace range with single HTMLBlock node
|
|
114215
|
-
const mdNode = createHTMLBlockNode(htmlString, children[i].position, runScripts, safeMode);
|
|
114216
|
-
root.children.splice(i, closingIdx - i + 1, mdNode);
|
|
114393
|
+
if (isBold) {
|
|
114394
|
+
parts.push({
|
|
114395
|
+
type: 'strong',
|
|
114396
|
+
children: [{ type: 'text', value: content }],
|
|
114397
|
+
});
|
|
114217
114398
|
}
|
|
114399
|
+
else {
|
|
114400
|
+
parts.push({
|
|
114401
|
+
type: 'emphasis',
|
|
114402
|
+
children: [{ type: 'text', value: content }],
|
|
114403
|
+
});
|
|
114404
|
+
}
|
|
114405
|
+
lastIndex = matchIndex + fullMatch.length;
|
|
114406
|
+
return;
|
|
114218
114407
|
}
|
|
114219
|
-
|
|
114220
|
-
|
|
114221
|
-
|
|
114222
|
-
|
|
114223
|
-
visit(tree, 'html', (node, index, parent) => {
|
|
114224
|
-
if (!parent || index === undefined)
|
|
114225
|
-
return;
|
|
114226
|
-
const value = node.value;
|
|
114227
|
-
if (!value)
|
|
114228
|
-
return;
|
|
114229
|
-
// Case 1: Full HTMLBlock in single node
|
|
114230
|
-
const fullMatch = value.match(/^<HTMLBlock(\s[^>]*)?>([\s\S]*)<\/HTMLBlock>$/);
|
|
114231
|
-
if (fullMatch) {
|
|
114232
|
-
const attrs = fullMatch[1] || '';
|
|
114233
|
-
let content = fullMatch[2] || '';
|
|
114234
|
-
// Remove template literal syntax if present: {`...`}
|
|
114235
|
-
content = content.replace(/^\s*\{\s*`/, '').replace(/`\s*\}\s*$/, '');
|
|
114236
|
-
// Decode protected content that was base64 encoded during preprocessing
|
|
114237
|
-
content = decodeProtectedContent(content);
|
|
114238
|
-
const htmlString = formatHtmlForMdxish(content);
|
|
114239
|
-
const runScripts = extractRunScriptsAttr(attrs);
|
|
114240
|
-
const safeMode = extractBooleanAttr(attrs, 'safeMode');
|
|
114241
|
-
parent.children[index] = createHTMLBlockNode(htmlString, node.position, runScripts, safeMode);
|
|
114242
|
-
return;
|
|
114243
|
-
}
|
|
114244
|
-
// Case 2: Opening tag only (split by blank lines)
|
|
114245
|
-
if (value === '<HTMLBlock>' || value.match(/^<HTMLBlock\s[^>]*>$/)) {
|
|
114246
|
-
const siblings = parent.children;
|
|
114247
|
-
let closingIdx = -1;
|
|
114248
|
-
// Find closing tag in siblings
|
|
114249
|
-
for (let i = index + 1; i < siblings.length; i += 1) {
|
|
114250
|
-
const sibling = siblings[i];
|
|
114251
|
-
if (sibling.type === 'html') {
|
|
114252
|
-
const sibVal = sibling.value;
|
|
114253
|
-
if (sibVal === '</HTMLBlock>' || sibVal?.includes('</HTMLBlock>')) {
|
|
114254
|
-
closingIdx = i;
|
|
114255
|
-
break;
|
|
114256
|
-
}
|
|
114408
|
+
if (matchIndex > lastIndex) {
|
|
114409
|
+
const beforeText = text.slice(lastIndex, matchIndex);
|
|
114410
|
+
if (beforeText) {
|
|
114411
|
+
parts.push({ type: 'text', value: beforeText });
|
|
114257
114412
|
}
|
|
114258
114413
|
}
|
|
114259
|
-
|
|
114260
|
-
|
|
114261
|
-
//
|
|
114262
|
-
const
|
|
114263
|
-
|
|
114264
|
-
|
|
114265
|
-
|
|
114266
|
-
|
|
114267
|
-
|
|
114268
|
-
|
|
114269
|
-
|
|
114270
|
-
|
|
114271
|
-
|
|
114272
|
-
|
|
114273
|
-
|
|
114414
|
+
const wordBefore = match[1]; // e.g., "Hello" in "Hello** Wrong Bold**"
|
|
114415
|
+
const contentWithSpaceAfter = match[3]; // Content when there's a space after opening markers
|
|
114416
|
+
const trailingSpace1 = match[4] || ''; // Space before closing markers (for "** text **" pattern)
|
|
114417
|
+
const contentWithSpaceBefore = match[5]; // Content when there's only a space before closing markers
|
|
114418
|
+
const trailingSpace2 = match[6] || ''; // Space before closing markers (for "**text **" pattern)
|
|
114419
|
+
const trailingSpace = trailingSpace1 || trailingSpace2; // Combined trailing space
|
|
114420
|
+
const content = (contentWithSpaceAfter || contentWithSpaceBefore || '').trim();
|
|
114421
|
+
const afterChar = match[7]; // Character after closing markers (if any)
|
|
114422
|
+
const markerPos = fullMatch.indexOf(marker);
|
|
114423
|
+
const spacesBeforeMarkers = wordBefore
|
|
114424
|
+
? fullMatch.slice(wordBefore.length, markerPos)
|
|
114425
|
+
: fullMatch.slice(0, markerPos);
|
|
114426
|
+
const shouldAddSpace = !!contentWithSpaceAfter && !!wordBefore && !spacesBeforeMarkers;
|
|
114427
|
+
if (wordBefore) {
|
|
114428
|
+
const spacing = spacesBeforeMarkers + (shouldAddSpace ? ' ' : '');
|
|
114429
|
+
parts.push({ type: 'text', value: wordBefore + spacing });
|
|
114274
114430
|
}
|
|
114275
|
-
|
|
114276
|
-
|
|
114277
|
-
const htmlString = formatHtmlForMdxish(decodedContent);
|
|
114278
|
-
const runScripts = extractRunScriptsAttr(value);
|
|
114279
|
-
const safeMode = extractBooleanAttr(value, 'safeMode');
|
|
114280
|
-
// Replace opening tag with HTMLBlock node, remove consumed siblings
|
|
114281
|
-
parent.children[index] = createHTMLBlockNode(htmlString, node.position, runScripts, safeMode);
|
|
114282
|
-
parent.children.splice(index + 1, closingIdx - index);
|
|
114283
|
-
}
|
|
114284
|
-
});
|
|
114285
|
-
// Handle HTMLBlock inside paragraphs (parsed as inline elements)
|
|
114286
|
-
visit(tree, 'paragraph', (node, index, parent) => {
|
|
114287
|
-
if (!parent || index === undefined)
|
|
114288
|
-
return;
|
|
114289
|
-
const children = node.children || [];
|
|
114290
|
-
let htmlBlockStartIdx = -1;
|
|
114291
|
-
let htmlBlockEndIdx = -1;
|
|
114292
|
-
let templateLiteralStartIdx = -1;
|
|
114293
|
-
let templateLiteralEndIdx = -1;
|
|
114294
|
-
for (let i = 0; i < children.length; i += 1) {
|
|
114295
|
-
const child = children[i];
|
|
114296
|
-
if (child.type === 'html' && typeof child.value === 'string') {
|
|
114297
|
-
const value = child.value;
|
|
114298
|
-
if (value === '<HTMLBlock>' || value.match(/^<HTMLBlock\s[^>]*>$/)) {
|
|
114299
|
-
htmlBlockStartIdx = i;
|
|
114300
|
-
}
|
|
114301
|
-
else if (value === '</HTMLBlock>') {
|
|
114302
|
-
htmlBlockEndIdx = i;
|
|
114303
|
-
}
|
|
114431
|
+
else if (spacesBeforeMarkers) {
|
|
114432
|
+
parts.push({ type: 'text', value: spacesBeforeMarkers });
|
|
114304
114433
|
}
|
|
114305
|
-
|
|
114306
|
-
|
|
114307
|
-
|
|
114308
|
-
|
|
114309
|
-
|
|
114434
|
+
if (content) {
|
|
114435
|
+
if (isBold) {
|
|
114436
|
+
parts.push({
|
|
114437
|
+
type: 'strong',
|
|
114438
|
+
children: [{ type: 'text', value: content }],
|
|
114439
|
+
});
|
|
114310
114440
|
}
|
|
114311
|
-
|
|
114312
|
-
|
|
114313
|
-
|
|
114314
|
-
|
|
114315
|
-
|
|
114316
|
-
templateLiteralEndIdx = i;
|
|
114441
|
+
else {
|
|
114442
|
+
parts.push({
|
|
114443
|
+
type: 'emphasis',
|
|
114444
|
+
children: [{ type: 'text', value: content }],
|
|
114445
|
+
});
|
|
114317
114446
|
}
|
|
114318
114447
|
}
|
|
114319
|
-
|
|
114320
|
-
|
|
114321
|
-
|
|
114322
|
-
|
|
114323
|
-
|
|
114324
|
-
|
|
114325
|
-
|
|
114326
|
-
|
|
114327
|
-
|
|
114328
|
-
|
|
114329
|
-
const child = children[i];
|
|
114330
|
-
templateContent.push(collectTextContent(child));
|
|
114448
|
+
if (afterChar) {
|
|
114449
|
+
const prefix = trailingSpace ? ' ' : '';
|
|
114450
|
+
parts.push({ type: 'text', value: prefix + afterChar });
|
|
114451
|
+
}
|
|
114452
|
+
lastIndex = matchIndex + fullMatch.length;
|
|
114453
|
+
});
|
|
114454
|
+
if (lastIndex < text.length) {
|
|
114455
|
+
const remainingText = text.slice(lastIndex);
|
|
114456
|
+
if (remainingText) {
|
|
114457
|
+
parts.push({ type: 'text', value: remainingText });
|
|
114331
114458
|
}
|
|
114332
|
-
// Decode protected content that was base64 encoded during preprocessing
|
|
114333
|
-
const decodedContent = decodeProtectedContent(templateContent.join(''));
|
|
114334
|
-
const htmlString = formatHtmlForMdxish(decodedContent);
|
|
114335
|
-
const runScripts = openingTag.value ? extractRunScriptsAttr(openingTag.value) : undefined;
|
|
114336
|
-
const safeMode = openingTag.value ? extractBooleanAttr(openingTag.value, 'safeMode') : undefined;
|
|
114337
|
-
const mdNode = createHTMLBlockNode(htmlString, node.position, runScripts, safeMode);
|
|
114338
|
-
parent.children[index] = mdNode;
|
|
114339
114459
|
}
|
|
114340
|
-
|
|
114341
|
-
|
|
114342
|
-
|
|
114343
|
-
const html = node.data?.hProperties?.html;
|
|
114344
|
-
if (html &&
|
|
114345
|
-
(!node.children ||
|
|
114346
|
-
node.children.length === 0 ||
|
|
114347
|
-
(node.children.length === 1 && node.children[0].type === 'text' && node.children[0].value !== html))) {
|
|
114348
|
-
node.children = [
|
|
114349
|
-
{
|
|
114350
|
-
type: 'text',
|
|
114351
|
-
value: html,
|
|
114352
|
-
},
|
|
114353
|
-
];
|
|
114460
|
+
if (parts.length > 0) {
|
|
114461
|
+
parent.children.splice(index, 1, ...parts);
|
|
114462
|
+
return [SKIP, index + parts.length];
|
|
114354
114463
|
}
|
|
114464
|
+
return undefined;
|
|
114355
114465
|
});
|
|
114466
|
+
// Handle malformed emphasis spanning multiple nodes (e.g., **text [link](url) **)
|
|
114467
|
+
visitMultiNodeEmphasis(tree);
|
|
114356
114468
|
return tree;
|
|
114357
114469
|
};
|
|
114358
|
-
/* harmony default export */ const
|
|
114470
|
+
/* harmony default export */ const normalize_malformed_md_syntax = (normalizeEmphasisAST);
|
|
114471
|
+
|
|
114472
|
+
;// ./processor/transform/mdxish/magic-blocks/placeholder.ts
|
|
114473
|
+
const EMPTY_IMAGE_PLACEHOLDER = {
|
|
114474
|
+
type: 'image',
|
|
114475
|
+
url: '',
|
|
114476
|
+
alt: '',
|
|
114477
|
+
title: '',
|
|
114478
|
+
data: { hProperties: {} },
|
|
114479
|
+
};
|
|
114480
|
+
const EMPTY_EMBED_PLACEHOLDER = {
|
|
114481
|
+
type: 'embed',
|
|
114482
|
+
children: [{ type: 'link', url: '', title: '', children: [{ type: 'text', value: '' }] }],
|
|
114483
|
+
data: { hName: 'embed-block', hProperties: { url: '', href: '', title: '' } },
|
|
114484
|
+
};
|
|
114485
|
+
const EMPTY_RECIPE_PLACEHOLDER = {
|
|
114486
|
+
type: 'mdxJsxFlowElement',
|
|
114487
|
+
name: 'Recipe',
|
|
114488
|
+
attributes: [],
|
|
114489
|
+
children: [],
|
|
114490
|
+
};
|
|
114491
|
+
const EMPTY_CALLOUT_PLACEHOLDER = {
|
|
114492
|
+
type: 'mdxJsxFlowElement',
|
|
114493
|
+
name: 'Callout',
|
|
114494
|
+
attributes: [
|
|
114495
|
+
{ type: 'mdxJsxAttribute', name: 'icon', value: '📘' },
|
|
114496
|
+
{ type: 'mdxJsxAttribute', name: 'theme', value: 'info' },
|
|
114497
|
+
{ type: 'mdxJsxAttribute', name: 'type', value: 'info' },
|
|
114498
|
+
{ type: 'mdxJsxAttribute', name: 'empty', value: 'true' },
|
|
114499
|
+
],
|
|
114500
|
+
children: [{ type: 'heading', depth: 3, children: [{ type: 'text', value: '' }] }],
|
|
114501
|
+
};
|
|
114502
|
+
const EMPTY_TABLE_PLACEHOLDER = {
|
|
114503
|
+
type: 'table',
|
|
114504
|
+
align: ['left', 'left'],
|
|
114505
|
+
children: [
|
|
114506
|
+
{ type: 'tableRow', children: [{ type: 'tableCell', children: [{ type: 'text', value: '' }] }] },
|
|
114507
|
+
{ type: 'tableRow', children: [{ type: 'tableCell', children: [{ type: 'text', value: '' }] }] },
|
|
114508
|
+
],
|
|
114509
|
+
};
|
|
114510
|
+
const EMPTY_CODE_PLACEHOLDER = {
|
|
114511
|
+
type: 'code',
|
|
114512
|
+
value: '',
|
|
114513
|
+
lang: null,
|
|
114514
|
+
meta: null,
|
|
114515
|
+
};
|
|
114516
|
+
|
|
114517
|
+
;// ./processor/transform/mdxish/magic-blocks/magic-block-transformer.ts
|
|
114518
|
+
|
|
114359
114519
|
|
|
114360
|
-
;// ./processor/transform/mdxish/mdxish-magic-blocks.ts
|
|
114361
114520
|
|
|
114362
114521
|
|
|
114363
114522
|
|
|
114364
114523
|
|
|
114365
114524
|
|
|
114366
114525
|
/**
|
|
114367
|
-
*
|
|
114368
|
-
* Group 1: block type (e.g., "image", "code", "callout")
|
|
114369
|
-
* Group 2: JSON content between the tags
|
|
114370
|
-
* Taken from the v6 branch
|
|
114371
|
-
*/
|
|
114372
|
-
const RGXP = /^\s*\[block:([^\]]*)\]([^]+?)\[\/block\]/;
|
|
114373
|
-
/**
|
|
114374
|
-
* Wraps a node in a "pinned" container if sidebar: true is set in the JSON.
|
|
114375
|
-
* Pinned blocks are displayed in a sidebar/floating position in the UI.
|
|
114526
|
+
* Wraps a node in a "pinned" container if sidebar: true is set.
|
|
114376
114527
|
*/
|
|
114377
|
-
const wrapPinnedBlocks = (node,
|
|
114378
|
-
if (!
|
|
114528
|
+
const wrapPinnedBlocks = (node, data) => {
|
|
114529
|
+
if (!data.sidebar)
|
|
114379
114530
|
return node;
|
|
114380
114531
|
return {
|
|
114381
114532
|
children: [node],
|
|
@@ -114391,34 +114542,24 @@ const imgSizeValues = {
|
|
|
114391
114542
|
original: 'auto',
|
|
114392
114543
|
};
|
|
114393
114544
|
/**
|
|
114394
|
-
* Proxy that resolves image sizing values
|
|
114395
|
-
* - "full" → "100%", "original" → "auto" (from imgSizeValues)
|
|
114396
|
-
* - Pure numbers like "50" → "50%" (percentage)
|
|
114397
|
-
* - Anything else passes through as-is (e.g., "200px")
|
|
114545
|
+
* Proxy that resolves image sizing values.
|
|
114398
114546
|
*/
|
|
114399
114547
|
const imgWidthBySize = new Proxy(imgSizeValues, {
|
|
114400
114548
|
get: (widths, size) => (size?.match(/^\d+$/) ? `${size}%` : size in widths ? widths[size] : size),
|
|
114401
114549
|
});
|
|
114402
|
-
// Simple text to inline nodes (just returns text node - no markdown parsing)
|
|
114403
114550
|
const textToInline = (text) => [{ type: 'text', value: text }];
|
|
114404
|
-
// Simple text to block nodes (wraps in paragraph)
|
|
114405
114551
|
const textToBlock = (text) => [{ children: textToInline(text), type: 'paragraph' }];
|
|
114406
114552
|
/** Parses markdown and html to markdown nodes */
|
|
114407
|
-
const contentParser = unified().use(remarkParse).use(remarkGfm);
|
|
114408
|
-
// Table cells may contain html or markdown content, so we need to parse it accordingly instead of keeping it as raw text
|
|
114553
|
+
const contentParser = unified().use(remarkParse).use(remarkGfm).use(normalize_malformed_md_syntax);
|
|
114409
114554
|
const parseTableCell = (text) => {
|
|
114410
114555
|
if (!text.trim())
|
|
114411
114556
|
return [{ type: 'text', value: '' }];
|
|
114412
114557
|
const tree = contentParser.runSync(contentParser.parse(text));
|
|
114413
|
-
// If there are multiple block-level nodes, keep them as-is to preserve the document structure and spacing
|
|
114414
114558
|
if (tree.children.length > 1) {
|
|
114415
114559
|
return tree.children;
|
|
114416
114560
|
}
|
|
114417
|
-
return tree.children.flatMap(n =>
|
|
114418
|
-
// This unwraps the extra p node that might appear & wrapping the content
|
|
114419
|
-
n.type === 'paragraph' && 'children' in n ? n.children : [n]);
|
|
114561
|
+
return tree.children.flatMap(n => n.type === 'paragraph' && 'children' in n ? n.children : [n]);
|
|
114420
114562
|
};
|
|
114421
|
-
// Parse markdown/HTML into block-level nodes (preserves paragraphs, headings, lists, etc.)
|
|
114422
114563
|
const parseBlock = (text) => {
|
|
114423
114564
|
if (!text.trim())
|
|
114424
114565
|
return [{ type: 'paragraph', children: [{ type: 'text', value: '' }] }];
|
|
@@ -114426,44 +114567,43 @@ const parseBlock = (text) => {
|
|
|
114426
114567
|
return tree.children;
|
|
114427
114568
|
};
|
|
114428
114569
|
/**
|
|
114429
|
-
*
|
|
114430
|
-
|
|
114431
|
-
|
|
114432
|
-
|
|
114433
|
-
|
|
114434
|
-
|
|
114435
|
-
|
|
114436
|
-
|
|
114437
|
-
|
|
114438
|
-
|
|
114439
|
-
|
|
114440
|
-
|
|
114441
|
-
|
|
114442
|
-
|
|
114443
|
-
|
|
114444
|
-
|
|
114445
|
-
|
|
114446
|
-
|
|
114570
|
+
* Transform a magicBlock node into final MDAST nodes.
|
|
114571
|
+
*/
|
|
114572
|
+
function transformMagicBlock(blockType, data, rawValue, options = {}) {
|
|
114573
|
+
const { compatibilityMode = false, safeMode = false } = options;
|
|
114574
|
+
// Handle empty data by returning placeholder nodes for known block types
|
|
114575
|
+
// This allows the editor to show appropriate placeholder UI instead of nothing
|
|
114576
|
+
if (Object.keys(data).length < 1) {
|
|
114577
|
+
switch (blockType) {
|
|
114578
|
+
case 'image':
|
|
114579
|
+
return [EMPTY_IMAGE_PLACEHOLDER];
|
|
114580
|
+
case 'embed':
|
|
114581
|
+
return [EMPTY_EMBED_PLACEHOLDER];
|
|
114582
|
+
case 'code':
|
|
114583
|
+
return [EMPTY_CODE_PLACEHOLDER];
|
|
114584
|
+
case 'callout':
|
|
114585
|
+
return [EMPTY_CALLOUT_PLACEHOLDER];
|
|
114586
|
+
case 'parameters':
|
|
114587
|
+
case 'table':
|
|
114588
|
+
return [EMPTY_TABLE_PLACEHOLDER];
|
|
114589
|
+
case 'recipe':
|
|
114590
|
+
case 'tutorial-tile':
|
|
114591
|
+
return [EMPTY_RECIPE_PLACEHOLDER];
|
|
114592
|
+
default:
|
|
114593
|
+
return [{ type: 'paragraph', children: [{ type: 'text', value: rawValue }] }];
|
|
114594
|
+
}
|
|
114447
114595
|
}
|
|
114448
|
-
|
|
114449
|
-
// eslint-disable-next-line no-console
|
|
114450
|
-
console.error('Invalid Magic Block JSON:', err);
|
|
114451
|
-
if (alwaysThrow)
|
|
114452
|
-
throw new Error('Invalid Magic Block JSON');
|
|
114453
|
-
return [];
|
|
114454
|
-
}
|
|
114455
|
-
if (Object.keys(json).length < 1)
|
|
114456
|
-
return [];
|
|
114457
|
-
// Each case handles a different magic block type and returns appropriate MDAST nodes
|
|
114458
|
-
switch (type) {
|
|
114459
|
-
// Code blocks: single code block or tabbed code blocks (multiple languages)
|
|
114596
|
+
switch (blockType) {
|
|
114460
114597
|
case 'code': {
|
|
114461
|
-
const codeJson =
|
|
114598
|
+
const codeJson = data;
|
|
114599
|
+
if (!codeJson.codes || !Array.isArray(codeJson.codes)) {
|
|
114600
|
+
return [wrapPinnedBlocks(EMPTY_CODE_PLACEHOLDER, data)];
|
|
114601
|
+
}
|
|
114462
114602
|
const children = codeJson.codes.map(obj => ({
|
|
114463
114603
|
className: 'tab-panel',
|
|
114464
114604
|
data: { hName: 'code', hProperties: { lang: obj.language, meta: obj.name || null } },
|
|
114465
114605
|
lang: obj.language,
|
|
114466
|
-
meta: obj.name || null,
|
|
114606
|
+
meta: obj.name || null,
|
|
114467
114607
|
type: 'code',
|
|
114468
114608
|
value: obj.code.trim(),
|
|
114469
114609
|
}));
|
|
@@ -114473,31 +114613,31 @@ function parseMagicBlock(raw, options = {}) {
|
|
|
114473
114613
|
if (!children[0].value)
|
|
114474
114614
|
return [];
|
|
114475
114615
|
if (!(children[0].meta || children[0].lang))
|
|
114476
|
-
return [wrapPinnedBlocks(children[0],
|
|
114616
|
+
return [wrapPinnedBlocks(children[0], data)];
|
|
114477
114617
|
}
|
|
114478
114618
|
// Multiple code blocks or a single code block with a tab name (meta or language) renders as a code tabs block
|
|
114479
|
-
return [wrapPinnedBlocks({ children, className: 'tabs', data: { hName: 'CodeTabs' }, type: 'code-tabs' },
|
|
114619
|
+
return [wrapPinnedBlocks({ children, className: 'tabs', data: { hName: 'CodeTabs' }, type: 'code-tabs' }, data)];
|
|
114480
114620
|
}
|
|
114481
|
-
// API header: renders as a heading element (h1-h6)
|
|
114482
114621
|
case 'api-header': {
|
|
114483
|
-
const headerJson =
|
|
114484
|
-
// In compatibility mode, default to h1; otherwise h2
|
|
114622
|
+
const headerJson = data;
|
|
114485
114623
|
const depth = headerJson.level || (compatibilityMode ? 1 : 2);
|
|
114486
114624
|
return [
|
|
114487
114625
|
wrapPinnedBlocks({
|
|
114488
114626
|
children: 'title' in headerJson ? textToInline(headerJson.title || '') : [],
|
|
114489
114627
|
depth,
|
|
114490
114628
|
type: 'heading',
|
|
114491
|
-
},
|
|
114629
|
+
}, data),
|
|
114492
114630
|
];
|
|
114493
114631
|
}
|
|
114494
|
-
// Image block: renders as <img> or <figure> with caption
|
|
114495
114632
|
case 'image': {
|
|
114496
|
-
const imageJson =
|
|
114633
|
+
const imageJson = data;
|
|
114634
|
+
if (!imageJson.images || !Array.isArray(imageJson.images)) {
|
|
114635
|
+
return [wrapPinnedBlocks(EMPTY_IMAGE_PLACEHOLDER, data)];
|
|
114636
|
+
}
|
|
114497
114637
|
const imgData = imageJson.images.find(i => i.image);
|
|
114498
|
-
if (!imgData?.image)
|
|
114499
|
-
return [];
|
|
114500
|
-
|
|
114638
|
+
if (!imgData?.image) {
|
|
114639
|
+
return [wrapPinnedBlocks(EMPTY_IMAGE_PLACEHOLDER, data)];
|
|
114640
|
+
}
|
|
114501
114641
|
const [url, title, alt] = imgData.image;
|
|
114502
114642
|
const block = {
|
|
114503
114643
|
alt: alt || imgData.caption || '',
|
|
@@ -114512,57 +114652,66 @@ function parseMagicBlock(raw, options = {}) {
|
|
|
114512
114652
|
type: 'image',
|
|
114513
114653
|
url,
|
|
114514
114654
|
};
|
|
114515
|
-
// Wrap in <figure> if caption is present
|
|
114516
114655
|
const img = imgData.caption
|
|
114517
114656
|
? {
|
|
114518
114657
|
children: [
|
|
114519
114658
|
block,
|
|
114520
|
-
{ children:
|
|
114659
|
+
{ children: parseBlock(imgData.caption), data: { hName: 'figcaption' }, type: 'figcaption' },
|
|
114521
114660
|
],
|
|
114522
114661
|
data: { hName: 'figure' },
|
|
114523
114662
|
type: 'figure',
|
|
114524
114663
|
url,
|
|
114525
114664
|
}
|
|
114526
114665
|
: block;
|
|
114527
|
-
return [wrapPinnedBlocks(img,
|
|
114666
|
+
return [wrapPinnedBlocks(img, data)];
|
|
114528
114667
|
}
|
|
114529
|
-
// Callout: info/warning/error boxes with icon and theme
|
|
114530
114668
|
case 'callout': {
|
|
114531
|
-
const calloutJson =
|
|
114532
|
-
// Preset callout types map to [icon, theme] tuples
|
|
114669
|
+
const calloutJson = data;
|
|
114533
114670
|
const types = {
|
|
114534
114671
|
danger: ['❗️', 'error'],
|
|
114535
114672
|
info: ['📘', 'info'],
|
|
114536
114673
|
success: ['👍', 'okay'],
|
|
114537
114674
|
warning: ['🚧', 'warn'],
|
|
114538
114675
|
};
|
|
114539
|
-
// Resolve type to [icon, theme] - use preset if available, otherwise custom
|
|
114540
114676
|
const resolvedType = typeof calloutJson.type === 'string' && calloutJson.type in types
|
|
114541
114677
|
? types[calloutJson.type]
|
|
114542
114678
|
: [calloutJson.icon || '👍', typeof calloutJson.type === 'string' ? calloutJson.type : 'default'];
|
|
114543
114679
|
const [icon, theme] = Array.isArray(resolvedType) ? resolvedType : ['👍', 'default'];
|
|
114544
114680
|
if (!(calloutJson.title || calloutJson.body))
|
|
114545
114681
|
return [];
|
|
114546
|
-
|
|
114547
|
-
const
|
|
114548
|
-
const
|
|
114682
|
+
const hasTitle = !!calloutJson.title?.trim();
|
|
114683
|
+
const hasBody = !!calloutJson.body?.trim();
|
|
114684
|
+
const empty = !hasTitle;
|
|
114549
114685
|
const children = [];
|
|
114550
|
-
if (
|
|
114551
|
-
const
|
|
114552
|
-
|
|
114686
|
+
if (hasTitle) {
|
|
114687
|
+
const titleBlocks = parseBlock(calloutJson.title || '');
|
|
114688
|
+
if (titleBlocks.length > 0 && titleBlocks[0].type === 'paragraph') {
|
|
114689
|
+
const firstTitle = titleBlocks[0];
|
|
114690
|
+
const heading = {
|
|
114691
|
+
type: 'heading',
|
|
114692
|
+
depth: 3,
|
|
114693
|
+
children: (firstTitle.children || []),
|
|
114694
|
+
};
|
|
114695
|
+
children.push(heading);
|
|
114696
|
+
children.push(...titleBlocks.slice(1));
|
|
114697
|
+
}
|
|
114698
|
+
else {
|
|
114699
|
+
children.push(...titleBlocks);
|
|
114700
|
+
}
|
|
114701
|
+
}
|
|
114702
|
+
else {
|
|
114703
|
+
// Add empty heading placeholder so body goes to children.slice(1)
|
|
114704
|
+
// The Callout component expects children[0] to be the heading
|
|
114705
|
+
children.push({
|
|
114553
114706
|
type: 'heading',
|
|
114554
114707
|
depth: 3,
|
|
114555
|
-
children:
|
|
114556
|
-
};
|
|
114557
|
-
children.push(heading);
|
|
114558
|
-
children.push(...titleBlocks.slice(1), ...bodyBlocks);
|
|
114708
|
+
children: [{ type: 'text', value: '' }],
|
|
114709
|
+
});
|
|
114559
114710
|
}
|
|
114560
|
-
|
|
114561
|
-
|
|
114711
|
+
if (hasBody) {
|
|
114712
|
+
const bodyBlocks = parseBlock(calloutJson.body || '');
|
|
114713
|
+
children.push(...bodyBlocks);
|
|
114562
114714
|
}
|
|
114563
|
-
// If there is no title or title is empty
|
|
114564
|
-
const empty = !titleBlocks.length || !titleBlocks[0].children[0]?.value;
|
|
114565
|
-
// Create mdxJsxFlowElement directly for mdxish
|
|
114566
114715
|
const calloutElement = {
|
|
114567
114716
|
type: 'mdxJsxFlowElement',
|
|
114568
114717
|
name: 'Callout',
|
|
@@ -114574,23 +114723,17 @@ function parseMagicBlock(raw, options = {}) {
|
|
|
114574
114723
|
]),
|
|
114575
114724
|
children: children,
|
|
114576
114725
|
};
|
|
114577
|
-
return [wrapPinnedBlocks(calloutElement,
|
|
114726
|
+
return [wrapPinnedBlocks(calloutElement, data)];
|
|
114578
114727
|
}
|
|
114579
|
-
// Parameters: renders as a table (used for API parameters, etc.)
|
|
114580
114728
|
case 'parameters': {
|
|
114581
|
-
const paramsJson =
|
|
114582
|
-
const { cols, data, rows } = paramsJson;
|
|
114583
|
-
if (!Object.keys(
|
|
114729
|
+
const paramsJson = data;
|
|
114730
|
+
const { cols, data: tableData, rows } = paramsJson;
|
|
114731
|
+
if (!tableData || !Object.keys(tableData).length)
|
|
114584
114732
|
return [];
|
|
114585
|
-
|
|
114586
|
-
|
|
114587
|
-
|
|
114588
|
-
* Example: { "h-0": "Name", "h-1": "Type", "0-0": "id", "0-1": "string" }
|
|
114589
|
-
* Becomes: [["Name", "Type"], ["id", "string"]]
|
|
114590
|
-
*/
|
|
114591
|
-
const sparseData = Object.entries(data).reduce((mapped, [key, v]) => {
|
|
114733
|
+
if (typeof cols !== 'number' || typeof rows !== 'number' || cols < 1 || rows < 0)
|
|
114734
|
+
return [];
|
|
114735
|
+
const sparseData = Object.entries(tableData).reduce((mapped, [key, v]) => {
|
|
114592
114736
|
const [row, col] = key.split('-');
|
|
114593
|
-
// Header row ("h") becomes index 0, data rows are offset by 1
|
|
114594
114737
|
const rowIndex = row === 'h' ? 0 : parseInt(row, 10) + 1;
|
|
114595
114738
|
const colIndex = parseInt(col, 10);
|
|
114596
114739
|
if (!mapped[rowIndex])
|
|
@@ -114598,9 +114741,8 @@ function parseMagicBlock(raw, options = {}) {
|
|
|
114598
114741
|
mapped[rowIndex][colIndex] = v;
|
|
114599
114742
|
return mapped;
|
|
114600
114743
|
}, []);
|
|
114601
|
-
// In compatibility mode, wrap cell content in paragraphs; otherwise inline text
|
|
114602
114744
|
const tokenizeCell = compatibilityMode ? textToBlock : parseTableCell;
|
|
114603
|
-
const
|
|
114745
|
+
const tableChildren = Array.from({ length: rows + 1 }, (_, y) => ({
|
|
114604
114746
|
children: Array.from({ length: cols }, (__, x) => ({
|
|
114605
114747
|
children: sparseData[y]?.[x] ? tokenizeCell(sparseData[y][x]) : [{ type: 'text', value: '' }],
|
|
114606
114748
|
type: y === 0 ? 'tableHead' : 'tableCell',
|
|
@@ -114608,14 +114750,15 @@ function parseMagicBlock(raw, options = {}) {
|
|
|
114608
114750
|
type: 'tableRow',
|
|
114609
114751
|
}));
|
|
114610
114752
|
return [
|
|
114611
|
-
wrapPinnedBlocks({ align: paramsJson.align ?? new Array(cols).fill('left'), children, type: 'table' },
|
|
114753
|
+
wrapPinnedBlocks({ align: paramsJson.align ?? new Array(cols).fill('left'), children: tableChildren, type: 'table' }, data),
|
|
114612
114754
|
];
|
|
114613
114755
|
}
|
|
114614
|
-
// Embed: external content (YouTube, etc.) with provider detection
|
|
114615
114756
|
case 'embed': {
|
|
114616
|
-
const embedJson =
|
|
114757
|
+
const embedJson = data;
|
|
114758
|
+
if (!embedJson.url) {
|
|
114759
|
+
return [wrapPinnedBlocks(EMPTY_EMBED_PLACEHOLDER, data)];
|
|
114760
|
+
}
|
|
114617
114761
|
const { html, title, url } = embedJson;
|
|
114618
|
-
// Extract provider name from URL hostname (e.g., "youtube.com" → "youtube.com")
|
|
114619
114762
|
try {
|
|
114620
114763
|
embedJson.provider = new URL(url).hostname
|
|
114621
114764
|
.split(/(?:www)?\./)
|
|
@@ -114632,12 +114775,13 @@ function parseMagicBlock(raw, options = {}) {
|
|
|
114632
114775
|
],
|
|
114633
114776
|
data: { hName: 'embed-block', hProperties: { ...embedJson, href: url, html, title, url } },
|
|
114634
114777
|
type: 'embed',
|
|
114635
|
-
},
|
|
114778
|
+
}, data),
|
|
114636
114779
|
];
|
|
114637
114780
|
}
|
|
114638
|
-
// HTML block: raw HTML content (scripts enabled only in compatibility mode)
|
|
114639
114781
|
case 'html': {
|
|
114640
|
-
const htmlJson =
|
|
114782
|
+
const htmlJson = data;
|
|
114783
|
+
if (typeof htmlJson.html !== 'string')
|
|
114784
|
+
return [];
|
|
114641
114785
|
return [
|
|
114642
114786
|
wrapPinnedBlocks({
|
|
114643
114787
|
data: {
|
|
@@ -114645,39 +114789,33 @@ function parseMagicBlock(raw, options = {}) {
|
|
|
114645
114789
|
hProperties: { html: htmlJson.html, runScripts: compatibilityMode, safeMode },
|
|
114646
114790
|
},
|
|
114647
114791
|
type: 'html-block',
|
|
114648
|
-
},
|
|
114792
|
+
}, data),
|
|
114649
114793
|
];
|
|
114650
114794
|
}
|
|
114651
|
-
// Recipe/TutorialTile: renders as Recipe component
|
|
114652
114795
|
case 'recipe':
|
|
114653
114796
|
case 'tutorial-tile': {
|
|
114654
|
-
const recipeJson =
|
|
114797
|
+
const recipeJson = data;
|
|
114655
114798
|
if (!recipeJson.slug || !recipeJson.title)
|
|
114656
114799
|
return [];
|
|
114657
|
-
// Create mdxJsxFlowElement directly for mdxish flow
|
|
114658
|
-
// Note: Don't wrap in pinned blocks for mdxish - rehypeMdxishComponents handles component resolution
|
|
114659
|
-
// The node structure matches what mdxishComponentBlocks creates for JSX tags
|
|
114660
114800
|
const recipeNode = {
|
|
114661
114801
|
type: 'mdxJsxFlowElement',
|
|
114662
114802
|
name: 'Recipe',
|
|
114663
114803
|
attributes: toAttributes(recipeJson, ['slug', 'title']),
|
|
114664
114804
|
children: [],
|
|
114665
|
-
// Position is optional but helps with debugging
|
|
114666
114805
|
position: undefined,
|
|
114667
114806
|
};
|
|
114668
114807
|
return [recipeNode];
|
|
114669
114808
|
}
|
|
114670
|
-
// Unknown block types: render as generic div with JSON properties
|
|
114671
114809
|
default: {
|
|
114672
|
-
const text =
|
|
114810
|
+
const text = data.text || data.html || '';
|
|
114673
114811
|
return [
|
|
114674
|
-
wrapPinnedBlocks({ children: textToBlock(text), data: { hName:
|
|
114812
|
+
wrapPinnedBlocks({ children: textToBlock(text), data: { hName: blockType || 'div', hProperties: data, ...data }, type: 'div' }, data),
|
|
114675
114813
|
];
|
|
114676
114814
|
}
|
|
114677
114815
|
}
|
|
114678
114816
|
}
|
|
114679
114817
|
/**
|
|
114680
|
-
*
|
|
114818
|
+
* Check if a child node is a flow element that needs unwrapping.
|
|
114681
114819
|
*/
|
|
114682
114820
|
const blockTypes = [
|
|
114683
114821
|
'heading',
|
|
@@ -114703,29 +114841,19 @@ const blockTypes = [
|
|
|
114703
114841
|
*/
|
|
114704
114842
|
const isBlockNode = (node) => blockTypes.includes(node.type);
|
|
114705
114843
|
/**
|
|
114706
|
-
* Unified plugin that
|
|
114707
|
-
*
|
|
114708
|
-
* During preprocessing, extractMagicBlocks replaces [block:TYPE]...[/block]
|
|
114709
|
-
* with inline code tokens like `__MAGIC_BLOCK_0__`. This plugin finds those
|
|
114710
|
-
* tokens in the parsed MDAST and replaces them with the parsed block content.
|
|
114844
|
+
* Unified plugin that transforms magicBlock nodes into final MDAST nodes.
|
|
114711
114845
|
*/
|
|
114712
|
-
const
|
|
114713
|
-
if (!blocks.length)
|
|
114714
|
-
return;
|
|
114715
|
-
// Map: key → original raw magic block content
|
|
114716
|
-
const magicBlockKeys = new Map(blocks.map(({ key, raw }) => [key, raw]));
|
|
114717
|
-
// Collect replacements to apply (we need to visit in reverse to maintain indices)
|
|
114846
|
+
const magicBlockTransformer = (options = {}) => tree => {
|
|
114718
114847
|
const replacements = [];
|
|
114719
|
-
|
|
114720
|
-
|
|
114721
|
-
if (!parent || index == null)
|
|
114722
|
-
return undefined;
|
|
114723
|
-
const raw = magicBlockKeys.get(node.value);
|
|
114724
|
-
if (!raw)
|
|
114725
|
-
return undefined;
|
|
114726
|
-
const children = parseMagicBlock(raw);
|
|
114727
|
-
if (!children.length)
|
|
114848
|
+
visit(tree, 'magicBlock', (node, index, parent) => {
|
|
114849
|
+
if (!parent || index === undefined)
|
|
114728
114850
|
return undefined;
|
|
114851
|
+
const children = transformMagicBlock(node.blockType, node.data, node.value, options);
|
|
114852
|
+
if (!children.length) {
|
|
114853
|
+
// Remove the node if transformation returns nothing
|
|
114854
|
+
parent.children.splice(index, 1);
|
|
114855
|
+
return [SKIP, index];
|
|
114856
|
+
}
|
|
114729
114857
|
// If parent is a paragraph and we're inserting block nodes (which must not be in paragraphs), lift them out
|
|
114730
114858
|
if (parent.type === 'paragraph' && children.some(child => isBlockNode(child))) {
|
|
114731
114859
|
const blockNodes = [];
|
|
@@ -114794,7 +114922,557 @@ const magicBlockRestorer = ({ blocks }) => tree => {
|
|
|
114794
114922
|
}
|
|
114795
114923
|
}
|
|
114796
114924
|
};
|
|
114797
|
-
/* harmony default export */ const
|
|
114925
|
+
/* harmony default export */ const magic_block_transformer = (magicBlockTransformer);
|
|
114926
|
+
|
|
114927
|
+
;// ./processor/transform/mdxish/mdxish-html-blocks.ts
|
|
114928
|
+
|
|
114929
|
+
|
|
114930
|
+
|
|
114931
|
+
|
|
114932
|
+
/**
|
|
114933
|
+
* Decodes HTMLBlock content that was protected during preprocessing.
|
|
114934
|
+
* Content is wrapped in <!--RDMX_HTMLBLOCK:base64:RDMX_HTMLBLOCK-->
|
|
114935
|
+
*/
|
|
114936
|
+
function decodeProtectedContent(content) {
|
|
114937
|
+
// Escape special regex characters in the markers
|
|
114938
|
+
const startEscaped = HTML_BLOCK_CONTENT_START.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
114939
|
+
const endEscaped = HTML_BLOCK_CONTENT_END.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
114940
|
+
const markerRegex = new RegExp(`${startEscaped}([A-Za-z0-9+/=]+)${endEscaped}`, 'g');
|
|
114941
|
+
return content.replace(markerRegex, (_match, encoded) => {
|
|
114942
|
+
try {
|
|
114943
|
+
return base64Decode(encoded);
|
|
114944
|
+
}
|
|
114945
|
+
catch {
|
|
114946
|
+
return encoded;
|
|
114947
|
+
}
|
|
114948
|
+
});
|
|
114949
|
+
}
|
|
114950
|
+
/**
|
|
114951
|
+
* Collects text content from a node and its children recursively
|
|
114952
|
+
*/
|
|
114953
|
+
function collectTextContent(node) {
|
|
114954
|
+
const parts = [];
|
|
114955
|
+
if (node.type === 'text' && node.value) {
|
|
114956
|
+
parts.push(node.value);
|
|
114957
|
+
}
|
|
114958
|
+
else if (node.type === 'html' && node.value) {
|
|
114959
|
+
parts.push(node.value);
|
|
114960
|
+
}
|
|
114961
|
+
else if (node.type === 'inlineCode' && node.value) {
|
|
114962
|
+
parts.push(node.value);
|
|
114963
|
+
}
|
|
114964
|
+
else if (node.type === 'code' && node.value) {
|
|
114965
|
+
// Reconstruct code fence syntax (markdown parser consumes opening ```)
|
|
114966
|
+
const lang = node.lang || '';
|
|
114967
|
+
const fence = `\`\`\`${lang ? `${lang}\n` : ''}`;
|
|
114968
|
+
parts.push(fence);
|
|
114969
|
+
parts.push(node.value);
|
|
114970
|
+
// Add newline before closing fence if missing
|
|
114971
|
+
const closingFence = node.value.endsWith('\n') ? '```' : '\n```';
|
|
114972
|
+
parts.push(closingFence);
|
|
114973
|
+
}
|
|
114974
|
+
else if (node.children && Array.isArray(node.children)) {
|
|
114975
|
+
node.children.forEach(child => {
|
|
114976
|
+
if (typeof child === 'object' && child !== null) {
|
|
114977
|
+
parts.push(collectTextContent(child));
|
|
114978
|
+
}
|
|
114979
|
+
});
|
|
114980
|
+
}
|
|
114981
|
+
return parts.join('');
|
|
114982
|
+
}
|
|
114983
|
+
/**
|
|
114984
|
+
* Extracts boolean attribute from HTML tag. Handles JSX (safeMode={true}) and string (safeMode="true") syntax.
|
|
114985
|
+
* Returns "true"/"false" string to survive rehypeRaw serialization.
|
|
114986
|
+
*/
|
|
114987
|
+
function extractBooleanAttr(attrs, name) {
|
|
114988
|
+
// Try JSX syntax: name={true|false}
|
|
114989
|
+
const jsxMatch = attrs.match(new RegExp(`${name}=\\{(true|false)\\}`));
|
|
114990
|
+
if (jsxMatch) {
|
|
114991
|
+
return jsxMatch[1];
|
|
114992
|
+
}
|
|
114993
|
+
// Try string syntax: name="true"|true
|
|
114994
|
+
const stringMatch = attrs.match(new RegExp(`${name}="?(true|false)"?`));
|
|
114995
|
+
if (stringMatch) {
|
|
114996
|
+
return stringMatch[1];
|
|
114997
|
+
}
|
|
114998
|
+
return undefined;
|
|
114999
|
+
}
|
|
115000
|
+
/**
|
|
115001
|
+
* Extracts runScripts attribute from HTML tag. Returns boolean for "true"/"false", string for other values, or undefined if not found.
|
|
115002
|
+
*/
|
|
115003
|
+
function extractRunScriptsAttr(attrs) {
|
|
115004
|
+
const runScriptsMatch = attrs.match(/runScripts="?([^">\s]+)"?/);
|
|
115005
|
+
if (!runScriptsMatch) {
|
|
115006
|
+
return undefined;
|
|
115007
|
+
}
|
|
115008
|
+
const value = runScriptsMatch[1];
|
|
115009
|
+
if (value === 'true') {
|
|
115010
|
+
return true;
|
|
115011
|
+
}
|
|
115012
|
+
if (value === 'false') {
|
|
115013
|
+
return false;
|
|
115014
|
+
}
|
|
115015
|
+
return value;
|
|
115016
|
+
}
|
|
115017
|
+
/**
|
|
115018
|
+
* Creates an HTMLBlock node from HTML string and optional attributes
|
|
115019
|
+
*/
|
|
115020
|
+
function createHTMLBlockNode(htmlString, position, runScripts, safeMode) {
|
|
115021
|
+
return {
|
|
115022
|
+
position,
|
|
115023
|
+
children: [{ type: 'text', value: htmlString }],
|
|
115024
|
+
type: NodeTypes.htmlBlock,
|
|
115025
|
+
data: {
|
|
115026
|
+
hName: 'html-block',
|
|
115027
|
+
hProperties: {
|
|
115028
|
+
html: htmlString,
|
|
115029
|
+
...(runScripts !== undefined && { runScripts }),
|
|
115030
|
+
...(safeMode !== undefined && { safeMode }),
|
|
115031
|
+
},
|
|
115032
|
+
},
|
|
115033
|
+
};
|
|
115034
|
+
}
|
|
115035
|
+
/**
|
|
115036
|
+
* Checks for opening tag only (for split detection)
|
|
115037
|
+
*/
|
|
115038
|
+
function hasOpeningTagOnly(node) {
|
|
115039
|
+
let hasOpening = false;
|
|
115040
|
+
let hasClosed = false;
|
|
115041
|
+
let attrs = '';
|
|
115042
|
+
const check = (n) => {
|
|
115043
|
+
if (n.type === 'html' && n.value) {
|
|
115044
|
+
if (n.value === '<HTMLBlock>') {
|
|
115045
|
+
hasOpening = true;
|
|
115046
|
+
}
|
|
115047
|
+
else {
|
|
115048
|
+
const match = n.value.match(/^<HTMLBlock(\s[^>]*)?>$/);
|
|
115049
|
+
if (match) {
|
|
115050
|
+
hasOpening = true;
|
|
115051
|
+
attrs = match[1] || '';
|
|
115052
|
+
}
|
|
115053
|
+
}
|
|
115054
|
+
if (n.value === '</HTMLBlock>' || n.value.includes('</HTMLBlock>')) {
|
|
115055
|
+
hasClosed = true;
|
|
115056
|
+
}
|
|
115057
|
+
}
|
|
115058
|
+
if (n.children && Array.isArray(n.children)) {
|
|
115059
|
+
n.children.forEach(child => {
|
|
115060
|
+
check(child);
|
|
115061
|
+
});
|
|
115062
|
+
}
|
|
115063
|
+
};
|
|
115064
|
+
check(node);
|
|
115065
|
+
// Return true only if opening without closing (split case)
|
|
115066
|
+
return { attrs, found: hasOpening && !hasClosed };
|
|
115067
|
+
}
|
|
115068
|
+
/**
|
|
115069
|
+
* Checks if a node contains an HTMLBlock closing tag
|
|
115070
|
+
*/
|
|
115071
|
+
function hasClosingTag(node) {
|
|
115072
|
+
if (node.type === 'html' && node.value) {
|
|
115073
|
+
if (node.value === '</HTMLBlock>' || node.value.includes('</HTMLBlock>'))
|
|
115074
|
+
return true;
|
|
115075
|
+
}
|
|
115076
|
+
if (node.children && Array.isArray(node.children)) {
|
|
115077
|
+
return node.children.some(child => hasClosingTag(child));
|
|
115078
|
+
}
|
|
115079
|
+
return false;
|
|
115080
|
+
}
|
|
115081
|
+
/**
|
|
115082
|
+
* Transforms HTMLBlock MDX JSX to html-block nodes. Handles <HTMLBlock>{`...`}</HTMLBlock> syntax.
|
|
115083
|
+
*/
|
|
115084
|
+
const mdxishHtmlBlocks = () => tree => {
|
|
115085
|
+
// Handle HTMLBlock split across root children (caused by newlines)
|
|
115086
|
+
visit(tree, 'root', (root) => {
|
|
115087
|
+
const children = root.children;
|
|
115088
|
+
let i = 0;
|
|
115089
|
+
while (i < children.length) {
|
|
115090
|
+
const child = children[i];
|
|
115091
|
+
const { attrs, found: hasOpening } = hasOpeningTagOnly(child);
|
|
115092
|
+
if (hasOpening) {
|
|
115093
|
+
// Find closing tag in subsequent siblings
|
|
115094
|
+
let closingIdx = -1;
|
|
115095
|
+
for (let j = i + 1; j < children.length; j += 1) {
|
|
115096
|
+
if (hasClosingTag(children[j])) {
|
|
115097
|
+
closingIdx = j;
|
|
115098
|
+
break;
|
|
115099
|
+
}
|
|
115100
|
+
}
|
|
115101
|
+
if (closingIdx !== -1) {
|
|
115102
|
+
// Collect inner content between tags
|
|
115103
|
+
const contentParts = [];
|
|
115104
|
+
for (let j = i; j <= closingIdx; j += 1) {
|
|
115105
|
+
const node = children[j];
|
|
115106
|
+
contentParts.push(collectTextContent(node));
|
|
115107
|
+
}
|
|
115108
|
+
// Remove the opening/closing tags and template literal syntax from content
|
|
115109
|
+
let content = contentParts.join('');
|
|
115110
|
+
content = content.replace(/^<HTMLBlock[^>]*>\s*\{?\s*`?/, '').replace(/`?\s*\}?\s*<\/HTMLBlock>$/, '');
|
|
115111
|
+
// Decode protected content that was base64 encoded during preprocessing
|
|
115112
|
+
content = decodeProtectedContent(content);
|
|
115113
|
+
const htmlString = formatHtmlForMdxish(content);
|
|
115114
|
+
const runScripts = extractRunScriptsAttr(attrs);
|
|
115115
|
+
const safeMode = extractBooleanAttr(attrs, 'safeMode');
|
|
115116
|
+
// Replace range with single HTMLBlock node
|
|
115117
|
+
const mdNode = createHTMLBlockNode(htmlString, children[i].position, runScripts, safeMode);
|
|
115118
|
+
root.children.splice(i, closingIdx - i + 1, mdNode);
|
|
115119
|
+
}
|
|
115120
|
+
}
|
|
115121
|
+
i += 1;
|
|
115122
|
+
}
|
|
115123
|
+
});
|
|
115124
|
+
// Handle HTMLBlock parsed as HTML elements (when template literal contains block-level HTML tags)
|
|
115125
|
+
visit(tree, 'html', (node, index, parent) => {
|
|
115126
|
+
if (!parent || index === undefined)
|
|
115127
|
+
return;
|
|
115128
|
+
const value = node.value;
|
|
115129
|
+
if (!value)
|
|
115130
|
+
return;
|
|
115131
|
+
// Case 1: Full HTMLBlock in single node
|
|
115132
|
+
const fullMatch = value.match(/^<HTMLBlock(\s[^>]*)?>([\s\S]*)<\/HTMLBlock>$/);
|
|
115133
|
+
if (fullMatch) {
|
|
115134
|
+
const attrs = fullMatch[1] || '';
|
|
115135
|
+
let content = fullMatch[2] || '';
|
|
115136
|
+
// Remove template literal syntax if present: {`...`}
|
|
115137
|
+
content = content.replace(/^\s*\{\s*`/, '').replace(/`\s*\}\s*$/, '');
|
|
115138
|
+
// Decode protected content that was base64 encoded during preprocessing
|
|
115139
|
+
content = decodeProtectedContent(content);
|
|
115140
|
+
const htmlString = formatHtmlForMdxish(content);
|
|
115141
|
+
const runScripts = extractRunScriptsAttr(attrs);
|
|
115142
|
+
const safeMode = extractBooleanAttr(attrs, 'safeMode');
|
|
115143
|
+
parent.children[index] = createHTMLBlockNode(htmlString, node.position, runScripts, safeMode);
|
|
115144
|
+
return;
|
|
115145
|
+
}
|
|
115146
|
+
// Case 2: Opening tag only (split by blank lines)
|
|
115147
|
+
if (value === '<HTMLBlock>' || value.match(/^<HTMLBlock\s[^>]*>$/)) {
|
|
115148
|
+
const siblings = parent.children;
|
|
115149
|
+
let closingIdx = -1;
|
|
115150
|
+
// Find closing tag in siblings
|
|
115151
|
+
for (let i = index + 1; i < siblings.length; i += 1) {
|
|
115152
|
+
const sibling = siblings[i];
|
|
115153
|
+
if (sibling.type === 'html') {
|
|
115154
|
+
const sibVal = sibling.value;
|
|
115155
|
+
if (sibVal === '</HTMLBlock>' || sibVal?.includes('</HTMLBlock>')) {
|
|
115156
|
+
closingIdx = i;
|
|
115157
|
+
break;
|
|
115158
|
+
}
|
|
115159
|
+
}
|
|
115160
|
+
}
|
|
115161
|
+
if (closingIdx === -1)
|
|
115162
|
+
return;
|
|
115163
|
+
// Collect content between tags, skipping template literal delimiters
|
|
115164
|
+
const contentParts = [];
|
|
115165
|
+
for (let i = index + 1; i < closingIdx; i += 1) {
|
|
115166
|
+
const sibling = siblings[i];
|
|
115167
|
+
// Skip template literal delimiters
|
|
115168
|
+
if (sibling.type === 'text') {
|
|
115169
|
+
const textVal = sibling.value;
|
|
115170
|
+
if (textVal === '{' || textVal === '}' || textVal === '{`' || textVal === '`}') {
|
|
115171
|
+
// eslint-disable-next-line no-continue
|
|
115172
|
+
continue;
|
|
115173
|
+
}
|
|
115174
|
+
}
|
|
115175
|
+
contentParts.push(collectTextContent(sibling));
|
|
115176
|
+
}
|
|
115177
|
+
// Decode protected content that was base64 encoded during preprocessing
|
|
115178
|
+
const decodedContent = decodeProtectedContent(contentParts.join(''));
|
|
115179
|
+
const htmlString = formatHtmlForMdxish(decodedContent);
|
|
115180
|
+
const runScripts = extractRunScriptsAttr(value);
|
|
115181
|
+
const safeMode = extractBooleanAttr(value, 'safeMode');
|
|
115182
|
+
// Replace opening tag with HTMLBlock node, remove consumed siblings
|
|
115183
|
+
parent.children[index] = createHTMLBlockNode(htmlString, node.position, runScripts, safeMode);
|
|
115184
|
+
parent.children.splice(index + 1, closingIdx - index);
|
|
115185
|
+
}
|
|
115186
|
+
});
|
|
115187
|
+
// Handle HTMLBlock inside paragraphs (parsed as inline elements)
|
|
115188
|
+
visit(tree, 'paragraph', (node, index, parent) => {
|
|
115189
|
+
if (!parent || index === undefined)
|
|
115190
|
+
return;
|
|
115191
|
+
const children = node.children || [];
|
|
115192
|
+
let htmlBlockStartIdx = -1;
|
|
115193
|
+
let htmlBlockEndIdx = -1;
|
|
115194
|
+
let templateLiteralStartIdx = -1;
|
|
115195
|
+
let templateLiteralEndIdx = -1;
|
|
115196
|
+
for (let i = 0; i < children.length; i += 1) {
|
|
115197
|
+
const child = children[i];
|
|
115198
|
+
if (child.type === 'html' && typeof child.value === 'string') {
|
|
115199
|
+
const value = child.value;
|
|
115200
|
+
if (value === '<HTMLBlock>' || value.match(/^<HTMLBlock\s[^>]*>$/)) {
|
|
115201
|
+
htmlBlockStartIdx = i;
|
|
115202
|
+
}
|
|
115203
|
+
else if (value === '</HTMLBlock>') {
|
|
115204
|
+
htmlBlockEndIdx = i;
|
|
115205
|
+
}
|
|
115206
|
+
}
|
|
115207
|
+
// Find opening brace after HTMLBlock start
|
|
115208
|
+
if (htmlBlockStartIdx !== -1 && templateLiteralStartIdx === -1 && child.type === 'text') {
|
|
115209
|
+
const value = child.value;
|
|
115210
|
+
if (value === '{') {
|
|
115211
|
+
templateLiteralStartIdx = i;
|
|
115212
|
+
}
|
|
115213
|
+
}
|
|
115214
|
+
// Find closing brace before HTMLBlock end
|
|
115215
|
+
if (htmlBlockStartIdx !== -1 && htmlBlockEndIdx === -1 && child.type === 'text') {
|
|
115216
|
+
const value = child.value;
|
|
115217
|
+
if (value === '}') {
|
|
115218
|
+
templateLiteralEndIdx = i;
|
|
115219
|
+
}
|
|
115220
|
+
}
|
|
115221
|
+
}
|
|
115222
|
+
if (htmlBlockStartIdx !== -1 &&
|
|
115223
|
+
htmlBlockEndIdx !== -1 &&
|
|
115224
|
+
templateLiteralStartIdx !== -1 &&
|
|
115225
|
+
templateLiteralEndIdx !== -1 &&
|
|
115226
|
+
templateLiteralStartIdx < templateLiteralEndIdx) {
|
|
115227
|
+
const openingTag = children[htmlBlockStartIdx];
|
|
115228
|
+
// Collect content between braces (handles code blocks)
|
|
115229
|
+
const templateContent = [];
|
|
115230
|
+
for (let i = templateLiteralStartIdx + 1; i < templateLiteralEndIdx; i += 1) {
|
|
115231
|
+
const child = children[i];
|
|
115232
|
+
templateContent.push(collectTextContent(child));
|
|
115233
|
+
}
|
|
115234
|
+
// Decode protected content that was base64 encoded during preprocessing
|
|
115235
|
+
const decodedContent = decodeProtectedContent(templateContent.join(''));
|
|
115236
|
+
const htmlString = formatHtmlForMdxish(decodedContent);
|
|
115237
|
+
const runScripts = openingTag.value ? extractRunScriptsAttr(openingTag.value) : undefined;
|
|
115238
|
+
const safeMode = openingTag.value ? extractBooleanAttr(openingTag.value, 'safeMode') : undefined;
|
|
115239
|
+
const mdNode = createHTMLBlockNode(htmlString, node.position, runScripts, safeMode);
|
|
115240
|
+
parent.children[index] = mdNode;
|
|
115241
|
+
}
|
|
115242
|
+
});
|
|
115243
|
+
// Ensure html-block nodes have HTML in children as text node
|
|
115244
|
+
visit(tree, 'html-block', (node) => {
|
|
115245
|
+
const html = node.data?.hProperties?.html;
|
|
115246
|
+
if (html &&
|
|
115247
|
+
(!node.children ||
|
|
115248
|
+
node.children.length === 0 ||
|
|
115249
|
+
(node.children.length === 1 && node.children[0].type === 'text' && node.children[0].value !== html))) {
|
|
115250
|
+
node.children = [
|
|
115251
|
+
{
|
|
115252
|
+
type: 'text',
|
|
115253
|
+
value: html,
|
|
115254
|
+
},
|
|
115255
|
+
];
|
|
115256
|
+
}
|
|
115257
|
+
});
|
|
115258
|
+
return tree;
|
|
115259
|
+
};
|
|
115260
|
+
/* harmony default export */ const mdxish_html_blocks = (mdxishHtmlBlocks);
|
|
115261
|
+
|
|
115262
|
+
;// ./processor/transform/mdxish/mdxish-jsx-to-mdast.ts
|
|
115263
|
+
|
|
115264
|
+
|
|
115265
|
+
|
|
115266
|
+
const transformImage = (jsx) => {
|
|
115267
|
+
const attrs = getAttrs(jsx);
|
|
115268
|
+
const { align, alt = '', border, caption, className, height, lazy, src = '', title = '', width } = attrs;
|
|
115269
|
+
const hProperties = {
|
|
115270
|
+
alt,
|
|
115271
|
+
src,
|
|
115272
|
+
title,
|
|
115273
|
+
...(align && { align }),
|
|
115274
|
+
...(border !== undefined && { border: String(border) }),
|
|
115275
|
+
...(caption && { caption }),
|
|
115276
|
+
...(className && { className }),
|
|
115277
|
+
...(height !== undefined && { height: String(height) }),
|
|
115278
|
+
...(lazy !== undefined && { lazy }),
|
|
115279
|
+
...(width !== undefined && { width: String(width) }),
|
|
115280
|
+
};
|
|
115281
|
+
return {
|
|
115282
|
+
type: NodeTypes.imageBlock,
|
|
115283
|
+
align,
|
|
115284
|
+
alt,
|
|
115285
|
+
border: border !== undefined ? String(border) : undefined,
|
|
115286
|
+
caption,
|
|
115287
|
+
className,
|
|
115288
|
+
height: height !== undefined ? String(height) : undefined,
|
|
115289
|
+
lazy,
|
|
115290
|
+
src,
|
|
115291
|
+
title,
|
|
115292
|
+
width: width !== undefined ? String(width) : undefined,
|
|
115293
|
+
data: {
|
|
115294
|
+
hName: 'img',
|
|
115295
|
+
hProperties,
|
|
115296
|
+
},
|
|
115297
|
+
position: jsx.position,
|
|
115298
|
+
};
|
|
115299
|
+
};
|
|
115300
|
+
const transformCallout = (jsx) => {
|
|
115301
|
+
const attrs = getAttrs(jsx);
|
|
115302
|
+
const { empty = false, icon = '', theme = '' } = attrs;
|
|
115303
|
+
return {
|
|
115304
|
+
type: NodeTypes.callout,
|
|
115305
|
+
children: jsx.children,
|
|
115306
|
+
data: {
|
|
115307
|
+
hName: 'Callout',
|
|
115308
|
+
hProperties: {
|
|
115309
|
+
empty,
|
|
115310
|
+
icon,
|
|
115311
|
+
theme,
|
|
115312
|
+
},
|
|
115313
|
+
},
|
|
115314
|
+
position: jsx.position,
|
|
115315
|
+
};
|
|
115316
|
+
};
|
|
115317
|
+
const transformEmbed = (jsx) => {
|
|
115318
|
+
const attrs = getAttrs(jsx);
|
|
115319
|
+
const { favicon, html, iframe, image, providerName, providerUrl, title = '', url = '' } = attrs;
|
|
115320
|
+
return {
|
|
115321
|
+
type: NodeTypes.embedBlock,
|
|
115322
|
+
title,
|
|
115323
|
+
url,
|
|
115324
|
+
data: {
|
|
115325
|
+
hName: 'embed',
|
|
115326
|
+
hProperties: {
|
|
115327
|
+
title,
|
|
115328
|
+
url,
|
|
115329
|
+
...(favicon && { favicon }),
|
|
115330
|
+
...(html && { html }),
|
|
115331
|
+
...(iframe !== undefined && { iframe }),
|
|
115332
|
+
...(image && { image }),
|
|
115333
|
+
...(providerName && { providerName }),
|
|
115334
|
+
...(providerUrl && { providerUrl }),
|
|
115335
|
+
},
|
|
115336
|
+
},
|
|
115337
|
+
position: jsx.position,
|
|
115338
|
+
};
|
|
115339
|
+
};
|
|
115340
|
+
const transformRecipe = (jsx) => {
|
|
115341
|
+
const attrs = getAttrs(jsx);
|
|
115342
|
+
const { backgroundColor = '', emoji = '', id = '', link = '', slug = '', title = '' } = attrs;
|
|
115343
|
+
return {
|
|
115344
|
+
type: NodeTypes.recipe,
|
|
115345
|
+
backgroundColor,
|
|
115346
|
+
emoji,
|
|
115347
|
+
id,
|
|
115348
|
+
link,
|
|
115349
|
+
slug,
|
|
115350
|
+
title,
|
|
115351
|
+
position: jsx.position,
|
|
115352
|
+
};
|
|
115353
|
+
};
|
|
115354
|
+
/**
|
|
115355
|
+
* Transform a magic block image node into an ImageBlock.
|
|
115356
|
+
* Magic block images have structure: { type: 'image', url, title, alt, data.hProperties }
|
|
115357
|
+
*/
|
|
115358
|
+
const transformMagicBlockImage = (node) => {
|
|
115359
|
+
const { alt = '', data, position, title = '', url = '' } = node;
|
|
115360
|
+
const hProps = data?.hProperties || {};
|
|
115361
|
+
const { align, border, width } = hProps;
|
|
115362
|
+
const hProperties = {
|
|
115363
|
+
alt,
|
|
115364
|
+
src: url,
|
|
115365
|
+
title,
|
|
115366
|
+
...(align && { align }),
|
|
115367
|
+
...(border && { border }),
|
|
115368
|
+
...(width && { width }),
|
|
115369
|
+
};
|
|
115370
|
+
return {
|
|
115371
|
+
type: NodeTypes.imageBlock,
|
|
115372
|
+
align,
|
|
115373
|
+
alt,
|
|
115374
|
+
border,
|
|
115375
|
+
src: url,
|
|
115376
|
+
title,
|
|
115377
|
+
width,
|
|
115378
|
+
data: {
|
|
115379
|
+
hName: 'img',
|
|
115380
|
+
hProperties,
|
|
115381
|
+
},
|
|
115382
|
+
position,
|
|
115383
|
+
};
|
|
115384
|
+
};
|
|
115385
|
+
/**
|
|
115386
|
+
* Transform a magic block embed node into an EmbedBlock.
|
|
115387
|
+
* Magic block embeds have structure: { type: 'embed', children, data.hProperties }
|
|
115388
|
+
*/
|
|
115389
|
+
const transformMagicBlockEmbed = (node) => {
|
|
115390
|
+
const { data, position } = node;
|
|
115391
|
+
const hProps = data?.hProperties || {};
|
|
115392
|
+
const { favicon, html, image, providerName, providerUrl, title = '', url = '' } = hProps;
|
|
115393
|
+
return {
|
|
115394
|
+
type: NodeTypes.embedBlock,
|
|
115395
|
+
title,
|
|
115396
|
+
url,
|
|
115397
|
+
data: {
|
|
115398
|
+
hName: 'embed',
|
|
115399
|
+
hProperties: {
|
|
115400
|
+
title,
|
|
115401
|
+
url,
|
|
115402
|
+
...(favicon && { favicon }),
|
|
115403
|
+
...(html && { html }),
|
|
115404
|
+
...(image && { image }),
|
|
115405
|
+
...(providerName && { providerName }),
|
|
115406
|
+
...(providerUrl && { providerUrl }),
|
|
115407
|
+
},
|
|
115408
|
+
},
|
|
115409
|
+
position,
|
|
115410
|
+
};
|
|
115411
|
+
};
|
|
115412
|
+
const COMPONENT_MAP = {
|
|
115413
|
+
Callout: transformCallout,
|
|
115414
|
+
Embed: transformEmbed,
|
|
115415
|
+
Image: transformImage,
|
|
115416
|
+
Recipe: transformRecipe,
|
|
115417
|
+
};
|
|
115418
|
+
/**
|
|
115419
|
+
* Transform mdxJsxFlowElement nodes and magic block nodes into proper MDAST node types.
|
|
115420
|
+
*
|
|
115421
|
+
* This transformer runs after mdxishComponentBlocks and converts:
|
|
115422
|
+
* - JSX component elements (Image, Callout, Embed, Recipe) into their corresponding MDAST types
|
|
115423
|
+
* - Magic block image nodes (type: 'image') into image-block
|
|
115424
|
+
* - Magic block embed nodes (type: 'embed') into embed-block
|
|
115425
|
+
* - Figure nodes containing images (from magic blocks with captions) - transforms the inner image
|
|
115426
|
+
*
|
|
115427
|
+
* This is controlled by the `newEditorTypes` flag to maintain backwards compatibility.
|
|
115428
|
+
*/
|
|
115429
|
+
const mdxishJsxToMdast = () => tree => {
|
|
115430
|
+
// Transform JSX components (Image, Callout, Embed, Recipe)
|
|
115431
|
+
visit(tree, 'mdxJsxFlowElement', (node, index, parent) => {
|
|
115432
|
+
if (!parent || index === undefined || !node.name)
|
|
115433
|
+
return;
|
|
115434
|
+
const transformer = COMPONENT_MAP[node.name];
|
|
115435
|
+
if (!transformer)
|
|
115436
|
+
return;
|
|
115437
|
+
const newNode = transformer(node);
|
|
115438
|
+
// Replace the JSX node with the MDAST node
|
|
115439
|
+
parent.children[index] = newNode;
|
|
115440
|
+
});
|
|
115441
|
+
// Transform magic block images (type: 'image') to image-block
|
|
115442
|
+
// Note: Standard markdown images are wrapped in paragraphs and handled by imageTransformer
|
|
115443
|
+
// Magic block images are direct children of root, so we handle them here
|
|
115444
|
+
visit(tree, 'image', (node, index, parent) => {
|
|
115445
|
+
if (!parent || index === undefined)
|
|
115446
|
+
return SKIP;
|
|
115447
|
+
// Skip images inside paragraphs (those are standard markdown images handled by imageTransformer)
|
|
115448
|
+
if (parent.type === 'paragraph')
|
|
115449
|
+
return SKIP;
|
|
115450
|
+
const newNode = transformMagicBlockImage(node);
|
|
115451
|
+
parent.children[index] = newNode;
|
|
115452
|
+
return SKIP;
|
|
115453
|
+
});
|
|
115454
|
+
// Transform magic block embeds (type: 'embed') to embed-block
|
|
115455
|
+
visit(tree, 'embed', (node, index, parent) => {
|
|
115456
|
+
if (!parent || index === undefined)
|
|
115457
|
+
return SKIP;
|
|
115458
|
+
const newNode = transformMagicBlockEmbed(node);
|
|
115459
|
+
parent.children[index] = newNode;
|
|
115460
|
+
return SKIP;
|
|
115461
|
+
});
|
|
115462
|
+
// Transform images inside figure nodes (magic blocks with captions)
|
|
115463
|
+
const isFigure = (node) => node.type === 'figure';
|
|
115464
|
+
visit(tree, isFigure, node => {
|
|
115465
|
+
// Find and transform the image child
|
|
115466
|
+
node.children = node.children.map(child => {
|
|
115467
|
+
if (child.type === 'image') {
|
|
115468
|
+
return transformMagicBlockImage(child);
|
|
115469
|
+
}
|
|
115470
|
+
return child;
|
|
115471
|
+
});
|
|
115472
|
+
});
|
|
115473
|
+
return tree;
|
|
115474
|
+
};
|
|
115475
|
+
/* harmony default export */ const mdxish_jsx_to_mdast = (mdxishJsxToMdast);
|
|
114798
115476
|
|
|
114799
115477
|
;// ./processor/transform/mdxish/mdxish-mermaid.ts
|
|
114800
115478
|
|
|
@@ -114836,25 +115514,50 @@ const componentTagPattern = /<(\/?[A-Z][A-Za-z0-9_]*)([^>]*?)(\/?)>/g;
|
|
|
114836
115514
|
|
|
114837
115515
|
;// ./processor/transform/mdxish/mdxish-snake-case-components.ts
|
|
114838
115516
|
|
|
115517
|
+
|
|
114839
115518
|
/**
|
|
114840
115519
|
* Replaces snake_case component names with valid HTML placeholders.
|
|
114841
115520
|
* Required because remark-parse rejects tags with underscores.
|
|
114842
115521
|
* Example: `<Snake_case />` → `<MDXishSnakeCase0 />`
|
|
115522
|
+
*
|
|
115523
|
+
* Code blocks and inline code are protected and will not be transformed.
|
|
115524
|
+
*
|
|
115525
|
+
* @param content - The markdown content to process
|
|
115526
|
+
* @param options - Options including knownComponents to filter by
|
|
114843
115527
|
*/
|
|
114844
|
-
function processSnakeCaseComponent(content) {
|
|
115528
|
+
function processSnakeCaseComponent(content, options = {}) {
|
|
115529
|
+
const { knownComponents } = options;
|
|
114845
115530
|
// Early exit if no potential snake_case components
|
|
114846
115531
|
if (!/[A-Z][A-Za-z0-9]*_[A-Za-z0-9_]*/.test(content)) {
|
|
114847
115532
|
return { content, mapping: {} };
|
|
114848
115533
|
}
|
|
115534
|
+
// Step 1: Extract code blocks to protect them from transformation
|
|
115535
|
+
const { protectedCode, protectedContent } = protectCodeBlocks(content);
|
|
115536
|
+
// Find the highest existing placeholder number to avoid collisions
|
|
115537
|
+
// e.g., if content has <MDXishSnakeCase0 />, start counter from 1
|
|
115538
|
+
const placeholderPattern = /MDXishSnakeCase(\d+)/g;
|
|
115539
|
+
let startCounter = 0;
|
|
115540
|
+
let placeholderMatch;
|
|
115541
|
+
while ((placeholderMatch = placeholderPattern.exec(content)) !== null) {
|
|
115542
|
+
const num = parseInt(placeholderMatch[1], 10);
|
|
115543
|
+
if (num >= startCounter) {
|
|
115544
|
+
startCounter = num + 1;
|
|
115545
|
+
}
|
|
115546
|
+
}
|
|
114849
115547
|
const mapping = {};
|
|
114850
115548
|
const reverseMap = new Map();
|
|
114851
|
-
let counter =
|
|
114852
|
-
|
|
115549
|
+
let counter = startCounter;
|
|
115550
|
+
// Step 2: Transform snake_case components in non-code content
|
|
115551
|
+
const processedContent = protectedContent.replace(componentTagPattern, (match, tagName, attrs, selfClosing) => {
|
|
114853
115552
|
if (!tagName.includes('_')) {
|
|
114854
115553
|
return match;
|
|
114855
115554
|
}
|
|
114856
115555
|
const isClosing = tagName.startsWith('/');
|
|
114857
115556
|
const cleanTagName = isClosing ? tagName.slice(1) : tagName;
|
|
115557
|
+
// Only transform if it's a known component (or if no filter is provided)
|
|
115558
|
+
if (knownComponents && !knownComponents.has(cleanTagName)) {
|
|
115559
|
+
return match;
|
|
115560
|
+
}
|
|
114858
115561
|
let placeholder = reverseMap.get(cleanTagName);
|
|
114859
115562
|
if (!placeholder) {
|
|
114860
115563
|
// eslint-disable-next-line no-plusplus
|
|
@@ -114865,8 +115568,10 @@ function processSnakeCaseComponent(content) {
|
|
|
114865
115568
|
const processedTagName = isClosing ? `/${placeholder}` : placeholder;
|
|
114866
115569
|
return `<${processedTagName}${attrs}${selfClosing}>`;
|
|
114867
115570
|
});
|
|
115571
|
+
// Step 3: Restore code blocks (untouched)
|
|
115572
|
+
const finalContent = restoreCodeBlocks(processedContent, protectedCode);
|
|
114868
115573
|
return {
|
|
114869
|
-
content:
|
|
115574
|
+
content: finalContent,
|
|
114870
115575
|
mapping,
|
|
114871
115576
|
};
|
|
114872
115577
|
}
|
|
@@ -114883,139 +115588,6 @@ function restoreSnakeCase(placeholderName, mapping) {
|
|
|
114883
115588
|
return matchingKey ? mapping[matchingKey] : placeholderName;
|
|
114884
115589
|
}
|
|
114885
115590
|
|
|
114886
|
-
;// ./processor/transform/mdxish/normalize-malformed-md-syntax.ts
|
|
114887
|
-
|
|
114888
|
-
// Patterns to detect for bold (** and __) and italic (* and _) syntax:
|
|
114889
|
-
// Bold: ** text**, **text **, word** text**, ** text **
|
|
114890
|
-
// Italic: * text*, *text *, word* text*, * text *
|
|
114891
|
-
// Same patterns for underscore variants
|
|
114892
|
-
// We use separate patterns for each marker type to allow this flexibility.
|
|
114893
|
-
// Pattern for ** bold **
|
|
114894
|
-
// Groups: 1=wordBefore, 2=marker, 3=contentWithSpaceAfter, 4=trailingSpace1, 5=contentWithSpaceBefore, 6=trailingSpace2, 7=afterChar
|
|
114895
|
-
// trailingSpace1 is for "** text **" pattern, trailingSpace2 is for "**text **" pattern
|
|
114896
|
-
const asteriskBoldRegex = /([^*\s]+)?\s*(\*\*)(?:\s+((?:[^*\n]|\*(?!\*))+?)(\s*)\2|((?:[^*\n]|\*(?!\*))+?)(\s+)\2)(\S|$)?/g;
|
|
114897
|
-
// Pattern for __ bold __
|
|
114898
|
-
const underscoreBoldRegex = /([^_\s]+)?\s*(__)(?:\s+((?:[^_\n]|_(?!_))+?)(\s*)\2|((?:[^_\n]|_(?!_))+?)(\s+)\2)(\S|$)?/g;
|
|
114899
|
-
// Pattern for * italic *
|
|
114900
|
-
const asteriskItalicRegex = /([^*\s]+)?\s*(\*)(?!\*)(?:\s+([^*\n]+?)(\s*)\2|([^*\n]+?)(\s+)\2)(\S|$)?/g;
|
|
114901
|
-
// Pattern for _ italic _
|
|
114902
|
-
const underscoreItalicRegex = /([^_\s]+)?\s*(_)(?!_)(?:\s+([^_\n]+?)(\s*)\2|([^_\n]+?)(\s+)\2)(\S|$)?/g;
|
|
114903
|
-
/**
|
|
114904
|
-
* A remark plugin that normalizes malformed bold and italic markers in text nodes.
|
|
114905
|
-
* Detects patterns like `** bold**`, `Hello** Wrong Bold**`, `__ bold__`, `Hello__ Wrong Bold__`,
|
|
114906
|
-
* `* italic*`, `Hello* Wrong Italic*`, `_ italic_`, or `Hello_ Wrong Italic_`
|
|
114907
|
-
* and converts them to proper strong/emphasis nodes, matching the behavior of the legacy rdmd engine.
|
|
114908
|
-
*
|
|
114909
|
-
* Supports both asterisk (`**bold**`, `*italic*`) and underscore (`__bold__`, `_italic_`) syntax.
|
|
114910
|
-
* Also supports snake_case content like `** some_snake_case**`.
|
|
114911
|
-
*
|
|
114912
|
-
* This runs after remark-parse, which (in v11+) is strict and doesn't parse
|
|
114913
|
-
* malformed emphasis syntax. This plugin post-processes the AST to handle these cases.
|
|
114914
|
-
*/
|
|
114915
|
-
const normalizeEmphasisAST = () => (tree) => {
|
|
114916
|
-
visit(tree, 'text', function visitor(node, index, parent) {
|
|
114917
|
-
if (index === undefined || !parent)
|
|
114918
|
-
return undefined;
|
|
114919
|
-
// Skip if inside code blocks or inline code
|
|
114920
|
-
if (parent.type === 'inlineCode' || parent.type === 'code') {
|
|
114921
|
-
return undefined;
|
|
114922
|
-
}
|
|
114923
|
-
const text = node.value;
|
|
114924
|
-
const allMatches = [];
|
|
114925
|
-
[...text.matchAll(asteriskBoldRegex)].forEach(match => {
|
|
114926
|
-
allMatches.push({ isBold: true, marker: '**', match });
|
|
114927
|
-
});
|
|
114928
|
-
[...text.matchAll(underscoreBoldRegex)].forEach(match => {
|
|
114929
|
-
allMatches.push({ isBold: true, marker: '__', match });
|
|
114930
|
-
});
|
|
114931
|
-
[...text.matchAll(asteriskItalicRegex)].forEach(match => {
|
|
114932
|
-
allMatches.push({ isBold: false, marker: '*', match });
|
|
114933
|
-
});
|
|
114934
|
-
[...text.matchAll(underscoreItalicRegex)].forEach(match => {
|
|
114935
|
-
allMatches.push({ isBold: false, marker: '_', match });
|
|
114936
|
-
});
|
|
114937
|
-
if (allMatches.length === 0)
|
|
114938
|
-
return undefined;
|
|
114939
|
-
allMatches.sort((a, b) => (a.match.index ?? 0) - (b.match.index ?? 0));
|
|
114940
|
-
const filteredMatches = [];
|
|
114941
|
-
let lastEnd = 0;
|
|
114942
|
-
allMatches.forEach(info => {
|
|
114943
|
-
const start = info.match.index ?? 0;
|
|
114944
|
-
const end = start + info.match[0].length;
|
|
114945
|
-
if (start >= lastEnd) {
|
|
114946
|
-
filteredMatches.push(info);
|
|
114947
|
-
lastEnd = end;
|
|
114948
|
-
}
|
|
114949
|
-
});
|
|
114950
|
-
if (filteredMatches.length === 0)
|
|
114951
|
-
return undefined;
|
|
114952
|
-
const parts = [];
|
|
114953
|
-
let lastIndex = 0;
|
|
114954
|
-
filteredMatches.forEach(({ match, marker, isBold }) => {
|
|
114955
|
-
const matchIndex = match.index ?? 0;
|
|
114956
|
-
const fullMatch = match[0];
|
|
114957
|
-
if (matchIndex > lastIndex) {
|
|
114958
|
-
const beforeText = text.slice(lastIndex, matchIndex);
|
|
114959
|
-
if (beforeText) {
|
|
114960
|
-
parts.push({ type: 'text', value: beforeText });
|
|
114961
|
-
}
|
|
114962
|
-
}
|
|
114963
|
-
const wordBefore = match[1]; // e.g., "Hello" in "Hello** Wrong Bold**"
|
|
114964
|
-
const contentWithSpaceAfter = match[3]; // Content when there's a space after opening markers
|
|
114965
|
-
const trailingSpace1 = match[4] || ''; // Space before closing markers (for "** text **" pattern)
|
|
114966
|
-
const contentWithSpaceBefore = match[5]; // Content when there's only a space before closing markers
|
|
114967
|
-
const trailingSpace2 = match[6] || ''; // Space before closing markers (for "**text **" pattern)
|
|
114968
|
-
const trailingSpace = trailingSpace1 || trailingSpace2; // Combined trailing space
|
|
114969
|
-
const content = (contentWithSpaceAfter || contentWithSpaceBefore || '').trim();
|
|
114970
|
-
const afterChar = match[7]; // Character after closing markers (if any)
|
|
114971
|
-
const markerPos = fullMatch.indexOf(marker);
|
|
114972
|
-
const spacesBeforeMarkers = wordBefore
|
|
114973
|
-
? fullMatch.slice(wordBefore.length, markerPos)
|
|
114974
|
-
: fullMatch.slice(0, markerPos);
|
|
114975
|
-
const shouldAddSpace = !!contentWithSpaceAfter && !!wordBefore && !spacesBeforeMarkers;
|
|
114976
|
-
if (wordBefore) {
|
|
114977
|
-
const spacing = spacesBeforeMarkers + (shouldAddSpace ? ' ' : '');
|
|
114978
|
-
parts.push({ type: 'text', value: wordBefore + spacing });
|
|
114979
|
-
}
|
|
114980
|
-
else if (spacesBeforeMarkers) {
|
|
114981
|
-
parts.push({ type: 'text', value: spacesBeforeMarkers });
|
|
114982
|
-
}
|
|
114983
|
-
if (content) {
|
|
114984
|
-
if (isBold) {
|
|
114985
|
-
parts.push({
|
|
114986
|
-
type: 'strong',
|
|
114987
|
-
children: [{ type: 'text', value: content }],
|
|
114988
|
-
});
|
|
114989
|
-
}
|
|
114990
|
-
else {
|
|
114991
|
-
parts.push({
|
|
114992
|
-
type: 'emphasis',
|
|
114993
|
-
children: [{ type: 'text', value: content }],
|
|
114994
|
-
});
|
|
114995
|
-
}
|
|
114996
|
-
}
|
|
114997
|
-
if (afterChar) {
|
|
114998
|
-
const prefix = trailingSpace ? ' ' : '';
|
|
114999
|
-
parts.push({ type: 'text', value: prefix + afterChar });
|
|
115000
|
-
}
|
|
115001
|
-
lastIndex = matchIndex + fullMatch.length;
|
|
115002
|
-
});
|
|
115003
|
-
if (lastIndex < text.length) {
|
|
115004
|
-
const remainingText = text.slice(lastIndex);
|
|
115005
|
-
if (remainingText) {
|
|
115006
|
-
parts.push({ type: 'text', value: remainingText });
|
|
115007
|
-
}
|
|
115008
|
-
}
|
|
115009
|
-
if (parts.length > 0) {
|
|
115010
|
-
parent.children.splice(index, 1, ...parts);
|
|
115011
|
-
return [SKIP, index + parts.length];
|
|
115012
|
-
}
|
|
115013
|
-
return undefined;
|
|
115014
|
-
});
|
|
115015
|
-
return tree;
|
|
115016
|
-
};
|
|
115017
|
-
/* harmony default export */ const normalize_malformed_md_syntax = (normalizeEmphasisAST);
|
|
115018
|
-
|
|
115019
115591
|
;// ./processor/transform/mdxish/normalize-table-separator.ts
|
|
115020
115592
|
/**
|
|
115021
115593
|
* Preprocessor to normalize malformed GFM table separator syntax.
|
|
@@ -115248,57 +115820,1172 @@ const variablesTextTransformer = () => tree => {
|
|
|
115248
115820
|
};
|
|
115249
115821
|
/* harmony default export */ const variables_text = (variablesTextTransformer);
|
|
115250
115822
|
|
|
115251
|
-
;// ./lib/
|
|
115823
|
+
;// ./lib/mdast-util/magic-block/index.ts
|
|
115824
|
+
const contextMap = new WeakMap();
|
|
115252
115825
|
/**
|
|
115253
|
-
*
|
|
115254
|
-
* and `[/block]`, including new lines. Negative lookahead for the closing
|
|
115255
|
-
* `[/block]` tag is required to prevent greedy matching to ensure it stops at
|
|
115256
|
-
* the first closing tag it encounters preventing vulnerability to polynomial
|
|
115257
|
-
* backtracking issues.
|
|
115826
|
+
* Find the magicBlock token in the token ancestry.
|
|
115258
115827
|
*/
|
|
115259
|
-
|
|
115828
|
+
function findMagicBlockToken() {
|
|
115829
|
+
// Walk up the token stack to find the magicBlock token
|
|
115830
|
+
const events = this.tokenStack;
|
|
115831
|
+
for (let i = events.length - 1; i >= 0; i -= 1) {
|
|
115832
|
+
const token = events[i][0];
|
|
115833
|
+
if (token.type === 'magicBlock') {
|
|
115834
|
+
return token;
|
|
115835
|
+
}
|
|
115836
|
+
}
|
|
115837
|
+
return undefined;
|
|
115838
|
+
}
|
|
115260
115839
|
/**
|
|
115261
|
-
*
|
|
115262
|
-
* Returns the modified markdown and an array of extracted blocks.
|
|
115840
|
+
* Enter handler: Create a new magicBlock node.
|
|
115263
115841
|
*/
|
|
115264
|
-
function
|
|
115265
|
-
|
|
115266
|
-
|
|
115267
|
-
|
|
115268
|
-
|
|
115269
|
-
|
|
115270
|
-
|
|
115271
|
-
|
|
115272
|
-
|
|
115273
|
-
* Token is a wrapper around the `key` to serialize & influence how the
|
|
115274
|
-
* magic block is parsed in the remark pipeline.
|
|
115275
|
-
* - Use backticks so it becomes a code span, preventing `remarkParse` from
|
|
115276
|
-
* parsing special characters in the token as markdown syntax
|
|
115277
|
-
* - Prepend a newline to ensure it is parsed as a block level node
|
|
115278
|
-
* - Append a newline to ensure it is separated from following content
|
|
115279
|
-
*/
|
|
115280
|
-
const token = `\n\`${key}\`\n`;
|
|
115281
|
-
blocks.push({ key, raw: match, token });
|
|
115282
|
-
index += 1;
|
|
115283
|
-
return token;
|
|
115284
|
-
});
|
|
115285
|
-
return { replaced, blocks };
|
|
115842
|
+
function enterMagicBlock(token) {
|
|
115843
|
+
// Initialize context for this magic block
|
|
115844
|
+
contextMap.set(token, { blockType: '', dataChunks: [] });
|
|
115845
|
+
this.enter({
|
|
115846
|
+
type: 'magicBlock',
|
|
115847
|
+
blockType: '',
|
|
115848
|
+
data: {},
|
|
115849
|
+
value: '',
|
|
115850
|
+
}, token);
|
|
115286
115851
|
}
|
|
115287
115852
|
/**
|
|
115288
|
-
*
|
|
115853
|
+
* Exit handler for block type: Extract the block type from the token.
|
|
115289
115854
|
*/
|
|
115290
|
-
function
|
|
115291
|
-
|
|
115292
|
-
|
|
115293
|
-
|
|
115294
|
-
|
|
115295
|
-
|
|
115296
|
-
|
|
115297
|
-
|
|
115298
|
-
|
|
115299
|
-
|
|
115300
|
-
|
|
115855
|
+
function exitMagicBlockType(token) {
|
|
115856
|
+
const blockToken = findMagicBlockToken.call(this);
|
|
115857
|
+
if (!blockToken)
|
|
115858
|
+
return;
|
|
115859
|
+
const context = contextMap.get(blockToken);
|
|
115860
|
+
if (context) {
|
|
115861
|
+
context.blockType = this.sliceSerialize(token);
|
|
115862
|
+
}
|
|
115863
|
+
}
|
|
115864
|
+
/**
|
|
115865
|
+
* Exit handler for block data: Accumulate JSON content chunks.
|
|
115866
|
+
*/
|
|
115867
|
+
function exitMagicBlockData(token) {
|
|
115868
|
+
const blockToken = findMagicBlockToken.call(this);
|
|
115869
|
+
if (!blockToken)
|
|
115870
|
+
return;
|
|
115871
|
+
const context = contextMap.get(blockToken);
|
|
115872
|
+
if (context) {
|
|
115873
|
+
context.dataChunks.push(this.sliceSerialize(token));
|
|
115874
|
+
}
|
|
115875
|
+
}
|
|
115876
|
+
/**
|
|
115877
|
+
* Exit handler for line endings: Preserve newlines in multiline blocks.
|
|
115878
|
+
*/
|
|
115879
|
+
function exitMagicBlockLineEnding(token) {
|
|
115880
|
+
const blockToken = findMagicBlockToken.call(this);
|
|
115881
|
+
if (!blockToken)
|
|
115882
|
+
return;
|
|
115883
|
+
const context = contextMap.get(blockToken);
|
|
115884
|
+
if (context) {
|
|
115885
|
+
context.dataChunks.push(this.sliceSerialize(token));
|
|
115886
|
+
}
|
|
115301
115887
|
}
|
|
115888
|
+
/**
|
|
115889
|
+
* Exit handler for end marker: If this is a failed end marker check (not the final marker),
|
|
115890
|
+
* add its content to the data chunks so we don't lose characters like '['.
|
|
115891
|
+
*/
|
|
115892
|
+
function exitMagicBlockMarkerEnd(token) {
|
|
115893
|
+
const blockToken = findMagicBlockToken.call(this);
|
|
115894
|
+
if (!blockToken)
|
|
115895
|
+
return;
|
|
115896
|
+
// Get the content of the marker
|
|
115897
|
+
const markerContent = this.sliceSerialize(token);
|
|
115898
|
+
// If this marker doesn't end with ']', it's a failed check and content belongs to data
|
|
115899
|
+
// The successful end marker would be "[/block]"
|
|
115900
|
+
if (!markerContent.endsWith(']') || markerContent !== '[/block]') {
|
|
115901
|
+
const context = contextMap.get(blockToken);
|
|
115902
|
+
if (context) {
|
|
115903
|
+
context.dataChunks.push(markerContent);
|
|
115904
|
+
}
|
|
115905
|
+
}
|
|
115906
|
+
}
|
|
115907
|
+
/**
|
|
115908
|
+
* Exit handler: Finalize the magicBlock node with parsed JSON data.
|
|
115909
|
+
*/
|
|
115910
|
+
function exitMagicBlock(token) {
|
|
115911
|
+
const context = contextMap.get(token);
|
|
115912
|
+
const node = this.stack[this.stack.length - 1];
|
|
115913
|
+
if (context) {
|
|
115914
|
+
const rawJson = context.dataChunks.join('');
|
|
115915
|
+
node.blockType = context.blockType;
|
|
115916
|
+
node.value = `[block:${context.blockType}]${rawJson}[/block]`;
|
|
115917
|
+
// Parse JSON data
|
|
115918
|
+
try {
|
|
115919
|
+
node.data = JSON.parse(rawJson.trim());
|
|
115920
|
+
}
|
|
115921
|
+
catch {
|
|
115922
|
+
// Invalid JSON - store empty object but keep the raw value
|
|
115923
|
+
node.data = {};
|
|
115924
|
+
}
|
|
115925
|
+
// Clean up context
|
|
115926
|
+
contextMap.delete(token);
|
|
115927
|
+
}
|
|
115928
|
+
this.exit(token);
|
|
115929
|
+
}
|
|
115930
|
+
/**
|
|
115931
|
+
* Handler to serialize magicBlock nodes back to markdown.
|
|
115932
|
+
*/
|
|
115933
|
+
const handleMagicBlock = (node) => {
|
|
115934
|
+
const magicNode = node;
|
|
115935
|
+
// If we have the original raw value, use it
|
|
115936
|
+
if (magicNode.value) {
|
|
115937
|
+
return magicNode.value;
|
|
115938
|
+
}
|
|
115939
|
+
// Otherwise reconstruct from parsed data
|
|
115940
|
+
const json = JSON.stringify(magicNode.data, null, 2);
|
|
115941
|
+
return `[block:${magicNode.blockType}]\n${json}\n[/block]`;
|
|
115942
|
+
};
|
|
115943
|
+
/**
|
|
115944
|
+
* Create an extension for `mdast-util-from-markdown` to enable magic blocks.
|
|
115945
|
+
*
|
|
115946
|
+
* Converts micromark magic block tokens into `magicBlock` MDAST nodes.
|
|
115947
|
+
*
|
|
115948
|
+
* @returns Extension for `mdast-util-from-markdown`
|
|
115949
|
+
*/
|
|
115950
|
+
function magicBlockFromMarkdown() {
|
|
115951
|
+
return {
|
|
115952
|
+
enter: {
|
|
115953
|
+
magicBlock: enterMagicBlock,
|
|
115954
|
+
},
|
|
115955
|
+
exit: {
|
|
115956
|
+
magicBlockType: exitMagicBlockType,
|
|
115957
|
+
magicBlockData: exitMagicBlockData,
|
|
115958
|
+
magicBlockLineEnding: exitMagicBlockLineEnding,
|
|
115959
|
+
magicBlockMarkerEnd: exitMagicBlockMarkerEnd,
|
|
115960
|
+
magicBlock: exitMagicBlock,
|
|
115961
|
+
},
|
|
115962
|
+
};
|
|
115963
|
+
}
|
|
115964
|
+
/**
|
|
115965
|
+
* Create an extension for `mdast-util-to-markdown` to serialize magic blocks.
|
|
115966
|
+
*
|
|
115967
|
+
* Converts `magicBlock` MDAST nodes back to `[block:TYPE]JSON[/block]` syntax.
|
|
115968
|
+
*
|
|
115969
|
+
* @returns Extension for `mdast-util-to-markdown`
|
|
115970
|
+
*/
|
|
115971
|
+
function magicBlockToMarkdown() {
|
|
115972
|
+
return {
|
|
115973
|
+
handlers: {
|
|
115974
|
+
magicBlock: handleMagicBlock,
|
|
115975
|
+
},
|
|
115976
|
+
};
|
|
115977
|
+
}
|
|
115978
|
+
|
|
115979
|
+
;// ./node_modules/micromark-util-symbol/lib/codes.js
|
|
115980
|
+
/**
|
|
115981
|
+
* Character codes.
|
|
115982
|
+
*
|
|
115983
|
+
* This module is compiled away!
|
|
115984
|
+
*
|
|
115985
|
+
* micromark works based on character codes.
|
|
115986
|
+
* This module contains constants for the ASCII block and the replacement
|
|
115987
|
+
* character.
|
|
115988
|
+
* A couple of them are handled in a special way, such as the line endings
|
|
115989
|
+
* (CR, LF, and CR+LF, commonly known as end-of-line: EOLs), the tab (horizontal
|
|
115990
|
+
* tab) and its expansion based on what column it’s at (virtual space),
|
|
115991
|
+
* and the end-of-file (eof) character.
|
|
115992
|
+
* As values are preprocessed before handling them, the actual characters LF,
|
|
115993
|
+
* CR, HT, and NUL (which is present as the replacement character), are
|
|
115994
|
+
* guaranteed to not exist.
|
|
115995
|
+
*
|
|
115996
|
+
* Unicode basic latin block.
|
|
115997
|
+
*/
|
|
115998
|
+
const codes = /** @type {const} */ ({
|
|
115999
|
+
carriageReturn: -5,
|
|
116000
|
+
lineFeed: -4,
|
|
116001
|
+
carriageReturnLineFeed: -3,
|
|
116002
|
+
horizontalTab: -2,
|
|
116003
|
+
virtualSpace: -1,
|
|
116004
|
+
eof: null,
|
|
116005
|
+
nul: 0,
|
|
116006
|
+
soh: 1,
|
|
116007
|
+
stx: 2,
|
|
116008
|
+
etx: 3,
|
|
116009
|
+
eot: 4,
|
|
116010
|
+
enq: 5,
|
|
116011
|
+
ack: 6,
|
|
116012
|
+
bel: 7,
|
|
116013
|
+
bs: 8,
|
|
116014
|
+
ht: 9, // `\t`
|
|
116015
|
+
lf: 10, // `\n`
|
|
116016
|
+
vt: 11, // `\v`
|
|
116017
|
+
ff: 12, // `\f`
|
|
116018
|
+
cr: 13, // `\r`
|
|
116019
|
+
so: 14,
|
|
116020
|
+
si: 15,
|
|
116021
|
+
dle: 16,
|
|
116022
|
+
dc1: 17,
|
|
116023
|
+
dc2: 18,
|
|
116024
|
+
dc3: 19,
|
|
116025
|
+
dc4: 20,
|
|
116026
|
+
nak: 21,
|
|
116027
|
+
syn: 22,
|
|
116028
|
+
etb: 23,
|
|
116029
|
+
can: 24,
|
|
116030
|
+
em: 25,
|
|
116031
|
+
sub: 26,
|
|
116032
|
+
esc: 27,
|
|
116033
|
+
fs: 28,
|
|
116034
|
+
gs: 29,
|
|
116035
|
+
rs: 30,
|
|
116036
|
+
us: 31,
|
|
116037
|
+
space: 32,
|
|
116038
|
+
exclamationMark: 33, // `!`
|
|
116039
|
+
quotationMark: 34, // `"`
|
|
116040
|
+
numberSign: 35, // `#`
|
|
116041
|
+
dollarSign: 36, // `$`
|
|
116042
|
+
percentSign: 37, // `%`
|
|
116043
|
+
ampersand: 38, // `&`
|
|
116044
|
+
apostrophe: 39, // `'`
|
|
116045
|
+
leftParenthesis: 40, // `(`
|
|
116046
|
+
rightParenthesis: 41, // `)`
|
|
116047
|
+
asterisk: 42, // `*`
|
|
116048
|
+
plusSign: 43, // `+`
|
|
116049
|
+
comma: 44, // `,`
|
|
116050
|
+
dash: 45, // `-`
|
|
116051
|
+
dot: 46, // `.`
|
|
116052
|
+
slash: 47, // `/`
|
|
116053
|
+
digit0: 48, // `0`
|
|
116054
|
+
digit1: 49, // `1`
|
|
116055
|
+
digit2: 50, // `2`
|
|
116056
|
+
digit3: 51, // `3`
|
|
116057
|
+
digit4: 52, // `4`
|
|
116058
|
+
digit5: 53, // `5`
|
|
116059
|
+
digit6: 54, // `6`
|
|
116060
|
+
digit7: 55, // `7`
|
|
116061
|
+
digit8: 56, // `8`
|
|
116062
|
+
digit9: 57, // `9`
|
|
116063
|
+
colon: 58, // `:`
|
|
116064
|
+
semicolon: 59, // `;`
|
|
116065
|
+
lessThan: 60, // `<`
|
|
116066
|
+
equalsTo: 61, // `=`
|
|
116067
|
+
greaterThan: 62, // `>`
|
|
116068
|
+
questionMark: 63, // `?`
|
|
116069
|
+
atSign: 64, // `@`
|
|
116070
|
+
uppercaseA: 65, // `A`
|
|
116071
|
+
uppercaseB: 66, // `B`
|
|
116072
|
+
uppercaseC: 67, // `C`
|
|
116073
|
+
uppercaseD: 68, // `D`
|
|
116074
|
+
uppercaseE: 69, // `E`
|
|
116075
|
+
uppercaseF: 70, // `F`
|
|
116076
|
+
uppercaseG: 71, // `G`
|
|
116077
|
+
uppercaseH: 72, // `H`
|
|
116078
|
+
uppercaseI: 73, // `I`
|
|
116079
|
+
uppercaseJ: 74, // `J`
|
|
116080
|
+
uppercaseK: 75, // `K`
|
|
116081
|
+
uppercaseL: 76, // `L`
|
|
116082
|
+
uppercaseM: 77, // `M`
|
|
116083
|
+
uppercaseN: 78, // `N`
|
|
116084
|
+
uppercaseO: 79, // `O`
|
|
116085
|
+
uppercaseP: 80, // `P`
|
|
116086
|
+
uppercaseQ: 81, // `Q`
|
|
116087
|
+
uppercaseR: 82, // `R`
|
|
116088
|
+
uppercaseS: 83, // `S`
|
|
116089
|
+
uppercaseT: 84, // `T`
|
|
116090
|
+
uppercaseU: 85, // `U`
|
|
116091
|
+
uppercaseV: 86, // `V`
|
|
116092
|
+
uppercaseW: 87, // `W`
|
|
116093
|
+
uppercaseX: 88, // `X`
|
|
116094
|
+
uppercaseY: 89, // `Y`
|
|
116095
|
+
uppercaseZ: 90, // `Z`
|
|
116096
|
+
leftSquareBracket: 91, // `[`
|
|
116097
|
+
backslash: 92, // `\`
|
|
116098
|
+
rightSquareBracket: 93, // `]`
|
|
116099
|
+
caret: 94, // `^`
|
|
116100
|
+
underscore: 95, // `_`
|
|
116101
|
+
graveAccent: 96, // `` ` ``
|
|
116102
|
+
lowercaseA: 97, // `a`
|
|
116103
|
+
lowercaseB: 98, // `b`
|
|
116104
|
+
lowercaseC: 99, // `c`
|
|
116105
|
+
lowercaseD: 100, // `d`
|
|
116106
|
+
lowercaseE: 101, // `e`
|
|
116107
|
+
lowercaseF: 102, // `f`
|
|
116108
|
+
lowercaseG: 103, // `g`
|
|
116109
|
+
lowercaseH: 104, // `h`
|
|
116110
|
+
lowercaseI: 105, // `i`
|
|
116111
|
+
lowercaseJ: 106, // `j`
|
|
116112
|
+
lowercaseK: 107, // `k`
|
|
116113
|
+
lowercaseL: 108, // `l`
|
|
116114
|
+
lowercaseM: 109, // `m`
|
|
116115
|
+
lowercaseN: 110, // `n`
|
|
116116
|
+
lowercaseO: 111, // `o`
|
|
116117
|
+
lowercaseP: 112, // `p`
|
|
116118
|
+
lowercaseQ: 113, // `q`
|
|
116119
|
+
lowercaseR: 114, // `r`
|
|
116120
|
+
lowercaseS: 115, // `s`
|
|
116121
|
+
lowercaseT: 116, // `t`
|
|
116122
|
+
lowercaseU: 117, // `u`
|
|
116123
|
+
lowercaseV: 118, // `v`
|
|
116124
|
+
lowercaseW: 119, // `w`
|
|
116125
|
+
lowercaseX: 120, // `x`
|
|
116126
|
+
lowercaseY: 121, // `y`
|
|
116127
|
+
lowercaseZ: 122, // `z`
|
|
116128
|
+
leftCurlyBrace: 123, // `{`
|
|
116129
|
+
verticalBar: 124, // `|`
|
|
116130
|
+
rightCurlyBrace: 125, // `}`
|
|
116131
|
+
tilde: 126, // `~`
|
|
116132
|
+
del: 127,
|
|
116133
|
+
// Unicode Specials block.
|
|
116134
|
+
byteOrderMarker: 65_279,
|
|
116135
|
+
// Unicode Specials block.
|
|
116136
|
+
replacementCharacter: 65_533 // `�`
|
|
116137
|
+
})
|
|
116138
|
+
|
|
116139
|
+
;// ./lib/micromark/magic-block/syntax.ts
|
|
116140
|
+
|
|
116141
|
+
|
|
116142
|
+
/**
|
|
116143
|
+
* Known magic block types that the tokenizer will recognize.
|
|
116144
|
+
* Unknown types will not be tokenized as magic blocks.
|
|
116145
|
+
*/
|
|
116146
|
+
const KNOWN_BLOCK_TYPES = new Set([
|
|
116147
|
+
'code',
|
|
116148
|
+
'api-header',
|
|
116149
|
+
'image',
|
|
116150
|
+
'callout',
|
|
116151
|
+
'parameters',
|
|
116152
|
+
'table',
|
|
116153
|
+
'embed',
|
|
116154
|
+
'html',
|
|
116155
|
+
'recipe',
|
|
116156
|
+
'tutorial-tile',
|
|
116157
|
+
]);
|
|
116158
|
+
/**
|
|
116159
|
+
* Check if a character is valid for a magic block type identifier.
|
|
116160
|
+
* Types can contain alphanumeric characters and hyphens (e.g., "api-header", "tutorial-tile")
|
|
116161
|
+
*/
|
|
116162
|
+
function isTypeChar(code) {
|
|
116163
|
+
return asciiAlphanumeric(code) || code === codes.dash;
|
|
116164
|
+
}
|
|
116165
|
+
/**
|
|
116166
|
+
* Creates the opening marker state machine: [block:
|
|
116167
|
+
* Returns the first state function to start parsing.
|
|
116168
|
+
*/
|
|
116169
|
+
function createOpeningMarkerParser(effects, nok, onComplete) {
|
|
116170
|
+
const expectB = (code) => {
|
|
116171
|
+
if (code !== codes.lowercaseB)
|
|
116172
|
+
return nok(code);
|
|
116173
|
+
effects.consume(code);
|
|
116174
|
+
return expectL;
|
|
116175
|
+
};
|
|
116176
|
+
const expectL = (code) => {
|
|
116177
|
+
if (code !== codes.lowercaseL)
|
|
116178
|
+
return nok(code);
|
|
116179
|
+
effects.consume(code);
|
|
116180
|
+
return expectO;
|
|
116181
|
+
};
|
|
116182
|
+
const expectO = (code) => {
|
|
116183
|
+
if (code !== codes.lowercaseO)
|
|
116184
|
+
return nok(code);
|
|
116185
|
+
effects.consume(code);
|
|
116186
|
+
return expectC;
|
|
116187
|
+
};
|
|
116188
|
+
const expectC = (code) => {
|
|
116189
|
+
if (code !== codes.lowercaseC)
|
|
116190
|
+
return nok(code);
|
|
116191
|
+
effects.consume(code);
|
|
116192
|
+
return expectK;
|
|
116193
|
+
};
|
|
116194
|
+
const expectK = (code) => {
|
|
116195
|
+
if (code !== codes.lowercaseK)
|
|
116196
|
+
return nok(code);
|
|
116197
|
+
effects.consume(code);
|
|
116198
|
+
return expectColon;
|
|
116199
|
+
};
|
|
116200
|
+
const expectColon = (code) => {
|
|
116201
|
+
if (code !== codes.colon)
|
|
116202
|
+
return nok(code);
|
|
116203
|
+
effects.consume(code);
|
|
116204
|
+
effects.exit('magicBlockMarkerStart');
|
|
116205
|
+
effects.enter('magicBlockType');
|
|
116206
|
+
return onComplete;
|
|
116207
|
+
};
|
|
116208
|
+
return expectB;
|
|
116209
|
+
}
|
|
116210
|
+
/**
|
|
116211
|
+
* Creates the type capture state machine.
|
|
116212
|
+
* Captures type characters until ] and validates against known types.
|
|
116213
|
+
*/
|
|
116214
|
+
function createTypeCaptureParser(effects, nok, onComplete, blockTypeRef) {
|
|
116215
|
+
const captureTypeFirst = (code) => {
|
|
116216
|
+
// Reject empty type name [block:]
|
|
116217
|
+
if (code === codes.rightSquareBracket) {
|
|
116218
|
+
return nok(code);
|
|
116219
|
+
}
|
|
116220
|
+
if (isTypeChar(code)) {
|
|
116221
|
+
blockTypeRef.value += String.fromCharCode(code);
|
|
116222
|
+
effects.consume(code);
|
|
116223
|
+
return captureType;
|
|
116224
|
+
}
|
|
116225
|
+
return nok(code);
|
|
116226
|
+
};
|
|
116227
|
+
const captureType = (code) => {
|
|
116228
|
+
if (code === codes.rightSquareBracket) {
|
|
116229
|
+
if (!KNOWN_BLOCK_TYPES.has(blockTypeRef.value)) {
|
|
116230
|
+
return nok(code);
|
|
116231
|
+
}
|
|
116232
|
+
effects.exit('magicBlockType');
|
|
116233
|
+
effects.enter('magicBlockMarkerTypeEnd');
|
|
116234
|
+
effects.consume(code);
|
|
116235
|
+
effects.exit('magicBlockMarkerTypeEnd');
|
|
116236
|
+
return onComplete;
|
|
116237
|
+
}
|
|
116238
|
+
if (isTypeChar(code)) {
|
|
116239
|
+
blockTypeRef.value += String.fromCharCode(code);
|
|
116240
|
+
effects.consume(code);
|
|
116241
|
+
return captureType;
|
|
116242
|
+
}
|
|
116243
|
+
return nok(code);
|
|
116244
|
+
};
|
|
116245
|
+
return { first: captureTypeFirst, remaining: captureType };
|
|
116246
|
+
}
|
|
116247
|
+
/**
|
|
116248
|
+
* Creates the closing marker state machine: /block]
|
|
116249
|
+
* Handles partial matches by calling onMismatch to fall back to data capture.
|
|
116250
|
+
*/
|
|
116251
|
+
function createClosingMarkerParser(effects, onSuccess, onMismatch, onEof, jsonState) {
|
|
116252
|
+
const handleMismatch = (code) => {
|
|
116253
|
+
if (jsonState && code === codes.quotationMark) {
|
|
116254
|
+
jsonState.inString = true;
|
|
116255
|
+
}
|
|
116256
|
+
return onMismatch(code);
|
|
116257
|
+
};
|
|
116258
|
+
const expectSlash = (code) => {
|
|
116259
|
+
if (code === null)
|
|
116260
|
+
return onEof(code);
|
|
116261
|
+
if (code !== codes.slash)
|
|
116262
|
+
return handleMismatch(code);
|
|
116263
|
+
effects.consume(code);
|
|
116264
|
+
return expectB;
|
|
116265
|
+
};
|
|
116266
|
+
const expectB = (code) => {
|
|
116267
|
+
if (code === null)
|
|
116268
|
+
return onEof(code);
|
|
116269
|
+
if (code !== codes.lowercaseB)
|
|
116270
|
+
return handleMismatch(code);
|
|
116271
|
+
effects.consume(code);
|
|
116272
|
+
return expectL;
|
|
116273
|
+
};
|
|
116274
|
+
const expectL = (code) => {
|
|
116275
|
+
if (code === null)
|
|
116276
|
+
return onEof(code);
|
|
116277
|
+
if (code !== codes.lowercaseL)
|
|
116278
|
+
return handleMismatch(code);
|
|
116279
|
+
effects.consume(code);
|
|
116280
|
+
return expectO;
|
|
116281
|
+
};
|
|
116282
|
+
const expectO = (code) => {
|
|
116283
|
+
if (code === null)
|
|
116284
|
+
return onEof(code);
|
|
116285
|
+
if (code !== codes.lowercaseO)
|
|
116286
|
+
return handleMismatch(code);
|
|
116287
|
+
effects.consume(code);
|
|
116288
|
+
return expectC;
|
|
116289
|
+
};
|
|
116290
|
+
const expectC = (code) => {
|
|
116291
|
+
if (code === null)
|
|
116292
|
+
return onEof(code);
|
|
116293
|
+
if (code !== codes.lowercaseC)
|
|
116294
|
+
return handleMismatch(code);
|
|
116295
|
+
effects.consume(code);
|
|
116296
|
+
return expectK;
|
|
116297
|
+
};
|
|
116298
|
+
const expectK = (code) => {
|
|
116299
|
+
if (code === null)
|
|
116300
|
+
return onEof(code);
|
|
116301
|
+
if (code !== codes.lowercaseK)
|
|
116302
|
+
return handleMismatch(code);
|
|
116303
|
+
effects.consume(code);
|
|
116304
|
+
return expectBracket;
|
|
116305
|
+
};
|
|
116306
|
+
const expectBracket = (code) => {
|
|
116307
|
+
if (code === null)
|
|
116308
|
+
return onEof(code);
|
|
116309
|
+
if (code !== codes.rightSquareBracket)
|
|
116310
|
+
return handleMismatch(code);
|
|
116311
|
+
effects.consume(code);
|
|
116312
|
+
return onSuccess;
|
|
116313
|
+
};
|
|
116314
|
+
return { expectSlash };
|
|
116315
|
+
}
|
|
116316
|
+
/**
|
|
116317
|
+
* Partial construct for checking non-lazy continuation.
|
|
116318
|
+
* This is used by the flow tokenizer to check if we can continue
|
|
116319
|
+
* parsing on the next line.
|
|
116320
|
+
*/
|
|
116321
|
+
const syntax_nonLazyContinuation = {
|
|
116322
|
+
partial: true,
|
|
116323
|
+
tokenize: syntax_tokenizeNonLazyContinuation,
|
|
116324
|
+
};
|
|
116325
|
+
/**
|
|
116326
|
+
* Tokenizer for non-lazy continuation checking.
|
|
116327
|
+
* Returns ok if the next line is non-lazy (can continue), nok if lazy.
|
|
116328
|
+
*/
|
|
116329
|
+
function syntax_tokenizeNonLazyContinuation(effects, ok, nok) {
|
|
116330
|
+
const lineStart = (code) => {
|
|
116331
|
+
// `this` here refers to the micromark parser
|
|
116332
|
+
// since we are just passing functions as references and not actually calling them
|
|
116333
|
+
// micromarks's internal parser will call this automatically with appropriate arguments
|
|
116334
|
+
return this.parser.lazy[this.now().line] ? nok(code) : ok(code);
|
|
116335
|
+
};
|
|
116336
|
+
const start = (code) => {
|
|
116337
|
+
if (code === null) {
|
|
116338
|
+
return nok(code);
|
|
116339
|
+
}
|
|
116340
|
+
if (!markdownLineEnding(code)) {
|
|
116341
|
+
return nok(code);
|
|
116342
|
+
}
|
|
116343
|
+
effects.enter('magicBlockLineEnding');
|
|
116344
|
+
effects.consume(code);
|
|
116345
|
+
effects.exit('magicBlockLineEnding');
|
|
116346
|
+
return lineStart;
|
|
116347
|
+
};
|
|
116348
|
+
return start;
|
|
116349
|
+
}
|
|
116350
|
+
/**
|
|
116351
|
+
* Create a micromark extension for magic block syntax.
|
|
116352
|
+
*
|
|
116353
|
+
* This extension handles both single-line and multiline magic blocks:
|
|
116354
|
+
* - Flow construct (concrete): Handles block-level multiline magic blocks at document level
|
|
116355
|
+
* - Text construct: Handles inline magic blocks in lists, paragraphs, etc.
|
|
116356
|
+
*
|
|
116357
|
+
* The flow construct is marked as "concrete" which prevents it from being
|
|
116358
|
+
* interrupted by container markers (like `>` for blockquotes or `-` for lists).
|
|
116359
|
+
*/
|
|
116360
|
+
function magicBlock() {
|
|
116361
|
+
return {
|
|
116362
|
+
// Flow construct - handles block-level magic blocks at document root
|
|
116363
|
+
// Marked as concrete to prevent interruption by containers
|
|
116364
|
+
flow: {
|
|
116365
|
+
[codes.leftSquareBracket]: {
|
|
116366
|
+
name: 'magicBlock',
|
|
116367
|
+
concrete: true,
|
|
116368
|
+
tokenize: tokenizeMagicBlockFlow,
|
|
116369
|
+
},
|
|
116370
|
+
},
|
|
116371
|
+
// Text construct - handles magic blocks in inline contexts (lists, paragraphs)
|
|
116372
|
+
text: {
|
|
116373
|
+
[codes.leftSquareBracket]: {
|
|
116374
|
+
name: 'magicBlock',
|
|
116375
|
+
tokenize: tokenizeMagicBlockText,
|
|
116376
|
+
},
|
|
116377
|
+
},
|
|
116378
|
+
};
|
|
116379
|
+
}
|
|
116380
|
+
/**
|
|
116381
|
+
* Flow tokenizer for block-level magic blocks (multiline).
|
|
116382
|
+
* Uses the continuation checking pattern from code fences.
|
|
116383
|
+
*/
|
|
116384
|
+
function tokenizeMagicBlockFlow(effects, ok, nok) {
|
|
116385
|
+
// State for tracking JSON content
|
|
116386
|
+
const jsonState = { escapeNext: false, inString: false };
|
|
116387
|
+
const blockTypeRef = { value: '' };
|
|
116388
|
+
let seenOpenBrace = false;
|
|
116389
|
+
// Create shared parsers for opening marker and type capture
|
|
116390
|
+
const typeParser = createTypeCaptureParser(effects, nok, beforeData, blockTypeRef);
|
|
116391
|
+
const openingMarkerParser = createOpeningMarkerParser(effects, nok, typeParser.first);
|
|
116392
|
+
return start;
|
|
116393
|
+
function start(code) {
|
|
116394
|
+
if (code !== codes.leftSquareBracket)
|
|
116395
|
+
return nok(code);
|
|
116396
|
+
effects.enter('magicBlock');
|
|
116397
|
+
effects.enter('magicBlockMarkerStart');
|
|
116398
|
+
effects.consume(code);
|
|
116399
|
+
return openingMarkerParser;
|
|
116400
|
+
}
|
|
116401
|
+
/**
|
|
116402
|
+
* State before data content - handles line endings or starts data capture.
|
|
116403
|
+
*/
|
|
116404
|
+
function beforeData(code) {
|
|
116405
|
+
// EOF - magic block must be closed
|
|
116406
|
+
if (code === null) {
|
|
116407
|
+
effects.exit('magicBlock');
|
|
116408
|
+
return nok(code);
|
|
116409
|
+
}
|
|
116410
|
+
// Line ending before any data - check continuation
|
|
116411
|
+
if (markdownLineEnding(code)) {
|
|
116412
|
+
return effects.check(syntax_nonLazyContinuation, continuationOkBeforeData, after)(code);
|
|
116413
|
+
}
|
|
116414
|
+
// Check for closing marker directly (without entering data)
|
|
116415
|
+
// This handles cases like [block:type]\n{}\n\n[/block] where there are
|
|
116416
|
+
// newlines after the data object
|
|
116417
|
+
if (code === codes.leftSquareBracket) {
|
|
116418
|
+
effects.enter('magicBlockMarkerEnd');
|
|
116419
|
+
effects.consume(code);
|
|
116420
|
+
return expectSlashFromContinuation;
|
|
116421
|
+
}
|
|
116422
|
+
// If we've already seen the opening brace, just continue capturing data
|
|
116423
|
+
if (seenOpenBrace) {
|
|
116424
|
+
effects.enter('magicBlockData');
|
|
116425
|
+
return captureData(code);
|
|
116426
|
+
}
|
|
116427
|
+
// Skip whitespace (spaces/tabs) before the data - stay in beforeData
|
|
116428
|
+
// Don't enter magicBlockData token yet until we confirm there's a '{'
|
|
116429
|
+
if (code === codes.space || code === codes.horizontalTab) {
|
|
116430
|
+
return beforeDataWhitespace(code);
|
|
116431
|
+
}
|
|
116432
|
+
// Data must start with '{' for valid JSON
|
|
116433
|
+
if (code !== codes.leftCurlyBrace) {
|
|
116434
|
+
effects.exit('magicBlock');
|
|
116435
|
+
return nok(code);
|
|
116436
|
+
}
|
|
116437
|
+
// We have '{' - enter data token and start capturing
|
|
116438
|
+
seenOpenBrace = true;
|
|
116439
|
+
effects.enter('magicBlockData');
|
|
116440
|
+
return captureData(code);
|
|
116441
|
+
}
|
|
116442
|
+
/**
|
|
116443
|
+
* Consume whitespace before the data without creating a token.
|
|
116444
|
+
* Uses a temporary token to satisfy micromark's requirement.
|
|
116445
|
+
*/
|
|
116446
|
+
function beforeDataWhitespace(code) {
|
|
116447
|
+
if (code === null) {
|
|
116448
|
+
effects.exit('magicBlock');
|
|
116449
|
+
return nok(code);
|
|
116450
|
+
}
|
|
116451
|
+
if (markdownLineEnding(code)) {
|
|
116452
|
+
return effects.check(syntax_nonLazyContinuation, continuationOkBeforeData, after)(code);
|
|
116453
|
+
}
|
|
116454
|
+
if (code === codes.space || code === codes.horizontalTab) {
|
|
116455
|
+
// We need to consume this but can't without a token - use magicBlockData
|
|
116456
|
+
// and track that we haven't seen '{' yet
|
|
116457
|
+
effects.enter('magicBlockData');
|
|
116458
|
+
effects.consume(code);
|
|
116459
|
+
return beforeDataWhitespaceContinue;
|
|
116460
|
+
}
|
|
116461
|
+
if (code === codes.leftCurlyBrace) {
|
|
116462
|
+
seenOpenBrace = true;
|
|
116463
|
+
effects.enter('magicBlockData');
|
|
116464
|
+
return captureData(code);
|
|
116465
|
+
}
|
|
116466
|
+
effects.exit('magicBlock');
|
|
116467
|
+
return nok(code);
|
|
116468
|
+
}
|
|
116469
|
+
/**
|
|
116470
|
+
* Continue consuming whitespace or validate the opening brace.
|
|
116471
|
+
*/
|
|
116472
|
+
function beforeDataWhitespaceContinue(code) {
|
|
116473
|
+
if (code === null) {
|
|
116474
|
+
effects.exit('magicBlockData');
|
|
116475
|
+
effects.exit('magicBlock');
|
|
116476
|
+
return nok(code);
|
|
116477
|
+
}
|
|
116478
|
+
if (markdownLineEnding(code)) {
|
|
116479
|
+
effects.exit('magicBlockData');
|
|
116480
|
+
return effects.check(syntax_nonLazyContinuation, continuationOk, after)(code);
|
|
116481
|
+
}
|
|
116482
|
+
if (code === codes.space || code === codes.horizontalTab) {
|
|
116483
|
+
effects.consume(code);
|
|
116484
|
+
return beforeDataWhitespaceContinue;
|
|
116485
|
+
}
|
|
116486
|
+
if (code === codes.leftCurlyBrace) {
|
|
116487
|
+
seenOpenBrace = true;
|
|
116488
|
+
return captureData(code);
|
|
116489
|
+
}
|
|
116490
|
+
effects.exit('magicBlockData');
|
|
116491
|
+
effects.exit('magicBlock');
|
|
116492
|
+
return nok(code);
|
|
116493
|
+
}
|
|
116494
|
+
/**
|
|
116495
|
+
* Continuation OK before we've entered data token.
|
|
116496
|
+
*/
|
|
116497
|
+
function continuationOkBeforeData(code) {
|
|
116498
|
+
effects.enter('magicBlockLineEnding');
|
|
116499
|
+
effects.consume(code);
|
|
116500
|
+
effects.exit('magicBlockLineEnding');
|
|
116501
|
+
return beforeData; // Stay in beforeData state - don't enter magicBlockData yet
|
|
116502
|
+
}
|
|
116503
|
+
function captureData(code) {
|
|
116504
|
+
// EOF - magic block must be closed
|
|
116505
|
+
if (code === null) {
|
|
116506
|
+
effects.exit('magicBlockData');
|
|
116507
|
+
effects.exit('magicBlock');
|
|
116508
|
+
return nok(code);
|
|
116509
|
+
}
|
|
116510
|
+
// At line ending, check if we can continue on the next line
|
|
116511
|
+
if (markdownLineEnding(code)) {
|
|
116512
|
+
effects.exit('magicBlockData');
|
|
116513
|
+
return effects.check(syntax_nonLazyContinuation, continuationOk, after)(code);
|
|
116514
|
+
}
|
|
116515
|
+
if (jsonState.escapeNext) {
|
|
116516
|
+
jsonState.escapeNext = false;
|
|
116517
|
+
effects.consume(code);
|
|
116518
|
+
return captureData;
|
|
116519
|
+
}
|
|
116520
|
+
if (jsonState.inString) {
|
|
116521
|
+
if (code === codes.backslash) {
|
|
116522
|
+
jsonState.escapeNext = true;
|
|
116523
|
+
effects.consume(code);
|
|
116524
|
+
return captureData;
|
|
116525
|
+
}
|
|
116526
|
+
if (code === codes.quotationMark) {
|
|
116527
|
+
jsonState.inString = false;
|
|
116528
|
+
}
|
|
116529
|
+
effects.consume(code);
|
|
116530
|
+
return captureData;
|
|
116531
|
+
}
|
|
116532
|
+
if (code === codes.quotationMark) {
|
|
116533
|
+
jsonState.inString = true;
|
|
116534
|
+
effects.consume(code);
|
|
116535
|
+
return captureData;
|
|
116536
|
+
}
|
|
116537
|
+
if (code === codes.leftSquareBracket) {
|
|
116538
|
+
effects.exit('magicBlockData');
|
|
116539
|
+
effects.enter('magicBlockMarkerEnd');
|
|
116540
|
+
effects.consume(code);
|
|
116541
|
+
return expectSlash;
|
|
116542
|
+
}
|
|
116543
|
+
effects.consume(code);
|
|
116544
|
+
return captureData;
|
|
116545
|
+
}
|
|
116546
|
+
/**
|
|
116547
|
+
* Called when non-lazy continuation check passes - we can continue parsing.
|
|
116548
|
+
*/
|
|
116549
|
+
function continuationOk(code) {
|
|
116550
|
+
// Consume the line ending
|
|
116551
|
+
effects.enter('magicBlockLineEnding');
|
|
116552
|
+
effects.consume(code);
|
|
116553
|
+
effects.exit('magicBlockLineEnding');
|
|
116554
|
+
return continuationStart;
|
|
116555
|
+
}
|
|
116556
|
+
/**
|
|
116557
|
+
* Start of continuation line - check for more line endings, closing marker, or start capturing data.
|
|
116558
|
+
*/
|
|
116559
|
+
function continuationStart(code) {
|
|
116560
|
+
// Handle consecutive line endings
|
|
116561
|
+
if (code === null) {
|
|
116562
|
+
effects.exit('magicBlock');
|
|
116563
|
+
return nok(code);
|
|
116564
|
+
}
|
|
116565
|
+
if (markdownLineEnding(code)) {
|
|
116566
|
+
return effects.check(syntax_nonLazyContinuation, continuationOkBeforeData, after)(code);
|
|
116567
|
+
}
|
|
116568
|
+
// Check if this is the start of the closing marker [/block]
|
|
116569
|
+
// If so, handle it directly without entering magicBlockData
|
|
116570
|
+
if (code === codes.leftSquareBracket) {
|
|
116571
|
+
effects.enter('magicBlockMarkerEnd');
|
|
116572
|
+
effects.consume(code);
|
|
116573
|
+
return expectSlashFromContinuation;
|
|
116574
|
+
}
|
|
116575
|
+
effects.enter('magicBlockData');
|
|
116576
|
+
return captureData(code);
|
|
116577
|
+
}
|
|
116578
|
+
/**
|
|
116579
|
+
* Check for closing marker slash when coming from continuation.
|
|
116580
|
+
* If not a closing marker, create an empty data token and continue.
|
|
116581
|
+
*/
|
|
116582
|
+
function expectSlashFromContinuation(code) {
|
|
116583
|
+
if (code === null) {
|
|
116584
|
+
effects.exit('magicBlockMarkerEnd');
|
|
116585
|
+
effects.exit('magicBlock');
|
|
116586
|
+
return nok(code);
|
|
116587
|
+
}
|
|
116588
|
+
if (markdownLineEnding(code)) {
|
|
116589
|
+
effects.exit('magicBlockMarkerEnd');
|
|
116590
|
+
return effects.check(syntax_nonLazyContinuation, continuationOkBeforeData, after)(code);
|
|
116591
|
+
}
|
|
116592
|
+
if (code === codes.slash) {
|
|
116593
|
+
effects.consume(code);
|
|
116594
|
+
return expectClosingB;
|
|
116595
|
+
}
|
|
116596
|
+
// Not a closing marker - this is data content
|
|
116597
|
+
// Exit marker and enter data
|
|
116598
|
+
effects.exit('magicBlockMarkerEnd');
|
|
116599
|
+
effects.enter('magicBlockData');
|
|
116600
|
+
// The [ was consumed by the marker, so we need to conceptually "have it" in our data
|
|
116601
|
+
// But since we already consumed it into the marker, we need a different approach
|
|
116602
|
+
// Re-classify: consume this character as data and continue
|
|
116603
|
+
if (code === codes.quotationMark)
|
|
116604
|
+
jsonState.inString = true;
|
|
116605
|
+
effects.consume(code);
|
|
116606
|
+
return captureData;
|
|
116607
|
+
}
|
|
116608
|
+
function expectSlash(code) {
|
|
116609
|
+
if (code === null) {
|
|
116610
|
+
effects.exit('magicBlockMarkerEnd');
|
|
116611
|
+
effects.exit('magicBlock');
|
|
116612
|
+
return nok(code);
|
|
116613
|
+
}
|
|
116614
|
+
// At line ending during marker parsing
|
|
116615
|
+
if (markdownLineEnding(code)) {
|
|
116616
|
+
effects.exit('magicBlockMarkerEnd');
|
|
116617
|
+
return effects.check(syntax_nonLazyContinuation, continuationOk, after)(code);
|
|
116618
|
+
}
|
|
116619
|
+
if (code !== codes.slash) {
|
|
116620
|
+
effects.exit('magicBlockMarkerEnd');
|
|
116621
|
+
effects.enter('magicBlockData');
|
|
116622
|
+
if (code === codes.quotationMark)
|
|
116623
|
+
jsonState.inString = true;
|
|
116624
|
+
effects.consume(code);
|
|
116625
|
+
return captureData;
|
|
116626
|
+
}
|
|
116627
|
+
effects.consume(code);
|
|
116628
|
+
return expectClosingB;
|
|
116629
|
+
}
|
|
116630
|
+
function expectClosingB(code) {
|
|
116631
|
+
if (code === null) {
|
|
116632
|
+
effects.exit('magicBlockMarkerEnd');
|
|
116633
|
+
effects.exit('magicBlock');
|
|
116634
|
+
return nok(code);
|
|
116635
|
+
}
|
|
116636
|
+
if (code !== codes.lowercaseB) {
|
|
116637
|
+
effects.exit('magicBlockMarkerEnd');
|
|
116638
|
+
effects.enter('magicBlockData');
|
|
116639
|
+
if (code === codes.quotationMark)
|
|
116640
|
+
jsonState.inString = true;
|
|
116641
|
+
effects.consume(code);
|
|
116642
|
+
return captureData;
|
|
116643
|
+
}
|
|
116644
|
+
effects.consume(code);
|
|
116645
|
+
return expectClosingL;
|
|
116646
|
+
}
|
|
116647
|
+
function expectClosingL(code) {
|
|
116648
|
+
if (code === null) {
|
|
116649
|
+
effects.exit('magicBlockMarkerEnd');
|
|
116650
|
+
effects.exit('magicBlock');
|
|
116651
|
+
return nok(code);
|
|
116652
|
+
}
|
|
116653
|
+
if (code !== codes.lowercaseL) {
|
|
116654
|
+
effects.exit('magicBlockMarkerEnd');
|
|
116655
|
+
effects.enter('magicBlockData');
|
|
116656
|
+
if (code === codes.quotationMark)
|
|
116657
|
+
jsonState.inString = true;
|
|
116658
|
+
effects.consume(code);
|
|
116659
|
+
return captureData;
|
|
116660
|
+
}
|
|
116661
|
+
effects.consume(code);
|
|
116662
|
+
return expectClosingO;
|
|
116663
|
+
}
|
|
116664
|
+
function expectClosingO(code) {
|
|
116665
|
+
if (code === null) {
|
|
116666
|
+
effects.exit('magicBlockMarkerEnd');
|
|
116667
|
+
effects.exit('magicBlock');
|
|
116668
|
+
return nok(code);
|
|
116669
|
+
}
|
|
116670
|
+
if (code !== codes.lowercaseO) {
|
|
116671
|
+
effects.exit('magicBlockMarkerEnd');
|
|
116672
|
+
effects.enter('magicBlockData');
|
|
116673
|
+
if (code === codes.quotationMark)
|
|
116674
|
+
jsonState.inString = true;
|
|
116675
|
+
effects.consume(code);
|
|
116676
|
+
return captureData;
|
|
116677
|
+
}
|
|
116678
|
+
effects.consume(code);
|
|
116679
|
+
return expectClosingC;
|
|
116680
|
+
}
|
|
116681
|
+
function expectClosingC(code) {
|
|
116682
|
+
if (code === null) {
|
|
116683
|
+
effects.exit('magicBlockMarkerEnd');
|
|
116684
|
+
effects.exit('magicBlock');
|
|
116685
|
+
return nok(code);
|
|
116686
|
+
}
|
|
116687
|
+
if (code !== codes.lowercaseC) {
|
|
116688
|
+
effects.exit('magicBlockMarkerEnd');
|
|
116689
|
+
effects.enter('magicBlockData');
|
|
116690
|
+
if (code === codes.quotationMark)
|
|
116691
|
+
jsonState.inString = true;
|
|
116692
|
+
effects.consume(code);
|
|
116693
|
+
return captureData;
|
|
116694
|
+
}
|
|
116695
|
+
effects.consume(code);
|
|
116696
|
+
return expectClosingK;
|
|
116697
|
+
}
|
|
116698
|
+
function expectClosingK(code) {
|
|
116699
|
+
if (code === null) {
|
|
116700
|
+
effects.exit('magicBlockMarkerEnd');
|
|
116701
|
+
effects.exit('magicBlock');
|
|
116702
|
+
return nok(code);
|
|
116703
|
+
}
|
|
116704
|
+
if (code !== codes.lowercaseK) {
|
|
116705
|
+
effects.exit('magicBlockMarkerEnd');
|
|
116706
|
+
effects.enter('magicBlockData');
|
|
116707
|
+
if (code === codes.quotationMark)
|
|
116708
|
+
jsonState.inString = true;
|
|
116709
|
+
effects.consume(code);
|
|
116710
|
+
return captureData;
|
|
116711
|
+
}
|
|
116712
|
+
effects.consume(code);
|
|
116713
|
+
return expectClosingBracket;
|
|
116714
|
+
}
|
|
116715
|
+
function expectClosingBracket(code) {
|
|
116716
|
+
if (code === null) {
|
|
116717
|
+
effects.exit('magicBlockMarkerEnd');
|
|
116718
|
+
effects.exit('magicBlock');
|
|
116719
|
+
return nok(code);
|
|
116720
|
+
}
|
|
116721
|
+
if (code !== codes.rightSquareBracket) {
|
|
116722
|
+
effects.exit('magicBlockMarkerEnd');
|
|
116723
|
+
effects.enter('magicBlockData');
|
|
116724
|
+
if (code === codes.quotationMark)
|
|
116725
|
+
jsonState.inString = true;
|
|
116726
|
+
effects.consume(code);
|
|
116727
|
+
return captureData;
|
|
116728
|
+
}
|
|
116729
|
+
effects.consume(code);
|
|
116730
|
+
effects.exit('magicBlockMarkerEnd');
|
|
116731
|
+
// Check for trailing whitespace on the same line
|
|
116732
|
+
return consumeTrailing;
|
|
116733
|
+
}
|
|
116734
|
+
/**
|
|
116735
|
+
* Consume trailing whitespace (spaces/tabs) on the same line after [/block].
|
|
116736
|
+
* For concrete flow constructs, we must end at eol/eof or fail.
|
|
116737
|
+
* Trailing whitespace is consumed; other content causes nok (text tokenizer handles it).
|
|
116738
|
+
*/
|
|
116739
|
+
function consumeTrailing(code) {
|
|
116740
|
+
// End of file - done
|
|
116741
|
+
if (code === null) {
|
|
116742
|
+
effects.exit('magicBlock');
|
|
116743
|
+
return ok(code);
|
|
116744
|
+
}
|
|
116745
|
+
// Line ending - done
|
|
116746
|
+
if (markdownLineEnding(code)) {
|
|
116747
|
+
effects.exit('magicBlock');
|
|
116748
|
+
return ok(code);
|
|
116749
|
+
}
|
|
116750
|
+
// Space or tab - consume as trailing whitespace
|
|
116751
|
+
if (code === codes.space || code === codes.horizontalTab) {
|
|
116752
|
+
effects.enter('magicBlockTrailing');
|
|
116753
|
+
effects.consume(code);
|
|
116754
|
+
return consumeTrailingContinue;
|
|
116755
|
+
}
|
|
116756
|
+
// Any other character - fail flow tokenizer, let text tokenizer handle it
|
|
116757
|
+
effects.exit('magicBlock');
|
|
116758
|
+
return nok(code);
|
|
116759
|
+
}
|
|
116760
|
+
/**
|
|
116761
|
+
* Continue consuming trailing whitespace.
|
|
116762
|
+
*/
|
|
116763
|
+
function consumeTrailingContinue(code) {
|
|
116764
|
+
// End of file - done
|
|
116765
|
+
if (code === null) {
|
|
116766
|
+
effects.exit('magicBlockTrailing');
|
|
116767
|
+
effects.exit('magicBlock');
|
|
116768
|
+
return ok(code);
|
|
116769
|
+
}
|
|
116770
|
+
// Line ending - done
|
|
116771
|
+
if (markdownLineEnding(code)) {
|
|
116772
|
+
effects.exit('magicBlockTrailing');
|
|
116773
|
+
effects.exit('magicBlock');
|
|
116774
|
+
return ok(code);
|
|
116775
|
+
}
|
|
116776
|
+
// More space or tab - keep consuming
|
|
116777
|
+
if (code === codes.space || code === codes.horizontalTab) {
|
|
116778
|
+
effects.consume(code);
|
|
116779
|
+
return consumeTrailingContinue;
|
|
116780
|
+
}
|
|
116781
|
+
// Non-whitespace after whitespace - fail flow tokenizer
|
|
116782
|
+
effects.exit('magicBlockTrailing');
|
|
116783
|
+
effects.exit('magicBlock');
|
|
116784
|
+
return nok(code);
|
|
116785
|
+
}
|
|
116786
|
+
/**
|
|
116787
|
+
* Called when we can't continue (lazy line or EOF) - exit the construct.
|
|
116788
|
+
*/
|
|
116789
|
+
function after(code) {
|
|
116790
|
+
effects.exit('magicBlock');
|
|
116791
|
+
return nok(code);
|
|
116792
|
+
}
|
|
116793
|
+
}
|
|
116794
|
+
/**
|
|
116795
|
+
* Text tokenizer for single-line magic blocks only.
|
|
116796
|
+
* Used in inline contexts like list items and paragraphs.
|
|
116797
|
+
* Multiline blocks are handled by the flow tokenizer.
|
|
116798
|
+
*/
|
|
116799
|
+
function tokenizeMagicBlockText(effects, ok, nok) {
|
|
116800
|
+
// State for tracking JSON content
|
|
116801
|
+
const jsonState = { escapeNext: false, inString: false };
|
|
116802
|
+
const blockTypeRef = { value: '' };
|
|
116803
|
+
let seenOpenBrace = false;
|
|
116804
|
+
// Create shared parsers for opening marker and type capture
|
|
116805
|
+
const typeParser = createTypeCaptureParser(effects, nok, beforeData, blockTypeRef);
|
|
116806
|
+
const openingMarkerParser = createOpeningMarkerParser(effects, nok, typeParser.first);
|
|
116807
|
+
// Success handler for closing marker - exits tokens and returns ok
|
|
116808
|
+
const closingSuccess = (code) => {
|
|
116809
|
+
effects.exit('magicBlockMarkerEnd');
|
|
116810
|
+
effects.exit('magicBlock');
|
|
116811
|
+
return ok(code);
|
|
116812
|
+
};
|
|
116813
|
+
// Mismatch handler - falls back to data capture
|
|
116814
|
+
const closingMismatch = (code) => {
|
|
116815
|
+
effects.exit('magicBlockMarkerEnd');
|
|
116816
|
+
effects.enter('magicBlockData');
|
|
116817
|
+
if (code === codes.quotationMark)
|
|
116818
|
+
jsonState.inString = true;
|
|
116819
|
+
effects.consume(code);
|
|
116820
|
+
return captureData;
|
|
116821
|
+
};
|
|
116822
|
+
// EOF handler
|
|
116823
|
+
const closingEof = (_code) => nok(_code);
|
|
116824
|
+
// Create closing marker parsers
|
|
116825
|
+
const closingFromBeforeData = createClosingMarkerParser(effects, closingSuccess, closingMismatch, closingEof, jsonState);
|
|
116826
|
+
const closingFromData = createClosingMarkerParser(effects, closingSuccess, closingMismatch, closingEof, jsonState);
|
|
116827
|
+
return start;
|
|
116828
|
+
function start(code) {
|
|
116829
|
+
if (code !== codes.leftSquareBracket)
|
|
116830
|
+
return nok(code);
|
|
116831
|
+
effects.enter('magicBlock');
|
|
116832
|
+
effects.enter('magicBlockMarkerStart');
|
|
116833
|
+
effects.consume(code);
|
|
116834
|
+
return openingMarkerParser;
|
|
116835
|
+
}
|
|
116836
|
+
/**
|
|
116837
|
+
* State before data content - handles line endings before entering data token.
|
|
116838
|
+
* Whitespace before '{' is allowed.
|
|
116839
|
+
*/
|
|
116840
|
+
function beforeData(code) {
|
|
116841
|
+
// Fail on EOF - magic block must be closed
|
|
116842
|
+
if (code === null) {
|
|
116843
|
+
return nok(code);
|
|
116844
|
+
}
|
|
116845
|
+
// Handle line endings before any data - consume them without entering data token
|
|
116846
|
+
if (markdownLineEnding(code)) {
|
|
116847
|
+
effects.enter('magicBlockLineEnding');
|
|
116848
|
+
effects.consume(code);
|
|
116849
|
+
effects.exit('magicBlockLineEnding');
|
|
116850
|
+
return beforeData;
|
|
116851
|
+
}
|
|
116852
|
+
// Check for closing marker directly (without entering data)
|
|
116853
|
+
if (code === codes.leftSquareBracket) {
|
|
116854
|
+
effects.enter('magicBlockMarkerEnd');
|
|
116855
|
+
effects.consume(code);
|
|
116856
|
+
return closingFromBeforeData.expectSlash;
|
|
116857
|
+
}
|
|
116858
|
+
// If we've already seen the opening brace, just continue capturing data
|
|
116859
|
+
if (seenOpenBrace) {
|
|
116860
|
+
effects.enter('magicBlockData');
|
|
116861
|
+
return captureData(code);
|
|
116862
|
+
}
|
|
116863
|
+
// Skip whitespace (spaces/tabs) before the data
|
|
116864
|
+
if (code === codes.space || code === codes.horizontalTab) {
|
|
116865
|
+
return beforeDataWhitespace(code);
|
|
116866
|
+
}
|
|
116867
|
+
// Data must start with '{' for valid JSON
|
|
116868
|
+
if (code !== codes.leftCurlyBrace) {
|
|
116869
|
+
return nok(code);
|
|
116870
|
+
}
|
|
116871
|
+
// We have '{' - enter data token and start capturing
|
|
116872
|
+
seenOpenBrace = true;
|
|
116873
|
+
effects.enter('magicBlockData');
|
|
116874
|
+
return captureData(code);
|
|
116875
|
+
}
|
|
116876
|
+
/**
|
|
116877
|
+
* Consume whitespace before the data.
|
|
116878
|
+
*/
|
|
116879
|
+
function beforeDataWhitespace(code) {
|
|
116880
|
+
if (code === null) {
|
|
116881
|
+
return nok(code);
|
|
116882
|
+
}
|
|
116883
|
+
if (markdownLineEnding(code)) {
|
|
116884
|
+
effects.enter('magicBlockLineEnding');
|
|
116885
|
+
effects.consume(code);
|
|
116886
|
+
effects.exit('magicBlockLineEnding');
|
|
116887
|
+
return beforeData;
|
|
116888
|
+
}
|
|
116889
|
+
if (code === codes.space || code === codes.horizontalTab) {
|
|
116890
|
+
effects.enter('magicBlockData');
|
|
116891
|
+
effects.consume(code);
|
|
116892
|
+
return beforeDataWhitespaceContinue;
|
|
116893
|
+
}
|
|
116894
|
+
if (code === codes.leftCurlyBrace) {
|
|
116895
|
+
seenOpenBrace = true;
|
|
116896
|
+
effects.enter('magicBlockData');
|
|
116897
|
+
return captureData(code);
|
|
116898
|
+
}
|
|
116899
|
+
return nok(code);
|
|
116900
|
+
}
|
|
116901
|
+
/**
|
|
116902
|
+
* Continue consuming whitespace or validate the opening brace.
|
|
116903
|
+
*/
|
|
116904
|
+
function beforeDataWhitespaceContinue(code) {
|
|
116905
|
+
if (code === null) {
|
|
116906
|
+
effects.exit('magicBlockData');
|
|
116907
|
+
return nok(code);
|
|
116908
|
+
}
|
|
116909
|
+
if (markdownLineEnding(code)) {
|
|
116910
|
+
effects.exit('magicBlockData');
|
|
116911
|
+
effects.enter('magicBlockLineEnding');
|
|
116912
|
+
effects.consume(code);
|
|
116913
|
+
effects.exit('magicBlockLineEnding');
|
|
116914
|
+
return beforeData;
|
|
116915
|
+
}
|
|
116916
|
+
if (code === codes.space || code === codes.horizontalTab) {
|
|
116917
|
+
effects.consume(code);
|
|
116918
|
+
return beforeDataWhitespaceContinue;
|
|
116919
|
+
}
|
|
116920
|
+
if (code === codes.leftCurlyBrace) {
|
|
116921
|
+
seenOpenBrace = true;
|
|
116922
|
+
return captureData(code);
|
|
116923
|
+
}
|
|
116924
|
+
effects.exit('magicBlockData');
|
|
116925
|
+
return nok(code);
|
|
116926
|
+
}
|
|
116927
|
+
function captureData(code) {
|
|
116928
|
+
// Fail on EOF - magic block must be closed
|
|
116929
|
+
if (code === null) {
|
|
116930
|
+
effects.exit('magicBlockData');
|
|
116931
|
+
return nok(code);
|
|
116932
|
+
}
|
|
116933
|
+
// Handle multiline magic blocks within text/paragraphs
|
|
116934
|
+
// Exit data, consume line ending with proper token, then re-enter data
|
|
116935
|
+
if (markdownLineEnding(code)) {
|
|
116936
|
+
effects.exit('magicBlockData');
|
|
116937
|
+
effects.enter('magicBlockLineEnding');
|
|
116938
|
+
effects.consume(code);
|
|
116939
|
+
effects.exit('magicBlockLineEnding');
|
|
116940
|
+
return beforeData; // Go back to beforeData to handle potential empty lines
|
|
116941
|
+
}
|
|
116942
|
+
if (jsonState.escapeNext) {
|
|
116943
|
+
jsonState.escapeNext = false;
|
|
116944
|
+
effects.consume(code);
|
|
116945
|
+
return captureData;
|
|
116946
|
+
}
|
|
116947
|
+
if (jsonState.inString) {
|
|
116948
|
+
if (code === codes.backslash) {
|
|
116949
|
+
jsonState.escapeNext = true;
|
|
116950
|
+
effects.consume(code);
|
|
116951
|
+
return captureData;
|
|
116952
|
+
}
|
|
116953
|
+
if (code === codes.quotationMark) {
|
|
116954
|
+
jsonState.inString = false;
|
|
116955
|
+
}
|
|
116956
|
+
effects.consume(code);
|
|
116957
|
+
return captureData;
|
|
116958
|
+
}
|
|
116959
|
+
if (code === codes.quotationMark) {
|
|
116960
|
+
jsonState.inString = true;
|
|
116961
|
+
effects.consume(code);
|
|
116962
|
+
return captureData;
|
|
116963
|
+
}
|
|
116964
|
+
if (code === codes.leftSquareBracket) {
|
|
116965
|
+
effects.exit('magicBlockData');
|
|
116966
|
+
effects.enter('magicBlockMarkerEnd');
|
|
116967
|
+
effects.consume(code);
|
|
116968
|
+
return closingFromData.expectSlash;
|
|
116969
|
+
}
|
|
116970
|
+
effects.consume(code);
|
|
116971
|
+
return captureData;
|
|
116972
|
+
}
|
|
116973
|
+
}
|
|
116974
|
+
/* harmony default export */ const syntax = ((/* unused pure expression or super */ null && (magicBlock)));
|
|
116975
|
+
|
|
116976
|
+
;// ./lib/micromark/magic-block/index.ts
|
|
116977
|
+
/**
|
|
116978
|
+
* Micromark extension for magic block syntax.
|
|
116979
|
+
*
|
|
116980
|
+
* Usage:
|
|
116981
|
+
* ```ts
|
|
116982
|
+
* import { magicBlock } from './lib/micromark/magic-block';
|
|
116983
|
+
*
|
|
116984
|
+
* const processor = unified()
|
|
116985
|
+
* .data('micromarkExtensions', [magicBlock()])
|
|
116986
|
+
* ```
|
|
116987
|
+
*/
|
|
116988
|
+
|
|
115302
116989
|
|
|
115303
116990
|
;// ./lib/utils/mdxish/mdxish-load-components.ts
|
|
115304
116991
|
|
|
@@ -115365,46 +117052,56 @@ function loadComponents() {
|
|
|
115365
117052
|
|
|
115366
117053
|
|
|
115367
117054
|
|
|
117055
|
+
|
|
117056
|
+
|
|
115368
117057
|
|
|
115369
117058
|
|
|
115370
117059
|
|
|
115371
117060
|
const defaultTransformers = [callouts, code_tabs, gemoji_, transform_embeds];
|
|
115372
117061
|
function mdxishAstProcessor(mdContent, opts = {}) {
|
|
115373
|
-
const { components: userComponents = {}, jsxContext = {}, useTailwind } = opts;
|
|
117062
|
+
const { components: userComponents = {}, jsxContext = {}, newEditorTypes = false, safeMode = false, useTailwind, } = opts;
|
|
115374
117063
|
const components = {
|
|
115375
117064
|
...loadComponents(),
|
|
115376
117065
|
...userComponents,
|
|
115377
117066
|
};
|
|
117067
|
+
// Build set of known component names for snake_case filtering
|
|
117068
|
+
const knownComponents = new Set(Object.keys(components));
|
|
115378
117069
|
// Preprocessing pipeline: Transform content to be parser-ready
|
|
115379
|
-
// Step 1:
|
|
115380
|
-
const
|
|
115381
|
-
// Step 2:
|
|
115382
|
-
const
|
|
115383
|
-
|
|
115384
|
-
|
|
115385
|
-
// Step
|
|
115386
|
-
|
|
115387
|
-
const { content: parserReadyContent, mapping: snakeCaseMapping } = processSnakeCaseComponent(contentAfterJSXEvaluation);
|
|
117070
|
+
// Step 1: Normalize malformed table separator syntax (e.g., `|: ---` → `| :---`)
|
|
117071
|
+
const contentAfterTableNormalization = normalizeTableSeparator(mdContent);
|
|
117072
|
+
// Step 2: Evaluate JSX expressions in attributes
|
|
117073
|
+
const contentAfterJSXEvaluation = safeMode
|
|
117074
|
+
? contentAfterTableNormalization
|
|
117075
|
+
: preprocessJSXExpressions(contentAfterTableNormalization, jsxContext);
|
|
117076
|
+
// Step 3: Replace snake_case component names with parser-safe placeholders
|
|
117077
|
+
const { content: parserReadyContent, mapping: snakeCaseMapping } = processSnakeCaseComponent(contentAfterJSXEvaluation, { knownComponents });
|
|
115388
117078
|
// Create string map for tailwind transformer
|
|
115389
117079
|
const tempComponentsMap = Object.entries(components).reduce((acc, [key, value]) => {
|
|
115390
117080
|
acc[key] = String(value);
|
|
115391
117081
|
return acc;
|
|
115392
117082
|
}, {});
|
|
117083
|
+
// Get mdxExpression extension and remove its flow construct to prevent
|
|
117084
|
+
// `{...}` from interrupting paragraphs (which breaks multiline magic blocks)
|
|
117085
|
+
const mdxExprExt = mdxExpression({ allowEmpty: true });
|
|
117086
|
+
const mdxExprTextOnly = {
|
|
117087
|
+
text: mdxExprExt.text,
|
|
117088
|
+
};
|
|
115393
117089
|
const processor = unified()
|
|
115394
|
-
.data('micromarkExtensions', [
|
|
115395
|
-
.data('fromMarkdownExtensions', [mdxExpressionFromMarkdown()])
|
|
117090
|
+
.data('micromarkExtensions', safeMode ? [magicBlock()] : [magicBlock(), mdxExprTextOnly])
|
|
117091
|
+
.data('fromMarkdownExtensions', safeMode ? [magicBlockFromMarkdown()] : [magicBlockFromMarkdown(), mdxExpressionFromMarkdown()])
|
|
115396
117092
|
.use(remarkParse)
|
|
115397
117093
|
.use(remarkFrontmatter)
|
|
115398
117094
|
.use(normalize_malformed_md_syntax)
|
|
115399
|
-
.use(
|
|
117095
|
+
.use(magic_block_transformer)
|
|
115400
117096
|
.use(transform_images, { isMdxish: true })
|
|
115401
117097
|
.use(defaultTransformers)
|
|
115402
117098
|
.use(mdxish_component_blocks)
|
|
115403
117099
|
.use(restore_snake_case_component_name, { mapping: snakeCaseMapping })
|
|
115404
117100
|
.use(mdxish_tables)
|
|
115405
117101
|
.use(mdxish_html_blocks)
|
|
115406
|
-
.use(
|
|
115407
|
-
.use(
|
|
117102
|
+
.use(newEditorTypes ? mdxish_jsx_to_mdast : undefined) // Convert JSX elements to MDAST types
|
|
117103
|
+
.use(safeMode ? undefined : evaluate_expressions, { context: jsxContext }) // Evaluate MDX expressions using jsxContext
|
|
117104
|
+
.use(variables_text) // Parse {user.*} patterns from text
|
|
115408
117105
|
.use(useTailwind ? transform_tailwind : undefined, { components: tempComponentsMap })
|
|
115409
117106
|
.use(remarkGfm);
|
|
115410
117107
|
return {
|
|
@@ -115421,10 +117118,14 @@ function mdxishAstProcessor(mdContent, opts = {}) {
|
|
|
115421
117118
|
* Converts an Mdast to a Markdown string.
|
|
115422
117119
|
*/
|
|
115423
117120
|
function mdxishMdastToMd(mdast) {
|
|
115424
|
-
const md = unified()
|
|
117121
|
+
const md = unified()
|
|
117122
|
+
.use(remarkGfm)
|
|
117123
|
+
.use(mdxishCompilers)
|
|
117124
|
+
.use(remarkStringify, {
|
|
115425
117125
|
bullet: '-',
|
|
115426
117126
|
emphasis: '_',
|
|
115427
|
-
})
|
|
117127
|
+
})
|
|
117128
|
+
.stringify(mdast);
|
|
115428
117129
|
return md;
|
|
115429
117130
|
}
|
|
115430
117131
|
/**
|
|
@@ -115920,11 +117621,14 @@ const tags = (doc) => {
|
|
|
115920
117621
|
|
|
115921
117622
|
|
|
115922
117623
|
|
|
117624
|
+
|
|
115923
117625
|
const mdxishTags_tags = (doc) => {
|
|
115924
|
-
const { replaced: sanitizedDoc } = extractMagicBlocks(doc);
|
|
115925
117626
|
const set = new Set();
|
|
115926
|
-
const processor = remark()
|
|
115927
|
-
|
|
117627
|
+
const processor = remark()
|
|
117628
|
+
.data('micromarkExtensions', [magicBlock()])
|
|
117629
|
+
.data('fromMarkdownExtensions', [magicBlockFromMarkdown()])
|
|
117630
|
+
.use(mdxish_component_blocks);
|
|
117631
|
+
const tree = processor.parse(doc);
|
|
115928
117632
|
visit(processor.runSync(tree), isMDXElement, (node) => {
|
|
115929
117633
|
if (node.name?.match(/^[A-Z]/)) {
|
|
115930
117634
|
set.add(node.name);
|
|
@@ -115945,12 +117649,15 @@ const mdxishTags_tags = (doc) => {
|
|
|
115945
117649
|
|
|
115946
117650
|
|
|
115947
117651
|
|
|
117652
|
+
|
|
115948
117653
|
/**
|
|
115949
117654
|
* Removes Markdown and MDX comments.
|
|
115950
117655
|
*/
|
|
115951
117656
|
async function stripComments(doc, { mdx, mdxish } = {}) {
|
|
115952
|
-
const
|
|
115953
|
-
|
|
117657
|
+
const processor = unified()
|
|
117658
|
+
.data('micromarkExtensions', [magicBlock()])
|
|
117659
|
+
.data('fromMarkdownExtensions', [magicBlockFromMarkdown()])
|
|
117660
|
+
.data('toMarkdownExtensions', [magicBlockToMarkdown()]);
|
|
115954
117661
|
// we still require these two extensions because:
|
|
115955
117662
|
// 1. we can rely on remarkMdx to parse MDXish
|
|
115956
117663
|
// 2. we need to parse JSX comments into mdxTextExpression nodes so that the transformers can pick them up
|
|
@@ -115992,10 +117699,8 @@ async function stripComments(doc, { mdx, mdxish } = {}) {
|
|
|
115992
117699
|
},
|
|
115993
117700
|
],
|
|
115994
117701
|
});
|
|
115995
|
-
const file = await processor.process(
|
|
115996
|
-
|
|
115997
|
-
const restored = restoreMagicBlocks(stringified, blocks);
|
|
115998
|
-
return restored;
|
|
117702
|
+
const file = await processor.process(doc);
|
|
117703
|
+
return String(file).trim();
|
|
115999
117704
|
}
|
|
116000
117705
|
/* harmony default export */ const lib_stripComments = (stripComments);
|
|
116001
117706
|
|