@readme/markdown 14.10.0 → 14.10.1

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.js CHANGED
@@ -76783,6 +76783,7 @@ const readmeComponents = (opts) => () => tree => {
76783
76783
 
76784
76784
 
76785
76785
 
76786
+
76786
76787
  const imageAttrs = ['align', 'alt', 'caption', 'border', 'height', 'src', 'title', 'width', 'lazy', 'className'];
76787
76788
  const readmeToMdx = () => tree => {
76788
76789
  // Unwrap pinned nodes, replace rdme-pin with its child node
@@ -76873,7 +76874,11 @@ const readmeToMdx = () => tree => {
76873
76874
  }
76874
76875
  });
76875
76876
  visit(tree, NodeTypes.imageBlock, (image, index, parent) => {
76876
- const attributes = toAttributes({ ...image, ...image.data.hProperties }, imageAttrs);
76877
+ // Special case for caption: Captions are parsed as children of the image node, so we need to extract them explicitly
76878
+ const captionChildren = Array.isArray(image.children) ? image.children : [];
76879
+ // We want to add it back to the attributes, so re-serialize it to string
76880
+ const caption = captionChildren.length ? toMarkdown({ type: 'root', children: captionChildren }).trim() : '';
76881
+ const attributes = toAttributes({ ...image, ...image.data.hProperties, ...(caption && { caption }) }, imageAttrs);
76877
76882
  if (hasExtra(attributes)) {
76878
76883
  parent.children.splice(index, 1, {
76879
76884
  type: 'mdxJsxFlowElement',
@@ -98611,6 +98616,11 @@ function smartCamelCase(str) {
98611
98616
  if (str.includes('-')) {
98612
98617
  return str.replace(/-([a-z])/g, (_, char) => char.toUpperCase());
98613
98618
  }
98619
+ // Keys that already carry casing (e.g. `cardWidth`) are correct as-is; running
98620
+ // boundary matching over them mangles inner matches (e.g. `id` inside `Width`).
98621
+ if (/[A-Z]/.test(str)) {
98622
+ return str;
98623
+ }
98614
98624
  const allBoundaries = [...REACT_HTML_PROP_BOUNDARIES, ...CSS_STYLE_PROP_BOUNDARIES, ...CUSTOM_PROP_BOUNDARIES];
98615
98625
  // Return as-is if already a boundary word to avoid incorrect splitting
98616
98626
  if (allBoundaries.includes(str.toLowerCase())) {
@@ -101719,6 +101729,27 @@ const parseSibling = (stack, parent, index, sibling, safeMode) => {
101719
101729
  stack.push(parent);
101720
101730
  }
101721
101731
  };
101732
+ /**
101733
+ * Advance a point by the substring of source consumed from it.
101734
+ */
101735
+ const pointAfter = (start, consumed) => {
101736
+ const newlineIndex = consumed.lastIndexOf('\n');
101737
+ const newlineCount = newlineIndex === -1 ? 0 : consumed.split('\n').length - 1;
101738
+ return {
101739
+ line: start.line + newlineCount,
101740
+ column: newlineCount === 0 ? start.column + consumed.length : consumed.length - newlineIndex,
101741
+ offset: start.offset + consumed.length,
101742
+ };
101743
+ };
101744
+ /**
101745
+ * Build a position ending at `consumedLength` into the html node's value, so the
101746
+ * component doesn't claim trailing content the tokenizer swallowed into one node.
101747
+ */
101748
+ const positionEndingAtConsumed = (nodePosition, value, consumedLength) => {
101749
+ if (!nodePosition?.start)
101750
+ return nodePosition;
101751
+ return { start: nodePosition.start, end: pointAfter(nodePosition.start, value.slice(0, consumedLength)) };
101752
+ };
101722
101753
  /**
101723
101754
  * Create an MdxJsxFlowElement node from component data.
101724
101755
  */
@@ -101778,10 +101809,16 @@ const mdxishMdxComponentBlocks = (opts = {}) => tree => {
101778
101809
  const value = node.value;
101779
101810
  if (node.type !== 'html' || typeof value !== 'string')
101780
101811
  return;
101781
- const parsed = parseTag(value.trim(), parseOpts);
101812
+ const trimmed = value.trim();
101813
+ const parsed = parseTag(trimmed, parseOpts);
101782
101814
  if (!parsed)
101783
101815
  return;
101784
101816
  const { tag, attributes, selfClosing, contentAfterTag = '' } = parsed;
101817
+ // Offset of `trimmed` within the (possibly whitespace-padded) html node value,
101818
+ // so consumed-length math maps back onto the node's real source offsets.
101819
+ const leadingWhitespace = value.length - value.trimStart().length;
101820
+ // Index right after the opening tag's `>` within `trimmed`.
101821
+ const openingTagEnd = trimmed.length - contentAfterTag.length;
101785
101822
  // Skip tags that have dedicated transformers
101786
101823
  if (GENERIC_MDX_COMPONENT_EXCLUDED_TAGS.has(tag))
101787
101824
  return;
@@ -101806,6 +101843,8 @@ const mdxishMdxComponentBlocks = (opts = {}) => tree => {
101806
101843
  attributes,
101807
101844
  children: [],
101808
101845
  startPosition: node.position,
101846
+ // End at the self-closing tag, not at any trailing content.
101847
+ endPosition: positionEndingAtConsumed(node.position, value, leadingWhitespace + openingTagEnd),
101809
101848
  });
101810
101849
  substituteNodeWithMdxNode(parent, index, componentNode);
101811
101850
  // Check and parse if there's relevant content after the current closing tag
@@ -101837,6 +101876,8 @@ const mdxishMdxComponentBlocks = (opts = {}) => tree => {
101837
101876
  attributes,
101838
101877
  children: parsedChildren,
101839
101878
  startPosition: node.position,
101879
+ // End at the closing tag, not at trailing content re-parsed as siblings below.
101880
+ endPosition: positionEndingAtConsumed(node.position, value, leadingWhitespace + openingTagEnd + closingTagIndex + closingTagStr.length),
101840
101881
  });
101841
101882
  substituteNodeWithMdxNode(parent, index, componentNode);
101842
101883
  // After the closing tag, there might be more content to be processed
@@ -104107,6 +104148,24 @@ function restoreHTMLElements(content, htmlElements) {
104107
104148
  return content;
104108
104149
  return content.replace(HTML_ELEM_PLACEHOLDER, (_m, idx) => htmlElements[parseInt(idx, 10)]);
104109
104150
  }
104151
+ const ESM_DECLARATION_START = /^(?:export|import)\b/;
104152
+ /**
104153
+ * Whether a line begins a top-level `export`/`import` declaration. Its braces
104154
+ * are valid JS owned by the mdxjsEsm tokenizer (which tolerates blank lines),
104155
+ * so escaping them would corrupt the source and break acorn parsing.
104156
+ */
104157
+ function startsEsmDeclaration(chars, lineStart) {
104158
+ return ESM_DECLARATION_START.test(chars.slice(lineStart, lineStart + 7).join(''));
104159
+ }
104160
+ function isBlankLine(chars, lineStart) {
104161
+ for (let i = lineStart; i < chars.length; i += 1) {
104162
+ if (chars[i] === '\n')
104163
+ return true;
104164
+ if (chars[i] !== ' ' && chars[i] !== '\t')
104165
+ return false;
104166
+ }
104167
+ return true;
104168
+ }
104110
104169
  /**
104111
104170
  * Escapes unbalanced and paragraph-spanning braces so MDX doesn't trip on them.
104112
104171
  */
@@ -104122,8 +104181,18 @@ function escapeProblematicBraces(content) {
104122
104181
  // Convert to array of Unicode code points so that emojis and multi-byte characters are correctly tracked
104123
104182
  const chars = Array.from(protectedContent);
104124
104183
  const openStack = [];
104184
+ // Whether the current top-level statement is an `export`/`import` declaration.
104185
+ let insideEsmDeclaration = false;
104125
104186
  for (let i = 0; i < chars.length; i += 1) {
104126
104187
  const ch = chars[i];
104188
+ // At a top-level (brace depth 0) line start, decide whether we're inside an
104189
+ // ESM declaration. The flag persists across the declaration's own lines.
104190
+ if (openStack.length === 0 && (i === 0 || chars[i - 1] === '\n')) {
104191
+ if (startsEsmDeclaration(chars, i))
104192
+ insideEsmDeclaration = true;
104193
+ else if (isBlankLine(chars, i))
104194
+ insideEsmDeclaration = false;
104195
+ }
104127
104196
  // Track string delimiters inside expressions to ignore braces within them
104128
104197
  if (openStack.length > 0) {
104129
104198
  if (strDelim) {
@@ -104181,19 +104250,22 @@ function escapeProblematicBraces(content) {
104181
104250
  if (!isAttrExpr && openStack.length > 0 && openStack[openStack.length - 1].isAttrExpr) {
104182
104251
  isAttrExpr = true;
104183
104252
  }
104184
- openStack.push({ pos: i, hasBlankLine: false, isAttrExpr });
104253
+ openStack.push({ pos: i, hasBlankLine: false, isAttrExpr, isEsmDeclaration: insideEsmDeclaration });
104185
104254
  lastNewlinePos = -2;
104186
104255
  }
104187
104256
  else if (ch === '}') {
104188
104257
  if (openStack.length > 0) {
104189
104258
  const entry = openStack.pop();
104259
+ // The declaration's braces are closed; later top-level braces are not ESM.
104260
+ if (openStack.length === 0)
104261
+ insideEsmDeclaration = false;
104190
104262
  // Pure `{/* ... */}` comments are handled downstream by the jsxComment
104191
104263
  // tokenizer — escaping their braces would prevent it from running.
104192
104264
  const isPureJsxComment = chars[entry.pos + 1] === '/' &&
104193
104265
  chars[entry.pos + 2] === '*' &&
104194
104266
  chars[i - 1] === '/' &&
104195
104267
  chars[i - 2] === '*';
104196
- if (entry.hasBlankLine && !isPureJsxComment && !entry.isAttrExpr) {
104268
+ if (entry.hasBlankLine && !isPureJsxComment && !entry.isAttrExpr && !entry.isEsmDeclaration) {
104197
104269
  toEscape.add(entry.pos);
104198
104270
  toEscape.add(i);
104199
104271
  }
@@ -104202,9 +104274,16 @@ function escapeProblematicBraces(content) {
104202
104274
  toEscape.add(i);
104203
104275
  }
104204
104276
  }
104277
+ else if (ch === ';' && openStack.length === 0) {
104278
+ // A top-level `;` ends the current statement, ESM or otherwise.
104279
+ insideEsmDeclaration = false;
104280
+ }
104205
104281
  }
104206
104282
  // Anything still open is unbalanced.
104207
- openStack.forEach(entry => toEscape.add(entry.pos));
104283
+ openStack.forEach(entry => {
104284
+ if (!entry.isEsmDeclaration)
104285
+ toEscape.add(entry.pos);
104286
+ });
104208
104287
  // Reconstruct the content with the escaped braces.
104209
104288
  const escapedContent = toEscape.size === 0 ? protectedContent : chars.map((ch, i) => (toEscape.has(i) ? `\\${ch}` : ch)).join('');
104210
104289
  return restoreHTMLElements(escapedContent, htmlElements);
@@ -105703,11 +105782,18 @@ function exportComponentsForRehype(components) {
105703
105782
  * semantics). That breaks any component prop that is genuinely an array. With
105704
105783
  * `passNode: true` we get the original hast node in `props.node` for components
105705
105784
  * and can read the raw properties directly.
105785
+ *
105786
+ * Only arrays win over `rest`, overwriting other shapes would undo correct
105787
+ * React conversions like `style="color:red"` → `{ color: 'red' }`.
105706
105788
  */
105707
105789
  function createElementPreservingHastProps(type, props, ...children) {
105708
105790
  if (props?.node?.properties) {
105709
105791
  const { node, ...rest } = props;
105710
- const mergedProps = { ...rest, ...node.properties };
105792
+ const mergedProps = { ...rest };
105793
+ Object.entries(node.properties).forEach(([key, value]) => {
105794
+ if (Array.isArray(value))
105795
+ mergedProps[key] = value;
105796
+ });
105711
105797
  // Strip undefined so positional args don't shadow node.properties.children
105712
105798
  const definedChildren = children.filter(c => c !== undefined);
105713
105799
  return external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_default().createElement(type, mergedProps, ...definedChildren);
package/dist/main.node.js CHANGED
@@ -97007,6 +97007,7 @@ const readmeComponents = (opts) => () => tree => {
97007
97007
 
97008
97008
 
97009
97009
 
97010
+
97010
97011
  const imageAttrs = ['align', 'alt', 'caption', 'border', 'height', 'src', 'title', 'width', 'lazy', 'className'];
97011
97012
  const readmeToMdx = () => tree => {
97012
97013
  // Unwrap pinned nodes, replace rdme-pin with its child node
@@ -97097,7 +97098,11 @@ const readmeToMdx = () => tree => {
97097
97098
  }
97098
97099
  });
97099
97100
  visit(tree, NodeTypes.imageBlock, (image, index, parent) => {
97100
- const attributes = toAttributes({ ...image, ...image.data.hProperties }, imageAttrs);
97101
+ // Special case for caption: Captions are parsed as children of the image node, so we need to extract them explicitly
97102
+ const captionChildren = Array.isArray(image.children) ? image.children : [];
97103
+ // We want to add it back to the attributes, so re-serialize it to string
97104
+ const caption = captionChildren.length ? toMarkdown({ type: 'root', children: captionChildren }).trim() : '';
97105
+ const attributes = toAttributes({ ...image, ...image.data.hProperties, ...(caption && { caption }) }, imageAttrs);
97101
97106
  if (hasExtra(attributes)) {
97102
97107
  parent.children.splice(index, 1, {
97103
97108
  type: 'mdxJsxFlowElement',
@@ -118835,6 +118840,11 @@ function smartCamelCase(str) {
118835
118840
  if (str.includes('-')) {
118836
118841
  return str.replace(/-([a-z])/g, (_, char) => char.toUpperCase());
118837
118842
  }
118843
+ // Keys that already carry casing (e.g. `cardWidth`) are correct as-is; running
118844
+ // boundary matching over them mangles inner matches (e.g. `id` inside `Width`).
118845
+ if (/[A-Z]/.test(str)) {
118846
+ return str;
118847
+ }
118838
118848
  const allBoundaries = [...REACT_HTML_PROP_BOUNDARIES, ...CSS_STYLE_PROP_BOUNDARIES, ...CUSTOM_PROP_BOUNDARIES];
118839
118849
  // Return as-is if already a boundary word to avoid incorrect splitting
118840
118850
  if (allBoundaries.includes(str.toLowerCase())) {
@@ -121943,6 +121953,27 @@ const parseSibling = (stack, parent, index, sibling, safeMode) => {
121943
121953
  stack.push(parent);
121944
121954
  }
121945
121955
  };
121956
+ /**
121957
+ * Advance a point by the substring of source consumed from it.
121958
+ */
121959
+ const pointAfter = (start, consumed) => {
121960
+ const newlineIndex = consumed.lastIndexOf('\n');
121961
+ const newlineCount = newlineIndex === -1 ? 0 : consumed.split('\n').length - 1;
121962
+ return {
121963
+ line: start.line + newlineCount,
121964
+ column: newlineCount === 0 ? start.column + consumed.length : consumed.length - newlineIndex,
121965
+ offset: start.offset + consumed.length,
121966
+ };
121967
+ };
121968
+ /**
121969
+ * Build a position ending at `consumedLength` into the html node's value, so the
121970
+ * component doesn't claim trailing content the tokenizer swallowed into one node.
121971
+ */
121972
+ const positionEndingAtConsumed = (nodePosition, value, consumedLength) => {
121973
+ if (!nodePosition?.start)
121974
+ return nodePosition;
121975
+ return { start: nodePosition.start, end: pointAfter(nodePosition.start, value.slice(0, consumedLength)) };
121976
+ };
121946
121977
  /**
121947
121978
  * Create an MdxJsxFlowElement node from component data.
121948
121979
  */
@@ -122002,10 +122033,16 @@ const mdxishMdxComponentBlocks = (opts = {}) => tree => {
122002
122033
  const value = node.value;
122003
122034
  if (node.type !== 'html' || typeof value !== 'string')
122004
122035
  return;
122005
- const parsed = parseTag(value.trim(), parseOpts);
122036
+ const trimmed = value.trim();
122037
+ const parsed = parseTag(trimmed, parseOpts);
122006
122038
  if (!parsed)
122007
122039
  return;
122008
122040
  const { tag, attributes, selfClosing, contentAfterTag = '' } = parsed;
122041
+ // Offset of `trimmed` within the (possibly whitespace-padded) html node value,
122042
+ // so consumed-length math maps back onto the node's real source offsets.
122043
+ const leadingWhitespace = value.length - value.trimStart().length;
122044
+ // Index right after the opening tag's `>` within `trimmed`.
122045
+ const openingTagEnd = trimmed.length - contentAfterTag.length;
122009
122046
  // Skip tags that have dedicated transformers
122010
122047
  if (GENERIC_MDX_COMPONENT_EXCLUDED_TAGS.has(tag))
122011
122048
  return;
@@ -122030,6 +122067,8 @@ const mdxishMdxComponentBlocks = (opts = {}) => tree => {
122030
122067
  attributes,
122031
122068
  children: [],
122032
122069
  startPosition: node.position,
122070
+ // End at the self-closing tag, not at any trailing content.
122071
+ endPosition: positionEndingAtConsumed(node.position, value, leadingWhitespace + openingTagEnd),
122033
122072
  });
122034
122073
  substituteNodeWithMdxNode(parent, index, componentNode);
122035
122074
  // Check and parse if there's relevant content after the current closing tag
@@ -122061,6 +122100,8 @@ const mdxishMdxComponentBlocks = (opts = {}) => tree => {
122061
122100
  attributes,
122062
122101
  children: parsedChildren,
122063
122102
  startPosition: node.position,
122103
+ // End at the closing tag, not at trailing content re-parsed as siblings below.
122104
+ endPosition: positionEndingAtConsumed(node.position, value, leadingWhitespace + openingTagEnd + closingTagIndex + closingTagStr.length),
122064
122105
  });
122065
122106
  substituteNodeWithMdxNode(parent, index, componentNode);
122066
122107
  // After the closing tag, there might be more content to be processed
@@ -124331,6 +124372,24 @@ function restoreHTMLElements(content, htmlElements) {
124331
124372
  return content;
124332
124373
  return content.replace(HTML_ELEM_PLACEHOLDER, (_m, idx) => htmlElements[parseInt(idx, 10)]);
124333
124374
  }
124375
+ const ESM_DECLARATION_START = /^(?:export|import)\b/;
124376
+ /**
124377
+ * Whether a line begins a top-level `export`/`import` declaration. Its braces
124378
+ * are valid JS owned by the mdxjsEsm tokenizer (which tolerates blank lines),
124379
+ * so escaping them would corrupt the source and break acorn parsing.
124380
+ */
124381
+ function startsEsmDeclaration(chars, lineStart) {
124382
+ return ESM_DECLARATION_START.test(chars.slice(lineStart, lineStart + 7).join(''));
124383
+ }
124384
+ function isBlankLine(chars, lineStart) {
124385
+ for (let i = lineStart; i < chars.length; i += 1) {
124386
+ if (chars[i] === '\n')
124387
+ return true;
124388
+ if (chars[i] !== ' ' && chars[i] !== '\t')
124389
+ return false;
124390
+ }
124391
+ return true;
124392
+ }
124334
124393
  /**
124335
124394
  * Escapes unbalanced and paragraph-spanning braces so MDX doesn't trip on them.
124336
124395
  */
@@ -124346,8 +124405,18 @@ function escapeProblematicBraces(content) {
124346
124405
  // Convert to array of Unicode code points so that emojis and multi-byte characters are correctly tracked
124347
124406
  const chars = Array.from(protectedContent);
124348
124407
  const openStack = [];
124408
+ // Whether the current top-level statement is an `export`/`import` declaration.
124409
+ let insideEsmDeclaration = false;
124349
124410
  for (let i = 0; i < chars.length; i += 1) {
124350
124411
  const ch = chars[i];
124412
+ // At a top-level (brace depth 0) line start, decide whether we're inside an
124413
+ // ESM declaration. The flag persists across the declaration's own lines.
124414
+ if (openStack.length === 0 && (i === 0 || chars[i - 1] === '\n')) {
124415
+ if (startsEsmDeclaration(chars, i))
124416
+ insideEsmDeclaration = true;
124417
+ else if (isBlankLine(chars, i))
124418
+ insideEsmDeclaration = false;
124419
+ }
124351
124420
  // Track string delimiters inside expressions to ignore braces within them
124352
124421
  if (openStack.length > 0) {
124353
124422
  if (strDelim) {
@@ -124405,19 +124474,22 @@ function escapeProblematicBraces(content) {
124405
124474
  if (!isAttrExpr && openStack.length > 0 && openStack[openStack.length - 1].isAttrExpr) {
124406
124475
  isAttrExpr = true;
124407
124476
  }
124408
- openStack.push({ pos: i, hasBlankLine: false, isAttrExpr });
124477
+ openStack.push({ pos: i, hasBlankLine: false, isAttrExpr, isEsmDeclaration: insideEsmDeclaration });
124409
124478
  lastNewlinePos = -2;
124410
124479
  }
124411
124480
  else if (ch === '}') {
124412
124481
  if (openStack.length > 0) {
124413
124482
  const entry = openStack.pop();
124483
+ // The declaration's braces are closed; later top-level braces are not ESM.
124484
+ if (openStack.length === 0)
124485
+ insideEsmDeclaration = false;
124414
124486
  // Pure `{/* ... */}` comments are handled downstream by the jsxComment
124415
124487
  // tokenizer — escaping their braces would prevent it from running.
124416
124488
  const isPureJsxComment = chars[entry.pos + 1] === '/' &&
124417
124489
  chars[entry.pos + 2] === '*' &&
124418
124490
  chars[i - 1] === '/' &&
124419
124491
  chars[i - 2] === '*';
124420
- if (entry.hasBlankLine && !isPureJsxComment && !entry.isAttrExpr) {
124492
+ if (entry.hasBlankLine && !isPureJsxComment && !entry.isAttrExpr && !entry.isEsmDeclaration) {
124421
124493
  toEscape.add(entry.pos);
124422
124494
  toEscape.add(i);
124423
124495
  }
@@ -124426,9 +124498,16 @@ function escapeProblematicBraces(content) {
124426
124498
  toEscape.add(i);
124427
124499
  }
124428
124500
  }
124501
+ else if (ch === ';' && openStack.length === 0) {
124502
+ // A top-level `;` ends the current statement, ESM or otherwise.
124503
+ insideEsmDeclaration = false;
124504
+ }
124429
124505
  }
124430
124506
  // Anything still open is unbalanced.
124431
- openStack.forEach(entry => toEscape.add(entry.pos));
124507
+ openStack.forEach(entry => {
124508
+ if (!entry.isEsmDeclaration)
124509
+ toEscape.add(entry.pos);
124510
+ });
124432
124511
  // Reconstruct the content with the escaped braces.
124433
124512
  const escapedContent = toEscape.size === 0 ? protectedContent : chars.map((ch, i) => (toEscape.has(i) ? `\\${ch}` : ch)).join('');
124434
124513
  return restoreHTMLElements(escapedContent, htmlElements);
@@ -125927,11 +126006,18 @@ function exportComponentsForRehype(components) {
125927
126006
  * semantics). That breaks any component prop that is genuinely an array. With
125928
126007
  * `passNode: true` we get the original hast node in `props.node` for components
125929
126008
  * and can read the raw properties directly.
126009
+ *
126010
+ * Only arrays win over `rest`, overwriting other shapes would undo correct
126011
+ * React conversions like `style="color:red"` → `{ color: 'red' }`.
125930
126012
  */
125931
126013
  function createElementPreservingHastProps(type, props, ...children) {
125932
126014
  if (props?.node?.properties) {
125933
126015
  const { node, ...rest } = props;
125934
- const mergedProps = { ...rest, ...node.properties };
126016
+ const mergedProps = { ...rest };
126017
+ Object.entries(node.properties).forEach(([key, value]) => {
126018
+ if (Array.isArray(value))
126019
+ mergedProps[key] = value;
126020
+ });
125935
126021
  // Strip undefined so positional args don't shadow node.properties.children
125936
126022
  const definedChildren = children.filter(c => c !== undefined);
125937
126023
  return external_react_default().createElement(type, mergedProps, ...definedChildren);