-
-
Notifications
You must be signed in to change notification settings - Fork 6.3k
/
restore-jsx.ts
81 lines (68 loc) · 1.96 KB
/
restore-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
import type * as babelCore from '@babel/core'
import type { PluginItem, types as t } from '@babel/core'
type RestoredJSX = [result: t.File | null | undefined, isCommonJS: boolean]
let babelRestoreJSX: Promise<PluginItem> | undefined
const jsxNotFound: RestoredJSX = [null, false]
/** Restore JSX from `React.createElement` calls */
export async function restoreJSX(
babel: typeof babelCore,
code: string,
filename: string
): Promise<RestoredJSX> {
// Avoid parsing the optimized react-dom since it will never
// contain compiled JSX and it's a pretty big file (800kb).
if (filename.includes('/.vite/react-dom.js')) {
return jsxNotFound
}
const [reactAlias, isCommonJS] = parseReactAlias(code)
if (!reactAlias) {
return jsxNotFound
}
const reactJsxRE = new RegExp(
'\\b' + reactAlias + '\\.(createElement|Fragment)\\b',
'g'
)
let hasCompiledJsx = false
code = code.replace(reactJsxRE, (_, prop) => {
hasCompiledJsx = true
// Replace with "React" so JSX can be reverse compiled.
return 'React.' + prop
})
if (!hasCompiledJsx) {
return jsxNotFound
}
// Support modules that use `import {Fragment} from 'react'`
code = code.replace(
/createElement\(Fragment,/g,
'createElement(React.Fragment,'
)
babelRestoreJSX ||= import('./babel-restore-jsx')
const result = await babel.transformAsync(code, {
babelrc: false,
configFile: false,
ast: true,
code: false,
filename,
parserOpts: {
plugins: ['jsx']
},
// @ts-ignore
plugins: [(await babelRestoreJSX).default]
})
return [result?.ast, isCommonJS]
}
function parseReactAlias(
code: string
): [alias: string | undefined, isCommonJS: boolean] {
let match = code.match(
/\b(var|let|const) +(\w+) *= *require\(["']react["']\)/
)
if (match) {
return [match[2], true]
}
match = code.match(/^import (\w+).+? from ["']react["']/m)
if (match) {
return [match[1], false]
}
return [undefined, false]
}