@readme/markdown 14.11.4 → 14.11.5

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.js CHANGED
@@ -97322,6 +97322,19 @@ const compile_list_item_listItem = (node, parent, state, info) => {
97322
97322
  const plain_plain = (node) => node.value;
97323
97323
  /* harmony default export */ const compile_plain = (plain_plain);
97324
97324
 
97325
+ ;// ./processor/compile/text.ts
97326
+
97327
+ // A `_` flanked by word characters can never open or close emphasis under
97328
+ // CommonMark's flanking rules, so the escape mdast-util-to-markdown adds to
97329
+ // intraword underscores is unnecessary and only produces noisy `\_` diffs.
97330
+ // Uses Unicode letter/number classes so non-ASCII words (e.g. `é_pay`) match.
97331
+ const INTRAWORD_UNDERSCORE_ESCAPE = /(?<=[\p{L}\p{N}_])\\_(?=[\p{L}\p{N}_]|\\_)/gu;
97332
+ const compile_text_text = (node, parent, state, info) => {
97333
+ const serialized = handle.text(node, parent, state, info);
97334
+ return serialized.replace(INTRAWORD_UNDERSCORE_ESCAPE, '_');
97335
+ };
97336
+ /* harmony default export */ const compile_text = (compile_text_text);
97337
+
97325
97338
  ;// ./processor/compile/index.ts
97326
97339
 
97327
97340
 
@@ -97334,11 +97347,11 @@ const plain_plain = (node) => node.value;
97334
97347
 
97335
97348
 
97336
97349
 
97350
+
97337
97351
  function compilers(mdxish = false) {
97338
97352
  const data = this.data();
97339
97353
  const toMarkdownExtensions = data.toMarkdownExtensions || (data.toMarkdownExtensions = []);
97340
97354
  const handlers = {
97341
- ...(mdxish && { [NodeTypes.anchor]: compile_anchor }),
97342
97355
  [NodeTypes.callout]: compile_callout,
97343
97356
  [NodeTypes.codeTabs]: compile_code_tabs,
97344
97357
  [NodeTypes.embedBlock]: compile_embed,
@@ -97346,15 +97359,18 @@ function compilers(mdxish = false) {
97346
97359
  [NodeTypes.glossary]: compile_compatibility,
97347
97360
  [NodeTypes.htmlBlock]: html_block,
97348
97361
  [NodeTypes.reusableContent]: compile_compatibility,
97349
- ...(mdxish && { [NodeTypes.variable]: compile_variable }),
97350
97362
  embed: compile_compatibility,
97351
97363
  escape: compile_compatibility,
97352
97364
  figure: compile_compatibility,
97353
97365
  html: compile_compatibility,
97354
97366
  i: compile_compatibility,
97355
- ...(mdxish && { listItem: list_item }),
97356
97367
  plain: compile_plain,
97357
97368
  yaml: compile_compatibility,
97369
+ // needed only for mdxish
97370
+ ...(mdxish && { [NodeTypes.anchor]: compile_anchor }),
97371
+ ...(mdxish && { listItem: list_item }),
97372
+ ...(mdxish && { text: compile_text }),
97373
+ ...(mdxish && { [NodeTypes.variable]: compile_variable }),
97358
97374
  };
97359
97375
  toMarkdownExtensions.push({ extensions: [{ handlers }] });
97360
97376
  }
@@ -102216,70 +102232,168 @@ const mdxishInlineMdxComponents = () => tree => {
102216
102232
  };
102217
102233
  /* harmony default export */ const inline_mdx_blocks = (mdxishInlineMdxComponents);
102218
102234
 
102235
+ ;// ./processor/transform/mdxish/terminate-html-flow-blocks.ts
102236
+
102237
+
102238
+
102239
+ const STANDALONE_HTML_LINE_REGEX = /^(<[a-z][^<>]*>|<\/[a-z][^<>]*>)+\s*$/;
102240
+ const HTML_LINE_WITH_CONTENT_REGEX = /^<[a-z][^<>]*>.*<\/[a-z][^<>]*>(?:[^<]*)$/;
102241
+ // A line that is exactly one lowercase opening (or self-closing) tag — the shape
102242
+ // that opens a CommonMark type-6/7 HTML block and swallows the lines below it.
102243
+ const SINGLE_OPENING_TAG_REGEX = /^<[a-z][^<>]*>$/;
102244
+ // Tags whose contents must be preserved as is, inserting a blank line after the
102245
+ // opener corrupts the payload.
102246
+ // htmlRawNames here refer to <pre>, <textarea>, <script>, <style>
102247
+ const RAW_CONTENT_TAGS = [...htmlRawNames, ...HTML_TABLE_STRUCTURE_TAGS];
102248
+ // The `(?=[\s/>])` lookahead avoids false matches on lookalike names like `<script-foo>`.
102249
+ const RAW_CONTENT_TAG_MATCHERS = RAW_CONTENT_TAGS.map(tag => ({
102250
+ open: new RegExp(`<${tag}(?=[\\s/>])[^>]*?(?<!/)>`, 'gi'),
102251
+ close: new RegExp(`</${tag}(?=[\\s>])[^>]*>`, 'gi'),
102252
+ }));
102253
+ function isLineHtml(line) {
102254
+ return STANDALONE_HTML_LINE_REGEX.test(line) || HTML_LINE_WITH_CONTENT_REGEX.test(line);
102255
+ }
102256
+ // Per-tag {opens, closes} counts of RAW_CONTENT_TAGS on one line — feeds both the
102257
+ // per-line net-open check and the cumulative still-open depth tracking.
102258
+ function countRawContentTags(line) {
102259
+ return RAW_CONTENT_TAG_MATCHERS.map(({ open, close }) => ({
102260
+ opens: (line.match(open) ?? []).length,
102261
+ closes: (line.match(close) ?? []).length,
102262
+ }));
102263
+ }
102264
+ // Indentation width in columns, counting a tab as 4 per CommonMark.
102265
+ function indentWidth(line) {
102266
+ const leading = line.match(/^[ \t]*/)?.[0] ?? '';
102267
+ return leading.replace(/\t/g, ' ').length;
102268
+ }
102269
+ /**
102270
+ * Decides whether a blank line belongs between `line` and `next` so the HTML
102271
+ * flow block opened on `line` terminates and `next` parses as markdown.
102272
+ */
102273
+ function shouldTerminateBetween(line, next, { insideRawContent, lineLeavesRawOpen }) {
102274
+ if (next.trim().length === 0)
102275
+ return false;
102276
+ const currentIndent = indentWidth(line);
102277
+ const nextIndent = indentWidth(next);
102278
+ // 4+ columns is CommonMark indented-code territory: an inserted blank line
102279
+ // would turn the next line into a code block instead of freeing it (#1344).
102280
+ if (currentIndent > 3 || nextIndent > 3)
102281
+ return false;
102282
+ const currentTrimmed = line.trim();
102283
+ const nextTrimmed = next.trim();
102284
+ if (!isLineHtml(currentTrimmed) || isLineHtml(nextTrimmed) || lineLeavesRawOpen) {
102285
+ return false;
102286
+ }
102287
+ // Column-0 pairs keep the original (pre-relaxation) rule, deliberately ignoring
102288
+ // `insideRawContent`: one unclosed <pre>/<table> typo would otherwise suppress
102289
+ // termination for the whole rest of the document.
102290
+ if (currentIndent === 0 && nextIndent === 0)
102291
+ return true;
102292
+ // Indented shapes (either line at 1–3 columns) are stricter: only a lone opening
102293
+ // tag followed by markdown. A next line opening a tag or expression must stay
102294
+ // glued for the mdxComponent tokenizer; raw-content payloads stay byte-exact.
102295
+ return (!insideRawContent &&
102296
+ SINGLE_OPENING_TAG_REGEX.test(currentTrimmed) &&
102297
+ !nextTrimmed.startsWith('<') &&
102298
+ !nextTrimmed.startsWith('{'));
102299
+ }
102300
+ /**
102301
+ * CommonMark HTML blocks (types 6/7) end only at a blank line, so markdown on the
102302
+ * next line is swallowed as HTML (`## heading` after `<div>` renders as literal text).
102303
+ * Inserts the missing blank line; the gating rules live in `shouldTerminateBetween`.
102304
+ *
102305
+ * @link https://spec.commonmark.org/0.29/#html-blocks
102306
+ */
102307
+ function terminateHtmlFlowBlocks(content) {
102308
+ const { protectedContent, protectedCode } = protectCodeBlocks(content);
102309
+ const lines = protectedContent.split('\n');
102310
+ const result = [];
102311
+ // Per-tag count of still-open raw-content elements at the current boundary.
102312
+ const rawContentDepths = RAW_CONTENT_TAG_MATCHERS.map(() => 0);
102313
+ for (let i = 0; i < lines.length; i += 1) {
102314
+ const line = lines[i];
102315
+ result.push(line);
102316
+ const tagCounts = countRawContentTags(line);
102317
+ tagCounts.forEach(({ opens, closes }, tagIndex) => {
102318
+ rawContentDepths[tagIndex] = Math.max(0, rawContentDepths[tagIndex] + opens - closes);
102319
+ });
102320
+ const next = lines[i + 1];
102321
+ const rawContentFacts = {
102322
+ insideRawContent: rawContentDepths.some(depth => depth > 0),
102323
+ lineLeavesRawOpen: tagCounts.some(({ opens, closes }) => opens > closes),
102324
+ };
102325
+ if (next !== undefined && shouldTerminateBetween(line, next, rawContentFacts)) {
102326
+ result.push('');
102327
+ }
102328
+ }
102329
+ return restoreCodeBlocks(result.join('\n'), protectedCode);
102330
+ }
102331
+
102219
102332
  ;// ./processor/transform/mdxish/components/mdx-blocks.ts
102220
102333
 
102221
102334
 
102222
102335
 
102223
102336
 
102224
102337
 
102225
- /** Matches a JSX attribute expression (e.g. `key={i}`) anywhere in a string. */
102338
+
102339
+
102340
+ // Matches a JSX attribute expression (e.g. `key={i}`) anywhere in a string. */
102226
102341
  const NESTED_ATTR_EXPRESSION_RE = /[\w-]+\s*=\s*\{/;
102227
- /**
102228
- * Reduce leading whitespace on all lines just enough to prevent
102229
- * remark from treating indented content as code blocks (4+ spaces).
102230
- * Preserves relative indentation so whitespace text nodes are
102231
- * maintained in the HAST output.
102342
+ // Name shape mirrors `componentTagPattern`; the lookbehind skips the inner tag
102343
+ // of a legacy `<<VARIABLE>>`.
102344
+ const NESTED_COMPONENT_TAG_RE = /(?<!<)<([A-Z][A-Za-z0-9_]*)[\s/>]/g;
102345
+ // Excludes tags with dedicated transformers (`Table`, `HTMLBlock`, inline
102346
+ // components), which expect their wrapper to stay raw.
102347
+ const hasNestedGenericComponentTag = (content) => [...content.matchAll(NESTED_COMPONENT_TAG_RE)].some(match => !GENERIC_MDX_COMPONENT_EXCLUDED_TAGS.has(match[1]));
102348
+ /**
102349
+ * Strip the shared leading indentation from a component body so readability indentation
102350
+ * isn't parsed as indented code (4+ spaces), e.g. ` <p>` / ` text` -> `<p>` / ` text`.
102351
+ * Relative indentation is kept, so content genuinely 4+ columns deeper stays code. We
102352
+ * only strip when a line actually reaches 4 columns; otherwise the body is left as-is so
102353
+ * its leading whitespace survives as text nodes (mixed component + HTML content needs it).
102232
102354
  */
102233
102355
  function safeDeindent(text) {
102234
102356
  const lines = text.split('\n');
102235
102357
  const nonEmptyLines = lines.filter(line => line.trim().length > 0);
102236
102358
  if (nonEmptyLines.length === 0)
102237
102359
  return text;
102238
- const minIndent = Math.min(...nonEmptyLines.map(line => {
102239
- const match = line.match(/^(\s*)/);
102240
- return match ? match[1].length : 0;
102241
- }));
102242
- // Only strip enough indent to keep all lines below the 4-space code threshold
102243
- const stripAmount = Math.max(0, minIndent - 3);
102360
+ // Indent counts characters (tab = 1), unlike indentWidth's CommonMark columns
102361
+ // (tab = 4) in terminate-html-flow-blocks.
102362
+ const indents = nonEmptyLines.map(line => line.match(/^(\s*)/)?.[1].length ?? 0);
102363
+ const minIndent = Math.min(...indents);
102364
+ const maxIndent = Math.max(...indents);
102365
+ const stripAmount = maxIndent > 3 ? minIndent : 0;
102244
102366
  if (stripAmount === 0)
102245
102367
  return text;
102246
102368
  return lines.map(line => line.slice(stripAmount)).join('\n');
102247
102369
  }
102248
102370
  /**
102249
- * Parse markdown content into mdast children nodes.
102250
- * Dedents the content first to prevent indented component content
102251
- * (from nested components) from being treated as code blocks.
102371
+ * Parse component-body markdown into mdast children. Dedenting shifts columns and
102372
+ * stales the top-level `terminateHtmlFlowBlocks` decisions, so that one preprocessor
102373
+ * re-runs here; other column-anchored fixups (compact headings, tables) do not.
102252
102374
  */
102253
102375
  const parseMdChildren = (value, safeMode) => {
102254
- const parsed = getInlineMdProcessor({ safeMode }).parse(safeDeindent(value).trim());
102376
+ const parsed = getInlineMdProcessor({ safeMode }).parse(terminateHtmlFlowBlocks(safeDeindent(value).trim()));
102255
102377
  return parsed.children || [];
102256
102378
  };
102257
- /**
102258
- * Parse substring content of a node and update the parent's children to include the new nodes.
102259
- */
102379
+ // Parses trailing content into sibling nodes and re-queues the parent so any
102380
+ // components among them get processed.
102260
102381
  const parseSibling = (stack, parent, index, sibling, safeMode) => {
102261
102382
  const siblingNodes = parseMdChildren(sibling, safeMode);
102262
- // The new sibling nodes might contain new components to be processed
102263
102383
  if (siblingNodes.length > 0) {
102264
102384
  parent.children.splice(index + 1, 0, ...siblingNodes);
102265
102385
  stack.push(parent);
102266
102386
  }
102267
102387
  };
102268
- /**
102269
- * Build a position ending at `consumedLength` into the html node's value, so the
102270
- * component doesn't claim trailing content the tokenizer swallowed into one node.
102271
- */
102388
+ // Ends the position at `consumedLength` so the component doesn't claim trailing
102389
+ // content the tokenizer swallowed into the same html node.
102272
102390
  const positionEndingAtConsumed = (nodePosition, value, consumedLength) => {
102273
102391
  if (!nodePosition?.start)
102274
102392
  return nodePosition;
102275
102393
  return { start: nodePosition.start, end: pointAfter(nodePosition.start, value.slice(0, consumedLength)) };
102276
102394
  };
102277
- /**
102278
- * Build a position ending right after the last occurrence of `closingTag` within
102279
- * this node's span in the original source. Used in the trailing-content path so
102280
- * the offset is computed against the real source bytes (including blockquote/list
102281
- * prefixes that were stripped from the html node's value).
102282
- */
102395
+ // Like `positionEndingAtConsumed`, but measures against the original source so
102396
+ // blockquote/list prefixes stripped from the html node's value are counted.
102283
102397
  const positionEndingAtClosingTagInSource = (nodePosition, closingTag, source) => {
102284
102398
  if (!nodePosition?.start || !nodePosition.end)
102285
102399
  return nodePosition;
@@ -102290,9 +102404,6 @@ const positionEndingAtClosingTagInSource = (nodePosition, closingTag, source) =>
102290
102404
  const consumed = nodeSource.slice(0, closingTagOffset + closingTag.length);
102291
102405
  return { start: nodePosition.start, end: pointAfter(nodePosition.start, consumed) };
102292
102406
  };
102293
- /**
102294
- * Create an MdxJsxFlowElement node from component data.
102295
- */
102296
102407
  const createComponentNode = ({ tag, attributes, children, startPosition, endPosition }) => ({
102297
102408
  type: 'mdxJsxFlowElement',
102298
102409
  name: tag,
@@ -102345,8 +102456,7 @@ const mdxishMdxComponentBlocks = (opts = {}) => (tree, file) => {
102345
102456
  if ('children' in node && Array.isArray(node.children)) {
102346
102457
  stack.push(node);
102347
102458
  }
102348
- // Only visit HTML nodes with an actual html tag,
102349
- // which means a potential unparsed MDX component
102459
+ // Only html nodes can be an unparsed MDX component.
102350
102460
  const value = node.value;
102351
102461
  if (node.type !== 'html' || typeof value !== 'string')
102352
102462
  return;
@@ -102355,32 +102465,25 @@ const mdxishMdxComponentBlocks = (opts = {}) => (tree, file) => {
102355
102465
  if (!parsed)
102356
102466
  return;
102357
102467
  const { tag, attributes, selfClosing, contentAfterTag = '' } = parsed;
102358
- // Offset of `trimmed` within the (possibly whitespace-padded) html node value,
102359
- // so consumed-length math maps back onto the node's real source offsets.
102468
+ // Offsets so consumed-length math maps back onto the node's real source.
102360
102469
  const leadingWhitespace = value.length - value.trimStart().length;
102361
- // Index right after the opening tag's `>` within `trimmed`.
102362
102470
  const openingTagEnd = trimmed.length - contentAfterTag.length;
102363
- // Skip tags that have dedicated transformers
102364
102471
  if (GENERIC_MDX_COMPONENT_EXCLUDED_TAGS.has(tag))
102365
- return;
102472
+ return; // owned by dedicated transformers
102366
102473
  const isPascal = isPascalCase(tag);
102367
- // Lowercase inline tags (inside a paragraph) with `{…}` attributes are
102368
- // promoted to `mdxJsxTextElement` by mdxishInlineComponentBlocks. Skip
102369
- // them here so they stay as html for that pass; PascalCase components
102370
- // keep going through this transformer (they stay flow-level even when
102371
- // inline, which is how ReadMe's custom components are modeled).
102474
+ // Lowercase inline tags with `{…}` attributes belong to
102475
+ // mdxishInlineComponentBlocks; leave them as html for that pass. PascalCase
102476
+ // components stay flow-level even when inline (ReadMe's component model).
102372
102477
  if (!isPascal && parent.type === 'paragraph')
102373
102478
  return;
102374
- // Lowercase HTML tags are eligible when they (or a descendant tag in their
102375
- // content, e.g. a `.map()` body returning JSX with `key={i}`) carry a
102376
- // JSX-expression attribute. Without this, a wrapper `<div>` with only plain
102377
- // attributes (or none) swallows its whole nested block — including any
102378
- // `style={{...}}`/`.map()` JSX inside it — as literal HTML text, which
102379
- // rehype-raw's parse5 pass then garbles (it has no notion of JS expressions).
102380
- // Plain HTML with no expressions anywhere stays as an html node so
102381
- // rehype-raw handles it as normal.
102479
+ // A lowercase wrapper is only promoted when it (or a descendant) carries a
102480
+ // JSX expression or nests a component; otherwise it would swallow that inner
102481
+ // JSX/component as literal text that rehype-raw's parse5 pass can't handle.
102482
+ // Table-structural wrappers are excluded `mdxishTables` re-parses those.
102382
102483
  const hasNestedExpressionAttr = !selfClosing && NESTED_ATTR_EXPRESSION_RE.test(contentAfterTag);
102383
- if (!isPascal && !hasExpressionAttr(attributes) && !hasNestedExpressionAttr)
102484
+ const isTableStructuralTag = tag === 'table' || tableTags.has(tag);
102485
+ const hasNestedComponentTag = !selfClosing && !isTableStructuralTag && hasNestedGenericComponentTag(contentAfterTag);
102486
+ if (!isPascal && !hasExpressionAttr(attributes) && !hasNestedExpressionAttr && !hasNestedComponentTag)
102384
102487
  return;
102385
102488
  const closingTagStr = `</${tag}>`;
102386
102489
  // Case 1: Self-closing tag
@@ -102394,7 +102497,6 @@ const mdxishMdxComponentBlocks = (opts = {}) => (tree, file) => {
102394
102497
  endPosition: positionEndingAtConsumed(node.position, value, leadingWhitespace + openingTagEnd),
102395
102498
  });
102396
102499
  substituteNodeWithMdxNode(parent, index, componentNode);
102397
- // Check and parse if there's relevant content after the current closing tag
102398
102500
  const remainingContent = contentAfterTag.trim();
102399
102501
  if (remainingContent) {
102400
102502
  parseSibling(stack, parent, index, remainingContent, safeMode);
@@ -102403,17 +102505,14 @@ const mdxishMdxComponentBlocks = (opts = {}) => (tree, file) => {
102403
102505
  }
102404
102506
  // Case 2: Self-contained block (closing tag in content)
102405
102507
  if (contentAfterTag.includes(closingTagStr)) {
102406
- // Find the first closing tag
102407
102508
  const closingTagIndex = contentAfterTag.lastIndexOf(closingTagStr);
102408
- // Pass raw (untrimmed) content so dedent in parseMdChildren can
102409
- // normalize indentation before trimming
102509
+ // Untrimmed so parseMdChildren can dedent before trimming.
102410
102510
  const componentInnerContent = contentAfterTag.substring(0, closingTagIndex);
102411
102511
  const contentAfterClose = contentAfterTag.substring(closingTagIndex + closingTagStr.length).trim();
102412
102512
  let parsedChildren = componentInnerContent.trim()
102413
102513
  ? parseMdChildren(componentInnerContent, safeMode)
102414
102514
  : [];
102415
- // Lowercase HTML tags are usually inline (e.g. <a>, <span>). Remark wraps
102416
- // bare text in a paragraph; unwrap when there's exactly one paragraph so
102515
+ // Lowercase tags are usually inline; unwrap a sole paragraph so their
102417
102516
  // phrasing content isn't spuriously block-wrapped.
102418
102517
  if (!isPascal && parsedChildren.length === 1 && parsedChildren[0].type === 'paragraph') {
102419
102518
  parsedChildren = parsedChildren[0].children;
@@ -102423,12 +102522,9 @@ const mdxishMdxComponentBlocks = (opts = {}) => (tree, file) => {
102423
102522
  attributes,
102424
102523
  children: parsedChildren,
102425
102524
  startPosition: node.position,
102426
- // When trailing content follows the closing tag, compute the end position precisely
102427
- // within the html node's value so the component doesn't claim that content.
102428
- // Prefer source-based positioning when the original source is available: the html
102429
- // node's value has '> '/space prefixes stripped for blockquotes/list items, so
102430
- // positionEndingAtConsumed would undercount source offsets. When the entire node
102431
- // is consumed, use the original node position directly.
102525
+ // With trailing content, end precisely at the closing tag. Prefer source
102526
+ // offsets when available (the node's value strips blockquote/list
102527
+ // prefixes); otherwise fall back to the whole node position.
102432
102528
  endPosition: contentAfterClose
102433
102529
  ? source
102434
102530
  ? positionEndingAtClosingTagInSource(node.position, closingTagStr, source)
@@ -102436,17 +102532,16 @@ const mdxishMdxComponentBlocks = (opts = {}) => (tree, file) => {
102436
102532
  : node.position,
102437
102533
  });
102438
102534
  substituteNodeWithMdxNode(parent, index, componentNode);
102439
- // After the closing tag, there might be more content to be processed
102535
+ // Re-queue whichever side may hold further components.
102440
102536
  if (contentAfterClose) {
102441
102537
  parseSibling(stack, parent, index, contentAfterClose, safeMode);
102442
102538
  }
102443
102539
  else if (componentNode.children.length > 0) {
102444
- // The content inside the component block might contain new components to be processed
102445
102540
  stack.push(componentNode);
102446
102541
  }
102447
102542
  }
102448
102543
  };
102449
- // Process the nodes with the components depth-first to maintain the correct order of the nodes
102544
+ // Depth-first so nodes keep their source order.
102450
102545
  while (stack.length) {
102451
102546
  const parent = stack.pop();
102452
102547
  if (parent?.children) {
@@ -105279,73 +105374,6 @@ function normalizeTableSeparator(content) {
105279
105374
  }
105280
105375
  /* harmony default export */ const normalize_table_separator = ((/* unused pure expression or super */ null && (normalizeTableSeparator)));
105281
105376
 
105282
- ;// ./processor/transform/mdxish/terminate-html-flow-blocks.ts
105283
-
105284
-
105285
-
105286
- const STANDALONE_HTML_LINE_REGEX = /^(<[a-z][^<>]*>|<\/[a-z][^<>]*>)+\s*$/;
105287
- const HTML_LINE_WITH_CONTENT_REGEX = /^<[a-z][^<>]*>.*<\/[a-z][^<>]*>(?:[^<]*)$/;
105288
- // Tags whose contents must be preserved as is, inserting a blank line after the
105289
- // opener corrupts the payload.
105290
- // htmlRawNames here refer to <pre>, <textarea>, <script>, <style>
105291
- const RAW_CONTENT_TAGS = [...htmlRawNames, ...HTML_TABLE_STRUCTURE_TAGS];
105292
- // The `(?=[\s/>])` lookahead avoids false matches on lookalike names like `<script-foo>`.
105293
- const RAW_CONTENT_TAG_MATCHERS = RAW_CONTENT_TAGS.map(tag => ({
105294
- open: new RegExp(`<${tag}(?=[\\s/>])[^>]*?(?<!/)>`, 'gi'),
105295
- close: new RegExp(`</${tag}(?=[\\s>])[^>]*>`, 'gi'),
105296
- }));
105297
- function isLineHtml(line) {
105298
- return STANDALONE_HTML_LINE_REGEX.test(line) || HTML_LINE_WITH_CONTENT_REGEX.test(line);
105299
- }
105300
- // True if any RAW_CONTENT_TAGS opener on this line is not closed on the same line.
105301
- function hasUnclosedRawContentOpener(line) {
105302
- return RAW_CONTENT_TAG_MATCHERS.some(({ open, close }) => {
105303
- const opens = (line.match(open) ?? []).length;
105304
- const closes = (line.match(close) ?? []).length;
105305
- return opens > closes;
105306
- });
105307
- }
105308
- /**
105309
- * Preprocessor to terminate HTML flow blocks.
105310
- *
105311
- * In CommonMark, HTML blocks (types 6 and 7) only terminate on a blank line.
105312
- * Without one, any content on the next line is consumed as part of the HTML block
105313
- * and never parsed as its own construct. For example, a `[block:callout]` immediately
105314
- * following `<div><p></p></div>` gets swallowed into the HTML flow token.
105315
- *
105316
- * @link https://spec.commonmark.org/0.29/#html-blocks
105317
- *
105318
- * This preprocessor inserts a blank line after standalone HTML lines when the next
105319
- * line is non-blank and not an HTML construct, ensuring micromark's HTML flow
105320
- * tokenizer terminates and subsequent content is parsed independently.
105321
- *
105322
- * Conditions:
105323
- * 1. Only non-indented lines with lowercase tag names are considered. Uppercase tags
105324
- * (e.g. `<Table>`, `<MyComponent>`) are JSX custom components and don't trigger
105325
- * CommonMark HTML blocks.
105326
- * 2. Lines inside protected blocks (e.g. fenced code) are left untouched.
105327
- * 3. Lines with an unclosed RAW_CONTENT_TAGS opener are exempted.
105328
- */
105329
- function terminateHtmlFlowBlocks(content) {
105330
- const { protectedContent, protectedCode } = protectCodeBlocks(content);
105331
- const lines = protectedContent.split('\n');
105332
- const result = [];
105333
- for (let i = 0; i < lines.length; i += 1) {
105334
- result.push(lines[i]);
105335
- // Skip blank & indented lines
105336
- if (i >= lines.length - 1 || lines[i + 1].trim().length === 0 || lines[i + 1].startsWith(' ') || lines[i + 1].startsWith('\t')) {
105337
- // eslint-disable-next-line no-continue
105338
- continue;
105339
- }
105340
- const isCurrentLineHtml = isLineHtml(lines[i]);
105341
- const isNextLineHtml = isLineHtml(lines[i + 1]);
105342
- if (isCurrentLineHtml && !isNextLineHtml && !hasUnclosedRawContentOpener(lines[i])) {
105343
- result.push('');
105344
- }
105345
- }
105346
- return restoreCodeBlocks(result.join('\n'), protectedCode);
105347
- }
105348
-
105349
105377
  ;// ./processor/transform/mdxish/variables-code.ts
105350
105378
 
105351
105379
 
@@ -106213,6 +106241,12 @@ function mdxishMdastToMd(mdast) {
106213
106241
  .use(remarkStringify, {
106214
106242
  bullet: '-',
106215
106243
  emphasis: '_',
106244
+ // Escape literal braces in text so they don't parse as (often
106245
+ // unterminated) MDX expressions on the next round trip.
106246
+ unsafe: [
106247
+ { character: '{', inConstruct: 'phrasing' },
106248
+ { character: '}', inConstruct: 'phrasing' },
106249
+ ],
106216
106250
  });
106217
106251
  return processor.stringify(processor.runSync(mdast));
106218
106252
  }