UNPKG

@react-native/babel-preset

Version:

Babel preset for React Native applications

271 lines (241 loc) • 8.15 kB
/** * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * @format * @noflow */ 'use strict'; const passthroughSyntaxPlugins = require('../passthrough-syntax-plugins'); const lazyImports = require('./lazy-imports'); const EXCLUDED_FIRST_PARTY_PATHS = [ /[/\\]node_modules[/\\]/, /[/\\]packages[/\\]react-native[/\\]/, /[/\\]packages[/\\]jest-preset[/\\]/, /[/\\]packages[/\\]virtualized-lists[/\\]/, /[/\\]private[/\\]react-native-fantom[/\\]/, ]; function isTypeScriptSource(fileName) { return !!fileName && fileName.endsWith('.ts'); } function isTSXSource(fileName) { return !!fileName && fileName.endsWith('.tsx'); } function isFirstParty(fileName) { return ( !!fileName && !EXCLUDED_FIRST_PARTY_PATHS.some(regex => regex.test(fileName)) ); } // Called by Babel whenever caller information changes between transform calls // for a given config. If the return value changes, Babel re-evaluates // getPreset, which is otherwise cached based on `options`. This must be pure, // and should be cheap. function getTransformProfile(caller) { return caller?.unstable_transformProfile ?? 'default'; } // use `this.foo = bar` instead of `this.defineProperty('foo', ...)` const loose = true; const getPreset = (src, options, babel) => { const transformProfile = options?.unstable_transformProfile ?? babel?.caller(getTransformProfile); const dev = options?.dev ?? babel.env('development'); // Hermes V1 (aka Static Hermes) uses more optimised profiles. // There is currently no difference between stable and canary, but canary // may in future be used to test features in pre-prod Hermes versions. const isHermesV1 = transformProfile === 'hermes-stable' || transformProfile === 'hermes-canary'; // We enable regenerator in dev builds for the time being because // Hermes V1 doesn't yet fully support debugging native generators. // (e.g. - it's not possible to inspect local variables when paused in a // generator). // // Use native generators in release mode because it has already yielded perf // wins. The next release of Hermes will close this gap, so this won't // be permanent. const enableRegenerator = isHermesV1 && dev; // Preserve class syntax and related if we're using Hermes V1. const preserveClasses = isHermesV1; const isNull = src == null; const hasClass = isNull || src.indexOf('class') !== -1; const extraPlugins = []; const firstPartyPlugins = []; if (!options.useTransformReactJSXExperimental) { extraPlugins.push([ require('@babel/plugin-transform-react-jsx'), {runtime: 'automatic'}, ]); } if ( !options.disableStaticViewConfigsCodegen && (src === null || /\bcodegenNativeComponent</.test(src)) ) { extraPlugins.push([require('@react-native/babel-plugin-codegen')]); } if (!options || !options.disableImportExportTransform) { extraPlugins.push( [require('@babel/plugin-proposal-export-default-from')], [ require('@babel/plugin-transform-modules-commonjs'), { strict: false, strictMode: false, // prevent "use strict" injections lazy: options && options.lazyImportExportTransform != null ? options.lazyImportExportTransform : importSpecifier => lazyImports.has(importSpecifier), allowTopLevelThis: true, // dont rewrite global `this` -> `undefined` }, ], ); } if (hasClass && !preserveClasses) { extraPlugins.push([require('@babel/plugin-transform-classes')]); } extraPlugins.push([ require('@babel/plugin-transform-named-capturing-groups-regex'), ]); // Needed for regenerator if (enableRegenerator) { extraPlugins.push([ require('@babel/plugin-transform-optional-catch-binding'), ]); } extraPlugins.push([ require('@babel/plugin-transform-destructuring'), {useBuiltIns: true}, ]); if (isNull || src.indexOf('async') !== -1) { extraPlugins.push([ require('@babel/plugin-transform-async-generator-functions'), ]); extraPlugins.push([require('@babel/plugin-transform-async-to-generator')]); } if ( isNull || src.indexOf('React.createClass') !== -1 || src.indexOf('createReactClass') !== -1 ) { extraPlugins.push([require('@babel/plugin-transform-react-display-name')]); } // This is also needed for regenerator if (enableRegenerator && (isNull || src.indexOf('?.') !== -1)) { extraPlugins.push([ require('@babel/plugin-transform-optional-chaining'), {loose: true}, ]); } // This is also needed for regenerator if (enableRegenerator && (isNull || src.indexOf('??') !== -1)) { extraPlugins.push([ require('@babel/plugin-transform-nullish-coalescing-operator'), {loose: true}, ]); } if (options && dev && !options.disableDeepImportWarnings) { firstPartyPlugins.push([require('../plugin-warn-on-deep-imports.js')]); } if (options && dev && !options.useTransformReactJSXExperimental) { extraPlugins.push([require('@babel/plugin-transform-react-jsx-source')]); extraPlugins.push([require('@babel/plugin-transform-react-jsx-self')]); } if ( enableRegenerator && (isNull || (src.indexOf('for') !== -1 && src.indexOf('of') !== -1)) ) { // Needed for regenerator extraPlugins.push([ require('@babel/plugin-transform-for-of'), {loose: true}, ]); } if (!options || options.enableBabelRuntime !== false) { // Allows configuring a specific runtime version to optimize output const isVersion = typeof options?.enableBabelRuntime === 'string'; extraPlugins.push([ require('@babel/plugin-transform-runtime'), { helpers: true, regenerator: enableRegenerator, ...(isVersion && {version: options.enableBabelRuntime}), }, ]); } else if (enableRegenerator) { extraPlugins.push([require('@babel/plugin-transform-regenerator')]); } return { comments: false, compact: options.compact !== false, overrides: [ // the flow strip types plugin must go BEFORE class properties! // there'll be a test case that fails if you don't. { plugins: [require('@babel/plugin-transform-flow-strip-types')], }, { plugins: [ [ require('babel-plugin-syntax-hermes-parser'), { parseLangTypes: 'flow', reactRuntimeTarget: '19', ...options.hermesParserOptions, }, ], [require('babel-plugin-transform-flow-enums')], [require('@babel/plugin-transform-block-scoping')], ...(preserveClasses ? [] : [[require('@babel/plugin-transform-class-properties'), {loose}]]), [require('@babel/plugin-transform-private-methods'), {loose}], [ require('@babel/plugin-transform-private-property-in-object'), {loose}, ], [require('@babel/plugin-syntax-dynamic-import')], [require('@babel/plugin-syntax-export-default-from')], ...passthroughSyntaxPlugins, [require('@babel/plugin-transform-unicode-regex')], ], }, { test: isTypeScriptSource, plugins: [ [ require('@babel/plugin-transform-typescript'), { isTSX: false, allowNamespaces: true, }, ], ], }, { test: isTSXSource, plugins: [ [ require('@babel/plugin-transform-typescript'), { isTSX: true, allowNamespaces: true, }, ], ], }, { test: isFirstParty, plugins: firstPartyPlugins, }, { plugins: extraPlugins, }, ], }; }; module.exports = (options, babel) => { return getPreset(null, options, babel); }; module.exports.getPreset = getPreset;