@readme/markdown 13.0.0 → 13.1.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
@@ -73306,6 +73306,59 @@ const isCalloutStructure = (node) => {
73306
73306
  const firstTextChild = firstChild.children?.[0];
73307
73307
  return firstTextChild?.type === 'text';
73308
73308
  };
73309
+ /**
73310
+ * Finds the first text node containing a newline in a paragraph's children.
73311
+ * Returns the index and the newline position within that text node.
73312
+ */
73313
+ const findNewlineInParagraph = (paragraph) => {
73314
+ for (let i = 0; i < paragraph.children.length; i += 1) {
73315
+ const child = paragraph.children[i];
73316
+ if (child.type === 'text' && typeof child.value === 'string') {
73317
+ const newlineIndex = child.value.indexOf('\n');
73318
+ if (newlineIndex !== -1) {
73319
+ return { index: i, newlineIndex };
73320
+ }
73321
+ }
73322
+ }
73323
+ return null;
73324
+ };
73325
+ /**
73326
+ * Splits a paragraph at the first newline, separating heading content (before \n)
73327
+ * from body content (after \n). Mutates the paragraph to contain only heading children.
73328
+ */
73329
+ const splitParagraphAtNewline = (paragraph) => {
73330
+ const splitPoint = findNewlineInParagraph(paragraph);
73331
+ if (!splitPoint)
73332
+ return null;
73333
+ const { index, newlineIndex } = splitPoint;
73334
+ const originalChildren = paragraph.children;
73335
+ const textNode = originalChildren[index];
73336
+ const beforeNewline = textNode.value.slice(0, newlineIndex);
73337
+ const afterNewline = textNode.value.slice(newlineIndex + 1);
73338
+ // Split paragraph: heading = children[0..index-1] + text before newline
73339
+ const headingChildren = originalChildren.slice(0, index);
73340
+ if (beforeNewline.length > 0 || headingChildren.length === 0) {
73341
+ headingChildren.push({ type: 'text', value: beforeNewline });
73342
+ }
73343
+ paragraph.children = headingChildren;
73344
+ // Body = text after newline + remaining children from original array
73345
+ const bodyChildren = [];
73346
+ if (afterNewline.length > 0) {
73347
+ bodyChildren.push({ type: 'text', value: afterNewline });
73348
+ }
73349
+ bodyChildren.push(...originalChildren.slice(index + 1));
73350
+ return bodyChildren.length > 0 ? bodyChildren : null;
73351
+ };
73352
+ /**
73353
+ * Removes the icon/match prefix from the first text node in a paragraph.
73354
+ * This is needed to clean up the raw AST after we've extracted the icon.
73355
+ */
73356
+ const removeIconPrefix = (paragraph, prefixLength) => {
73357
+ const firstTextNode = findFirst(paragraph);
73358
+ if (firstTextNode && 'value' in firstTextNode && typeof firstTextNode.value === 'string') {
73359
+ firstTextNode.value = firstTextNode.value.slice(prefixLength);
73360
+ }
73361
+ };
73309
73362
  const processBlockquote = (node, index, parent) => {
73310
73363
  if (!isCalloutStructure(node)) {
73311
73364
  // Only stringify empty blockquotes (no extractable text content)
@@ -73330,22 +73383,39 @@ const processBlockquote = (node, index, parent) => {
73330
73383
  const firstParagraph = node.children[0];
73331
73384
  const startText = lib_plain(firstParagraph).toString();
73332
73385
  const [match, icon] = startText.match(callouts_regex) || [];
73386
+ const firstParagraphOriginalEnd = firstParagraph.position.end;
73333
73387
  if (icon && match) {
73334
- const heading = startText.slice(match.length);
73335
- const empty = !heading.length && firstParagraph.children.length === 1;
73388
+ // Handle cases where heading and body are on the same line separated by a newline.
73389
+ // Example: "> ⚠️ **Bold heading**\nBody text here"
73390
+ const bodyChildren = splitParagraphAtNewline(firstParagraph);
73391
+ const didSplit = bodyChildren !== null;
73392
+ // Extract heading text after removing the icon prefix.
73393
+ // Use `plain()` to handle complex markdown structures (bold, inline code, etc.)
73394
+ const headingText = lib_plain(firstParagraph)
73395
+ .toString()
73396
+ .slice(match.length);
73397
+ // Clean up the raw AST by removing the icon prefix from the first text node
73398
+ removeIconPrefix(firstParagraph, match.length);
73399
+ const empty = !headingText.length && firstParagraph.children.length === 1;
73336
73400
  const theme = themes[icon] || 'default';
73337
- const firstChild = findFirst(node.children[0]);
73338
- if (firstChild && 'value' in firstChild && typeof firstChild.value === 'string') {
73339
- firstChild.value = firstChild.value.slice(match.length);
73340
- }
73341
- if (heading) {
73401
+ // Convert the first paragraph (first children of node) to a heading if it has content or was split
73402
+ if (headingText || didSplit) {
73342
73403
  node.children[0] = wrapHeading(node);
73343
- // @note: We add to the offset/column the length of the unicode
73344
- // character that was stripped off, so that the start position of the
73345
- // heading/text matches where it actually starts.
73404
+ // Adjust position to account for the stripped icon prefix
73346
73405
  node.children[0].position.start.offset += match.length;
73347
73406
  node.children[0].position.start.column += match.length;
73348
73407
  }
73408
+ // Insert body content as a separate paragraph after the heading
73409
+ if (bodyChildren) {
73410
+ node.children.splice(1, 0, {
73411
+ type: 'paragraph',
73412
+ children: bodyChildren,
73413
+ position: {
73414
+ start: node.children[0].position.end,
73415
+ end: firstParagraphOriginalEnd,
73416
+ },
73417
+ });
73418
+ }
73349
73419
  Object.assign(node, {
73350
73420
  type: NodeTypes.callout,
73351
73421
  data: {
@@ -106760,7 +106830,7 @@ const CUSTOM_PROP_BOUNDARIES = [
106760
106830
  /**
106761
106831
  * Tags that should be passed through and handled at runtime (not by the mdxish plugin)
106762
106832
  */
106763
- const RUNTIME_COMPONENT_TAGS = new Set(['Variable', 'variable', 'rdme-pin']);
106833
+ const RUNTIME_COMPONENT_TAGS = new Set(['Variable', 'variable', 'html-block', 'rdme-pin']);
106764
106834
  /**
106765
106835
  * Standard HTML tags that should never be treated as custom components.
106766
106836
  * Uses the html-tags package, converted to a Set<string> for efficient lookups.
@@ -114056,6 +114126,349 @@ const evaluateExpressions = ({ context = {} } = {}) => tree => {
114056
114126
  };
114057
114127
  /* harmony default export */ const evaluate_expressions = (evaluateExpressions);
114058
114128
 
114129
+ ;// ./processor/transform/mdxish/normalize-malformed-md-syntax.ts
114130
+
114131
+ // Marker patterns for multi-node emphasis detection
114132
+ const MARKER_PATTERNS = [
114133
+ { isBold: true, marker: '**' },
114134
+ { isBold: true, marker: '__' },
114135
+ { isBold: false, marker: '*' },
114136
+ { isBold: false, marker: '_' },
114137
+ ];
114138
+ // Patterns to detect for bold (** and __) and italic (* and _) syntax:
114139
+ // Bold: ** text**, **text **, word** text**, ** text **
114140
+ // Italic: * text*, *text *, word* text*, * text *
114141
+ // Same patterns for underscore variants
114142
+ // We use separate patterns for each marker type to allow this flexibility.
114143
+ // Pattern for ** bold **
114144
+ // Groups: 1=wordBefore, 2=marker, 3=contentWithSpaceAfter, 4=trailingSpace1, 5=contentWithSpaceBefore, 6=trailingSpace2, 7=afterChar
114145
+ // trailingSpace1 is for "** text **" pattern, trailingSpace2 is for "**text **" pattern
114146
+ const asteriskBoldRegex = /([^*\s]+)?\s*(\*\*)(?:\s+((?:[^*\n]|\*(?!\*))+?)(\s*)\2|((?:[^*\n]|\*(?!\*))+?)(\s+)\2)(\S|$)?/g;
114147
+ // Pattern for __ bold __
114148
+ const underscoreBoldRegex = /([^_\s]+)?\s*(__)(?:\s+((?:[^_\n]|_(?!_))+?)(\s*)\2|((?:[^_\n]|_(?!_))+?)(\s+)\2)(\S|$)?/g;
114149
+ // Pattern for * italic *
114150
+ const asteriskItalicRegex = /([^*\s]+)?\s*(\*)(?!\*)(?:\s+([^*\n]+?)(\s*)\2|([^*\n]+?)(\s+)\2)(\S|$)?/g;
114151
+ // Pattern for _ italic _
114152
+ const underscoreItalicRegex = /([^_\s]+)?\s*(_)(?!_)(?:\s+([^_\n]+?)(\s*)\2|([^_\n]+?)(\s+)\2)(\S|$)?/g;
114153
+ // CommonMark ignores intraword underscores or asteriks, but we want to italicize/bold the inner part
114154
+ // Pattern for intraword _word_ in words like hello_world_
114155
+ const intrawordUnderscoreItalicRegex = /(\w)_(?!_)([a-zA-Z0-9]+)_(?![\w_])/g;
114156
+ // Pattern for intraword __word__ in words like hello__world__
114157
+ const intrawordUnderscoreBoldRegex = /(\w)__([a-zA-Z0-9]+)__(?![\w_])/g;
114158
+ // Pattern for intraword *word* in words like hello*world*
114159
+ const intrawordAsteriskItalicRegex = /(\w)\*(?!\*)([a-zA-Z0-9]+)\*(?![\w*])/g;
114160
+ // Pattern for intraword **word** in words like hello**world**
114161
+ const intrawordAsteriskBoldRegex = /(\w)\*\*([a-zA-Z0-9]+)\*\*(?![\w*])/g;
114162
+ /**
114163
+ * Finds opening emphasis marker in a text value.
114164
+ * Returns marker info if found, null otherwise.
114165
+ */
114166
+ function findOpeningMarker(text) {
114167
+ const results = MARKER_PATTERNS.map(({ isBold, marker }) => {
114168
+ if (marker === '*' && text.startsWith('**'))
114169
+ return null;
114170
+ if (marker === '_' && text.startsWith('__'))
114171
+ return null;
114172
+ if (text.startsWith(marker) && text.length > marker.length) {
114173
+ return { isBold, marker, textAfter: text.slice(marker.length), textBefore: '' };
114174
+ }
114175
+ const idx = text.indexOf(marker);
114176
+ if (idx > 0 && !/\s/.test(text[idx - 1])) {
114177
+ if (marker === '*' && text.slice(idx).startsWith('**'))
114178
+ return null;
114179
+ if (marker === '_' && text.slice(idx).startsWith('__'))
114180
+ return null;
114181
+ const after = text.slice(idx + marker.length);
114182
+ if (after.length > 0) {
114183
+ return { isBold, marker, textAfter: after, textBefore: text.slice(0, idx) };
114184
+ }
114185
+ }
114186
+ return null;
114187
+ });
114188
+ return results.find(r => r !== null) ?? null;
114189
+ }
114190
+ /**
114191
+ * Finds the end/closing marker in a text node for multi-node emphasis.
114192
+ */
114193
+ function findEndMarker(text, marker) {
114194
+ const spacePattern = ` ${marker}`;
114195
+ const spaceIdx = text.indexOf(spacePattern);
114196
+ if (spaceIdx >= 0) {
114197
+ if (marker === '*' && text.slice(spaceIdx + 1).startsWith('**'))
114198
+ return null;
114199
+ if (marker === '_' && text.slice(spaceIdx + 1).startsWith('__'))
114200
+ return null;
114201
+ return {
114202
+ textAfter: text.slice(spaceIdx + spacePattern.length),
114203
+ textBefore: text.slice(0, spaceIdx),
114204
+ };
114205
+ }
114206
+ if (text.startsWith(marker)) {
114207
+ if (marker === '*' && text.startsWith('**'))
114208
+ return null;
114209
+ if (marker === '_' && text.startsWith('__'))
114210
+ return null;
114211
+ return {
114212
+ textAfter: text.slice(marker.length),
114213
+ textBefore: '',
114214
+ };
114215
+ }
114216
+ return null;
114217
+ }
114218
+ /**
114219
+ * Scan children for an opening emphasis marker in a text node.
114220
+ */
114221
+ function findOpeningInChildren(children) {
114222
+ let result = null;
114223
+ children.some((child, idx) => {
114224
+ if (child.type !== 'text')
114225
+ return false;
114226
+ const found = findOpeningMarker(child.value);
114227
+ if (found) {
114228
+ result = { idx, opening: found };
114229
+ return true;
114230
+ }
114231
+ return false;
114232
+ });
114233
+ return result;
114234
+ }
114235
+ /**
114236
+ * Scan children (after openingIdx) for a closing emphasis marker.
114237
+ */
114238
+ function findClosingInChildren(children, openingIdx, marker) {
114239
+ let result = null;
114240
+ children.slice(openingIdx + 1).some((child, relativeIdx) => {
114241
+ if (child.type !== 'text')
114242
+ return false;
114243
+ const found = findEndMarker(child.value, marker);
114244
+ if (found) {
114245
+ result = { closingIdx: openingIdx + 1 + relativeIdx, closing: found };
114246
+ return true;
114247
+ }
114248
+ return false;
114249
+ });
114250
+ return result;
114251
+ }
114252
+ /**
114253
+ * Build the replacement nodes for a matched emphasis pair.
114254
+ */
114255
+ function buildReplacementNodes(container, { opening, openingIdx, closing, closingIdx }) {
114256
+ const newNodes = [];
114257
+ if (opening.textBefore) {
114258
+ newNodes.push({ type: 'text', value: `${opening.textBefore} ` });
114259
+ }
114260
+ const emphasisChildren = [];
114261
+ const openingText = opening.textAfter.replace(/^\s+/, '');
114262
+ if (openingText) {
114263
+ emphasisChildren.push({ type: 'text', value: openingText });
114264
+ }
114265
+ container.children.slice(openingIdx + 1, closingIdx).forEach(child => {
114266
+ emphasisChildren.push(child);
114267
+ });
114268
+ const closingText = closing.textBefore.replace(/\s+$/, '');
114269
+ if (closingText) {
114270
+ emphasisChildren.push({ type: 'text', value: closingText });
114271
+ }
114272
+ if (emphasisChildren.length > 0) {
114273
+ const emphasisNode = opening.isBold
114274
+ ? { type: 'strong', children: emphasisChildren }
114275
+ : { type: 'emphasis', children: emphasisChildren };
114276
+ newNodes.push(emphasisNode);
114277
+ }
114278
+ if (closing.textAfter) {
114279
+ newNodes.push({ type: 'text', value: closing.textAfter });
114280
+ }
114281
+ return newNodes;
114282
+ }
114283
+ /**
114284
+ * Find and transform one multi-node emphasis pair in the container.
114285
+ * Returns true if a pair was found and transformed, false otherwise.
114286
+ */
114287
+ function processOneEmphasisPair(container) {
114288
+ const openingResult = findOpeningInChildren(container.children);
114289
+ if (!openingResult)
114290
+ return false;
114291
+ const { idx: openingIdx, opening } = openingResult;
114292
+ const closingResult = findClosingInChildren(container.children, openingIdx, opening.marker);
114293
+ if (!closingResult)
114294
+ return false;
114295
+ const { closingIdx, closing } = closingResult;
114296
+ const newNodes = buildReplacementNodes(container, { opening, openingIdx, closing, closingIdx });
114297
+ const deleteCount = closingIdx - openingIdx + 1;
114298
+ container.children.splice(openingIdx, deleteCount, ...newNodes);
114299
+ return true;
114300
+ }
114301
+ /**
114302
+ * Handle malformed emphasis that spans multiple AST nodes.
114303
+ * E.g., "**bold [link](url)**" where markers are in different text nodes.
114304
+ */
114305
+ function visitMultiNodeEmphasis(tree) {
114306
+ const containerTypes = ['paragraph', 'heading', 'tableCell', 'listItem', 'blockquote'];
114307
+ visit(tree, node => {
114308
+ if (!containerTypes.includes(node.type))
114309
+ return;
114310
+ if (!('children' in node) || !Array.isArray(node.children))
114311
+ return;
114312
+ const container = node;
114313
+ let foundPair = true;
114314
+ while (foundPair) {
114315
+ foundPair = processOneEmphasisPair(container);
114316
+ }
114317
+ });
114318
+ }
114319
+ /**
114320
+ * A remark plugin that normalizes malformed bold and italic markers in text nodes.
114321
+ * Detects patterns like `** bold**`, `Hello** Wrong Bold**`, `__ bold__`, `Hello__ Wrong Bold__`,
114322
+ * `* italic*`, `Hello* Wrong Italic*`, `_ italic_`, or `Hello_ Wrong Italic_`
114323
+ * and converts them to proper strong/emphasis nodes, matching the behavior of the legacy rdmd engine.
114324
+ *
114325
+ * Supports both asterisk (`**bold**`, `*italic*`) and underscore (`__bold__`, `_italic_`) syntax.
114326
+ * Also supports snake_case content like `** some_snake_case**`.
114327
+ *
114328
+ * This runs after remark-parse, which (in v11+) is strict and doesn't parse
114329
+ * malformed emphasis syntax. This plugin post-processes the AST to handle these cases.
114330
+ */
114331
+ const normalizeEmphasisAST = () => (tree) => {
114332
+ visit(tree, 'text', function visitor(node, index, parent) {
114333
+ if (index === undefined || !parent)
114334
+ return undefined;
114335
+ // Skip if inside code blocks or inline code
114336
+ if (parent.type === 'inlineCode' || parent.type === 'code') {
114337
+ return undefined;
114338
+ }
114339
+ const text = node.value;
114340
+ const allMatches = [];
114341
+ [...text.matchAll(asteriskBoldRegex)].forEach(match => {
114342
+ allMatches.push({ isBold: true, marker: '**', match });
114343
+ });
114344
+ [...text.matchAll(underscoreBoldRegex)].forEach(match => {
114345
+ allMatches.push({ isBold: true, marker: '__', match });
114346
+ });
114347
+ [...text.matchAll(asteriskItalicRegex)].forEach(match => {
114348
+ allMatches.push({ isBold: false, marker: '*', match });
114349
+ });
114350
+ [...text.matchAll(underscoreItalicRegex)].forEach(match => {
114351
+ allMatches.push({ isBold: false, marker: '_', match });
114352
+ });
114353
+ [...text.matchAll(intrawordUnderscoreItalicRegex)].forEach(match => {
114354
+ allMatches.push({ isBold: false, isIntraword: true, marker: '_', match });
114355
+ });
114356
+ [...text.matchAll(intrawordUnderscoreBoldRegex)].forEach(match => {
114357
+ allMatches.push({ isBold: true, isIntraword: true, marker: '__', match });
114358
+ });
114359
+ [...text.matchAll(intrawordAsteriskItalicRegex)].forEach(match => {
114360
+ allMatches.push({ isBold: false, isIntraword: true, marker: '*', match });
114361
+ });
114362
+ [...text.matchAll(intrawordAsteriskBoldRegex)].forEach(match => {
114363
+ allMatches.push({ isBold: true, isIntraword: true, marker: '**', match });
114364
+ });
114365
+ if (allMatches.length === 0)
114366
+ return undefined;
114367
+ allMatches.sort((a, b) => (a.match.index ?? 0) - (b.match.index ?? 0));
114368
+ const filteredMatches = [];
114369
+ let lastEnd = 0;
114370
+ allMatches.forEach(info => {
114371
+ const start = info.match.index ?? 0;
114372
+ const end = start + info.match[0].length;
114373
+ if (start >= lastEnd) {
114374
+ filteredMatches.push(info);
114375
+ lastEnd = end;
114376
+ }
114377
+ });
114378
+ if (filteredMatches.length === 0)
114379
+ return undefined;
114380
+ const parts = [];
114381
+ let lastIndex = 0;
114382
+ filteredMatches.forEach(({ isBold, isIntraword, marker, match }) => {
114383
+ const matchIndex = match.index ?? 0;
114384
+ const fullMatch = match[0];
114385
+ if (isIntraword) {
114386
+ // handles cases like hello_world_ where we only want to italicize 'world'
114387
+ const charBefore = match[1] || ''; // e.g., "l" in "hello_world_"
114388
+ const content = match[2]; // e.g., "world"
114389
+ const combinedBefore = text.slice(lastIndex, matchIndex) + charBefore;
114390
+ if (combinedBefore) {
114391
+ parts.push({ type: 'text', value: combinedBefore });
114392
+ }
114393
+ if (isBold) {
114394
+ parts.push({
114395
+ type: 'strong',
114396
+ children: [{ type: 'text', value: content }],
114397
+ });
114398
+ }
114399
+ else {
114400
+ parts.push({
114401
+ type: 'emphasis',
114402
+ children: [{ type: 'text', value: content }],
114403
+ });
114404
+ }
114405
+ lastIndex = matchIndex + fullMatch.length;
114406
+ return;
114407
+ }
114408
+ if (matchIndex > lastIndex) {
114409
+ const beforeText = text.slice(lastIndex, matchIndex);
114410
+ if (beforeText) {
114411
+ parts.push({ type: 'text', value: beforeText });
114412
+ }
114413
+ }
114414
+ const wordBefore = match[1]; // e.g., "Hello" in "Hello** Wrong Bold**"
114415
+ const contentWithSpaceAfter = match[3]; // Content when there's a space after opening markers
114416
+ const trailingSpace1 = match[4] || ''; // Space before closing markers (for "** text **" pattern)
114417
+ const contentWithSpaceBefore = match[5]; // Content when there's only a space before closing markers
114418
+ const trailingSpace2 = match[6] || ''; // Space before closing markers (for "**text **" pattern)
114419
+ const trailingSpace = trailingSpace1 || trailingSpace2; // Combined trailing space
114420
+ const content = (contentWithSpaceAfter || contentWithSpaceBefore || '').trim();
114421
+ const afterChar = match[7]; // Character after closing markers (if any)
114422
+ const markerPos = fullMatch.indexOf(marker);
114423
+ const spacesBeforeMarkers = wordBefore
114424
+ ? fullMatch.slice(wordBefore.length, markerPos)
114425
+ : fullMatch.slice(0, markerPos);
114426
+ const shouldAddSpace = !!contentWithSpaceAfter && !!wordBefore && !spacesBeforeMarkers;
114427
+ if (wordBefore) {
114428
+ const spacing = spacesBeforeMarkers + (shouldAddSpace ? ' ' : '');
114429
+ parts.push({ type: 'text', value: wordBefore + spacing });
114430
+ }
114431
+ else if (spacesBeforeMarkers) {
114432
+ parts.push({ type: 'text', value: spacesBeforeMarkers });
114433
+ }
114434
+ if (content) {
114435
+ if (isBold) {
114436
+ parts.push({
114437
+ type: 'strong',
114438
+ children: [{ type: 'text', value: content }],
114439
+ });
114440
+ }
114441
+ else {
114442
+ parts.push({
114443
+ type: 'emphasis',
114444
+ children: [{ type: 'text', value: content }],
114445
+ });
114446
+ }
114447
+ }
114448
+ if (afterChar) {
114449
+ const prefix = trailingSpace ? ' ' : '';
114450
+ parts.push({ type: 'text', value: prefix + afterChar });
114451
+ }
114452
+ lastIndex = matchIndex + fullMatch.length;
114453
+ });
114454
+ if (lastIndex < text.length) {
114455
+ const remainingText = text.slice(lastIndex);
114456
+ if (remainingText) {
114457
+ parts.push({ type: 'text', value: remainingText });
114458
+ }
114459
+ }
114460
+ if (parts.length > 0) {
114461
+ parent.children.splice(index, 1, ...parts);
114462
+ return [SKIP, index + parts.length];
114463
+ }
114464
+ return undefined;
114465
+ });
114466
+ // Handle malformed emphasis spanning multiple nodes (e.g., **text [link](url) **)
114467
+ visitMultiNodeEmphasis(tree);
114468
+ return tree;
114469
+ };
114470
+ /* harmony default export */ const normalize_malformed_md_syntax = (normalizeEmphasisAST);
114471
+
114059
114472
  ;// ./processor/transform/mdxish/magic-blocks/placeholder.ts
114060
114473
  const EMPTY_IMAGE_PLACEHOLDER = {
114061
114474
  type: 'image',
@@ -114108,6 +114521,7 @@ const EMPTY_CODE_PLACEHOLDER = {
114108
114521
 
114109
114522
 
114110
114523
 
114524
+
114111
114525
  /**
114112
114526
  * Wraps a node in a "pinned" container if sidebar: true is set.
114113
114527
  */
@@ -114135,7 +114549,8 @@ const imgWidthBySize = new Proxy(imgSizeValues, {
114135
114549
  });
114136
114550
  const textToInline = (text) => [{ type: 'text', value: text }];
114137
114551
  const textToBlock = (text) => [{ children: textToInline(text), type: 'paragraph' }];
114138
- const contentParser = unified().use(remarkParse).use(remarkGfm);
114552
+ /** Parses markdown and html to markdown nodes */
114553
+ const contentParser = unified().use(remarkParse).use(remarkGfm).use(normalize_malformed_md_syntax);
114139
114554
  const parseTableCell = (text) => {
114140
114555
  if (!text.trim())
114141
114556
  return [{ type: 'text', value: '' }];
@@ -115173,139 +115588,6 @@ function restoreSnakeCase(placeholderName, mapping) {
115173
115588
  return matchingKey ? mapping[matchingKey] : placeholderName;
115174
115589
  }
115175
115590
 
115176
- ;// ./processor/transform/mdxish/normalize-malformed-md-syntax.ts
115177
-
115178
- // Patterns to detect for bold (** and __) and italic (* and _) syntax:
115179
- // Bold: ** text**, **text **, word** text**, ** text **
115180
- // Italic: * text*, *text *, word* text*, * text *
115181
- // Same patterns for underscore variants
115182
- // We use separate patterns for each marker type to allow this flexibility.
115183
- // Pattern for ** bold **
115184
- // Groups: 1=wordBefore, 2=marker, 3=contentWithSpaceAfter, 4=trailingSpace1, 5=contentWithSpaceBefore, 6=trailingSpace2, 7=afterChar
115185
- // trailingSpace1 is for "** text **" pattern, trailingSpace2 is for "**text **" pattern
115186
- const asteriskBoldRegex = /([^*\s]+)?\s*(\*\*)(?:\s+((?:[^*\n]|\*(?!\*))+?)(\s*)\2|((?:[^*\n]|\*(?!\*))+?)(\s+)\2)(\S|$)?/g;
115187
- // Pattern for __ bold __
115188
- const underscoreBoldRegex = /([^_\s]+)?\s*(__)(?:\s+((?:[^_\n]|_(?!_))+?)(\s*)\2|((?:[^_\n]|_(?!_))+?)(\s+)\2)(\S|$)?/g;
115189
- // Pattern for * italic *
115190
- const asteriskItalicRegex = /([^*\s]+)?\s*(\*)(?!\*)(?:\s+([^*\n]+?)(\s*)\2|([^*\n]+?)(\s+)\2)(\S|$)?/g;
115191
- // Pattern for _ italic _
115192
- const underscoreItalicRegex = /([^_\s]+)?\s*(_)(?!_)(?:\s+([^_\n]+?)(\s*)\2|([^_\n]+?)(\s+)\2)(\S|$)?/g;
115193
- /**
115194
- * A remark plugin that normalizes malformed bold and italic markers in text nodes.
115195
- * Detects patterns like `** bold**`, `Hello** Wrong Bold**`, `__ bold__`, `Hello__ Wrong Bold__`,
115196
- * `* italic*`, `Hello* Wrong Italic*`, `_ italic_`, or `Hello_ Wrong Italic_`
115197
- * and converts them to proper strong/emphasis nodes, matching the behavior of the legacy rdmd engine.
115198
- *
115199
- * Supports both asterisk (`**bold**`, `*italic*`) and underscore (`__bold__`, `_italic_`) syntax.
115200
- * Also supports snake_case content like `** some_snake_case**`.
115201
- *
115202
- * This runs after remark-parse, which (in v11+) is strict and doesn't parse
115203
- * malformed emphasis syntax. This plugin post-processes the AST to handle these cases.
115204
- */
115205
- const normalizeEmphasisAST = () => (tree) => {
115206
- visit(tree, 'text', function visitor(node, index, parent) {
115207
- if (index === undefined || !parent)
115208
- return undefined;
115209
- // Skip if inside code blocks or inline code
115210
- if (parent.type === 'inlineCode' || parent.type === 'code') {
115211
- return undefined;
115212
- }
115213
- const text = node.value;
115214
- const allMatches = [];
115215
- [...text.matchAll(asteriskBoldRegex)].forEach(match => {
115216
- allMatches.push({ isBold: true, marker: '**', match });
115217
- });
115218
- [...text.matchAll(underscoreBoldRegex)].forEach(match => {
115219
- allMatches.push({ isBold: true, marker: '__', match });
115220
- });
115221
- [...text.matchAll(asteriskItalicRegex)].forEach(match => {
115222
- allMatches.push({ isBold: false, marker: '*', match });
115223
- });
115224
- [...text.matchAll(underscoreItalicRegex)].forEach(match => {
115225
- allMatches.push({ isBold: false, marker: '_', match });
115226
- });
115227
- if (allMatches.length === 0)
115228
- return undefined;
115229
- allMatches.sort((a, b) => (a.match.index ?? 0) - (b.match.index ?? 0));
115230
- const filteredMatches = [];
115231
- let lastEnd = 0;
115232
- allMatches.forEach(info => {
115233
- const start = info.match.index ?? 0;
115234
- const end = start + info.match[0].length;
115235
- if (start >= lastEnd) {
115236
- filteredMatches.push(info);
115237
- lastEnd = end;
115238
- }
115239
- });
115240
- if (filteredMatches.length === 0)
115241
- return undefined;
115242
- const parts = [];
115243
- let lastIndex = 0;
115244
- filteredMatches.forEach(({ match, marker, isBold }) => {
115245
- const matchIndex = match.index ?? 0;
115246
- const fullMatch = match[0];
115247
- if (matchIndex > lastIndex) {
115248
- const beforeText = text.slice(lastIndex, matchIndex);
115249
- if (beforeText) {
115250
- parts.push({ type: 'text', value: beforeText });
115251
- }
115252
- }
115253
- const wordBefore = match[1]; // e.g., "Hello" in "Hello** Wrong Bold**"
115254
- const contentWithSpaceAfter = match[3]; // Content when there's a space after opening markers
115255
- const trailingSpace1 = match[4] || ''; // Space before closing markers (for "** text **" pattern)
115256
- const contentWithSpaceBefore = match[5]; // Content when there's only a space before closing markers
115257
- const trailingSpace2 = match[6] || ''; // Space before closing markers (for "**text **" pattern)
115258
- const trailingSpace = trailingSpace1 || trailingSpace2; // Combined trailing space
115259
- const content = (contentWithSpaceAfter || contentWithSpaceBefore || '').trim();
115260
- const afterChar = match[7]; // Character after closing markers (if any)
115261
- const markerPos = fullMatch.indexOf(marker);
115262
- const spacesBeforeMarkers = wordBefore
115263
- ? fullMatch.slice(wordBefore.length, markerPos)
115264
- : fullMatch.slice(0, markerPos);
115265
- const shouldAddSpace = !!contentWithSpaceAfter && !!wordBefore && !spacesBeforeMarkers;
115266
- if (wordBefore) {
115267
- const spacing = spacesBeforeMarkers + (shouldAddSpace ? ' ' : '');
115268
- parts.push({ type: 'text', value: wordBefore + spacing });
115269
- }
115270
- else if (spacesBeforeMarkers) {
115271
- parts.push({ type: 'text', value: spacesBeforeMarkers });
115272
- }
115273
- if (content) {
115274
- if (isBold) {
115275
- parts.push({
115276
- type: 'strong',
115277
- children: [{ type: 'text', value: content }],
115278
- });
115279
- }
115280
- else {
115281
- parts.push({
115282
- type: 'emphasis',
115283
- children: [{ type: 'text', value: content }],
115284
- });
115285
- }
115286
- }
115287
- if (afterChar) {
115288
- const prefix = trailingSpace ? ' ' : '';
115289
- parts.push({ type: 'text', value: prefix + afterChar });
115290
- }
115291
- lastIndex = matchIndex + fullMatch.length;
115292
- });
115293
- if (lastIndex < text.length) {
115294
- const remainingText = text.slice(lastIndex);
115295
- if (remainingText) {
115296
- parts.push({ type: 'text', value: remainingText });
115297
- }
115298
- }
115299
- if (parts.length > 0) {
115300
- parent.children.splice(index, 1, ...parts);
115301
- return [SKIP, index + parts.length];
115302
- }
115303
- return undefined;
115304
- });
115305
- return tree;
115306
- };
115307
- /* harmony default export */ const normalize_malformed_md_syntax = (normalizeEmphasisAST);
115308
-
115309
115591
  ;// ./processor/transform/mdxish/normalize-table-separator.ts
115310
115592
  /**
115311
115593
  * Preprocessor to normalize malformed GFM table separator syntax.
@@ -116777,7 +117059,7 @@ function loadComponents() {
116777
117059
 
116778
117060
  const defaultTransformers = [callouts, code_tabs, gemoji_, transform_embeds];
116779
117061
  function mdxishAstProcessor(mdContent, opts = {}) {
116780
- const { components: userComponents = {}, jsxContext = {}, newEditorTypes = false, useTailwind } = opts;
117062
+ const { components: userComponents = {}, jsxContext = {}, newEditorTypes = false, safeMode = false, useTailwind, } = opts;
116781
117063
  const components = {
116782
117064
  ...loadComponents(),
116783
117065
  ...userComponents,
@@ -116788,7 +117070,9 @@ function mdxishAstProcessor(mdContent, opts = {}) {
116788
117070
  // Step 1: Normalize malformed table separator syntax (e.g., `|: ---` → `| :---`)
116789
117071
  const contentAfterTableNormalization = normalizeTableSeparator(mdContent);
116790
117072
  // Step 2: Evaluate JSX expressions in attributes
116791
- const contentAfterJSXEvaluation = preprocessJSXExpressions(contentAfterTableNormalization, jsxContext);
117073
+ const contentAfterJSXEvaluation = safeMode
117074
+ ? contentAfterTableNormalization
117075
+ : preprocessJSXExpressions(contentAfterTableNormalization, jsxContext);
116792
117076
  // Step 3: Replace snake_case component names with parser-safe placeholders
116793
117077
  const { content: parserReadyContent, mapping: snakeCaseMapping } = processSnakeCaseComponent(contentAfterJSXEvaluation, { knownComponents });
116794
117078
  // Create string map for tailwind transformer
@@ -116803,8 +117087,8 @@ function mdxishAstProcessor(mdContent, opts = {}) {
116803
117087
  text: mdxExprExt.text,
116804
117088
  };
116805
117089
  const processor = unified()
116806
- .data('micromarkExtensions', [magicBlock(), mdxExprTextOnly])
116807
- .data('fromMarkdownExtensions', [magicBlockFromMarkdown(), mdxExpressionFromMarkdown()])
117090
+ .data('micromarkExtensions', safeMode ? [magicBlock()] : [magicBlock(), mdxExprTextOnly])
117091
+ .data('fromMarkdownExtensions', safeMode ? [magicBlockFromMarkdown()] : [magicBlockFromMarkdown(), mdxExpressionFromMarkdown()])
116808
117092
  .use(remarkParse)
116809
117093
  .use(remarkFrontmatter)
116810
117094
  .use(normalize_malformed_md_syntax)
@@ -116816,8 +117100,8 @@ function mdxishAstProcessor(mdContent, opts = {}) {
116816
117100
  .use(mdxish_tables)
116817
117101
  .use(mdxish_html_blocks)
116818
117102
  .use(newEditorTypes ? mdxish_jsx_to_mdast : undefined) // Convert JSX elements to MDAST types
116819
- .use(evaluate_expressions, { context: jsxContext }) // Evaluate MDX expressions using jsxContext
116820
- .use(variables_text) // Parse {user.*} patterns from text (can't rely on remarkMdx)
117103
+ .use(safeMode ? undefined : evaluate_expressions, { context: jsxContext }) // Evaluate MDX expressions using jsxContext
117104
+ .use(variables_text) // Parse {user.*} patterns from text
116821
117105
  .use(useTailwind ? transform_tailwind : undefined, { components: tempComponentsMap })
116822
117106
  .use(remarkGfm);
116823
117107
  return {