@@ -36,13 +36,46 @@ export interface Options {
3636 /**
3737 * Babel configuration applied in both dev and prod.
3838 */
39- babel ?: TransformOptions
39+ babel ?: BabelOptions
4040 /**
4141 * @deprecated Use `babel.parserOpts.plugins` instead
4242 */
4343 parserPlugins ?: ParserOptions [ 'plugins' ]
4444}
4545
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+
4679export default function viteReact ( opts : Options = { } ) : PluginOption [ ] {
4780 // Provide default values for Rollup compat.
4881 let base = '/'
@@ -54,11 +87,18 @@ export default function viteReact(opts: Options = {}): PluginOption[] {
5487
5588 const useAutomaticRuntime = opts . jsxRuntime !== 'classic'
5689
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
6095
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:
62102 // - import * as React from 'react';
63103 // - import React from 'react';
64104 // - import React, {useEffect} from 'react';
@@ -88,15 +128,21 @@ export default function viteReact(opts: Options = {}): PluginOption[] {
88128 )
89129 }
90130
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 (
96138 `[@vitejs/plugin-react] You should stop using "${ plugin . name } " ` +
97139 `since this plugin conflicts with it.`
98140 )
99- )
141+
142+ if ( plugin . api ?. reactBabel ) {
143+ plugin . api . reactBabel ( babelOptions , config )
144+ }
145+ } )
100146 } ,
101147 async transform ( code , id , options ) {
102148 const ssr = typeof options === 'boolean' ? options : options ?. ssr === true
@@ -113,7 +159,7 @@ export default function viteReact(opts: Options = {}): PluginOption[] {
113159 const isProjectFile =
114160 ! isNodeModules && ( id [ 0 ] === '\0' || id . startsWith ( projectRoot + '/' ) )
115161
116- const plugins = isProjectFile ? [ ...userPlugins ] : [ ]
162+ const plugins = isProjectFile ? [ ...babelOptions . plugins ] : [ ]
117163
118164 let useFastRefresh = false
119165 if ( ! skipFastRefresh && ! ssr && ! isNodeModules ) {
@@ -179,15 +225,15 @@ export default function viteReact(opts: Options = {}): PluginOption[] {
179225 // module, including node_modules and linked packages.
180226 const shouldSkip =
181227 ! plugins . length &&
182- ! opts . babel ? .configFile &&
183- ! ( isProjectFile && opts . babel ? .babelrc )
228+ ! babelOptions . configFile &&
229+ ! ( isProjectFile && babelOptions . babelrc )
184230
185231 if ( shouldSkip ) {
186232 return // Avoid parsing if no plugins exist.
187233 }
188234
189- const parserPlugins : typeof userParserPlugins = [
190- ...userParserPlugins ,
235+ const parserPlugins : typeof babelOptions . parserOpts . plugins = [
236+ ...babelOptions . parserOpts . plugins ,
191237 'importMeta' ,
192238 // This plugin is applied before esbuild transforms the code,
193239 // so we need to enable some stage 3 syntax that is supported in
@@ -206,35 +252,32 @@ export default function viteReact(opts: Options = {}): PluginOption[] {
206252 parserPlugins . push ( 'typescript' )
207253 }
208254
209- const isReasonReact = extension . endsWith ( '.bs.js' )
255+ const transformAsync = ast
256+ ? babel . transformFromAstAsync . bind ( babel , ast , code )
257+ : babel . transformAsync . bind ( babel , code )
210258
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 ,
215262 ast : ! isReasonReact ,
216263 root : projectRoot ,
217264 filename : id ,
218265 sourceFileName : filepath ,
219266 parserOpts : {
220- ...opts . babel ? .parserOpts ,
267+ ...babelOptions . parserOpts ,
221268 sourceType : 'module' ,
222269 allowAwaitOutsideFunction : true ,
223270 plugins : parserPlugins
224271 } ,
225272 generatorOpts : {
226- ...opts . babel ? .generatorOpts ,
273+ ...babelOptions . generatorOpts ,
227274 decoratorsBeforeExport : true
228275 } ,
229276 plugins,
230277 sourceMaps : true ,
231278 // Vite handles sourcemap flattening
232279 inputSourceMap : false as any
233- }
234-
235- const result = ast
236- ? await babel . transformFromAstAsync ( ast , code , babelOpts )
237- : await babel . transformAsync ( code , babelOpts )
280+ } )
238281
239282 if ( result ) {
240283 let code = result . code !
0 commit comments