1- import type { RenderableTreeNode , Tag } from "@markdoc/markdoc" ;
1+ import { Tag } from "@markdoc/markdoc" ;
2+ import type { RenderableTreeNode } from "@markdoc/markdoc" ;
23import slugify from "slugify" ;
34
45export interface Heading {
@@ -16,6 +17,26 @@ export interface Heading {
1617 id ?: string ;
1718}
1819
20+ const getTextContent = ( children : RenderableTreeNode [ ] ) : string => {
21+ return children . reduce ( ( text : string , child ) : string => {
22+ if ( typeof child === "string" || typeof child === "number" ) {
23+ return text + child ;
24+ } else if ( typeof child === "object" && Tag . isTag ( child ) ) {
25+ return text + getTextContent ( child . children ) ;
26+ }
27+ return text ;
28+ } , "" ) ;
29+ } ;
30+
31+ const getSlug = ( tag : Tag ) : string => {
32+ if ( tag . attributes . id && typeof tag . attributes . id === "string" ) {
33+ return tag . attributes . id ;
34+ }
35+ return slugify ( getTextContent ( tag . children ) , {
36+ lower : true ,
37+ strict : true ,
38+ } ) as string ;
39+ } ;
1940/**
2041 * Recursively collects all heading nodes from a Markdoc AST
2142 * @param node - The Markdoc AST node to process
@@ -37,16 +58,11 @@ export function collectHeadings(
3758 if ( typeof node === "object" && node !== null && "name" in node ) {
3859 const tag = node as Tag ;
3960 if ( tag . name . match ( / ^ h \d $ / ) ) {
40- const title = tag . children [ 0 ] ;
41- if ( typeof title === "string" ) {
42- sections . push ( {
43- level : parseInt ( tag . name [ 1 ] ) ,
44- title,
45- id :
46- ( tag . attributes . id as string ) ||
47- ( slugify ( title , { lower : true , strict : true } ) as string ) ,
48- } ) ;
49- }
61+ sections . push ( {
62+ level : parseInt ( tag . name [ 1 ] ) ,
63+ title : getTextContent ( tag . children ) ,
64+ id : getSlug ( tag ) ,
65+ } ) ;
5066 }
5167
5268 // Handle node children
0 commit comments