-
Notifications
You must be signed in to change notification settings - Fork 1.9k
/
Copy pathwithTableOfContents.mjs
119 lines (109 loc) · 3.74 KB
/
withTableOfContents.mjs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
import { addExport } from './utils.mjs'
import slugify from '@sindresorhus/slugify'
import { toString } from 'mdast-util-to-string'
import { parseExpressionAt } from 'acorn'
import { filter } from 'unist-util-filter'
export const withTableOfContents = () => {
return (tree) => {
const contents = []
for (let nodeIndex = 0; nodeIndex < tree.children.length; nodeIndex++) {
let node = tree.children[nodeIndex]
if (node.type === 'heading' && [2, 3, 4].includes(node.depth)) {
let level = node.depth
let title = toString(
filter(node, (n) => n.type !== 'mdxJsxTextElement' || n.name !== 'small')
)
let slug = slugify(title)
let allOtherSlugs = contents.flatMap((entry) => [
entry.slug,
...entry.children.map(({ slug }) => slug),
])
let slugIndex = 1
while (allOtherSlugs.indexOf(slug) > -1) {
slug = `${slugify(title)}-${slugIndex}`
slugIndex++
}
node.type = 'mdxJsxFlowElement'
let props = {
level,
id: slug,
}
if (tree.children[nodeIndex + 1]) {
let { children, position, value, ...element } = tree.children[nodeIndex + 1]
if (element.type === 'heading') {
props.nextElement = element
}
}
if (node.children[0].type === 'mdxJsxTextElement' && node.children[0].name === 'Heading') {
let override = node.children[0].attributes.find((attr) => attr.name === 'toc')?.value
if (override) {
title = override
slug = slugify(title)
props.id = slug
node.children[0].attributes = node.children[0].attributes.filter(
(attr) => attr.name !== 'toc'
)
}
for (let prop in props) {
let value = JSON.stringify(props[prop])
node.children[0].attributes.push({
type: 'mdxJsxAttribute',
name: prop,
value: {
type: 'mdxJsxAttributeValueExpression',
value,
data: {
estree: {
type: 'Program',
body: [
{
type: 'ExpressionStatement',
expression: parseExpressionAt(value, 0, { ecmaVersion: 'latest' }),
},
],
},
},
},
})
}
tree.children[nodeIndex] = node.children[0]
} else {
node.name = 'Heading'
node.attributes = []
for (let prop in props) {
let value = JSON.stringify(props[prop])
node.attributes.push({
type: 'mdxJsxAttribute',
name: prop,
value: {
type: 'mdxJsxAttributeValueExpression',
value,
data: {
estree: {
type: 'Program',
body: [
{
type: 'ExpressionStatement',
expression: parseExpressionAt(value, 0, { ecmaVersion: 'latest' }),
},
],
},
},
},
})
}
}
if (level === 2) {
contents.push({ title, slug, children: [] })
} else if (level === 3) {
contents[contents.length - 1].children.push({ title, slug, children: [] })
} else {
contents[contents.length - 1].children[
contents[contents.length - 1].children.length - 1
].children.push({ title, slug })
}
}
}
addExport(tree, 'tableOfContents', contents)
}
}