-
Notifications
You must be signed in to change notification settings - Fork 2
/
index.js
143 lines (131 loc) · 4.33 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
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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
import * as acorn from 'acorn'
import { visit as estreeVisit } from 'estree-util-visit'
import { visit as unistVisit } from 'unist-util-visit'
const PROP_NAME = 'annotation'
function setAnnotation(node, annotation) {
let data = node.data || (node.data = {})
let props = data.hProperties || (data.hProperties = {})
props[PROP_NAME] = annotation
}
function isThematicBreak(str) {
if (!str) return false
let trimmed = str.trim()
return trimmed === '---' || trimmed === '***'
}
export const mdxAnnotations = {
remark() {
return (tree) => {
unistVisit(tree, (node, nodeIndex, parentNode) => {
if (node.type === 'code') {
let meta = node.meta ?? ''
let lang
let annotationIndex = node.lang?.match(/{\s*{/)?.index
if (typeof annotationIndex === 'number') {
lang = node.lang.slice(0, annotationIndex) || null
meta = `${node.lang.slice(annotationIndex)}${meta}`
}
if (/^{\s*{.*?}\s*}$/.test(meta)) {
setAnnotation(node, meta.slice(1, -1))
node.meta = null
if (typeof lang !== 'undefined') {
node.lang = lang
}
return
}
}
if (node.type === 'tableRow') {
if (
node.children.length === 1 &&
node.children[0].type === 'tableCell' &&
node.children[0].children.length === 1 &&
node.children[0].children[0].type === 'mdxTextExpression'
) {
setAnnotation(parentNode, node.children[0].children[0].value)
parentNode.children.splice(nodeIndex, 1)
}
return
}
if (
node.type === 'paragraph' &&
node.children.length === 2 &&
node.children[0].type === 'text' &&
isThematicBreak(node.children[0].value) &&
node.children[1].type === 'mdxTextExpression'
) {
node.type = 'thematicBreak'
setAnnotation(node, node.children[1].value)
delete node.children
return
}
if (!('children' in node) || node.children.length === 0) return
for (let i = 0; i < node.children.length; i++) {
let child = node.children[i]
if (
child.type === 'mdxTextExpression' &&
child.value.startsWith('{') &&
child.value.endsWith('}')
) {
let prev = node.children[i - 1]
if (!prev) {
continue
}
let refNode
if (prev.type === 'text' && i === node.children.length - 1) {
refNode = node
if (
node.type === 'paragraph' &&
parentNode.type === 'listItem' &&
parentNode.children.length === 1
) {
refNode = parentNode
}
prev.value = prev.value.trimEnd()
} else {
refNode = prev
}
setAnnotation(refNode, child.value)
node.children.splice(i, 1)
}
}
})
}
},
rehype() {
return (tree) => {
unistVisit(tree, 'element', (node, _nodeIndex, parentNode) => {
if (
node.tagName === 'code' &&
PROP_NAME in node.properties &&
parentNode.type === 'element' &&
parentNode.tagName === 'pre'
) {
parentNode.properties[PROP_NAME] = node.properties[PROP_NAME]
delete node.properties[PROP_NAME]
}
})
}
},
recma() {
return (tree) => {
estreeVisit(tree, (node) => {
if (node.type !== 'CallExpression') return
if (
node.callee.name !== '_jsxs' &&
node.callee.name !== '_jsx' &&
node.callee.name !== '_jsxDEV'
)
return
let propsNode = node.arguments[1]
if (propsNode?.type !== 'ObjectExpression') return
let propNode = propsNode.properties.find((property) => property.key?.name === PROP_NAME)
if (propNode) {
let annotationNode = acorn.parse('(' + propNode.value.value.trim() + ')', {
ecmaVersion: 'latest',
}).body[0].expression
propsNode.properties.splice(propsNode.properties.indexOf(propNode), 1)
propsNode.properties.push({ type: 'SpreadElement', argument: annotationNode })
}
})
}
},
}