@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.js CHANGED
@@ -11423,7 +11423,7 @@ module.exports = function () {
11423
11423
 
11424
11424
  /***/ },
11425
11425
 
11426
- /***/ 9607
11426
+ /***/ 8846
11427
11427
  (module, __webpack_exports__, __webpack_require__) {
11428
11428
 
11429
11429
  "use strict";
@@ -54965,18 +54965,53 @@ function evaluate(source, scope = {}) {
54965
54965
  return new Function(...names, `return (${source})`)(...values);
54966
54966
  }
54967
54967
  /**
54968
- * Advance a `Point` by the `consumed` substring that follows it, returning the
54969
- * point at the consumed string's end. Lets split-out sub-nodes carry accurate
54970
- * document positions so downstream offset shifting stays correct.
54968
+ * Width each container prefix adds back per line of `value`, aligned from the line end since a
54969
+ * tab in the prefix is expanded to spaces in `value`. None (a verbatim slice) when the source
54970
+ * is missing or doesn't line up.
54971
54971
  */
54972
- const pointAfter = (start, consumed) => {
54973
- const newlineIndex = consumed.lastIndexOf('\n');
54974
- const newlineCount = newlineIndex === -1 ? 0 : consumed.split('\n').length - 1;
54975
- return {
54976
- line: start.line + newlineCount,
54977
- // Same line → advance the base column; a later line → column is the run since its newline.
54978
- column: newlineCount === 0 ? start.column + consumed.length : consumed.length - newlineIndex,
54979
- offset: (start.offset ?? 0) + consumed.length,
54972
+ const containerPrefixWidths = (valueLines, source, position) => {
54973
+ const sourceLines = source?.slice(position.start.offset, position.end.offset).split('\n');
54974
+ const linesUp = sourceLines?.length === valueLines.length &&
54975
+ sourceLines.every((line, i) => line.endsWith(valueLines[i].trimStart()));
54976
+ return valueLines.map((line, i) => (linesUp ? sourceLines[i].length - line.length : 0));
54977
+ };
54978
+ /**
54979
+ * Maps offsets in a node's `value` to points in `source`. Containers strip a prefix from every
54980
+ * continuation line of `value`, so a plain offset shift drifts by line (CX-4028).
54981
+ */
54982
+ const createValueToSourceMapper = (position, value, source) => {
54983
+ if (position?.start.offset === undefined)
54984
+ return null;
54985
+ const { start } = position;
54986
+ const valueLines = value.split('\n');
54987
+ const prefixWidths = containerPrefixWidths(valueLines, source, position);
54988
+ const lines = [];
54989
+ let valueStart = 0;
54990
+ let sourceStart = start.offset;
54991
+ valueLines.forEach((line, i) => {
54992
+ lines.push({ prefixWidth: prefixWidths[i], sourceStart, valueStart });
54993
+ valueStart += line.length + 1;
54994
+ sourceStart += prefixWidths[i] + line.length + 1;
54995
+ });
54996
+ return valueOffset => {
54997
+ // Binary search: a large table maps one point per node.
54998
+ let low = 0;
54999
+ let high = lines.length - 1;
55000
+ while (low < high) {
55001
+ const mid = Math.ceil((low + high) / 2);
55002
+ if (lines[mid].valueStart <= valueOffset)
55003
+ low = mid;
55004
+ else
55005
+ high = mid - 1;
55006
+ }
55007
+ const { prefixWidth, sourceStart: lineSourceStart, valueStart: lineValueStart } = lines[low];
55008
+ // Clamped: an expanded tab can make a leading-whitespace offset fall before the line.
55009
+ const sourceColumn = Math.max(0, prefixWidth + valueOffset - lineValueStart);
55010
+ return {
55011
+ line: start.line + low,
55012
+ column: (low === 0 ? start.column : 1) + sourceColumn,
55013
+ offset: lineSourceStart + sourceColumn,
55014
+ };
54980
55015
  };
54981
55016
  };
54982
55017
  /**
@@ -55639,7 +55674,9 @@ const processBlockquote = (node, index, parent, isMdxish = false) => {
55639
55674
  }
55640
55675
  Object.assign(node, {
55641
55676
  type: NodeTypes.callout,
55677
+ // Keep the blockquote's data, e.g. the `reparseSource` its positions index into.
55642
55678
  data: {
55679
+ ...node.data,
55643
55680
  hName: 'Callout',
55644
55681
  hProperties: {
55645
55682
  icon,
@@ -55672,14 +55709,55 @@ const calloutTransformer = ({ isMdxish = false } = {}) => {
55672
55709
  };
55673
55710
  /* harmony default export */ const callouts = (calloutTransformer);
55674
55711
 
55712
+ ;// ./processor/transform/mdxish/reparse-source.ts
55713
+ /**
55714
+ * Mark re-parsed subtree roots with the string their positions index into, so consumers
55715
+ * slicing by offset use the right source (see `Data.reparseSource`). Only roots are
55716
+ * stamped; descendants resolve via their nearest stamped ancestor. Roots a deeper
55717
+ * re-parse already stamped keep their own string.
55718
+ */
55719
+ const stampReparseSource = (roots, reparseSource) => {
55720
+ roots.forEach(root => {
55721
+ if (!root.data?.reparseSource)
55722
+ root.data = { ...root.data, reparseSource };
55723
+ });
55724
+ };
55725
+ /**
55726
+ * Splice `replacements` over `parent.children[index]`. They take over the replaced node's
55727
+ * position, so they also take over the coordinate space that position indexes into.
55728
+ */
55729
+ const replaceInheritingReparseSource = (parent, index, replacements) => {
55730
+ const replacedSource = parent.children[index]?.data?.reparseSource;
55731
+ if (replacedSource)
55732
+ stampReparseSource(replacements, replacedSource);
55733
+ parent.children.splice(index, 1, ...replacements);
55734
+ };
55735
+ /**
55736
+ * The string `node`'s offsets index into: the nearest stamp on it or an ancestor, else the
55737
+ * document source.
55738
+ */
55739
+ const resolveReparseSource = (node, ancestors, documentSource) => {
55740
+ if (node.data?.reparseSource)
55741
+ return node.data.reparseSource;
55742
+ for (let i = ancestors.length - 1; i >= 0; i -= 1) {
55743
+ const { reparseSource } = ancestors[i].data ?? {};
55744
+ if (reparseSource)
55745
+ return reparseSource;
55746
+ }
55747
+ return documentSource;
55748
+ };
55749
+
55675
55750
  ;// ./processor/transform/code-tabs.ts
55676
55751
 
55677
55752
 
55753
+
55678
55754
  const isCode = (node) => node?.type === 'code';
55679
55755
  const codeTabsTransformer = ({ copyButtons } = {}) => (tree) => {
55680
55756
  visit(tree, 'code', (node) => {
55681
55757
  const { lang, meta, value } = node;
55682
55758
  node.data = {
55759
+ // Ensure positions are passed through
55760
+ ...node.data,
55683
55761
  hProperties: { lang, meta, value, copyButtons },
55684
55762
  };
55685
55763
  });
@@ -55726,6 +55804,9 @@ const codeTabsTransformer = ({ copyButtons } = {}) => (tree) => {
55726
55804
  end: children[children.length - 1].position.end,
55727
55805
  },
55728
55806
  };
55807
+ // Adjacent siblings share a coordinate space, so the wrapper takes the first block's.
55808
+ if (node.data?.reparseSource)
55809
+ stampReparseSource([codeTabs], node.data.reparseSource);
55729
55810
  parent.children.splice(index, children.length, codeTabs);
55730
55811
  });
55731
55812
  return tree;
@@ -80108,6 +80189,7 @@ const walkTags = (html, handlers) => {
80108
80189
  };
80109
80190
 
80110
80191
  ;// ./processor/transform/mdxish/tables/utils.ts
80192
+
80111
80193
  const tableTags = new Set([
80112
80194
  'thead',
80113
80195
  'tbody',
@@ -80126,6 +80208,9 @@ const tableTags = new Set([
80126
80208
  const unwrapParagraphNodes = (children) => {
80127
80209
  return children.flatMap(child => {
80128
80210
  if (child.type === 'paragraph' && 'children' in child && Array.isArray(child.children)) {
80211
+ // The unwrap drops a stamped root, so its children inherit that coordinate space.
80212
+ if (child.data?.reparseSource)
80213
+ stampReparseSource(child.children, child.data.reparseSource);
80129
80214
  return child.children;
80130
80215
  }
80131
80216
  return [child];
@@ -80721,9 +80806,10 @@ const findTableRanges = (html) => {
80721
80806
  * The surrounding raw HTML (the wrapper's open/close tags) would be re-nested
80722
80807
  * around the parsed tables by rehype-raw.
80723
80808
  *
80724
- * Returns null when there is no wrapped table to extract.
80809
+ * Returns null when there is no wrapped table to extract. `source` is the string the node's
80810
+ * positions index into, so the parts' positions survive stripped container prefixes.
80725
80811
  */
80726
- const splitHtmlWithNestedTables = (node) => {
80812
+ const splitHtmlWithNestedTables = (node, source) => {
80727
80813
  const { value } = node;
80728
80814
  // This is a top-level table, so we don't need to split it
80729
80815
  if (TOP_LEVEL_TABLE_TAG_RE.test(value))
@@ -80736,13 +80822,11 @@ const splitHtmlWithNestedTables = (node) => {
80736
80822
  const ranges = findTableRanges(value);
80737
80823
  if (ranges.length === 0)
80738
80824
  return null;
80739
- const base = node.position?.start;
80825
+ const toSourcePoint = createValueToSourceMapper(node.position, value, source);
80740
80826
  const sliceToHtml = (from, to) => ({
80741
80827
  type: 'html',
80742
80828
  value: value.slice(from, to),
80743
- ...(base && {
80744
- position: { start: pointAfter(base, value.slice(0, from)), end: pointAfter(base, value.slice(0, to)) },
80745
- }),
80829
+ ...(toSourcePoint && { position: { start: toSourcePoint(from), end: toSourcePoint(to) } }),
80746
80830
  });
80747
80831
  const parts = [];
80748
80832
  let cursor = 0;
@@ -80777,6 +80861,8 @@ const splitHtmlWithNestedTables = (node) => {
80777
80861
 
80778
80862
 
80779
80863
 
80864
+
80865
+
80780
80866
 
80781
80867
 
80782
80868
 
@@ -80829,7 +80915,7 @@ const tableRepairs = [
80829
80915
  * The plugins in the processor allows parsing markdown & special syntax inside the table cells
80830
80916
  * After parsing, we need to update the node positions
80831
80917
  */
80832
- const parseTableNode = (processor, node, repair) => {
80918
+ const parseTableNode = (processor, node, source, repair) => {
80833
80919
  let parsed;
80834
80920
  try {
80835
80921
  parsed = processor.runSync(processor.parse(node.value));
@@ -80843,19 +80929,18 @@ const parseTableNode = (processor, node, repair) => {
80843
80929
  if (repair) {
80844
80930
  remapPositionsThroughLayers(parsed, repair.originalSource, repair.layers);
80845
80931
  }
80846
- // The subparser produces positions relative to `node.value`; shift them by
80847
- // the outer node's offset/line so consumers can slice the full source.
80848
- const baseOffset = node.position?.start?.offset ?? 0;
80849
- const baseLine = (node.position?.start?.line ?? 1) - 1;
80932
+ // The subparser produces positions relative to `node.value`; map them into
80933
+ // the outer source so consumers can slice it.
80934
+ const toSourcePoint = createValueToSourceMapper(node.position, repair?.originalSource ?? node.value, source);
80935
+ if (!toSourcePoint)
80936
+ return parsed;
80850
80937
  visit(parsed, child => {
80851
- if (child.position?.start) {
80852
- child.position.start.offset = (child.position.start.offset ?? 0) + baseOffset;
80853
- child.position.start.line += baseLine;
80854
- }
80855
- if (child.position?.end) {
80856
- child.position.end.offset = (child.position.end.offset ?? 0) + baseOffset;
80857
- child.position.end.line += baseLine;
80858
- }
80938
+ if (!child.position)
80939
+ return;
80940
+ child.position = {
80941
+ start: toSourcePoint(child.position.start.offset ?? 0),
80942
+ end: toSourcePoint(child.position.end.offset ?? 0),
80943
+ };
80859
80944
  });
80860
80945
  return parsed;
80861
80946
  };
@@ -80885,12 +80970,10 @@ const hasFlowContent = (nodes) => {
80885
80970
  return nodes.some(node => !phrasing(node) && node.type !== 'paragraph');
80886
80971
  };
80887
80972
  /**
80888
- * Process a Table node: re-parse text-only cell content, then output as
80889
- * a markdown table (phrasing-only) or keep as JSX <Table> (has flow content).
80973
+ * Process a Table node: re-parse text-only cell content, then return it as
80974
+ * a markdown table (phrasing-only) or keep it as JSX <Table> (has flow content).
80890
80975
  */
80891
- const processTableNode = (node, index, parent, documentPosition) => {
80892
- if (node.name !== 'Table' && node.name !== 'table')
80893
- return;
80976
+ const processTableNode = (node, documentPosition) => {
80894
80977
  const position = documentPosition ?? node.position;
80895
80978
  const { align: alignAttr } = getAttrs(node);
80896
80979
  const align = Array.isArray(alignAttr) ? alignAttr : null;
@@ -80917,6 +81000,8 @@ const processTableNode = (node, index, parent, documentPosition) => {
80917
81000
  try {
80918
81001
  const parsed = tableNodeProcessor.runSync(tableNodeProcessor.parse(textContent));
80919
81002
  if (parsed.children.length > 0) {
81003
+ // Positions index into the extracted text rather than the table source (CX-4004).
81004
+ stampReparseSource(parsed.children, textContent);
80920
81005
  cell.children = parsed.children;
80921
81006
  if (hasFlowContent(parsed.children)) {
80922
81007
  tableHasFlowContent = true;
@@ -80959,11 +81044,7 @@ const processTableNode = (node, index, parent, documentPosition) => {
80959
81044
  : unwrapParagraphNodes(el.children);
80960
81045
  el.children = removeWhitespaceOnlyTextNodes(unwrapped);
80961
81046
  });
80962
- parent.children[index] = {
80963
- ...node,
80964
- position,
80965
- };
80966
- return;
81047
+ return { ...node, position };
80967
81048
  }
80968
81049
  // All cells are phrasing-only — convert to markdown table
80969
81050
  const children = [];
@@ -81041,7 +81122,7 @@ const processTableNode = (node, index, parent, documentPosition) => {
81041
81122
  const alignArray = align && columnCount > 0
81042
81123
  ? align.slice(0, columnCount).concat(new Array(Math.max(0, columnCount - align.length)).fill(null))
81043
81124
  : new Array(columnCount).fill(null);
81044
- const mdNode = {
81125
+ return {
81045
81126
  align: alignArray,
81046
81127
  type: 'table',
81047
81128
  position,
@@ -81049,14 +81130,13 @@ const processTableNode = (node, index, parent, documentPosition) => {
81049
81130
  // Remember the author's spelling so the serializer can write `<table>` back instead of `<Table>`
81050
81131
  ...(node.name === 'table' && { data: { lowercaseTable: true } }),
81051
81132
  };
81052
- parent.children[index] = mdNode;
81053
81133
  };
81054
81134
  /**
81055
81135
  * Apply `tableRepairs` cumulatively, re-parsing after every change and stopping
81056
81136
  * once the accumulated result parses. Each layer's inserts are relative to the
81057
81137
  * string that repair received, so they stay ordered for position remapping.
81058
81138
  */
81059
- const repairAndReparse = (node) => {
81139
+ const repairAndReparse = (node, source) => {
81060
81140
  let repairedValue = node.value;
81061
81141
  const layers = [];
81062
81142
  let parsed;
@@ -81066,11 +81146,45 @@ const repairAndReparse = (node) => {
81066
81146
  return false;
81067
81147
  repairedValue = value;
81068
81148
  layers.push(inserts);
81069
- parsed = parseTableNode(tableNodeProcessor, { ...node, value: repairedValue }, { layers, originalSource: node.value });
81149
+ parsed = parseTableNode(tableNodeProcessor, { ...node, value: repairedValue }, source, {
81150
+ layers,
81151
+ originalSource: node.value,
81152
+ });
81070
81153
  return Boolean(parsed);
81071
81154
  });
81072
81155
  return parsed;
81073
81156
  };
81157
+ /**
81158
+ * Re-parse a table html node and replace it with a markdown / JSX table, or the
81159
+ * fallback parse's fragments for a lowercase table mdxjs rejects.
81160
+ */
81161
+ const replaceTableHtml = (node, index, parent, source) => {
81162
+ // Because the processor uses remarkMdx, it is stricter in what it accepts
81163
+ // and only accepts valid MDX syntax in the table node. To get around that,
81164
+ // fall back to the cumulative repairs when the first parse fails.
81165
+ const parsed = parseTableNode(tableNodeProcessor, node, source) ?? repairAndReparse(node, source);
81166
+ if (parsed) {
81167
+ // If the table is parsed successfully, we can now process it further
81168
+ // to build on the markdown / JSX table
81169
+ visit(parsed, isMDXElement, (tableNode) => {
81170
+ if (tableNode.name !== 'Table' && tableNode.name !== 'table')
81171
+ return undefined;
81172
+ replaceInheritingReparseSource(parent, index, [processTableNode(tableNode, node.position)]);
81173
+ return (/* inlined export .EXIT */false);
81174
+ });
81175
+ }
81176
+ else if (node.value.startsWith('<table')) {
81177
+ // If the parsing still fails, give an opportunity to the fallback parser
81178
+ // without remarkMdx to process lowercase tables as it's likely to not
81179
+ // have needed MDX parsing anyway
81180
+ const fallback = parseTableNode(fallbackTableNodeProcessor, node, source);
81181
+ if (!fallback || fallback.children.length <= 1)
81182
+ return;
81183
+ replaceInheritingReparseSource(parent, index, fallback.children);
81184
+ }
81185
+ // Otherwise, there's no point in trying to parse the table content further
81186
+ // More repairs are needed in that case
81187
+ };
81074
81188
  /**
81075
81189
  * Converts JSX Table elements to markdown table nodes and re-parses markdown in cells.
81076
81190
  *
@@ -81082,24 +81196,26 @@ const repairAndReparse = (node) => {
81082
81196
  * When cell content contains block-level nodes (callouts, code blocks, etc.), the table
81083
81197
  * is kept as a JSX <Table> element so that remarkRehype can properly handle the flow content.
81084
81198
  */
81085
- const mdxishTables = () => tree => {
81199
+ const mdxishTables = () => (tree, file) => {
81200
+ // Positions index into the document unless a re-parse stamped another source.
81201
+ const documentSource = file?.value ? String(file.value) : undefined;
81086
81202
  // Pre-pass: lift `<table>`s wrapped in a raw HTML block out into their own
81087
81203
  // html nodes so the main pass below treats them like top-level tables.
81088
- visit(tree, 'html', (_node, index, parent) => {
81089
- const node = _node;
81090
- if (typeof index !== 'number' || !parent || !('children' in parent))
81204
+ visitParents(tree, 'html', (node, ancestors) => {
81205
+ const parent = ancestors[ancestors.length - 1];
81206
+ if (!parent)
81091
81207
  return;
81092
- const parts = splitHtmlWithNestedTables(node);
81208
+ const parts = splitHtmlWithNestedTables(node, resolveReparseSource(node, ancestors, documentSource));
81093
81209
  if (!parts)
81094
81210
  return;
81095
81211
  // The inserted parts can't re-trigger a split (table parts start with
81096
81212
  // `<table`; the wrapper slices hold no table), so plain in-place splicing
81097
81213
  // visits each once without looping.
81098
- parent.children.splice(index, 1, ...parts);
81214
+ replaceInheritingReparseSource(parent, parent.children.indexOf(node), parts);
81099
81215
  });
81100
- visit(tree, 'html', (_node, index, parent) => {
81101
- const node = _node;
81102
- if (typeof index !== 'number' || !parent || !('children' in parent))
81216
+ visitParents(tree, 'html', (node, ancestors) => {
81217
+ const parent = ancestors[ancestors.length - 1];
81218
+ if (!parent)
81103
81219
  return;
81104
81220
  if (!node.value.startsWith('<Table') && !node.value.startsWith('<table'))
81105
81221
  return;
@@ -81107,33 +81223,8 @@ const mdxishTables = () => tree => {
81107
81223
  // paragraph. Leave them untouched so rehype-raw can reassemble the table.
81108
81224
  if (parent.type === 'paragraph')
81109
81225
  return;
81110
- // Because the processor uses remarkMdx, it is stricter in what it accepts
81111
- // and only accepts valid MDX syntax in the table node. To get around that,
81112
- // fall back to the cumulative repairs when the first parse fails.
81113
- const parsed = parseTableNode(tableNodeProcessor, node) ?? repairAndReparse(node);
81114
- if (parsed) {
81115
- // If the table is parsed successfully, we can now process it further
81116
- // to build on the markdown / JSX table
81117
- visit(parsed, isMDXElement, (tableNode) => {
81118
- if (tableNode.name !== 'Table' && tableNode.name !== 'table')
81119
- return undefined;
81120
- processTableNode(tableNode, index, parent, node.position);
81121
- return (/* inlined export .EXIT */false);
81122
- });
81123
- }
81124
- else if (node.value.startsWith('<table')) {
81125
- // If the parsing still fails, give an opportunity to the fallback parser
81126
- // without remarkMdx to process lowercase tables as it's likely to not
81127
- // have needed MDX parsing anyway
81128
- const fallback = parseTableNode(fallbackTableNodeProcessor, node);
81129
- if (!fallback || fallback.children.length <= 1)
81130
- return;
81131
- parent.children.splice(index, 1, ...fallback.children);
81132
- }
81133
- // Otherwise, there's no point in trying to parse the table content further
81134
- // More repairs are needed in that case
81226
+ replaceTableHtml(node, parent.children.indexOf(node), parent, resolveReparseSource(node, ancestors, documentSource));
81135
81227
  });
81136
- return tree;
81137
81228
  };
81138
81229
  /* harmony default export */ const mdxish_tables = (mdxishTables);
81139
81230
 
@@ -103941,9 +104032,9 @@ const mdxishCalloutToJsx = () => tree => {
103941
104032
  visit(tree, NodeTypes.callout, (node, index, parent) => {
103942
104033
  if (!parent || index === undefined)
103943
104034
  return;
103944
- let { icon, theme } = node.data.hProperties;
103945
- if (!icon && theme)
103946
- icon = defaultIcons[theme];
104035
+ // No default-icon fill: an empty `icon` means the author removed it
104036
+ const { icon } = node.data.hProperties;
104037
+ let { theme } = node.data.hProperties;
103947
104038
  if (!theme && icon)
103948
104039
  theme = themes[icon] || 'default';
103949
104040
  // Mutate in place to keep the children array identity so nested callouts in
@@ -104494,18 +104585,6 @@ const getInlineMdProcessor = ({ safeMode = false } = {}) => {
104494
104585
  }
104495
104586
  return processor;
104496
104587
  };
104497
- /**
104498
- * Mark re-parsed subtree roots with the string their positions index into, so consumers
104499
- * slicing by offset use the right source (see `Data.reparseSource`). Only roots are
104500
- * stamped; descendants resolve via their nearest stamped ancestor. Roots a deeper
104501
- * re-parse already stamped keep their own string.
104502
- */
104503
- const stampReparseSource = (roots, reparseSource) => {
104504
- roots.forEach(root => {
104505
- if (!root.data?.reparseSource)
104506
- root.data = { ...root.data, reparseSource };
104507
- });
104508
- };
104509
104588
  /**
104510
104589
  * True when a tag name starts with an uppercase letter — ReadMe's marker for
104511
104590
  * a custom MDX component (vs a lowercase HTML tag).
@@ -104592,6 +104671,7 @@ function findBalancedClosingTagIndex(content, tag) {
104592
104671
 
104593
104672
 
104594
104673
 
104674
+
104595
104675
  /**
104596
104676
  * Parse the body of an inline component as phrasing content. Remark always
104597
104677
  * wraps bare text in a paragraph; flatten that single-paragraph wrapper so
@@ -104843,6 +104923,7 @@ function terminateHtmlFlowBlocks(content) {
104843
104923
 
104844
104924
 
104845
104925
 
104926
+
104846
104927
  // Matches a JSX attribute expression (e.g. `key={i}`) anywhere in a string. One name
104847
104928
  // char suffices for existence — `[\w-]+` backtracked quadratically over huge attributes.
104848
104929
  const NESTED_ATTR_EXPRESSION_RE = /[\w-]\s*=\s*\{/;
@@ -104924,26 +105005,12 @@ const parseSibling = (parent, index, sibling, safeMode, promoted) => {
104924
105005
  }
104925
105006
  };
104926
105007
  // Ends the position at `consumedLength` so the component doesn't claim trailing
104927
- // content the tokenizer swallowed into the same html node. Measured against the source
104928
- // when available: each value line is its source line minus the container prefix
104929
- // (list indent, `> `) micromark stripped, so those prefixes are counted back in.
105008
+ // content the tokenizer swallowed into the same html node.
104930
105009
  const positionEndingAtConsumed = (nodePosition, value, consumedLength, source) => {
104931
- if (!nodePosition?.start)
105010
+ const toSourcePoint = createValueToSourceMapper(nodePosition, value, source);
105011
+ if (!nodePosition || !toSourcePoint)
104932
105012
  return nodePosition;
104933
- const consumedValue = value.slice(0, consumedLength);
104934
- const sourceLines = source?.slice(nodePosition.start.offset, nodePosition.end?.offset).split('\n');
104935
- const valueLines = value.split('\n');
104936
- if (!sourceLines || sourceLines.length !== valueLines.length) {
104937
- return { start: nodePosition.start, end: pointAfter(nodePosition.start, consumedValue) };
104938
- }
104939
- const consumedLines = consumedValue.split('\n');
104940
- const lastLineIndex = consumedLines.length - 1;
104941
- const prefixWidth = sourceLines[lastLineIndex].length - valueLines[lastLineIndex].length;
104942
- const consumedSource = [
104943
- ...sourceLines.slice(0, lastLineIndex),
104944
- sourceLines[lastLineIndex].slice(0, prefixWidth + consumedLines[lastLineIndex].length),
104945
- ].join('\n');
104946
- return { start: nodePosition.start, end: pointAfter(nodePosition.start, consumedSource) };
105013
+ return { start: nodePosition.start, end: toSourcePoint(consumedLength) };
104947
105014
  };
104948
105015
  const createComponentNode = ({ tag, attributes, children, startPosition, endPosition, }) => ({
104949
105016
  type: 'mdxJsxFlowElement',
@@ -104955,14 +105022,6 @@ const createComponentNode = ({ tag, attributes, children, startPosition, endPosi
104955
105022
  end: endPosition?.end ?? startPosition?.end,
104956
105023
  },
104957
105024
  });
104958
- // The promoted node takes over the html node's position, so it also takes over the
104959
- // coordinate space that position belongs to.
104960
- const substituteNodeWithMdxNode = (parent, index, mdxNode) => {
104961
- const replacedSource = parent.children[index]?.data?.reparseSource;
104962
- if (replacedSource)
104963
- stampReparseSource([mdxNode], replacedSource);
104964
- parent.children.splice(index, 1, mdxNode);
104965
- };
104966
105025
  /**
104967
105026
  * Transform PascalCase HTML nodes into mdxJsxFlowElement nodes.
104968
105027
  *
@@ -105061,7 +105120,7 @@ function promoteComponentBlocks(tree, safeMode, source) {
105061
105120
  ? positionEndingAtConsumed(node.position, value, leadingWhitespace + openingTagEnd, source)
105062
105121
  : node.position,
105063
105122
  });
105064
- substituteNodeWithMdxNode(parent, index, componentNode);
105123
+ replaceInheritingReparseSource(parent, index, [componentNode]);
105065
105124
  if (remainingContent) {
105066
105125
  parseSibling(parent, index, remainingContent, safeMode, promoted);
105067
105126
  }
@@ -105112,7 +105171,7 @@ function promoteComponentBlocks(tree, safeMode, source) {
105112
105171
  startPosition: node.position,
105113
105172
  endPosition,
105114
105173
  });
105115
- substituteNodeWithMdxNode(parent, index, componentNode);
105174
+ replaceInheritingReparseSource(parent, index, [componentNode]);
105116
105175
  // The unwrap reparented the children out of their paragraph, so re-walk them
105117
105176
  // since the children HTML may contain promotable syntax (e.g. `{…}`-attr tags)
105118
105177
  if (unwrappedSoleParagraph) {
@@ -106554,6 +106613,7 @@ const magicBlockTransformer = (options = {}) => tree => {
106554
106613
 
106555
106614
 
106556
106615
 
106616
+
106557
106617
  // `<HTMLBlock …>{`…`}</HTMLBlock>` embedded inside a raw HTML block (e.g. a
106558
106618
  // single-line `<div>…</div>`). CommonMark slurps the whole div as one `html`
106559
106619
  // node, so the tokenizer never sees the HTMLBlock — we recover it here.
@@ -106660,7 +106720,8 @@ const mdxishHtmlBlocks = () => tree => {
106660
106720
  return;
106661
106721
  const exprChild = element.children.find(child => child.type === 'mdxFlowExpression' || child.type === 'mdxTextExpression');
106662
106722
  const openingTagIndent = (element.position?.start.column ?? 1) - 1;
106663
- parent.children[index] = createHtmlBlockNode(formatHtmlForMdxish(extractTemplateLiteral(exprChild?.value), openingTagIndent), element.position, toRunScripts(jsxAttr(element, 'runScripts')), jsxAttr(element, 'safeMode'));
106723
+ const htmlBlock = createHtmlBlockNode(formatHtmlForMdxish(extractTemplateLiteral(exprChild?.value), openingTagIndent), element.position, toRunScripts(jsxAttr(element, 'runScripts')), jsxAttr(element, 'safeMode'));
106724
+ replaceInheritingReparseSource(parent, index, [htmlBlock]);
106664
106725
  });
106665
106726
  // Shape 2: raw HTML blob.
106666
106727
  visit(tree, 'html', (node, index, parent) => {
@@ -106668,11 +106729,85 @@ const mdxishHtmlBlocks = () => tree => {
106668
106729
  return;
106669
106730
  const replacement = splitRawHtmlBlocks(node);
106670
106731
  if (replacement)
106671
- parent.children.splice(index, 1, ...replacement);
106732
+ replaceInheritingReparseSource(parent, index, replacement);
106672
106733
  });
106673
106734
  };
106674
106735
  /* harmony default export */ const mdxish_html_blocks = (mdxishHtmlBlocks);
106675
106736
 
106737
+ ;// ./processor/transform/mdxish/tables/cell-width.ts
106738
+
106739
+
106740
+ // Key must follow `{` or `,` so `minWidth`/`maxWidth` don't match on their suffix
106741
+ const STYLE_OBJECT_WIDTH_REGEX = /[{,]\s*["']?width["']?\s*:\s*(?:"([^"]*)"|'([^']*)'|([^,}]+))/i;
106742
+ // First colon only, so `url(http://…)` values stay intact
106743
+ const CSS_DECLARATION_REGEX = /:(.*)/s;
106744
+ const UNITLESS_NUMBER_REGEX = /^\d+(?:\.\d+)?$/;
106745
+ // HTML reads a `width` attribute as a leading number plus optional `%`, ignoring the rest
106746
+ const HTML_DIMENSION_REGEX = /^\s*(\d+(?:\.\d+)?)\s*(%?)/;
106747
+ /** Bare numbers are pixels. A value carrying declaration separators is not a single width. */
106748
+ const normalizeWidth = (value) => {
106749
+ if (typeof value === 'number')
106750
+ return `${value}px`;
106751
+ if (typeof value !== 'string' || !value.trim())
106752
+ return null;
106753
+ const trimmed = value.trim();
106754
+ if (/[;:{}]/.test(trimmed))
106755
+ return null;
106756
+ return UNITLESS_NUMBER_REGEX.test(trimmed) ? `${trimmed}px` : trimmed;
106757
+ };
106758
+ const widthFromAttribute = (value) => {
106759
+ if (typeof value === 'number')
106760
+ return `${value}px`;
106761
+ if (typeof value !== 'string')
106762
+ return null;
106763
+ const match = HTML_DIMENSION_REGEX.exec(value);
106764
+ return match ? `${match[1]}${match[2] || 'px'}` : null;
106765
+ };
106766
+ const cssPropertyNames = (style) => style
106767
+ .split(';')
106768
+ .map(declaration => declaration.split(CSS_DECLARATION_REGEX)[0].trim().toLowerCase())
106769
+ .filter(Boolean);
106770
+ /** Handles a CSS declaration list and a style object safe mode left as a string (`{ width: "30%" }`). */
106771
+ const widthFromStyleString = (style) => {
106772
+ const trimmed = style.trim();
106773
+ if (trimmed.startsWith('{')) {
106774
+ return STYLE_OBJECT_WIDTH_REGEX.exec(trimmed)?.slice(1).find(Boolean);
106775
+ }
106776
+ return trimmed
106777
+ .split(';')
106778
+ .map(declaration => declaration.split(CSS_DECLARATION_REGEX))
106779
+ .find(([property]) => property.trim().toLowerCase() === 'width')?.[1];
106780
+ };
106781
+ /** Accepts `style={{ width }}`, `style="width: …"` and the hand-authored `width="…"`. */
106782
+ const getCellWidth = (cell) => {
106783
+ const { style, width } = getAttrs(cell);
106784
+ let styleWidth;
106785
+ if (style_object_to_css_isPlainObject(style)) {
106786
+ styleWidth = style.width;
106787
+ }
106788
+ else if (typeof style === 'string') {
106789
+ styleWidth = widthFromStyleString(style);
106790
+ }
106791
+ return normalizeWidth(styleWidth) ?? widthFromAttribute(width);
106792
+ };
106793
+ /** True when the cell's attributes state nothing but its width, which mdast can carry. */
106794
+ const hasOnlyWidthAttributes = (cell) => {
106795
+ if (cell.attributes.length === 0)
106796
+ return false;
106797
+ const { style } = getAttrs(cell);
106798
+ return cell.attributes.every(attribute => {
106799
+ if (!('name' in attribute))
106800
+ return false;
106801
+ if (attribute.name === 'width')
106802
+ return true;
106803
+ if (attribute.name !== 'style')
106804
+ return false;
106805
+ if (style_object_to_css_isPlainObject(style))
106806
+ return Object.keys(style).every(key => key === 'width');
106807
+ return typeof style === 'string' && cssPropertyNames(style).every(property => property === 'width');
106808
+ });
106809
+ };
106810
+
106676
106811
  ;// ./processor/transform/mdxish/mdxish-jsx-to-mdast.ts
106677
106812
 
106678
106813
 
@@ -106680,6 +106815,8 @@ const mdxishHtmlBlocks = () => tree => {
106680
106815
 
106681
106816
 
106682
106817
 
106818
+
106819
+
106683
106820
  function toImageAlign(value) {
106684
106821
  if (value === 'left' || value === 'center' || value === 'right') {
106685
106822
  return value;
@@ -106895,6 +107032,15 @@ const transformAnchor = (jsx) => {
106895
107032
  position: jsx.position,
106896
107033
  };
106897
107034
  };
107035
+ /**
107036
+ * Parses an image caption's markdown. The caption is re-parsed on its own, so the
107037
+ * children's positions index into it rather than the document.
107038
+ */
107039
+ const parseCaptionChildren = (caption) => {
107040
+ const { children } = lib_mdast(caption);
107041
+ stampReparseSource(children, caption);
107042
+ return children;
107043
+ };
106898
107044
  /**
106899
107045
  * Transforms an `<Image />` JSX element into an image-block MDAST node.
106900
107046
  * Normalizes attributes (align, border, width→sizing) and parses caption markdown into children.
@@ -106925,7 +107071,7 @@ const transformImage = (jsx) => {
106925
107071
  alt,
106926
107072
  border: toBool(border),
106927
107073
  caption,
106928
- children: caption ? lib_mdast(caption).children : [],
107074
+ children: caption ? parseCaptionChildren(caption) : [],
106929
107075
  className,
106930
107076
  framed: toBool(framed),
106931
107077
  height: height !== undefined ? String(height) : undefined,
@@ -107121,10 +107267,40 @@ const wrapBareCellsInRow = (node) => {
107121
107267
  });
107122
107268
  };
107123
107269
  /**
107124
- * Converts a JSX <Table> element to an MDAST table node with alignment.
107125
- * Returns null for header-less tables since MDAST always promotes the first row to <thead>.
107270
+ * A lowercase <table> reaches here only because `mdxishTables` kept it as JSX for its
107271
+ * attributes. Widths on first-row cells are the one attribute mdast can carry; anything
107272
+ * else stays JSX so it still renders.
107273
+ */
107274
+ const lowercaseTableIsConvertible = (jsx) => {
107275
+ if (jsx.attributes.length > 0)
107276
+ return false;
107277
+ const headerCells = new Set();
107278
+ let headerRowSeen = false;
107279
+ let convertible = true;
107280
+ visit(jsx, isMDXElement, (child) => {
107281
+ if (child.name === 'tr' && !headerRowSeen) {
107282
+ headerRowSeen = true;
107283
+ visit(child, mdxish_jsx_to_mdast_isTableCell, cell => {
107284
+ headerCells.add(cell);
107285
+ });
107286
+ }
107287
+ if (child.name === null || !tableTags.has(child.name) || child.attributes.length === 0)
107288
+ return;
107289
+ if (!headerCells.has(child) || !hasOnlyWidthAttributes(child) || getCellWidth(child) === null) {
107290
+ convertible = false;
107291
+ }
107292
+ });
107293
+ return convertible;
107294
+ };
107295
+ /**
107296
+ * Converts a JSX <Table>, or a lowercase <table> carrying only widths, to an MDAST table
107297
+ * node with alignment and widths. Returns null for header-less tables since MDAST always
107298
+ * promotes the first row to <thead>.
107126
107299
  */
107127
107300
  const transformTable = (jsx) => {
107301
+ const isLowercase = jsx.name === 'table';
107302
+ if (isLowercase && !lowercaseTableIsConvertible(jsx))
107303
+ return null;
107128
107304
  wrapBareCellsInRow(jsx);
107129
107305
  let hasThead = false;
107130
107306
  visit(jsx, isMDXElement, (child) => {
@@ -107136,6 +107312,7 @@ const transformTable = (jsx) => {
107136
107312
  const { align: alignAttr } = getAttrs(jsx);
107137
107313
  const align = Array.isArray(alignAttr) ? alignAttr : null;
107138
107314
  const rows = [];
107315
+ const widths = [];
107139
107316
  visit(jsx, isMDXElement, (child) => {
107140
107317
  if (child.name !== 'thead' && child.name !== 'tbody')
107141
107318
  return;
@@ -107143,6 +107320,7 @@ const transformTable = (jsx) => {
107143
107320
  if (row.name !== 'tr')
107144
107321
  return;
107145
107322
  const cells = [];
107323
+ const isHeaderRow = rows.length === 0;
107146
107324
  visit(row, mdxish_jsx_to_mdast_isTableCell, (cell) => {
107147
107325
  const parsedChildren = unwrapSoleParagraph(cell.children);
107148
107326
  cells.push({
@@ -107150,6 +107328,8 @@ const transformTable = (jsx) => {
107150
107328
  children: parsedChildren,
107151
107329
  position: cell.position,
107152
107330
  });
107331
+ if (isHeaderRow)
107332
+ widths.push(getCellWidth(cell));
107153
107333
  });
107154
107334
  rows.push({
107155
107335
  type: 'tableRow',
@@ -107162,11 +107342,16 @@ const transformTable = (jsx) => {
107162
107342
  const alignArray = align && columnCount > 0
107163
107343
  ? align.slice(0, columnCount).concat(Array.from({ length: Math.max(0, columnCount - align.length) }, () => null))
107164
107344
  : Array.from({ length: columnCount }, () => null);
107345
+ const data = {
107346
+ ...(isLowercase && { lowercaseTable: true }),
107347
+ ...(widths.some(Boolean) && { widths }),
107348
+ };
107165
107349
  return {
107166
107350
  type: 'table',
107167
107351
  align: alignArray,
107168
107352
  position: jsx.position,
107169
107353
  children: rows,
107354
+ ...(Object.keys(data).length > 0 && { data }),
107170
107355
  };
107171
107356
  };
107172
107357
  /**
@@ -107196,6 +107381,7 @@ const COMPONENT_MAP = {
107196
107381
  figure: transformFigure,
107197
107382
  Recipe: transformRecipe,
107198
107383
  Table: transformTable,
107384
+ table: transformTable,
107199
107385
  };
107200
107386
  /**
107201
107387
  * Transform mdxJsxFlowElement nodes, magic block nodes, and HTML figure elements
@@ -107222,8 +107408,7 @@ const mdxishJsxToMdast = () => tree => {
107222
107408
  const newNode = transformer(node);
107223
107409
  if (!newNode)
107224
107410
  return;
107225
- // Replace the JSX node with the MDAST node
107226
- parent.children[index] = newNode;
107411
+ replaceInheritingReparseSource(parent, index, [newNode]);
107227
107412
  });
107228
107413
  // Inline JSX components (Anchor)
107229
107414
  visit(tree, 'mdxJsxTextElement', (node, index, parent) => {
@@ -107671,6 +107856,28 @@ const restoreBooleanProperties = () => tree => {
107671
107856
  };
107672
107857
 
107673
107858
 
107859
+ ;// ./processor/transform/mdxish/tables/cell-style.ts
107860
+ /** `left` is the browser default, so it's omitted. Lowercase tables get the HTML string form. */
107861
+ const cellStyle = (align, width = null, { html = false } = {}) => {
107862
+ if (html) {
107863
+ if (!width)
107864
+ return null;
107865
+ return { type: 'mdxJsxAttribute', name: 'style', value: `width: ${width}` };
107866
+ }
107867
+ const declarations = [];
107868
+ if (align && align !== 'left')
107869
+ declarations.push(`textAlign: ${JSON.stringify(align)}`);
107870
+ if (width)
107871
+ declarations.push(`width: ${JSON.stringify(width)}`);
107872
+ if (declarations.length === 0)
107873
+ return null;
107874
+ return {
107875
+ type: 'mdxJsxAttribute',
107876
+ name: 'style',
107877
+ value: { type: 'mdxJsxAttributeValueExpression', value: `{ ${declarations.join(', ')} }` },
107878
+ };
107879
+ };
107880
+
107674
107881
  ;// ./processor/transform/mdxish/tables/gfm-cell-normalization.ts
107675
107882
  /** `<br />` is the only line break a GFM pipe cell can carry, since cells are single-line. */
107676
107883
  const createLineBreak = () => ({
@@ -107709,19 +107916,8 @@ const normalizeCellChildrenForGfm = (cell) => normalizeNodes(cell.children);
107709
107916
 
107710
107917
 
107711
107918
 
107919
+
107712
107920
  const SELF_CLOSING_JSX_REGEX = /^\s*<[A-Z][^>]*\/>\s*$/;
107713
- const mdxish_tables_to_jsx_alignToStyle = (align) => {
107714
- if (!align || align === 'left')
107715
- return null;
107716
- return {
107717
- type: 'mdxJsxAttribute',
107718
- name: 'style',
107719
- value: {
107720
- type: 'mdxJsxAttributeValueExpression',
107721
- value: `{ textAlign: "${align}" }`,
107722
- },
107723
- };
107724
- };
107725
107921
  const mdxish_tables_to_jsx_isTableCell = (node) => ['tableHead', 'tableCell'].includes(node.type);
107726
107922
  const mdxish_tables_to_jsx_isLiteral = (node) => 'value' in node;
107727
107923
  /**
@@ -107777,12 +107973,17 @@ const mdxishTablesToJsx = () => tree => {
107777
107973
  gfmCells.push([cell, normalized]);
107778
107974
  return undefined;
107779
107975
  });
107780
- const stayLowercase = table.data?.lowercaseTable === true;
107976
+ const widths = table.data?.widths ?? [];
107977
+ const hasWidths = widths.some(Boolean);
107978
+ // Lowercase cells can carry a width but not an alignment, so an aligned table becomes <Table>
107979
+ const hasAlignment = table.align.some(align => align && align !== 'left');
107980
+ const stayLowercase = table.data?.lowercaseTable === true && !hasAlignment;
107781
107981
  // We transform to GFM tables if:
107782
107982
  // 1. If it doesn't contain complex elements that the syntax can't represent
107783
- // 2. The original table is not lowercase HTML <table>, because we want to
107983
+ // 2. The original table is not lowercase HTML <table>, because we want to
107784
107984
  // preserve original tables as much as possible
107785
- if (!requiresJsxTable && !stayLowercase) {
107985
+ // 3. No column has a width, which GFM can't express
107986
+ if (!requiresJsxTable && !stayLowercase && !hasWidths) {
107786
107987
  gfmCells.forEach(([cell, children]) => {
107787
107988
  cell.children = children;
107788
107989
  });
@@ -107794,7 +107995,8 @@ const mdxishTablesToJsx = () => tree => {
107794
107995
  if (!headerRow)
107795
107996
  return;
107796
107997
  visit(table, mdxish_tables_to_jsx_isTableCell, flattenBreaksToNewlines);
107797
- const styles = table.align.map(mdxish_tables_to_jsx_alignToStyle);
107998
+ const headerStyles = table.align.map((align, i) => cellStyle(align, widths[i] ?? null, { html: stayLowercase }));
107999
+ const bodyStyles = table.align.map(align => cellStyle(align, null, { html: stayLowercase }));
107798
108000
  const head = {
107799
108001
  attributes: [],
107800
108002
  type: 'mdxJsxFlowElement',
@@ -107810,7 +108012,7 @@ const mdxishTablesToJsx = () => tree => {
107810
108012
  type: 'mdxJsxFlowElement',
107811
108013
  name: 'th',
107812
108014
  children: cell.children,
107813
- ...(styles[cellIndex] && { attributes: [styles[cellIndex]] }),
108015
+ ...(headerStyles[cellIndex] && { attributes: [headerStyles[cellIndex]] }),
107814
108016
  };
107815
108017
  }),
107816
108018
  },
@@ -107830,7 +108032,7 @@ const mdxishTablesToJsx = () => tree => {
107830
108032
  type: 'mdxJsxFlowElement',
107831
108033
  name: 'td',
107832
108034
  children: cell.children,
107833
- ...(styles[cellIndex] && { attributes: [styles[cellIndex]] }),
108035
+ ...(bodyStyles[cellIndex] && { attributes: [bodyStyles[cellIndex]] }),
107834
108036
  };
107835
108037
  }),
107836
108038
  };
@@ -109243,7 +109445,7 @@ module.exports = /*#__PURE__*/JSON.parse('{"*":["about","acceptCharset","accessK
109243
109445
  /******/ // startup
109244
109446
  /******/ // Load entry module and return exports
109245
109447
  /******/ // This entry module used 'module' so it can't be inlined
109246
- /******/ let __webpack_exports__ = __webpack_require__(9607);
109448
+ /******/ let __webpack_exports__ = __webpack_require__(8846);
109247
109449
  /******/
109248
109450
  /******/ return __webpack_exports__;
109249
109451
  /******/ })()