You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

185 lines
7.1 KiB

const lazyImportsBlacklist = require('./lazy-imports-blacklist');
let hasWarnedJsxRename = false;
module.exports = function (api, options = {}) {
const { web = {}, native = {} } = options;
const bundler = api.caller(getBundler);
const isWebpack = bundler === 'webpack';
let platform = api.caller(getPlatform);
// If the `platform` prop is not defined then this must be a custom config that isn't
// defining a platform in the babel-loader. Currently this may happen with Next.js + Expo web.
if (!platform && isWebpack) {
platform = 'web';
}
const platformOptions =
platform === 'web'
? {
// Only disable import/export transform when Webpack is used because
// Metro does not support tree-shaking.
disableImportExportTransform: !!isWebpack,
...web,
}
: { disableImportExportTransform: false, ...native };
// Note that if `options.lazyImports` is not set (i.e., `null` or `undefined`),
// `metro-react-native-babel-preset` will handle it.
const lazyImportsOption = options && options.lazyImports;
const extraPlugins = [];
if ('useTransformReactJsxExperimental' in platformOptions && !hasWarnedJsxRename) {
// https://github.com/expo/expo/pull/13945#pullrequestreview-724327024
hasWarnedJsxRename = true;
console.warn(
'Warning: useTransformReactJsxExperimental has been renamed to useTransformReactJSXExperimental (capitalized JSX) in react-native@0.64.0'
);
}
// Set true to disable `@babel/plugin-transform-react-jsx`
// we override this logic outside of the metro preset so we can add support for
// React 17 automatic JSX transformations.
// If the logic for `useTransformReactJSXExperimental` ever changes in `metro-react-native-babel-preset`
// then this block should be updated to reflect those changes.
if (!platformOptions.useTransformReactJSXExperimental) {
extraPlugins.push([
require('@babel/plugin-transform-react-jsx'),
{
// Defaults to `automatic`, pass in `classic` to disable auto JSX transformations.
runtime: (options && options.jsxRuntime) || 'automatic',
...(options &&
options.jsxRuntime !== 'classic' && {
importSource: (options && options.jsxImportSource) || 'react',
}),
},
]);
// Purposefully not adding the deprecated packages:
// `@babel/plugin-transform-react-jsx-self` and `@babel/plugin-transform-react-jsx-source`
// back to the preset.
}
return {
presets: [
[
// We use `require` here instead of directly using the package name because we want to
// specifically use the `metro-react-native-babel-preset` installed by this package (ex:
// `babel-preset-expo/node_modules/`). This way the preset will not change unintentionally.
// Reference: https://github.com/expo/expo/pull/4685#discussion_r307143920
require('metro-react-native-babel-preset'),
{
// Defaults to undefined, set to something truthy to disable `@babel/plugin-transform-react-jsx-self` and `@babel/plugin-transform-react-jsx-source`.
withDevTools: platformOptions.withDevTools,
// Defaults to undefined, set to `true` to disable `@babel/plugin-transform-flow-strip-types`
disableFlowStripTypesTransform: platformOptions.disableFlowStripTypesTransform,
// Defaults to undefined, set to `false` to disable `@babel/plugin-transform-runtime`
enableBabelRuntime: platformOptions.enableBabelRuntime,
// Defaults to `'default'`, can also use `'hermes-canary'`
unstable_transformProfile: platformOptions.unstable_transformProfile,
// Set true to disable `@babel/plugin-transform-react-jsx` and
// the deprecated packages `@babel/plugin-transform-react-jsx-self`, and `@babel/plugin-transform-react-jsx-source`.
//
// Otherwise, you'll sometime get errors like the following (starting in Expo SDK 43, React Native 64, React 17):
//
// TransformError App.js: /path/to/App.js: Duplicate __self prop found. You are most likely using the deprecated transform-react-jsx-self Babel plugin.
// Both __source and __self are automatically set when using the automatic jsxRuntime. Please remove transform-react-jsx-source and transform-react-jsx-self from your Babel config.
useTransformReactJSXExperimental: true,
disableImportExportTransform: platformOptions.disableImportExportTransform,
lazyImportExportTransform:
lazyImportsOption === true
? (importModuleSpecifier) => {
// Do not lazy-initialize packages that are local imports (similar to `lazy: true`
// behavior) or are in the blacklist.
return !(
importModuleSpecifier.includes('./') ||
lazyImportsBlacklist.has(importModuleSpecifier)
);
}
: // Pass the option directly to `metro-react-native-babel-preset`, which in turn
// passes it to `babel-plugin-transform-modules-commonjs`
lazyImportsOption,
},
],
],
plugins: [
getObjectRestSpreadPlugin(),
...extraPlugins,
getAliasPlugin(),
[require.resolve('@babel/plugin-proposal-decorators'), { legacy: true }],
platform === 'web' && [require.resolve('babel-plugin-react-native-web')],
isWebpack && platform !== 'web' && [require.resolve('./plugins/disable-ambiguous-requires')],
].filter(Boolean),
};
};
function getAliasPlugin() {
const aliases = {};
if (hasModule('@expo/vector-icons')) {
aliases['react-native-vector-icons'] = '@expo/vector-icons';
}
if (Object.keys(aliases).length) {
return [
require.resolve('babel-plugin-module-resolver'),
{
alias: aliases,
},
];
}
return null;
}
/**
* metro-react-native-babel-preset configures this plugin with `{ loose: true }`, which breaks all
* getters and setters in spread objects. We need to add this plugin ourself without that option.
* @see https://github.com/expo/expo/pull/11960#issuecomment-887796455
*/
function getObjectRestSpreadPlugin() {
return [require.resolve('@babel/plugin-proposal-object-rest-spread'), { loose: false }];
}
function hasModule(name) {
try {
return !!require.resolve(name);
} catch (error) {
if (error.code === 'MODULE_NOT_FOUND' && error.message.includes(name)) {
return false;
}
throw error;
}
}
function getPlatform(caller) {
return caller && caller.platform;
}
/**
* Get the name of the `bundler`.
*
* @param {*} caller
*/
function getBundler(caller) {
if (!caller) return null;
const { bundler, name } = caller;
if (!bundler) {
if (name === 'metro') {
// This is a hack to determine if metro is being used.
return 'metro';
} else if (name === 'next-babel-turbo-loader') {
// NextJS 11
return 'webpack';
} else if (name === 'babel-loader') {
// expo/webpack-config, gatsby, storybook, and next.js <10
return 'webpack';
}
}
// Perhaps we should add a check to log once when an unexpected bundler is being used.
return bundler || null;
}