@readme/markdown 12.2.0 → 13.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/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 +1 -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 +1996 -575
- package/dist/main.node.js +1994 -573
- 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;
|
|
@@ -90721,7 +90725,7 @@ const toAttributes = (object, keys = []) => {
|
|
|
90721
90725
|
if (keys.length > 0 && !keys.includes(name))
|
|
90722
90726
|
return;
|
|
90723
90727
|
let value;
|
|
90724
|
-
if (typeof v === 'undefined' || v === null || v === '') {
|
|
90728
|
+
if (typeof v === 'undefined' || v === null || v === '' || v === false) {
|
|
90725
90729
|
return;
|
|
90726
90730
|
}
|
|
90727
90731
|
else if (typeof v === 'string') {
|
|
@@ -111869,6 +111873,10 @@ ${reformatHTML(html)}
|
|
|
111869
111873
|
const plain_plain = (node) => node.value;
|
|
111870
111874
|
/* harmony default export */ const compile_plain = (plain_plain);
|
|
111871
111875
|
|
|
111876
|
+
;// ./processor/compile/variable.ts
|
|
111877
|
+
const variable = (node) => `{user.${node.data?.hProperties?.name || ''}}`;
|
|
111878
|
+
/* harmony default export */ const compile_variable = (variable);
|
|
111879
|
+
|
|
111872
111880
|
;// ./processor/compile/index.ts
|
|
111873
111881
|
|
|
111874
111882
|
|
|
@@ -111878,7 +111886,8 @@ const plain_plain = (node) => node.value;
|
|
|
111878
111886
|
|
|
111879
111887
|
|
|
111880
111888
|
|
|
111881
|
-
|
|
111889
|
+
|
|
111890
|
+
function compilers(mdxish = false) {
|
|
111882
111891
|
const data = this.data();
|
|
111883
111892
|
const toMarkdownExtensions = data.toMarkdownExtensions || (data.toMarkdownExtensions = []);
|
|
111884
111893
|
const handlers = {
|
|
@@ -111889,6 +111898,7 @@ function compilers() {
|
|
|
111889
111898
|
[NodeTypes.glossary]: compile_compatibility,
|
|
111890
111899
|
[NodeTypes.htmlBlock]: html_block,
|
|
111891
111900
|
[NodeTypes.reusableContent]: compile_compatibility,
|
|
111901
|
+
...(mdxish && { [NodeTypes.variable]: compile_variable }),
|
|
111892
111902
|
embed: compile_compatibility,
|
|
111893
111903
|
escape: compile_compatibility,
|
|
111894
111904
|
figure: compile_compatibility,
|
|
@@ -111899,6 +111909,9 @@ function compilers() {
|
|
|
111899
111909
|
};
|
|
111900
111910
|
toMarkdownExtensions.push({ extensions: [{ handlers }] });
|
|
111901
111911
|
}
|
|
111912
|
+
function mdxishCompilers() {
|
|
111913
|
+
return compilers.call(this, true);
|
|
111914
|
+
}
|
|
111902
111915
|
/* harmony default export */ const processor_compile = (compilers);
|
|
111903
111916
|
|
|
111904
111917
|
;// ./processor/transform/escape-pipes-in-tables.ts
|
|
@@ -113609,13 +113622,17 @@ const htmlBlockHandler = (_state, node) => {
|
|
|
113609
113622
|
const embedHandler = (state, node) => {
|
|
113610
113623
|
// Assert to get the minimum properties we need
|
|
113611
113624
|
const { data } = node;
|
|
113625
|
+
// Magic block embeds (hName === 'embed-block') render as Embed component
|
|
113626
|
+
// which doesn't use children - it renders based on props only
|
|
113627
|
+
const isMagicBlockEmbed = data?.hName === NodeTypes.embedBlock;
|
|
113612
113628
|
return {
|
|
113613
113629
|
type: 'element',
|
|
113614
113630
|
// To differentiate between regular embeds and magic block embeds,
|
|
113615
113631
|
// magic block embeds have a certain hName
|
|
113616
|
-
tagName:
|
|
113632
|
+
tagName: isMagicBlockEmbed ? 'Embed' : 'embed',
|
|
113617
113633
|
properties: data?.hProperties,
|
|
113618
|
-
children
|
|
113634
|
+
// Don't include children for magic block embeds - Embed component renders based on props
|
|
113635
|
+
children: isMagicBlockEmbed ? [] : state.all(node),
|
|
113619
113636
|
};
|
|
113620
113637
|
};
|
|
113621
113638
|
const mdxComponentHandlers = {
|
|
@@ -113628,7 +113645,102 @@ const mdxComponentHandlers = {
|
|
|
113628
113645
|
[NodeTypes.htmlBlock]: htmlBlockHandler,
|
|
113629
113646
|
};
|
|
113630
113647
|
|
|
113648
|
+
;// ./lib/utils/mdxish/protect-code-blocks.ts
|
|
113649
|
+
/**
|
|
113650
|
+
* Replaces code blocks and inline code with placeholders to protect them from preprocessing.
|
|
113651
|
+
*
|
|
113652
|
+
* @param content - The markdown content to process
|
|
113653
|
+
* @returns Object containing protected content and arrays of original code blocks
|
|
113654
|
+
* @example
|
|
113655
|
+
* ```typescript
|
|
113656
|
+
* const input = 'Text with `inline code` and ```fenced block```';
|
|
113657
|
+
* protectCodeBlocks(input)
|
|
113658
|
+
* // Returns: {
|
|
113659
|
+
* // protectedCode: {
|
|
113660
|
+
* // codeBlocks: ['```fenced block```'],
|
|
113661
|
+
* // inlineCode: ['`inline code`']
|
|
113662
|
+
* // },
|
|
113663
|
+
* // protectedContent: 'Text with ___INLINE_CODE_0___ and ___CODE_BLOCK_0___'
|
|
113664
|
+
* // }
|
|
113665
|
+
* ```
|
|
113666
|
+
*/
|
|
113667
|
+
function protectCodeBlocks(content) {
|
|
113668
|
+
const codeBlocks = [];
|
|
113669
|
+
const inlineCode = [];
|
|
113670
|
+
let protectedContent = '';
|
|
113671
|
+
let remaining = content;
|
|
113672
|
+
let codeBlockStart = remaining.indexOf('```');
|
|
113673
|
+
while (codeBlockStart !== -1) {
|
|
113674
|
+
protectedContent += remaining.slice(0, codeBlockStart);
|
|
113675
|
+
remaining = remaining.slice(codeBlockStart);
|
|
113676
|
+
const codeBlockEnd = remaining.indexOf('```', 3);
|
|
113677
|
+
if (codeBlockEnd === -1) {
|
|
113678
|
+
break;
|
|
113679
|
+
}
|
|
113680
|
+
const match = remaining.slice(0, codeBlockEnd + 3);
|
|
113681
|
+
const index = codeBlocks.length;
|
|
113682
|
+
codeBlocks.push(match);
|
|
113683
|
+
protectedContent += `___CODE_BLOCK_${index}___`;
|
|
113684
|
+
remaining = remaining.slice(codeBlockEnd + 3);
|
|
113685
|
+
codeBlockStart = remaining.indexOf('```');
|
|
113686
|
+
}
|
|
113687
|
+
protectedContent += remaining;
|
|
113688
|
+
protectedContent = protectedContent.replace(/`([^`\n]+)`/g, match => {
|
|
113689
|
+
const index = inlineCode.length;
|
|
113690
|
+
inlineCode.push(match);
|
|
113691
|
+
return `___INLINE_CODE_${index}___`;
|
|
113692
|
+
});
|
|
113693
|
+
return { protectedCode: { codeBlocks, inlineCode }, protectedContent };
|
|
113694
|
+
}
|
|
113695
|
+
/**
|
|
113696
|
+
* Restores inline code by replacing placeholders with original content.
|
|
113697
|
+
*
|
|
113698
|
+
* @param content - Content with inline code placeholders
|
|
113699
|
+
* @param protectedCode - The protected code arrays
|
|
113700
|
+
* @returns Content with inline code restored
|
|
113701
|
+
*/
|
|
113702
|
+
function restoreInlineCode(content, protectedCode) {
|
|
113703
|
+
return content.replace(/___INLINE_CODE_(\d+)___/g, (_m, idx) => {
|
|
113704
|
+
return protectedCode.inlineCode[parseInt(idx, 10)];
|
|
113705
|
+
});
|
|
113706
|
+
}
|
|
113707
|
+
/**
|
|
113708
|
+
* Restores fenced code blocks by replacing placeholders with original content.
|
|
113709
|
+
*
|
|
113710
|
+
* @param content - Content with code block placeholders
|
|
113711
|
+
* @param protectedCode - The protected code arrays
|
|
113712
|
+
* @returns Content with code blocks restored
|
|
113713
|
+
*/
|
|
113714
|
+
function restoreFencedCodeBlocks(content, protectedCode) {
|
|
113715
|
+
return content.replace(/___CODE_BLOCK_(\d+)___/g, (_m, idx) => {
|
|
113716
|
+
return protectedCode.codeBlocks[parseInt(idx, 10)];
|
|
113717
|
+
});
|
|
113718
|
+
}
|
|
113719
|
+
/**
|
|
113720
|
+
* Restores all code blocks and inline code by replacing placeholders with original content.
|
|
113721
|
+
*
|
|
113722
|
+
* @param content - Content with code placeholders
|
|
113723
|
+
* @param protectedCode - The protected code arrays
|
|
113724
|
+
* @returns Content with all code blocks and inline code restored
|
|
113725
|
+
* @example
|
|
113726
|
+
* ```typescript
|
|
113727
|
+
* const content = 'Text with ___INLINE_CODE_0___ and ___CODE_BLOCK_0___';
|
|
113728
|
+
* const protectedCode = {
|
|
113729
|
+
* codeBlocks: ['```js\ncode\n```'],
|
|
113730
|
+
* inlineCode: ['`inline`']
|
|
113731
|
+
* };
|
|
113732
|
+
* restoreCodeBlocks(content, protectedCode)
|
|
113733
|
+
* // Returns: 'Text with `inline` and ```js\ncode\n```'
|
|
113734
|
+
* ```
|
|
113735
|
+
*/
|
|
113736
|
+
function restoreCodeBlocks(content, protectedCode) {
|
|
113737
|
+
let restored = restoreFencedCodeBlocks(content, protectedCode);
|
|
113738
|
+
restored = restoreInlineCode(restored, protectedCode);
|
|
113739
|
+
return restored;
|
|
113740
|
+
}
|
|
113741
|
+
|
|
113631
113742
|
;// ./processor/transform/mdxish/preprocess-jsx-expressions.ts
|
|
113743
|
+
|
|
113632
113744
|
// Base64 encode (Node.js + browser compatible)
|
|
113633
113745
|
function base64Encode(str) {
|
|
113634
113746
|
if (typeof Buffer !== 'undefined') {
|
|
@@ -113695,52 +113807,6 @@ function protectHTMLBlockContent(content) {
|
|
|
113695
113807
|
return `${openTag}${HTML_BLOCK_CONTENT_START}${encoded}${HTML_BLOCK_CONTENT_END}${closeTag}`;
|
|
113696
113808
|
});
|
|
113697
113809
|
}
|
|
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
113810
|
/**
|
|
113745
113811
|
* Removes JSX-style comments (e.g., { /* comment *\/ }) from content.
|
|
113746
113812
|
*
|
|
@@ -113783,16 +113849,6 @@ function extractBalancedBraces(content, start) {
|
|
|
113783
113849
|
return null;
|
|
113784
113850
|
return { content: content.slice(start, pos - 1), end: pos };
|
|
113785
113851
|
}
|
|
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
113852
|
/**
|
|
113797
113853
|
* Escapes unbalanced braces in content to prevent MDX expression parsing errors.
|
|
113798
113854
|
* Handles: already-escaped braces, string literals inside expressions, nested balanced braces.
|
|
@@ -113923,28 +113979,6 @@ function evaluateAttributeExpressions(content, context, protectedCode) {
|
|
|
113923
113979
|
result += content.slice(lastEnd);
|
|
113924
113980
|
return result;
|
|
113925
113981
|
}
|
|
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
113982
|
/**
|
|
113949
113983
|
* Preprocesses JSX-like expressions in markdown before parsing.
|
|
113950
113984
|
* Inline expressions are handled separately; attribute expressions are processed here.
|
|
@@ -113967,7 +114001,7 @@ function preprocessJSXExpressions(content, context = {}) {
|
|
|
113967
114001
|
// Step 4: Escape unbalanced braces to prevent MDX expression parsing errors
|
|
113968
114002
|
processed = escapeUnbalancedBraces(processed);
|
|
113969
114003
|
// Step 5: Restore protected code blocks
|
|
113970
|
-
processed =
|
|
114004
|
+
processed = restoreCodeBlocks(processed, protectedCode);
|
|
113971
114005
|
return processed;
|
|
113972
114006
|
}
|
|
113973
114007
|
|
|
@@ -114022,6 +114056,459 @@ const evaluateExpressions = ({ context = {} } = {}) => tree => {
|
|
|
114022
114056
|
};
|
|
114023
114057
|
/* harmony default export */ const evaluate_expressions = (evaluateExpressions);
|
|
114024
114058
|
|
|
114059
|
+
;// ./processor/transform/mdxish/magic-blocks/placeholder.ts
|
|
114060
|
+
const EMPTY_IMAGE_PLACEHOLDER = {
|
|
114061
|
+
type: 'image',
|
|
114062
|
+
url: '',
|
|
114063
|
+
alt: '',
|
|
114064
|
+
title: '',
|
|
114065
|
+
data: { hProperties: {} },
|
|
114066
|
+
};
|
|
114067
|
+
const EMPTY_EMBED_PLACEHOLDER = {
|
|
114068
|
+
type: 'embed',
|
|
114069
|
+
children: [{ type: 'link', url: '', title: '', children: [{ type: 'text', value: '' }] }],
|
|
114070
|
+
data: { hName: 'embed-block', hProperties: { url: '', href: '', title: '' } },
|
|
114071
|
+
};
|
|
114072
|
+
const EMPTY_RECIPE_PLACEHOLDER = {
|
|
114073
|
+
type: 'mdxJsxFlowElement',
|
|
114074
|
+
name: 'Recipe',
|
|
114075
|
+
attributes: [],
|
|
114076
|
+
children: [],
|
|
114077
|
+
};
|
|
114078
|
+
const EMPTY_CALLOUT_PLACEHOLDER = {
|
|
114079
|
+
type: 'mdxJsxFlowElement',
|
|
114080
|
+
name: 'Callout',
|
|
114081
|
+
attributes: [
|
|
114082
|
+
{ type: 'mdxJsxAttribute', name: 'icon', value: '📘' },
|
|
114083
|
+
{ type: 'mdxJsxAttribute', name: 'theme', value: 'info' },
|
|
114084
|
+
{ type: 'mdxJsxAttribute', name: 'type', value: 'info' },
|
|
114085
|
+
{ type: 'mdxJsxAttribute', name: 'empty', value: 'true' },
|
|
114086
|
+
],
|
|
114087
|
+
children: [{ type: 'heading', depth: 3, children: [{ type: 'text', value: '' }] }],
|
|
114088
|
+
};
|
|
114089
|
+
const EMPTY_TABLE_PLACEHOLDER = {
|
|
114090
|
+
type: 'table',
|
|
114091
|
+
align: ['left', 'left'],
|
|
114092
|
+
children: [
|
|
114093
|
+
{ type: 'tableRow', children: [{ type: 'tableCell', children: [{ type: 'text', value: '' }] }] },
|
|
114094
|
+
{ type: 'tableRow', children: [{ type: 'tableCell', children: [{ type: 'text', value: '' }] }] },
|
|
114095
|
+
],
|
|
114096
|
+
};
|
|
114097
|
+
const EMPTY_CODE_PLACEHOLDER = {
|
|
114098
|
+
type: 'code',
|
|
114099
|
+
value: '',
|
|
114100
|
+
lang: null,
|
|
114101
|
+
meta: null,
|
|
114102
|
+
};
|
|
114103
|
+
|
|
114104
|
+
;// ./processor/transform/mdxish/magic-blocks/magic-block-transformer.ts
|
|
114105
|
+
|
|
114106
|
+
|
|
114107
|
+
|
|
114108
|
+
|
|
114109
|
+
|
|
114110
|
+
|
|
114111
|
+
/**
|
|
114112
|
+
* Wraps a node in a "pinned" container if sidebar: true is set.
|
|
114113
|
+
*/
|
|
114114
|
+
const wrapPinnedBlocks = (node, data) => {
|
|
114115
|
+
if (!data.sidebar)
|
|
114116
|
+
return node;
|
|
114117
|
+
return {
|
|
114118
|
+
children: [node],
|
|
114119
|
+
data: { hName: 'rdme-pin', hProperties: { className: 'pin' } },
|
|
114120
|
+
type: 'rdme-pin',
|
|
114121
|
+
};
|
|
114122
|
+
};
|
|
114123
|
+
/**
|
|
114124
|
+
* Named size presets for image widths.
|
|
114125
|
+
*/
|
|
114126
|
+
const imgSizeValues = {
|
|
114127
|
+
full: '100%',
|
|
114128
|
+
original: 'auto',
|
|
114129
|
+
};
|
|
114130
|
+
/**
|
|
114131
|
+
* Proxy that resolves image sizing values.
|
|
114132
|
+
*/
|
|
114133
|
+
const imgWidthBySize = new Proxy(imgSizeValues, {
|
|
114134
|
+
get: (widths, size) => (size?.match(/^\d+$/) ? `${size}%` : size in widths ? widths[size] : size),
|
|
114135
|
+
});
|
|
114136
|
+
const textToInline = (text) => [{ type: 'text', value: text }];
|
|
114137
|
+
const textToBlock = (text) => [{ children: textToInline(text), type: 'paragraph' }];
|
|
114138
|
+
const contentParser = unified().use(remarkParse).use(remarkGfm);
|
|
114139
|
+
const parseTableCell = (text) => {
|
|
114140
|
+
if (!text.trim())
|
|
114141
|
+
return [{ type: 'text', value: '' }];
|
|
114142
|
+
const tree = contentParser.runSync(contentParser.parse(text));
|
|
114143
|
+
if (tree.children.length > 1) {
|
|
114144
|
+
return tree.children;
|
|
114145
|
+
}
|
|
114146
|
+
return tree.children.flatMap(n => n.type === 'paragraph' && 'children' in n ? n.children : [n]);
|
|
114147
|
+
};
|
|
114148
|
+
const parseBlock = (text) => {
|
|
114149
|
+
if (!text.trim())
|
|
114150
|
+
return [{ type: 'paragraph', children: [{ type: 'text', value: '' }] }];
|
|
114151
|
+
const tree = contentParser.runSync(contentParser.parse(text));
|
|
114152
|
+
return tree.children;
|
|
114153
|
+
};
|
|
114154
|
+
/**
|
|
114155
|
+
* Transform a magicBlock node into final MDAST nodes.
|
|
114156
|
+
*/
|
|
114157
|
+
function transformMagicBlock(blockType, data, rawValue, options = {}) {
|
|
114158
|
+
const { compatibilityMode = false, safeMode = false } = options;
|
|
114159
|
+
// Handle empty data by returning placeholder nodes for known block types
|
|
114160
|
+
// This allows the editor to show appropriate placeholder UI instead of nothing
|
|
114161
|
+
if (Object.keys(data).length < 1) {
|
|
114162
|
+
switch (blockType) {
|
|
114163
|
+
case 'image':
|
|
114164
|
+
return [EMPTY_IMAGE_PLACEHOLDER];
|
|
114165
|
+
case 'embed':
|
|
114166
|
+
return [EMPTY_EMBED_PLACEHOLDER];
|
|
114167
|
+
case 'code':
|
|
114168
|
+
return [EMPTY_CODE_PLACEHOLDER];
|
|
114169
|
+
case 'callout':
|
|
114170
|
+
return [EMPTY_CALLOUT_PLACEHOLDER];
|
|
114171
|
+
case 'parameters':
|
|
114172
|
+
case 'table':
|
|
114173
|
+
return [EMPTY_TABLE_PLACEHOLDER];
|
|
114174
|
+
case 'recipe':
|
|
114175
|
+
case 'tutorial-tile':
|
|
114176
|
+
return [EMPTY_RECIPE_PLACEHOLDER];
|
|
114177
|
+
default:
|
|
114178
|
+
return [{ type: 'paragraph', children: [{ type: 'text', value: rawValue }] }];
|
|
114179
|
+
}
|
|
114180
|
+
}
|
|
114181
|
+
switch (blockType) {
|
|
114182
|
+
case 'code': {
|
|
114183
|
+
const codeJson = data;
|
|
114184
|
+
if (!codeJson.codes || !Array.isArray(codeJson.codes)) {
|
|
114185
|
+
return [wrapPinnedBlocks(EMPTY_CODE_PLACEHOLDER, data)];
|
|
114186
|
+
}
|
|
114187
|
+
const children = codeJson.codes.map(obj => ({
|
|
114188
|
+
className: 'tab-panel',
|
|
114189
|
+
data: { hName: 'code', hProperties: { lang: obj.language, meta: obj.name || null } },
|
|
114190
|
+
lang: obj.language,
|
|
114191
|
+
meta: obj.name || null,
|
|
114192
|
+
type: 'code',
|
|
114193
|
+
value: obj.code.trim(),
|
|
114194
|
+
}));
|
|
114195
|
+
// Single code block without a tab name (meta or language) renders as a plain code block
|
|
114196
|
+
// Otherwise, we want to render it as a code tabs block
|
|
114197
|
+
if (children.length === 1) {
|
|
114198
|
+
if (!children[0].value)
|
|
114199
|
+
return [];
|
|
114200
|
+
if (!(children[0].meta || children[0].lang))
|
|
114201
|
+
return [wrapPinnedBlocks(children[0], data)];
|
|
114202
|
+
}
|
|
114203
|
+
// Multiple code blocks or a single code block with a tab name (meta or language) renders as a code tabs block
|
|
114204
|
+
return [wrapPinnedBlocks({ children, className: 'tabs', data: { hName: 'CodeTabs' }, type: 'code-tabs' }, data)];
|
|
114205
|
+
}
|
|
114206
|
+
case 'api-header': {
|
|
114207
|
+
const headerJson = data;
|
|
114208
|
+
const depth = headerJson.level || (compatibilityMode ? 1 : 2);
|
|
114209
|
+
return [
|
|
114210
|
+
wrapPinnedBlocks({
|
|
114211
|
+
children: 'title' in headerJson ? textToInline(headerJson.title || '') : [],
|
|
114212
|
+
depth,
|
|
114213
|
+
type: 'heading',
|
|
114214
|
+
}, data),
|
|
114215
|
+
];
|
|
114216
|
+
}
|
|
114217
|
+
case 'image': {
|
|
114218
|
+
const imageJson = data;
|
|
114219
|
+
if (!imageJson.images || !Array.isArray(imageJson.images)) {
|
|
114220
|
+
return [wrapPinnedBlocks(EMPTY_IMAGE_PLACEHOLDER, data)];
|
|
114221
|
+
}
|
|
114222
|
+
const imgData = imageJson.images.find(i => i.image);
|
|
114223
|
+
if (!imgData?.image) {
|
|
114224
|
+
return [wrapPinnedBlocks(EMPTY_IMAGE_PLACEHOLDER, data)];
|
|
114225
|
+
}
|
|
114226
|
+
const [url, title, alt] = imgData.image;
|
|
114227
|
+
const block = {
|
|
114228
|
+
alt: alt || imgData.caption || '',
|
|
114229
|
+
data: {
|
|
114230
|
+
hProperties: {
|
|
114231
|
+
...(imgData.align && { align: imgData.align }),
|
|
114232
|
+
...(imgData.border && { border: imgData.border.toString() }),
|
|
114233
|
+
...(imgData.sizing && { width: imgWidthBySize[imgData.sizing] }),
|
|
114234
|
+
},
|
|
114235
|
+
},
|
|
114236
|
+
title,
|
|
114237
|
+
type: 'image',
|
|
114238
|
+
url,
|
|
114239
|
+
};
|
|
114240
|
+
const img = imgData.caption
|
|
114241
|
+
? {
|
|
114242
|
+
children: [
|
|
114243
|
+
block,
|
|
114244
|
+
{ children: parseBlock(imgData.caption), data: { hName: 'figcaption' }, type: 'figcaption' },
|
|
114245
|
+
],
|
|
114246
|
+
data: { hName: 'figure' },
|
|
114247
|
+
type: 'figure',
|
|
114248
|
+
url,
|
|
114249
|
+
}
|
|
114250
|
+
: block;
|
|
114251
|
+
return [wrapPinnedBlocks(img, data)];
|
|
114252
|
+
}
|
|
114253
|
+
case 'callout': {
|
|
114254
|
+
const calloutJson = data;
|
|
114255
|
+
const types = {
|
|
114256
|
+
danger: ['❗️', 'error'],
|
|
114257
|
+
info: ['📘', 'info'],
|
|
114258
|
+
success: ['👍', 'okay'],
|
|
114259
|
+
warning: ['🚧', 'warn'],
|
|
114260
|
+
};
|
|
114261
|
+
const resolvedType = typeof calloutJson.type === 'string' && calloutJson.type in types
|
|
114262
|
+
? types[calloutJson.type]
|
|
114263
|
+
: [calloutJson.icon || '👍', typeof calloutJson.type === 'string' ? calloutJson.type : 'default'];
|
|
114264
|
+
const [icon, theme] = Array.isArray(resolvedType) ? resolvedType : ['👍', 'default'];
|
|
114265
|
+
if (!(calloutJson.title || calloutJson.body))
|
|
114266
|
+
return [];
|
|
114267
|
+
const hasTitle = !!calloutJson.title?.trim();
|
|
114268
|
+
const hasBody = !!calloutJson.body?.trim();
|
|
114269
|
+
const empty = !hasTitle;
|
|
114270
|
+
const children = [];
|
|
114271
|
+
if (hasTitle) {
|
|
114272
|
+
const titleBlocks = parseBlock(calloutJson.title || '');
|
|
114273
|
+
if (titleBlocks.length > 0 && titleBlocks[0].type === 'paragraph') {
|
|
114274
|
+
const firstTitle = titleBlocks[0];
|
|
114275
|
+
const heading = {
|
|
114276
|
+
type: 'heading',
|
|
114277
|
+
depth: 3,
|
|
114278
|
+
children: (firstTitle.children || []),
|
|
114279
|
+
};
|
|
114280
|
+
children.push(heading);
|
|
114281
|
+
children.push(...titleBlocks.slice(1));
|
|
114282
|
+
}
|
|
114283
|
+
else {
|
|
114284
|
+
children.push(...titleBlocks);
|
|
114285
|
+
}
|
|
114286
|
+
}
|
|
114287
|
+
else {
|
|
114288
|
+
// Add empty heading placeholder so body goes to children.slice(1)
|
|
114289
|
+
// The Callout component expects children[0] to be the heading
|
|
114290
|
+
children.push({
|
|
114291
|
+
type: 'heading',
|
|
114292
|
+
depth: 3,
|
|
114293
|
+
children: [{ type: 'text', value: '' }],
|
|
114294
|
+
});
|
|
114295
|
+
}
|
|
114296
|
+
if (hasBody) {
|
|
114297
|
+
const bodyBlocks = parseBlock(calloutJson.body || '');
|
|
114298
|
+
children.push(...bodyBlocks);
|
|
114299
|
+
}
|
|
114300
|
+
const calloutElement = {
|
|
114301
|
+
type: 'mdxJsxFlowElement',
|
|
114302
|
+
name: 'Callout',
|
|
114303
|
+
attributes: toAttributes({ icon, theme: theme || 'default', type: theme || 'default', empty }, [
|
|
114304
|
+
'icon',
|
|
114305
|
+
'theme',
|
|
114306
|
+
'type',
|
|
114307
|
+
'empty',
|
|
114308
|
+
]),
|
|
114309
|
+
children: children,
|
|
114310
|
+
};
|
|
114311
|
+
return [wrapPinnedBlocks(calloutElement, data)];
|
|
114312
|
+
}
|
|
114313
|
+
case 'parameters': {
|
|
114314
|
+
const paramsJson = data;
|
|
114315
|
+
const { cols, data: tableData, rows } = paramsJson;
|
|
114316
|
+
if (!tableData || !Object.keys(tableData).length)
|
|
114317
|
+
return [];
|
|
114318
|
+
if (typeof cols !== 'number' || typeof rows !== 'number' || cols < 1 || rows < 0)
|
|
114319
|
+
return [];
|
|
114320
|
+
const sparseData = Object.entries(tableData).reduce((mapped, [key, v]) => {
|
|
114321
|
+
const [row, col] = key.split('-');
|
|
114322
|
+
const rowIndex = row === 'h' ? 0 : parseInt(row, 10) + 1;
|
|
114323
|
+
const colIndex = parseInt(col, 10);
|
|
114324
|
+
if (!mapped[rowIndex])
|
|
114325
|
+
mapped[rowIndex] = [];
|
|
114326
|
+
mapped[rowIndex][colIndex] = v;
|
|
114327
|
+
return mapped;
|
|
114328
|
+
}, []);
|
|
114329
|
+
const tokenizeCell = compatibilityMode ? textToBlock : parseTableCell;
|
|
114330
|
+
const tableChildren = Array.from({ length: rows + 1 }, (_, y) => ({
|
|
114331
|
+
children: Array.from({ length: cols }, (__, x) => ({
|
|
114332
|
+
children: sparseData[y]?.[x] ? tokenizeCell(sparseData[y][x]) : [{ type: 'text', value: '' }],
|
|
114333
|
+
type: y === 0 ? 'tableHead' : 'tableCell',
|
|
114334
|
+
})),
|
|
114335
|
+
type: 'tableRow',
|
|
114336
|
+
}));
|
|
114337
|
+
return [
|
|
114338
|
+
wrapPinnedBlocks({ align: paramsJson.align ?? new Array(cols).fill('left'), children: tableChildren, type: 'table' }, data),
|
|
114339
|
+
];
|
|
114340
|
+
}
|
|
114341
|
+
case 'embed': {
|
|
114342
|
+
const embedJson = data;
|
|
114343
|
+
if (!embedJson.url) {
|
|
114344
|
+
return [wrapPinnedBlocks(EMPTY_EMBED_PLACEHOLDER, data)];
|
|
114345
|
+
}
|
|
114346
|
+
const { html, title, url } = embedJson;
|
|
114347
|
+
try {
|
|
114348
|
+
embedJson.provider = new URL(url).hostname
|
|
114349
|
+
.split(/(?:www)?\./)
|
|
114350
|
+
.filter(i => i)
|
|
114351
|
+
.join('.');
|
|
114352
|
+
}
|
|
114353
|
+
catch {
|
|
114354
|
+
embedJson.provider = url;
|
|
114355
|
+
}
|
|
114356
|
+
return [
|
|
114357
|
+
wrapPinnedBlocks({
|
|
114358
|
+
children: [
|
|
114359
|
+
{ children: [{ type: 'text', value: title || '' }], title: embedJson.provider, type: 'link', url },
|
|
114360
|
+
],
|
|
114361
|
+
data: { hName: 'embed-block', hProperties: { ...embedJson, href: url, html, title, url } },
|
|
114362
|
+
type: 'embed',
|
|
114363
|
+
}, data),
|
|
114364
|
+
];
|
|
114365
|
+
}
|
|
114366
|
+
case 'html': {
|
|
114367
|
+
const htmlJson = data;
|
|
114368
|
+
if (typeof htmlJson.html !== 'string')
|
|
114369
|
+
return [];
|
|
114370
|
+
return [
|
|
114371
|
+
wrapPinnedBlocks({
|
|
114372
|
+
data: {
|
|
114373
|
+
hName: 'html-block',
|
|
114374
|
+
hProperties: { html: htmlJson.html, runScripts: compatibilityMode, safeMode },
|
|
114375
|
+
},
|
|
114376
|
+
type: 'html-block',
|
|
114377
|
+
}, data),
|
|
114378
|
+
];
|
|
114379
|
+
}
|
|
114380
|
+
case 'recipe':
|
|
114381
|
+
case 'tutorial-tile': {
|
|
114382
|
+
const recipeJson = data;
|
|
114383
|
+
if (!recipeJson.slug || !recipeJson.title)
|
|
114384
|
+
return [];
|
|
114385
|
+
const recipeNode = {
|
|
114386
|
+
type: 'mdxJsxFlowElement',
|
|
114387
|
+
name: 'Recipe',
|
|
114388
|
+
attributes: toAttributes(recipeJson, ['slug', 'title']),
|
|
114389
|
+
children: [],
|
|
114390
|
+
position: undefined,
|
|
114391
|
+
};
|
|
114392
|
+
return [recipeNode];
|
|
114393
|
+
}
|
|
114394
|
+
default: {
|
|
114395
|
+
const text = data.text || data.html || '';
|
|
114396
|
+
return [
|
|
114397
|
+
wrapPinnedBlocks({ children: textToBlock(text), data: { hName: blockType || 'div', hProperties: data, ...data }, type: 'div' }, data),
|
|
114398
|
+
];
|
|
114399
|
+
}
|
|
114400
|
+
}
|
|
114401
|
+
}
|
|
114402
|
+
/**
|
|
114403
|
+
* Check if a child node is a flow element that needs unwrapping.
|
|
114404
|
+
*/
|
|
114405
|
+
const blockTypes = [
|
|
114406
|
+
'heading',
|
|
114407
|
+
'code',
|
|
114408
|
+
'code-tabs',
|
|
114409
|
+
'paragraph',
|
|
114410
|
+
'blockquote',
|
|
114411
|
+
'list',
|
|
114412
|
+
'table',
|
|
114413
|
+
'thematicBreak',
|
|
114414
|
+
'html',
|
|
114415
|
+
'yaml',
|
|
114416
|
+
'toml',
|
|
114417
|
+
'rdme-pin',
|
|
114418
|
+
'rdme-callout',
|
|
114419
|
+
'html-block',
|
|
114420
|
+
'embed',
|
|
114421
|
+
'figure',
|
|
114422
|
+
'mdxJsxFlowElement',
|
|
114423
|
+
];
|
|
114424
|
+
/**
|
|
114425
|
+
* Check if a node is a block-level node (cannot be inside a paragraph)
|
|
114426
|
+
*/
|
|
114427
|
+
const isBlockNode = (node) => blockTypes.includes(node.type);
|
|
114428
|
+
/**
|
|
114429
|
+
* Unified plugin that transforms magicBlock nodes into final MDAST nodes.
|
|
114430
|
+
*/
|
|
114431
|
+
const magicBlockTransformer = (options = {}) => tree => {
|
|
114432
|
+
const replacements = [];
|
|
114433
|
+
visit(tree, 'magicBlock', (node, index, parent) => {
|
|
114434
|
+
if (!parent || index === undefined)
|
|
114435
|
+
return undefined;
|
|
114436
|
+
const children = transformMagicBlock(node.blockType, node.data, node.value, options);
|
|
114437
|
+
if (!children.length) {
|
|
114438
|
+
// Remove the node if transformation returns nothing
|
|
114439
|
+
parent.children.splice(index, 1);
|
|
114440
|
+
return [SKIP, index];
|
|
114441
|
+
}
|
|
114442
|
+
// If parent is a paragraph and we're inserting block nodes (which must not be in paragraphs), lift them out
|
|
114443
|
+
if (parent.type === 'paragraph' && children.some(child => isBlockNode(child))) {
|
|
114444
|
+
const blockNodes = [];
|
|
114445
|
+
const inlineNodes = [];
|
|
114446
|
+
// Separate block and inline nodes
|
|
114447
|
+
children.forEach(child => {
|
|
114448
|
+
if (isBlockNode(child)) {
|
|
114449
|
+
blockNodes.push(child);
|
|
114450
|
+
}
|
|
114451
|
+
else {
|
|
114452
|
+
inlineNodes.push(child);
|
|
114453
|
+
}
|
|
114454
|
+
});
|
|
114455
|
+
const before = parent.children.slice(0, index);
|
|
114456
|
+
const after = parent.children.slice(index + 1);
|
|
114457
|
+
replacements.push({
|
|
114458
|
+
parent,
|
|
114459
|
+
blockNodes,
|
|
114460
|
+
inlineNodes,
|
|
114461
|
+
before,
|
|
114462
|
+
after,
|
|
114463
|
+
});
|
|
114464
|
+
}
|
|
114465
|
+
else {
|
|
114466
|
+
// Normal case: just replace the inlineCode with the children
|
|
114467
|
+
parent.children.splice(index, 1, ...children);
|
|
114468
|
+
}
|
|
114469
|
+
return undefined;
|
|
114470
|
+
});
|
|
114471
|
+
// Second pass: apply replacements that require lifting block nodes out of paragraphs
|
|
114472
|
+
// Process in reverse order to maintain correct indices
|
|
114473
|
+
for (let i = replacements.length - 1; i >= 0; i -= 1) {
|
|
114474
|
+
const { after, before, blockNodes, inlineNodes, parent } = replacements[i];
|
|
114475
|
+
// Find the paragraph's position in the root
|
|
114476
|
+
const rootChildren = tree.children;
|
|
114477
|
+
const paraIndex = rootChildren.findIndex(child => child === parent);
|
|
114478
|
+
if (paraIndex === -1) {
|
|
114479
|
+
// Paragraph not found in root - fall back to normal replacement
|
|
114480
|
+
// This shouldn't happen normally, but handle it gracefully
|
|
114481
|
+
// Reconstruct the original index from before.length
|
|
114482
|
+
const originalIndex = before.length;
|
|
114483
|
+
parent.children.splice(originalIndex, 1, ...blockNodes, ...inlineNodes);
|
|
114484
|
+
// eslint-disable-next-line no-continue
|
|
114485
|
+
continue;
|
|
114486
|
+
}
|
|
114487
|
+
// Update or remove the paragraph
|
|
114488
|
+
if (inlineNodes.length > 0) {
|
|
114489
|
+
// Keep paragraph with inline nodes
|
|
114490
|
+
parent.children = [...before, ...inlineNodes, ...after];
|
|
114491
|
+
// Insert block nodes after the paragraph
|
|
114492
|
+
if (blockNodes.length > 0) {
|
|
114493
|
+
rootChildren.splice(paraIndex + 1, 0, ...blockNodes);
|
|
114494
|
+
}
|
|
114495
|
+
}
|
|
114496
|
+
else if (before.length === 0 && after.length === 0) {
|
|
114497
|
+
// Remove empty paragraph and replace with block nodes
|
|
114498
|
+
rootChildren.splice(paraIndex, 1, ...blockNodes);
|
|
114499
|
+
}
|
|
114500
|
+
else {
|
|
114501
|
+
// Keep paragraph with remaining content
|
|
114502
|
+
parent.children = [...before, ...after];
|
|
114503
|
+
// Insert block nodes after the paragraph
|
|
114504
|
+
if (blockNodes.length > 0) {
|
|
114505
|
+
rootChildren.splice(paraIndex + 1, 0, ...blockNodes);
|
|
114506
|
+
}
|
|
114507
|
+
}
|
|
114508
|
+
}
|
|
114509
|
+
};
|
|
114510
|
+
/* harmony default export */ const magic_block_transformer = (magicBlockTransformer);
|
|
114511
|
+
|
|
114025
114512
|
;// ./processor/transform/mdxish/mdxish-html-blocks.ts
|
|
114026
114513
|
|
|
114027
114514
|
|
|
@@ -114357,444 +114844,220 @@ const mdxishHtmlBlocks = () => tree => {
|
|
|
114357
114844
|
};
|
|
114358
114845
|
/* harmony default export */ const mdxish_html_blocks = (mdxishHtmlBlocks);
|
|
114359
114846
|
|
|
114360
|
-
;// ./processor/transform/mdxish/mdxish-
|
|
114847
|
+
;// ./processor/transform/mdxish/mdxish-jsx-to-mdast.ts
|
|
114361
114848
|
|
|
114362
114849
|
|
|
114363
114850
|
|
|
114364
|
-
|
|
114365
|
-
|
|
114366
|
-
|
|
114367
|
-
|
|
114368
|
-
|
|
114369
|
-
|
|
114370
|
-
|
|
114371
|
-
|
|
114372
|
-
|
|
114373
|
-
|
|
114374
|
-
|
|
114375
|
-
|
|
114376
|
-
|
|
114377
|
-
|
|
114378
|
-
|
|
114379
|
-
return node;
|
|
114851
|
+
const transformImage = (jsx) => {
|
|
114852
|
+
const attrs = getAttrs(jsx);
|
|
114853
|
+
const { align, alt = '', border, caption, className, height, lazy, src = '', title = '', width } = attrs;
|
|
114854
|
+
const hProperties = {
|
|
114855
|
+
alt,
|
|
114856
|
+
src,
|
|
114857
|
+
title,
|
|
114858
|
+
...(align && { align }),
|
|
114859
|
+
...(border !== undefined && { border: String(border) }),
|
|
114860
|
+
...(caption && { caption }),
|
|
114861
|
+
...(className && { className }),
|
|
114862
|
+
...(height !== undefined && { height: String(height) }),
|
|
114863
|
+
...(lazy !== undefined && { lazy }),
|
|
114864
|
+
...(width !== undefined && { width: String(width) }),
|
|
114865
|
+
};
|
|
114380
114866
|
return {
|
|
114381
|
-
|
|
114382
|
-
|
|
114383
|
-
|
|
114867
|
+
type: NodeTypes.imageBlock,
|
|
114868
|
+
align,
|
|
114869
|
+
alt,
|
|
114870
|
+
border: border !== undefined ? String(border) : undefined,
|
|
114871
|
+
caption,
|
|
114872
|
+
className,
|
|
114873
|
+
height: height !== undefined ? String(height) : undefined,
|
|
114874
|
+
lazy,
|
|
114875
|
+
src,
|
|
114876
|
+
title,
|
|
114877
|
+
width: width !== undefined ? String(width) : undefined,
|
|
114878
|
+
data: {
|
|
114879
|
+
hName: 'img',
|
|
114880
|
+
hProperties,
|
|
114881
|
+
},
|
|
114882
|
+
position: jsx.position,
|
|
114384
114883
|
};
|
|
114385
114884
|
};
|
|
114386
|
-
|
|
114387
|
-
|
|
114388
|
-
|
|
114389
|
-
|
|
114390
|
-
|
|
114391
|
-
|
|
114885
|
+
const transformCallout = (jsx) => {
|
|
114886
|
+
const attrs = getAttrs(jsx);
|
|
114887
|
+
const { empty = false, icon = '', theme = '' } = attrs;
|
|
114888
|
+
return {
|
|
114889
|
+
type: NodeTypes.callout,
|
|
114890
|
+
children: jsx.children,
|
|
114891
|
+
data: {
|
|
114892
|
+
hName: 'Callout',
|
|
114893
|
+
hProperties: {
|
|
114894
|
+
empty,
|
|
114895
|
+
icon,
|
|
114896
|
+
theme,
|
|
114897
|
+
},
|
|
114898
|
+
},
|
|
114899
|
+
position: jsx.position,
|
|
114900
|
+
};
|
|
114901
|
+
};
|
|
114902
|
+
const transformEmbed = (jsx) => {
|
|
114903
|
+
const attrs = getAttrs(jsx);
|
|
114904
|
+
const { favicon, html, iframe, image, providerName, providerUrl, title = '', url = '' } = attrs;
|
|
114905
|
+
return {
|
|
114906
|
+
type: NodeTypes.embedBlock,
|
|
114907
|
+
title,
|
|
114908
|
+
url,
|
|
114909
|
+
data: {
|
|
114910
|
+
hName: 'embed',
|
|
114911
|
+
hProperties: {
|
|
114912
|
+
title,
|
|
114913
|
+
url,
|
|
114914
|
+
...(favicon && { favicon }),
|
|
114915
|
+
...(html && { html }),
|
|
114916
|
+
...(iframe !== undefined && { iframe }),
|
|
114917
|
+
...(image && { image }),
|
|
114918
|
+
...(providerName && { providerName }),
|
|
114919
|
+
...(providerUrl && { providerUrl }),
|
|
114920
|
+
},
|
|
114921
|
+
},
|
|
114922
|
+
position: jsx.position,
|
|
114923
|
+
};
|
|
114924
|
+
};
|
|
114925
|
+
const transformRecipe = (jsx) => {
|
|
114926
|
+
const attrs = getAttrs(jsx);
|
|
114927
|
+
const { backgroundColor = '', emoji = '', id = '', link = '', slug = '', title = '' } = attrs;
|
|
114928
|
+
return {
|
|
114929
|
+
type: NodeTypes.recipe,
|
|
114930
|
+
backgroundColor,
|
|
114931
|
+
emoji,
|
|
114932
|
+
id,
|
|
114933
|
+
link,
|
|
114934
|
+
slug,
|
|
114935
|
+
title,
|
|
114936
|
+
position: jsx.position,
|
|
114937
|
+
};
|
|
114392
114938
|
};
|
|
114393
114939
|
/**
|
|
114394
|
-
*
|
|
114395
|
-
*
|
|
114396
|
-
* - Pure numbers like "50" → "50%" (percentage)
|
|
114397
|
-
* - Anything else passes through as-is (e.g., "200px")
|
|
114940
|
+
* Transform a magic block image node into an ImageBlock.
|
|
114941
|
+
* Magic block images have structure: { type: 'image', url, title, alt, data.hProperties }
|
|
114398
114942
|
*/
|
|
114399
|
-
const
|
|
114400
|
-
|
|
114401
|
-
}
|
|
114402
|
-
|
|
114403
|
-
const
|
|
114404
|
-
|
|
114405
|
-
|
|
114406
|
-
|
|
114407
|
-
|
|
114408
|
-
|
|
114409
|
-
|
|
114410
|
-
|
|
114411
|
-
|
|
114412
|
-
|
|
114413
|
-
|
|
114414
|
-
|
|
114415
|
-
|
|
114416
|
-
|
|
114417
|
-
|
|
114418
|
-
|
|
114419
|
-
|
|
114420
|
-
|
|
114421
|
-
|
|
114422
|
-
|
|
114423
|
-
|
|
114424
|
-
|
|
114425
|
-
const tree = contentParser.runSync(contentParser.parse(text));
|
|
114426
|
-
return tree.children;
|
|
114943
|
+
const transformMagicBlockImage = (node) => {
|
|
114944
|
+
const { alt = '', data, position, title = '', url = '' } = node;
|
|
114945
|
+
const hProps = data?.hProperties || {};
|
|
114946
|
+
const { align, border, width } = hProps;
|
|
114947
|
+
const hProperties = {
|
|
114948
|
+
alt,
|
|
114949
|
+
src: url,
|
|
114950
|
+
title,
|
|
114951
|
+
...(align && { align }),
|
|
114952
|
+
...(border && { border }),
|
|
114953
|
+
...(width && { width }),
|
|
114954
|
+
};
|
|
114955
|
+
return {
|
|
114956
|
+
type: NodeTypes.imageBlock,
|
|
114957
|
+
align,
|
|
114958
|
+
alt,
|
|
114959
|
+
border,
|
|
114960
|
+
src: url,
|
|
114961
|
+
title,
|
|
114962
|
+
width,
|
|
114963
|
+
data: {
|
|
114964
|
+
hName: 'img',
|
|
114965
|
+
hProperties,
|
|
114966
|
+
},
|
|
114967
|
+
position,
|
|
114968
|
+
};
|
|
114427
114969
|
};
|
|
114428
114970
|
/**
|
|
114429
|
-
*
|
|
114430
|
-
*
|
|
114431
|
-
* @param raw - The raw magic block string including [block:TYPE] and [/block] tags
|
|
114432
|
-
* @param options - Parsing options for compatibility and error handling
|
|
114433
|
-
* @returns Array of MDAST nodes representing the parsed block
|
|
114971
|
+
* Transform a magic block embed node into an EmbedBlock.
|
|
114972
|
+
* Magic block embeds have structure: { type: 'embed', children, data.hProperties }
|
|
114434
114973
|
*/
|
|
114435
|
-
|
|
114436
|
-
const {
|
|
114437
|
-
const
|
|
114438
|
-
|
|
114439
|
-
|
|
114440
|
-
|
|
114441
|
-
|
|
114442
|
-
|
|
114443
|
-
|
|
114444
|
-
|
|
114445
|
-
|
|
114446
|
-
json = JSON.parse(jsonStr);
|
|
114447
|
-
}
|
|
114448
|
-
catch (err) {
|
|
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)
|
|
114460
|
-
case 'code': {
|
|
114461
|
-
const codeJson = json;
|
|
114462
|
-
const children = codeJson.codes.map(obj => ({
|
|
114463
|
-
className: 'tab-panel',
|
|
114464
|
-
data: { hName: 'code', hProperties: { lang: obj.language, meta: obj.name || null } },
|
|
114465
|
-
lang: obj.language,
|
|
114466
|
-
meta: obj.name || null, // Tab name shown in the UI
|
|
114467
|
-
type: 'code',
|
|
114468
|
-
value: obj.code.trim(),
|
|
114469
|
-
}));
|
|
114470
|
-
// Single code block without a tab name (meta or language) renders as a plain code block
|
|
114471
|
-
// Otherwise, we want to render it as a code tabs block
|
|
114472
|
-
if (children.length === 1) {
|
|
114473
|
-
if (!children[0].value)
|
|
114474
|
-
return [];
|
|
114475
|
-
if (!(children[0].meta || children[0].lang))
|
|
114476
|
-
return [wrapPinnedBlocks(children[0], json)];
|
|
114477
|
-
}
|
|
114478
|
-
// 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' }, json)];
|
|
114480
|
-
}
|
|
114481
|
-
// API header: renders as a heading element (h1-h6)
|
|
114482
|
-
case 'api-header': {
|
|
114483
|
-
const headerJson = json;
|
|
114484
|
-
// In compatibility mode, default to h1; otherwise h2
|
|
114485
|
-
const depth = headerJson.level || (compatibilityMode ? 1 : 2);
|
|
114486
|
-
return [
|
|
114487
|
-
wrapPinnedBlocks({
|
|
114488
|
-
children: 'title' in headerJson ? textToInline(headerJson.title || '') : [],
|
|
114489
|
-
depth,
|
|
114490
|
-
type: 'heading',
|
|
114491
|
-
}, json),
|
|
114492
|
-
];
|
|
114493
|
-
}
|
|
114494
|
-
// Image block: renders as <img> or <figure> with caption
|
|
114495
|
-
case 'image': {
|
|
114496
|
-
const imageJson = json;
|
|
114497
|
-
const imgData = imageJson.images.find(i => i.image);
|
|
114498
|
-
if (!imgData?.image)
|
|
114499
|
-
return [];
|
|
114500
|
-
// Image array format: [url, title?, alt?]
|
|
114501
|
-
const [url, title, alt] = imgData.image;
|
|
114502
|
-
const block = {
|
|
114503
|
-
alt: alt || imgData.caption || '',
|
|
114504
|
-
data: {
|
|
114505
|
-
hProperties: {
|
|
114506
|
-
...(imgData.align && { align: imgData.align }),
|
|
114507
|
-
...(imgData.border && { border: imgData.border.toString() }),
|
|
114508
|
-
...(imgData.sizing && { width: imgWidthBySize[imgData.sizing] }),
|
|
114509
|
-
},
|
|
114510
|
-
},
|
|
114974
|
+
const transformMagicBlockEmbed = (node) => {
|
|
114975
|
+
const { data, position } = node;
|
|
114976
|
+
const hProps = data?.hProperties || {};
|
|
114977
|
+
const { favicon, html, image, providerName, providerUrl, title = '', url = '' } = hProps;
|
|
114978
|
+
return {
|
|
114979
|
+
type: NodeTypes.embedBlock,
|
|
114980
|
+
title,
|
|
114981
|
+
url,
|
|
114982
|
+
data: {
|
|
114983
|
+
hName: 'embed',
|
|
114984
|
+
hProperties: {
|
|
114511
114985
|
title,
|
|
114512
|
-
type: 'image',
|
|
114513
114986
|
url,
|
|
114514
|
-
|
|
114515
|
-
|
|
114516
|
-
|
|
114517
|
-
|
|
114518
|
-
|
|
114519
|
-
|
|
114520
|
-
|
|
114521
|
-
|
|
114522
|
-
|
|
114523
|
-
|
|
114524
|
-
|
|
114525
|
-
|
|
114526
|
-
|
|
114527
|
-
|
|
114528
|
-
|
|
114529
|
-
|
|
114530
|
-
case 'callout': {
|
|
114531
|
-
const calloutJson = json;
|
|
114532
|
-
// Preset callout types map to [icon, theme] tuples
|
|
114533
|
-
const types = {
|
|
114534
|
-
danger: ['❗️', 'error'],
|
|
114535
|
-
info: ['📘', 'info'],
|
|
114536
|
-
success: ['👍', 'okay'],
|
|
114537
|
-
warning: ['🚧', 'warn'],
|
|
114538
|
-
};
|
|
114539
|
-
// Resolve type to [icon, theme] - use preset if available, otherwise custom
|
|
114540
|
-
const resolvedType = typeof calloutJson.type === 'string' && calloutJson.type in types
|
|
114541
|
-
? types[calloutJson.type]
|
|
114542
|
-
: [calloutJson.icon || '👍', typeof calloutJson.type === 'string' ? calloutJson.type : 'default'];
|
|
114543
|
-
const [icon, theme] = Array.isArray(resolvedType) ? resolvedType : ['👍', 'default'];
|
|
114544
|
-
if (!(calloutJson.title || calloutJson.body))
|
|
114545
|
-
return [];
|
|
114546
|
-
// Parses html & markdown content
|
|
114547
|
-
const titleBlocks = parseBlock(calloutJson.title || '');
|
|
114548
|
-
const bodyBlocks = parseBlock(calloutJson.body || '');
|
|
114549
|
-
const children = [];
|
|
114550
|
-
if (titleBlocks.length > 0 && titleBlocks[0].type === 'paragraph') {
|
|
114551
|
-
const firstTitle = titleBlocks[0];
|
|
114552
|
-
const heading = {
|
|
114553
|
-
type: 'heading',
|
|
114554
|
-
depth: 3,
|
|
114555
|
-
children: (firstTitle.children || []),
|
|
114556
|
-
};
|
|
114557
|
-
children.push(heading);
|
|
114558
|
-
children.push(...titleBlocks.slice(1), ...bodyBlocks);
|
|
114559
|
-
}
|
|
114560
|
-
else {
|
|
114561
|
-
children.push(...titleBlocks, ...bodyBlocks);
|
|
114562
|
-
}
|
|
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
|
-
const calloutElement = {
|
|
114567
|
-
type: 'mdxJsxFlowElement',
|
|
114568
|
-
name: 'Callout',
|
|
114569
|
-
attributes: toAttributes({ icon, theme: theme || 'default', type: theme || 'default', empty }, [
|
|
114570
|
-
'icon',
|
|
114571
|
-
'theme',
|
|
114572
|
-
'type',
|
|
114573
|
-
'empty',
|
|
114574
|
-
]),
|
|
114575
|
-
children: children,
|
|
114576
|
-
};
|
|
114577
|
-
return [wrapPinnedBlocks(calloutElement, json)];
|
|
114578
|
-
}
|
|
114579
|
-
// Parameters: renders as a table (used for API parameters, etc.)
|
|
114580
|
-
case 'parameters': {
|
|
114581
|
-
const paramsJson = json;
|
|
114582
|
-
const { cols, data, rows } = paramsJson;
|
|
114583
|
-
if (!Object.keys(data).length)
|
|
114584
|
-
return [];
|
|
114585
|
-
/**
|
|
114586
|
-
* Convert sparse key-value data to 2D array.
|
|
114587
|
-
* Keys are formatted as "ROW-COL" where ROW is "h" for header or a number.
|
|
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]) => {
|
|
114592
|
-
const [row, col] = key.split('-');
|
|
114593
|
-
// Header row ("h") becomes index 0, data rows are offset by 1
|
|
114594
|
-
const rowIndex = row === 'h' ? 0 : parseInt(row, 10) + 1;
|
|
114595
|
-
const colIndex = parseInt(col, 10);
|
|
114596
|
-
if (!mapped[rowIndex])
|
|
114597
|
-
mapped[rowIndex] = [];
|
|
114598
|
-
mapped[rowIndex][colIndex] = v;
|
|
114599
|
-
return mapped;
|
|
114600
|
-
}, []);
|
|
114601
|
-
// In compatibility mode, wrap cell content in paragraphs; otherwise inline text
|
|
114602
|
-
const tokenizeCell = compatibilityMode ? textToBlock : parseTableCell;
|
|
114603
|
-
const children = Array.from({ length: rows + 1 }, (_, y) => ({
|
|
114604
|
-
children: Array.from({ length: cols }, (__, x) => ({
|
|
114605
|
-
children: sparseData[y]?.[x] ? tokenizeCell(sparseData[y][x]) : [{ type: 'text', value: '' }],
|
|
114606
|
-
type: y === 0 ? 'tableHead' : 'tableCell',
|
|
114607
|
-
})),
|
|
114608
|
-
type: 'tableRow',
|
|
114609
|
-
}));
|
|
114610
|
-
return [
|
|
114611
|
-
wrapPinnedBlocks({ align: paramsJson.align ?? new Array(cols).fill('left'), children, type: 'table' }, json),
|
|
114612
|
-
];
|
|
114613
|
-
}
|
|
114614
|
-
// Embed: external content (YouTube, etc.) with provider detection
|
|
114615
|
-
case 'embed': {
|
|
114616
|
-
const embedJson = json;
|
|
114617
|
-
const { html, title, url } = embedJson;
|
|
114618
|
-
// Extract provider name from URL hostname (e.g., "youtube.com" → "youtube.com")
|
|
114619
|
-
try {
|
|
114620
|
-
embedJson.provider = new URL(url).hostname
|
|
114621
|
-
.split(/(?:www)?\./)
|
|
114622
|
-
.filter(i => i)
|
|
114623
|
-
.join('.');
|
|
114624
|
-
}
|
|
114625
|
-
catch {
|
|
114626
|
-
embedJson.provider = url;
|
|
114627
|
-
}
|
|
114628
|
-
return [
|
|
114629
|
-
wrapPinnedBlocks({
|
|
114630
|
-
children: [
|
|
114631
|
-
{ children: [{ type: 'text', value: title || '' }], title: embedJson.provider, type: 'link', url },
|
|
114632
|
-
],
|
|
114633
|
-
data: { hName: 'embed-block', hProperties: { ...embedJson, href: url, html, title, url } },
|
|
114634
|
-
type: 'embed',
|
|
114635
|
-
}, json),
|
|
114636
|
-
];
|
|
114637
|
-
}
|
|
114638
|
-
// HTML block: raw HTML content (scripts enabled only in compatibility mode)
|
|
114639
|
-
case 'html': {
|
|
114640
|
-
const htmlJson = json;
|
|
114641
|
-
return [
|
|
114642
|
-
wrapPinnedBlocks({
|
|
114643
|
-
data: {
|
|
114644
|
-
hName: 'html-block',
|
|
114645
|
-
hProperties: { html: htmlJson.html, runScripts: compatibilityMode, safeMode },
|
|
114646
|
-
},
|
|
114647
|
-
type: 'html-block',
|
|
114648
|
-
}, json),
|
|
114649
|
-
];
|
|
114650
|
-
}
|
|
114651
|
-
// Recipe/TutorialTile: renders as Recipe component
|
|
114652
|
-
case 'recipe':
|
|
114653
|
-
case 'tutorial-tile': {
|
|
114654
|
-
const recipeJson = json;
|
|
114655
|
-
if (!recipeJson.slug || !recipeJson.title)
|
|
114656
|
-
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
|
-
const recipeNode = {
|
|
114661
|
-
type: 'mdxJsxFlowElement',
|
|
114662
|
-
name: 'Recipe',
|
|
114663
|
-
attributes: toAttributes(recipeJson, ['slug', 'title']),
|
|
114664
|
-
children: [],
|
|
114665
|
-
// Position is optional but helps with debugging
|
|
114666
|
-
position: undefined,
|
|
114667
|
-
};
|
|
114668
|
-
return [recipeNode];
|
|
114669
|
-
}
|
|
114670
|
-
// Unknown block types: render as generic div with JSON properties
|
|
114671
|
-
default: {
|
|
114672
|
-
const text = json.text || json.html || '';
|
|
114673
|
-
return [
|
|
114674
|
-
wrapPinnedBlocks({ children: textToBlock(text), data: { hName: type || 'div', hProperties: json, ...json }, type: 'div' }, json),
|
|
114675
|
-
];
|
|
114676
|
-
}
|
|
114677
|
-
}
|
|
114678
|
-
}
|
|
114679
|
-
/**
|
|
114680
|
-
* Block-level node types that cannot be nested inside paragraphs.
|
|
114681
|
-
*/
|
|
114682
|
-
const blockTypes = [
|
|
114683
|
-
'heading',
|
|
114684
|
-
'code',
|
|
114685
|
-
'code-tabs',
|
|
114686
|
-
'paragraph',
|
|
114687
|
-
'blockquote',
|
|
114688
|
-
'list',
|
|
114689
|
-
'table',
|
|
114690
|
-
'thematicBreak',
|
|
114691
|
-
'html',
|
|
114692
|
-
'yaml',
|
|
114693
|
-
'toml',
|
|
114694
|
-
'rdme-pin',
|
|
114695
|
-
'rdme-callout',
|
|
114696
|
-
'html-block',
|
|
114697
|
-
'embed',
|
|
114698
|
-
'figure',
|
|
114699
|
-
'mdxJsxFlowElement',
|
|
114700
|
-
];
|
|
114701
|
-
/**
|
|
114702
|
-
* Check if a node is a block-level node (cannot be inside a paragraph)
|
|
114703
|
-
*/
|
|
114704
|
-
const isBlockNode = (node) => blockTypes.includes(node.type);
|
|
114987
|
+
...(favicon && { favicon }),
|
|
114988
|
+
...(html && { html }),
|
|
114989
|
+
...(image && { image }),
|
|
114990
|
+
...(providerName && { providerName }),
|
|
114991
|
+
...(providerUrl && { providerUrl }),
|
|
114992
|
+
},
|
|
114993
|
+
},
|
|
114994
|
+
position,
|
|
114995
|
+
};
|
|
114996
|
+
};
|
|
114997
|
+
const COMPONENT_MAP = {
|
|
114998
|
+
Callout: transformCallout,
|
|
114999
|
+
Embed: transformEmbed,
|
|
115000
|
+
Image: transformImage,
|
|
115001
|
+
Recipe: transformRecipe,
|
|
115002
|
+
};
|
|
114705
115003
|
/**
|
|
114706
|
-
*
|
|
115004
|
+
* Transform mdxJsxFlowElement nodes and magic block nodes into proper MDAST node types.
|
|
115005
|
+
*
|
|
115006
|
+
* This transformer runs after mdxishComponentBlocks and converts:
|
|
115007
|
+
* - JSX component elements (Image, Callout, Embed, Recipe) into their corresponding MDAST types
|
|
115008
|
+
* - Magic block image nodes (type: 'image') into image-block
|
|
115009
|
+
* - Magic block embed nodes (type: 'embed') into embed-block
|
|
115010
|
+
* - Figure nodes containing images (from magic blocks with captions) - transforms the inner image
|
|
114707
115011
|
*
|
|
114708
|
-
*
|
|
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.
|
|
115012
|
+
* This is controlled by the `newEditorTypes` flag to maintain backwards compatibility.
|
|
114711
115013
|
*/
|
|
114712
|
-
const
|
|
114713
|
-
|
|
114714
|
-
|
|
114715
|
-
|
|
114716
|
-
|
|
114717
|
-
|
|
114718
|
-
|
|
114719
|
-
|
|
114720
|
-
|
|
114721
|
-
|
|
114722
|
-
|
|
114723
|
-
const raw = magicBlockKeys.get(node.value);
|
|
114724
|
-
if (!raw)
|
|
114725
|
-
return undefined;
|
|
114726
|
-
const children = parseMagicBlock(raw);
|
|
114727
|
-
if (!children.length)
|
|
114728
|
-
return undefined;
|
|
114729
|
-
// If parent is a paragraph and we're inserting block nodes (which must not be in paragraphs), lift them out
|
|
114730
|
-
if (parent.type === 'paragraph' && children.some(child => isBlockNode(child))) {
|
|
114731
|
-
const blockNodes = [];
|
|
114732
|
-
const inlineNodes = [];
|
|
114733
|
-
// Separate block and inline nodes
|
|
114734
|
-
children.forEach(child => {
|
|
114735
|
-
if (isBlockNode(child)) {
|
|
114736
|
-
blockNodes.push(child);
|
|
114737
|
-
}
|
|
114738
|
-
else {
|
|
114739
|
-
inlineNodes.push(child);
|
|
114740
|
-
}
|
|
114741
|
-
});
|
|
114742
|
-
const before = parent.children.slice(0, index);
|
|
114743
|
-
const after = parent.children.slice(index + 1);
|
|
114744
|
-
replacements.push({
|
|
114745
|
-
parent,
|
|
114746
|
-
blockNodes,
|
|
114747
|
-
inlineNodes,
|
|
114748
|
-
before,
|
|
114749
|
-
after,
|
|
114750
|
-
});
|
|
114751
|
-
}
|
|
114752
|
-
else {
|
|
114753
|
-
// Normal case: just replace the inlineCode with the children
|
|
114754
|
-
parent.children.splice(index, 1, ...children);
|
|
114755
|
-
}
|
|
114756
|
-
return undefined;
|
|
115014
|
+
const mdxishJsxToMdast = () => tree => {
|
|
115015
|
+
// Transform JSX components (Image, Callout, Embed, Recipe)
|
|
115016
|
+
visit(tree, 'mdxJsxFlowElement', (node, index, parent) => {
|
|
115017
|
+
if (!parent || index === undefined || !node.name)
|
|
115018
|
+
return;
|
|
115019
|
+
const transformer = COMPONENT_MAP[node.name];
|
|
115020
|
+
if (!transformer)
|
|
115021
|
+
return;
|
|
115022
|
+
const newNode = transformer(node);
|
|
115023
|
+
// Replace the JSX node with the MDAST node
|
|
115024
|
+
parent.children[index] = newNode;
|
|
114757
115025
|
});
|
|
114758
|
-
//
|
|
114759
|
-
//
|
|
114760
|
-
|
|
114761
|
-
|
|
114762
|
-
|
|
114763
|
-
|
|
114764
|
-
|
|
114765
|
-
if (
|
|
114766
|
-
|
|
114767
|
-
|
|
114768
|
-
|
|
114769
|
-
|
|
114770
|
-
|
|
114771
|
-
|
|
114772
|
-
|
|
114773
|
-
|
|
114774
|
-
|
|
114775
|
-
|
|
114776
|
-
|
|
114777
|
-
|
|
114778
|
-
|
|
114779
|
-
|
|
114780
|
-
|
|
114781
|
-
|
|
114782
|
-
|
|
114783
|
-
|
|
114784
|
-
|
|
114785
|
-
|
|
114786
|
-
}
|
|
114787
|
-
else {
|
|
114788
|
-
// Keep paragraph with remaining content
|
|
114789
|
-
parent.children = [...before, ...after];
|
|
114790
|
-
// Insert block nodes after the paragraph
|
|
114791
|
-
if (blockNodes.length > 0) {
|
|
114792
|
-
rootChildren.splice(paraIndex + 1, 0, ...blockNodes);
|
|
115026
|
+
// Transform magic block images (type: 'image') to image-block
|
|
115027
|
+
// Note: Standard markdown images are wrapped in paragraphs and handled by imageTransformer
|
|
115028
|
+
// Magic block images are direct children of root, so we handle them here
|
|
115029
|
+
visit(tree, 'image', (node, index, parent) => {
|
|
115030
|
+
if (!parent || index === undefined)
|
|
115031
|
+
return SKIP;
|
|
115032
|
+
// Skip images inside paragraphs (those are standard markdown images handled by imageTransformer)
|
|
115033
|
+
if (parent.type === 'paragraph')
|
|
115034
|
+
return SKIP;
|
|
115035
|
+
const newNode = transformMagicBlockImage(node);
|
|
115036
|
+
parent.children[index] = newNode;
|
|
115037
|
+
return SKIP;
|
|
115038
|
+
});
|
|
115039
|
+
// Transform magic block embeds (type: 'embed') to embed-block
|
|
115040
|
+
visit(tree, 'embed', (node, index, parent) => {
|
|
115041
|
+
if (!parent || index === undefined)
|
|
115042
|
+
return SKIP;
|
|
115043
|
+
const newNode = transformMagicBlockEmbed(node);
|
|
115044
|
+
parent.children[index] = newNode;
|
|
115045
|
+
return SKIP;
|
|
115046
|
+
});
|
|
115047
|
+
// Transform images inside figure nodes (magic blocks with captions)
|
|
115048
|
+
const isFigure = (node) => node.type === 'figure';
|
|
115049
|
+
visit(tree, isFigure, node => {
|
|
115050
|
+
// Find and transform the image child
|
|
115051
|
+
node.children = node.children.map(child => {
|
|
115052
|
+
if (child.type === 'image') {
|
|
115053
|
+
return transformMagicBlockImage(child);
|
|
114793
115054
|
}
|
|
114794
|
-
|
|
114795
|
-
|
|
115055
|
+
return child;
|
|
115056
|
+
});
|
|
115057
|
+
});
|
|
115058
|
+
return tree;
|
|
114796
115059
|
};
|
|
114797
|
-
/* harmony default export */ const
|
|
115060
|
+
/* harmony default export */ const mdxish_jsx_to_mdast = (mdxishJsxToMdast);
|
|
114798
115061
|
|
|
114799
115062
|
;// ./processor/transform/mdxish/mdxish-mermaid.ts
|
|
114800
115063
|
|
|
@@ -114836,25 +115099,50 @@ const componentTagPattern = /<(\/?[A-Z][A-Za-z0-9_]*)([^>]*?)(\/?)>/g;
|
|
|
114836
115099
|
|
|
114837
115100
|
;// ./processor/transform/mdxish/mdxish-snake-case-components.ts
|
|
114838
115101
|
|
|
115102
|
+
|
|
114839
115103
|
/**
|
|
114840
115104
|
* Replaces snake_case component names with valid HTML placeholders.
|
|
114841
115105
|
* Required because remark-parse rejects tags with underscores.
|
|
114842
115106
|
* Example: `<Snake_case />` → `<MDXishSnakeCase0 />`
|
|
115107
|
+
*
|
|
115108
|
+
* Code blocks and inline code are protected and will not be transformed.
|
|
115109
|
+
*
|
|
115110
|
+
* @param content - The markdown content to process
|
|
115111
|
+
* @param options - Options including knownComponents to filter by
|
|
114843
115112
|
*/
|
|
114844
|
-
function processSnakeCaseComponent(content) {
|
|
115113
|
+
function processSnakeCaseComponent(content, options = {}) {
|
|
115114
|
+
const { knownComponents } = options;
|
|
114845
115115
|
// Early exit if no potential snake_case components
|
|
114846
115116
|
if (!/[A-Z][A-Za-z0-9]*_[A-Za-z0-9_]*/.test(content)) {
|
|
114847
115117
|
return { content, mapping: {} };
|
|
114848
115118
|
}
|
|
115119
|
+
// Step 1: Extract code blocks to protect them from transformation
|
|
115120
|
+
const { protectedCode, protectedContent } = protectCodeBlocks(content);
|
|
115121
|
+
// Find the highest existing placeholder number to avoid collisions
|
|
115122
|
+
// e.g., if content has <MDXishSnakeCase0 />, start counter from 1
|
|
115123
|
+
const placeholderPattern = /MDXishSnakeCase(\d+)/g;
|
|
115124
|
+
let startCounter = 0;
|
|
115125
|
+
let placeholderMatch;
|
|
115126
|
+
while ((placeholderMatch = placeholderPattern.exec(content)) !== null) {
|
|
115127
|
+
const num = parseInt(placeholderMatch[1], 10);
|
|
115128
|
+
if (num >= startCounter) {
|
|
115129
|
+
startCounter = num + 1;
|
|
115130
|
+
}
|
|
115131
|
+
}
|
|
114849
115132
|
const mapping = {};
|
|
114850
115133
|
const reverseMap = new Map();
|
|
114851
|
-
let counter =
|
|
114852
|
-
|
|
115134
|
+
let counter = startCounter;
|
|
115135
|
+
// Step 2: Transform snake_case components in non-code content
|
|
115136
|
+
const processedContent = protectedContent.replace(componentTagPattern, (match, tagName, attrs, selfClosing) => {
|
|
114853
115137
|
if (!tagName.includes('_')) {
|
|
114854
115138
|
return match;
|
|
114855
115139
|
}
|
|
114856
115140
|
const isClosing = tagName.startsWith('/');
|
|
114857
115141
|
const cleanTagName = isClosing ? tagName.slice(1) : tagName;
|
|
115142
|
+
// Only transform if it's a known component (or if no filter is provided)
|
|
115143
|
+
if (knownComponents && !knownComponents.has(cleanTagName)) {
|
|
115144
|
+
return match;
|
|
115145
|
+
}
|
|
114858
115146
|
let placeholder = reverseMap.get(cleanTagName);
|
|
114859
115147
|
if (!placeholder) {
|
|
114860
115148
|
// eslint-disable-next-line no-plusplus
|
|
@@ -114865,8 +115153,10 @@ function processSnakeCaseComponent(content) {
|
|
|
114865
115153
|
const processedTagName = isClosing ? `/${placeholder}` : placeholder;
|
|
114866
115154
|
return `<${processedTagName}${attrs}${selfClosing}>`;
|
|
114867
115155
|
});
|
|
115156
|
+
// Step 3: Restore code blocks (untouched)
|
|
115157
|
+
const finalContent = restoreCodeBlocks(processedContent, protectedCode);
|
|
114868
115158
|
return {
|
|
114869
|
-
content:
|
|
115159
|
+
content: finalContent,
|
|
114870
115160
|
mapping,
|
|
114871
115161
|
};
|
|
114872
115162
|
}
|
|
@@ -115248,58 +115538,1173 @@ const variablesTextTransformer = () => tree => {
|
|
|
115248
115538
|
};
|
|
115249
115539
|
/* harmony default export */ const variables_text = (variablesTextTransformer);
|
|
115250
115540
|
|
|
115251
|
-
;// ./lib/
|
|
115541
|
+
;// ./lib/mdast-util/magic-block/index.ts
|
|
115542
|
+
const contextMap = new WeakMap();
|
|
115252
115543
|
/**
|
|
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.
|
|
115544
|
+
* Find the magicBlock token in the token ancestry.
|
|
115258
115545
|
*/
|
|
115259
|
-
|
|
115546
|
+
function findMagicBlockToken() {
|
|
115547
|
+
// Walk up the token stack to find the magicBlock token
|
|
115548
|
+
const events = this.tokenStack;
|
|
115549
|
+
for (let i = events.length - 1; i >= 0; i -= 1) {
|
|
115550
|
+
const token = events[i][0];
|
|
115551
|
+
if (token.type === 'magicBlock') {
|
|
115552
|
+
return token;
|
|
115553
|
+
}
|
|
115554
|
+
}
|
|
115555
|
+
return undefined;
|
|
115556
|
+
}
|
|
115260
115557
|
/**
|
|
115261
|
-
*
|
|
115262
|
-
* Returns the modified markdown and an array of extracted blocks.
|
|
115558
|
+
* Enter handler: Create a new magicBlock node.
|
|
115263
115559
|
*/
|
|
115264
|
-
function
|
|
115265
|
-
|
|
115266
|
-
|
|
115267
|
-
|
|
115268
|
-
|
|
115269
|
-
|
|
115270
|
-
|
|
115271
|
-
|
|
115272
|
-
|
|
115273
|
-
|
|
115274
|
-
|
|
115275
|
-
|
|
115276
|
-
|
|
115277
|
-
|
|
115278
|
-
|
|
115279
|
-
|
|
115280
|
-
|
|
115281
|
-
|
|
115282
|
-
|
|
115283
|
-
|
|
115284
|
-
}
|
|
115285
|
-
|
|
115560
|
+
function enterMagicBlock(token) {
|
|
115561
|
+
// Initialize context for this magic block
|
|
115562
|
+
contextMap.set(token, { blockType: '', dataChunks: [] });
|
|
115563
|
+
this.enter({
|
|
115564
|
+
type: 'magicBlock',
|
|
115565
|
+
blockType: '',
|
|
115566
|
+
data: {},
|
|
115567
|
+
value: '',
|
|
115568
|
+
}, token);
|
|
115569
|
+
}
|
|
115570
|
+
/**
|
|
115571
|
+
* Exit handler for block type: Extract the block type from the token.
|
|
115572
|
+
*/
|
|
115573
|
+
function exitMagicBlockType(token) {
|
|
115574
|
+
const blockToken = findMagicBlockToken.call(this);
|
|
115575
|
+
if (!blockToken)
|
|
115576
|
+
return;
|
|
115577
|
+
const context = contextMap.get(blockToken);
|
|
115578
|
+
if (context) {
|
|
115579
|
+
context.blockType = this.sliceSerialize(token);
|
|
115580
|
+
}
|
|
115581
|
+
}
|
|
115582
|
+
/**
|
|
115583
|
+
* Exit handler for block data: Accumulate JSON content chunks.
|
|
115584
|
+
*/
|
|
115585
|
+
function exitMagicBlockData(token) {
|
|
115586
|
+
const blockToken = findMagicBlockToken.call(this);
|
|
115587
|
+
if (!blockToken)
|
|
115588
|
+
return;
|
|
115589
|
+
const context = contextMap.get(blockToken);
|
|
115590
|
+
if (context) {
|
|
115591
|
+
context.dataChunks.push(this.sliceSerialize(token));
|
|
115592
|
+
}
|
|
115286
115593
|
}
|
|
115287
115594
|
/**
|
|
115288
|
-
*
|
|
115595
|
+
* Exit handler for line endings: Preserve newlines in multiline blocks.
|
|
115289
115596
|
*/
|
|
115290
|
-
function
|
|
115291
|
-
|
|
115292
|
-
|
|
115293
|
-
|
|
115294
|
-
|
|
115295
|
-
|
|
115296
|
-
|
|
115297
|
-
|
|
115298
|
-
|
|
115299
|
-
|
|
115300
|
-
|
|
115597
|
+
function exitMagicBlockLineEnding(token) {
|
|
115598
|
+
const blockToken = findMagicBlockToken.call(this);
|
|
115599
|
+
if (!blockToken)
|
|
115600
|
+
return;
|
|
115601
|
+
const context = contextMap.get(blockToken);
|
|
115602
|
+
if (context) {
|
|
115603
|
+
context.dataChunks.push(this.sliceSerialize(token));
|
|
115604
|
+
}
|
|
115605
|
+
}
|
|
115606
|
+
/**
|
|
115607
|
+
* Exit handler for end marker: If this is a failed end marker check (not the final marker),
|
|
115608
|
+
* add its content to the data chunks so we don't lose characters like '['.
|
|
115609
|
+
*/
|
|
115610
|
+
function exitMagicBlockMarkerEnd(token) {
|
|
115611
|
+
const blockToken = findMagicBlockToken.call(this);
|
|
115612
|
+
if (!blockToken)
|
|
115613
|
+
return;
|
|
115614
|
+
// Get the content of the marker
|
|
115615
|
+
const markerContent = this.sliceSerialize(token);
|
|
115616
|
+
// If this marker doesn't end with ']', it's a failed check and content belongs to data
|
|
115617
|
+
// The successful end marker would be "[/block]"
|
|
115618
|
+
if (!markerContent.endsWith(']') || markerContent !== '[/block]') {
|
|
115619
|
+
const context = contextMap.get(blockToken);
|
|
115620
|
+
if (context) {
|
|
115621
|
+
context.dataChunks.push(markerContent);
|
|
115622
|
+
}
|
|
115623
|
+
}
|
|
115624
|
+
}
|
|
115625
|
+
/**
|
|
115626
|
+
* Exit handler: Finalize the magicBlock node with parsed JSON data.
|
|
115627
|
+
*/
|
|
115628
|
+
function exitMagicBlock(token) {
|
|
115629
|
+
const context = contextMap.get(token);
|
|
115630
|
+
const node = this.stack[this.stack.length - 1];
|
|
115631
|
+
if (context) {
|
|
115632
|
+
const rawJson = context.dataChunks.join('');
|
|
115633
|
+
node.blockType = context.blockType;
|
|
115634
|
+
node.value = `[block:${context.blockType}]${rawJson}[/block]`;
|
|
115635
|
+
// Parse JSON data
|
|
115636
|
+
try {
|
|
115637
|
+
node.data = JSON.parse(rawJson.trim());
|
|
115638
|
+
}
|
|
115639
|
+
catch {
|
|
115640
|
+
// Invalid JSON - store empty object but keep the raw value
|
|
115641
|
+
node.data = {};
|
|
115642
|
+
}
|
|
115643
|
+
// Clean up context
|
|
115644
|
+
contextMap.delete(token);
|
|
115645
|
+
}
|
|
115646
|
+
this.exit(token);
|
|
115647
|
+
}
|
|
115648
|
+
/**
|
|
115649
|
+
* Handler to serialize magicBlock nodes back to markdown.
|
|
115650
|
+
*/
|
|
115651
|
+
const handleMagicBlock = (node) => {
|
|
115652
|
+
const magicNode = node;
|
|
115653
|
+
// If we have the original raw value, use it
|
|
115654
|
+
if (magicNode.value) {
|
|
115655
|
+
return magicNode.value;
|
|
115656
|
+
}
|
|
115657
|
+
// Otherwise reconstruct from parsed data
|
|
115658
|
+
const json = JSON.stringify(magicNode.data, null, 2);
|
|
115659
|
+
return `[block:${magicNode.blockType}]\n${json}\n[/block]`;
|
|
115660
|
+
};
|
|
115661
|
+
/**
|
|
115662
|
+
* Create an extension for `mdast-util-from-markdown` to enable magic blocks.
|
|
115663
|
+
*
|
|
115664
|
+
* Converts micromark magic block tokens into `magicBlock` MDAST nodes.
|
|
115665
|
+
*
|
|
115666
|
+
* @returns Extension for `mdast-util-from-markdown`
|
|
115667
|
+
*/
|
|
115668
|
+
function magicBlockFromMarkdown() {
|
|
115669
|
+
return {
|
|
115670
|
+
enter: {
|
|
115671
|
+
magicBlock: enterMagicBlock,
|
|
115672
|
+
},
|
|
115673
|
+
exit: {
|
|
115674
|
+
magicBlockType: exitMagicBlockType,
|
|
115675
|
+
magicBlockData: exitMagicBlockData,
|
|
115676
|
+
magicBlockLineEnding: exitMagicBlockLineEnding,
|
|
115677
|
+
magicBlockMarkerEnd: exitMagicBlockMarkerEnd,
|
|
115678
|
+
magicBlock: exitMagicBlock,
|
|
115679
|
+
},
|
|
115680
|
+
};
|
|
115681
|
+
}
|
|
115682
|
+
/**
|
|
115683
|
+
* Create an extension for `mdast-util-to-markdown` to serialize magic blocks.
|
|
115684
|
+
*
|
|
115685
|
+
* Converts `magicBlock` MDAST nodes back to `[block:TYPE]JSON[/block]` syntax.
|
|
115686
|
+
*
|
|
115687
|
+
* @returns Extension for `mdast-util-to-markdown`
|
|
115688
|
+
*/
|
|
115689
|
+
function magicBlockToMarkdown() {
|
|
115690
|
+
return {
|
|
115691
|
+
handlers: {
|
|
115692
|
+
magicBlock: handleMagicBlock,
|
|
115693
|
+
},
|
|
115694
|
+
};
|
|
115301
115695
|
}
|
|
115302
115696
|
|
|
115697
|
+
;// ./node_modules/micromark-util-symbol/lib/codes.js
|
|
115698
|
+
/**
|
|
115699
|
+
* Character codes.
|
|
115700
|
+
*
|
|
115701
|
+
* This module is compiled away!
|
|
115702
|
+
*
|
|
115703
|
+
* micromark works based on character codes.
|
|
115704
|
+
* This module contains constants for the ASCII block and the replacement
|
|
115705
|
+
* character.
|
|
115706
|
+
* A couple of them are handled in a special way, such as the line endings
|
|
115707
|
+
* (CR, LF, and CR+LF, commonly known as end-of-line: EOLs), the tab (horizontal
|
|
115708
|
+
* tab) and its expansion based on what column it’s at (virtual space),
|
|
115709
|
+
* and the end-of-file (eof) character.
|
|
115710
|
+
* As values are preprocessed before handling them, the actual characters LF,
|
|
115711
|
+
* CR, HT, and NUL (which is present as the replacement character), are
|
|
115712
|
+
* guaranteed to not exist.
|
|
115713
|
+
*
|
|
115714
|
+
* Unicode basic latin block.
|
|
115715
|
+
*/
|
|
115716
|
+
const codes = /** @type {const} */ ({
|
|
115717
|
+
carriageReturn: -5,
|
|
115718
|
+
lineFeed: -4,
|
|
115719
|
+
carriageReturnLineFeed: -3,
|
|
115720
|
+
horizontalTab: -2,
|
|
115721
|
+
virtualSpace: -1,
|
|
115722
|
+
eof: null,
|
|
115723
|
+
nul: 0,
|
|
115724
|
+
soh: 1,
|
|
115725
|
+
stx: 2,
|
|
115726
|
+
etx: 3,
|
|
115727
|
+
eot: 4,
|
|
115728
|
+
enq: 5,
|
|
115729
|
+
ack: 6,
|
|
115730
|
+
bel: 7,
|
|
115731
|
+
bs: 8,
|
|
115732
|
+
ht: 9, // `\t`
|
|
115733
|
+
lf: 10, // `\n`
|
|
115734
|
+
vt: 11, // `\v`
|
|
115735
|
+
ff: 12, // `\f`
|
|
115736
|
+
cr: 13, // `\r`
|
|
115737
|
+
so: 14,
|
|
115738
|
+
si: 15,
|
|
115739
|
+
dle: 16,
|
|
115740
|
+
dc1: 17,
|
|
115741
|
+
dc2: 18,
|
|
115742
|
+
dc3: 19,
|
|
115743
|
+
dc4: 20,
|
|
115744
|
+
nak: 21,
|
|
115745
|
+
syn: 22,
|
|
115746
|
+
etb: 23,
|
|
115747
|
+
can: 24,
|
|
115748
|
+
em: 25,
|
|
115749
|
+
sub: 26,
|
|
115750
|
+
esc: 27,
|
|
115751
|
+
fs: 28,
|
|
115752
|
+
gs: 29,
|
|
115753
|
+
rs: 30,
|
|
115754
|
+
us: 31,
|
|
115755
|
+
space: 32,
|
|
115756
|
+
exclamationMark: 33, // `!`
|
|
115757
|
+
quotationMark: 34, // `"`
|
|
115758
|
+
numberSign: 35, // `#`
|
|
115759
|
+
dollarSign: 36, // `$`
|
|
115760
|
+
percentSign: 37, // `%`
|
|
115761
|
+
ampersand: 38, // `&`
|
|
115762
|
+
apostrophe: 39, // `'`
|
|
115763
|
+
leftParenthesis: 40, // `(`
|
|
115764
|
+
rightParenthesis: 41, // `)`
|
|
115765
|
+
asterisk: 42, // `*`
|
|
115766
|
+
plusSign: 43, // `+`
|
|
115767
|
+
comma: 44, // `,`
|
|
115768
|
+
dash: 45, // `-`
|
|
115769
|
+
dot: 46, // `.`
|
|
115770
|
+
slash: 47, // `/`
|
|
115771
|
+
digit0: 48, // `0`
|
|
115772
|
+
digit1: 49, // `1`
|
|
115773
|
+
digit2: 50, // `2`
|
|
115774
|
+
digit3: 51, // `3`
|
|
115775
|
+
digit4: 52, // `4`
|
|
115776
|
+
digit5: 53, // `5`
|
|
115777
|
+
digit6: 54, // `6`
|
|
115778
|
+
digit7: 55, // `7`
|
|
115779
|
+
digit8: 56, // `8`
|
|
115780
|
+
digit9: 57, // `9`
|
|
115781
|
+
colon: 58, // `:`
|
|
115782
|
+
semicolon: 59, // `;`
|
|
115783
|
+
lessThan: 60, // `<`
|
|
115784
|
+
equalsTo: 61, // `=`
|
|
115785
|
+
greaterThan: 62, // `>`
|
|
115786
|
+
questionMark: 63, // `?`
|
|
115787
|
+
atSign: 64, // `@`
|
|
115788
|
+
uppercaseA: 65, // `A`
|
|
115789
|
+
uppercaseB: 66, // `B`
|
|
115790
|
+
uppercaseC: 67, // `C`
|
|
115791
|
+
uppercaseD: 68, // `D`
|
|
115792
|
+
uppercaseE: 69, // `E`
|
|
115793
|
+
uppercaseF: 70, // `F`
|
|
115794
|
+
uppercaseG: 71, // `G`
|
|
115795
|
+
uppercaseH: 72, // `H`
|
|
115796
|
+
uppercaseI: 73, // `I`
|
|
115797
|
+
uppercaseJ: 74, // `J`
|
|
115798
|
+
uppercaseK: 75, // `K`
|
|
115799
|
+
uppercaseL: 76, // `L`
|
|
115800
|
+
uppercaseM: 77, // `M`
|
|
115801
|
+
uppercaseN: 78, // `N`
|
|
115802
|
+
uppercaseO: 79, // `O`
|
|
115803
|
+
uppercaseP: 80, // `P`
|
|
115804
|
+
uppercaseQ: 81, // `Q`
|
|
115805
|
+
uppercaseR: 82, // `R`
|
|
115806
|
+
uppercaseS: 83, // `S`
|
|
115807
|
+
uppercaseT: 84, // `T`
|
|
115808
|
+
uppercaseU: 85, // `U`
|
|
115809
|
+
uppercaseV: 86, // `V`
|
|
115810
|
+
uppercaseW: 87, // `W`
|
|
115811
|
+
uppercaseX: 88, // `X`
|
|
115812
|
+
uppercaseY: 89, // `Y`
|
|
115813
|
+
uppercaseZ: 90, // `Z`
|
|
115814
|
+
leftSquareBracket: 91, // `[`
|
|
115815
|
+
backslash: 92, // `\`
|
|
115816
|
+
rightSquareBracket: 93, // `]`
|
|
115817
|
+
caret: 94, // `^`
|
|
115818
|
+
underscore: 95, // `_`
|
|
115819
|
+
graveAccent: 96, // `` ` ``
|
|
115820
|
+
lowercaseA: 97, // `a`
|
|
115821
|
+
lowercaseB: 98, // `b`
|
|
115822
|
+
lowercaseC: 99, // `c`
|
|
115823
|
+
lowercaseD: 100, // `d`
|
|
115824
|
+
lowercaseE: 101, // `e`
|
|
115825
|
+
lowercaseF: 102, // `f`
|
|
115826
|
+
lowercaseG: 103, // `g`
|
|
115827
|
+
lowercaseH: 104, // `h`
|
|
115828
|
+
lowercaseI: 105, // `i`
|
|
115829
|
+
lowercaseJ: 106, // `j`
|
|
115830
|
+
lowercaseK: 107, // `k`
|
|
115831
|
+
lowercaseL: 108, // `l`
|
|
115832
|
+
lowercaseM: 109, // `m`
|
|
115833
|
+
lowercaseN: 110, // `n`
|
|
115834
|
+
lowercaseO: 111, // `o`
|
|
115835
|
+
lowercaseP: 112, // `p`
|
|
115836
|
+
lowercaseQ: 113, // `q`
|
|
115837
|
+
lowercaseR: 114, // `r`
|
|
115838
|
+
lowercaseS: 115, // `s`
|
|
115839
|
+
lowercaseT: 116, // `t`
|
|
115840
|
+
lowercaseU: 117, // `u`
|
|
115841
|
+
lowercaseV: 118, // `v`
|
|
115842
|
+
lowercaseW: 119, // `w`
|
|
115843
|
+
lowercaseX: 120, // `x`
|
|
115844
|
+
lowercaseY: 121, // `y`
|
|
115845
|
+
lowercaseZ: 122, // `z`
|
|
115846
|
+
leftCurlyBrace: 123, // `{`
|
|
115847
|
+
verticalBar: 124, // `|`
|
|
115848
|
+
rightCurlyBrace: 125, // `}`
|
|
115849
|
+
tilde: 126, // `~`
|
|
115850
|
+
del: 127,
|
|
115851
|
+
// Unicode Specials block.
|
|
115852
|
+
byteOrderMarker: 65_279,
|
|
115853
|
+
// Unicode Specials block.
|
|
115854
|
+
replacementCharacter: 65_533 // `�`
|
|
115855
|
+
})
|
|
115856
|
+
|
|
115857
|
+
;// ./lib/micromark/magic-block/syntax.ts
|
|
115858
|
+
|
|
115859
|
+
|
|
115860
|
+
/**
|
|
115861
|
+
* Known magic block types that the tokenizer will recognize.
|
|
115862
|
+
* Unknown types will not be tokenized as magic blocks.
|
|
115863
|
+
*/
|
|
115864
|
+
const KNOWN_BLOCK_TYPES = new Set([
|
|
115865
|
+
'code',
|
|
115866
|
+
'api-header',
|
|
115867
|
+
'image',
|
|
115868
|
+
'callout',
|
|
115869
|
+
'parameters',
|
|
115870
|
+
'table',
|
|
115871
|
+
'embed',
|
|
115872
|
+
'html',
|
|
115873
|
+
'recipe',
|
|
115874
|
+
'tutorial-tile',
|
|
115875
|
+
]);
|
|
115876
|
+
/**
|
|
115877
|
+
* Check if a character is valid for a magic block type identifier.
|
|
115878
|
+
* Types can contain alphanumeric characters and hyphens (e.g., "api-header", "tutorial-tile")
|
|
115879
|
+
*/
|
|
115880
|
+
function isTypeChar(code) {
|
|
115881
|
+
return asciiAlphanumeric(code) || code === codes.dash;
|
|
115882
|
+
}
|
|
115883
|
+
/**
|
|
115884
|
+
* Creates the opening marker state machine: [block:
|
|
115885
|
+
* Returns the first state function to start parsing.
|
|
115886
|
+
*/
|
|
115887
|
+
function createOpeningMarkerParser(effects, nok, onComplete) {
|
|
115888
|
+
const expectB = (code) => {
|
|
115889
|
+
if (code !== codes.lowercaseB)
|
|
115890
|
+
return nok(code);
|
|
115891
|
+
effects.consume(code);
|
|
115892
|
+
return expectL;
|
|
115893
|
+
};
|
|
115894
|
+
const expectL = (code) => {
|
|
115895
|
+
if (code !== codes.lowercaseL)
|
|
115896
|
+
return nok(code);
|
|
115897
|
+
effects.consume(code);
|
|
115898
|
+
return expectO;
|
|
115899
|
+
};
|
|
115900
|
+
const expectO = (code) => {
|
|
115901
|
+
if (code !== codes.lowercaseO)
|
|
115902
|
+
return nok(code);
|
|
115903
|
+
effects.consume(code);
|
|
115904
|
+
return expectC;
|
|
115905
|
+
};
|
|
115906
|
+
const expectC = (code) => {
|
|
115907
|
+
if (code !== codes.lowercaseC)
|
|
115908
|
+
return nok(code);
|
|
115909
|
+
effects.consume(code);
|
|
115910
|
+
return expectK;
|
|
115911
|
+
};
|
|
115912
|
+
const expectK = (code) => {
|
|
115913
|
+
if (code !== codes.lowercaseK)
|
|
115914
|
+
return nok(code);
|
|
115915
|
+
effects.consume(code);
|
|
115916
|
+
return expectColon;
|
|
115917
|
+
};
|
|
115918
|
+
const expectColon = (code) => {
|
|
115919
|
+
if (code !== codes.colon)
|
|
115920
|
+
return nok(code);
|
|
115921
|
+
effects.consume(code);
|
|
115922
|
+
effects.exit('magicBlockMarkerStart');
|
|
115923
|
+
effects.enter('magicBlockType');
|
|
115924
|
+
return onComplete;
|
|
115925
|
+
};
|
|
115926
|
+
return expectB;
|
|
115927
|
+
}
|
|
115928
|
+
/**
|
|
115929
|
+
* Creates the type capture state machine.
|
|
115930
|
+
* Captures type characters until ] and validates against known types.
|
|
115931
|
+
*/
|
|
115932
|
+
function createTypeCaptureParser(effects, nok, onComplete, blockTypeRef) {
|
|
115933
|
+
const captureTypeFirst = (code) => {
|
|
115934
|
+
// Reject empty type name [block:]
|
|
115935
|
+
if (code === codes.rightSquareBracket) {
|
|
115936
|
+
return nok(code);
|
|
115937
|
+
}
|
|
115938
|
+
if (isTypeChar(code)) {
|
|
115939
|
+
blockTypeRef.value += String.fromCharCode(code);
|
|
115940
|
+
effects.consume(code);
|
|
115941
|
+
return captureType;
|
|
115942
|
+
}
|
|
115943
|
+
return nok(code);
|
|
115944
|
+
};
|
|
115945
|
+
const captureType = (code) => {
|
|
115946
|
+
if (code === codes.rightSquareBracket) {
|
|
115947
|
+
if (!KNOWN_BLOCK_TYPES.has(blockTypeRef.value)) {
|
|
115948
|
+
return nok(code);
|
|
115949
|
+
}
|
|
115950
|
+
effects.exit('magicBlockType');
|
|
115951
|
+
effects.enter('magicBlockMarkerTypeEnd');
|
|
115952
|
+
effects.consume(code);
|
|
115953
|
+
effects.exit('magicBlockMarkerTypeEnd');
|
|
115954
|
+
return onComplete;
|
|
115955
|
+
}
|
|
115956
|
+
if (isTypeChar(code)) {
|
|
115957
|
+
blockTypeRef.value += String.fromCharCode(code);
|
|
115958
|
+
effects.consume(code);
|
|
115959
|
+
return captureType;
|
|
115960
|
+
}
|
|
115961
|
+
return nok(code);
|
|
115962
|
+
};
|
|
115963
|
+
return { first: captureTypeFirst, remaining: captureType };
|
|
115964
|
+
}
|
|
115965
|
+
/**
|
|
115966
|
+
* Creates the closing marker state machine: /block]
|
|
115967
|
+
* Handles partial matches by calling onMismatch to fall back to data capture.
|
|
115968
|
+
*/
|
|
115969
|
+
function createClosingMarkerParser(effects, onSuccess, onMismatch, onEof, jsonState) {
|
|
115970
|
+
const handleMismatch = (code) => {
|
|
115971
|
+
if (jsonState && code === codes.quotationMark) {
|
|
115972
|
+
jsonState.inString = true;
|
|
115973
|
+
}
|
|
115974
|
+
return onMismatch(code);
|
|
115975
|
+
};
|
|
115976
|
+
const expectSlash = (code) => {
|
|
115977
|
+
if (code === null)
|
|
115978
|
+
return onEof(code);
|
|
115979
|
+
if (code !== codes.slash)
|
|
115980
|
+
return handleMismatch(code);
|
|
115981
|
+
effects.consume(code);
|
|
115982
|
+
return expectB;
|
|
115983
|
+
};
|
|
115984
|
+
const expectB = (code) => {
|
|
115985
|
+
if (code === null)
|
|
115986
|
+
return onEof(code);
|
|
115987
|
+
if (code !== codes.lowercaseB)
|
|
115988
|
+
return handleMismatch(code);
|
|
115989
|
+
effects.consume(code);
|
|
115990
|
+
return expectL;
|
|
115991
|
+
};
|
|
115992
|
+
const expectL = (code) => {
|
|
115993
|
+
if (code === null)
|
|
115994
|
+
return onEof(code);
|
|
115995
|
+
if (code !== codes.lowercaseL)
|
|
115996
|
+
return handleMismatch(code);
|
|
115997
|
+
effects.consume(code);
|
|
115998
|
+
return expectO;
|
|
115999
|
+
};
|
|
116000
|
+
const expectO = (code) => {
|
|
116001
|
+
if (code === null)
|
|
116002
|
+
return onEof(code);
|
|
116003
|
+
if (code !== codes.lowercaseO)
|
|
116004
|
+
return handleMismatch(code);
|
|
116005
|
+
effects.consume(code);
|
|
116006
|
+
return expectC;
|
|
116007
|
+
};
|
|
116008
|
+
const expectC = (code) => {
|
|
116009
|
+
if (code === null)
|
|
116010
|
+
return onEof(code);
|
|
116011
|
+
if (code !== codes.lowercaseC)
|
|
116012
|
+
return handleMismatch(code);
|
|
116013
|
+
effects.consume(code);
|
|
116014
|
+
return expectK;
|
|
116015
|
+
};
|
|
116016
|
+
const expectK = (code) => {
|
|
116017
|
+
if (code === null)
|
|
116018
|
+
return onEof(code);
|
|
116019
|
+
if (code !== codes.lowercaseK)
|
|
116020
|
+
return handleMismatch(code);
|
|
116021
|
+
effects.consume(code);
|
|
116022
|
+
return expectBracket;
|
|
116023
|
+
};
|
|
116024
|
+
const expectBracket = (code) => {
|
|
116025
|
+
if (code === null)
|
|
116026
|
+
return onEof(code);
|
|
116027
|
+
if (code !== codes.rightSquareBracket)
|
|
116028
|
+
return handleMismatch(code);
|
|
116029
|
+
effects.consume(code);
|
|
116030
|
+
return onSuccess;
|
|
116031
|
+
};
|
|
116032
|
+
return { expectSlash };
|
|
116033
|
+
}
|
|
116034
|
+
/**
|
|
116035
|
+
* Partial construct for checking non-lazy continuation.
|
|
116036
|
+
* This is used by the flow tokenizer to check if we can continue
|
|
116037
|
+
* parsing on the next line.
|
|
116038
|
+
*/
|
|
116039
|
+
const syntax_nonLazyContinuation = {
|
|
116040
|
+
partial: true,
|
|
116041
|
+
tokenize: syntax_tokenizeNonLazyContinuation,
|
|
116042
|
+
};
|
|
116043
|
+
/**
|
|
116044
|
+
* Tokenizer for non-lazy continuation checking.
|
|
116045
|
+
* Returns ok if the next line is non-lazy (can continue), nok if lazy.
|
|
116046
|
+
*/
|
|
116047
|
+
function syntax_tokenizeNonLazyContinuation(effects, ok, nok) {
|
|
116048
|
+
const lineStart = (code) => {
|
|
116049
|
+
// `this` here refers to the micromark parser
|
|
116050
|
+
// since we are just passing functions as references and not actually calling them
|
|
116051
|
+
// micromarks's internal parser will call this automatically with appropriate arguments
|
|
116052
|
+
return this.parser.lazy[this.now().line] ? nok(code) : ok(code);
|
|
116053
|
+
};
|
|
116054
|
+
const start = (code) => {
|
|
116055
|
+
if (code === null) {
|
|
116056
|
+
return nok(code);
|
|
116057
|
+
}
|
|
116058
|
+
if (!markdownLineEnding(code)) {
|
|
116059
|
+
return nok(code);
|
|
116060
|
+
}
|
|
116061
|
+
effects.enter('magicBlockLineEnding');
|
|
116062
|
+
effects.consume(code);
|
|
116063
|
+
effects.exit('magicBlockLineEnding');
|
|
116064
|
+
return lineStart;
|
|
116065
|
+
};
|
|
116066
|
+
return start;
|
|
116067
|
+
}
|
|
116068
|
+
/**
|
|
116069
|
+
* Create a micromark extension for magic block syntax.
|
|
116070
|
+
*
|
|
116071
|
+
* This extension handles both single-line and multiline magic blocks:
|
|
116072
|
+
* - Flow construct (concrete): Handles block-level multiline magic blocks at document level
|
|
116073
|
+
* - Text construct: Handles inline magic blocks in lists, paragraphs, etc.
|
|
116074
|
+
*
|
|
116075
|
+
* The flow construct is marked as "concrete" which prevents it from being
|
|
116076
|
+
* interrupted by container markers (like `>` for blockquotes or `-` for lists).
|
|
116077
|
+
*/
|
|
116078
|
+
function magicBlock() {
|
|
116079
|
+
return {
|
|
116080
|
+
// Flow construct - handles block-level magic blocks at document root
|
|
116081
|
+
// Marked as concrete to prevent interruption by containers
|
|
116082
|
+
flow: {
|
|
116083
|
+
[codes.leftSquareBracket]: {
|
|
116084
|
+
name: 'magicBlock',
|
|
116085
|
+
concrete: true,
|
|
116086
|
+
tokenize: tokenizeMagicBlockFlow,
|
|
116087
|
+
},
|
|
116088
|
+
},
|
|
116089
|
+
// Text construct - handles magic blocks in inline contexts (lists, paragraphs)
|
|
116090
|
+
text: {
|
|
116091
|
+
[codes.leftSquareBracket]: {
|
|
116092
|
+
name: 'magicBlock',
|
|
116093
|
+
tokenize: tokenizeMagicBlockText,
|
|
116094
|
+
},
|
|
116095
|
+
},
|
|
116096
|
+
};
|
|
116097
|
+
}
|
|
116098
|
+
/**
|
|
116099
|
+
* Flow tokenizer for block-level magic blocks (multiline).
|
|
116100
|
+
* Uses the continuation checking pattern from code fences.
|
|
116101
|
+
*/
|
|
116102
|
+
function tokenizeMagicBlockFlow(effects, ok, nok) {
|
|
116103
|
+
// State for tracking JSON content
|
|
116104
|
+
const jsonState = { escapeNext: false, inString: false };
|
|
116105
|
+
const blockTypeRef = { value: '' };
|
|
116106
|
+
let seenOpenBrace = false;
|
|
116107
|
+
// Create shared parsers for opening marker and type capture
|
|
116108
|
+
const typeParser = createTypeCaptureParser(effects, nok, beforeData, blockTypeRef);
|
|
116109
|
+
const openingMarkerParser = createOpeningMarkerParser(effects, nok, typeParser.first);
|
|
116110
|
+
return start;
|
|
116111
|
+
function start(code) {
|
|
116112
|
+
if (code !== codes.leftSquareBracket)
|
|
116113
|
+
return nok(code);
|
|
116114
|
+
effects.enter('magicBlock');
|
|
116115
|
+
effects.enter('magicBlockMarkerStart');
|
|
116116
|
+
effects.consume(code);
|
|
116117
|
+
return openingMarkerParser;
|
|
116118
|
+
}
|
|
116119
|
+
/**
|
|
116120
|
+
* State before data content - handles line endings or starts data capture.
|
|
116121
|
+
*/
|
|
116122
|
+
function beforeData(code) {
|
|
116123
|
+
// EOF - magic block must be closed
|
|
116124
|
+
if (code === null) {
|
|
116125
|
+
effects.exit('magicBlock');
|
|
116126
|
+
return nok(code);
|
|
116127
|
+
}
|
|
116128
|
+
// Line ending before any data - check continuation
|
|
116129
|
+
if (markdownLineEnding(code)) {
|
|
116130
|
+
return effects.check(syntax_nonLazyContinuation, continuationOkBeforeData, after)(code);
|
|
116131
|
+
}
|
|
116132
|
+
// Check for closing marker directly (without entering data)
|
|
116133
|
+
// This handles cases like [block:type]\n{}\n\n[/block] where there are
|
|
116134
|
+
// newlines after the data object
|
|
116135
|
+
if (code === codes.leftSquareBracket) {
|
|
116136
|
+
effects.enter('magicBlockMarkerEnd');
|
|
116137
|
+
effects.consume(code);
|
|
116138
|
+
return expectSlashFromContinuation;
|
|
116139
|
+
}
|
|
116140
|
+
// If we've already seen the opening brace, just continue capturing data
|
|
116141
|
+
if (seenOpenBrace) {
|
|
116142
|
+
effects.enter('magicBlockData');
|
|
116143
|
+
return captureData(code);
|
|
116144
|
+
}
|
|
116145
|
+
// Skip whitespace (spaces/tabs) before the data - stay in beforeData
|
|
116146
|
+
// Don't enter magicBlockData token yet until we confirm there's a '{'
|
|
116147
|
+
if (code === codes.space || code === codes.horizontalTab) {
|
|
116148
|
+
return beforeDataWhitespace(code);
|
|
116149
|
+
}
|
|
116150
|
+
// Data must start with '{' for valid JSON
|
|
116151
|
+
if (code !== codes.leftCurlyBrace) {
|
|
116152
|
+
effects.exit('magicBlock');
|
|
116153
|
+
return nok(code);
|
|
116154
|
+
}
|
|
116155
|
+
// We have '{' - enter data token and start capturing
|
|
116156
|
+
seenOpenBrace = true;
|
|
116157
|
+
effects.enter('magicBlockData');
|
|
116158
|
+
return captureData(code);
|
|
116159
|
+
}
|
|
116160
|
+
/**
|
|
116161
|
+
* Consume whitespace before the data without creating a token.
|
|
116162
|
+
* Uses a temporary token to satisfy micromark's requirement.
|
|
116163
|
+
*/
|
|
116164
|
+
function beforeDataWhitespace(code) {
|
|
116165
|
+
if (code === null) {
|
|
116166
|
+
effects.exit('magicBlock');
|
|
116167
|
+
return nok(code);
|
|
116168
|
+
}
|
|
116169
|
+
if (markdownLineEnding(code)) {
|
|
116170
|
+
return effects.check(syntax_nonLazyContinuation, continuationOkBeforeData, after)(code);
|
|
116171
|
+
}
|
|
116172
|
+
if (code === codes.space || code === codes.horizontalTab) {
|
|
116173
|
+
// We need to consume this but can't without a token - use magicBlockData
|
|
116174
|
+
// and track that we haven't seen '{' yet
|
|
116175
|
+
effects.enter('magicBlockData');
|
|
116176
|
+
effects.consume(code);
|
|
116177
|
+
return beforeDataWhitespaceContinue;
|
|
116178
|
+
}
|
|
116179
|
+
if (code === codes.leftCurlyBrace) {
|
|
116180
|
+
seenOpenBrace = true;
|
|
116181
|
+
effects.enter('magicBlockData');
|
|
116182
|
+
return captureData(code);
|
|
116183
|
+
}
|
|
116184
|
+
effects.exit('magicBlock');
|
|
116185
|
+
return nok(code);
|
|
116186
|
+
}
|
|
116187
|
+
/**
|
|
116188
|
+
* Continue consuming whitespace or validate the opening brace.
|
|
116189
|
+
*/
|
|
116190
|
+
function beforeDataWhitespaceContinue(code) {
|
|
116191
|
+
if (code === null) {
|
|
116192
|
+
effects.exit('magicBlockData');
|
|
116193
|
+
effects.exit('magicBlock');
|
|
116194
|
+
return nok(code);
|
|
116195
|
+
}
|
|
116196
|
+
if (markdownLineEnding(code)) {
|
|
116197
|
+
effects.exit('magicBlockData');
|
|
116198
|
+
return effects.check(syntax_nonLazyContinuation, continuationOk, after)(code);
|
|
116199
|
+
}
|
|
116200
|
+
if (code === codes.space || code === codes.horizontalTab) {
|
|
116201
|
+
effects.consume(code);
|
|
116202
|
+
return beforeDataWhitespaceContinue;
|
|
116203
|
+
}
|
|
116204
|
+
if (code === codes.leftCurlyBrace) {
|
|
116205
|
+
seenOpenBrace = true;
|
|
116206
|
+
return captureData(code);
|
|
116207
|
+
}
|
|
116208
|
+
effects.exit('magicBlockData');
|
|
116209
|
+
effects.exit('magicBlock');
|
|
116210
|
+
return nok(code);
|
|
116211
|
+
}
|
|
116212
|
+
/**
|
|
116213
|
+
* Continuation OK before we've entered data token.
|
|
116214
|
+
*/
|
|
116215
|
+
function continuationOkBeforeData(code) {
|
|
116216
|
+
effects.enter('magicBlockLineEnding');
|
|
116217
|
+
effects.consume(code);
|
|
116218
|
+
effects.exit('magicBlockLineEnding');
|
|
116219
|
+
return beforeData; // Stay in beforeData state - don't enter magicBlockData yet
|
|
116220
|
+
}
|
|
116221
|
+
function captureData(code) {
|
|
116222
|
+
// EOF - magic block must be closed
|
|
116223
|
+
if (code === null) {
|
|
116224
|
+
effects.exit('magicBlockData');
|
|
116225
|
+
effects.exit('magicBlock');
|
|
116226
|
+
return nok(code);
|
|
116227
|
+
}
|
|
116228
|
+
// At line ending, check if we can continue on the next line
|
|
116229
|
+
if (markdownLineEnding(code)) {
|
|
116230
|
+
effects.exit('magicBlockData');
|
|
116231
|
+
return effects.check(syntax_nonLazyContinuation, continuationOk, after)(code);
|
|
116232
|
+
}
|
|
116233
|
+
if (jsonState.escapeNext) {
|
|
116234
|
+
jsonState.escapeNext = false;
|
|
116235
|
+
effects.consume(code);
|
|
116236
|
+
return captureData;
|
|
116237
|
+
}
|
|
116238
|
+
if (jsonState.inString) {
|
|
116239
|
+
if (code === codes.backslash) {
|
|
116240
|
+
jsonState.escapeNext = true;
|
|
116241
|
+
effects.consume(code);
|
|
116242
|
+
return captureData;
|
|
116243
|
+
}
|
|
116244
|
+
if (code === codes.quotationMark) {
|
|
116245
|
+
jsonState.inString = false;
|
|
116246
|
+
}
|
|
116247
|
+
effects.consume(code);
|
|
116248
|
+
return captureData;
|
|
116249
|
+
}
|
|
116250
|
+
if (code === codes.quotationMark) {
|
|
116251
|
+
jsonState.inString = true;
|
|
116252
|
+
effects.consume(code);
|
|
116253
|
+
return captureData;
|
|
116254
|
+
}
|
|
116255
|
+
if (code === codes.leftSquareBracket) {
|
|
116256
|
+
effects.exit('magicBlockData');
|
|
116257
|
+
effects.enter('magicBlockMarkerEnd');
|
|
116258
|
+
effects.consume(code);
|
|
116259
|
+
return expectSlash;
|
|
116260
|
+
}
|
|
116261
|
+
effects.consume(code);
|
|
116262
|
+
return captureData;
|
|
116263
|
+
}
|
|
116264
|
+
/**
|
|
116265
|
+
* Called when non-lazy continuation check passes - we can continue parsing.
|
|
116266
|
+
*/
|
|
116267
|
+
function continuationOk(code) {
|
|
116268
|
+
// Consume the line ending
|
|
116269
|
+
effects.enter('magicBlockLineEnding');
|
|
116270
|
+
effects.consume(code);
|
|
116271
|
+
effects.exit('magicBlockLineEnding');
|
|
116272
|
+
return continuationStart;
|
|
116273
|
+
}
|
|
116274
|
+
/**
|
|
116275
|
+
* Start of continuation line - check for more line endings, closing marker, or start capturing data.
|
|
116276
|
+
*/
|
|
116277
|
+
function continuationStart(code) {
|
|
116278
|
+
// Handle consecutive line endings
|
|
116279
|
+
if (code === null) {
|
|
116280
|
+
effects.exit('magicBlock');
|
|
116281
|
+
return nok(code);
|
|
116282
|
+
}
|
|
116283
|
+
if (markdownLineEnding(code)) {
|
|
116284
|
+
return effects.check(syntax_nonLazyContinuation, continuationOkBeforeData, after)(code);
|
|
116285
|
+
}
|
|
116286
|
+
// Check if this is the start of the closing marker [/block]
|
|
116287
|
+
// If so, handle it directly without entering magicBlockData
|
|
116288
|
+
if (code === codes.leftSquareBracket) {
|
|
116289
|
+
effects.enter('magicBlockMarkerEnd');
|
|
116290
|
+
effects.consume(code);
|
|
116291
|
+
return expectSlashFromContinuation;
|
|
116292
|
+
}
|
|
116293
|
+
effects.enter('magicBlockData');
|
|
116294
|
+
return captureData(code);
|
|
116295
|
+
}
|
|
116296
|
+
/**
|
|
116297
|
+
* Check for closing marker slash when coming from continuation.
|
|
116298
|
+
* If not a closing marker, create an empty data token and continue.
|
|
116299
|
+
*/
|
|
116300
|
+
function expectSlashFromContinuation(code) {
|
|
116301
|
+
if (code === null) {
|
|
116302
|
+
effects.exit('magicBlockMarkerEnd');
|
|
116303
|
+
effects.exit('magicBlock');
|
|
116304
|
+
return nok(code);
|
|
116305
|
+
}
|
|
116306
|
+
if (markdownLineEnding(code)) {
|
|
116307
|
+
effects.exit('magicBlockMarkerEnd');
|
|
116308
|
+
return effects.check(syntax_nonLazyContinuation, continuationOkBeforeData, after)(code);
|
|
116309
|
+
}
|
|
116310
|
+
if (code === codes.slash) {
|
|
116311
|
+
effects.consume(code);
|
|
116312
|
+
return expectClosingB;
|
|
116313
|
+
}
|
|
116314
|
+
// Not a closing marker - this is data content
|
|
116315
|
+
// Exit marker and enter data
|
|
116316
|
+
effects.exit('magicBlockMarkerEnd');
|
|
116317
|
+
effects.enter('magicBlockData');
|
|
116318
|
+
// The [ was consumed by the marker, so we need to conceptually "have it" in our data
|
|
116319
|
+
// But since we already consumed it into the marker, we need a different approach
|
|
116320
|
+
// Re-classify: consume this character as data and continue
|
|
116321
|
+
if (code === codes.quotationMark)
|
|
116322
|
+
jsonState.inString = true;
|
|
116323
|
+
effects.consume(code);
|
|
116324
|
+
return captureData;
|
|
116325
|
+
}
|
|
116326
|
+
function expectSlash(code) {
|
|
116327
|
+
if (code === null) {
|
|
116328
|
+
effects.exit('magicBlockMarkerEnd');
|
|
116329
|
+
effects.exit('magicBlock');
|
|
116330
|
+
return nok(code);
|
|
116331
|
+
}
|
|
116332
|
+
// At line ending during marker parsing
|
|
116333
|
+
if (markdownLineEnding(code)) {
|
|
116334
|
+
effects.exit('magicBlockMarkerEnd');
|
|
116335
|
+
return effects.check(syntax_nonLazyContinuation, continuationOk, after)(code);
|
|
116336
|
+
}
|
|
116337
|
+
if (code !== codes.slash) {
|
|
116338
|
+
effects.exit('magicBlockMarkerEnd');
|
|
116339
|
+
effects.enter('magicBlockData');
|
|
116340
|
+
if (code === codes.quotationMark)
|
|
116341
|
+
jsonState.inString = true;
|
|
116342
|
+
effects.consume(code);
|
|
116343
|
+
return captureData;
|
|
116344
|
+
}
|
|
116345
|
+
effects.consume(code);
|
|
116346
|
+
return expectClosingB;
|
|
116347
|
+
}
|
|
116348
|
+
function expectClosingB(code) {
|
|
116349
|
+
if (code === null) {
|
|
116350
|
+
effects.exit('magicBlockMarkerEnd');
|
|
116351
|
+
effects.exit('magicBlock');
|
|
116352
|
+
return nok(code);
|
|
116353
|
+
}
|
|
116354
|
+
if (code !== codes.lowercaseB) {
|
|
116355
|
+
effects.exit('magicBlockMarkerEnd');
|
|
116356
|
+
effects.enter('magicBlockData');
|
|
116357
|
+
if (code === codes.quotationMark)
|
|
116358
|
+
jsonState.inString = true;
|
|
116359
|
+
effects.consume(code);
|
|
116360
|
+
return captureData;
|
|
116361
|
+
}
|
|
116362
|
+
effects.consume(code);
|
|
116363
|
+
return expectClosingL;
|
|
116364
|
+
}
|
|
116365
|
+
function expectClosingL(code) {
|
|
116366
|
+
if (code === null) {
|
|
116367
|
+
effects.exit('magicBlockMarkerEnd');
|
|
116368
|
+
effects.exit('magicBlock');
|
|
116369
|
+
return nok(code);
|
|
116370
|
+
}
|
|
116371
|
+
if (code !== codes.lowercaseL) {
|
|
116372
|
+
effects.exit('magicBlockMarkerEnd');
|
|
116373
|
+
effects.enter('magicBlockData');
|
|
116374
|
+
if (code === codes.quotationMark)
|
|
116375
|
+
jsonState.inString = true;
|
|
116376
|
+
effects.consume(code);
|
|
116377
|
+
return captureData;
|
|
116378
|
+
}
|
|
116379
|
+
effects.consume(code);
|
|
116380
|
+
return expectClosingO;
|
|
116381
|
+
}
|
|
116382
|
+
function expectClosingO(code) {
|
|
116383
|
+
if (code === null) {
|
|
116384
|
+
effects.exit('magicBlockMarkerEnd');
|
|
116385
|
+
effects.exit('magicBlock');
|
|
116386
|
+
return nok(code);
|
|
116387
|
+
}
|
|
116388
|
+
if (code !== codes.lowercaseO) {
|
|
116389
|
+
effects.exit('magicBlockMarkerEnd');
|
|
116390
|
+
effects.enter('magicBlockData');
|
|
116391
|
+
if (code === codes.quotationMark)
|
|
116392
|
+
jsonState.inString = true;
|
|
116393
|
+
effects.consume(code);
|
|
116394
|
+
return captureData;
|
|
116395
|
+
}
|
|
116396
|
+
effects.consume(code);
|
|
116397
|
+
return expectClosingC;
|
|
116398
|
+
}
|
|
116399
|
+
function expectClosingC(code) {
|
|
116400
|
+
if (code === null) {
|
|
116401
|
+
effects.exit('magicBlockMarkerEnd');
|
|
116402
|
+
effects.exit('magicBlock');
|
|
116403
|
+
return nok(code);
|
|
116404
|
+
}
|
|
116405
|
+
if (code !== codes.lowercaseC) {
|
|
116406
|
+
effects.exit('magicBlockMarkerEnd');
|
|
116407
|
+
effects.enter('magicBlockData');
|
|
116408
|
+
if (code === codes.quotationMark)
|
|
116409
|
+
jsonState.inString = true;
|
|
116410
|
+
effects.consume(code);
|
|
116411
|
+
return captureData;
|
|
116412
|
+
}
|
|
116413
|
+
effects.consume(code);
|
|
116414
|
+
return expectClosingK;
|
|
116415
|
+
}
|
|
116416
|
+
function expectClosingK(code) {
|
|
116417
|
+
if (code === null) {
|
|
116418
|
+
effects.exit('magicBlockMarkerEnd');
|
|
116419
|
+
effects.exit('magicBlock');
|
|
116420
|
+
return nok(code);
|
|
116421
|
+
}
|
|
116422
|
+
if (code !== codes.lowercaseK) {
|
|
116423
|
+
effects.exit('magicBlockMarkerEnd');
|
|
116424
|
+
effects.enter('magicBlockData');
|
|
116425
|
+
if (code === codes.quotationMark)
|
|
116426
|
+
jsonState.inString = true;
|
|
116427
|
+
effects.consume(code);
|
|
116428
|
+
return captureData;
|
|
116429
|
+
}
|
|
116430
|
+
effects.consume(code);
|
|
116431
|
+
return expectClosingBracket;
|
|
116432
|
+
}
|
|
116433
|
+
function expectClosingBracket(code) {
|
|
116434
|
+
if (code === null) {
|
|
116435
|
+
effects.exit('magicBlockMarkerEnd');
|
|
116436
|
+
effects.exit('magicBlock');
|
|
116437
|
+
return nok(code);
|
|
116438
|
+
}
|
|
116439
|
+
if (code !== codes.rightSquareBracket) {
|
|
116440
|
+
effects.exit('magicBlockMarkerEnd');
|
|
116441
|
+
effects.enter('magicBlockData');
|
|
116442
|
+
if (code === codes.quotationMark)
|
|
116443
|
+
jsonState.inString = true;
|
|
116444
|
+
effects.consume(code);
|
|
116445
|
+
return captureData;
|
|
116446
|
+
}
|
|
116447
|
+
effects.consume(code);
|
|
116448
|
+
effects.exit('magicBlockMarkerEnd');
|
|
116449
|
+
// Check for trailing whitespace on the same line
|
|
116450
|
+
return consumeTrailing;
|
|
116451
|
+
}
|
|
116452
|
+
/**
|
|
116453
|
+
* Consume trailing whitespace (spaces/tabs) on the same line after [/block].
|
|
116454
|
+
* For concrete flow constructs, we must end at eol/eof or fail.
|
|
116455
|
+
* Trailing whitespace is consumed; other content causes nok (text tokenizer handles it).
|
|
116456
|
+
*/
|
|
116457
|
+
function consumeTrailing(code) {
|
|
116458
|
+
// End of file - done
|
|
116459
|
+
if (code === null) {
|
|
116460
|
+
effects.exit('magicBlock');
|
|
116461
|
+
return ok(code);
|
|
116462
|
+
}
|
|
116463
|
+
// Line ending - done
|
|
116464
|
+
if (markdownLineEnding(code)) {
|
|
116465
|
+
effects.exit('magicBlock');
|
|
116466
|
+
return ok(code);
|
|
116467
|
+
}
|
|
116468
|
+
// Space or tab - consume as trailing whitespace
|
|
116469
|
+
if (code === codes.space || code === codes.horizontalTab) {
|
|
116470
|
+
effects.enter('magicBlockTrailing');
|
|
116471
|
+
effects.consume(code);
|
|
116472
|
+
return consumeTrailingContinue;
|
|
116473
|
+
}
|
|
116474
|
+
// Any other character - fail flow tokenizer, let text tokenizer handle it
|
|
116475
|
+
effects.exit('magicBlock');
|
|
116476
|
+
return nok(code);
|
|
116477
|
+
}
|
|
116478
|
+
/**
|
|
116479
|
+
* Continue consuming trailing whitespace.
|
|
116480
|
+
*/
|
|
116481
|
+
function consumeTrailingContinue(code) {
|
|
116482
|
+
// End of file - done
|
|
116483
|
+
if (code === null) {
|
|
116484
|
+
effects.exit('magicBlockTrailing');
|
|
116485
|
+
effects.exit('magicBlock');
|
|
116486
|
+
return ok(code);
|
|
116487
|
+
}
|
|
116488
|
+
// Line ending - done
|
|
116489
|
+
if (markdownLineEnding(code)) {
|
|
116490
|
+
effects.exit('magicBlockTrailing');
|
|
116491
|
+
effects.exit('magicBlock');
|
|
116492
|
+
return ok(code);
|
|
116493
|
+
}
|
|
116494
|
+
// More space or tab - keep consuming
|
|
116495
|
+
if (code === codes.space || code === codes.horizontalTab) {
|
|
116496
|
+
effects.consume(code);
|
|
116497
|
+
return consumeTrailingContinue;
|
|
116498
|
+
}
|
|
116499
|
+
// Non-whitespace after whitespace - fail flow tokenizer
|
|
116500
|
+
effects.exit('magicBlockTrailing');
|
|
116501
|
+
effects.exit('magicBlock');
|
|
116502
|
+
return nok(code);
|
|
116503
|
+
}
|
|
116504
|
+
/**
|
|
116505
|
+
* Called when we can't continue (lazy line or EOF) - exit the construct.
|
|
116506
|
+
*/
|
|
116507
|
+
function after(code) {
|
|
116508
|
+
effects.exit('magicBlock');
|
|
116509
|
+
return nok(code);
|
|
116510
|
+
}
|
|
116511
|
+
}
|
|
116512
|
+
/**
|
|
116513
|
+
* Text tokenizer for single-line magic blocks only.
|
|
116514
|
+
* Used in inline contexts like list items and paragraphs.
|
|
116515
|
+
* Multiline blocks are handled by the flow tokenizer.
|
|
116516
|
+
*/
|
|
116517
|
+
function tokenizeMagicBlockText(effects, ok, nok) {
|
|
116518
|
+
// State for tracking JSON content
|
|
116519
|
+
const jsonState = { escapeNext: false, inString: false };
|
|
116520
|
+
const blockTypeRef = { value: '' };
|
|
116521
|
+
let seenOpenBrace = false;
|
|
116522
|
+
// Create shared parsers for opening marker and type capture
|
|
116523
|
+
const typeParser = createTypeCaptureParser(effects, nok, beforeData, blockTypeRef);
|
|
116524
|
+
const openingMarkerParser = createOpeningMarkerParser(effects, nok, typeParser.first);
|
|
116525
|
+
// Success handler for closing marker - exits tokens and returns ok
|
|
116526
|
+
const closingSuccess = (code) => {
|
|
116527
|
+
effects.exit('magicBlockMarkerEnd');
|
|
116528
|
+
effects.exit('magicBlock');
|
|
116529
|
+
return ok(code);
|
|
116530
|
+
};
|
|
116531
|
+
// Mismatch handler - falls back to data capture
|
|
116532
|
+
const closingMismatch = (code) => {
|
|
116533
|
+
effects.exit('magicBlockMarkerEnd');
|
|
116534
|
+
effects.enter('magicBlockData');
|
|
116535
|
+
if (code === codes.quotationMark)
|
|
116536
|
+
jsonState.inString = true;
|
|
116537
|
+
effects.consume(code);
|
|
116538
|
+
return captureData;
|
|
116539
|
+
};
|
|
116540
|
+
// EOF handler
|
|
116541
|
+
const closingEof = (_code) => nok(_code);
|
|
116542
|
+
// Create closing marker parsers
|
|
116543
|
+
const closingFromBeforeData = createClosingMarkerParser(effects, closingSuccess, closingMismatch, closingEof, jsonState);
|
|
116544
|
+
const closingFromData = createClosingMarkerParser(effects, closingSuccess, closingMismatch, closingEof, jsonState);
|
|
116545
|
+
return start;
|
|
116546
|
+
function start(code) {
|
|
116547
|
+
if (code !== codes.leftSquareBracket)
|
|
116548
|
+
return nok(code);
|
|
116549
|
+
effects.enter('magicBlock');
|
|
116550
|
+
effects.enter('magicBlockMarkerStart');
|
|
116551
|
+
effects.consume(code);
|
|
116552
|
+
return openingMarkerParser;
|
|
116553
|
+
}
|
|
116554
|
+
/**
|
|
116555
|
+
* State before data content - handles line endings before entering data token.
|
|
116556
|
+
* Whitespace before '{' is allowed.
|
|
116557
|
+
*/
|
|
116558
|
+
function beforeData(code) {
|
|
116559
|
+
// Fail on EOF - magic block must be closed
|
|
116560
|
+
if (code === null) {
|
|
116561
|
+
return nok(code);
|
|
116562
|
+
}
|
|
116563
|
+
// Handle line endings before any data - consume them without entering data token
|
|
116564
|
+
if (markdownLineEnding(code)) {
|
|
116565
|
+
effects.enter('magicBlockLineEnding');
|
|
116566
|
+
effects.consume(code);
|
|
116567
|
+
effects.exit('magicBlockLineEnding');
|
|
116568
|
+
return beforeData;
|
|
116569
|
+
}
|
|
116570
|
+
// Check for closing marker directly (without entering data)
|
|
116571
|
+
if (code === codes.leftSquareBracket) {
|
|
116572
|
+
effects.enter('magicBlockMarkerEnd');
|
|
116573
|
+
effects.consume(code);
|
|
116574
|
+
return closingFromBeforeData.expectSlash;
|
|
116575
|
+
}
|
|
116576
|
+
// If we've already seen the opening brace, just continue capturing data
|
|
116577
|
+
if (seenOpenBrace) {
|
|
116578
|
+
effects.enter('magicBlockData');
|
|
116579
|
+
return captureData(code);
|
|
116580
|
+
}
|
|
116581
|
+
// Skip whitespace (spaces/tabs) before the data
|
|
116582
|
+
if (code === codes.space || code === codes.horizontalTab) {
|
|
116583
|
+
return beforeDataWhitespace(code);
|
|
116584
|
+
}
|
|
116585
|
+
// Data must start with '{' for valid JSON
|
|
116586
|
+
if (code !== codes.leftCurlyBrace) {
|
|
116587
|
+
return nok(code);
|
|
116588
|
+
}
|
|
116589
|
+
// We have '{' - enter data token and start capturing
|
|
116590
|
+
seenOpenBrace = true;
|
|
116591
|
+
effects.enter('magicBlockData');
|
|
116592
|
+
return captureData(code);
|
|
116593
|
+
}
|
|
116594
|
+
/**
|
|
116595
|
+
* Consume whitespace before the data.
|
|
116596
|
+
*/
|
|
116597
|
+
function beforeDataWhitespace(code) {
|
|
116598
|
+
if (code === null) {
|
|
116599
|
+
return nok(code);
|
|
116600
|
+
}
|
|
116601
|
+
if (markdownLineEnding(code)) {
|
|
116602
|
+
effects.enter('magicBlockLineEnding');
|
|
116603
|
+
effects.consume(code);
|
|
116604
|
+
effects.exit('magicBlockLineEnding');
|
|
116605
|
+
return beforeData;
|
|
116606
|
+
}
|
|
116607
|
+
if (code === codes.space || code === codes.horizontalTab) {
|
|
116608
|
+
effects.enter('magicBlockData');
|
|
116609
|
+
effects.consume(code);
|
|
116610
|
+
return beforeDataWhitespaceContinue;
|
|
116611
|
+
}
|
|
116612
|
+
if (code === codes.leftCurlyBrace) {
|
|
116613
|
+
seenOpenBrace = true;
|
|
116614
|
+
effects.enter('magicBlockData');
|
|
116615
|
+
return captureData(code);
|
|
116616
|
+
}
|
|
116617
|
+
return nok(code);
|
|
116618
|
+
}
|
|
116619
|
+
/**
|
|
116620
|
+
* Continue consuming whitespace or validate the opening brace.
|
|
116621
|
+
*/
|
|
116622
|
+
function beforeDataWhitespaceContinue(code) {
|
|
116623
|
+
if (code === null) {
|
|
116624
|
+
effects.exit('magicBlockData');
|
|
116625
|
+
return nok(code);
|
|
116626
|
+
}
|
|
116627
|
+
if (markdownLineEnding(code)) {
|
|
116628
|
+
effects.exit('magicBlockData');
|
|
116629
|
+
effects.enter('magicBlockLineEnding');
|
|
116630
|
+
effects.consume(code);
|
|
116631
|
+
effects.exit('magicBlockLineEnding');
|
|
116632
|
+
return beforeData;
|
|
116633
|
+
}
|
|
116634
|
+
if (code === codes.space || code === codes.horizontalTab) {
|
|
116635
|
+
effects.consume(code);
|
|
116636
|
+
return beforeDataWhitespaceContinue;
|
|
116637
|
+
}
|
|
116638
|
+
if (code === codes.leftCurlyBrace) {
|
|
116639
|
+
seenOpenBrace = true;
|
|
116640
|
+
return captureData(code);
|
|
116641
|
+
}
|
|
116642
|
+
effects.exit('magicBlockData');
|
|
116643
|
+
return nok(code);
|
|
116644
|
+
}
|
|
116645
|
+
function captureData(code) {
|
|
116646
|
+
// Fail on EOF - magic block must be closed
|
|
116647
|
+
if (code === null) {
|
|
116648
|
+
effects.exit('magicBlockData');
|
|
116649
|
+
return nok(code);
|
|
116650
|
+
}
|
|
116651
|
+
// Handle multiline magic blocks within text/paragraphs
|
|
116652
|
+
// Exit data, consume line ending with proper token, then re-enter data
|
|
116653
|
+
if (markdownLineEnding(code)) {
|
|
116654
|
+
effects.exit('magicBlockData');
|
|
116655
|
+
effects.enter('magicBlockLineEnding');
|
|
116656
|
+
effects.consume(code);
|
|
116657
|
+
effects.exit('magicBlockLineEnding');
|
|
116658
|
+
return beforeData; // Go back to beforeData to handle potential empty lines
|
|
116659
|
+
}
|
|
116660
|
+
if (jsonState.escapeNext) {
|
|
116661
|
+
jsonState.escapeNext = false;
|
|
116662
|
+
effects.consume(code);
|
|
116663
|
+
return captureData;
|
|
116664
|
+
}
|
|
116665
|
+
if (jsonState.inString) {
|
|
116666
|
+
if (code === codes.backslash) {
|
|
116667
|
+
jsonState.escapeNext = true;
|
|
116668
|
+
effects.consume(code);
|
|
116669
|
+
return captureData;
|
|
116670
|
+
}
|
|
116671
|
+
if (code === codes.quotationMark) {
|
|
116672
|
+
jsonState.inString = false;
|
|
116673
|
+
}
|
|
116674
|
+
effects.consume(code);
|
|
116675
|
+
return captureData;
|
|
116676
|
+
}
|
|
116677
|
+
if (code === codes.quotationMark) {
|
|
116678
|
+
jsonState.inString = true;
|
|
116679
|
+
effects.consume(code);
|
|
116680
|
+
return captureData;
|
|
116681
|
+
}
|
|
116682
|
+
if (code === codes.leftSquareBracket) {
|
|
116683
|
+
effects.exit('magicBlockData');
|
|
116684
|
+
effects.enter('magicBlockMarkerEnd');
|
|
116685
|
+
effects.consume(code);
|
|
116686
|
+
return closingFromData.expectSlash;
|
|
116687
|
+
}
|
|
116688
|
+
effects.consume(code);
|
|
116689
|
+
return captureData;
|
|
116690
|
+
}
|
|
116691
|
+
}
|
|
116692
|
+
/* harmony default export */ const syntax = ((/* unused pure expression or super */ null && (magicBlock)));
|
|
116693
|
+
|
|
116694
|
+
;// ./lib/micromark/magic-block/index.ts
|
|
116695
|
+
/**
|
|
116696
|
+
* Micromark extension for magic block syntax.
|
|
116697
|
+
*
|
|
116698
|
+
* Usage:
|
|
116699
|
+
* ```ts
|
|
116700
|
+
* import { magicBlock } from './lib/micromark/magic-block';
|
|
116701
|
+
*
|
|
116702
|
+
* const processor = unified()
|
|
116703
|
+
* .data('micromarkExtensions', [magicBlock()])
|
|
116704
|
+
* ```
|
|
116705
|
+
*/
|
|
116706
|
+
|
|
116707
|
+
|
|
115303
116708
|
;// ./lib/utils/mdxish/mdxish-load-components.ts
|
|
115304
116709
|
|
|
115305
116710
|
|
|
@@ -115365,44 +116770,52 @@ function loadComponents() {
|
|
|
115365
116770
|
|
|
115366
116771
|
|
|
115367
116772
|
|
|
116773
|
+
|
|
116774
|
+
|
|
115368
116775
|
|
|
115369
116776
|
|
|
115370
116777
|
|
|
115371
116778
|
const defaultTransformers = [callouts, code_tabs, gemoji_, transform_embeds];
|
|
115372
116779
|
function mdxishAstProcessor(mdContent, opts = {}) {
|
|
115373
|
-
const { components: userComponents = {}, jsxContext = {}, useTailwind } = opts;
|
|
116780
|
+
const { components: userComponents = {}, jsxContext = {}, newEditorTypes = false, useTailwind } = opts;
|
|
115374
116781
|
const components = {
|
|
115375
116782
|
...loadComponents(),
|
|
115376
116783
|
...userComponents,
|
|
115377
116784
|
};
|
|
116785
|
+
// Build set of known component names for snake_case filtering
|
|
116786
|
+
const knownComponents = new Set(Object.keys(components));
|
|
115378
116787
|
// Preprocessing pipeline: Transform content to be parser-ready
|
|
115379
|
-
// Step 1:
|
|
115380
|
-
const
|
|
115381
|
-
// Step 2:
|
|
115382
|
-
const contentAfterTableNormalization = normalizeTableSeparator(contentAfterMagicBlocks);
|
|
115383
|
-
// Step 3: Evaluate JSX expressions in attributes
|
|
116788
|
+
// Step 1: Normalize malformed table separator syntax (e.g., `|: ---` → `| :---`)
|
|
116789
|
+
const contentAfterTableNormalization = normalizeTableSeparator(mdContent);
|
|
116790
|
+
// Step 2: Evaluate JSX expressions in attributes
|
|
115384
116791
|
const contentAfterJSXEvaluation = preprocessJSXExpressions(contentAfterTableNormalization, jsxContext);
|
|
115385
|
-
// Step
|
|
115386
|
-
|
|
115387
|
-
const { content: parserReadyContent, mapping: snakeCaseMapping } = processSnakeCaseComponent(contentAfterJSXEvaluation);
|
|
116792
|
+
// Step 3: Replace snake_case component names with parser-safe placeholders
|
|
116793
|
+
const { content: parserReadyContent, mapping: snakeCaseMapping } = processSnakeCaseComponent(contentAfterJSXEvaluation, { knownComponents });
|
|
115388
116794
|
// Create string map for tailwind transformer
|
|
115389
116795
|
const tempComponentsMap = Object.entries(components).reduce((acc, [key, value]) => {
|
|
115390
116796
|
acc[key] = String(value);
|
|
115391
116797
|
return acc;
|
|
115392
116798
|
}, {});
|
|
116799
|
+
// Get mdxExpression extension and remove its flow construct to prevent
|
|
116800
|
+
// `{...}` from interrupting paragraphs (which breaks multiline magic blocks)
|
|
116801
|
+
const mdxExprExt = mdxExpression({ allowEmpty: true });
|
|
116802
|
+
const mdxExprTextOnly = {
|
|
116803
|
+
text: mdxExprExt.text,
|
|
116804
|
+
};
|
|
115393
116805
|
const processor = unified()
|
|
115394
|
-
.data('micromarkExtensions', [
|
|
115395
|
-
.data('fromMarkdownExtensions', [mdxExpressionFromMarkdown()])
|
|
116806
|
+
.data('micromarkExtensions', [magicBlock(), mdxExprTextOnly])
|
|
116807
|
+
.data('fromMarkdownExtensions', [magicBlockFromMarkdown(), mdxExpressionFromMarkdown()])
|
|
115396
116808
|
.use(remarkParse)
|
|
115397
116809
|
.use(remarkFrontmatter)
|
|
115398
116810
|
.use(normalize_malformed_md_syntax)
|
|
115399
|
-
.use(
|
|
116811
|
+
.use(magic_block_transformer)
|
|
115400
116812
|
.use(transform_images, { isMdxish: true })
|
|
115401
116813
|
.use(defaultTransformers)
|
|
115402
116814
|
.use(mdxish_component_blocks)
|
|
115403
116815
|
.use(restore_snake_case_component_name, { mapping: snakeCaseMapping })
|
|
115404
116816
|
.use(mdxish_tables)
|
|
115405
116817
|
.use(mdxish_html_blocks)
|
|
116818
|
+
.use(newEditorTypes ? mdxish_jsx_to_mdast : undefined) // Convert JSX elements to MDAST types
|
|
115406
116819
|
.use(evaluate_expressions, { context: jsxContext }) // Evaluate MDX expressions using jsxContext
|
|
115407
116820
|
.use(variables_text) // Parse {user.*} patterns from text (can't rely on remarkMdx)
|
|
115408
116821
|
.use(useTailwind ? transform_tailwind : undefined, { components: tempComponentsMap })
|
|
@@ -115421,10 +116834,14 @@ function mdxishAstProcessor(mdContent, opts = {}) {
|
|
|
115421
116834
|
* Converts an Mdast to a Markdown string.
|
|
115422
116835
|
*/
|
|
115423
116836
|
function mdxishMdastToMd(mdast) {
|
|
115424
|
-
const md = unified()
|
|
116837
|
+
const md = unified()
|
|
116838
|
+
.use(remarkGfm)
|
|
116839
|
+
.use(mdxishCompilers)
|
|
116840
|
+
.use(remarkStringify, {
|
|
115425
116841
|
bullet: '-',
|
|
115426
116842
|
emphasis: '_',
|
|
115427
|
-
})
|
|
116843
|
+
})
|
|
116844
|
+
.stringify(mdast);
|
|
115428
116845
|
return md;
|
|
115429
116846
|
}
|
|
115430
116847
|
/**
|
|
@@ -115920,11 +117337,14 @@ const tags = (doc) => {
|
|
|
115920
117337
|
|
|
115921
117338
|
|
|
115922
117339
|
|
|
117340
|
+
|
|
115923
117341
|
const mdxishTags_tags = (doc) => {
|
|
115924
|
-
const { replaced: sanitizedDoc } = extractMagicBlocks(doc);
|
|
115925
117342
|
const set = new Set();
|
|
115926
|
-
const processor = remark()
|
|
115927
|
-
|
|
117343
|
+
const processor = remark()
|
|
117344
|
+
.data('micromarkExtensions', [magicBlock()])
|
|
117345
|
+
.data('fromMarkdownExtensions', [magicBlockFromMarkdown()])
|
|
117346
|
+
.use(mdxish_component_blocks);
|
|
117347
|
+
const tree = processor.parse(doc);
|
|
115928
117348
|
visit(processor.runSync(tree), isMDXElement, (node) => {
|
|
115929
117349
|
if (node.name?.match(/^[A-Z]/)) {
|
|
115930
117350
|
set.add(node.name);
|
|
@@ -115945,12 +117365,15 @@ const mdxishTags_tags = (doc) => {
|
|
|
115945
117365
|
|
|
115946
117366
|
|
|
115947
117367
|
|
|
117368
|
+
|
|
115948
117369
|
/**
|
|
115949
117370
|
* Removes Markdown and MDX comments.
|
|
115950
117371
|
*/
|
|
115951
117372
|
async function stripComments(doc, { mdx, mdxish } = {}) {
|
|
115952
|
-
const
|
|
115953
|
-
|
|
117373
|
+
const processor = unified()
|
|
117374
|
+
.data('micromarkExtensions', [magicBlock()])
|
|
117375
|
+
.data('fromMarkdownExtensions', [magicBlockFromMarkdown()])
|
|
117376
|
+
.data('toMarkdownExtensions', [magicBlockToMarkdown()]);
|
|
115954
117377
|
// we still require these two extensions because:
|
|
115955
117378
|
// 1. we can rely on remarkMdx to parse MDXish
|
|
115956
117379
|
// 2. we need to parse JSX comments into mdxTextExpression nodes so that the transformers can pick them up
|
|
@@ -115992,10 +117415,8 @@ async function stripComments(doc, { mdx, mdxish } = {}) {
|
|
|
115992
117415
|
},
|
|
115993
117416
|
],
|
|
115994
117417
|
});
|
|
115995
|
-
const file = await processor.process(
|
|
115996
|
-
|
|
115997
|
-
const restored = restoreMagicBlocks(stringified, blocks);
|
|
115998
|
-
return restored;
|
|
117418
|
+
const file = await processor.process(doc);
|
|
117419
|
+
return String(file).trim();
|
|
115999
117420
|
}
|
|
116000
117421
|
/* harmony default export */ const lib_stripComments = (stripComments);
|
|
116001
117422
|
|