@readme/markdown 11.10.0 → 11.11.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
@@ -19237,17 +19237,17 @@ const Callout = (props) => {
19237
19237
 
19238
19238
  const Card = ({ badge, children, href, kind = 'card', icon, iconColor, target, title }) => {
19239
19239
  const Tag = href ? 'a' : 'div';
19240
- return (external_react_default().createElement(Tag, { className: `rm-Card rm-Card_${kind}`, href: href, target: target },
19241
- icon && external_react_default().createElement("i", { className: `rm-Card-icon fa-duotone fa-solid ${icon}`, style: { '--Card-icon-color': iconColor } }),
19242
- external_react_default().createElement("div", { className: "rm-Card-content" },
19243
- title && (external_react_default().createElement("div", { className: "rm-Card-title" },
19240
+ return (external_react_default().createElement(Tag, { className: `Card Card_${kind}`, href: href, target: target },
19241
+ icon && external_react_default().createElement("i", { className: `Card-icon fa-duotone fa-solid ${icon}`, style: { '--Card-icon-color': iconColor } }),
19242
+ external_react_default().createElement("div", { className: "Card-content" },
19243
+ title && (external_react_default().createElement("div", { className: "Card-title" },
19244
19244
  title,
19245
- badge && external_react_default().createElement("span", { className: "rm-Card-badge" }, badge),
19246
- href && external_react_default().createElement("i", { "aria-hidden": "true", className: "rm-Card-arrow fa-regular fa-arrow-right" }))),
19245
+ badge && external_react_default().createElement("span", { className: "Card-badge" }, badge),
19246
+ href && external_react_default().createElement("i", { "aria-hidden": "true", className: "Card-arrow fa-regular fa-arrow-right" }))),
19247
19247
  children)));
19248
19248
  };
19249
19249
  const CardsGrid = ({ cardWidth = '200px', columns = 'auto-fit', children }) => {
19250
- return (external_react_default().createElement("div", { className: "rm-CardsGrid", style: { '--CardsGrid-cardWidth': cardWidth, '--CardsGrid-columns': columns } }, children));
19250
+ return (external_react_default().createElement("div", { className: "CardsGrid", style: { '--CardsGrid-cardWidth': cardWidth, '--CardsGrid-columns': columns } }, children));
19251
19251
  };
19252
19252
  /* harmony default export */ const Cards = (CardsGrid);
19253
19253
 
@@ -90788,7 +90788,7 @@ const mdxToHast = () => tree => {
90788
90788
  ;// ./processor/transform/mdxish/mdxish-component-blocks.ts
90789
90789
 
90790
90790
 
90791
- const tagPattern = /^<([A-Z][A-Za-z0-9]*)([^>]*?)(\/?)>([\s\S]*)?$/;
90791
+ const tagPattern = /^<([A-Z][A-Za-z0-9_]*)([^>]*?)(\/?)>([\s\S]*)?$/;
90792
90792
  const attributePattern = /([a-zA-Z_:][-a-zA-Z0-9_:.]*)(?:\s*=\s*("[^"]*"|'[^']*'|[^\s"'>]+))?/g;
90793
90793
  const inlineMdProcessor = unified().use(remarkParse);
90794
90794
  const isClosingTag = (value, tag) => value.trim() === `</${tag}>`;
@@ -113185,82 +113185,7 @@ const mdxComponentHandlers = {
113185
113185
  [NodeTypes.htmlBlock]: htmlBlockHandler,
113186
113186
  };
113187
113187
 
113188
- ;// ./processor/transform/mdxish/evaluate-expressions.ts
113189
-
113190
- /**
113191
- * AST transformer to evaluate MDX expressions using the provided context.
113192
- * Replaces mdxFlowExpression and mdxTextExpression nodes with their evaluated values.
113193
- */
113194
- const evaluateExpressions = ({ context = {} } = {}) => tree => {
113195
- visit(tree, ['mdxFlowExpression', 'mdxTextExpression'], (node, index, parent) => {
113196
- if (!parent || index === null || index === undefined)
113197
- return;
113198
- const expressionNode = node;
113199
- if (!('value' in expressionNode))
113200
- return;
113201
- // JSX attribute expressions are handled by preprocessing; code blocks are protected
113202
- const expression = expressionNode.value.trim();
113203
- // Skip if expression is empty (shouldn't happen, but defensive)
113204
- if (!expression)
113205
- return;
113206
- try {
113207
- // Evaluate the expression using the context
113208
- const contextKeys = Object.keys(context);
113209
- const contextValues = Object.values(context);
113210
- // If no context provided, leave expression as-is
113211
- if (contextKeys.length === 0) {
113212
- parent.children.splice(index, 1, {
113213
- type: 'text',
113214
- value: `{${expression}}`,
113215
- position: node.position,
113216
- });
113217
- return;
113218
- }
113219
- // eslint-disable-next-line no-new-func
113220
- const func = new Function(...contextKeys, `return ${expression}`);
113221
- const result = func(...contextValues);
113222
- // Convert result to text node(s)
113223
- if (result === null || result === undefined) {
113224
- // Replace with empty text node (don't remove, as it affects positioning)
113225
- parent.children.splice(index, 1, {
113226
- type: 'text',
113227
- value: '',
113228
- position: node.position,
113229
- });
113230
- return;
113231
- }
113232
- let textValue;
113233
- if (typeof result === 'object') {
113234
- textValue = JSON.stringify(result);
113235
- }
113236
- else {
113237
- textValue = String(result).replace(/\s+/g, ' ').trim();
113238
- }
113239
- // Replace expression node with text node
113240
- parent.children.splice(index, 1, {
113241
- type: 'text',
113242
- value: textValue,
113243
- position: node.position,
113244
- });
113245
- }
113246
- catch (_error) {
113247
- // If evaluation fails, leave the expression as-is (fallback to text)
113248
- parent.children.splice(index, 1, {
113249
- type: 'text',
113250
- value: `{${expression}}`,
113251
- position: node.position,
113252
- });
113253
- }
113254
- });
113255
- return tree;
113256
- };
113257
- /* harmony default export */ const evaluate_expressions = (evaluateExpressions);
113258
-
113259
113188
  ;// ./processor/transform/mdxish/preprocess-jsx-expressions.ts
113260
- /**
113261
- * Pre-processes JSX-like expressions before markdown parsing.
113262
- * Converts href={'value'} to href="value", evaluates {expressions}, etc.
113263
- */
113264
113189
  // Base64 encode (Node.js + browser compatible)
113265
113190
  function base64Encode(str) {
113266
113191
  if (typeof Buffer !== 'undefined') {
@@ -113516,6 +113441,57 @@ function preprocessJSXExpressions(content, context = {}) {
113516
113441
  return processed;
113517
113442
  }
113518
113443
 
113444
+ ;// ./processor/transform/mdxish/evaluate-expressions.ts
113445
+
113446
+
113447
+ /**
113448
+ * AST transformer to evaluate MDX expressions using the provided context.
113449
+ * Replaces mdxFlowExpression and mdxTextExpression nodes with their evaluated values.
113450
+ */
113451
+ const evaluateExpressions = ({ context = {} } = {}) => tree => {
113452
+ visit(tree, ['mdxFlowExpression', 'mdxTextExpression'], (node, index, parent) => {
113453
+ if (!parent || index === null || index === undefined)
113454
+ return;
113455
+ const expressionNode = node;
113456
+ if (!('value' in expressionNode))
113457
+ return;
113458
+ const expression = expressionNode.value.trim();
113459
+ // Skip if expression is empty (shouldn't happen, but defensive)
113460
+ if (!expression)
113461
+ return;
113462
+ try {
113463
+ const result = evaluateExpression(expression, context);
113464
+ // Extract evaluated value text
113465
+ let textValue;
113466
+ if (result === null || result === undefined) {
113467
+ textValue = '';
113468
+ }
113469
+ else if (typeof result === 'object') {
113470
+ textValue = JSON.stringify(result);
113471
+ }
113472
+ else {
113473
+ textValue = String(result).replace(/\s+/g, ' ').trim();
113474
+ }
113475
+ // Replace expression node with text node since the expression is conceptually a text
113476
+ parent.children.splice(index, 1, {
113477
+ type: 'text',
113478
+ value: textValue,
113479
+ position: node.position,
113480
+ });
113481
+ }
113482
+ catch (_error) {
113483
+ // If evaluation fails, leave the expression as-is (fallback to text)
113484
+ parent.children.splice(index, 1, {
113485
+ type: 'text',
113486
+ value: `{${expression}}`,
113487
+ position: node.position,
113488
+ });
113489
+ }
113490
+ });
113491
+ return tree;
113492
+ };
113493
+ /* harmony default export */ const evaluate_expressions = (evaluateExpressions);
113494
+
113519
113495
  ;// ./processor/transform/mdxish/mdxish-html-blocks.ts
113520
113496
 
113521
113497
 
@@ -113856,6 +113832,7 @@ const mdxishHtmlBlocks = () => tree => {
113856
113832
 
113857
113833
 
113858
113834
 
113835
+
113859
113836
  /**
113860
113837
  * Matches legacy magic block syntax: [block:TYPE]...JSON...[/block]
113861
113838
  * Group 1: block type (e.g., "image", "code", "callout")
@@ -114024,16 +114001,34 @@ function parseMagicBlock(raw, options = {}) {
114024
114001
  const [icon, theme] = Array.isArray(resolvedType) ? resolvedType : ['👍', 'default'];
114025
114002
  if (!(calloutJson.title || calloutJson.body))
114026
114003
  return [];
114027
- return [
114028
- wrapPinnedBlocks({
114029
- children: [...textToBlock(calloutJson.title || ''), ...textToBlock(calloutJson.body || '')],
114030
- data: {
114031
- hName: 'rdme-callout',
114032
- hProperties: { icon, theme: theme || 'default', title: calloutJson.title, value: calloutJson.body },
114033
- },
114034
- type: 'rdme-callout',
114035
- }, json),
114036
- ];
114004
+ const titleBlocks = textToBlock(calloutJson.title || '');
114005
+ const bodyBlocks = textToBlock(calloutJson.body || '');
114006
+ const children = [];
114007
+ if (titleBlocks.length > 0 && titleBlocks[0].type === 'paragraph') {
114008
+ const firstTitle = titleBlocks[0];
114009
+ const heading = {
114010
+ type: 'heading',
114011
+ depth: 3,
114012
+ children: (firstTitle.children || []),
114013
+ };
114014
+ children.push(heading);
114015
+ children.push(...titleBlocks.slice(1), ...bodyBlocks);
114016
+ }
114017
+ else {
114018
+ children.push(...titleBlocks, ...bodyBlocks);
114019
+ }
114020
+ // Create mdxJsxFlowElement directly for mdxish
114021
+ const calloutElement = {
114022
+ type: 'mdxJsxFlowElement',
114023
+ name: 'Callout',
114024
+ attributes: toAttributes({ icon, theme: theme || 'default', type: theme || 'default' }, [
114025
+ 'icon',
114026
+ 'theme',
114027
+ 'type',
114028
+ ]),
114029
+ children: children,
114030
+ };
114031
+ return [wrapPinnedBlocks(calloutElement, json)];
114037
114032
  }
114038
114033
  // Parameters: renders as a table (used for API parameters, etc.)
114039
114034
  case 'parameters': {
@@ -114116,6 +114111,12 @@ function parseMagicBlock(raw, options = {}) {
114116
114111
  }
114117
114112
  }
114118
114113
  }
114114
+ /**
114115
+ * Check if a child node is a flow element that needs unwrapping (mdxJsxFlowElement, etc.)
114116
+ */
114117
+ const needsUnwrapping = (child) => {
114118
+ return child.type === 'mdxJsxFlowElement';
114119
+ };
114119
114120
  /**
114120
114121
  * Unified plugin that restores magic blocks from placeholder tokens.
114121
114122
  *
@@ -114129,21 +114130,271 @@ const magicBlockRestorer = ({ blocks }) => tree => {
114129
114130
  // Map: key → original raw magic block content
114130
114131
  const magicBlockKeys = new Map(blocks.map(({ key, raw }) => [key, raw]));
114131
114132
  // Find inlineCode nodes that match our placeholder tokens
114133
+ const modifications = [];
114132
114134
  visit(tree, 'inlineCode', (node, index, parent) => {
114133
114135
  if (!parent || index == null)
114134
- return;
114136
+ return undefined;
114135
114137
  const raw = magicBlockKeys.get(node.value);
114136
114138
  if (!raw)
114137
- return;
114138
- // Parse the original magic block and replace the placeholder with the result
114139
+ return undefined;
114139
114140
  const children = parseMagicBlock(raw);
114140
114141
  if (!children.length)
114141
- return;
114142
+ return undefined;
114143
+ if (children[0] && needsUnwrapping(children[0]) && parent.type === 'paragraph') {
114144
+ // Find paragraph's parent and unwrap
114145
+ let paragraphParent;
114146
+ visit(tree, 'paragraph', (p, pIndex, pParent) => {
114147
+ if (p === parent && pParent && 'children' in pParent) {
114148
+ paragraphParent = pParent;
114149
+ return false;
114150
+ }
114151
+ return undefined;
114152
+ });
114153
+ if (paragraphParent) {
114154
+ const paragraphIndex = paragraphParent.children.indexOf(parent);
114155
+ if (paragraphIndex !== -1) {
114156
+ modifications.push({ children, index: paragraphIndex, parent: paragraphParent });
114157
+ }
114158
+ }
114159
+ return SKIP;
114160
+ }
114161
+ parent.children.splice(index, 1, ...children);
114162
+ return [SKIP, index + children.length];
114163
+ });
114164
+ // Apply modifications in reverse order to avoid index shifting
114165
+ modifications.reverse().forEach(({ children, index, parent }) => {
114142
114166
  parent.children.splice(index, 1, ...children);
114143
114167
  });
114144
114168
  };
114145
114169
  /* harmony default export */ const mdxish_magic_blocks = (magicBlockRestorer);
114146
114170
 
114171
+ ;// ./lib/constants.ts
114172
+ /**
114173
+ * Pattern to match component tags (PascalCase or snake_case)
114174
+ */
114175
+ const componentTagPattern = /<(\/?[A-Z][A-Za-z0-9_]*)([^>]*?)(\/?)>/g;
114176
+
114177
+ ;// ./processor/transform/mdxish/mdxish-snake-case-components.ts
114178
+
114179
+ /**
114180
+ * Replaces snake_case component names with valid HTML placeholders.
114181
+ * Required because remark-parse rejects tags with underscores.
114182
+ * Example: `<Snake_case />` → `<MDXishSnakeCase0 />`
114183
+ */
114184
+ function processSnakeCaseComponent(content) {
114185
+ // Early exit if no potential snake_case components
114186
+ if (!/[A-Z][A-Za-z0-9]*_[A-Za-z0-9_]*/.test(content)) {
114187
+ return { content, mapping: {} };
114188
+ }
114189
+ const mapping = {};
114190
+ const reverseMap = new Map();
114191
+ let counter = 0;
114192
+ const processedContent = content.replace(componentTagPattern, (match, tagName, attrs, selfClosing) => {
114193
+ if (!tagName.includes('_')) {
114194
+ return match;
114195
+ }
114196
+ const isClosing = tagName.startsWith('/');
114197
+ const cleanTagName = isClosing ? tagName.slice(1) : tagName;
114198
+ let placeholder = reverseMap.get(cleanTagName);
114199
+ if (!placeholder) {
114200
+ // eslint-disable-next-line no-plusplus
114201
+ placeholder = `MDXishSnakeCase${counter++}`;
114202
+ mapping[placeholder] = cleanTagName;
114203
+ reverseMap.set(cleanTagName, placeholder);
114204
+ }
114205
+ const processedTagName = isClosing ? `/${placeholder}` : placeholder;
114206
+ return `<${processedTagName}${attrs}${selfClosing}>`;
114207
+ });
114208
+ return {
114209
+ content: processedContent,
114210
+ mapping,
114211
+ };
114212
+ }
114213
+ /**
114214
+ * Restores placeholder name to original snake_case name.
114215
+ * Uses case-insensitive matching since HTML parsers normalize to lowercase.
114216
+ */
114217
+ function restoreSnakeCase(placeholderName, mapping) {
114218
+ if (mapping[placeholderName]) {
114219
+ return mapping[placeholderName];
114220
+ }
114221
+ const lowerName = placeholderName.toLowerCase();
114222
+ const matchingKey = Object.keys(mapping).find(key => key.toLowerCase() === lowerName);
114223
+ return matchingKey ? mapping[matchingKey] : placeholderName;
114224
+ }
114225
+
114226
+ ;// ./processor/transform/mdxish/normalize-malformed-md-syntax.ts
114227
+
114228
+ // Patterns to detect for bold (** and __) and italic (* and _) syntax:
114229
+ // Bold: ** text**, **text **, word** text**, ** text **
114230
+ // Italic: * text*, *text *, word* text*, * text *
114231
+ // Same patterns for underscore variants
114232
+ // We use separate patterns for each marker type to allow this flexibility.
114233
+ // Pattern for ** bold **
114234
+ // Groups: 1=wordBefore, 2=marker, 3=contentWithSpaceAfter, 4=trailingSpace1, 5=contentWithSpaceBefore, 6=trailingSpace2, 7=afterChar
114235
+ // trailingSpace1 is for "** text **" pattern, trailingSpace2 is for "**text **" pattern
114236
+ const asteriskBoldRegex = /([^*\s]+)?\s*(\*\*)(?:\s+((?:[^*\n]|\*(?!\*))+?)(\s*)\2|((?:[^*\n]|\*(?!\*))+?)(\s+)\2)(\S|$)?/g;
114237
+ // Pattern for __ bold __
114238
+ const underscoreBoldRegex = /([^_\s]+)?\s*(__)(?:\s+((?:[^_\n]|_(?!_))+?)(\s*)\2|((?:[^_\n]|_(?!_))+?)(\s+)\2)(\S|$)?/g;
114239
+ // Pattern for * italic *
114240
+ const asteriskItalicRegex = /([^*\s]+)?\s*(\*)(?!\*)(?:\s+([^*\n]+?)(\s*)\2|([^*\n]+?)(\s+)\2)(\S|$)?/g;
114241
+ // Pattern for _ italic _
114242
+ const underscoreItalicRegex = /([^_\s]+)?\s*(_)(?!_)(?:\s+([^_\n]+?)(\s*)\2|([^_\n]+?)(\s+)\2)(\S|$)?/g;
114243
+ /**
114244
+ * A remark plugin that normalizes malformed bold and italic markers in text nodes.
114245
+ * Detects patterns like `** bold**`, `Hello** Wrong Bold**`, `__ bold__`, `Hello__ Wrong Bold__`,
114246
+ * `* italic*`, `Hello* Wrong Italic*`, `_ italic_`, or `Hello_ Wrong Italic_`
114247
+ * and converts them to proper strong/emphasis nodes, matching the behavior of the legacy rdmd engine.
114248
+ *
114249
+ * Supports both asterisk (`**bold**`, `*italic*`) and underscore (`__bold__`, `_italic_`) syntax.
114250
+ * Also supports snake_case content like `** some_snake_case**`.
114251
+ *
114252
+ * This runs after remark-parse, which (in v11+) is strict and doesn't parse
114253
+ * malformed emphasis syntax. This plugin post-processes the AST to handle these cases.
114254
+ */
114255
+ const normalizeEmphasisAST = () => (tree) => {
114256
+ visit(tree, 'text', function visitor(node, index, parent) {
114257
+ if (index === undefined || !parent)
114258
+ return undefined;
114259
+ // Skip if inside code blocks or inline code
114260
+ if (parent.type === 'inlineCode' || parent.type === 'code') {
114261
+ return undefined;
114262
+ }
114263
+ const text = node.value;
114264
+ const allMatches = [];
114265
+ [...text.matchAll(asteriskBoldRegex)].forEach(match => {
114266
+ allMatches.push({ isBold: true, marker: '**', match });
114267
+ });
114268
+ [...text.matchAll(underscoreBoldRegex)].forEach(match => {
114269
+ allMatches.push({ isBold: true, marker: '__', match });
114270
+ });
114271
+ [...text.matchAll(asteriskItalicRegex)].forEach(match => {
114272
+ allMatches.push({ isBold: false, marker: '*', match });
114273
+ });
114274
+ [...text.matchAll(underscoreItalicRegex)].forEach(match => {
114275
+ allMatches.push({ isBold: false, marker: '_', match });
114276
+ });
114277
+ if (allMatches.length === 0)
114278
+ return undefined;
114279
+ allMatches.sort((a, b) => (a.match.index ?? 0) - (b.match.index ?? 0));
114280
+ const filteredMatches = [];
114281
+ let lastEnd = 0;
114282
+ allMatches.forEach(info => {
114283
+ const start = info.match.index ?? 0;
114284
+ const end = start + info.match[0].length;
114285
+ if (start >= lastEnd) {
114286
+ filteredMatches.push(info);
114287
+ lastEnd = end;
114288
+ }
114289
+ });
114290
+ if (filteredMatches.length === 0)
114291
+ return undefined;
114292
+ const parts = [];
114293
+ let lastIndex = 0;
114294
+ filteredMatches.forEach(({ match, marker, isBold }) => {
114295
+ const matchIndex = match.index ?? 0;
114296
+ const fullMatch = match[0];
114297
+ if (matchIndex > lastIndex) {
114298
+ const beforeText = text.slice(lastIndex, matchIndex);
114299
+ if (beforeText) {
114300
+ parts.push({ type: 'text', value: beforeText });
114301
+ }
114302
+ }
114303
+ const wordBefore = match[1]; // e.g., "Hello" in "Hello** Wrong Bold**"
114304
+ const contentWithSpaceAfter = match[3]; // Content when there's a space after opening markers
114305
+ const trailingSpace1 = match[4] || ''; // Space before closing markers (for "** text **" pattern)
114306
+ const contentWithSpaceBefore = match[5]; // Content when there's only a space before closing markers
114307
+ const trailingSpace2 = match[6] || ''; // Space before closing markers (for "**text **" pattern)
114308
+ const trailingSpace = trailingSpace1 || trailingSpace2; // Combined trailing space
114309
+ const content = (contentWithSpaceAfter || contentWithSpaceBefore || '').trim();
114310
+ const afterChar = match[7]; // Character after closing markers (if any)
114311
+ const markerPos = fullMatch.indexOf(marker);
114312
+ const spacesBeforeMarkers = wordBefore
114313
+ ? fullMatch.slice(wordBefore.length, markerPos)
114314
+ : fullMatch.slice(0, markerPos);
114315
+ const shouldAddSpace = !!contentWithSpaceAfter && !!wordBefore && !spacesBeforeMarkers;
114316
+ if (wordBefore) {
114317
+ const spacing = spacesBeforeMarkers + (shouldAddSpace ? ' ' : '');
114318
+ parts.push({ type: 'text', value: wordBefore + spacing });
114319
+ }
114320
+ else if (spacesBeforeMarkers) {
114321
+ parts.push({ type: 'text', value: spacesBeforeMarkers });
114322
+ }
114323
+ if (content) {
114324
+ if (isBold) {
114325
+ parts.push({
114326
+ type: 'strong',
114327
+ children: [{ type: 'text', value: content }],
114328
+ });
114329
+ }
114330
+ else {
114331
+ parts.push({
114332
+ type: 'emphasis',
114333
+ children: [{ type: 'text', value: content }],
114334
+ });
114335
+ }
114336
+ }
114337
+ if (afterChar) {
114338
+ const prefix = trailingSpace ? ' ' : '';
114339
+ parts.push({ type: 'text', value: prefix + afterChar });
114340
+ }
114341
+ lastIndex = matchIndex + fullMatch.length;
114342
+ });
114343
+ if (lastIndex < text.length) {
114344
+ const remainingText = text.slice(lastIndex);
114345
+ if (remainingText) {
114346
+ parts.push({ type: 'text', value: remainingText });
114347
+ }
114348
+ }
114349
+ if (parts.length > 0) {
114350
+ parent.children.splice(index, 1, ...parts);
114351
+ return [SKIP, index + parts.length];
114352
+ }
114353
+ return undefined;
114354
+ });
114355
+ return tree;
114356
+ };
114357
+ /* harmony default export */ const normalize_malformed_md_syntax = (normalizeEmphasisAST);
114358
+
114359
+ ;// ./processor/transform/mdxish/restore-snake-case-component-name.ts
114360
+
114361
+
114362
+ /**
114363
+ * Restores snake_case component names from placeholders after parsing.
114364
+ * Runs after mdxishComponentBlocks converts HTML nodes to mdxJsxFlowElement.
114365
+ */
114366
+ const restoreSnakeCaseComponentNames = (options) => {
114367
+ const { mapping } = options;
114368
+ return tree => {
114369
+ if (!mapping || Object.keys(mapping).length === 0) {
114370
+ return tree;
114371
+ }
114372
+ visit(tree, 'mdxJsxFlowElement', (node) => {
114373
+ if (node.name) {
114374
+ node.name = restoreSnakeCase(node.name, mapping);
114375
+ }
114376
+ });
114377
+ // Pre-compile regex patterns for better performance
114378
+ const regexPatterns = Object.entries(mapping).map(([placeholder, original]) => ({
114379
+ regex: new RegExp(`(<\\/?)(${placeholder})(\\s|\\/?>)`, 'gi'),
114380
+ original,
114381
+ }));
114382
+ visit(tree, 'html', (node) => {
114383
+ if (node.value) {
114384
+ let newValue = node.value;
114385
+ regexPatterns.forEach(({ regex, original }) => {
114386
+ newValue = newValue.replace(regex, `$1${original}$3`);
114387
+ });
114388
+ if (newValue !== node.value) {
114389
+ node.value = newValue;
114390
+ }
114391
+ }
114392
+ });
114393
+ return tree;
114394
+ };
114395
+ };
114396
+ /* harmony default export */ const restore_snake_case_component_name = (restoreSnakeCaseComponentNames);
114397
+
114147
114398
  ;// ./processor/transform/mdxish/variables-text.ts
114148
114399
 
114149
114400
 
@@ -114229,14 +114480,17 @@ function extractMagicBlocks(markdown) {
114229
114480
  const replaced = markdown.replace(MAGIC_BLOCK_REGEX, match => {
114230
114481
  /**
114231
114482
  * Key is the unique identifier for the magic block
114232
- * Token is a wrapper around the key to serialize & influences how the block is parsed in the pipeline
114233
- * with the temporary key
114234
- * - Use backticks so it becomes a code span, preventing remarkParse from parsing
114235
- * special characters in the token as markdown syntax
114236
- * - Prepend a newline to the token to ensure it is parsed as a block level node
114237
114483
  */
114238
114484
  const key = `__MAGIC_BLOCK_${index}__`;
114239
- const token = `\n\`${key}\``;
114485
+ /**
114486
+ * Token is a wrapper around the `key` to serialize & influence how the
114487
+ * magic block is parsed in the remark pipeline.
114488
+ * - Use backticks so it becomes a code span, preventing `remarkParse` from
114489
+ * parsing special characters in the token as markdown syntax
114490
+ * - Prepend a newline to ensure it is parsed as a block level node
114491
+ * - Append a newline to ensure it is separated from following content
114492
+ */
114493
+ const token = `\n\`${key}\`\n`;
114240
114494
  blocks.push({ key, raw: match, token });
114241
114495
  index += 1;
114242
114496
  return token;
@@ -114247,17 +114501,16 @@ function extractMagicBlocks(markdown) {
114247
114501
  * Restore extracted magic blocks back into a markdown string.
114248
114502
  */
114249
114503
  function restoreMagicBlocks(replaced, blocks) {
114250
- let content = replaced;
114251
- // If a magic block is at the start of the document, the extraction token's prepended
114252
- // newline will have been trimmed during processing. We need to account for that here
114253
- // to ensure the token is found and replaced correctly.
114254
- const isTokenAtStart = content.startsWith(blocks[0]?.token.trimStart());
114255
- if (isTokenAtStart) {
114256
- content = `\n${content}`;
114257
- }
114258
- return blocks.reduce((acc, { token, raw }) => {
114259
- return acc.split(token).join(raw);
114504
+ // If a magic block is at the start or end of the document, the extraction
114505
+ // token's newlines will have been trimmed during processing. We need to
114506
+ // account for that here to ensure the token is found and replaced correctly.
114507
+ // These extra newlines will be removed again when the final string is trimmed.
114508
+ const content = `\n${replaced}\n`;
114509
+ const restoredContent = blocks.reduce((acc, { token, raw }) => {
114510
+ // Ensure each magic block is separated by newlines when restored.
114511
+ return acc.split(token).join(`\n${raw}\n`);
114260
114512
  }, content);
114513
+ return restoredContent.trim();
114261
114514
  }
114262
114515
 
114263
114516
  ;// ./lib/utils/mdxish/mdxish-load-components.ts
@@ -114316,6 +114569,9 @@ function loadComponents() {
114316
114569
 
114317
114570
 
114318
114571
 
114572
+
114573
+
114574
+
114319
114575
 
114320
114576
 
114321
114577
 
@@ -114333,10 +114589,15 @@ function mdxish(mdContent, opts = {}) {
114333
114589
  ...loadComponents(),
114334
114590
  ...userComponents,
114335
114591
  };
114336
- // Preprocess content: extract legacy magic blocks and evaluate JSX attribute expressions
114337
- const { replaced, blocks } = extractMagicBlocks(mdContent);
114338
- const processedContent = preprocessJSXExpressions(replaced, jsxContext);
114339
- // Create string map of components for tailwind transformer
114592
+ // Preprocessing pipeline: Transform content to be parser-ready
114593
+ // Step 1: Extract legacy magic blocks
114594
+ const { replaced: contentAfterMagicBlocks, blocks } = extractMagicBlocks(mdContent);
114595
+ // Step 2: Evaluate JSX expressions in attributes
114596
+ const contentAfterJSXEvaluation = preprocessJSXExpressions(contentAfterMagicBlocks, jsxContext);
114597
+ // Step 3: Replace snake_case component names with parser-safe placeholders
114598
+ // (e.g., <Snake_case /> → <MDXishSnakeCase0 /> which will be restored after parsing)
114599
+ const { content: parserReadyContent, mapping: snakeCaseMapping } = processSnakeCaseComponent(contentAfterJSXEvaluation);
114600
+ // Create string map for tailwind transformer
114340
114601
  const tempComponentsMap = Object.entries(components).reduce((acc, [key, value]) => {
114341
114602
  acc[key] = String(value);
114342
114603
  return acc;
@@ -114346,10 +114607,12 @@ function mdxish(mdContent, opts = {}) {
114346
114607
  .data('fromMarkdownExtensions', [mdxExpressionFromMarkdown()])
114347
114608
  .use(remarkParse)
114348
114609
  .use(remarkFrontmatter)
114610
+ .use(normalize_malformed_md_syntax)
114349
114611
  .use(mdxish_magic_blocks, { blocks })
114350
114612
  .use(transform_images, { isMdxish: true })
114351
114613
  .use(defaultTransformers)
114352
114614
  .use(mdxish_component_blocks)
114615
+ .use(restore_snake_case_component_name, { mapping: snakeCaseMapping })
114353
114616
  .use(mdxish_tables)
114354
114617
  .use(mdxish_html_blocks)
114355
114618
  .use(evaluate_expressions, { context: jsxContext }) // Evaluate MDX expressions using jsxContext
@@ -114363,8 +114626,8 @@ function mdxish(mdContent, opts = {}) {
114363
114626
  components,
114364
114627
  processMarkdown: (markdown) => mdxish(markdown, opts),
114365
114628
  });
114366
- const vfile = new VFile({ value: processedContent });
114367
- const hast = processor.runSync(processor.parse(processedContent), vfile);
114629
+ const vfile = new VFile({ value: parserReadyContent });
114630
+ const hast = processor.runSync(processor.parse(parserReadyContent), vfile);
114368
114631
  if (!hast) {
114369
114632
  throw new Error('Markdown pipeline did not produce a HAST tree.');
114370
114633
  }
@@ -114801,8 +115064,7 @@ const tags = (doc) => {
114801
115064
  const mdxishTags_tags = (doc) => {
114802
115065
  const { replaced: sanitizedDoc } = extractMagicBlocks(doc);
114803
115066
  const set = new Set();
114804
- const processor = remark()
114805
- .use(mdxish_component_blocks);
115067
+ const processor = remark().use(mdxish_component_blocks);
114806
115068
  const tree = processor.parse(sanitizedDoc);
114807
115069
  visit(processor.runSync(tree), isMDXElement, (node) => {
114808
115070
  if (node.name?.match(/^[A-Z]/)) {
@@ -114888,7 +115150,7 @@ async function stripComments(doc, { mdx } = {}) {
114888
115150
  // Our markdown renderer uses this to group these code blocks into a tabbed interface.
114889
115151
  (left, right) => {
114890
115152
  // 0 = no newline between blocks
114891
- return (left.type === 'code' && right.type === 'code') ? 0 : undefined;
115153
+ return left.type === 'code' && right.type === 'code' ? 0 : undefined;
114892
115154
  },
114893
115155
  ],
114894
115156
  });