@readme/markdown 15.4.0 → 15.5.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/Accordion/index.tsx +5 -1
- package/components/Tabs/index.tsx +9 -1
- package/dist/hooks/useRestartAnimatedImages/index.d.ts +14 -0
- package/dist/lib/utils/mdxish/mdxish-expression.d.ts +7 -0
- package/dist/lib/utils/mdxish/mdxish-get-component-name.d.ts +2 -0
- package/dist/main.js +233 -215
- package/dist/main.node.js +233 -215
- package/dist/main.node.js.map +1 -1
- package/dist/processor/compile/list-item.d.ts +2 -2
- package/dist/processor/compile/table-cell-block-markers.d.ts +11 -0
- package/dist/processor/compile/text.d.ts +3 -2
- package/dist/processor/transform/mdxish/evaluate-expressions.d.ts +11 -5
- package/dist/render-fixture.node.js +233 -215
- package/dist/render-fixture.node.js.map +1 -1
- package/package.json +1 -1
- package/dist/processor/transform/mdxish/components/self-closing-blocks.d.ts +0 -21
- package/dist/processor/transform/mdxish/components/snake-case-components.d.ts +0 -28
- package/dist/processor/transform/mdxish/restore-snake-case-component-name.d.ts +0 -12
|
@@ -19045,7 +19045,7 @@ module.exports = function () {
|
|
|
19045
19045
|
|
|
19046
19046
|
/***/ },
|
|
19047
19047
|
|
|
19048
|
-
/***/
|
|
19048
|
+
/***/ 8691
|
|
19049
19049
|
(module, __webpack_exports__, __webpack_require__) {
|
|
19050
19050
|
|
|
19051
19051
|
"use strict";
|
|
@@ -86684,18 +86684,77 @@ const gemojiTransformer = () => (tree) => {
|
|
|
86684
86684
|
};
|
|
86685
86685
|
/* harmony default export */ const gemoji_ = (gemojiTransformer);
|
|
86686
86686
|
|
|
86687
|
+
;// ./hooks/useRestartAnimatedImages/index.tsx
|
|
86688
|
+
|
|
86689
|
+
/**
|
|
86690
|
+
* GIF is the one extension that implies animation. `.webp` and `.png` (the extension an APNG
|
|
86691
|
+
* ships under) are overwhelmingly static, so matching them would reload far more images than
|
|
86692
|
+
* it rewinds; telling those apart needs the bytes, not the URL.
|
|
86693
|
+
*/
|
|
86694
|
+
const ANIMATED_IMAGE_SRC = /\.gif(\?|#|$)/i;
|
|
86695
|
+
const restartAnimatedImages = (root) => {
|
|
86696
|
+
root?.querySelectorAll('img').forEach(img => {
|
|
86697
|
+
if (!ANIMATED_IMAGE_SRC.test(img.src))
|
|
86698
|
+
return;
|
|
86699
|
+
const { src } = img;
|
|
86700
|
+
// There's no seek API for animated images; reassigning `src` is the only reset
|
|
86701
|
+
img.src = '';
|
|
86702
|
+
img.src = src;
|
|
86703
|
+
});
|
|
86704
|
+
};
|
|
86705
|
+
const useRestartAfterFirstPaint = (revealKey, restart) => {
|
|
86706
|
+
const isFirstRender = (0,external_react_.useRef)(true);
|
|
86707
|
+
const latestRestart = (0,external_react_.useRef)(restart);
|
|
86708
|
+
latestRestart.current = restart;
|
|
86709
|
+
(0,external_react_.useEffect)(() => {
|
|
86710
|
+
if (isFirstRender.current) {
|
|
86711
|
+
isFirstRender.current = false;
|
|
86712
|
+
return;
|
|
86713
|
+
}
|
|
86714
|
+
latestRestart.current();
|
|
86715
|
+
}, [revealKey]);
|
|
86716
|
+
};
|
|
86717
|
+
/**
|
|
86718
|
+
* Restarts GIF playback inside the panel that just became active.
|
|
86719
|
+
*
|
|
86720
|
+
* Tabs keep every panel mounted, so an <img> is never re-created and a GIF is
|
|
86721
|
+
* shown mid-loop (or frozen on its last frame) when its tab is re-selected.
|
|
86722
|
+
*
|
|
86723
|
+
* @returns a ref to attach to each panel element, indexed by tab.
|
|
86724
|
+
*/
|
|
86725
|
+
function useRestartAnimatedImages(activeIndex) {
|
|
86726
|
+
const panelRefs = (0,external_react_.useRef)([]);
|
|
86727
|
+
useRestartAfterFirstPaint(activeIndex, () => restartAnimatedImages(panelRefs.current[activeIndex]));
|
|
86728
|
+
return panelRefs;
|
|
86729
|
+
}
|
|
86730
|
+
/**
|
|
86731
|
+
* Restarts GIF playback inside an element that has just been revealed after being hidden —
|
|
86732
|
+
* a collapsed <details>, whose images have been animating out of sight since page load.
|
|
86733
|
+
*/
|
|
86734
|
+
function useRestartAnimatedImagesOnReveal(isRevealed) {
|
|
86735
|
+
const contentRef = (0,external_react_.useRef)(null);
|
|
86736
|
+
useRestartAfterFirstPaint(isRevealed, () => {
|
|
86737
|
+
// Rewinding content the reader just hid would be wasted work
|
|
86738
|
+
if (isRevealed)
|
|
86739
|
+
restartAnimatedImages(contentRef.current);
|
|
86740
|
+
});
|
|
86741
|
+
return contentRef;
|
|
86742
|
+
}
|
|
86743
|
+
|
|
86687
86744
|
;// ./components/Accordion/index.tsx
|
|
86688
86745
|
|
|
86689
86746
|
|
|
86690
86747
|
|
|
86748
|
+
|
|
86691
86749
|
const Accordion = ({ children, icon, iconColor, title }) => {
|
|
86692
86750
|
const [isOpen, setIsOpen] = (0,external_react_.useState)(false);
|
|
86751
|
+
const contentRef = useRestartAnimatedImagesOnReveal(isOpen);
|
|
86693
86752
|
return (external_react_default().createElement("details", { className: "Accordion", onToggle: () => setIsOpen(!isOpen) },
|
|
86694
86753
|
external_react_default().createElement("summary", { className: "Accordion-title" },
|
|
86695
86754
|
external_react_default().createElement("i", { className: `Accordion-toggleIcon${isOpen ? '_opened' : ''} fa fa-regular fa-chevron-right` }),
|
|
86696
86755
|
icon && external_react_default().createElement(components_Icon, { className: "Accordion-icon", icon: icon, iconColor: iconColor }),
|
|
86697
86756
|
title),
|
|
86698
|
-
external_react_default().createElement("div", { className: "Accordion-content" }, children)));
|
|
86757
|
+
external_react_default().createElement("div", { ref: contentRef, className: "Accordion-content" }, children)));
|
|
86699
86758
|
};
|
|
86700
86759
|
/* harmony default export */ const components_Accordion = (Accordion);
|
|
86701
86760
|
|
|
@@ -92513,11 +92572,13 @@ function TableOfContents({ children }) {
|
|
|
92513
92572
|
|
|
92514
92573
|
|
|
92515
92574
|
|
|
92575
|
+
|
|
92516
92576
|
const Tab = ({ children }) => {
|
|
92517
92577
|
return external_react_default().createElement("div", { className: "TabContent" }, children);
|
|
92518
92578
|
};
|
|
92519
92579
|
const Tabs = ({ children }) => {
|
|
92520
92580
|
const [activeTab, setActiveTab] = (0,external_react_.useState)(0);
|
|
92581
|
+
const panelRefs = useRestartAnimatedImages(activeTab);
|
|
92521
92582
|
// React passes `children` as a single element when there's only one child, so normalize.
|
|
92522
92583
|
const tabs = external_react_default().Children.toArray(children);
|
|
92523
92584
|
return (external_react_default().createElement("div", { className: "TabGroup" },
|
|
@@ -92525,7 +92586,9 @@ const Tabs = ({ children }) => {
|
|
|
92525
92586
|
external_react_default().createElement("nav", { className: "TabGroup-nav" }, tabs.map((tab, index) => (external_react_default().createElement("button", { key: tab.key, className: `TabGroup-tab${activeTab === index ? '_active' : ''}`, onClick: () => setActiveTab(index) },
|
|
92526
92587
|
tab.props.icon && (external_react_default().createElement(components_Icon, { className: "TabGroup-icon", icon: tab.props.icon, iconColor: tab.props.iconColor })),
|
|
92527
92588
|
tab.props.title))))),
|
|
92528
|
-
external_react_default().createElement("section", null, tabs.map((tab, index) => (external_react_default().createElement("div", { key: tab.key,
|
|
92589
|
+
external_react_default().createElement("section", null, tabs.map((tab, index) => (external_react_default().createElement("div", { key: tab.key, ref: el => {
|
|
92590
|
+
panelRefs.current[index] = el;
|
|
92591
|
+
}, hidden: index !== activeTab }, tab))))));
|
|
92529
92592
|
};
|
|
92530
92593
|
/* harmony default export */ const components_Tabs = (Tabs);
|
|
92531
92594
|
|
|
@@ -96519,6 +96582,10 @@ function syntax_createTokenize(mode) {
|
|
|
96519
96582
|
effects.consume(code);
|
|
96520
96583
|
return inBraceExpr;
|
|
96521
96584
|
}
|
|
96585
|
+
// A raw `<` can't sit in an opening tag (quotes/braces are handled above);
|
|
96586
|
+
// bailing bounds each attempt to the next `<` instead of rescanning the line.
|
|
96587
|
+
if (code === codes.lessThan)
|
|
96588
|
+
return nok(code);
|
|
96522
96589
|
effects.consume(code);
|
|
96523
96590
|
return afterOpenTagName;
|
|
96524
96591
|
}
|
|
@@ -121553,7 +121620,7 @@ const listMarkerRegex = /^(?:[*+-]|\d+[.)])(?:([\r\n]| {1,3})|$)/;
|
|
|
121553
121620
|
* with their checkbox intact (for example, `- [ ]`) instead of dropping it
|
|
121554
121621
|
* We can add more adjustments if needed
|
|
121555
121622
|
*/
|
|
121556
|
-
const compile_list_item_listItem = (node, parent, state, info) => {
|
|
121623
|
+
const compile_list_item_listItem = ((node, parent, state, info) => {
|
|
121557
121624
|
const head = node.children[0];
|
|
121558
121625
|
const isCheckbox = typeof node.checked === 'boolean' && head && head.type === 'paragraph';
|
|
121559
121626
|
if (!isCheckbox) {
|
|
@@ -121577,15 +121644,47 @@ const compile_list_item_listItem = (node, parent, state, info) => {
|
|
|
121577
121644
|
return `${marker}${actualSeparator}${checkbox}`;
|
|
121578
121645
|
});
|
|
121579
121646
|
return value;
|
|
121580
|
-
};
|
|
121647
|
+
});
|
|
121581
121648
|
/* harmony default export */ const list_item = (compile_list_item_listItem);
|
|
121582
121649
|
|
|
121583
121650
|
;// ./processor/compile/plain.ts
|
|
121584
121651
|
const plain_plain = (node) => node.value;
|
|
121585
121652
|
/* harmony default export */ const compile_plain = (plain_plain);
|
|
121586
121653
|
|
|
121654
|
+
;// ./processor/compile/table-cell-block-markers.ts
|
|
121655
|
+
const CELL_START_BLOCK_MARKER = /^(?:[-+](?=\s|$)|#{1,6}(?=\s|$)|>)/;
|
|
121656
|
+
const BR_HTML = /^\s*<br\s*\/?>\s*$/i;
|
|
121657
|
+
const isLineBreak = (node) => {
|
|
121658
|
+
if (node.type === 'break')
|
|
121659
|
+
return true;
|
|
121660
|
+
if (node.type === 'html')
|
|
121661
|
+
return BR_HTML.test(node.value);
|
|
121662
|
+
return node.type === 'mdxJsxTextElement' && 'name' in node && node.name?.toLowerCase() === 'br';
|
|
121663
|
+
};
|
|
121664
|
+
const followsLineBreak = (node, parent) => {
|
|
121665
|
+
const siblings = (parent?.children ?? []);
|
|
121666
|
+
const previous = siblings[siblings.indexOf(node) - 1];
|
|
121667
|
+
return !!previous && isLineBreak(previous);
|
|
121668
|
+
};
|
|
121669
|
+
/**
|
|
121670
|
+
* Re-escapes a leading block marker (`-`, `+`, `#`, `>`) at the start of a table cell, or
|
|
121671
|
+
* after a `<br />` within one.
|
|
121672
|
+
*
|
|
121673
|
+
* A cell is serialized mid-line, so the `atBreak` patterns that escape these characters
|
|
121674
|
+
* never fire and an author's `| \- one |` round trips to `| - one |` (RM-17203). Cells are
|
|
121675
|
+
* serialized as `containerPhrasing(cell, { before: '|' })`, so `|` marks the cell start.
|
|
121676
|
+
*/
|
|
121677
|
+
const escapeCellStartBlockMarker = (serialized, node, parent, state, info) => {
|
|
121678
|
+
if (serialized.startsWith('\\') || !CELL_START_BLOCK_MARKER.test(serialized))
|
|
121679
|
+
return serialized;
|
|
121680
|
+
if (!state.stack.includes('tableCell') || !(info.before === '|' || followsLineBreak(node, parent)))
|
|
121681
|
+
return serialized;
|
|
121682
|
+
return `\\${serialized}`;
|
|
121683
|
+
};
|
|
121684
|
+
|
|
121587
121685
|
;// ./processor/compile/text.ts
|
|
121588
121686
|
|
|
121687
|
+
|
|
121589
121688
|
// A `_` flanked by word characters can never open or close emphasis under
|
|
121590
121689
|
// CommonMark's flanking rules, so the escape mdast-util-to-markdown adds to
|
|
121591
121690
|
// intraword underscores is unnecessary and only produces noisy `\_` diffs.
|
|
@@ -121593,9 +121692,10 @@ const plain_plain = (node) => node.value;
|
|
|
121593
121692
|
const INTRAWORD_UNDERSCORE_ESCAPE = /(?<=[\p{L}\p{N}_])\\_(?=[\p{L}\p{N}_]|\\_)/gu;
|
|
121594
121693
|
const compile_text_text = (node, parent, state, info) => {
|
|
121595
121694
|
const serialized = handle.text(node, parent, state, info);
|
|
121596
|
-
return serialized
|
|
121695
|
+
return escapeCellStartBlockMarker(serialized, node, parent, state, info);
|
|
121597
121696
|
};
|
|
121598
|
-
|
|
121697
|
+
const mdxishText = (node, parent, state, info) => compile_text_text(node, parent, state, info).replace(INTRAWORD_UNDERSCORE_ESCAPE, '_');
|
|
121698
|
+
/* harmony default export */ const compile_text = (mdxishText);
|
|
121599
121699
|
|
|
121600
121700
|
;// ./processor/compile/index.ts
|
|
121601
121701
|
|
|
@@ -121627,6 +121727,7 @@ function compilers(mdxish = false) {
|
|
|
121627
121727
|
html: compile_compatibility,
|
|
121628
121728
|
i: compile_compatibility,
|
|
121629
121729
|
plain: compile_plain,
|
|
121730
|
+
text: compile_text_text,
|
|
121630
121731
|
yaml: compile_compatibility,
|
|
121631
121732
|
// needed only for mdxish
|
|
121632
121733
|
...(mdxish && { list: compile_list }),
|
|
@@ -122284,13 +122385,14 @@ function restoreCodeBlocks(content, protectedCode) {
|
|
|
122284
122385
|
*
|
|
122285
122386
|
* The attribute portion skips over quoted strings (`"..."` and `'...'`) so that
|
|
122286
122387
|
* a `/>` inside an attribute value (e.g. `title="use /> here"`) does not cause
|
|
122287
|
-
* a premature match
|
|
122388
|
+
* a premature match, and stops at an unquoted `<` so a stray `<x` in prose
|
|
122389
|
+
* doesn't scan to the end of the document (quadratic).
|
|
122288
122390
|
*
|
|
122289
122391
|
* Only matches lowercase tag names to avoid interfering with PascalCase
|
|
122290
122392
|
* JSX custom components (e.g. `<MyComponent />`), which are handled
|
|
122291
122393
|
* separately by components/mdx-blocks.
|
|
122292
122394
|
*/
|
|
122293
|
-
const SELF_CLOSING_TAG_RE = /<([a-z][a-z0-9-]*)((?:\s+(?:[
|
|
122395
|
+
const SELF_CLOSING_TAG_RE = /<([a-z][a-z0-9-]*)((?:\s+(?:[^<>"']*(?:"[^"]*"|'[^']*'))*[^<>"']*)?)?\s*\/>/g;
|
|
122294
122396
|
/**
|
|
122295
122397
|
* String-level preprocessor that converts self-closing non-void HTML tags
|
|
122296
122398
|
* into explicitly closed tags.
|
|
@@ -122955,8 +123057,9 @@ function terminateHtmlFlowBlocks(content) {
|
|
|
122955
123057
|
|
|
122956
123058
|
|
|
122957
123059
|
|
|
122958
|
-
// Matches a JSX attribute expression (e.g. `key={i}`) anywhere in a string.
|
|
122959
|
-
|
|
123060
|
+
// Matches a JSX attribute expression (e.g. `key={i}`) anywhere in a string. One name
|
|
123061
|
+
// char suffices for existence — `[\w-]+` backtracked quadratically over huge attributes.
|
|
123062
|
+
const NESTED_ATTR_EXPRESSION_RE = /[\w-]\s*=\s*\{/;
|
|
122960
123063
|
// Name shape mirrors `componentTagPattern`; the lookbehind skips the inner tag
|
|
122961
123064
|
// of a legacy `<<VARIABLE>>`.
|
|
122962
123065
|
const NESTED_COMPONENT_TAG_RE = /(?<!<)<([A-Z][A-Za-z0-9_]*)[\s/>]/g;
|
|
@@ -123255,150 +123358,6 @@ const mdxishMdxComponentBlocks = (opts = {}) => (tree, file) => {
|
|
|
123255
123358
|
};
|
|
123256
123359
|
/* harmony default export */ const mdx_blocks = (mdxishMdxComponentBlocks);
|
|
123257
123360
|
|
|
123258
|
-
;// ./processor/transform/mdxish/components/self-closing-blocks.ts
|
|
123259
|
-
|
|
123260
|
-
|
|
123261
|
-
/**
|
|
123262
|
-
* Tags to process as self-closing blocks.
|
|
123263
|
-
* These components use simple string attributes (no JSX expressions like `data={[...]}`).
|
|
123264
|
-
* Components with JSX expression attributes should NOT be added here as parseAttributes
|
|
123265
|
-
* cannot handle them correctly.
|
|
123266
|
-
*/
|
|
123267
|
-
const SELF_CLOSING_BLOCK_TAGS = new Set(['Embed', 'Recipe']);
|
|
123268
|
-
// Regex to match self-closing PascalCase tags (handles multi-line)
|
|
123269
|
-
const selfClosingTagPattern = /^<([A-Z][A-Za-z0-9_]*)([\s\S]*?)\/>$/;
|
|
123270
|
-
/**
|
|
123271
|
-
* Try to convert a paragraph node containing a self-closing JSX component into an mdxJsxFlowElement.
|
|
123272
|
-
* Returns the new node if conversion succeeded, or null if the node doesn't match.
|
|
123273
|
-
*/
|
|
123274
|
-
const tryConvertToMdxNode = (node) => {
|
|
123275
|
-
if (node.children.length !== 1)
|
|
123276
|
-
return null;
|
|
123277
|
-
const child = node.children[0];
|
|
123278
|
-
if (child.type !== 'html')
|
|
123279
|
-
return null;
|
|
123280
|
-
const value = child.value?.trim();
|
|
123281
|
-
if (!value)
|
|
123282
|
-
return null;
|
|
123283
|
-
const match = value.match(selfClosingTagPattern);
|
|
123284
|
-
if (!match)
|
|
123285
|
-
return null;
|
|
123286
|
-
const [, tag, attrString] = match;
|
|
123287
|
-
if (!SELF_CLOSING_BLOCK_TAGS.has(tag))
|
|
123288
|
-
return null;
|
|
123289
|
-
return {
|
|
123290
|
-
type: 'mdxJsxFlowElement',
|
|
123291
|
-
name: tag,
|
|
123292
|
-
attributes: parseAttributes(attrString),
|
|
123293
|
-
children: [],
|
|
123294
|
-
position: node.position,
|
|
123295
|
-
};
|
|
123296
|
-
};
|
|
123297
|
-
/**
|
|
123298
|
-
* Transform paragraph-wrapped self-closing JSX components into mdxJsxFlowElement nodes.
|
|
123299
|
-
*
|
|
123300
|
-
* CommonMark wraps multi-line JSX in paragraphs when the opening tag isn't complete
|
|
123301
|
-
* on one line. This plugin detects these structures and unwraps them for components
|
|
123302
|
-
* in the SELF_CLOSING_BLOCK_TAGS allowlist.
|
|
123303
|
-
*
|
|
123304
|
-
* Input structure:
|
|
123305
|
-
* ```
|
|
123306
|
-
* paragraph > html: "<Embed\n typeOfEmbed=\"youtube\"\n/>"
|
|
123307
|
-
* ```
|
|
123308
|
-
*
|
|
123309
|
-
* Output structure:
|
|
123310
|
-
* ```
|
|
123311
|
-
* mdxJsxFlowElement: { name: "Embed", attributes: [...], children: [] }
|
|
123312
|
-
* ```
|
|
123313
|
-
*/
|
|
123314
|
-
const mdxishSelfClosingBlocks = () => tree => {
|
|
123315
|
-
visit(tree, 'paragraph', (node, index, parent) => {
|
|
123316
|
-
if (index === undefined || !parent)
|
|
123317
|
-
return;
|
|
123318
|
-
const mdxNode = tryConvertToMdxNode(node);
|
|
123319
|
-
if (mdxNode) {
|
|
123320
|
-
parent.children.splice(index, 1, mdxNode);
|
|
123321
|
-
}
|
|
123322
|
-
});
|
|
123323
|
-
};
|
|
123324
|
-
/* harmony default export */ const self_closing_blocks = (mdxishSelfClosingBlocks);
|
|
123325
|
-
|
|
123326
|
-
;// ./processor/transform/mdxish/components/snake-case-components.ts
|
|
123327
|
-
|
|
123328
|
-
|
|
123329
|
-
/**
|
|
123330
|
-
* Replaces snake_case component names with valid HTML placeholders.
|
|
123331
|
-
* Required because remark-parse rejects tags with underscores.
|
|
123332
|
-
* Example: `<Snake_case />` → `<MDXishSnakeCase0 />`
|
|
123333
|
-
*
|
|
123334
|
-
* Code blocks and inline code are protected and will not be transformed.
|
|
123335
|
-
*
|
|
123336
|
-
* @param content - The markdown content to process
|
|
123337
|
-
* @param options - Options including knownComponents to filter by
|
|
123338
|
-
*/
|
|
123339
|
-
function processSnakeCaseComponent(content, options = {}) {
|
|
123340
|
-
const { knownComponents } = options;
|
|
123341
|
-
// Early exit if no potential snake_case components
|
|
123342
|
-
if (!/[A-Z][A-Za-z0-9]*_[A-Za-z0-9_]*/.test(content)) {
|
|
123343
|
-
return { content, mapping: {} };
|
|
123344
|
-
}
|
|
123345
|
-
// Step 1: Extract code blocks to protect them from transformation
|
|
123346
|
-
const { protectedCode, protectedContent } = protectCodeBlocks(content);
|
|
123347
|
-
// Find the highest existing placeholder number to avoid collisions
|
|
123348
|
-
// e.g., if content has <MDXishSnakeCase0 />, start counter from 1
|
|
123349
|
-
const placeholderPattern = /MDXishSnakeCase(\d+)/g;
|
|
123350
|
-
let startCounter = 0;
|
|
123351
|
-
let placeholderMatch;
|
|
123352
|
-
while ((placeholderMatch = placeholderPattern.exec(content)) !== null) {
|
|
123353
|
-
const num = parseInt(placeholderMatch[1], 10);
|
|
123354
|
-
if (num >= startCounter) {
|
|
123355
|
-
startCounter = num + 1;
|
|
123356
|
-
}
|
|
123357
|
-
}
|
|
123358
|
-
const mapping = {};
|
|
123359
|
-
const reverseMap = new Map();
|
|
123360
|
-
let counter = startCounter;
|
|
123361
|
-
// Step 2: Transform snake_case components in non-code content
|
|
123362
|
-
const processedContent = protectedContent.replace(componentTagPattern, (match, tagName, attrs, selfClosing) => {
|
|
123363
|
-
if (!tagName.includes('_')) {
|
|
123364
|
-
return match;
|
|
123365
|
-
}
|
|
123366
|
-
const isClosing = tagName.startsWith('/');
|
|
123367
|
-
const cleanTagName = isClosing ? tagName.slice(1) : tagName;
|
|
123368
|
-
// Only transform if it's a known component (or if no filter is provided)
|
|
123369
|
-
if (knownComponents && !knownComponents.has(cleanTagName)) {
|
|
123370
|
-
return match;
|
|
123371
|
-
}
|
|
123372
|
-
let placeholder = reverseMap.get(cleanTagName);
|
|
123373
|
-
if (!placeholder) {
|
|
123374
|
-
// eslint-disable-next-line no-plusplus
|
|
123375
|
-
placeholder = `MDXishSnakeCase${counter++}`;
|
|
123376
|
-
mapping[placeholder] = cleanTagName;
|
|
123377
|
-
reverseMap.set(cleanTagName, placeholder);
|
|
123378
|
-
}
|
|
123379
|
-
const processedTagName = isClosing ? `/${placeholder}` : placeholder;
|
|
123380
|
-
return `<${processedTagName}${attrs}${selfClosing}>`;
|
|
123381
|
-
});
|
|
123382
|
-
// Step 3: Restore code blocks (untouched)
|
|
123383
|
-
const finalContent = restoreCodeBlocks(processedContent, protectedCode);
|
|
123384
|
-
return {
|
|
123385
|
-
content: finalContent,
|
|
123386
|
-
mapping,
|
|
123387
|
-
};
|
|
123388
|
-
}
|
|
123389
|
-
/**
|
|
123390
|
-
* Restores placeholder name to original snake_case name.
|
|
123391
|
-
* Uses case-insensitive matching since HTML parsers normalize to lowercase.
|
|
123392
|
-
*/
|
|
123393
|
-
function restoreSnakeCase(placeholderName, mapping) {
|
|
123394
|
-
if (mapping[placeholderName]) {
|
|
123395
|
-
return mapping[placeholderName];
|
|
123396
|
-
}
|
|
123397
|
-
const lowerName = placeholderName.toLowerCase();
|
|
123398
|
-
const matchingKey = Object.keys(mapping).find(key => key.toLowerCase() === lowerName);
|
|
123399
|
-
return matchingKey ? mapping[matchingKey] : placeholderName;
|
|
123400
|
-
}
|
|
123401
|
-
|
|
123402
123361
|
;// ./processor/transform/mdxish/resolve-esm-imports.ts
|
|
123403
123362
|
|
|
123404
123363
|
// We provide React as a default module so that components can use hooks
|
|
@@ -123583,6 +123542,54 @@ const containsJsxNode = (value) => {
|
|
|
123583
123542
|
return true;
|
|
123584
123543
|
return Object.values(value).some(containsJsxNode);
|
|
123585
123544
|
};
|
|
123545
|
+
/** Read the component name off a JSX element name node (`Foo`, `Foo.Bar`, `foo:Bar`). */
|
|
123546
|
+
const jsxElementName = (name) => {
|
|
123547
|
+
if (name === null || typeof name !== 'object')
|
|
123548
|
+
return undefined;
|
|
123549
|
+
const node = name;
|
|
123550
|
+
if (node.type === 'JSXIdentifier')
|
|
123551
|
+
return typeof node.name === 'string' ? node.name : undefined;
|
|
123552
|
+
// `<Foo.Bar/>` and `<foo:Bar/>` resolve through their leftmost part.
|
|
123553
|
+
if (node.type === 'JSXMemberExpression')
|
|
123554
|
+
return jsxElementName(node.object);
|
|
123555
|
+
if (node.type === 'JSXNamespacedName')
|
|
123556
|
+
return jsxElementName(node.namespace);
|
|
123557
|
+
return undefined;
|
|
123558
|
+
};
|
|
123559
|
+
/**
|
|
123560
|
+
* Collect the capitalized names an expression uses as JSX tags. Parsed rather than pattern
|
|
123561
|
+
* matched: `{count < Max ? <Foo/> : <Bar/>}` puts a capitalized name straight after a `<` without
|
|
123562
|
+
* it being a tag, and only the parser can tell the two apart. Unparseable input yields nothing —
|
|
123563
|
+
* evaluation is about to throw on it anyway.
|
|
123564
|
+
*/
|
|
123565
|
+
const jsxComponentNames = (expression) => {
|
|
123566
|
+
let program;
|
|
123567
|
+
try {
|
|
123568
|
+
program = parseExpression(expression);
|
|
123569
|
+
}
|
|
123570
|
+
catch {
|
|
123571
|
+
return [];
|
|
123572
|
+
}
|
|
123573
|
+
const names = new Set();
|
|
123574
|
+
const walk = (value) => {
|
|
123575
|
+
if (Array.isArray(value)) {
|
|
123576
|
+
value.forEach(walk);
|
|
123577
|
+
return;
|
|
123578
|
+
}
|
|
123579
|
+
if (value === null || typeof value !== 'object')
|
|
123580
|
+
return;
|
|
123581
|
+
const node = value;
|
|
123582
|
+
if (node.type === 'JSXOpeningElement') {
|
|
123583
|
+
const name = jsxElementName(node.name);
|
|
123584
|
+
// Lowercase tags compile to a string type, never a variable reference.
|
|
123585
|
+
if (name && /^[A-Z]/.test(name))
|
|
123586
|
+
names.add(name);
|
|
123587
|
+
}
|
|
123588
|
+
Object.values(node).forEach(walk);
|
|
123589
|
+
};
|
|
123590
|
+
walk(program);
|
|
123591
|
+
return Array.from(names);
|
|
123592
|
+
};
|
|
123586
123593
|
/** Convert a program's JSX into `React.createElement` calls and evaluate it. `scope` must provide `React`. */
|
|
123587
123594
|
const evalJsxProgram = (program, scope) => {
|
|
123588
123595
|
buildJsx(program, { runtime: 'classic', pragma: 'React.createElement', pragmaFrag: 'React.Fragment' });
|
|
@@ -123769,6 +123776,28 @@ function reactElementToHast(node) {
|
|
|
123769
123776
|
|
|
123770
123777
|
|
|
123771
123778
|
|
|
123779
|
+
|
|
123780
|
+
|
|
123781
|
+
|
|
123782
|
+
|
|
123783
|
+
/**
|
|
123784
|
+
* Bind the components a given expression actually uses as tags. Scoped per expression on purpose:
|
|
123785
|
+
* binding the whole hash would shadow same-named globals (a `math` component would break
|
|
123786
|
+
* `{Math.max(1, 2)}`) and pad `evaluate`'s `new Function` parameter list. Resolution defers to
|
|
123787
|
+
* `getComponentName` so an expression and a plain tag always reach the same component.
|
|
123788
|
+
*/
|
|
123789
|
+
const componentScope = (expression, components) => {
|
|
123790
|
+
const scope = {};
|
|
123791
|
+
jsxComponentNames(expression).forEach(name => {
|
|
123792
|
+
// `getComponentName` normalizes the tag, never the key, so it can't match `<MyBlock/>` to a
|
|
123793
|
+
// `my_block` entry; compare the key's PascalCase form for that direction.
|
|
123794
|
+
const tagName = getComponentName(name, components) ?? Object.keys(components).find(k => toPascalCase(k) === name);
|
|
123795
|
+
if (!tagName)
|
|
123796
|
+
return;
|
|
123797
|
+
scope[name] = (props) => external_react_default().createElement(tagName, props);
|
|
123798
|
+
});
|
|
123799
|
+
return scope;
|
|
123800
|
+
};
|
|
123772
123801
|
/**
|
|
123773
123802
|
* We divide the result of an expression into two categories:
|
|
123774
123803
|
* 1. Renderable values: HTML, JSX, e.g. .map() returning JSX
|
|
@@ -123779,6 +123808,17 @@ const isRenderable = (value) => {
|
|
|
123779
123808
|
return true;
|
|
123780
123809
|
return Array.isArray(value) && value.some(isRenderable);
|
|
123781
123810
|
};
|
|
123811
|
+
/**
|
|
123812
|
+
* Whether an expression evaluated to block-level content. Components count as block-level
|
|
123813
|
+
* unless they're on the inline list, matching the assumption the rest of the component
|
|
123814
|
+
* pipeline makes.
|
|
123815
|
+
*/
|
|
123816
|
+
const isBlockResult = (children) => children.some(child => {
|
|
123817
|
+
if (child.type !== 'element' && child.type !== 'mdx-jsx')
|
|
123818
|
+
return false;
|
|
123819
|
+
const { tagName } = child;
|
|
123820
|
+
return !STANDARD_HTML_TAGS.has(tagName.toLowerCase()) && !INLINE_COMPONENT_TAGS.has(tagName);
|
|
123821
|
+
});
|
|
123782
123822
|
/** Turn a non-renderable evaluation result into a text node. */
|
|
123783
123823
|
const createTextNode = (result, position) => {
|
|
123784
123824
|
if (result === null || result === undefined)
|
|
@@ -123790,13 +123830,22 @@ const createTextNode = (result, position) => {
|
|
|
123790
123830
|
/**
|
|
123791
123831
|
* AST transformer to evaluate MDX expressions.
|
|
123792
123832
|
* Replaces mdxFlowExpression and mdxTextExpression nodes with their evaluated values.
|
|
123793
|
-
* Self-contained expressions resolve directly (e.g. `{1+1}`); expressions that
|
|
123794
|
-
*
|
|
123795
|
-
* earlier `export const/function` (collected onto
|
|
123796
|
-
* Anything else falls through to the error branch and is kept as
|
|
123797
|
-
|
|
123798
|
-
|
|
123799
|
-
|
|
123833
|
+
* Self-contained expressions resolve directly (e.g. `{1+1}`); expressions that reference
|
|
123834
|
+
* identifiers can resolve if those identifiers are a custom component, the `user` variables
|
|
123835
|
+
* object, or were introduced by an earlier `export const/function` (collected onto
|
|
123836
|
+
* `file.data.mdxishScope`). Anything else falls through to the error branch and is kept as
|
|
123837
|
+
* literal `{...}` text.
|
|
123838
|
+
*/
|
|
123839
|
+
const evaluateExpressions = ({ components, variables } = {}) => (tree, file) => {
|
|
123840
|
+
const baseScope = {
|
|
123841
|
+
// `User` matches the fallback the MDX path binds in `run.tsx`. Only when variables were
|
|
123842
|
+
// supplied: the proxy never throws, so an unconditional bind would resolve `user.*` on
|
|
123843
|
+
// surfaces that render without them instead of leaving literal text.
|
|
123844
|
+
...(variables ? { user: user(variables) } : {}),
|
|
123845
|
+
// In-document exports win, matching `renderMdxish`.
|
|
123846
|
+
...file.data.mdxishScope,
|
|
123847
|
+
React: (external_react_default()),
|
|
123848
|
+
};
|
|
123800
123849
|
visit(tree, ['mdxFlowExpression', 'mdxTextExpression'], (node, index, parent) => {
|
|
123801
123850
|
if (!parent || index === null || index === undefined)
|
|
123802
123851
|
return;
|
|
@@ -123806,6 +123855,7 @@ const evaluateExpressions = () => (tree, file) => {
|
|
|
123806
123855
|
if (!expression)
|
|
123807
123856
|
return;
|
|
123808
123857
|
try {
|
|
123858
|
+
const scope = { ...componentScope(expression, components ?? {}), ...baseScope };
|
|
123809
123859
|
const result = evalExpression(expression, scope);
|
|
123810
123860
|
if (isRenderable(result)) {
|
|
123811
123861
|
// Stash hast built straight from the React tree; `mdxExpressionHandler` emits it and it
|
|
@@ -123826,6 +123876,22 @@ const evaluateExpressions = () => (tree, file) => {
|
|
|
123826
123876
|
parent.children.splice(index, 1, { type: 'text', value: `{${processed}}`, position });
|
|
123827
123877
|
}
|
|
123828
123878
|
});
|
|
123879
|
+
// A text expression is parsed inside a paragraph, but its result can be block content: a
|
|
123880
|
+
// `<Tabs>` renders a `<div>`, and a browser closes the `<p>` before it, so the DOM it builds
|
|
123881
|
+
// no longer matches what was rendered and hydration fails. Lift the expression out when
|
|
123882
|
+
// that's all the paragraph holds.
|
|
123883
|
+
visit(tree, 'paragraph', (node, index, parent) => {
|
|
123884
|
+
if (!parent || index === null || index === undefined)
|
|
123885
|
+
return;
|
|
123886
|
+
const meaningful = node.children.filter(child => !(child.type === 'text' && !child.value.trim()));
|
|
123887
|
+
const [only] = meaningful;
|
|
123888
|
+
if (meaningful.length !== 1 || only.type !== 'mdxTextExpression')
|
|
123889
|
+
return;
|
|
123890
|
+
const hChildren = only.data?.hChildren;
|
|
123891
|
+
if (!hChildren || !isBlockResult(hChildren))
|
|
123892
|
+
return;
|
|
123893
|
+
parent.children.splice(index, 1, only);
|
|
123894
|
+
});
|
|
123829
123895
|
return tree;
|
|
123830
123896
|
};
|
|
123831
123897
|
/* harmony default export */ const evaluate_expressions = (evaluateExpressions);
|
|
@@ -127053,45 +127119,6 @@ const resolveDeferredAttributeExpressionProps = () => (tree, file) => {
|
|
|
127053
127119
|
};
|
|
127054
127120
|
/* harmony default export */ const resolve_deferred_attribute_expression_props = (resolveDeferredAttributeExpressionProps);
|
|
127055
127121
|
|
|
127056
|
-
;// ./processor/transform/mdxish/restore-snake-case-component-name.ts
|
|
127057
|
-
|
|
127058
|
-
|
|
127059
|
-
/**
|
|
127060
|
-
* Restores snake_case component names from placeholders after parsing.
|
|
127061
|
-
* Runs after mdxishComponentBlocks converts HTML nodes to mdxJsxFlowElement.
|
|
127062
|
-
*/
|
|
127063
|
-
const restoreSnakeCaseComponentNames = (options) => {
|
|
127064
|
-
const { mapping } = options;
|
|
127065
|
-
return tree => {
|
|
127066
|
-
if (!mapping || Object.keys(mapping).length === 0) {
|
|
127067
|
-
return tree;
|
|
127068
|
-
}
|
|
127069
|
-
visit(tree, 'mdxJsxFlowElement', (node) => {
|
|
127070
|
-
if (node.name) {
|
|
127071
|
-
node.name = restoreSnakeCase(node.name, mapping);
|
|
127072
|
-
}
|
|
127073
|
-
});
|
|
127074
|
-
// Pre-compile regex patterns for better performance
|
|
127075
|
-
const regexPatterns = Object.entries(mapping).map(([placeholder, original]) => ({
|
|
127076
|
-
regex: new RegExp(`(<\\/?)(${placeholder})(\\s|\\/?>)`, 'gi'),
|
|
127077
|
-
original,
|
|
127078
|
-
}));
|
|
127079
|
-
visit(tree, 'html', (node) => {
|
|
127080
|
-
if (node.value) {
|
|
127081
|
-
let newValue = node.value;
|
|
127082
|
-
regexPatterns.forEach(({ regex, original }) => {
|
|
127083
|
-
newValue = newValue.replace(regex, `$1${original}$3`);
|
|
127084
|
-
});
|
|
127085
|
-
if (newValue !== node.value) {
|
|
127086
|
-
node.value = newValue;
|
|
127087
|
-
}
|
|
127088
|
-
}
|
|
127089
|
-
});
|
|
127090
|
-
return tree;
|
|
127091
|
-
};
|
|
127092
|
-
};
|
|
127093
|
-
/* harmony default export */ const restore_snake_case_component_name = (restoreSnakeCaseComponentNames);
|
|
127094
|
-
|
|
127095
127122
|
;// ./processor/transform/mdxish/retain-boolean-attributes.ts
|
|
127096
127123
|
|
|
127097
127124
|
// Private Use Area character (U+E000) which is extremely unlikely to appear in real content.
|
|
@@ -127614,9 +127641,6 @@ function loadComponents() {
|
|
|
127614
127641
|
|
|
127615
127642
|
|
|
127616
127643
|
|
|
127617
|
-
|
|
127618
|
-
|
|
127619
|
-
|
|
127620
127644
|
|
|
127621
127645
|
|
|
127622
127646
|
|
|
@@ -127638,10 +127662,8 @@ const defaultTransformers = [
|
|
|
127638
127662
|
* 5. Terminate HTML flow blocks so subsequent content isn't swallowed
|
|
127639
127663
|
* 6. Close invalid "self-closing" HTML tags (e.g., `<i />` → `<i></i>`)
|
|
127640
127664
|
* 7. Normalize compact ATX headings (e.g., `#Heading` → `# Heading`)
|
|
127641
|
-
* 8. Replace snake_case component names with parser-safe placeholders
|
|
127642
127665
|
*/
|
|
127643
|
-
function preprocessContent(content
|
|
127644
|
-
const { knownComponents } = opts;
|
|
127666
|
+
function preprocessContent(content) {
|
|
127645
127667
|
// Runs first so `jsxTable` sees a literal `</table>` (and the HTML-line
|
|
127646
127668
|
// classification in `terminateHtmlFlowBlocks` is accurate)
|
|
127647
127669
|
let result = normalizeClosingTagWhitespace(content);
|
|
@@ -127654,7 +127676,7 @@ function preprocessContent(content, opts) {
|
|
|
127654
127676
|
result = terminateHtmlFlowBlocks(result);
|
|
127655
127677
|
result = closeSelfClosingHtmlTags(result);
|
|
127656
127678
|
result = normalizeCompactHeadings(result);
|
|
127657
|
-
return
|
|
127679
|
+
return result;
|
|
127658
127680
|
}
|
|
127659
127681
|
function mdxishAstProcessor(mdContent, opts = {}) {
|
|
127660
127682
|
const { components: userComponents = {}, hardBreaks: enableHardBreaks = true, newEditorTypes = false, safeMode = false, useTailwind, } = opts;
|
|
@@ -127662,9 +127684,7 @@ function mdxishAstProcessor(mdContent, opts = {}) {
|
|
|
127662
127684
|
...loadComponents(),
|
|
127663
127685
|
...userComponents,
|
|
127664
127686
|
};
|
|
127665
|
-
|
|
127666
|
-
const knownComponents = new Set(Object.keys(components));
|
|
127667
|
-
const { content: parserReadyContent, mapping: snakeCaseMapping } = preprocessContent(mdContent, { knownComponents });
|
|
127687
|
+
const parserReadyContent = preprocessContent(mdContent);
|
|
127668
127688
|
// Create string map for tailwind transformer
|
|
127669
127689
|
const tempComponentsMap = Object.entries(components).reduce((acc, [key, value]) => {
|
|
127670
127690
|
acc[key] = String(value);
|
|
@@ -127677,10 +127697,8 @@ function mdxishAstProcessor(mdContent, opts = {}) {
|
|
|
127677
127697
|
.use(remarkParse)
|
|
127678
127698
|
.use(remarkFrontmatter)
|
|
127679
127699
|
.use(normalize_malformed_md_syntax)
|
|
127680
|
-
.use(self_closing_blocks)
|
|
127681
127700
|
.use(mdx_blocks, { safeMode })
|
|
127682
127701
|
.use(inline_html, { safeMode })
|
|
127683
|
-
.use(restore_snake_case_component_name, { mapping: snakeCaseMapping })
|
|
127684
127702
|
.use(mdxish_tables)
|
|
127685
127703
|
.use(mdxish_html_blocks) // Convert every <HTMLBlock> shape → html-block
|
|
127686
127704
|
// The next few transformers must appear after mdxishMdxComponentBlocks
|
|
@@ -127756,7 +127774,7 @@ function mdxish(mdContent, opts = {}) {
|
|
|
127756
127774
|
processor
|
|
127757
127775
|
.use(safeMode ? undefined : evaluate_exports) // Evaluate `export const/function` and stash scope on file.data.mdxishScope
|
|
127758
127776
|
.use(enableHardBreaks ? hard_breaks : undefined) // Must precede evaluateExpressions to avoid splitting the \n in an evaluated template literal into a <br> node
|
|
127759
|
-
.use(safeMode ? undefined : evaluate_expressions) // Evaluate self-contained MDX expressions (e.g. `{1+1}`)
|
|
127777
|
+
.use(safeMode ? undefined : evaluate_expressions, { components, variables }) // Evaluate self-contained MDX expressions (e.g. `{1+1}`)
|
|
127760
127778
|
.use(safeMode ? undefined : evaluate_style_block_expressions) // Evaluate `<style>{`...`}</style>` template literals into plain CSS
|
|
127761
127779
|
.use(variables_code, { variables }) // Resolve <<...>> and {user.*} inside code and inline code nodes
|
|
127762
127780
|
.use(remarkRehype, { allowDangerousHtml: true, handlers: mdxComponentHandlers })
|
|
@@ -128847,7 +128865,7 @@ module.exports = /*#__PURE__*/JSON.parse('{"*":["about","acceptCharset","accessK
|
|
|
128847
128865
|
/******/ // startup
|
|
128848
128866
|
/******/ // Load entry module and return exports
|
|
128849
128867
|
/******/ // This entry module used 'module' so it can't be inlined
|
|
128850
|
-
/******/ let __webpack_exports__ = __webpack_require__(
|
|
128868
|
+
/******/ let __webpack_exports__ = __webpack_require__(8691);
|
|
128851
128869
|
/******/ module.exports = __webpack_exports__;
|
|
128852
128870
|
/******/
|
|
128853
128871
|
/******/ })()
|