@readme/markdown 13.8.2 → 13.8.4

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
@@ -12110,17 +12110,22 @@ function getScrollParent(el) {
12110
12110
  * corresponding TOC links so the reader always knows where they are.
12111
12111
  */
12112
12112
  function useScrollHighlight(navRef) {
12113
- const [linkCount, setLinkCount] = (0,external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_.useState)(0);
12113
+ const [tocKey, setTocKey] = (0,external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_.useState)('');
12114
+ // Re-check after every render so we detect when children change
12115
+ // (e.g. after page navigation). Only triggers a re-render when the
12116
+ // set of TOC link hrefs actually differs.
12114
12117
  (0,external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_.useEffect)(() => {
12115
12118
  const nav = navRef.current;
12116
12119
  if (!nav)
12117
12120
  return;
12118
- const count = nav.querySelectorAll('a[href^="#"]').length;
12119
- setLinkCount(count);
12120
- }, [navRef]);
12121
+ const key = Array.from(nav.querySelectorAll('a[href^="#"]'))
12122
+ .map(a => a.getAttribute('href'))
12123
+ .join('\0');
12124
+ setTocKey(key);
12125
+ });
12121
12126
  (0,external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_.useEffect)(() => {
12122
12127
  const nav = navRef.current;
12123
- if (!nav || typeof IntersectionObserver === 'undefined' || linkCount === 0)
12128
+ if (!nav || typeof IntersectionObserver === 'undefined' || !tocKey)
12124
12129
  return undefined;
12125
12130
  const linkMap = buildLinkMap(nav);
12126
12131
  if (linkMap.size === 0)
@@ -12186,18 +12191,33 @@ function useScrollHighlight(navRef) {
12186
12191
  // Click a ToC link → immediately activate it, suppress the observer
12187
12192
  // until the smooth scroll finishes, then hand control back.
12188
12193
  const onClick = (e) => {
12189
- const anchor = e.target.closest?.('a[href^="#"]');
12190
- if (!anchor)
12194
+ if (!(e.target instanceof Element))
12195
+ return;
12196
+ const anchor = e.target.closest('a[href^="#"]');
12197
+ if (!(anchor instanceof HTMLAnchorElement))
12191
12198
  return;
12192
- const id = decodeURIComponent(anchor.getAttribute('href').slice(1));
12199
+ const id = decodeURIComponent(anchor.hash.slice(1));
12193
12200
  if (!linkMap.has(id))
12194
12201
  return;
12195
- e.preventDefault();
12202
+ if (window.location.hash !== anchor.hash) {
12203
+ window.location.hash = anchor.hash;
12204
+ }
12196
12205
  activate(id);
12197
12206
  clickLocked = true;
12198
- const unlock = () => { clickLocked = false; };
12207
+ let unlockTimer = null;
12208
+ const unlock = () => {
12209
+ clickLocked = false;
12210
+ scrollTarget.removeEventListener('scrollend', unlock);
12211
+ window.removeEventListener('hashchange', unlock);
12212
+ if (unlockTimer !== null) {
12213
+ window.clearTimeout(unlockTimer);
12214
+ unlockTimer = null;
12215
+ }
12216
+ };
12199
12217
  scrollTarget.addEventListener('scrollend', unlock, { once: true });
12200
- document.getElementById(id)?.scrollIntoView({ behavior: 'smooth' });
12218
+ window.addEventListener('hashchange', unlock, { once: true });
12219
+ // Fallback in case scrollend and hashchange don't fire
12220
+ unlockTimer = window.setTimeout(unlock, 500);
12201
12221
  };
12202
12222
  headings.forEach(el => { observer.observe(el); });
12203
12223
  scrollTarget.addEventListener('scroll', onScroll, { passive: true });
@@ -12214,7 +12234,7 @@ function useScrollHighlight(navRef) {
12214
12234
  scrollTarget.removeEventListener('scroll', onScroll);
12215
12235
  nav.removeEventListener('click', onClick);
12216
12236
  };
12217
- }, [navRef, linkCount]);
12237
+ }, [navRef, tocKey]);
12218
12238
  }
12219
12239
  function TableOfContents({ children }) {
12220
12240
  const navRef = (0,external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_.useRef)(null);
@@ -96456,10 +96476,10 @@ const parseTableCell = (text) => {
96456
96476
  node.value = escapeInvalidTags(node.value);
96457
96477
  }
96458
96478
  });
96459
- if (tree.children.length > 1) {
96460
- return tree.children;
96461
- }
96462
- return tree.children.flatMap(n => n.type === 'paragraph' && 'children' in n ? n.children : [n]);
96479
+ const result = tree.children.length > 1
96480
+ ? tree.children
96481
+ : tree.children.flatMap(n => n.type === 'paragraph' && 'children' in n ? n.children : [n]);
96482
+ return result;
96463
96483
  };
96464
96484
  const parseBlock = (text) => {
96465
96485
  if (!text.trim())
@@ -96671,7 +96691,9 @@ function transformMagicBlock(blockType, data, rawValue, options = {}) {
96671
96691
  mapped[rowIndex][colIndex] = v;
96672
96692
  return mapped;
96673
96693
  }, []);
96674
- const tokenizeCell = compatibilityMode ? textToBlock : parseTableCell;
96694
+ const tokenizeCell = compatibilityMode
96695
+ ? textToBlock
96696
+ : parseTableCell;
96675
96697
  const tableChildren = Array.from({ length: rows + 1 }, (_, y) => ({
96676
96698
  children: Array.from({ length: cols }, (__, x) => ({
96677
96699
  children: sparseData[y]?.[x] ? tokenizeCell(preprocessBody(sparseData[y][x])) : [{ type: 'text', value: '' }],
@@ -98364,6 +98386,7 @@ function restoreSnakeCase(placeholderName, mapping) {
98364
98386
  ;// ./processor/transform/mdxish/mdxish-tables-to-jsx.ts
98365
98387
 
98366
98388
 
98389
+
98367
98390
  const SELF_CLOSING_JSX_REGEX = /^\s*<[A-Z][^>]*\/>\s*$/;
98368
98391
  const mdxish_tables_to_jsx_alignToStyle = (align) => {
98369
98392
  if (!align || align === 'left')
@@ -98393,22 +98416,27 @@ const mdxishTablesToJsx = () => tree => {
98393
98416
  visit(table, mdxish_tables_to_jsx_isTableCell, (cell) => {
98394
98417
  if (hasFlowContent || cell.children.length === 0)
98395
98418
  return;
98396
- const content = cell.children.length === 1 && cell.children[0].type === 'paragraph'
98397
- ? cell.children[0].children[0]
98398
- : cell.children[0];
98399
- if (!content)
98400
- return;
98401
98419
  visit(cell, 'break', (_, breakIndex, breakParent) => {
98402
98420
  breakParent.children.splice(breakIndex, 1, { type: 'text', value: '\n' });
98403
98421
  });
98404
- if (!(phrasing(content) || content.type === 'plain') && content.type !== 'escape') {
98405
- // Plain HTML (e.g. <div>Hello</div>) is skipped here — it stays in GFM cells fine.
98406
- // But self-closing JSX components (e.g. <Image src="..." caption="..." />) serialize
98407
- // with newlines that break GFM cells, so they must trigger JSX <Table> serialization.
98408
- const isPlainHtml = content.type === 'html' && !SELF_CLOSING_JSX_REGEX.test(content.value);
98409
- if (!isPlainHtml) {
98410
- hasFlowContent = true;
98422
+ // Check if any child is "flow" content (block-level) that requires JSX <Table>
98423
+ // serialization instead of GFM. `phrasing()` from mdast-util-phrasing returns
98424
+ // true for inline node types (text, emphasis, strong, link, etc.) which are
98425
+ // safe to keep in GFM cells.
98426
+ const hasFlowChild = cell.children.some(child => {
98427
+ if (child.type === 'paragraph' || child.type === 'plain' || child.type === 'escape')
98428
+ return false;
98429
+ if (child.type === NodeTypes.variable)
98430
+ return false;
98431
+ if (phrasing(child))
98432
+ return false;
98433
+ if (child.type === 'html') {
98434
+ return SELF_CLOSING_JSX_REGEX.test(child.value);
98411
98435
  }
98436
+ return true;
98437
+ });
98438
+ if (hasFlowChild) {
98439
+ hasFlowContent = true;
98412
98440
  }
98413
98441
  if (!hasFlowContent) {
98414
98442
  visit(cell, mdxish_tables_to_jsx_isLiteral, (node) => {
@@ -98486,6 +98514,40 @@ const mdxishTablesToJsx = () => tree => {
98486
98514
  };
98487
98515
  /* harmony default export */ const mdxish_tables_to_jsx = (mdxishTablesToJsx);
98488
98516
 
98517
+ ;// ./processor/transform/mdxish/normalize-compact-headings.ts
98518
+
98519
+ /**
98520
+ * Preprocessor to normalize compact headings.
98521
+ *
98522
+ * CommonMark requires whitespace after # for headings, but users often omit it.
98523
+ * This preprocessor adds the space while being careful not to modify:
98524
+ * - Content inside fenced code blocks (protected via protectCodeBlocks)
98525
+ * - Escaped hashtags (\#)
98526
+ * - Mid-line hashtags (text #hashtag)
98527
+ *
98528
+ * Examples:
98529
+ * - `#Header` → `# Header`
98530
+ * - `##Title` → `## Title`
98531
+ * - `######H6` → `###### H6`
98532
+ */
98533
+ function normalizeCompactHeadings(content) {
98534
+ const { protectedContent, protectedCode } = protectCodeBlocks(content);
98535
+ const normalizedLines = protectedContent.split('\n').map(line => {
98536
+ // Skip escaped hashtags
98537
+ if (line.startsWith('\\#')) {
98538
+ return line;
98539
+ }
98540
+ // Match compact heading: start of line, 1-6 #, followed by non-space/non-# character
98541
+ const headingMatch = line.match(/^(#{1,6})([^\s#])/);
98542
+ if (headingMatch) {
98543
+ // Insert space after hashtags: "##Header" → "## Header"
98544
+ return `${headingMatch[1]} ${line.slice(headingMatch[1].length)}`;
98545
+ }
98546
+ return line;
98547
+ });
98548
+ return restoreCodeBlocks(normalizedLines.join('\n'), protectedCode);
98549
+ }
98550
+
98489
98551
  ;// ./processor/transform/mdxish/normalize-table-separator.ts
98490
98552
  /**
98491
98553
  * Preprocessor to normalize malformed GFM table separator syntax.
@@ -100665,6 +100727,7 @@ function loadComponents() {
100665
100727
 
100666
100728
 
100667
100729
 
100730
+
100668
100731
 
100669
100732
 
100670
100733
  const defaultTransformers = [
@@ -100688,6 +100751,7 @@ function preprocessContent(content, opts) {
100688
100751
  let result = normalizeTableSeparator(content);
100689
100752
  result = terminateHtmlFlowBlocks(result);
100690
100753
  result = closeSelfClosingHtmlTags(result);
100754
+ result = normalizeCompactHeadings(result);
100691
100755
  result = safeMode ? result : preprocessJSXExpressions(result, jsxContext);
100692
100756
  return processSnakeCaseComponent(result, { knownComponents });
100693
100757
  }
package/dist/main.node.js CHANGED
@@ -24706,17 +24706,22 @@ function TableOfContents_getScrollParent(el) {
24706
24706
  * corresponding TOC links so the reader always knows where they are.
24707
24707
  */
24708
24708
  function useScrollHighlight(navRef) {
24709
- const [linkCount, setLinkCount] = (0,external_react_.useState)(0);
24709
+ const [tocKey, setTocKey] = (0,external_react_.useState)('');
24710
+ // Re-check after every render so we detect when children change
24711
+ // (e.g. after page navigation). Only triggers a re-render when the
24712
+ // set of TOC link hrefs actually differs.
24710
24713
  (0,external_react_.useEffect)(() => {
24711
24714
  const nav = navRef.current;
24712
24715
  if (!nav)
24713
24716
  return;
24714
- const count = nav.querySelectorAll('a[href^="#"]').length;
24715
- setLinkCount(count);
24716
- }, [navRef]);
24717
+ const key = Array.from(nav.querySelectorAll('a[href^="#"]'))
24718
+ .map(a => a.getAttribute('href'))
24719
+ .join('\0');
24720
+ setTocKey(key);
24721
+ });
24717
24722
  (0,external_react_.useEffect)(() => {
24718
24723
  const nav = navRef.current;
24719
- if (!nav || typeof IntersectionObserver === 'undefined' || linkCount === 0)
24724
+ if (!nav || typeof IntersectionObserver === 'undefined' || !tocKey)
24720
24725
  return undefined;
24721
24726
  const linkMap = buildLinkMap(nav);
24722
24727
  if (linkMap.size === 0)
@@ -24782,18 +24787,33 @@ function useScrollHighlight(navRef) {
24782
24787
  // Click a ToC link → immediately activate it, suppress the observer
24783
24788
  // until the smooth scroll finishes, then hand control back.
24784
24789
  const onClick = (e) => {
24785
- const anchor = e.target.closest?.('a[href^="#"]');
24786
- if (!anchor)
24790
+ if (!(e.target instanceof Element))
24791
+ return;
24792
+ const anchor = e.target.closest('a[href^="#"]');
24793
+ if (!(anchor instanceof HTMLAnchorElement))
24787
24794
  return;
24788
- const id = decodeURIComponent(anchor.getAttribute('href').slice(1));
24795
+ const id = decodeURIComponent(anchor.hash.slice(1));
24789
24796
  if (!linkMap.has(id))
24790
24797
  return;
24791
- e.preventDefault();
24798
+ if (window.location.hash !== anchor.hash) {
24799
+ window.location.hash = anchor.hash;
24800
+ }
24792
24801
  activate(id);
24793
24802
  clickLocked = true;
24794
- const unlock = () => { clickLocked = false; };
24803
+ let unlockTimer = null;
24804
+ const unlock = () => {
24805
+ clickLocked = false;
24806
+ scrollTarget.removeEventListener('scrollend', unlock);
24807
+ window.removeEventListener('hashchange', unlock);
24808
+ if (unlockTimer !== null) {
24809
+ window.clearTimeout(unlockTimer);
24810
+ unlockTimer = null;
24811
+ }
24812
+ };
24795
24813
  scrollTarget.addEventListener('scrollend', unlock, { once: true });
24796
- document.getElementById(id)?.scrollIntoView({ behavior: 'smooth' });
24814
+ window.addEventListener('hashchange', unlock, { once: true });
24815
+ // Fallback in case scrollend and hashchange don't fire
24816
+ unlockTimer = window.setTimeout(unlock, 500);
24797
24817
  };
24798
24818
  headings.forEach(el => { observer.observe(el); });
24799
24819
  scrollTarget.addEventListener('scroll', onScroll, { passive: true });
@@ -24810,7 +24830,7 @@ function useScrollHighlight(navRef) {
24810
24830
  scrollTarget.removeEventListener('scroll', onScroll);
24811
24831
  nav.removeEventListener('click', onClick);
24812
24832
  };
24813
- }, [navRef, linkCount]);
24833
+ }, [navRef, tocKey]);
24814
24834
  }
24815
24835
  function TableOfContents({ children }) {
24816
24836
  const navRef = (0,external_react_.useRef)(null);
@@ -116650,10 +116670,10 @@ const parseTableCell = (text) => {
116650
116670
  node.value = escapeInvalidTags(node.value);
116651
116671
  }
116652
116672
  });
116653
- if (tree.children.length > 1) {
116654
- return tree.children;
116655
- }
116656
- return tree.children.flatMap(n => n.type === 'paragraph' && 'children' in n ? n.children : [n]);
116673
+ const result = tree.children.length > 1
116674
+ ? tree.children
116675
+ : tree.children.flatMap(n => n.type === 'paragraph' && 'children' in n ? n.children : [n]);
116676
+ return result;
116657
116677
  };
116658
116678
  const parseBlock = (text) => {
116659
116679
  if (!text.trim())
@@ -116865,7 +116885,9 @@ function transformMagicBlock(blockType, data, rawValue, options = {}) {
116865
116885
  mapped[rowIndex][colIndex] = v;
116866
116886
  return mapped;
116867
116887
  }, []);
116868
- const tokenizeCell = compatibilityMode ? textToBlock : parseTableCell;
116888
+ const tokenizeCell = compatibilityMode
116889
+ ? textToBlock
116890
+ : parseTableCell;
116869
116891
  const tableChildren = Array.from({ length: rows + 1 }, (_, y) => ({
116870
116892
  children: Array.from({ length: cols }, (__, x) => ({
116871
116893
  children: sparseData[y]?.[x] ? tokenizeCell(preprocessBody(sparseData[y][x])) : [{ type: 'text', value: '' }],
@@ -118558,6 +118580,7 @@ function restoreSnakeCase(placeholderName, mapping) {
118558
118580
  ;// ./processor/transform/mdxish/mdxish-tables-to-jsx.ts
118559
118581
 
118560
118582
 
118583
+
118561
118584
  const SELF_CLOSING_JSX_REGEX = /^\s*<[A-Z][^>]*\/>\s*$/;
118562
118585
  const mdxish_tables_to_jsx_alignToStyle = (align) => {
118563
118586
  if (!align || align === 'left')
@@ -118587,22 +118610,27 @@ const mdxishTablesToJsx = () => tree => {
118587
118610
  visit(table, mdxish_tables_to_jsx_isTableCell, (cell) => {
118588
118611
  if (hasFlowContent || cell.children.length === 0)
118589
118612
  return;
118590
- const content = cell.children.length === 1 && cell.children[0].type === 'paragraph'
118591
- ? cell.children[0].children[0]
118592
- : cell.children[0];
118593
- if (!content)
118594
- return;
118595
118613
  visit(cell, 'break', (_, breakIndex, breakParent) => {
118596
118614
  breakParent.children.splice(breakIndex, 1, { type: 'text', value: '\n' });
118597
118615
  });
118598
- if (!(phrasing(content) || content.type === 'plain') && content.type !== 'escape') {
118599
- // Plain HTML (e.g. <div>Hello</div>) is skipped here — it stays in GFM cells fine.
118600
- // But self-closing JSX components (e.g. <Image src="..." caption="..." />) serialize
118601
- // with newlines that break GFM cells, so they must trigger JSX <Table> serialization.
118602
- const isPlainHtml = content.type === 'html' && !SELF_CLOSING_JSX_REGEX.test(content.value);
118603
- if (!isPlainHtml) {
118604
- hasFlowContent = true;
118616
+ // Check if any child is "flow" content (block-level) that requires JSX <Table>
118617
+ // serialization instead of GFM. `phrasing()` from mdast-util-phrasing returns
118618
+ // true for inline node types (text, emphasis, strong, link, etc.) which are
118619
+ // safe to keep in GFM cells.
118620
+ const hasFlowChild = cell.children.some(child => {
118621
+ if (child.type === 'paragraph' || child.type === 'plain' || child.type === 'escape')
118622
+ return false;
118623
+ if (child.type === NodeTypes.variable)
118624
+ return false;
118625
+ if (phrasing(child))
118626
+ return false;
118627
+ if (child.type === 'html') {
118628
+ return SELF_CLOSING_JSX_REGEX.test(child.value);
118605
118629
  }
118630
+ return true;
118631
+ });
118632
+ if (hasFlowChild) {
118633
+ hasFlowContent = true;
118606
118634
  }
118607
118635
  if (!hasFlowContent) {
118608
118636
  visit(cell, mdxish_tables_to_jsx_isLiteral, (node) => {
@@ -118680,6 +118708,40 @@ const mdxishTablesToJsx = () => tree => {
118680
118708
  };
118681
118709
  /* harmony default export */ const mdxish_tables_to_jsx = (mdxishTablesToJsx);
118682
118710
 
118711
+ ;// ./processor/transform/mdxish/normalize-compact-headings.ts
118712
+
118713
+ /**
118714
+ * Preprocessor to normalize compact headings.
118715
+ *
118716
+ * CommonMark requires whitespace after # for headings, but users often omit it.
118717
+ * This preprocessor adds the space while being careful not to modify:
118718
+ * - Content inside fenced code blocks (protected via protectCodeBlocks)
118719
+ * - Escaped hashtags (\#)
118720
+ * - Mid-line hashtags (text #hashtag)
118721
+ *
118722
+ * Examples:
118723
+ * - `#Header` → `# Header`
118724
+ * - `##Title` → `## Title`
118725
+ * - `######H6` → `###### H6`
118726
+ */
118727
+ function normalizeCompactHeadings(content) {
118728
+ const { protectedContent, protectedCode } = protectCodeBlocks(content);
118729
+ const normalizedLines = protectedContent.split('\n').map(line => {
118730
+ // Skip escaped hashtags
118731
+ if (line.startsWith('\\#')) {
118732
+ return line;
118733
+ }
118734
+ // Match compact heading: start of line, 1-6 #, followed by non-space/non-# character
118735
+ const headingMatch = line.match(/^(#{1,6})([^\s#])/);
118736
+ if (headingMatch) {
118737
+ // Insert space after hashtags: "##Header" → "## Header"
118738
+ return `${headingMatch[1]} ${line.slice(headingMatch[1].length)}`;
118739
+ }
118740
+ return line;
118741
+ });
118742
+ return restoreCodeBlocks(normalizedLines.join('\n'), protectedCode);
118743
+ }
118744
+
118683
118745
  ;// ./processor/transform/mdxish/normalize-table-separator.ts
118684
118746
  /**
118685
118747
  * Preprocessor to normalize malformed GFM table separator syntax.
@@ -120859,6 +120921,7 @@ function loadComponents() {
120859
120921
 
120860
120922
 
120861
120923
 
120924
+
120862
120925
 
120863
120926
 
120864
120927
  const defaultTransformers = [
@@ -120882,6 +120945,7 @@ function preprocessContent(content, opts) {
120882
120945
  let result = normalizeTableSeparator(content);
120883
120946
  result = terminateHtmlFlowBlocks(result);
120884
120947
  result = closeSelfClosingHtmlTags(result);
120948
+ result = normalizeCompactHeadings(result);
120885
120949
  result = safeMode ? result : preprocessJSXExpressions(result, jsxContext);
120886
120950
  return processSnakeCaseComponent(result, { knownComponents });
120887
120951
  }