@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.
@@ -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
- }