@readme/markdown 15.0.0 → 15.0.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
@@ -27250,72 +27250,319 @@ function push(list, items) {
27250
27250
  return items
27251
27251
  }
27252
27252
 
27253
+ ;// ./node_modules/micromark-util-subtokenize/lib/splice-buffer.js
27254
+ /**
27255
+ * Some of the internal operations of micromark do lots of editing
27256
+ * operations on very large arrays. This runs into problems with two
27257
+ * properties of most circa-2020 JavaScript interpreters:
27258
+ *
27259
+ * - Array-length modifications at the high end of an array (push/pop) are
27260
+ * expected to be common and are implemented in (amortized) time
27261
+ * proportional to the number of elements added or removed, whereas
27262
+ * other operations (shift/unshift and splice) are much less efficient.
27263
+ * - Function arguments are passed on the stack, so adding tens of thousands
27264
+ * of elements to an array with `arr.push(...newElements)` will frequently
27265
+ * cause stack overflows. (see <https://stackoverflow.com/questions/22123769/rangeerror-maximum-call-stack-size-exceeded-why>)
27266
+ *
27267
+ * SpliceBuffers are an implementation of gap buffers, which are a
27268
+ * generalization of the "queue made of two stacks" idea. The splice buffer
27269
+ * maintains a cursor, and moving the cursor has cost proportional to the
27270
+ * distance the cursor moves, but inserting, deleting, or splicing in
27271
+ * new information at the cursor is as efficient as the push/pop operation.
27272
+ * This allows for an efficient sequence of splices (or pushes, pops, shifts,
27273
+ * or unshifts) as long such edits happen at the same part of the array or
27274
+ * generally sweep through the array from the beginning to the end.
27275
+ *
27276
+ * The interface for splice buffers also supports large numbers of inputs by
27277
+ * passing a single array argument rather passing multiple arguments on the
27278
+ * function call stack.
27279
+ *
27280
+ * @template T
27281
+ * Item type.
27282
+ */
27283
+ class SpliceBuffer {
27284
+ /**
27285
+ * @param {ReadonlyArray<T> | null | undefined} [initial]
27286
+ * Initial items (optional).
27287
+ * @returns
27288
+ * Splice buffer.
27289
+ */
27290
+ constructor(initial) {
27291
+ /** @type {Array<T>} */
27292
+ this.left = initial ? [...initial] : [];
27293
+ /** @type {Array<T>} */
27294
+ this.right = [];
27295
+ }
27296
+
27297
+ /**
27298
+ * Array access;
27299
+ * does not move the cursor.
27300
+ *
27301
+ * @param {number} index
27302
+ * Index.
27303
+ * @return {T}
27304
+ * Item.
27305
+ */
27306
+ get(index) {
27307
+ if (index < 0 || index >= this.left.length + this.right.length) {
27308
+ throw new RangeError('Cannot access index `' + index + '` in a splice buffer of size `' + (this.left.length + this.right.length) + '`');
27309
+ }
27310
+ if (index < this.left.length) return this.left[index];
27311
+ return this.right[this.right.length - index + this.left.length - 1];
27312
+ }
27313
+
27314
+ /**
27315
+ * The length of the splice buffer, one greater than the largest index in the
27316
+ * array.
27317
+ */
27318
+ get length() {
27319
+ return this.left.length + this.right.length;
27320
+ }
27321
+
27322
+ /**
27323
+ * Remove and return `list[0]`;
27324
+ * moves the cursor to `0`.
27325
+ *
27326
+ * @returns {T | undefined}
27327
+ * Item, optional.
27328
+ */
27329
+ shift() {
27330
+ this.setCursor(0);
27331
+ return this.right.pop();
27332
+ }
27333
+
27334
+ /**
27335
+ * Slice the buffer to get an array;
27336
+ * does not move the cursor.
27337
+ *
27338
+ * @param {number} start
27339
+ * Start.
27340
+ * @param {number | null | undefined} [end]
27341
+ * End (optional).
27342
+ * @returns {Array<T>}
27343
+ * Array of items.
27344
+ */
27345
+ slice(start, end) {
27346
+ /** @type {number} */
27347
+ const stop = end === null || end === undefined ? Number.POSITIVE_INFINITY : end;
27348
+ if (stop < this.left.length) {
27349
+ return this.left.slice(start, stop);
27350
+ }
27351
+ if (start > this.left.length) {
27352
+ return this.right.slice(this.right.length - stop + this.left.length, this.right.length - start + this.left.length).reverse();
27353
+ }
27354
+ return this.left.slice(start).concat(this.right.slice(this.right.length - stop + this.left.length).reverse());
27355
+ }
27356
+
27357
+ /**
27358
+ * Mimics the behavior of Array.prototype.splice() except for the change of
27359
+ * interface necessary to avoid segfaults when patching in very large arrays.
27360
+ *
27361
+ * This operation moves cursor is moved to `start` and results in the cursor
27362
+ * placed after any inserted items.
27363
+ *
27364
+ * @param {number} start
27365
+ * Start;
27366
+ * zero-based index at which to start changing the array;
27367
+ * negative numbers count backwards from the end of the array and values
27368
+ * that are out-of bounds are clamped to the appropriate end of the array.
27369
+ * @param {number | null | undefined} [deleteCount=0]
27370
+ * Delete count (default: `0`);
27371
+ * maximum number of elements to delete, starting from start.
27372
+ * @param {Array<T> | null | undefined} [items=[]]
27373
+ * Items to include in place of the deleted items (default: `[]`).
27374
+ * @return {Array<T>}
27375
+ * Any removed items.
27376
+ */
27377
+ splice(start, deleteCount, items) {
27378
+ /** @type {number} */
27379
+ const count = deleteCount || 0;
27380
+ this.setCursor(Math.trunc(start));
27381
+ const removed = this.right.splice(this.right.length - count, Number.POSITIVE_INFINITY);
27382
+ if (items) chunkedPush(this.left, items);
27383
+ return removed.reverse();
27384
+ }
27385
+
27386
+ /**
27387
+ * Remove and return the highest-numbered item in the array, so
27388
+ * `list[list.length - 1]`;
27389
+ * Moves the cursor to `length`.
27390
+ *
27391
+ * @returns {T | undefined}
27392
+ * Item, optional.
27393
+ */
27394
+ pop() {
27395
+ this.setCursor(Number.POSITIVE_INFINITY);
27396
+ return this.left.pop();
27397
+ }
27398
+
27399
+ /**
27400
+ * Inserts a single item to the high-numbered side of the array;
27401
+ * moves the cursor to `length`.
27402
+ *
27403
+ * @param {T} item
27404
+ * Item.
27405
+ * @returns {undefined}
27406
+ * Nothing.
27407
+ */
27408
+ push(item) {
27409
+ this.setCursor(Number.POSITIVE_INFINITY);
27410
+ this.left.push(item);
27411
+ }
27412
+
27413
+ /**
27414
+ * Inserts many items to the high-numbered side of the array.
27415
+ * Moves the cursor to `length`.
27416
+ *
27417
+ * @param {Array<T>} items
27418
+ * Items.
27419
+ * @returns {undefined}
27420
+ * Nothing.
27421
+ */
27422
+ pushMany(items) {
27423
+ this.setCursor(Number.POSITIVE_INFINITY);
27424
+ chunkedPush(this.left, items);
27425
+ }
27426
+
27427
+ /**
27428
+ * Inserts a single item to the low-numbered side of the array;
27429
+ * Moves the cursor to `0`.
27430
+ *
27431
+ * @param {T} item
27432
+ * Item.
27433
+ * @returns {undefined}
27434
+ * Nothing.
27435
+ */
27436
+ unshift(item) {
27437
+ this.setCursor(0);
27438
+ this.right.push(item);
27439
+ }
27440
+
27441
+ /**
27442
+ * Inserts many items to the low-numbered side of the array;
27443
+ * moves the cursor to `0`.
27444
+ *
27445
+ * @param {Array<T>} items
27446
+ * Items.
27447
+ * @returns {undefined}
27448
+ * Nothing.
27449
+ */
27450
+ unshiftMany(items) {
27451
+ this.setCursor(0);
27452
+ chunkedPush(this.right, items.reverse());
27453
+ }
27454
+
27455
+ /**
27456
+ * Move the cursor to a specific position in the array. Requires
27457
+ * time proportional to the distance moved.
27458
+ *
27459
+ * If `n < 0`, the cursor will end up at the beginning.
27460
+ * If `n > length`, the cursor will end up at the end.
27461
+ *
27462
+ * @param {number} n
27463
+ * Position.
27464
+ * @return {undefined}
27465
+ * Nothing.
27466
+ */
27467
+ setCursor(n) {
27468
+ if (n === this.left.length || n > this.left.length && this.right.length === 0 || n < 0 && this.left.length === 0) return;
27469
+ if (n < this.left.length) {
27470
+ // Move cursor to the this.left
27471
+ const removed = this.left.splice(n, Number.POSITIVE_INFINITY);
27472
+ chunkedPush(this.right, removed.reverse());
27473
+ } else {
27474
+ // Move cursor to the this.right
27475
+ const removed = this.right.splice(this.left.length + this.right.length - n, Number.POSITIVE_INFINITY);
27476
+ chunkedPush(this.left, removed.reverse());
27477
+ }
27478
+ }
27479
+ }
27480
+
27481
+ /**
27482
+ * Avoid stack overflow by pushing items onto the stack in segments
27483
+ *
27484
+ * @template T
27485
+ * Item type.
27486
+ * @param {Array<T>} list
27487
+ * List to inject into.
27488
+ * @param {ReadonlyArray<T>} right
27489
+ * Items to inject.
27490
+ * @return {undefined}
27491
+ * Nothing.
27492
+ */
27493
+ function chunkedPush(list, right) {
27494
+ /** @type {number} */
27495
+ let chunkStart = 0;
27496
+ if (right.length < 10000) {
27497
+ list.push(...right);
27498
+ } else {
27499
+ while (chunkStart < right.length) {
27500
+ list.push(...right.slice(chunkStart, chunkStart + 10000));
27501
+ chunkStart += 10000;
27502
+ }
27503
+ }
27504
+ }
27253
27505
  ;// ./node_modules/micromark-util-subtokenize/index.js
27254
27506
  /**
27255
- * @typedef {import('micromark-util-types').Chunk} Chunk
27256
- * @typedef {import('micromark-util-types').Event} Event
27257
- * @typedef {import('micromark-util-types').Token} Token
27507
+ * @import {Chunk, Event, Token} from 'micromark-util-types'
27258
27508
  */
27259
27509
 
27260
27510
 
27511
+
27512
+
27513
+ // Hidden API exposed for testing.
27514
+
27515
+
27261
27516
  /**
27262
27517
  * Tokenize subcontent.
27263
27518
  *
27264
- * @param {Array<Event>} events
27519
+ * @param {Array<Event>} eventsArray
27265
27520
  * List of events.
27266
27521
  * @returns {boolean}
27267
27522
  * Whether subtokens were found.
27268
- */ // eslint-disable-next-line complexity
27269
- function subtokenize(events) {
27523
+ */
27524
+ // eslint-disable-next-line complexity
27525
+ function subtokenize(eventsArray) {
27270
27526
  /** @type {Record<string, number>} */
27271
- const jumps = {}
27272
- let index = -1
27527
+ const jumps = {};
27528
+ let index = -1;
27273
27529
  /** @type {Event} */
27274
- let event
27530
+ let event;
27275
27531
  /** @type {number | undefined} */
27276
- let lineIndex
27532
+ let lineIndex;
27277
27533
  /** @type {number} */
27278
- let otherIndex
27534
+ let otherIndex;
27279
27535
  /** @type {Event} */
27280
- let otherEvent
27536
+ let otherEvent;
27281
27537
  /** @type {Array<Event>} */
27282
- let parameters
27538
+ let parameters;
27283
27539
  /** @type {Array<Event>} */
27284
- let subevents
27540
+ let subevents;
27285
27541
  /** @type {boolean | undefined} */
27286
- let more
27542
+ let more;
27543
+ const events = new SpliceBuffer(eventsArray);
27287
27544
  while (++index < events.length) {
27288
27545
  while (index in jumps) {
27289
- index = jumps[index]
27546
+ index = jumps[index];
27290
27547
  }
27291
- event = events[index]
27548
+ event = events.get(index);
27292
27549
 
27293
27550
  // Add a hook for the GFM tasklist extension, which needs to know if text
27294
27551
  // is in the first content of a list item.
27295
- if (
27296
- index &&
27297
- event[1].type === 'chunkFlow' &&
27298
- events[index - 1][1].type === 'listItemPrefix'
27299
- ) {
27300
- subevents = event[1]._tokenizer.events
27301
- otherIndex = 0
27302
- if (
27303
- otherIndex < subevents.length &&
27304
- subevents[otherIndex][1].type === 'lineEndingBlank'
27305
- ) {
27306
- otherIndex += 2
27552
+ if (index && event[1].type === "chunkFlow" && events.get(index - 1)[1].type === "listItemPrefix") {
27553
+ subevents = event[1]._tokenizer.events;
27554
+ otherIndex = 0;
27555
+ if (otherIndex < subevents.length && subevents[otherIndex][1].type === "lineEndingBlank") {
27556
+ otherIndex += 2;
27307
27557
  }
27308
- if (
27309
- otherIndex < subevents.length &&
27310
- subevents[otherIndex][1].type === 'content'
27311
- ) {
27558
+ if (otherIndex < subevents.length && subevents[otherIndex][1].type === "content") {
27312
27559
  while (++otherIndex < subevents.length) {
27313
- if (subevents[otherIndex][1].type === 'content') {
27314
- break
27560
+ if (subevents[otherIndex][1].type === "content") {
27561
+ break;
27315
27562
  }
27316
- if (subevents[otherIndex][1].type === 'chunkText') {
27317
- subevents[otherIndex][1]._isInFirstContentOfListItem = true
27318
- otherIndex++
27563
+ if (subevents[otherIndex][1].type === "chunkText") {
27564
+ subevents[otherIndex][1]._isInFirstContentOfListItem = true;
27565
+ otherIndex++;
27319
27566
  }
27320
27567
  }
27321
27568
  }
@@ -27324,158 +27571,160 @@ function subtokenize(events) {
27324
27571
  // Enter.
27325
27572
  if (event[0] === 'enter') {
27326
27573
  if (event[1].contentType) {
27327
- Object.assign(jumps, subcontent(events, index))
27328
- index = jumps[index]
27329
- more = true
27574
+ Object.assign(jumps, subcontent(events, index));
27575
+ index = jumps[index];
27576
+ more = true;
27330
27577
  }
27331
27578
  }
27332
27579
  // Exit.
27333
27580
  else if (event[1]._container) {
27334
- otherIndex = index
27335
- lineIndex = undefined
27581
+ otherIndex = index;
27582
+ lineIndex = undefined;
27336
27583
  while (otherIndex--) {
27337
- otherEvent = events[otherIndex]
27338
- if (
27339
- otherEvent[1].type === 'lineEnding' ||
27340
- otherEvent[1].type === 'lineEndingBlank'
27341
- ) {
27584
+ otherEvent = events.get(otherIndex);
27585
+ if (otherEvent[1].type === "lineEnding" || otherEvent[1].type === "lineEndingBlank") {
27342
27586
  if (otherEvent[0] === 'enter') {
27343
27587
  if (lineIndex) {
27344
- events[lineIndex][1].type = 'lineEndingBlank'
27588
+ events.get(lineIndex)[1].type = "lineEndingBlank";
27345
27589
  }
27346
- otherEvent[1].type = 'lineEnding'
27347
- lineIndex = otherIndex
27590
+ otherEvent[1].type = "lineEnding";
27591
+ lineIndex = otherIndex;
27348
27592
  }
27593
+ } else if (otherEvent[1].type === "linePrefix") {
27594
+ // Move past.
27349
27595
  } else {
27350
- break
27596
+ break;
27351
27597
  }
27352
27598
  }
27353
27599
  if (lineIndex) {
27354
27600
  // Fix position.
27355
- event[1].end = Object.assign({}, events[lineIndex][1].start)
27601
+ event[1].end = {
27602
+ ...events.get(lineIndex)[1].start
27603
+ };
27356
27604
 
27357
27605
  // Switch container exit w/ line endings.
27358
- parameters = events.slice(lineIndex, index)
27359
- parameters.unshift(event)
27360
- splice(events, lineIndex, index - lineIndex + 1, parameters)
27606
+ parameters = events.slice(lineIndex, index);
27607
+ parameters.unshift(event);
27608
+ events.splice(lineIndex, index - lineIndex + 1, parameters);
27361
27609
  }
27362
27610
  }
27363
27611
  }
27364
- return !more
27612
+
27613
+ // The changes to the `events` buffer must be copied back into the eventsArray
27614
+ splice(eventsArray, 0, Number.POSITIVE_INFINITY, events.slice(0));
27615
+ return !more;
27365
27616
  }
27366
27617
 
27367
27618
  /**
27368
27619
  * Tokenize embedded tokens.
27369
27620
  *
27370
- * @param {Array<Event>} events
27621
+ * @param {SpliceBuffer<Event>} events
27622
+ * Events.
27371
27623
  * @param {number} eventIndex
27624
+ * Index.
27372
27625
  * @returns {Record<string, number>}
27626
+ * Gaps.
27373
27627
  */
27374
27628
  function subcontent(events, eventIndex) {
27375
- const token = events[eventIndex][1]
27376
- const context = events[eventIndex][2]
27377
- let startPosition = eventIndex - 1
27629
+ const token = events.get(eventIndex)[1];
27630
+ const context = events.get(eventIndex)[2];
27631
+ let startPosition = eventIndex - 1;
27378
27632
  /** @type {Array<number>} */
27379
- const startPositions = []
27380
- const tokenizer =
27381
- token._tokenizer || context.parser[token.contentType](token.start)
27382
- const childEvents = tokenizer.events
27633
+ const startPositions = [];
27634
+ const tokenizer = token._tokenizer || context.parser[token.contentType](token.start);
27635
+ const childEvents = tokenizer.events;
27383
27636
  /** @type {Array<[number, number]>} */
27384
- const jumps = []
27637
+ const jumps = [];
27385
27638
  /** @type {Record<string, number>} */
27386
- const gaps = {}
27639
+ const gaps = {};
27387
27640
  /** @type {Array<Chunk>} */
27388
- let stream
27641
+ let stream;
27389
27642
  /** @type {Token | undefined} */
27390
- let previous
27391
- let index = -1
27643
+ let previous;
27644
+ let index = -1;
27392
27645
  /** @type {Token | undefined} */
27393
- let current = token
27394
- let adjust = 0
27395
- let start = 0
27396
- const breaks = [start]
27646
+ let current = token;
27647
+ let adjust = 0;
27648
+ let start = 0;
27649
+ const breaks = [start];
27397
27650
 
27398
27651
  // Loop forward through the linked tokens to pass them in order to the
27399
27652
  // subtokenizer.
27400
27653
  while (current) {
27401
27654
  // Find the position of the event for this token.
27402
- while (events[++startPosition][1] !== current) {
27655
+ while (events.get(++startPosition)[1] !== current) {
27403
27656
  // Empty.
27404
27657
  }
27405
- startPositions.push(startPosition)
27658
+ startPositions.push(startPosition);
27406
27659
  if (!current._tokenizer) {
27407
- stream = context.sliceStream(current)
27660
+ stream = context.sliceStream(current);
27408
27661
  if (!current.next) {
27409
- stream.push(null)
27662
+ stream.push(null);
27410
27663
  }
27411
27664
  if (previous) {
27412
- tokenizer.defineSkip(current.start)
27665
+ tokenizer.defineSkip(current.start);
27413
27666
  }
27414
27667
  if (current._isInFirstContentOfListItem) {
27415
- tokenizer._gfmTasklistFirstContentOfListItem = true
27668
+ tokenizer._gfmTasklistFirstContentOfListItem = true;
27416
27669
  }
27417
- tokenizer.write(stream)
27670
+ tokenizer.write(stream);
27418
27671
  if (current._isInFirstContentOfListItem) {
27419
- tokenizer._gfmTasklistFirstContentOfListItem = undefined
27672
+ tokenizer._gfmTasklistFirstContentOfListItem = undefined;
27420
27673
  }
27421
27674
  }
27422
27675
 
27423
27676
  // Unravel the next token.
27424
- previous = current
27425
- current = current.next
27677
+ previous = current;
27678
+ current = current.next;
27426
27679
  }
27427
27680
 
27428
27681
  // Now, loop back through all events (and linked tokens), to figure out which
27429
27682
  // parts belong where.
27430
- current = token
27683
+ current = token;
27431
27684
  while (++index < childEvents.length) {
27432
27685
  if (
27433
- // Find a void token that includes a break.
27434
- childEvents[index][0] === 'exit' &&
27435
- childEvents[index - 1][0] === 'enter' &&
27436
- childEvents[index][1].type === childEvents[index - 1][1].type &&
27437
- childEvents[index][1].start.line !== childEvents[index][1].end.line
27438
- ) {
27439
- start = index + 1
27440
- breaks.push(start)
27686
+ // Find a void token that includes a break.
27687
+ childEvents[index][0] === 'exit' && childEvents[index - 1][0] === 'enter' && childEvents[index][1].type === childEvents[index - 1][1].type && childEvents[index][1].start.line !== childEvents[index][1].end.line) {
27688
+ start = index + 1;
27689
+ breaks.push(start);
27441
27690
  // Help GC.
27442
- current._tokenizer = undefined
27443
- current.previous = undefined
27444
- current = current.next
27691
+ current._tokenizer = undefined;
27692
+ current.previous = undefined;
27693
+ current = current.next;
27445
27694
  }
27446
27695
  }
27447
27696
 
27448
27697
  // Help GC.
27449
- tokenizer.events = []
27698
+ tokenizer.events = [];
27450
27699
 
27451
27700
  // If there’s one more token (which is the cases for lines that end in an
27452
27701
  // EOF), that’s perfect: the last point we found starts it.
27453
27702
  // If there isn’t then make sure any remaining content is added to it.
27454
27703
  if (current) {
27455
27704
  // Help GC.
27456
- current._tokenizer = undefined
27457
- current.previous = undefined
27705
+ current._tokenizer = undefined;
27706
+ current.previous = undefined;
27458
27707
  } else {
27459
- breaks.pop()
27708
+ breaks.pop();
27460
27709
  }
27461
27710
 
27462
27711
  // Now splice the events from the subtokenizer into the current events,
27463
27712
  // moving back to front so that splice indices aren’t affected.
27464
- index = breaks.length
27713
+ index = breaks.length;
27465
27714
  while (index--) {
27466
- const slice = childEvents.slice(breaks[index], breaks[index + 1])
27467
- const start = startPositions.pop()
27468
- jumps.unshift([start, start + slice.length - 1])
27469
- splice(events, start, 2, slice)
27715
+ const slice = childEvents.slice(breaks[index], breaks[index + 1]);
27716
+ const start = startPositions.pop();
27717
+ jumps.push([start, start + slice.length - 1]);
27718
+ events.splice(start, 2, slice);
27470
27719
  }
27471
- index = -1
27720
+ jumps.reverse();
27721
+ index = -1;
27472
27722
  while (++index < jumps.length) {
27473
- gaps[adjust + jumps[index][0]] = adjust + jumps[index][1]
27474
- adjust += jumps[index][1] - jumps[index][0] - 1
27723
+ gaps[adjust + jumps[index][0]] = adjust + jumps[index][1];
27724
+ adjust += jumps[index][1] - jumps[index][0] - 1;
27475
27725
  }
27476
- return gaps
27726
+ return gaps;
27477
27727
  }
27478
-
27479
27728
  ;// ./node_modules/micromark/lib/postprocess.js
27480
27729
  /**
27481
27730
  * @typedef {import('micromark-util-types').Event} Event
@@ -74740,12 +74989,104 @@ const mdast = (text, opts = {}) => {
74740
74989
  */
74741
74990
  const jsxAcornParser = Parser.extend(acorn_jsx_default()());
74742
74991
 
74992
+ ;// ./lib/utils/literal-expression.ts
74993
+
74994
+ const unsupported = (description) => new Error(`not a literal expression: ${description}`);
74995
+ /** Narrows a resolved value for arithmetic, so operands never coerce to `NaN` or `"[object Object]"`. */
74996
+ const asNumber = (value) => {
74997
+ if (typeof value !== 'number')
74998
+ throw unsupported('non-numeric operand');
74999
+ return value;
75000
+ };
75001
+ const asOperand = (value) => {
75002
+ if (typeof value !== 'number' && typeof value !== 'string')
75003
+ throw unsupported('non-primitive operand');
75004
+ return value;
75005
+ };
75006
+ const ARITHMETIC_OPERATORS = {
75007
+ // `+` doubles as string concatenation, so it accepts a string on either side.
75008
+ '+': (left, right) => (typeof left === 'number' && typeof right === 'number' ? left + right : `${left}${right}`),
75009
+ '-': (left, right) => asNumber(left) - asNumber(right),
75010
+ '*': (left, right) => asNumber(left) * asNumber(right),
75011
+ '/': (left, right) => asNumber(left) / asNumber(right),
75012
+ };
75013
+ /** Resolve one estree node, recursing into arrays and objects. Throws for anything not on the allowlist. */
75014
+ const resolveNode = (node) => {
75015
+ switch (node.type) {
75016
+ case 'Literal':
75017
+ // Regex and bigint `Literal`s hold values a tree consumer can't serialise;
75018
+ // a BigInt throws outright in `JSON.stringify`.
75019
+ if ('regex' in node || 'bigint' in node)
75020
+ throw unsupported('regex or bigint literal');
75021
+ return node.value;
75022
+ case 'Identifier':
75023
+ if (node.name !== 'undefined')
75024
+ throw unsupported(`identifier \`${node.name}\``);
75025
+ return undefined;
75026
+ case 'TemplateLiteral':
75027
+ if (node.expressions.length)
75028
+ throw unsupported('template substitution');
75029
+ return node.quasis.map(quasi => quasi.value.cooked).join('');
75030
+ case 'UnaryExpression':
75031
+ if (node.operator !== '-')
75032
+ throw unsupported(`operator \`${node.operator}\``);
75033
+ return -asNumber(resolveNode(node.argument));
75034
+ case 'BinaryExpression': {
75035
+ const applyOperator = ARITHMETIC_OPERATORS[node.operator];
75036
+ if (!applyOperator)
75037
+ throw unsupported(`operator \`${node.operator}\``);
75038
+ return applyOperator(asOperand(resolveNode(node.left)), asOperand(resolveNode(node.right)));
75039
+ }
75040
+ case 'ArrayExpression':
75041
+ return node.elements.map(element => (element === null ? null : resolveNode(element)));
75042
+ case 'ObjectExpression':
75043
+ return node.properties.reduce((memo, property) => {
75044
+ if (property.type !== 'Property' || property.computed)
75045
+ throw unsupported('computed or spread property');
75046
+ const { key } = property;
75047
+ if (key.type !== 'Identifier' && key.type !== 'Literal')
75048
+ throw unsupported('property key');
75049
+ const name = key.type === 'Identifier' ? key.name : String(key.value);
75050
+ // `__proto__` would replace the object's prototype rather than add a property.
75051
+ if (name === '__proto__')
75052
+ throw unsupported('`__proto__` key');
75053
+ memo[name] = resolveNode(property.value);
75054
+ return memo;
75055
+ }, {});
75056
+ default:
75057
+ throw unsupported(node.type);
75058
+ }
75059
+ };
75060
+ /**
75061
+ * Resolve a JSX attribute expression's source to its value without executing it.
75062
+ *
75063
+ * Supports the literal syntax attributes actually use — `true`, `"a"`, `` `a` ``,
75064
+ * `undefined`, `{ textAlign: "left" }`, `["left"]`, `1 + 1`, `'https://' + 'x.com'` —
75065
+ * and refuses everything else, so no identifier, member access, call, assignment or
75066
+ * function body can ever run. The trade-off is that expressions needing a scope or a
75067
+ * method call (`{item.url}`, `{"a".toUpperCase()}`) throw, and callers keep the raw
75068
+ * source instead — the same fallback they already used for an expression that threw.
75069
+ * mdxish rendering is unaffected, since it resolves those later with its scoped
75070
+ * evaluator; only consumers reading attributes straight off the tree see the source.
75071
+ *
75072
+ * @param source expression body, without the surrounding braces
75073
+ * @throws if `source` is unparseable or isn't a supported literal expression
75074
+ */
75075
+ const evaluateLiteralExpression = (source) => {
75076
+ const expression = jsxAcornParser.parseExpressionAt(source, 0, { ecmaVersion: 'latest' });
75077
+ // acorn stops at the first complete expression, so anything trailing means this isn't one.
75078
+ if (expression.end !== source.trimEnd().length)
75079
+ throw unsupported('trailing content');
75080
+ return resolveNode(expression);
75081
+ };
75082
+
74743
75083
  ;// ./processor/utils.ts
74744
75084
 
74745
75085
 
74746
75086
 
74747
75087
 
74748
75088
 
75089
+
74749
75090
  /**
74750
75091
  * Evaluate a JavaScript expression source and return its value.
74751
75092
  *
@@ -74843,8 +75184,10 @@ const getAttrs = (jsx) => jsx.attributes.reduce((memo, attr) => {
74843
75184
  memo[attr.name] = decode_decodeHTMLStrict(attr.value);
74844
75185
  }
74845
75186
  else if (attr.value?.value !== undefined) {
75187
+ // Expression values only survive to here when safeMode is off; `flattenAttributeExpressions`
75188
+ // rewrites them to plain strings at the head of the pipeline otherwise.
74846
75189
  try {
74847
- memo[attr.name] = evaluate(attr.value.value);
75190
+ memo[attr.name] = evaluateLiteralExpression(attr.value.value);
74848
75191
  }
74849
75192
  catch {
74850
75193
  memo[attr.name] = attr.value.value;
@@ -75625,6 +75968,31 @@ const embedTransformer = () => {
75625
75968
  };
75626
75969
  /* harmony default export */ const transform_embeds = (embedTransformer);
75627
75970
 
75971
+ ;// ./processor/transform/flatten-attribute-expressions.ts
75972
+
75973
+
75974
+ /**
75975
+ * Rewrites JSX attribute expressions (`icon={String(1 + 3)}`) into plain string attributes
75976
+ * holding their literal source, so nothing downstream can evaluate them.
75977
+ *
75978
+ * This is the RMDX counterpart to mdxish's `preserveExpressionsAsText` parse option: safeMode's
75979
+ * contract is enforced once, at the head of the pipeline, rather than at every `getAttrs()` call
75980
+ * site. Only registered when safeMode is on.
75981
+ */
75982
+ const flattenAttributeExpressions = () => tree => {
75983
+ visit(tree, isMDXElement, (node) => {
75984
+ node.attributes.forEach(attr => {
75985
+ if (!('name' in attr))
75986
+ return;
75987
+ if (attr.value === null || typeof attr.value === 'string')
75988
+ return;
75989
+ attr.value = attr.value.value;
75990
+ });
75991
+ });
75992
+ return tree;
75993
+ };
75994
+ /* harmony default export */ const flatten_attribute_expressions = (flattenAttributeExpressions);
75995
+
75628
75996
  ;// ./node_modules/gemoji/index.js
75629
75997
  /**
75630
75998
  * @typedef Gemoji
@@ -101394,6 +101762,7 @@ const variables = ({ asMdx } = { asMdx: true }) => tree => {
101394
101762
 
101395
101763
 
101396
101764
 
101765
+
101397
101766
  const defaultTransforms = {
101398
101767
  calloutTransformer: callouts,
101399
101768
  codeTabsTransformer: code_tabs,
@@ -101421,6 +101790,9 @@ const astProcessor = (opts = {}) => {
101421
101790
  const components = opts.components || {};
101422
101791
  let processor = remark()
101423
101792
  .use(remarkMdx)
101793
+ // Must precede every other transformer: it strips evaluable attribute expressions so no
101794
+ // downstream `getAttrs()` call can reach `evaluate()`.
101795
+ .use(opts.safeMode ? flatten_attribute_expressions : undefined)
101424
101796
  .use(remarkPlugins)
101425
101797
  .use(opts.remarkPlugins)
101426
101798
  .use(transform_variables, { asMdx: false })
@@ -113491,7 +113863,9 @@ class MdxSyntaxError extends SyntaxError {
113491
113863
  * as `\r<br>`. Treating both characters as hard breaks doubles the spacing.
113492
113864
  */
113493
113865
  const hardBreaks = () => tree => {
113494
- findAndReplace(tree, [/\r?\n/g, () => ({ type: 'break' })]);
113866
+ // Skip `html-block`: its opaque payload is rendered verbatim, so turning its newlines
113867
+ // into `break` nodes is unnecessary and costly on large blocks.
113868
+ findAndReplace(tree, [/\r?\n/g, () => ({ type: 'break' })], { ignore: ['html-block'] });
113495
113869
  };
113496
113870
  /* harmony default export */ const hard_breaks = (hardBreaks);
113497
113871
 
@@ -115998,12 +116372,59 @@ const hastscript_lib_h = create_h_createH(node_modules_property_information_html
115998
116372
  /** @type {ReturnType<createH>} */
115999
116373
  const lib_s = create_h_createH(node_modules_property_information_svg, 'g', svg_case_sensitive_tag_names_svgCaseSensitiveTagNames)
116000
116374
 
116375
+ ;// ./utils/user.ts
116376
+ /**
116377
+ * Coerce a user variable value to a string for substitution into markdown text.
116378
+ * Non-string values (arrays, objects, numbers) are stringified via JSON or `String()`
116379
+ * so that `<<var>>` syntax doesn't produce `[object Object]` for structured data like
116380
+ * JWT `keys`.
116381
+ */
116382
+ const stringifyVariableValue = (value) => {
116383
+ if (typeof value === 'string')
116384
+ return value;
116385
+ if (value == null)
116386
+ return '';
116387
+ if (typeof value === 'object') {
116388
+ try {
116389
+ return JSON.stringify(value) ?? '';
116390
+ }
116391
+ catch {
116392
+ console.warn('[WARNING] Could not stringify a structured user variable.');
116393
+ return '';
116394
+ }
116395
+ }
116396
+ return String(value);
116397
+ };
116398
+ /**
116399
+ * Flatten `variables.user` into a string-keyed string-valued record by coercing
116400
+ * each value. Used by markdown substitution paths that need a plain
116401
+ * `Record<string, string>` lookup.
116402
+ */
116403
+ const flattenUserVariables = (user) => Object.fromEntries(Object.entries(user).map(([name, value]) => [name, stringifyVariableValue(value)]));
116404
+ const User = (variables) => {
116405
+ const { user = {}, defaults = [] } = variables || {};
116406
+ return new Proxy(user, {
116407
+ get(target, attribute) {
116408
+ if (typeof attribute === 'symbol') {
116409
+ return '';
116410
+ }
116411
+ if (attribute in target) {
116412
+ return target[attribute];
116413
+ }
116414
+ const def = defaults.find((d) => d.name === attribute);
116415
+ return def ? def.default : attribute.toUpperCase();
116416
+ },
116417
+ });
116418
+ };
116419
+ /* harmony default export */ const user = (User);
116420
+
116001
116421
  ;// ./processor/plugin/toc.ts
116002
116422
 
116003
116423
 
116004
116424
 
116005
116425
 
116006
116426
 
116427
+
116007
116428
  const HEADING_TAGS = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'];
116008
116429
  const isHeadingTag = (tag) => (tag ? HEADING_TAGS.includes(tag) : false);
116009
116430
  const isStandardHtmlElement = (node) => STANDARD_HTML_TAGS.has(node.tagName.toLowerCase());
@@ -116115,7 +116536,7 @@ const flattenVariables = (variables) => {
116115
116536
  if (!variables)
116116
116537
  return {};
116117
116538
  return {
116118
- ...variables.user,
116539
+ ...flattenUserVariables(variables.user),
116119
116540
  ...Object.fromEntries((variables.defaults || []).filter(d => !(d.name in variables.user)).map(d => [d.name, d.default])),
116120
116541
  };
116121
116542
  };
@@ -116263,7 +116684,7 @@ const exports_exports = (doc) => {
116263
116684
 
116264
116685
  const hast = (text, opts = {}) => {
116265
116686
  const components = Object.entries(opts.components || {}).reduce((memo, [name, doc]) => {
116266
- memo[name] = lib_mdast(doc);
116687
+ memo[name] = lib_mdast(doc, { safeMode: opts.safeMode });
116267
116688
  return memo;
116268
116689
  }, {});
116269
116690
  const processor = ast_processor(opts)
@@ -123960,6 +124381,10 @@ const NESTED_ATTR_EXPRESSION_RE = /[\w-]+\s*=\s*\{/;
123960
124381
  // Name shape mirrors `componentTagPattern`; the lookbehind skips the inner tag
123961
124382
  // of a legacy `<<VARIABLE>>`.
123962
124383
  const NESTED_COMPONENT_TAG_RE = /(?<!<)<([A-Z][A-Za-z0-9_]*)[\s/>]/g;
124384
+ // Whole blank lines at a component body's edges. `\n` sits inside each group so a
124385
+ // match can only ever span complete lines, never part of a content line.
124386
+ const LEADING_BLANK_LINES_RE = /^(?:[ \t]*\n)+/;
124387
+ const TRAILING_BLANK_LINES_RE = /(?:\n[ \t]*)+$/;
123963
124388
  // Excludes tags with dedicated transformers (`Table`, `HTMLBlock`, inline
123964
124389
  // components), which expect their wrapper to stay raw.
123965
124390
  const hasNestedGenericComponentTag = (content) => [...content.matchAll(NESTED_COMPONENT_TAG_RE)].some(match => !GENERIC_MDX_COMPONENT_EXCLUDED_TAGS.has(match[1]));
@@ -123993,13 +124418,21 @@ function safeDeindent(text) {
123993
124418
  })
123994
124419
  .join('\n');
123995
124420
  }
124421
+ /**
124422
+ * Drop the blank lines at a component body's edges, keeping the first content line's
124423
+ * indent: `.trim()` stripped that line alone, so a uniformly indented body reparsed
124424
+ * lopsided and every list item after the first nested. The trailing pattern
124425
+ * takes the last content line's newline but leaves its spaces, which are code content
124426
+ * when a fence runs into the end tag.
124427
+ */
124428
+ const trimBlankEdges = (text) => text.replace(LEADING_BLANK_LINES_RE, '').replace(TRAILING_BLANK_LINES_RE, '');
123996
124429
  /**
123997
124430
  * Parse component-body markdown into mdast children. Dedenting shifts columns and
123998
124431
  * stales the top-level `terminateHtmlFlowBlocks` decisions, so that one preprocessor
123999
124432
  * re-runs here; other column-anchored fixups (compact headings, tables) do not.
124000
124433
  */
124001
124434
  const parseMdChildren = (value, safeMode) => {
124002
- const reparseSource = terminateHtmlFlowBlocks(safeDeindent(value).trim());
124435
+ const reparseSource = terminateHtmlFlowBlocks(trimBlankEdges(safeDeindent(value)));
124003
124436
  const parsed = getInlineMdProcessor({ safeMode }).parse(reparseSource);
124004
124437
  // Promote nested wrappers bottom-up so an outer wrapper sees markdown buried in a
124005
124438
  // child claimed whole (e.g. `<li>` in `<ol>`) before its containsMarkdownConstruct check (RM-17560).
@@ -124164,7 +124597,8 @@ function promoteComponentBlocks(tree, safeMode, source) {
124164
124597
  // Case 2: Self-contained block (closing tag in content)
124165
124598
  const closingTagIndex = isPlainLowercaseHtml ? plainClosingTagIndex : contentAfterTag.lastIndexOf(closingTagStr);
124166
124599
  if (closingTagIndex >= 0) {
124167
- // Untrimmed so parseMdChildren can dedent before trimming.
124600
+ // Left as authored: parseMdChildren needs the original columns to dedent by, and
124601
+ // it clears the blank edges itself.
124168
124602
  const componentInnerContent = contentAfterTag.substring(0, closingTagIndex);
124169
124603
  const contentAfterClose = contentAfterTag.substring(closingTagIndex + closingTagStr.length).trim();
124170
124604
  let parsedChildren = [];
@@ -126886,6 +127320,7 @@ function normalizeTableSeparator(content) {
126886
127320
  ;// ./processor/transform/mdxish/variables-code.ts
126887
127321
 
126888
127322
 
127323
+
126889
127324
  // Single combined regex so that resolved values from one pattern are never re-scanned by the other.
126890
127325
  const COMBINED_VARIABLE_REGEX = new RegExp(`${variable_dist.VARIABLE_REGEXP}|${variable_dist.MDX_VARIABLE_REGEXP}`, 'giu');
126891
127326
  // Flatten variables into a single object for easy lookup
@@ -126894,7 +127329,7 @@ function variables_code_flattenVariables(variables) {
126894
127329
  return {};
126895
127330
  return {
126896
127331
  ...Object.fromEntries((variables.defaults || []).map(d => [d.name, d.default])),
126897
- ...variables.user,
127332
+ ...flattenUserVariables(variables.user),
126898
127333
  };
126899
127334
  }
126900
127335
  function resolveCodeVariables(value, resolvedVariables) {
@@ -127438,24 +127873,6 @@ const Contexts = ({ children, terms = [], variables = { user: {}, defaults: [] }
127438
127873
  };
127439
127874
  /* harmony default export */ const contexts = (Contexts);
127440
127875
 
127441
- ;// ./utils/user.ts
127442
- const User = (variables) => {
127443
- const { user = {}, defaults = [] } = variables || {};
127444
- return new Proxy(user, {
127445
- get(target, attribute) {
127446
- if (typeof attribute === 'symbol') {
127447
- return '';
127448
- }
127449
- if (attribute in target) {
127450
- return target[attribute];
127451
- }
127452
- const def = defaults.find((d) => d.name === attribute);
127453
- return def ? def.default : attribute.toUpperCase();
127454
- },
127455
- });
127456
- };
127457
- /* harmony default export */ const user = (User);
127458
-
127459
127876
  ;// ./lib/utils/makeUseMdxComponents.ts
127460
127877
 
127461
127878
 
@@ -127488,6 +127905,7 @@ const makeUseMDXComponents = (more = {}) => {
127488
127905
 
127489
127906
  ;// ./lib/utils/mdxish/mdxish-variables.ts
127490
127907
 
127908
+
127491
127909
  // The `$` guard skips template-literal interpolation: `${user.name}` embeds `{user.name}`, and
127492
127910
  // substituting it would leave a mangled `` `Hi $Name` `` behind. Those belong to an expression,
127493
127911
  // which either evaluated already or is meant to stay literal.
@@ -127511,7 +127929,7 @@ function resolveAttributeVariables(value, user) {
127511
127929
  .replace(MDX_VARIABLE_REGEX, (source, escapePrefix, name, escapeSuffix) => {
127512
127930
  if (escapePrefix || escapeSuffix)
127513
127931
  return source;
127514
- return user[name];
127932
+ return stringifyVariableValue(user[name]);
127515
127933
  });
127516
127934
  }
127517
127935
 
@@ -127787,7 +128205,8 @@ const run_run = (string, _opts = {}) => {
127787
128205
 
127788
128206
  const tags = (doc) => {
127789
128207
  const set = new Set();
127790
- visit(lib_mdast(doc), isMDXElement, (node) => {
128208
+ // Tag names never depend on evaluated attribute values, so always parse in safeMode.
128209
+ visit(lib_mdast(doc, { safeMode: true }), isMDXElement, (node) => {
127791
128210
  if (node.name?.match(/^[A-Z]/)) {
127792
128211
  set.add(node.name);
127793
128212
  }
@@ -127803,13 +128222,14 @@ const tags = (doc) => {
127803
128222
 
127804
128223
 
127805
128224
 
127806
- const { micromarkExtensions, fromMarkdownExtensions } = mdxishExtensions(FEATURES.tags);
128225
+ const { micromarkExtensions, fromMarkdownExtensions } = mdxishExtensions(FEATURES.tags, { safeMode: true });
127807
128226
  const mdxishTags_tags = (doc) => {
127808
128227
  const set = new Set();
127809
128228
  const processor = remark()
127810
128229
  .data('micromarkExtensions', micromarkExtensions)
127811
128230
  .data('fromMarkdownExtensions', fromMarkdownExtensions)
127812
- .use(mdx_blocks)
128231
+ // Tag names never depend on evaluated attribute values, so always parse in safeMode.
128232
+ .use(mdx_blocks, { safeMode: true })
127813
128233
  .use(mdxish_tables);
127814
128234
  const tree = processor.parse(doc);
127815
128235
  visit(processor.runSync(tree), isMDXElement, (node) => {