@vitejs/plugin-react 1.3.0 → 2.0.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/chunks/babel-restore-jsx.cjs +125 -0
- package/dist/chunks/babel-restore-jsx.mjs +123 -0
- package/dist/index.cjs +399 -0
- package/dist/index.d.ts +62 -62
- package/dist/{index.js → index.mjs} +63 -226
- package/package.json +25 -13
- package/src/fast-refresh.ts +8 -3
- package/src/index.ts +4 -16
- package/src/jsx-runtime/babel-import-to-require.ts +1 -1
- package/src/jsx-runtime/babel-restore-jsx.spec.ts +2 -1
- package/src/jsx-runtime/restore-jsx.spec.ts +56 -0
- package/src/jsx-runtime/restore-jsx.ts +39 -20
package/src/index.ts
CHANGED
@@ -2,7 +2,7 @@ import type { ParserOptions, TransformOptions, types as t } from '@babel/core'
|
|
2
2
|
import * as babel from '@babel/core'
|
3
3
|
import { createFilter } from '@rollup/pluginutils'
|
4
4
|
import resolve from 'resolve'
|
5
|
-
import type { Plugin, PluginOption } from 'vite'
|
5
|
+
import type { Plugin, PluginOption, ResolvedConfig } from 'vite'
|
6
6
|
import {
|
7
7
|
addRefreshWrapper,
|
8
8
|
isRefreshBoundary,
|
@@ -38,15 +38,10 @@ export interface Options {
|
|
38
38
|
* @default true
|
39
39
|
*/
|
40
40
|
jsxPure?: boolean
|
41
|
-
|
42
41
|
/**
|
43
42
|
* Babel configuration applied in both dev and prod.
|
44
43
|
*/
|
45
44
|
babel?: BabelOptions
|
46
|
-
/**
|
47
|
-
* @deprecated Use `babel.parserOpts.plugins` instead
|
48
|
-
*/
|
49
|
-
parserPlugins?: ParserOptions['plugins']
|
50
45
|
}
|
51
46
|
|
52
47
|
export type BabelOptions = Omit<
|
@@ -104,7 +99,7 @@ export default function viteReact(opts: Options = {}): PluginOption[] {
|
|
104
99
|
babelOptions.presets ||= []
|
105
100
|
babelOptions.overrides ||= []
|
106
101
|
babelOptions.parserOpts ||= {} as any
|
107
|
-
babelOptions.parserOpts.plugins ||=
|
102
|
+
babelOptions.parserOpts.plugins ||= []
|
108
103
|
|
109
104
|
// Support patterns like:
|
110
105
|
// - import * as React from 'react';
|
@@ -154,7 +149,7 @@ export default function viteReact(opts: Options = {}): PluginOption[] {
|
|
154
149
|
},
|
155
150
|
async transform(code, id, options) {
|
156
151
|
const ssr = typeof options === 'boolean' ? options : options?.ssr === true
|
157
|
-
// File extension could be mocked/
|
152
|
+
// File extension could be mocked/overridden in querystring.
|
158
153
|
const [filepath, querystring = ''] = id.split('?')
|
159
154
|
const [extension = ''] =
|
160
155
|
querystring.match(fileExtensionRE) ||
|
@@ -176,7 +171,7 @@ export default function viteReact(opts: Options = {}): PluginOption[] {
|
|
176
171
|
if (isReactModule && filter(id)) {
|
177
172
|
useFastRefresh = true
|
178
173
|
plugins.push([
|
179
|
-
await loadPlugin('react-refresh/babel
|
174
|
+
await loadPlugin('react-refresh/babel'),
|
180
175
|
{ skipEnvCheck: true }
|
181
176
|
])
|
182
177
|
}
|
@@ -373,10 +368,3 @@ viteReact.preambleCode = preambleCode
|
|
373
368
|
function loadPlugin(path: string): Promise<any> {
|
374
369
|
return import(path).then((module) => module.default || module)
|
375
370
|
}
|
376
|
-
|
377
|
-
// overwrite for cjs require('...')() usage
|
378
|
-
// The following lines are inserted by scripts/patchEsbuildDist.ts,
|
379
|
-
// this doesn't bundle correctly after esbuild 0.14.4
|
380
|
-
//
|
381
|
-
// module.exports = viteReact
|
382
|
-
// viteReact['default'] = viteReact
|
@@ -0,0 +1,56 @@
|
|
1
|
+
import * as babel from '@babel/core'
|
2
|
+
import { describe, expect, it } from 'vitest'
|
3
|
+
import { restoreJSX } from './restore-jsx'
|
4
|
+
|
5
|
+
async function jsx(sourceCode: string) {
|
6
|
+
const [ast] = await restoreJSX(babel, sourceCode, 'test.js')
|
7
|
+
if (ast == null) {
|
8
|
+
return ast
|
9
|
+
}
|
10
|
+
const { code } = await babel.transformFromAstAsync(ast, null, {
|
11
|
+
configFile: false
|
12
|
+
})
|
13
|
+
return code
|
14
|
+
}
|
15
|
+
// jsx(`import React__default, { PureComponent, Component, forwardRef, memo, createElement } from 'react';
|
16
|
+
// React__default.createElement(Foo)`)
|
17
|
+
// Tests adapted from: https://github.com/flying-sheep/babel-plugin-transform-react-createelement-to-jsx/blob/63137b6/test/index.js
|
18
|
+
describe('restore-jsx', () => {
|
19
|
+
it('should trans to ', async () => {
|
20
|
+
expect(
|
21
|
+
await jsx(`import React__default, { PureComponent, Component, forwardRef, memo, createElement } from 'react';
|
22
|
+
React__default.createElement(foo)`)
|
23
|
+
).toBeNull()
|
24
|
+
expect(
|
25
|
+
await jsx(`import React__default, { PureComponent, Component, forwardRef, memo, createElement } from 'react';
|
26
|
+
React__default.createElement("h1")`)
|
27
|
+
).toMatch(`<h1 />;`)
|
28
|
+
expect(
|
29
|
+
await jsx(`import React__default, { PureComponent, Component, forwardRef, memo, createElement } from 'react';
|
30
|
+
React__default.createElement(Foo)`)
|
31
|
+
).toMatch(`<Foo />;`)
|
32
|
+
expect(
|
33
|
+
await jsx(`import React__default, { PureComponent, Component, forwardRef, memo, createElement } from 'react';
|
34
|
+
React__default.createElement(Foo.Bar)`)
|
35
|
+
).toMatch(`<Foo.Bar />;`)
|
36
|
+
expect(
|
37
|
+
await jsx(`import React__default, { PureComponent, Component, forwardRef, memo, createElement } from 'react';
|
38
|
+
React__default.createElement(Foo.Bar.Baz)`)
|
39
|
+
).toMatch(`<Foo.Bar.Baz />;`)
|
40
|
+
})
|
41
|
+
|
42
|
+
it('should handle props', async () => {
|
43
|
+
expect(
|
44
|
+
await jsx(`import React__default, { PureComponent, Component, forwardRef, memo, createElement } from 'react';
|
45
|
+
React__default.createElement(foo, {hi: there})`)
|
46
|
+
).toBeNull()
|
47
|
+
expect(
|
48
|
+
await jsx(`import React__default, { PureComponent, Component, forwardRef, memo, createElement } from 'react';
|
49
|
+
React__default.createElement("h1", {hi: there})`)
|
50
|
+
).toMatch(`<h1 hi={there} />;`)
|
51
|
+
expect(
|
52
|
+
await jsx(`import React__default, { PureComponent, Component, forwardRef, memo, createElement } from 'react';
|
53
|
+
React__default.createElement(Foo, {hi: there})`)
|
54
|
+
).toMatch(`<Foo hi={there} />;`)
|
55
|
+
})
|
56
|
+
})
|
@@ -7,6 +7,18 @@ let babelRestoreJSX: Promise<PluginItem> | undefined
|
|
7
7
|
|
8
8
|
const jsxNotFound: RestoredJSX = [null, false]
|
9
9
|
|
10
|
+
async function getBabelRestoreJSX() {
|
11
|
+
if (!babelRestoreJSX)
|
12
|
+
babelRestoreJSX = import('./babel-restore-jsx').then((r) => {
|
13
|
+
const fn = r.default
|
14
|
+
if ('default' in fn)
|
15
|
+
// @ts-expect-error
|
16
|
+
return fn.default
|
17
|
+
return fn
|
18
|
+
})
|
19
|
+
return babelRestoreJSX
|
20
|
+
}
|
21
|
+
|
10
22
|
/** Restore JSX from `React.createElement` calls */
|
11
23
|
export async function restoreJSX(
|
12
24
|
babel: typeof babelCore,
|
@@ -20,34 +32,42 @@ export async function restoreJSX(
|
|
20
32
|
}
|
21
33
|
|
22
34
|
const [reactAlias, isCommonJS] = parseReactAlias(code)
|
35
|
+
|
23
36
|
if (!reactAlias) {
|
24
37
|
return jsxNotFound
|
25
38
|
}
|
26
39
|
|
27
|
-
const reactJsxRE = new RegExp(
|
28
|
-
'\\b' + reactAlias + '\\.(createElement|Fragment)\\b',
|
29
|
-
'g'
|
30
|
-
)
|
31
|
-
|
32
40
|
let hasCompiledJsx = false
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
41
|
+
|
42
|
+
const fragmentPattern = `\\b${reactAlias}\\.Fragment\\b`
|
43
|
+
const createElementPattern = `\\b${reactAlias}\\.createElement\\(\\s*([A-Z"'][\\w$.]*["']?)`
|
44
|
+
|
45
|
+
// Replace the alias with "React" so JSX can be reverse compiled.
|
46
|
+
code = code
|
47
|
+
.replace(new RegExp(fragmentPattern, 'g'), () => {
|
48
|
+
hasCompiledJsx = true
|
49
|
+
return 'React.Fragment'
|
50
|
+
})
|
51
|
+
.replace(new RegExp(createElementPattern, 'g'), (original, component) => {
|
52
|
+
if (/^[a-z][\w$]*$/.test(component)) {
|
53
|
+
// Take care not to replace the alias for `createElement` calls whose
|
54
|
+
// component is a lowercased variable, since the `restoreJSX` Babel
|
55
|
+
// plugin leaves them untouched.
|
56
|
+
return original
|
57
|
+
}
|
58
|
+
hasCompiledJsx = true
|
59
|
+
return (
|
60
|
+
'React.createElement(' +
|
61
|
+
// Assume `Fragment` is equivalent to `React.Fragment` so modules
|
62
|
+
// that use `import {Fragment} from 'react'` are reverse compiled.
|
63
|
+
(component === 'Fragment' ? 'React.Fragment' : component)
|
64
|
+
)
|
65
|
+
})
|
38
66
|
|
39
67
|
if (!hasCompiledJsx) {
|
40
68
|
return jsxNotFound
|
41
69
|
}
|
42
70
|
|
43
|
-
// Support modules that use `import {Fragment} from 'react'`
|
44
|
-
code = code.replace(
|
45
|
-
/createElement\(Fragment,/g,
|
46
|
-
'createElement(React.Fragment,'
|
47
|
-
)
|
48
|
-
|
49
|
-
babelRestoreJSX ||= import('./babel-restore-jsx')
|
50
|
-
|
51
71
|
const result = await babel.transformAsync(code, {
|
52
72
|
babelrc: false,
|
53
73
|
configFile: false,
|
@@ -57,8 +77,7 @@ export async function restoreJSX(
|
|
57
77
|
parserOpts: {
|
58
78
|
plugins: ['jsx']
|
59
79
|
},
|
60
|
-
|
61
|
-
plugins: [(await babelRestoreJSX).default]
|
80
|
+
plugins: [await getBabelRestoreJSX()]
|
62
81
|
})
|
63
82
|
|
64
83
|
return [result?.ast, isCommonJS]
|