I have a task to convert H1 to H6 tags from a markdown file into a JavaScript hierarchy for use in a Table of Contents.
The current list is generated by AstroJS and follows this format
[{depth: 1, text: 'I am a H1'}, {depth: 2: 'I am a H2}]
.
Key Points to Note
- The markdown content is user-generated.
- The list may consist of a single root heading (
H1 -> H2 -> H3
), or - It may contain multiple root headings (
H2 -> H3, H2 -> H3
) - It might also include unconventional combinations of headings (
H3, H2 -> H3
) - There could be incomplete nesting levels as well (
H1 -> H3 -> H6
)
We are seeking a JavaScript or TypeScript example for this conversion.
Below are three scenarios based on Markdown content being processed by an AstroJS website.
Scenario 1: Single Root Heading
This set of headings maintains the SEO-friendly structure with a single H1 followed by subsequent headings.
In Markdown Format:
# 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' },
]
JavaScript Hierarchy Representation:
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);
// Returns: 1
Scenario 2: Multiple Root Headings
This type of markdown, commonly found in listicle pages, includes multiple H2s as root nodes.
In Markdown Format:
## 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' }
]
JavaScript Hierarchy Model:
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);
// Returns: 3
Non-Conventional Headings List
This kind of list deviates when there is metadata or breadcrumb data preceding the main 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' },
]
JavaScript Hierarchy Structure:
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);
// Returns: 3