-
-
Notifications
You must be signed in to change notification settings - Fork 5.8k
/
restore-jsx.ts
74 lines (63 loc) · 1.64 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
import type * as babelCore from '@babel/core'
type RestoredJSX = [
result: babelCore.types.File | null | undefined,
isCommonJS: boolean
]
let babelRestoreJSX: Promise<babelCore.PluginItem> | undefined
const jsxNotFound: RestoredJSX = [null, false]
async function getBabelRestoreJSX() {
if (!babelRestoreJSX)
babelRestoreJSX = import('./babel-restore-jsx').then((r) => {
const fn = r.default
if ('default' in fn)
// @ts-expect-error
return fn.default
return fn
})
return babelRestoreJSX
}
/** Restore JSX from `React.createElement` calls */
export async function restoreJSX(
babel: typeof babelCore,
code: string,
filename: string
): Promise<RestoredJSX> {
const [reactAlias, isCommonJS] = parseReactAlias(code)
if (!reactAlias) {
return jsxNotFound
}
const reactJsxRE = new RegExp(
`\\b${reactAlias}\\.(createElement|Fragment)\\b`,
'g'
)
if (!reactJsxRE.test(code)) {
return jsxNotFound
}
const result = await babel.transformAsync(code, {
babelrc: false,
configFile: false,
ast: true,
code: false,
filename,
parserOpts: {
plugins: ['jsx']
},
plugins: [[await getBabelRestoreJSX(), { reactAlias }]]
})
return [result?.ast, isCommonJS]
}
export function parseReactAlias(
code: string
): [alias: string | undefined, isCommonJS: boolean] {
let match = code.match(
/\b(var|let|const)\s+([^=\{\s]+)\s*=\s*require\(["']react["']\)/
)
if (match) {
return [match[2], true]
}
match = code.match(/^import\s+(?:\*\s+as\s+)?(\w+).+?\bfrom\s*["']react["']/m)
if (match) {
return [match[1], false]
}
return [undefined, false]
}