Skip to content

Commit 8932a9c

Browse files
janicduplessisfacebook-github-bot
authored andcommitted
Use babel runtime instead of relying on global babelHelpers and regenerator (#198)
Summary: **Summary** The RN transformer currently relies on the enviroment providing babelHelpers and regeneratorRuntime as globals by using 'babel-external-helpers'. This wasn't really a problem before since helpers were stable and we could maintain our copy easily but it seems like there are more now with babel 7 and it makes sense to include only those used by the app. This is exactly what babel/transform-runtime does. It will alias all helpers and calls to regeneratorRuntime to files in the babel/runtime package. This will solve issues like this facebook/react-native#20150 caused by missing babelHelpers. This solution also avoids bloating babelHelpers to fix OSS issues like the one linked before. **Test plan** - Updated tests so they all pass. - Tested that it actually works by applying the changes locally in an RN app. - Added a test for async functions, to make sure regenerator is aliased properly and doesn't depend on the global. - Made sure require-test.js still fails if the require implementation contains babel helpers (by adding an empty class in the file). Pull Request resolved: #198 Reviewed By: mjesun Differential Revision: D8833903 Pulled By: rafeca fbshipit-source-id: 7081f769f288ab358ba89ae8ee72a513bb12e225
1 parent 0c28693 commit 8932a9c

File tree

7 files changed

+116
-31
lines changed

7 files changed

+116
-31
lines changed

packages/metro-react-native-babel-preset/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
"@babel/plugin-transform-react-jsx": "^7.0.0",
4444
"@babel/plugin-transform-react-jsx-source": "^7.0.0",
4545
"@babel/plugin-transform-regenerator": "^7.0.0",
46+
"@babel/plugin-transform-runtime": "^7.0.0",
4647
"@babel/plugin-transform-shorthand-properties": "^7.0.0",
4748
"@babel/plugin-transform-spread": "^7.0.0",
4849
"@babel/plugin-transform-sticky-regex": "^7.0.0",

packages/metro-react-native-babel-preset/src/configs/main.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,14 @@ const reactDisplayName = [
8080
const reactJsxSource = [require('@babel/plugin-transform-react-jsx-source')];
8181
const symbolMember = [require('../transforms/transform-symbol-member')];
8282

83+
const babelRuntime = [
84+
require('@babel/plugin-transform-runtime'),
85+
{
86+
helpers: true,
87+
regenerator: true,
88+
},
89+
];
90+
8391
const getPreset = (src, options) => {
8492
const isNull = src == null;
8593
const hasClass = isNull || src.indexOf('class') !== -1;
@@ -135,6 +143,10 @@ const getPreset = (src, options) => {
135143
extraPlugins.push(reactJsxSource);
136144
}
137145

146+
if (!options || !options.disableBabelRuntime) {
147+
extraPlugins.push(babelRuntime);
148+
}
149+
138150
return {
139151
comments: false,
140152
compact: true,

packages/metro/src/JSTransformer/worker/__tests__/__snapshots__/worker-test.js.snap

Lines changed: 42 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,110 +11,110 @@ Array [
1111
0,
1212
],
1313
Array [
14-
4,
14+
6,
1515
0,
1616
5,
1717
0,
1818
],
1919
Array [
20-
6,
20+
8,
2121
0,
2222
2,
2323
0,
2424
"require",
2525
],
2626
Array [
27-
6,
27+
8,
2828
2,
2929
2,
3030
0,
3131
"require",
3232
],
3333
Array [
34-
6,
34+
8,
3535
13,
3636
2,
3737
7,
3838
],
3939
Array [
40-
6,
40+
8,
4141
39,
4242
2,
4343
0,
4444
],
4545
Array [
46-
8,
46+
10,
4747
0,
4848
3,
4949
0,
5050
"arbitrary",
5151
],
5252
Array [
53-
8,
53+
10,
5454
2,
5555
3,
5656
0,
5757
"arbitrary",
5858
],
5959
Array [
60-
8,
60+
10,
6161
11,
6262
3,
6363
9,
6464
],
6565
Array [
66-
8,
66+
10,
6767
12,
6868
3,
6969
10,
7070
"code",
7171
],
7272
Array [
73-
8,
73+
10,
7474
16,
7575
3,
7676
9,
7777
],
7878
Array [
79-
8,
79+
10,
8080
17,
8181
3,
8282
0,
8383
],
8484
Array [
85-
10,
85+
12,
8686
0,
8787
4,
8888
0,
8989
],
9090
Array [
91-
10,
91+
12,
9292
6,
9393
4,
9494
6,
9595
"b",
9696
],
9797
Array [
98-
10,
98+
12,
9999
7,
100100
4,
101101
7,
102102
],
103103
Array [
104-
10,
104+
12,
105105
10,
106106
4,
107107
10,
108108
"require",
109109
],
110110
Array [
111-
10,
111+
12,
112112
21,
113113
4,
114114
17,
115115
],
116116
Array [
117-
10,
117+
12,
118118
45,
119119
4,
120120
0,
@@ -210,6 +210,31 @@ Array [
210210
]
211211
`;
212212

213+
exports[`code transformation worker: transforms an es module with regenerator 1`] = `
214+
"__d(function (global, _$$_REQUIRE, _$$_IMPORT_DEFAULT, _$$_IMPORT_ALL, module, exports, _dependencyMap) {
215+
var _interopRequireDefault = _$$_REQUIRE(_dependencyMap[0], \\"@babel/runtime/helpers/interopRequireDefault\\");
216+
217+
Object.defineProperty(exports, \\"__esModule\\", {
218+
value: true
219+
});
220+
exports.test = test;
221+
222+
var _regenerator = _interopRequireDefault(_$$_REQUIRE(_dependencyMap[1], \\"@babel/runtime/regenerator\\"));
223+
224+
function test() {
225+
return _regenerator.default.async(function test$(_context) {
226+
while (1) {
227+
switch (_context.prev = _context.next) {
228+
case 0:
229+
case \\"end\\":
230+
return _context.stop();
231+
}
232+
}
233+
}, null, this);
234+
}
235+
});"
236+
`;
237+
213238
exports[`code transformation worker: transforms import/export syntax when experimental flag is on 1`] = `
214239
Array [
215240
Array [

packages/metro/src/JSTransformer/worker/__tests__/worker-test.js

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -142,24 +142,63 @@ describe('code transformation worker:', () => {
142142
'__d(function (global, _$$_REQUIRE, _$$_IMPORT_DEFAULT, _$$_IMPORT_ALL, module, exports, _dependencyMap) {',
143143
" 'use strict';",
144144
'',
145-
' var _c = babelHelpers.interopRequireDefault(_$$_REQUIRE(_dependencyMap[0], "./c"));',
145+
' var _interopRequireDefault = _$$_REQUIRE(_dependencyMap[0], "@babel/runtime/helpers/interopRequireDefault");',
146146
'',
147-
' _$$_REQUIRE(_dependencyMap[1], "./a");',
147+
' var _c = _interopRequireDefault(_$$_REQUIRE(_dependencyMap[1], "./c"));',
148+
'',
149+
' _$$_REQUIRE(_dependencyMap[2], "./a");',
148150
'',
149151
' arbitrary(code);',
150152
'',
151-
' var b = _$$_REQUIRE(_dependencyMap[2], "b");',
153+
' var b = _$$_REQUIRE(_dependencyMap[3], "b");',
152154
'});',
153155
].join('\n'),
154156
);
155157
expect(result.output[0].data.map).toMatchSnapshot();
156158
expect(result.dependencies).toEqual([
159+
{
160+
data: {isAsync: false},
161+
name: '@babel/runtime/helpers/interopRequireDefault',
162+
},
157163
{data: {isAsync: false}, name: './c'},
158164
{data: {isAsync: false}, name: './a'},
159165
{data: {isAsync: false}, name: 'b'},
160166
]);
161167
});
162168

169+
it('transforms an es module with regenerator', async () => {
170+
const result = await transform(
171+
'/root/local/file.js',
172+
'local/file.js',
173+
'export async function test() {}',
174+
{
175+
assetExts: [],
176+
assetPlugins: [],
177+
assetRegistryPath: '',
178+
asyncRequireModulePath: 'asyncRequire',
179+
isScript: false,
180+
minifierPath: 'minifyModulePath',
181+
babelTransformerPath,
182+
transformOptions: {dev: true},
183+
dynamicDepsInPackages: 'reject',
184+
},
185+
);
186+
187+
expect(result.output[0].type).toBe('js/module');
188+
expect(result.output[0].data.code).toMatchSnapshot();
189+
expect(result.output[0].data.map).toHaveLength(13);
190+
expect(result.dependencies).toEqual([
191+
{
192+
data: {isAsync: false},
193+
name: '@babel/runtime/helpers/interopRequireDefault',
194+
},
195+
{
196+
data: {isAsync: false},
197+
name: '@babel/runtime/regenerator',
198+
},
199+
]);
200+
});
201+
163202
it('transforms import/export syntax when experimental flag is on', async () => {
164203
const contents = ['import c from "./c";'].join('\n');
165204

packages/metro/src/lib/polyfills/__tests__/require-test.js

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,6 @@ const fs = require('fs');
1414

1515
const {transformSync} = require('@babel/core');
1616

17-
// Include the external-helpers plugin to be able to detect if they're
18-
// needed when transforming the requirejs implementation.
19-
const PLUGINS = ['@babel/plugin-external-helpers'];
20-
2117
function createModule(
2218
moduleSystem,
2319
moduleId,
@@ -34,7 +30,6 @@ describe('require', () => {
3430
return transformSync(rawCode, {
3531
ast: false,
3632
babelrc: false,
37-
plugins: PLUGINS.map(require),
3833
presets: [require.resolve('metro-react-native-babel-preset')],
3934
retainLines: true,
4035
sourceMaps: 'inline',
@@ -56,9 +51,9 @@ describe('require', () => {
5651
});
5752

5853
it('does not need any babel helper logic', () => {
59-
// Super-simple check to validate that no babel helpers are used.
60-
// This check will need to be updated if https://fburl.com/6z0y2kf8 changes.
61-
expect(moduleSystemCode.includes('babelHelpers')).toBe(false);
54+
// The react native preset uses @babel/transform-runtime so helpers will be
55+
// imported from @babel/runtime.
56+
expect(moduleSystemCode.includes('@babel/runtime')).toBe(false);
6257
});
6358

6459
it('works with plain bundles', () => {

packages/metro/src/reactNativeTransformer.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
'use strict';
1313

1414
const crypto = require('crypto');
15-
const externalHelpersPlugin = require('@babel/plugin-external-helpers');
1615
const fs = require('fs');
1716
const inlineRequiresPlugin = require('babel-preset-fbjs/plugins/inline-requires');
1817
const makeHMRConfig = require('metro-react-native-babel-preset/src/configs/hmr');
@@ -25,7 +24,6 @@ import type {Plugins as BabelPlugins} from 'babel-core';
2524

2625
const cacheKeyParts = [
2726
fs.readFileSync(__filename),
28-
require('@babel/plugin-external-helpers/package.json').version,
2927
require('babel-preset-fbjs/package.json').version,
3028
];
3129

@@ -118,7 +116,7 @@ function buildBabelConfig(filename, options, plugins?: BabelPlugins = []) {
118116
let config = Object.assign({}, babelRC, extraConfig);
119117

120118
// Add extra plugins
121-
const extraPlugins = [externalHelpersPlugin];
119+
const extraPlugins = [];
122120

123121
if (options.inlineRequires) {
124122
extraPlugins.push(inlineRequiresPlugin);

yarn.lock

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -632,6 +632,15 @@
632632
dependencies:
633633
regenerator-transform "^0.13.3"
634634

635+
"@babel/plugin-transform-runtime@^7.0.0":
636+
version "7.0.0"
637+
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.0.0.tgz#0f1443c07bac16dba8efa939e0c61d6922740062"
638+
integrity sha512-yECRVxRu25Nsf6IY5v5XrXhcW9ZHomUQiq30VO8H7r3JYPcBJDTcxZmT+6v1O3QKKrDp1Wp40LinGbcd+jlp9A==
639+
dependencies:
640+
"@babel/helper-module-imports" "^7.0.0"
641+
"@babel/helper-plugin-utils" "^7.0.0"
642+
resolve "^1.8.1"
643+
635644
"@babel/plugin-transform-shorthand-properties@^7.0.0":
636645
version "7.0.0"
637646
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.0.0.tgz#85f8af592dcc07647541a0350e8c95c7bf419d15"
@@ -6753,6 +6762,12 @@ resolve@^1.3.2, resolve@^1.5.0:
67536762
dependencies:
67546763
path-parse "^1.0.5"
67556764

6765+
resolve@^1.8.1:
6766+
version "1.8.1"
6767+
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.8.1.tgz#82f1ec19a423ac1fbd080b0bab06ba36e84a7a26"
6768+
dependencies:
6769+
path-parse "^1.0.5"
6770+
67566771
restore-cursor@^2.0.0:
67576772
version "2.0.0"
67586773
resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf"

0 commit comments

Comments
 (0)