Repository navigation
Conversation
…nsformer ## Summary: Helpers that `@babel/plugin-transform-runtime` imports from `@babel/runtime` can fail to resolve, depending on where, or whether, a project installs it. Worse, `@react-native/babel-preset` doesn't know which version it'll get, so the plugin assumes the oldest and inlines newer helpers instead, eg `_interopRequireWildcard` into every module that uses `import * as` (#57123). Metro now offers its own `@babel/runtime`, which always exists, at a version it knows. This forwards it from `@react-native/metro-babel-transformer`, which React Native projects use and which currently drops it. With `transformer.enableBabelRuntime: true` (the default), the wiring is: 1. Metro's transform worker passes the Babel transformer `babelRuntimeModuleName: 'metro:babel-runtime'` and the installed version of Metro's `@babel/runtime`. (Landed in react/metro#1957, unreleased) 2. `@react-native/metro-babel-transformer` forwards both to the preset. (This PR) 3. `@react-native/babel-preset` configures `@babel/plugin-transform-runtime` with that `moduleName` and `version`. (Landed in #57974) 4. Helpers are emitted as `metro:babel-runtime/helpers/...` imports, which Metro's `metro:` resolver maps to its own `@babel/runtime`. (Landed in Metro 0.87.1) 5. Metro adds that `@babel/runtime` to the file map, wherever it's installed. (Landed in react/metro#2065, unreleased) How this PR forwards them depends on the project's Babel config: - With a project Babel config naming the preset, as in the template, they reach the preset as Babel caller data. - Without one, the version replaces `enableBabelRuntime: true` in the preset's options, because preset options take precedence over caller data. `babelRuntimeModuleName` already reaches the preset there. Until the next Metro release, Metro doesn't pass either value, so this is a no-op, but harmless. The Flow suppression for the new fields can go once React Native depends on that release. ## Changelog: [GENERAL] [ADDED] - `@react-native/metro-babel-transformer` passes Metro's own `@babel/runtime` and its version to `@react-native/babel-preset`, when Metro provides them ## Test Plan: New tests transform `import * as foo from './foo'` with and without a project `babel.config.js` naming the preset. When Metro passes `metro:babel-runtime` and a version, the helper is imported from `metro:babel-runtime/helpers/interopRequireWildcard`, and otherwise it's inlined as before. The import cases fail without this change. End to end, with Metro from react/metro#1957, the iOS release bundle of `private/helloworld` no longer inlines helpers: 199 copies of `_interopRequireWildcard` and 63 of `_callSuper` become three shared modules. Against current defaults: | | Current defaults | With this PR | Change | |---|---|---|---| | Minified JS | 906,504 B | 807,389 B | -10.9% | | Minified JS, gzip -9 | 217,794 B | 204,595 B | -6.1% | | Hermes bytecode | 1,261,863 B | 1,173,832 B | -7.0% | Sample output is in react/metro#1957. Not run on a device.
|
Is there ever a case where we want to make sure the same |
The runtime transform is just space-saving so it doesn't functionally matter if other tools use a different version (or disable the plugin entirely) - as long as they're not telling Babel that a newer version is installed. Here, because we only pass the runtime version from Metro, other tools will fall back to the default It's still the case though that Jest etc need Tbh, it should probably be disabled by default for Jest just for simplicity. More verbose generated code doesn't really matter in that context.
Not much, apart from maybe the above for Jest. The main reason before this stack was that users needed to make sure it was installed in all of their source roots, and that could go wrong. Meta is one consumer I know of that intentionally disables it though - iirc using a global with all of the helpers on it, instead of This stack is a possible step to making that whole thing go away. |
|
@javache has imported this pull request. If you are a Meta employee, you can view this in D126404231. |
This is the RN side of react/metro#1957
Summary:
Helpers that
@babel/plugin-transform-runtimeimports from@babel/runtimecan fail to resolve, depending on where, or whether, a project installs it. Worse,@react-native/babel-presetdoesn't know which version it'll get, so the plugin assumes the oldest and inlines newer helpers instead, eg_interopRequireWildcardinto every module that usesimport * as(#57123).Metro now offers its own
@babel/runtime, which always exists, at a version it knows. This forwards it from@react-native/metro-babel-transformer, which React Native projects use and which currently drops it. Withtransformer.enableBabelRuntime: true(the default), the wiring is:babelRuntimeModuleName: 'metro:babel-runtime'and the installed version of Metro's@babel/runtime. (Landed in Pass Metro's own @babel/runtime to Babel presets metro#1957, unreleased)@react-native/metro-babel-transformerforwards both to the preset. (This PR)@react-native/babel-presetconfigures@babel/plugin-transform-runtimewith thatmoduleNameandversion. (Landed in Allow configuring Babel runtime via caller in @react-native/babel-preset (#57974) #57974)metro:babel-runtime/helpers/...imports, which Metro'smetro:resolver maps to its own@babel/runtime. (Landed in Metro 0.87.1)@babel/runtimeto the file map, wherever it's installed. (Landed in Add Metro's own @babel/runtime to the file map as a dynamic root metro#2065, unreleased)How this PR forwards them depends on the project's Babel config:
enableBabelRuntime: truein the preset's options, because preset options take precedence over caller data.babelRuntimeModuleNamealready reaches the preset there.Until the next Metro release, Metro doesn't pass either value, so this is a no-op, but harmless. The Flow suppression for the new fields can go once React Native depends on that release.
Changelog:
[GENERAL] [ADDED] -
@react-native/metro-babel-transformerpasses Metro's own@babel/runtimeand its version to@react-native/babel-preset, when Metro provides themTest Plan:
New tests transform
import * as foo from './foo'with and without a projectbabel.config.jsnaming the preset. When Metro passesmetro:babel-runtimeand a version, the helper is imported frommetro:babel-runtime/helpers/interopRequireWildcard, and otherwise it's inlined as before. The import cases fail without this change.End to end, with Metro from react/metro#1957, the iOS release bundle of
private/helloworldno longer inlines helpers: 199 copies of_interopRequireWildcardand 63 of_callSuperbecome three shared modules. Against current defaults:Sample output is in react/metro#1957. Not run on a device.