@readme/markdown 15.9.0 → 15.9.2

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.
@@ -16243,6 +16243,15 @@ function cloneSort(comparator) {
16243
16243
  return templateFn(comparator);
16244
16244
  }
16245
16245
 
16246
+ let isEvalAllowed = (function () {
16247
+ try {
16248
+ new Function('return 0')();
16249
+ return true;
16250
+ } catch {
16251
+ return false;
16252
+ }
16253
+ })();
16254
+
16246
16255
  /**
16247
16256
  * Sort the given array in-place with the given comparator function.
16248
16257
  *
@@ -16254,7 +16263,7 @@ function cloneSort(comparator) {
16254
16263
 
16255
16264
  let sortCache = new WeakMap();
16256
16265
  exports.g = function (ary, comparator, start = 0) {
16257
- let doQuickSort = sortCache.get(comparator);
16266
+ let doQuickSort = isEvalAllowed ? sortCache.get(comparator) : SortTemplate(comparator);
16258
16267
  if (doQuickSort === void 0) {
16259
16268
  doQuickSort = cloneSort(comparator);
16260
16269
  sortCache.set(comparator, doQuickSort);
@@ -16282,6 +16291,14 @@ var ArraySet = (__webpack_require__(4361)/* .ArraySet */ .C);
16282
16291
  var base64VLQ = __webpack_require__(582);
16283
16292
  var quickSort = (__webpack_require__(4951)/* .quickSort */ .g);
16284
16293
 
16294
+ // Upper bound for a section's `offset.line` in an indexed source map. A flat
16295
+ // map spends one ';' per generated line, so a tiny indexed map with a huge
16296
+ // offset.line turns into a huge amount of work and memory once its mappings
16297
+ // are copied into a SourceMapGenerator (CVE-2026-93749). Ten million lines is
16298
+ // far beyond any real generated file. Columns are VLQ-encoded as deltas and
16299
+ // don't amplify, so they only need to be valid integers.
16300
+ var MAX_SECTION_OFFSET_LINE = 1e7;
16301
+
16285
16302
  function SourceMapConsumer(aSourceMap, aSourceMapURL) {
16286
16303
  var sourceMap = aSourceMap;
16287
16304
  if (typeof aSourceMap === 'string') {
@@ -17206,6 +17223,7 @@ function IndexedSourceMapConsumer(aSourceMap, aSourceMapURL) {
17206
17223
  line: -1,
17207
17224
  column: 0
17208
17225
  };
17226
+ var maxOffsetLine = 0;
17209
17227
  this._sections = sections.map(function (s) {
17210
17228
  if (s.url) {
17211
17229
  // The url field will require support for asynchronicity.
@@ -17216,12 +17234,32 @@ function IndexedSourceMapConsumer(aSourceMap, aSourceMapURL) {
17216
17234
  var offsetLine = util.getArg(offset, 'line');
17217
17235
  var offsetColumn = util.getArg(offset, 'column');
17218
17236
 
17237
+ if (!isValidOffset(offsetLine) || !isValidOffset(offsetColumn)) {
17238
+ throw new Error('Section offset line and column must be non-negative integers.');
17239
+ }
17240
+ if (offsetLine > MAX_SECTION_OFFSET_LINE) {
17241
+ throw new Error('Section offset line must not exceed ' + MAX_SECTION_OFFSET_LINE + '.');
17242
+ }
17243
+
17219
17244
  if (offsetLine < lastOffset.line ||
17220
17245
  (offsetLine === lastOffset.line && offsetColumn < lastOffset.column)) {
17221
17246
  throw new Error('Section offsets must be ordered and non-overlapping.');
17222
17247
  }
17223
17248
  lastOffset = offset;
17224
17249
 
17250
+ var consumer = new SourceMapConsumer(util.getArg(s, 'map'), aSourceMapURL);
17251
+
17252
+ // Offsets of a nested indexed map add up with this one, so apply the
17253
+ // bound to the total.
17254
+ var totalOffsetLine = offsetLine + (consumer._maxOffsetLine || 0);
17255
+ if (totalOffsetLine > MAX_SECTION_OFFSET_LINE) {
17256
+ throw new Error('Section offset line must not exceed ' + MAX_SECTION_OFFSET_LINE +
17257
+ ', including offsets of nested sections.');
17258
+ }
17259
+ if (totalOffsetLine > maxOffsetLine) {
17260
+ maxOffsetLine = totalOffsetLine;
17261
+ }
17262
+
17225
17263
  return {
17226
17264
  generatedOffset: {
17227
17265
  // The offset fields are 0-based, but we use 1-based indices when
@@ -17229,9 +17267,24 @@ function IndexedSourceMapConsumer(aSourceMap, aSourceMapURL) {
17229
17267
  generatedLine: offsetLine + 1,
17230
17268
  generatedColumn: offsetColumn + 1
17231
17269
  },
17232
- consumer: new SourceMapConsumer(util.getArg(s, 'map'), aSourceMapURL)
17270
+ consumer: consumer
17233
17271
  }
17234
17272
  });
17273
+
17274
+ // The largest offset.line of any section, including nested sections.
17275
+ this._maxOffsetLine = maxOffsetLine;
17276
+ }
17277
+
17278
+ /**
17279
+ * Section offsets come from untrusted input and are used as line/column
17280
+ * numbers, so only accept non-negative safe integers (rejects NaN, Infinity,
17281
+ * strings, fractions, etc).
17282
+ */
17283
+ function isValidOffset(aValue) {
17284
+ return typeof aValue === 'number' &&
17285
+ aValue >= 0 &&
17286
+ aValue <= 9007199254740991 && // Number.MAX_SAFE_INTEGER
17287
+ Math.floor(aValue) === aValue;
17235
17288
  }
17236
17289
 
17237
17290
  IndexedSourceMapConsumer.prototype = Object.create(SourceMapConsumer.prototype);
@@ -17249,8 +17302,12 @@ Object.defineProperty(IndexedSourceMapConsumer.prototype, 'sources', {
17249
17302
  get: function () {
17250
17303
  var sources = [];
17251
17304
  for (var i = 0; i < this._sections.length; i++) {
17252
- for (var j = 0; j < this._sections[i].consumer.sources.length; j++) {
17253
- sources.push(this._sections[i].consumer.sources[j]);
17305
+ // Read the getter once per section: for a nested indexed map it is
17306
+ // itself this getter, and re-reading it per item is exponential in the
17307
+ // nesting depth (CVE-2026-93749).
17308
+ var sectionSources = this._sections[i].consumer.sources;
17309
+ for (var j = 0; j < sectionSources.length; j++) {
17310
+ sources.push(sectionSources[j]);
17254
17311
  }
17255
17312
  }
17256
17313
  return sources;
@@ -17814,10 +17871,12 @@ SourceMapGenerator.prototype._serializeMappings =
17814
17871
 
17815
17872
  if (mapping.generatedLine !== previousGeneratedLine) {
17816
17873
  previousGeneratedColumn = 0;
17817
- while (mapping.generatedLine !== previousGeneratedLine) {
17818
- next += ';';
17819
- previousGeneratedLine++;
17820
- }
17874
+ // One ';' per skipped line. Build large gaps with a single flat
17875
+ // string: appending ';' one by one creates a rope node per line, which
17876
+ // can exhaust the heap for a big gap (CVE-2026-93749).
17877
+ var lineDelta = mapping.generatedLine - previousGeneratedLine;
17878
+ next += lineDelta === 1 ? ';' : ';'.repeat(lineDelta);
17879
+ previousGeneratedLine = mapping.generatedLine;
17821
17880
  }
17822
17881
  else {
17823
17882
  if (i > 0) {
@@ -18031,6 +18090,13 @@ SourceNode.fromStringWithSourceMap =
18031
18090
  // to the SourceNode without any mapping.
18032
18091
  // Each line is added as separate string.
18033
18092
  while (lastGeneratedLine < mapping.generatedLine) {
18093
+ if (remainingLinesIndex >= remainingLines.length) {
18094
+ // The generated code is exhausted. Skip the rest instead of adding
18095
+ // empty lines one by one: a mapping may point far past the end of
18096
+ // the code (e.g. an indexed map section with a huge offset.line).
18097
+ lastGeneratedLine = mapping.generatedLine;
18098
+ break;
18099
+ }
18034
18100
  node.add(shiftNextLine());
18035
18101
  lastGeneratedLine++;
18036
18102
  }
@@ -19045,7 +19111,7 @@ module.exports = function () {
19045
19111
 
19046
19112
  /***/ },
19047
19113
 
19048
- /***/ 8900
19114
+ /***/ 1186
19049
19115
  (module, __webpack_exports__, __webpack_require__) {
19050
19116
 
19051
19117
  "use strict";
@@ -68781,18 +68847,53 @@ function evaluate(source, scope = {}) {
68781
68847
  return new Function(...names, `return (${source})`)(...values);
68782
68848
  }
68783
68849
  /**
68784
- * Advance a `Point` by the `consumed` substring that follows it, returning the
68785
- * point at the consumed string's end. Lets split-out sub-nodes carry accurate
68786
- * document positions so downstream offset shifting stays correct.
68850
+ * Width each container prefix adds back per line of `value`, aligned from the line end since a
68851
+ * tab in the prefix is expanded to spaces in `value`. None (a verbatim slice) when the source
68852
+ * is missing or doesn't line up.
68787
68853
  */
68788
- const pointAfter = (start, consumed) => {
68789
- const newlineIndex = consumed.lastIndexOf('\n');
68790
- const newlineCount = newlineIndex === -1 ? 0 : consumed.split('\n').length - 1;
68791
- return {
68792
- line: start.line + newlineCount,
68793
- // Same line → advance the base column; a later line → column is the run since its newline.
68794
- column: newlineCount === 0 ? start.column + consumed.length : consumed.length - newlineIndex,
68795
- offset: (start.offset ?? 0) + consumed.length,
68854
+ const containerPrefixWidths = (valueLines, source, position) => {
68855
+ const sourceLines = source?.slice(position.start.offset, position.end.offset).split('\n');
68856
+ const linesUp = sourceLines?.length === valueLines.length &&
68857
+ sourceLines.every((line, i) => line.endsWith(valueLines[i].trimStart()));
68858
+ return valueLines.map((line, i) => (linesUp ? sourceLines[i].length - line.length : 0));
68859
+ };
68860
+ /**
68861
+ * Maps offsets in a node's `value` to points in `source`. Containers strip a prefix from every
68862
+ * continuation line of `value`, so a plain offset shift drifts by line (CX-4028).
68863
+ */
68864
+ const createValueToSourceMapper = (position, value, source) => {
68865
+ if (position?.start.offset === undefined)
68866
+ return null;
68867
+ const { start } = position;
68868
+ const valueLines = value.split('\n');
68869
+ const prefixWidths = containerPrefixWidths(valueLines, source, position);
68870
+ const lines = [];
68871
+ let valueStart = 0;
68872
+ let sourceStart = start.offset;
68873
+ valueLines.forEach((line, i) => {
68874
+ lines.push({ prefixWidth: prefixWidths[i], sourceStart, valueStart });
68875
+ valueStart += line.length + 1;
68876
+ sourceStart += prefixWidths[i] + line.length + 1;
68877
+ });
68878
+ return valueOffset => {
68879
+ // Binary search: a large table maps one point per node.
68880
+ let low = 0;
68881
+ let high = lines.length - 1;
68882
+ while (low < high) {
68883
+ const mid = Math.ceil((low + high) / 2);
68884
+ if (lines[mid].valueStart <= valueOffset)
68885
+ low = mid;
68886
+ else
68887
+ high = mid - 1;
68888
+ }
68889
+ const { prefixWidth, sourceStart: lineSourceStart, valueStart: lineValueStart } = lines[low];
68890
+ // Clamped: an expanded tab can make a leading-whitespace offset fall before the line.
68891
+ const sourceColumn = Math.max(0, prefixWidth + valueOffset - lineValueStart);
68892
+ return {
68893
+ line: start.line + low,
68894
+ column: (low === 0 ? start.column : 1) + sourceColumn,
68895
+ offset: lineSourceStart + sourceColumn,
68896
+ };
68796
68897
  };
68797
68898
  };
68798
68899
  /**
@@ -69455,7 +69556,9 @@ const processBlockquote = (node, index, parent, isMdxish = false) => {
69455
69556
  }
69456
69557
  Object.assign(node, {
69457
69558
  type: NodeTypes.callout,
69559
+ // Keep the blockquote's data, e.g. the `reparseSource` its positions index into.
69458
69560
  data: {
69561
+ ...node.data,
69459
69562
  hName: 'Callout',
69460
69563
  hProperties: {
69461
69564
  icon,
@@ -69488,14 +69591,55 @@ const calloutTransformer = ({ isMdxish = false } = {}) => {
69488
69591
  };
69489
69592
  /* harmony default export */ const callouts = (calloutTransformer);
69490
69593
 
69594
+ ;// ./processor/transform/mdxish/reparse-source.ts
69595
+ /**
69596
+ * Mark re-parsed subtree roots with the string their positions index into, so consumers
69597
+ * slicing by offset use the right source (see `Data.reparseSource`). Only roots are
69598
+ * stamped; descendants resolve via their nearest stamped ancestor. Roots a deeper
69599
+ * re-parse already stamped keep their own string.
69600
+ */
69601
+ const stampReparseSource = (roots, reparseSource) => {
69602
+ roots.forEach(root => {
69603
+ if (!root.data?.reparseSource)
69604
+ root.data = { ...root.data, reparseSource };
69605
+ });
69606
+ };
69607
+ /**
69608
+ * Splice `replacements` over `parent.children[index]`. They take over the replaced node's
69609
+ * position, so they also take over the coordinate space that position indexes into.
69610
+ */
69611
+ const replaceInheritingReparseSource = (parent, index, replacements) => {
69612
+ const replacedSource = parent.children[index]?.data?.reparseSource;
69613
+ if (replacedSource)
69614
+ stampReparseSource(replacements, replacedSource);
69615
+ parent.children.splice(index, 1, ...replacements);
69616
+ };
69617
+ /**
69618
+ * The string `node`'s offsets index into: the nearest stamp on it or an ancestor, else the
69619
+ * document source.
69620
+ */
69621
+ const resolveReparseSource = (node, ancestors, documentSource) => {
69622
+ if (node.data?.reparseSource)
69623
+ return node.data.reparseSource;
69624
+ for (let i = ancestors.length - 1; i >= 0; i -= 1) {
69625
+ const { reparseSource } = ancestors[i].data ?? {};
69626
+ if (reparseSource)
69627
+ return reparseSource;
69628
+ }
69629
+ return documentSource;
69630
+ };
69631
+
69491
69632
  ;// ./processor/transform/code-tabs.ts
69492
69633
 
69493
69634
 
69635
+
69494
69636
  const isCode = (node) => node?.type === 'code';
69495
69637
  const codeTabsTransformer = ({ copyButtons } = {}) => (tree) => {
69496
69638
  visit(tree, 'code', (node) => {
69497
69639
  const { lang, meta, value } = node;
69498
69640
  node.data = {
69641
+ // Ensure positions are passed through
69642
+ ...node.data,
69499
69643
  hProperties: { lang, meta, value, copyButtons },
69500
69644
  };
69501
69645
  });
@@ -69542,6 +69686,9 @@ const codeTabsTransformer = ({ copyButtons } = {}) => (tree) => {
69542
69686
  end: children[children.length - 1].position.end,
69543
69687
  },
69544
69688
  };
69689
+ // Adjacent siblings share a coordinate space, so the wrapper takes the first block's.
69690
+ if (node.data?.reparseSource)
69691
+ stampReparseSource([codeTabs], node.data.reparseSource);
69545
69692
  parent.children.splice(index, children.length, codeTabs);
69546
69693
  });
69547
69694
  return tree;
@@ -92176,11 +92323,12 @@ const HTMLBlock = ({ children = '', html: htmlProp, runScripts, safeMode: safeMo
92176
92323
  // In MDX mode, safeMode is passed in as a boolean from JSX parsing
92177
92324
  // In MDXish mode, safeMode comes in as a string from HAST props
92178
92325
  const safeMode = typeof safeModeRaw !== 'boolean' ? safeModeRaw === 'true' : safeModeRaw;
92179
- const [cleanedHtml, exec] = extractScripts(html);
92326
+ // Memoised so `exec` is stable across rerenders and the effect below only runs when the html changes.
92327
+ const [cleanedHtml, exec] = (0,external_react_.useMemo)(() => extractScripts(html), [html]);
92180
92328
  (0,external_react_.useEffect)(() => {
92181
- if (typeof window !== 'undefined' && typeof runScripts === 'boolean' && runScripts && !safeMode)
92329
+ if (typeof window !== 'undefined' && typeof runScripts === 'boolean' && runScripts)
92182
92330
  exec();
92183
- }, [runScripts, safeMode, exec]);
92331
+ }, [runScripts, exec]);
92184
92332
  if (nonStringChildren) {
92185
92333
  // Fail soft: a non-string child (e.g. JSX that wasn't serialized back to a
92186
92334
  // raw string) should never throw, so render the child nodes directly
@@ -100237,6 +100385,7 @@ const walkTags = (html, handlers) => {
100237
100385
  };
100238
100386
 
100239
100387
  ;// ./processor/transform/mdxish/tables/utils.ts
100388
+
100240
100389
  const tableTags = new Set([
100241
100390
  'thead',
100242
100391
  'tbody',
@@ -100255,6 +100404,9 @@ const tableTags = new Set([
100255
100404
  const unwrapParagraphNodes = (children) => {
100256
100405
  return children.flatMap(child => {
100257
100406
  if (child.type === 'paragraph' && 'children' in child && Array.isArray(child.children)) {
100407
+ // The unwrap drops a stamped root, so its children inherit that coordinate space.
100408
+ if (child.data?.reparseSource)
100409
+ stampReparseSource(child.children, child.data.reparseSource);
100258
100410
  return child.children;
100259
100411
  }
100260
100412
  return [child];
@@ -100850,9 +101002,10 @@ const findTableRanges = (html) => {
100850
101002
  * The surrounding raw HTML (the wrapper's open/close tags) would be re-nested
100851
101003
  * around the parsed tables by rehype-raw.
100852
101004
  *
100853
- * Returns null when there is no wrapped table to extract.
101005
+ * Returns null when there is no wrapped table to extract. `source` is the string the node's
101006
+ * positions index into, so the parts' positions survive stripped container prefixes.
100854
101007
  */
100855
- const splitHtmlWithNestedTables = (node) => {
101008
+ const splitHtmlWithNestedTables = (node, source) => {
100856
101009
  const { value } = node;
100857
101010
  // This is a top-level table, so we don't need to split it
100858
101011
  if (TOP_LEVEL_TABLE_TAG_RE.test(value))
@@ -100865,13 +101018,11 @@ const splitHtmlWithNestedTables = (node) => {
100865
101018
  const ranges = findTableRanges(value);
100866
101019
  if (ranges.length === 0)
100867
101020
  return null;
100868
- const base = node.position?.start;
101021
+ const toSourcePoint = createValueToSourceMapper(node.position, value, source);
100869
101022
  const sliceToHtml = (from, to) => ({
100870
101023
  type: 'html',
100871
101024
  value: value.slice(from, to),
100872
- ...(base && {
100873
- position: { start: pointAfter(base, value.slice(0, from)), end: pointAfter(base, value.slice(0, to)) },
100874
- }),
101025
+ ...(toSourcePoint && { position: { start: toSourcePoint(from), end: toSourcePoint(to) } }),
100875
101026
  });
100876
101027
  const parts = [];
100877
101028
  let cursor = 0;
@@ -100906,6 +101057,8 @@ const splitHtmlWithNestedTables = (node) => {
100906
101057
 
100907
101058
 
100908
101059
 
101060
+
101061
+
100909
101062
 
100910
101063
 
100911
101064
 
@@ -100958,7 +101111,7 @@ const tableRepairs = [
100958
101111
  * The plugins in the processor allows parsing markdown & special syntax inside the table cells
100959
101112
  * After parsing, we need to update the node positions
100960
101113
  */
100961
- const parseTableNode = (processor, node, repair) => {
101114
+ const parseTableNode = (processor, node, source, repair) => {
100962
101115
  let parsed;
100963
101116
  try {
100964
101117
  parsed = processor.runSync(processor.parse(node.value));
@@ -100972,19 +101125,18 @@ const parseTableNode = (processor, node, repair) => {
100972
101125
  if (repair) {
100973
101126
  remapPositionsThroughLayers(parsed, repair.originalSource, repair.layers);
100974
101127
  }
100975
- // The subparser produces positions relative to `node.value`; shift them by
100976
- // the outer node's offset/line so consumers can slice the full source.
100977
- const baseOffset = node.position?.start?.offset ?? 0;
100978
- const baseLine = (node.position?.start?.line ?? 1) - 1;
101128
+ // The subparser produces positions relative to `node.value`; map them into
101129
+ // the outer source so consumers can slice it.
101130
+ const toSourcePoint = createValueToSourceMapper(node.position, repair?.originalSource ?? node.value, source);
101131
+ if (!toSourcePoint)
101132
+ return parsed;
100979
101133
  visit(parsed, child => {
100980
- if (child.position?.start) {
100981
- child.position.start.offset = (child.position.start.offset ?? 0) + baseOffset;
100982
- child.position.start.line += baseLine;
100983
- }
100984
- if (child.position?.end) {
100985
- child.position.end.offset = (child.position.end.offset ?? 0) + baseOffset;
100986
- child.position.end.line += baseLine;
100987
- }
101134
+ if (!child.position)
101135
+ return;
101136
+ child.position = {
101137
+ start: toSourcePoint(child.position.start.offset ?? 0),
101138
+ end: toSourcePoint(child.position.end.offset ?? 0),
101139
+ };
100988
101140
  });
100989
101141
  return parsed;
100990
101142
  };
@@ -101014,12 +101166,10 @@ const hasFlowContent = (nodes) => {
101014
101166
  return nodes.some(node => !phrasing(node) && node.type !== 'paragraph');
101015
101167
  };
101016
101168
  /**
101017
- * Process a Table node: re-parse text-only cell content, then output as
101018
- * a markdown table (phrasing-only) or keep as JSX <Table> (has flow content).
101169
+ * Process a Table node: re-parse text-only cell content, then return it as
101170
+ * a markdown table (phrasing-only) or keep it as JSX <Table> (has flow content).
101019
101171
  */
101020
- const processTableNode = (node, index, parent, documentPosition) => {
101021
- if (node.name !== 'Table' && node.name !== 'table')
101022
- return;
101172
+ const processTableNode = (node, documentPosition) => {
101023
101173
  const position = documentPosition ?? node.position;
101024
101174
  const { align: alignAttr } = getAttrs(node);
101025
101175
  const align = Array.isArray(alignAttr) ? alignAttr : null;
@@ -101046,6 +101196,8 @@ const processTableNode = (node, index, parent, documentPosition) => {
101046
101196
  try {
101047
101197
  const parsed = tableNodeProcessor.runSync(tableNodeProcessor.parse(textContent));
101048
101198
  if (parsed.children.length > 0) {
101199
+ // Positions index into the extracted text rather than the table source (CX-4004).
101200
+ stampReparseSource(parsed.children, textContent);
101049
101201
  cell.children = parsed.children;
101050
101202
  if (hasFlowContent(parsed.children)) {
101051
101203
  tableHasFlowContent = true;
@@ -101088,11 +101240,7 @@ const processTableNode = (node, index, parent, documentPosition) => {
101088
101240
  : unwrapParagraphNodes(el.children);
101089
101241
  el.children = removeWhitespaceOnlyTextNodes(unwrapped);
101090
101242
  });
101091
- parent.children[index] = {
101092
- ...node,
101093
- position,
101094
- };
101095
- return;
101243
+ return { ...node, position };
101096
101244
  }
101097
101245
  // All cells are phrasing-only — convert to markdown table
101098
101246
  const children = [];
@@ -101170,7 +101318,7 @@ const processTableNode = (node, index, parent, documentPosition) => {
101170
101318
  const alignArray = align && columnCount > 0
101171
101319
  ? align.slice(0, columnCount).concat(new Array(Math.max(0, columnCount - align.length)).fill(null))
101172
101320
  : new Array(columnCount).fill(null);
101173
- const mdNode = {
101321
+ return {
101174
101322
  align: alignArray,
101175
101323
  type: 'table',
101176
101324
  position,
@@ -101178,14 +101326,13 @@ const processTableNode = (node, index, parent, documentPosition) => {
101178
101326
  // Remember the author's spelling so the serializer can write `<table>` back instead of `<Table>`
101179
101327
  ...(node.name === 'table' && { data: { lowercaseTable: true } }),
101180
101328
  };
101181
- parent.children[index] = mdNode;
101182
101329
  };
101183
101330
  /**
101184
101331
  * Apply `tableRepairs` cumulatively, re-parsing after every change and stopping
101185
101332
  * once the accumulated result parses. Each layer's inserts are relative to the
101186
101333
  * string that repair received, so they stay ordered for position remapping.
101187
101334
  */
101188
- const repairAndReparse = (node) => {
101335
+ const repairAndReparse = (node, source) => {
101189
101336
  let repairedValue = node.value;
101190
101337
  const layers = [];
101191
101338
  let parsed;
@@ -101195,11 +101342,45 @@ const repairAndReparse = (node) => {
101195
101342
  return false;
101196
101343
  repairedValue = value;
101197
101344
  layers.push(inserts);
101198
- parsed = parseTableNode(tableNodeProcessor, { ...node, value: repairedValue }, { layers, originalSource: node.value });
101345
+ parsed = parseTableNode(tableNodeProcessor, { ...node, value: repairedValue }, source, {
101346
+ layers,
101347
+ originalSource: node.value,
101348
+ });
101199
101349
  return Boolean(parsed);
101200
101350
  });
101201
101351
  return parsed;
101202
101352
  };
101353
+ /**
101354
+ * Re-parse a table html node and replace it with a markdown / JSX table, or the
101355
+ * fallback parse's fragments for a lowercase table mdxjs rejects.
101356
+ */
101357
+ const replaceTableHtml = (node, index, parent, source) => {
101358
+ // Because the processor uses remarkMdx, it is stricter in what it accepts
101359
+ // and only accepts valid MDX syntax in the table node. To get around that,
101360
+ // fall back to the cumulative repairs when the first parse fails.
101361
+ const parsed = parseTableNode(tableNodeProcessor, node, source) ?? repairAndReparse(node, source);
101362
+ if (parsed) {
101363
+ // If the table is parsed successfully, we can now process it further
101364
+ // to build on the markdown / JSX table
101365
+ visit(parsed, isMDXElement, (tableNode) => {
101366
+ if (tableNode.name !== 'Table' && tableNode.name !== 'table')
101367
+ return undefined;
101368
+ replaceInheritingReparseSource(parent, index, [processTableNode(tableNode, node.position)]);
101369
+ return (/* inlined export .EXIT */false);
101370
+ });
101371
+ }
101372
+ else if (node.value.startsWith('<table')) {
101373
+ // If the parsing still fails, give an opportunity to the fallback parser
101374
+ // without remarkMdx to process lowercase tables as it's likely to not
101375
+ // have needed MDX parsing anyway
101376
+ const fallback = parseTableNode(fallbackTableNodeProcessor, node, source);
101377
+ if (!fallback || fallback.children.length <= 1)
101378
+ return;
101379
+ replaceInheritingReparseSource(parent, index, fallback.children);
101380
+ }
101381
+ // Otherwise, there's no point in trying to parse the table content further
101382
+ // More repairs are needed in that case
101383
+ };
101203
101384
  /**
101204
101385
  * Converts JSX Table elements to markdown table nodes and re-parses markdown in cells.
101205
101386
  *
@@ -101211,24 +101392,26 @@ const repairAndReparse = (node) => {
101211
101392
  * When cell content contains block-level nodes (callouts, code blocks, etc.), the table
101212
101393
  * is kept as a JSX <Table> element so that remarkRehype can properly handle the flow content.
101213
101394
  */
101214
- const mdxishTables = () => tree => {
101395
+ const mdxishTables = () => (tree, file) => {
101396
+ // Positions index into the document unless a re-parse stamped another source.
101397
+ const documentSource = file?.value ? String(file.value) : undefined;
101215
101398
  // Pre-pass: lift `<table>`s wrapped in a raw HTML block out into their own
101216
101399
  // html nodes so the main pass below treats them like top-level tables.
101217
- visit(tree, 'html', (_node, index, parent) => {
101218
- const node = _node;
101219
- if (typeof index !== 'number' || !parent || !('children' in parent))
101400
+ visitParents(tree, 'html', (node, ancestors) => {
101401
+ const parent = ancestors[ancestors.length - 1];
101402
+ if (!parent)
101220
101403
  return;
101221
- const parts = splitHtmlWithNestedTables(node);
101404
+ const parts = splitHtmlWithNestedTables(node, resolveReparseSource(node, ancestors, documentSource));
101222
101405
  if (!parts)
101223
101406
  return;
101224
101407
  // The inserted parts can't re-trigger a split (table parts start with
101225
101408
  // `<table`; the wrapper slices hold no table), so plain in-place splicing
101226
101409
  // visits each once without looping.
101227
- parent.children.splice(index, 1, ...parts);
101410
+ replaceInheritingReparseSource(parent, parent.children.indexOf(node), parts);
101228
101411
  });
101229
- visit(tree, 'html', (_node, index, parent) => {
101230
- const node = _node;
101231
- if (typeof index !== 'number' || !parent || !('children' in parent))
101412
+ visitParents(tree, 'html', (node, ancestors) => {
101413
+ const parent = ancestors[ancestors.length - 1];
101414
+ if (!parent)
101232
101415
  return;
101233
101416
  if (!node.value.startsWith('<Table') && !node.value.startsWith('<table'))
101234
101417
  return;
@@ -101236,33 +101419,8 @@ const mdxishTables = () => tree => {
101236
101419
  // paragraph. Leave them untouched so rehype-raw can reassemble the table.
101237
101420
  if (parent.type === 'paragraph')
101238
101421
  return;
101239
- // Because the processor uses remarkMdx, it is stricter in what it accepts
101240
- // and only accepts valid MDX syntax in the table node. To get around that,
101241
- // fall back to the cumulative repairs when the first parse fails.
101242
- const parsed = parseTableNode(tableNodeProcessor, node) ?? repairAndReparse(node);
101243
- if (parsed) {
101244
- // If the table is parsed successfully, we can now process it further
101245
- // to build on the markdown / JSX table
101246
- visit(parsed, isMDXElement, (tableNode) => {
101247
- if (tableNode.name !== 'Table' && tableNode.name !== 'table')
101248
- return undefined;
101249
- processTableNode(tableNode, index, parent, node.position);
101250
- return (/* inlined export .EXIT */false);
101251
- });
101252
- }
101253
- else if (node.value.startsWith('<table')) {
101254
- // If the parsing still fails, give an opportunity to the fallback parser
101255
- // without remarkMdx to process lowercase tables as it's likely to not
101256
- // have needed MDX parsing anyway
101257
- const fallback = parseTableNode(fallbackTableNodeProcessor, node);
101258
- if (!fallback || fallback.children.length <= 1)
101259
- return;
101260
- parent.children.splice(index, 1, ...fallback.children);
101261
- }
101262
- // Otherwise, there's no point in trying to parse the table content further
101263
- // More repairs are needed in that case
101422
+ replaceTableHtml(node, parent.children.indexOf(node), parent, resolveReparseSource(node, ancestors, documentSource));
101264
101423
  });
101265
- return tree;
101266
101424
  };
101267
101425
  /* harmony default export */ const mdxish_tables = (mdxishTables);
101268
101426
 
@@ -122425,32 +122583,6 @@ const mdxComponentHandlers = {
122425
122583
  [NodeTypes.htmlBlock]: htmlBlockHandler,
122426
122584
  };
122427
122585
 
122428
- ;// ./processor/plugin/safe-mode-html-blocks.ts
122429
-
122430
-
122431
- /**
122432
- * Whether a tag name renders through the `HTMLBlock` component. Mirrors the
122433
- * separator-agnostic, case-insensitive lookup in `getComponentName` and the
122434
- * rehype-react component map, so `html-block`, `HTMLBlock`, `HtmlBlock` and
122435
- * `htmlblock` all match.
122436
- */
122437
- const isHtmlBlockTag = (tagName) => toPascalCase(tagName).toLowerCase() === 'htmlblock';
122438
- /**
122439
- * Forces `safeMode` on every element that renders through `HTMLBlock` so it is
122440
- * shown as escaped text instead of injected.
122441
- *
122442
- * Runs last, so it sees every producer of the element — `[block:html]`,
122443
- * `<HTMLBlock>` with a template body or an `html` attribute, and a literal
122444
- * `<html-block>` — and it is unconditional so an author-supplied
122445
- * `safeMode="false"` can't opt out.
122446
- */
122447
- const enforceSafeModeHtmlBlocks = () => tree => {
122448
- visit(tree, 'element', node => {
122449
- if (isHtmlBlockTag(node.tagName))
122450
- node.properties.safeMode = 'true';
122451
- });
122452
- };
122453
-
122454
122586
  ;// ./processor/transform/mdxish/anchor-to-jsx.ts
122455
122587
  /* unused harmony import specifier */ var anchor_to_jsx_visit;
122456
122588
  /* unused harmony import specifier */ var anchor_to_jsx_NodeTypes;
@@ -122490,7 +122622,6 @@ const mdxishAnchorToJsx = () => tree => {
122490
122622
 
122491
122623
  ;// ./processor/transform/mdxish/callout-to-jsx.ts
122492
122624
  /* unused harmony import specifier */ var callout_to_jsx_visit;
122493
- /* unused harmony import specifier */ var callout_to_jsx_defaultIcons;
122494
122625
  /* unused harmony import specifier */ var callout_to_jsx_themes;
122495
122626
  /* unused harmony import specifier */ var callout_to_jsx_NodeTypes;
122496
122627
  /* unused harmony import specifier */ var callout_to_jsx_toAttributes;
@@ -122513,9 +122644,9 @@ const mdxishCalloutToJsx = () => tree => {
122513
122644
  callout_to_jsx_visit(tree, callout_to_jsx_NodeTypes.callout, (node, index, parent) => {
122514
122645
  if (!parent || index === undefined)
122515
122646
  return;
122516
- let { icon, theme } = node.data.hProperties;
122517
- if (!icon && theme)
122518
- icon = callout_to_jsx_defaultIcons[theme];
122647
+ // No default-icon fill: an empty `icon` means the author removed it
122648
+ const { icon } = node.data.hProperties;
122649
+ let { theme } = node.data.hProperties;
122519
122650
  if (!theme && icon)
122520
122651
  theme = callout_to_jsx_themes[icon] || 'default';
122521
122652
  // Mutate in place to keep the children array identity so nested callouts in
@@ -123066,18 +123197,6 @@ const getInlineMdProcessor = ({ safeMode = false } = {}) => {
123066
123197
  }
123067
123198
  return processor;
123068
123199
  };
123069
- /**
123070
- * Mark re-parsed subtree roots with the string their positions index into, so consumers
123071
- * slicing by offset use the right source (see `Data.reparseSource`). Only roots are
123072
- * stamped; descendants resolve via their nearest stamped ancestor. Roots a deeper
123073
- * re-parse already stamped keep their own string.
123074
- */
123075
- const stampReparseSource = (roots, reparseSource) => {
123076
- roots.forEach(root => {
123077
- if (!root.data?.reparseSource)
123078
- root.data = { ...root.data, reparseSource };
123079
- });
123080
- };
123081
123200
  /**
123082
123201
  * True when a tag name starts with an uppercase letter — ReadMe's marker for
123083
123202
  * a custom MDX component (vs a lowercase HTML tag).
@@ -123164,6 +123283,7 @@ function findBalancedClosingTagIndex(content, tag) {
123164
123283
 
123165
123284
 
123166
123285
 
123286
+
123167
123287
  /**
123168
123288
  * Parse the body of an inline component as phrasing content. Remark always
123169
123289
  * wraps bare text in a paragraph; flatten that single-paragraph wrapper so
@@ -123415,6 +123535,7 @@ function terminateHtmlFlowBlocks(content) {
123415
123535
 
123416
123536
 
123417
123537
 
123538
+
123418
123539
  // Matches a JSX attribute expression (e.g. `key={i}`) anywhere in a string. One name
123419
123540
  // char suffices for existence — `[\w-]+` backtracked quadratically over huge attributes.
123420
123541
  const NESTED_ATTR_EXPRESSION_RE = /[\w-]\s*=\s*\{/;
@@ -123496,26 +123617,12 @@ const parseSibling = (parent, index, sibling, safeMode, promoted) => {
123496
123617
  }
123497
123618
  };
123498
123619
  // Ends the position at `consumedLength` so the component doesn't claim trailing
123499
- // content the tokenizer swallowed into the same html node. Measured against the source
123500
- // when available: each value line is its source line minus the container prefix
123501
- // (list indent, `> `) micromark stripped, so those prefixes are counted back in.
123620
+ // content the tokenizer swallowed into the same html node.
123502
123621
  const positionEndingAtConsumed = (nodePosition, value, consumedLength, source) => {
123503
- if (!nodePosition?.start)
123622
+ const toSourcePoint = createValueToSourceMapper(nodePosition, value, source);
123623
+ if (!nodePosition || !toSourcePoint)
123504
123624
  return nodePosition;
123505
- const consumedValue = value.slice(0, consumedLength);
123506
- const sourceLines = source?.slice(nodePosition.start.offset, nodePosition.end?.offset).split('\n');
123507
- const valueLines = value.split('\n');
123508
- if (!sourceLines || sourceLines.length !== valueLines.length) {
123509
- return { start: nodePosition.start, end: pointAfter(nodePosition.start, consumedValue) };
123510
- }
123511
- const consumedLines = consumedValue.split('\n');
123512
- const lastLineIndex = consumedLines.length - 1;
123513
- const prefixWidth = sourceLines[lastLineIndex].length - valueLines[lastLineIndex].length;
123514
- const consumedSource = [
123515
- ...sourceLines.slice(0, lastLineIndex),
123516
- sourceLines[lastLineIndex].slice(0, prefixWidth + consumedLines[lastLineIndex].length),
123517
- ].join('\n');
123518
- return { start: nodePosition.start, end: pointAfter(nodePosition.start, consumedSource) };
123625
+ return { start: nodePosition.start, end: toSourcePoint(consumedLength) };
123519
123626
  };
123520
123627
  const createComponentNode = ({ tag, attributes, children, startPosition, endPosition, }) => ({
123521
123628
  type: 'mdxJsxFlowElement',
@@ -123527,14 +123634,6 @@ const createComponentNode = ({ tag, attributes, children, startPosition, endPosi
123527
123634
  end: endPosition?.end ?? startPosition?.end,
123528
123635
  },
123529
123636
  });
123530
- // The promoted node takes over the html node's position, so it also takes over the
123531
- // coordinate space that position belongs to.
123532
- const substituteNodeWithMdxNode = (parent, index, mdxNode) => {
123533
- const replacedSource = parent.children[index]?.data?.reparseSource;
123534
- if (replacedSource)
123535
- stampReparseSource([mdxNode], replacedSource);
123536
- parent.children.splice(index, 1, mdxNode);
123537
- };
123538
123637
  /**
123539
123638
  * Transform PascalCase HTML nodes into mdxJsxFlowElement nodes.
123540
123639
  *
@@ -123633,7 +123732,7 @@ function promoteComponentBlocks(tree, safeMode, source) {
123633
123732
  ? positionEndingAtConsumed(node.position, value, leadingWhitespace + openingTagEnd, source)
123634
123733
  : node.position,
123635
123734
  });
123636
- substituteNodeWithMdxNode(parent, index, componentNode);
123735
+ replaceInheritingReparseSource(parent, index, [componentNode]);
123637
123736
  if (remainingContent) {
123638
123737
  parseSibling(parent, index, remainingContent, safeMode, promoted);
123639
123738
  }
@@ -123684,7 +123783,7 @@ function promoteComponentBlocks(tree, safeMode, source) {
123684
123783
  startPosition: node.position,
123685
123784
  endPosition,
123686
123785
  });
123687
- substituteNodeWithMdxNode(parent, index, componentNode);
123786
+ replaceInheritingReparseSource(parent, index, [componentNode]);
123688
123787
  // The unwrap reparented the children out of their paragraph, so re-walk them
123689
123788
  // since the children HTML may contain promotable syntax (e.g. `{…}`-attr tags)
123690
123789
  if (unwrappedSoleParagraph) {
@@ -126173,7 +126272,7 @@ const parseApiHeaderTitle = (text) => {
126173
126272
  * Transform a magicBlock node into final MDAST nodes.
126174
126273
  */
126175
126274
  function transformMagicBlock(blockType, data, rawValue, options = {}) {
126176
- const { compatibilityMode = false, hardBreaks = true } = options;
126275
+ const { compatibilityMode = false, hardBreaks = true, safeMode = false } = options;
126177
126276
  // Handle empty data by returning placeholder nodes for known block types
126178
126277
  // This allows the editor to show appropriate placeholder UI instead of nothing
126179
126278
  if (Object.keys(data).length < 1) {
@@ -126391,7 +126490,7 @@ function transformMagicBlock(blockType, data, rawValue, options = {}) {
126391
126490
  wrapPinnedBlocks({
126392
126491
  data: {
126393
126492
  hName: 'html-block',
126394
- hProperties: { html: htmlJson.html, runScripts: compatibilityMode },
126493
+ hProperties: { html: htmlJson.html, runScripts: compatibilityMode, safeMode },
126395
126494
  },
126396
126495
  type: 'html-block',
126397
126496
  }, data),
@@ -126532,6 +126631,7 @@ const magicBlockTransformer = (options = {}) => tree => {
126532
126631
 
126533
126632
 
126534
126633
 
126634
+
126535
126635
  // `<HTMLBlock …>{`…`}</HTMLBlock>` embedded inside a raw HTML block (e.g. a
126536
126636
  // single-line `<div>…</div>`). CommonMark slurps the whole div as one `html`
126537
126637
  // node, so the tokenizer never sees the HTMLBlock — we recover it here.
@@ -126638,7 +126738,8 @@ const mdxishHtmlBlocks = () => tree => {
126638
126738
  return;
126639
126739
  const exprChild = element.children.find(child => child.type === 'mdxFlowExpression' || child.type === 'mdxTextExpression');
126640
126740
  const openingTagIndent = (element.position?.start.column ?? 1) - 1;
126641
- parent.children[index] = createHtmlBlockNode(formatHtmlForMdxish(extractTemplateLiteral(exprChild?.value), openingTagIndent), element.position, toRunScripts(jsxAttr(element, 'runScripts')), jsxAttr(element, 'safeMode'));
126741
+ const htmlBlock = createHtmlBlockNode(formatHtmlForMdxish(extractTemplateLiteral(exprChild?.value), openingTagIndent), element.position, toRunScripts(jsxAttr(element, 'runScripts')), jsxAttr(element, 'safeMode'));
126742
+ replaceInheritingReparseSource(parent, index, [htmlBlock]);
126642
126743
  });
126643
126744
  // Shape 2: raw HTML blob.
126644
126745
  visit(tree, 'html', (node, index, parent) => {
@@ -126646,7 +126747,7 @@ const mdxishHtmlBlocks = () => tree => {
126646
126747
  return;
126647
126748
  const replacement = splitRawHtmlBlocks(node);
126648
126749
  if (replacement)
126649
- parent.children.splice(index, 1, ...replacement);
126750
+ replaceInheritingReparseSource(parent, index, replacement);
126650
126751
  });
126651
126752
  };
126652
126753
  /* harmony default export */ const mdxish_html_blocks = (mdxishHtmlBlocks);
@@ -126733,6 +126834,7 @@ const hasOnlyWidthAttributes = (cell) => {
126733
126834
 
126734
126835
 
126735
126836
 
126837
+
126736
126838
  function toImageAlign(value) {
126737
126839
  if (value === 'left' || value === 'center' || value === 'right') {
126738
126840
  return value;
@@ -126948,6 +127050,15 @@ const transformAnchor = (jsx) => {
126948
127050
  position: jsx.position,
126949
127051
  };
126950
127052
  };
127053
+ /**
127054
+ * Parses an image caption's markdown. The caption is re-parsed on its own, so the
127055
+ * children's positions index into it rather than the document.
127056
+ */
127057
+ const parseCaptionChildren = (caption) => {
127058
+ const { children } = lib_mdast(caption);
127059
+ stampReparseSource(children, caption);
127060
+ return children;
127061
+ };
126951
127062
  /**
126952
127063
  * Transforms an `<Image />` JSX element into an image-block MDAST node.
126953
127064
  * Normalizes attributes (align, border, width→sizing) and parses caption markdown into children.
@@ -126978,7 +127089,7 @@ const transformImage = (jsx) => {
126978
127089
  alt,
126979
127090
  border: toBool(border),
126980
127091
  caption,
126981
- children: caption ? lib_mdast(caption).children : [],
127092
+ children: caption ? parseCaptionChildren(caption) : [],
126982
127093
  className,
126983
127094
  framed: toBool(framed),
126984
127095
  height: height !== undefined ? String(height) : undefined,
@@ -127315,8 +127426,7 @@ const mdxishJsxToMdast = () => tree => {
127315
127426
  const newNode = transformer(node);
127316
127427
  if (!newNode)
127317
127428
  return;
127318
- // Replace the JSX node with the MDAST node
127319
- parent.children[index] = newNode;
127429
+ replaceInheritingReparseSource(parent, index, [newNode]);
127320
127430
  });
127321
127431
  // Inline JSX components (Anchor)
127322
127432
  visit(tree, 'mdxJsxTextElement', (node, index, parent) => {
@@ -128172,7 +128282,6 @@ function loadComponents() {
128172
128282
 
128173
128283
 
128174
128284
 
128175
-
128176
128285
 
128177
128286
 
128178
128287
  const defaultTransformers = [
@@ -128328,8 +128437,7 @@ function mdxish(mdContent, opts = {}) {
128328
128437
  .use(rehypeMdxishComponents, {
128329
128438
  components,
128330
128439
  processMarkdown: (markdown) => mdxish(markdown, opts),
128331
- })
128332
- .use(safeMode ? enforceSafeModeHtmlBlocks : undefined); // Force safeMode on every HTMLBlock; runs last so every producer is covered
128440
+ });
128333
128441
  const vfile = new VFile({ value: parserReadyContent });
128334
128442
  const mdast = processor.parse(parserReadyContent);
128335
128443
  const hast = processor.runSync(mdast, vfile);
@@ -129405,7 +129513,7 @@ module.exports = /*#__PURE__*/JSON.parse('{"*":["about","acceptCharset","accessK
129405
129513
  /******/ // startup
129406
129514
  /******/ // Load entry module and return exports
129407
129515
  /******/ // This entry module used 'module' so it can't be inlined
129408
- /******/ let __webpack_exports__ = __webpack_require__(8900);
129516
+ /******/ let __webpack_exports__ = __webpack_require__(1186);
129409
129517
  /******/ module.exports = __webpack_exports__;
129410
129518
  /******/
129411
129519
  /******/ })()