-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
/
index.ts
111 lines (91 loc) · 3.16 KB
/
index.ts
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
import * as url from 'url'
import generate from '@babel/generator'
import t from '@babel/types'
import visit from 'unist-util-visit'
import remove from 'unist-util-remove'
// TODO: create types
// match component name by regexp
const componentName = (value: any) => {
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: any) => (first: number, last: number) => {
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: any) => child.value))
}
if (found.value && found.value.length > 0) {
values.push(found.value)
}
if (i !== first) remove(tree, found)
}
}
return values
}
const mergeNodeWithoutCloseTag = (tree: any, node: any, idx: any) => {
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: any) => tagOpen.test(val)
const hasCloseTag = (val: any) => tagClose.test(val)
const hasJustCloseTag = (val: any) =>
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 }: any) => {
if (children) return children.some((c: any) => hasJustCloseTag(c.value))
return hasJustCloseTag(value)
})
if (tagCloseIdx > -1 && tagCloseIdx !== idx) {
// 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')
}
}
const createImgSrc = (src: string) => {
const parsed = url.parse(src)
if (parsed.protocol) {
return t.stringLiteral(src)
}
let { pathname } = parsed as { pathname: string }
if (!/^(?:\.[./]+|@)/.test(pathname)) {
pathname = `./${pathname}`
}
return t.jsxExpressionContainer(
t.callExpression(t.identifier('require'), [t.stringLiteral(pathname)])
)
}
const imageToJsx = (node: any): string =>
generate(
t.jsxOpeningElement(
t.jsxIdentifier('img'),
[
t.jsxAttribute(t.jsxIdentifier('alt'), t.stringLiteral(node.alt)),
t.jsxAttribute(t.jsxIdentifier('src'), createImgSrc(node.url)),
],
true
)
).code
// turns `html` nodes into `jsx` nodes
export default () => (tree: any) => {
visit(tree, 'image', (node: any): void => {
// check if a node has just open tag
node.type = 'jsx'
node.value = imageToJsx(node)
})
visit(tree, 'jsx', (node: any, idx: any): void => {
// check if a node has just open tag
mergeNodeWithoutCloseTag(tree, node, idx)
})
}