babel.config.js
Rama main · réplica de lectura del repositorio original.
require('dotenv').config();
/**
* Expo SDK 56 replaces global fetch with expo/fetch unless this is set at transform time.
* dotenv loads `.env` above; babel needs this here so release bundles inline the value.
*/
process.env.EXPO_PUBLIC_USE_RN_FETCH = process.env.EXPO_PUBLIC_USE_RN_FETCH ?? '1';
const {expoInlineEnvVars} = require('babel-preset-expo/build/plugins/inline-env-vars');
/**
* Custom plugin that prints a file name when it's being processed by babel.
* Disabled by default. To enable, set DEBUG_BABEL_TRACE=true in the environment.
*/
function traceTransformer() {
return {
visitor: {
Program(path, state) {
console.log('🔧 Transforming file:', state.filename);
},
},
};
}
const isTestEnv = process.env.BABEL_ENV === 'test' || process.env.NODE_ENV === 'test';
// One config for both remaining callers: `@callstack/repack` (native builds, app source via its
// loaders, allowlisted node_modules via babel-swc-loader) and `babel-jest` (tests, coverage).
const config = {
presets: [require('@react-native/babel-preset')],
plugins: [
// This is needed due to a react-native bug: https://github.com/facebook/react-native/issues/29084#issuecomment-1030732709
// It is included in @react-native/babel-preset but needs to be before plugin-proposal-class-properties or FlatList will break
'@babel/plugin-transform-flow-strip-types',
// Inline EXPO_PUBLIC_* env vars into release bundles (e.g. EXPO_PUBLIC_USE_RN_FETCH to keep RN fetch).
expoInlineEnvVars,
['@babel/plugin-proposal-class-properties', {loose: true}],
['@babel/plugin-proposal-private-methods', {loose: true}],
['@babel/plugin-proposal-private-property-in-object', {loose: true}],
/* Fullstory */
'@fullstory/react-native',
[
'@fullstory/babel-plugin-annotate-react',
{
native: true,
},
],
// Import alias for native devices
[
'module-resolver',
{
extensions: [
'.native.js',
'.native.jsx',
'.native.ts',
'.native.tsx',
'.js',
'.jsx',
'.ts',
'.tsx',
'.ios.js',
'.ios.jsx',
'.ios.ts',
'.ios.tsx',
'.android.js',
'.android.jsx',
'.android.ts',
'.android.tx',
],
alias: {
'@assets': './assets',
'@components': './src/components',
'@hooks': './src/hooks',
'@libs': './src/libs',
'@navigation': './src/libs/Navigation',
'@pages': './src/pages',
'@prompts': './prompts',
'@styles': './src/styles',
// This path is provide alias for files like `ONYXKEYS` and `CONST`.
'@src': './src',
'@userActions': './src/libs/actions',
'@github': './.github',
'@selectors': './src/selectors',
},
},
],
'@babel/plugin-transform-export-namespace-from',
// The worklets babel plugin needs to be last, as stated here: https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/getting-started/
// Skipped under Jest (isTestEnv): reanimated and worklets are fully mocked in jest/setup.ts, so the
// plugin's worklet transform is wasted work - and its output is not cached, so a heavy nested
// Babel transform re-runs on every test run.
...(isTestEnv
? []
: [
[
'react-native-worklets/plugin',
{
bundleMode: true,
// In Bundle Mode, an import that a worklet captures in its closure becomes a "remote function"
// and throws when called synchronously on a Worklet Runtime. The `react-native-live-markdown`
// ExpensiMark parser runs on the LiveMarkdownRuntime and directly imports `ExpensiMark`,
// `unescapeText` (from `expensify-common/utils`) and `decode` (from `html-entities`) into its
// worklets, so those imports must be forwarded to avoid the "Tried to synchronously call a
// Remote Function" crash. Their transitive dependencies (expensify-common's `Str`, `punycode`,
// `awesome-phonenumber`, html-entities' internals, ...) do NOT need forwarding: the bundler puts
// the whole `require()` graph onto the Worklet Runtime, so they resolve locally there.
// `relativePaths` only needs to cover live-markdown's own `./rangeUtils` (expensify-common and
// html-entities resolve to CommonJS, where `relativePaths` forwarding never fires).
// See https://docs.swmansion.com/react-native-worklets/docs/bundleMode/importForwarding
importForwarding: {
moduleNames: ['expensify-common', 'expensify-common/utils', 'html-entities'],
relativePaths: ['@expensify/react-native-live-markdown'],
},
},
],
]),
],
env: {
production: {
plugins: [['transform-remove-console', {exclude: ['error', 'warn']}]],
},
test: {
plugins: ['@babel/plugin-transform-dynamic-import'],
},
},
};
if (process.env.DEBUG_BABEL_TRACE) {
config.plugins.push(traceTransformer);
}
/*
* We use <React.Profiler> and react-native-performance to capture/monitor stats
* By default <React.Profiler> is disabled in production as it adds small overhead
* When CAPTURE_METRICS is set we're explicitly saying that we want to capture metrics
* To enable the <Profiler> for release builds we add these aliases */
if (process.env.CAPTURE_METRICS === 'true') {
const path = require('path');
const profilingRenderer = path.resolve(__dirname, './node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-profiling');
config.plugins.push([
'module-resolver',
{
root: ['./'],
alias: {
'ReactNativeRenderer-prod': profilingRenderer,
'scheduler/tracing': 'scheduler/tracing-profiling',
},
},
'extra-alias',
]);
}
// Re.Pack runs babel only on allowlisted node_modules (babel-swc-loader): app-source aliases don't
// apply there, and worklets are transformed by worklets-loader instead, so drop the alias plugin
// and the worklet options.
const repack = {
...config,
plugins: config.plugins
.filter((plugin) => !(Array.isArray(plugin) && plugin[0] === 'module-resolver' && plugin[2] !== 'extra-alias'))
.map((plugin) => (Array.isArray(plugin) && plugin[0] === 'react-native-worklets/plugin' ? 'react-native-worklets/plugin' : plugin)),
};
module.exports = (api) => {
if (process.env.DEBUG_BABEL_CONFIG === 'true') {
console.debug('babel.config.js');
console.debug(' - api.version:', api.version);
console.debug(' - api.env:', api.env());
console.debug(' - process.env.NODE_ENV:', process.env.NODE_ENV);
console.debug(' - process.env.BABEL_ENV:', process.env.BABEL_ENV);
}
// For native builds the caller is "@callstack/repack", for tests "babel-jest".
// The web build and Storybook (Rsbuild) don't call into this file at all, and anything else
// (an editor plugin, a stray script) still gets no config rather than an unintended one.
const runningIn = api.caller((args = {}) => args.name);
if (process.env.DEBUG_BABEL_CONFIG === 'true') {
console.debug(' - running in: ', runningIn);
}
if (runningIn === '@callstack/repack') {
return repack;
}
return runningIn === 'babel-jest' ? config : {};
};