/
jsx.ts
97 lines (83 loc) 路 2.4 KB
/
jsx.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
import { Node } from 'unist';
import visit from 'unist-util-visit';
import visitParents from 'unist-util-visit-parents';
const SINGLE_TAGS_EXPS = [
'area',
'base',
'br',
'col',
'command',
'embed',
'hr',
'img',
'input',
'keygen',
'link',
'meta',
'param',
'source',
'track',
'wbr',
].map(tag => new RegExp(`<(${tag}[^>]*?)>`, 'g'));
function hasSubClassName(className: string[], subCls: string) {
return (className || []).find(cls => cls.indexOf(subCls) > -1);
}
const textVisitor = (node, ancestors) => {
const parentNode = ancestors[ancestors.length - 1];
const closestCodeAncestor = ancestors
.slice()
.reverse()
.find(ancestor => ancestor.tagName === 'code');
// escape { & } for JSX
node.value = node.value.replace(/([{}])/g, "{'$1'}");
// convert \n to <br> in code block for JSX, for render indents & newlines
if (
closestCodeAncestor &&
(hasSubClassName(closestCodeAncestor.properties.className, 'language-') ||
ancestors[ancestors.length - 2].tagName === 'pre')
) {
const replace = node.value.split('\n').reduce((result, str, isNotFirst) => {
if (isNotFirst) {
result.push({ type: 'raw', value: '<br />' });
}
if (str) {
result.push({ type: 'text', value: str });
}
return result;
}, []);
// replace original children
parentNode.children.splice(parentNode.children.indexOf(node), 1, ...replace);
}
};
const rawVisitor = (node, i, parent) => {
const PRE_EXP = /^<pre>([^]+)<\/pre>$/;
const COMMENT_EXP = /^\s*<!--[^]+-->\s*$/;
// convert \n to <br> in code block for pre tag
if (PRE_EXP.test(node.value)) {
const content = node.value
.match(PRE_EXP)[1]
.replace(/^\n|\n$/g, '')
.replace(/\n/g, '<br />');
node.value = `<pre>${content}</pre>`;
}
// remove HTML comments for JSX
if (COMMENT_EXP.test(node.value)) {
parent.children.splice(i, 1);
}
// convert all self-closing HTML tag
// see also: https://github.com/umijs/umi/blob/master/packages/umi-build-dev/src/htmlToJSX.js#L118
if (!node.previewer) {
SINGLE_TAGS_EXPS.forEach(regex => {
node.value = node.value.replace(regex, (_, str) => {
if (str.endsWith('/')) {
return `<${str}>`;
}
return `<${str} />`;
});
});
}
};
export default () => (ast: Node) => {
visitParents(ast, 'text', textVisitor);
visit(ast, 'raw', rawVisitor);
};