@readme/markdown 13.7.1 → 13.7.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/components/CodeTabs/index.tsx +62 -19
- package/components/TailwindStyle/index.tsx +18 -7
- package/dist/lib/mdxish.d.ts +1 -1
- package/dist/main.js +708 -444
- package/dist/main.node.js +708 -444
- package/dist/main.node.js.map +1 -1
- package/dist/processor/transform/mdxish/mdxish-tables-to-jsx.d.ts +11 -0
- package/package.json +2 -2
package/dist/main.js
CHANGED
|
@@ -11704,10 +11704,47 @@ var dist_utils_default = /*#__PURE__*/__webpack_require__.n(dist_utils);
|
|
|
11704
11704
|
|
|
11705
11705
|
let mermaid;
|
|
11706
11706
|
const { uppercase } = (dist_utils_default());
|
|
11707
|
+
// Module-level queue that batches mermaid nodes across all CodeTabs instances into a
|
|
11708
|
+
// single mermaid.run() call. This is necessary because mermaid generates SVG element IDs
|
|
11709
|
+
// using Date.now(), which collides when multiple diagrams render in the same millisecond.
|
|
11710
|
+
// Colliding IDs cause diagrams to overlap or break layout.
|
|
11711
|
+
//
|
|
11712
|
+
// Why not use `deterministicIDSeed` with a unique ID per diagram? Mermaid's implementation
|
|
11713
|
+
// only uses seed.length (not the seed value) to compute the starting ID, so every UUID
|
|
11714
|
+
// (36 chars) produces the same `mermaid-36` prefix — the collision remains.
|
|
11715
|
+
// See: https://github.com/mermaid-js/mermaid/blob/mermaid%4011.12.0/packages/mermaid/src/utils.ts#L755-L761
|
|
11716
|
+
//
|
|
11717
|
+
// These vars must be module-scoped (not per-instance refs) because the batching requires
|
|
11718
|
+
// cross-instance coordination. They are short-lived: the queue drains on the next macrotask
|
|
11719
|
+
// and cleanup clears everything on unmount.
|
|
11720
|
+
let mermaidQueue = [];
|
|
11721
|
+
let mermaidFlushTimer = null;
|
|
11722
|
+
let currentTheme;
|
|
11723
|
+
function queueMermaidNode(node, theme) {
|
|
11724
|
+
mermaidQueue.push(node);
|
|
11725
|
+
currentTheme = theme;
|
|
11726
|
+
if (!mermaidFlushTimer) {
|
|
11727
|
+
// setTimeout(0) defers to a macrotask, after all useEffects have queued their nodes
|
|
11728
|
+
mermaidFlushTimer = setTimeout(async () => {
|
|
11729
|
+
const nodes = [...mermaidQueue];
|
|
11730
|
+
mermaidQueue = [];
|
|
11731
|
+
mermaidFlushTimer = null;
|
|
11732
|
+
const module = await Promise.resolve(/* import() */).then(__webpack_require__.t.bind(__webpack_require__, 1387, 23));
|
|
11733
|
+
mermaid = module.default;
|
|
11734
|
+
mermaid.initialize({
|
|
11735
|
+
startOnLoad: false,
|
|
11736
|
+
theme: currentTheme === 'dark' ? 'dark' : 'default',
|
|
11737
|
+
deterministicIds: true,
|
|
11738
|
+
});
|
|
11739
|
+
await mermaid.run({ nodes });
|
|
11740
|
+
}, 0);
|
|
11741
|
+
}
|
|
11742
|
+
}
|
|
11707
11743
|
const CodeTabs = (props) => {
|
|
11708
11744
|
const { children } = props;
|
|
11709
11745
|
const theme = (0,external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_.useContext)(Theme);
|
|
11710
11746
|
const isHydrated = useHydrated();
|
|
11747
|
+
const mermaidRef = (0,external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_.useRef)(null);
|
|
11711
11748
|
// Handle both array (from rehype-react in rendering mdxish) and single element (MDX/JSX runtime) cases
|
|
11712
11749
|
// The children here is the individual code block objects
|
|
11713
11750
|
const childrenArray = Array.isArray(children) ? children : [children];
|
|
@@ -11718,22 +11755,20 @@ const CodeTabs = (props) => {
|
|
|
11718
11755
|
return Array.isArray(pre?.props?.children) ? pre.props.children[0] : pre?.props?.children;
|
|
11719
11756
|
};
|
|
11720
11757
|
const containAtLeastOneMermaid = childrenArray.some(pre => getCodeComponent(pre)?.props?.lang === 'mermaid');
|
|
11721
|
-
// Render Mermaid diagram
|
|
11722
11758
|
(0,external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_.useEffect)(() => {
|
|
11723
|
-
//
|
|
11724
|
-
|
|
11725
|
-
|
|
11726
|
-
|
|
11727
|
-
|
|
11728
|
-
|
|
11729
|
-
|
|
11730
|
-
|
|
11731
|
-
|
|
11732
|
-
|
|
11733
|
-
|
|
11734
|
-
|
|
11735
|
-
|
|
11736
|
-
}
|
|
11759
|
+
// Wait for hydration so mermaid's DOM mutations don't cause mismatches
|
|
11760
|
+
if (typeof window !== 'undefined' && containAtLeastOneMermaid && isHydrated && mermaidRef.current) {
|
|
11761
|
+
queueMermaidNode(mermaidRef.current, theme);
|
|
11762
|
+
}
|
|
11763
|
+
return () => {
|
|
11764
|
+
// Clear the batch timer on unmount to prevent mermaid from running
|
|
11765
|
+
// after the DOM is torn down
|
|
11766
|
+
if (mermaidFlushTimer) {
|
|
11767
|
+
clearTimeout(mermaidFlushTimer);
|
|
11768
|
+
mermaidFlushTimer = null;
|
|
11769
|
+
mermaidQueue = [];
|
|
11770
|
+
}
|
|
11771
|
+
};
|
|
11737
11772
|
}, [containAtLeastOneMermaid, theme, isHydrated]);
|
|
11738
11773
|
function handleClick({ target }, index) {
|
|
11739
11774
|
const $wrap = target.parentElement.parentElement;
|
|
@@ -11749,15 +11784,13 @@ const CodeTabs = (props) => {
|
|
|
11749
11784
|
const codeComponent = getCodeComponent(childrenArray[0]);
|
|
11750
11785
|
if (codeComponent?.props?.lang === 'mermaid') {
|
|
11751
11786
|
const value = codeComponent?.props?.value;
|
|
11752
|
-
return external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_default().createElement("pre", { className: "mermaid-render mermaid_single" }, value);
|
|
11787
|
+
return (external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_default().createElement("pre", { ref: mermaidRef, className: "mermaid-render mermaid_single" }, value));
|
|
11753
11788
|
}
|
|
11754
11789
|
}
|
|
11755
11790
|
return (external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_default().createElement("div", { className: `CodeTabs CodeTabs_initial theme-${theme}` },
|
|
11756
11791
|
external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_default().createElement("div", { className: "CodeTabs-toolbar" }, childrenArray.map((pre, i) => {
|
|
11757
11792
|
// the first or only child should be our Code component
|
|
11758
|
-
const tabCodeComponent = Array.isArray(pre.props?.children)
|
|
11759
|
-
? pre.props.children[0]
|
|
11760
|
-
: pre.props?.children;
|
|
11793
|
+
const tabCodeComponent = Array.isArray(pre.props?.children) ? pre.props.children[0] : pre.props?.children;
|
|
11761
11794
|
const lang = tabCodeComponent?.props?.lang;
|
|
11762
11795
|
const meta = tabCodeComponent?.props?.meta;
|
|
11763
11796
|
/* istanbul ignore next */
|
|
@@ -12309,10 +12342,19 @@ const TailwindStyle = ({ children, darkModeDataAttribute }) => {
|
|
|
12309
12342
|
records.forEach(record => {
|
|
12310
12343
|
if (record.type === 'childList') {
|
|
12311
12344
|
record.addedNodes.forEach(node => {
|
|
12312
|
-
if (!(node instanceof HTMLElement)
|
|
12345
|
+
if (!(node instanceof HTMLElement))
|
|
12313
12346
|
return;
|
|
12314
|
-
|
|
12315
|
-
|
|
12347
|
+
// Check the added node itself
|
|
12348
|
+
if (node.classList.contains(tailwindPrefix)) {
|
|
12349
|
+
traverse(node, addClasses);
|
|
12350
|
+
shouldUpdate = true;
|
|
12351
|
+
}
|
|
12352
|
+
// Also check descendants — React may insert a parent node
|
|
12353
|
+
// whose children contain TailwindRoot elements
|
|
12354
|
+
node.querySelectorAll(`.${tailwindPrefix}`).forEach(child => {
|
|
12355
|
+
traverse(child, addClasses);
|
|
12356
|
+
shouldUpdate = true;
|
|
12357
|
+
});
|
|
12316
12358
|
});
|
|
12317
12359
|
}
|
|
12318
12360
|
else if (record.type === 'attributes') {
|
|
@@ -12321,10 +12363,10 @@ const TailwindStyle = ({ children, darkModeDataAttribute }) => {
|
|
|
12321
12363
|
addClasses(record.target);
|
|
12322
12364
|
shouldUpdate = true;
|
|
12323
12365
|
}
|
|
12324
|
-
if (shouldUpdate) {
|
|
12325
|
-
setClasses(Array.from(classesSet.current));
|
|
12326
|
-
}
|
|
12327
12366
|
});
|
|
12367
|
+
if (shouldUpdate) {
|
|
12368
|
+
setClasses(Array.from(classesSet.current));
|
|
12369
|
+
}
|
|
12328
12370
|
});
|
|
12329
12371
|
observer.observe(ref.current.parentElement, {
|
|
12330
12372
|
subtree: true,
|
|
@@ -70410,9 +70452,416 @@ const mdast = (text, opts = {}) => {
|
|
|
70410
70452
|
};
|
|
70411
70453
|
/* harmony default export */ const lib_mdast = (mdast);
|
|
70412
70454
|
|
|
70455
|
+
;// ./lib/utils/mdxish/protect-code-blocks.ts
|
|
70456
|
+
/**
|
|
70457
|
+
* Replaces code blocks and inline code with placeholders to protect them from preprocessing.
|
|
70458
|
+
*
|
|
70459
|
+
* @param content - The markdown content to process
|
|
70460
|
+
* @returns Object containing protected content and arrays of original code blocks
|
|
70461
|
+
* @example
|
|
70462
|
+
* ```typescript
|
|
70463
|
+
* const input = 'Text with `inline code` and ```fenced block```';
|
|
70464
|
+
* protectCodeBlocks(input)
|
|
70465
|
+
* // Returns: {
|
|
70466
|
+
* // protectedCode: {
|
|
70467
|
+
* // codeBlocks: ['```fenced block```'],
|
|
70468
|
+
* // inlineCode: ['`inline code`']
|
|
70469
|
+
* // },
|
|
70470
|
+
* // protectedContent: 'Text with ___INLINE_CODE_0___ and ___CODE_BLOCK_0___'
|
|
70471
|
+
* // }
|
|
70472
|
+
* ```
|
|
70473
|
+
*/
|
|
70474
|
+
function protectCodeBlocks(content) {
|
|
70475
|
+
const codeBlocks = [];
|
|
70476
|
+
const inlineCode = [];
|
|
70477
|
+
let protectedContent = '';
|
|
70478
|
+
let remaining = content;
|
|
70479
|
+
let codeBlockStart = remaining.indexOf('```');
|
|
70480
|
+
while (codeBlockStart !== -1) {
|
|
70481
|
+
protectedContent += remaining.slice(0, codeBlockStart);
|
|
70482
|
+
remaining = remaining.slice(codeBlockStart);
|
|
70483
|
+
const codeBlockEnd = remaining.indexOf('```', 3);
|
|
70484
|
+
if (codeBlockEnd === -1) {
|
|
70485
|
+
break;
|
|
70486
|
+
}
|
|
70487
|
+
const match = remaining.slice(0, codeBlockEnd + 3);
|
|
70488
|
+
const index = codeBlocks.length;
|
|
70489
|
+
codeBlocks.push(match);
|
|
70490
|
+
protectedContent += `___CODE_BLOCK_${index}___`;
|
|
70491
|
+
remaining = remaining.slice(codeBlockEnd + 3);
|
|
70492
|
+
codeBlockStart = remaining.indexOf('```');
|
|
70493
|
+
}
|
|
70494
|
+
protectedContent += remaining;
|
|
70495
|
+
protectedContent = protectedContent.replace(/`([^`\n]+)`/g, match => {
|
|
70496
|
+
const index = inlineCode.length;
|
|
70497
|
+
inlineCode.push(match);
|
|
70498
|
+
return `___INLINE_CODE_${index}___`;
|
|
70499
|
+
});
|
|
70500
|
+
return { protectedCode: { codeBlocks, inlineCode }, protectedContent };
|
|
70501
|
+
}
|
|
70502
|
+
/**
|
|
70503
|
+
* Restores inline code by replacing placeholders with original content.
|
|
70504
|
+
*
|
|
70505
|
+
* @param content - Content with inline code placeholders
|
|
70506
|
+
* @param protectedCode - The protected code arrays
|
|
70507
|
+
* @returns Content with inline code restored
|
|
70508
|
+
*/
|
|
70509
|
+
function restoreInlineCode(content, protectedCode) {
|
|
70510
|
+
return content.replace(/___INLINE_CODE_(\d+)___/g, (_m, idx) => {
|
|
70511
|
+
return protectedCode.inlineCode[parseInt(idx, 10)];
|
|
70512
|
+
});
|
|
70513
|
+
}
|
|
70514
|
+
/**
|
|
70515
|
+
* Restores fenced code blocks by replacing placeholders with original content.
|
|
70516
|
+
*
|
|
70517
|
+
* @param content - Content with code block placeholders
|
|
70518
|
+
* @param protectedCode - The protected code arrays
|
|
70519
|
+
* @returns Content with code blocks restored
|
|
70520
|
+
*/
|
|
70521
|
+
function restoreFencedCodeBlocks(content, protectedCode) {
|
|
70522
|
+
return content.replace(/___CODE_BLOCK_(\d+)___/g, (_m, idx) => {
|
|
70523
|
+
return protectedCode.codeBlocks[parseInt(idx, 10)];
|
|
70524
|
+
});
|
|
70525
|
+
}
|
|
70526
|
+
/**
|
|
70527
|
+
* Restores all code blocks and inline code by replacing placeholders with original content.
|
|
70528
|
+
*
|
|
70529
|
+
* @param content - Content with code placeholders
|
|
70530
|
+
* @param protectedCode - The protected code arrays
|
|
70531
|
+
* @returns Content with all code blocks and inline code restored
|
|
70532
|
+
* @example
|
|
70533
|
+
* ```typescript
|
|
70534
|
+
* const content = 'Text with ___INLINE_CODE_0___ and ___CODE_BLOCK_0___';
|
|
70535
|
+
* const protectedCode = {
|
|
70536
|
+
* codeBlocks: ['```js\ncode\n```'],
|
|
70537
|
+
* inlineCode: ['`inline`']
|
|
70538
|
+
* };
|
|
70539
|
+
* restoreCodeBlocks(content, protectedCode)
|
|
70540
|
+
* // Returns: 'Text with `inline` and ```js\ncode\n```'
|
|
70541
|
+
* ```
|
|
70542
|
+
*/
|
|
70543
|
+
function restoreCodeBlocks(content, protectedCode) {
|
|
70544
|
+
let restored = restoreFencedCodeBlocks(content, protectedCode);
|
|
70545
|
+
restored = restoreInlineCode(restored, protectedCode);
|
|
70546
|
+
return restored;
|
|
70547
|
+
}
|
|
70548
|
+
|
|
70549
|
+
;// ./processor/transform/mdxish/preprocess-jsx-expressions.ts
|
|
70550
|
+
|
|
70551
|
+
// Base64 encode (Node.js + browser compatible)
|
|
70552
|
+
function base64Encode(str) {
|
|
70553
|
+
if (typeof Buffer !== 'undefined') {
|
|
70554
|
+
return Buffer.from(str, 'utf-8').toString('base64');
|
|
70555
|
+
}
|
|
70556
|
+
return btoa(unescape(encodeURIComponent(str)));
|
|
70557
|
+
}
|
|
70558
|
+
// Base64 decode (Node.js + browser compatible)
|
|
70559
|
+
function base64Decode(str) {
|
|
70560
|
+
if (typeof Buffer !== 'undefined') {
|
|
70561
|
+
return Buffer.from(str, 'base64').toString('utf-8');
|
|
70562
|
+
}
|
|
70563
|
+
return decodeURIComponent(escape(atob(str)));
|
|
70564
|
+
}
|
|
70565
|
+
function escapeHtmlAttribute(value) {
|
|
70566
|
+
return value
|
|
70567
|
+
.replace(/&/g, '&')
|
|
70568
|
+
.replace(/"/g, '"')
|
|
70569
|
+
.replace(/</g, '<')
|
|
70570
|
+
.replace(/>/g, '>')
|
|
70571
|
+
.replace(/\n/g, ' ');
|
|
70572
|
+
}
|
|
70573
|
+
// Marker prefix for JSON-serialized complex values (arrays/objects)
|
|
70574
|
+
// Using a prefix that won't conflict with regular string values
|
|
70575
|
+
const JSON_VALUE_MARKER = '__MDXISH_JSON__';
|
|
70576
|
+
// Markers for protected HTMLBlock content (HTML comments avoid markdown parsing issues)
|
|
70577
|
+
const HTML_BLOCK_CONTENT_START = '<!--RDMX_HTMLBLOCK:';
|
|
70578
|
+
const HTML_BLOCK_CONTENT_END = ':RDMX_HTMLBLOCK-->';
|
|
70579
|
+
/**
|
|
70580
|
+
* Evaluates a JavaScript expression using context variables.
|
|
70581
|
+
*
|
|
70582
|
+
* @param expression
|
|
70583
|
+
* @param context
|
|
70584
|
+
* @returns The evaluated result
|
|
70585
|
+
* @example
|
|
70586
|
+
* ```typescript
|
|
70587
|
+
* const context = { baseUrl: 'https://example.com', path: '/api' };
|
|
70588
|
+
* evaluateExpression('baseUrl + path', context)
|
|
70589
|
+
* // Returns: 'https://example.com/api'
|
|
70590
|
+
* ```
|
|
70591
|
+
*/
|
|
70592
|
+
function evaluateExpression(expression, context) {
|
|
70593
|
+
const contextKeys = Object.keys(context);
|
|
70594
|
+
const contextValues = Object.values(context);
|
|
70595
|
+
// eslint-disable-next-line no-new-func
|
|
70596
|
+
const func = new Function(...contextKeys, `return ${expression}`);
|
|
70597
|
+
return func(...contextValues);
|
|
70598
|
+
}
|
|
70599
|
+
/**
|
|
70600
|
+
* Base64 encodes HTMLBlock template literal content to prevent markdown parser from consuming <script>/<style> tags.
|
|
70601
|
+
*
|
|
70602
|
+
* @param content
|
|
70603
|
+
* @returns Content with HTMLBlock template literals base64 encoded in HTML comments
|
|
70604
|
+
* @example
|
|
70605
|
+
* ```typescript
|
|
70606
|
+
* const input = '<HTMLBlock>{`<script>alert("xss")</script>`}</HTMLBlock>';
|
|
70607
|
+
* protectHTMLBlockContent(input)
|
|
70608
|
+
* // Returns: '<HTMLBlock><!--RDMX_HTMLBLOCK:PHNjcmlwdD5hbGVydCgieHNzIik8L3NjcmlwdD4=:RDMX_HTMLBLOCK--></HTMLBlock>'
|
|
70609
|
+
* ```
|
|
70610
|
+
*/
|
|
70611
|
+
function protectHTMLBlockContent(content) {
|
|
70612
|
+
return content.replace(/(<HTMLBlock[^>]*>)\{\s*`((?:[^`\\]|\\.)*)`\s*\}(<\/HTMLBlock>)/g, (_match, openTag, templateContent, closeTag) => {
|
|
70613
|
+
const encoded = base64Encode(templateContent);
|
|
70614
|
+
return `${openTag}${HTML_BLOCK_CONTENT_START}${encoded}${HTML_BLOCK_CONTENT_END}${closeTag}`;
|
|
70615
|
+
});
|
|
70616
|
+
}
|
|
70617
|
+
/**
|
|
70618
|
+
* Removes JSX-style comments (e.g., { /* comment *\/ }) from content.
|
|
70619
|
+
*
|
|
70620
|
+
* @param content
|
|
70621
|
+
* @returns Content with JSX comments removed
|
|
70622
|
+
* @example
|
|
70623
|
+
* ```typescript
|
|
70624
|
+
* removeJSXComments('Text { /* comment *\/ } more text')
|
|
70625
|
+
* // Returns: 'Text more text'
|
|
70626
|
+
* ```
|
|
70627
|
+
*/
|
|
70628
|
+
function removeJSXComments(content) {
|
|
70629
|
+
return content.replace(/\{\s*\/\*[^*]*(?:\*(?!\/)[^*]*)*\*\/\s*\}/g, '');
|
|
70630
|
+
}
|
|
70631
|
+
/**
|
|
70632
|
+
* Extracts content between balanced braces, handling nested braces.
|
|
70633
|
+
*
|
|
70634
|
+
* @param content
|
|
70635
|
+
* @param start
|
|
70636
|
+
* @returns Object with extracted content and end position, or null if braces are unbalanced
|
|
70637
|
+
* @example
|
|
70638
|
+
* ```typescript
|
|
70639
|
+
* const input = 'foo{bar{baz}qux}end';
|
|
70640
|
+
* extractBalancedBraces(input, 3) // start at position 3 (after '{')
|
|
70641
|
+
* // Returns: { content: 'bar{baz}qux', end: 16 }
|
|
70642
|
+
* ```
|
|
70643
|
+
*/
|
|
70644
|
+
function extractBalancedBraces(content, start) {
|
|
70645
|
+
let depth = 1;
|
|
70646
|
+
let pos = start;
|
|
70647
|
+
while (pos < content.length && depth > 0) {
|
|
70648
|
+
const char = content[pos];
|
|
70649
|
+
if (char === '{')
|
|
70650
|
+
depth += 1;
|
|
70651
|
+
else if (char === '}')
|
|
70652
|
+
depth -= 1;
|
|
70653
|
+
pos += 1;
|
|
70654
|
+
}
|
|
70655
|
+
if (depth !== 0)
|
|
70656
|
+
return null;
|
|
70657
|
+
return { content: content.slice(start, pos - 1), end: pos };
|
|
70658
|
+
}
|
|
70659
|
+
/**
|
|
70660
|
+
* Escapes problematic braces in content to prevent MDX expression parsing errors.
|
|
70661
|
+
* Handles three cases:
|
|
70662
|
+
* 1. Unbalanced braces (e.g., `{foo` without closing `}`)
|
|
70663
|
+
* 2. Paragraph-spanning expressions (e.g., `{\n\n}` where blank line splits paragraphs)
|
|
70664
|
+
* 3. Skips HTML elements to prevent backslashes appearing in output
|
|
70665
|
+
*
|
|
70666
|
+
*/
|
|
70667
|
+
function escapeProblematicBraces(content) {
|
|
70668
|
+
// Skip HTML elements — their content should never be escaped because
|
|
70669
|
+
// rehypeRaw parses them into hast elements, making `\` literal text in output
|
|
70670
|
+
const htmlElements = [];
|
|
70671
|
+
const safe = content.replace(/<([a-z][a-zA-Z0-9]*)(?:\s[^>]*)?>[\s\S]*?<\/\1>/g, match => {
|
|
70672
|
+
const idx = htmlElements.length;
|
|
70673
|
+
htmlElements.push(match);
|
|
70674
|
+
return `___HTML_ELEM_${idx}___`;
|
|
70675
|
+
});
|
|
70676
|
+
const toEscape = new Set();
|
|
70677
|
+
// Convert to array of Unicode code points to handle emojis and multi-byte characters correctly
|
|
70678
|
+
const chars = Array.from(safe);
|
|
70679
|
+
let strDelim = null;
|
|
70680
|
+
let strEscaped = false;
|
|
70681
|
+
// Stack of open braces with their state
|
|
70682
|
+
const openStack = [];
|
|
70683
|
+
// Track position of last newline (outside strings) to detect blank lines
|
|
70684
|
+
let lastNewlinePos = -2; // -2 means no recent newline
|
|
70685
|
+
for (let i = 0; i < chars.length; i += 1) {
|
|
70686
|
+
const ch = chars[i];
|
|
70687
|
+
// Track string delimiters inside expressions to ignore braces within them
|
|
70688
|
+
if (openStack.length > 0) {
|
|
70689
|
+
if (strDelim) {
|
|
70690
|
+
if (strEscaped)
|
|
70691
|
+
strEscaped = false;
|
|
70692
|
+
else if (ch === '\\')
|
|
70693
|
+
strEscaped = true;
|
|
70694
|
+
else if (ch === strDelim)
|
|
70695
|
+
strDelim = null;
|
|
70696
|
+
// eslint-disable-next-line no-continue
|
|
70697
|
+
continue;
|
|
70698
|
+
}
|
|
70699
|
+
if (ch === '"' || ch === "'" || ch === '`') {
|
|
70700
|
+
strDelim = ch;
|
|
70701
|
+
// eslint-disable-next-line no-continue
|
|
70702
|
+
continue;
|
|
70703
|
+
}
|
|
70704
|
+
// Track newlines to detect blank lines (paragraph boundaries)
|
|
70705
|
+
if (ch === '\n') {
|
|
70706
|
+
// Check if this newline creates a blank line (only whitespace since last newline)
|
|
70707
|
+
if (lastNewlinePos >= 0) {
|
|
70708
|
+
const between = chars.slice(lastNewlinePos + 1, i).join('');
|
|
70709
|
+
if (/^[ \t]*$/.test(between)) {
|
|
70710
|
+
// This is a blank line - mark all open expressions as paragraph-spanning
|
|
70711
|
+
openStack.forEach(entry => {
|
|
70712
|
+
entry.hasBlankLine = true;
|
|
70713
|
+
});
|
|
70714
|
+
}
|
|
70715
|
+
}
|
|
70716
|
+
lastNewlinePos = i;
|
|
70717
|
+
}
|
|
70718
|
+
}
|
|
70719
|
+
// Skip already-escaped braces (count preceding backslashes)
|
|
70720
|
+
if (ch === '{' || ch === '}') {
|
|
70721
|
+
let bs = 0;
|
|
70722
|
+
for (let j = i - 1; j >= 0 && chars[j] === '\\'; j -= 1)
|
|
70723
|
+
bs += 1;
|
|
70724
|
+
if (bs % 2 === 1) {
|
|
70725
|
+
// eslint-disable-next-line no-continue
|
|
70726
|
+
continue;
|
|
70727
|
+
}
|
|
70728
|
+
}
|
|
70729
|
+
if (ch === '{') {
|
|
70730
|
+
openStack.push({ pos: i, hasBlankLine: false });
|
|
70731
|
+
lastNewlinePos = -2; // Reset newline tracking for new expression
|
|
70732
|
+
}
|
|
70733
|
+
else if (ch === '}') {
|
|
70734
|
+
if (openStack.length > 0) {
|
|
70735
|
+
const entry = openStack.pop();
|
|
70736
|
+
// If expression spans paragraph boundary, escape both braces
|
|
70737
|
+
if (entry.hasBlankLine) {
|
|
70738
|
+
toEscape.add(entry.pos);
|
|
70739
|
+
toEscape.add(i);
|
|
70740
|
+
}
|
|
70741
|
+
}
|
|
70742
|
+
else {
|
|
70743
|
+
// Unbalanced closing brace (no matching open)
|
|
70744
|
+
toEscape.add(i);
|
|
70745
|
+
}
|
|
70746
|
+
}
|
|
70747
|
+
}
|
|
70748
|
+
// Any remaining open braces are unbalanced
|
|
70749
|
+
openStack.forEach(entry => toEscape.add(entry.pos));
|
|
70750
|
+
// If there are no problematic braces, return safe content as-is;
|
|
70751
|
+
// otherwise, escape each problematic `{` or `}` so MDX doesn't treat them as expressions.
|
|
70752
|
+
let result = toEscape.size === 0
|
|
70753
|
+
? safe
|
|
70754
|
+
: chars.map((ch, i) => (toEscape.has(i) ? `\\${ch}` : ch)).join('');
|
|
70755
|
+
// Restore HTML elements
|
|
70756
|
+
if (htmlElements.length > 0) {
|
|
70757
|
+
result = result.replace(/___HTML_ELEM_(\d+)___/g, (_m, idx) => htmlElements[parseInt(idx, 10)]);
|
|
70758
|
+
}
|
|
70759
|
+
return result;
|
|
70760
|
+
}
|
|
70761
|
+
/**
|
|
70762
|
+
* Converts JSX attribute expressions (attribute={expression}) to HTML attributes (attribute="value").
|
|
70763
|
+
* Handles style objects (camelCase → kebab-case), className → class, and JSON stringifies objects.
|
|
70764
|
+
*
|
|
70765
|
+
* @param content
|
|
70766
|
+
* @param context
|
|
70767
|
+
* @returns Content with attribute expressions evaluated and converted to HTML attributes
|
|
70768
|
+
* @example
|
|
70769
|
+
* ```typescript
|
|
70770
|
+
* const context = { baseUrl: 'https://example.com' };
|
|
70771
|
+
* const input = '<a href={baseUrl}>Link</a>';
|
|
70772
|
+
* evaluateAttributeExpressions(input, context)
|
|
70773
|
+
* // Returns: '<a href="https://example.com">Link</a>'
|
|
70774
|
+
* ```
|
|
70775
|
+
*/
|
|
70776
|
+
function evaluateAttributeExpressions(content, context, protectedCode) {
|
|
70777
|
+
const attrStartRegex = /(\w+)=\{/g;
|
|
70778
|
+
let result = '';
|
|
70779
|
+
let lastEnd = 0;
|
|
70780
|
+
let match = attrStartRegex.exec(content);
|
|
70781
|
+
while (match !== null) {
|
|
70782
|
+
const attributeName = match[1];
|
|
70783
|
+
const braceStart = match.index + match[0].length;
|
|
70784
|
+
const extracted = extractBalancedBraces(content, braceStart);
|
|
70785
|
+
if (extracted) {
|
|
70786
|
+
// The expression might contain template literals in MDX component tag props
|
|
70787
|
+
// E.g. <Component greeting={`Hello World!`} />
|
|
70788
|
+
// that is marked as inline code. So we need to restore the inline codes
|
|
70789
|
+
// in the expression to evaluate it
|
|
70790
|
+
let expression = extracted.content;
|
|
70791
|
+
if (protectedCode) {
|
|
70792
|
+
expression = restoreInlineCode(expression, protectedCode);
|
|
70793
|
+
}
|
|
70794
|
+
const fullMatchEnd = extracted.end;
|
|
70795
|
+
result += content.slice(lastEnd, match.index);
|
|
70796
|
+
try {
|
|
70797
|
+
const evalResult = evaluateExpression(expression, context);
|
|
70798
|
+
if (typeof evalResult === 'object' && evalResult !== null) {
|
|
70799
|
+
if (attributeName === 'style') {
|
|
70800
|
+
const cssString = Object.entries(evalResult)
|
|
70801
|
+
.map(([key, value]) => {
|
|
70802
|
+
const cssKey = key.replace(/([A-Z])/g, '-$1').toLowerCase();
|
|
70803
|
+
return `${cssKey}: ${value}`;
|
|
70804
|
+
})
|
|
70805
|
+
.join('; ');
|
|
70806
|
+
result += `style="${cssString}"`;
|
|
70807
|
+
}
|
|
70808
|
+
else {
|
|
70809
|
+
// These are arrays / objects attribute values
|
|
70810
|
+
// Mark JSON-serialized values with a prefix so they can be parsed back correctly
|
|
70811
|
+
const jsonValue = escapeHtmlAttribute(JSON_VALUE_MARKER + JSON.stringify(evalResult));
|
|
70812
|
+
// Use double quotes so that multi-paragraph values are not split into multiple attributes by the processors
|
|
70813
|
+
result += `${attributeName}="${jsonValue}"`;
|
|
70814
|
+
}
|
|
70815
|
+
}
|
|
70816
|
+
else if (attributeName === 'className') {
|
|
70817
|
+
// Escape special characters so that it doesn't break and split the attribute value to nodes
|
|
70818
|
+
// This will be restored later in the pipeline
|
|
70819
|
+
result += `class="${escapeHtmlAttribute(String(evalResult))}"`;
|
|
70820
|
+
}
|
|
70821
|
+
else {
|
|
70822
|
+
result += `${attributeName}="${escapeHtmlAttribute(String(evalResult))}"`;
|
|
70823
|
+
}
|
|
70824
|
+
}
|
|
70825
|
+
catch (_error) {
|
|
70826
|
+
result += content.slice(match.index, fullMatchEnd);
|
|
70827
|
+
}
|
|
70828
|
+
lastEnd = fullMatchEnd;
|
|
70829
|
+
attrStartRegex.lastIndex = fullMatchEnd;
|
|
70830
|
+
}
|
|
70831
|
+
match = attrStartRegex.exec(content);
|
|
70832
|
+
}
|
|
70833
|
+
result += content.slice(lastEnd);
|
|
70834
|
+
return result;
|
|
70835
|
+
}
|
|
70836
|
+
/**
|
|
70837
|
+
* Preprocesses JSX-like expressions in markdown before parsing.
|
|
70838
|
+
* Inline expressions are handled separately; attribute expressions are processed here.
|
|
70839
|
+
*
|
|
70840
|
+
* @param content
|
|
70841
|
+
* @param context
|
|
70842
|
+
* @returns Preprocessed content ready for markdown parsing
|
|
70843
|
+
*/
|
|
70844
|
+
function preprocessJSXExpressions(content, context = {}) {
|
|
70845
|
+
// Step 0: Base64 encode HTMLBlock content
|
|
70846
|
+
let processed = protectHTMLBlockContent(content);
|
|
70847
|
+
// Step 1: Protect code blocks and inline code
|
|
70848
|
+
const { protectedCode, protectedContent } = protectCodeBlocks(processed);
|
|
70849
|
+
// Step 2: Evaluate attribute expressions (JSX attribute syntax: href={baseUrl})
|
|
70850
|
+
// For inline expressions, we use a library to parse the expression & evaluate it later
|
|
70851
|
+
// For attribute expressions, it was difficult to use a library to parse them, so do it manually
|
|
70852
|
+
processed = evaluateAttributeExpressions(protectedContent, context, protectedCode);
|
|
70853
|
+
// Step 3: Escape problematic braces to prevent MDX expression parsing errors
|
|
70854
|
+
// This handles both unbalanced braces and paragraph-spanning expressions in one pass
|
|
70855
|
+
processed = escapeProblematicBraces(processed);
|
|
70856
|
+
// Step 4: Restore protected code blocks
|
|
70857
|
+
processed = restoreCodeBlocks(processed, protectedCode);
|
|
70858
|
+
return processed;
|
|
70859
|
+
}
|
|
70860
|
+
|
|
70413
70861
|
;// ./processor/utils.ts
|
|
70414
70862
|
|
|
70415
70863
|
|
|
70864
|
+
|
|
70416
70865
|
/**
|
|
70417
70866
|
* Formats the hProperties of a node as a string, so they can be compiled back into JSX/MDX.
|
|
70418
70867
|
* This currently sets all the values to a string since we process/compile the MDX on the fly
|
|
@@ -70468,7 +70917,17 @@ const getHPropKeys = (node) => {
|
|
|
70468
70917
|
const getAttrs = (jsx) => jsx.attributes.reduce((memo, attr) => {
|
|
70469
70918
|
if ('name' in attr) {
|
|
70470
70919
|
if (typeof attr.value === 'string') {
|
|
70471
|
-
|
|
70920
|
+
if (attr.value.startsWith(JSON_VALUE_MARKER)) {
|
|
70921
|
+
try {
|
|
70922
|
+
memo[attr.name] = JSON.parse(attr.value.slice(JSON_VALUE_MARKER.length));
|
|
70923
|
+
}
|
|
70924
|
+
catch {
|
|
70925
|
+
memo[attr.name] = attr.value;
|
|
70926
|
+
}
|
|
70927
|
+
}
|
|
70928
|
+
else {
|
|
70929
|
+
memo[attr.name] = attr.value;
|
|
70930
|
+
}
|
|
70472
70931
|
}
|
|
70473
70932
|
else if (attr.value === null) {
|
|
70474
70933
|
memo[attr.name] = true;
|
|
@@ -71242,10 +71701,10 @@ const hasFlowContent = (nodes) => {
|
|
|
71242
71701
|
* Process a Table node: re-parse text-only cell content, then output as
|
|
71243
71702
|
* a markdown table (phrasing-only) or keep as JSX <Table> (has flow content).
|
|
71244
71703
|
*/
|
|
71245
|
-
const processTableNode = (node, index, parent) => {
|
|
71704
|
+
const processTableNode = (node, index, parent, documentPosition) => {
|
|
71246
71705
|
if (node.name !== 'Table')
|
|
71247
71706
|
return;
|
|
71248
|
-
const
|
|
71707
|
+
const position = documentPosition ?? node.position;
|
|
71249
71708
|
const { align: alignAttr } = getAttrs(node);
|
|
71250
71709
|
const align = Array.isArray(alignAttr) ? alignAttr : null;
|
|
71251
71710
|
let tableHasFlowContent = false;
|
|
@@ -71368,7 +71827,7 @@ const mdxishTables = () => tree => {
|
|
|
71368
71827
|
const parsed = tableNodeProcessor.runSync(tableNodeProcessor.parse(node.value));
|
|
71369
71828
|
visit(parsed, isMDXElement, (tableNode) => {
|
|
71370
71829
|
if (tableNode.name === 'Table') {
|
|
71371
|
-
processTableNode(tableNode, index, parent);
|
|
71830
|
+
processTableNode(tableNode, index, parent, node.position);
|
|
71372
71831
|
// Stop after the outermost Table so nested Tables don't overwrite parent.children[index]
|
|
71373
71832
|
// we let it get handled naturally
|
|
71374
71833
|
return EXIT;
|
|
@@ -93843,412 +94302,6 @@ const mdxComponentHandlers = {
|
|
|
93843
94302
|
[NodeTypes.htmlBlock]: htmlBlockHandler,
|
|
93844
94303
|
};
|
|
93845
94304
|
|
|
93846
|
-
;// ./lib/utils/mdxish/protect-code-blocks.ts
|
|
93847
|
-
/**
|
|
93848
|
-
* Replaces code blocks and inline code with placeholders to protect them from preprocessing.
|
|
93849
|
-
*
|
|
93850
|
-
* @param content - The markdown content to process
|
|
93851
|
-
* @returns Object containing protected content and arrays of original code blocks
|
|
93852
|
-
* @example
|
|
93853
|
-
* ```typescript
|
|
93854
|
-
* const input = 'Text with `inline code` and ```fenced block```';
|
|
93855
|
-
* protectCodeBlocks(input)
|
|
93856
|
-
* // Returns: {
|
|
93857
|
-
* // protectedCode: {
|
|
93858
|
-
* // codeBlocks: ['```fenced block```'],
|
|
93859
|
-
* // inlineCode: ['`inline code`']
|
|
93860
|
-
* // },
|
|
93861
|
-
* // protectedContent: 'Text with ___INLINE_CODE_0___ and ___CODE_BLOCK_0___'
|
|
93862
|
-
* // }
|
|
93863
|
-
* ```
|
|
93864
|
-
*/
|
|
93865
|
-
function protectCodeBlocks(content) {
|
|
93866
|
-
const codeBlocks = [];
|
|
93867
|
-
const inlineCode = [];
|
|
93868
|
-
let protectedContent = '';
|
|
93869
|
-
let remaining = content;
|
|
93870
|
-
let codeBlockStart = remaining.indexOf('```');
|
|
93871
|
-
while (codeBlockStart !== -1) {
|
|
93872
|
-
protectedContent += remaining.slice(0, codeBlockStart);
|
|
93873
|
-
remaining = remaining.slice(codeBlockStart);
|
|
93874
|
-
const codeBlockEnd = remaining.indexOf('```', 3);
|
|
93875
|
-
if (codeBlockEnd === -1) {
|
|
93876
|
-
break;
|
|
93877
|
-
}
|
|
93878
|
-
const match = remaining.slice(0, codeBlockEnd + 3);
|
|
93879
|
-
const index = codeBlocks.length;
|
|
93880
|
-
codeBlocks.push(match);
|
|
93881
|
-
protectedContent += `___CODE_BLOCK_${index}___`;
|
|
93882
|
-
remaining = remaining.slice(codeBlockEnd + 3);
|
|
93883
|
-
codeBlockStart = remaining.indexOf('```');
|
|
93884
|
-
}
|
|
93885
|
-
protectedContent += remaining;
|
|
93886
|
-
protectedContent = protectedContent.replace(/`([^`\n]+)`/g, match => {
|
|
93887
|
-
const index = inlineCode.length;
|
|
93888
|
-
inlineCode.push(match);
|
|
93889
|
-
return `___INLINE_CODE_${index}___`;
|
|
93890
|
-
});
|
|
93891
|
-
return { protectedCode: { codeBlocks, inlineCode }, protectedContent };
|
|
93892
|
-
}
|
|
93893
|
-
/**
|
|
93894
|
-
* Restores inline code by replacing placeholders with original content.
|
|
93895
|
-
*
|
|
93896
|
-
* @param content - Content with inline code placeholders
|
|
93897
|
-
* @param protectedCode - The protected code arrays
|
|
93898
|
-
* @returns Content with inline code restored
|
|
93899
|
-
*/
|
|
93900
|
-
function restoreInlineCode(content, protectedCode) {
|
|
93901
|
-
return content.replace(/___INLINE_CODE_(\d+)___/g, (_m, idx) => {
|
|
93902
|
-
return protectedCode.inlineCode[parseInt(idx, 10)];
|
|
93903
|
-
});
|
|
93904
|
-
}
|
|
93905
|
-
/**
|
|
93906
|
-
* Restores fenced code blocks by replacing placeholders with original content.
|
|
93907
|
-
*
|
|
93908
|
-
* @param content - Content with code block placeholders
|
|
93909
|
-
* @param protectedCode - The protected code arrays
|
|
93910
|
-
* @returns Content with code blocks restored
|
|
93911
|
-
*/
|
|
93912
|
-
function restoreFencedCodeBlocks(content, protectedCode) {
|
|
93913
|
-
return content.replace(/___CODE_BLOCK_(\d+)___/g, (_m, idx) => {
|
|
93914
|
-
return protectedCode.codeBlocks[parseInt(idx, 10)];
|
|
93915
|
-
});
|
|
93916
|
-
}
|
|
93917
|
-
/**
|
|
93918
|
-
* Restores all code blocks and inline code by replacing placeholders with original content.
|
|
93919
|
-
*
|
|
93920
|
-
* @param content - Content with code placeholders
|
|
93921
|
-
* @param protectedCode - The protected code arrays
|
|
93922
|
-
* @returns Content with all code blocks and inline code restored
|
|
93923
|
-
* @example
|
|
93924
|
-
* ```typescript
|
|
93925
|
-
* const content = 'Text with ___INLINE_CODE_0___ and ___CODE_BLOCK_0___';
|
|
93926
|
-
* const protectedCode = {
|
|
93927
|
-
* codeBlocks: ['```js\ncode\n```'],
|
|
93928
|
-
* inlineCode: ['`inline`']
|
|
93929
|
-
* };
|
|
93930
|
-
* restoreCodeBlocks(content, protectedCode)
|
|
93931
|
-
* // Returns: 'Text with `inline` and ```js\ncode\n```'
|
|
93932
|
-
* ```
|
|
93933
|
-
*/
|
|
93934
|
-
function restoreCodeBlocks(content, protectedCode) {
|
|
93935
|
-
let restored = restoreFencedCodeBlocks(content, protectedCode);
|
|
93936
|
-
restored = restoreInlineCode(restored, protectedCode);
|
|
93937
|
-
return restored;
|
|
93938
|
-
}
|
|
93939
|
-
|
|
93940
|
-
;// ./processor/transform/mdxish/preprocess-jsx-expressions.ts
|
|
93941
|
-
|
|
93942
|
-
// Base64 encode (Node.js + browser compatible)
|
|
93943
|
-
function base64Encode(str) {
|
|
93944
|
-
if (typeof Buffer !== 'undefined') {
|
|
93945
|
-
return Buffer.from(str, 'utf-8').toString('base64');
|
|
93946
|
-
}
|
|
93947
|
-
return btoa(unescape(encodeURIComponent(str)));
|
|
93948
|
-
}
|
|
93949
|
-
// Base64 decode (Node.js + browser compatible)
|
|
93950
|
-
function base64Decode(str) {
|
|
93951
|
-
if (typeof Buffer !== 'undefined') {
|
|
93952
|
-
return Buffer.from(str, 'base64').toString('utf-8');
|
|
93953
|
-
}
|
|
93954
|
-
return decodeURIComponent(escape(atob(str)));
|
|
93955
|
-
}
|
|
93956
|
-
function escapeHtmlAttribute(value) {
|
|
93957
|
-
return value
|
|
93958
|
-
.replace(/&/g, '&')
|
|
93959
|
-
.replace(/"/g, '"')
|
|
93960
|
-
.replace(/</g, '<')
|
|
93961
|
-
.replace(/>/g, '>')
|
|
93962
|
-
.replace(/\n/g, ' ');
|
|
93963
|
-
}
|
|
93964
|
-
// Marker prefix for JSON-serialized complex values (arrays/objects)
|
|
93965
|
-
// Using a prefix that won't conflict with regular string values
|
|
93966
|
-
const JSON_VALUE_MARKER = '__MDXISH_JSON__';
|
|
93967
|
-
// Markers for protected HTMLBlock content (HTML comments avoid markdown parsing issues)
|
|
93968
|
-
const HTML_BLOCK_CONTENT_START = '<!--RDMX_HTMLBLOCK:';
|
|
93969
|
-
const HTML_BLOCK_CONTENT_END = ':RDMX_HTMLBLOCK-->';
|
|
93970
|
-
/**
|
|
93971
|
-
* Evaluates a JavaScript expression using context variables.
|
|
93972
|
-
*
|
|
93973
|
-
* @param expression
|
|
93974
|
-
* @param context
|
|
93975
|
-
* @returns The evaluated result
|
|
93976
|
-
* @example
|
|
93977
|
-
* ```typescript
|
|
93978
|
-
* const context = { baseUrl: 'https://example.com', path: '/api' };
|
|
93979
|
-
* evaluateExpression('baseUrl + path', context)
|
|
93980
|
-
* // Returns: 'https://example.com/api'
|
|
93981
|
-
* ```
|
|
93982
|
-
*/
|
|
93983
|
-
function evaluateExpression(expression, context) {
|
|
93984
|
-
const contextKeys = Object.keys(context);
|
|
93985
|
-
const contextValues = Object.values(context);
|
|
93986
|
-
// eslint-disable-next-line no-new-func
|
|
93987
|
-
const func = new Function(...contextKeys, `return ${expression}`);
|
|
93988
|
-
return func(...contextValues);
|
|
93989
|
-
}
|
|
93990
|
-
/**
|
|
93991
|
-
* Base64 encodes HTMLBlock template literal content to prevent markdown parser from consuming <script>/<style> tags.
|
|
93992
|
-
*
|
|
93993
|
-
* @param content
|
|
93994
|
-
* @returns Content with HTMLBlock template literals base64 encoded in HTML comments
|
|
93995
|
-
* @example
|
|
93996
|
-
* ```typescript
|
|
93997
|
-
* const input = '<HTMLBlock>{`<script>alert("xss")</script>`}</HTMLBlock>';
|
|
93998
|
-
* protectHTMLBlockContent(input)
|
|
93999
|
-
* // Returns: '<HTMLBlock><!--RDMX_HTMLBLOCK:PHNjcmlwdD5hbGVydCgieHNzIik8L3NjcmlwdD4=:RDMX_HTMLBLOCK--></HTMLBlock>'
|
|
94000
|
-
* ```
|
|
94001
|
-
*/
|
|
94002
|
-
function protectHTMLBlockContent(content) {
|
|
94003
|
-
return content.replace(/(<HTMLBlock[^>]*>)\{\s*`((?:[^`\\]|\\.)*)`\s*\}(<\/HTMLBlock>)/g, (_match, openTag, templateContent, closeTag) => {
|
|
94004
|
-
const encoded = base64Encode(templateContent);
|
|
94005
|
-
return `${openTag}${HTML_BLOCK_CONTENT_START}${encoded}${HTML_BLOCK_CONTENT_END}${closeTag}`;
|
|
94006
|
-
});
|
|
94007
|
-
}
|
|
94008
|
-
/**
|
|
94009
|
-
* Removes JSX-style comments (e.g., { /* comment *\/ }) from content.
|
|
94010
|
-
*
|
|
94011
|
-
* @param content
|
|
94012
|
-
* @returns Content with JSX comments removed
|
|
94013
|
-
* @example
|
|
94014
|
-
* ```typescript
|
|
94015
|
-
* removeJSXComments('Text { /* comment *\/ } more text')
|
|
94016
|
-
* // Returns: 'Text more text'
|
|
94017
|
-
* ```
|
|
94018
|
-
*/
|
|
94019
|
-
function removeJSXComments(content) {
|
|
94020
|
-
return content.replace(/\{\s*\/\*[^*]*(?:\*(?!\/)[^*]*)*\*\/\s*\}/g, '');
|
|
94021
|
-
}
|
|
94022
|
-
/**
|
|
94023
|
-
* Extracts content between balanced braces, handling nested braces.
|
|
94024
|
-
*
|
|
94025
|
-
* @param content
|
|
94026
|
-
* @param start
|
|
94027
|
-
* @returns Object with extracted content and end position, or null if braces are unbalanced
|
|
94028
|
-
* @example
|
|
94029
|
-
* ```typescript
|
|
94030
|
-
* const input = 'foo{bar{baz}qux}end';
|
|
94031
|
-
* extractBalancedBraces(input, 3) // start at position 3 (after '{')
|
|
94032
|
-
* // Returns: { content: 'bar{baz}qux', end: 16 }
|
|
94033
|
-
* ```
|
|
94034
|
-
*/
|
|
94035
|
-
function extractBalancedBraces(content, start) {
|
|
94036
|
-
let depth = 1;
|
|
94037
|
-
let pos = start;
|
|
94038
|
-
while (pos < content.length && depth > 0) {
|
|
94039
|
-
const char = content[pos];
|
|
94040
|
-
if (char === '{')
|
|
94041
|
-
depth += 1;
|
|
94042
|
-
else if (char === '}')
|
|
94043
|
-
depth -= 1;
|
|
94044
|
-
pos += 1;
|
|
94045
|
-
}
|
|
94046
|
-
if (depth !== 0)
|
|
94047
|
-
return null;
|
|
94048
|
-
return { content: content.slice(start, pos - 1), end: pos };
|
|
94049
|
-
}
|
|
94050
|
-
/**
|
|
94051
|
-
* Escapes problematic braces in content to prevent MDX expression parsing errors.
|
|
94052
|
-
* Handles three cases:
|
|
94053
|
-
* 1. Unbalanced braces (e.g., `{foo` without closing `}`)
|
|
94054
|
-
* 2. Paragraph-spanning expressions (e.g., `{\n\n}` where blank line splits paragraphs)
|
|
94055
|
-
* 3. Skips HTML elements to prevent backslashes appearing in output
|
|
94056
|
-
*
|
|
94057
|
-
*/
|
|
94058
|
-
function escapeProblematicBraces(content) {
|
|
94059
|
-
// Skip HTML elements — their content should never be escaped because
|
|
94060
|
-
// rehypeRaw parses them into hast elements, making `\` literal text in output
|
|
94061
|
-
const htmlElements = [];
|
|
94062
|
-
const safe = content.replace(/<([a-z][a-zA-Z0-9]*)(?:\s[^>]*)?>[\s\S]*?<\/\1>/g, match => {
|
|
94063
|
-
const idx = htmlElements.length;
|
|
94064
|
-
htmlElements.push(match);
|
|
94065
|
-
return `___HTML_ELEM_${idx}___`;
|
|
94066
|
-
});
|
|
94067
|
-
const toEscape = new Set();
|
|
94068
|
-
// Convert to array of Unicode code points to handle emojis and multi-byte characters correctly
|
|
94069
|
-
const chars = Array.from(safe);
|
|
94070
|
-
let strDelim = null;
|
|
94071
|
-
let strEscaped = false;
|
|
94072
|
-
// Stack of open braces with their state
|
|
94073
|
-
const openStack = [];
|
|
94074
|
-
// Track position of last newline (outside strings) to detect blank lines
|
|
94075
|
-
let lastNewlinePos = -2; // -2 means no recent newline
|
|
94076
|
-
for (let i = 0; i < chars.length; i += 1) {
|
|
94077
|
-
const ch = chars[i];
|
|
94078
|
-
// Track string delimiters inside expressions to ignore braces within them
|
|
94079
|
-
if (openStack.length > 0) {
|
|
94080
|
-
if (strDelim) {
|
|
94081
|
-
if (strEscaped)
|
|
94082
|
-
strEscaped = false;
|
|
94083
|
-
else if (ch === '\\')
|
|
94084
|
-
strEscaped = true;
|
|
94085
|
-
else if (ch === strDelim)
|
|
94086
|
-
strDelim = null;
|
|
94087
|
-
// eslint-disable-next-line no-continue
|
|
94088
|
-
continue;
|
|
94089
|
-
}
|
|
94090
|
-
if (ch === '"' || ch === "'" || ch === '`') {
|
|
94091
|
-
strDelim = ch;
|
|
94092
|
-
// eslint-disable-next-line no-continue
|
|
94093
|
-
continue;
|
|
94094
|
-
}
|
|
94095
|
-
// Track newlines to detect blank lines (paragraph boundaries)
|
|
94096
|
-
if (ch === '\n') {
|
|
94097
|
-
// Check if this newline creates a blank line (only whitespace since last newline)
|
|
94098
|
-
if (lastNewlinePos >= 0) {
|
|
94099
|
-
const between = chars.slice(lastNewlinePos + 1, i).join('');
|
|
94100
|
-
if (/^[ \t]*$/.test(between)) {
|
|
94101
|
-
// This is a blank line - mark all open expressions as paragraph-spanning
|
|
94102
|
-
openStack.forEach(entry => {
|
|
94103
|
-
entry.hasBlankLine = true;
|
|
94104
|
-
});
|
|
94105
|
-
}
|
|
94106
|
-
}
|
|
94107
|
-
lastNewlinePos = i;
|
|
94108
|
-
}
|
|
94109
|
-
}
|
|
94110
|
-
// Skip already-escaped braces (count preceding backslashes)
|
|
94111
|
-
if (ch === '{' || ch === '}') {
|
|
94112
|
-
let bs = 0;
|
|
94113
|
-
for (let j = i - 1; j >= 0 && chars[j] === '\\'; j -= 1)
|
|
94114
|
-
bs += 1;
|
|
94115
|
-
if (bs % 2 === 1) {
|
|
94116
|
-
// eslint-disable-next-line no-continue
|
|
94117
|
-
continue;
|
|
94118
|
-
}
|
|
94119
|
-
}
|
|
94120
|
-
if (ch === '{') {
|
|
94121
|
-
openStack.push({ pos: i, hasBlankLine: false });
|
|
94122
|
-
lastNewlinePos = -2; // Reset newline tracking for new expression
|
|
94123
|
-
}
|
|
94124
|
-
else if (ch === '}') {
|
|
94125
|
-
if (openStack.length > 0) {
|
|
94126
|
-
const entry = openStack.pop();
|
|
94127
|
-
// If expression spans paragraph boundary, escape both braces
|
|
94128
|
-
if (entry.hasBlankLine) {
|
|
94129
|
-
toEscape.add(entry.pos);
|
|
94130
|
-
toEscape.add(i);
|
|
94131
|
-
}
|
|
94132
|
-
}
|
|
94133
|
-
else {
|
|
94134
|
-
// Unbalanced closing brace (no matching open)
|
|
94135
|
-
toEscape.add(i);
|
|
94136
|
-
}
|
|
94137
|
-
}
|
|
94138
|
-
}
|
|
94139
|
-
// Any remaining open braces are unbalanced
|
|
94140
|
-
openStack.forEach(entry => toEscape.add(entry.pos));
|
|
94141
|
-
// If there are no problematic braces, return safe content as-is;
|
|
94142
|
-
// otherwise, escape each problematic `{` or `}` so MDX doesn't treat them as expressions.
|
|
94143
|
-
let result = toEscape.size === 0
|
|
94144
|
-
? safe
|
|
94145
|
-
: chars.map((ch, i) => (toEscape.has(i) ? `\\${ch}` : ch)).join('');
|
|
94146
|
-
// Restore HTML elements
|
|
94147
|
-
if (htmlElements.length > 0) {
|
|
94148
|
-
result = result.replace(/___HTML_ELEM_(\d+)___/g, (_m, idx) => htmlElements[parseInt(idx, 10)]);
|
|
94149
|
-
}
|
|
94150
|
-
return result;
|
|
94151
|
-
}
|
|
94152
|
-
/**
|
|
94153
|
-
* Converts JSX attribute expressions (attribute={expression}) to HTML attributes (attribute="value").
|
|
94154
|
-
* Handles style objects (camelCase → kebab-case), className → class, and JSON stringifies objects.
|
|
94155
|
-
*
|
|
94156
|
-
* @param content
|
|
94157
|
-
* @param context
|
|
94158
|
-
* @returns Content with attribute expressions evaluated and converted to HTML attributes
|
|
94159
|
-
* @example
|
|
94160
|
-
* ```typescript
|
|
94161
|
-
* const context = { baseUrl: 'https://example.com' };
|
|
94162
|
-
* const input = '<a href={baseUrl}>Link</a>';
|
|
94163
|
-
* evaluateAttributeExpressions(input, context)
|
|
94164
|
-
* // Returns: '<a href="https://example.com">Link</a>'
|
|
94165
|
-
* ```
|
|
94166
|
-
*/
|
|
94167
|
-
function evaluateAttributeExpressions(content, context, protectedCode) {
|
|
94168
|
-
const attrStartRegex = /(\w+)=\{/g;
|
|
94169
|
-
let result = '';
|
|
94170
|
-
let lastEnd = 0;
|
|
94171
|
-
let match = attrStartRegex.exec(content);
|
|
94172
|
-
while (match !== null) {
|
|
94173
|
-
const attributeName = match[1];
|
|
94174
|
-
const braceStart = match.index + match[0].length;
|
|
94175
|
-
const extracted = extractBalancedBraces(content, braceStart);
|
|
94176
|
-
if (extracted) {
|
|
94177
|
-
// The expression might contain template literals in MDX component tag props
|
|
94178
|
-
// E.g. <Component greeting={`Hello World!`} />
|
|
94179
|
-
// that is marked as inline code. So we need to restore the inline codes
|
|
94180
|
-
// in the expression to evaluate it
|
|
94181
|
-
let expression = extracted.content;
|
|
94182
|
-
if (protectedCode) {
|
|
94183
|
-
expression = restoreInlineCode(expression, protectedCode);
|
|
94184
|
-
}
|
|
94185
|
-
const fullMatchEnd = extracted.end;
|
|
94186
|
-
result += content.slice(lastEnd, match.index);
|
|
94187
|
-
try {
|
|
94188
|
-
const evalResult = evaluateExpression(expression, context);
|
|
94189
|
-
if (typeof evalResult === 'object' && evalResult !== null) {
|
|
94190
|
-
if (attributeName === 'style') {
|
|
94191
|
-
const cssString = Object.entries(evalResult)
|
|
94192
|
-
.map(([key, value]) => {
|
|
94193
|
-
const cssKey = key.replace(/([A-Z])/g, '-$1').toLowerCase();
|
|
94194
|
-
return `${cssKey}: ${value}`;
|
|
94195
|
-
})
|
|
94196
|
-
.join('; ');
|
|
94197
|
-
result += `style="${cssString}"`;
|
|
94198
|
-
}
|
|
94199
|
-
else {
|
|
94200
|
-
// These are arrays / objects attribute values
|
|
94201
|
-
// Mark JSON-serialized values with a prefix so they can be parsed back correctly
|
|
94202
|
-
const jsonValue = escapeHtmlAttribute(JSON_VALUE_MARKER + JSON.stringify(evalResult));
|
|
94203
|
-
// Use double quotes so that multi-paragraph values are not split into multiple attributes by the processors
|
|
94204
|
-
result += `${attributeName}="${jsonValue}"`;
|
|
94205
|
-
}
|
|
94206
|
-
}
|
|
94207
|
-
else if (attributeName === 'className') {
|
|
94208
|
-
// Escape special characters so that it doesn't break and split the attribute value to nodes
|
|
94209
|
-
// This will be restored later in the pipeline
|
|
94210
|
-
result += `class="${escapeHtmlAttribute(String(evalResult))}"`;
|
|
94211
|
-
}
|
|
94212
|
-
else {
|
|
94213
|
-
result += `${attributeName}="${escapeHtmlAttribute(String(evalResult))}"`;
|
|
94214
|
-
}
|
|
94215
|
-
}
|
|
94216
|
-
catch (_error) {
|
|
94217
|
-
result += content.slice(match.index, fullMatchEnd);
|
|
94218
|
-
}
|
|
94219
|
-
lastEnd = fullMatchEnd;
|
|
94220
|
-
attrStartRegex.lastIndex = fullMatchEnd;
|
|
94221
|
-
}
|
|
94222
|
-
match = attrStartRegex.exec(content);
|
|
94223
|
-
}
|
|
94224
|
-
result += content.slice(lastEnd);
|
|
94225
|
-
return result;
|
|
94226
|
-
}
|
|
94227
|
-
/**
|
|
94228
|
-
* Preprocesses JSX-like expressions in markdown before parsing.
|
|
94229
|
-
* Inline expressions are handled separately; attribute expressions are processed here.
|
|
94230
|
-
*
|
|
94231
|
-
* @param content
|
|
94232
|
-
* @param context
|
|
94233
|
-
* @returns Preprocessed content ready for markdown parsing
|
|
94234
|
-
*/
|
|
94235
|
-
function preprocessJSXExpressions(content, context = {}) {
|
|
94236
|
-
// Step 0: Base64 encode HTMLBlock content
|
|
94237
|
-
let processed = protectHTMLBlockContent(content);
|
|
94238
|
-
// Step 1: Protect code blocks and inline code
|
|
94239
|
-
const { protectedCode, protectedContent } = protectCodeBlocks(processed);
|
|
94240
|
-
// Step 2: Evaluate attribute expressions (JSX attribute syntax: href={baseUrl})
|
|
94241
|
-
// For inline expressions, we use a library to parse the expression & evaluate it later
|
|
94242
|
-
// For attribute expressions, it was difficult to use a library to parse them, so do it manually
|
|
94243
|
-
processed = evaluateAttributeExpressions(protectedContent, context, protectedCode);
|
|
94244
|
-
// Step 3: Escape problematic braces to prevent MDX expression parsing errors
|
|
94245
|
-
// This handles both unbalanced braces and paragraph-spanning expressions in one pass
|
|
94246
|
-
processed = escapeProblematicBraces(processed);
|
|
94247
|
-
// Step 4: Restore protected code blocks
|
|
94248
|
-
processed = restoreCodeBlocks(processed, protectedCode);
|
|
94249
|
-
return processed;
|
|
94250
|
-
}
|
|
94251
|
-
|
|
94252
94305
|
;// ./processor/transform/mdxish/evaluate-expressions.ts
|
|
94253
94306
|
|
|
94254
94307
|
|
|
@@ -96042,11 +96095,33 @@ const parseBlock = (text) => {
|
|
|
96042
96095
|
const tree = contentParser.runSync(contentParser.parse(text));
|
|
96043
96096
|
return tree.children;
|
|
96044
96097
|
};
|
|
96045
|
-
|
|
96098
|
+
/**
|
|
96099
|
+
* Minimal parser for api-header titles.
|
|
96100
|
+
* Disables markdown constructs that are not parsed in legacy (headings, lists)
|
|
96101
|
+
*/
|
|
96102
|
+
const apiHeaderTitleParser = unified()
|
|
96103
|
+
.data('micromarkExtensions', [
|
|
96104
|
+
legacyVariable(),
|
|
96105
|
+
looseHtmlEntity(),
|
|
96106
|
+
{
|
|
96107
|
+
disable: {
|
|
96108
|
+
null: [
|
|
96109
|
+
'blockQuote',
|
|
96110
|
+
'headingAtx',
|
|
96111
|
+
'list',
|
|
96112
|
+
'thematicBreak',
|
|
96113
|
+
],
|
|
96114
|
+
},
|
|
96115
|
+
},
|
|
96116
|
+
])
|
|
96117
|
+
.data('fromMarkdownExtensions', [legacyVariableFromMarkdown(), looseHtmlEntityFromMarkdown()])
|
|
96118
|
+
.use(remarkParse)
|
|
96119
|
+
.use(remarkGfm);
|
|
96120
|
+
const parseApiHeaderTitle = (text) => {
|
|
96046
96121
|
if (!text.trim())
|
|
96047
96122
|
return textToInline(text);
|
|
96048
|
-
const tree =
|
|
96049
|
-
return tree.children;
|
|
96123
|
+
const tree = apiHeaderTitleParser.runSync(apiHeaderTitleParser.parse(text));
|
|
96124
|
+
return tree.children.flatMap(n => n.type === 'paragraph' && 'children' in n ? n.children : [n]);
|
|
96050
96125
|
};
|
|
96051
96126
|
/**
|
|
96052
96127
|
* Transform a magicBlock node into final MDAST nodes.
|
|
@@ -96105,7 +96180,7 @@ function transformMagicBlock(blockType, data, rawValue, options = {}) {
|
|
|
96105
96180
|
const depth = headerJson.level || (compatibilityMode ? 1 : 2);
|
|
96106
96181
|
return [
|
|
96107
96182
|
wrapPinnedBlocks({
|
|
96108
|
-
children: 'title' in headerJson ?
|
|
96183
|
+
children: 'title' in headerJson ? parseApiHeaderTitle(headerJson.title || '') : [],
|
|
96109
96184
|
depth,
|
|
96110
96185
|
type: 'heading',
|
|
96111
96186
|
}, data),
|
|
@@ -97337,11 +97412,66 @@ const transformMagicBlockEmbed = (node) => {
|
|
|
97337
97412
|
position,
|
|
97338
97413
|
};
|
|
97339
97414
|
};
|
|
97415
|
+
const mdxish_jsx_to_mdast_isTableCell = (node) => isMDXElement(node) && ['th', 'td'].includes(node.name);
|
|
97416
|
+
/**
|
|
97417
|
+
* Converts a JSX <Table> element to an MDAST table node with alignment.
|
|
97418
|
+
* Returns null for header-less tables since MDAST always promotes the first row to <thead>.
|
|
97419
|
+
*/
|
|
97420
|
+
const transformTable = (jsx) => {
|
|
97421
|
+
let hasThead = false;
|
|
97422
|
+
visit(jsx, isMDXElement, (child) => {
|
|
97423
|
+
if (child.name === 'thead')
|
|
97424
|
+
hasThead = true;
|
|
97425
|
+
});
|
|
97426
|
+
if (!hasThead)
|
|
97427
|
+
return null;
|
|
97428
|
+
const { align: alignAttr } = getAttrs(jsx);
|
|
97429
|
+
const align = Array.isArray(alignAttr) ? alignAttr : null;
|
|
97430
|
+
const rows = [];
|
|
97431
|
+
visit(jsx, isMDXElement, (child) => {
|
|
97432
|
+
if (child.name !== 'thead' && child.name !== 'tbody')
|
|
97433
|
+
return;
|
|
97434
|
+
visit(child, isMDXElement, (row) => {
|
|
97435
|
+
if (row.name !== 'tr')
|
|
97436
|
+
return;
|
|
97437
|
+
const cells = [];
|
|
97438
|
+
visit(row, mdxish_jsx_to_mdast_isTableCell, (cell) => {
|
|
97439
|
+
const parsedChildren = cell.children.flatMap(parsedNode => {
|
|
97440
|
+
if (parsedNode.type === 'paragraph' && 'children' in parsedNode && parsedNode.children) {
|
|
97441
|
+
return parsedNode.children;
|
|
97442
|
+
}
|
|
97443
|
+
return [parsedNode];
|
|
97444
|
+
});
|
|
97445
|
+
cells.push({
|
|
97446
|
+
type: 'tableCell',
|
|
97447
|
+
children: parsedChildren,
|
|
97448
|
+
position: cell.position,
|
|
97449
|
+
});
|
|
97450
|
+
});
|
|
97451
|
+
rows.push({
|
|
97452
|
+
type: 'tableRow',
|
|
97453
|
+
children: cells,
|
|
97454
|
+
position: row.position,
|
|
97455
|
+
});
|
|
97456
|
+
});
|
|
97457
|
+
});
|
|
97458
|
+
const columnCount = rows[0]?.children?.length || 0;
|
|
97459
|
+
const alignArray = align && columnCount > 0
|
|
97460
|
+
? align.slice(0, columnCount).concat(Array.from({ length: Math.max(0, columnCount - align.length) }, () => null))
|
|
97461
|
+
: Array.from({ length: columnCount }, () => null);
|
|
97462
|
+
return {
|
|
97463
|
+
type: 'table',
|
|
97464
|
+
align: alignArray,
|
|
97465
|
+
position: jsx.position,
|
|
97466
|
+
children: rows,
|
|
97467
|
+
};
|
|
97468
|
+
};
|
|
97340
97469
|
const COMPONENT_MAP = {
|
|
97341
97470
|
Callout: transformCallout,
|
|
97342
97471
|
Embed: transformEmbed,
|
|
97343
97472
|
Image: transformImage,
|
|
97344
97473
|
Recipe: transformRecipe,
|
|
97474
|
+
Table: transformTable,
|
|
97345
97475
|
};
|
|
97346
97476
|
/**
|
|
97347
97477
|
* Transform mdxJsxFlowElement nodes and magic block nodes into proper MDAST node types.
|
|
@@ -97364,6 +97494,8 @@ const mdxishJsxToMdast = () => tree => {
|
|
|
97364
97494
|
if (!transformer)
|
|
97365
97495
|
return;
|
|
97366
97496
|
const newNode = transformer(node);
|
|
97497
|
+
if (!newNode)
|
|
97498
|
+
return;
|
|
97367
97499
|
// Replace the JSX node with the MDAST node
|
|
97368
97500
|
parent.children[index] = newNode;
|
|
97369
97501
|
});
|
|
@@ -97644,6 +97776,126 @@ function restoreSnakeCase(placeholderName, mapping) {
|
|
|
97644
97776
|
return matchingKey ? mapping[matchingKey] : placeholderName;
|
|
97645
97777
|
}
|
|
97646
97778
|
|
|
97779
|
+
;// ./processor/transform/mdxish/mdxish-tables-to-jsx.ts
|
|
97780
|
+
|
|
97781
|
+
|
|
97782
|
+
const mdxish_tables_to_jsx_alignToStyle = (align) => {
|
|
97783
|
+
if (!align || align === 'left')
|
|
97784
|
+
return null;
|
|
97785
|
+
return {
|
|
97786
|
+
type: 'mdxJsxAttribute',
|
|
97787
|
+
name: 'style',
|
|
97788
|
+
value: {
|
|
97789
|
+
type: 'mdxJsxAttributeValueExpression',
|
|
97790
|
+
value: `{ textAlign: "${align}" }`,
|
|
97791
|
+
},
|
|
97792
|
+
};
|
|
97793
|
+
};
|
|
97794
|
+
const mdxish_tables_to_jsx_isTableCell = (node) => ['tableHead', 'tableCell'].includes(node.type);
|
|
97795
|
+
const mdxish_tables_to_jsx_isLiteral = (node) => 'value' in node;
|
|
97796
|
+
/**
|
|
97797
|
+
* Mdxish-specific version of `tablesToJsx`. Differs from the shared MDX version:
|
|
97798
|
+
*
|
|
97799
|
+
* - Excludes `html` nodes from triggering JSX conversion because raw HTML
|
|
97800
|
+
* inside JSX `<Table>` breaks remarkMdx parsing on the deserialization roundtrip.
|
|
97801
|
+
* - Skips empty cells instead of aborting the entire visit so that flow content
|
|
97802
|
+
* in later cells is still detected.
|
|
97803
|
+
*/
|
|
97804
|
+
const mdxishTablesToJsx = () => tree => {
|
|
97805
|
+
visit(tree, (node) => ['table', 'tableau'].includes(node.type), (table, index, parent) => {
|
|
97806
|
+
let hasFlowContent = false;
|
|
97807
|
+
visit(table, mdxish_tables_to_jsx_isTableCell, (cell) => {
|
|
97808
|
+
if (cell.children.length === 0)
|
|
97809
|
+
return;
|
|
97810
|
+
const content = cell.children.length === 1 && cell.children[0].type === 'paragraph'
|
|
97811
|
+
? cell.children[0].children[0]
|
|
97812
|
+
: cell.children[0];
|
|
97813
|
+
if (!content)
|
|
97814
|
+
return;
|
|
97815
|
+
visit(cell, 'break', (_, breakIndex, breakParent) => {
|
|
97816
|
+
breakParent.children.splice(breakIndex, 1, { type: 'text', value: '\n' });
|
|
97817
|
+
});
|
|
97818
|
+
if (!(phrasing(content) || content.type === 'plain') && content.type !== 'escape') {
|
|
97819
|
+
if (content.type === 'html')
|
|
97820
|
+
return;
|
|
97821
|
+
hasFlowContent = true;
|
|
97822
|
+
return EXIT;
|
|
97823
|
+
}
|
|
97824
|
+
visit(cell, mdxish_tables_to_jsx_isLiteral, (node) => {
|
|
97825
|
+
if (node.value.match(/\n/)) {
|
|
97826
|
+
hasFlowContent = true;
|
|
97827
|
+
return EXIT;
|
|
97828
|
+
}
|
|
97829
|
+
});
|
|
97830
|
+
});
|
|
97831
|
+
if (!hasFlowContent) {
|
|
97832
|
+
table.type = 'table';
|
|
97833
|
+
return;
|
|
97834
|
+
}
|
|
97835
|
+
const styles = table.align.map(mdxish_tables_to_jsx_alignToStyle);
|
|
97836
|
+
const head = {
|
|
97837
|
+
attributes: [],
|
|
97838
|
+
type: 'mdxJsxFlowElement',
|
|
97839
|
+
name: 'thead',
|
|
97840
|
+
children: [
|
|
97841
|
+
{
|
|
97842
|
+
attributes: [],
|
|
97843
|
+
type: 'mdxJsxFlowElement',
|
|
97844
|
+
name: 'tr',
|
|
97845
|
+
children: table.children[0].children.map((cell, cellIndex) => {
|
|
97846
|
+
return {
|
|
97847
|
+
attributes: [],
|
|
97848
|
+
type: 'mdxJsxFlowElement',
|
|
97849
|
+
name: 'th',
|
|
97850
|
+
children: cell.children,
|
|
97851
|
+
...(styles[cellIndex] && { attributes: [styles[cellIndex]] }),
|
|
97852
|
+
};
|
|
97853
|
+
}),
|
|
97854
|
+
},
|
|
97855
|
+
],
|
|
97856
|
+
};
|
|
97857
|
+
const body = {
|
|
97858
|
+
attributes: [],
|
|
97859
|
+
type: 'mdxJsxFlowElement',
|
|
97860
|
+
name: 'tbody',
|
|
97861
|
+
children: table.children.splice(1).map(row => {
|
|
97862
|
+
return {
|
|
97863
|
+
attributes: [],
|
|
97864
|
+
type: 'mdxJsxFlowElement',
|
|
97865
|
+
name: 'tr',
|
|
97866
|
+
children: row.children.map((cell, cellIndex) => {
|
|
97867
|
+
return {
|
|
97868
|
+
type: 'mdxJsxFlowElement',
|
|
97869
|
+
name: 'td',
|
|
97870
|
+
children: cell.children,
|
|
97871
|
+
...(styles[cellIndex] && { attributes: [styles[cellIndex]] }),
|
|
97872
|
+
};
|
|
97873
|
+
}),
|
|
97874
|
+
};
|
|
97875
|
+
}),
|
|
97876
|
+
};
|
|
97877
|
+
const attributes = [
|
|
97878
|
+
{
|
|
97879
|
+
type: 'mdxJsxAttribute',
|
|
97880
|
+
name: 'align',
|
|
97881
|
+
value: {
|
|
97882
|
+
type: 'mdxJsxAttributeValueExpression',
|
|
97883
|
+
value: JSON.stringify(table.align),
|
|
97884
|
+
},
|
|
97885
|
+
},
|
|
97886
|
+
];
|
|
97887
|
+
const jsx = {
|
|
97888
|
+
type: 'mdxJsxFlowElement',
|
|
97889
|
+
name: 'Table',
|
|
97890
|
+
attributes: table.align.find(a => a) ? attributes : [],
|
|
97891
|
+
children: [head, body],
|
|
97892
|
+
};
|
|
97893
|
+
parent.children[index] = jsx;
|
|
97894
|
+
});
|
|
97895
|
+
return tree;
|
|
97896
|
+
};
|
|
97897
|
+
/* harmony default export */ const mdxish_tables_to_jsx = (mdxishTablesToJsx);
|
|
97898
|
+
|
|
97647
97899
|
;// ./processor/transform/mdxish/normalize-table-separator.ts
|
|
97648
97900
|
/**
|
|
97649
97901
|
* Preprocessor to normalize malformed GFM table separator syntax.
|
|
@@ -99812,6 +100064,8 @@ function loadComponents() {
|
|
|
99812
100064
|
|
|
99813
100065
|
|
|
99814
100066
|
|
|
100067
|
+
|
|
100068
|
+
|
|
99815
100069
|
|
|
99816
100070
|
|
|
99817
100071
|
|
|
@@ -99909,18 +100163,28 @@ function mdxishAstProcessor(mdContent, opts = {}) {
|
|
|
99909
100163
|
};
|
|
99910
100164
|
}
|
|
99911
100165
|
/**
|
|
99912
|
-
*
|
|
100166
|
+
* Registers the mdx-jsx serialization extension so remark-stringify
|
|
100167
|
+
* can convert JSX nodes (e.g. `<Table>`) to markdown.
|
|
100168
|
+
*/
|
|
100169
|
+
function mdxJsxStringify() {
|
|
100170
|
+
const data = this.data();
|
|
100171
|
+
const extensions = data.toMarkdownExtensions || (data.toMarkdownExtensions = []);
|
|
100172
|
+
extensions.push({ extensions: [mdxJsxToMarkdown()] });
|
|
100173
|
+
}
|
|
100174
|
+
/**
|
|
100175
|
+
* Serializes an Mdast back into a markdown string.
|
|
99913
100176
|
*/
|
|
99914
100177
|
function mdxishMdastToMd(mdast) {
|
|
99915
|
-
const
|
|
100178
|
+
const processor = unified()
|
|
99916
100179
|
.use(remarkGfm)
|
|
100180
|
+
.use(mdxish_tables_to_jsx)
|
|
99917
100181
|
.use(mdxishCompilers)
|
|
100182
|
+
.use(mdxJsxStringify)
|
|
99918
100183
|
.use(remarkStringify, {
|
|
99919
100184
|
bullet: '-',
|
|
99920
100185
|
emphasis: '_',
|
|
99921
|
-
})
|
|
99922
|
-
|
|
99923
|
-
return md;
|
|
100186
|
+
});
|
|
100187
|
+
return processor.stringify(processor.runSync(mdast));
|
|
99924
100188
|
}
|
|
99925
100189
|
/**
|
|
99926
100190
|
* Processes markdown content with MDX syntax support and returns a HAST.
|