@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.
package/dist/main.node.js CHANGED
@@ -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
- /***/ 5789
19114
+ /***/ 6831
19049
19115
  (module, __webpack_exports__, __webpack_require__) {
19050
19116
 
19051
19117
  "use strict";
@@ -24741,11 +24807,12 @@ const HTMLBlock = ({ children = '', html: htmlProp, runScripts, safeMode: safeMo
24741
24807
  // In MDX mode, safeMode is passed in as a boolean from JSX parsing
24742
24808
  // In MDXish mode, safeMode comes in as a string from HAST props
24743
24809
  const safeMode = typeof safeModeRaw !== 'boolean' ? safeModeRaw === 'true' : safeModeRaw;
24744
- const [cleanedHtml, exec] = extractScripts(html);
24810
+ // Memoised so `exec` is stable across rerenders and the effect below only runs when the html changes.
24811
+ const [cleanedHtml, exec] = (0,external_react_.useMemo)(() => extractScripts(html), [html]);
24745
24812
  (0,external_react_.useEffect)(() => {
24746
- if (typeof window !== 'undefined' && typeof runScripts === 'boolean' && runScripts && !safeMode)
24813
+ if (typeof window !== 'undefined' && typeof runScripts === 'boolean' && runScripts)
24747
24814
  exec();
24748
- }, [runScripts, safeMode, exec]);
24815
+ }, [runScripts, exec]);
24749
24816
  if (nonStringChildren) {
24750
24817
  // Fail soft: a non-string child (e.g. JSX that wasn't serialized back to a
24751
24818
  // raw string) should never throw, so render the child nodes directly
@@ -75148,18 +75215,53 @@ function evaluate(source, scope = {}) {
75148
75215
  return new Function(...names, `return (${source})`)(...values);
75149
75216
  }
75150
75217
  /**
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.
75218
+ * Width each container prefix adds back per line of `value`, aligned from the line end since a
75219
+ * tab in the prefix is expanded to spaces in `value`. None (a verbatim slice) when the source
75220
+ * is missing or doesn't line up.
75154
75221
  */
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,
75222
+ const containerPrefixWidths = (valueLines, source, position) => {
75223
+ const sourceLines = source?.slice(position.start.offset, position.end.offset).split('\n');
75224
+ const linesUp = sourceLines?.length === valueLines.length &&
75225
+ sourceLines.every((line, i) => line.endsWith(valueLines[i].trimStart()));
75226
+ return valueLines.map((line, i) => (linesUp ? sourceLines[i].length - line.length : 0));
75227
+ };
75228
+ /**
75229
+ * Maps offsets in a node's `value` to points in `source`. Containers strip a prefix from every
75230
+ * continuation line of `value`, so a plain offset shift drifts by line (CX-4028).
75231
+ */
75232
+ const createValueToSourceMapper = (position, value, source) => {
75233
+ if (position?.start.offset === undefined)
75234
+ return null;
75235
+ const { start } = position;
75236
+ const valueLines = value.split('\n');
75237
+ const prefixWidths = containerPrefixWidths(valueLines, source, position);
75238
+ const lines = [];
75239
+ let valueStart = 0;
75240
+ let sourceStart = start.offset;
75241
+ valueLines.forEach((line, i) => {
75242
+ lines.push({ prefixWidth: prefixWidths[i], sourceStart, valueStart });
75243
+ valueStart += line.length + 1;
75244
+ sourceStart += prefixWidths[i] + line.length + 1;
75245
+ });
75246
+ return valueOffset => {
75247
+ // Binary search: a large table maps one point per node.
75248
+ let low = 0;
75249
+ let high = lines.length - 1;
75250
+ while (low < high) {
75251
+ const mid = Math.ceil((low + high) / 2);
75252
+ if (lines[mid].valueStart <= valueOffset)
75253
+ low = mid;
75254
+ else
75255
+ high = mid - 1;
75256
+ }
75257
+ const { prefixWidth, sourceStart: lineSourceStart, valueStart: lineValueStart } = lines[low];
75258
+ // Clamped: an expanded tab can make a leading-whitespace offset fall before the line.
75259
+ const sourceColumn = Math.max(0, prefixWidth + valueOffset - lineValueStart);
75260
+ return {
75261
+ line: start.line + low,
75262
+ column: (low === 0 ? start.column : 1) + sourceColumn,
75263
+ offset: lineSourceStart + sourceColumn,
75264
+ };
75163
75265
  };
75164
75266
  };
75165
75267
  /**
@@ -75822,7 +75924,9 @@ const processBlockquote = (node, index, parent, isMdxish = false) => {
75822
75924
  }
75823
75925
  Object.assign(node, {
75824
75926
  type: NodeTypes.callout,
75927
+ // Keep the blockquote's data, e.g. the `reparseSource` its positions index into.
75825
75928
  data: {
75929
+ ...node.data,
75826
75930
  hName: 'Callout',
75827
75931
  hProperties: {
75828
75932
  icon,
@@ -75855,14 +75959,55 @@ const calloutTransformer = ({ isMdxish = false } = {}) => {
75855
75959
  };
75856
75960
  /* harmony default export */ const callouts = (calloutTransformer);
75857
75961
 
75962
+ ;// ./processor/transform/mdxish/reparse-source.ts
75963
+ /**
75964
+ * Mark re-parsed subtree roots with the string their positions index into, so consumers
75965
+ * slicing by offset use the right source (see `Data.reparseSource`). Only roots are
75966
+ * stamped; descendants resolve via their nearest stamped ancestor. Roots a deeper
75967
+ * re-parse already stamped keep their own string.
75968
+ */
75969
+ const stampReparseSource = (roots, reparseSource) => {
75970
+ roots.forEach(root => {
75971
+ if (!root.data?.reparseSource)
75972
+ root.data = { ...root.data, reparseSource };
75973
+ });
75974
+ };
75975
+ /**
75976
+ * Splice `replacements` over `parent.children[index]`. They take over the replaced node's
75977
+ * position, so they also take over the coordinate space that position indexes into.
75978
+ */
75979
+ const replaceInheritingReparseSource = (parent, index, replacements) => {
75980
+ const replacedSource = parent.children[index]?.data?.reparseSource;
75981
+ if (replacedSource)
75982
+ stampReparseSource(replacements, replacedSource);
75983
+ parent.children.splice(index, 1, ...replacements);
75984
+ };
75985
+ /**
75986
+ * The string `node`'s offsets index into: the nearest stamp on it or an ancestor, else the
75987
+ * document source.
75988
+ */
75989
+ const resolveReparseSource = (node, ancestors, documentSource) => {
75990
+ if (node.data?.reparseSource)
75991
+ return node.data.reparseSource;
75992
+ for (let i = ancestors.length - 1; i >= 0; i -= 1) {
75993
+ const { reparseSource } = ancestors[i].data ?? {};
75994
+ if (reparseSource)
75995
+ return reparseSource;
75996
+ }
75997
+ return documentSource;
75998
+ };
75999
+
75858
76000
  ;// ./processor/transform/code-tabs.ts
75859
76001
 
75860
76002
 
76003
+
75861
76004
  const isCode = (node) => node?.type === 'code';
75862
76005
  const codeTabsTransformer = ({ copyButtons } = {}) => (tree) => {
75863
76006
  visit(tree, 'code', (node) => {
75864
76007
  const { lang, meta, value } = node;
75865
76008
  node.data = {
76009
+ // Ensure positions are passed through
76010
+ ...node.data,
75866
76011
  hProperties: { lang, meta, value, copyButtons },
75867
76012
  };
75868
76013
  });
@@ -75909,6 +76054,9 @@ const codeTabsTransformer = ({ copyButtons } = {}) => (tree) => {
75909
76054
  end: children[children.length - 1].position.end,
75910
76055
  },
75911
76056
  };
76057
+ // Adjacent siblings share a coordinate space, so the wrapper takes the first block's.
76058
+ if (node.data?.reparseSource)
76059
+ stampReparseSource([codeTabs], node.data.reparseSource);
75912
76060
  parent.children.splice(index, children.length, codeTabs);
75913
76061
  });
75914
76062
  return tree;
@@ -100291,6 +100439,7 @@ const walkTags = (html, handlers) => {
100291
100439
  };
100292
100440
 
100293
100441
  ;// ./processor/transform/mdxish/tables/utils.ts
100442
+
100294
100443
  const tableTags = new Set([
100295
100444
  'thead',
100296
100445
  'tbody',
@@ -100309,6 +100458,9 @@ const tableTags = new Set([
100309
100458
  const unwrapParagraphNodes = (children) => {
100310
100459
  return children.flatMap(child => {
100311
100460
  if (child.type === 'paragraph' && 'children' in child && Array.isArray(child.children)) {
100461
+ // The unwrap drops a stamped root, so its children inherit that coordinate space.
100462
+ if (child.data?.reparseSource)
100463
+ stampReparseSource(child.children, child.data.reparseSource);
100312
100464
  return child.children;
100313
100465
  }
100314
100466
  return [child];
@@ -100904,9 +101056,10 @@ const findTableRanges = (html) => {
100904
101056
  * The surrounding raw HTML (the wrapper's open/close tags) would be re-nested
100905
101057
  * around the parsed tables by rehype-raw.
100906
101058
  *
100907
- * Returns null when there is no wrapped table to extract.
101059
+ * Returns null when there is no wrapped table to extract. `source` is the string the node's
101060
+ * positions index into, so the parts' positions survive stripped container prefixes.
100908
101061
  */
100909
- const splitHtmlWithNestedTables = (node) => {
101062
+ const splitHtmlWithNestedTables = (node, source) => {
100910
101063
  const { value } = node;
100911
101064
  // This is a top-level table, so we don't need to split it
100912
101065
  if (TOP_LEVEL_TABLE_TAG_RE.test(value))
@@ -100919,13 +101072,11 @@ const splitHtmlWithNestedTables = (node) => {
100919
101072
  const ranges = findTableRanges(value);
100920
101073
  if (ranges.length === 0)
100921
101074
  return null;
100922
- const base = node.position?.start;
101075
+ const toSourcePoint = createValueToSourceMapper(node.position, value, source);
100923
101076
  const sliceToHtml = (from, to) => ({
100924
101077
  type: 'html',
100925
101078
  value: value.slice(from, to),
100926
- ...(base && {
100927
- position: { start: pointAfter(base, value.slice(0, from)), end: pointAfter(base, value.slice(0, to)) },
100928
- }),
101079
+ ...(toSourcePoint && { position: { start: toSourcePoint(from), end: toSourcePoint(to) } }),
100929
101080
  });
100930
101081
  const parts = [];
100931
101082
  let cursor = 0;
@@ -100960,6 +101111,8 @@ const splitHtmlWithNestedTables = (node) => {
100960
101111
 
100961
101112
 
100962
101113
 
101114
+
101115
+
100963
101116
 
100964
101117
 
100965
101118
 
@@ -101012,7 +101165,7 @@ const tableRepairs = [
101012
101165
  * The plugins in the processor allows parsing markdown & special syntax inside the table cells
101013
101166
  * After parsing, we need to update the node positions
101014
101167
  */
101015
- const parseTableNode = (processor, node, repair) => {
101168
+ const parseTableNode = (processor, node, source, repair) => {
101016
101169
  let parsed;
101017
101170
  try {
101018
101171
  parsed = processor.runSync(processor.parse(node.value));
@@ -101026,19 +101179,18 @@ const parseTableNode = (processor, node, repair) => {
101026
101179
  if (repair) {
101027
101180
  remapPositionsThroughLayers(parsed, repair.originalSource, repair.layers);
101028
101181
  }
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;
101182
+ // The subparser produces positions relative to `node.value`; map them into
101183
+ // the outer source so consumers can slice it.
101184
+ const toSourcePoint = createValueToSourceMapper(node.position, repair?.originalSource ?? node.value, source);
101185
+ if (!toSourcePoint)
101186
+ return parsed;
101033
101187
  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
- }
101188
+ if (!child.position)
101189
+ return;
101190
+ child.position = {
101191
+ start: toSourcePoint(child.position.start.offset ?? 0),
101192
+ end: toSourcePoint(child.position.end.offset ?? 0),
101193
+ };
101042
101194
  });
101043
101195
  return parsed;
101044
101196
  };
@@ -101068,12 +101220,10 @@ const hasFlowContent = (nodes) => {
101068
101220
  return nodes.some(node => !phrasing(node) && node.type !== 'paragraph');
101069
101221
  };
101070
101222
  /**
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).
101223
+ * Process a Table node: re-parse text-only cell content, then return it as
101224
+ * a markdown table (phrasing-only) or keep it as JSX <Table> (has flow content).
101073
101225
  */
101074
- const processTableNode = (node, index, parent, documentPosition) => {
101075
- if (node.name !== 'Table' && node.name !== 'table')
101076
- return;
101226
+ const processTableNode = (node, documentPosition) => {
101077
101227
  const position = documentPosition ?? node.position;
101078
101228
  const { align: alignAttr } = getAttrs(node);
101079
101229
  const align = Array.isArray(alignAttr) ? alignAttr : null;
@@ -101100,6 +101250,8 @@ const processTableNode = (node, index, parent, documentPosition) => {
101100
101250
  try {
101101
101251
  const parsed = tableNodeProcessor.runSync(tableNodeProcessor.parse(textContent));
101102
101252
  if (parsed.children.length > 0) {
101253
+ // Positions index into the extracted text rather than the table source (CX-4004).
101254
+ stampReparseSource(parsed.children, textContent);
101103
101255
  cell.children = parsed.children;
101104
101256
  if (hasFlowContent(parsed.children)) {
101105
101257
  tableHasFlowContent = true;
@@ -101142,11 +101294,7 @@ const processTableNode = (node, index, parent, documentPosition) => {
101142
101294
  : unwrapParagraphNodes(el.children);
101143
101295
  el.children = removeWhitespaceOnlyTextNodes(unwrapped);
101144
101296
  });
101145
- parent.children[index] = {
101146
- ...node,
101147
- position,
101148
- };
101149
- return;
101297
+ return { ...node, position };
101150
101298
  }
101151
101299
  // All cells are phrasing-only — convert to markdown table
101152
101300
  const children = [];
@@ -101224,7 +101372,7 @@ const processTableNode = (node, index, parent, documentPosition) => {
101224
101372
  const alignArray = align && columnCount > 0
101225
101373
  ? align.slice(0, columnCount).concat(new Array(Math.max(0, columnCount - align.length)).fill(null))
101226
101374
  : new Array(columnCount).fill(null);
101227
- const mdNode = {
101375
+ return {
101228
101376
  align: alignArray,
101229
101377
  type: 'table',
101230
101378
  position,
@@ -101232,14 +101380,13 @@ const processTableNode = (node, index, parent, documentPosition) => {
101232
101380
  // Remember the author's spelling so the serializer can write `<table>` back instead of `<Table>`
101233
101381
  ...(node.name === 'table' && { data: { lowercaseTable: true } }),
101234
101382
  };
101235
- parent.children[index] = mdNode;
101236
101383
  };
101237
101384
  /**
101238
101385
  * Apply `tableRepairs` cumulatively, re-parsing after every change and stopping
101239
101386
  * once the accumulated result parses. Each layer's inserts are relative to the
101240
101387
  * string that repair received, so they stay ordered for position remapping.
101241
101388
  */
101242
- const repairAndReparse = (node) => {
101389
+ const repairAndReparse = (node, source) => {
101243
101390
  let repairedValue = node.value;
101244
101391
  const layers = [];
101245
101392
  let parsed;
@@ -101249,11 +101396,45 @@ const repairAndReparse = (node) => {
101249
101396
  return false;
101250
101397
  repairedValue = value;
101251
101398
  layers.push(inserts);
101252
- parsed = parseTableNode(tableNodeProcessor, { ...node, value: repairedValue }, { layers, originalSource: node.value });
101399
+ parsed = parseTableNode(tableNodeProcessor, { ...node, value: repairedValue }, source, {
101400
+ layers,
101401
+ originalSource: node.value,
101402
+ });
101253
101403
  return Boolean(parsed);
101254
101404
  });
101255
101405
  return parsed;
101256
101406
  };
101407
+ /**
101408
+ * Re-parse a table html node and replace it with a markdown / JSX table, or the
101409
+ * fallback parse's fragments for a lowercase table mdxjs rejects.
101410
+ */
101411
+ const replaceTableHtml = (node, index, parent, source) => {
101412
+ // Because the processor uses remarkMdx, it is stricter in what it accepts
101413
+ // and only accepts valid MDX syntax in the table node. To get around that,
101414
+ // fall back to the cumulative repairs when the first parse fails.
101415
+ const parsed = parseTableNode(tableNodeProcessor, node, source) ?? repairAndReparse(node, source);
101416
+ if (parsed) {
101417
+ // If the table is parsed successfully, we can now process it further
101418
+ // to build on the markdown / JSX table
101419
+ visit(parsed, isMDXElement, (tableNode) => {
101420
+ if (tableNode.name !== 'Table' && tableNode.name !== 'table')
101421
+ return undefined;
101422
+ replaceInheritingReparseSource(parent, index, [processTableNode(tableNode, node.position)]);
101423
+ return (/* inlined export .EXIT */false);
101424
+ });
101425
+ }
101426
+ else if (node.value.startsWith('<table')) {
101427
+ // If the parsing still fails, give an opportunity to the fallback parser
101428
+ // without remarkMdx to process lowercase tables as it's likely to not
101429
+ // have needed MDX parsing anyway
101430
+ const fallback = parseTableNode(fallbackTableNodeProcessor, node, source);
101431
+ if (!fallback || fallback.children.length <= 1)
101432
+ return;
101433
+ replaceInheritingReparseSource(parent, index, fallback.children);
101434
+ }
101435
+ // Otherwise, there's no point in trying to parse the table content further
101436
+ // More repairs are needed in that case
101437
+ };
101257
101438
  /**
101258
101439
  * Converts JSX Table elements to markdown table nodes and re-parses markdown in cells.
101259
101440
  *
@@ -101265,24 +101446,26 @@ const repairAndReparse = (node) => {
101265
101446
  * When cell content contains block-level nodes (callouts, code blocks, etc.), the table
101266
101447
  * is kept as a JSX <Table> element so that remarkRehype can properly handle the flow content.
101267
101448
  */
101268
- const mdxishTables = () => tree => {
101449
+ const mdxishTables = () => (tree, file) => {
101450
+ // Positions index into the document unless a re-parse stamped another source.
101451
+ const documentSource = file?.value ? String(file.value) : undefined;
101269
101452
  // Pre-pass: lift `<table>`s wrapped in a raw HTML block out into their own
101270
101453
  // 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))
101454
+ visitParents(tree, 'html', (node, ancestors) => {
101455
+ const parent = ancestors[ancestors.length - 1];
101456
+ if (!parent)
101274
101457
  return;
101275
- const parts = splitHtmlWithNestedTables(node);
101458
+ const parts = splitHtmlWithNestedTables(node, resolveReparseSource(node, ancestors, documentSource));
101276
101459
  if (!parts)
101277
101460
  return;
101278
101461
  // The inserted parts can't re-trigger a split (table parts start with
101279
101462
  // `<table`; the wrapper slices hold no table), so plain in-place splicing
101280
101463
  // visits each once without looping.
101281
- parent.children.splice(index, 1, ...parts);
101464
+ replaceInheritingReparseSource(parent, parent.children.indexOf(node), parts);
101282
101465
  });
101283
- visit(tree, 'html', (_node, index, parent) => {
101284
- const node = _node;
101285
- if (typeof index !== 'number' || !parent || !('children' in parent))
101466
+ visitParents(tree, 'html', (node, ancestors) => {
101467
+ const parent = ancestors[ancestors.length - 1];
101468
+ if (!parent)
101286
101469
  return;
101287
101470
  if (!node.value.startsWith('<Table') && !node.value.startsWith('<table'))
101288
101471
  return;
@@ -101290,33 +101473,8 @@ const mdxishTables = () => tree => {
101290
101473
  // paragraph. Leave them untouched so rehype-raw can reassemble the table.
101291
101474
  if (parent.type === 'paragraph')
101292
101475
  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
101476
+ replaceTableHtml(node, parent.children.indexOf(node), parent, resolveReparseSource(node, ancestors, documentSource));
101318
101477
  });
101319
- return tree;
101320
101478
  };
101321
101479
  /* harmony default export */ const mdxish_tables = (mdxishTables);
101322
101480
 
@@ -124071,32 +124229,6 @@ const mdxComponentHandlers = {
124071
124229
  [NodeTypes.htmlBlock]: htmlBlockHandler,
124072
124230
  };
124073
124231
 
124074
- ;// ./processor/plugin/safe-mode-html-blocks.ts
124075
-
124076
-
124077
- /**
124078
- * Whether a tag name renders through the `HTMLBlock` component. Mirrors the
124079
- * separator-agnostic, case-insensitive lookup in `getComponentName` and the
124080
- * rehype-react component map, so `html-block`, `HTMLBlock`, `HtmlBlock` and
124081
- * `htmlblock` all match.
124082
- */
124083
- const isHtmlBlockTag = (tagName) => toPascalCase(tagName).toLowerCase() === 'htmlblock';
124084
- /**
124085
- * Forces `safeMode` on every element that renders through `HTMLBlock` so it is
124086
- * shown as escaped text instead of injected.
124087
- *
124088
- * Runs last, so it sees every producer of the element — `[block:html]`,
124089
- * `<HTMLBlock>` with a template body or an `html` attribute, and a literal
124090
- * `<html-block>` — and it is unconditional so an author-supplied
124091
- * `safeMode="false"` can't opt out.
124092
- */
124093
- const enforceSafeModeHtmlBlocks = () => tree => {
124094
- visit(tree, 'element', node => {
124095
- if (isHtmlBlockTag(node.tagName))
124096
- node.properties.safeMode = 'true';
124097
- });
124098
- };
124099
-
124100
124232
  ;// ./processor/transform/mdxish/anchor-to-jsx.ts
124101
124233
 
124102
124234
 
@@ -124150,9 +124282,9 @@ const mdxishCalloutToJsx = () => tree => {
124150
124282
  visit(tree, NodeTypes.callout, (node, index, parent) => {
124151
124283
  if (!parent || index === undefined)
124152
124284
  return;
124153
- let { icon, theme } = node.data.hProperties;
124154
- if (!icon && theme)
124155
- icon = defaultIcons[theme];
124285
+ // No default-icon fill: an empty `icon` means the author removed it
124286
+ const { icon } = node.data.hProperties;
124287
+ let { theme } = node.data.hProperties;
124156
124288
  if (!theme && icon)
124157
124289
  theme = themes[icon] || 'default';
124158
124290
  // Mutate in place to keep the children array identity so nested callouts in
@@ -124703,18 +124835,6 @@ const getInlineMdProcessor = ({ safeMode = false } = {}) => {
124703
124835
  }
124704
124836
  return processor;
124705
124837
  };
124706
- /**
124707
- * Mark re-parsed subtree roots with the string their positions index into, so consumers
124708
- * slicing by offset use the right source (see `Data.reparseSource`). Only roots are
124709
- * stamped; descendants resolve via their nearest stamped ancestor. Roots a deeper
124710
- * re-parse already stamped keep their own string.
124711
- */
124712
- const stampReparseSource = (roots, reparseSource) => {
124713
- roots.forEach(root => {
124714
- if (!root.data?.reparseSource)
124715
- root.data = { ...root.data, reparseSource };
124716
- });
124717
- };
124718
124838
  /**
124719
124839
  * True when a tag name starts with an uppercase letter — ReadMe's marker for
124720
124840
  * a custom MDX component (vs a lowercase HTML tag).
@@ -124801,6 +124921,7 @@ function findBalancedClosingTagIndex(content, tag) {
124801
124921
 
124802
124922
 
124803
124923
 
124924
+
124804
124925
  /**
124805
124926
  * Parse the body of an inline component as phrasing content. Remark always
124806
124927
  * wraps bare text in a paragraph; flatten that single-paragraph wrapper so
@@ -125052,6 +125173,7 @@ function terminateHtmlFlowBlocks(content) {
125052
125173
 
125053
125174
 
125054
125175
 
125176
+
125055
125177
  // Matches a JSX attribute expression (e.g. `key={i}`) anywhere in a string. One name
125056
125178
  // char suffices for existence — `[\w-]+` backtracked quadratically over huge attributes.
125057
125179
  const NESTED_ATTR_EXPRESSION_RE = /[\w-]\s*=\s*\{/;
@@ -125133,26 +125255,12 @@ const parseSibling = (parent, index, sibling, safeMode, promoted) => {
125133
125255
  }
125134
125256
  };
125135
125257
  // Ends the position at `consumedLength` so the component doesn't claim trailing
125136
- // content the tokenizer swallowed into the same html node. Measured against the source
125137
- // when available: each value line is its source line minus the container prefix
125138
- // (list indent, `> `) micromark stripped, so those prefixes are counted back in.
125258
+ // content the tokenizer swallowed into the same html node.
125139
125259
  const positionEndingAtConsumed = (nodePosition, value, consumedLength, source) => {
125140
- if (!nodePosition?.start)
125260
+ const toSourcePoint = createValueToSourceMapper(nodePosition, value, source);
125261
+ if (!nodePosition || !toSourcePoint)
125141
125262
  return nodePosition;
125142
- const consumedValue = value.slice(0, consumedLength);
125143
- const sourceLines = source?.slice(nodePosition.start.offset, nodePosition.end?.offset).split('\n');
125144
- const valueLines = value.split('\n');
125145
- if (!sourceLines || sourceLines.length !== valueLines.length) {
125146
- return { start: nodePosition.start, end: pointAfter(nodePosition.start, consumedValue) };
125147
- }
125148
- const consumedLines = consumedValue.split('\n');
125149
- const lastLineIndex = consumedLines.length - 1;
125150
- const prefixWidth = sourceLines[lastLineIndex].length - valueLines[lastLineIndex].length;
125151
- const consumedSource = [
125152
- ...sourceLines.slice(0, lastLineIndex),
125153
- sourceLines[lastLineIndex].slice(0, prefixWidth + consumedLines[lastLineIndex].length),
125154
- ].join('\n');
125155
- return { start: nodePosition.start, end: pointAfter(nodePosition.start, consumedSource) };
125263
+ return { start: nodePosition.start, end: toSourcePoint(consumedLength) };
125156
125264
  };
125157
125265
  const createComponentNode = ({ tag, attributes, children, startPosition, endPosition, }) => ({
125158
125266
  type: 'mdxJsxFlowElement',
@@ -125164,14 +125272,6 @@ const createComponentNode = ({ tag, attributes, children, startPosition, endPosi
125164
125272
  end: endPosition?.end ?? startPosition?.end,
125165
125273
  },
125166
125274
  });
125167
- // The promoted node takes over the html node's position, so it also takes over the
125168
- // coordinate space that position belongs to.
125169
- const substituteNodeWithMdxNode = (parent, index, mdxNode) => {
125170
- const replacedSource = parent.children[index]?.data?.reparseSource;
125171
- if (replacedSource)
125172
- stampReparseSource([mdxNode], replacedSource);
125173
- parent.children.splice(index, 1, mdxNode);
125174
- };
125175
125275
  /**
125176
125276
  * Transform PascalCase HTML nodes into mdxJsxFlowElement nodes.
125177
125277
  *
@@ -125270,7 +125370,7 @@ function promoteComponentBlocks(tree, safeMode, source) {
125270
125370
  ? positionEndingAtConsumed(node.position, value, leadingWhitespace + openingTagEnd, source)
125271
125371
  : node.position,
125272
125372
  });
125273
- substituteNodeWithMdxNode(parent, index, componentNode);
125373
+ replaceInheritingReparseSource(parent, index, [componentNode]);
125274
125374
  if (remainingContent) {
125275
125375
  parseSibling(parent, index, remainingContent, safeMode, promoted);
125276
125376
  }
@@ -125321,7 +125421,7 @@ function promoteComponentBlocks(tree, safeMode, source) {
125321
125421
  startPosition: node.position,
125322
125422
  endPosition,
125323
125423
  });
125324
- substituteNodeWithMdxNode(parent, index, componentNode);
125424
+ replaceInheritingReparseSource(parent, index, [componentNode]);
125325
125425
  // The unwrap reparented the children out of their paragraph, so re-walk them
125326
125426
  // since the children HTML may contain promotable syntax (e.g. `{…}`-attr tags)
125327
125427
  if (unwrappedSoleParagraph) {
@@ -126404,7 +126504,7 @@ const parseApiHeaderTitle = (text) => {
126404
126504
  * Transform a magicBlock node into final MDAST nodes.
126405
126505
  */
126406
126506
  function transformMagicBlock(blockType, data, rawValue, options = {}) {
126407
- const { compatibilityMode = false, hardBreaks = true } = options;
126507
+ const { compatibilityMode = false, hardBreaks = true, safeMode = false } = options;
126408
126508
  // Handle empty data by returning placeholder nodes for known block types
126409
126509
  // This allows the editor to show appropriate placeholder UI instead of nothing
126410
126510
  if (Object.keys(data).length < 1) {
@@ -126622,7 +126722,7 @@ function transformMagicBlock(blockType, data, rawValue, options = {}) {
126622
126722
  wrapPinnedBlocks({
126623
126723
  data: {
126624
126724
  hName: 'html-block',
126625
- hProperties: { html: htmlJson.html, runScripts: compatibilityMode },
126725
+ hProperties: { html: htmlJson.html, runScripts: compatibilityMode, safeMode },
126626
126726
  },
126627
126727
  type: 'html-block',
126628
126728
  }, data),
@@ -126763,6 +126863,7 @@ const magicBlockTransformer = (options = {}) => tree => {
126763
126863
 
126764
126864
 
126765
126865
 
126866
+
126766
126867
  // `<HTMLBlock …>{`…`}</HTMLBlock>` embedded inside a raw HTML block (e.g. a
126767
126868
  // single-line `<div>…</div>`). CommonMark slurps the whole div as one `html`
126768
126869
  // node, so the tokenizer never sees the HTMLBlock — we recover it here.
@@ -126869,7 +126970,8 @@ const mdxishHtmlBlocks = () => tree => {
126869
126970
  return;
126870
126971
  const exprChild = element.children.find(child => child.type === 'mdxFlowExpression' || child.type === 'mdxTextExpression');
126871
126972
  const openingTagIndent = (element.position?.start.column ?? 1) - 1;
126872
- parent.children[index] = createHtmlBlockNode(formatHtmlForMdxish(extractTemplateLiteral(exprChild?.value), openingTagIndent), element.position, toRunScripts(jsxAttr(element, 'runScripts')), jsxAttr(element, 'safeMode'));
126973
+ const htmlBlock = createHtmlBlockNode(formatHtmlForMdxish(extractTemplateLiteral(exprChild?.value), openingTagIndent), element.position, toRunScripts(jsxAttr(element, 'runScripts')), jsxAttr(element, 'safeMode'));
126974
+ replaceInheritingReparseSource(parent, index, [htmlBlock]);
126873
126975
  });
126874
126976
  // Shape 2: raw HTML blob.
126875
126977
  visit(tree, 'html', (node, index, parent) => {
@@ -126877,7 +126979,7 @@ const mdxishHtmlBlocks = () => tree => {
126877
126979
  return;
126878
126980
  const replacement = splitRawHtmlBlocks(node);
126879
126981
  if (replacement)
126880
- parent.children.splice(index, 1, ...replacement);
126982
+ replaceInheritingReparseSource(parent, index, replacement);
126881
126983
  });
126882
126984
  };
126883
126985
  /* harmony default export */ const mdxish_html_blocks = (mdxishHtmlBlocks);
@@ -126964,6 +127066,7 @@ const hasOnlyWidthAttributes = (cell) => {
126964
127066
 
126965
127067
 
126966
127068
 
127069
+
126967
127070
  function toImageAlign(value) {
126968
127071
  if (value === 'left' || value === 'center' || value === 'right') {
126969
127072
  return value;
@@ -127179,6 +127282,15 @@ const transformAnchor = (jsx) => {
127179
127282
  position: jsx.position,
127180
127283
  };
127181
127284
  };
127285
+ /**
127286
+ * Parses an image caption's markdown. The caption is re-parsed on its own, so the
127287
+ * children's positions index into it rather than the document.
127288
+ */
127289
+ const parseCaptionChildren = (caption) => {
127290
+ const { children } = lib_mdast(caption);
127291
+ stampReparseSource(children, caption);
127292
+ return children;
127293
+ };
127182
127294
  /**
127183
127295
  * Transforms an `<Image />` JSX element into an image-block MDAST node.
127184
127296
  * Normalizes attributes (align, border, width→sizing) and parses caption markdown into children.
@@ -127209,7 +127321,7 @@ const transformImage = (jsx) => {
127209
127321
  alt,
127210
127322
  border: toBool(border),
127211
127323
  caption,
127212
- children: caption ? lib_mdast(caption).children : [],
127324
+ children: caption ? parseCaptionChildren(caption) : [],
127213
127325
  className,
127214
127326
  framed: toBool(framed),
127215
127327
  height: height !== undefined ? String(height) : undefined,
@@ -127546,8 +127658,7 @@ const mdxishJsxToMdast = () => tree => {
127546
127658
  const newNode = transformer(node);
127547
127659
  if (!newNode)
127548
127660
  return;
127549
- // Replace the JSX node with the MDAST node
127550
- parent.children[index] = newNode;
127661
+ replaceInheritingReparseSource(parent, index, [newNode]);
127551
127662
  });
127552
127663
  // Inline JSX components (Anchor)
127553
127664
  visit(tree, 'mdxJsxTextElement', (node, index, parent) => {
@@ -128457,7 +128568,6 @@ function loadComponents() {
128457
128568
 
128458
128569
 
128459
128570
 
128460
-
128461
128571
 
128462
128572
 
128463
128573
  const defaultTransformers = [
@@ -128613,8 +128723,7 @@ function mdxish(mdContent, opts = {}) {
128613
128723
  .use(rehypeMdxishComponents, {
128614
128724
  components,
128615
128725
  processMarkdown: (markdown) => mdxish(markdown, opts),
128616
- })
128617
- .use(safeMode ? enforceSafeModeHtmlBlocks : undefined); // Force safeMode on every HTMLBlock; runs last so every producer is covered
128726
+ });
128618
128727
  const vfile = new VFile({ value: parserReadyContent });
128619
128728
  const mdast = processor.parse(parserReadyContent);
128620
128729
  const hast = processor.runSync(mdast, vfile);
@@ -129645,7 +129754,7 @@ module.exports = /*#__PURE__*/JSON.parse('{"*":["about","acceptCharset","accessK
129645
129754
  /******/ // startup
129646
129755
  /******/ // Load entry module and return exports
129647
129756
  /******/ // This entry module used 'module' so it can't be inlined
129648
- /******/ let __webpack_exports__ = __webpack_require__(5789);
129757
+ /******/ let __webpack_exports__ = __webpack_require__(6831);
129649
129758
  /******/ module.exports = __webpack_exports__;
129650
129759
  /******/
129651
129760
  /******/ })()