@vitejs/plugin-react 2.1.0 → 2.2.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/dist/chunks/babel-restore-jsx.cjs +1 -1
- package/dist/index.cjs +53 -23
- package/dist/index.d.ts +5 -0
- package/dist/index.mjs +44 -8
- package/package.json +6 -7
- package/src/babel.d.ts +0 -4
- package/src/fast-refresh.ts +0 -117
- package/src/index.ts +0 -473
- package/src/jsx-runtime/babel-import-to-require.ts +0 -35
- package/src/jsx-runtime/babel-restore-jsx.spec.ts +0 -132
- package/src/jsx-runtime/babel-restore-jsx.ts +0 -232
- package/src/jsx-runtime/restore-jsx.spec.ts +0 -147
- package/src/jsx-runtime/restore-jsx.ts +0 -74
@@ -1,232 +0,0 @@
|
|
1
|
-
/**
|
2
|
-
* https://github.com/flying-sheep/babel-plugin-transform-react-createelement-to-jsx
|
3
|
-
* @license GNU General Public License v3.0
|
4
|
-
*/
|
5
|
-
import type * as babel from '@babel/core'
|
6
|
-
|
7
|
-
interface PluginOptions {
|
8
|
-
reactAlias: string
|
9
|
-
}
|
10
|
-
|
11
|
-
/**
|
12
|
-
* Visitor factory for babel, converting React.createElement(...) to <jsx ...>...</jsx>
|
13
|
-
*
|
14
|
-
* What we want to handle here is this CallExpression:
|
15
|
-
*
|
16
|
-
* React.createElement(
|
17
|
-
* type: StringLiteral|Identifier|MemberExpression,
|
18
|
-
* [props: ObjectExpression|Expression],
|
19
|
-
* [...children: StringLiteral|Expression]
|
20
|
-
* )
|
21
|
-
*
|
22
|
-
* Any of those arguments might also be missing (undefined) and/or invalid.
|
23
|
-
*/
|
24
|
-
export default function (
|
25
|
-
{ types: t }: typeof babel,
|
26
|
-
{ reactAlias = 'React' }: PluginOptions
|
27
|
-
): babel.PluginObj {
|
28
|
-
/**
|
29
|
-
* Get a `JSXElement` from a `CallExpression`.
|
30
|
-
* Returns `null` if this impossible.
|
31
|
-
*/
|
32
|
-
function getJSXNode(node: any): any {
|
33
|
-
if (!isReactCreateElement(node)) {
|
34
|
-
return null
|
35
|
-
}
|
36
|
-
|
37
|
-
//nameNode and propsNode may be undefined, getJSX* need to handle that
|
38
|
-
const [nameNode, propsNode, ...childNodes] = node.arguments
|
39
|
-
|
40
|
-
const name = getJSXName(nameNode)
|
41
|
-
if (name == null) {
|
42
|
-
return null //name is required
|
43
|
-
}
|
44
|
-
|
45
|
-
const props = getJSXProps(propsNode)
|
46
|
-
if (props == null) {
|
47
|
-
return null //no props → [], invalid → null
|
48
|
-
}
|
49
|
-
|
50
|
-
const children = getJSXChildren(childNodes)
|
51
|
-
if (children == null) {
|
52
|
-
return null //no children → [], invalid → null
|
53
|
-
}
|
54
|
-
|
55
|
-
if (
|
56
|
-
t.isJSXMemberExpression(name) &&
|
57
|
-
t.isJSXIdentifier(name.object) &&
|
58
|
-
name.object.name === reactAlias &&
|
59
|
-
name.property.name === 'Fragment'
|
60
|
-
) {
|
61
|
-
return t.jsxFragment(
|
62
|
-
t.jsxOpeningFragment(),
|
63
|
-
t.jsxClosingFragment(),
|
64
|
-
children
|
65
|
-
)
|
66
|
-
}
|
67
|
-
|
68
|
-
// self-closing tag if no children
|
69
|
-
const selfClosing = children.length === 0
|
70
|
-
const startTag = t.jsxOpeningElement(name, props, selfClosing)
|
71
|
-
startTag.loc = node.loc
|
72
|
-
const endTag = selfClosing ? null : t.jsxClosingElement(name)
|
73
|
-
|
74
|
-
return t.jsxElement(startTag, endTag, children, selfClosing)
|
75
|
-
}
|
76
|
-
|
77
|
-
/**
|
78
|
-
* Get a JSXIdentifier or JSXMemberExpression from a Node of known type.
|
79
|
-
* Returns null if an unknown node type, null or undefined is passed.
|
80
|
-
*/
|
81
|
-
function getJSXName(node: any): any {
|
82
|
-
if (node == null) {
|
83
|
-
return null
|
84
|
-
}
|
85
|
-
|
86
|
-
const name = getJSXIdentifier(node, true)
|
87
|
-
if (name != null) {
|
88
|
-
return name
|
89
|
-
}
|
90
|
-
|
91
|
-
if (!t.isMemberExpression(node)) {
|
92
|
-
return null
|
93
|
-
}
|
94
|
-
const object = getJSXName(node.object)
|
95
|
-
const property = getJSXName(node.property)
|
96
|
-
if (object == null || property == null) {
|
97
|
-
return null
|
98
|
-
}
|
99
|
-
return t.jsxMemberExpression(object, property)
|
100
|
-
}
|
101
|
-
|
102
|
-
/**
|
103
|
-
* Get an array of JSX(Spread)Attribute from a props ObjectExpression.
|
104
|
-
* Handles the _extends Expression babel creates from SpreadElement nodes.
|
105
|
-
* Returns null if a validation error occurs.
|
106
|
-
*/
|
107
|
-
function getJSXProps(node: any): any[] | null {
|
108
|
-
if (node == null || isNullLikeNode(node)) {
|
109
|
-
return []
|
110
|
-
}
|
111
|
-
|
112
|
-
if (
|
113
|
-
t.isCallExpression(node) &&
|
114
|
-
t.isIdentifier(node.callee, { name: '_extends' })
|
115
|
-
) {
|
116
|
-
const props: any[] = node.arguments.map(getJSXProps)
|
117
|
-
//if calling this recursively works, flatten.
|
118
|
-
if (props.every((prop) => prop != null)) {
|
119
|
-
return [].concat(...props)
|
120
|
-
}
|
121
|
-
}
|
122
|
-
|
123
|
-
if (!t.isObjectExpression(node) && t.isExpression(node))
|
124
|
-
return [t.jsxSpreadAttribute(node)]
|
125
|
-
|
126
|
-
if (!isPlainObjectExpression(node)) {
|
127
|
-
return null
|
128
|
-
}
|
129
|
-
return node.properties
|
130
|
-
.map((prop: any) =>
|
131
|
-
t.isObjectProperty(prop)
|
132
|
-
? t.jsxAttribute(
|
133
|
-
getJSXIdentifier(prop.key)!,
|
134
|
-
getJSXAttributeValue(prop.value)
|
135
|
-
)
|
136
|
-
: t.jsxSpreadAttribute(prop.argument)
|
137
|
-
)
|
138
|
-
.filter((prop: any) =>
|
139
|
-
t.isJSXIdentifier(prop.name)
|
140
|
-
? prop.name.name !== '__self' && prop.name.name !== '__source'
|
141
|
-
: true
|
142
|
-
)
|
143
|
-
}
|
144
|
-
|
145
|
-
function getJSXChild(node: any) {
|
146
|
-
if (t.isStringLiteral(node)) {
|
147
|
-
return t.jsxText(node.value)
|
148
|
-
}
|
149
|
-
if (isReactCreateElement(node)) {
|
150
|
-
return getJSXNode(node)
|
151
|
-
}
|
152
|
-
if (t.isExpression(node)) {
|
153
|
-
return t.jsxExpressionContainer(node)
|
154
|
-
}
|
155
|
-
return null
|
156
|
-
}
|
157
|
-
|
158
|
-
function getJSXChildren(nodes: any[]) {
|
159
|
-
const children = nodes
|
160
|
-
.filter((node) => !isNullLikeNode(node))
|
161
|
-
.map(getJSXChild)
|
162
|
-
if (children.some((child) => child == null)) {
|
163
|
-
return null
|
164
|
-
}
|
165
|
-
return children
|
166
|
-
}
|
167
|
-
|
168
|
-
function getJSXIdentifier(node: any, tag = false) {
|
169
|
-
//TODO: JSXNamespacedName
|
170
|
-
if (t.isIdentifier(node) && (!tag || node.name.match(/^[A-Z]/))) {
|
171
|
-
return t.jsxIdentifier(node.name)
|
172
|
-
}
|
173
|
-
if (t.isStringLiteral(node)) {
|
174
|
-
return t.jsxIdentifier(node.value)
|
175
|
-
}
|
176
|
-
return null
|
177
|
-
}
|
178
|
-
|
179
|
-
function getJSXAttributeValue(node: any) {
|
180
|
-
if (t.isStringLiteral(node)) {
|
181
|
-
return node
|
182
|
-
}
|
183
|
-
if (t.isJSXElement(node)) {
|
184
|
-
return node
|
185
|
-
}
|
186
|
-
if (t.isExpression(node)) {
|
187
|
-
return t.jsxExpressionContainer(node)
|
188
|
-
}
|
189
|
-
return null
|
190
|
-
}
|
191
|
-
|
192
|
-
/**
|
193
|
-
* Tests if a node is a CallExpression with callee `React.createElement`
|
194
|
-
*/
|
195
|
-
const isReactCreateElement = (node: any) =>
|
196
|
-
t.isCallExpression(node) &&
|
197
|
-
t.isMemberExpression(node.callee) &&
|
198
|
-
t.isIdentifier(node.callee.object, { name: reactAlias }) &&
|
199
|
-
t.isIdentifier(node.callee.property, { name: 'createElement' }) &&
|
200
|
-
!node.callee.computed
|
201
|
-
|
202
|
-
/**
|
203
|
-
* Tests if a node is `null` or `undefined`
|
204
|
-
*/
|
205
|
-
const isNullLikeNode = (node: any) =>
|
206
|
-
t.isNullLiteral(node) || t.isIdentifier(node, { name: 'undefined' })
|
207
|
-
|
208
|
-
/**
|
209
|
-
* Tests if a node is an object expression with noncomputed, nonmethod attrs
|
210
|
-
*/
|
211
|
-
const isPlainObjectExpression = (node: any) =>
|
212
|
-
t.isObjectExpression(node) &&
|
213
|
-
node.properties.every(
|
214
|
-
(property) =>
|
215
|
-
t.isSpreadElement(property) ||
|
216
|
-
(t.isObjectProperty(property, { computed: false }) &&
|
217
|
-
getJSXIdentifier(property.key) != null &&
|
218
|
-
getJSXAttributeValue(property.value) != null)
|
219
|
-
)
|
220
|
-
|
221
|
-
return {
|
222
|
-
visitor: {
|
223
|
-
CallExpression(path) {
|
224
|
-
const node = getJSXNode(path.node)
|
225
|
-
if (node == null) {
|
226
|
-
return null
|
227
|
-
}
|
228
|
-
path.replaceWith(node)
|
229
|
-
}
|
230
|
-
}
|
231
|
-
}
|
232
|
-
}
|
@@ -1,147 +0,0 @@
|
|
1
|
-
import * as babel from '@babel/core'
|
2
|
-
import { describe, expect, it } from 'vitest'
|
3
|
-
import { parseReactAlias, restoreJSX } from './restore-jsx'
|
4
|
-
|
5
|
-
describe('parseReactAlias', () => {
|
6
|
-
it('handles cjs require', () => {
|
7
|
-
expect(parseReactAlias(`const React = require("react")`))
|
8
|
-
.toMatchInlineSnapshot(`
|
9
|
-
[
|
10
|
-
"React",
|
11
|
-
true,
|
12
|
-
]
|
13
|
-
`)
|
14
|
-
})
|
15
|
-
|
16
|
-
it('handles cjs require (minified)', () => {
|
17
|
-
expect(parseReactAlias(`var F=require('foo');var R=require('react')`))
|
18
|
-
.toMatchInlineSnapshot(`
|
19
|
-
[
|
20
|
-
"R",
|
21
|
-
true,
|
22
|
-
]
|
23
|
-
`)
|
24
|
-
})
|
25
|
-
|
26
|
-
it('does not handle destructured cjs require', () => {
|
27
|
-
expect(parseReactAlias(`var {createElement} = require("react")`))
|
28
|
-
.toMatchInlineSnapshot(`
|
29
|
-
[
|
30
|
-
undefined,
|
31
|
-
false,
|
32
|
-
]
|
33
|
-
`)
|
34
|
-
})
|
35
|
-
|
36
|
-
it('handles esm import', () => {
|
37
|
-
expect(parseReactAlias(`import React from 'react'`)).toMatchInlineSnapshot(`
|
38
|
-
[
|
39
|
-
"React",
|
40
|
-
false,
|
41
|
-
]
|
42
|
-
`)
|
43
|
-
})
|
44
|
-
|
45
|
-
it('handles esm import namespace', () => {
|
46
|
-
expect(parseReactAlias(`import * as React from "react"`))
|
47
|
-
.toMatchInlineSnapshot(`
|
48
|
-
[
|
49
|
-
"React",
|
50
|
-
false,
|
51
|
-
]
|
52
|
-
`)
|
53
|
-
})
|
54
|
-
|
55
|
-
it('does not handle destructured esm import', () => {
|
56
|
-
expect(parseReactAlias(`import {createElement} from "react"`))
|
57
|
-
.toMatchInlineSnapshot(`
|
58
|
-
[
|
59
|
-
undefined,
|
60
|
-
false,
|
61
|
-
]
|
62
|
-
`)
|
63
|
-
})
|
64
|
-
})
|
65
|
-
|
66
|
-
async function jsx(sourceCode: string) {
|
67
|
-
const [ast] = await restoreJSX(babel, sourceCode, 'test.js')
|
68
|
-
if (ast == null) {
|
69
|
-
return ast
|
70
|
-
}
|
71
|
-
const { code } = await babel.transformFromAstAsync(ast, null, {
|
72
|
-
configFile: false
|
73
|
-
})
|
74
|
-
return code
|
75
|
-
}
|
76
|
-
// jsx(`import React__default, { PureComponent, Component, forwardRef, memo, createElement } from 'react';
|
77
|
-
// React__default.createElement(Foo)`)
|
78
|
-
// Tests adapted from: https://github.com/flying-sheep/babel-plugin-transform-react-createelement-to-jsx/blob/63137b6/test/index.js
|
79
|
-
describe('restore-jsx', () => {
|
80
|
-
it('should trans to ', async () => {
|
81
|
-
expect(
|
82
|
-
await jsx(`import React__default, { PureComponent, Component, forwardRef, memo, createElement } from 'react';
|
83
|
-
React__default.createElement(foo)`)
|
84
|
-
).toMatchInlineSnapshot(`
|
85
|
-
"import React__default, { PureComponent, Component, forwardRef, memo, createElement } from 'react';
|
86
|
-
React__default.createElement(foo);"
|
87
|
-
`)
|
88
|
-
expect(
|
89
|
-
await jsx(`import React__default, { PureComponent, Component, forwardRef, memo, createElement } from 'react';
|
90
|
-
React__default.createElement("h1")`)
|
91
|
-
).toMatch(`<h1 />;`)
|
92
|
-
expect(
|
93
|
-
await jsx(`import React__default, { PureComponent, Component, forwardRef, memo, createElement } from 'react';
|
94
|
-
React__default.createElement(Foo)`)
|
95
|
-
).toMatch(`<Foo />;`)
|
96
|
-
expect(
|
97
|
-
await jsx(`import React__default, { PureComponent, Component, forwardRef, memo, createElement } from 'react';
|
98
|
-
React__default.createElement(Foo.Bar)`)
|
99
|
-
).toMatch(`<Foo.Bar />;`)
|
100
|
-
expect(
|
101
|
-
await jsx(`import React__default, { PureComponent, Component, forwardRef, memo, createElement } from 'react';
|
102
|
-
React__default.createElement(Foo.Bar.Baz)`)
|
103
|
-
).toMatch(`<Foo.Bar.Baz />;`)
|
104
|
-
})
|
105
|
-
|
106
|
-
it('should handle props', async () => {
|
107
|
-
expect(
|
108
|
-
await jsx(`import React__default, { PureComponent, Component, forwardRef, memo, createElement } from 'react';
|
109
|
-
React__default.createElement(foo, {hi: there})`)
|
110
|
-
).toMatchInlineSnapshot(`
|
111
|
-
"import React__default, { PureComponent, Component, forwardRef, memo, createElement } from 'react';
|
112
|
-
React__default.createElement(foo, {
|
113
|
-
hi: there
|
114
|
-
});"
|
115
|
-
`)
|
116
|
-
expect(
|
117
|
-
await jsx(`import React__default, { PureComponent, Component, forwardRef, memo, createElement } from 'react';
|
118
|
-
React__default.createElement("h1", {hi: there})`)
|
119
|
-
).toMatch(`<h1 hi={there} />;`)
|
120
|
-
expect(
|
121
|
-
await jsx(`import React__default, { PureComponent, Component, forwardRef, memo, createElement } from 'react';
|
122
|
-
React__default.createElement(Foo, {hi: there})`)
|
123
|
-
).toMatch(`<Foo hi={there} />;`)
|
124
|
-
})
|
125
|
-
|
126
|
-
it('should handle Fragment', async () => {
|
127
|
-
expect(
|
128
|
-
await jsx(`import R, { Fragment } from 'react';
|
129
|
-
R.createElement(Fragment)
|
130
|
-
`)
|
131
|
-
).toMatchInlineSnapshot(`
|
132
|
-
"import R, { Fragment } from 'react';
|
133
|
-
<Fragment />;"
|
134
|
-
`)
|
135
|
-
})
|
136
|
-
|
137
|
-
it('should handle Fragment alias', async () => {
|
138
|
-
expect(
|
139
|
-
await jsx(`import RA, { Fragment as F } from 'react';
|
140
|
-
RA.createElement(F, null, RA.createElement(RA.Fragment))
|
141
|
-
`)
|
142
|
-
).toMatchInlineSnapshot(`
|
143
|
-
"import RA, { Fragment as F } from 'react';
|
144
|
-
<F><></></F>;"
|
145
|
-
`)
|
146
|
-
})
|
147
|
-
})
|
@@ -1,74 +0,0 @@
|
|
1
|
-
import type * as babelCore from '@babel/core'
|
2
|
-
|
3
|
-
type RestoredJSX = [
|
4
|
-
result: babelCore.types.File | null | undefined,
|
5
|
-
isCommonJS: boolean
|
6
|
-
]
|
7
|
-
|
8
|
-
let babelRestoreJSX: Promise<babelCore.PluginItem> | undefined
|
9
|
-
|
10
|
-
const jsxNotFound: RestoredJSX = [null, false]
|
11
|
-
|
12
|
-
async function getBabelRestoreJSX() {
|
13
|
-
if (!babelRestoreJSX)
|
14
|
-
babelRestoreJSX = import('./babel-restore-jsx').then((r) => {
|
15
|
-
const fn = r.default
|
16
|
-
if ('default' in fn)
|
17
|
-
// @ts-expect-error
|
18
|
-
return fn.default
|
19
|
-
return fn
|
20
|
-
})
|
21
|
-
return babelRestoreJSX
|
22
|
-
}
|
23
|
-
|
24
|
-
/** Restore JSX from `React.createElement` calls */
|
25
|
-
export async function restoreJSX(
|
26
|
-
babel: typeof babelCore,
|
27
|
-
code: string,
|
28
|
-
filename: string
|
29
|
-
): Promise<RestoredJSX> {
|
30
|
-
const [reactAlias, isCommonJS] = parseReactAlias(code)
|
31
|
-
|
32
|
-
if (!reactAlias) {
|
33
|
-
return jsxNotFound
|
34
|
-
}
|
35
|
-
|
36
|
-
const reactJsxRE = new RegExp(
|
37
|
-
`\\b${reactAlias}\\.(createElement|Fragment)\\b`,
|
38
|
-
'g'
|
39
|
-
)
|
40
|
-
|
41
|
-
if (!reactJsxRE.test(code)) {
|
42
|
-
return jsxNotFound
|
43
|
-
}
|
44
|
-
|
45
|
-
const result = await babel.transformAsync(code, {
|
46
|
-
babelrc: false,
|
47
|
-
configFile: false,
|
48
|
-
ast: true,
|
49
|
-
code: false,
|
50
|
-
filename,
|
51
|
-
parserOpts: {
|
52
|
-
plugins: ['jsx']
|
53
|
-
},
|
54
|
-
plugins: [[await getBabelRestoreJSX(), { reactAlias }]]
|
55
|
-
})
|
56
|
-
|
57
|
-
return [result?.ast, isCommonJS]
|
58
|
-
}
|
59
|
-
|
60
|
-
export function parseReactAlias(
|
61
|
-
code: string
|
62
|
-
): [alias: string | undefined, isCommonJS: boolean] {
|
63
|
-
let match = code.match(
|
64
|
-
/\b(var|let|const)\s+([^=\{\s]+)\s*=\s*require\(["']react["']\)/
|
65
|
-
)
|
66
|
-
if (match) {
|
67
|
-
return [match[2], true]
|
68
|
-
}
|
69
|
-
match = code.match(/^import\s+(?:\*\s+as\s+)?(\w+).+?\bfrom\s*["']react["']/m)
|
70
|
-
if (match) {
|
71
|
-
return [match[1], false]
|
72
|
-
}
|
73
|
-
return [undefined, false]
|
74
|
-
}
|