@readme/markdown 14.11.4 → 14.12.0

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);
@@ -117546,6 +117565,19 @@ const compile_list_item_listItem = (node, parent, state, info) => {
117546
117565
  const plain_plain = (node) => node.value;
117547
117566
  /* harmony default export */ const compile_plain = (plain_plain);
117548
117567
 
117568
+ ;// ./processor/compile/text.ts
117569
+
117570
+ // A `_` flanked by word characters can never open or close emphasis under
117571
+ // CommonMark's flanking rules, so the escape mdast-util-to-markdown adds to
117572
+ // intraword underscores is unnecessary and only produces noisy `\_` diffs.
117573
+ // Uses Unicode letter/number classes so non-ASCII words (e.g. `é_pay`) match.
117574
+ const INTRAWORD_UNDERSCORE_ESCAPE = /(?<=[\p{L}\p{N}_])\\_(?=[\p{L}\p{N}_]|\\_)/gu;
117575
+ const compile_text_text = (node, parent, state, info) => {
117576
+ const serialized = handle.text(node, parent, state, info);
117577
+ return serialized.replace(INTRAWORD_UNDERSCORE_ESCAPE, '_');
117578
+ };
117579
+ /* harmony default export */ const compile_text = (compile_text_text);
117580
+
117549
117581
  ;// ./processor/compile/index.ts
117550
117582
 
117551
117583
 
@@ -117558,11 +117590,11 @@ const plain_plain = (node) => node.value;
117558
117590
 
117559
117591
 
117560
117592
 
117593
+
117561
117594
  function compilers(mdxish = false) {
117562
117595
  const data = this.data();
117563
117596
  const toMarkdownExtensions = data.toMarkdownExtensions || (data.toMarkdownExtensions = []);
117564
117597
  const handlers = {
117565
- ...(mdxish && { [NodeTypes.anchor]: compile_anchor }),
117566
117598
  [NodeTypes.callout]: compile_callout,
117567
117599
  [NodeTypes.codeTabs]: compile_code_tabs,
117568
117600
  [NodeTypes.embedBlock]: compile_embed,
@@ -117570,15 +117602,18 @@ function compilers(mdxish = false) {
117570
117602
  [NodeTypes.glossary]: compile_compatibility,
117571
117603
  [NodeTypes.htmlBlock]: html_block,
117572
117604
  [NodeTypes.reusableContent]: compile_compatibility,
117573
- ...(mdxish && { [NodeTypes.variable]: compile_variable }),
117574
117605
  embed: compile_compatibility,
117575
117606
  escape: compile_compatibility,
117576
117607
  figure: compile_compatibility,
117577
117608
  html: compile_compatibility,
117578
117609
  i: compile_compatibility,
117579
- ...(mdxish && { listItem: list_item }),
117580
117610
  plain: compile_plain,
117581
117611
  yaml: compile_compatibility,
117612
+ // needed only for mdxish
117613
+ ...(mdxish && { [NodeTypes.anchor]: compile_anchor }),
117614
+ ...(mdxish && { listItem: list_item }),
117615
+ ...(mdxish && { text: compile_text }),
117616
+ ...(mdxish && { [NodeTypes.variable]: compile_variable }),
117582
117617
  };
117583
117618
  toMarkdownExtensions.push({ extensions: [{ handlers }] });
117584
117619
  }
@@ -119857,6 +119892,55 @@ function emptyTaskListItemFromMarkdown() {
119857
119892
  };
119858
119893
  }
119859
119894
 
119895
+ ;// ./lib/mdast-util/jsx-table/index.ts
119896
+ const jsx_table_contextMap = new WeakMap();
119897
+ function findJsxTableToken() {
119898
+ const events = this.tokenStack;
119899
+ for (let i = events.length - 1; i >= 0; i -= 1) {
119900
+ if (events[i][0].type === 'jsxTable')
119901
+ return events[i][0];
119902
+ }
119903
+ return undefined;
119904
+ }
119905
+ function enterJsxTable(token) {
119906
+ jsx_table_contextMap.set(token, { chunks: [], lastEndLine: token.start.line });
119907
+ this.enter({ type: 'html', value: '' }, token);
119908
+ }
119909
+ function exitJsxTableData(token) {
119910
+ const tableToken = findJsxTableToken.call(this);
119911
+ if (!tableToken)
119912
+ return;
119913
+ const ctx = jsx_table_contextMap.get(tableToken);
119914
+ if (ctx) {
119915
+ const gap = token.start.line - ctx.lastEndLine;
119916
+ if (ctx.chunks.length > 0 && gap > 0) {
119917
+ ctx.chunks.push('\n'.repeat(gap));
119918
+ }
119919
+ ctx.chunks.push(this.sliceSerialize(token));
119920
+ ctx.lastEndLine = token.end.line;
119921
+ }
119922
+ }
119923
+ function exitJsxTable(token) {
119924
+ const ctx = jsx_table_contextMap.get(token);
119925
+ const node = this.stack[this.stack.length - 1];
119926
+ if (ctx) {
119927
+ node.value = ctx.chunks.join('');
119928
+ jsx_table_contextMap.delete(token);
119929
+ }
119930
+ this.exit(token);
119931
+ }
119932
+ function jsxTableFromMarkdown() {
119933
+ return {
119934
+ enter: {
119935
+ jsxTable: enterJsxTable,
119936
+ },
119937
+ exit: {
119938
+ jsxTableData: exitJsxTableData,
119939
+ jsxTable: exitJsxTable,
119940
+ },
119941
+ };
119942
+ }
119943
+
119860
119944
  ;// ./lib/mdast-util/magic-block/index.ts
119861
119945
  const magic_block_contextMap = new WeakMap();
119862
119946
  /**
@@ -120046,867 +120130,16 @@ function mdxComponentFromMarkdown() {
120046
120130
  };
120047
120131
  }
120048
120132
 
120049
- ;// ./lib/micromark/magic-block/syntax.ts
120050
-
120051
-
120052
- /**
120053
- * Known magic block types that the tokenizer will recognize.
120054
- * Unknown types will not be tokenized as magic blocks.
120055
- */
120056
- const KNOWN_BLOCK_TYPES = new Set([
120057
- 'code',
120058
- 'api-header',
120059
- 'image',
120060
- 'callout',
120061
- 'parameters',
120062
- 'table',
120063
- 'embed',
120064
- 'html',
120065
- 'recipe',
120066
- 'tutorial-tile',
120067
- ]);
120068
- /**
120069
- * Check if a character is valid for a magic block type identifier.
120070
- * Types can contain alphanumeric characters and hyphens (e.g., "api-header", "tutorial-tile")
120071
- */
120072
- function isTypeChar(code) {
120073
- return asciiAlphanumeric(code) || code === codes.dash;
120074
- }
120075
- /**
120076
- * Creates the opening marker state machine: [block:
120077
- * Returns the first state function to start parsing.
120078
- */
120079
- function createOpeningMarkerParser(effects, nok, onComplete) {
120080
- const expectB = (code) => {
120081
- if (code !== codes.lowercaseB)
120082
- return nok(code);
120083
- effects.consume(code);
120084
- return expectL;
120085
- };
120086
- const expectL = (code) => {
120087
- if (code !== codes.lowercaseL)
120088
- return nok(code);
120089
- effects.consume(code);
120090
- return expectO;
120091
- };
120092
- const expectO = (code) => {
120093
- if (code !== codes.lowercaseO)
120094
- return nok(code);
120095
- effects.consume(code);
120096
- return expectC;
120097
- };
120098
- const expectC = (code) => {
120099
- if (code !== codes.lowercaseC)
120100
- return nok(code);
120101
- effects.consume(code);
120102
- return expectK;
120103
- };
120104
- const expectK = (code) => {
120105
- if (code !== codes.lowercaseK)
120106
- return nok(code);
120107
- effects.consume(code);
120108
- return expectColon;
120109
- };
120110
- const expectColon = (code) => {
120111
- if (code !== codes.colon)
120112
- return nok(code);
120113
- effects.consume(code);
120114
- effects.exit('magicBlockMarkerStart');
120115
- effects.enter('magicBlockType');
120116
- return onComplete;
120117
- };
120118
- return expectB;
120119
- }
120120
- /**
120121
- * Creates the type capture state machine.
120122
- * Captures type characters until ] and validates against known types.
120123
- */
120124
- function createTypeCaptureParser(effects, nok, onComplete, blockTypeRef) {
120125
- const captureTypeFirst = (code) => {
120126
- // Reject empty type name [block:]
120127
- if (code === codes.rightSquareBracket) {
120128
- return nok(code);
120129
- }
120130
- if (isTypeChar(code)) {
120131
- blockTypeRef.value += String.fromCharCode(code);
120132
- effects.consume(code);
120133
- return captureType;
120134
- }
120135
- return nok(code);
120136
- };
120137
- const captureType = (code) => {
120138
- if (code === codes.rightSquareBracket) {
120139
- if (!KNOWN_BLOCK_TYPES.has(blockTypeRef.value)) {
120140
- return nok(code);
120141
- }
120142
- effects.exit('magicBlockType');
120143
- effects.enter('magicBlockMarkerTypeEnd');
120144
- effects.consume(code);
120145
- effects.exit('magicBlockMarkerTypeEnd');
120146
- return onComplete;
120147
- }
120148
- if (isTypeChar(code)) {
120149
- blockTypeRef.value += String.fromCharCode(code);
120150
- effects.consume(code);
120151
- return captureType;
120152
- }
120153
- return nok(code);
120154
- };
120155
- return { first: captureTypeFirst, remaining: captureType };
120156
- }
120157
- /**
120158
- * Creates the closing marker state machine: /block]
120159
- * Handles partial matches by calling onMismatch to fall back to data capture.
120160
- */
120161
- function createClosingMarkerParser(effects, onSuccess, onMismatch, onEof, jsonState) {
120162
- const handleMismatch = (code) => {
120163
- if (jsonState && code === codes.quotationMark) {
120164
- jsonState.inString = true;
120165
- }
120166
- return onMismatch(code);
120167
- };
120168
- const expectSlash = (code) => {
120169
- if (code === null)
120170
- return onEof(code);
120171
- if (code !== codes.slash)
120172
- return handleMismatch(code);
120173
- effects.consume(code);
120174
- return expectB;
120175
- };
120176
- const expectB = (code) => {
120177
- if (code === null)
120178
- return onEof(code);
120179
- if (code !== codes.lowercaseB)
120180
- return handleMismatch(code);
120181
- effects.consume(code);
120182
- return expectL;
120183
- };
120184
- const expectL = (code) => {
120185
- if (code === null)
120186
- return onEof(code);
120187
- if (code !== codes.lowercaseL)
120188
- return handleMismatch(code);
120189
- effects.consume(code);
120190
- return expectO;
120191
- };
120192
- const expectO = (code) => {
120193
- if (code === null)
120194
- return onEof(code);
120195
- if (code !== codes.lowercaseO)
120196
- return handleMismatch(code);
120197
- effects.consume(code);
120198
- return expectC;
120199
- };
120200
- const expectC = (code) => {
120201
- if (code === null)
120202
- return onEof(code);
120203
- if (code !== codes.lowercaseC)
120204
- return handleMismatch(code);
120205
- effects.consume(code);
120206
- return expectK;
120207
- };
120208
- const expectK = (code) => {
120209
- if (code === null)
120210
- return onEof(code);
120211
- if (code !== codes.lowercaseK)
120212
- return handleMismatch(code);
120213
- effects.consume(code);
120214
- return expectBracket;
120215
- };
120216
- const expectBracket = (code) => {
120217
- if (code === null)
120218
- return onEof(code);
120219
- if (code !== codes.rightSquareBracket)
120220
- return handleMismatch(code);
120221
- effects.consume(code);
120222
- return onSuccess;
120223
- };
120224
- return { expectSlash };
120225
- }
120226
- /**
120227
- * Partial construct for checking non-lazy continuation.
120228
- * This is used by the flow tokenizer to check if we can continue
120229
- * parsing on the next line.
120230
- */
120231
- const syntax_nonLazyContinuation = {
120232
- partial: true,
120233
- tokenize: syntax_tokenizeNonLazyContinuation,
120234
- };
120235
- /**
120236
- * Tokenizer for non-lazy continuation checking.
120237
- * Returns ok if the next line is non-lazy (can continue), nok if lazy.
120238
- */
120239
- function syntax_tokenizeNonLazyContinuation(effects, ok, nok) {
120240
- const lineStart = (code) => {
120241
- // `this` here refers to the micromark parser
120242
- // since we are just passing functions as references and not actually calling them
120243
- // micromarks's internal parser will call this automatically with appropriate arguments
120244
- return this.parser.lazy[this.now().line] ? nok(code) : ok(code);
120245
- };
120246
- const start = (code) => {
120247
- if (code === null) {
120248
- return nok(code);
120249
- }
120250
- if (!markdownLineEnding(code)) {
120251
- return nok(code);
120252
- }
120253
- effects.enter('magicBlockLineEnding');
120254
- effects.consume(code);
120255
- effects.exit('magicBlockLineEnding');
120256
- return lineStart;
120257
- };
120258
- return start;
120259
- }
120133
+ ;// ./node_modules/micromark-util-symbol/lib/types.js
120260
120134
  /**
120261
- * Create a micromark extension for magic block syntax.
120262
- *
120263
- * This extension handles both single-line and multiline magic blocks:
120264
- * - Flow construct (concrete): Handles block-level multiline magic blocks at document level
120265
- * - Text construct: Handles inline magic blocks in lists, paragraphs, etc.
120135
+ * This module is compiled away!
120266
120136
  *
120267
- * The flow construct is marked as "concrete" which prevents it from being
120268
- * interrupted by container markers (like `>` for blockquotes or `-` for lists).
120269
- */
120270
- function magicBlock() {
120271
- return {
120272
- // Flow construct - handles block-level magic blocks at document root
120273
- // Marked as concrete to prevent interruption by containers
120274
- flow: {
120275
- [codes.leftSquareBracket]: {
120276
- name: 'magicBlock',
120277
- concrete: true,
120278
- tokenize: tokenizeMagicBlockFlow,
120279
- },
120280
- },
120281
- // Text construct - handles magic blocks in inline contexts (lists, paragraphs)
120282
- text: {
120283
- [codes.leftSquareBracket]: {
120284
- name: 'magicBlock',
120285
- tokenize: tokenizeMagicBlockText,
120286
- },
120287
- },
120288
- };
120289
- }
120290
- /**
120291
- * Flow tokenizer for block-level magic blocks (multiline).
120292
- * Uses the continuation checking pattern from code fences.
120293
- */
120294
- function tokenizeMagicBlockFlow(effects, ok, nok) {
120295
- // State for tracking JSON content
120296
- const jsonState = { escapeNext: false, inString: false };
120297
- const blockTypeRef = { value: '' };
120298
- let seenOpenBrace = false;
120299
- // Create shared parsers for opening marker and type capture
120300
- const typeParser = createTypeCaptureParser(effects, nok, beforeData, blockTypeRef);
120301
- const openingMarkerParser = createOpeningMarkerParser(effects, nok, typeParser.first);
120302
- return start;
120303
- function start(code) {
120304
- if (code !== codes.leftSquareBracket)
120305
- return nok(code);
120306
- effects.enter('magicBlock');
120307
- effects.enter('magicBlockMarkerStart');
120308
- effects.consume(code);
120309
- return openingMarkerParser;
120310
- }
120311
- /**
120312
- * State before data content - handles line endings or starts data capture.
120313
- */
120314
- function beforeData(code) {
120315
- // EOF - magic block must be closed
120316
- if (code === null) {
120317
- effects.exit('magicBlock');
120318
- return nok(code);
120319
- }
120320
- // Line ending before any data - check continuation
120321
- if (markdownLineEnding(code)) {
120322
- return effects.check(syntax_nonLazyContinuation, continuationOkBeforeData, after)(code);
120323
- }
120324
- // Check for closing marker directly (without entering data)
120325
- // This handles cases like [block:type]\n{}\n\n[/block] where there are
120326
- // newlines after the data object
120327
- if (code === codes.leftSquareBracket) {
120328
- effects.enter('magicBlockMarkerEnd');
120329
- effects.consume(code);
120330
- return expectSlashFromContinuation;
120331
- }
120332
- // If we've already seen the opening brace, just continue capturing data
120333
- if (seenOpenBrace) {
120334
- effects.enter('magicBlockData');
120335
- return captureData(code);
120336
- }
120337
- // Skip whitespace (spaces/tabs) before the data - stay in beforeData
120338
- // Don't enter magicBlockData token yet until we confirm there's a '{'
120339
- if (code === codes.space || code === codes.horizontalTab) {
120340
- return beforeDataWhitespace(code);
120341
- }
120342
- // Data must start with '{' for valid JSON
120343
- if (code !== codes.leftCurlyBrace) {
120344
- effects.exit('magicBlock');
120345
- return nok(code);
120346
- }
120347
- // We have '{' - enter data token and start capturing
120348
- seenOpenBrace = true;
120349
- effects.enter('magicBlockData');
120350
- return captureData(code);
120351
- }
120352
- /**
120353
- * Consume whitespace before the data without creating a token.
120354
- * Uses a temporary token to satisfy micromark's requirement.
120355
- */
120356
- function beforeDataWhitespace(code) {
120357
- if (code === null) {
120358
- effects.exit('magicBlock');
120359
- return nok(code);
120360
- }
120361
- if (markdownLineEnding(code)) {
120362
- return effects.check(syntax_nonLazyContinuation, continuationOkBeforeData, after)(code);
120363
- }
120364
- if (code === codes.space || code === codes.horizontalTab) {
120365
- // We need to consume this but can't without a token - use magicBlockData
120366
- // and track that we haven't seen '{' yet
120367
- effects.enter('magicBlockData');
120368
- effects.consume(code);
120369
- return beforeDataWhitespaceContinue;
120370
- }
120371
- if (code === codes.leftCurlyBrace) {
120372
- seenOpenBrace = true;
120373
- effects.enter('magicBlockData');
120374
- return captureData(code);
120375
- }
120376
- effects.exit('magicBlock');
120377
- return nok(code);
120378
- }
120379
- /**
120380
- * Continue consuming whitespace or validate the opening brace.
120381
- */
120382
- function beforeDataWhitespaceContinue(code) {
120383
- if (code === null) {
120384
- effects.exit('magicBlockData');
120385
- effects.exit('magicBlock');
120386
- return nok(code);
120387
- }
120388
- if (markdownLineEnding(code)) {
120389
- effects.exit('magicBlockData');
120390
- return effects.check(syntax_nonLazyContinuation, continuationOk, after)(code);
120391
- }
120392
- if (code === codes.space || code === codes.horizontalTab) {
120393
- effects.consume(code);
120394
- return beforeDataWhitespaceContinue;
120395
- }
120396
- if (code === codes.leftCurlyBrace) {
120397
- seenOpenBrace = true;
120398
- return captureData(code);
120399
- }
120400
- effects.exit('magicBlockData');
120401
- effects.exit('magicBlock');
120402
- return nok(code);
120403
- }
120404
- /**
120405
- * Continuation OK before we've entered data token.
120406
- */
120407
- function continuationOkBeforeData(code) {
120408
- effects.enter('magicBlockLineEnding');
120409
- effects.consume(code);
120410
- effects.exit('magicBlockLineEnding');
120411
- return beforeData; // Stay in beforeData state - don't enter magicBlockData yet
120412
- }
120413
- function captureData(code) {
120414
- // EOF - magic block must be closed
120415
- if (code === null) {
120416
- effects.exit('magicBlockData');
120417
- effects.exit('magicBlock');
120418
- return nok(code);
120419
- }
120420
- // At line ending, check if we can continue on the next line
120421
- if (markdownLineEnding(code)) {
120422
- effects.exit('magicBlockData');
120423
- return effects.check(syntax_nonLazyContinuation, continuationOk, after)(code);
120424
- }
120425
- if (jsonState.escapeNext) {
120426
- jsonState.escapeNext = false;
120427
- effects.consume(code);
120428
- return captureData;
120429
- }
120430
- if (jsonState.inString) {
120431
- if (code === codes.backslash) {
120432
- jsonState.escapeNext = true;
120433
- effects.consume(code);
120434
- return captureData;
120435
- }
120436
- if (code === codes.quotationMark) {
120437
- jsonState.inString = false;
120438
- }
120439
- effects.consume(code);
120440
- return captureData;
120441
- }
120442
- if (code === codes.quotationMark) {
120443
- jsonState.inString = true;
120444
- effects.consume(code);
120445
- return captureData;
120446
- }
120447
- if (code === codes.leftSquareBracket) {
120448
- effects.exit('magicBlockData');
120449
- effects.enter('magicBlockMarkerEnd');
120450
- effects.consume(code);
120451
- return expectSlash;
120452
- }
120453
- effects.consume(code);
120454
- return captureData;
120455
- }
120456
- /**
120457
- * Called when non-lazy continuation check passes - we can continue parsing.
120458
- */
120459
- function continuationOk(code) {
120460
- // Consume the line ending
120461
- effects.enter('magicBlockLineEnding');
120462
- effects.consume(code);
120463
- effects.exit('magicBlockLineEnding');
120464
- return continuationStart;
120465
- }
120466
- /**
120467
- * Start of continuation line - check for more line endings, closing marker, or start capturing data.
120468
- */
120469
- function continuationStart(code) {
120470
- // Handle consecutive line endings
120471
- if (code === null) {
120472
- effects.exit('magicBlock');
120473
- return nok(code);
120474
- }
120475
- if (markdownLineEnding(code)) {
120476
- return effects.check(syntax_nonLazyContinuation, continuationOkBeforeData, after)(code);
120477
- }
120478
- // Check if this is the start of the closing marker [/block]
120479
- // If so, handle it directly without entering magicBlockData
120480
- if (code === codes.leftSquareBracket) {
120481
- effects.enter('magicBlockMarkerEnd');
120482
- effects.consume(code);
120483
- return expectSlashFromContinuation;
120484
- }
120485
- effects.enter('magicBlockData');
120486
- return captureData(code);
120487
- }
120488
- /**
120489
- * Check for closing marker slash when coming from continuation.
120490
- * If not a closing marker, create an empty data token and continue.
120491
- */
120492
- function expectSlashFromContinuation(code) {
120493
- if (code === null) {
120494
- effects.exit('magicBlockMarkerEnd');
120495
- effects.exit('magicBlock');
120496
- return nok(code);
120497
- }
120498
- if (markdownLineEnding(code)) {
120499
- effects.exit('magicBlockMarkerEnd');
120500
- return effects.check(syntax_nonLazyContinuation, continuationOkBeforeData, after)(code);
120501
- }
120502
- if (code === codes.slash) {
120503
- effects.consume(code);
120504
- return expectClosingB;
120505
- }
120506
- // Not a closing marker - this is data content
120507
- // Exit marker and enter data
120508
- effects.exit('magicBlockMarkerEnd');
120509
- effects.enter('magicBlockData');
120510
- // The [ was consumed by the marker, so we need to conceptually "have it" in our data
120511
- // But since we already consumed it into the marker, we need a different approach
120512
- // Re-classify: consume this character as data and continue
120513
- if (code === codes.quotationMark)
120514
- jsonState.inString = true;
120515
- effects.consume(code);
120516
- return captureData;
120517
- }
120518
- function expectSlash(code) {
120519
- if (code === null) {
120520
- effects.exit('magicBlockMarkerEnd');
120521
- effects.exit('magicBlock');
120522
- return nok(code);
120523
- }
120524
- // At line ending during marker parsing
120525
- if (markdownLineEnding(code)) {
120526
- effects.exit('magicBlockMarkerEnd');
120527
- return effects.check(syntax_nonLazyContinuation, continuationOk, after)(code);
120528
- }
120529
- if (code !== codes.slash) {
120530
- effects.exit('magicBlockMarkerEnd');
120531
- effects.enter('magicBlockData');
120532
- if (code === codes.quotationMark)
120533
- jsonState.inString = true;
120534
- effects.consume(code);
120535
- return captureData;
120536
- }
120537
- effects.consume(code);
120538
- return expectClosingB;
120539
- }
120540
- function expectClosingB(code) {
120541
- if (code === null) {
120542
- effects.exit('magicBlockMarkerEnd');
120543
- effects.exit('magicBlock');
120544
- return nok(code);
120545
- }
120546
- if (code !== codes.lowercaseB) {
120547
- effects.exit('magicBlockMarkerEnd');
120548
- effects.enter('magicBlockData');
120549
- if (code === codes.quotationMark)
120550
- jsonState.inString = true;
120551
- effects.consume(code);
120552
- return captureData;
120553
- }
120554
- effects.consume(code);
120555
- return expectClosingL;
120556
- }
120557
- function expectClosingL(code) {
120558
- if (code === null) {
120559
- effects.exit('magicBlockMarkerEnd');
120560
- effects.exit('magicBlock');
120561
- return nok(code);
120562
- }
120563
- if (code !== codes.lowercaseL) {
120564
- effects.exit('magicBlockMarkerEnd');
120565
- effects.enter('magicBlockData');
120566
- if (code === codes.quotationMark)
120567
- jsonState.inString = true;
120568
- effects.consume(code);
120569
- return captureData;
120570
- }
120571
- effects.consume(code);
120572
- return expectClosingO;
120573
- }
120574
- function expectClosingO(code) {
120575
- if (code === null) {
120576
- effects.exit('magicBlockMarkerEnd');
120577
- effects.exit('magicBlock');
120578
- return nok(code);
120579
- }
120580
- if (code !== codes.lowercaseO) {
120581
- effects.exit('magicBlockMarkerEnd');
120582
- effects.enter('magicBlockData');
120583
- if (code === codes.quotationMark)
120584
- jsonState.inString = true;
120585
- effects.consume(code);
120586
- return captureData;
120587
- }
120588
- effects.consume(code);
120589
- return expectClosingC;
120590
- }
120591
- function expectClosingC(code) {
120592
- if (code === null) {
120593
- effects.exit('magicBlockMarkerEnd');
120594
- effects.exit('magicBlock');
120595
- return nok(code);
120596
- }
120597
- if (code !== codes.lowercaseC) {
120598
- effects.exit('magicBlockMarkerEnd');
120599
- effects.enter('magicBlockData');
120600
- if (code === codes.quotationMark)
120601
- jsonState.inString = true;
120602
- effects.consume(code);
120603
- return captureData;
120604
- }
120605
- effects.consume(code);
120606
- return expectClosingK;
120607
- }
120608
- function expectClosingK(code) {
120609
- if (code === null) {
120610
- effects.exit('magicBlockMarkerEnd');
120611
- effects.exit('magicBlock');
120612
- return nok(code);
120613
- }
120614
- if (code !== codes.lowercaseK) {
120615
- effects.exit('magicBlockMarkerEnd');
120616
- effects.enter('magicBlockData');
120617
- if (code === codes.quotationMark)
120618
- jsonState.inString = true;
120619
- effects.consume(code);
120620
- return captureData;
120621
- }
120622
- effects.consume(code);
120623
- return expectClosingBracket;
120624
- }
120625
- function expectClosingBracket(code) {
120626
- if (code === null) {
120627
- effects.exit('magicBlockMarkerEnd');
120628
- effects.exit('magicBlock');
120629
- return nok(code);
120630
- }
120631
- if (code !== codes.rightSquareBracket) {
120632
- effects.exit('magicBlockMarkerEnd');
120633
- effects.enter('magicBlockData');
120634
- if (code === codes.quotationMark)
120635
- jsonState.inString = true;
120636
- effects.consume(code);
120637
- return captureData;
120638
- }
120639
- effects.consume(code);
120640
- effects.exit('magicBlockMarkerEnd');
120641
- // Check for trailing whitespace on the same line
120642
- return consumeTrailing;
120643
- }
120644
- /**
120645
- * Consume trailing whitespace (spaces/tabs) on the same line after [/block].
120646
- * For concrete flow constructs, we must end at eol/eof or fail.
120647
- * Trailing whitespace is consumed; other content causes nok (text tokenizer handles it).
120648
- */
120649
- function consumeTrailing(code) {
120650
- // End of file - done
120651
- if (code === null) {
120652
- effects.exit('magicBlock');
120653
- return ok(code);
120654
- }
120655
- // Line ending - done
120656
- if (markdownLineEnding(code)) {
120657
- effects.exit('magicBlock');
120658
- return ok(code);
120659
- }
120660
- // Space or tab - consume as trailing whitespace
120661
- if (code === codes.space || code === codes.horizontalTab) {
120662
- effects.enter('magicBlockTrailing');
120663
- effects.consume(code);
120664
- return consumeTrailingContinue;
120665
- }
120666
- // Any other character - fail flow tokenizer, let text tokenizer handle it
120667
- effects.exit('magicBlock');
120668
- return nok(code);
120669
- }
120670
- /**
120671
- * Continue consuming trailing whitespace.
120672
- */
120673
- function consumeTrailingContinue(code) {
120674
- // End of file - done
120675
- if (code === null) {
120676
- effects.exit('magicBlockTrailing');
120677
- effects.exit('magicBlock');
120678
- return ok(code);
120679
- }
120680
- // Line ending - done
120681
- if (markdownLineEnding(code)) {
120682
- effects.exit('magicBlockTrailing');
120683
- effects.exit('magicBlock');
120684
- return ok(code);
120685
- }
120686
- // More space or tab - keep consuming
120687
- if (code === codes.space || code === codes.horizontalTab) {
120688
- effects.consume(code);
120689
- return consumeTrailingContinue;
120690
- }
120691
- // Non-whitespace after whitespace - fail flow tokenizer
120692
- effects.exit('magicBlockTrailing');
120693
- effects.exit('magicBlock');
120694
- return nok(code);
120695
- }
120696
- /**
120697
- * Called when we can't continue (lazy line or EOF) - exit the construct.
120698
- */
120699
- function after(code) {
120700
- effects.exit('magicBlock');
120701
- return nok(code);
120702
- }
120703
- }
120704
- /**
120705
- * Text tokenizer for single-line magic blocks only.
120706
- * Used in inline contexts like list items and paragraphs.
120707
- * Multiline blocks are handled by the flow tokenizer.
120708
- */
120709
- function tokenizeMagicBlockText(effects, ok, nok) {
120710
- // State for tracking JSON content
120711
- const jsonState = { escapeNext: false, inString: false };
120712
- const blockTypeRef = { value: '' };
120713
- let seenOpenBrace = false;
120714
- // Create shared parsers for opening marker and type capture
120715
- const typeParser = createTypeCaptureParser(effects, nok, beforeData, blockTypeRef);
120716
- const openingMarkerParser = createOpeningMarkerParser(effects, nok, typeParser.first);
120717
- // Success handler for closing marker - exits tokens and returns ok
120718
- const closingSuccess = (code) => {
120719
- effects.exit('magicBlockMarkerEnd');
120720
- effects.exit('magicBlock');
120721
- return ok(code);
120722
- };
120723
- // Mismatch handler - falls back to data capture
120724
- const closingMismatch = (code) => {
120725
- effects.exit('magicBlockMarkerEnd');
120726
- effects.enter('magicBlockData');
120727
- if (code === codes.quotationMark)
120728
- jsonState.inString = true;
120729
- effects.consume(code);
120730
- return captureData;
120731
- };
120732
- // EOF handler
120733
- const closingEof = (_code) => nok(_code);
120734
- // Create closing marker parsers
120735
- const closingFromBeforeData = createClosingMarkerParser(effects, closingSuccess, closingMismatch, closingEof, jsonState);
120736
- const closingFromData = createClosingMarkerParser(effects, closingSuccess, closingMismatch, closingEof, jsonState);
120737
- return start;
120738
- function start(code) {
120739
- if (code !== codes.leftSquareBracket)
120740
- return nok(code);
120741
- effects.enter('magicBlock');
120742
- effects.enter('magicBlockMarkerStart');
120743
- effects.consume(code);
120744
- return openingMarkerParser;
120745
- }
120746
- /**
120747
- * State before data content - handles line endings before entering data token.
120748
- * Whitespace before '{' is allowed.
120749
- */
120750
- function beforeData(code) {
120751
- // Fail on EOF - magic block must be closed
120752
- if (code === null) {
120753
- return nok(code);
120754
- }
120755
- // Handle line endings before any data - consume them without entering data token
120756
- if (markdownLineEnding(code)) {
120757
- effects.enter('magicBlockLineEnding');
120758
- effects.consume(code);
120759
- effects.exit('magicBlockLineEnding');
120760
- return beforeData;
120761
- }
120762
- // Check for closing marker directly (without entering data)
120763
- if (code === codes.leftSquareBracket) {
120764
- effects.enter('magicBlockMarkerEnd');
120765
- effects.consume(code);
120766
- return closingFromBeforeData.expectSlash;
120767
- }
120768
- // If we've already seen the opening brace, just continue capturing data
120769
- if (seenOpenBrace) {
120770
- effects.enter('magicBlockData');
120771
- return captureData(code);
120772
- }
120773
- // Skip whitespace (spaces/tabs) before the data
120774
- if (code === codes.space || code === codes.horizontalTab) {
120775
- return beforeDataWhitespace(code);
120776
- }
120777
- // Data must start with '{' for valid JSON
120778
- if (code !== codes.leftCurlyBrace) {
120779
- return nok(code);
120780
- }
120781
- // We have '{' - enter data token and start capturing
120782
- seenOpenBrace = true;
120783
- effects.enter('magicBlockData');
120784
- return captureData(code);
120785
- }
120786
- /**
120787
- * Consume whitespace before the data.
120788
- */
120789
- function beforeDataWhitespace(code) {
120790
- if (code === null) {
120791
- return nok(code);
120792
- }
120793
- if (markdownLineEnding(code)) {
120794
- effects.enter('magicBlockLineEnding');
120795
- effects.consume(code);
120796
- effects.exit('magicBlockLineEnding');
120797
- return beforeData;
120798
- }
120799
- if (code === codes.space || code === codes.horizontalTab) {
120800
- effects.enter('magicBlockData');
120801
- effects.consume(code);
120802
- return beforeDataWhitespaceContinue;
120803
- }
120804
- if (code === codes.leftCurlyBrace) {
120805
- seenOpenBrace = true;
120806
- effects.enter('magicBlockData');
120807
- return captureData(code);
120808
- }
120809
- return nok(code);
120810
- }
120811
- /**
120812
- * Continue consuming whitespace or validate the opening brace.
120813
- */
120814
- function beforeDataWhitespaceContinue(code) {
120815
- if (code === null) {
120816
- effects.exit('magicBlockData');
120817
- return nok(code);
120818
- }
120819
- if (markdownLineEnding(code)) {
120820
- effects.exit('magicBlockData');
120821
- effects.enter('magicBlockLineEnding');
120822
- effects.consume(code);
120823
- effects.exit('magicBlockLineEnding');
120824
- return beforeData;
120825
- }
120826
- if (code === codes.space || code === codes.horizontalTab) {
120827
- effects.consume(code);
120828
- return beforeDataWhitespaceContinue;
120829
- }
120830
- if (code === codes.leftCurlyBrace) {
120831
- seenOpenBrace = true;
120832
- return captureData(code);
120833
- }
120834
- effects.exit('magicBlockData');
120835
- return nok(code);
120836
- }
120837
- function captureData(code) {
120838
- // Fail on EOF - magic block must be closed
120839
- if (code === null) {
120840
- effects.exit('magicBlockData');
120841
- return nok(code);
120842
- }
120843
- // Handle multiline magic blocks within text/paragraphs
120844
- // Exit data, consume line ending with proper token, then re-enter data
120845
- if (markdownLineEnding(code)) {
120846
- effects.exit('magicBlockData');
120847
- effects.enter('magicBlockLineEnding');
120848
- effects.consume(code);
120849
- effects.exit('magicBlockLineEnding');
120850
- return beforeData; // Go back to beforeData to handle potential empty lines
120851
- }
120852
- if (jsonState.escapeNext) {
120853
- jsonState.escapeNext = false;
120854
- effects.consume(code);
120855
- return captureData;
120856
- }
120857
- if (jsonState.inString) {
120858
- if (code === codes.backslash) {
120859
- jsonState.escapeNext = true;
120860
- effects.consume(code);
120861
- return captureData;
120862
- }
120863
- if (code === codes.quotationMark) {
120864
- jsonState.inString = false;
120865
- }
120866
- effects.consume(code);
120867
- return captureData;
120868
- }
120869
- if (code === codes.quotationMark) {
120870
- jsonState.inString = true;
120871
- effects.consume(code);
120872
- return captureData;
120873
- }
120874
- if (code === codes.leftSquareBracket) {
120875
- effects.exit('magicBlockData');
120876
- effects.enter('magicBlockMarkerEnd');
120877
- effects.consume(code);
120878
- return closingFromData.expectSlash;
120879
- }
120880
- effects.consume(code);
120881
- return captureData;
120882
- }
120883
- }
120884
- /* harmony default export */ const syntax = ((/* unused pure expression or super */ null && (magicBlock)));
120885
-
120886
- ;// ./lib/micromark/magic-block/index.ts
120887
- /**
120888
- * Micromark extension for magic block syntax.
120889
- *
120890
- * Usage:
120891
- * ```ts
120892
- * import { magicBlock } from './lib/micromark/magic-block';
120893
- *
120894
- * const processor = unified()
120895
- * .data('micromarkExtensions', [magicBlock()])
120896
- * ```
120897
- */
120898
-
120899
-
120900
- ;// ./node_modules/micromark-util-symbol/lib/types.js
120901
- /**
120902
- * This module is compiled away!
120903
- *
120904
- * Here is the list of all types of tokens exposed by micromark, with a short
120905
- * explanation of what they include and where they are found.
120906
- * In picking names, generally, the rule is to be as explicit as possible
120907
- * instead of reusing names.
120908
- * For example, there is a `definitionDestination` and a `resourceDestination`,
120909
- * instead of one shared name.
120137
+ * Here is the list of all types of tokens exposed by micromark, with a short
120138
+ * explanation of what they include and where they are found.
120139
+ * In picking names, generally, the rule is to be as explicit as possible
120140
+ * instead of reusing names.
120141
+ * For example, there is a `definitionDestination` and a `resourceDestination`,
120142
+ * instead of one shared name.
120910
120143
  */
120911
120144
 
120912
120145
  // Note: when changing the next record, you must also change `TokenTypeMap`
@@ -121352,36 +120585,18 @@ const types_types = /** @type {const} */ ({
121352
120585
  chunkString: 'chunkString'
121353
120586
  })
121354
120587
 
121355
- ;// ./lib/micromark/mdx-component/syntax.ts
121356
-
121357
-
121358
-
120588
+ ;// ./lib/micromark/jsx-table/syntax.ts
121359
120589
 
121360
120590
 
121361
- // Raw tags (type-1: pre/script/style/textarea) and block tags (type-6: div,
121362
- // section, …) always start a block, so they stay flow even with trailing
121363
- // content. Other lowercase tags (i, span, …) follow the type-7 rule and only
121364
- // stay flow when nothing trails the close tag.
121365
- const htmlFlowTagNames = new Set([...htmlRawNames, ...htmlBlockNames]);
121366
- // Lowercase type-6 block tags claimable in flow even without a `{…}` attribute, so
121367
- // blank lines between nested JSX siblings don't fragment the block. Excludes table
121368
- // tags (mdxishTables owns their blank lines) and voids (never close).
121369
- const plainBlockClaimTagNames = new Set([...htmlBlockNames].filter(tag => !HTML_TABLE_STRUCTURE_TAGS.has(tag) && !HTML_VOID_ELEMENTS.has(tag)));
121370
120591
  const syntax_nonLazyContinuationStart = {
121371
120592
  tokenize: syntax_tokenizeNonLazyContinuationStart,
121372
120593
  partial: true,
121373
120594
  };
121374
- // Lookahead for `plainClaimLineStart`: is this line markup-only, or a paragraph
121375
- // that merely starts with a tag? Run via `effects.check` so it never consumes.
121376
- const markupOnlyContinuation = {
121377
- tokenize: tokenizeMarkupOnlyContinuation,
121378
- partial: true,
121379
- };
121380
- function resolveToMdxComponent(events) {
120595
+ function resolveToJsxTable(events) {
121381
120596
  let index = events.length;
121382
120597
  while (index > 0) {
121383
120598
  index -= 1;
121384
- if (events[index][0] === 'enter' && events[index][1].type === 'mdxComponent') {
120599
+ if (events[index][0] === 'enter' && events[index][1].type === 'jsxTable') {
121385
120600
  break;
121386
120601
  }
121387
120602
  }
@@ -121392,61 +120607,1167 @@ function resolveToMdxComponent(events) {
121392
120607
  }
121393
120608
  return events;
121394
120609
  }
121395
- const mdxComponentFlowConstruct = {
121396
- name: 'mdxComponent',
121397
- tokenize: createTokenize('flow'),
121398
- resolveTo: resolveToMdxComponent,
120610
+ const jsxTableConstruct = {
120611
+ name: 'jsxTable',
120612
+ tokenize: tokenizeJsxTable,
120613
+ resolveTo: resolveToJsxTable,
121399
120614
  concrete: true,
121400
120615
  };
121401
- const mdxComponentTextConstruct = {
121402
- name: 'mdxComponentText',
121403
- tokenize: createTokenize('text'),
121404
- };
121405
- /**
121406
- * Factory for both flow (block) and text (inline) variants.
121407
- *
121408
- * **Flow** — the original behavior: claims PascalCase components (always) and
121409
- * lowercase HTML tags that carry at least one `{…}` attribute expression.
121410
- * Multi-line, concrete, `afterClose` consumes the rest of the line.
121411
- *
121412
- * **Text** — runs inside paragraphs / inline context. Claims lowercase tags and
121413
- * inline PascalCase components (`INLINE_COMPONENT_TAGS` — Anchor, Glossary), both
121414
- * gated on at least one `{…}` brace attribute. All other PascalCase stays
121415
- * flow-only, matching how ReadMe's custom components are authored. Aborts on line
121416
- * endings (inline constructs don't span lines) and exits immediately after
121417
- * `</tag>` so the paragraph's inline parser picks up the trailing text.
121418
- */
121419
- function createTokenize(mode) {
121420
- const isFlow = mode === 'flow';
121421
- return function tokenize(effects, ok, nok) {
121422
- // eslint-disable-next-line @typescript-eslint/no-this-alias
121423
- const self = this;
121424
- let tagName = '';
121425
- let depth = 0;
121426
- let closingTagName = '';
121427
- // For lowercase tags we only want to claim the block if it uses JSX
121428
- // attribute expression syntax (`attr={...}`). Plain HTML should fall
121429
- // through to CommonMark html-flow. Flow mode claims any PascalCase block
121430
- // component; text mode claims only inline PascalCase components
121431
- // (INLINE_COMPONENT_TAGS — Anchor, Glossary), also brace-gated.
121432
- let isLowercaseTag = false;
121433
- let sawBraceAttr = false;
121434
- // A plain lowercase block tag claimed without a `{…}` attribute, gated by
121435
- // `plainClaimLineStart`: after a blank line it may only continue on a tag line.
121436
- let isPlainBlockClaim = false;
121437
- let pendingBlankLine = false;
121438
- // Code span tracking
121439
- let codeSpanOpenSize = 0;
121440
- let codeSpanCloseSize = 0;
121441
- // Fenced code block tracking
121442
- let fenceChar = null;
121443
- let fenceLength = 0;
121444
- let fenceCloseLength = 0;
121445
- let atLineStart = false;
121446
- // True once this construct consumes any line ending; lets `afterClose`
121447
- // treat only single-line lowercase tags as inline candidates.
121448
- let sawLineEnding = false;
121449
- // Bail when the opener line has unmatched tag-like tokens in its body.
120616
+ function tokenizeJsxTable(effects, ok, nok) {
120617
+ let codeSpanOpenSize = 0;
120618
+ let codeSpanCloseSize = 0;
120619
+ let depth = 1;
120620
+ const ABLE_SUFFIX = [codes.lowercaseA, codes.lowercaseB, codes.lowercaseL, codes.lowercaseE];
120621
+ /** Build a state chain that matches a sequence of character codes. */
120622
+ function matchChars(chars, onMatch, onFail) {
120623
+ if (chars.length === 0)
120624
+ return onMatch;
120625
+ return ((code) => {
120626
+ if (code === chars[0]) {
120627
+ effects.consume(code);
120628
+ return matchChars(chars.slice(1), onMatch, onFail);
120629
+ }
120630
+ return onFail(code);
120631
+ });
120632
+ }
120633
+ return start;
120634
+ function start(code) {
120635
+ if (code !== codes.lessThan)
120636
+ return nok(code);
120637
+ effects.enter('jsxTable');
120638
+ effects.enter('jsxTableData');
120639
+ effects.consume(code);
120640
+ return afterLessThan;
120641
+ }
120642
+ function afterLessThan(code) {
120643
+ if (code === codes.uppercaseT || code === codes.lowercaseT) {
120644
+ effects.consume(code);
120645
+ return matchChars(ABLE_SUFFIX, afterTagName, nok);
120646
+ }
120647
+ return nok(code);
120648
+ }
120649
+ function afterTagName(code) {
120650
+ if (code === codes.greaterThan || code === codes.slash || code === codes.space || code === codes.horizontalTab) {
120651
+ effects.consume(code);
120652
+ return body;
120653
+ }
120654
+ return nok(code);
120655
+ }
120656
+ function body(code) {
120657
+ // Reject unclosed <Table> so it falls back to normal HTML block parsing
120658
+ // instead of swallowing all subsequent content to EOF
120659
+ if (code === null) {
120660
+ return nok(code);
120661
+ }
120662
+ if (markdownLineEnding(code)) {
120663
+ effects.exit('jsxTableData');
120664
+ return continuationStart(code);
120665
+ }
120666
+ if (code === codes.backslash) {
120667
+ effects.consume(code);
120668
+ return escapedChar;
120669
+ }
120670
+ if (code === codes.lessThan) {
120671
+ effects.consume(code);
120672
+ return closeSlash;
120673
+ }
120674
+ // Skip over backtick code spans so `</Table>` in inline code isn't
120675
+ // treated as the closing tag
120676
+ if (code === codes.graveAccent) {
120677
+ codeSpanOpenSize = 0;
120678
+ return countOpenTicks(code);
120679
+ }
120680
+ effects.consume(code);
120681
+ return body;
120682
+ }
120683
+ function countOpenTicks(code) {
120684
+ if (code === codes.graveAccent) {
120685
+ codeSpanOpenSize += 1;
120686
+ effects.consume(code);
120687
+ return countOpenTicks;
120688
+ }
120689
+ return inCodeSpan(code);
120690
+ }
120691
+ function inCodeSpan(code) {
120692
+ if (code === null || markdownLineEnding(code))
120693
+ return body(code);
120694
+ if (code === codes.graveAccent) {
120695
+ codeSpanCloseSize = 0;
120696
+ return countCloseTicks(code);
120697
+ }
120698
+ effects.consume(code);
120699
+ return inCodeSpan;
120700
+ }
120701
+ function countCloseTicks(code) {
120702
+ if (code === codes.graveAccent) {
120703
+ codeSpanCloseSize += 1;
120704
+ effects.consume(code);
120705
+ return countCloseTicks;
120706
+ }
120707
+ return codeSpanCloseSize === codeSpanOpenSize ? body(code) : inCodeSpan(code);
120708
+ }
120709
+ function escapedChar(code) {
120710
+ if (code === null || markdownLineEnding(code)) {
120711
+ return body(code);
120712
+ }
120713
+ effects.consume(code);
120714
+ return body;
120715
+ }
120716
+ function closeSlash(code) {
120717
+ if (code === codes.slash) {
120718
+ effects.consume(code);
120719
+ return closeTagFirstChar;
120720
+ }
120721
+ if (code === codes.uppercaseT || code === codes.lowercaseT) {
120722
+ effects.consume(code);
120723
+ return matchChars(ABLE_SUFFIX, openAfterTagName, body);
120724
+ }
120725
+ return body(code);
120726
+ }
120727
+ function closeTagFirstChar(code) {
120728
+ if (code === codes.uppercaseT || code === codes.lowercaseT) {
120729
+ effects.consume(code);
120730
+ return matchChars(ABLE_SUFFIX, closeGt, body);
120731
+ }
120732
+ return body(code);
120733
+ }
120734
+ function openAfterTagName(code) {
120735
+ if (code === codes.greaterThan || code === codes.slash || code === codes.space || code === codes.horizontalTab) {
120736
+ depth += 1;
120737
+ effects.consume(code);
120738
+ return body;
120739
+ }
120740
+ return body(code);
120741
+ }
120742
+ function closeGt(code) {
120743
+ if (code === codes.greaterThan) {
120744
+ depth -= 1;
120745
+ effects.consume(code);
120746
+ if (depth === 0) {
120747
+ return afterClose;
120748
+ }
120749
+ return body;
120750
+ }
120751
+ return body(code);
120752
+ }
120753
+ function afterClose(code) {
120754
+ if (code === null || markdownLineEnding(code)) {
120755
+ effects.exit('jsxTableData');
120756
+ effects.exit('jsxTable');
120757
+ return ok(code);
120758
+ }
120759
+ effects.consume(code);
120760
+ return afterClose;
120761
+ }
120762
+ // Line ending handling — follows the htmlFlow pattern
120763
+ function continuationStart(code) {
120764
+ return effects.check(syntax_nonLazyContinuationStart, continuationStartNonLazy, continuationAfter)(code);
120765
+ }
120766
+ function continuationStartNonLazy(code) {
120767
+ effects.enter(types_types.lineEnding);
120768
+ effects.consume(code);
120769
+ effects.exit(types_types.lineEnding);
120770
+ return continuationBefore;
120771
+ }
120772
+ function continuationBefore(code) {
120773
+ if (code === null || markdownLineEnding(code)) {
120774
+ return continuationStart(code);
120775
+ }
120776
+ effects.enter('jsxTableData');
120777
+ return body(code);
120778
+ }
120779
+ function continuationAfter(code) {
120780
+ // At EOF without </Table>, reject so content isn't swallowed
120781
+ if (code === null) {
120782
+ return nok(code);
120783
+ }
120784
+ effects.exit('jsxTable');
120785
+ return ok(code);
120786
+ }
120787
+ }
120788
+ function syntax_tokenizeNonLazyContinuationStart(effects, ok, nok) {
120789
+ // eslint-disable-next-line @typescript-eslint/no-this-alias
120790
+ const self = this;
120791
+ return start;
120792
+ function start(code) {
120793
+ if (markdownLineEnding(code)) {
120794
+ effects.enter(types_types.lineEnding);
120795
+ effects.consume(code);
120796
+ effects.exit(types_types.lineEnding);
120797
+ return after;
120798
+ }
120799
+ return nok(code);
120800
+ }
120801
+ function after(code) {
120802
+ if (self.parser.lazy[self.now().line]) {
120803
+ return nok(code);
120804
+ }
120805
+ return ok(code);
120806
+ }
120807
+ }
120808
+ /**
120809
+ * Micromark extension that tokenizes `<Table>...</Table>` and `<table>...</table>`
120810
+ * as a single flow block.
120811
+ *
120812
+ * Prevents CommonMark HTML block type 6 from fragmenting table blocks at blank lines.
120813
+ */
120814
+ function jsxTable() {
120815
+ return {
120816
+ flow: {
120817
+ [codes.lessThan]: [jsxTableConstruct],
120818
+ },
120819
+ };
120820
+ }
120821
+
120822
+ ;// ./lib/micromark/jsx-table/index.ts
120823
+
120824
+
120825
+ ;// ./lib/micromark/magic-block/syntax.ts
120826
+
120827
+
120828
+ /**
120829
+ * Known magic block types that the tokenizer will recognize.
120830
+ * Unknown types will not be tokenized as magic blocks.
120831
+ */
120832
+ const KNOWN_BLOCK_TYPES = new Set([
120833
+ 'code',
120834
+ 'api-header',
120835
+ 'image',
120836
+ 'callout',
120837
+ 'parameters',
120838
+ 'table',
120839
+ 'embed',
120840
+ 'html',
120841
+ 'recipe',
120842
+ 'tutorial-tile',
120843
+ ]);
120844
+ /**
120845
+ * Check if a character is valid for a magic block type identifier.
120846
+ * Types can contain alphanumeric characters and hyphens (e.g., "api-header", "tutorial-tile")
120847
+ */
120848
+ function isTypeChar(code) {
120849
+ return asciiAlphanumeric(code) || code === codes.dash;
120850
+ }
120851
+ /**
120852
+ * Creates the opening marker state machine: [block:
120853
+ * Returns the first state function to start parsing.
120854
+ */
120855
+ function createOpeningMarkerParser(effects, nok, onComplete) {
120856
+ const expectB = (code) => {
120857
+ if (code !== codes.lowercaseB)
120858
+ return nok(code);
120859
+ effects.consume(code);
120860
+ return expectL;
120861
+ };
120862
+ const expectL = (code) => {
120863
+ if (code !== codes.lowercaseL)
120864
+ return nok(code);
120865
+ effects.consume(code);
120866
+ return expectO;
120867
+ };
120868
+ const expectO = (code) => {
120869
+ if (code !== codes.lowercaseO)
120870
+ return nok(code);
120871
+ effects.consume(code);
120872
+ return expectC;
120873
+ };
120874
+ const expectC = (code) => {
120875
+ if (code !== codes.lowercaseC)
120876
+ return nok(code);
120877
+ effects.consume(code);
120878
+ return expectK;
120879
+ };
120880
+ const expectK = (code) => {
120881
+ if (code !== codes.lowercaseK)
120882
+ return nok(code);
120883
+ effects.consume(code);
120884
+ return expectColon;
120885
+ };
120886
+ const expectColon = (code) => {
120887
+ if (code !== codes.colon)
120888
+ return nok(code);
120889
+ effects.consume(code);
120890
+ effects.exit('magicBlockMarkerStart');
120891
+ effects.enter('magicBlockType');
120892
+ return onComplete;
120893
+ };
120894
+ return expectB;
120895
+ }
120896
+ /**
120897
+ * Creates the type capture state machine.
120898
+ * Captures type characters until ] and validates against known types.
120899
+ */
120900
+ function createTypeCaptureParser(effects, nok, onComplete, blockTypeRef) {
120901
+ const captureTypeFirst = (code) => {
120902
+ // Reject empty type name [block:]
120903
+ if (code === codes.rightSquareBracket) {
120904
+ return nok(code);
120905
+ }
120906
+ if (isTypeChar(code)) {
120907
+ blockTypeRef.value += String.fromCharCode(code);
120908
+ effects.consume(code);
120909
+ return captureType;
120910
+ }
120911
+ return nok(code);
120912
+ };
120913
+ const captureType = (code) => {
120914
+ if (code === codes.rightSquareBracket) {
120915
+ if (!KNOWN_BLOCK_TYPES.has(blockTypeRef.value)) {
120916
+ return nok(code);
120917
+ }
120918
+ effects.exit('magicBlockType');
120919
+ effects.enter('magicBlockMarkerTypeEnd');
120920
+ effects.consume(code);
120921
+ effects.exit('magicBlockMarkerTypeEnd');
120922
+ return onComplete;
120923
+ }
120924
+ if (isTypeChar(code)) {
120925
+ blockTypeRef.value += String.fromCharCode(code);
120926
+ effects.consume(code);
120927
+ return captureType;
120928
+ }
120929
+ return nok(code);
120930
+ };
120931
+ return { first: captureTypeFirst, remaining: captureType };
120932
+ }
120933
+ /**
120934
+ * Creates the closing marker state machine: /block]
120935
+ * Handles partial matches by calling onMismatch to fall back to data capture.
120936
+ */
120937
+ function createClosingMarkerParser(effects, onSuccess, onMismatch, onEof, jsonState) {
120938
+ const handleMismatch = (code) => {
120939
+ if (jsonState && code === codes.quotationMark) {
120940
+ jsonState.inString = true;
120941
+ }
120942
+ return onMismatch(code);
120943
+ };
120944
+ const expectSlash = (code) => {
120945
+ if (code === null)
120946
+ return onEof(code);
120947
+ if (code !== codes.slash)
120948
+ return handleMismatch(code);
120949
+ effects.consume(code);
120950
+ return expectB;
120951
+ };
120952
+ const expectB = (code) => {
120953
+ if (code === null)
120954
+ return onEof(code);
120955
+ if (code !== codes.lowercaseB)
120956
+ return handleMismatch(code);
120957
+ effects.consume(code);
120958
+ return expectL;
120959
+ };
120960
+ const expectL = (code) => {
120961
+ if (code === null)
120962
+ return onEof(code);
120963
+ if (code !== codes.lowercaseL)
120964
+ return handleMismatch(code);
120965
+ effects.consume(code);
120966
+ return expectO;
120967
+ };
120968
+ const expectO = (code) => {
120969
+ if (code === null)
120970
+ return onEof(code);
120971
+ if (code !== codes.lowercaseO)
120972
+ return handleMismatch(code);
120973
+ effects.consume(code);
120974
+ return expectC;
120975
+ };
120976
+ const expectC = (code) => {
120977
+ if (code === null)
120978
+ return onEof(code);
120979
+ if (code !== codes.lowercaseC)
120980
+ return handleMismatch(code);
120981
+ effects.consume(code);
120982
+ return expectK;
120983
+ };
120984
+ const expectK = (code) => {
120985
+ if (code === null)
120986
+ return onEof(code);
120987
+ if (code !== codes.lowercaseK)
120988
+ return handleMismatch(code);
120989
+ effects.consume(code);
120990
+ return expectBracket;
120991
+ };
120992
+ const expectBracket = (code) => {
120993
+ if (code === null)
120994
+ return onEof(code);
120995
+ if (code !== codes.rightSquareBracket)
120996
+ return handleMismatch(code);
120997
+ effects.consume(code);
120998
+ return onSuccess;
120999
+ };
121000
+ return { expectSlash };
121001
+ }
121002
+ /**
121003
+ * Partial construct for checking non-lazy continuation.
121004
+ * This is used by the flow tokenizer to check if we can continue
121005
+ * parsing on the next line.
121006
+ */
121007
+ const syntax_nonLazyContinuation = {
121008
+ partial: true,
121009
+ tokenize: syntax_tokenizeNonLazyContinuation,
121010
+ };
121011
+ /**
121012
+ * Tokenizer for non-lazy continuation checking.
121013
+ * Returns ok if the next line is non-lazy (can continue), nok if lazy.
121014
+ */
121015
+ function syntax_tokenizeNonLazyContinuation(effects, ok, nok) {
121016
+ const lineStart = (code) => {
121017
+ // `this` here refers to the micromark parser
121018
+ // since we are just passing functions as references and not actually calling them
121019
+ // micromarks's internal parser will call this automatically with appropriate arguments
121020
+ return this.parser.lazy[this.now().line] ? nok(code) : ok(code);
121021
+ };
121022
+ const start = (code) => {
121023
+ if (code === null) {
121024
+ return nok(code);
121025
+ }
121026
+ if (!markdownLineEnding(code)) {
121027
+ return nok(code);
121028
+ }
121029
+ effects.enter('magicBlockLineEnding');
121030
+ effects.consume(code);
121031
+ effects.exit('magicBlockLineEnding');
121032
+ return lineStart;
121033
+ };
121034
+ return start;
121035
+ }
121036
+ /**
121037
+ * Create a micromark extension for magic block syntax.
121038
+ *
121039
+ * This extension handles both single-line and multiline magic blocks:
121040
+ * - Flow construct (concrete): Handles block-level multiline magic blocks at document level
121041
+ * - Text construct: Handles inline magic blocks in lists, paragraphs, etc.
121042
+ *
121043
+ * The flow construct is marked as "concrete" which prevents it from being
121044
+ * interrupted by container markers (like `>` for blockquotes or `-` for lists).
121045
+ */
121046
+ function magicBlock() {
121047
+ return {
121048
+ // Flow construct - handles block-level magic blocks at document root
121049
+ // Marked as concrete to prevent interruption by containers
121050
+ flow: {
121051
+ [codes.leftSquareBracket]: {
121052
+ name: 'magicBlock',
121053
+ concrete: true,
121054
+ tokenize: tokenizeMagicBlockFlow,
121055
+ },
121056
+ },
121057
+ // Text construct - handles magic blocks in inline contexts (lists, paragraphs)
121058
+ text: {
121059
+ [codes.leftSquareBracket]: {
121060
+ name: 'magicBlock',
121061
+ tokenize: tokenizeMagicBlockText,
121062
+ },
121063
+ },
121064
+ };
121065
+ }
121066
+ /**
121067
+ * Flow tokenizer for block-level magic blocks (multiline).
121068
+ * Uses the continuation checking pattern from code fences.
121069
+ */
121070
+ function tokenizeMagicBlockFlow(effects, ok, nok) {
121071
+ // State for tracking JSON content
121072
+ const jsonState = { escapeNext: false, inString: false };
121073
+ const blockTypeRef = { value: '' };
121074
+ let seenOpenBrace = false;
121075
+ // Create shared parsers for opening marker and type capture
121076
+ const typeParser = createTypeCaptureParser(effects, nok, beforeData, blockTypeRef);
121077
+ const openingMarkerParser = createOpeningMarkerParser(effects, nok, typeParser.first);
121078
+ return start;
121079
+ function start(code) {
121080
+ if (code !== codes.leftSquareBracket)
121081
+ return nok(code);
121082
+ effects.enter('magicBlock');
121083
+ effects.enter('magicBlockMarkerStart');
121084
+ effects.consume(code);
121085
+ return openingMarkerParser;
121086
+ }
121087
+ /**
121088
+ * State before data content - handles line endings or starts data capture.
121089
+ */
121090
+ function beforeData(code) {
121091
+ // EOF - magic block must be closed
121092
+ if (code === null) {
121093
+ effects.exit('magicBlock');
121094
+ return nok(code);
121095
+ }
121096
+ // Line ending before any data - check continuation
121097
+ if (markdownLineEnding(code)) {
121098
+ return effects.check(syntax_nonLazyContinuation, continuationOkBeforeData, after)(code);
121099
+ }
121100
+ // Check for closing marker directly (without entering data)
121101
+ // This handles cases like [block:type]\n{}\n\n[/block] where there are
121102
+ // newlines after the data object
121103
+ if (code === codes.leftSquareBracket) {
121104
+ effects.enter('magicBlockMarkerEnd');
121105
+ effects.consume(code);
121106
+ return expectSlashFromContinuation;
121107
+ }
121108
+ // If we've already seen the opening brace, just continue capturing data
121109
+ if (seenOpenBrace) {
121110
+ effects.enter('magicBlockData');
121111
+ return captureData(code);
121112
+ }
121113
+ // Skip whitespace (spaces/tabs) before the data - stay in beforeData
121114
+ // Don't enter magicBlockData token yet until we confirm there's a '{'
121115
+ if (code === codes.space || code === codes.horizontalTab) {
121116
+ return beforeDataWhitespace(code);
121117
+ }
121118
+ // Data must start with '{' for valid JSON
121119
+ if (code !== codes.leftCurlyBrace) {
121120
+ effects.exit('magicBlock');
121121
+ return nok(code);
121122
+ }
121123
+ // We have '{' - enter data token and start capturing
121124
+ seenOpenBrace = true;
121125
+ effects.enter('magicBlockData');
121126
+ return captureData(code);
121127
+ }
121128
+ /**
121129
+ * Consume whitespace before the data without creating a token.
121130
+ * Uses a temporary token to satisfy micromark's requirement.
121131
+ */
121132
+ function beforeDataWhitespace(code) {
121133
+ if (code === null) {
121134
+ effects.exit('magicBlock');
121135
+ return nok(code);
121136
+ }
121137
+ if (markdownLineEnding(code)) {
121138
+ return effects.check(syntax_nonLazyContinuation, continuationOkBeforeData, after)(code);
121139
+ }
121140
+ if (code === codes.space || code === codes.horizontalTab) {
121141
+ // We need to consume this but can't without a token - use magicBlockData
121142
+ // and track that we haven't seen '{' yet
121143
+ effects.enter('magicBlockData');
121144
+ effects.consume(code);
121145
+ return beforeDataWhitespaceContinue;
121146
+ }
121147
+ if (code === codes.leftCurlyBrace) {
121148
+ seenOpenBrace = true;
121149
+ effects.enter('magicBlockData');
121150
+ return captureData(code);
121151
+ }
121152
+ effects.exit('magicBlock');
121153
+ return nok(code);
121154
+ }
121155
+ /**
121156
+ * Continue consuming whitespace or validate the opening brace.
121157
+ */
121158
+ function beforeDataWhitespaceContinue(code) {
121159
+ if (code === null) {
121160
+ effects.exit('magicBlockData');
121161
+ effects.exit('magicBlock');
121162
+ return nok(code);
121163
+ }
121164
+ if (markdownLineEnding(code)) {
121165
+ effects.exit('magicBlockData');
121166
+ return effects.check(syntax_nonLazyContinuation, continuationOk, after)(code);
121167
+ }
121168
+ if (code === codes.space || code === codes.horizontalTab) {
121169
+ effects.consume(code);
121170
+ return beforeDataWhitespaceContinue;
121171
+ }
121172
+ if (code === codes.leftCurlyBrace) {
121173
+ seenOpenBrace = true;
121174
+ return captureData(code);
121175
+ }
121176
+ effects.exit('magicBlockData');
121177
+ effects.exit('magicBlock');
121178
+ return nok(code);
121179
+ }
121180
+ /**
121181
+ * Continuation OK before we've entered data token.
121182
+ */
121183
+ function continuationOkBeforeData(code) {
121184
+ effects.enter('magicBlockLineEnding');
121185
+ effects.consume(code);
121186
+ effects.exit('magicBlockLineEnding');
121187
+ return beforeData; // Stay in beforeData state - don't enter magicBlockData yet
121188
+ }
121189
+ function captureData(code) {
121190
+ // EOF - magic block must be closed
121191
+ if (code === null) {
121192
+ effects.exit('magicBlockData');
121193
+ effects.exit('magicBlock');
121194
+ return nok(code);
121195
+ }
121196
+ // At line ending, check if we can continue on the next line
121197
+ if (markdownLineEnding(code)) {
121198
+ effects.exit('magicBlockData');
121199
+ return effects.check(syntax_nonLazyContinuation, continuationOk, after)(code);
121200
+ }
121201
+ if (jsonState.escapeNext) {
121202
+ jsonState.escapeNext = false;
121203
+ effects.consume(code);
121204
+ return captureData;
121205
+ }
121206
+ if (jsonState.inString) {
121207
+ if (code === codes.backslash) {
121208
+ jsonState.escapeNext = true;
121209
+ effects.consume(code);
121210
+ return captureData;
121211
+ }
121212
+ if (code === codes.quotationMark) {
121213
+ jsonState.inString = false;
121214
+ }
121215
+ effects.consume(code);
121216
+ return captureData;
121217
+ }
121218
+ if (code === codes.quotationMark) {
121219
+ jsonState.inString = true;
121220
+ effects.consume(code);
121221
+ return captureData;
121222
+ }
121223
+ if (code === codes.leftSquareBracket) {
121224
+ effects.exit('magicBlockData');
121225
+ effects.enter('magicBlockMarkerEnd');
121226
+ effects.consume(code);
121227
+ return expectSlash;
121228
+ }
121229
+ effects.consume(code);
121230
+ return captureData;
121231
+ }
121232
+ /**
121233
+ * Called when non-lazy continuation check passes - we can continue parsing.
121234
+ */
121235
+ function continuationOk(code) {
121236
+ // Consume the line ending
121237
+ effects.enter('magicBlockLineEnding');
121238
+ effects.consume(code);
121239
+ effects.exit('magicBlockLineEnding');
121240
+ return continuationStart;
121241
+ }
121242
+ /**
121243
+ * Start of continuation line - check for more line endings, closing marker, or start capturing data.
121244
+ */
121245
+ function continuationStart(code) {
121246
+ // Handle consecutive line endings
121247
+ if (code === null) {
121248
+ effects.exit('magicBlock');
121249
+ return nok(code);
121250
+ }
121251
+ if (markdownLineEnding(code)) {
121252
+ return effects.check(syntax_nonLazyContinuation, continuationOkBeforeData, after)(code);
121253
+ }
121254
+ // Check if this is the start of the closing marker [/block]
121255
+ // If so, handle it directly without entering magicBlockData
121256
+ if (code === codes.leftSquareBracket) {
121257
+ effects.enter('magicBlockMarkerEnd');
121258
+ effects.consume(code);
121259
+ return expectSlashFromContinuation;
121260
+ }
121261
+ effects.enter('magicBlockData');
121262
+ return captureData(code);
121263
+ }
121264
+ /**
121265
+ * Check for closing marker slash when coming from continuation.
121266
+ * If not a closing marker, create an empty data token and continue.
121267
+ */
121268
+ function expectSlashFromContinuation(code) {
121269
+ if (code === null) {
121270
+ effects.exit('magicBlockMarkerEnd');
121271
+ effects.exit('magicBlock');
121272
+ return nok(code);
121273
+ }
121274
+ if (markdownLineEnding(code)) {
121275
+ effects.exit('magicBlockMarkerEnd');
121276
+ return effects.check(syntax_nonLazyContinuation, continuationOkBeforeData, after)(code);
121277
+ }
121278
+ if (code === codes.slash) {
121279
+ effects.consume(code);
121280
+ return expectClosingB;
121281
+ }
121282
+ // Not a closing marker - this is data content
121283
+ // Exit marker and enter data
121284
+ effects.exit('magicBlockMarkerEnd');
121285
+ effects.enter('magicBlockData');
121286
+ // The [ was consumed by the marker, so we need to conceptually "have it" in our data
121287
+ // But since we already consumed it into the marker, we need a different approach
121288
+ // Re-classify: consume this character as data and continue
121289
+ if (code === codes.quotationMark)
121290
+ jsonState.inString = true;
121291
+ effects.consume(code);
121292
+ return captureData;
121293
+ }
121294
+ function expectSlash(code) {
121295
+ if (code === null) {
121296
+ effects.exit('magicBlockMarkerEnd');
121297
+ effects.exit('magicBlock');
121298
+ return nok(code);
121299
+ }
121300
+ // At line ending during marker parsing
121301
+ if (markdownLineEnding(code)) {
121302
+ effects.exit('magicBlockMarkerEnd');
121303
+ return effects.check(syntax_nonLazyContinuation, continuationOk, after)(code);
121304
+ }
121305
+ if (code !== codes.slash) {
121306
+ effects.exit('magicBlockMarkerEnd');
121307
+ effects.enter('magicBlockData');
121308
+ if (code === codes.quotationMark)
121309
+ jsonState.inString = true;
121310
+ effects.consume(code);
121311
+ return captureData;
121312
+ }
121313
+ effects.consume(code);
121314
+ return expectClosingB;
121315
+ }
121316
+ function expectClosingB(code) {
121317
+ if (code === null) {
121318
+ effects.exit('magicBlockMarkerEnd');
121319
+ effects.exit('magicBlock');
121320
+ return nok(code);
121321
+ }
121322
+ if (code !== codes.lowercaseB) {
121323
+ effects.exit('magicBlockMarkerEnd');
121324
+ effects.enter('magicBlockData');
121325
+ if (code === codes.quotationMark)
121326
+ jsonState.inString = true;
121327
+ effects.consume(code);
121328
+ return captureData;
121329
+ }
121330
+ effects.consume(code);
121331
+ return expectClosingL;
121332
+ }
121333
+ function expectClosingL(code) {
121334
+ if (code === null) {
121335
+ effects.exit('magicBlockMarkerEnd');
121336
+ effects.exit('magicBlock');
121337
+ return nok(code);
121338
+ }
121339
+ if (code !== codes.lowercaseL) {
121340
+ effects.exit('magicBlockMarkerEnd');
121341
+ effects.enter('magicBlockData');
121342
+ if (code === codes.quotationMark)
121343
+ jsonState.inString = true;
121344
+ effects.consume(code);
121345
+ return captureData;
121346
+ }
121347
+ effects.consume(code);
121348
+ return expectClosingO;
121349
+ }
121350
+ function expectClosingO(code) {
121351
+ if (code === null) {
121352
+ effects.exit('magicBlockMarkerEnd');
121353
+ effects.exit('magicBlock');
121354
+ return nok(code);
121355
+ }
121356
+ if (code !== codes.lowercaseO) {
121357
+ effects.exit('magicBlockMarkerEnd');
121358
+ effects.enter('magicBlockData');
121359
+ if (code === codes.quotationMark)
121360
+ jsonState.inString = true;
121361
+ effects.consume(code);
121362
+ return captureData;
121363
+ }
121364
+ effects.consume(code);
121365
+ return expectClosingC;
121366
+ }
121367
+ function expectClosingC(code) {
121368
+ if (code === null) {
121369
+ effects.exit('magicBlockMarkerEnd');
121370
+ effects.exit('magicBlock');
121371
+ return nok(code);
121372
+ }
121373
+ if (code !== codes.lowercaseC) {
121374
+ effects.exit('magicBlockMarkerEnd');
121375
+ effects.enter('magicBlockData');
121376
+ if (code === codes.quotationMark)
121377
+ jsonState.inString = true;
121378
+ effects.consume(code);
121379
+ return captureData;
121380
+ }
121381
+ effects.consume(code);
121382
+ return expectClosingK;
121383
+ }
121384
+ function expectClosingK(code) {
121385
+ if (code === null) {
121386
+ effects.exit('magicBlockMarkerEnd');
121387
+ effects.exit('magicBlock');
121388
+ return nok(code);
121389
+ }
121390
+ if (code !== codes.lowercaseK) {
121391
+ effects.exit('magicBlockMarkerEnd');
121392
+ effects.enter('magicBlockData');
121393
+ if (code === codes.quotationMark)
121394
+ jsonState.inString = true;
121395
+ effects.consume(code);
121396
+ return captureData;
121397
+ }
121398
+ effects.consume(code);
121399
+ return expectClosingBracket;
121400
+ }
121401
+ function expectClosingBracket(code) {
121402
+ if (code === null) {
121403
+ effects.exit('magicBlockMarkerEnd');
121404
+ effects.exit('magicBlock');
121405
+ return nok(code);
121406
+ }
121407
+ if (code !== codes.rightSquareBracket) {
121408
+ effects.exit('magicBlockMarkerEnd');
121409
+ effects.enter('magicBlockData');
121410
+ if (code === codes.quotationMark)
121411
+ jsonState.inString = true;
121412
+ effects.consume(code);
121413
+ return captureData;
121414
+ }
121415
+ effects.consume(code);
121416
+ effects.exit('magicBlockMarkerEnd');
121417
+ // Check for trailing whitespace on the same line
121418
+ return consumeTrailing;
121419
+ }
121420
+ /**
121421
+ * Consume trailing whitespace (spaces/tabs) on the same line after [/block].
121422
+ * For concrete flow constructs, we must end at eol/eof or fail.
121423
+ * Trailing whitespace is consumed; other content causes nok (text tokenizer handles it).
121424
+ */
121425
+ function consumeTrailing(code) {
121426
+ // End of file - done
121427
+ if (code === null) {
121428
+ effects.exit('magicBlock');
121429
+ return ok(code);
121430
+ }
121431
+ // Line ending - done
121432
+ if (markdownLineEnding(code)) {
121433
+ effects.exit('magicBlock');
121434
+ return ok(code);
121435
+ }
121436
+ // Space or tab - consume as trailing whitespace
121437
+ if (code === codes.space || code === codes.horizontalTab) {
121438
+ effects.enter('magicBlockTrailing');
121439
+ effects.consume(code);
121440
+ return consumeTrailingContinue;
121441
+ }
121442
+ // Any other character - fail flow tokenizer, let text tokenizer handle it
121443
+ effects.exit('magicBlock');
121444
+ return nok(code);
121445
+ }
121446
+ /**
121447
+ * Continue consuming trailing whitespace.
121448
+ */
121449
+ function consumeTrailingContinue(code) {
121450
+ // End of file - done
121451
+ if (code === null) {
121452
+ effects.exit('magicBlockTrailing');
121453
+ effects.exit('magicBlock');
121454
+ return ok(code);
121455
+ }
121456
+ // Line ending - done
121457
+ if (markdownLineEnding(code)) {
121458
+ effects.exit('magicBlockTrailing');
121459
+ effects.exit('magicBlock');
121460
+ return ok(code);
121461
+ }
121462
+ // More space or tab - keep consuming
121463
+ if (code === codes.space || code === codes.horizontalTab) {
121464
+ effects.consume(code);
121465
+ return consumeTrailingContinue;
121466
+ }
121467
+ // Non-whitespace after whitespace - fail flow tokenizer
121468
+ effects.exit('magicBlockTrailing');
121469
+ effects.exit('magicBlock');
121470
+ return nok(code);
121471
+ }
121472
+ /**
121473
+ * Called when we can't continue (lazy line or EOF) - exit the construct.
121474
+ */
121475
+ function after(code) {
121476
+ effects.exit('magicBlock');
121477
+ return nok(code);
121478
+ }
121479
+ }
121480
+ /**
121481
+ * Text tokenizer for single-line magic blocks only.
121482
+ * Used in inline contexts like list items and paragraphs.
121483
+ * Multiline blocks are handled by the flow tokenizer.
121484
+ */
121485
+ function tokenizeMagicBlockText(effects, ok, nok) {
121486
+ // State for tracking JSON content
121487
+ const jsonState = { escapeNext: false, inString: false };
121488
+ const blockTypeRef = { value: '' };
121489
+ let seenOpenBrace = false;
121490
+ // Create shared parsers for opening marker and type capture
121491
+ const typeParser = createTypeCaptureParser(effects, nok, beforeData, blockTypeRef);
121492
+ const openingMarkerParser = createOpeningMarkerParser(effects, nok, typeParser.first);
121493
+ // Success handler for closing marker - exits tokens and returns ok
121494
+ const closingSuccess = (code) => {
121495
+ effects.exit('magicBlockMarkerEnd');
121496
+ effects.exit('magicBlock');
121497
+ return ok(code);
121498
+ };
121499
+ // Mismatch handler - falls back to data capture
121500
+ const closingMismatch = (code) => {
121501
+ effects.exit('magicBlockMarkerEnd');
121502
+ effects.enter('magicBlockData');
121503
+ if (code === codes.quotationMark)
121504
+ jsonState.inString = true;
121505
+ effects.consume(code);
121506
+ return captureData;
121507
+ };
121508
+ // EOF handler
121509
+ const closingEof = (_code) => nok(_code);
121510
+ // Create closing marker parsers
121511
+ const closingFromBeforeData = createClosingMarkerParser(effects, closingSuccess, closingMismatch, closingEof, jsonState);
121512
+ const closingFromData = createClosingMarkerParser(effects, closingSuccess, closingMismatch, closingEof, jsonState);
121513
+ return start;
121514
+ function start(code) {
121515
+ if (code !== codes.leftSquareBracket)
121516
+ return nok(code);
121517
+ effects.enter('magicBlock');
121518
+ effects.enter('magicBlockMarkerStart');
121519
+ effects.consume(code);
121520
+ return openingMarkerParser;
121521
+ }
121522
+ /**
121523
+ * State before data content - handles line endings before entering data token.
121524
+ * Whitespace before '{' is allowed.
121525
+ */
121526
+ function beforeData(code) {
121527
+ // Fail on EOF - magic block must be closed
121528
+ if (code === null) {
121529
+ return nok(code);
121530
+ }
121531
+ // Handle line endings before any data - consume them without entering data token
121532
+ if (markdownLineEnding(code)) {
121533
+ effects.enter('magicBlockLineEnding');
121534
+ effects.consume(code);
121535
+ effects.exit('magicBlockLineEnding');
121536
+ return beforeData;
121537
+ }
121538
+ // Check for closing marker directly (without entering data)
121539
+ if (code === codes.leftSquareBracket) {
121540
+ effects.enter('magicBlockMarkerEnd');
121541
+ effects.consume(code);
121542
+ return closingFromBeforeData.expectSlash;
121543
+ }
121544
+ // If we've already seen the opening brace, just continue capturing data
121545
+ if (seenOpenBrace) {
121546
+ effects.enter('magicBlockData');
121547
+ return captureData(code);
121548
+ }
121549
+ // Skip whitespace (spaces/tabs) before the data
121550
+ if (code === codes.space || code === codes.horizontalTab) {
121551
+ return beforeDataWhitespace(code);
121552
+ }
121553
+ // Data must start with '{' for valid JSON
121554
+ if (code !== codes.leftCurlyBrace) {
121555
+ return nok(code);
121556
+ }
121557
+ // We have '{' - enter data token and start capturing
121558
+ seenOpenBrace = true;
121559
+ effects.enter('magicBlockData');
121560
+ return captureData(code);
121561
+ }
121562
+ /**
121563
+ * Consume whitespace before the data.
121564
+ */
121565
+ function beforeDataWhitespace(code) {
121566
+ if (code === null) {
121567
+ return nok(code);
121568
+ }
121569
+ if (markdownLineEnding(code)) {
121570
+ effects.enter('magicBlockLineEnding');
121571
+ effects.consume(code);
121572
+ effects.exit('magicBlockLineEnding');
121573
+ return beforeData;
121574
+ }
121575
+ if (code === codes.space || code === codes.horizontalTab) {
121576
+ effects.enter('magicBlockData');
121577
+ effects.consume(code);
121578
+ return beforeDataWhitespaceContinue;
121579
+ }
121580
+ if (code === codes.leftCurlyBrace) {
121581
+ seenOpenBrace = true;
121582
+ effects.enter('magicBlockData');
121583
+ return captureData(code);
121584
+ }
121585
+ return nok(code);
121586
+ }
121587
+ /**
121588
+ * Continue consuming whitespace or validate the opening brace.
121589
+ */
121590
+ function beforeDataWhitespaceContinue(code) {
121591
+ if (code === null) {
121592
+ effects.exit('magicBlockData');
121593
+ return nok(code);
121594
+ }
121595
+ if (markdownLineEnding(code)) {
121596
+ effects.exit('magicBlockData');
121597
+ effects.enter('magicBlockLineEnding');
121598
+ effects.consume(code);
121599
+ effects.exit('magicBlockLineEnding');
121600
+ return beforeData;
121601
+ }
121602
+ if (code === codes.space || code === codes.horizontalTab) {
121603
+ effects.consume(code);
121604
+ return beforeDataWhitespaceContinue;
121605
+ }
121606
+ if (code === codes.leftCurlyBrace) {
121607
+ seenOpenBrace = true;
121608
+ return captureData(code);
121609
+ }
121610
+ effects.exit('magicBlockData');
121611
+ return nok(code);
121612
+ }
121613
+ function captureData(code) {
121614
+ // Fail on EOF - magic block must be closed
121615
+ if (code === null) {
121616
+ effects.exit('magicBlockData');
121617
+ return nok(code);
121618
+ }
121619
+ // Handle multiline magic blocks within text/paragraphs
121620
+ // Exit data, consume line ending with proper token, then re-enter data
121621
+ if (markdownLineEnding(code)) {
121622
+ effects.exit('magicBlockData');
121623
+ effects.enter('magicBlockLineEnding');
121624
+ effects.consume(code);
121625
+ effects.exit('magicBlockLineEnding');
121626
+ return beforeData; // Go back to beforeData to handle potential empty lines
121627
+ }
121628
+ if (jsonState.escapeNext) {
121629
+ jsonState.escapeNext = false;
121630
+ effects.consume(code);
121631
+ return captureData;
121632
+ }
121633
+ if (jsonState.inString) {
121634
+ if (code === codes.backslash) {
121635
+ jsonState.escapeNext = true;
121636
+ effects.consume(code);
121637
+ return captureData;
121638
+ }
121639
+ if (code === codes.quotationMark) {
121640
+ jsonState.inString = false;
121641
+ }
121642
+ effects.consume(code);
121643
+ return captureData;
121644
+ }
121645
+ if (code === codes.quotationMark) {
121646
+ jsonState.inString = true;
121647
+ effects.consume(code);
121648
+ return captureData;
121649
+ }
121650
+ if (code === codes.leftSquareBracket) {
121651
+ effects.exit('magicBlockData');
121652
+ effects.enter('magicBlockMarkerEnd');
121653
+ effects.consume(code);
121654
+ return closingFromData.expectSlash;
121655
+ }
121656
+ effects.consume(code);
121657
+ return captureData;
121658
+ }
121659
+ }
121660
+ /* harmony default export */ const syntax = ((/* unused pure expression or super */ null && (magicBlock)));
121661
+
121662
+ ;// ./lib/micromark/magic-block/index.ts
121663
+ /**
121664
+ * Micromark extension for magic block syntax.
121665
+ *
121666
+ * Usage:
121667
+ * ```ts
121668
+ * import { magicBlock } from './lib/micromark/magic-block';
121669
+ *
121670
+ * const processor = unified()
121671
+ * .data('micromarkExtensions', [magicBlock()])
121672
+ * ```
121673
+ */
121674
+
121675
+
121676
+ ;// ./lib/micromark/mdx-component/syntax.ts
121677
+
121678
+
121679
+
121680
+
121681
+
121682
+ // Raw tags (type-1: pre/script/style/textarea) and block tags (type-6: div,
121683
+ // section, …) always start a block, so they stay flow even with trailing
121684
+ // content. Other lowercase tags (i, span, …) follow the type-7 rule and only
121685
+ // stay flow when nothing trails the close tag.
121686
+ const htmlFlowTagNames = new Set([...htmlRawNames, ...htmlBlockNames]);
121687
+ // Lowercase type-6 block tags claimable in flow even without a `{…}` attribute, so
121688
+ // blank lines between nested JSX siblings don't fragment the block. Excludes table
121689
+ // tags (mdxishTables owns their blank lines) and voids (never close).
121690
+ const plainBlockClaimTagNames = new Set([...htmlBlockNames].filter(tag => !HTML_TABLE_STRUCTURE_TAGS.has(tag) && !HTML_VOID_ELEMENTS.has(tag)));
121691
+ const mdx_component_syntax_nonLazyContinuationStart = {
121692
+ tokenize: mdx_component_syntax_tokenizeNonLazyContinuationStart,
121693
+ partial: true,
121694
+ };
121695
+ // Lookahead for `plainClaimLineStart`: is this line markup-only, or a paragraph
121696
+ // that merely starts with a tag? Run via `effects.check` so it never consumes.
121697
+ const markupOnlyContinuation = {
121698
+ tokenize: tokenizeMarkupOnlyContinuation,
121699
+ partial: true,
121700
+ };
121701
+ function resolveToMdxComponent(events) {
121702
+ let index = events.length;
121703
+ while (index > 0) {
121704
+ index -= 1;
121705
+ if (events[index][0] === 'enter' && events[index][1].type === 'mdxComponent') {
121706
+ break;
121707
+ }
121708
+ }
121709
+ if (index > 1 && events[index - 2][1].type === types_types.linePrefix) {
121710
+ events[index][1].start = events[index - 2][1].start;
121711
+ events[index + 1][1].start = events[index - 2][1].start;
121712
+ events.splice(index - 2, 2);
121713
+ }
121714
+ return events;
121715
+ }
121716
+ const mdxComponentFlowConstruct = {
121717
+ name: 'mdxComponent',
121718
+ tokenize: createTokenize('flow'),
121719
+ resolveTo: resolveToMdxComponent,
121720
+ concrete: true,
121721
+ };
121722
+ const mdxComponentTextConstruct = {
121723
+ name: 'mdxComponentText',
121724
+ tokenize: createTokenize('text'),
121725
+ };
121726
+ /**
121727
+ * Factory for both flow (block) and text (inline) variants.
121728
+ *
121729
+ * **Flow** — the original behavior: claims PascalCase components (always) and
121730
+ * lowercase HTML tags that carry at least one `{…}` attribute expression.
121731
+ * Multi-line, concrete, `afterClose` consumes the rest of the line.
121732
+ *
121733
+ * **Text** — runs inside paragraphs / inline context. Claims lowercase tags and
121734
+ * inline PascalCase components (`INLINE_COMPONENT_TAGS` — Anchor, Glossary), both
121735
+ * gated on at least one `{…}` brace attribute. All other PascalCase stays
121736
+ * flow-only, matching how ReadMe's custom components are authored. Aborts on line
121737
+ * endings (inline constructs don't span lines) and exits immediately after
121738
+ * `</tag>` so the paragraph's inline parser picks up the trailing text.
121739
+ */
121740
+ function createTokenize(mode) {
121741
+ const isFlow = mode === 'flow';
121742
+ return function tokenize(effects, ok, nok) {
121743
+ // eslint-disable-next-line @typescript-eslint/no-this-alias
121744
+ const self = this;
121745
+ let tagName = '';
121746
+ let depth = 0;
121747
+ let closingTagName = '';
121748
+ // For lowercase tags we only want to claim the block if it uses JSX
121749
+ // attribute expression syntax (`attr={...}`). Plain HTML should fall
121750
+ // through to CommonMark html-flow. Flow mode claims any PascalCase block
121751
+ // component; text mode claims only inline PascalCase components
121752
+ // (INLINE_COMPONENT_TAGS — Anchor, Glossary), also brace-gated.
121753
+ let isLowercaseTag = false;
121754
+ let sawBraceAttr = false;
121755
+ // A plain lowercase block tag claimed without a `{…}` attribute, gated by
121756
+ // `plainClaimLineStart`: after a blank line it may only continue on a tag line.
121757
+ let isPlainBlockClaim = false;
121758
+ let pendingBlankLine = false;
121759
+ // Code span tracking
121760
+ let codeSpanOpenSize = 0;
121761
+ let codeSpanCloseSize = 0;
121762
+ // Fenced code block tracking
121763
+ let fenceChar = null;
121764
+ let fenceLength = 0;
121765
+ let fenceCloseLength = 0;
121766
+ let atLineStart = false;
121767
+ // True once this construct consumes any line ending; lets `afterClose`
121768
+ // treat only single-line lowercase tags as inline candidates.
121769
+ let sawLineEnding = false;
121770
+ // Bail when the opener line has unmatched tag-like tokens in its body.
121450
121771
  // `<Foo>_<Bar>.csv` leaves opens > closes; matched shapes like
121451
121772
  // `<Callout>x <strong>y</strong>` balance to 0. Without this,
121452
121773
  // `concrete: true` causes orphan openers to eat sibling blockquotes.
@@ -121734,7 +122055,7 @@ function createTokenize(mode) {
121734
122055
  }
121735
122056
  // Continuation for multi-line opening tags
121736
122057
  function openTagContinuationStart(code) {
121737
- return effects.check(syntax_nonLazyContinuationStart, openTagContinuationNonLazy, continuationAfter)(code);
122058
+ return effects.check(mdx_component_syntax_nonLazyContinuationStart, openTagContinuationNonLazy, continuationAfter)(code);
121738
122059
  }
121739
122060
  function openTagContinuationNonLazy(code) {
121740
122061
  sawLineEnding = true;
@@ -121859,7 +122180,7 @@ function createTokenize(mode) {
121859
122180
  return inFencedCode;
121860
122181
  }
121861
122182
  function fencedCodeContinuationStart(code) {
121862
- return effects.check(syntax_nonLazyContinuationStart, fencedCodeContinuationNonLazy, continuationAfter)(code);
122183
+ return effects.check(mdx_component_syntax_nonLazyContinuationStart, fencedCodeContinuationNonLazy, continuationAfter)(code);
121863
122184
  }
121864
122185
  function fencedCodeContinuationNonLazy(code) {
121865
122186
  sawLineEnding = true;
@@ -121874,6 +122195,16 @@ function createTokenize(mode) {
121874
122195
  }
121875
122196
  effects.enter('mdxComponentData');
121876
122197
  fenceCloseLength = 0;
122198
+ return fencedCodeMaybeClose(code);
122199
+ }
122200
+ // Skip leading indentation before the closing-fence check: an indented fence
122201
+ // (the norm in a component body) closes on an equally-indented line, else the
122202
+ // closer is never matched and scanning runs to EOF (CX-3704).
122203
+ function fencedCodeMaybeClose(code) {
122204
+ if (markdownSpace(code)) {
122205
+ effects.consume(code);
122206
+ return fencedCodeMaybeClose;
122207
+ }
121877
122208
  // Check for closing fence
121878
122209
  if (code === fenceChar) {
121879
122210
  fenceCloseLength = 1;
@@ -122057,7 +122388,7 @@ function createTokenize(mode) {
122057
122388
  }
122058
122389
  // ── Body continuation (line endings) ───────────────────────────────────
122059
122390
  function bodyContinuationStart(code) {
122060
- return effects.check(syntax_nonLazyContinuationStart, bodyContinuationNonLazy, continuationAfter)(code);
122391
+ return effects.check(mdx_component_syntax_nonLazyContinuationStart, bodyContinuationNonLazy, continuationAfter)(code);
122061
122392
  }
122062
122393
  function bodyContinuationNonLazy(code) {
122063
122394
  sawLineEnding = true;
@@ -122089,6 +122420,13 @@ function createTokenize(mode) {
122089
122420
  }
122090
122421
  // Dispatch a non-blank continuation line: fenced code at line start, else body.
122091
122422
  function bodyLineStart(code) {
122423
+ // Skip leading indentation while staying "at line start" so an indented
122424
+ // fence is still recognized. Otherwise the first space clears `atLineStart`
122425
+ // and its content — including any unbalanced `{` — stays live text (CX-3704).
122426
+ if (atLineStart && markdownSpace(code)) {
122427
+ effects.consume(code);
122428
+ return bodyLineStart;
122429
+ }
122092
122430
  if (atLineStart && code === codes.tilde)
122093
122431
  return bodyAfterLineStart(code);
122094
122432
  if (atLineStart && code === codes.graveAccent) {
@@ -122139,7 +122477,7 @@ function createTokenize(mode) {
122139
122477
  }
122140
122478
  };
122141
122479
  }
122142
- function syntax_tokenizeNonLazyContinuationStart(effects, ok, nok) {
122480
+ function mdx_component_syntax_tokenizeNonLazyContinuationStart(effects, ok, nok) {
122143
122481
  // eslint-disable-next-line @typescript-eslint/no-this-alias
122144
122482
  const self = this;
122145
122483
  return start;
@@ -122247,9 +122585,20 @@ function mdxComponent() {
122247
122585
 
122248
122586
 
122249
122587
 
122588
+
122589
+
122590
+
122591
+
122592
+
122593
+
122250
122594
  const buildInlineMdProcessor = (safeMode) => {
122251
- const micromarkExts = [mdxComponent(), syntax_gemoji(), legacyVariable(), magicBlock()];
122595
+ // `jsxTable` must be present so a `<Table>`/`<table>` nested in a component
122596
+ // body (e.g. a `<Callout>`) is captured as one html node. Without it, blank
122597
+ // lines between rows let CommonMark HTML block type 6 fragment the table and
122598
+ // its rows spill out as text / indented code blocks (CX-3705).
122599
+ const micromarkExts = [jsxTable(), mdxComponent(), syntax_gemoji(), legacyVariable(), magicBlock()];
122252
122600
  const fromMarkdownExts = [
122601
+ jsxTableFromMarkdown(),
122253
122602
  mdxComponentFromMarkdown(),
122254
122603
  gemojiFromMarkdown(),
122255
122604
  legacyVariableFromMarkdown(),
@@ -122309,6 +122658,65 @@ const toMdxJsxTextElement = (name, attributes, children, position) => ({
122309
122658
  children,
122310
122659
  ...(position ? { position } : {}),
122311
122660
  });
122661
+ // Raw-body tags (pre/script/style/textarea) must stay byte-exact; table
122662
+ // structure (`table` + `tableTags`) is owned by `mdxishTables` and figures by
122663
+ // `mdxishJsxToMdast`, both of which run later on raw html nodes.
122664
+ const NON_PROMOTABLE_PLAIN_TAGS = new Set([...htmlRawNames, ...tableTags, 'table', 'figure', 'figcaption']);
122665
+ const NESTED_TABLE_RE = /<table[\s>]/i;
122666
+ const isMarkdownPromotableHtmlTag = (tag) => !NON_PROMOTABLE_PLAIN_TAGS.has(tag);
122667
+ // Expression nodes count as plain so `<div>{1+1}</div>` keeps its current
122668
+ // literal-brace behavior; variables/glossary already resolve inside raw html.
122669
+ const PLAIN_CONTENT_TYPES = new Set([
122670
+ 'paragraph',
122671
+ 'text',
122672
+ 'html',
122673
+ 'mdxTextExpression',
122674
+ 'mdxFlowExpression',
122675
+ NodeTypes.variable,
122676
+ NodeTypes.glossary,
122677
+ ]);
122678
+ // Promoting plain HTML is only worth bypassing rehype-raw's parse5 pass when
122679
+ // the body parses into an actual markdown construct.
122680
+ const containsMarkdownConstruct = (nodes) => nodes.some(node => !PLAIN_CONTENT_TYPES.has(node.type) ||
122681
+ ('children' in node && Array.isArray(node.children) && containsMarkdownConstruct(node.children)));
122682
+ /**
122683
+ * Index of the `</tag>` that balances the already-consumed opening tag (the
122684
+ * caller starts us one level deep). `lastIndexOf` mis-slices sibling same-tag
122685
+ * pairs like `<div>**a**</div><div>**b**</div>`, so we depth-match instead —
122686
+ * delegating to `walkTags` (htmlparser2) so quoted attributes (`title="</div>"`),
122687
+ * code spans, and legacy `<<VARIABLE>>` are handled for free. Returns -1 when
122688
+ * the wrapper is left open.
122689
+ */
122690
+ function findBalancedClosingTagIndex(content, tag) {
122691
+ const target = tag.toLowerCase();
122692
+ const canonicalCloserLength = tag.length + 3; // `</tag>`
122693
+ // The caller already stripped the opening tag, so re-attach one: htmlparser2
122694
+ // drops an unmatched closer, and we want it balanced. Offsets shift by the
122695
+ // prefix length.
122696
+ const prefix = `<${tag}>`;
122697
+ let depth = 0;
122698
+ let closeIndex = -1;
122699
+ walkTags(prefix + content, {
122700
+ onOpen: ({ name, isSelfClosing, isStrayCloser }) => {
122701
+ if (closeIndex >= 0 || isSelfClosing || isStrayCloser)
122702
+ return;
122703
+ if (name.toLowerCase() === target)
122704
+ depth += 1;
122705
+ },
122706
+ onClose: ({ name, start, end, implicit }) => {
122707
+ if (closeIndex >= 0 || implicit || name.toLowerCase() !== target)
122708
+ return;
122709
+ // Only canonical `</tag>` closers — the caller slices by that length, so a
122710
+ // whitespaced `</tag >` would misalign; leaving it unmatched keeps it raw.
122711
+ if (end - start !== canonicalCloserLength)
122712
+ return;
122713
+ depth -= 1;
122714
+ if (depth === 0)
122715
+ closeIndex = start - prefix.length;
122716
+ },
122717
+ });
122718
+ return closeIndex;
122719
+ }
122312
122720
 
122313
122721
  ;// ./processor/transform/mdxish/components/inline-html.ts
122314
122722
 
@@ -122440,70 +122848,168 @@ const mdxishInlineMdxComponents = () => tree => {
122440
122848
  };
122441
122849
  /* harmony default export */ const inline_mdx_blocks = (mdxishInlineMdxComponents);
122442
122850
 
122851
+ ;// ./processor/transform/mdxish/terminate-html-flow-blocks.ts
122852
+
122853
+
122854
+
122855
+ const STANDALONE_HTML_LINE_REGEX = /^(<[a-z][^<>]*>|<\/[a-z][^<>]*>)+\s*$/;
122856
+ const HTML_LINE_WITH_CONTENT_REGEX = /^<[a-z][^<>]*>.*<\/[a-z][^<>]*>(?:[^<]*)$/;
122857
+ // A line that is exactly one lowercase opening (or self-closing) tag — the shape
122858
+ // that opens a CommonMark type-6/7 HTML block and swallows the lines below it.
122859
+ const SINGLE_OPENING_TAG_REGEX = /^<[a-z][^<>]*>$/;
122860
+ // Tags whose contents must be preserved as is, inserting a blank line after the
122861
+ // opener corrupts the payload.
122862
+ // htmlRawNames here refer to <pre>, <textarea>, <script>, <style>
122863
+ const RAW_CONTENT_TAGS = [...htmlRawNames, ...HTML_TABLE_STRUCTURE_TAGS];
122864
+ // The `(?=[\s/>])` lookahead avoids false matches on lookalike names like `<script-foo>`.
122865
+ const RAW_CONTENT_TAG_MATCHERS = RAW_CONTENT_TAGS.map(tag => ({
122866
+ open: new RegExp(`<${tag}(?=[\\s/>])[^>]*?(?<!/)>`, 'gi'),
122867
+ close: new RegExp(`</${tag}(?=[\\s>])[^>]*>`, 'gi'),
122868
+ }));
122869
+ function isLineHtml(line) {
122870
+ return STANDALONE_HTML_LINE_REGEX.test(line) || HTML_LINE_WITH_CONTENT_REGEX.test(line);
122871
+ }
122872
+ // Per-tag {opens, closes} counts of RAW_CONTENT_TAGS on one line — feeds both the
122873
+ // per-line net-open check and the cumulative still-open depth tracking.
122874
+ function countRawContentTags(line) {
122875
+ return RAW_CONTENT_TAG_MATCHERS.map(({ open, close }) => ({
122876
+ opens: (line.match(open) ?? []).length,
122877
+ closes: (line.match(close) ?? []).length,
122878
+ }));
122879
+ }
122880
+ // Indentation width in columns, counting a tab as 4 per CommonMark.
122881
+ function indentWidth(line) {
122882
+ const leading = line.match(/^[ \t]*/)?.[0] ?? '';
122883
+ return leading.replace(/\t/g, ' ').length;
122884
+ }
122885
+ /**
122886
+ * Decides whether a blank line belongs between `line` and `next` so the HTML
122887
+ * flow block opened on `line` terminates and `next` parses as markdown.
122888
+ */
122889
+ function shouldTerminateBetween(line, next, { insideRawContent, lineLeavesRawOpen }) {
122890
+ if (next.trim().length === 0)
122891
+ return false;
122892
+ const currentIndent = indentWidth(line);
122893
+ const nextIndent = indentWidth(next);
122894
+ // 4+ columns is CommonMark indented-code territory: an inserted blank line
122895
+ // would turn the next line into a code block instead of freeing it (#1344).
122896
+ if (currentIndent > 3 || nextIndent > 3)
122897
+ return false;
122898
+ const currentTrimmed = line.trim();
122899
+ const nextTrimmed = next.trim();
122900
+ if (!isLineHtml(currentTrimmed) || isLineHtml(nextTrimmed) || lineLeavesRawOpen) {
122901
+ return false;
122902
+ }
122903
+ // Column-0 pairs keep the original (pre-relaxation) rule, deliberately ignoring
122904
+ // `insideRawContent`: one unclosed <pre>/<table> typo would otherwise suppress
122905
+ // termination for the whole rest of the document.
122906
+ if (currentIndent === 0 && nextIndent === 0)
122907
+ return true;
122908
+ // Indented shapes (either line at 1–3 columns) are stricter: only a lone opening
122909
+ // tag followed by markdown. A next line opening a tag or expression must stay
122910
+ // glued for the mdxComponent tokenizer; raw-content payloads stay byte-exact.
122911
+ return (!insideRawContent &&
122912
+ SINGLE_OPENING_TAG_REGEX.test(currentTrimmed) &&
122913
+ !nextTrimmed.startsWith('<') &&
122914
+ !nextTrimmed.startsWith('{'));
122915
+ }
122916
+ /**
122917
+ * CommonMark HTML blocks (types 6/7) end only at a blank line, so markdown on the
122918
+ * next line is swallowed as HTML (`## heading` after `<div>` renders as literal text).
122919
+ * Inserts the missing blank line; the gating rules live in `shouldTerminateBetween`.
122920
+ *
122921
+ * @link https://spec.commonmark.org/0.29/#html-blocks
122922
+ */
122923
+ function terminateHtmlFlowBlocks(content) {
122924
+ const { protectedContent, protectedCode } = protectCodeBlocks(content);
122925
+ const lines = protectedContent.split('\n');
122926
+ const result = [];
122927
+ // Per-tag count of still-open raw-content elements at the current boundary.
122928
+ const rawContentDepths = RAW_CONTENT_TAG_MATCHERS.map(() => 0);
122929
+ for (let i = 0; i < lines.length; i += 1) {
122930
+ const line = lines[i];
122931
+ result.push(line);
122932
+ const tagCounts = countRawContentTags(line);
122933
+ tagCounts.forEach(({ opens, closes }, tagIndex) => {
122934
+ rawContentDepths[tagIndex] = Math.max(0, rawContentDepths[tagIndex] + opens - closes);
122935
+ });
122936
+ const next = lines[i + 1];
122937
+ const rawContentFacts = {
122938
+ insideRawContent: rawContentDepths.some(depth => depth > 0),
122939
+ lineLeavesRawOpen: tagCounts.some(({ opens, closes }) => opens > closes),
122940
+ };
122941
+ if (next !== undefined && shouldTerminateBetween(line, next, rawContentFacts)) {
122942
+ result.push('');
122943
+ }
122944
+ }
122945
+ return restoreCodeBlocks(result.join('\n'), protectedCode);
122946
+ }
122947
+
122443
122948
  ;// ./processor/transform/mdxish/components/mdx-blocks.ts
122444
122949
 
122445
122950
 
122446
122951
 
122447
122952
 
122448
122953
 
122449
- /** Matches a JSX attribute expression (e.g. `key={i}`) anywhere in a string. */
122954
+
122955
+
122956
+ // Matches a JSX attribute expression (e.g. `key={i}`) anywhere in a string. */
122450
122957
  const NESTED_ATTR_EXPRESSION_RE = /[\w-]+\s*=\s*\{/;
122451
- /**
122452
- * Reduce leading whitespace on all lines just enough to prevent
122453
- * remark from treating indented content as code blocks (4+ spaces).
122454
- * Preserves relative indentation so whitespace text nodes are
122455
- * maintained in the HAST output.
122958
+ // Name shape mirrors `componentTagPattern`; the lookbehind skips the inner tag
122959
+ // of a legacy `<<VARIABLE>>`.
122960
+ const NESTED_COMPONENT_TAG_RE = /(?<!<)<([A-Z][A-Za-z0-9_]*)[\s/>]/g;
122961
+ // Excludes tags with dedicated transformers (`Table`, `HTMLBlock`, inline
122962
+ // components), which expect their wrapper to stay raw.
122963
+ const hasNestedGenericComponentTag = (content) => [...content.matchAll(NESTED_COMPONENT_TAG_RE)].some(match => !GENERIC_MDX_COMPONENT_EXCLUDED_TAGS.has(match[1]));
122964
+ /**
122965
+ * Strip the shared leading indentation from a component body so readability indentation
122966
+ * isn't parsed as indented code (4+ spaces), e.g. ` <p>` / ` text` -> `<p>` / ` text`.
122967
+ * Relative indentation is kept, so content genuinely 4+ columns deeper stays code. We
122968
+ * only strip when a line actually reaches 4 columns; otherwise the body is left as-is so
122969
+ * its leading whitespace survives as text nodes (mixed component + HTML content needs it).
122456
122970
  */
122457
122971
  function safeDeindent(text) {
122458
122972
  const lines = text.split('\n');
122459
122973
  const nonEmptyLines = lines.filter(line => line.trim().length > 0);
122460
122974
  if (nonEmptyLines.length === 0)
122461
122975
  return text;
122462
- const minIndent = Math.min(...nonEmptyLines.map(line => {
122463
- const match = line.match(/^(\s*)/);
122464
- return match ? match[1].length : 0;
122465
- }));
122466
- // Only strip enough indent to keep all lines below the 4-space code threshold
122467
- const stripAmount = Math.max(0, minIndent - 3);
122976
+ // Indent counts characters (tab = 1), unlike indentWidth's CommonMark columns
122977
+ // (tab = 4) in terminate-html-flow-blocks.
122978
+ const indents = nonEmptyLines.map(line => line.match(/^(\s*)/)?.[1].length ?? 0);
122979
+ const minIndent = Math.min(...indents);
122980
+ const maxIndent = Math.max(...indents);
122981
+ const stripAmount = maxIndent > 3 ? minIndent : 0;
122468
122982
  if (stripAmount === 0)
122469
122983
  return text;
122470
122984
  return lines.map(line => line.slice(stripAmount)).join('\n');
122471
122985
  }
122472
122986
  /**
122473
- * Parse markdown content into mdast children nodes.
122474
- * Dedents the content first to prevent indented component content
122475
- * (from nested components) from being treated as code blocks.
122987
+ * Parse component-body markdown into mdast children. Dedenting shifts columns and
122988
+ * stales the top-level `terminateHtmlFlowBlocks` decisions, so that one preprocessor
122989
+ * re-runs here; other column-anchored fixups (compact headings, tables) do not.
122476
122990
  */
122477
122991
  const parseMdChildren = (value, safeMode) => {
122478
- const parsed = getInlineMdProcessor({ safeMode }).parse(safeDeindent(value).trim());
122992
+ const parsed = getInlineMdProcessor({ safeMode }).parse(terminateHtmlFlowBlocks(safeDeindent(value).trim()));
122479
122993
  return parsed.children || [];
122480
122994
  };
122481
- /**
122482
- * Parse substring content of a node and update the parent's children to include the new nodes.
122483
- */
122995
+ // Parses trailing content into sibling nodes and re-queues the parent so any
122996
+ // components among them get processed.
122484
122997
  const parseSibling = (stack, parent, index, sibling, safeMode) => {
122485
122998
  const siblingNodes = parseMdChildren(sibling, safeMode);
122486
- // The new sibling nodes might contain new components to be processed
122487
122999
  if (siblingNodes.length > 0) {
122488
123000
  parent.children.splice(index + 1, 0, ...siblingNodes);
122489
123001
  stack.push(parent);
122490
123002
  }
122491
123003
  };
122492
- /**
122493
- * Build a position ending at `consumedLength` into the html node's value, so the
122494
- * component doesn't claim trailing content the tokenizer swallowed into one node.
122495
- */
123004
+ // Ends the position at `consumedLength` so the component doesn't claim trailing
123005
+ // content the tokenizer swallowed into the same html node.
122496
123006
  const positionEndingAtConsumed = (nodePosition, value, consumedLength) => {
122497
123007
  if (!nodePosition?.start)
122498
123008
  return nodePosition;
122499
123009
  return { start: nodePosition.start, end: pointAfter(nodePosition.start, value.slice(0, consumedLength)) };
122500
123010
  };
122501
- /**
122502
- * Build a position ending right after the last occurrence of `closingTag` within
122503
- * this node's span in the original source. Used in the trailing-content path so
122504
- * the offset is computed against the real source bytes (including blockquote/list
122505
- * prefixes that were stripped from the html node's value).
122506
- */
123011
+ // Like `positionEndingAtConsumed`, but measures against the original source so
123012
+ // blockquote/list prefixes stripped from the html node's value are counted.
122507
123013
  const positionEndingAtClosingTagInSource = (nodePosition, closingTag, source) => {
122508
123014
  if (!nodePosition?.start || !nodePosition.end)
122509
123015
  return nodePosition;
@@ -122514,9 +123020,6 @@ const positionEndingAtClosingTagInSource = (nodePosition, closingTag, source) =>
122514
123020
  const consumed = nodeSource.slice(0, closingTagOffset + closingTag.length);
122515
123021
  return { start: nodePosition.start, end: pointAfter(nodePosition.start, consumed) };
122516
123022
  };
122517
- /**
122518
- * Create an MdxJsxFlowElement node from component data.
122519
- */
122520
123023
  const createComponentNode = ({ tag, attributes, children, startPosition, endPosition }) => ({
122521
123024
  type: 'mdxJsxFlowElement',
122522
123025
  name: tag,
@@ -122538,6 +123041,10 @@ const substituteNodeWithMdxNode = (parent, index, mdxNode) => {
122538
123041
  * This transformer identifies these patterns and converts them to proper MDX JSX elements so they
122539
123042
  * can be accurately recognized and rendered later with their component definition code.
122540
123043
  *
123044
+ * Note: The main goal is to promote PascalCase tags to MDX elements, but we want to promote
123045
+ * normal HTML to MDX elements in some cases so they get the full custom parsing behavior.
123046
+ * E.g. tags with JSX expressions, nested components, etc.
123047
+ *
122541
123048
  * The mdx-component micromark tokenizer ensures that multi-line components are captured
122542
123049
  * as single HTML nodes, so this transformer only needs to handle two cases:
122543
123050
  *
@@ -122569,8 +123076,7 @@ const mdxishMdxComponentBlocks = (opts = {}) => (tree, file) => {
122569
123076
  if ('children' in node && Array.isArray(node.children)) {
122570
123077
  stack.push(node);
122571
123078
  }
122572
- // Only visit HTML nodes with an actual html tag,
122573
- // which means a potential unparsed MDX component
123079
+ // Only html nodes can be an unparsed MDX component.
122574
123080
  const value = node.value;
122575
123081
  if (node.type !== 'html' || typeof value !== 'string')
122576
123082
  return;
@@ -122579,32 +123085,34 @@ const mdxishMdxComponentBlocks = (opts = {}) => (tree, file) => {
122579
123085
  if (!parsed)
122580
123086
  return;
122581
123087
  const { tag, attributes, selfClosing, contentAfterTag = '' } = parsed;
122582
- // Offset of `trimmed` within the (possibly whitespace-padded) html node value,
122583
- // so consumed-length math maps back onto the node's real source offsets.
123088
+ // Offsets so consumed-length math maps back onto the node's real source.
122584
123089
  const leadingWhitespace = value.length - value.trimStart().length;
122585
- // Index right after the opening tag's `>` within `trimmed`.
122586
123090
  const openingTagEnd = trimmed.length - contentAfterTag.length;
122587
- // Skip tags that have dedicated transformers
122588
123091
  if (GENERIC_MDX_COMPONENT_EXCLUDED_TAGS.has(tag))
122589
- return;
123092
+ return; // owned by dedicated transformers
122590
123093
  const isPascal = isPascalCase(tag);
122591
- // Lowercase inline tags (inside a paragraph) with `{…}` attributes are
122592
- // promoted to `mdxJsxTextElement` by mdxishInlineComponentBlocks. Skip
122593
- // them here so they stay as html for that pass; PascalCase components
122594
- // keep going through this transformer (they stay flow-level even when
122595
- // inline, which is how ReadMe's custom components are modeled).
123094
+ // ==== SPECIAL CASES TO PROMOTE NORMAL HTML TO MDX ELEMENTS ====
123095
+ // Lowercase inline tags with `{…}` attributes belong to
123096
+ // mdxishInlineComponentBlocks; leave them as html for that pass. PascalCase
123097
+ // components stay flow-level even when inline (ReadMe's component model).
122596
123098
  if (!isPascal && parent.type === 'paragraph')
122597
123099
  return;
122598
- // Lowercase HTML tags are eligible when they (or a descendant tag in their
122599
- // content, e.g. a `.map()` body returning JSX with `key={i}`) carry a
122600
- // JSX-expression attribute. Without this, a wrapper `<div>` with only plain
122601
- // attributes (or none) swallows its whole nested block including any
122602
- // `style={{...}}`/`.map()` JSX inside it as literal HTML text, which
122603
- // rehype-raw's parse5 pass then garbles (it has no notion of JS expressions).
122604
- // Plain HTML with no expressions anywhere stays as an html node so
122605
- // rehype-raw handles it as normal.
122606
- const hasNestedExpressionAttr = !selfClosing && NESTED_ATTR_EXPRESSION_RE.test(contentAfterTag);
122607
- if (!isPascal && !hasExpressionAttr(attributes) && !hasNestedExpressionAttr)
123100
+ // A lowercase wrapper is only promoted when it (or a descendant) carries a
123101
+ // JSX expression or nests a component; otherwise it would swallow that inner
123102
+ // JSX/component as literal text that rehype-raw's parse5 pass can't handle.
123103
+ // Table-structural wrappers are excluded from both`mdxishTables` re-parses
123104
+ // those, so a `{}` in a cell (e.g. `<code>--depth={n}</code>`) must not
123105
+ // accidentally promote the table to an MDX element prematurely.
123106
+ const isTableStructuralTag = tag === 'table' || tableTags.has(tag);
123107
+ const hasNestedExpressionAttr = !selfClosing && !isTableStructuralTag && NESTED_ATTR_EXPRESSION_RE.test(contentAfterTag);
123108
+ const hasNestedComponentTag = !selfClosing && !isTableStructuralTag && hasNestedGenericComponentTag(contentAfterTag);
123109
+ // Promotion: By default commonmark doesn't parse markdown in single line HTML tags (e.g. <div>**bold**</div>)
123110
+ // To support that, we try to promote them to MDX elements so the markdown gets parsed
123111
+ const isPlainLowercaseHtml = !isPascal && !hasExpressionAttr(attributes) && !hasNestedExpressionAttr && !hasNestedComponentTag;
123112
+ const plainClosingTagIndex = isPlainLowercaseHtml && !selfClosing && isMarkdownPromotableHtmlTag(tag) && !NESTED_TABLE_RE.test(contentAfterTag)
123113
+ ? findBalancedClosingTagIndex(contentAfterTag, tag)
123114
+ : -1;
123115
+ if (isPlainLowercaseHtml && plainClosingTagIndex < 0)
122608
123116
  return;
122609
123117
  const closingTagStr = `</${tag}>`;
122610
123118
  // Case 1: Self-closing tag
@@ -122618,7 +123126,6 @@ const mdxishMdxComponentBlocks = (opts = {}) => (tree, file) => {
122618
123126
  endPosition: positionEndingAtConsumed(node.position, value, leadingWhitespace + openingTagEnd),
122619
123127
  });
122620
123128
  substituteNodeWithMdxNode(parent, index, componentNode);
122621
- // Check and parse if there's relevant content after the current closing tag
122622
123129
  const remainingContent = contentAfterTag.trim();
122623
123130
  if (remainingContent) {
122624
123131
  parseSibling(stack, parent, index, remainingContent, safeMode);
@@ -122626,18 +123133,27 @@ const mdxishMdxComponentBlocks = (opts = {}) => (tree, file) => {
122626
123133
  return;
122627
123134
  }
122628
123135
  // Case 2: Self-contained block (closing tag in content)
122629
- if (contentAfterTag.includes(closingTagStr)) {
122630
- // Find the first closing tag
122631
- const closingTagIndex = contentAfterTag.lastIndexOf(closingTagStr);
122632
- // Pass raw (untrimmed) content so dedent in parseMdChildren can
122633
- // normalize indentation before trimming
123136
+ const closingTagIndex = isPlainLowercaseHtml ? plainClosingTagIndex : contentAfterTag.lastIndexOf(closingTagStr);
123137
+ if (closingTagIndex >= 0) {
123138
+ // Untrimmed so parseMdChildren can dedent before trimming.
122634
123139
  const componentInnerContent = contentAfterTag.substring(0, closingTagIndex);
122635
123140
  const contentAfterClose = contentAfterTag.substring(closingTagIndex + closingTagStr.length).trim();
122636
- let parsedChildren = componentInnerContent.trim()
122637
- ? parseMdChildren(componentInnerContent, safeMode)
122638
- : [];
122639
- // Lowercase HTML tags are usually inline (e.g. <a>, <span>). Remark wraps
122640
- // bare text in a paragraph; unwrap when there's exactly one paragraph so
123141
+ let parsedChildren = [];
123142
+ if (componentInnerContent.trim()) {
123143
+ try {
123144
+ parsedChildren = parseMdChildren(componentInnerContent, safeMode);
123145
+ }
123146
+ catch (error) {
123147
+ // Plain HTML bodies can hold anything (e.g. stray braces the strict
123148
+ // expression parser rejects) — keep the node raw instead of throwing.
123149
+ if (isPlainLowercaseHtml)
123150
+ return;
123151
+ throw error;
123152
+ }
123153
+ }
123154
+ if (isPlainLowercaseHtml && !containsMarkdownConstruct(parsedChildren))
123155
+ return;
123156
+ // Lowercase tags are usually inline; unwrap a sole paragraph so their
122641
123157
  // phrasing content isn't spuriously block-wrapped.
122642
123158
  if (!isPascal && parsedChildren.length === 1 && parsedChildren[0].type === 'paragraph') {
122643
123159
  parsedChildren = parsedChildren[0].children;
@@ -122647,12 +123163,9 @@ const mdxishMdxComponentBlocks = (opts = {}) => (tree, file) => {
122647
123163
  attributes,
122648
123164
  children: parsedChildren,
122649
123165
  startPosition: node.position,
122650
- // When trailing content follows the closing tag, compute the end position precisely
122651
- // within the html node's value so the component doesn't claim that content.
122652
- // Prefer source-based positioning when the original source is available: the html
122653
- // node's value has '> '/space prefixes stripped for blockquotes/list items, so
122654
- // positionEndingAtConsumed would undercount source offsets. When the entire node
122655
- // is consumed, use the original node position directly.
123166
+ // With trailing content, end precisely at the closing tag. Prefer source
123167
+ // offsets when available (the node's value strips blockquote/list
123168
+ // prefixes); otherwise fall back to the whole node position.
122656
123169
  endPosition: contentAfterClose
122657
123170
  ? source
122658
123171
  ? positionEndingAtClosingTagInSource(node.position, closingTagStr, source)
@@ -122660,17 +123173,16 @@ const mdxishMdxComponentBlocks = (opts = {}) => (tree, file) => {
122660
123173
  : node.position,
122661
123174
  });
122662
123175
  substituteNodeWithMdxNode(parent, index, componentNode);
122663
- // After the closing tag, there might be more content to be processed
123176
+ // Re-queue whichever side may hold further components.
122664
123177
  if (contentAfterClose) {
122665
123178
  parseSibling(stack, parent, index, contentAfterClose, safeMode);
122666
123179
  }
122667
123180
  else if (componentNode.children.length > 0) {
122668
- // The content inside the component block might contain new components to be processed
122669
123181
  stack.push(componentNode);
122670
123182
  }
122671
123183
  }
122672
123184
  };
122673
- // Process the nodes with the components depth-first to maintain the correct order of the nodes
123185
+ // Depth-first so nodes keep their source order.
122674
123186
  while (stack.length) {
122675
123187
  const parent = stack.pop();
122676
123188
  if (parent?.children) {
@@ -124205,9 +124717,6 @@ const magicBlockTransformer = (options = {}) => tree => {
124205
124717
  // single-line `<div>…</div>`). CommonMark slurps the whole div as one `html`
124206
124718
  // node, so the tokenizer never sees the HTMLBlock — we recover it here.
124207
124719
  const RAW_HTML_BLOCK_RE = /<HTMLBlock\b([^>]*)>\s*\{\s*`((?:[^`\\]|\\.)*)`\s*\}\s*<\/HTMLBlock>/g;
124208
- // Opening `<HTMLBlock …>` as its own `html` node — produced inside a paragraph
124209
- // when an HTMLBlock appears inline alongside text.
124210
- const HTML_BLOCK_OPEN_RE = /^<HTMLBlock\b([^>]*)>$/;
124211
124720
  /**
124212
124721
  * Builds the canonical `html-block` MDAST node the renderer expects.
124213
124722
  */
@@ -124288,13 +124797,14 @@ const splitRawHtmlBlocks = (node) => {
124288
124797
  /**
124289
124798
  * Converts every `<HTMLBlock>` shape that survives parsing into the canonical
124290
124799
  * `html-block` MDAST node, reading the body from the tokenizer's template-literal
124291
- * expression. Three shapes occur:
124800
+ * expression. Two shapes occur:
124292
124801
  *
124293
- * 1. JSX element (`mdxJsxFlowElement`/`mdxJsxTextElement`) — multiline/block
124294
- * context and table cells (after their remarkMdx re-parse).
124295
- * 2. Raw `html` blob (`splitRawHtmlBlocks`) single-line top-level, or nested
124296
- * in raw HTML like an inline `<div>`.
124297
- * 3. Inline-in-paragraph split into `html` + expression + `html` siblings.
124802
+ * 1. JSX element (`mdxJsxFlowElement`/`mdxJsxTextElement`) — table cells, after
124803
+ * their remarkMdx re-parse (that re-parse runs without the htmlBlockComponent
124804
+ * tokenizer, so `<HTMLBlock>` arrives as JSX rather than an opaque `html` node).
124805
+ * 2. Raw `html` blob (`splitRawHtmlBlocks`) the htmlBlockComponent tokenizer's
124806
+ * opaque output (top-level and inline), or an `<HTMLBlock>` embedded in a
124807
+ * larger raw-HTML node like an inline `<div>` that the tokenizer never saw.
124298
124808
  *
124299
124809
  * Runs *after* `mdxishTables` so table cells are re-parsed first;
124300
124810
  * `mdxishTables` recognizes the still-JSX `<HTMLBlock>` element when deciding to
@@ -124319,36 +124829,6 @@ const mdxishHtmlBlocks = () => tree => {
124319
124829
  if (replacement)
124320
124830
  parent.children.splice(index, 1, ...replacement);
124321
124831
  });
124322
- // Shape 3: inline within a paragraph — `<HTMLBlock>` open/close arrive as
124323
- // separate `html` siblings with the template-literal expression between them.
124324
- visit(tree, 'paragraph', (paragraph) => {
124325
- // An html-block is block content, so it isn't a valid PhrasingContent child;
124326
- // widen to RootContent (which HTMLBlock belongs to) for the in-place splice.
124327
- const children = paragraph.children;
124328
- for (let i = 0; i < children.length; i += 1) {
124329
- const open = children[i];
124330
- const openMatch = open.type === 'html' ? open.value.match(HTML_BLOCK_OPEN_RE) : null;
124331
- if (!openMatch)
124332
- continue; // eslint-disable-line no-continue
124333
- const closeIdx = children.findIndex((child, j) => j > i && child.type === 'html' && child.value === '</HTMLBlock>');
124334
- if (closeIdx === -1)
124335
- continue; // eslint-disable-line no-continue
124336
- const body = children
124337
- .slice(i + 1, closeIdx)
124338
- .map(child => {
124339
- if (child.type === 'mdxTextExpression' || child.type === 'mdxFlowExpression') {
124340
- return extractTemplateLiteral(child.value);
124341
- }
124342
- // Preserve raw text from any other phrasing sibling (e.g. stray
124343
- // whitespace or content the tokenizer didn't claim) so it isn't
124344
- // silently dropped from the html payload.
124345
- return 'value' in child && typeof child.value === 'string' ? child.value : '';
124346
- })
124347
- .join('');
124348
- const openingTagIndent = (open.position?.start.column ?? 1) - 1;
124349
- children.splice(i, closeIdx - i + 1, htmlBlockFromRaw(openMatch[1], body, open.position, openingTagIndent));
124350
- }
124351
- });
124352
124832
  };
124353
124833
  /* harmony default export */ const mdxish_html_blocks = (mdxishHtmlBlocks);
124354
124834
 
@@ -125048,6 +125528,29 @@ const mdxishMermaidTransformer = () => (tree) => {
125048
125528
  };
125049
125529
  /* harmony default export */ const mdxish_mermaid = (mdxishMermaidTransformer);
125050
125530
 
125531
+ ;// ./processor/transform/mdxish/normalize-closing-tag-whitespace.ts
125532
+
125533
+
125534
+ // Spaces/tabs only — newlines would let prose `<` / `>` across lines look like one tag.
125535
+ const SPACED_CLOSING_TAG_RE = /<\/[ \t]*([a-zA-Z][a-zA-Z0-9-]*)[ \t]*>/g;
125536
+ /**
125537
+ * Canonicalize spaced closing tags (`</ td >` → `</td>`) for known HTML names.
125538
+ *
125539
+ * In HTML, `</` + whitespace is a bogus comment, so `</ table >` never closes the
125540
+ * table (jsxTable misses it → empty table + pre; CX-3706). Only standard HTML tags;
125541
+ * custom components, prose (`a </ b`), and code blocks are left alone.
125542
+ *
125543
+ * @example
125544
+ * normalizeClosingTagWhitespace('</ td >') // '</td>'
125545
+ * normalizeClosingTagWhitespace('</table >') // '</table>'
125546
+ * normalizeClosingTagWhitespace('a </ b >') // unchanged
125547
+ */
125548
+ function normalizeClosingTagWhitespace(content) {
125549
+ const { protectedContent, protectedCode } = protectCodeBlocks(content);
125550
+ const result = protectedContent.replace(SPACED_CLOSING_TAG_RE, (match, tagName) => STANDARD_HTML_TAGS.has(tagName.toLowerCase()) ? `</${tagName}>` : match);
125551
+ return restoreCodeBlocks(result, protectedCode);
125552
+ }
125553
+
125051
125554
  ;// ./processor/transform/mdxish/normalize-compact-headings.ts
125052
125555
 
125053
125556
  /**
@@ -125503,278 +126006,488 @@ function normalizeTableSeparator(content) {
125503
126006
  }
125504
126007
  /* harmony default export */ const normalize_table_separator = ((/* unused pure expression or super */ null && (normalizeTableSeparator)));
125505
126008
 
125506
- ;// ./processor/transform/mdxish/terminate-html-flow-blocks.ts
125507
-
126009
+ ;// ./processor/transform/mdxish/variables-code.ts
125508
126010
 
125509
126011
 
125510
- const STANDALONE_HTML_LINE_REGEX = /^(<[a-z][^<>]*>|<\/[a-z][^<>]*>)+\s*$/;
125511
- const HTML_LINE_WITH_CONTENT_REGEX = /^<[a-z][^<>]*>.*<\/[a-z][^<>]*>(?:[^<]*)$/;
125512
- // Tags whose contents must be preserved as is, inserting a blank line after the
125513
- // opener corrupts the payload.
125514
- // htmlRawNames here refer to <pre>, <textarea>, <script>, <style>
125515
- const RAW_CONTENT_TAGS = [...htmlRawNames, ...HTML_TABLE_STRUCTURE_TAGS];
125516
- // The `(?=[\s/>])` lookahead avoids false matches on lookalike names like `<script-foo>`.
125517
- const RAW_CONTENT_TAG_MATCHERS = RAW_CONTENT_TAGS.map(tag => ({
125518
- open: new RegExp(`<${tag}(?=[\\s/>])[^>]*?(?<!/)>`, 'gi'),
125519
- close: new RegExp(`</${tag}(?=[\\s>])[^>]*>`, 'gi'),
125520
- }));
125521
- function isLineHtml(line) {
125522
- return STANDALONE_HTML_LINE_REGEX.test(line) || HTML_LINE_WITH_CONTENT_REGEX.test(line);
126012
+ // Single combined regex so that resolved values from one pattern are never re-scanned by the other.
126013
+ const COMBINED_VARIABLE_REGEX = new RegExp(`${variable_dist.VARIABLE_REGEXP}|${variable_dist.MDX_VARIABLE_REGEXP}`, 'giu');
126014
+ // Flatten variables into a single object for easy lookup
126015
+ function variables_code_flattenVariables(variables) {
126016
+ if (!variables)
126017
+ return {};
126018
+ return {
126019
+ ...Object.fromEntries((variables.defaults || []).map(d => [d.name, d.default])),
126020
+ ...variables.user,
126021
+ };
125523
126022
  }
125524
- // True if any RAW_CONTENT_TAGS opener on this line is not closed on the same line.
125525
- function hasUnclosedRawContentOpener(line) {
125526
- return RAW_CONTENT_TAG_MATCHERS.some(({ open, close }) => {
125527
- const opens = (line.match(open) ?? []).length;
125528
- const closes = (line.match(close) ?? []).length;
125529
- return opens > closes;
126023
+ function resolveCodeVariables(value, resolvedVariables) {
126024
+ return value.replace(COMBINED_VARIABLE_REGEX, (match, legacyName, mdxEscapePrefix, mdxVarName, mdxEscapeSuffix) => {
126025
+ // Legacy variable: <<...>>
126026
+ if (legacyName !== undefined) {
126027
+ if (match.startsWith('\\<<') || match.endsWith('\\>>'))
126028
+ return match;
126029
+ const name = legacyName.trim();
126030
+ if (name.startsWith('glossary:'))
126031
+ return name.toUpperCase();
126032
+ return name in resolvedVariables ? resolvedVariables[name] : name.toUpperCase();
126033
+ }
126034
+ // MDX variable: {user.*}
126035
+ if (mdxEscapePrefix || mdxEscapeSuffix)
126036
+ return match;
126037
+ if (mdxVarName in resolvedVariables)
126038
+ return resolvedVariables[mdxVarName];
126039
+ const fullPath = match.slice(1, -1);
126040
+ return fullPath.toUpperCase();
125530
126041
  });
125531
126042
  }
125532
126043
  /**
125533
- * Preprocessor to terminate HTML flow blocks.
126044
+ * A remark mdast plugin that resolves legacy variables <<...>> and MDX variables {user.*} inside code and inline code nodes
126045
+ * to their values. Uses regexes from the readme variable to search for variables in the code string.
125534
126046
  *
125535
- * In CommonMark, HTML blocks (types 6 and 7) only terminate on a blank line.
125536
- * Without one, any content on the next line is consumed as part of the HTML block
125537
- * and never parsed as its own construct. For example, a `[block:callout]` immediately
125538
- * following `<div><p></p></div>` gets swallowed into the HTML flow token.
126047
+ * This is needed because variables in code blocks and inline cannot be tokenized, and also we need to maintain the code string
126048
+ * in the code nodes. This enables engine side variable resolution in codes which improves UX
126049
+ */
126050
+ const variablesCodeResolver = ({ variables } = {}) => tree => {
126051
+ const resolvedVariables = variables_code_flattenVariables(variables);
126052
+ visit(tree, 'inlineCode', (node) => {
126053
+ if (!node.value)
126054
+ return;
126055
+ node.value = resolveCodeVariables(node.value, resolvedVariables);
126056
+ });
126057
+ visit(tree, 'code', (node) => {
126058
+ if (!node.value)
126059
+ return;
126060
+ if (node.lang === 'mermaid')
126061
+ return;
126062
+ const nextValue = resolveCodeVariables(node.value, resolvedVariables);
126063
+ node.value = nextValue;
126064
+ // Keep code-tabs/readme-components hProperties in sync with node.value
126065
+ // because renderers read `value` from hProperties.
126066
+ if (node.data?.hProperties && typeof node.data.hProperties === 'object') {
126067
+ node.data.hProperties.value = nextValue;
126068
+ }
126069
+ });
126070
+ return tree;
126071
+ };
126072
+ /* harmony default export */ const variables_code = (variablesCodeResolver);
126073
+
126074
+ ;// ./processor/transform/mdxish/variables-text.ts
126075
+
126076
+
126077
+ /**
126078
+ * Matches {user.<field>} patterns:
126079
+ * - {user.name}
126080
+ * - {user.email}
126081
+ * - {user['field']}
126082
+ * - {user["field"]}
125539
126083
  *
125540
- * @link https://spec.commonmark.org/0.29/#html-blocks
126084
+ * Captures the field name in group 1 (dot notation) or group 2 (bracket notation)
126085
+ */
126086
+ const USER_VAR_REGEX = /\{user\.(\w+)\}|\{user\[['"](\w+)['"]\]\}/g;
126087
+ function makeVariableNode(varName, rawValue) {
126088
+ return {
126089
+ type: NodeTypes.variable,
126090
+ data: {
126091
+ hName: 'Variable',
126092
+ hProperties: { name: varName },
126093
+ },
126094
+ value: rawValue,
126095
+ };
126096
+ }
126097
+ /**
126098
+ * A remark plugin that parses {user.<field>} patterns from text nodes and
126099
+ * mdx expression nodes, creating Variable nodes for runtime resolution.
125541
126100
  *
125542
- * This preprocessor inserts a blank line after standalone HTML lines when the next
125543
- * line is non-blank and not an HTML construct, ensuring micromark's HTML flow
125544
- * tokenizer terminates and subsequent content is parsed independently.
126101
+ * Handles:
126102
+ * - `text` nodes: when safeMode is true or after expression evaluation
126103
+ * - `mdxTextExpression` nodes: when mdxExpression has parsed {user.*} before evaluation
126104
+ * - `mdxFlowExpression` nodes: when {user.*} appears on its own line (e.g. inside JSX table cells)
125545
126105
  *
125546
- * Conditions:
125547
- * 1. Only non-indented lines with lowercase tag names are considered. Uppercase tags
125548
- * (e.g. `<Table>`, `<MyComponent>`) are JSX custom components and don't trigger
125549
- * CommonMark HTML blocks.
125550
- * 2. Lines inside protected blocks (e.g. fenced code) are left untouched.
125551
- * 3. Lines with an unclosed RAW_CONTENT_TAGS opener are exempted.
126106
+ * Supports any user field: name, email, email_verified, exp, iat, etc.
125552
126107
  */
125553
- function terminateHtmlFlowBlocks(content) {
125554
- const { protectedContent, protectedCode } = protectCodeBlocks(content);
125555
- const lines = protectedContent.split('\n');
125556
- const result = [];
125557
- for (let i = 0; i < lines.length; i += 1) {
125558
- result.push(lines[i]);
125559
- // Skip blank & indented lines
125560
- if (i >= lines.length - 1 || lines[i + 1].trim().length === 0 || lines[i + 1].startsWith(' ') || lines[i + 1].startsWith('\t')) {
125561
- // eslint-disable-next-line no-continue
125562
- continue;
126108
+ function visitExpressionNode(node, index, parent) {
126109
+ if (index === undefined || !parent)
126110
+ return;
126111
+ const wrapped = `{${(node.value ?? '').trim()}}`;
126112
+ const matches = [...wrapped.matchAll(USER_VAR_REGEX)];
126113
+ if (matches.length !== 1)
126114
+ return;
126115
+ const varName = matches[0][1] || matches[0][2];
126116
+ parent.children.splice(index, 1, makeVariableNode(varName, wrapped));
126117
+ }
126118
+ const variablesTextTransformer = () => tree => {
126119
+ visit(tree, 'mdxTextExpression', (node, index, parent) => {
126120
+ visitExpressionNode(node, index, parent);
126121
+ });
126122
+ visit(tree, 'mdxFlowExpression', (node, index, parent) => {
126123
+ visitExpressionNode(node, index, parent);
126124
+ });
126125
+ visit(tree, 'text', (node, index, parent) => {
126126
+ if (index === undefined || !parent)
126127
+ return;
126128
+ // Skip if inside inline code
126129
+ if (parent.type === 'inlineCode')
126130
+ return;
126131
+ const text = node.value;
126132
+ if (typeof text !== 'string' || !text.trim())
126133
+ return;
126134
+ if (!text.includes('{user.') && !text.includes('{user['))
126135
+ return;
126136
+ const matches = [...text.matchAll(USER_VAR_REGEX)];
126137
+ if (matches.length === 0)
126138
+ return;
126139
+ const parts = [];
126140
+ let lastIndex = 0;
126141
+ matches.forEach(match => {
126142
+ const matchIndex = match.index ?? 0;
126143
+ // Add text before the match
126144
+ if (matchIndex > lastIndex) {
126145
+ parts.push({ type: 'text', value: text.slice(lastIndex, matchIndex) });
126146
+ }
126147
+ // Extract variable name from either capture group (dot or bracket notation)
126148
+ const varName = match[1] || match[2];
126149
+ parts.push(makeVariableNode(varName, match[0]));
126150
+ lastIndex = matchIndex + match[0].length;
126151
+ });
126152
+ // Add remaining text after last match
126153
+ if (lastIndex < text.length) {
126154
+ parts.push({ type: 'text', value: text.slice(lastIndex) });
125563
126155
  }
125564
- const isCurrentLineHtml = isLineHtml(lines[i]);
125565
- const isNextLineHtml = isLineHtml(lines[i + 1]);
125566
- if (isCurrentLineHtml && !isNextLineHtml && !hasUnclosedRawContentOpener(lines[i])) {
125567
- result.push('');
126156
+ // Replace node with parts
126157
+ if (parts.length > 1 || (parts.length === 1 && parts[0].type !== 'text')) {
126158
+ parent.children.splice(index, 1, ...parts);
126159
+ }
126160
+ });
126161
+ return tree;
126162
+ };
126163
+ /* harmony default export */ const variables_text = (variablesTextTransformer);
126164
+
126165
+ ;// ./lib/mdast-util/html-block-component/index.ts
126166
+ const html_block_component_contextMap = new WeakMap();
126167
+ function findHtmlBlockComponentToken() {
126168
+ const events = this.tokenStack;
126169
+ for (let i = events.length - 1; i >= 0; i -= 1) {
126170
+ if (events[i][0].type === 'htmlBlockComponent')
126171
+ return events[i][0];
126172
+ }
126173
+ return undefined;
126174
+ }
126175
+ function enterHtmlBlockComponent(token) {
126176
+ html_block_component_contextMap.set(token, { chunks: [], lastEndLine: token.start.line });
126177
+ this.enter({ type: 'html', value: '' }, token);
126178
+ }
126179
+ function exitHtmlBlockComponentData(token) {
126180
+ const componentToken = findHtmlBlockComponentToken.call(this);
126181
+ if (!componentToken)
126182
+ return;
126183
+ const ctx = html_block_component_contextMap.get(componentToken);
126184
+ if (ctx) {
126185
+ const gap = token.start.line - ctx.lastEndLine;
126186
+ if (ctx.chunks.length > 0 && gap > 0) {
126187
+ ctx.chunks.push('\n'.repeat(gap));
126188
+ }
126189
+ ctx.chunks.push(this.sliceSerialize(token));
126190
+ ctx.lastEndLine = token.end.line;
126191
+ }
126192
+ }
126193
+ function exitHtmlBlockComponent(token) {
126194
+ const ctx = html_block_component_contextMap.get(token);
126195
+ const node = this.stack[this.stack.length - 1];
126196
+ if (ctx) {
126197
+ node.value = ctx.chunks.join('');
126198
+ html_block_component_contextMap.delete(token);
126199
+ }
126200
+ this.exit(token);
126201
+ }
126202
+ function htmlBlockComponentFromMarkdown() {
126203
+ return {
126204
+ enter: {
126205
+ htmlBlockComponent: enterHtmlBlockComponent,
126206
+ },
126207
+ exit: {
126208
+ htmlBlockComponentData: exitHtmlBlockComponentData,
126209
+ htmlBlockComponent: exitHtmlBlockComponent,
126210
+ },
126211
+ };
126212
+ }
126213
+
126214
+ ;// ./lib/micromark/html-block-component/syntax.ts
126215
+
126216
+
126217
+ const TAG_SUFFIX = [
126218
+ codes.uppercaseT,
126219
+ codes.uppercaseM,
126220
+ codes.uppercaseL,
126221
+ codes.uppercaseB,
126222
+ codes.lowercaseL,
126223
+ codes.lowercaseO,
126224
+ codes.lowercaseC,
126225
+ codes.lowercaseK,
126226
+ ];
126227
+ // ---------------------------------------------------------------------------
126228
+ // Shared tokenizer factory
126229
+ // ---------------------------------------------------------------------------
126230
+ /**
126231
+ * Creates a tokenize function for `<HTMLBlock>...</HTMLBlock>`.
126232
+ *
126233
+ * - **flow** (block-level): supports multiline content via line continuations,
126234
+ * consumes trailing whitespace after the closing tag.
126235
+ * - **text** (inline): single-line only, exits immediately after the closing tag.
126236
+ */
126237
+ function syntax_createTokenize(mode) {
126238
+ return function tokenize(effects, ok, nok) {
126239
+ let depth = 1;
126240
+ function matchChars(chars, onMatch, onFail) {
126241
+ if (chars.length === 0)
126242
+ return onMatch;
126243
+ const next = (code) => {
126244
+ if (code === chars[0]) {
126245
+ effects.consume(code);
126246
+ return matchChars(chars.slice(1), onMatch, onFail);
126247
+ }
126248
+ return onFail(code);
126249
+ };
126250
+ return next;
126251
+ }
126252
+ function matchTagName(onMatch, onFail) {
126253
+ const next = (code) => {
126254
+ if (code === codes.uppercaseH) {
126255
+ effects.consume(code);
126256
+ return matchChars(TAG_SUFFIX, onMatch, onFail);
126257
+ }
126258
+ return onFail(code);
126259
+ };
126260
+ return next;
126261
+ }
126262
+ return start;
126263
+ function start(code) {
126264
+ if (code !== codes.lessThan)
126265
+ return nok(code);
126266
+ effects.enter('htmlBlockComponent');
126267
+ effects.enter('htmlBlockComponentData');
126268
+ effects.consume(code);
126269
+ return matchTagName(afterTagName, nok);
126270
+ }
126271
+ function afterTagName(code) {
126272
+ if (code === codes.greaterThan) {
126273
+ effects.consume(code);
126274
+ return body;
126275
+ }
126276
+ if (code === codes.space || code === codes.horizontalTab) {
126277
+ effects.consume(code);
126278
+ return inAttributes;
126279
+ }
126280
+ if (mode === 'flow' && markdownLineEnding(code)) {
126281
+ effects.exit('htmlBlockComponentData');
126282
+ return attributeContinuationStart(code);
126283
+ }
126284
+ if (code === codes.slash) {
126285
+ effects.consume(code);
126286
+ return selfClose;
126287
+ }
126288
+ return nok(code);
126289
+ }
126290
+ function inAttributes(code) {
126291
+ if (code === codes.greaterThan) {
126292
+ effects.consume(code);
126293
+ return body;
126294
+ }
126295
+ if (code === null) {
126296
+ return nok(code);
126297
+ }
126298
+ if (markdownLineEnding(code)) {
126299
+ if (mode === 'text')
126300
+ return nok(code);
126301
+ effects.exit('htmlBlockComponentData');
126302
+ return attributeContinuationStart(code);
126303
+ }
126304
+ effects.consume(code);
126305
+ return inAttributes;
126306
+ }
126307
+ function attributeContinuationStart(code) {
126308
+ return effects.check(html_block_component_syntax_nonLazyContinuationStart, attributeContinuationNonLazy, continuationAfter)(code);
126309
+ }
126310
+ function attributeContinuationNonLazy(code) {
126311
+ effects.enter(types_types.lineEnding);
126312
+ effects.consume(code);
126313
+ effects.exit(types_types.lineEnding);
126314
+ return attributeContinuationBefore;
126315
+ }
126316
+ function attributeContinuationBefore(code) {
126317
+ if (code === null || markdownLineEnding(code)) {
126318
+ return attributeContinuationStart(code);
126319
+ }
126320
+ effects.enter('htmlBlockComponentData');
126321
+ return inAttributes(code);
126322
+ }
126323
+ function selfClose(code) {
126324
+ if (code === codes.greaterThan) {
126325
+ effects.consume(code);
126326
+ return mode === 'flow' ? afterClose : done(code);
126327
+ }
126328
+ return nok(code);
126329
+ }
126330
+ function body(code) {
126331
+ if (code === null)
126332
+ return nok(code);
126333
+ if (markdownLineEnding(code)) {
126334
+ if (mode === 'text') {
126335
+ // Text constructs operate on paragraph content which spans lines
126336
+ effects.consume(code);
126337
+ return body;
126338
+ }
126339
+ effects.exit('htmlBlockComponentData');
126340
+ return continuationStart(code);
126341
+ }
126342
+ if (code === codes.lessThan) {
126343
+ effects.consume(code);
126344
+ return closeSlash;
126345
+ }
126346
+ effects.consume(code);
126347
+ return body;
126348
+ }
126349
+ function closeSlash(code) {
126350
+ if (code === codes.slash) {
126351
+ effects.consume(code);
126352
+ return matchTagName(closeGt, body);
126353
+ }
126354
+ return matchTagName(openAfterTagName, body)(code);
126355
+ }
126356
+ function openAfterTagName(code) {
126357
+ if (code === codes.greaterThan || code === codes.space || code === codes.horizontalTab) {
126358
+ depth += 1;
126359
+ effects.consume(code);
126360
+ return body;
126361
+ }
126362
+ return body(code);
126363
+ }
126364
+ function closeGt(code) {
126365
+ if (code === codes.greaterThan) {
126366
+ depth -= 1;
126367
+ effects.consume(code);
126368
+ if (depth === 0) {
126369
+ return mode === 'flow' ? afterClose : done(code);
126370
+ }
126371
+ return body;
126372
+ }
126373
+ return body(code);
126374
+ }
126375
+ // -- flow-only states ---------------------------------------------------
126376
+ function afterClose(code) {
126377
+ if (code === null || markdownLineEnding(code)) {
126378
+ return done(code);
126379
+ }
126380
+ if (code === codes.space || code === codes.horizontalTab) {
126381
+ effects.consume(code);
126382
+ return afterClose;
126383
+ }
126384
+ // Reject so the block re-parses as a paragraph, deferring to the
126385
+ // text tokenizer which preserves trailing content in the same line.
126386
+ return nok(code);
125568
126387
  }
125569
- }
125570
- return restoreCodeBlocks(result.join('\n'), protectedCode);
125571
- }
125572
-
125573
- ;// ./processor/transform/mdxish/variables-code.ts
125574
-
125575
-
125576
- // Single combined regex so that resolved values from one pattern are never re-scanned by the other.
125577
- const COMBINED_VARIABLE_REGEX = new RegExp(`${variable_dist.VARIABLE_REGEXP}|${variable_dist.MDX_VARIABLE_REGEXP}`, 'giu');
125578
- // Flatten variables into a single object for easy lookup
125579
- function variables_code_flattenVariables(variables) {
125580
- if (!variables)
125581
- return {};
125582
- return {
125583
- ...Object.fromEntries((variables.defaults || []).map(d => [d.name, d.default])),
125584
- ...variables.user,
125585
- };
125586
- }
125587
- function resolveCodeVariables(value, resolvedVariables) {
125588
- return value.replace(COMBINED_VARIABLE_REGEX, (match, legacyName, mdxEscapePrefix, mdxVarName, mdxEscapeSuffix) => {
125589
- // Legacy variable: <<...>>
125590
- if (legacyName !== undefined) {
125591
- if (match.startsWith('\\<<') || match.endsWith('\\>>'))
125592
- return match;
125593
- const name = legacyName.trim();
125594
- if (name.startsWith('glossary:'))
125595
- return name.toUpperCase();
125596
- return name in resolvedVariables ? resolvedVariables[name] : name.toUpperCase();
126388
+ // -- flow-only: line continuation ---------------------------------------
126389
+ function continuationStart(code) {
126390
+ return effects.check(html_block_component_syntax_nonLazyContinuationStart, continuationStartNonLazy, continuationAfter)(code);
125597
126391
  }
125598
- // MDX variable: {user.*}
125599
- if (mdxEscapePrefix || mdxEscapeSuffix)
125600
- return match;
125601
- if (mdxVarName in resolvedVariables)
125602
- return resolvedVariables[mdxVarName];
125603
- const fullPath = match.slice(1, -1);
125604
- return fullPath.toUpperCase();
125605
- });
125606
- }
125607
- /**
125608
- * A remark mdast plugin that resolves legacy variables <<...>> and MDX variables {user.*} inside code and inline code nodes
125609
- * to their values. Uses regexes from the readme variable to search for variables in the code string.
125610
- *
125611
- * This is needed because variables in code blocks and inline cannot be tokenized, and also we need to maintain the code string
125612
- * in the code nodes. This enables engine side variable resolution in codes which improves UX
125613
- */
125614
- const variablesCodeResolver = ({ variables } = {}) => tree => {
125615
- const resolvedVariables = variables_code_flattenVariables(variables);
125616
- visit(tree, 'inlineCode', (node) => {
125617
- if (!node.value)
125618
- return;
125619
- node.value = resolveCodeVariables(node.value, resolvedVariables);
125620
- });
125621
- visit(tree, 'code', (node) => {
125622
- if (!node.value)
125623
- return;
125624
- if (node.lang === 'mermaid')
125625
- return;
125626
- const nextValue = resolveCodeVariables(node.value, resolvedVariables);
125627
- node.value = nextValue;
125628
- // Keep code-tabs/readme-components hProperties in sync with node.value
125629
- // because renderers read `value` from hProperties.
125630
- if (node.data?.hProperties && typeof node.data.hProperties === 'object') {
125631
- node.data.hProperties.value = nextValue;
126392
+ function continuationStartNonLazy(code) {
126393
+ effects.enter(types_types.lineEnding);
126394
+ effects.consume(code);
126395
+ effects.exit(types_types.lineEnding);
126396
+ return continuationBefore;
125632
126397
  }
125633
- });
125634
- return tree;
125635
- };
125636
- /* harmony default export */ const variables_code = (variablesCodeResolver);
125637
-
125638
- ;// ./processor/transform/mdxish/variables-text.ts
125639
-
125640
-
125641
- /**
125642
- * Matches {user.<field>} patterns:
125643
- * - {user.name}
125644
- * - {user.email}
125645
- * - {user['field']}
125646
- * - {user["field"]}
125647
- *
125648
- * Captures the field name in group 1 (dot notation) or group 2 (bracket notation)
125649
- */
125650
- const USER_VAR_REGEX = /\{user\.(\w+)\}|\{user\[['"](\w+)['"]\]\}/g;
125651
- function makeVariableNode(varName, rawValue) {
125652
- return {
125653
- type: NodeTypes.variable,
125654
- data: {
125655
- hName: 'Variable',
125656
- hProperties: { name: varName },
125657
- },
125658
- value: rawValue,
125659
- };
125660
- }
125661
- /**
125662
- * A remark plugin that parses {user.<field>} patterns from text nodes and
125663
- * mdx expression nodes, creating Variable nodes for runtime resolution.
125664
- *
125665
- * Handles:
125666
- * - `text` nodes: when safeMode is true or after expression evaluation
125667
- * - `mdxTextExpression` nodes: when mdxExpression has parsed {user.*} before evaluation
125668
- * - `mdxFlowExpression` nodes: when {user.*} appears on its own line (e.g. inside JSX table cells)
125669
- *
125670
- * Supports any user field: name, email, email_verified, exp, iat, etc.
125671
- */
125672
- function visitExpressionNode(node, index, parent) {
125673
- if (index === undefined || !parent)
125674
- return;
125675
- const wrapped = `{${(node.value ?? '').trim()}}`;
125676
- const matches = [...wrapped.matchAll(USER_VAR_REGEX)];
125677
- if (matches.length !== 1)
125678
- return;
125679
- const varName = matches[0][1] || matches[0][2];
125680
- parent.children.splice(index, 1, makeVariableNode(varName, wrapped));
125681
- }
125682
- const variablesTextTransformer = () => tree => {
125683
- visit(tree, 'mdxTextExpression', (node, index, parent) => {
125684
- visitExpressionNode(node, index, parent);
125685
- });
125686
- visit(tree, 'mdxFlowExpression', (node, index, parent) => {
125687
- visitExpressionNode(node, index, parent);
125688
- });
125689
- visit(tree, 'text', (node, index, parent) => {
125690
- if (index === undefined || !parent)
125691
- return;
125692
- // Skip if inside inline code
125693
- if (parent.type === 'inlineCode')
125694
- return;
125695
- const text = node.value;
125696
- if (typeof text !== 'string' || !text.trim())
125697
- return;
125698
- if (!text.includes('{user.') && !text.includes('{user['))
125699
- return;
125700
- const matches = [...text.matchAll(USER_VAR_REGEX)];
125701
- if (matches.length === 0)
125702
- return;
125703
- const parts = [];
125704
- let lastIndex = 0;
125705
- matches.forEach(match => {
125706
- const matchIndex = match.index ?? 0;
125707
- // Add text before the match
125708
- if (matchIndex > lastIndex) {
125709
- parts.push({ type: 'text', value: text.slice(lastIndex, matchIndex) });
126398
+ function continuationBefore(code) {
126399
+ if (code === null || markdownLineEnding(code)) {
126400
+ return continuationStart(code);
125710
126401
  }
125711
- // Extract variable name from either capture group (dot or bracket notation)
125712
- const varName = match[1] || match[2];
125713
- parts.push(makeVariableNode(varName, match[0]));
125714
- lastIndex = matchIndex + match[0].length;
125715
- });
125716
- // Add remaining text after last match
125717
- if (lastIndex < text.length) {
125718
- parts.push({ type: 'text', value: text.slice(lastIndex) });
126402
+ effects.enter('htmlBlockComponentData');
126403
+ return body(code);
125719
126404
  }
125720
- // Replace node with parts
125721
- if (parts.length > 1 || (parts.length === 1 && parts[0].type !== 'text')) {
125722
- parent.children.splice(index, 1, ...parts);
126405
+ function continuationAfter(code) {
126406
+ if (code === null)
126407
+ return nok(code);
126408
+ effects.exit('htmlBlockComponent');
126409
+ return ok(code);
125723
126410
  }
125724
- });
125725
- return tree;
126411
+ // -- shared exit --------------------------------------------------------
126412
+ function done(_code) {
126413
+ effects.exit('htmlBlockComponentData');
126414
+ effects.exit('htmlBlockComponent');
126415
+ return ok(_code);
126416
+ }
126417
+ };
126418
+ }
126419
+ // ---------------------------------------------------------------------------
126420
+ // Flow construct (block-level)
126421
+ // ---------------------------------------------------------------------------
126422
+ const html_block_component_syntax_nonLazyContinuationStart = {
126423
+ tokenize: html_block_component_syntax_tokenizeNonLazyContinuationStart,
126424
+ partial: true,
125726
126425
  };
125727
- /* harmony default export */ const variables_text = (variablesTextTransformer);
125728
-
125729
- ;// ./lib/mdast-util/jsx-table/index.ts
125730
- const jsx_table_contextMap = new WeakMap();
125731
- function findJsxTableToken() {
125732
- const events = this.tokenStack;
125733
- for (let i = events.length - 1; i >= 0; i -= 1) {
125734
- if (events[i][0].type === 'jsxTable')
125735
- return events[i][0];
126426
+ function resolveToHtmlBlockComponent(events) {
126427
+ let index = events.length;
126428
+ while (index > 0) {
126429
+ index -= 1;
126430
+ if (events[index][0] === 'enter' && events[index][1].type === 'htmlBlockComponent') {
126431
+ break;
126432
+ }
125736
126433
  }
125737
- return undefined;
125738
- }
125739
- function enterJsxTable(token) {
125740
- jsx_table_contextMap.set(token, { chunks: [], lastEndLine: token.start.line });
125741
- this.enter({ type: 'html', value: '' }, token);
126434
+ if (index > 1 && events[index - 2][1].type === types_types.linePrefix) {
126435
+ events[index][1].start = events[index - 2][1].start;
126436
+ events[index + 1][1].start = events[index - 2][1].start;
126437
+ events.splice(index - 2, 2);
126438
+ }
126439
+ return events;
125742
126440
  }
125743
- function exitJsxTableData(token) {
125744
- const tableToken = findJsxTableToken.call(this);
125745
- if (!tableToken)
125746
- return;
125747
- const ctx = jsx_table_contextMap.get(tableToken);
125748
- if (ctx) {
125749
- const gap = token.start.line - ctx.lastEndLine;
125750
- if (ctx.chunks.length > 0 && gap > 0) {
125751
- ctx.chunks.push('\n'.repeat(gap));
126441
+ const htmlBlockComponentFlowConstruct = {
126442
+ name: 'htmlBlockComponent',
126443
+ tokenize: syntax_createTokenize('flow'),
126444
+ resolveTo: resolveToHtmlBlockComponent,
126445
+ concrete: true,
126446
+ };
126447
+ function html_block_component_syntax_tokenizeNonLazyContinuationStart(effects, ok, nok) {
126448
+ // eslint-disable-next-line @typescript-eslint/no-this-alias
126449
+ const self = this;
126450
+ return start;
126451
+ function start(code) {
126452
+ if (markdownLineEnding(code)) {
126453
+ effects.enter(types_types.lineEnding);
126454
+ effects.consume(code);
126455
+ effects.exit(types_types.lineEnding);
126456
+ return after;
125752
126457
  }
125753
- ctx.chunks.push(this.sliceSerialize(token));
125754
- ctx.lastEndLine = token.end.line;
126458
+ return nok(code);
125755
126459
  }
125756
- }
125757
- function exitJsxTable(token) {
125758
- const ctx = jsx_table_contextMap.get(token);
125759
- const node = this.stack[this.stack.length - 1];
125760
- if (ctx) {
125761
- node.value = ctx.chunks.join('');
125762
- jsx_table_contextMap.delete(token);
126460
+ function after(code) {
126461
+ if (self.parser.lazy[self.now().line]) {
126462
+ return nok(code);
126463
+ }
126464
+ return ok(code);
125763
126465
  }
125764
- this.exit(token);
125765
126466
  }
125766
- function jsxTableFromMarkdown() {
126467
+ // ---------------------------------------------------------------------------
126468
+ // Text construct (inline)
126469
+ // ---------------------------------------------------------------------------
126470
+ const htmlBlockComponentTextConstruct = {
126471
+ name: 'htmlBlockComponent',
126472
+ tokenize: syntax_createTokenize('text'),
126473
+ };
126474
+ // ---------------------------------------------------------------------------
126475
+ // Extension
126476
+ // ---------------------------------------------------------------------------
126477
+ function htmlBlockComponent() {
125767
126478
  return {
125768
- enter: {
125769
- jsxTable: enterJsxTable,
126479
+ flow: {
126480
+ [codes.lessThan]: [htmlBlockComponentFlowConstruct],
125770
126481
  },
125771
- exit: {
125772
- jsxTableData: exitJsxTableData,
125773
- jsxTable: exitJsxTable,
126482
+ text: {
126483
+ [codes.lessThan]: [htmlBlockComponentTextConstruct],
125774
126484
  },
125775
126485
  };
125776
126486
  }
125777
126487
 
126488
+ ;// ./lib/micromark/html-block-component/index.ts
126489
+
126490
+
125778
126491
  ;// ./lib/micromark/jsx-comment/syntax.ts
125779
126492
 
125780
126493
 
@@ -125897,243 +126610,6 @@ function jsxComment() {
125897
126610
  };
125898
126611
  }
125899
126612
 
125900
- ;// ./lib/micromark/jsx-table/syntax.ts
125901
-
125902
-
125903
- const jsx_table_syntax_nonLazyContinuationStart = {
125904
- tokenize: jsx_table_syntax_tokenizeNonLazyContinuationStart,
125905
- partial: true,
125906
- };
125907
- function resolveToJsxTable(events) {
125908
- let index = events.length;
125909
- while (index > 0) {
125910
- index -= 1;
125911
- if (events[index][0] === 'enter' && events[index][1].type === 'jsxTable') {
125912
- break;
125913
- }
125914
- }
125915
- if (index > 1 && events[index - 2][1].type === types_types.linePrefix) {
125916
- events[index][1].start = events[index - 2][1].start;
125917
- events[index + 1][1].start = events[index - 2][1].start;
125918
- events.splice(index - 2, 2);
125919
- }
125920
- return events;
125921
- }
125922
- const jsxTableConstruct = {
125923
- name: 'jsxTable',
125924
- tokenize: tokenizeJsxTable,
125925
- resolveTo: resolveToJsxTable,
125926
- concrete: true,
125927
- };
125928
- function tokenizeJsxTable(effects, ok, nok) {
125929
- let codeSpanOpenSize = 0;
125930
- let codeSpanCloseSize = 0;
125931
- let depth = 1;
125932
- const ABLE_SUFFIX = [codes.lowercaseA, codes.lowercaseB, codes.lowercaseL, codes.lowercaseE];
125933
- /** Build a state chain that matches a sequence of character codes. */
125934
- function matchChars(chars, onMatch, onFail) {
125935
- if (chars.length === 0)
125936
- return onMatch;
125937
- return ((code) => {
125938
- if (code === chars[0]) {
125939
- effects.consume(code);
125940
- return matchChars(chars.slice(1), onMatch, onFail);
125941
- }
125942
- return onFail(code);
125943
- });
125944
- }
125945
- return start;
125946
- function start(code) {
125947
- if (code !== codes.lessThan)
125948
- return nok(code);
125949
- effects.enter('jsxTable');
125950
- effects.enter('jsxTableData');
125951
- effects.consume(code);
125952
- return afterLessThan;
125953
- }
125954
- function afterLessThan(code) {
125955
- if (code === codes.uppercaseT || code === codes.lowercaseT) {
125956
- effects.consume(code);
125957
- return matchChars(ABLE_SUFFIX, afterTagName, nok);
125958
- }
125959
- return nok(code);
125960
- }
125961
- function afterTagName(code) {
125962
- if (code === codes.greaterThan || code === codes.slash || code === codes.space || code === codes.horizontalTab) {
125963
- effects.consume(code);
125964
- return body;
125965
- }
125966
- return nok(code);
125967
- }
125968
- function body(code) {
125969
- // Reject unclosed <Table> so it falls back to normal HTML block parsing
125970
- // instead of swallowing all subsequent content to EOF
125971
- if (code === null) {
125972
- return nok(code);
125973
- }
125974
- if (markdownLineEnding(code)) {
125975
- effects.exit('jsxTableData');
125976
- return continuationStart(code);
125977
- }
125978
- if (code === codes.backslash) {
125979
- effects.consume(code);
125980
- return escapedChar;
125981
- }
125982
- if (code === codes.lessThan) {
125983
- effects.consume(code);
125984
- return closeSlash;
125985
- }
125986
- // Skip over backtick code spans so `</Table>` in inline code isn't
125987
- // treated as the closing tag
125988
- if (code === codes.graveAccent) {
125989
- codeSpanOpenSize = 0;
125990
- return countOpenTicks(code);
125991
- }
125992
- effects.consume(code);
125993
- return body;
125994
- }
125995
- function countOpenTicks(code) {
125996
- if (code === codes.graveAccent) {
125997
- codeSpanOpenSize += 1;
125998
- effects.consume(code);
125999
- return countOpenTicks;
126000
- }
126001
- return inCodeSpan(code);
126002
- }
126003
- function inCodeSpan(code) {
126004
- if (code === null || markdownLineEnding(code))
126005
- return body(code);
126006
- if (code === codes.graveAccent) {
126007
- codeSpanCloseSize = 0;
126008
- return countCloseTicks(code);
126009
- }
126010
- effects.consume(code);
126011
- return inCodeSpan;
126012
- }
126013
- function countCloseTicks(code) {
126014
- if (code === codes.graveAccent) {
126015
- codeSpanCloseSize += 1;
126016
- effects.consume(code);
126017
- return countCloseTicks;
126018
- }
126019
- return codeSpanCloseSize === codeSpanOpenSize ? body(code) : inCodeSpan(code);
126020
- }
126021
- function escapedChar(code) {
126022
- if (code === null || markdownLineEnding(code)) {
126023
- return body(code);
126024
- }
126025
- effects.consume(code);
126026
- return body;
126027
- }
126028
- function closeSlash(code) {
126029
- if (code === codes.slash) {
126030
- effects.consume(code);
126031
- return closeTagFirstChar;
126032
- }
126033
- if (code === codes.uppercaseT || code === codes.lowercaseT) {
126034
- effects.consume(code);
126035
- return matchChars(ABLE_SUFFIX, openAfterTagName, body);
126036
- }
126037
- return body(code);
126038
- }
126039
- function closeTagFirstChar(code) {
126040
- if (code === codes.uppercaseT || code === codes.lowercaseT) {
126041
- effects.consume(code);
126042
- return matchChars(ABLE_SUFFIX, closeGt, body);
126043
- }
126044
- return body(code);
126045
- }
126046
- function openAfterTagName(code) {
126047
- if (code === codes.greaterThan || code === codes.slash || code === codes.space || code === codes.horizontalTab) {
126048
- depth += 1;
126049
- effects.consume(code);
126050
- return body;
126051
- }
126052
- return body(code);
126053
- }
126054
- function closeGt(code) {
126055
- if (code === codes.greaterThan) {
126056
- depth -= 1;
126057
- effects.consume(code);
126058
- if (depth === 0) {
126059
- return afterClose;
126060
- }
126061
- return body;
126062
- }
126063
- return body(code);
126064
- }
126065
- function afterClose(code) {
126066
- if (code === null || markdownLineEnding(code)) {
126067
- effects.exit('jsxTableData');
126068
- effects.exit('jsxTable');
126069
- return ok(code);
126070
- }
126071
- effects.consume(code);
126072
- return afterClose;
126073
- }
126074
- // Line ending handling — follows the htmlFlow pattern
126075
- function continuationStart(code) {
126076
- return effects.check(jsx_table_syntax_nonLazyContinuationStart, continuationStartNonLazy, continuationAfter)(code);
126077
- }
126078
- function continuationStartNonLazy(code) {
126079
- effects.enter(types_types.lineEnding);
126080
- effects.consume(code);
126081
- effects.exit(types_types.lineEnding);
126082
- return continuationBefore;
126083
- }
126084
- function continuationBefore(code) {
126085
- if (code === null || markdownLineEnding(code)) {
126086
- return continuationStart(code);
126087
- }
126088
- effects.enter('jsxTableData');
126089
- return body(code);
126090
- }
126091
- function continuationAfter(code) {
126092
- // At EOF without </Table>, reject so content isn't swallowed
126093
- if (code === null) {
126094
- return nok(code);
126095
- }
126096
- effects.exit('jsxTable');
126097
- return ok(code);
126098
- }
126099
- }
126100
- function jsx_table_syntax_tokenizeNonLazyContinuationStart(effects, ok, nok) {
126101
- // eslint-disable-next-line @typescript-eslint/no-this-alias
126102
- const self = this;
126103
- return start;
126104
- function start(code) {
126105
- if (markdownLineEnding(code)) {
126106
- effects.enter(types_types.lineEnding);
126107
- effects.consume(code);
126108
- effects.exit(types_types.lineEnding);
126109
- return after;
126110
- }
126111
- return nok(code);
126112
- }
126113
- function after(code) {
126114
- if (self.parser.lazy[self.now().line]) {
126115
- return nok(code);
126116
- }
126117
- return ok(code);
126118
- }
126119
- }
126120
- /**
126121
- * Micromark extension that tokenizes `<Table>...</Table>` and `<table>...</table>`
126122
- * as a single flow block.
126123
- *
126124
- * Prevents CommonMark HTML block type 6 from fragmenting table blocks at blank lines.
126125
- */
126126
- function jsxTable() {
126127
- return {
126128
- flow: {
126129
- [codes.lessThan]: [jsxTableConstruct],
126130
- },
126131
- };
126132
- }
126133
-
126134
- ;// ./lib/micromark/jsx-table/index.ts
126135
-
126136
-
126137
126613
  ;// ./lib/micromark/mdx-expression-lenient/syntax.ts
126138
126614
 
126139
126615
 
@@ -126309,6 +126785,9 @@ function loadComponents() {
126309
126785
 
126310
126786
 
126311
126787
 
126788
+
126789
+
126790
+
126312
126791
 
126313
126792
 
126314
126793
 
@@ -126323,15 +126802,19 @@ const defaultTransformers = [
126323
126802
  * CommonMark/remark limitations and reach parity with legacy (rdmd) rendering.
126324
126803
  *
126325
126804
  * Runs a series of string-level transformations before micromark/remark parsing:
126326
- * 1. Normalize malformed table separator syntax (e.g., `|: ---``| :---`)
126327
- * 2. Terminate HTML flow blocks so subsequent content isn't swallowed
126328
- * 3. Close invalid "self-closing" HTML tags (e.g., `<i />` → `<i></i>`)
126329
- * 4. Normalize compact ATX headings (e.g., `#Heading``# Heading`)
126330
- * 5. Replace snake_case component names with parser-safe placeholders
126805
+ * 1. Canonicalize closing tags with stray whitespace (e.g., `</ td >` `</td>`)
126806
+ * 2. Normalize malformed table separator syntax (e.g., `|: ---` → `| :---`)
126807
+ * 3. Terminate HTML flow blocks so subsequent content isn't swallowed
126808
+ * 4. Close invalid "self-closing" HTML tags (e.g., `<i />` `<i></i>`)
126809
+ * 5. Normalize compact ATX headings (e.g., `#Heading` → `# Heading`)
126810
+ * 6. Replace snake_case component names with parser-safe placeholders
126331
126811
  */
126332
126812
  function preprocessContent(content, opts) {
126333
126813
  const { knownComponents } = opts;
126334
- let result = normalizeTableSeparator(content);
126814
+ // Runs first so `jsxTable` sees a literal `</table>` (and the HTML-line
126815
+ // classification in `terminateHtmlFlowBlocks` is accurate)
126816
+ let result = normalizeClosingTagWhitespace(content);
126817
+ result = normalizeTableSeparator(result);
126335
126818
  result = terminateHtmlFlowBlocks(result);
126336
126819
  result = closeSelfClosingHtmlTags(result);
126337
126820
  result = normalizeCompactHeadings(result);
@@ -126360,6 +126843,7 @@ function mdxishAstProcessor(mdContent, opts = {}) {
126360
126843
  syntax_gemoji(),
126361
126844
  legacyVariable(),
126362
126845
  looseHtmlEntity(),
126846
+ htmlBlockComponent(),
126363
126847
  ];
126364
126848
  const fromMarkdownExts = [
126365
126849
  jsxTableFromMarkdown(),
@@ -126369,6 +126853,7 @@ function mdxishAstProcessor(mdContent, opts = {}) {
126369
126853
  legacyVariableFromMarkdown(),
126370
126854
  emptyTaskListItemFromMarkdown(),
126371
126855
  looseHtmlEntityFromMarkdown(),
126856
+ htmlBlockComponentFromMarkdown(),
126372
126857
  ];
126373
126858
  if (!safeMode) {
126374
126859
  // Insert mdx expression (text-only, no flow) after gemoji at index 3
@@ -126382,6 +126867,10 @@ function mdxishAstProcessor(mdContent, opts = {}) {
126382
126867
  // JSX comment tokenizer must come before magicBlock so it claims `{/* ... */}` first
126383
126868
  micromarkExts.unshift(jsxComment());
126384
126869
  }
126870
+ // Claim `<HTMLBlock>` as one opaque token so broad tokenizers can't fragment its body
126871
+ // We put this last as micromark tries the last-registered extension first, so push (not unshift) to win the `<` race.
126872
+ // micromarkExts.push(htmlBlockComponent());
126873
+ // fromMarkdownExts.push(htmlBlockComponentFromMarkdown());
126385
126874
  const processor = unified()
126386
126875
  .data('micromarkExtensions', micromarkExts)
126387
126876
  .data('fromMarkdownExtensions', fromMarkdownExts)
@@ -126437,6 +126926,12 @@ function mdxishMdastToMd(mdast) {
126437
126926
  .use(remarkStringify, {
126438
126927
  bullet: '-',
126439
126928
  emphasis: '_',
126929
+ // Escape literal braces in text so they don't parse as (often
126930
+ // unterminated) MDX expressions on the next round trip.
126931
+ unsafe: [
126932
+ { character: '{', inConstruct: 'phrasing' },
126933
+ { character: '}', inConstruct: 'phrasing' },
126934
+ ],
126440
126935
  });
126441
126936
  return processor.stringify(processor.runSync(mdast));
126442
126937
  }
@@ -126975,332 +127470,6 @@ const mdxishTags_tags = (doc) => {
126975
127470
  };
126976
127471
  /* harmony default export */ const mdxishTags = (mdxishTags_tags);
126977
127472
 
126978
- ;// ./lib/mdast-util/html-block-component/index.ts
126979
- const html_block_component_contextMap = new WeakMap();
126980
- function findHtmlBlockComponentToken() {
126981
- const events = this.tokenStack;
126982
- for (let i = events.length - 1; i >= 0; i -= 1) {
126983
- if (events[i][0].type === 'htmlBlockComponent')
126984
- return events[i][0];
126985
- }
126986
- return undefined;
126987
- }
126988
- function enterHtmlBlockComponent(token) {
126989
- html_block_component_contextMap.set(token, { chunks: [], lastEndLine: token.start.line });
126990
- this.enter({ type: 'html', value: '' }, token);
126991
- }
126992
- function exitHtmlBlockComponentData(token) {
126993
- const componentToken = findHtmlBlockComponentToken.call(this);
126994
- if (!componentToken)
126995
- return;
126996
- const ctx = html_block_component_contextMap.get(componentToken);
126997
- if (ctx) {
126998
- const gap = token.start.line - ctx.lastEndLine;
126999
- if (ctx.chunks.length > 0 && gap > 0) {
127000
- ctx.chunks.push('\n'.repeat(gap));
127001
- }
127002
- ctx.chunks.push(this.sliceSerialize(token));
127003
- ctx.lastEndLine = token.end.line;
127004
- }
127005
- }
127006
- function exitHtmlBlockComponent(token) {
127007
- const ctx = html_block_component_contextMap.get(token);
127008
- const node = this.stack[this.stack.length - 1];
127009
- if (ctx) {
127010
- node.value = ctx.chunks.join('');
127011
- html_block_component_contextMap.delete(token);
127012
- }
127013
- this.exit(token);
127014
- }
127015
- function htmlBlockComponentFromMarkdown() {
127016
- return {
127017
- enter: {
127018
- htmlBlockComponent: enterHtmlBlockComponent,
127019
- },
127020
- exit: {
127021
- htmlBlockComponentData: exitHtmlBlockComponentData,
127022
- htmlBlockComponent: exitHtmlBlockComponent,
127023
- },
127024
- };
127025
- }
127026
-
127027
- ;// ./lib/micromark/html-block-component/syntax.ts
127028
-
127029
-
127030
- const TAG_SUFFIX = [
127031
- codes.uppercaseT,
127032
- codes.uppercaseM,
127033
- codes.uppercaseL,
127034
- codes.uppercaseB,
127035
- codes.lowercaseL,
127036
- codes.lowercaseO,
127037
- codes.lowercaseC,
127038
- codes.lowercaseK,
127039
- ];
127040
- // ---------------------------------------------------------------------------
127041
- // Shared tokenizer factory
127042
- // ---------------------------------------------------------------------------
127043
- /**
127044
- * Creates a tokenize function for `<HTMLBlock>...</HTMLBlock>`.
127045
- *
127046
- * - **flow** (block-level): supports multiline content via line continuations,
127047
- * consumes trailing whitespace after the closing tag.
127048
- * - **text** (inline): single-line only, exits immediately after the closing tag.
127049
- */
127050
- function syntax_createTokenize(mode) {
127051
- return function tokenize(effects, ok, nok) {
127052
- let depth = 1;
127053
- function matchChars(chars, onMatch, onFail) {
127054
- if (chars.length === 0)
127055
- return onMatch;
127056
- const next = (code) => {
127057
- if (code === chars[0]) {
127058
- effects.consume(code);
127059
- return matchChars(chars.slice(1), onMatch, onFail);
127060
- }
127061
- return onFail(code);
127062
- };
127063
- return next;
127064
- }
127065
- function matchTagName(onMatch, onFail) {
127066
- const next = (code) => {
127067
- if (code === codes.uppercaseH) {
127068
- effects.consume(code);
127069
- return matchChars(TAG_SUFFIX, onMatch, onFail);
127070
- }
127071
- return onFail(code);
127072
- };
127073
- return next;
127074
- }
127075
- return start;
127076
- function start(code) {
127077
- if (code !== codes.lessThan)
127078
- return nok(code);
127079
- effects.enter('htmlBlockComponent');
127080
- effects.enter('htmlBlockComponentData');
127081
- effects.consume(code);
127082
- return matchTagName(afterTagName, nok);
127083
- }
127084
- function afterTagName(code) {
127085
- if (code === codes.greaterThan) {
127086
- effects.consume(code);
127087
- return body;
127088
- }
127089
- if (code === codes.space || code === codes.horizontalTab) {
127090
- effects.consume(code);
127091
- return inAttributes;
127092
- }
127093
- if (mode === 'flow' && markdownLineEnding(code)) {
127094
- effects.exit('htmlBlockComponentData');
127095
- return attributeContinuationStart(code);
127096
- }
127097
- if (code === codes.slash) {
127098
- effects.consume(code);
127099
- return selfClose;
127100
- }
127101
- return nok(code);
127102
- }
127103
- function inAttributes(code) {
127104
- if (code === codes.greaterThan) {
127105
- effects.consume(code);
127106
- return body;
127107
- }
127108
- if (code === null) {
127109
- return nok(code);
127110
- }
127111
- if (markdownLineEnding(code)) {
127112
- if (mode === 'text')
127113
- return nok(code);
127114
- effects.exit('htmlBlockComponentData');
127115
- return attributeContinuationStart(code);
127116
- }
127117
- effects.consume(code);
127118
- return inAttributes;
127119
- }
127120
- function attributeContinuationStart(code) {
127121
- return effects.check(html_block_component_syntax_nonLazyContinuationStart, attributeContinuationNonLazy, continuationAfter)(code);
127122
- }
127123
- function attributeContinuationNonLazy(code) {
127124
- effects.enter(types_types.lineEnding);
127125
- effects.consume(code);
127126
- effects.exit(types_types.lineEnding);
127127
- return attributeContinuationBefore;
127128
- }
127129
- function attributeContinuationBefore(code) {
127130
- if (code === null || markdownLineEnding(code)) {
127131
- return attributeContinuationStart(code);
127132
- }
127133
- effects.enter('htmlBlockComponentData');
127134
- return inAttributes(code);
127135
- }
127136
- function selfClose(code) {
127137
- if (code === codes.greaterThan) {
127138
- effects.consume(code);
127139
- return mode === 'flow' ? afterClose : done(code);
127140
- }
127141
- return nok(code);
127142
- }
127143
- function body(code) {
127144
- if (code === null)
127145
- return nok(code);
127146
- if (markdownLineEnding(code)) {
127147
- if (mode === 'text') {
127148
- // Text constructs operate on paragraph content which spans lines
127149
- effects.consume(code);
127150
- return body;
127151
- }
127152
- effects.exit('htmlBlockComponentData');
127153
- return continuationStart(code);
127154
- }
127155
- if (code === codes.lessThan) {
127156
- effects.consume(code);
127157
- return closeSlash;
127158
- }
127159
- effects.consume(code);
127160
- return body;
127161
- }
127162
- function closeSlash(code) {
127163
- if (code === codes.slash) {
127164
- effects.consume(code);
127165
- return matchTagName(closeGt, body);
127166
- }
127167
- return matchTagName(openAfterTagName, body)(code);
127168
- }
127169
- function openAfterTagName(code) {
127170
- if (code === codes.greaterThan || code === codes.space || code === codes.horizontalTab) {
127171
- depth += 1;
127172
- effects.consume(code);
127173
- return body;
127174
- }
127175
- return body(code);
127176
- }
127177
- function closeGt(code) {
127178
- if (code === codes.greaterThan) {
127179
- depth -= 1;
127180
- effects.consume(code);
127181
- if (depth === 0) {
127182
- return mode === 'flow' ? afterClose : done(code);
127183
- }
127184
- return body;
127185
- }
127186
- return body(code);
127187
- }
127188
- // -- flow-only states ---------------------------------------------------
127189
- function afterClose(code) {
127190
- if (code === null || markdownLineEnding(code)) {
127191
- return done(code);
127192
- }
127193
- if (code === codes.space || code === codes.horizontalTab) {
127194
- effects.consume(code);
127195
- return afterClose;
127196
- }
127197
- // Reject so the block re-parses as a paragraph, deferring to the
127198
- // text tokenizer which preserves trailing content in the same line.
127199
- return nok(code);
127200
- }
127201
- // -- flow-only: line continuation ---------------------------------------
127202
- function continuationStart(code) {
127203
- return effects.check(html_block_component_syntax_nonLazyContinuationStart, continuationStartNonLazy, continuationAfter)(code);
127204
- }
127205
- function continuationStartNonLazy(code) {
127206
- effects.enter(types_types.lineEnding);
127207
- effects.consume(code);
127208
- effects.exit(types_types.lineEnding);
127209
- return continuationBefore;
127210
- }
127211
- function continuationBefore(code) {
127212
- if (code === null || markdownLineEnding(code)) {
127213
- return continuationStart(code);
127214
- }
127215
- effects.enter('htmlBlockComponentData');
127216
- return body(code);
127217
- }
127218
- function continuationAfter(code) {
127219
- if (code === null)
127220
- return nok(code);
127221
- effects.exit('htmlBlockComponent');
127222
- return ok(code);
127223
- }
127224
- // -- shared exit --------------------------------------------------------
127225
- function done(_code) {
127226
- effects.exit('htmlBlockComponentData');
127227
- effects.exit('htmlBlockComponent');
127228
- return ok(_code);
127229
- }
127230
- };
127231
- }
127232
- // ---------------------------------------------------------------------------
127233
- // Flow construct (block-level)
127234
- // ---------------------------------------------------------------------------
127235
- const html_block_component_syntax_nonLazyContinuationStart = {
127236
- tokenize: html_block_component_syntax_tokenizeNonLazyContinuationStart,
127237
- partial: true,
127238
- };
127239
- function resolveToHtmlBlockComponent(events) {
127240
- let index = events.length;
127241
- while (index > 0) {
127242
- index -= 1;
127243
- if (events[index][0] === 'enter' && events[index][1].type === 'htmlBlockComponent') {
127244
- break;
127245
- }
127246
- }
127247
- if (index > 1 && events[index - 2][1].type === types_types.linePrefix) {
127248
- events[index][1].start = events[index - 2][1].start;
127249
- events[index + 1][1].start = events[index - 2][1].start;
127250
- events.splice(index - 2, 2);
127251
- }
127252
- return events;
127253
- }
127254
- const htmlBlockComponentFlowConstruct = {
127255
- name: 'htmlBlockComponent',
127256
- tokenize: syntax_createTokenize('flow'),
127257
- resolveTo: resolveToHtmlBlockComponent,
127258
- concrete: true,
127259
- };
127260
- function html_block_component_syntax_tokenizeNonLazyContinuationStart(effects, ok, nok) {
127261
- // eslint-disable-next-line @typescript-eslint/no-this-alias
127262
- const self = this;
127263
- return start;
127264
- function start(code) {
127265
- if (markdownLineEnding(code)) {
127266
- effects.enter(types_types.lineEnding);
127267
- effects.consume(code);
127268
- effects.exit(types_types.lineEnding);
127269
- return after;
127270
- }
127271
- return nok(code);
127272
- }
127273
- function after(code) {
127274
- if (self.parser.lazy[self.now().line]) {
127275
- return nok(code);
127276
- }
127277
- return ok(code);
127278
- }
127279
- }
127280
- // ---------------------------------------------------------------------------
127281
- // Text construct (inline)
127282
- // ---------------------------------------------------------------------------
127283
- const htmlBlockComponentTextConstruct = {
127284
- name: 'htmlBlockComponent',
127285
- tokenize: syntax_createTokenize('text'),
127286
- };
127287
- // ---------------------------------------------------------------------------
127288
- // Extension
127289
- // ---------------------------------------------------------------------------
127290
- function htmlBlockComponent() {
127291
- return {
127292
- flow: {
127293
- [codes.lessThan]: [htmlBlockComponentFlowConstruct],
127294
- },
127295
- text: {
127296
- [codes.lessThan]: [htmlBlockComponentTextConstruct],
127297
- },
127298
- };
127299
- }
127300
-
127301
- ;// ./lib/micromark/html-block-component/index.ts
127302
-
127303
-
127304
127473
  ;// ./lib/utils/extractMagicBlocks.ts
127305
127474
  /**
127306
127475
  * The content matching in this regex captures everything between `[block:TYPE]`