@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/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 ||= opts.parserPlugins || []
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/overriden in querystring.
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.js'),
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
@@ -1,5 +1,5 @@
1
1
  import type * as babelCore from '@babel/core'
2
- import type { types as t, Visitor } from '@babel/core'
2
+ import type { Visitor, types as t } from '@babel/core'
3
3
 
4
4
  /**
5
5
  * Replace this:
@@ -1,5 +1,6 @@
1
- import babelRestoreJSX from './babel-restore-jsx'
2
1
  import * as babel from '@babel/core'
2
+ import { describe, expect, it } from 'vitest'
3
+ import babelRestoreJSX from './babel-restore-jsx'
3
4
 
4
5
  function jsx(code: string) {
5
6
  return babel.transform(code, {
@@ -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
- code = code.replace(reactJsxRE, (_, prop) => {
34
- hasCompiledJsx = true
35
- // Replace with "React" so JSX can be reverse compiled.
36
- return 'React.' + prop
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
- // @ts-ignore
61
- plugins: [(await babelRestoreJSX).default]
80
+ plugins: [await getBabelRestoreJSX()]
62
81
  })
63
82
 
64
83
  return [result?.ast, isCommonJS]