@vitejs/plugin-react 1.1.3 → 1.1.4

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/CHANGELOG.md CHANGED
@@ -1,3 +1,18 @@
1
+ ## [1.1.4](https://github.com/vitejs/vite/compare/plugin-react@1.1.3...plugin-react@1.1.4) (2022-01-04)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * **plugin-react:** check for import React statement in .js files ([#6320](https://github.com/vitejs/vite/issues/6320)) ([bd9e97b](https://github.com/vitejs/vite/commit/bd9e97bd1b9156059b78b531871a12f6f47c04b1)), closes [#6148](https://github.com/vitejs/vite/issues/6148) [#6148](https://github.com/vitejs/vite/issues/6148)
7
+ * **plugin-react:** restore-jsx bug when component name is lowercase ([#6110](https://github.com/vitejs/vite/issues/6110)) ([ce65c56](https://github.com/vitejs/vite/commit/ce65c567a64fad3be4209cbd1132e62e905fe349))
8
+
9
+
10
+ ### Features
11
+
12
+ * **plugin-react:** check for `api.reactBabel` on other plugins ([#5454](https://github.com/vitejs/vite/issues/5454)) ([2ab41b3](https://github.com/vitejs/vite/commit/2ab41b3184d2452be4fa0b427f05c791311644aa))
13
+
14
+
15
+
1
16
  ## [1.1.3](https://github.com/vitejs/vite/compare/plugin-react@1.1.2...plugin-react@1.1.3) (2021-12-13)
2
17
 
3
18
 
package/README.md CHANGED
@@ -33,6 +33,16 @@ react({
33
33
  })
34
34
  ```
35
35
 
36
+ ## Opting out of the automatic JSX runtime
37
+
38
+ By default, the plugin uses the [automatic JSX runtime](https://github.com/alloc/vite-react-jsx#faq). However, if you encounter any issues, you may opt out using the `jsxRuntime` option.
39
+
40
+ ```js
41
+ react({
42
+ jsxRuntime: 'classic'
43
+ })
44
+ ```
45
+
36
46
  ## Babel configuration
37
47
 
38
48
  The `babel` option lets you add plugins, presets, and [other configuration](https://babeljs.io/docs/en/options) to the Babel transformation performed on each JSX/TSX file.
package/dist/index.d.ts CHANGED
@@ -2,6 +2,8 @@ import type { ParserOptions } from '@babel/core';
2
2
  import type { PluginOption } from 'vite';
3
3
  import type { TransformOptions } from '@babel/core';
4
4
 
5
+ export declare type BabelOptions = Omit<TransformOptions, 'ast' | 'filename' | 'root' | 'sourceFileName' | 'sourceMaps' | 'inputSourceMap'>;
6
+
5
7
  export declare interface Options {
6
8
  include?: string | RegExp | Array<string | RegExp>;
7
9
  exclude?: string | RegExp | Array<string | RegExp>;
@@ -24,13 +26,25 @@ export declare interface Options {
24
26
  /**
25
27
  * Babel configuration applied in both dev and prod.
26
28
  */
27
- babel?: TransformOptions;
29
+ babel?: BabelOptions;
28
30
  /**
29
31
  * @deprecated Use `babel.parserOpts.plugins` instead
30
32
  */
31
33
  parserPlugins?: ParserOptions['plugins'];
32
34
  }
33
35
 
36
+ /**
37
+ * The object type used by the `options` passed to plugins with
38
+ * an `api.reactBabel` method.
39
+ */
40
+ export declare interface ReactBabelOptions extends BabelOptions {
41
+ plugins: Extract<BabelOptions['plugins'], any[]>;
42
+ presets: Extract<BabelOptions['presets'], any[]>;
43
+ parserOpts: ParserOptions & {
44
+ plugins: Extract<ParserOptions['plugins'], any[]>;
45
+ };
46
+ }
47
+
34
48
  declare function viteReact(opts?: Options): PluginOption[];
35
49
 
36
50
  declare namespace viteReact {
package/dist/index.js CHANGED
@@ -78,7 +78,7 @@ function babel_restore_jsx_default({ types: t }) {
78
78
  if (node == null) {
79
79
  return null;
80
80
  }
81
- const name = getJSXIdentifier(node);
81
+ const name = getJSXIdentifier(node, true);
82
82
  if (name != null) {
83
83
  return name;
84
84
  }
@@ -128,8 +128,8 @@ function babel_restore_jsx_default({ types: t }) {
128
128
  }
129
129
  return children;
130
130
  }
131
- function getJSXIdentifier(node) {
132
- if (t.isIdentifier(node)) {
131
+ function getJSXIdentifier(node, tag = false) {
132
+ if (t.isIdentifier(node) && (!tag || node.name.match(/^[A-Z]/))) {
133
133
  return t.jsxIdentifier(node.name);
134
134
  }
135
135
  if (t.isStringLiteral(node)) {
@@ -265,9 +265,7 @@ function isComponentLikeName(name) {
265
265
  }
266
266
 
267
267
  // src/jsx-runtime/babel-import-to-require.ts
268
- function babelImportToRequire({
269
- types: t
270
- }) {
268
+ function babelImportToRequire({ types: t }) {
271
269
  return {
272
270
  visitor: {
273
271
  ImportDeclaration(path) {
@@ -330,7 +328,7 @@ function parseReactAlias(code) {
330
328
 
331
329
  // src/index.ts
332
330
  function viteReact(opts = {}) {
333
- var _a, _b, _c;
331
+ var _a;
334
332
  let base = "/";
335
333
  let filter = (0, import_pluginutils.createFilter)(opts.include, opts.exclude);
336
334
  let isProduction = true;
@@ -338,8 +336,14 @@ function viteReact(opts = {}) {
338
336
  let skipFastRefresh = opts.fastRefresh === false;
339
337
  let skipReactImport = false;
340
338
  const useAutomaticRuntime = opts.jsxRuntime !== "classic";
341
- const userPlugins = ((_a = opts.babel) == null ? void 0 : _a.plugins) || [];
342
- const userParserPlugins = opts.parserPlugins || ((_c = (_b = opts.babel) == null ? void 0 : _b.parserOpts) == null ? void 0 : _c.plugins) || [];
339
+ const babelOptions = __spreadValues({
340
+ babelrc: false,
341
+ configFile: false
342
+ }, opts.babel);
343
+ babelOptions.plugins || (babelOptions.plugins = []);
344
+ babelOptions.presets || (babelOptions.presets = []);
345
+ babelOptions.parserOpts || (babelOptions.parserOpts = {});
346
+ (_a = babelOptions.parserOpts).plugins || (_a.plugins = opts.parserPlugins || []);
343
347
  const importReactRE = /(^|\n)import\s+(\*\s+as\s+)?React(,|\s+)/;
344
348
  const fileExtensionRE = /\.[^\/\s\?]+$/;
345
349
  const viteBabel = {
@@ -358,10 +362,17 @@ function viteReact(opts = {}) {
358
362
  skipReactImport = true;
359
363
  config.logger.warn("[@vitejs/plugin-react] This plugin imports React for you automatically, so you can stop using `esbuild.jsxInject` for that purpose.");
360
364
  }
361
- config.plugins.forEach((plugin) => (plugin.name === "react-refresh" || plugin !== viteReactJsx && plugin.name === "vite:react-jsx") && config.logger.warn(`[@vitejs/plugin-react] You should stop using "${plugin.name}" since this plugin conflicts with it.`));
365
+ config.plugins.forEach((plugin) => {
366
+ var _a2;
367
+ const hasConflict = plugin.name === "react-refresh" || plugin !== viteReactJsx && plugin.name === "vite:react-jsx";
368
+ if (hasConflict)
369
+ return config.logger.warn(`[@vitejs/plugin-react] You should stop using "${plugin.name}" since this plugin conflicts with it.`);
370
+ if ((_a2 = plugin.api) == null ? void 0 : _a2.reactBabel) {
371
+ plugin.api.reactBabel(babelOptions, config);
372
+ }
373
+ });
362
374
  },
363
375
  async transform(code, id, options) {
364
- var _a2, _b2, _c2, _d;
365
376
  const ssr = typeof options === "boolean" ? options : (options == null ? void 0 : options.ssr) === true;
366
377
  const [filepath, querystring = ""] = id.split("?");
367
378
  const [extension = ""] = querystring.match(fileExtensionRE) || filepath.match(fileExtensionRE) || [];
@@ -369,10 +380,10 @@ function viteReact(opts = {}) {
369
380
  const isJSX = extension.endsWith("x");
370
381
  const isNodeModules = id.includes("/node_modules/");
371
382
  const isProjectFile = !isNodeModules && (id[0] === "\0" || id.startsWith(projectRoot + "/"));
372
- const plugins = isProjectFile ? [...userPlugins] : [];
383
+ const plugins = isProjectFile ? [...babelOptions.plugins] : [];
373
384
  let useFastRefresh = false;
374
385
  if (!skipFastRefresh && !ssr && !isNodeModules) {
375
- const isReactModule = isJSX || code.includes("react");
386
+ const isReactModule = isJSX || importReactRE.test(code);
376
387
  if (isReactModule && filter(id)) {
377
388
  useFastRefresh = true;
378
389
  plugins.push([
@@ -406,12 +417,12 @@ function viteReact(opts = {}) {
406
417
  }
407
418
  }
408
419
  }
409
- const shouldSkip = !plugins.length && !((_a2 = opts.babel) == null ? void 0 : _a2.configFile) && !(isProjectFile && ((_b2 = opts.babel) == null ? void 0 : _b2.babelrc));
420
+ const shouldSkip = !plugins.length && !babelOptions.configFile && !(isProjectFile && babelOptions.babelrc);
410
421
  if (shouldSkip) {
411
422
  return;
412
423
  }
413
424
  const parserPlugins = [
414
- ...userParserPlugins,
425
+ ...babelOptions.parserOpts.plugins,
415
426
  "importMeta",
416
427
  "topLevelAwait",
417
428
  "classProperties",
@@ -424,28 +435,25 @@ function viteReact(opts = {}) {
424
435
  if (/\.tsx?$/.test(extension)) {
425
436
  parserPlugins.push("typescript");
426
437
  }
438
+ const transformAsync2 = ast ? babel.transformFromAstAsync.bind(babel, ast, code) : babel.transformAsync.bind(babel, code);
427
439
  const isReasonReact = extension.endsWith(".bs.js");
428
- const babelOpts = __spreadProps(__spreadValues({
429
- babelrc: false,
430
- configFile: false
431
- }, opts.babel), {
440
+ const result = await transformAsync2(__spreadProps(__spreadValues({}, babelOptions), {
432
441
  ast: !isReasonReact,
433
442
  root: projectRoot,
434
443
  filename: id,
435
444
  sourceFileName: filepath,
436
- parserOpts: __spreadProps(__spreadValues({}, (_c2 = opts.babel) == null ? void 0 : _c2.parserOpts), {
445
+ parserOpts: __spreadProps(__spreadValues({}, babelOptions.parserOpts), {
437
446
  sourceType: "module",
438
447
  allowAwaitOutsideFunction: true,
439
448
  plugins: parserPlugins
440
449
  }),
441
- generatorOpts: __spreadProps(__spreadValues({}, (_d = opts.babel) == null ? void 0 : _d.generatorOpts), {
450
+ generatorOpts: __spreadProps(__spreadValues({}, babelOptions.generatorOpts), {
442
451
  decoratorsBeforeExport: true
443
452
  }),
444
453
  plugins,
445
454
  sourceMaps: true,
446
455
  inputSourceMap: false
447
- });
448
- const result = ast ? await babel.transformFromAstAsync(ast, code, babelOpts) : await babel.transformAsync(code, babelOpts);
456
+ }));
449
457
  if (result) {
450
458
  let code2 = result.code;
451
459
  if (useFastRefresh && /\$RefreshReg\$\(/.test(code2)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vitejs/plugin-react",
3
- "version": "1.1.3",
3
+ "version": "1.1.4",
4
4
  "license": "MIT",
5
5
  "author": "Evan You",
6
6
  "contributors": [
@@ -18,7 +18,7 @@
18
18
  "build-bundle": "esbuild src/index.ts --bundle --platform=node --target=node12 --external:@babel/* --external:@rollup/* --external:resolve --external:react-refresh/* --outfile=dist/index.js",
19
19
  "build-types": "tsc -p . --emitDeclarationOnly --outDir temp && api-extractor run && rimraf temp",
20
20
  "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s --commit-path . --lerna-package plugin-react",
21
- "release": "node ../../scripts/release.cjs"
21
+ "release": "ts-node ../../scripts/release.ts"
22
22
  },
23
23
  "engines": {
24
24
  "node": ">=12.0.0"
@@ -33,12 +33,12 @@
33
33
  },
34
34
  "homepage": "https://github.com/vitejs/vite/tree/main/packages/plugin-react#readme",
35
35
  "dependencies": {
36
- "@babel/core": "^7.16.0",
37
- "@babel/plugin-transform-react-jsx": "^7.16.0",
38
- "@babel/plugin-transform-react-jsx-development": "^7.16.0",
39
- "@babel/plugin-transform-react-jsx-self": "^7.16.0",
40
- "@babel/plugin-transform-react-jsx-source": "^7.16.0",
41
- "@rollup/pluginutils": "^4.1.1",
36
+ "@babel/core": "^7.16.5",
37
+ "@babel/plugin-transform-react-jsx": "^7.16.5",
38
+ "@babel/plugin-transform-react-jsx-development": "^7.16.5",
39
+ "@babel/plugin-transform-react-jsx-self": "^7.16.5",
40
+ "@babel/plugin-transform-react-jsx-source": "^7.16.5",
41
+ "@rollup/pluginutils": "^4.1.2",
42
42
  "react-refresh": "^0.11.0",
43
43
  "resolve": "^1.20.0"
44
44
  }
package/src/index.ts CHANGED
@@ -36,13 +36,46 @@ export interface Options {
36
36
  /**
37
37
  * Babel configuration applied in both dev and prod.
38
38
  */
39
- babel?: TransformOptions
39
+ babel?: BabelOptions
40
40
  /**
41
41
  * @deprecated Use `babel.parserOpts.plugins` instead
42
42
  */
43
43
  parserPlugins?: ParserOptions['plugins']
44
44
  }
45
45
 
46
+ export type BabelOptions = Omit<
47
+ TransformOptions,
48
+ | 'ast'
49
+ | 'filename'
50
+ | 'root'
51
+ | 'sourceFileName'
52
+ | 'sourceMaps'
53
+ | 'inputSourceMap'
54
+ >
55
+
56
+ /**
57
+ * The object type used by the `options` passed to plugins with
58
+ * an `api.reactBabel` method.
59
+ */
60
+ export interface ReactBabelOptions extends BabelOptions {
61
+ plugins: Extract<BabelOptions['plugins'], any[]>
62
+ presets: Extract<BabelOptions['presets'], any[]>
63
+ parserOpts: ParserOptions & {
64
+ plugins: Extract<ParserOptions['plugins'], any[]>
65
+ }
66
+ }
67
+
68
+ declare module 'vite' {
69
+ export interface Plugin {
70
+ api?: {
71
+ /**
72
+ * Manipulate the Babel options of `@vitejs/plugin-react`
73
+ */
74
+ reactBabel?: (options: ReactBabelOptions, config: ResolvedConfig) => void
75
+ }
76
+ }
77
+ }
78
+
46
79
  export default function viteReact(opts: Options = {}): PluginOption[] {
47
80
  // Provide default values for Rollup compat.
48
81
  let base = '/'
@@ -54,11 +87,18 @@ export default function viteReact(opts: Options = {}): PluginOption[] {
54
87
 
55
88
  const useAutomaticRuntime = opts.jsxRuntime !== 'classic'
56
89
 
57
- const userPlugins = opts.babel?.plugins || []
58
- const userParserPlugins =
59
- opts.parserPlugins || opts.babel?.parserOpts?.plugins || []
90
+ const babelOptions = {
91
+ babelrc: false,
92
+ configFile: false,
93
+ ...opts.babel
94
+ } as ReactBabelOptions
60
95
 
61
- // Support pattens like:
96
+ babelOptions.plugins ||= []
97
+ babelOptions.presets ||= []
98
+ babelOptions.parserOpts ||= {} as any
99
+ babelOptions.parserOpts.plugins ||= opts.parserPlugins || []
100
+
101
+ // Support patterns like:
62
102
  // - import * as React from 'react';
63
103
  // - import React from 'react';
64
104
  // - import React, {useEffect} from 'react';
@@ -88,15 +128,21 @@ export default function viteReact(opts: Options = {}): PluginOption[] {
88
128
  )
89
129
  }
90
130
 
91
- config.plugins.forEach(
92
- (plugin) =>
93
- (plugin.name === 'react-refresh' ||
94
- (plugin !== viteReactJsx && plugin.name === 'vite:react-jsx')) &&
95
- config.logger.warn(
131
+ config.plugins.forEach((plugin) => {
132
+ const hasConflict =
133
+ plugin.name === 'react-refresh' ||
134
+ (plugin !== viteReactJsx && plugin.name === 'vite:react-jsx')
135
+
136
+ if (hasConflict)
137
+ return config.logger.warn(
96
138
  `[@vitejs/plugin-react] You should stop using "${plugin.name}" ` +
97
139
  `since this plugin conflicts with it.`
98
140
  )
99
- )
141
+
142
+ if (plugin.api?.reactBabel) {
143
+ plugin.api.reactBabel(babelOptions, config)
144
+ }
145
+ })
100
146
  },
101
147
  async transform(code, id, options) {
102
148
  const ssr = typeof options === 'boolean' ? options : options?.ssr === true
@@ -113,12 +159,12 @@ export default function viteReact(opts: Options = {}): PluginOption[] {
113
159
  const isProjectFile =
114
160
  !isNodeModules && (id[0] === '\0' || id.startsWith(projectRoot + '/'))
115
161
 
116
- const plugins = isProjectFile ? [...userPlugins] : []
162
+ const plugins = isProjectFile ? [...babelOptions.plugins] : []
117
163
 
118
164
  let useFastRefresh = false
119
165
  if (!skipFastRefresh && !ssr && !isNodeModules) {
120
166
  // Modules with .js or .ts extension must import React.
121
- const isReactModule = isJSX || code.includes('react')
167
+ const isReactModule = isJSX || importReactRE.test(code)
122
168
  if (isReactModule && filter(id)) {
123
169
  useFastRefresh = true
124
170
  plugins.push([
@@ -179,15 +225,15 @@ export default function viteReact(opts: Options = {}): PluginOption[] {
179
225
  // module, including node_modules and linked packages.
180
226
  const shouldSkip =
181
227
  !plugins.length &&
182
- !opts.babel?.configFile &&
183
- !(isProjectFile && opts.babel?.babelrc)
228
+ !babelOptions.configFile &&
229
+ !(isProjectFile && babelOptions.babelrc)
184
230
 
185
231
  if (shouldSkip) {
186
232
  return // Avoid parsing if no plugins exist.
187
233
  }
188
234
 
189
- const parserPlugins: typeof userParserPlugins = [
190
- ...userParserPlugins,
235
+ const parserPlugins: typeof babelOptions.parserOpts.plugins = [
236
+ ...babelOptions.parserOpts.plugins,
191
237
  'importMeta',
192
238
  // This plugin is applied before esbuild transforms the code,
193
239
  // so we need to enable some stage 3 syntax that is supported in
@@ -206,35 +252,32 @@ export default function viteReact(opts: Options = {}): PluginOption[] {
206
252
  parserPlugins.push('typescript')
207
253
  }
208
254
 
209
- const isReasonReact = extension.endsWith('.bs.js')
255
+ const transformAsync = ast
256
+ ? babel.transformFromAstAsync.bind(babel, ast, code)
257
+ : babel.transformAsync.bind(babel, code)
210
258
 
211
- const babelOpts: TransformOptions = {
212
- babelrc: false,
213
- configFile: false,
214
- ...opts.babel,
259
+ const isReasonReact = extension.endsWith('.bs.js')
260
+ const result = await transformAsync({
261
+ ...babelOptions,
215
262
  ast: !isReasonReact,
216
263
  root: projectRoot,
217
264
  filename: id,
218
265
  sourceFileName: filepath,
219
266
  parserOpts: {
220
- ...opts.babel?.parserOpts,
267
+ ...babelOptions.parserOpts,
221
268
  sourceType: 'module',
222
269
  allowAwaitOutsideFunction: true,
223
270
  plugins: parserPlugins
224
271
  },
225
272
  generatorOpts: {
226
- ...opts.babel?.generatorOpts,
273
+ ...babelOptions.generatorOpts,
227
274
  decoratorsBeforeExport: true
228
275
  },
229
276
  plugins,
230
277
  sourceMaps: true,
231
278
  // Vite handles sourcemap flattening
232
279
  inputSourceMap: false as any
233
- }
234
-
235
- const result = ast
236
- ? await babel.transformFromAstAsync(ast, code, babelOpts)
237
- : await babel.transformAsync(code, babelOpts)
280
+ })
238
281
 
239
282
  if (result) {
240
283
  let code = result.code!
@@ -1,3 +1,4 @@
1
+ import type * as babelCore from '@babel/core'
1
2
  import type { types as t, Visitor } from '@babel/core'
2
3
 
3
4
  /**
@@ -9,9 +10,7 @@ import type { types as t, Visitor } from '@babel/core'
9
10
  *
10
11
  * var _jsx = require("react/jsx-runtime").jsx
11
12
  */
12
- export function babelImportToRequire({
13
- types: t
14
- }: typeof import('@babel/core')): {
13
+ export function babelImportToRequire({ types: t }: typeof babelCore): {
15
14
  visitor: Visitor
16
15
  } {
17
16
  return {
@@ -108,4 +108,10 @@ describe('babel-restore-jsx', () => {
108
108
  )
109
109
  ).toMatchInlineSnapshot(`"<h1>{foo ? <p /> : null}</h1>;"`)
110
110
  })
111
+
112
+ it('should handle lowercase component names', () => {
113
+ expect(jsx('React.createElement(aaa)')).toMatchInlineSnapshot(
114
+ `"React.createElement(aaa);"`
115
+ )
116
+ })
111
117
  })
@@ -2,7 +2,7 @@
2
2
  * https://github.com/flying-sheep/babel-plugin-transform-react-createelement-to-jsx
3
3
  * @license GNU General Public License v3.0
4
4
  */
5
- import * as babel from '@babel/core'
5
+ import type * as babel from '@babel/core'
6
6
 
7
7
  /**
8
8
  * Visitor factory for babel, converting React.createElement(...) to <jsx ...>...</jsx>
@@ -76,7 +76,7 @@ export default function ({ types: t }: typeof babel): babel.PluginObj {
76
76
  return null
77
77
  }
78
78
 
79
- const name = getJSXIdentifier(node)
79
+ const name = getJSXIdentifier(node, true)
80
80
  if (name != null) {
81
81
  return name
82
82
  }
@@ -152,9 +152,9 @@ export default function ({ types: t }: typeof babel): babel.PluginObj {
152
152
  return children
153
153
  }
154
154
 
155
- function getJSXIdentifier(node: any) {
155
+ function getJSXIdentifier(node: any, tag = false) {
156
156
  //TODO: JSXNamespacedName
157
- if (t.isIdentifier(node)) {
157
+ if (t.isIdentifier(node) && (!tag || node.name.match(/^[A-Z]/))) {
158
158
  return t.jsxIdentifier(node.name)
159
159
  }
160
160
  if (t.isStringLiteral(node)) {
@@ -1,3 +1,4 @@
1
+ import type * as babelCore from '@babel/core'
1
2
  import type { PluginItem, types as t } from '@babel/core'
2
3
 
3
4
  type RestoredJSX = [result: t.File | null | undefined, isCommonJS: boolean]
@@ -8,7 +9,7 @@ const jsxNotFound: RestoredJSX = [null, false]
8
9
 
9
10
  /** Restore JSX from `React.createElement` calls */
10
11
  export async function restoreJSX(
11
- babel: typeof import('@babel/core'),
12
+ babel: typeof babelCore,
12
13
  code: string,
13
14
  filename: string
14
15
  ): Promise<RestoredJSX> {