@readme/markdown 14.11.5 → 14.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/main.node.js CHANGED
@@ -24839,17 +24839,12 @@ const extractScripts = (html = '') => {
24839
24839
  return [cleaned, () => scripts.map(js => window.eval(js))];
24840
24840
  };
24841
24841
  const HTMLBlock = ({ children = '', html: htmlProp, runScripts, safeMode: safeModeRaw = false }) => {
24842
- // Determine HTML source: MDXish uses html prop (from HAST), MDX uses children
24843
- let html = '';
24844
- if (htmlProp !== undefined) {
24845
- html = htmlProp;
24846
- }
24847
- else {
24848
- if (typeof children !== 'string') {
24849
- throw new TypeError('HTMLBlock: children must be a string');
24850
- }
24851
- html = children;
24852
- }
24842
+ // Determine HTML source: MDXish uses html prop (from HAST), MDX uses children.
24843
+ // A non-string child (no html prop) can't be injected as raw HTML — see the
24844
+ // fail-soft fallback below.
24845
+ const htmlSource = htmlProp !== undefined ? htmlProp : children;
24846
+ const nonStringChildren = typeof htmlSource !== 'string';
24847
+ const html = nonStringChildren ? '' : htmlSource;
24853
24848
  // eslint-disable-next-line no-param-reassign
24854
24849
  runScripts = typeof runScripts !== 'boolean' ? runScripts === 'true' : runScripts;
24855
24850
  // In MDX mode, safeMode is passed in as a boolean from JSX parsing
@@ -24860,6 +24855,11 @@ const HTMLBlock = ({ children = '', html: htmlProp, runScripts, safeMode: safeMo
24860
24855
  if (typeof window !== 'undefined' && typeof runScripts === 'boolean' && runScripts)
24861
24856
  exec();
24862
24857
  }, [runScripts, exec]);
24858
+ if (nonStringChildren) {
24859
+ // Fail soft: a non-string child (e.g. JSX that wasn't serialized back to a
24860
+ // raw string) should never throw, so render the child nodes directly
24861
+ return external_react_default().createElement("div", { className: "rdmd-html" }, children);
24862
+ }
24863
24863
  if (safeMode) {
24864
24864
  return (external_react_default().createElement("pre", { className: "html-unsafe" },
24865
24865
  external_react_default().createElement("code", null, html)));
@@ -96670,6 +96670,9 @@ const TOP_LEVEL_TABLE_TAG_RE = /^<(?:table|Table)(?=[\s/>])/;
96670
96670
  * Uses `walkTags` so `<table>`s inside code spans / fenced blocks (masked away)
96671
96671
  * are never matched.
96672
96672
  *
96673
+ * `<table>`s inside an `<HTMLBlock>` body are also ignored: that body is opaque
96674
+ * raw HTML, so lifting a table out of it would corrupt the block.
96675
+ *
96673
96676
  * Note: An implicitly-closed table (no `</table>`, so htmlparser2 synthesizes the
96674
96677
  * close at a later tag) is skipped: splitting there would swallow whatever
96675
96678
  * followed the table into the fragment and drop it, so we leave such a node
@@ -96677,21 +96680,35 @@ const TOP_LEVEL_TABLE_TAG_RE = /^<(?:table|Table)(?=[\s/>])/;
96677
96680
  */
96678
96681
  const findTableRanges = (html) => {
96679
96682
  const ranges = [];
96680
- let depth = 0;
96683
+ let tableDepth = 0;
96684
+ let htmlBlockDepth = 0;
96681
96685
  let start = 0;
96682
96686
  walkTags(html, {
96683
- onOpen: ({ name, start: openStart, isStrayCloser }) => {
96684
- if (name.toLowerCase() !== 'table' || isStrayCloser)
96687
+ onOpen: ({ name, start: openStart, isSelfClosing, isStrayCloser }) => {
96688
+ if (isStrayCloser)
96685
96689
  return;
96686
- if (depth === 0)
96690
+ // `<HTMLBlock/>` has no body to protect; only a real open enters one.
96691
+ if (name === 'HTMLBlock') {
96692
+ if (!isSelfClosing)
96693
+ htmlBlockDepth += 1;
96694
+ return;
96695
+ }
96696
+ if (htmlBlockDepth > 0 || name.toLowerCase() !== 'table')
96697
+ return;
96698
+ if (tableDepth === 0)
96687
96699
  start = openStart;
96688
- depth += 1;
96700
+ tableDepth += 1;
96689
96701
  },
96690
96702
  onClose: ({ name, end, implicit }) => {
96691
- if (implicit || name.toLowerCase() !== 'table' || depth === 0)
96703
+ if (name === 'HTMLBlock') {
96704
+ if (!implicit && htmlBlockDepth > 0)
96705
+ htmlBlockDepth -= 1;
96692
96706
  return;
96693
- depth -= 1;
96694
- if (depth === 0)
96707
+ }
96708
+ if (implicit || htmlBlockDepth > 0 || name.toLowerCase() !== 'table' || tableDepth === 0)
96709
+ return;
96710
+ tableDepth -= 1;
96711
+ if (tableDepth === 0)
96695
96712
  ranges.push({ start, end });
96696
96713
  },
96697
96714
  });
@@ -96711,7 +96728,9 @@ const splitHtmlWithNestedTables = (node) => {
96711
96728
  // This is a top-level table, so we don't need to split it
96712
96729
  if (TOP_LEVEL_TABLE_TAG_RE.test(value))
96713
96730
  return null;
96714
- // No table text anywhere in the value → skip the htmlparser2 walk entirely.
96731
+ // No table text anywhere → skip the htmlparser2 walk entirely. (A `<table>` that
96732
+ // only appears inside an `<HTMLBlock>` body still yields no ranges below, so it's
96733
+ // left whole for `mdxishHtmlBlocks` to convert to an html-block next.)
96715
96734
  if (!/<\/?table/i.test(value))
96716
96735
  return null;
96717
96736
  const ranges = findTableRanges(value);
@@ -109863,63 +109882,6 @@ function rehypeSanitize(options) {
109863
109882
  }
109864
109883
  }
109865
109884
 
109866
- ;// ./node_modules/mdast-util-newline-to-break/lib/index.js
109867
- /**
109868
- * @typedef {import('mdast').Nodes} Nodes
109869
- * @typedef {import('mdast-util-find-and-replace').ReplaceFunction} ReplaceFunction
109870
- */
109871
-
109872
-
109873
-
109874
- /**
109875
- * Turn normal line endings into hard breaks.
109876
- *
109877
- * @param {Nodes} tree
109878
- * Tree to change.
109879
- * @returns {undefined}
109880
- * Nothing.
109881
- */
109882
- function newlineToBreak(tree) {
109883
- findAndReplace(tree, [/\r?\n|\r/g, lib_replace])
109884
- }
109885
-
109886
- /**
109887
- * Replace line endings.
109888
- *
109889
- * @type {ReplaceFunction}
109890
- */
109891
- function lib_replace() {
109892
- return {type: 'break'}
109893
- }
109894
-
109895
- ;// ./node_modules/remark-breaks/lib/index.js
109896
- /**
109897
- * @typedef {import('mdast').Root} Root
109898
- */
109899
-
109900
-
109901
-
109902
- /**
109903
- * Support hard breaks without needing spaces or escapes (turns enters into
109904
- * `<br>`s).
109905
- *
109906
- * @returns
109907
- * Transform.
109908
- */
109909
- function remarkBreaks() {
109910
- /**
109911
- * Transform.
109912
- *
109913
- * @param {Root} tree
109914
- * Tree.
109915
- * @returns {undefined}
109916
- * Nothing.
109917
- */
109918
- return function (tree) {
109919
- newlineToBreak(tree)
109920
- }
109921
- }
109922
-
109923
109885
  ;// ./errors/mdx-syntax-error.ts
109924
109886
  class MdxSyntaxError extends SyntaxError {
109925
109887
  original = null;
@@ -109941,6 +109903,22 @@ class MdxSyntaxError extends SyntaxError {
109941
109903
  }
109942
109904
  }
109943
109905
 
109906
+ ;// ./processor/plugin/hard-breaks.ts
109907
+
109908
+ /**
109909
+ * Converts LF and CRLF line endings into hard breaks while leaving standalone
109910
+ * carriage returns as soft whitespace.
109911
+ *
109912
+ * Unlike `remark-breaks`, this does not promote a lone `\r` into a `<br>`.
109913
+ * Standalone carriage returns can be left behind by generators that replace
109914
+ * the LF in Windows line endings with an explicit `<br>`, producing input such
109915
+ * as `\r<br>`. Treating both characters as hard breaks doubles the spacing.
109916
+ */
109917
+ const hardBreaks = () => tree => {
109918
+ findAndReplace(tree, [/\r?\n/g, () => ({ type: 'break' })]);
109919
+ };
109920
+ /* harmony default export */ const hard_breaks = (hardBreaks);
109921
+
109944
109922
  ;// ./node_modules/estree-util-value-to-estree/dist/estree-util-value-to-estree.js
109945
109923
  /**
109946
109924
  * Create an ESTree identifier node for a given name.
@@ -112645,7 +112623,7 @@ const compile_compile = (text, { components = {}, missingComponents, copyButtons
112645
112623
  const remarkPlugins = [
112646
112624
  remarkFrontmatter,
112647
112625
  remarkGfm,
112648
- ...(hardBreaks ? [remarkBreaks] : []),
112626
+ ...(hardBreaks ? [hard_breaks] : []),
112649
112627
  ...Object.values(transforms),
112650
112628
  [codeTabsTransformer, { copyButtons }],
112651
112629
  [
@@ -119873,6 +119851,55 @@ function emptyTaskListItemFromMarkdown() {
119873
119851
  };
119874
119852
  }
119875
119853
 
119854
+ ;// ./lib/mdast-util/jsx-table/index.ts
119855
+ const jsx_table_contextMap = new WeakMap();
119856
+ function findJsxTableToken() {
119857
+ const events = this.tokenStack;
119858
+ for (let i = events.length - 1; i >= 0; i -= 1) {
119859
+ if (events[i][0].type === 'jsxTable')
119860
+ return events[i][0];
119861
+ }
119862
+ return undefined;
119863
+ }
119864
+ function enterJsxTable(token) {
119865
+ jsx_table_contextMap.set(token, { chunks: [], lastEndLine: token.start.line });
119866
+ this.enter({ type: 'html', value: '' }, token);
119867
+ }
119868
+ function exitJsxTableData(token) {
119869
+ const tableToken = findJsxTableToken.call(this);
119870
+ if (!tableToken)
119871
+ return;
119872
+ const ctx = jsx_table_contextMap.get(tableToken);
119873
+ if (ctx) {
119874
+ const gap = token.start.line - ctx.lastEndLine;
119875
+ if (ctx.chunks.length > 0 && gap > 0) {
119876
+ ctx.chunks.push('\n'.repeat(gap));
119877
+ }
119878
+ ctx.chunks.push(this.sliceSerialize(token));
119879
+ ctx.lastEndLine = token.end.line;
119880
+ }
119881
+ }
119882
+ function exitJsxTable(token) {
119883
+ const ctx = jsx_table_contextMap.get(token);
119884
+ const node = this.stack[this.stack.length - 1];
119885
+ if (ctx) {
119886
+ node.value = ctx.chunks.join('');
119887
+ jsx_table_contextMap.delete(token);
119888
+ }
119889
+ this.exit(token);
119890
+ }
119891
+ function jsxTableFromMarkdown() {
119892
+ return {
119893
+ enter: {
119894
+ jsxTable: enterJsxTable,
119895
+ },
119896
+ exit: {
119897
+ jsxTableData: exitJsxTableData,
119898
+ jsxTable: exitJsxTable,
119899
+ },
119900
+ };
119901
+ }
119902
+
119876
119903
  ;// ./lib/mdast-util/magic-block/index.ts
119877
119904
  const magic_block_contextMap = new WeakMap();
119878
119905
  /**
@@ -120062,867 +120089,16 @@ function mdxComponentFromMarkdown() {
120062
120089
  };
120063
120090
  }
120064
120091
 
120065
- ;// ./lib/micromark/magic-block/syntax.ts
120066
-
120067
-
120068
- /**
120069
- * Known magic block types that the tokenizer will recognize.
120070
- * Unknown types will not be tokenized as magic blocks.
120071
- */
120072
- const KNOWN_BLOCK_TYPES = new Set([
120073
- 'code',
120074
- 'api-header',
120075
- 'image',
120076
- 'callout',
120077
- 'parameters',
120078
- 'table',
120079
- 'embed',
120080
- 'html',
120081
- 'recipe',
120082
- 'tutorial-tile',
120083
- ]);
120084
- /**
120085
- * Check if a character is valid for a magic block type identifier.
120086
- * Types can contain alphanumeric characters and hyphens (e.g., "api-header", "tutorial-tile")
120087
- */
120088
- function isTypeChar(code) {
120089
- return asciiAlphanumeric(code) || code === codes.dash;
120090
- }
120091
- /**
120092
- * Creates the opening marker state machine: [block:
120093
- * Returns the first state function to start parsing.
120094
- */
120095
- function createOpeningMarkerParser(effects, nok, onComplete) {
120096
- const expectB = (code) => {
120097
- if (code !== codes.lowercaseB)
120098
- return nok(code);
120099
- effects.consume(code);
120100
- return expectL;
120101
- };
120102
- const expectL = (code) => {
120103
- if (code !== codes.lowercaseL)
120104
- return nok(code);
120105
- effects.consume(code);
120106
- return expectO;
120107
- };
120108
- const expectO = (code) => {
120109
- if (code !== codes.lowercaseO)
120110
- return nok(code);
120111
- effects.consume(code);
120112
- return expectC;
120113
- };
120114
- const expectC = (code) => {
120115
- if (code !== codes.lowercaseC)
120116
- return nok(code);
120117
- effects.consume(code);
120118
- return expectK;
120119
- };
120120
- const expectK = (code) => {
120121
- if (code !== codes.lowercaseK)
120122
- return nok(code);
120123
- effects.consume(code);
120124
- return expectColon;
120125
- };
120126
- const expectColon = (code) => {
120127
- if (code !== codes.colon)
120128
- return nok(code);
120129
- effects.consume(code);
120130
- effects.exit('magicBlockMarkerStart');
120131
- effects.enter('magicBlockType');
120132
- return onComplete;
120133
- };
120134
- return expectB;
120135
- }
120136
- /**
120137
- * Creates the type capture state machine.
120138
- * Captures type characters until ] and validates against known types.
120139
- */
120140
- function createTypeCaptureParser(effects, nok, onComplete, blockTypeRef) {
120141
- const captureTypeFirst = (code) => {
120142
- // Reject empty type name [block:]
120143
- if (code === codes.rightSquareBracket) {
120144
- return nok(code);
120145
- }
120146
- if (isTypeChar(code)) {
120147
- blockTypeRef.value += String.fromCharCode(code);
120148
- effects.consume(code);
120149
- return captureType;
120150
- }
120151
- return nok(code);
120152
- };
120153
- const captureType = (code) => {
120154
- if (code === codes.rightSquareBracket) {
120155
- if (!KNOWN_BLOCK_TYPES.has(blockTypeRef.value)) {
120156
- return nok(code);
120157
- }
120158
- effects.exit('magicBlockType');
120159
- effects.enter('magicBlockMarkerTypeEnd');
120160
- effects.consume(code);
120161
- effects.exit('magicBlockMarkerTypeEnd');
120162
- return onComplete;
120163
- }
120164
- if (isTypeChar(code)) {
120165
- blockTypeRef.value += String.fromCharCode(code);
120166
- effects.consume(code);
120167
- return captureType;
120168
- }
120169
- return nok(code);
120170
- };
120171
- return { first: captureTypeFirst, remaining: captureType };
120172
- }
120173
- /**
120174
- * Creates the closing marker state machine: /block]
120175
- * Handles partial matches by calling onMismatch to fall back to data capture.
120176
- */
120177
- function createClosingMarkerParser(effects, onSuccess, onMismatch, onEof, jsonState) {
120178
- const handleMismatch = (code) => {
120179
- if (jsonState && code === codes.quotationMark) {
120180
- jsonState.inString = true;
120181
- }
120182
- return onMismatch(code);
120183
- };
120184
- const expectSlash = (code) => {
120185
- if (code === null)
120186
- return onEof(code);
120187
- if (code !== codes.slash)
120188
- return handleMismatch(code);
120189
- effects.consume(code);
120190
- return expectB;
120191
- };
120192
- const expectB = (code) => {
120193
- if (code === null)
120194
- return onEof(code);
120195
- if (code !== codes.lowercaseB)
120196
- return handleMismatch(code);
120197
- effects.consume(code);
120198
- return expectL;
120199
- };
120200
- const expectL = (code) => {
120201
- if (code === null)
120202
- return onEof(code);
120203
- if (code !== codes.lowercaseL)
120204
- return handleMismatch(code);
120205
- effects.consume(code);
120206
- return expectO;
120207
- };
120208
- const expectO = (code) => {
120209
- if (code === null)
120210
- return onEof(code);
120211
- if (code !== codes.lowercaseO)
120212
- return handleMismatch(code);
120213
- effects.consume(code);
120214
- return expectC;
120215
- };
120216
- const expectC = (code) => {
120217
- if (code === null)
120218
- return onEof(code);
120219
- if (code !== codes.lowercaseC)
120220
- return handleMismatch(code);
120221
- effects.consume(code);
120222
- return expectK;
120223
- };
120224
- const expectK = (code) => {
120225
- if (code === null)
120226
- return onEof(code);
120227
- if (code !== codes.lowercaseK)
120228
- return handleMismatch(code);
120229
- effects.consume(code);
120230
- return expectBracket;
120231
- };
120232
- const expectBracket = (code) => {
120233
- if (code === null)
120234
- return onEof(code);
120235
- if (code !== codes.rightSquareBracket)
120236
- return handleMismatch(code);
120237
- effects.consume(code);
120238
- return onSuccess;
120239
- };
120240
- return { expectSlash };
120241
- }
120242
- /**
120243
- * Partial construct for checking non-lazy continuation.
120244
- * This is used by the flow tokenizer to check if we can continue
120245
- * parsing on the next line.
120246
- */
120247
- const syntax_nonLazyContinuation = {
120248
- partial: true,
120249
- tokenize: syntax_tokenizeNonLazyContinuation,
120250
- };
120251
- /**
120252
- * Tokenizer for non-lazy continuation checking.
120253
- * Returns ok if the next line is non-lazy (can continue), nok if lazy.
120254
- */
120255
- function syntax_tokenizeNonLazyContinuation(effects, ok, nok) {
120256
- const lineStart = (code) => {
120257
- // `this` here refers to the micromark parser
120258
- // since we are just passing functions as references and not actually calling them
120259
- // micromarks's internal parser will call this automatically with appropriate arguments
120260
- return this.parser.lazy[this.now().line] ? nok(code) : ok(code);
120261
- };
120262
- const start = (code) => {
120263
- if (code === null) {
120264
- return nok(code);
120265
- }
120266
- if (!markdownLineEnding(code)) {
120267
- return nok(code);
120268
- }
120269
- effects.enter('magicBlockLineEnding');
120270
- effects.consume(code);
120271
- effects.exit('magicBlockLineEnding');
120272
- return lineStart;
120273
- };
120274
- return start;
120275
- }
120092
+ ;// ./node_modules/micromark-util-symbol/lib/types.js
120276
120093
  /**
120277
- * Create a micromark extension for magic block syntax.
120278
- *
120279
- * This extension handles both single-line and multiline magic blocks:
120280
- * - Flow construct (concrete): Handles block-level multiline magic blocks at document level
120281
- * - Text construct: Handles inline magic blocks in lists, paragraphs, etc.
120094
+ * This module is compiled away!
120282
120095
  *
120283
- * The flow construct is marked as "concrete" which prevents it from being
120284
- * interrupted by container markers (like `>` for blockquotes or `-` for lists).
120285
- */
120286
- function magicBlock() {
120287
- return {
120288
- // Flow construct - handles block-level magic blocks at document root
120289
- // Marked as concrete to prevent interruption by containers
120290
- flow: {
120291
- [codes.leftSquareBracket]: {
120292
- name: 'magicBlock',
120293
- concrete: true,
120294
- tokenize: tokenizeMagicBlockFlow,
120295
- },
120296
- },
120297
- // Text construct - handles magic blocks in inline contexts (lists, paragraphs)
120298
- text: {
120299
- [codes.leftSquareBracket]: {
120300
- name: 'magicBlock',
120301
- tokenize: tokenizeMagicBlockText,
120302
- },
120303
- },
120304
- };
120305
- }
120306
- /**
120307
- * Flow tokenizer for block-level magic blocks (multiline).
120308
- * Uses the continuation checking pattern from code fences.
120309
- */
120310
- function tokenizeMagicBlockFlow(effects, ok, nok) {
120311
- // State for tracking JSON content
120312
- const jsonState = { escapeNext: false, inString: false };
120313
- const blockTypeRef = { value: '' };
120314
- let seenOpenBrace = false;
120315
- // Create shared parsers for opening marker and type capture
120316
- const typeParser = createTypeCaptureParser(effects, nok, beforeData, blockTypeRef);
120317
- const openingMarkerParser = createOpeningMarkerParser(effects, nok, typeParser.first);
120318
- return start;
120319
- function start(code) {
120320
- if (code !== codes.leftSquareBracket)
120321
- return nok(code);
120322
- effects.enter('magicBlock');
120323
- effects.enter('magicBlockMarkerStart');
120324
- effects.consume(code);
120325
- return openingMarkerParser;
120326
- }
120327
- /**
120328
- * State before data content - handles line endings or starts data capture.
120329
- */
120330
- function beforeData(code) {
120331
- // EOF - magic block must be closed
120332
- if (code === null) {
120333
- effects.exit('magicBlock');
120334
- return nok(code);
120335
- }
120336
- // Line ending before any data - check continuation
120337
- if (markdownLineEnding(code)) {
120338
- return effects.check(syntax_nonLazyContinuation, continuationOkBeforeData, after)(code);
120339
- }
120340
- // Check for closing marker directly (without entering data)
120341
- // This handles cases like [block:type]\n{}\n\n[/block] where there are
120342
- // newlines after the data object
120343
- if (code === codes.leftSquareBracket) {
120344
- effects.enter('magicBlockMarkerEnd');
120345
- effects.consume(code);
120346
- return expectSlashFromContinuation;
120347
- }
120348
- // If we've already seen the opening brace, just continue capturing data
120349
- if (seenOpenBrace) {
120350
- effects.enter('magicBlockData');
120351
- return captureData(code);
120352
- }
120353
- // Skip whitespace (spaces/tabs) before the data - stay in beforeData
120354
- // Don't enter magicBlockData token yet until we confirm there's a '{'
120355
- if (code === codes.space || code === codes.horizontalTab) {
120356
- return beforeDataWhitespace(code);
120357
- }
120358
- // Data must start with '{' for valid JSON
120359
- if (code !== codes.leftCurlyBrace) {
120360
- effects.exit('magicBlock');
120361
- return nok(code);
120362
- }
120363
- // We have '{' - enter data token and start capturing
120364
- seenOpenBrace = true;
120365
- effects.enter('magicBlockData');
120366
- return captureData(code);
120367
- }
120368
- /**
120369
- * Consume whitespace before the data without creating a token.
120370
- * Uses a temporary token to satisfy micromark's requirement.
120371
- */
120372
- function beforeDataWhitespace(code) {
120373
- if (code === null) {
120374
- effects.exit('magicBlock');
120375
- return nok(code);
120376
- }
120377
- if (markdownLineEnding(code)) {
120378
- return effects.check(syntax_nonLazyContinuation, continuationOkBeforeData, after)(code);
120379
- }
120380
- if (code === codes.space || code === codes.horizontalTab) {
120381
- // We need to consume this but can't without a token - use magicBlockData
120382
- // and track that we haven't seen '{' yet
120383
- effects.enter('magicBlockData');
120384
- effects.consume(code);
120385
- return beforeDataWhitespaceContinue;
120386
- }
120387
- if (code === codes.leftCurlyBrace) {
120388
- seenOpenBrace = true;
120389
- effects.enter('magicBlockData');
120390
- return captureData(code);
120391
- }
120392
- effects.exit('magicBlock');
120393
- return nok(code);
120394
- }
120395
- /**
120396
- * Continue consuming whitespace or validate the opening brace.
120397
- */
120398
- function beforeDataWhitespaceContinue(code) {
120399
- if (code === null) {
120400
- effects.exit('magicBlockData');
120401
- effects.exit('magicBlock');
120402
- return nok(code);
120403
- }
120404
- if (markdownLineEnding(code)) {
120405
- effects.exit('magicBlockData');
120406
- return effects.check(syntax_nonLazyContinuation, continuationOk, after)(code);
120407
- }
120408
- if (code === codes.space || code === codes.horizontalTab) {
120409
- effects.consume(code);
120410
- return beforeDataWhitespaceContinue;
120411
- }
120412
- if (code === codes.leftCurlyBrace) {
120413
- seenOpenBrace = true;
120414
- return captureData(code);
120415
- }
120416
- effects.exit('magicBlockData');
120417
- effects.exit('magicBlock');
120418
- return nok(code);
120419
- }
120420
- /**
120421
- * Continuation OK before we've entered data token.
120422
- */
120423
- function continuationOkBeforeData(code) {
120424
- effects.enter('magicBlockLineEnding');
120425
- effects.consume(code);
120426
- effects.exit('magicBlockLineEnding');
120427
- return beforeData; // Stay in beforeData state - don't enter magicBlockData yet
120428
- }
120429
- function captureData(code) {
120430
- // EOF - magic block must be closed
120431
- if (code === null) {
120432
- effects.exit('magicBlockData');
120433
- effects.exit('magicBlock');
120434
- return nok(code);
120435
- }
120436
- // At line ending, check if we can continue on the next line
120437
- if (markdownLineEnding(code)) {
120438
- effects.exit('magicBlockData');
120439
- return effects.check(syntax_nonLazyContinuation, continuationOk, after)(code);
120440
- }
120441
- if (jsonState.escapeNext) {
120442
- jsonState.escapeNext = false;
120443
- effects.consume(code);
120444
- return captureData;
120445
- }
120446
- if (jsonState.inString) {
120447
- if (code === codes.backslash) {
120448
- jsonState.escapeNext = true;
120449
- effects.consume(code);
120450
- return captureData;
120451
- }
120452
- if (code === codes.quotationMark) {
120453
- jsonState.inString = false;
120454
- }
120455
- effects.consume(code);
120456
- return captureData;
120457
- }
120458
- if (code === codes.quotationMark) {
120459
- jsonState.inString = true;
120460
- effects.consume(code);
120461
- return captureData;
120462
- }
120463
- if (code === codes.leftSquareBracket) {
120464
- effects.exit('magicBlockData');
120465
- effects.enter('magicBlockMarkerEnd');
120466
- effects.consume(code);
120467
- return expectSlash;
120468
- }
120469
- effects.consume(code);
120470
- return captureData;
120471
- }
120472
- /**
120473
- * Called when non-lazy continuation check passes - we can continue parsing.
120474
- */
120475
- function continuationOk(code) {
120476
- // Consume the line ending
120477
- effects.enter('magicBlockLineEnding');
120478
- effects.consume(code);
120479
- effects.exit('magicBlockLineEnding');
120480
- return continuationStart;
120481
- }
120482
- /**
120483
- * Start of continuation line - check for more line endings, closing marker, or start capturing data.
120484
- */
120485
- function continuationStart(code) {
120486
- // Handle consecutive line endings
120487
- if (code === null) {
120488
- effects.exit('magicBlock');
120489
- return nok(code);
120490
- }
120491
- if (markdownLineEnding(code)) {
120492
- return effects.check(syntax_nonLazyContinuation, continuationOkBeforeData, after)(code);
120493
- }
120494
- // Check if this is the start of the closing marker [/block]
120495
- // If so, handle it directly without entering magicBlockData
120496
- if (code === codes.leftSquareBracket) {
120497
- effects.enter('magicBlockMarkerEnd');
120498
- effects.consume(code);
120499
- return expectSlashFromContinuation;
120500
- }
120501
- effects.enter('magicBlockData');
120502
- return captureData(code);
120503
- }
120504
- /**
120505
- * Check for closing marker slash when coming from continuation.
120506
- * If not a closing marker, create an empty data token and continue.
120507
- */
120508
- function expectSlashFromContinuation(code) {
120509
- if (code === null) {
120510
- effects.exit('magicBlockMarkerEnd');
120511
- effects.exit('magicBlock');
120512
- return nok(code);
120513
- }
120514
- if (markdownLineEnding(code)) {
120515
- effects.exit('magicBlockMarkerEnd');
120516
- return effects.check(syntax_nonLazyContinuation, continuationOkBeforeData, after)(code);
120517
- }
120518
- if (code === codes.slash) {
120519
- effects.consume(code);
120520
- return expectClosingB;
120521
- }
120522
- // Not a closing marker - this is data content
120523
- // Exit marker and enter data
120524
- effects.exit('magicBlockMarkerEnd');
120525
- effects.enter('magicBlockData');
120526
- // The [ was consumed by the marker, so we need to conceptually "have it" in our data
120527
- // But since we already consumed it into the marker, we need a different approach
120528
- // Re-classify: consume this character as data and continue
120529
- if (code === codes.quotationMark)
120530
- jsonState.inString = true;
120531
- effects.consume(code);
120532
- return captureData;
120533
- }
120534
- function expectSlash(code) {
120535
- if (code === null) {
120536
- effects.exit('magicBlockMarkerEnd');
120537
- effects.exit('magicBlock');
120538
- return nok(code);
120539
- }
120540
- // At line ending during marker parsing
120541
- if (markdownLineEnding(code)) {
120542
- effects.exit('magicBlockMarkerEnd');
120543
- return effects.check(syntax_nonLazyContinuation, continuationOk, after)(code);
120544
- }
120545
- if (code !== codes.slash) {
120546
- effects.exit('magicBlockMarkerEnd');
120547
- effects.enter('magicBlockData');
120548
- if (code === codes.quotationMark)
120549
- jsonState.inString = true;
120550
- effects.consume(code);
120551
- return captureData;
120552
- }
120553
- effects.consume(code);
120554
- return expectClosingB;
120555
- }
120556
- function expectClosingB(code) {
120557
- if (code === null) {
120558
- effects.exit('magicBlockMarkerEnd');
120559
- effects.exit('magicBlock');
120560
- return nok(code);
120561
- }
120562
- if (code !== codes.lowercaseB) {
120563
- effects.exit('magicBlockMarkerEnd');
120564
- effects.enter('magicBlockData');
120565
- if (code === codes.quotationMark)
120566
- jsonState.inString = true;
120567
- effects.consume(code);
120568
- return captureData;
120569
- }
120570
- effects.consume(code);
120571
- return expectClosingL;
120572
- }
120573
- function expectClosingL(code) {
120574
- if (code === null) {
120575
- effects.exit('magicBlockMarkerEnd');
120576
- effects.exit('magicBlock');
120577
- return nok(code);
120578
- }
120579
- if (code !== codes.lowercaseL) {
120580
- effects.exit('magicBlockMarkerEnd');
120581
- effects.enter('magicBlockData');
120582
- if (code === codes.quotationMark)
120583
- jsonState.inString = true;
120584
- effects.consume(code);
120585
- return captureData;
120586
- }
120587
- effects.consume(code);
120588
- return expectClosingO;
120589
- }
120590
- function expectClosingO(code) {
120591
- if (code === null) {
120592
- effects.exit('magicBlockMarkerEnd');
120593
- effects.exit('magicBlock');
120594
- return nok(code);
120595
- }
120596
- if (code !== codes.lowercaseO) {
120597
- effects.exit('magicBlockMarkerEnd');
120598
- effects.enter('magicBlockData');
120599
- if (code === codes.quotationMark)
120600
- jsonState.inString = true;
120601
- effects.consume(code);
120602
- return captureData;
120603
- }
120604
- effects.consume(code);
120605
- return expectClosingC;
120606
- }
120607
- function expectClosingC(code) {
120608
- if (code === null) {
120609
- effects.exit('magicBlockMarkerEnd');
120610
- effects.exit('magicBlock');
120611
- return nok(code);
120612
- }
120613
- if (code !== codes.lowercaseC) {
120614
- effects.exit('magicBlockMarkerEnd');
120615
- effects.enter('magicBlockData');
120616
- if (code === codes.quotationMark)
120617
- jsonState.inString = true;
120618
- effects.consume(code);
120619
- return captureData;
120620
- }
120621
- effects.consume(code);
120622
- return expectClosingK;
120623
- }
120624
- function expectClosingK(code) {
120625
- if (code === null) {
120626
- effects.exit('magicBlockMarkerEnd');
120627
- effects.exit('magicBlock');
120628
- return nok(code);
120629
- }
120630
- if (code !== codes.lowercaseK) {
120631
- effects.exit('magicBlockMarkerEnd');
120632
- effects.enter('magicBlockData');
120633
- if (code === codes.quotationMark)
120634
- jsonState.inString = true;
120635
- effects.consume(code);
120636
- return captureData;
120637
- }
120638
- effects.consume(code);
120639
- return expectClosingBracket;
120640
- }
120641
- function expectClosingBracket(code) {
120642
- if (code === null) {
120643
- effects.exit('magicBlockMarkerEnd');
120644
- effects.exit('magicBlock');
120645
- return nok(code);
120646
- }
120647
- if (code !== codes.rightSquareBracket) {
120648
- effects.exit('magicBlockMarkerEnd');
120649
- effects.enter('magicBlockData');
120650
- if (code === codes.quotationMark)
120651
- jsonState.inString = true;
120652
- effects.consume(code);
120653
- return captureData;
120654
- }
120655
- effects.consume(code);
120656
- effects.exit('magicBlockMarkerEnd');
120657
- // Check for trailing whitespace on the same line
120658
- return consumeTrailing;
120659
- }
120660
- /**
120661
- * Consume trailing whitespace (spaces/tabs) on the same line after [/block].
120662
- * For concrete flow constructs, we must end at eol/eof or fail.
120663
- * Trailing whitespace is consumed; other content causes nok (text tokenizer handles it).
120664
- */
120665
- function consumeTrailing(code) {
120666
- // End of file - done
120667
- if (code === null) {
120668
- effects.exit('magicBlock');
120669
- return ok(code);
120670
- }
120671
- // Line ending - done
120672
- if (markdownLineEnding(code)) {
120673
- effects.exit('magicBlock');
120674
- return ok(code);
120675
- }
120676
- // Space or tab - consume as trailing whitespace
120677
- if (code === codes.space || code === codes.horizontalTab) {
120678
- effects.enter('magicBlockTrailing');
120679
- effects.consume(code);
120680
- return consumeTrailingContinue;
120681
- }
120682
- // Any other character - fail flow tokenizer, let text tokenizer handle it
120683
- effects.exit('magicBlock');
120684
- return nok(code);
120685
- }
120686
- /**
120687
- * Continue consuming trailing whitespace.
120688
- */
120689
- function consumeTrailingContinue(code) {
120690
- // End of file - done
120691
- if (code === null) {
120692
- effects.exit('magicBlockTrailing');
120693
- effects.exit('magicBlock');
120694
- return ok(code);
120695
- }
120696
- // Line ending - done
120697
- if (markdownLineEnding(code)) {
120698
- effects.exit('magicBlockTrailing');
120699
- effects.exit('magicBlock');
120700
- return ok(code);
120701
- }
120702
- // More space or tab - keep consuming
120703
- if (code === codes.space || code === codes.horizontalTab) {
120704
- effects.consume(code);
120705
- return consumeTrailingContinue;
120706
- }
120707
- // Non-whitespace after whitespace - fail flow tokenizer
120708
- effects.exit('magicBlockTrailing');
120709
- effects.exit('magicBlock');
120710
- return nok(code);
120711
- }
120712
- /**
120713
- * Called when we can't continue (lazy line or EOF) - exit the construct.
120714
- */
120715
- function after(code) {
120716
- effects.exit('magicBlock');
120717
- return nok(code);
120718
- }
120719
- }
120720
- /**
120721
- * Text tokenizer for single-line magic blocks only.
120722
- * Used in inline contexts like list items and paragraphs.
120723
- * Multiline blocks are handled by the flow tokenizer.
120724
- */
120725
- function tokenizeMagicBlockText(effects, ok, nok) {
120726
- // State for tracking JSON content
120727
- const jsonState = { escapeNext: false, inString: false };
120728
- const blockTypeRef = { value: '' };
120729
- let seenOpenBrace = false;
120730
- // Create shared parsers for opening marker and type capture
120731
- const typeParser = createTypeCaptureParser(effects, nok, beforeData, blockTypeRef);
120732
- const openingMarkerParser = createOpeningMarkerParser(effects, nok, typeParser.first);
120733
- // Success handler for closing marker - exits tokens and returns ok
120734
- const closingSuccess = (code) => {
120735
- effects.exit('magicBlockMarkerEnd');
120736
- effects.exit('magicBlock');
120737
- return ok(code);
120738
- };
120739
- // Mismatch handler - falls back to data capture
120740
- const closingMismatch = (code) => {
120741
- effects.exit('magicBlockMarkerEnd');
120742
- effects.enter('magicBlockData');
120743
- if (code === codes.quotationMark)
120744
- jsonState.inString = true;
120745
- effects.consume(code);
120746
- return captureData;
120747
- };
120748
- // EOF handler
120749
- const closingEof = (_code) => nok(_code);
120750
- // Create closing marker parsers
120751
- const closingFromBeforeData = createClosingMarkerParser(effects, closingSuccess, closingMismatch, closingEof, jsonState);
120752
- const closingFromData = createClosingMarkerParser(effects, closingSuccess, closingMismatch, closingEof, jsonState);
120753
- return start;
120754
- function start(code) {
120755
- if (code !== codes.leftSquareBracket)
120756
- return nok(code);
120757
- effects.enter('magicBlock');
120758
- effects.enter('magicBlockMarkerStart');
120759
- effects.consume(code);
120760
- return openingMarkerParser;
120761
- }
120762
- /**
120763
- * State before data content - handles line endings before entering data token.
120764
- * Whitespace before '{' is allowed.
120765
- */
120766
- function beforeData(code) {
120767
- // Fail on EOF - magic block must be closed
120768
- if (code === null) {
120769
- return nok(code);
120770
- }
120771
- // Handle line endings before any data - consume them without entering data token
120772
- if (markdownLineEnding(code)) {
120773
- effects.enter('magicBlockLineEnding');
120774
- effects.consume(code);
120775
- effects.exit('magicBlockLineEnding');
120776
- return beforeData;
120777
- }
120778
- // Check for closing marker directly (without entering data)
120779
- if (code === codes.leftSquareBracket) {
120780
- effects.enter('magicBlockMarkerEnd');
120781
- effects.consume(code);
120782
- return closingFromBeforeData.expectSlash;
120783
- }
120784
- // If we've already seen the opening brace, just continue capturing data
120785
- if (seenOpenBrace) {
120786
- effects.enter('magicBlockData');
120787
- return captureData(code);
120788
- }
120789
- // Skip whitespace (spaces/tabs) before the data
120790
- if (code === codes.space || code === codes.horizontalTab) {
120791
- return beforeDataWhitespace(code);
120792
- }
120793
- // Data must start with '{' for valid JSON
120794
- if (code !== codes.leftCurlyBrace) {
120795
- return nok(code);
120796
- }
120797
- // We have '{' - enter data token and start capturing
120798
- seenOpenBrace = true;
120799
- effects.enter('magicBlockData');
120800
- return captureData(code);
120801
- }
120802
- /**
120803
- * Consume whitespace before the data.
120804
- */
120805
- function beforeDataWhitespace(code) {
120806
- if (code === null) {
120807
- return nok(code);
120808
- }
120809
- if (markdownLineEnding(code)) {
120810
- effects.enter('magicBlockLineEnding');
120811
- effects.consume(code);
120812
- effects.exit('magicBlockLineEnding');
120813
- return beforeData;
120814
- }
120815
- if (code === codes.space || code === codes.horizontalTab) {
120816
- effects.enter('magicBlockData');
120817
- effects.consume(code);
120818
- return beforeDataWhitespaceContinue;
120819
- }
120820
- if (code === codes.leftCurlyBrace) {
120821
- seenOpenBrace = true;
120822
- effects.enter('magicBlockData');
120823
- return captureData(code);
120824
- }
120825
- return nok(code);
120826
- }
120827
- /**
120828
- * Continue consuming whitespace or validate the opening brace.
120829
- */
120830
- function beforeDataWhitespaceContinue(code) {
120831
- if (code === null) {
120832
- effects.exit('magicBlockData');
120833
- return nok(code);
120834
- }
120835
- if (markdownLineEnding(code)) {
120836
- effects.exit('magicBlockData');
120837
- effects.enter('magicBlockLineEnding');
120838
- effects.consume(code);
120839
- effects.exit('magicBlockLineEnding');
120840
- return beforeData;
120841
- }
120842
- if (code === codes.space || code === codes.horizontalTab) {
120843
- effects.consume(code);
120844
- return beforeDataWhitespaceContinue;
120845
- }
120846
- if (code === codes.leftCurlyBrace) {
120847
- seenOpenBrace = true;
120848
- return captureData(code);
120849
- }
120850
- effects.exit('magicBlockData');
120851
- return nok(code);
120852
- }
120853
- function captureData(code) {
120854
- // Fail on EOF - magic block must be closed
120855
- if (code === null) {
120856
- effects.exit('magicBlockData');
120857
- return nok(code);
120858
- }
120859
- // Handle multiline magic blocks within text/paragraphs
120860
- // Exit data, consume line ending with proper token, then re-enter data
120861
- if (markdownLineEnding(code)) {
120862
- effects.exit('magicBlockData');
120863
- effects.enter('magicBlockLineEnding');
120864
- effects.consume(code);
120865
- effects.exit('magicBlockLineEnding');
120866
- return beforeData; // Go back to beforeData to handle potential empty lines
120867
- }
120868
- if (jsonState.escapeNext) {
120869
- jsonState.escapeNext = false;
120870
- effects.consume(code);
120871
- return captureData;
120872
- }
120873
- if (jsonState.inString) {
120874
- if (code === codes.backslash) {
120875
- jsonState.escapeNext = true;
120876
- effects.consume(code);
120877
- return captureData;
120878
- }
120879
- if (code === codes.quotationMark) {
120880
- jsonState.inString = false;
120881
- }
120882
- effects.consume(code);
120883
- return captureData;
120884
- }
120885
- if (code === codes.quotationMark) {
120886
- jsonState.inString = true;
120887
- effects.consume(code);
120888
- return captureData;
120889
- }
120890
- if (code === codes.leftSquareBracket) {
120891
- effects.exit('magicBlockData');
120892
- effects.enter('magicBlockMarkerEnd');
120893
- effects.consume(code);
120894
- return closingFromData.expectSlash;
120895
- }
120896
- effects.consume(code);
120897
- return captureData;
120898
- }
120899
- }
120900
- /* harmony default export */ const syntax = ((/* unused pure expression or super */ null && (magicBlock)));
120901
-
120902
- ;// ./lib/micromark/magic-block/index.ts
120903
- /**
120904
- * Micromark extension for magic block syntax.
120905
- *
120906
- * Usage:
120907
- * ```ts
120908
- * import { magicBlock } from './lib/micromark/magic-block';
120909
- *
120910
- * const processor = unified()
120911
- * .data('micromarkExtensions', [magicBlock()])
120912
- * ```
120913
- */
120914
-
120915
-
120916
- ;// ./node_modules/micromark-util-symbol/lib/types.js
120917
- /**
120918
- * This module is compiled away!
120919
- *
120920
- * Here is the list of all types of tokens exposed by micromark, with a short
120921
- * explanation of what they include and where they are found.
120922
- * In picking names, generally, the rule is to be as explicit as possible
120923
- * instead of reusing names.
120924
- * For example, there is a `definitionDestination` and a `resourceDestination`,
120925
- * instead of one shared name.
120096
+ * Here is the list of all types of tokens exposed by micromark, with a short
120097
+ * explanation of what they include and where they are found.
120098
+ * In picking names, generally, the rule is to be as explicit as possible
120099
+ * instead of reusing names.
120100
+ * For example, there is a `definitionDestination` and a `resourceDestination`,
120101
+ * instead of one shared name.
120926
120102
  */
120927
120103
 
120928
120104
  // Note: when changing the next record, you must also change `TokenTypeMap`
@@ -121368,36 +120544,18 @@ const types_types = /** @type {const} */ ({
121368
120544
  chunkString: 'chunkString'
121369
120545
  })
121370
120546
 
121371
- ;// ./lib/micromark/mdx-component/syntax.ts
121372
-
121373
-
121374
-
120547
+ ;// ./lib/micromark/jsx-table/syntax.ts
121375
120548
 
121376
120549
 
121377
- // Raw tags (type-1: pre/script/style/textarea) and block tags (type-6: div,
121378
- // section, …) always start a block, so they stay flow even with trailing
121379
- // content. Other lowercase tags (i, span, …) follow the type-7 rule and only
121380
- // stay flow when nothing trails the close tag.
121381
- const htmlFlowTagNames = new Set([...htmlRawNames, ...htmlBlockNames]);
121382
- // Lowercase type-6 block tags claimable in flow even without a `{…}` attribute, so
121383
- // blank lines between nested JSX siblings don't fragment the block. Excludes table
121384
- // tags (mdxishTables owns their blank lines) and voids (never close).
121385
- const plainBlockClaimTagNames = new Set([...htmlBlockNames].filter(tag => !HTML_TABLE_STRUCTURE_TAGS.has(tag) && !HTML_VOID_ELEMENTS.has(tag)));
121386
120550
  const syntax_nonLazyContinuationStart = {
121387
120551
  tokenize: syntax_tokenizeNonLazyContinuationStart,
121388
120552
  partial: true,
121389
120553
  };
121390
- // Lookahead for `plainClaimLineStart`: is this line markup-only, or a paragraph
121391
- // that merely starts with a tag? Run via `effects.check` so it never consumes.
121392
- const markupOnlyContinuation = {
121393
- tokenize: tokenizeMarkupOnlyContinuation,
121394
- partial: true,
121395
- };
121396
- function resolveToMdxComponent(events) {
120554
+ function resolveToJsxTable(events) {
121397
120555
  let index = events.length;
121398
120556
  while (index > 0) {
121399
120557
  index -= 1;
121400
- if (events[index][0] === 'enter' && events[index][1].type === 'mdxComponent') {
120558
+ if (events[index][0] === 'enter' && events[index][1].type === 'jsxTable') {
121401
120559
  break;
121402
120560
  }
121403
120561
  }
@@ -121408,21 +120566,1127 @@ function resolveToMdxComponent(events) {
121408
120566
  }
121409
120567
  return events;
121410
120568
  }
121411
- const mdxComponentFlowConstruct = {
121412
- name: 'mdxComponent',
121413
- tokenize: createTokenize('flow'),
121414
- resolveTo: resolveToMdxComponent,
120569
+ const jsxTableConstruct = {
120570
+ name: 'jsxTable',
120571
+ tokenize: tokenizeJsxTable,
120572
+ resolveTo: resolveToJsxTable,
121415
120573
  concrete: true,
121416
120574
  };
121417
- const mdxComponentTextConstruct = {
121418
- name: 'mdxComponentText',
121419
- tokenize: createTokenize('text'),
121420
- };
121421
- /**
121422
- * Factory for both flow (block) and text (inline) variants.
121423
- *
121424
- * **Flow** — the original behavior: claims PascalCase components (always) and
121425
- * lowercase HTML tags that carry at least one `{…}` attribute expression.
120575
+ function tokenizeJsxTable(effects, ok, nok) {
120576
+ let codeSpanOpenSize = 0;
120577
+ let codeSpanCloseSize = 0;
120578
+ let depth = 1;
120579
+ const ABLE_SUFFIX = [codes.lowercaseA, codes.lowercaseB, codes.lowercaseL, codes.lowercaseE];
120580
+ /** Build a state chain that matches a sequence of character codes. */
120581
+ function matchChars(chars, onMatch, onFail) {
120582
+ if (chars.length === 0)
120583
+ return onMatch;
120584
+ return ((code) => {
120585
+ if (code === chars[0]) {
120586
+ effects.consume(code);
120587
+ return matchChars(chars.slice(1), onMatch, onFail);
120588
+ }
120589
+ return onFail(code);
120590
+ });
120591
+ }
120592
+ return start;
120593
+ function start(code) {
120594
+ if (code !== codes.lessThan)
120595
+ return nok(code);
120596
+ effects.enter('jsxTable');
120597
+ effects.enter('jsxTableData');
120598
+ effects.consume(code);
120599
+ return afterLessThan;
120600
+ }
120601
+ function afterLessThan(code) {
120602
+ if (code === codes.uppercaseT || code === codes.lowercaseT) {
120603
+ effects.consume(code);
120604
+ return matchChars(ABLE_SUFFIX, afterTagName, nok);
120605
+ }
120606
+ return nok(code);
120607
+ }
120608
+ function afterTagName(code) {
120609
+ if (code === codes.greaterThan || code === codes.slash || code === codes.space || code === codes.horizontalTab) {
120610
+ effects.consume(code);
120611
+ return body;
120612
+ }
120613
+ return nok(code);
120614
+ }
120615
+ function body(code) {
120616
+ // Reject unclosed <Table> so it falls back to normal HTML block parsing
120617
+ // instead of swallowing all subsequent content to EOF
120618
+ if (code === null) {
120619
+ return nok(code);
120620
+ }
120621
+ if (markdownLineEnding(code)) {
120622
+ effects.exit('jsxTableData');
120623
+ return continuationStart(code);
120624
+ }
120625
+ if (code === codes.backslash) {
120626
+ effects.consume(code);
120627
+ return escapedChar;
120628
+ }
120629
+ if (code === codes.lessThan) {
120630
+ effects.consume(code);
120631
+ return closeSlash;
120632
+ }
120633
+ // Skip over backtick code spans so `</Table>` in inline code isn't
120634
+ // treated as the closing tag
120635
+ if (code === codes.graveAccent) {
120636
+ codeSpanOpenSize = 0;
120637
+ return countOpenTicks(code);
120638
+ }
120639
+ effects.consume(code);
120640
+ return body;
120641
+ }
120642
+ function countOpenTicks(code) {
120643
+ if (code === codes.graveAccent) {
120644
+ codeSpanOpenSize += 1;
120645
+ effects.consume(code);
120646
+ return countOpenTicks;
120647
+ }
120648
+ return inCodeSpan(code);
120649
+ }
120650
+ function inCodeSpan(code) {
120651
+ if (code === null || markdownLineEnding(code))
120652
+ return body(code);
120653
+ if (code === codes.graveAccent) {
120654
+ codeSpanCloseSize = 0;
120655
+ return countCloseTicks(code);
120656
+ }
120657
+ effects.consume(code);
120658
+ return inCodeSpan;
120659
+ }
120660
+ function countCloseTicks(code) {
120661
+ if (code === codes.graveAccent) {
120662
+ codeSpanCloseSize += 1;
120663
+ effects.consume(code);
120664
+ return countCloseTicks;
120665
+ }
120666
+ return codeSpanCloseSize === codeSpanOpenSize ? body(code) : inCodeSpan(code);
120667
+ }
120668
+ function escapedChar(code) {
120669
+ if (code === null || markdownLineEnding(code)) {
120670
+ return body(code);
120671
+ }
120672
+ effects.consume(code);
120673
+ return body;
120674
+ }
120675
+ function closeSlash(code) {
120676
+ if (code === codes.slash) {
120677
+ effects.consume(code);
120678
+ return closeTagFirstChar;
120679
+ }
120680
+ if (code === codes.uppercaseT || code === codes.lowercaseT) {
120681
+ effects.consume(code);
120682
+ return matchChars(ABLE_SUFFIX, openAfterTagName, body);
120683
+ }
120684
+ return body(code);
120685
+ }
120686
+ function closeTagFirstChar(code) {
120687
+ if (code === codes.uppercaseT || code === codes.lowercaseT) {
120688
+ effects.consume(code);
120689
+ return matchChars(ABLE_SUFFIX, closeGt, body);
120690
+ }
120691
+ return body(code);
120692
+ }
120693
+ function openAfterTagName(code) {
120694
+ if (code === codes.greaterThan || code === codes.slash || code === codes.space || code === codes.horizontalTab) {
120695
+ depth += 1;
120696
+ effects.consume(code);
120697
+ return body;
120698
+ }
120699
+ return body(code);
120700
+ }
120701
+ function closeGt(code) {
120702
+ if (code === codes.greaterThan) {
120703
+ depth -= 1;
120704
+ effects.consume(code);
120705
+ if (depth === 0) {
120706
+ return afterClose;
120707
+ }
120708
+ return body;
120709
+ }
120710
+ return body(code);
120711
+ }
120712
+ function afterClose(code) {
120713
+ if (code === null || markdownLineEnding(code)) {
120714
+ effects.exit('jsxTableData');
120715
+ effects.exit('jsxTable');
120716
+ return ok(code);
120717
+ }
120718
+ effects.consume(code);
120719
+ return afterClose;
120720
+ }
120721
+ // Line ending handling — follows the htmlFlow pattern
120722
+ function continuationStart(code) {
120723
+ return effects.check(syntax_nonLazyContinuationStart, continuationStartNonLazy, continuationAfter)(code);
120724
+ }
120725
+ function continuationStartNonLazy(code) {
120726
+ effects.enter(types_types.lineEnding);
120727
+ effects.consume(code);
120728
+ effects.exit(types_types.lineEnding);
120729
+ return continuationBefore;
120730
+ }
120731
+ function continuationBefore(code) {
120732
+ if (code === null || markdownLineEnding(code)) {
120733
+ return continuationStart(code);
120734
+ }
120735
+ effects.enter('jsxTableData');
120736
+ return body(code);
120737
+ }
120738
+ function continuationAfter(code) {
120739
+ // At EOF without </Table>, reject so content isn't swallowed
120740
+ if (code === null) {
120741
+ return nok(code);
120742
+ }
120743
+ effects.exit('jsxTable');
120744
+ return ok(code);
120745
+ }
120746
+ }
120747
+ function syntax_tokenizeNonLazyContinuationStart(effects, ok, nok) {
120748
+ // eslint-disable-next-line @typescript-eslint/no-this-alias
120749
+ const self = this;
120750
+ return start;
120751
+ function start(code) {
120752
+ if (markdownLineEnding(code)) {
120753
+ effects.enter(types_types.lineEnding);
120754
+ effects.consume(code);
120755
+ effects.exit(types_types.lineEnding);
120756
+ return after;
120757
+ }
120758
+ return nok(code);
120759
+ }
120760
+ function after(code) {
120761
+ if (self.parser.lazy[self.now().line]) {
120762
+ return nok(code);
120763
+ }
120764
+ return ok(code);
120765
+ }
120766
+ }
120767
+ /**
120768
+ * Micromark extension that tokenizes `<Table>...</Table>` and `<table>...</table>`
120769
+ * as a single flow block.
120770
+ *
120771
+ * Prevents CommonMark HTML block type 6 from fragmenting table blocks at blank lines.
120772
+ */
120773
+ function jsxTable() {
120774
+ return {
120775
+ flow: {
120776
+ [codes.lessThan]: [jsxTableConstruct],
120777
+ },
120778
+ };
120779
+ }
120780
+
120781
+ ;// ./lib/micromark/jsx-table/index.ts
120782
+
120783
+
120784
+ ;// ./lib/micromark/magic-block/syntax.ts
120785
+
120786
+
120787
+ /**
120788
+ * Known magic block types that the tokenizer will recognize.
120789
+ * Unknown types will not be tokenized as magic blocks.
120790
+ */
120791
+ const KNOWN_BLOCK_TYPES = new Set([
120792
+ 'code',
120793
+ 'api-header',
120794
+ 'image',
120795
+ 'callout',
120796
+ 'parameters',
120797
+ 'table',
120798
+ 'embed',
120799
+ 'html',
120800
+ 'recipe',
120801
+ 'tutorial-tile',
120802
+ ]);
120803
+ /**
120804
+ * Check if a character is valid for a magic block type identifier.
120805
+ * Types can contain alphanumeric characters and hyphens (e.g., "api-header", "tutorial-tile")
120806
+ */
120807
+ function isTypeChar(code) {
120808
+ return asciiAlphanumeric(code) || code === codes.dash;
120809
+ }
120810
+ /**
120811
+ * Creates the opening marker state machine: [block:
120812
+ * Returns the first state function to start parsing.
120813
+ */
120814
+ function createOpeningMarkerParser(effects, nok, onComplete) {
120815
+ const expectB = (code) => {
120816
+ if (code !== codes.lowercaseB)
120817
+ return nok(code);
120818
+ effects.consume(code);
120819
+ return expectL;
120820
+ };
120821
+ const expectL = (code) => {
120822
+ if (code !== codes.lowercaseL)
120823
+ return nok(code);
120824
+ effects.consume(code);
120825
+ return expectO;
120826
+ };
120827
+ const expectO = (code) => {
120828
+ if (code !== codes.lowercaseO)
120829
+ return nok(code);
120830
+ effects.consume(code);
120831
+ return expectC;
120832
+ };
120833
+ const expectC = (code) => {
120834
+ if (code !== codes.lowercaseC)
120835
+ return nok(code);
120836
+ effects.consume(code);
120837
+ return expectK;
120838
+ };
120839
+ const expectK = (code) => {
120840
+ if (code !== codes.lowercaseK)
120841
+ return nok(code);
120842
+ effects.consume(code);
120843
+ return expectColon;
120844
+ };
120845
+ const expectColon = (code) => {
120846
+ if (code !== codes.colon)
120847
+ return nok(code);
120848
+ effects.consume(code);
120849
+ effects.exit('magicBlockMarkerStart');
120850
+ effects.enter('magicBlockType');
120851
+ return onComplete;
120852
+ };
120853
+ return expectB;
120854
+ }
120855
+ /**
120856
+ * Creates the type capture state machine.
120857
+ * Captures type characters until ] and validates against known types.
120858
+ */
120859
+ function createTypeCaptureParser(effects, nok, onComplete, blockTypeRef) {
120860
+ const captureTypeFirst = (code) => {
120861
+ // Reject empty type name [block:]
120862
+ if (code === codes.rightSquareBracket) {
120863
+ return nok(code);
120864
+ }
120865
+ if (isTypeChar(code)) {
120866
+ blockTypeRef.value += String.fromCharCode(code);
120867
+ effects.consume(code);
120868
+ return captureType;
120869
+ }
120870
+ return nok(code);
120871
+ };
120872
+ const captureType = (code) => {
120873
+ if (code === codes.rightSquareBracket) {
120874
+ if (!KNOWN_BLOCK_TYPES.has(blockTypeRef.value)) {
120875
+ return nok(code);
120876
+ }
120877
+ effects.exit('magicBlockType');
120878
+ effects.enter('magicBlockMarkerTypeEnd');
120879
+ effects.consume(code);
120880
+ effects.exit('magicBlockMarkerTypeEnd');
120881
+ return onComplete;
120882
+ }
120883
+ if (isTypeChar(code)) {
120884
+ blockTypeRef.value += String.fromCharCode(code);
120885
+ effects.consume(code);
120886
+ return captureType;
120887
+ }
120888
+ return nok(code);
120889
+ };
120890
+ return { first: captureTypeFirst, remaining: captureType };
120891
+ }
120892
+ /**
120893
+ * Creates the closing marker state machine: /block]
120894
+ * Handles partial matches by calling onMismatch to fall back to data capture.
120895
+ */
120896
+ function createClosingMarkerParser(effects, onSuccess, onMismatch, onEof, jsonState) {
120897
+ const handleMismatch = (code) => {
120898
+ if (jsonState && code === codes.quotationMark) {
120899
+ jsonState.inString = true;
120900
+ }
120901
+ return onMismatch(code);
120902
+ };
120903
+ const expectSlash = (code) => {
120904
+ if (code === null)
120905
+ return onEof(code);
120906
+ if (code !== codes.slash)
120907
+ return handleMismatch(code);
120908
+ effects.consume(code);
120909
+ return expectB;
120910
+ };
120911
+ const expectB = (code) => {
120912
+ if (code === null)
120913
+ return onEof(code);
120914
+ if (code !== codes.lowercaseB)
120915
+ return handleMismatch(code);
120916
+ effects.consume(code);
120917
+ return expectL;
120918
+ };
120919
+ const expectL = (code) => {
120920
+ if (code === null)
120921
+ return onEof(code);
120922
+ if (code !== codes.lowercaseL)
120923
+ return handleMismatch(code);
120924
+ effects.consume(code);
120925
+ return expectO;
120926
+ };
120927
+ const expectO = (code) => {
120928
+ if (code === null)
120929
+ return onEof(code);
120930
+ if (code !== codes.lowercaseO)
120931
+ return handleMismatch(code);
120932
+ effects.consume(code);
120933
+ return expectC;
120934
+ };
120935
+ const expectC = (code) => {
120936
+ if (code === null)
120937
+ return onEof(code);
120938
+ if (code !== codes.lowercaseC)
120939
+ return handleMismatch(code);
120940
+ effects.consume(code);
120941
+ return expectK;
120942
+ };
120943
+ const expectK = (code) => {
120944
+ if (code === null)
120945
+ return onEof(code);
120946
+ if (code !== codes.lowercaseK)
120947
+ return handleMismatch(code);
120948
+ effects.consume(code);
120949
+ return expectBracket;
120950
+ };
120951
+ const expectBracket = (code) => {
120952
+ if (code === null)
120953
+ return onEof(code);
120954
+ if (code !== codes.rightSquareBracket)
120955
+ return handleMismatch(code);
120956
+ effects.consume(code);
120957
+ return onSuccess;
120958
+ };
120959
+ return { expectSlash };
120960
+ }
120961
+ /**
120962
+ * Partial construct for checking non-lazy continuation.
120963
+ * This is used by the flow tokenizer to check if we can continue
120964
+ * parsing on the next line.
120965
+ */
120966
+ const syntax_nonLazyContinuation = {
120967
+ partial: true,
120968
+ tokenize: syntax_tokenizeNonLazyContinuation,
120969
+ };
120970
+ /**
120971
+ * Tokenizer for non-lazy continuation checking.
120972
+ * Returns ok if the next line is non-lazy (can continue), nok if lazy.
120973
+ */
120974
+ function syntax_tokenizeNonLazyContinuation(effects, ok, nok) {
120975
+ const lineStart = (code) => {
120976
+ // `this` here refers to the micromark parser
120977
+ // since we are just passing functions as references and not actually calling them
120978
+ // micromarks's internal parser will call this automatically with appropriate arguments
120979
+ return this.parser.lazy[this.now().line] ? nok(code) : ok(code);
120980
+ };
120981
+ const start = (code) => {
120982
+ if (code === null) {
120983
+ return nok(code);
120984
+ }
120985
+ if (!markdownLineEnding(code)) {
120986
+ return nok(code);
120987
+ }
120988
+ effects.enter('magicBlockLineEnding');
120989
+ effects.consume(code);
120990
+ effects.exit('magicBlockLineEnding');
120991
+ return lineStart;
120992
+ };
120993
+ return start;
120994
+ }
120995
+ /**
120996
+ * Create a micromark extension for magic block syntax.
120997
+ *
120998
+ * This extension handles both single-line and multiline magic blocks:
120999
+ * - Flow construct (concrete): Handles block-level multiline magic blocks at document level
121000
+ * - Text construct: Handles inline magic blocks in lists, paragraphs, etc.
121001
+ *
121002
+ * The flow construct is marked as "concrete" which prevents it from being
121003
+ * interrupted by container markers (like `>` for blockquotes or `-` for lists).
121004
+ */
121005
+ function magicBlock() {
121006
+ return {
121007
+ // Flow construct - handles block-level magic blocks at document root
121008
+ // Marked as concrete to prevent interruption by containers
121009
+ flow: {
121010
+ [codes.leftSquareBracket]: {
121011
+ name: 'magicBlock',
121012
+ concrete: true,
121013
+ tokenize: tokenizeMagicBlockFlow,
121014
+ },
121015
+ },
121016
+ // Text construct - handles magic blocks in inline contexts (lists, paragraphs)
121017
+ text: {
121018
+ [codes.leftSquareBracket]: {
121019
+ name: 'magicBlock',
121020
+ tokenize: tokenizeMagicBlockText,
121021
+ },
121022
+ },
121023
+ };
121024
+ }
121025
+ /**
121026
+ * Flow tokenizer for block-level magic blocks (multiline).
121027
+ * Uses the continuation checking pattern from code fences.
121028
+ */
121029
+ function tokenizeMagicBlockFlow(effects, ok, nok) {
121030
+ // State for tracking JSON content
121031
+ const jsonState = { escapeNext: false, inString: false };
121032
+ const blockTypeRef = { value: '' };
121033
+ let seenOpenBrace = false;
121034
+ // Create shared parsers for opening marker and type capture
121035
+ const typeParser = createTypeCaptureParser(effects, nok, beforeData, blockTypeRef);
121036
+ const openingMarkerParser = createOpeningMarkerParser(effects, nok, typeParser.first);
121037
+ return start;
121038
+ function start(code) {
121039
+ if (code !== codes.leftSquareBracket)
121040
+ return nok(code);
121041
+ effects.enter('magicBlock');
121042
+ effects.enter('magicBlockMarkerStart');
121043
+ effects.consume(code);
121044
+ return openingMarkerParser;
121045
+ }
121046
+ /**
121047
+ * State before data content - handles line endings or starts data capture.
121048
+ */
121049
+ function beforeData(code) {
121050
+ // EOF - magic block must be closed
121051
+ if (code === null) {
121052
+ effects.exit('magicBlock');
121053
+ return nok(code);
121054
+ }
121055
+ // Line ending before any data - check continuation
121056
+ if (markdownLineEnding(code)) {
121057
+ return effects.check(syntax_nonLazyContinuation, continuationOkBeforeData, after)(code);
121058
+ }
121059
+ // Check for closing marker directly (without entering data)
121060
+ // This handles cases like [block:type]\n{}\n\n[/block] where there are
121061
+ // newlines after the data object
121062
+ if (code === codes.leftSquareBracket) {
121063
+ effects.enter('magicBlockMarkerEnd');
121064
+ effects.consume(code);
121065
+ return expectSlashFromContinuation;
121066
+ }
121067
+ // If we've already seen the opening brace, just continue capturing data
121068
+ if (seenOpenBrace) {
121069
+ effects.enter('magicBlockData');
121070
+ return captureData(code);
121071
+ }
121072
+ // Skip whitespace (spaces/tabs) before the data - stay in beforeData
121073
+ // Don't enter magicBlockData token yet until we confirm there's a '{'
121074
+ if (code === codes.space || code === codes.horizontalTab) {
121075
+ return beforeDataWhitespace(code);
121076
+ }
121077
+ // Data must start with '{' for valid JSON
121078
+ if (code !== codes.leftCurlyBrace) {
121079
+ effects.exit('magicBlock');
121080
+ return nok(code);
121081
+ }
121082
+ // We have '{' - enter data token and start capturing
121083
+ seenOpenBrace = true;
121084
+ effects.enter('magicBlockData');
121085
+ return captureData(code);
121086
+ }
121087
+ /**
121088
+ * Consume whitespace before the data without creating a token.
121089
+ * Uses a temporary token to satisfy micromark's requirement.
121090
+ */
121091
+ function beforeDataWhitespace(code) {
121092
+ if (code === null) {
121093
+ effects.exit('magicBlock');
121094
+ return nok(code);
121095
+ }
121096
+ if (markdownLineEnding(code)) {
121097
+ return effects.check(syntax_nonLazyContinuation, continuationOkBeforeData, after)(code);
121098
+ }
121099
+ if (code === codes.space || code === codes.horizontalTab) {
121100
+ // We need to consume this but can't without a token - use magicBlockData
121101
+ // and track that we haven't seen '{' yet
121102
+ effects.enter('magicBlockData');
121103
+ effects.consume(code);
121104
+ return beforeDataWhitespaceContinue;
121105
+ }
121106
+ if (code === codes.leftCurlyBrace) {
121107
+ seenOpenBrace = true;
121108
+ effects.enter('magicBlockData');
121109
+ return captureData(code);
121110
+ }
121111
+ effects.exit('magicBlock');
121112
+ return nok(code);
121113
+ }
121114
+ /**
121115
+ * Continue consuming whitespace or validate the opening brace.
121116
+ */
121117
+ function beforeDataWhitespaceContinue(code) {
121118
+ if (code === null) {
121119
+ effects.exit('magicBlockData');
121120
+ effects.exit('magicBlock');
121121
+ return nok(code);
121122
+ }
121123
+ if (markdownLineEnding(code)) {
121124
+ effects.exit('magicBlockData');
121125
+ return effects.check(syntax_nonLazyContinuation, continuationOk, after)(code);
121126
+ }
121127
+ if (code === codes.space || code === codes.horizontalTab) {
121128
+ effects.consume(code);
121129
+ return beforeDataWhitespaceContinue;
121130
+ }
121131
+ if (code === codes.leftCurlyBrace) {
121132
+ seenOpenBrace = true;
121133
+ return captureData(code);
121134
+ }
121135
+ effects.exit('magicBlockData');
121136
+ effects.exit('magicBlock');
121137
+ return nok(code);
121138
+ }
121139
+ /**
121140
+ * Continuation OK before we've entered data token.
121141
+ */
121142
+ function continuationOkBeforeData(code) {
121143
+ effects.enter('magicBlockLineEnding');
121144
+ effects.consume(code);
121145
+ effects.exit('magicBlockLineEnding');
121146
+ return beforeData; // Stay in beforeData state - don't enter magicBlockData yet
121147
+ }
121148
+ function captureData(code) {
121149
+ // EOF - magic block must be closed
121150
+ if (code === null) {
121151
+ effects.exit('magicBlockData');
121152
+ effects.exit('magicBlock');
121153
+ return nok(code);
121154
+ }
121155
+ // At line ending, check if we can continue on the next line
121156
+ if (markdownLineEnding(code)) {
121157
+ effects.exit('magicBlockData');
121158
+ return effects.check(syntax_nonLazyContinuation, continuationOk, after)(code);
121159
+ }
121160
+ if (jsonState.escapeNext) {
121161
+ jsonState.escapeNext = false;
121162
+ effects.consume(code);
121163
+ return captureData;
121164
+ }
121165
+ if (jsonState.inString) {
121166
+ if (code === codes.backslash) {
121167
+ jsonState.escapeNext = true;
121168
+ effects.consume(code);
121169
+ return captureData;
121170
+ }
121171
+ if (code === codes.quotationMark) {
121172
+ jsonState.inString = false;
121173
+ }
121174
+ effects.consume(code);
121175
+ return captureData;
121176
+ }
121177
+ if (code === codes.quotationMark) {
121178
+ jsonState.inString = true;
121179
+ effects.consume(code);
121180
+ return captureData;
121181
+ }
121182
+ if (code === codes.leftSquareBracket) {
121183
+ effects.exit('magicBlockData');
121184
+ effects.enter('magicBlockMarkerEnd');
121185
+ effects.consume(code);
121186
+ return expectSlash;
121187
+ }
121188
+ effects.consume(code);
121189
+ return captureData;
121190
+ }
121191
+ /**
121192
+ * Called when non-lazy continuation check passes - we can continue parsing.
121193
+ */
121194
+ function continuationOk(code) {
121195
+ // Consume the line ending
121196
+ effects.enter('magicBlockLineEnding');
121197
+ effects.consume(code);
121198
+ effects.exit('magicBlockLineEnding');
121199
+ return continuationStart;
121200
+ }
121201
+ /**
121202
+ * Start of continuation line - check for more line endings, closing marker, or start capturing data.
121203
+ */
121204
+ function continuationStart(code) {
121205
+ // Handle consecutive line endings
121206
+ if (code === null) {
121207
+ effects.exit('magicBlock');
121208
+ return nok(code);
121209
+ }
121210
+ if (markdownLineEnding(code)) {
121211
+ return effects.check(syntax_nonLazyContinuation, continuationOkBeforeData, after)(code);
121212
+ }
121213
+ // Check if this is the start of the closing marker [/block]
121214
+ // If so, handle it directly without entering magicBlockData
121215
+ if (code === codes.leftSquareBracket) {
121216
+ effects.enter('magicBlockMarkerEnd');
121217
+ effects.consume(code);
121218
+ return expectSlashFromContinuation;
121219
+ }
121220
+ effects.enter('magicBlockData');
121221
+ return captureData(code);
121222
+ }
121223
+ /**
121224
+ * Check for closing marker slash when coming from continuation.
121225
+ * If not a closing marker, create an empty data token and continue.
121226
+ */
121227
+ function expectSlashFromContinuation(code) {
121228
+ if (code === null) {
121229
+ effects.exit('magicBlockMarkerEnd');
121230
+ effects.exit('magicBlock');
121231
+ return nok(code);
121232
+ }
121233
+ if (markdownLineEnding(code)) {
121234
+ effects.exit('magicBlockMarkerEnd');
121235
+ return effects.check(syntax_nonLazyContinuation, continuationOkBeforeData, after)(code);
121236
+ }
121237
+ if (code === codes.slash) {
121238
+ effects.consume(code);
121239
+ return expectClosingB;
121240
+ }
121241
+ // Not a closing marker - this is data content
121242
+ // Exit marker and enter data
121243
+ effects.exit('magicBlockMarkerEnd');
121244
+ effects.enter('magicBlockData');
121245
+ // The [ was consumed by the marker, so we need to conceptually "have it" in our data
121246
+ // But since we already consumed it into the marker, we need a different approach
121247
+ // Re-classify: consume this character as data and continue
121248
+ if (code === codes.quotationMark)
121249
+ jsonState.inString = true;
121250
+ effects.consume(code);
121251
+ return captureData;
121252
+ }
121253
+ function expectSlash(code) {
121254
+ if (code === null) {
121255
+ effects.exit('magicBlockMarkerEnd');
121256
+ effects.exit('magicBlock');
121257
+ return nok(code);
121258
+ }
121259
+ // At line ending during marker parsing
121260
+ if (markdownLineEnding(code)) {
121261
+ effects.exit('magicBlockMarkerEnd');
121262
+ return effects.check(syntax_nonLazyContinuation, continuationOk, after)(code);
121263
+ }
121264
+ if (code !== codes.slash) {
121265
+ effects.exit('magicBlockMarkerEnd');
121266
+ effects.enter('magicBlockData');
121267
+ if (code === codes.quotationMark)
121268
+ jsonState.inString = true;
121269
+ effects.consume(code);
121270
+ return captureData;
121271
+ }
121272
+ effects.consume(code);
121273
+ return expectClosingB;
121274
+ }
121275
+ function expectClosingB(code) {
121276
+ if (code === null) {
121277
+ effects.exit('magicBlockMarkerEnd');
121278
+ effects.exit('magicBlock');
121279
+ return nok(code);
121280
+ }
121281
+ if (code !== codes.lowercaseB) {
121282
+ effects.exit('magicBlockMarkerEnd');
121283
+ effects.enter('magicBlockData');
121284
+ if (code === codes.quotationMark)
121285
+ jsonState.inString = true;
121286
+ effects.consume(code);
121287
+ return captureData;
121288
+ }
121289
+ effects.consume(code);
121290
+ return expectClosingL;
121291
+ }
121292
+ function expectClosingL(code) {
121293
+ if (code === null) {
121294
+ effects.exit('magicBlockMarkerEnd');
121295
+ effects.exit('magicBlock');
121296
+ return nok(code);
121297
+ }
121298
+ if (code !== codes.lowercaseL) {
121299
+ effects.exit('magicBlockMarkerEnd');
121300
+ effects.enter('magicBlockData');
121301
+ if (code === codes.quotationMark)
121302
+ jsonState.inString = true;
121303
+ effects.consume(code);
121304
+ return captureData;
121305
+ }
121306
+ effects.consume(code);
121307
+ return expectClosingO;
121308
+ }
121309
+ function expectClosingO(code) {
121310
+ if (code === null) {
121311
+ effects.exit('magicBlockMarkerEnd');
121312
+ effects.exit('magicBlock');
121313
+ return nok(code);
121314
+ }
121315
+ if (code !== codes.lowercaseO) {
121316
+ effects.exit('magicBlockMarkerEnd');
121317
+ effects.enter('magicBlockData');
121318
+ if (code === codes.quotationMark)
121319
+ jsonState.inString = true;
121320
+ effects.consume(code);
121321
+ return captureData;
121322
+ }
121323
+ effects.consume(code);
121324
+ return expectClosingC;
121325
+ }
121326
+ function expectClosingC(code) {
121327
+ if (code === null) {
121328
+ effects.exit('magicBlockMarkerEnd');
121329
+ effects.exit('magicBlock');
121330
+ return nok(code);
121331
+ }
121332
+ if (code !== codes.lowercaseC) {
121333
+ effects.exit('magicBlockMarkerEnd');
121334
+ effects.enter('magicBlockData');
121335
+ if (code === codes.quotationMark)
121336
+ jsonState.inString = true;
121337
+ effects.consume(code);
121338
+ return captureData;
121339
+ }
121340
+ effects.consume(code);
121341
+ return expectClosingK;
121342
+ }
121343
+ function expectClosingK(code) {
121344
+ if (code === null) {
121345
+ effects.exit('magicBlockMarkerEnd');
121346
+ effects.exit('magicBlock');
121347
+ return nok(code);
121348
+ }
121349
+ if (code !== codes.lowercaseK) {
121350
+ effects.exit('magicBlockMarkerEnd');
121351
+ effects.enter('magicBlockData');
121352
+ if (code === codes.quotationMark)
121353
+ jsonState.inString = true;
121354
+ effects.consume(code);
121355
+ return captureData;
121356
+ }
121357
+ effects.consume(code);
121358
+ return expectClosingBracket;
121359
+ }
121360
+ function expectClosingBracket(code) {
121361
+ if (code === null) {
121362
+ effects.exit('magicBlockMarkerEnd');
121363
+ effects.exit('magicBlock');
121364
+ return nok(code);
121365
+ }
121366
+ if (code !== codes.rightSquareBracket) {
121367
+ effects.exit('magicBlockMarkerEnd');
121368
+ effects.enter('magicBlockData');
121369
+ if (code === codes.quotationMark)
121370
+ jsonState.inString = true;
121371
+ effects.consume(code);
121372
+ return captureData;
121373
+ }
121374
+ effects.consume(code);
121375
+ effects.exit('magicBlockMarkerEnd');
121376
+ // Check for trailing whitespace on the same line
121377
+ return consumeTrailing;
121378
+ }
121379
+ /**
121380
+ * Consume trailing whitespace (spaces/tabs) on the same line after [/block].
121381
+ * For concrete flow constructs, we must end at eol/eof or fail.
121382
+ * Trailing whitespace is consumed; other content causes nok (text tokenizer handles it).
121383
+ */
121384
+ function consumeTrailing(code) {
121385
+ // End of file - done
121386
+ if (code === null) {
121387
+ effects.exit('magicBlock');
121388
+ return ok(code);
121389
+ }
121390
+ // Line ending - done
121391
+ if (markdownLineEnding(code)) {
121392
+ effects.exit('magicBlock');
121393
+ return ok(code);
121394
+ }
121395
+ // Space or tab - consume as trailing whitespace
121396
+ if (code === codes.space || code === codes.horizontalTab) {
121397
+ effects.enter('magicBlockTrailing');
121398
+ effects.consume(code);
121399
+ return consumeTrailingContinue;
121400
+ }
121401
+ // Any other character - fail flow tokenizer, let text tokenizer handle it
121402
+ effects.exit('magicBlock');
121403
+ return nok(code);
121404
+ }
121405
+ /**
121406
+ * Continue consuming trailing whitespace.
121407
+ */
121408
+ function consumeTrailingContinue(code) {
121409
+ // End of file - done
121410
+ if (code === null) {
121411
+ effects.exit('magicBlockTrailing');
121412
+ effects.exit('magicBlock');
121413
+ return ok(code);
121414
+ }
121415
+ // Line ending - done
121416
+ if (markdownLineEnding(code)) {
121417
+ effects.exit('magicBlockTrailing');
121418
+ effects.exit('magicBlock');
121419
+ return ok(code);
121420
+ }
121421
+ // More space or tab - keep consuming
121422
+ if (code === codes.space || code === codes.horizontalTab) {
121423
+ effects.consume(code);
121424
+ return consumeTrailingContinue;
121425
+ }
121426
+ // Non-whitespace after whitespace - fail flow tokenizer
121427
+ effects.exit('magicBlockTrailing');
121428
+ effects.exit('magicBlock');
121429
+ return nok(code);
121430
+ }
121431
+ /**
121432
+ * Called when we can't continue (lazy line or EOF) - exit the construct.
121433
+ */
121434
+ function after(code) {
121435
+ effects.exit('magicBlock');
121436
+ return nok(code);
121437
+ }
121438
+ }
121439
+ /**
121440
+ * Text tokenizer for single-line magic blocks only.
121441
+ * Used in inline contexts like list items and paragraphs.
121442
+ * Multiline blocks are handled by the flow tokenizer.
121443
+ */
121444
+ function tokenizeMagicBlockText(effects, ok, nok) {
121445
+ // State for tracking JSON content
121446
+ const jsonState = { escapeNext: false, inString: false };
121447
+ const blockTypeRef = { value: '' };
121448
+ let seenOpenBrace = false;
121449
+ // Create shared parsers for opening marker and type capture
121450
+ const typeParser = createTypeCaptureParser(effects, nok, beforeData, blockTypeRef);
121451
+ const openingMarkerParser = createOpeningMarkerParser(effects, nok, typeParser.first);
121452
+ // Success handler for closing marker - exits tokens and returns ok
121453
+ const closingSuccess = (code) => {
121454
+ effects.exit('magicBlockMarkerEnd');
121455
+ effects.exit('magicBlock');
121456
+ return ok(code);
121457
+ };
121458
+ // Mismatch handler - falls back to data capture
121459
+ const closingMismatch = (code) => {
121460
+ effects.exit('magicBlockMarkerEnd');
121461
+ effects.enter('magicBlockData');
121462
+ if (code === codes.quotationMark)
121463
+ jsonState.inString = true;
121464
+ effects.consume(code);
121465
+ return captureData;
121466
+ };
121467
+ // EOF handler
121468
+ const closingEof = (_code) => nok(_code);
121469
+ // Create closing marker parsers
121470
+ const closingFromBeforeData = createClosingMarkerParser(effects, closingSuccess, closingMismatch, closingEof, jsonState);
121471
+ const closingFromData = createClosingMarkerParser(effects, closingSuccess, closingMismatch, closingEof, jsonState);
121472
+ return start;
121473
+ function start(code) {
121474
+ if (code !== codes.leftSquareBracket)
121475
+ return nok(code);
121476
+ effects.enter('magicBlock');
121477
+ effects.enter('magicBlockMarkerStart');
121478
+ effects.consume(code);
121479
+ return openingMarkerParser;
121480
+ }
121481
+ /**
121482
+ * State before data content - handles line endings before entering data token.
121483
+ * Whitespace before '{' is allowed.
121484
+ */
121485
+ function beforeData(code) {
121486
+ // Fail on EOF - magic block must be closed
121487
+ if (code === null) {
121488
+ return nok(code);
121489
+ }
121490
+ // Handle line endings before any data - consume them without entering data token
121491
+ if (markdownLineEnding(code)) {
121492
+ effects.enter('magicBlockLineEnding');
121493
+ effects.consume(code);
121494
+ effects.exit('magicBlockLineEnding');
121495
+ return beforeData;
121496
+ }
121497
+ // Check for closing marker directly (without entering data)
121498
+ if (code === codes.leftSquareBracket) {
121499
+ effects.enter('magicBlockMarkerEnd');
121500
+ effects.consume(code);
121501
+ return closingFromBeforeData.expectSlash;
121502
+ }
121503
+ // If we've already seen the opening brace, just continue capturing data
121504
+ if (seenOpenBrace) {
121505
+ effects.enter('magicBlockData');
121506
+ return captureData(code);
121507
+ }
121508
+ // Skip whitespace (spaces/tabs) before the data
121509
+ if (code === codes.space || code === codes.horizontalTab) {
121510
+ return beforeDataWhitespace(code);
121511
+ }
121512
+ // Data must start with '{' for valid JSON
121513
+ if (code !== codes.leftCurlyBrace) {
121514
+ return nok(code);
121515
+ }
121516
+ // We have '{' - enter data token and start capturing
121517
+ seenOpenBrace = true;
121518
+ effects.enter('magicBlockData');
121519
+ return captureData(code);
121520
+ }
121521
+ /**
121522
+ * Consume whitespace before the data.
121523
+ */
121524
+ function beforeDataWhitespace(code) {
121525
+ if (code === null) {
121526
+ return nok(code);
121527
+ }
121528
+ if (markdownLineEnding(code)) {
121529
+ effects.enter('magicBlockLineEnding');
121530
+ effects.consume(code);
121531
+ effects.exit('magicBlockLineEnding');
121532
+ return beforeData;
121533
+ }
121534
+ if (code === codes.space || code === codes.horizontalTab) {
121535
+ effects.enter('magicBlockData');
121536
+ effects.consume(code);
121537
+ return beforeDataWhitespaceContinue;
121538
+ }
121539
+ if (code === codes.leftCurlyBrace) {
121540
+ seenOpenBrace = true;
121541
+ effects.enter('magicBlockData');
121542
+ return captureData(code);
121543
+ }
121544
+ return nok(code);
121545
+ }
121546
+ /**
121547
+ * Continue consuming whitespace or validate the opening brace.
121548
+ */
121549
+ function beforeDataWhitespaceContinue(code) {
121550
+ if (code === null) {
121551
+ effects.exit('magicBlockData');
121552
+ return nok(code);
121553
+ }
121554
+ if (markdownLineEnding(code)) {
121555
+ effects.exit('magicBlockData');
121556
+ effects.enter('magicBlockLineEnding');
121557
+ effects.consume(code);
121558
+ effects.exit('magicBlockLineEnding');
121559
+ return beforeData;
121560
+ }
121561
+ if (code === codes.space || code === codes.horizontalTab) {
121562
+ effects.consume(code);
121563
+ return beforeDataWhitespaceContinue;
121564
+ }
121565
+ if (code === codes.leftCurlyBrace) {
121566
+ seenOpenBrace = true;
121567
+ return captureData(code);
121568
+ }
121569
+ effects.exit('magicBlockData');
121570
+ return nok(code);
121571
+ }
121572
+ function captureData(code) {
121573
+ // Fail on EOF - magic block must be closed
121574
+ if (code === null) {
121575
+ effects.exit('magicBlockData');
121576
+ return nok(code);
121577
+ }
121578
+ // Handle multiline magic blocks within text/paragraphs
121579
+ // Exit data, consume line ending with proper token, then re-enter data
121580
+ if (markdownLineEnding(code)) {
121581
+ effects.exit('magicBlockData');
121582
+ effects.enter('magicBlockLineEnding');
121583
+ effects.consume(code);
121584
+ effects.exit('magicBlockLineEnding');
121585
+ return beforeData; // Go back to beforeData to handle potential empty lines
121586
+ }
121587
+ if (jsonState.escapeNext) {
121588
+ jsonState.escapeNext = false;
121589
+ effects.consume(code);
121590
+ return captureData;
121591
+ }
121592
+ if (jsonState.inString) {
121593
+ if (code === codes.backslash) {
121594
+ jsonState.escapeNext = true;
121595
+ effects.consume(code);
121596
+ return captureData;
121597
+ }
121598
+ if (code === codes.quotationMark) {
121599
+ jsonState.inString = false;
121600
+ }
121601
+ effects.consume(code);
121602
+ return captureData;
121603
+ }
121604
+ if (code === codes.quotationMark) {
121605
+ jsonState.inString = true;
121606
+ effects.consume(code);
121607
+ return captureData;
121608
+ }
121609
+ if (code === codes.leftSquareBracket) {
121610
+ effects.exit('magicBlockData');
121611
+ effects.enter('magicBlockMarkerEnd');
121612
+ effects.consume(code);
121613
+ return closingFromData.expectSlash;
121614
+ }
121615
+ effects.consume(code);
121616
+ return captureData;
121617
+ }
121618
+ }
121619
+ /* harmony default export */ const syntax = ((/* unused pure expression or super */ null && (magicBlock)));
121620
+
121621
+ ;// ./lib/micromark/magic-block/index.ts
121622
+ /**
121623
+ * Micromark extension for magic block syntax.
121624
+ *
121625
+ * Usage:
121626
+ * ```ts
121627
+ * import { magicBlock } from './lib/micromark/magic-block';
121628
+ *
121629
+ * const processor = unified()
121630
+ * .data('micromarkExtensions', [magicBlock()])
121631
+ * ```
121632
+ */
121633
+
121634
+
121635
+ ;// ./lib/micromark/mdx-component/syntax.ts
121636
+
121637
+
121638
+
121639
+
121640
+
121641
+ // Raw tags (type-1: pre/script/style/textarea) and block tags (type-6: div,
121642
+ // section, …) always start a block, so they stay flow even with trailing
121643
+ // content. Other lowercase tags (i, span, …) follow the type-7 rule and only
121644
+ // stay flow when nothing trails the close tag.
121645
+ const htmlFlowTagNames = new Set([...htmlRawNames, ...htmlBlockNames]);
121646
+ // Lowercase type-6 block tags claimable in flow even without a `{…}` attribute, so
121647
+ // blank lines between nested JSX siblings don't fragment the block. Excludes table
121648
+ // tags (mdxishTables owns their blank lines) and voids (never close).
121649
+ const plainBlockClaimTagNames = new Set([...htmlBlockNames].filter(tag => !HTML_TABLE_STRUCTURE_TAGS.has(tag) && !HTML_VOID_ELEMENTS.has(tag)));
121650
+ const mdx_component_syntax_nonLazyContinuationStart = {
121651
+ tokenize: mdx_component_syntax_tokenizeNonLazyContinuationStart,
121652
+ partial: true,
121653
+ };
121654
+ // Lookahead for `plainClaimLineStart`: is this line markup-only, or a paragraph
121655
+ // that merely starts with a tag? Run via `effects.check` so it never consumes.
121656
+ const markupOnlyContinuation = {
121657
+ tokenize: tokenizeMarkupOnlyContinuation,
121658
+ partial: true,
121659
+ };
121660
+ function resolveToMdxComponent(events) {
121661
+ let index = events.length;
121662
+ while (index > 0) {
121663
+ index -= 1;
121664
+ if (events[index][0] === 'enter' && events[index][1].type === 'mdxComponent') {
121665
+ break;
121666
+ }
121667
+ }
121668
+ if (index > 1 && events[index - 2][1].type === types_types.linePrefix) {
121669
+ events[index][1].start = events[index - 2][1].start;
121670
+ events[index + 1][1].start = events[index - 2][1].start;
121671
+ events.splice(index - 2, 2);
121672
+ }
121673
+ return events;
121674
+ }
121675
+ const mdxComponentFlowConstruct = {
121676
+ name: 'mdxComponent',
121677
+ tokenize: createTokenize('flow'),
121678
+ resolveTo: resolveToMdxComponent,
121679
+ concrete: true,
121680
+ };
121681
+ const mdxComponentTextConstruct = {
121682
+ name: 'mdxComponentText',
121683
+ tokenize: createTokenize('text'),
121684
+ };
121685
+ /**
121686
+ * Factory for both flow (block) and text (inline) variants.
121687
+ *
121688
+ * **Flow** — the original behavior: claims PascalCase components (always) and
121689
+ * lowercase HTML tags that carry at least one `{…}` attribute expression.
121426
121690
  * Multi-line, concrete, `afterClose` consumes the rest of the line.
121427
121691
  *
121428
121692
  * **Text** — runs inside paragraphs / inline context. Claims lowercase tags and
@@ -121750,7 +122014,7 @@ function createTokenize(mode) {
121750
122014
  }
121751
122015
  // Continuation for multi-line opening tags
121752
122016
  function openTagContinuationStart(code) {
121753
- return effects.check(syntax_nonLazyContinuationStart, openTagContinuationNonLazy, continuationAfter)(code);
122017
+ return effects.check(mdx_component_syntax_nonLazyContinuationStart, openTagContinuationNonLazy, continuationAfter)(code);
121754
122018
  }
121755
122019
  function openTagContinuationNonLazy(code) {
121756
122020
  sawLineEnding = true;
@@ -121875,7 +122139,7 @@ function createTokenize(mode) {
121875
122139
  return inFencedCode;
121876
122140
  }
121877
122141
  function fencedCodeContinuationStart(code) {
121878
- return effects.check(syntax_nonLazyContinuationStart, fencedCodeContinuationNonLazy, continuationAfter)(code);
122142
+ return effects.check(mdx_component_syntax_nonLazyContinuationStart, fencedCodeContinuationNonLazy, continuationAfter)(code);
121879
122143
  }
121880
122144
  function fencedCodeContinuationNonLazy(code) {
121881
122145
  sawLineEnding = true;
@@ -121890,6 +122154,16 @@ function createTokenize(mode) {
121890
122154
  }
121891
122155
  effects.enter('mdxComponentData');
121892
122156
  fenceCloseLength = 0;
122157
+ return fencedCodeMaybeClose(code);
122158
+ }
122159
+ // Skip leading indentation before the closing-fence check: an indented fence
122160
+ // (the norm in a component body) closes on an equally-indented line, else the
122161
+ // closer is never matched and scanning runs to EOF (CX-3704).
122162
+ function fencedCodeMaybeClose(code) {
122163
+ if (markdownSpace(code)) {
122164
+ effects.consume(code);
122165
+ return fencedCodeMaybeClose;
122166
+ }
121893
122167
  // Check for closing fence
121894
122168
  if (code === fenceChar) {
121895
122169
  fenceCloseLength = 1;
@@ -122073,7 +122347,7 @@ function createTokenize(mode) {
122073
122347
  }
122074
122348
  // ── Body continuation (line endings) ───────────────────────────────────
122075
122349
  function bodyContinuationStart(code) {
122076
- return effects.check(syntax_nonLazyContinuationStart, bodyContinuationNonLazy, continuationAfter)(code);
122350
+ return effects.check(mdx_component_syntax_nonLazyContinuationStart, bodyContinuationNonLazy, continuationAfter)(code);
122077
122351
  }
122078
122352
  function bodyContinuationNonLazy(code) {
122079
122353
  sawLineEnding = true;
@@ -122105,6 +122379,13 @@ function createTokenize(mode) {
122105
122379
  }
122106
122380
  // Dispatch a non-blank continuation line: fenced code at line start, else body.
122107
122381
  function bodyLineStart(code) {
122382
+ // Skip leading indentation while staying "at line start" so an indented
122383
+ // fence is still recognized. Otherwise the first space clears `atLineStart`
122384
+ // and its content — including any unbalanced `{` — stays live text (CX-3704).
122385
+ if (atLineStart && markdownSpace(code)) {
122386
+ effects.consume(code);
122387
+ return bodyLineStart;
122388
+ }
122108
122389
  if (atLineStart && code === codes.tilde)
122109
122390
  return bodyAfterLineStart(code);
122110
122391
  if (atLineStart && code === codes.graveAccent) {
@@ -122155,7 +122436,7 @@ function createTokenize(mode) {
122155
122436
  }
122156
122437
  };
122157
122438
  }
122158
- function syntax_tokenizeNonLazyContinuationStart(effects, ok, nok) {
122439
+ function mdx_component_syntax_tokenizeNonLazyContinuationStart(effects, ok, nok) {
122159
122440
  // eslint-disable-next-line @typescript-eslint/no-this-alias
122160
122441
  const self = this;
122161
122442
  return start;
@@ -122263,9 +122544,20 @@ function mdxComponent() {
122263
122544
 
122264
122545
 
122265
122546
 
122547
+
122548
+
122549
+
122550
+
122551
+
122552
+
122266
122553
  const buildInlineMdProcessor = (safeMode) => {
122267
- const micromarkExts = [mdxComponent(), syntax_gemoji(), legacyVariable(), magicBlock()];
122554
+ // `jsxTable` must be present so a `<Table>`/`<table>` nested in a component
122555
+ // body (e.g. a `<Callout>`) is captured as one html node. Without it, blank
122556
+ // lines between rows let CommonMark HTML block type 6 fragment the table and
122557
+ // its rows spill out as text / indented code blocks (CX-3705).
122558
+ const micromarkExts = [jsxTable(), mdxComponent(), syntax_gemoji(), legacyVariable(), magicBlock()];
122268
122559
  const fromMarkdownExts = [
122560
+ jsxTableFromMarkdown(),
122269
122561
  mdxComponentFromMarkdown(),
122270
122562
  gemojiFromMarkdown(),
122271
122563
  legacyVariableFromMarkdown(),
@@ -122325,6 +122617,65 @@ const toMdxJsxTextElement = (name, attributes, children, position) => ({
122325
122617
  children,
122326
122618
  ...(position ? { position } : {}),
122327
122619
  });
122620
+ // Raw-body tags (pre/script/style/textarea) must stay byte-exact; table
122621
+ // structure (`table` + `tableTags`) is owned by `mdxishTables` and figures by
122622
+ // `mdxishJsxToMdast`, both of which run later on raw html nodes.
122623
+ const NON_PROMOTABLE_PLAIN_TAGS = new Set([...htmlRawNames, ...tableTags, 'table', 'figure', 'figcaption']);
122624
+ const NESTED_TABLE_RE = /<table[\s>]/i;
122625
+ const isMarkdownPromotableHtmlTag = (tag) => !NON_PROMOTABLE_PLAIN_TAGS.has(tag);
122626
+ // Expression nodes count as plain so `<div>{1+1}</div>` keeps its current
122627
+ // literal-brace behavior; variables/glossary already resolve inside raw html.
122628
+ const PLAIN_CONTENT_TYPES = new Set([
122629
+ 'paragraph',
122630
+ 'text',
122631
+ 'html',
122632
+ 'mdxTextExpression',
122633
+ 'mdxFlowExpression',
122634
+ NodeTypes.variable,
122635
+ NodeTypes.glossary,
122636
+ ]);
122637
+ // Promoting plain HTML is only worth bypassing rehype-raw's parse5 pass when
122638
+ // the body parses into an actual markdown construct.
122639
+ const containsMarkdownConstruct = (nodes) => nodes.some(node => !PLAIN_CONTENT_TYPES.has(node.type) ||
122640
+ ('children' in node && Array.isArray(node.children) && containsMarkdownConstruct(node.children)));
122641
+ /**
122642
+ * Index of the `</tag>` that balances the already-consumed opening tag (the
122643
+ * caller starts us one level deep). `lastIndexOf` mis-slices sibling same-tag
122644
+ * pairs like `<div>**a**</div><div>**b**</div>`, so we depth-match instead —
122645
+ * delegating to `walkTags` (htmlparser2) so quoted attributes (`title="</div>"`),
122646
+ * code spans, and legacy `<<VARIABLE>>` are handled for free. Returns -1 when
122647
+ * the wrapper is left open.
122648
+ */
122649
+ function findBalancedClosingTagIndex(content, tag) {
122650
+ const target = tag.toLowerCase();
122651
+ const canonicalCloserLength = tag.length + 3; // `</tag>`
122652
+ // The caller already stripped the opening tag, so re-attach one: htmlparser2
122653
+ // drops an unmatched closer, and we want it balanced. Offsets shift by the
122654
+ // prefix length.
122655
+ const prefix = `<${tag}>`;
122656
+ let depth = 0;
122657
+ let closeIndex = -1;
122658
+ walkTags(prefix + content, {
122659
+ onOpen: ({ name, isSelfClosing, isStrayCloser }) => {
122660
+ if (closeIndex >= 0 || isSelfClosing || isStrayCloser)
122661
+ return;
122662
+ if (name.toLowerCase() === target)
122663
+ depth += 1;
122664
+ },
122665
+ onClose: ({ name, start, end, implicit }) => {
122666
+ if (closeIndex >= 0 || implicit || name.toLowerCase() !== target)
122667
+ return;
122668
+ // Only canonical `</tag>` closers — the caller slices by that length, so a
122669
+ // whitespaced `</tag >` would misalign; leaving it unmatched keeps it raw.
122670
+ if (end - start !== canonicalCloserLength)
122671
+ return;
122672
+ depth -= 1;
122673
+ if (depth === 0)
122674
+ closeIndex = start - prefix.length;
122675
+ },
122676
+ });
122677
+ return closeIndex;
122678
+ }
122328
122679
 
122329
122680
  ;// ./processor/transform/mdxish/components/inline-html.ts
122330
122681
 
@@ -122456,10 +122807,36 @@ const mdxishInlineMdxComponents = () => tree => {
122456
122807
  };
122457
122808
  /* harmony default export */ const inline_mdx_blocks = (mdxishInlineMdxComponents);
122458
122809
 
122810
+ ;// ./processor/transform/mdxish/indentation.ts
122811
+ /**
122812
+ * CommonMark indentation helpers, shared by the mdxish preprocessors so tab- and
122813
+ * space-indented content is measured (and sliced) on one scale.
122814
+ *
122815
+ * CommonMark's indented-code threshold is 4 *columns*, and a tab is a 4-column tab
122816
+ * stop — not a fixed 4 characters. Counting characters (tab = 1) under-measures
122817
+ * tab-indented lines, letting them slip past the 4-column gate so their content
122818
+ * fragments into code blocks. Keeping one implementation here stops the two callers
122819
+ * from drifting on what "4 columns" means.
122820
+ */
122821
+ /** The run of leading spaces/tabs on a line. */
122822
+ const leadingIndent = (line) => line.match(/^[ \t]*/)?.[0] ?? '';
122823
+ /**
122824
+ * Expand a leading-whitespace run to spaces using CommonMark's 4-column tab stops,
122825
+ * so a run of mixed tabs/spaces can be measured by length and sliced by column.
122826
+ */
122827
+ function expandIndentToColumns(indent) {
122828
+ return indent
122829
+ .split('')
122830
+ .reduce((columns, char) => columns + (char === '\t' ? ' '.repeat(4 - (columns.length % 4)) : ' '), '');
122831
+ }
122832
+ /** Indentation width of a line in CommonMark columns (a tab advances to the next 4-column stop). */
122833
+ const indentWidth = (line) => expandIndentToColumns(leadingIndent(line)).length;
122834
+
122459
122835
  ;// ./processor/transform/mdxish/terminate-html-flow-blocks.ts
122460
122836
 
122461
122837
 
122462
122838
 
122839
+
122463
122840
  const STANDALONE_HTML_LINE_REGEX = /^(<[a-z][^<>]*>|<\/[a-z][^<>]*>)+\s*$/;
122464
122841
  const HTML_LINE_WITH_CONTENT_REGEX = /^<[a-z][^<>]*>.*<\/[a-z][^<>]*>(?:[^<]*)$/;
122465
122842
  // A line that is exactly one lowercase opening (or self-closing) tag — the shape
@@ -122485,11 +122862,6 @@ function countRawContentTags(line) {
122485
122862
  closes: (line.match(close) ?? []).length,
122486
122863
  }));
122487
122864
  }
122488
- // Indentation width in columns, counting a tab as 4 per CommonMark.
122489
- function indentWidth(line) {
122490
- const leading = line.match(/^[ \t]*/)?.[0] ?? '';
122491
- return leading.replace(/\t/g, ' ').length;
122492
- }
122493
122865
  /**
122494
122866
  * Decides whether a blank line belongs between `line` and `next` so the HTML
122495
122867
  * flow block opened on `line` terminates and `next` parses as markdown.
@@ -122561,6 +122933,7 @@ function terminateHtmlFlowBlocks(content) {
122561
122933
 
122562
122934
 
122563
122935
 
122936
+
122564
122937
  // Matches a JSX attribute expression (e.g. `key={i}`) anywhere in a string. */
122565
122938
  const NESTED_ATTR_EXPRESSION_RE = /[\w-]+\s*=\s*\{/;
122566
122939
  // Name shape mirrors `componentTagPattern`; the lookbehind skips the inner tag
@@ -122571,25 +122944,33 @@ const NESTED_COMPONENT_TAG_RE = /(?<!<)<([A-Z][A-Za-z0-9_]*)[\s/>]/g;
122571
122944
  const hasNestedGenericComponentTag = (content) => [...content.matchAll(NESTED_COMPONENT_TAG_RE)].some(match => !GENERIC_MDX_COMPONENT_EXCLUDED_TAGS.has(match[1]));
122572
122945
  /**
122573
122946
  * Strip the shared leading indentation from a component body so readability indentation
122574
- * isn't parsed as indented code (4+ spaces), e.g. ` <p>` / ` text` -> `<p>` / ` text`.
122947
+ * isn't parsed as indented code (4+ columns), e.g. ` <p>` / ` text` -> `<p>` / ` text`.
122575
122948
  * Relative indentation is kept, so content genuinely 4+ columns deeper stays code. We
122576
122949
  * only strip when a line actually reaches 4 columns; otherwise the body is left as-is so
122577
122950
  * its leading whitespace survives as text nodes (mixed component + HTML content needs it).
122951
+ *
122952
+ * Indentation is measured in CommonMark columns (tab = up to 4), matching how the parser
122953
+ * decides indented code. A char count (tab = 1) under-measures tab-indented bodies, so
122954
+ * they slip past the 4-column gate and their nested content fragments into code blocks.
122578
122955
  */
122579
122956
  function safeDeindent(text) {
122580
122957
  const lines = text.split('\n');
122581
122958
  const nonEmptyLines = lines.filter(line => line.trim().length > 0);
122582
122959
  if (nonEmptyLines.length === 0)
122583
122960
  return text;
122584
- // Indent counts characters (tab = 1), unlike indentWidth's CommonMark columns
122585
- // (tab = 4) in terminate-html-flow-blocks.
122586
- const indents = nonEmptyLines.map(line => line.match(/^(\s*)/)?.[1].length ?? 0);
122961
+ const indents = nonEmptyLines.map(line => expandIndentToColumns(leadingIndent(line)).length);
122587
122962
  const minIndent = Math.min(...indents);
122588
122963
  const maxIndent = Math.max(...indents);
122589
- const stripAmount = maxIndent > 3 ? minIndent : 0;
122590
- if (stripAmount === 0)
122964
+ if (maxIndent < 4 || minIndent === 0)
122591
122965
  return text;
122592
- return lines.map(line => line.slice(stripAmount)).join('\n');
122966
+ // Expand each line's leading run to spaces before slicing so a shared indent of mixed
122967
+ // tabs/spaces (and partial-tab remainders) strips cleanly while relative depth survives.
122968
+ return lines
122969
+ .map(line => {
122970
+ const indent = leadingIndent(line);
122971
+ return expandIndentToColumns(indent).slice(minIndent) + line.slice(indent.length);
122972
+ })
122973
+ .join('\n');
122593
122974
  }
122594
122975
  /**
122595
122976
  * Parse component-body markdown into mdast children. Dedenting shifts columns and
@@ -122649,6 +123030,10 @@ const substituteNodeWithMdxNode = (parent, index, mdxNode) => {
122649
123030
  * This transformer identifies these patterns and converts them to proper MDX JSX elements so they
122650
123031
  * can be accurately recognized and rendered later with their component definition code.
122651
123032
  *
123033
+ * Note: The main goal is to promote PascalCase tags to MDX elements, but we want to promote
123034
+ * normal HTML to MDX elements in some cases so they get the full custom parsing behavior.
123035
+ * E.g. tags with JSX expressions, nested components, etc.
123036
+ *
122652
123037
  * The mdx-component micromark tokenizer ensures that multi-line components are captured
122653
123038
  * as single HTML nodes, so this transformer only needs to handle two cases:
122654
123039
  *
@@ -122695,6 +123080,7 @@ const mdxishMdxComponentBlocks = (opts = {}) => (tree, file) => {
122695
123080
  if (GENERIC_MDX_COMPONENT_EXCLUDED_TAGS.has(tag))
122696
123081
  return; // owned by dedicated transformers
122697
123082
  const isPascal = isPascalCase(tag);
123083
+ // ==== SPECIAL CASES TO PROMOTE NORMAL HTML TO MDX ELEMENTS ====
122698
123084
  // Lowercase inline tags with `{…}` attributes belong to
122699
123085
  // mdxishInlineComponentBlocks; leave them as html for that pass. PascalCase
122700
123086
  // components stay flow-level even when inline (ReadMe's component model).
@@ -122703,11 +123089,19 @@ const mdxishMdxComponentBlocks = (opts = {}) => (tree, file) => {
122703
123089
  // A lowercase wrapper is only promoted when it (or a descendant) carries a
122704
123090
  // JSX expression or nests a component; otherwise it would swallow that inner
122705
123091
  // JSX/component as literal text that rehype-raw's parse5 pass can't handle.
122706
- // Table-structural wrappers are excluded — `mdxishTables` re-parses those.
122707
- const hasNestedExpressionAttr = !selfClosing && NESTED_ATTR_EXPRESSION_RE.test(contentAfterTag);
123092
+ // Table-structural wrappers are excluded from both — `mdxishTables` re-parses
123093
+ // those, so a `{…}` in a cell (e.g. `<code>--depth={n}</code>`) must not
123094
+ // accidentally promote the table to an MDX element prematurely.
122708
123095
  const isTableStructuralTag = tag === 'table' || tableTags.has(tag);
123096
+ const hasNestedExpressionAttr = !selfClosing && !isTableStructuralTag && NESTED_ATTR_EXPRESSION_RE.test(contentAfterTag);
122709
123097
  const hasNestedComponentTag = !selfClosing && !isTableStructuralTag && hasNestedGenericComponentTag(contentAfterTag);
122710
- if (!isPascal && !hasExpressionAttr(attributes) && !hasNestedExpressionAttr && !hasNestedComponentTag)
123098
+ // Promotion: By default commonmark doesn't parse markdown in single line HTML tags (e.g. <div>**bold**</div>)
123099
+ // To support that, we try to promote them to MDX elements so the markdown gets parsed
123100
+ const isPlainLowercaseHtml = !isPascal && !hasExpressionAttr(attributes) && !hasNestedExpressionAttr && !hasNestedComponentTag;
123101
+ const plainClosingTagIndex = isPlainLowercaseHtml && !selfClosing && isMarkdownPromotableHtmlTag(tag) && !NESTED_TABLE_RE.test(contentAfterTag)
123102
+ ? findBalancedClosingTagIndex(contentAfterTag, tag)
123103
+ : -1;
123104
+ if (isPlainLowercaseHtml && plainClosingTagIndex < 0)
122711
123105
  return;
122712
123106
  const closingTagStr = `</${tag}>`;
122713
123107
  // Case 1: Self-closing tag
@@ -122728,14 +123122,26 @@ const mdxishMdxComponentBlocks = (opts = {}) => (tree, file) => {
122728
123122
  return;
122729
123123
  }
122730
123124
  // Case 2: Self-contained block (closing tag in content)
122731
- if (contentAfterTag.includes(closingTagStr)) {
122732
- const closingTagIndex = contentAfterTag.lastIndexOf(closingTagStr);
123125
+ const closingTagIndex = isPlainLowercaseHtml ? plainClosingTagIndex : contentAfterTag.lastIndexOf(closingTagStr);
123126
+ if (closingTagIndex >= 0) {
122733
123127
  // Untrimmed so parseMdChildren can dedent before trimming.
122734
123128
  const componentInnerContent = contentAfterTag.substring(0, closingTagIndex);
122735
123129
  const contentAfterClose = contentAfterTag.substring(closingTagIndex + closingTagStr.length).trim();
122736
- let parsedChildren = componentInnerContent.trim()
122737
- ? parseMdChildren(componentInnerContent, safeMode)
122738
- : [];
123130
+ let parsedChildren = [];
123131
+ if (componentInnerContent.trim()) {
123132
+ try {
123133
+ parsedChildren = parseMdChildren(componentInnerContent, safeMode);
123134
+ }
123135
+ catch (error) {
123136
+ // Plain HTML bodies can hold anything (e.g. stray braces the strict
123137
+ // expression parser rejects) — keep the node raw instead of throwing.
123138
+ if (isPlainLowercaseHtml)
123139
+ return;
123140
+ throw error;
123141
+ }
123142
+ }
123143
+ if (isPlainLowercaseHtml && !containsMarkdownConstruct(parsedChildren))
123144
+ return;
122739
123145
  // Lowercase tags are usually inline; unwrap a sole paragraph so their
122740
123146
  // phrasing content isn't spuriously block-wrapped.
122741
123147
  if (!isPascal && parsedChildren.length === 1 && parsedChildren[0].type === 'paragraph') {
@@ -123741,7 +124147,7 @@ const textToBlock = (text) => [{ children: textToInline(text), type: 'paragraph'
123741
124147
  /**
123742
124148
  * Converts leading newlines in magic block content to `<br>` tags.
123743
124149
  * Leading newlines are stripped by remark-parse before they become soft break nodes,
123744
- * so remark-breaks cannot handle them. We convert them to HTML `<br>` tags instead.
124150
+ * so the hard-breaks plugin cannot handle them. We convert them to HTML `<br>` tags instead.
123745
124151
  */
123746
124152
  const ensureLeadingBreaks = (text) => text.replace(/^\n+/, match => '<br>'.repeat(match.length));
123747
124153
  /** Preprocesses magic block body content before parsing. */
@@ -123753,7 +124159,7 @@ const contentParser = unified()
123753
124159
  .data('micromarkExtensions', [syntax_gemoji(), legacyVariable(), looseHtmlEntity()])
123754
124160
  .data('fromMarkdownExtensions', [gemojiFromMarkdown(), legacyVariableFromMarkdown(), emptyTaskListItemFromMarkdown(), looseHtmlEntityFromMarkdown()])
123755
124161
  .use(remarkParse)
123756
- .use(remarkBreaks)
124162
+ .use(hard_breaks)
123757
124163
  .use(remarkGfm)
123758
124164
  .use(normalize_malformed_md_syntax);
123759
124165
  /**
@@ -124300,9 +124706,6 @@ const magicBlockTransformer = (options = {}) => tree => {
124300
124706
  // single-line `<div>…</div>`). CommonMark slurps the whole div as one `html`
124301
124707
  // node, so the tokenizer never sees the HTMLBlock — we recover it here.
124302
124708
  const RAW_HTML_BLOCK_RE = /<HTMLBlock\b([^>]*)>\s*\{\s*`((?:[^`\\]|\\.)*)`\s*\}\s*<\/HTMLBlock>/g;
124303
- // Opening `<HTMLBlock …>` as its own `html` node — produced inside a paragraph
124304
- // when an HTMLBlock appears inline alongside text.
124305
- const HTML_BLOCK_OPEN_RE = /^<HTMLBlock\b([^>]*)>$/;
124306
124709
  /**
124307
124710
  * Builds the canonical `html-block` MDAST node the renderer expects.
124308
124711
  */
@@ -124383,13 +124786,14 @@ const splitRawHtmlBlocks = (node) => {
124383
124786
  /**
124384
124787
  * Converts every `<HTMLBlock>` shape that survives parsing into the canonical
124385
124788
  * `html-block` MDAST node, reading the body from the tokenizer's template-literal
124386
- * expression. Three shapes occur:
124789
+ * expression. Two shapes occur:
124387
124790
  *
124388
- * 1. JSX element (`mdxJsxFlowElement`/`mdxJsxTextElement`) — multiline/block
124389
- * context and table cells (after their remarkMdx re-parse).
124390
- * 2. Raw `html` blob (`splitRawHtmlBlocks`) single-line top-level, or nested
124391
- * in raw HTML like an inline `<div>`.
124392
- * 3. Inline-in-paragraph split into `html` + expression + `html` siblings.
124791
+ * 1. JSX element (`mdxJsxFlowElement`/`mdxJsxTextElement`) — table cells, after
124792
+ * their remarkMdx re-parse (that re-parse runs without the htmlBlockComponent
124793
+ * tokenizer, so `<HTMLBlock>` arrives as JSX rather than an opaque `html` node).
124794
+ * 2. Raw `html` blob (`splitRawHtmlBlocks`) the htmlBlockComponent tokenizer's
124795
+ * opaque output (top-level and inline), or an `<HTMLBlock>` embedded in a
124796
+ * larger raw-HTML node like an inline `<div>` that the tokenizer never saw.
124393
124797
  *
124394
124798
  * Runs *after* `mdxishTables` so table cells are re-parsed first;
124395
124799
  * `mdxishTables` recognizes the still-JSX `<HTMLBlock>` element when deciding to
@@ -124414,36 +124818,6 @@ const mdxishHtmlBlocks = () => tree => {
124414
124818
  if (replacement)
124415
124819
  parent.children.splice(index, 1, ...replacement);
124416
124820
  });
124417
- // Shape 3: inline within a paragraph — `<HTMLBlock>` open/close arrive as
124418
- // separate `html` siblings with the template-literal expression between them.
124419
- visit(tree, 'paragraph', (paragraph) => {
124420
- // An html-block is block content, so it isn't a valid PhrasingContent child;
124421
- // widen to RootContent (which HTMLBlock belongs to) for the in-place splice.
124422
- const children = paragraph.children;
124423
- for (let i = 0; i < children.length; i += 1) {
124424
- const open = children[i];
124425
- const openMatch = open.type === 'html' ? open.value.match(HTML_BLOCK_OPEN_RE) : null;
124426
- if (!openMatch)
124427
- continue; // eslint-disable-line no-continue
124428
- const closeIdx = children.findIndex((child, j) => j > i && child.type === 'html' && child.value === '</HTMLBlock>');
124429
- if (closeIdx === -1)
124430
- continue; // eslint-disable-line no-continue
124431
- const body = children
124432
- .slice(i + 1, closeIdx)
124433
- .map(child => {
124434
- if (child.type === 'mdxTextExpression' || child.type === 'mdxFlowExpression') {
124435
- return extractTemplateLiteral(child.value);
124436
- }
124437
- // Preserve raw text from any other phrasing sibling (e.g. stray
124438
- // whitespace or content the tokenizer didn't claim) so it isn't
124439
- // silently dropped from the html payload.
124440
- return 'value' in child && typeof child.value === 'string' ? child.value : '';
124441
- })
124442
- .join('');
124443
- const openingTagIndent = (open.position?.start.column ?? 1) - 1;
124444
- children.splice(i, closeIdx - i + 1, htmlBlockFromRaw(openMatch[1], body, open.position, openingTagIndent));
124445
- }
124446
- });
124447
124821
  };
124448
124822
  /* harmony default export */ const mdxish_html_blocks = (mdxishHtmlBlocks);
124449
124823
 
@@ -125143,6 +125517,29 @@ const mdxishMermaidTransformer = () => (tree) => {
125143
125517
  };
125144
125518
  /* harmony default export */ const mdxish_mermaid = (mdxishMermaidTransformer);
125145
125519
 
125520
+ ;// ./processor/transform/mdxish/normalize-closing-tag-whitespace.ts
125521
+
125522
+
125523
+ // Spaces/tabs only — newlines would let prose `<` / `>` across lines look like one tag.
125524
+ const SPACED_CLOSING_TAG_RE = /<\/[ \t]*([a-zA-Z][a-zA-Z0-9-]*)[ \t]*>/g;
125525
+ /**
125526
+ * Canonicalize spaced closing tags (`</ td >` → `</td>`) for known HTML names.
125527
+ *
125528
+ * In HTML, `</` + whitespace is a bogus comment, so `</ table >` never closes the
125529
+ * table (jsxTable misses it → empty table + pre; CX-3706). Only standard HTML tags;
125530
+ * custom components, prose (`a </ b`), and code blocks are left alone.
125531
+ *
125532
+ * @example
125533
+ * normalizeClosingTagWhitespace('</ td >') // '</td>'
125534
+ * normalizeClosingTagWhitespace('</table >') // '</table>'
125535
+ * normalizeClosingTagWhitespace('a </ b >') // unchanged
125536
+ */
125537
+ function normalizeClosingTagWhitespace(content) {
125538
+ const { protectedContent, protectedCode } = protectCodeBlocks(content);
125539
+ const result = protectedContent.replace(SPACED_CLOSING_TAG_RE, (match, tagName) => STANDARD_HTML_TAGS.has(tagName.toLowerCase()) ? `</${tagName}>` : match);
125540
+ return restoreCodeBlocks(result, protectedCode);
125541
+ }
125542
+
125146
125543
  ;// ./processor/transform/mdxish/normalize-compact-headings.ts
125147
125544
 
125148
125545
  /**
@@ -125623,186 +126020,463 @@ function resolveCodeVariables(value, resolvedVariables) {
125623
126020
  return name.toUpperCase();
125624
126021
  return name in resolvedVariables ? resolvedVariables[name] : name.toUpperCase();
125625
126022
  }
125626
- // MDX variable: {user.*}
125627
- if (mdxEscapePrefix || mdxEscapeSuffix)
125628
- return match;
125629
- if (mdxVarName in resolvedVariables)
125630
- return resolvedVariables[mdxVarName];
125631
- const fullPath = match.slice(1, -1);
125632
- return fullPath.toUpperCase();
125633
- });
125634
- }
125635
- /**
125636
- * A remark mdast plugin that resolves legacy variables <<...>> and MDX variables {user.*} inside code and inline code nodes
125637
- * to their values. Uses regexes from the readme variable to search for variables in the code string.
125638
- *
125639
- * This is needed because variables in code blocks and inline cannot be tokenized, and also we need to maintain the code string
125640
- * in the code nodes. This enables engine side variable resolution in codes which improves UX
125641
- */
125642
- const variablesCodeResolver = ({ variables } = {}) => tree => {
125643
- const resolvedVariables = variables_code_flattenVariables(variables);
125644
- visit(tree, 'inlineCode', (node) => {
125645
- if (!node.value)
125646
- return;
125647
- node.value = resolveCodeVariables(node.value, resolvedVariables);
125648
- });
125649
- visit(tree, 'code', (node) => {
125650
- if (!node.value)
125651
- return;
125652
- if (node.lang === 'mermaid')
125653
- return;
125654
- const nextValue = resolveCodeVariables(node.value, resolvedVariables);
125655
- node.value = nextValue;
125656
- // Keep code-tabs/readme-components hProperties in sync with node.value
125657
- // because renderers read `value` from hProperties.
125658
- if (node.data?.hProperties && typeof node.data.hProperties === 'object') {
125659
- node.data.hProperties.value = nextValue;
126023
+ // MDX variable: {user.*}
126024
+ if (mdxEscapePrefix || mdxEscapeSuffix)
126025
+ return match;
126026
+ if (mdxVarName in resolvedVariables)
126027
+ return resolvedVariables[mdxVarName];
126028
+ const fullPath = match.slice(1, -1);
126029
+ return fullPath.toUpperCase();
126030
+ });
126031
+ }
126032
+ /**
126033
+ * A remark mdast plugin that resolves legacy variables <<...>> and MDX variables {user.*} inside code and inline code nodes
126034
+ * to their values. Uses regexes from the readme variable to search for variables in the code string.
126035
+ *
126036
+ * This is needed because variables in code blocks and inline cannot be tokenized, and also we need to maintain the code string
126037
+ * in the code nodes. This enables engine side variable resolution in codes which improves UX
126038
+ */
126039
+ const variablesCodeResolver = ({ variables } = {}) => tree => {
126040
+ const resolvedVariables = variables_code_flattenVariables(variables);
126041
+ visit(tree, 'inlineCode', (node) => {
126042
+ if (!node.value)
126043
+ return;
126044
+ node.value = resolveCodeVariables(node.value, resolvedVariables);
126045
+ });
126046
+ visit(tree, 'code', (node) => {
126047
+ if (!node.value)
126048
+ return;
126049
+ if (node.lang === 'mermaid')
126050
+ return;
126051
+ const nextValue = resolveCodeVariables(node.value, resolvedVariables);
126052
+ node.value = nextValue;
126053
+ // Keep code-tabs/readme-components hProperties in sync with node.value
126054
+ // because renderers read `value` from hProperties.
126055
+ if (node.data?.hProperties && typeof node.data.hProperties === 'object') {
126056
+ node.data.hProperties.value = nextValue;
126057
+ }
126058
+ });
126059
+ return tree;
126060
+ };
126061
+ /* harmony default export */ const variables_code = (variablesCodeResolver);
126062
+
126063
+ ;// ./processor/transform/mdxish/variables-text.ts
126064
+
126065
+
126066
+ /**
126067
+ * Matches {user.<field>} patterns:
126068
+ * - {user.name}
126069
+ * - {user.email}
126070
+ * - {user['field']}
126071
+ * - {user["field"]}
126072
+ *
126073
+ * Captures the field name in group 1 (dot notation) or group 2 (bracket notation)
126074
+ */
126075
+ const USER_VAR_REGEX = /\{user\.(\w+)\}|\{user\[['"](\w+)['"]\]\}/g;
126076
+ function makeVariableNode(varName, rawValue) {
126077
+ return {
126078
+ type: NodeTypes.variable,
126079
+ data: {
126080
+ hName: 'Variable',
126081
+ hProperties: { name: varName },
126082
+ },
126083
+ value: rawValue,
126084
+ };
126085
+ }
126086
+ /**
126087
+ * A remark plugin that parses {user.<field>} patterns from text nodes and
126088
+ * mdx expression nodes, creating Variable nodes for runtime resolution.
126089
+ *
126090
+ * Handles:
126091
+ * - `text` nodes: when safeMode is true or after expression evaluation
126092
+ * - `mdxTextExpression` nodes: when mdxExpression has parsed {user.*} before evaluation
126093
+ * - `mdxFlowExpression` nodes: when {user.*} appears on its own line (e.g. inside JSX table cells)
126094
+ *
126095
+ * Supports any user field: name, email, email_verified, exp, iat, etc.
126096
+ */
126097
+ function visitExpressionNode(node, index, parent) {
126098
+ if (index === undefined || !parent)
126099
+ return;
126100
+ const wrapped = `{${(node.value ?? '').trim()}}`;
126101
+ const matches = [...wrapped.matchAll(USER_VAR_REGEX)];
126102
+ if (matches.length !== 1)
126103
+ return;
126104
+ const varName = matches[0][1] || matches[0][2];
126105
+ parent.children.splice(index, 1, makeVariableNode(varName, wrapped));
126106
+ }
126107
+ const variablesTextTransformer = () => tree => {
126108
+ visit(tree, 'mdxTextExpression', (node, index, parent) => {
126109
+ visitExpressionNode(node, index, parent);
126110
+ });
126111
+ visit(tree, 'mdxFlowExpression', (node, index, parent) => {
126112
+ visitExpressionNode(node, index, parent);
126113
+ });
126114
+ visit(tree, 'text', (node, index, parent) => {
126115
+ if (index === undefined || !parent)
126116
+ return;
126117
+ // Skip if inside inline code
126118
+ if (parent.type === 'inlineCode')
126119
+ return;
126120
+ const text = node.value;
126121
+ if (typeof text !== 'string' || !text.trim())
126122
+ return;
126123
+ if (!text.includes('{user.') && !text.includes('{user['))
126124
+ return;
126125
+ const matches = [...text.matchAll(USER_VAR_REGEX)];
126126
+ if (matches.length === 0)
126127
+ return;
126128
+ const parts = [];
126129
+ let lastIndex = 0;
126130
+ matches.forEach(match => {
126131
+ const matchIndex = match.index ?? 0;
126132
+ // Add text before the match
126133
+ if (matchIndex > lastIndex) {
126134
+ parts.push({ type: 'text', value: text.slice(lastIndex, matchIndex) });
126135
+ }
126136
+ // Extract variable name from either capture group (dot or bracket notation)
126137
+ const varName = match[1] || match[2];
126138
+ parts.push(makeVariableNode(varName, match[0]));
126139
+ lastIndex = matchIndex + match[0].length;
126140
+ });
126141
+ // Add remaining text after last match
126142
+ if (lastIndex < text.length) {
126143
+ parts.push({ type: 'text', value: text.slice(lastIndex) });
126144
+ }
126145
+ // Replace node with parts
126146
+ if (parts.length > 1 || (parts.length === 1 && parts[0].type !== 'text')) {
126147
+ parent.children.splice(index, 1, ...parts);
126148
+ }
126149
+ });
126150
+ return tree;
126151
+ };
126152
+ /* harmony default export */ const variables_text = (variablesTextTransformer);
126153
+
126154
+ ;// ./lib/mdast-util/html-block-component/index.ts
126155
+ const html_block_component_contextMap = new WeakMap();
126156
+ function findHtmlBlockComponentToken() {
126157
+ const events = this.tokenStack;
126158
+ for (let i = events.length - 1; i >= 0; i -= 1) {
126159
+ if (events[i][0].type === 'htmlBlockComponent')
126160
+ return events[i][0];
126161
+ }
126162
+ return undefined;
126163
+ }
126164
+ function enterHtmlBlockComponent(token) {
126165
+ html_block_component_contextMap.set(token, { chunks: [], lastEndLine: token.start.line });
126166
+ this.enter({ type: 'html', value: '' }, token);
126167
+ }
126168
+ function exitHtmlBlockComponentData(token) {
126169
+ const componentToken = findHtmlBlockComponentToken.call(this);
126170
+ if (!componentToken)
126171
+ return;
126172
+ const ctx = html_block_component_contextMap.get(componentToken);
126173
+ if (ctx) {
126174
+ const gap = token.start.line - ctx.lastEndLine;
126175
+ if (ctx.chunks.length > 0 && gap > 0) {
126176
+ ctx.chunks.push('\n'.repeat(gap));
126177
+ }
126178
+ ctx.chunks.push(this.sliceSerialize(token));
126179
+ ctx.lastEndLine = token.end.line;
126180
+ }
126181
+ }
126182
+ function exitHtmlBlockComponent(token) {
126183
+ const ctx = html_block_component_contextMap.get(token);
126184
+ const node = this.stack[this.stack.length - 1];
126185
+ if (ctx) {
126186
+ node.value = ctx.chunks.join('');
126187
+ html_block_component_contextMap.delete(token);
126188
+ }
126189
+ this.exit(token);
126190
+ }
126191
+ function htmlBlockComponentFromMarkdown() {
126192
+ return {
126193
+ enter: {
126194
+ htmlBlockComponent: enterHtmlBlockComponent,
126195
+ },
126196
+ exit: {
126197
+ htmlBlockComponentData: exitHtmlBlockComponentData,
126198
+ htmlBlockComponent: exitHtmlBlockComponent,
126199
+ },
126200
+ };
126201
+ }
126202
+
126203
+ ;// ./lib/micromark/html-block-component/syntax.ts
126204
+
126205
+
126206
+ const TAG_SUFFIX = [
126207
+ codes.uppercaseT,
126208
+ codes.uppercaseM,
126209
+ codes.uppercaseL,
126210
+ codes.uppercaseB,
126211
+ codes.lowercaseL,
126212
+ codes.lowercaseO,
126213
+ codes.lowercaseC,
126214
+ codes.lowercaseK,
126215
+ ];
126216
+ // ---------------------------------------------------------------------------
126217
+ // Shared tokenizer factory
126218
+ // ---------------------------------------------------------------------------
126219
+ /**
126220
+ * Creates a tokenize function for `<HTMLBlock>...</HTMLBlock>`.
126221
+ *
126222
+ * - **flow** (block-level): supports multiline content via line continuations,
126223
+ * consumes trailing whitespace after the closing tag.
126224
+ * - **text** (inline): single-line only, exits immediately after the closing tag.
126225
+ */
126226
+ function syntax_createTokenize(mode) {
126227
+ return function tokenize(effects, ok, nok) {
126228
+ let depth = 1;
126229
+ function matchChars(chars, onMatch, onFail) {
126230
+ if (chars.length === 0)
126231
+ return onMatch;
126232
+ const next = (code) => {
126233
+ if (code === chars[0]) {
126234
+ effects.consume(code);
126235
+ return matchChars(chars.slice(1), onMatch, onFail);
126236
+ }
126237
+ return onFail(code);
126238
+ };
126239
+ return next;
126240
+ }
126241
+ function matchTagName(onMatch, onFail) {
126242
+ const next = (code) => {
126243
+ if (code === codes.uppercaseH) {
126244
+ effects.consume(code);
126245
+ return matchChars(TAG_SUFFIX, onMatch, onFail);
126246
+ }
126247
+ return onFail(code);
126248
+ };
126249
+ return next;
126250
+ }
126251
+ return start;
126252
+ function start(code) {
126253
+ if (code !== codes.lessThan)
126254
+ return nok(code);
126255
+ effects.enter('htmlBlockComponent');
126256
+ effects.enter('htmlBlockComponentData');
126257
+ effects.consume(code);
126258
+ return matchTagName(afterTagName, nok);
126259
+ }
126260
+ function afterTagName(code) {
126261
+ if (code === codes.greaterThan) {
126262
+ effects.consume(code);
126263
+ return body;
126264
+ }
126265
+ if (code === codes.space || code === codes.horizontalTab) {
126266
+ effects.consume(code);
126267
+ return inAttributes;
126268
+ }
126269
+ if (mode === 'flow' && markdownLineEnding(code)) {
126270
+ effects.exit('htmlBlockComponentData');
126271
+ return attributeContinuationStart(code);
126272
+ }
126273
+ if (code === codes.slash) {
126274
+ effects.consume(code);
126275
+ return selfClose;
126276
+ }
126277
+ return nok(code);
126278
+ }
126279
+ function inAttributes(code) {
126280
+ if (code === codes.greaterThan) {
126281
+ effects.consume(code);
126282
+ return body;
126283
+ }
126284
+ if (code === null) {
126285
+ return nok(code);
126286
+ }
126287
+ if (markdownLineEnding(code)) {
126288
+ if (mode === 'text')
126289
+ return nok(code);
126290
+ effects.exit('htmlBlockComponentData');
126291
+ return attributeContinuationStart(code);
126292
+ }
126293
+ effects.consume(code);
126294
+ return inAttributes;
126295
+ }
126296
+ function attributeContinuationStart(code) {
126297
+ return effects.check(html_block_component_syntax_nonLazyContinuationStart, attributeContinuationNonLazy, continuationAfter)(code);
126298
+ }
126299
+ function attributeContinuationNonLazy(code) {
126300
+ effects.enter(types_types.lineEnding);
126301
+ effects.consume(code);
126302
+ effects.exit(types_types.lineEnding);
126303
+ return attributeContinuationBefore;
126304
+ }
126305
+ function attributeContinuationBefore(code) {
126306
+ if (code === null || markdownLineEnding(code)) {
126307
+ return attributeContinuationStart(code);
126308
+ }
126309
+ effects.enter('htmlBlockComponentData');
126310
+ return inAttributes(code);
126311
+ }
126312
+ function selfClose(code) {
126313
+ if (code === codes.greaterThan) {
126314
+ effects.consume(code);
126315
+ return mode === 'flow' ? afterClose : done(code);
126316
+ }
126317
+ return nok(code);
126318
+ }
126319
+ function body(code) {
126320
+ if (code === null)
126321
+ return nok(code);
126322
+ if (markdownLineEnding(code)) {
126323
+ if (mode === 'text') {
126324
+ // Text constructs operate on paragraph content which spans lines
126325
+ effects.consume(code);
126326
+ return body;
126327
+ }
126328
+ effects.exit('htmlBlockComponentData');
126329
+ return continuationStart(code);
126330
+ }
126331
+ if (code === codes.lessThan) {
126332
+ effects.consume(code);
126333
+ return closeSlash;
126334
+ }
126335
+ effects.consume(code);
126336
+ return body;
126337
+ }
126338
+ function closeSlash(code) {
126339
+ if (code === codes.slash) {
126340
+ effects.consume(code);
126341
+ return matchTagName(closeGt, body);
126342
+ }
126343
+ return matchTagName(openAfterTagName, body)(code);
126344
+ }
126345
+ function openAfterTagName(code) {
126346
+ if (code === codes.greaterThan || code === codes.space || code === codes.horizontalTab) {
126347
+ depth += 1;
126348
+ effects.consume(code);
126349
+ return body;
126350
+ }
126351
+ return body(code);
126352
+ }
126353
+ function closeGt(code) {
126354
+ if (code === codes.greaterThan) {
126355
+ depth -= 1;
126356
+ effects.consume(code);
126357
+ if (depth === 0) {
126358
+ return mode === 'flow' ? afterClose : done(code);
126359
+ }
126360
+ return body;
126361
+ }
126362
+ return body(code);
125660
126363
  }
125661
- });
125662
- return tree;
125663
- };
125664
- /* harmony default export */ const variables_code = (variablesCodeResolver);
125665
-
125666
- ;// ./processor/transform/mdxish/variables-text.ts
125667
-
125668
-
125669
- /**
125670
- * Matches {user.<field>} patterns:
125671
- * - {user.name}
125672
- * - {user.email}
125673
- * - {user['field']}
125674
- * - {user["field"]}
125675
- *
125676
- * Captures the field name in group 1 (dot notation) or group 2 (bracket notation)
125677
- */
125678
- const USER_VAR_REGEX = /\{user\.(\w+)\}|\{user\[['"](\w+)['"]\]\}/g;
125679
- function makeVariableNode(varName, rawValue) {
125680
- return {
125681
- type: NodeTypes.variable,
125682
- data: {
125683
- hName: 'Variable',
125684
- hProperties: { name: varName },
125685
- },
125686
- value: rawValue,
125687
- };
125688
- }
125689
- /**
125690
- * A remark plugin that parses {user.<field>} patterns from text nodes and
125691
- * mdx expression nodes, creating Variable nodes for runtime resolution.
125692
- *
125693
- * Handles:
125694
- * - `text` nodes: when safeMode is true or after expression evaluation
125695
- * - `mdxTextExpression` nodes: when mdxExpression has parsed {user.*} before evaluation
125696
- * - `mdxFlowExpression` nodes: when {user.*} appears on its own line (e.g. inside JSX table cells)
125697
- *
125698
- * Supports any user field: name, email, email_verified, exp, iat, etc.
125699
- */
125700
- function visitExpressionNode(node, index, parent) {
125701
- if (index === undefined || !parent)
125702
- return;
125703
- const wrapped = `{${(node.value ?? '').trim()}}`;
125704
- const matches = [...wrapped.matchAll(USER_VAR_REGEX)];
125705
- if (matches.length !== 1)
125706
- return;
125707
- const varName = matches[0][1] || matches[0][2];
125708
- parent.children.splice(index, 1, makeVariableNode(varName, wrapped));
125709
- }
125710
- const variablesTextTransformer = () => tree => {
125711
- visit(tree, 'mdxTextExpression', (node, index, parent) => {
125712
- visitExpressionNode(node, index, parent);
125713
- });
125714
- visit(tree, 'mdxFlowExpression', (node, index, parent) => {
125715
- visitExpressionNode(node, index, parent);
125716
- });
125717
- visit(tree, 'text', (node, index, parent) => {
125718
- if (index === undefined || !parent)
125719
- return;
125720
- // Skip if inside inline code
125721
- if (parent.type === 'inlineCode')
125722
- return;
125723
- const text = node.value;
125724
- if (typeof text !== 'string' || !text.trim())
125725
- return;
125726
- if (!text.includes('{user.') && !text.includes('{user['))
125727
- return;
125728
- const matches = [...text.matchAll(USER_VAR_REGEX)];
125729
- if (matches.length === 0)
125730
- return;
125731
- const parts = [];
125732
- let lastIndex = 0;
125733
- matches.forEach(match => {
125734
- const matchIndex = match.index ?? 0;
125735
- // Add text before the match
125736
- if (matchIndex > lastIndex) {
125737
- parts.push({ type: 'text', value: text.slice(lastIndex, matchIndex) });
126364
+ // -- flow-only states ---------------------------------------------------
126365
+ function afterClose(code) {
126366
+ if (code === null || markdownLineEnding(code)) {
126367
+ return done(code);
125738
126368
  }
125739
- // Extract variable name from either capture group (dot or bracket notation)
125740
- const varName = match[1] || match[2];
125741
- parts.push(makeVariableNode(varName, match[0]));
125742
- lastIndex = matchIndex + match[0].length;
125743
- });
125744
- // Add remaining text after last match
125745
- if (lastIndex < text.length) {
125746
- parts.push({ type: 'text', value: text.slice(lastIndex) });
126369
+ if (code === codes.space || code === codes.horizontalTab) {
126370
+ effects.consume(code);
126371
+ return afterClose;
126372
+ }
126373
+ // Reject so the block re-parses as a paragraph, deferring to the
126374
+ // text tokenizer which preserves trailing content in the same line.
126375
+ return nok(code);
125747
126376
  }
125748
- // Replace node with parts
125749
- if (parts.length > 1 || (parts.length === 1 && parts[0].type !== 'text')) {
125750
- parent.children.splice(index, 1, ...parts);
126377
+ // -- flow-only: line continuation ---------------------------------------
126378
+ function continuationStart(code) {
126379
+ return effects.check(html_block_component_syntax_nonLazyContinuationStart, continuationStartNonLazy, continuationAfter)(code);
125751
126380
  }
125752
- });
125753
- return tree;
126381
+ function continuationStartNonLazy(code) {
126382
+ effects.enter(types_types.lineEnding);
126383
+ effects.consume(code);
126384
+ effects.exit(types_types.lineEnding);
126385
+ return continuationBefore;
126386
+ }
126387
+ function continuationBefore(code) {
126388
+ if (code === null || markdownLineEnding(code)) {
126389
+ return continuationStart(code);
126390
+ }
126391
+ effects.enter('htmlBlockComponentData');
126392
+ return body(code);
126393
+ }
126394
+ function continuationAfter(code) {
126395
+ if (code === null)
126396
+ return nok(code);
126397
+ effects.exit('htmlBlockComponent');
126398
+ return ok(code);
126399
+ }
126400
+ // -- shared exit --------------------------------------------------------
126401
+ function done(_code) {
126402
+ effects.exit('htmlBlockComponentData');
126403
+ effects.exit('htmlBlockComponent');
126404
+ return ok(_code);
126405
+ }
126406
+ };
126407
+ }
126408
+ // ---------------------------------------------------------------------------
126409
+ // Flow construct (block-level)
126410
+ // ---------------------------------------------------------------------------
126411
+ const html_block_component_syntax_nonLazyContinuationStart = {
126412
+ tokenize: html_block_component_syntax_tokenizeNonLazyContinuationStart,
126413
+ partial: true,
125754
126414
  };
125755
- /* harmony default export */ const variables_text = (variablesTextTransformer);
125756
-
125757
- ;// ./lib/mdast-util/jsx-table/index.ts
125758
- const jsx_table_contextMap = new WeakMap();
125759
- function findJsxTableToken() {
125760
- const events = this.tokenStack;
125761
- for (let i = events.length - 1; i >= 0; i -= 1) {
125762
- if (events[i][0].type === 'jsxTable')
125763
- return events[i][0];
126415
+ function resolveToHtmlBlockComponent(events) {
126416
+ let index = events.length;
126417
+ while (index > 0) {
126418
+ index -= 1;
126419
+ if (events[index][0] === 'enter' && events[index][1].type === 'htmlBlockComponent') {
126420
+ break;
126421
+ }
125764
126422
  }
125765
- return undefined;
125766
- }
125767
- function enterJsxTable(token) {
125768
- jsx_table_contextMap.set(token, { chunks: [], lastEndLine: token.start.line });
125769
- this.enter({ type: 'html', value: '' }, token);
126423
+ if (index > 1 && events[index - 2][1].type === types_types.linePrefix) {
126424
+ events[index][1].start = events[index - 2][1].start;
126425
+ events[index + 1][1].start = events[index - 2][1].start;
126426
+ events.splice(index - 2, 2);
126427
+ }
126428
+ return events;
125770
126429
  }
125771
- function exitJsxTableData(token) {
125772
- const tableToken = findJsxTableToken.call(this);
125773
- if (!tableToken)
125774
- return;
125775
- const ctx = jsx_table_contextMap.get(tableToken);
125776
- if (ctx) {
125777
- const gap = token.start.line - ctx.lastEndLine;
125778
- if (ctx.chunks.length > 0 && gap > 0) {
125779
- ctx.chunks.push('\n'.repeat(gap));
126430
+ const htmlBlockComponentFlowConstruct = {
126431
+ name: 'htmlBlockComponent',
126432
+ tokenize: syntax_createTokenize('flow'),
126433
+ resolveTo: resolveToHtmlBlockComponent,
126434
+ concrete: true,
126435
+ };
126436
+ function html_block_component_syntax_tokenizeNonLazyContinuationStart(effects, ok, nok) {
126437
+ // eslint-disable-next-line @typescript-eslint/no-this-alias
126438
+ const self = this;
126439
+ return start;
126440
+ function start(code) {
126441
+ if (markdownLineEnding(code)) {
126442
+ effects.enter(types_types.lineEnding);
126443
+ effects.consume(code);
126444
+ effects.exit(types_types.lineEnding);
126445
+ return after;
125780
126446
  }
125781
- ctx.chunks.push(this.sliceSerialize(token));
125782
- ctx.lastEndLine = token.end.line;
126447
+ return nok(code);
125783
126448
  }
125784
- }
125785
- function exitJsxTable(token) {
125786
- const ctx = jsx_table_contextMap.get(token);
125787
- const node = this.stack[this.stack.length - 1];
125788
- if (ctx) {
125789
- node.value = ctx.chunks.join('');
125790
- jsx_table_contextMap.delete(token);
126449
+ function after(code) {
126450
+ if (self.parser.lazy[self.now().line]) {
126451
+ return nok(code);
126452
+ }
126453
+ return ok(code);
125791
126454
  }
125792
- this.exit(token);
125793
126455
  }
125794
- function jsxTableFromMarkdown() {
126456
+ // ---------------------------------------------------------------------------
126457
+ // Text construct (inline)
126458
+ // ---------------------------------------------------------------------------
126459
+ const htmlBlockComponentTextConstruct = {
126460
+ name: 'htmlBlockComponent',
126461
+ tokenize: syntax_createTokenize('text'),
126462
+ };
126463
+ // ---------------------------------------------------------------------------
126464
+ // Extension
126465
+ // ---------------------------------------------------------------------------
126466
+ function htmlBlockComponent() {
125795
126467
  return {
125796
- enter: {
125797
- jsxTable: enterJsxTable,
126468
+ flow: {
126469
+ [codes.lessThan]: [htmlBlockComponentFlowConstruct],
125798
126470
  },
125799
- exit: {
125800
- jsxTableData: exitJsxTableData,
125801
- jsxTable: exitJsxTable,
126471
+ text: {
126472
+ [codes.lessThan]: [htmlBlockComponentTextConstruct],
125802
126473
  },
125803
126474
  };
125804
126475
  }
125805
126476
 
126477
+ ;// ./lib/micromark/html-block-component/index.ts
126478
+
126479
+
125806
126480
  ;// ./lib/micromark/jsx-comment/syntax.ts
125807
126481
 
125808
126482
 
@@ -125925,243 +126599,6 @@ function jsxComment() {
125925
126599
  };
125926
126600
  }
125927
126601
 
125928
- ;// ./lib/micromark/jsx-table/syntax.ts
125929
-
125930
-
125931
- const jsx_table_syntax_nonLazyContinuationStart = {
125932
- tokenize: jsx_table_syntax_tokenizeNonLazyContinuationStart,
125933
- partial: true,
125934
- };
125935
- function resolveToJsxTable(events) {
125936
- let index = events.length;
125937
- while (index > 0) {
125938
- index -= 1;
125939
- if (events[index][0] === 'enter' && events[index][1].type === 'jsxTable') {
125940
- break;
125941
- }
125942
- }
125943
- if (index > 1 && events[index - 2][1].type === types_types.linePrefix) {
125944
- events[index][1].start = events[index - 2][1].start;
125945
- events[index + 1][1].start = events[index - 2][1].start;
125946
- events.splice(index - 2, 2);
125947
- }
125948
- return events;
125949
- }
125950
- const jsxTableConstruct = {
125951
- name: 'jsxTable',
125952
- tokenize: tokenizeJsxTable,
125953
- resolveTo: resolveToJsxTable,
125954
- concrete: true,
125955
- };
125956
- function tokenizeJsxTable(effects, ok, nok) {
125957
- let codeSpanOpenSize = 0;
125958
- let codeSpanCloseSize = 0;
125959
- let depth = 1;
125960
- const ABLE_SUFFIX = [codes.lowercaseA, codes.lowercaseB, codes.lowercaseL, codes.lowercaseE];
125961
- /** Build a state chain that matches a sequence of character codes. */
125962
- function matchChars(chars, onMatch, onFail) {
125963
- if (chars.length === 0)
125964
- return onMatch;
125965
- return ((code) => {
125966
- if (code === chars[0]) {
125967
- effects.consume(code);
125968
- return matchChars(chars.slice(1), onMatch, onFail);
125969
- }
125970
- return onFail(code);
125971
- });
125972
- }
125973
- return start;
125974
- function start(code) {
125975
- if (code !== codes.lessThan)
125976
- return nok(code);
125977
- effects.enter('jsxTable');
125978
- effects.enter('jsxTableData');
125979
- effects.consume(code);
125980
- return afterLessThan;
125981
- }
125982
- function afterLessThan(code) {
125983
- if (code === codes.uppercaseT || code === codes.lowercaseT) {
125984
- effects.consume(code);
125985
- return matchChars(ABLE_SUFFIX, afterTagName, nok);
125986
- }
125987
- return nok(code);
125988
- }
125989
- function afterTagName(code) {
125990
- if (code === codes.greaterThan || code === codes.slash || code === codes.space || code === codes.horizontalTab) {
125991
- effects.consume(code);
125992
- return body;
125993
- }
125994
- return nok(code);
125995
- }
125996
- function body(code) {
125997
- // Reject unclosed <Table> so it falls back to normal HTML block parsing
125998
- // instead of swallowing all subsequent content to EOF
125999
- if (code === null) {
126000
- return nok(code);
126001
- }
126002
- if (markdownLineEnding(code)) {
126003
- effects.exit('jsxTableData');
126004
- return continuationStart(code);
126005
- }
126006
- if (code === codes.backslash) {
126007
- effects.consume(code);
126008
- return escapedChar;
126009
- }
126010
- if (code === codes.lessThan) {
126011
- effects.consume(code);
126012
- return closeSlash;
126013
- }
126014
- // Skip over backtick code spans so `</Table>` in inline code isn't
126015
- // treated as the closing tag
126016
- if (code === codes.graveAccent) {
126017
- codeSpanOpenSize = 0;
126018
- return countOpenTicks(code);
126019
- }
126020
- effects.consume(code);
126021
- return body;
126022
- }
126023
- function countOpenTicks(code) {
126024
- if (code === codes.graveAccent) {
126025
- codeSpanOpenSize += 1;
126026
- effects.consume(code);
126027
- return countOpenTicks;
126028
- }
126029
- return inCodeSpan(code);
126030
- }
126031
- function inCodeSpan(code) {
126032
- if (code === null || markdownLineEnding(code))
126033
- return body(code);
126034
- if (code === codes.graveAccent) {
126035
- codeSpanCloseSize = 0;
126036
- return countCloseTicks(code);
126037
- }
126038
- effects.consume(code);
126039
- return inCodeSpan;
126040
- }
126041
- function countCloseTicks(code) {
126042
- if (code === codes.graveAccent) {
126043
- codeSpanCloseSize += 1;
126044
- effects.consume(code);
126045
- return countCloseTicks;
126046
- }
126047
- return codeSpanCloseSize === codeSpanOpenSize ? body(code) : inCodeSpan(code);
126048
- }
126049
- function escapedChar(code) {
126050
- if (code === null || markdownLineEnding(code)) {
126051
- return body(code);
126052
- }
126053
- effects.consume(code);
126054
- return body;
126055
- }
126056
- function closeSlash(code) {
126057
- if (code === codes.slash) {
126058
- effects.consume(code);
126059
- return closeTagFirstChar;
126060
- }
126061
- if (code === codes.uppercaseT || code === codes.lowercaseT) {
126062
- effects.consume(code);
126063
- return matchChars(ABLE_SUFFIX, openAfterTagName, body);
126064
- }
126065
- return body(code);
126066
- }
126067
- function closeTagFirstChar(code) {
126068
- if (code === codes.uppercaseT || code === codes.lowercaseT) {
126069
- effects.consume(code);
126070
- return matchChars(ABLE_SUFFIX, closeGt, body);
126071
- }
126072
- return body(code);
126073
- }
126074
- function openAfterTagName(code) {
126075
- if (code === codes.greaterThan || code === codes.slash || code === codes.space || code === codes.horizontalTab) {
126076
- depth += 1;
126077
- effects.consume(code);
126078
- return body;
126079
- }
126080
- return body(code);
126081
- }
126082
- function closeGt(code) {
126083
- if (code === codes.greaterThan) {
126084
- depth -= 1;
126085
- effects.consume(code);
126086
- if (depth === 0) {
126087
- return afterClose;
126088
- }
126089
- return body;
126090
- }
126091
- return body(code);
126092
- }
126093
- function afterClose(code) {
126094
- if (code === null || markdownLineEnding(code)) {
126095
- effects.exit('jsxTableData');
126096
- effects.exit('jsxTable');
126097
- return ok(code);
126098
- }
126099
- effects.consume(code);
126100
- return afterClose;
126101
- }
126102
- // Line ending handling — follows the htmlFlow pattern
126103
- function continuationStart(code) {
126104
- return effects.check(jsx_table_syntax_nonLazyContinuationStart, continuationStartNonLazy, continuationAfter)(code);
126105
- }
126106
- function continuationStartNonLazy(code) {
126107
- effects.enter(types_types.lineEnding);
126108
- effects.consume(code);
126109
- effects.exit(types_types.lineEnding);
126110
- return continuationBefore;
126111
- }
126112
- function continuationBefore(code) {
126113
- if (code === null || markdownLineEnding(code)) {
126114
- return continuationStart(code);
126115
- }
126116
- effects.enter('jsxTableData');
126117
- return body(code);
126118
- }
126119
- function continuationAfter(code) {
126120
- // At EOF without </Table>, reject so content isn't swallowed
126121
- if (code === null) {
126122
- return nok(code);
126123
- }
126124
- effects.exit('jsxTable');
126125
- return ok(code);
126126
- }
126127
- }
126128
- function jsx_table_syntax_tokenizeNonLazyContinuationStart(effects, ok, nok) {
126129
- // eslint-disable-next-line @typescript-eslint/no-this-alias
126130
- const self = this;
126131
- return start;
126132
- function start(code) {
126133
- if (markdownLineEnding(code)) {
126134
- effects.enter(types_types.lineEnding);
126135
- effects.consume(code);
126136
- effects.exit(types_types.lineEnding);
126137
- return after;
126138
- }
126139
- return nok(code);
126140
- }
126141
- function after(code) {
126142
- if (self.parser.lazy[self.now().line]) {
126143
- return nok(code);
126144
- }
126145
- return ok(code);
126146
- }
126147
- }
126148
- /**
126149
- * Micromark extension that tokenizes `<Table>...</Table>` and `<table>...</table>`
126150
- * as a single flow block.
126151
- *
126152
- * Prevents CommonMark HTML block type 6 from fragmenting table blocks at blank lines.
126153
- */
126154
- function jsxTable() {
126155
- return {
126156
- flow: {
126157
- [codes.lessThan]: [jsxTableConstruct],
126158
- },
126159
- };
126160
- }
126161
-
126162
- ;// ./lib/micromark/jsx-table/index.ts
126163
-
126164
-
126165
126602
  ;// ./lib/micromark/mdx-expression-lenient/syntax.ts
126166
126603
 
126167
126604
 
@@ -126337,6 +126774,9 @@ function loadComponents() {
126337
126774
 
126338
126775
 
126339
126776
 
126777
+
126778
+
126779
+
126340
126780
 
126341
126781
 
126342
126782
 
@@ -126351,15 +126791,19 @@ const defaultTransformers = [
126351
126791
  * CommonMark/remark limitations and reach parity with legacy (rdmd) rendering.
126352
126792
  *
126353
126793
  * Runs a series of string-level transformations before micromark/remark parsing:
126354
- * 1. Normalize malformed table separator syntax (e.g., `|: ---``| :---`)
126355
- * 2. Terminate HTML flow blocks so subsequent content isn't swallowed
126356
- * 3. Close invalid "self-closing" HTML tags (e.g., `<i />` → `<i></i>`)
126357
- * 4. Normalize compact ATX headings (e.g., `#Heading``# Heading`)
126358
- * 5. Replace snake_case component names with parser-safe placeholders
126794
+ * 1. Canonicalize closing tags with stray whitespace (e.g., `</ td >` `</td>`)
126795
+ * 2. Normalize malformed table separator syntax (e.g., `|: ---` → `| :---`)
126796
+ * 3. Terminate HTML flow blocks so subsequent content isn't swallowed
126797
+ * 4. Close invalid "self-closing" HTML tags (e.g., `<i />` `<i></i>`)
126798
+ * 5. Normalize compact ATX headings (e.g., `#Heading` → `# Heading`)
126799
+ * 6. Replace snake_case component names with parser-safe placeholders
126359
126800
  */
126360
126801
  function preprocessContent(content, opts) {
126361
126802
  const { knownComponents } = opts;
126362
- let result = normalizeTableSeparator(content);
126803
+ // Runs first so `jsxTable` sees a literal `</table>` (and the HTML-line
126804
+ // classification in `terminateHtmlFlowBlocks` is accurate)
126805
+ let result = normalizeClosingTagWhitespace(content);
126806
+ result = normalizeTableSeparator(result);
126363
126807
  result = terminateHtmlFlowBlocks(result);
126364
126808
  result = closeSelfClosingHtmlTags(result);
126365
126809
  result = normalizeCompactHeadings(result);
@@ -126388,6 +126832,7 @@ function mdxishAstProcessor(mdContent, opts = {}) {
126388
126832
  syntax_gemoji(),
126389
126833
  legacyVariable(),
126390
126834
  looseHtmlEntity(),
126835
+ htmlBlockComponent(),
126391
126836
  ];
126392
126837
  const fromMarkdownExts = [
126393
126838
  jsxTableFromMarkdown(),
@@ -126397,6 +126842,7 @@ function mdxishAstProcessor(mdContent, opts = {}) {
126397
126842
  legacyVariableFromMarkdown(),
126398
126843
  emptyTaskListItemFromMarkdown(),
126399
126844
  looseHtmlEntityFromMarkdown(),
126845
+ htmlBlockComponentFromMarkdown(),
126400
126846
  ];
126401
126847
  if (!safeMode) {
126402
126848
  // Insert mdx expression (text-only, no flow) after gemoji at index 3
@@ -126410,6 +126856,10 @@ function mdxishAstProcessor(mdContent, opts = {}) {
126410
126856
  // JSX comment tokenizer must come before magicBlock so it claims `{/* ... */}` first
126411
126857
  micromarkExts.unshift(jsxComment());
126412
126858
  }
126859
+ // Claim `<HTMLBlock>` as one opaque token so broad tokenizers can't fragment its body
126860
+ // We put this last as micromark tries the last-registered extension first, so push (not unshift) to win the `<` race.
126861
+ // micromarkExts.push(htmlBlockComponent());
126862
+ // fromMarkdownExts.push(htmlBlockComponentFromMarkdown());
126413
126863
  const processor = unified()
126414
126864
  .data('micromarkExtensions', micromarkExts)
126415
126865
  .data('fromMarkdownExtensions', fromMarkdownExts)
@@ -126493,7 +126943,7 @@ function mdxish(mdContent, opts = {}) {
126493
126943
  const { processor, parserReadyContent } = mdxishAstProcessor(contentWithoutComments, opts);
126494
126944
  processor
126495
126945
  .use(safeMode ? undefined : evaluate_exports) // Evaluate `export const/function` and stash scope on file.data.mdxishScope
126496
- .use(remarkBreaks) // Must precede evaluateExpressions to avoid splitting the \n in an evaluated template literal into a <br> node
126946
+ .use(hard_breaks) // Must precede evaluateExpressions to avoid splitting the \n in an evaluated template literal into a <br> node
126497
126947
  .use(safeMode ? undefined : evaluate_expressions) // Evaluate self-contained MDX expressions (e.g. `{1+1}`)
126498
126948
  .use(safeMode ? undefined : evaluate_style_block_expressions) // Evaluate `<style>{`...`}</style>` template literals into plain CSS
126499
126949
  .use(variables_code, { variables }) // Resolve <<...>> and {user.*} inside code and inline code nodes
@@ -127009,332 +127459,6 @@ const mdxishTags_tags = (doc) => {
127009
127459
  };
127010
127460
  /* harmony default export */ const mdxishTags = (mdxishTags_tags);
127011
127461
 
127012
- ;// ./lib/mdast-util/html-block-component/index.ts
127013
- const html_block_component_contextMap = new WeakMap();
127014
- function findHtmlBlockComponentToken() {
127015
- const events = this.tokenStack;
127016
- for (let i = events.length - 1; i >= 0; i -= 1) {
127017
- if (events[i][0].type === 'htmlBlockComponent')
127018
- return events[i][0];
127019
- }
127020
- return undefined;
127021
- }
127022
- function enterHtmlBlockComponent(token) {
127023
- html_block_component_contextMap.set(token, { chunks: [], lastEndLine: token.start.line });
127024
- this.enter({ type: 'html', value: '' }, token);
127025
- }
127026
- function exitHtmlBlockComponentData(token) {
127027
- const componentToken = findHtmlBlockComponentToken.call(this);
127028
- if (!componentToken)
127029
- return;
127030
- const ctx = html_block_component_contextMap.get(componentToken);
127031
- if (ctx) {
127032
- const gap = token.start.line - ctx.lastEndLine;
127033
- if (ctx.chunks.length > 0 && gap > 0) {
127034
- ctx.chunks.push('\n'.repeat(gap));
127035
- }
127036
- ctx.chunks.push(this.sliceSerialize(token));
127037
- ctx.lastEndLine = token.end.line;
127038
- }
127039
- }
127040
- function exitHtmlBlockComponent(token) {
127041
- const ctx = html_block_component_contextMap.get(token);
127042
- const node = this.stack[this.stack.length - 1];
127043
- if (ctx) {
127044
- node.value = ctx.chunks.join('');
127045
- html_block_component_contextMap.delete(token);
127046
- }
127047
- this.exit(token);
127048
- }
127049
- function htmlBlockComponentFromMarkdown() {
127050
- return {
127051
- enter: {
127052
- htmlBlockComponent: enterHtmlBlockComponent,
127053
- },
127054
- exit: {
127055
- htmlBlockComponentData: exitHtmlBlockComponentData,
127056
- htmlBlockComponent: exitHtmlBlockComponent,
127057
- },
127058
- };
127059
- }
127060
-
127061
- ;// ./lib/micromark/html-block-component/syntax.ts
127062
-
127063
-
127064
- const TAG_SUFFIX = [
127065
- codes.uppercaseT,
127066
- codes.uppercaseM,
127067
- codes.uppercaseL,
127068
- codes.uppercaseB,
127069
- codes.lowercaseL,
127070
- codes.lowercaseO,
127071
- codes.lowercaseC,
127072
- codes.lowercaseK,
127073
- ];
127074
- // ---------------------------------------------------------------------------
127075
- // Shared tokenizer factory
127076
- // ---------------------------------------------------------------------------
127077
- /**
127078
- * Creates a tokenize function for `<HTMLBlock>...</HTMLBlock>`.
127079
- *
127080
- * - **flow** (block-level): supports multiline content via line continuations,
127081
- * consumes trailing whitespace after the closing tag.
127082
- * - **text** (inline): single-line only, exits immediately after the closing tag.
127083
- */
127084
- function syntax_createTokenize(mode) {
127085
- return function tokenize(effects, ok, nok) {
127086
- let depth = 1;
127087
- function matchChars(chars, onMatch, onFail) {
127088
- if (chars.length === 0)
127089
- return onMatch;
127090
- const next = (code) => {
127091
- if (code === chars[0]) {
127092
- effects.consume(code);
127093
- return matchChars(chars.slice(1), onMatch, onFail);
127094
- }
127095
- return onFail(code);
127096
- };
127097
- return next;
127098
- }
127099
- function matchTagName(onMatch, onFail) {
127100
- const next = (code) => {
127101
- if (code === codes.uppercaseH) {
127102
- effects.consume(code);
127103
- return matchChars(TAG_SUFFIX, onMatch, onFail);
127104
- }
127105
- return onFail(code);
127106
- };
127107
- return next;
127108
- }
127109
- return start;
127110
- function start(code) {
127111
- if (code !== codes.lessThan)
127112
- return nok(code);
127113
- effects.enter('htmlBlockComponent');
127114
- effects.enter('htmlBlockComponentData');
127115
- effects.consume(code);
127116
- return matchTagName(afterTagName, nok);
127117
- }
127118
- function afterTagName(code) {
127119
- if (code === codes.greaterThan) {
127120
- effects.consume(code);
127121
- return body;
127122
- }
127123
- if (code === codes.space || code === codes.horizontalTab) {
127124
- effects.consume(code);
127125
- return inAttributes;
127126
- }
127127
- if (mode === 'flow' && markdownLineEnding(code)) {
127128
- effects.exit('htmlBlockComponentData');
127129
- return attributeContinuationStart(code);
127130
- }
127131
- if (code === codes.slash) {
127132
- effects.consume(code);
127133
- return selfClose;
127134
- }
127135
- return nok(code);
127136
- }
127137
- function inAttributes(code) {
127138
- if (code === codes.greaterThan) {
127139
- effects.consume(code);
127140
- return body;
127141
- }
127142
- if (code === null) {
127143
- return nok(code);
127144
- }
127145
- if (markdownLineEnding(code)) {
127146
- if (mode === 'text')
127147
- return nok(code);
127148
- effects.exit('htmlBlockComponentData');
127149
- return attributeContinuationStart(code);
127150
- }
127151
- effects.consume(code);
127152
- return inAttributes;
127153
- }
127154
- function attributeContinuationStart(code) {
127155
- return effects.check(html_block_component_syntax_nonLazyContinuationStart, attributeContinuationNonLazy, continuationAfter)(code);
127156
- }
127157
- function attributeContinuationNonLazy(code) {
127158
- effects.enter(types_types.lineEnding);
127159
- effects.consume(code);
127160
- effects.exit(types_types.lineEnding);
127161
- return attributeContinuationBefore;
127162
- }
127163
- function attributeContinuationBefore(code) {
127164
- if (code === null || markdownLineEnding(code)) {
127165
- return attributeContinuationStart(code);
127166
- }
127167
- effects.enter('htmlBlockComponentData');
127168
- return inAttributes(code);
127169
- }
127170
- function selfClose(code) {
127171
- if (code === codes.greaterThan) {
127172
- effects.consume(code);
127173
- return mode === 'flow' ? afterClose : done(code);
127174
- }
127175
- return nok(code);
127176
- }
127177
- function body(code) {
127178
- if (code === null)
127179
- return nok(code);
127180
- if (markdownLineEnding(code)) {
127181
- if (mode === 'text') {
127182
- // Text constructs operate on paragraph content which spans lines
127183
- effects.consume(code);
127184
- return body;
127185
- }
127186
- effects.exit('htmlBlockComponentData');
127187
- return continuationStart(code);
127188
- }
127189
- if (code === codes.lessThan) {
127190
- effects.consume(code);
127191
- return closeSlash;
127192
- }
127193
- effects.consume(code);
127194
- return body;
127195
- }
127196
- function closeSlash(code) {
127197
- if (code === codes.slash) {
127198
- effects.consume(code);
127199
- return matchTagName(closeGt, body);
127200
- }
127201
- return matchTagName(openAfterTagName, body)(code);
127202
- }
127203
- function openAfterTagName(code) {
127204
- if (code === codes.greaterThan || code === codes.space || code === codes.horizontalTab) {
127205
- depth += 1;
127206
- effects.consume(code);
127207
- return body;
127208
- }
127209
- return body(code);
127210
- }
127211
- function closeGt(code) {
127212
- if (code === codes.greaterThan) {
127213
- depth -= 1;
127214
- effects.consume(code);
127215
- if (depth === 0) {
127216
- return mode === 'flow' ? afterClose : done(code);
127217
- }
127218
- return body;
127219
- }
127220
- return body(code);
127221
- }
127222
- // -- flow-only states ---------------------------------------------------
127223
- function afterClose(code) {
127224
- if (code === null || markdownLineEnding(code)) {
127225
- return done(code);
127226
- }
127227
- if (code === codes.space || code === codes.horizontalTab) {
127228
- effects.consume(code);
127229
- return afterClose;
127230
- }
127231
- // Reject so the block re-parses as a paragraph, deferring to the
127232
- // text tokenizer which preserves trailing content in the same line.
127233
- return nok(code);
127234
- }
127235
- // -- flow-only: line continuation ---------------------------------------
127236
- function continuationStart(code) {
127237
- return effects.check(html_block_component_syntax_nonLazyContinuationStart, continuationStartNonLazy, continuationAfter)(code);
127238
- }
127239
- function continuationStartNonLazy(code) {
127240
- effects.enter(types_types.lineEnding);
127241
- effects.consume(code);
127242
- effects.exit(types_types.lineEnding);
127243
- return continuationBefore;
127244
- }
127245
- function continuationBefore(code) {
127246
- if (code === null || markdownLineEnding(code)) {
127247
- return continuationStart(code);
127248
- }
127249
- effects.enter('htmlBlockComponentData');
127250
- return body(code);
127251
- }
127252
- function continuationAfter(code) {
127253
- if (code === null)
127254
- return nok(code);
127255
- effects.exit('htmlBlockComponent');
127256
- return ok(code);
127257
- }
127258
- // -- shared exit --------------------------------------------------------
127259
- function done(_code) {
127260
- effects.exit('htmlBlockComponentData');
127261
- effects.exit('htmlBlockComponent');
127262
- return ok(_code);
127263
- }
127264
- };
127265
- }
127266
- // ---------------------------------------------------------------------------
127267
- // Flow construct (block-level)
127268
- // ---------------------------------------------------------------------------
127269
- const html_block_component_syntax_nonLazyContinuationStart = {
127270
- tokenize: html_block_component_syntax_tokenizeNonLazyContinuationStart,
127271
- partial: true,
127272
- };
127273
- function resolveToHtmlBlockComponent(events) {
127274
- let index = events.length;
127275
- while (index > 0) {
127276
- index -= 1;
127277
- if (events[index][0] === 'enter' && events[index][1].type === 'htmlBlockComponent') {
127278
- break;
127279
- }
127280
- }
127281
- if (index > 1 && events[index - 2][1].type === types_types.linePrefix) {
127282
- events[index][1].start = events[index - 2][1].start;
127283
- events[index + 1][1].start = events[index - 2][1].start;
127284
- events.splice(index - 2, 2);
127285
- }
127286
- return events;
127287
- }
127288
- const htmlBlockComponentFlowConstruct = {
127289
- name: 'htmlBlockComponent',
127290
- tokenize: syntax_createTokenize('flow'),
127291
- resolveTo: resolveToHtmlBlockComponent,
127292
- concrete: true,
127293
- };
127294
- function html_block_component_syntax_tokenizeNonLazyContinuationStart(effects, ok, nok) {
127295
- // eslint-disable-next-line @typescript-eslint/no-this-alias
127296
- const self = this;
127297
- return start;
127298
- function start(code) {
127299
- if (markdownLineEnding(code)) {
127300
- effects.enter(types_types.lineEnding);
127301
- effects.consume(code);
127302
- effects.exit(types_types.lineEnding);
127303
- return after;
127304
- }
127305
- return nok(code);
127306
- }
127307
- function after(code) {
127308
- if (self.parser.lazy[self.now().line]) {
127309
- return nok(code);
127310
- }
127311
- return ok(code);
127312
- }
127313
- }
127314
- // ---------------------------------------------------------------------------
127315
- // Text construct (inline)
127316
- // ---------------------------------------------------------------------------
127317
- const htmlBlockComponentTextConstruct = {
127318
- name: 'htmlBlockComponent',
127319
- tokenize: syntax_createTokenize('text'),
127320
- };
127321
- // ---------------------------------------------------------------------------
127322
- // Extension
127323
- // ---------------------------------------------------------------------------
127324
- function htmlBlockComponent() {
127325
- return {
127326
- flow: {
127327
- [codes.lessThan]: [htmlBlockComponentFlowConstruct],
127328
- },
127329
- text: {
127330
- [codes.lessThan]: [htmlBlockComponentTextConstruct],
127331
- },
127332
- };
127333
- }
127334
-
127335
- ;// ./lib/micromark/html-block-component/index.ts
127336
-
127337
-
127338
127462
  ;// ./lib/utils/extractMagicBlocks.ts
127339
127463
  /**
127340
127464
  * The content matching in this regex captures everything between `[block:TYPE]`