Skip to content

Commit

Permalink
feat(plugin-react): check for api.reactBabel on other plugins (#5454)
Browse files Browse the repository at this point in the history
  • Loading branch information
aleclarson committed Jan 4, 2022
1 parent bd9e97b commit 2ab41b3
Showing 1 changed file with 71 additions and 28 deletions.
99 changes: 71 additions & 28 deletions packages/plugin-react/src/index.ts
Expand Up @@ -36,13 +36,46 @@ export interface Options {
/**
* Babel configuration applied in both dev and prod.
*/
babel?: TransformOptions
babel?: BabelOptions
/**
* @deprecated Use `babel.parserOpts.plugins` instead
*/
parserPlugins?: ParserOptions['plugins']
}

export type BabelOptions = Omit<
TransformOptions,
| 'ast'
| 'filename'
| 'root'
| 'sourceFileName'
| 'sourceMaps'
| 'inputSourceMap'
>

/**
* The object type used by the `options` passed to plugins with
* an `api.reactBabel` method.
*/
export interface ReactBabelOptions extends BabelOptions {
plugins: Extract<BabelOptions['plugins'], any[]>
presets: Extract<BabelOptions['presets'], any[]>
parserOpts: ParserOptions & {
plugins: Extract<ParserOptions['plugins'], any[]>
}
}

declare module 'vite' {
export interface Plugin {
api?: {
/**
* Manipulate the Babel options of `@vitejs/plugin-react`
*/
reactBabel?: (options: ReactBabelOptions, config: ResolvedConfig) => void
}
}
}

export default function viteReact(opts: Options = {}): PluginOption[] {
// Provide default values for Rollup compat.
let base = '/'
Expand All @@ -54,11 +87,18 @@ export default function viteReact(opts: Options = {}): PluginOption[] {

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

const userPlugins = opts.babel?.plugins || []
const userParserPlugins =
opts.parserPlugins || opts.babel?.parserOpts?.plugins || []
const babelOptions = {
babelrc: false,
configFile: false,
...opts.babel
} as ReactBabelOptions

// Support pattens like:
babelOptions.plugins ||= []
babelOptions.presets ||= []
babelOptions.parserOpts ||= {} as any
babelOptions.parserOpts.plugins ||= opts.parserPlugins || []

// Support patterns like:
// - import * as React from 'react';
// - import React from 'react';
// - import React, {useEffect} from 'react';
Expand Down Expand Up @@ -88,15 +128,21 @@ export default function viteReact(opts: Options = {}): PluginOption[] {
)
}

config.plugins.forEach(
(plugin) =>
(plugin.name === 'react-refresh' ||
(plugin !== viteReactJsx && plugin.name === 'vite:react-jsx')) &&
config.logger.warn(
config.plugins.forEach((plugin) => {
const hasConflict =
plugin.name === 'react-refresh' ||
(plugin !== viteReactJsx && plugin.name === 'vite:react-jsx')

if (hasConflict)
return config.logger.warn(
`[@vitejs/plugin-react] You should stop using "${plugin.name}" ` +
`since this plugin conflicts with it.`
)
)

if (plugin.api?.reactBabel) {
plugin.api.reactBabel(babelOptions, config)
}
})
},
async transform(code, id, options) {
const ssr = typeof options === 'boolean' ? options : options?.ssr === true
Expand All @@ -113,7 +159,7 @@ export default function viteReact(opts: Options = {}): PluginOption[] {
const isProjectFile =
!isNodeModules && (id[0] === '\0' || id.startsWith(projectRoot + '/'))

const plugins = isProjectFile ? [...userPlugins] : []
const plugins = isProjectFile ? [...babelOptions.plugins] : []

let useFastRefresh = false
if (!skipFastRefresh && !ssr && !isNodeModules) {
Expand Down Expand Up @@ -179,15 +225,15 @@ export default function viteReact(opts: Options = {}): PluginOption[] {
// module, including node_modules and linked packages.
const shouldSkip =
!plugins.length &&
!opts.babel?.configFile &&
!(isProjectFile && opts.babel?.babelrc)
!babelOptions.configFile &&
!(isProjectFile && babelOptions.babelrc)

if (shouldSkip) {
return // Avoid parsing if no plugins exist.
}

const parserPlugins: typeof userParserPlugins = [
...userParserPlugins,
const parserPlugins: typeof babelOptions.parserOpts.plugins = [
...babelOptions.parserOpts.plugins,
'importMeta',
// This plugin is applied before esbuild transforms the code,
// so we need to enable some stage 3 syntax that is supported in
Expand All @@ -206,35 +252,32 @@ export default function viteReact(opts: Options = {}): PluginOption[] {
parserPlugins.push('typescript')
}

const isReasonReact = extension.endsWith('.bs.js')
const transformAsync = ast
? babel.transformFromAstAsync.bind(babel, ast, code)
: babel.transformAsync.bind(babel, code)

const babelOpts: TransformOptions = {
babelrc: false,
configFile: false,
...opts.babel,
const isReasonReact = extension.endsWith('.bs.js')
const result = await transformAsync({
...babelOptions,
ast: !isReasonReact,
root: projectRoot,
filename: id,
sourceFileName: filepath,
parserOpts: {
...opts.babel?.parserOpts,
...babelOptions.parserOpts,
sourceType: 'module',
allowAwaitOutsideFunction: true,
plugins: parserPlugins
},
generatorOpts: {
...opts.babel?.generatorOpts,
...babelOptions.generatorOpts,
decoratorsBeforeExport: true
},
plugins,
sourceMaps: true,
// Vite handles sourcemap flattening
inputSourceMap: false as any
}

const result = ast
? await babel.transformFromAstAsync(ast, code, babelOpts)
: await babel.transformAsync(code, babelOpts)
})

if (result) {
let code = result.code!
Expand Down

0 comments on commit 2ab41b3

Please sign in to comment.