-
Notifications
You must be signed in to change notification settings - Fork 44
/
Copy pathindex.js
93 lines (77 loc) · 2.4 KB
/
index.js
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
const toJSX = require('@mdx-js/mdx/mdx-hast-to-jsx').toJSX
function mdxTableOfContents(options = {}) {
let OldCompiler = this.Compiler
let info
this.Compiler = tree => {
let code = OldCompiler(tree, {}, options)
if (!info.hasTableOfContentsExport) {
code += `\nexport const tableOfContents = (components={}) => ${tableOfContentsListSerializer(info.tableOfContents)}\n`
}
return code
}
return function transformer(node) {
info = getInfo(node, options)
}
}
function getInfo(root, { minTableOfContentsLevel = 1, maxTableOfContentsLevel = 3 } = {}) {
let info = {
hasFrontMatterExport: false,
hasTableOfContentsExport: false,
tableOfContents: []
}
let levelIds = []
let tableOfContentsIds = {}
for (let i = 0; i < root.children.length; i++) {
let node = root.children[i]
if (node.type === 'export' && node.value.indexOf('tableOfContents') !== -1) {
info.hasTableOfContentsExport = true
}
if (node.type === 'element' && /^h\d$/.test(node.tagName)) {
let level = parseInt(node.tagName[1])
if (level >= minTableOfContentsLevel && level <= maxTableOfContentsLevel) {
let id = node.properties.id
levelIds[level - 1] = id
let parent = tableOfContentsIds[levelIds[level - 2]]
let item = {
id,
level,
title: toFragment(node.children),
children: [],
}
if (parent) {
parent.children.push(item)
}
else {
info.tableOfContents.push(item)
}
tableOfContentsIds[id] = item
}
}
}
return info
}
function toFragment(nodes) {
if (nodes.length === 1 && nodes[0].type === 'text') {
return JSON.stringify(nodes[0].value)
}
else {
return '<React.Fragment>'+nodes.map(toJSX).join('')+'</React.Fragment>'
}
}
function tableOfContentsListSerializer(nodes, indent = 0) {
return indentString(indent, `[
${nodes.map(node => tableOfContentsNodeSerializer(node, indent + 2)).join(',\n')}
]`)
}
function tableOfContentsNodeSerializer(node, indent = 0) {
return indentString(indent, `{
id: ${JSON.stringify(node.id)},
level: ${node.level},
title: ${node.title},
children: ${tableOfContentsListSerializer(node.children, indent + 2)}
}`)
}
function indentString(size, string) {
return string.split('\n').map(x => ' '.repeat(size)+x).join('\n').trim()
}
module.exports = mdxTableOfContents