@readme/markdown 11.11.0 → 11.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/main.js CHANGED
@@ -70584,77 +70584,197 @@ const mdxToHast = () => tree => {
70584
70584
  ;// ./processor/transform/mdxish/mdxish-component-blocks.ts
70585
70585
 
70586
70586
 
70587
- const tagPattern = /^<([A-Z][A-Za-z0-9_]*)([^>]*?)(\/?)>([\s\S]*)?$/;
70588
- const attributePattern = /([a-zA-Z_:][-a-zA-Z0-9_:.]*)(?:\s*=\s*("[^"]*"|'[^']*'|[^\s"'>]+))?/g;
70587
+ const pascalCaseTagPattern = /^<([A-Z][A-Za-z0-9_]*)([^>]*?)(\/?)>([\s\S]*)?$/;
70588
+ const tagAttributePattern = /([a-zA-Z_:][-a-zA-Z0-9_:.]*)(?:\s*=\s*("[^"]*"|'[^']*'|[^\s"'>]+))?/g;
70589
+ /**
70590
+ * Maximum number of siblings to scan forward when looking for a closing tag
70591
+ * to avoid scanning too far and degrading performance
70592
+ */
70593
+ const MAX_LOOKAHEAD = 30;
70594
+ /**
70595
+ * Tags that have dedicated transformers and should NOT be handled by this plugin.
70596
+ * These components have special parsing requirements that the generic component
70597
+ * block transformer cannot handle correctly.
70598
+ */
70599
+ const EXCLUDED_TAGS = new Set(['HTMLBlock', 'Table']);
70589
70600
  const inlineMdProcessor = unified().use(remarkParse);
70590
70601
  const isClosingTag = (value, tag) => value.trim() === `</${tag}>`;
70591
- // Remove matching closing tag from paragraph children; returns updated paragraph and removal status
70592
- const stripClosingFromParagraph = (node, tag) => {
70593
- if (!Array.isArray(node.children))
70594
- return { paragraph: node, found: false };
70595
- const children = [...node.children];
70596
- const closingIndex = children.findIndex(child => child.type === 'html' && isClosingTag(child.value || '', tag));
70597
- if (closingIndex === -1)
70598
- return { paragraph: node, found: false };
70599
- children.splice(closingIndex, 1);
70600
- return {
70601
- paragraph: { ...node, children },
70602
- found: true,
70603
- };
70604
- };
70605
- // Replace two child nodes (opening HTML tag + paragraph) with a single replacement node
70606
- const replaceChild = (parent, index, replacement) => {
70607
- parent.children.splice(index, 2, replacement);
70608
- };
70609
- // Parse markdown inside a component's inline content into mdast children.
70602
+ /**
70603
+ * Parse markdown content into mdast children nodes.
70604
+ */
70610
70605
  const parseMdChildren = (value) => {
70611
70606
  const parsed = inlineMdProcessor.parse(value);
70612
70607
  return parsed.children || [];
70613
70608
  };
70614
- // Convert raw attribute string into mdxJsxAttribute entries (strings only; no expressions).
70615
- // Handles both key-value attributes (theme="info") and boolean attributes (empty).
70609
+ /**
70610
+ * Convert raw attribute string into mdxJsxAttribute entries.
70611
+ * Handles both key-value attributes (theme="info") and boolean attributes (empty).
70612
+ */
70616
70613
  const parseAttributes = (raw) => {
70617
70614
  const attributes = [];
70618
70615
  const attrString = raw.trim();
70619
70616
  if (!attrString)
70620
70617
  return attributes;
70621
- // Reset regex lastIndex since it's a global regex that maintains state
70622
- attributePattern.lastIndex = 0;
70623
- let match = attributePattern.exec(attrString);
70618
+ tagAttributePattern.lastIndex = 0;
70619
+ let match = tagAttributePattern.exec(attrString);
70624
70620
  while (match !== null) {
70625
70621
  const [, attrName, attrValue] = match;
70626
- // Boolean attribute (no value) -> set to null
70627
- // Attribute with value -> clean and set string value
70628
- // Note: Attribute value types can't directly be numbers & booleans. String, nulls, undefined are supported.
70629
70622
  const value = attrValue ? attrValue.replace(/^['"]|['"]$/g, '') : null;
70630
- attributes.push({
70631
- type: 'mdxJsxAttribute',
70632
- name: attrName,
70633
- value,
70634
- });
70635
- match = attributePattern.exec(attrString);
70623
+ attributes.push({ type: 'mdxJsxAttribute', name: attrName, value });
70624
+ match = tagAttributePattern.exec(attrString);
70636
70625
  }
70637
70626
  return attributes;
70638
70627
  };
70639
- // Parse a single HTML-ish tag string into tag name, attributes, self-closing flag, and inline content.
70628
+ /**
70629
+ * Parse an HTML tag string into structured data.
70630
+ */
70640
70631
  const parseTag = (value) => {
70641
- const match = value.match(tagPattern);
70632
+ const match = value.match(pascalCaseTagPattern);
70642
70633
  if (!match)
70643
70634
  return null;
70644
- const [, tag, attrString = '', selfClosing = '', content = ''] = match;
70645
- const attributes = parseAttributes(attrString);
70635
+ const [, tag, attrString = '', selfClosing = '', contentAfterTag = ''] = match;
70646
70636
  return {
70647
70637
  tag,
70648
- attributes,
70638
+ attributes: parseAttributes(attrString),
70649
70639
  selfClosing: !!selfClosing,
70650
- content,
70640
+ contentAfterTag,
70651
70641
  };
70652
70642
  };
70653
- // Transform PascalCase HTML blocks into mdxJsxFlowElement nodes.
70654
- // Remark parses unknown tags as raw HTML; we rewrite them so MDX/rehype treats them as components.
70643
+ /**
70644
+ * Create an MdxJsxFlowElement node from component data.
70645
+ */
70646
+ const createComponentNode = ({ tag, attributes, children, startPosition, endPosition }) => ({
70647
+ type: 'mdxJsxFlowElement',
70648
+ name: tag,
70649
+ attributes,
70650
+ children,
70651
+ position: {
70652
+ start: startPosition?.start,
70653
+ end: endPosition?.end ?? startPosition?.end,
70654
+ },
70655
+ });
70656
+ /**
70657
+ * Remove a closing tag from a paragraph's children and return the updated paragraph.
70658
+ */
70659
+ const stripClosingTagFromParagraph = (node, tag) => {
70660
+ if (!Array.isArray(node.children))
70661
+ return { paragraph: node, found: false };
70662
+ const children = [...node.children];
70663
+ const closingIndex = children.findIndex(child => child.type === 'html' && isClosingTag(child.value || '', tag));
70664
+ if (closingIndex === -1)
70665
+ return { paragraph: node, found: false };
70666
+ children.splice(closingIndex, 1);
70667
+ return { paragraph: { ...node, children }, found: true };
70668
+ };
70669
+ /**
70670
+ * Scan forward through siblings to find a closing tag.
70671
+ * Handles:
70672
+ * - Exact match HTML siblings (e.g., `</Tag>`)
70673
+ * - HTML siblings with embedded closing tag (e.g., `...\n</Tag>`)
70674
+ * - Paragraph siblings containing the closing tag as a child
70675
+ *
70676
+ * Returns null if not found within MAX_LOOKAHEAD siblings
70677
+ */
70678
+ const scanForClosingTag = (parent, startIndex, tag) => {
70679
+ const closingTagStr = `</${tag}>`;
70680
+ const maxIndex = Math.min(startIndex + MAX_LOOKAHEAD, parent.children.length);
70681
+ let i = startIndex + 1;
70682
+ for (; i < maxIndex; i += 1) {
70683
+ const sibling = parent.children[i];
70684
+ // Check HTML siblings
70685
+ if (sibling.type === 'html') {
70686
+ const siblingValue = sibling.value || '';
70687
+ // Exact match (standalone closing tag)
70688
+ if (isClosingTag(siblingValue, tag)) {
70689
+ return { closingIndex: i, extraClosingChildren: [] };
70690
+ }
70691
+ // Embedded closing tag (closing tag at end of HTML block content)
70692
+ if (siblingValue.includes(closingTagStr)) {
70693
+ const contentBeforeClose = siblingValue.substring(0, siblingValue.lastIndexOf(closingTagStr)).trim();
70694
+ const extraChildren = contentBeforeClose
70695
+ ? parseMdChildren(contentBeforeClose)
70696
+ : [];
70697
+ return { closingIndex: i, extraClosingChildren: extraChildren };
70698
+ }
70699
+ }
70700
+ // Check paragraph siblings
70701
+ if (sibling.type === 'paragraph') {
70702
+ const { paragraph, found } = stripClosingTagFromParagraph(sibling, tag);
70703
+ if (found) {
70704
+ return { closingIndex: i, extraClosingChildren: [], strippedParagraph: paragraph };
70705
+ }
70706
+ }
70707
+ }
70708
+ if (i < parent.children.length) {
70709
+ // eslint-disable-next-line no-console
70710
+ console.warn(`Closing tag </${tag}> not found within ${MAX_LOOKAHEAD} siblings, stopping scan`);
70711
+ }
70712
+ return null;
70713
+ };
70714
+ const substituteNodeWithMdxNode = (parent, index, mdxNode) => {
70715
+ parent.children.splice(index, 1, mdxNode);
70716
+ };
70717
+ /**
70718
+ * Transform PascalCase HTML nodes into mdxJsxFlowElement nodes.
70719
+ *
70720
+ * Remark parses unknown/custom component tags as raw HTML nodes.
70721
+ * These are the custom readme MDX syntax for components.
70722
+ * This transformer identifies these patterns and converts them to proper MDX JSX elements so they
70723
+ * can be accurately recognized and rendered later with their component definition code.
70724
+ * Though for some tags, we need to handle them specially
70725
+ *
70726
+ * ## Supported HTML Structures
70727
+ *
70728
+ * ### 1. Self-closing tags
70729
+ * ```
70730
+ * <Component />
70731
+ * ```
70732
+ * Parsed as: `html: "<Component />"`
70733
+ *
70734
+ * ### 2. Self-contained blocks (entire component in single HTML node)
70735
+ * ```
70736
+ * <Button>Click me</Button>
70737
+ * ```
70738
+ * ```
70739
+ * <Component>
70740
+ * <h2>Title</h2>
70741
+ * <p>Content</p>
70742
+ * </Component>
70743
+ * ```
70744
+ * Parsed as: `html: "<Component>\n <h2>Title</h2>\n <p>Content</p>\n</Component>"`
70745
+ * The opening tag, content, and closing tag are all captured in one HTML node.
70746
+ *
70747
+ * ### 3. Multi-sibling components (closing tag in a following sibling)
70748
+ * Handles various structures where the closing tag is in a later sibling, such as:
70749
+ *
70750
+ * #### 3a. Block components (closing tag in sibling paragraph)
70751
+ * ```
70752
+ * <Callout>
70753
+ * Some **markdown** content
70754
+ * </Callout>
70755
+ * ```
70756
+ *
70757
+ * #### 3b. Multi-paragraph components (closing tag several siblings away)
70758
+ * ```
70759
+ * <Callout>
70760
+ *
70761
+ * First paragraph
70762
+ *
70763
+ * Second paragraph
70764
+ * </Callout>
70765
+ * ```
70766
+ *
70767
+ * #### 3c. Nested components split by blank lines (closing tag embedded in HTML sibling)
70768
+ * ```
70769
+ * <Outer>
70770
+ * <Inner>content</Inner>
70771
+ *
70772
+ * <Inner>content</Inner>
70773
+ * </Outer>
70774
+ * ```
70775
+ */
70655
70776
  const mdxishComponentBlocks = () => tree => {
70656
70777
  const stack = [tree];
70657
- // Process children depth-first, rewriting opening/closing component HTML pairs
70658
70778
  const processChildNode = (parent, index) => {
70659
70779
  const node = parent.children[index];
70660
70780
  if (!node)
@@ -70662,46 +70782,66 @@ const mdxishComponentBlocks = () => tree => {
70662
70782
  if ('children' in node && Array.isArray(node.children)) {
70663
70783
  stack.push(node);
70664
70784
  }
70785
+ // Only visit HTML nodes with an actual html tag
70665
70786
  const value = node.value;
70666
70787
  if (node.type !== 'html' || typeof value !== 'string')
70667
70788
  return;
70668
70789
  const parsed = parseTag(value);
70669
70790
  if (!parsed)
70670
70791
  return;
70671
- const { tag, attributes, selfClosing, content = '' } = parsed;
70672
- const extraChildren = content ? parseMdChildren(content.trimStart()) : [];
70792
+ const { tag, attributes, selfClosing, contentAfterTag = '' } = parsed;
70793
+ // Skip tags that have dedicated transformers
70794
+ if (EXCLUDED_TAGS.has(tag))
70795
+ return;
70796
+ const closingTagStr = `</${tag}>`;
70797
+ // Case 1: Self-closing tag
70673
70798
  if (selfClosing) {
70674
- const componentNode = {
70675
- type: 'mdxJsxFlowElement',
70676
- name: tag,
70799
+ const componentNode = createComponentNode({
70800
+ tag,
70677
70801
  attributes,
70678
70802
  children: [],
70679
- position: node.position,
70680
- };
70681
- parent.children.splice(index, 1, componentNode);
70803
+ startPosition: node.position,
70804
+ });
70805
+ substituteNodeWithMdxNode(parent, index, componentNode);
70682
70806
  return;
70683
70807
  }
70684
- const next = parent.children[index + 1];
70685
- if (!next || next.type !== 'paragraph')
70808
+ // Case 2: Self-contained block (closing tag in content)
70809
+ if (contentAfterTag.includes(closingTagStr)) {
70810
+ const componentInnerContent = contentAfterTag.substring(0, contentAfterTag.lastIndexOf(closingTagStr)).trim();
70811
+ const componentNode = createComponentNode({
70812
+ tag,
70813
+ attributes,
70814
+ children: componentInnerContent ? parseMdChildren(componentInnerContent) : [],
70815
+ startPosition: node.position,
70816
+ });
70817
+ substituteNodeWithMdxNode(parent, index, componentNode);
70686
70818
  return;
70687
- const { paragraph, found } = stripClosingFromParagraph(next, tag);
70688
- if (!found)
70819
+ }
70820
+ // Case 3: Multi-sibling component (closing tag in a following sibling)
70821
+ // Scans forward through siblings to find closing tag in HTML or paragraph nodes
70822
+ const scanResult = scanForClosingTag(parent, index, tag);
70823
+ if (!scanResult)
70689
70824
  return;
70690
- const componentNode = {
70691
- type: 'mdxJsxFlowElement',
70692
- name: tag,
70825
+ const { closingIndex, extraClosingChildren, strippedParagraph } = scanResult;
70826
+ const extraChildren = contentAfterTag ? parseMdChildren(contentAfterTag.trimStart()) : [];
70827
+ // Collect all intermediate siblings between opening tag and closing tag
70828
+ const intermediateChildren = parent.children.slice(index + 1, closingIndex);
70829
+ // For paragraph siblings, include the paragraph's children (with closing tag stripped)
70830
+ // For HTML siblings, include any content parsed from before the closing tag
70831
+ const closingChildren = strippedParagraph
70832
+ ? strippedParagraph.children
70833
+ : extraClosingChildren;
70834
+ const componentNode = createComponentNode({
70835
+ tag,
70693
70836
  attributes,
70694
- children: [
70695
- ...extraChildren,
70696
- ...paragraph.children,
70697
- ],
70698
- position: {
70699
- start: node.position?.start,
70700
- end: next.position?.end,
70701
- },
70702
- };
70703
- replaceChild(parent, index, componentNode);
70837
+ children: [...extraChildren, ...intermediateChildren, ...closingChildren],
70838
+ startPosition: node.position,
70839
+ endPosition: parent.children[closingIndex]?.position,
70840
+ });
70841
+ // Remove all nodes from opening tag to closing tag (inclusive) and replace with component node
70842
+ parent.children.splice(index, closingIndex - index + 1, componentNode);
70704
70843
  };
70844
+ // Travel the tree depth-first
70705
70845
  while (stack.length) {
70706
70846
  const parent = stack.pop();
70707
70847
  if (parent?.children) {
@@ -92845,8 +92985,11 @@ function smartCamelCase(str) {
92845
92985
  }
92846
92986
  // Sort by length (longest first) to prevent shorter matches (e.g., "column" in "columns")
92847
92987
  const sortedBoundaries = [...allBoundaries].sort((a, b) => b.length - a.length);
92988
+ // Use case-sensitive matching ('g' not 'gi') so that once a letter is
92989
+ // capitalized by a longer boundary, shorter boundaries won't re-match it.
92990
+ // This prevents issues like 'iconcolor' becoming 'iconColOr' instead of 'iconColor'.
92848
92991
  return sortedBoundaries.reduce((res, word) => {
92849
- const regex = new RegExp(`(${word})([a-z])`, 'gi');
92992
+ const regex = new RegExp(`(${word})([a-z])`, 'g');
92850
92993
  return res.replace(regex, (_, prefix, nextChar) => prefix.toLowerCase() + nextChar.toUpperCase());
92851
92994
  }, str);
92852
92995
  }
@@ -92972,7 +93115,21 @@ const htmlBlockHandler = (_state, node) => {
92972
93115
  children: [],
92973
93116
  };
92974
93117
  };
93118
+ // Convert embed magic blocks to Embed components
93119
+ const embedHandler = (state, node) => {
93120
+ // Assert to get the minimum properties we need
93121
+ const { data } = node;
93122
+ return {
93123
+ type: 'element',
93124
+ // To differentiate between regular embeds and magic block embeds,
93125
+ // magic block embeds have a certain hName
93126
+ tagName: data?.hName === NodeTypes.embedBlock ? 'Embed' : 'embed',
93127
+ properties: data?.hProperties,
93128
+ children: state.all(node),
93129
+ };
93130
+ };
92975
93131
  const mdxComponentHandlers = {
93132
+ embed: embedHandler,
92976
93133
  mdxFlowExpression: mdxExpressionHandler,
92977
93134
  mdxJsxFlowElement: mdxJsxElementHandler,
92978
93135
  mdxJsxTextElement: mdxJsxElementHandler,
@@ -93676,6 +93833,10 @@ const parseInline = (text) => {
93676
93833
  if (!text.trim())
93677
93834
  return [{ type: 'text', value: '' }];
93678
93835
  const tree = cellParser.runSync(cellParser.parse(text));
93836
+ // If there are multiple block-level nodes, keep them as-is to preserve the document structure and spacing
93837
+ if (tree.children.length > 1) {
93838
+ return tree.children;
93839
+ }
93679
93840
  return tree.children.flatMap(n =>
93680
93841
  // This unwraps the extra p node that might appear & wrapping the content
93681
93842
  n.type === 'paragraph' && 'children' in n ? n.children : [n]);
@@ -93813,14 +93974,17 @@ function parseMagicBlock(raw, options = {}) {
93813
93974
  else {
93814
93975
  children.push(...titleBlocks, ...bodyBlocks);
93815
93976
  }
93977
+ // If there is no title or title is empty
93978
+ const empty = !titleBlocks.length || !titleBlocks[0].children[0]?.value;
93816
93979
  // Create mdxJsxFlowElement directly for mdxish
93817
93980
  const calloutElement = {
93818
93981
  type: 'mdxJsxFlowElement',
93819
93982
  name: 'Callout',
93820
- attributes: toAttributes({ icon, theme: theme || 'default', type: theme || 'default' }, [
93983
+ attributes: toAttributes({ icon, theme: theme || 'default', type: theme || 'default', empty }, [
93821
93984
  'icon',
93822
93985
  'theme',
93823
93986
  'type',
93987
+ 'empty',
93824
93988
  ]),
93825
93989
  children: children,
93826
93990
  };
@@ -93878,9 +94042,9 @@ function parseMagicBlock(raw, options = {}) {
93878
94042
  return [
93879
94043
  wrapPinnedBlocks({
93880
94044
  children: [
93881
- { children: [{ type: 'text', value: title || null }], title: embedJson.provider, type: 'link', url },
94045
+ { children: [{ type: 'text', value: title || '' }], title: embedJson.provider, type: 'link', url },
93882
94046
  ],
93883
- data: { hName: 'rdme-embed', hProperties: { ...embedJson, href: url, html, title, url } },
94047
+ data: { hName: 'embed-block', hProperties: { ...embedJson, href: url, html, title, url } },
93884
94048
  type: 'embed',
93885
94049
  }, json),
93886
94050
  ];
@@ -93898,6 +94062,25 @@ function parseMagicBlock(raw, options = {}) {
93898
94062
  }, json),
93899
94063
  ];
93900
94064
  }
94065
+ // Recipe/TutorialTile: renders as Recipe component
94066
+ case 'recipe':
94067
+ case 'tutorial-tile': {
94068
+ const recipeJson = json;
94069
+ if (!recipeJson.slug || !recipeJson.title)
94070
+ return [];
94071
+ // Create mdxJsxFlowElement directly for mdxish flow
94072
+ // Note: Don't wrap in pinned blocks for mdxish - rehypeMdxishComponents handles component resolution
94073
+ // The node structure matches what mdxishComponentBlocks creates for JSX tags
94074
+ const recipeNode = {
94075
+ type: 'mdxJsxFlowElement',
94076
+ name: 'Recipe',
94077
+ attributes: toAttributes(recipeJson, ['slug', 'title']),
94078
+ children: [],
94079
+ // Position is optional but helps with debugging
94080
+ position: undefined,
94081
+ };
94082
+ return [recipeNode];
94083
+ }
93901
94084
  // Unknown block types: render as generic div with JSON properties
93902
94085
  default: {
93903
94086
  const text = json.text || json.html || '';
@@ -94191,6 +94374,45 @@ const restoreSnakeCaseComponentNames = (options) => {
94191
94374
  };
94192
94375
  /* harmony default export */ const restore_snake_case_component_name = (restoreSnakeCaseComponentNames);
94193
94376
 
94377
+ ;// ./processor/transform/mdxish/retain-boolean-attributes.ts
94378
+
94379
+ // Private Use Area character (U+E000) which is extremely unlikely to appear in real content.
94380
+ const TEMP_TRUE_BOOLEAN_VALUE = 'readme-this-is-a-temporary-boolean-attribute-\uE000';
94381
+ const TEMP_FALSE_BOOLEAN_VALUE = 'readme-this-is-a-temporary-boolean-attribute-\uE001';
94382
+ /**
94383
+ * Preserves boolean properties when passed to rehypeRaw because
94384
+ * rehypeRaw converts boolean properties in nodes to strings (e.g. true -> ""),
94385
+ * which can change the truthiness of the property. Hence we need to preserve the boolean properties.
94386
+ */
94387
+ const preserveBooleanProperties = () => tree => {
94388
+ visit(tree, 'element', (node) => {
94389
+ if (!node.properties)
94390
+ return;
94391
+ Object.entries(node.properties).forEach(([key, value]) => {
94392
+ if (typeof value === 'boolean') {
94393
+ node.properties[key] = value ? TEMP_TRUE_BOOLEAN_VALUE : TEMP_FALSE_BOOLEAN_VALUE;
94394
+ }
94395
+ });
94396
+ });
94397
+ return tree;
94398
+ };
94399
+ const restoreBooleanProperties = () => tree => {
94400
+ visit(tree, 'element', (node) => {
94401
+ if (!node.properties)
94402
+ return;
94403
+ Object.entries(node.properties).forEach(([key, value]) => {
94404
+ if (value === TEMP_TRUE_BOOLEAN_VALUE) {
94405
+ node.properties[key] = true;
94406
+ }
94407
+ else if (value === TEMP_FALSE_BOOLEAN_VALUE) {
94408
+ node.properties[key] = false;
94409
+ }
94410
+ });
94411
+ });
94412
+ return tree;
94413
+ };
94414
+
94415
+
94194
94416
  ;// ./processor/transform/mdxish/variables-text.ts
94195
94417
 
94196
94418
 
@@ -94218,6 +94440,8 @@ const variablesTextTransformer = () => tree => {
94218
94440
  if (parent.type === 'inlineCode')
94219
94441
  return;
94220
94442
  const text = node.value;
94443
+ if (typeof text !== 'string' || !text.trim())
94444
+ return;
94221
94445
  if (!text.includes('{user.') && !text.includes('{user['))
94222
94446
  return;
94223
94447
  const matches = [...text.matchAll(USER_VAR_REGEX)];
@@ -94370,6 +94594,7 @@ function loadComponents() {
94370
94594
 
94371
94595
 
94372
94596
 
94597
+
94373
94598
 
94374
94599
 
94375
94600
  const defaultTransformers = [callouts, code_tabs, gemoji_, transform_embeds];
@@ -94416,7 +94641,9 @@ function mdxish(mdContent, opts = {}) {
94416
94641
  .use(useTailwind ? transform_tailwind : undefined, { components: tempComponentsMap })
94417
94642
  .use(remarkGfm)
94418
94643
  .use(remarkRehype, { allowDangerousHtml: true, handlers: mdxComponentHandlers })
94644
+ .use(preserveBooleanProperties) // RehypeRaw converts boolean properties to empty strings
94419
94645
  .use(rehypeRaw, { passThrough: ['html-block'] })
94646
+ .use(restoreBooleanProperties)
94420
94647
  .use(rehypeSlug)
94421
94648
  .use(rehypeMdxishComponents, {
94422
94649
  components,