@@ -549,11 +549,23 @@ export async function renderReadmeHtml(
549549 toc . push ( { text : plainText , id, depth } )
550550 }
551551
552+ // The browser doesn't support anchors within anchors and automatically extracts them from each other,
553+ // causing a hydration error. To prevent this from happening in such cases, we use the anchor separately
554+ if ( htmlAnchorRe . test ( displayHtml ) ) {
555+ return `<h${ semanticLevel } id="${ id } " data-level="${ depth } "${ preservedAttrs } >${ displayHtml } <a href="#${ id } "></a></h${ semanticLevel } >\n`
556+ }
557+
552558 return `<h${ semanticLevel } id="${ id } " data-level="${ depth } "${ preservedAttrs } ><a href="#${ id } ">${ displayHtml } </a></h${ semanticLevel } >\n`
553559 }
554560
555561 renderer . heading = function ( { tokens, depth } : Tokens . Heading ) {
556- const displayHtml = this . parser . parseInline ( tokens )
562+ const anchorTokenRegex = / ^ < a ( \s .+ ) ? \/ ? > $ /
563+ const isAnchorHeading =
564+ anchorTokenRegex . test ( tokens [ 0 ] ?. raw ?? '' ) && tokens [ tokens . length - 1 ] ?. raw === '</a>'
565+
566+ // for anchor headings, we will ignore user-added id and add our own
567+ const tokensWithoutAnchor = isAnchorHeading ? tokens . slice ( 1 , - 1 ) : tokens
568+ const displayHtml = this . parser . parseInline ( tokensWithoutAnchor )
557569 const plainText = getHeadingPlainText ( displayHtml )
558570 const slugSource = getHeadingSlugSource ( displayHtml )
559571 return processHeading ( depth , displayHtml , plainText , slugSource )
@@ -643,6 +655,8 @@ ${html}
643655
644656 const { resolvedHref, extraAttrs } = processLink ( href , plainText || title || '' )
645657
658+ if ( ! resolvedHref ) return text
659+
646660 return `<a href="${ resolvedHref } "${ titleAttr } ${ extraAttrs } >${ text } </a>`
647661 }
648662
0 commit comments