forked from mdx-js/mdx
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.js
133 lines (105 loc) · 3.48 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
const visit = require('unist-util-visit')
const remove = require('unist-util-remove')
const IMPORT_REGEX = /^import/
const EXPORT_REGEX = /^export/
const isImport = text => IMPORT_REGEX.test(text)
const isExport = text => EXPORT_REGEX.test(text)
const restringify = node => {
if (node.type === 'link') {
return node.url
} else if (node.type === 'linkReference') {
return `[${node.children.map(n => n.value)}]`
} else {
return node.value
}
}
const modules = tree => {
return visit(tree, 'paragraph', (node, _i, parent) => {
// `import` must be defined at the top level to be a real import
if (parent.type !== 'root') {
return node
}
// Get the text from the text node
const { value } = node.children[0] || ''
// Sets type to `export` in the AST if it's an export
if (isExport(value)) {
node.type = 'export'
// Exports can have urls which remark-parse will turn into a child link node.
node.value = node.children.map(restringify).join(' ')
delete node.children
return node
}
// Import paragraphs only have text in 1 node
if (node.children.length !== 1) {
return node
}
// Sets type to `import` in the AST if it's an import
if (isImport(value)) {
node.type = 'import'
node.value = value
delete node.children
return node
}
return node
})
}
// match component name by regexp
const componentName = value => {
const match = value.match(/^\<\\?(\w+)/)
return match && match[1]
}
// iterate in a reverse way to merge values then delete the unused node
const valuesFromNodes = tree => (first, last) => {
const values = []
if (first !== last) {
for (let i = last; i >= first; i--) {
const found = tree.children[i]
if (found.children && found.children.length > 0) {
values.push(...found.children.map(child => child.value))
}
if (found.value && found.value.length > 0) {
values.push(found.value)
}
if (i !== first) remove(tree, found)
}
}
return values
}
const mergeNodeWithoutCloseTag = (tree, node, idx) => {
if (!node.value || typeof node.value !== 'string') return
// parse component name and create two regexp to check open and close tag
const component = componentName(node.value)
const tagOpen = new RegExp(`^\\<${component}`)
const tagClose = new RegExp(`\\<\\/${component}\\>$`)
const hasOpenTag = val => tagOpen.test(val)
const hasCloseTag = val => tagClose.test(val)
const hasJustCloseTag = val => val && !hasOpenTag(val) && hasCloseTag(val)
// return default value is has open and close tag
if (!component || (hasOpenTag(node.value) && hasCloseTag(node.value))) {
return
}
// when some node has just the open tag
// find node index with equivalent close tag
const tagCloseIdx = tree.children.findIndex(({ value, children }) => {
if (children) return children.some(c => hasJustCloseTag(c.value))
return hasJustCloseTag(value)
})
// merge all values from node open tag until node with the close tag
const mergeUntilCloseTag = valuesFromNodes(tree)
const values = mergeUntilCloseTag(idx, tagCloseIdx)
node.value = values.reverse().join('\n')
}
// turns `html` nodes into `jsx` nodes
const jsx = tree => {
visit(tree, 'html', visitor)
function visitor(node, idx) {
node.type = 'jsx'
// check if a node has just open tag
mergeNodeWithoutCloseTag(tree, node, idx)
}
}
module.exports = options => tree => {
modules(tree)
jsx(tree)
return tree
}