@@ -5,6 +5,7 @@ import { u as createTree } from 'unist-builder';
55import { SKIP , visit } from 'unist-util-visit' ;
66
77import buildExtraContent from './buildExtraContent.mjs' ;
8+ import { getLegacySlug } from './slugger.mjs' ;
89import getConfig from '../../../utils/configuration/index.mjs' ;
910import {
1011 GITHUB_BLOB_URL ,
@@ -20,12 +21,21 @@ import { QUERIES, UNIST } from '../../../utils/queries/index.mjs';
2021 * @param {import('unist').Parent } parent The parent node of the current node
2122 * @returns {import('hast').Element } The HTML AST tree of the heading content
2223 */
23- const buildHeading = ( { data, children, depth } , index , parent ) => {
24+ const buildHeading = (
25+ { data, children, depth } ,
26+ index ,
27+ parent ,
28+ apiStem ,
29+ deduplicateFn
30+ ) => {
31+ const legacySlug = getLegacySlug ( data . text , apiStem , deduplicateFn ) ;
2432 // Creates the heading element with the heading text and the link to the heading
2533 const headingElement = createElement ( `h${ depth + 1 } ` , [
2634 // The inner Heading markdown content is still using Remark nodes, and they need
2735 // to be converted into Rehype nodes
2836 ...children ,
37+ // Legacy anchor alias to preserve old external links
38+ createElement ( 'span' , createElement ( `a#${ legacySlug } ` ) ) ,
2939 // Creates the element that references the link to the heading
3040 // (The `#` anchor on the right of each Heading section)
3141 createElement (
@@ -220,6 +230,16 @@ const buildMetadataElement = (node, remark) => {
220230 * @param {import('unified').Processor } remark The Remark instance to be used to process
221231 */
222232export default ( headNodes , metadataEntries , remark ) => {
233+ // Legacy slug counter tracking for deduplication
234+ const legacyIdCounters = { __proto__ : null } ;
235+ /** Deduplicates legacy slugs by appending an incremented counter */
236+ const legacyDeduplicateFn = id => {
237+ if ( legacyIdCounters [ id ] !== undefined ) {
238+ return `${ id } _${ ++ legacyIdCounters [ id ] } ` ;
239+ }
240+ legacyIdCounters [ id ] = 0 ;
241+ return id ;
242+ } ;
223243 // Creates the root node for the content
224244 const parsedNodes = createTree (
225245 'root' ,
@@ -229,7 +249,9 @@ export default (headNodes, metadataEntries, remark) => {
229249 const content = structuredClone ( entry . content ) ;
230250
231251 // Parses the Heading nodes into Heading elements
232- visit ( content , UNIST . isHeading , buildHeading ) ;
252+ visit ( content , UNIST . isHeading , ( node , index , parent ) =>
253+ buildHeading ( node , index , parent , entry . api , legacyDeduplicateFn )
254+ ) ;
233255
234256 // Parses the Blockquotes into Stability elements
235257 // This is treated differently as we want to preserve the position of a Stability Index
0 commit comments