I would like to convert a list of H1 through H6 tags from a markdown file into a Javascript hierarchy for use in a Table of Contents.
Currently the list is generated by AstroJS in this format [{depth: 1, text: 'I am a H1'}, {depth: 2: 'I am a H2}].
Caveats
- The markdown is created by end-users.
- This list may have a single root heading (
H1 -> H2 -> H3), but - It may have multiple root headings (
H2 -> H3, H2 -> H3) or - It may have a non conventional list of headings (
H3, H2 -> H3) - It may skip nesting levels (
H1 -> H3 -> H6)
Looking for a Javascript or Typescript example.
The following three scenarios are based on some Markdown content that is being processed by an AstroJS website.
Single root heading
This standard SEO friendly set of headings has a single H1 followed by other headings
As markdown
# Main heading
## Sub heading 1
### More info
## Sub heading 2
### Even more info
## Sub heading 3 (edge case)
##### Deep nesting
As flat javascript array
headings = [
{ depth: 1, text: 'Main heading' },
{ depth: 2, text: 'Sub heading 1' },
{ depth: 3, text: 'More info' },
{ depth: 2, text: 'Sub heading 2' },
{ depth: 3, text: 'Even more info' },
{ depth: 2, text: 'Sub heading 3 (edge case)' },
{ depth: 6, text: 'Deep nesting' },
]
As javascript hierarchy
list_of_heading_heirachies = [
{ text: 'Main heading', headings: [
{ text: 'Sub heading 1', headings: [
{ text: 'More info', headings: [] },
] },
{ text: 'Sub heading 2', headings: [
{ text: 'Even more info', headings: [] },
] },
{ text: 'Sub heading 3 (edge case)', headings: [
{ text: 'Deep nesting', headings: [] },
] }
]}
]
console.log(list_of_heading_heirachies.length);
// => 1
Multiple root headings
This markdown (common to listicle pages) does not have a single root node like above, instead it has multiple H2s
As markdown
## Why is it done
### Why abc
### Why xyz
## How is it done
### How reason 1
### How reason 2
#### More info
## Conclusion
As flat javascript array
headings = [
{ depth: 2, 'Why is it done' },
{ depth: 3, 'Why abc' },
{ depth: 3, 'Why xyz' },
{ depth: 2, 'How is it done' },
{ depth: 3, 'How reason 1' },
{ depth: 3, 'How reason 2' },
{ depth: 4, 'More info' },
{ depth: 2, 'Conclusion' }
]
As javascript hierarchy
list_of_heading_heirachies = [
{ text: 'Why is it done', headings: [
{ text: 'Why abc', headings: [] },
{ text: 'Why xyz', headings: [] },
] },
{ text: 'How is it done', headings: [
{ text: 'How reason 1', headings: [] },
{ text: 'How reason 2', headings: [
{ text: 'More info', headings: [] },
] },
] },
{ text: 'Conclusion', headings: [] }
]
console.log(list_of_heading_heirachies.length);
// => 3
Non-conventional headings list
This non-conventional headings list happens when there is meta data or breadcrumb data before the general content headings
#### Home -> Blog -> Some Articles
### By Ben Hurr
#### 24th, Sep, 2022
# Some cool Article
## Why abc
### info on why
### more info on why
## How
### How we did it
## Conclusion
As flat javascript array
headings = [
{ depth: 4, text: 'Home -> Blog -> Some Articles' },
{ depth: 3, text: 'By Ben Hurr' },
{ depth: 4, text: '24th, Sep, 2022' },
{ depth: 1, text: 'Some cool Article' },
{ depth: 2, text: 'Why abc' },
{ depth: 3, text: 'info on why' },
{ depth: 3, text: 'more info on why' },
{ depth: 2, text: 'How' },
{ depth: 3, text: 'How we did it' },
{ depth: 2, text: 'Conclusion' },
]
As javascript hierarchy
list_of_heading_heirachies = [
{ text: 'Home -> Blog -> Some Articles', headings: [] },
{ text: 'By Ben Hurr', headings: [
{ text: '24th, Sep, 2022', headings: [] },
] },
{ text: 'Some cool Article', headings: [
{ text: 'Why abc', headings: [
{ text: 'info on why', headings: [] },
{ text: 'more info on why', headings: [] },
] },
{ text: 'How', headings: [
{ text: 'How we did it', headings: [] },
] },
{ text: 'Conclusion', headings: [] },
] },
]
console.log(list_of_heading_heirachies.length);
// => 3
