@readme/markdown 15.3.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/dist/main.node.js CHANGED
@@ -19045,7 +19045,7 @@ module.exports = function () {
19045
19045
 
19046
19046
  /***/ },
19047
19047
 
19048
- /***/ 5814
19048
+ /***/ 8719
19049
19049
  (module, __webpack_exports__, __webpack_require__) {
19050
19050
 
19051
19051
  "use strict";
@@ -19186,6 +19186,63 @@ __webpack_require__.d(util_types_namespaceObject, {
19186
19186
  // EXTERNAL MODULE: external "react"
19187
19187
  var external_react_ = __webpack_require__(4953);
19188
19188
  var external_react_default = /*#__PURE__*/__webpack_require__.n(external_react_);
19189
+ ;// ./hooks/useRestartAnimatedImages/index.tsx
19190
+
19191
+ /**
19192
+ * GIF is the one extension that implies animation. `.webp` and `.png` (the extension an APNG
19193
+ * ships under) are overwhelmingly static, so matching them would reload far more images than
19194
+ * it rewinds; telling those apart needs the bytes, not the URL.
19195
+ */
19196
+ const ANIMATED_IMAGE_SRC = /\.gif(\?|#|$)/i;
19197
+ const restartAnimatedImages = (root) => {
19198
+ root?.querySelectorAll('img').forEach(img => {
19199
+ if (!ANIMATED_IMAGE_SRC.test(img.src))
19200
+ return;
19201
+ const { src } = img;
19202
+ // There's no seek API for animated images; reassigning `src` is the only reset
19203
+ img.src = '';
19204
+ img.src = src;
19205
+ });
19206
+ };
19207
+ const useRestartAfterFirstPaint = (revealKey, restart) => {
19208
+ const isFirstRender = (0,external_react_.useRef)(true);
19209
+ const latestRestart = (0,external_react_.useRef)(restart);
19210
+ latestRestart.current = restart;
19211
+ (0,external_react_.useEffect)(() => {
19212
+ if (isFirstRender.current) {
19213
+ isFirstRender.current = false;
19214
+ return;
19215
+ }
19216
+ latestRestart.current();
19217
+ }, [revealKey]);
19218
+ };
19219
+ /**
19220
+ * Restarts GIF playback inside the panel that just became active.
19221
+ *
19222
+ * Tabs keep every panel mounted, so an <img> is never re-created and a GIF is
19223
+ * shown mid-loop (or frozen on its last frame) when its tab is re-selected.
19224
+ *
19225
+ * @returns a ref to attach to each panel element, indexed by tab.
19226
+ */
19227
+ function useRestartAnimatedImages(activeIndex) {
19228
+ const panelRefs = (0,external_react_.useRef)([]);
19229
+ useRestartAfterFirstPaint(activeIndex, () => restartAnimatedImages(panelRefs.current[activeIndex]));
19230
+ return panelRefs;
19231
+ }
19232
+ /**
19233
+ * Restarts GIF playback inside an element that has just been revealed after being hidden —
19234
+ * a collapsed <details>, whose images have been animating out of sight since page load.
19235
+ */
19236
+ function useRestartAnimatedImagesOnReveal(isRevealed) {
19237
+ const contentRef = (0,external_react_.useRef)(null);
19238
+ useRestartAfterFirstPaint(isRevealed, () => {
19239
+ // Rewinding content the reader just hid would be wasted work
19240
+ if (isRevealed)
19241
+ restartAnimatedImages(contentRef.current);
19242
+ });
19243
+ return contentRef;
19244
+ }
19245
+
19189
19246
  ;// ./components/Icon/index.tsx
19190
19247
 
19191
19248
  /** @see https://docs-v5.fontawesome.com/web/reference-icons */
@@ -19210,14 +19267,16 @@ const Icon = ({ className, faClassName, icon, iconColor }) => {
19210
19267
 
19211
19268
 
19212
19269
 
19270
+
19213
19271
  const Accordion = ({ children, icon, iconColor, title }) => {
19214
19272
  const [isOpen, setIsOpen] = (0,external_react_.useState)(false);
19273
+ const contentRef = useRestartAnimatedImagesOnReveal(isOpen);
19215
19274
  return (external_react_default().createElement("details", { className: "Accordion", onToggle: () => setIsOpen(!isOpen) },
19216
19275
  external_react_default().createElement("summary", { className: "Accordion-title" },
19217
19276
  external_react_default().createElement("i", { className: `Accordion-toggleIcon${isOpen ? '_opened' : ''} fa fa-regular fa-chevron-right` }),
19218
19277
  icon && external_react_default().createElement(components_Icon, { className: "Accordion-icon", icon: icon, iconColor: iconColor }),
19219
19278
  title),
19220
- external_react_default().createElement("div", { className: "Accordion-content" }, children)));
19279
+ external_react_default().createElement("div", { ref: contentRef, className: "Accordion-content" }, children)));
19221
19280
  };
19222
19281
  /* harmony default export */ const components_Accordion = (Accordion);
19223
19282
 
@@ -25080,11 +25139,13 @@ function TableOfContents({ children }) {
25080
25139
 
25081
25140
 
25082
25141
 
25142
+
25083
25143
  const Tab = ({ children }) => {
25084
25144
  return external_react_default().createElement("div", { className: "TabContent" }, children);
25085
25145
  };
25086
25146
  const Tabs = ({ children }) => {
25087
25147
  const [activeTab, setActiveTab] = (0,external_react_.useState)(0);
25148
+ const panelRefs = useRestartAnimatedImages(activeTab);
25088
25149
  // React passes `children` as a single element when there's only one child, so normalize.
25089
25150
  const tabs = external_react_default().Children.toArray(children);
25090
25151
  return (external_react_default().createElement("div", { className: "TabGroup" },
@@ -25092,7 +25153,9 @@ const Tabs = ({ children }) => {
25092
25153
  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) },
25093
25154
  tab.props.icon && (external_react_default().createElement(components_Icon, { className: "TabGroup-icon", icon: tab.props.icon, iconColor: tab.props.iconColor })),
25094
25155
  tab.props.title))))),
25095
- external_react_default().createElement("section", null, tabs.map((tab, index) => (external_react_default().createElement("div", { key: tab.key, hidden: index !== activeTab }, tab))))));
25156
+ external_react_default().createElement("section", null, tabs.map((tab, index) => (external_react_default().createElement("div", { key: tab.key, ref: el => {
25157
+ panelRefs.current[index] = el;
25158
+ }, hidden: index !== activeTab }, tab))))));
25096
25159
  };
25097
25160
  /* harmony default export */ const components_Tabs = (Tabs);
25098
25161
 
@@ -25260,7 +25323,7 @@ async function loadStylesheet(id, base) {
25260
25323
  async function loadModule() {
25261
25324
  throw new Error('The browser build does not support plugins or config files.');
25262
25325
  }
25263
- async function createCompiler({ darkModeDataAttribute }) {
25326
+ async function createCompiler({ darkModeDataAttribute, darkModeRootSelector, }) {
25264
25327
  let css = `
25265
25328
  @layer theme, base, components, utilities;
25266
25329
 
@@ -25268,9 +25331,12 @@ async function createCompiler({ darkModeDataAttribute }) {
25268
25331
  @import "tailwindcss/utilities.css" layer(utilities);
25269
25332
  `;
25270
25333
  if (darkModeDataAttribute) {
25334
+ // Anchors `dark:` to `darkModeRootSelector`'s own attribute instead of any ancestor's,
25335
+ // when supplied — see the `darkModeRootSelector` param doc below for why there's no default.
25336
+ const root = darkModeRootSelector || '';
25271
25337
  css += `
25272
25338
 
25273
- @custom-variant dark (&:where([${darkModeDataAttribute}=dark], [${darkModeDataAttribute}=dark] *));`;
25339
+ @custom-variant dark (&:where(${root}[${darkModeDataAttribute}=dark], ${root}[${darkModeDataAttribute}=dark] *));`;
25274
25340
  }
25275
25341
  return Hu(css, {
25276
25342
  base: '/',
@@ -25278,8 +25344,8 @@ async function createCompiler({ darkModeDataAttribute }) {
25278
25344
  loadModule,
25279
25345
  });
25280
25346
  }
25281
- async function tailwindCompiler(classes, { prefix, darkModeDataAttribute }) {
25282
- const compiler = await createCompiler({ darkModeDataAttribute });
25347
+ async function tailwindCompiler(classes, { prefix, darkModeDataAttribute, darkModeRootSelector, }) {
25348
+ const compiler = await createCompiler({ darkModeDataAttribute, darkModeRootSelector });
25283
25349
  const css = compiler.build(Array.from(classes));
25284
25350
  return lib_postcss([postcss_prefix_selector_default()({ prefix })]).process(css, { from: undefined });
25285
25351
  }
@@ -25294,7 +25360,7 @@ const traverse = (node, callback) => {
25294
25360
  traverse(child, callback);
25295
25361
  });
25296
25362
  };
25297
- const TailwindStyle = ({ children, darkModeDataAttribute }) => {
25363
+ const TailwindStyle = ({ children, darkModeDataAttribute, darkModeRootSelector }) => {
25298
25364
  const [stylesheet, setStylesheet] = (0,external_react_.useState)('');
25299
25365
  const classesSet = (0,external_react_.useRef)(new Set());
25300
25366
  const ref = (0,external_react_.useRef)(null);
@@ -25318,6 +25384,7 @@ const TailwindStyle = ({ children, darkModeDataAttribute }) => {
25318
25384
  const sheet = await tailwindCompiler(classes, {
25319
25385
  prefix: `.${tailwindPrefix}`,
25320
25386
  darkModeDataAttribute,
25387
+ darkModeRootSelector,
25321
25388
  });
25322
25389
  /* @note: don't insert an empty stylesheet */
25323
25390
  if (sheet.css.match(/^@layer utilities;/m))
@@ -25325,7 +25392,7 @@ const TailwindStyle = ({ children, darkModeDataAttribute }) => {
25325
25392
  setStylesheet(sheet.css);
25326
25393
  };
25327
25394
  run();
25328
- }, [classes, darkModeDataAttribute]);
25395
+ }, [classes, darkModeDataAttribute, darkModeRootSelector]);
25329
25396
  /*
25330
25397
  * @note: execute once on load
25331
25398
  */
@@ -96569,6 +96636,10 @@ function syntax_createTokenize(mode) {
96569
96636
  effects.consume(code);
96570
96637
  return inBraceExpr;
96571
96638
  }
96639
+ // A raw `<` can't sit in an opening tag (quotes/braces are handled above);
96640
+ // bailing bounds each attempt to the next `<` instead of rescanning the line.
96641
+ if (code === codes.lessThan)
96642
+ return nok(code);
96572
96643
  effects.consume(code);
96573
96644
  return afterOpenTagName;
96574
96645
  }
@@ -121775,7 +121846,7 @@ const listMarkerRegex = /^(?:[*+-]|\d+[.)])(?:([\r\n]| {1,3})|$)/;
121775
121846
  * with their checkbox intact (for example, `- [ ]`) instead of dropping it
121776
121847
  * We can add more adjustments if needed
121777
121848
  */
121778
- const compile_list_item_listItem = (node, parent, state, info) => {
121849
+ const compile_list_item_listItem = ((node, parent, state, info) => {
121779
121850
  const head = node.children[0];
121780
121851
  const isCheckbox = typeof node.checked === 'boolean' && head && head.type === 'paragraph';
121781
121852
  if (!isCheckbox) {
@@ -121799,15 +121870,47 @@ const compile_list_item_listItem = (node, parent, state, info) => {
121799
121870
  return `${marker}${actualSeparator}${checkbox}`;
121800
121871
  });
121801
121872
  return value;
121802
- };
121873
+ });
121803
121874
  /* harmony default export */ const list_item = (compile_list_item_listItem);
121804
121875
 
121805
121876
  ;// ./processor/compile/plain.ts
121806
121877
  const plain_plain = (node) => node.value;
121807
121878
  /* harmony default export */ const compile_plain = (plain_plain);
121808
121879
 
121880
+ ;// ./processor/compile/table-cell-block-markers.ts
121881
+ const CELL_START_BLOCK_MARKER = /^(?:[-+](?=\s|$)|#{1,6}(?=\s|$)|>)/;
121882
+ const BR_HTML = /^\s*<br\s*\/?>\s*$/i;
121883
+ const isLineBreak = (node) => {
121884
+ if (node.type === 'break')
121885
+ return true;
121886
+ if (node.type === 'html')
121887
+ return BR_HTML.test(node.value);
121888
+ return node.type === 'mdxJsxTextElement' && 'name' in node && node.name?.toLowerCase() === 'br';
121889
+ };
121890
+ const followsLineBreak = (node, parent) => {
121891
+ const siblings = (parent?.children ?? []);
121892
+ const previous = siblings[siblings.indexOf(node) - 1];
121893
+ return !!previous && isLineBreak(previous);
121894
+ };
121895
+ /**
121896
+ * Re-escapes a leading block marker (`-`, `+`, `#`, `>`) at the start of a table cell, or
121897
+ * after a `<br />` within one.
121898
+ *
121899
+ * A cell is serialized mid-line, so the `atBreak` patterns that escape these characters
121900
+ * never fire and an author's `| \- one |` round trips to `| - one |` (RM-17203). Cells are
121901
+ * serialized as `containerPhrasing(cell, { before: '|' })`, so `|` marks the cell start.
121902
+ */
121903
+ const escapeCellStartBlockMarker = (serialized, node, parent, state, info) => {
121904
+ if (serialized.startsWith('\\') || !CELL_START_BLOCK_MARKER.test(serialized))
121905
+ return serialized;
121906
+ if (!state.stack.includes('tableCell') || !(info.before === '|' || followsLineBreak(node, parent)))
121907
+ return serialized;
121908
+ return `\\${serialized}`;
121909
+ };
121910
+
121809
121911
  ;// ./processor/compile/text.ts
121810
121912
 
121913
+
121811
121914
  // A `_` flanked by word characters can never open or close emphasis under
121812
121915
  // CommonMark's flanking rules, so the escape mdast-util-to-markdown adds to
121813
121916
  // intraword underscores is unnecessary and only produces noisy `\_` diffs.
@@ -121815,9 +121918,10 @@ const plain_plain = (node) => node.value;
121815
121918
  const INTRAWORD_UNDERSCORE_ESCAPE = /(?<=[\p{L}\p{N}_])\\_(?=[\p{L}\p{N}_]|\\_)/gu;
121816
121919
  const compile_text_text = (node, parent, state, info) => {
121817
121920
  const serialized = handle.text(node, parent, state, info);
121818
- return serialized.replace(INTRAWORD_UNDERSCORE_ESCAPE, '_');
121921
+ return escapeCellStartBlockMarker(serialized, node, parent, state, info);
121819
121922
  };
121820
- /* harmony default export */ const compile_text = (compile_text_text);
121923
+ const mdxishText = (node, parent, state, info) => compile_text_text(node, parent, state, info).replace(INTRAWORD_UNDERSCORE_ESCAPE, '_');
121924
+ /* harmony default export */ const compile_text = (mdxishText);
121821
121925
 
121822
121926
  ;// ./processor/compile/index.ts
121823
121927
 
@@ -121849,6 +121953,7 @@ function compilers(mdxish = false) {
121849
121953
  html: compile_compatibility,
121850
121954
  i: compile_compatibility,
121851
121955
  plain: compile_plain,
121956
+ text: compile_text_text,
121852
121957
  yaml: compile_compatibility,
121853
121958
  // needed only for mdxish
121854
121959
  ...(mdxish && { list: compile_list }),
@@ -123917,13 +124022,14 @@ function restoreCodeBlocks(content, protectedCode) {
123917
124022
  *
123918
124023
  * The attribute portion skips over quoted strings (`"..."` and `'...'`) so that
123919
124024
  * a `/>` inside an attribute value (e.g. `title="use /> here"`) does not cause
123920
- * a premature match.
124025
+ * a premature match, and stops at an unquoted `<` so a stray `<x` in prose
124026
+ * doesn't scan to the end of the document (quadratic).
123921
124027
  *
123922
124028
  * Only matches lowercase tag names to avoid interfering with PascalCase
123923
124029
  * JSX custom components (e.g. `<MyComponent />`), which are handled
123924
124030
  * separately by components/mdx-blocks.
123925
124031
  */
123926
- const SELF_CLOSING_TAG_RE = /<([a-z][a-z0-9-]*)((?:\s+(?:[^>"']*(?:"[^"]*"|'[^']*'))*[^>"']*)?)?\s*\/>/g;
124032
+ const SELF_CLOSING_TAG_RE = /<([a-z][a-z0-9-]*)((?:\s+(?:[^<>"']*(?:"[^"]*"|'[^']*'))*[^<>"']*)?)?\s*\/>/g;
123927
124033
  /**
123928
124034
  * String-level preprocessor that converts self-closing non-void HTML tags
123929
124035
  * into explicitly closed tags.
@@ -124588,8 +124694,9 @@ function terminateHtmlFlowBlocks(content) {
124588
124694
 
124589
124695
 
124590
124696
 
124591
- // Matches a JSX attribute expression (e.g. `key={i}`) anywhere in a string. */
124592
- const NESTED_ATTR_EXPRESSION_RE = /[\w-]+\s*=\s*\{/;
124697
+ // Matches a JSX attribute expression (e.g. `key={i}`) anywhere in a string. One name
124698
+ // char suffices for existence — `[\w-]+` backtracked quadratically over huge attributes.
124699
+ const NESTED_ATTR_EXPRESSION_RE = /[\w-]\s*=\s*\{/;
124593
124700
  // Name shape mirrors `componentTagPattern`; the lookbehind skips the inner tag
124594
124701
  // of a legacy `<<VARIABLE>>`.
124595
124702
  const NESTED_COMPONENT_TAG_RE = /(?<!<)<([A-Z][A-Za-z0-9_]*)[\s/>]/g;
@@ -124888,150 +124995,6 @@ const mdxishMdxComponentBlocks = (opts = {}) => (tree, file) => {
124888
124995
  };
124889
124996
  /* harmony default export */ const mdx_blocks = (mdxishMdxComponentBlocks);
124890
124997
 
124891
- ;// ./processor/transform/mdxish/components/self-closing-blocks.ts
124892
-
124893
-
124894
- /**
124895
- * Tags to process as self-closing blocks.
124896
- * These components use simple string attributes (no JSX expressions like `data={[...]}`).
124897
- * Components with JSX expression attributes should NOT be added here as parseAttributes
124898
- * cannot handle them correctly.
124899
- */
124900
- const SELF_CLOSING_BLOCK_TAGS = new Set(['Embed', 'Recipe']);
124901
- // Regex to match self-closing PascalCase tags (handles multi-line)
124902
- const selfClosingTagPattern = /^<([A-Z][A-Za-z0-9_]*)([\s\S]*?)\/>$/;
124903
- /**
124904
- * Try to convert a paragraph node containing a self-closing JSX component into an mdxJsxFlowElement.
124905
- * Returns the new node if conversion succeeded, or null if the node doesn't match.
124906
- */
124907
- const tryConvertToMdxNode = (node) => {
124908
- if (node.children.length !== 1)
124909
- return null;
124910
- const child = node.children[0];
124911
- if (child.type !== 'html')
124912
- return null;
124913
- const value = child.value?.trim();
124914
- if (!value)
124915
- return null;
124916
- const match = value.match(selfClosingTagPattern);
124917
- if (!match)
124918
- return null;
124919
- const [, tag, attrString] = match;
124920
- if (!SELF_CLOSING_BLOCK_TAGS.has(tag))
124921
- return null;
124922
- return {
124923
- type: 'mdxJsxFlowElement',
124924
- name: tag,
124925
- attributes: parseAttributes(attrString),
124926
- children: [],
124927
- position: node.position,
124928
- };
124929
- };
124930
- /**
124931
- * Transform paragraph-wrapped self-closing JSX components into mdxJsxFlowElement nodes.
124932
- *
124933
- * CommonMark wraps multi-line JSX in paragraphs when the opening tag isn't complete
124934
- * on one line. This plugin detects these structures and unwraps them for components
124935
- * in the SELF_CLOSING_BLOCK_TAGS allowlist.
124936
- *
124937
- * Input structure:
124938
- * ```
124939
- * paragraph > html: "<Embed\n typeOfEmbed=\"youtube\"\n/>"
124940
- * ```
124941
- *
124942
- * Output structure:
124943
- * ```
124944
- * mdxJsxFlowElement: { name: "Embed", attributes: [...], children: [] }
124945
- * ```
124946
- */
124947
- const mdxishSelfClosingBlocks = () => tree => {
124948
- visit(tree, 'paragraph', (node, index, parent) => {
124949
- if (index === undefined || !parent)
124950
- return;
124951
- const mdxNode = tryConvertToMdxNode(node);
124952
- if (mdxNode) {
124953
- parent.children.splice(index, 1, mdxNode);
124954
- }
124955
- });
124956
- };
124957
- /* harmony default export */ const self_closing_blocks = (mdxishSelfClosingBlocks);
124958
-
124959
- ;// ./processor/transform/mdxish/components/snake-case-components.ts
124960
-
124961
-
124962
- /**
124963
- * Replaces snake_case component names with valid HTML placeholders.
124964
- * Required because remark-parse rejects tags with underscores.
124965
- * Example: `<Snake_case />` → `<MDXishSnakeCase0 />`
124966
- *
124967
- * Code blocks and inline code are protected and will not be transformed.
124968
- *
124969
- * @param content - The markdown content to process
124970
- * @param options - Options including knownComponents to filter by
124971
- */
124972
- function processSnakeCaseComponent(content, options = {}) {
124973
- const { knownComponents } = options;
124974
- // Early exit if no potential snake_case components
124975
- if (!/[A-Z][A-Za-z0-9]*_[A-Za-z0-9_]*/.test(content)) {
124976
- return { content, mapping: {} };
124977
- }
124978
- // Step 1: Extract code blocks to protect them from transformation
124979
- const { protectedCode, protectedContent } = protectCodeBlocks(content);
124980
- // Find the highest existing placeholder number to avoid collisions
124981
- // e.g., if content has <MDXishSnakeCase0 />, start counter from 1
124982
- const placeholderPattern = /MDXishSnakeCase(\d+)/g;
124983
- let startCounter = 0;
124984
- let placeholderMatch;
124985
- while ((placeholderMatch = placeholderPattern.exec(content)) !== null) {
124986
- const num = parseInt(placeholderMatch[1], 10);
124987
- if (num >= startCounter) {
124988
- startCounter = num + 1;
124989
- }
124990
- }
124991
- const mapping = {};
124992
- const reverseMap = new Map();
124993
- let counter = startCounter;
124994
- // Step 2: Transform snake_case components in non-code content
124995
- const processedContent = protectedContent.replace(componentTagPattern, (match, tagName, attrs, selfClosing) => {
124996
- if (!tagName.includes('_')) {
124997
- return match;
124998
- }
124999
- const isClosing = tagName.startsWith('/');
125000
- const cleanTagName = isClosing ? tagName.slice(1) : tagName;
125001
- // Only transform if it's a known component (or if no filter is provided)
125002
- if (knownComponents && !knownComponents.has(cleanTagName)) {
125003
- return match;
125004
- }
125005
- let placeholder = reverseMap.get(cleanTagName);
125006
- if (!placeholder) {
125007
- // eslint-disable-next-line no-plusplus
125008
- placeholder = `MDXishSnakeCase${counter++}`;
125009
- mapping[placeholder] = cleanTagName;
125010
- reverseMap.set(cleanTagName, placeholder);
125011
- }
125012
- const processedTagName = isClosing ? `/${placeholder}` : placeholder;
125013
- return `<${processedTagName}${attrs}${selfClosing}>`;
125014
- });
125015
- // Step 3: Restore code blocks (untouched)
125016
- const finalContent = restoreCodeBlocks(processedContent, protectedCode);
125017
- return {
125018
- content: finalContent,
125019
- mapping,
125020
- };
125021
- }
125022
- /**
125023
- * Restores placeholder name to original snake_case name.
125024
- * Uses case-insensitive matching since HTML parsers normalize to lowercase.
125025
- */
125026
- function restoreSnakeCase(placeholderName, mapping) {
125027
- if (mapping[placeholderName]) {
125028
- return mapping[placeholderName];
125029
- }
125030
- const lowerName = placeholderName.toLowerCase();
125031
- const matchingKey = Object.keys(mapping).find(key => key.toLowerCase() === lowerName);
125032
- return matchingKey ? mapping[matchingKey] : placeholderName;
125033
- }
125034
-
125035
124998
  ;// ./processor/transform/mdxish/resolve-esm-imports.ts
125036
124999
 
125037
125000
  // We provide React as a default module so that components can use hooks
@@ -125216,6 +125179,54 @@ const containsJsxNode = (value) => {
125216
125179
  return true;
125217
125180
  return Object.values(value).some(containsJsxNode);
125218
125181
  };
125182
+ /** Read the component name off a JSX element name node (`Foo`, `Foo.Bar`, `foo:Bar`). */
125183
+ const jsxElementName = (name) => {
125184
+ if (name === null || typeof name !== 'object')
125185
+ return undefined;
125186
+ const node = name;
125187
+ if (node.type === 'JSXIdentifier')
125188
+ return typeof node.name === 'string' ? node.name : undefined;
125189
+ // `<Foo.Bar/>` and `<foo:Bar/>` resolve through their leftmost part.
125190
+ if (node.type === 'JSXMemberExpression')
125191
+ return jsxElementName(node.object);
125192
+ if (node.type === 'JSXNamespacedName')
125193
+ return jsxElementName(node.namespace);
125194
+ return undefined;
125195
+ };
125196
+ /**
125197
+ * Collect the capitalized names an expression uses as JSX tags. Parsed rather than pattern
125198
+ * matched: `{count < Max ? <Foo/> : <Bar/>}` puts a capitalized name straight after a `<` without
125199
+ * it being a tag, and only the parser can tell the two apart. Unparseable input yields nothing —
125200
+ * evaluation is about to throw on it anyway.
125201
+ */
125202
+ const jsxComponentNames = (expression) => {
125203
+ let program;
125204
+ try {
125205
+ program = parseExpression(expression);
125206
+ }
125207
+ catch {
125208
+ return [];
125209
+ }
125210
+ const names = new Set();
125211
+ const walk = (value) => {
125212
+ if (Array.isArray(value)) {
125213
+ value.forEach(walk);
125214
+ return;
125215
+ }
125216
+ if (value === null || typeof value !== 'object')
125217
+ return;
125218
+ const node = value;
125219
+ if (node.type === 'JSXOpeningElement') {
125220
+ const name = jsxElementName(node.name);
125221
+ // Lowercase tags compile to a string type, never a variable reference.
125222
+ if (name && /^[A-Z]/.test(name))
125223
+ names.add(name);
125224
+ }
125225
+ Object.values(node).forEach(walk);
125226
+ };
125227
+ walk(program);
125228
+ return Array.from(names);
125229
+ };
125219
125230
  /** Convert a program's JSX into `React.createElement` calls and evaluate it. `scope` must provide `React`. */
125220
125231
  const evalJsxProgram = (program, scope) => {
125221
125232
  buildJsx(program, { runtime: 'classic', pragma: 'React.createElement', pragmaFrag: 'React.Fragment' });
@@ -125421,6 +125432,28 @@ function reactElementToHast(node) {
125421
125432
 
125422
125433
 
125423
125434
 
125435
+
125436
+
125437
+
125438
+
125439
+ /**
125440
+ * Bind the components a given expression actually uses as tags. Scoped per expression on purpose:
125441
+ * binding the whole hash would shadow same-named globals (a `math` component would break
125442
+ * `{Math.max(1, 2)}`) and pad `evaluate`'s `new Function` parameter list. Resolution defers to
125443
+ * `getComponentName` so an expression and a plain tag always reach the same component.
125444
+ */
125445
+ const componentScope = (expression, components) => {
125446
+ const scope = {};
125447
+ jsxComponentNames(expression).forEach(name => {
125448
+ // `getComponentName` normalizes the tag, never the key, so it can't match `<MyBlock/>` to a
125449
+ // `my_block` entry; compare the key's PascalCase form for that direction.
125450
+ const tagName = getComponentName(name, components) ?? Object.keys(components).find(k => toPascalCase(k) === name);
125451
+ if (!tagName)
125452
+ return;
125453
+ scope[name] = (props) => external_react_default().createElement(tagName, props);
125454
+ });
125455
+ return scope;
125456
+ };
125424
125457
  /**
125425
125458
  * We divide the result of an expression into two categories:
125426
125459
  * 1. Renderable values: HTML, JSX, e.g. .map() returning JSX
@@ -125431,6 +125464,17 @@ const isRenderable = (value) => {
125431
125464
  return true;
125432
125465
  return Array.isArray(value) && value.some(isRenderable);
125433
125466
  };
125467
+ /**
125468
+ * Whether an expression evaluated to block-level content. Components count as block-level
125469
+ * unless they're on the inline list, matching the assumption the rest of the component
125470
+ * pipeline makes.
125471
+ */
125472
+ const isBlockResult = (children) => children.some(child => {
125473
+ if (child.type !== 'element' && child.type !== 'mdx-jsx')
125474
+ return false;
125475
+ const { tagName } = child;
125476
+ return !STANDARD_HTML_TAGS.has(tagName.toLowerCase()) && !INLINE_COMPONENT_TAGS.has(tagName);
125477
+ });
125434
125478
  /** Turn a non-renderable evaluation result into a text node. */
125435
125479
  const createTextNode = (result, position) => {
125436
125480
  if (result === null || result === undefined)
@@ -125442,13 +125486,22 @@ const createTextNode = (result, position) => {
125442
125486
  /**
125443
125487
  * AST transformer to evaluate MDX expressions.
125444
125488
  * Replaces mdxFlowExpression and mdxTextExpression nodes with their evaluated values.
125445
- * Self-contained expressions resolve directly (e.g. `{1+1}`); expressions that
125446
- * reference identifiers can resolve if those identifiers were introduced by an
125447
- * earlier `export const/function` (collected onto `file.data.mdxishScope`).
125448
- * Anything else falls through to the error branch and is kept as literal `{...}` text.
125449
- */
125450
- const evaluateExpressions = () => (tree, file) => {
125451
- const scope = { ...(file.data.mdxishScope), React: (external_react_default()) };
125489
+ * Self-contained expressions resolve directly (e.g. `{1+1}`); expressions that reference
125490
+ * identifiers can resolve if those identifiers are a custom component, the `user` variables
125491
+ * object, or were introduced by an earlier `export const/function` (collected onto
125492
+ * `file.data.mdxishScope`). Anything else falls through to the error branch and is kept as
125493
+ * literal `{...}` text.
125494
+ */
125495
+ const evaluateExpressions = ({ components, variables } = {}) => (tree, file) => {
125496
+ const baseScope = {
125497
+ // `User` matches the fallback the MDX path binds in `run.tsx`. Only when variables were
125498
+ // supplied: the proxy never throws, so an unconditional bind would resolve `user.*` on
125499
+ // surfaces that render without them instead of leaving literal text.
125500
+ ...(variables ? { user: user(variables) } : {}),
125501
+ // In-document exports win, matching `renderMdxish`.
125502
+ ...file.data.mdxishScope,
125503
+ React: (external_react_default()),
125504
+ };
125452
125505
  visit(tree, ['mdxFlowExpression', 'mdxTextExpression'], (node, index, parent) => {
125453
125506
  if (!parent || index === null || index === undefined)
125454
125507
  return;
@@ -125458,6 +125511,7 @@ const evaluateExpressions = () => (tree, file) => {
125458
125511
  if (!expression)
125459
125512
  return;
125460
125513
  try {
125514
+ const scope = { ...componentScope(expression, components ?? {}), ...baseScope };
125461
125515
  const result = evalExpression(expression, scope);
125462
125516
  if (isRenderable(result)) {
125463
125517
  // Stash hast built straight from the React tree; `mdxExpressionHandler` emits it and it
@@ -125478,6 +125532,22 @@ const evaluateExpressions = () => (tree, file) => {
125478
125532
  parent.children.splice(index, 1, { type: 'text', value: `{${processed}}`, position });
125479
125533
  }
125480
125534
  });
125535
+ // A text expression is parsed inside a paragraph, but its result can be block content: a
125536
+ // `<Tabs>` renders a `<div>`, and a browser closes the `<p>` before it, so the DOM it builds
125537
+ // no longer matches what was rendered and hydration fails. Lift the expression out when
125538
+ // that's all the paragraph holds.
125539
+ visit(tree, 'paragraph', (node, index, parent) => {
125540
+ if (!parent || index === null || index === undefined)
125541
+ return;
125542
+ const meaningful = node.children.filter(child => !(child.type === 'text' && !child.value.trim()));
125543
+ const [only] = meaningful;
125544
+ if (meaningful.length !== 1 || only.type !== 'mdxTextExpression')
125545
+ return;
125546
+ const hChildren = only.data?.hChildren;
125547
+ if (!hChildren || !isBlockResult(hChildren))
125548
+ return;
125549
+ parent.children.splice(index, 1, only);
125550
+ });
125481
125551
  return tree;
125482
125552
  };
125483
125553
  /* harmony default export */ const evaluate_expressions = (evaluateExpressions);
@@ -127285,45 +127355,6 @@ const resolveDeferredAttributeExpressionProps = () => (tree, file) => {
127285
127355
  };
127286
127356
  /* harmony default export */ const resolve_deferred_attribute_expression_props = (resolveDeferredAttributeExpressionProps);
127287
127357
 
127288
- ;// ./processor/transform/mdxish/restore-snake-case-component-name.ts
127289
-
127290
-
127291
- /**
127292
- * Restores snake_case component names from placeholders after parsing.
127293
- * Runs after mdxishComponentBlocks converts HTML nodes to mdxJsxFlowElement.
127294
- */
127295
- const restoreSnakeCaseComponentNames = (options) => {
127296
- const { mapping } = options;
127297
- return tree => {
127298
- if (!mapping || Object.keys(mapping).length === 0) {
127299
- return tree;
127300
- }
127301
- visit(tree, 'mdxJsxFlowElement', (node) => {
127302
- if (node.name) {
127303
- node.name = restoreSnakeCase(node.name, mapping);
127304
- }
127305
- });
127306
- // Pre-compile regex patterns for better performance
127307
- const regexPatterns = Object.entries(mapping).map(([placeholder, original]) => ({
127308
- regex: new RegExp(`(<\\/?)(${placeholder})(\\s|\\/?>)`, 'gi'),
127309
- original,
127310
- }));
127311
- visit(tree, 'html', (node) => {
127312
- if (node.value) {
127313
- let newValue = node.value;
127314
- regexPatterns.forEach(({ regex, original }) => {
127315
- newValue = newValue.replace(regex, `$1${original}$3`);
127316
- });
127317
- if (newValue !== node.value) {
127318
- node.value = newValue;
127319
- }
127320
- }
127321
- });
127322
- return tree;
127323
- };
127324
- };
127325
- /* harmony default export */ const restore_snake_case_component_name = (restoreSnakeCaseComponentNames);
127326
-
127327
127358
  ;// ./processor/transform/mdxish/retain-boolean-attributes.ts
127328
127359
 
127329
127360
  // Private Use Area character (U+E000) which is extremely unlikely to appear in real content.
@@ -127865,9 +127896,6 @@ function loadComponents() {
127865
127896
 
127866
127897
 
127867
127898
 
127868
-
127869
-
127870
-
127871
127899
 
127872
127900
 
127873
127901
 
@@ -127889,10 +127917,8 @@ const defaultTransformers = [
127889
127917
  * 5. Terminate HTML flow blocks so subsequent content isn't swallowed
127890
127918
  * 6. Close invalid "self-closing" HTML tags (e.g., `<i />` → `<i></i>`)
127891
127919
  * 7. Normalize compact ATX headings (e.g., `#Heading` → `# Heading`)
127892
- * 8. Replace snake_case component names with parser-safe placeholders
127893
127920
  */
127894
- function preprocessContent(content, opts) {
127895
- const { knownComponents } = opts;
127921
+ function preprocessContent(content) {
127896
127922
  // Runs first so `jsxTable` sees a literal `</table>` (and the HTML-line
127897
127923
  // classification in `terminateHtmlFlowBlocks` is accurate)
127898
127924
  let result = normalizeClosingTagWhitespace(content);
@@ -127905,7 +127931,7 @@ function preprocessContent(content, opts) {
127905
127931
  result = terminateHtmlFlowBlocks(result);
127906
127932
  result = closeSelfClosingHtmlTags(result);
127907
127933
  result = normalizeCompactHeadings(result);
127908
- return processSnakeCaseComponent(result, { knownComponents });
127934
+ return result;
127909
127935
  }
127910
127936
  function mdxishAstProcessor(mdContent, opts = {}) {
127911
127937
  const { components: userComponents = {}, hardBreaks: enableHardBreaks = true, newEditorTypes = false, safeMode = false, useTailwind, } = opts;
@@ -127913,9 +127939,7 @@ function mdxishAstProcessor(mdContent, opts = {}) {
127913
127939
  ...loadComponents(),
127914
127940
  ...userComponents,
127915
127941
  };
127916
- // Build set of known component names for snake_case filtering
127917
- const knownComponents = new Set(Object.keys(components));
127918
- const { content: parserReadyContent, mapping: snakeCaseMapping } = preprocessContent(mdContent, { knownComponents });
127942
+ const parserReadyContent = preprocessContent(mdContent);
127919
127943
  // Create string map for tailwind transformer
127920
127944
  const tempComponentsMap = Object.entries(components).reduce((acc, [key, value]) => {
127921
127945
  acc[key] = String(value);
@@ -127928,10 +127952,8 @@ function mdxishAstProcessor(mdContent, opts = {}) {
127928
127952
  .use(remarkParse)
127929
127953
  .use(remarkFrontmatter)
127930
127954
  .use(normalize_malformed_md_syntax)
127931
- .use(self_closing_blocks)
127932
127955
  .use(mdx_blocks, { safeMode })
127933
127956
  .use(inline_html, { safeMode })
127934
- .use(restore_snake_case_component_name, { mapping: snakeCaseMapping })
127935
127957
  .use(mdxish_tables)
127936
127958
  .use(mdxish_html_blocks) // Convert every <HTMLBlock> shape → html-block
127937
127959
  // The next few transformers must appear after mdxishMdxComponentBlocks
@@ -128007,7 +128029,7 @@ function mdxish(mdContent, opts = {}) {
128007
128029
  processor
128008
128030
  .use(safeMode ? undefined : evaluate_exports) // Evaluate `export const/function` and stash scope on file.data.mdxishScope
128009
128031
  .use(enableHardBreaks ? hard_breaks : undefined) // Must precede evaluateExpressions to avoid splitting the \n in an evaluated template literal into a <br> node
128010
- .use(safeMode ? undefined : evaluate_expressions) // Evaluate self-contained MDX expressions (e.g. `{1+1}`)
128032
+ .use(safeMode ? undefined : evaluate_expressions, { components, variables }) // Evaluate self-contained MDX expressions (e.g. `{1+1}`)
128011
128033
  .use(safeMode ? undefined : evaluate_style_block_expressions) // Evaluate `<style>{`...`}</style>` template literals into plain CSS
128012
128034
  .use(variables_code, { variables }) // Resolve <<...>> and {user.*} inside code and inline code nodes
128013
128035
  .use(remarkRehype, { allowDangerousHtml: true, handlers: mdxComponentHandlers })
@@ -129053,7 +129075,7 @@ module.exports = /*#__PURE__*/JSON.parse('{"*":["about","acceptCharset","accessK
129053
129075
  /******/ // startup
129054
129076
  /******/ // Load entry module and return exports
129055
129077
  /******/ // This entry module used 'module' so it can't be inlined
129056
- /******/ let __webpack_exports__ = __webpack_require__(5814);
129078
+ /******/ let __webpack_exports__ = __webpack_require__(8719);
129057
129079
  /******/ module.exports = __webpack_exports__;
129058
129080
  /******/
129059
129081
  /******/ })()