Skip to content

Commit f4d6262

Browse files
cyco130aleclarson
andauthored
feat(plugin-react): allow options.babel to be a function (#6238)
Co-authored-by: Alec Larson <1925840+aleclarson@users.noreply.github.com>
1 parent 48f03e0 commit f4d6262

File tree

1 file changed

+59
-17
lines changed

1 file changed

+59
-17
lines changed

packages/plugin-react/src/index.ts

Lines changed: 59 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,9 @@ export interface Options {
4141
/**
4242
* Babel configuration applied in both dev and prod.
4343
*/
44-
babel?: BabelOptions
44+
babel?:
45+
| BabelOptions
46+
| ((id: string, options: { ssr?: boolean }) => BabelOptions)
4547
}
4648

4749
export type BabelOptions = Omit<
@@ -67,13 +69,21 @@ export interface ReactBabelOptions extends BabelOptions {
6769
}
6870
}
6971

72+
type ReactBabelHook = (
73+
babelConfig: ReactBabelOptions,
74+
context: ReactBabelHookContext,
75+
config: ResolvedConfig
76+
) => void
77+
78+
type ReactBabelHookContext = { ssr: boolean; id: string }
79+
7080
declare module 'vite' {
7181
export interface Plugin {
7282
api?: {
7383
/**
7484
* Manipulate the Babel options of `@vitejs/plugin-react`
7585
*/
76-
reactBabel?: (options: ReactBabelOptions, config: ResolvedConfig) => void
86+
reactBabel?: ReactBabelHook
7787
}
7888
}
7989
}
@@ -86,21 +96,14 @@ export default function viteReact(opts: Options = {}): PluginOption[] {
8696
let projectRoot = process.cwd()
8797
let skipFastRefresh = opts.fastRefresh === false
8898
let skipReactImport = false
99+
let runPluginOverrides = (
100+
options: ReactBabelOptions,
101+
context: ReactBabelHookContext
102+
) => false
103+
let staticBabelOptions: ReactBabelOptions | undefined
89104

90105
const useAutomaticRuntime = opts.jsxRuntime !== 'classic'
91106

92-
const babelOptions = {
93-
babelrc: false,
94-
configFile: false,
95-
...opts.babel
96-
} as ReactBabelOptions
97-
98-
babelOptions.plugins ||= []
99-
babelOptions.presets ||= []
100-
babelOptions.overrides ||= []
101-
babelOptions.parserOpts ||= {} as any
102-
babelOptions.parserOpts.plugins ||= []
103-
104107
// Support patterns like:
105108
// - import * as React from 'react';
106109
// - import React from 'react';
@@ -141,11 +144,22 @@ export default function viteReact(opts: Options = {}): PluginOption[] {
141144
`[@vitejs/plugin-react] You should stop using "${plugin.name}" ` +
142145
`since this plugin conflicts with it.`
143146
)
147+
})
148+
149+
runPluginOverrides = (babelOptions, context) => {
150+
const hooks = config.plugins
151+
.map((plugin) => plugin.api?.reactBabel)
152+
.filter(Boolean) as ReactBabelHook[]
144153

145-
if (plugin.api?.reactBabel) {
146-
plugin.api.reactBabel(babelOptions, config)
154+
if (hooks.length > 0) {
155+
return (runPluginOverrides = (babelOptions) => {
156+
hooks.forEach((hook) => hook(babelOptions, context, config))
157+
return true
158+
})(babelOptions)
147159
}
148-
})
160+
runPluginOverrides = () => false
161+
return false
162+
}
149163
},
150164
async transform(code, id, options) {
151165
const ssr = typeof options === 'boolean' ? options : options?.ssr === true
@@ -162,6 +176,18 @@ export default function viteReact(opts: Options = {}): PluginOption[] {
162176
const isProjectFile =
163177
!isNodeModules && (id[0] === '\0' || id.startsWith(projectRoot + '/'))
164178

179+
let babelOptions = staticBabelOptions
180+
if (typeof opts.babel === 'function') {
181+
const rawOptions = opts.babel(id, { ssr })
182+
babelOptions = createBabelOptions(rawOptions)
183+
runPluginOverrides(babelOptions, { ssr, id: id })
184+
} else if (!babelOptions) {
185+
babelOptions = createBabelOptions(opts.babel)
186+
if (!runPluginOverrides(babelOptions, { ssr, id: id })) {
187+
staticBabelOptions = babelOptions
188+
}
189+
}
190+
165191
const plugins = isProjectFile ? [...babelOptions.plugins] : []
166192

167193
let useFastRefresh = false
@@ -368,3 +394,19 @@ viteReact.preambleCode = preambleCode
368394
function loadPlugin(path: string): Promise<any> {
369395
return import(path).then((module) => module.default || module)
370396
}
397+
398+
function createBabelOptions(rawOptions?: BabelOptions) {
399+
const babelOptions = {
400+
babelrc: false,
401+
configFile: false,
402+
...rawOptions
403+
} as ReactBabelOptions
404+
405+
babelOptions.plugins ||= []
406+
babelOptions.presets ||= []
407+
babelOptions.overrides ||= []
408+
babelOptions.parserOpts ||= {} as any
409+
babelOptions.parserOpts.plugins ||= []
410+
411+
return babelOptions
412+
}

0 commit comments

Comments
 (0)