@@ -2,7 +2,12 @@ import type { MDXComponents } from 'mdx/types';
22import Image , { type ImageProps } from 'next/image' ;
33import Link from 'next/link' ;
44import { HashIcon } from 'lucide-react' ;
5- import type { AnchorHTMLAttributes , PropsWithChildren , ReactNode } from 'react' ;
5+ import React , {
6+ isValidElement ,
7+ type AnchorHTMLAttributes ,
8+ type PropsWithChildren ,
9+ type ReactNode ,
10+ } from 'react' ;
611import {
712 Table ,
813 TableBody ,
@@ -31,9 +36,34 @@ function HeadingLink({
3136 ) ;
3237}
3338
39+ /**
40+ * Safely convert a ReactNode to a string.
41+ * Handles strings, numbers, booleans, arrays, null/undefined, and React elements by
42+ * recursively extracting their children text.
43+ */
44+ function reactNodeToString ( node : ReactNode ) : string {
45+ if ( node === null || node === undefined ) return '' ;
46+ if (
47+ typeof node === 'string' ||
48+ typeof node === 'number' ||
49+ typeof node === 'boolean'
50+ ) {
51+ return String ( node ) ;
52+ }
53+ if ( Array . isArray ( node ) ) {
54+ return node . map ( reactNodeToString ) . join ( '' ) ;
55+ }
56+ if ( isValidElement ( node ) ) {
57+ // If it's a React element, try to extract its children
58+ const element = node as React . ReactElement < { children ?: ReactNode } > ;
59+ return reactNodeToString ( element . props . children ) ;
60+ }
61+ // Fallback to empty string for other types
62+ return '' ;
63+ }
64+
3465export const customComponents = {
3566 h1 : ( { children } : { children : ReactNode } ) => {
36- // @ts -expect-error I don't know what types I should define here, but this works at runtime
3767 const slug = slugify ( children ) ;
3868 return (
3969 < h1
@@ -50,8 +80,7 @@ export const customComponents = {
5080export function useMDXComponents ( components : MDXComponents ) : MDXComponents {
5181 return {
5282 h1 : customComponents . h1 ,
53- h2 : ( { children } ) => {
54- // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
83+ h2 : ( { children } : { children : ReactNode } ) => {
5584 const slug = slugify ( children ) ;
5685 return (
5786 < h2
@@ -62,8 +91,7 @@ export function useMDXComponents(components: MDXComponents): MDXComponents {
6291 </ h2 >
6392 ) ;
6493 } ,
65- h3 : ( { children } ) => {
66- // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
94+ h3 : ( { children } : { children : ReactNode } ) => {
6795 const slug = slugify ( children ) ;
6896 return (
6997 < h3
@@ -74,8 +102,7 @@ export function useMDXComponents(components: MDXComponents): MDXComponents {
74102 </ h3 >
75103 ) ;
76104 } ,
77- h4 : ( { children } ) => {
78- // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
105+ h4 : ( { children } : { children : ReactNode } ) => {
79106 const slug = slugify ( children ) ;
80107 return (
81108 < h4
@@ -86,8 +113,7 @@ export function useMDXComponents(components: MDXComponents): MDXComponents {
86113 </ h4 >
87114 ) ;
88115 } ,
89- h5 : ( { children } ) => {
90- // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
116+ h5 : ( { children } : { children : ReactNode } ) => {
91117 const slug = slugify ( children ) ;
92118 return (
93119 < h5
@@ -98,8 +124,7 @@ export function useMDXComponents(components: MDXComponents): MDXComponents {
98124 </ h5 >
99125 ) ;
100126 } ,
101- h6 : ( { children } ) => {
102- // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
127+ h6 : ( { children } : { children : ReactNode } ) => {
103128 const slug = slugify ( children ) ;
104129 return (
105130 < h6
@@ -149,18 +174,15 @@ export function useMDXComponents(components: MDXComponents): MDXComponents {
149174 } ;
150175}
151176
152- function slugify ( str : string ) : string {
153- return (
154- str
155- // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-conversion
156- . toString ( )
157- . toLowerCase ( )
158- . trim ( ) // Remove whitespace from both ends of a string
159- . replace ( / \s + / g, '-' ) // Replace spaces with -
160- . replace ( / & / g, '-and-' ) // Replace & with 'and'
161- . replace ( / [ ^ \w - ] + / g, '' ) // Remove all non-word characters except for -
162- . replace ( / - - + / g, '-' )
163- ) ; // Replace multiple - with single -
177+ function slugify ( node : ReactNode ) : string {
178+ const str = reactNodeToString ( node ) ;
179+ return str
180+ . toLowerCase ( )
181+ . trim ( ) // Remove whitespace from both ends of a string
182+ . replace ( / \s + / g, '-' ) // Replace spaces with -
183+ . replace ( / & / g, '-and-' ) // Replace & with 'and'
184+ . replace ( / [ ^ \w - ] + / g, '' ) // Remove all non-word characters except for -
185+ . replace ( / - - + / g, '-' ) ; // Replace multiple - with single -
164186}
165187
166188function CustomLink ( props : AnchorHTMLAttributes < HTMLAnchorElement > ) {
0 commit comments