@readme/markdown 15.8.0 → 15.9.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.node.js CHANGED
@@ -19045,7 +19045,7 @@ module.exports = function () {
19045
19045
 
19046
19046
  /***/ },
19047
19047
 
19048
- /***/ 4246
19048
+ /***/ 6831
19049
19049
  (module, __webpack_exports__, __webpack_require__) {
19050
19050
 
19051
19051
  "use strict";
@@ -75148,18 +75148,53 @@ function evaluate(source, scope = {}) {
75148
75148
  return new Function(...names, `return (${source})`)(...values);
75149
75149
  }
75150
75150
  /**
75151
- * Advance a `Point` by the `consumed` substring that follows it, returning the
75152
- * point at the consumed string's end. Lets split-out sub-nodes carry accurate
75153
- * document positions so downstream offset shifting stays correct.
75151
+ * Width each container prefix adds back per line of `value`, aligned from the line end since a
75152
+ * tab in the prefix is expanded to spaces in `value`. None (a verbatim slice) when the source
75153
+ * is missing or doesn't line up.
75154
75154
  */
75155
- const pointAfter = (start, consumed) => {
75156
- const newlineIndex = consumed.lastIndexOf('\n');
75157
- const newlineCount = newlineIndex === -1 ? 0 : consumed.split('\n').length - 1;
75158
- return {
75159
- line: start.line + newlineCount,
75160
- // Same line → advance the base column; a later line → column is the run since its newline.
75161
- column: newlineCount === 0 ? start.column + consumed.length : consumed.length - newlineIndex,
75162
- offset: (start.offset ?? 0) + consumed.length,
75155
+ const containerPrefixWidths = (valueLines, source, position) => {
75156
+ const sourceLines = source?.slice(position.start.offset, position.end.offset).split('\n');
75157
+ const linesUp = sourceLines?.length === valueLines.length &&
75158
+ sourceLines.every((line, i) => line.endsWith(valueLines[i].trimStart()));
75159
+ return valueLines.map((line, i) => (linesUp ? sourceLines[i].length - line.length : 0));
75160
+ };
75161
+ /**
75162
+ * Maps offsets in a node's `value` to points in `source`. Containers strip a prefix from every
75163
+ * continuation line of `value`, so a plain offset shift drifts by line (CX-4028).
75164
+ */
75165
+ const createValueToSourceMapper = (position, value, source) => {
75166
+ if (position?.start.offset === undefined)
75167
+ return null;
75168
+ const { start } = position;
75169
+ const valueLines = value.split('\n');
75170
+ const prefixWidths = containerPrefixWidths(valueLines, source, position);
75171
+ const lines = [];
75172
+ let valueStart = 0;
75173
+ let sourceStart = start.offset;
75174
+ valueLines.forEach((line, i) => {
75175
+ lines.push({ prefixWidth: prefixWidths[i], sourceStart, valueStart });
75176
+ valueStart += line.length + 1;
75177
+ sourceStart += prefixWidths[i] + line.length + 1;
75178
+ });
75179
+ return valueOffset => {
75180
+ // Binary search: a large table maps one point per node.
75181
+ let low = 0;
75182
+ let high = lines.length - 1;
75183
+ while (low < high) {
75184
+ const mid = Math.ceil((low + high) / 2);
75185
+ if (lines[mid].valueStart <= valueOffset)
75186
+ low = mid;
75187
+ else
75188
+ high = mid - 1;
75189
+ }
75190
+ const { prefixWidth, sourceStart: lineSourceStart, valueStart: lineValueStart } = lines[low];
75191
+ // Clamped: an expanded tab can make a leading-whitespace offset fall before the line.
75192
+ const sourceColumn = Math.max(0, prefixWidth + valueOffset - lineValueStart);
75193
+ return {
75194
+ line: start.line + low,
75195
+ column: (low === 0 ? start.column : 1) + sourceColumn,
75196
+ offset: lineSourceStart + sourceColumn,
75197
+ };
75163
75198
  };
75164
75199
  };
75165
75200
  /**
@@ -75822,7 +75857,9 @@ const processBlockquote = (node, index, parent, isMdxish = false) => {
75822
75857
  }
75823
75858
  Object.assign(node, {
75824
75859
  type: NodeTypes.callout,
75860
+ // Keep the blockquote's data, e.g. the `reparseSource` its positions index into.
75825
75861
  data: {
75862
+ ...node.data,
75826
75863
  hName: 'Callout',
75827
75864
  hProperties: {
75828
75865
  icon,
@@ -75855,14 +75892,55 @@ const calloutTransformer = ({ isMdxish = false } = {}) => {
75855
75892
  };
75856
75893
  /* harmony default export */ const callouts = (calloutTransformer);
75857
75894
 
75895
+ ;// ./processor/transform/mdxish/reparse-source.ts
75896
+ /**
75897
+ * Mark re-parsed subtree roots with the string their positions index into, so consumers
75898
+ * slicing by offset use the right source (see `Data.reparseSource`). Only roots are
75899
+ * stamped; descendants resolve via their nearest stamped ancestor. Roots a deeper
75900
+ * re-parse already stamped keep their own string.
75901
+ */
75902
+ const stampReparseSource = (roots, reparseSource) => {
75903
+ roots.forEach(root => {
75904
+ if (!root.data?.reparseSource)
75905
+ root.data = { ...root.data, reparseSource };
75906
+ });
75907
+ };
75908
+ /**
75909
+ * Splice `replacements` over `parent.children[index]`. They take over the replaced node's
75910
+ * position, so they also take over the coordinate space that position indexes into.
75911
+ */
75912
+ const replaceInheritingReparseSource = (parent, index, replacements) => {
75913
+ const replacedSource = parent.children[index]?.data?.reparseSource;
75914
+ if (replacedSource)
75915
+ stampReparseSource(replacements, replacedSource);
75916
+ parent.children.splice(index, 1, ...replacements);
75917
+ };
75918
+ /**
75919
+ * The string `node`'s offsets index into: the nearest stamp on it or an ancestor, else the
75920
+ * document source.
75921
+ */
75922
+ const resolveReparseSource = (node, ancestors, documentSource) => {
75923
+ if (node.data?.reparseSource)
75924
+ return node.data.reparseSource;
75925
+ for (let i = ancestors.length - 1; i >= 0; i -= 1) {
75926
+ const { reparseSource } = ancestors[i].data ?? {};
75927
+ if (reparseSource)
75928
+ return reparseSource;
75929
+ }
75930
+ return documentSource;
75931
+ };
75932
+
75858
75933
  ;// ./processor/transform/code-tabs.ts
75859
75934
 
75860
75935
 
75936
+
75861
75937
  const isCode = (node) => node?.type === 'code';
75862
75938
  const codeTabsTransformer = ({ copyButtons } = {}) => (tree) => {
75863
75939
  visit(tree, 'code', (node) => {
75864
75940
  const { lang, meta, value } = node;
75865
75941
  node.data = {
75942
+ // Ensure positions are passed through
75943
+ ...node.data,
75866
75944
  hProperties: { lang, meta, value, copyButtons },
75867
75945
  };
75868
75946
  });
@@ -75909,6 +75987,9 @@ const codeTabsTransformer = ({ copyButtons } = {}) => (tree) => {
75909
75987
  end: children[children.length - 1].position.end,
75910
75988
  },
75911
75989
  };
75990
+ // Adjacent siblings share a coordinate space, so the wrapper takes the first block's.
75991
+ if (node.data?.reparseSource)
75992
+ stampReparseSource([codeTabs], node.data.reparseSource);
75912
75993
  parent.children.splice(index, children.length, codeTabs);
75913
75994
  });
75914
75995
  return tree;
@@ -100291,6 +100372,7 @@ const walkTags = (html, handlers) => {
100291
100372
  };
100292
100373
 
100293
100374
  ;// ./processor/transform/mdxish/tables/utils.ts
100375
+
100294
100376
  const tableTags = new Set([
100295
100377
  'thead',
100296
100378
  'tbody',
@@ -100309,6 +100391,9 @@ const tableTags = new Set([
100309
100391
  const unwrapParagraphNodes = (children) => {
100310
100392
  return children.flatMap(child => {
100311
100393
  if (child.type === 'paragraph' && 'children' in child && Array.isArray(child.children)) {
100394
+ // The unwrap drops a stamped root, so its children inherit that coordinate space.
100395
+ if (child.data?.reparseSource)
100396
+ stampReparseSource(child.children, child.data.reparseSource);
100312
100397
  return child.children;
100313
100398
  }
100314
100399
  return [child];
@@ -100904,9 +100989,10 @@ const findTableRanges = (html) => {
100904
100989
  * The surrounding raw HTML (the wrapper's open/close tags) would be re-nested
100905
100990
  * around the parsed tables by rehype-raw.
100906
100991
  *
100907
- * Returns null when there is no wrapped table to extract.
100992
+ * Returns null when there is no wrapped table to extract. `source` is the string the node's
100993
+ * positions index into, so the parts' positions survive stripped container prefixes.
100908
100994
  */
100909
- const splitHtmlWithNestedTables = (node) => {
100995
+ const splitHtmlWithNestedTables = (node, source) => {
100910
100996
  const { value } = node;
100911
100997
  // This is a top-level table, so we don't need to split it
100912
100998
  if (TOP_LEVEL_TABLE_TAG_RE.test(value))
@@ -100919,13 +101005,11 @@ const splitHtmlWithNestedTables = (node) => {
100919
101005
  const ranges = findTableRanges(value);
100920
101006
  if (ranges.length === 0)
100921
101007
  return null;
100922
- const base = node.position?.start;
101008
+ const toSourcePoint = createValueToSourceMapper(node.position, value, source);
100923
101009
  const sliceToHtml = (from, to) => ({
100924
101010
  type: 'html',
100925
101011
  value: value.slice(from, to),
100926
- ...(base && {
100927
- position: { start: pointAfter(base, value.slice(0, from)), end: pointAfter(base, value.slice(0, to)) },
100928
- }),
101012
+ ...(toSourcePoint && { position: { start: toSourcePoint(from), end: toSourcePoint(to) } }),
100929
101013
  });
100930
101014
  const parts = [];
100931
101015
  let cursor = 0;
@@ -100960,6 +101044,8 @@ const splitHtmlWithNestedTables = (node) => {
100960
101044
 
100961
101045
 
100962
101046
 
101047
+
101048
+
100963
101049
 
100964
101050
 
100965
101051
 
@@ -101012,7 +101098,7 @@ const tableRepairs = [
101012
101098
  * The plugins in the processor allows parsing markdown & special syntax inside the table cells
101013
101099
  * After parsing, we need to update the node positions
101014
101100
  */
101015
- const parseTableNode = (processor, node, repair) => {
101101
+ const parseTableNode = (processor, node, source, repair) => {
101016
101102
  let parsed;
101017
101103
  try {
101018
101104
  parsed = processor.runSync(processor.parse(node.value));
@@ -101026,19 +101112,18 @@ const parseTableNode = (processor, node, repair) => {
101026
101112
  if (repair) {
101027
101113
  remapPositionsThroughLayers(parsed, repair.originalSource, repair.layers);
101028
101114
  }
101029
- // The subparser produces positions relative to `node.value`; shift them by
101030
- // the outer node's offset/line so consumers can slice the full source.
101031
- const baseOffset = node.position?.start?.offset ?? 0;
101032
- const baseLine = (node.position?.start?.line ?? 1) - 1;
101115
+ // The subparser produces positions relative to `node.value`; map them into
101116
+ // the outer source so consumers can slice it.
101117
+ const toSourcePoint = createValueToSourceMapper(node.position, repair?.originalSource ?? node.value, source);
101118
+ if (!toSourcePoint)
101119
+ return parsed;
101033
101120
  visit(parsed, child => {
101034
- if (child.position?.start) {
101035
- child.position.start.offset = (child.position.start.offset ?? 0) + baseOffset;
101036
- child.position.start.line += baseLine;
101037
- }
101038
- if (child.position?.end) {
101039
- child.position.end.offset = (child.position.end.offset ?? 0) + baseOffset;
101040
- child.position.end.line += baseLine;
101041
- }
101121
+ if (!child.position)
101122
+ return;
101123
+ child.position = {
101124
+ start: toSourcePoint(child.position.start.offset ?? 0),
101125
+ end: toSourcePoint(child.position.end.offset ?? 0),
101126
+ };
101042
101127
  });
101043
101128
  return parsed;
101044
101129
  };
@@ -101068,12 +101153,10 @@ const hasFlowContent = (nodes) => {
101068
101153
  return nodes.some(node => !phrasing(node) && node.type !== 'paragraph');
101069
101154
  };
101070
101155
  /**
101071
- * Process a Table node: re-parse text-only cell content, then output as
101072
- * a markdown table (phrasing-only) or keep as JSX <Table> (has flow content).
101156
+ * Process a Table node: re-parse text-only cell content, then return it as
101157
+ * a markdown table (phrasing-only) or keep it as JSX <Table> (has flow content).
101073
101158
  */
101074
- const processTableNode = (node, index, parent, documentPosition) => {
101075
- if (node.name !== 'Table' && node.name !== 'table')
101076
- return;
101159
+ const processTableNode = (node, documentPosition) => {
101077
101160
  const position = documentPosition ?? node.position;
101078
101161
  const { align: alignAttr } = getAttrs(node);
101079
101162
  const align = Array.isArray(alignAttr) ? alignAttr : null;
@@ -101100,6 +101183,8 @@ const processTableNode = (node, index, parent, documentPosition) => {
101100
101183
  try {
101101
101184
  const parsed = tableNodeProcessor.runSync(tableNodeProcessor.parse(textContent));
101102
101185
  if (parsed.children.length > 0) {
101186
+ // Positions index into the extracted text rather than the table source (CX-4004).
101187
+ stampReparseSource(parsed.children, textContent);
101103
101188
  cell.children = parsed.children;
101104
101189
  if (hasFlowContent(parsed.children)) {
101105
101190
  tableHasFlowContent = true;
@@ -101142,11 +101227,7 @@ const processTableNode = (node, index, parent, documentPosition) => {
101142
101227
  : unwrapParagraphNodes(el.children);
101143
101228
  el.children = removeWhitespaceOnlyTextNodes(unwrapped);
101144
101229
  });
101145
- parent.children[index] = {
101146
- ...node,
101147
- position,
101148
- };
101149
- return;
101230
+ return { ...node, position };
101150
101231
  }
101151
101232
  // All cells are phrasing-only — convert to markdown table
101152
101233
  const children = [];
@@ -101224,7 +101305,7 @@ const processTableNode = (node, index, parent, documentPosition) => {
101224
101305
  const alignArray = align && columnCount > 0
101225
101306
  ? align.slice(0, columnCount).concat(new Array(Math.max(0, columnCount - align.length)).fill(null))
101226
101307
  : new Array(columnCount).fill(null);
101227
- const mdNode = {
101308
+ return {
101228
101309
  align: alignArray,
101229
101310
  type: 'table',
101230
101311
  position,
@@ -101232,14 +101313,13 @@ const processTableNode = (node, index, parent, documentPosition) => {
101232
101313
  // Remember the author's spelling so the serializer can write `<table>` back instead of `<Table>`
101233
101314
  ...(node.name === 'table' && { data: { lowercaseTable: true } }),
101234
101315
  };
101235
- parent.children[index] = mdNode;
101236
101316
  };
101237
101317
  /**
101238
101318
  * Apply `tableRepairs` cumulatively, re-parsing after every change and stopping
101239
101319
  * once the accumulated result parses. Each layer's inserts are relative to the
101240
101320
  * string that repair received, so they stay ordered for position remapping.
101241
101321
  */
101242
- const repairAndReparse = (node) => {
101322
+ const repairAndReparse = (node, source) => {
101243
101323
  let repairedValue = node.value;
101244
101324
  const layers = [];
101245
101325
  let parsed;
@@ -101249,11 +101329,45 @@ const repairAndReparse = (node) => {
101249
101329
  return false;
101250
101330
  repairedValue = value;
101251
101331
  layers.push(inserts);
101252
- parsed = parseTableNode(tableNodeProcessor, { ...node, value: repairedValue }, { layers, originalSource: node.value });
101332
+ parsed = parseTableNode(tableNodeProcessor, { ...node, value: repairedValue }, source, {
101333
+ layers,
101334
+ originalSource: node.value,
101335
+ });
101253
101336
  return Boolean(parsed);
101254
101337
  });
101255
101338
  return parsed;
101256
101339
  };
101340
+ /**
101341
+ * Re-parse a table html node and replace it with a markdown / JSX table, or the
101342
+ * fallback parse's fragments for a lowercase table mdxjs rejects.
101343
+ */
101344
+ const replaceTableHtml = (node, index, parent, source) => {
101345
+ // Because the processor uses remarkMdx, it is stricter in what it accepts
101346
+ // and only accepts valid MDX syntax in the table node. To get around that,
101347
+ // fall back to the cumulative repairs when the first parse fails.
101348
+ const parsed = parseTableNode(tableNodeProcessor, node, source) ?? repairAndReparse(node, source);
101349
+ if (parsed) {
101350
+ // If the table is parsed successfully, we can now process it further
101351
+ // to build on the markdown / JSX table
101352
+ visit(parsed, isMDXElement, (tableNode) => {
101353
+ if (tableNode.name !== 'Table' && tableNode.name !== 'table')
101354
+ return undefined;
101355
+ replaceInheritingReparseSource(parent, index, [processTableNode(tableNode, node.position)]);
101356
+ return (/* inlined export .EXIT */false);
101357
+ });
101358
+ }
101359
+ else if (node.value.startsWith('<table')) {
101360
+ // If the parsing still fails, give an opportunity to the fallback parser
101361
+ // without remarkMdx to process lowercase tables as it's likely to not
101362
+ // have needed MDX parsing anyway
101363
+ const fallback = parseTableNode(fallbackTableNodeProcessor, node, source);
101364
+ if (!fallback || fallback.children.length <= 1)
101365
+ return;
101366
+ replaceInheritingReparseSource(parent, index, fallback.children);
101367
+ }
101368
+ // Otherwise, there's no point in trying to parse the table content further
101369
+ // More repairs are needed in that case
101370
+ };
101257
101371
  /**
101258
101372
  * Converts JSX Table elements to markdown table nodes and re-parses markdown in cells.
101259
101373
  *
@@ -101265,24 +101379,26 @@ const repairAndReparse = (node) => {
101265
101379
  * When cell content contains block-level nodes (callouts, code blocks, etc.), the table
101266
101380
  * is kept as a JSX <Table> element so that remarkRehype can properly handle the flow content.
101267
101381
  */
101268
- const mdxishTables = () => tree => {
101382
+ const mdxishTables = () => (tree, file) => {
101383
+ // Positions index into the document unless a re-parse stamped another source.
101384
+ const documentSource = file?.value ? String(file.value) : undefined;
101269
101385
  // Pre-pass: lift `<table>`s wrapped in a raw HTML block out into their own
101270
101386
  // html nodes so the main pass below treats them like top-level tables.
101271
- visit(tree, 'html', (_node, index, parent) => {
101272
- const node = _node;
101273
- if (typeof index !== 'number' || !parent || !('children' in parent))
101387
+ visitParents(tree, 'html', (node, ancestors) => {
101388
+ const parent = ancestors[ancestors.length - 1];
101389
+ if (!parent)
101274
101390
  return;
101275
- const parts = splitHtmlWithNestedTables(node);
101391
+ const parts = splitHtmlWithNestedTables(node, resolveReparseSource(node, ancestors, documentSource));
101276
101392
  if (!parts)
101277
101393
  return;
101278
101394
  // The inserted parts can't re-trigger a split (table parts start with
101279
101395
  // `<table`; the wrapper slices hold no table), so plain in-place splicing
101280
101396
  // visits each once without looping.
101281
- parent.children.splice(index, 1, ...parts);
101397
+ replaceInheritingReparseSource(parent, parent.children.indexOf(node), parts);
101282
101398
  });
101283
- visit(tree, 'html', (_node, index, parent) => {
101284
- const node = _node;
101285
- if (typeof index !== 'number' || !parent || !('children' in parent))
101399
+ visitParents(tree, 'html', (node, ancestors) => {
101400
+ const parent = ancestors[ancestors.length - 1];
101401
+ if (!parent)
101286
101402
  return;
101287
101403
  if (!node.value.startsWith('<Table') && !node.value.startsWith('<table'))
101288
101404
  return;
@@ -101290,33 +101406,8 @@ const mdxishTables = () => tree => {
101290
101406
  // paragraph. Leave them untouched so rehype-raw can reassemble the table.
101291
101407
  if (parent.type === 'paragraph')
101292
101408
  return;
101293
- // Because the processor uses remarkMdx, it is stricter in what it accepts
101294
- // and only accepts valid MDX syntax in the table node. To get around that,
101295
- // fall back to the cumulative repairs when the first parse fails.
101296
- const parsed = parseTableNode(tableNodeProcessor, node) ?? repairAndReparse(node);
101297
- if (parsed) {
101298
- // If the table is parsed successfully, we can now process it further
101299
- // to build on the markdown / JSX table
101300
- visit(parsed, isMDXElement, (tableNode) => {
101301
- if (tableNode.name !== 'Table' && tableNode.name !== 'table')
101302
- return undefined;
101303
- processTableNode(tableNode, index, parent, node.position);
101304
- return (/* inlined export .EXIT */false);
101305
- });
101306
- }
101307
- else if (node.value.startsWith('<table')) {
101308
- // If the parsing still fails, give an opportunity to the fallback parser
101309
- // without remarkMdx to process lowercase tables as it's likely to not
101310
- // have needed MDX parsing anyway
101311
- const fallback = parseTableNode(fallbackTableNodeProcessor, node);
101312
- if (!fallback || fallback.children.length <= 1)
101313
- return;
101314
- parent.children.splice(index, 1, ...fallback.children);
101315
- }
101316
- // Otherwise, there's no point in trying to parse the table content further
101317
- // More repairs are needed in that case
101409
+ replaceTableHtml(node, parent.children.indexOf(node), parent, resolveReparseSource(node, ancestors, documentSource));
101318
101410
  });
101319
- return tree;
101320
101411
  };
101321
101412
  /* harmony default export */ const mdxish_tables = (mdxishTables);
101322
101413
 
@@ -124124,9 +124215,9 @@ const mdxishCalloutToJsx = () => tree => {
124124
124215
  visit(tree, NodeTypes.callout, (node, index, parent) => {
124125
124216
  if (!parent || index === undefined)
124126
124217
  return;
124127
- let { icon, theme } = node.data.hProperties;
124128
- if (!icon && theme)
124129
- icon = defaultIcons[theme];
124218
+ // No default-icon fill: an empty `icon` means the author removed it
124219
+ const { icon } = node.data.hProperties;
124220
+ let { theme } = node.data.hProperties;
124130
124221
  if (!theme && icon)
124131
124222
  theme = themes[icon] || 'default';
124132
124223
  // Mutate in place to keep the children array identity so nested callouts in
@@ -124677,18 +124768,6 @@ const getInlineMdProcessor = ({ safeMode = false } = {}) => {
124677
124768
  }
124678
124769
  return processor;
124679
124770
  };
124680
- /**
124681
- * Mark re-parsed subtree roots with the string their positions index into, so consumers
124682
- * slicing by offset use the right source (see `Data.reparseSource`). Only roots are
124683
- * stamped; descendants resolve via their nearest stamped ancestor. Roots a deeper
124684
- * re-parse already stamped keep their own string.
124685
- */
124686
- const stampReparseSource = (roots, reparseSource) => {
124687
- roots.forEach(root => {
124688
- if (!root.data?.reparseSource)
124689
- root.data = { ...root.data, reparseSource };
124690
- });
124691
- };
124692
124771
  /**
124693
124772
  * True when a tag name starts with an uppercase letter — ReadMe's marker for
124694
124773
  * a custom MDX component (vs a lowercase HTML tag).
@@ -124775,6 +124854,7 @@ function findBalancedClosingTagIndex(content, tag) {
124775
124854
 
124776
124855
 
124777
124856
 
124857
+
124778
124858
  /**
124779
124859
  * Parse the body of an inline component as phrasing content. Remark always
124780
124860
  * wraps bare text in a paragraph; flatten that single-paragraph wrapper so
@@ -125026,6 +125106,7 @@ function terminateHtmlFlowBlocks(content) {
125026
125106
 
125027
125107
 
125028
125108
 
125109
+
125029
125110
  // Matches a JSX attribute expression (e.g. `key={i}`) anywhere in a string. One name
125030
125111
  // char suffices for existence — `[\w-]+` backtracked quadratically over huge attributes.
125031
125112
  const NESTED_ATTR_EXPRESSION_RE = /[\w-]\s*=\s*\{/;
@@ -125107,26 +125188,12 @@ const parseSibling = (parent, index, sibling, safeMode, promoted) => {
125107
125188
  }
125108
125189
  };
125109
125190
  // Ends the position at `consumedLength` so the component doesn't claim trailing
125110
- // content the tokenizer swallowed into the same html node. Measured against the source
125111
- // when available: each value line is its source line minus the container prefix
125112
- // (list indent, `> `) micromark stripped, so those prefixes are counted back in.
125191
+ // content the tokenizer swallowed into the same html node.
125113
125192
  const positionEndingAtConsumed = (nodePosition, value, consumedLength, source) => {
125114
- if (!nodePosition?.start)
125193
+ const toSourcePoint = createValueToSourceMapper(nodePosition, value, source);
125194
+ if (!nodePosition || !toSourcePoint)
125115
125195
  return nodePosition;
125116
- const consumedValue = value.slice(0, consumedLength);
125117
- const sourceLines = source?.slice(nodePosition.start.offset, nodePosition.end?.offset).split('\n');
125118
- const valueLines = value.split('\n');
125119
- if (!sourceLines || sourceLines.length !== valueLines.length) {
125120
- return { start: nodePosition.start, end: pointAfter(nodePosition.start, consumedValue) };
125121
- }
125122
- const consumedLines = consumedValue.split('\n');
125123
- const lastLineIndex = consumedLines.length - 1;
125124
- const prefixWidth = sourceLines[lastLineIndex].length - valueLines[lastLineIndex].length;
125125
- const consumedSource = [
125126
- ...sourceLines.slice(0, lastLineIndex),
125127
- sourceLines[lastLineIndex].slice(0, prefixWidth + consumedLines[lastLineIndex].length),
125128
- ].join('\n');
125129
- return { start: nodePosition.start, end: pointAfter(nodePosition.start, consumedSource) };
125196
+ return { start: nodePosition.start, end: toSourcePoint(consumedLength) };
125130
125197
  };
125131
125198
  const createComponentNode = ({ tag, attributes, children, startPosition, endPosition, }) => ({
125132
125199
  type: 'mdxJsxFlowElement',
@@ -125138,14 +125205,6 @@ const createComponentNode = ({ tag, attributes, children, startPosition, endPosi
125138
125205
  end: endPosition?.end ?? startPosition?.end,
125139
125206
  },
125140
125207
  });
125141
- // The promoted node takes over the html node's position, so it also takes over the
125142
- // coordinate space that position belongs to.
125143
- const substituteNodeWithMdxNode = (parent, index, mdxNode) => {
125144
- const replacedSource = parent.children[index]?.data?.reparseSource;
125145
- if (replacedSource)
125146
- stampReparseSource([mdxNode], replacedSource);
125147
- parent.children.splice(index, 1, mdxNode);
125148
- };
125149
125208
  /**
125150
125209
  * Transform PascalCase HTML nodes into mdxJsxFlowElement nodes.
125151
125210
  *
@@ -125244,7 +125303,7 @@ function promoteComponentBlocks(tree, safeMode, source) {
125244
125303
  ? positionEndingAtConsumed(node.position, value, leadingWhitespace + openingTagEnd, source)
125245
125304
  : node.position,
125246
125305
  });
125247
- substituteNodeWithMdxNode(parent, index, componentNode);
125306
+ replaceInheritingReparseSource(parent, index, [componentNode]);
125248
125307
  if (remainingContent) {
125249
125308
  parseSibling(parent, index, remainingContent, safeMode, promoted);
125250
125309
  }
@@ -125295,7 +125354,7 @@ function promoteComponentBlocks(tree, safeMode, source) {
125295
125354
  startPosition: node.position,
125296
125355
  endPosition,
125297
125356
  });
125298
- substituteNodeWithMdxNode(parent, index, componentNode);
125357
+ replaceInheritingReparseSource(parent, index, [componentNode]);
125299
125358
  // The unwrap reparented the children out of their paragraph, so re-walk them
125300
125359
  // since the children HTML may contain promotable syntax (e.g. `{…}`-attr tags)
125301
125360
  if (unwrappedSoleParagraph) {
@@ -126737,6 +126796,7 @@ const magicBlockTransformer = (options = {}) => tree => {
126737
126796
 
126738
126797
 
126739
126798
 
126799
+
126740
126800
  // `<HTMLBlock …>{`…`}</HTMLBlock>` embedded inside a raw HTML block (e.g. a
126741
126801
  // single-line `<div>…</div>`). CommonMark slurps the whole div as one `html`
126742
126802
  // node, so the tokenizer never sees the HTMLBlock — we recover it here.
@@ -126843,7 +126903,8 @@ const mdxishHtmlBlocks = () => tree => {
126843
126903
  return;
126844
126904
  const exprChild = element.children.find(child => child.type === 'mdxFlowExpression' || child.type === 'mdxTextExpression');
126845
126905
  const openingTagIndent = (element.position?.start.column ?? 1) - 1;
126846
- parent.children[index] = createHtmlBlockNode(formatHtmlForMdxish(extractTemplateLiteral(exprChild?.value), openingTagIndent), element.position, toRunScripts(jsxAttr(element, 'runScripts')), jsxAttr(element, 'safeMode'));
126906
+ const htmlBlock = createHtmlBlockNode(formatHtmlForMdxish(extractTemplateLiteral(exprChild?.value), openingTagIndent), element.position, toRunScripts(jsxAttr(element, 'runScripts')), jsxAttr(element, 'safeMode'));
126907
+ replaceInheritingReparseSource(parent, index, [htmlBlock]);
126847
126908
  });
126848
126909
  // Shape 2: raw HTML blob.
126849
126910
  visit(tree, 'html', (node, index, parent) => {
@@ -126851,11 +126912,85 @@ const mdxishHtmlBlocks = () => tree => {
126851
126912
  return;
126852
126913
  const replacement = splitRawHtmlBlocks(node);
126853
126914
  if (replacement)
126854
- parent.children.splice(index, 1, ...replacement);
126915
+ replaceInheritingReparseSource(parent, index, replacement);
126855
126916
  });
126856
126917
  };
126857
126918
  /* harmony default export */ const mdxish_html_blocks = (mdxishHtmlBlocks);
126858
126919
 
126920
+ ;// ./processor/transform/mdxish/tables/cell-width.ts
126921
+
126922
+
126923
+ // Key must follow `{` or `,` so `minWidth`/`maxWidth` don't match on their suffix
126924
+ const STYLE_OBJECT_WIDTH_REGEX = /[{,]\s*["']?width["']?\s*:\s*(?:"([^"]*)"|'([^']*)'|([^,}]+))/i;
126925
+ // First colon only, so `url(http://…)` values stay intact
126926
+ const CSS_DECLARATION_REGEX = /:(.*)/s;
126927
+ const UNITLESS_NUMBER_REGEX = /^\d+(?:\.\d+)?$/;
126928
+ // HTML reads a `width` attribute as a leading number plus optional `%`, ignoring the rest
126929
+ const HTML_DIMENSION_REGEX = /^\s*(\d+(?:\.\d+)?)\s*(%?)/;
126930
+ /** Bare numbers are pixels. A value carrying declaration separators is not a single width. */
126931
+ const normalizeWidth = (value) => {
126932
+ if (typeof value === 'number')
126933
+ return `${value}px`;
126934
+ if (typeof value !== 'string' || !value.trim())
126935
+ return null;
126936
+ const trimmed = value.trim();
126937
+ if (/[;:{}]/.test(trimmed))
126938
+ return null;
126939
+ return UNITLESS_NUMBER_REGEX.test(trimmed) ? `${trimmed}px` : trimmed;
126940
+ };
126941
+ const widthFromAttribute = (value) => {
126942
+ if (typeof value === 'number')
126943
+ return `${value}px`;
126944
+ if (typeof value !== 'string')
126945
+ return null;
126946
+ const match = HTML_DIMENSION_REGEX.exec(value);
126947
+ return match ? `${match[1]}${match[2] || 'px'}` : null;
126948
+ };
126949
+ const cssPropertyNames = (style) => style
126950
+ .split(';')
126951
+ .map(declaration => declaration.split(CSS_DECLARATION_REGEX)[0].trim().toLowerCase())
126952
+ .filter(Boolean);
126953
+ /** Handles a CSS declaration list and a style object safe mode left as a string (`{ width: "30%" }`). */
126954
+ const widthFromStyleString = (style) => {
126955
+ const trimmed = style.trim();
126956
+ if (trimmed.startsWith('{')) {
126957
+ return STYLE_OBJECT_WIDTH_REGEX.exec(trimmed)?.slice(1).find(Boolean);
126958
+ }
126959
+ return trimmed
126960
+ .split(';')
126961
+ .map(declaration => declaration.split(CSS_DECLARATION_REGEX))
126962
+ .find(([property]) => property.trim().toLowerCase() === 'width')?.[1];
126963
+ };
126964
+ /** Accepts `style={{ width }}`, `style="width: …"` and the hand-authored `width="…"`. */
126965
+ const getCellWidth = (cell) => {
126966
+ const { style, width } = getAttrs(cell);
126967
+ let styleWidth;
126968
+ if (style_object_to_css_isPlainObject(style)) {
126969
+ styleWidth = style.width;
126970
+ }
126971
+ else if (typeof style === 'string') {
126972
+ styleWidth = widthFromStyleString(style);
126973
+ }
126974
+ return normalizeWidth(styleWidth) ?? widthFromAttribute(width);
126975
+ };
126976
+ /** True when the cell's attributes state nothing but its width, which mdast can carry. */
126977
+ const hasOnlyWidthAttributes = (cell) => {
126978
+ if (cell.attributes.length === 0)
126979
+ return false;
126980
+ const { style } = getAttrs(cell);
126981
+ return cell.attributes.every(attribute => {
126982
+ if (!('name' in attribute))
126983
+ return false;
126984
+ if (attribute.name === 'width')
126985
+ return true;
126986
+ if (attribute.name !== 'style')
126987
+ return false;
126988
+ if (style_object_to_css_isPlainObject(style))
126989
+ return Object.keys(style).every(key => key === 'width');
126990
+ return typeof style === 'string' && cssPropertyNames(style).every(property => property === 'width');
126991
+ });
126992
+ };
126993
+
126859
126994
  ;// ./processor/transform/mdxish/mdxish-jsx-to-mdast.ts
126860
126995
 
126861
126996
 
@@ -126863,6 +126998,8 @@ const mdxishHtmlBlocks = () => tree => {
126863
126998
 
126864
126999
 
126865
127000
 
127001
+
127002
+
126866
127003
  function toImageAlign(value) {
126867
127004
  if (value === 'left' || value === 'center' || value === 'right') {
126868
127005
  return value;
@@ -127078,6 +127215,15 @@ const transformAnchor = (jsx) => {
127078
127215
  position: jsx.position,
127079
127216
  };
127080
127217
  };
127218
+ /**
127219
+ * Parses an image caption's markdown. The caption is re-parsed on its own, so the
127220
+ * children's positions index into it rather than the document.
127221
+ */
127222
+ const parseCaptionChildren = (caption) => {
127223
+ const { children } = lib_mdast(caption);
127224
+ stampReparseSource(children, caption);
127225
+ return children;
127226
+ };
127081
127227
  /**
127082
127228
  * Transforms an `<Image />` JSX element into an image-block MDAST node.
127083
127229
  * Normalizes attributes (align, border, width→sizing) and parses caption markdown into children.
@@ -127108,7 +127254,7 @@ const transformImage = (jsx) => {
127108
127254
  alt,
127109
127255
  border: toBool(border),
127110
127256
  caption,
127111
- children: caption ? lib_mdast(caption).children : [],
127257
+ children: caption ? parseCaptionChildren(caption) : [],
127112
127258
  className,
127113
127259
  framed: toBool(framed),
127114
127260
  height: height !== undefined ? String(height) : undefined,
@@ -127304,10 +127450,40 @@ const wrapBareCellsInRow = (node) => {
127304
127450
  });
127305
127451
  };
127306
127452
  /**
127307
- * Converts a JSX <Table> element to an MDAST table node with alignment.
127308
- * Returns null for header-less tables since MDAST always promotes the first row to <thead>.
127453
+ * A lowercase <table> reaches here only because `mdxishTables` kept it as JSX for its
127454
+ * attributes. Widths on first-row cells are the one attribute mdast can carry; anything
127455
+ * else stays JSX so it still renders.
127456
+ */
127457
+ const lowercaseTableIsConvertible = (jsx) => {
127458
+ if (jsx.attributes.length > 0)
127459
+ return false;
127460
+ const headerCells = new Set();
127461
+ let headerRowSeen = false;
127462
+ let convertible = true;
127463
+ visit(jsx, isMDXElement, (child) => {
127464
+ if (child.name === 'tr' && !headerRowSeen) {
127465
+ headerRowSeen = true;
127466
+ visit(child, mdxish_jsx_to_mdast_isTableCell, cell => {
127467
+ headerCells.add(cell);
127468
+ });
127469
+ }
127470
+ if (child.name === null || !tableTags.has(child.name) || child.attributes.length === 0)
127471
+ return;
127472
+ if (!headerCells.has(child) || !hasOnlyWidthAttributes(child) || getCellWidth(child) === null) {
127473
+ convertible = false;
127474
+ }
127475
+ });
127476
+ return convertible;
127477
+ };
127478
+ /**
127479
+ * Converts a JSX <Table>, or a lowercase <table> carrying only widths, to an MDAST table
127480
+ * node with alignment and widths. Returns null for header-less tables since MDAST always
127481
+ * promotes the first row to <thead>.
127309
127482
  */
127310
127483
  const transformTable = (jsx) => {
127484
+ const isLowercase = jsx.name === 'table';
127485
+ if (isLowercase && !lowercaseTableIsConvertible(jsx))
127486
+ return null;
127311
127487
  wrapBareCellsInRow(jsx);
127312
127488
  let hasThead = false;
127313
127489
  visit(jsx, isMDXElement, (child) => {
@@ -127319,6 +127495,7 @@ const transformTable = (jsx) => {
127319
127495
  const { align: alignAttr } = getAttrs(jsx);
127320
127496
  const align = Array.isArray(alignAttr) ? alignAttr : null;
127321
127497
  const rows = [];
127498
+ const widths = [];
127322
127499
  visit(jsx, isMDXElement, (child) => {
127323
127500
  if (child.name !== 'thead' && child.name !== 'tbody')
127324
127501
  return;
@@ -127326,6 +127503,7 @@ const transformTable = (jsx) => {
127326
127503
  if (row.name !== 'tr')
127327
127504
  return;
127328
127505
  const cells = [];
127506
+ const isHeaderRow = rows.length === 0;
127329
127507
  visit(row, mdxish_jsx_to_mdast_isTableCell, (cell) => {
127330
127508
  const parsedChildren = unwrapSoleParagraph(cell.children);
127331
127509
  cells.push({
@@ -127333,6 +127511,8 @@ const transformTable = (jsx) => {
127333
127511
  children: parsedChildren,
127334
127512
  position: cell.position,
127335
127513
  });
127514
+ if (isHeaderRow)
127515
+ widths.push(getCellWidth(cell));
127336
127516
  });
127337
127517
  rows.push({
127338
127518
  type: 'tableRow',
@@ -127345,11 +127525,16 @@ const transformTable = (jsx) => {
127345
127525
  const alignArray = align && columnCount > 0
127346
127526
  ? align.slice(0, columnCount).concat(Array.from({ length: Math.max(0, columnCount - align.length) }, () => null))
127347
127527
  : Array.from({ length: columnCount }, () => null);
127528
+ const data = {
127529
+ ...(isLowercase && { lowercaseTable: true }),
127530
+ ...(widths.some(Boolean) && { widths }),
127531
+ };
127348
127532
  return {
127349
127533
  type: 'table',
127350
127534
  align: alignArray,
127351
127535
  position: jsx.position,
127352
127536
  children: rows,
127537
+ ...(Object.keys(data).length > 0 && { data }),
127353
127538
  };
127354
127539
  };
127355
127540
  /**
@@ -127379,6 +127564,7 @@ const COMPONENT_MAP = {
127379
127564
  figure: transformFigure,
127380
127565
  Recipe: transformRecipe,
127381
127566
  Table: transformTable,
127567
+ table: transformTable,
127382
127568
  };
127383
127569
  /**
127384
127570
  * Transform mdxJsxFlowElement nodes, magic block nodes, and HTML figure elements
@@ -127405,8 +127591,7 @@ const mdxishJsxToMdast = () => tree => {
127405
127591
  const newNode = transformer(node);
127406
127592
  if (!newNode)
127407
127593
  return;
127408
- // Replace the JSX node with the MDAST node
127409
- parent.children[index] = newNode;
127594
+ replaceInheritingReparseSource(parent, index, [newNode]);
127410
127595
  });
127411
127596
  // Inline JSX components (Anchor)
127412
127597
  visit(tree, 'mdxJsxTextElement', (node, index, parent) => {
@@ -127854,6 +128039,28 @@ const restoreBooleanProperties = () => tree => {
127854
128039
  };
127855
128040
 
127856
128041
 
128042
+ ;// ./processor/transform/mdxish/tables/cell-style.ts
128043
+ /** `left` is the browser default, so it's omitted. Lowercase tables get the HTML string form. */
128044
+ const cellStyle = (align, width = null, { html = false } = {}) => {
128045
+ if (html) {
128046
+ if (!width)
128047
+ return null;
128048
+ return { type: 'mdxJsxAttribute', name: 'style', value: `width: ${width}` };
128049
+ }
128050
+ const declarations = [];
128051
+ if (align && align !== 'left')
128052
+ declarations.push(`textAlign: ${JSON.stringify(align)}`);
128053
+ if (width)
128054
+ declarations.push(`width: ${JSON.stringify(width)}`);
128055
+ if (declarations.length === 0)
128056
+ return null;
128057
+ return {
128058
+ type: 'mdxJsxAttribute',
128059
+ name: 'style',
128060
+ value: { type: 'mdxJsxAttributeValueExpression', value: `{ ${declarations.join(', ')} }` },
128061
+ };
128062
+ };
128063
+
127857
128064
  ;// ./processor/transform/mdxish/tables/gfm-cell-normalization.ts
127858
128065
  /** `<br />` is the only line break a GFM pipe cell can carry, since cells are single-line. */
127859
128066
  const createLineBreak = () => ({
@@ -127892,19 +128099,8 @@ const normalizeCellChildrenForGfm = (cell) => normalizeNodes(cell.children);
127892
128099
 
127893
128100
 
127894
128101
 
128102
+
127895
128103
  const SELF_CLOSING_JSX_REGEX = /^\s*<[A-Z][^>]*\/>\s*$/;
127896
- const mdxish_tables_to_jsx_alignToStyle = (align) => {
127897
- if (!align || align === 'left')
127898
- return null;
127899
- return {
127900
- type: 'mdxJsxAttribute',
127901
- name: 'style',
127902
- value: {
127903
- type: 'mdxJsxAttributeValueExpression',
127904
- value: `{ textAlign: "${align}" }`,
127905
- },
127906
- };
127907
- };
127908
128104
  const mdxish_tables_to_jsx_isTableCell = (node) => ['tableHead', 'tableCell'].includes(node.type);
127909
128105
  const mdxish_tables_to_jsx_isLiteral = (node) => 'value' in node;
127910
128106
  /**
@@ -127960,12 +128156,17 @@ const mdxishTablesToJsx = () => tree => {
127960
128156
  gfmCells.push([cell, normalized]);
127961
128157
  return undefined;
127962
128158
  });
127963
- const stayLowercase = table.data?.lowercaseTable === true;
128159
+ const widths = table.data?.widths ?? [];
128160
+ const hasWidths = widths.some(Boolean);
128161
+ // Lowercase cells can carry a width but not an alignment, so an aligned table becomes <Table>
128162
+ const hasAlignment = table.align.some(align => align && align !== 'left');
128163
+ const stayLowercase = table.data?.lowercaseTable === true && !hasAlignment;
127964
128164
  // We transform to GFM tables if:
127965
128165
  // 1. If it doesn't contain complex elements that the syntax can't represent
127966
- // 2. The original table is not lowercase HTML <table>, because we want to
128166
+ // 2. The original table is not lowercase HTML <table>, because we want to
127967
128167
  // preserve original tables as much as possible
127968
- if (!requiresJsxTable && !stayLowercase) {
128168
+ // 3. No column has a width, which GFM can't express
128169
+ if (!requiresJsxTable && !stayLowercase && !hasWidths) {
127969
128170
  gfmCells.forEach(([cell, children]) => {
127970
128171
  cell.children = children;
127971
128172
  });
@@ -127977,7 +128178,8 @@ const mdxishTablesToJsx = () => tree => {
127977
128178
  if (!headerRow)
127978
128179
  return;
127979
128180
  visit(table, mdxish_tables_to_jsx_isTableCell, flattenBreaksToNewlines);
127980
- const styles = table.align.map(mdxish_tables_to_jsx_alignToStyle);
128181
+ const headerStyles = table.align.map((align, i) => cellStyle(align, widths[i] ?? null, { html: stayLowercase }));
128182
+ const bodyStyles = table.align.map(align => cellStyle(align, null, { html: stayLowercase }));
127981
128183
  const head = {
127982
128184
  attributes: [],
127983
128185
  type: 'mdxJsxFlowElement',
@@ -127993,7 +128195,7 @@ const mdxishTablesToJsx = () => tree => {
127993
128195
  type: 'mdxJsxFlowElement',
127994
128196
  name: 'th',
127995
128197
  children: cell.children,
127996
- ...(styles[cellIndex] && { attributes: [styles[cellIndex]] }),
128198
+ ...(headerStyles[cellIndex] && { attributes: [headerStyles[cellIndex]] }),
127997
128199
  };
127998
128200
  }),
127999
128201
  },
@@ -128013,7 +128215,7 @@ const mdxishTablesToJsx = () => tree => {
128013
128215
  type: 'mdxJsxFlowElement',
128014
128216
  name: 'td',
128015
128217
  children: cell.children,
128016
- ...(styles[cellIndex] && { attributes: [styles[cellIndex]] }),
128218
+ ...(bodyStyles[cellIndex] && { attributes: [bodyStyles[cellIndex]] }),
128017
128219
  };
128018
128220
  }),
128019
128221
  };
@@ -129485,7 +129687,7 @@ module.exports = /*#__PURE__*/JSON.parse('{"*":["about","acceptCharset","accessK
129485
129687
  /******/ // startup
129486
129688
  /******/ // Load entry module and return exports
129487
129689
  /******/ // This entry module used 'module' so it can't be inlined
129488
- /******/ let __webpack_exports__ = __webpack_require__(4246);
129690
+ /******/ let __webpack_exports__ = __webpack_require__(6831);
129489
129691
  /******/ module.exports = __webpack_exports__;
129490
129692
  /******/
129491
129693
  /******/ })()