metro-transform-worker 0.66.0 → 0.68.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Copyright (c) Facebook, Inc. and its affiliates.
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
3
  *
4
4
  * This source code is licensed under the MIT license found in the
5
5
  * LICENSE file in the root directory of this source tree.
@@ -9,12 +9,12 @@
9
9
  */
10
10
  "use strict";
11
11
 
12
- const path = require("path");
13
-
14
12
  const { getAssetData } = require("metro/src/Assets");
15
13
 
16
14
  const { generateAssetCodeFileAst } = require("metro/src/Bundler/util");
17
15
 
16
+ const path = require("path");
17
+
18
18
  async function transform(
19
19
  { filename, options, src },
20
20
  assetRegistryPath,
@@ -24,7 +24,7 @@ async function transform(
24
24
  platform: "",
25
25
  projectRoot: "",
26
26
  inlineRequires: false,
27
- minify: false
27
+ minify: false,
28
28
  };
29
29
  const absolutePath = path.resolve(options.projectRoot, filename);
30
30
  const data = await getAssetData(
@@ -35,10 +35,10 @@ async function transform(
35
35
  options.publicPath
36
36
  );
37
37
  return {
38
- ast: generateAssetCodeFileAst(assetRegistryPath, data)
38
+ ast: generateAssetCodeFileAst(assetRegistryPath, data),
39
39
  };
40
40
  }
41
41
 
42
42
  module.exports = {
43
- transform
43
+ transform,
44
44
  };
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Copyright (c) Facebook, Inc. and its affiliates.
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
3
  *
4
4
  * This source code is licensed under the MIT license found in the
5
5
  * LICENSE file in the root directory of this source tree.
@@ -10,13 +10,12 @@
10
10
 
11
11
  'use strict';
12
12
 
13
- const path = require('path');
13
+ import type {File} from '@babel/types';
14
+ import type {BabelTransformerArgs} from 'metro-babel-transformer';
14
15
 
15
16
  const {getAssetData} = require('metro/src/Assets');
16
17
  const {generateAssetCodeFileAst} = require('metro/src/Bundler/util');
17
-
18
- import type {File} from '@babel/types';
19
- import type {BabelTransformerArgs} from 'metro-babel-transformer';
18
+ const path = require('path');
20
19
 
21
20
  async function transform(
22
21
  {filename, options, src}: BabelTransformerArgs,
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Copyright (c) Facebook, Inc. and its affiliates.
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
3
  *
4
4
  * This source code is licensed under the MIT license found in the
5
5
  * LICENSE file in the root directory of this source tree.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Copyright (c) Facebook, Inc. and its affiliates.
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
3
  *
4
4
  * This source code is licensed under the MIT license found in the
5
5
  * LICENSE file in the root directory of this source tree.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Copyright (c) Facebook, Inc. and its affiliates.
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
3
  *
4
4
  * This source code is licensed under the MIT license found in the
5
5
  * LICENSE file in the root directory of this source tree.
@@ -9,6 +9,6 @@
9
9
 
10
10
  'use strict';
11
11
 
12
- module.exports = function(data, callback) {
12
+ module.exports = function (data, callback) {
13
13
  callback(null, {});
14
14
  };
@@ -272,6 +272,46 @@ Object {
272
272
  }
273
273
  `;
274
274
 
275
+ exports[`transforms async generators 1`] = `
276
+ "__d(function (global, _$$_REQUIRE, _$$_IMPORT_DEFAULT, _$$_IMPORT_ALL, module, exports, _dependencyMap) {
277
+ var _interopRequireDefault = _$$_REQUIRE(_dependencyMap[0], \\"@babel/runtime/helpers/interopRequireDefault\\");
278
+
279
+ Object.defineProperty(exports, \\"__esModule\\", {
280
+ value: true
281
+ });
282
+ exports.test = test;
283
+
284
+ var _regenerator = _interopRequireDefault(_$$_REQUIRE(_dependencyMap[1], \\"@babel/runtime/regenerator\\"));
285
+
286
+ var _awaitAsyncGenerator2 = _interopRequireDefault(_$$_REQUIRE(_dependencyMap[2], \\"@babel/runtime/helpers/awaitAsyncGenerator\\"));
287
+
288
+ var _wrapAsyncGenerator2 = _interopRequireDefault(_$$_REQUIRE(_dependencyMap[3], \\"@babel/runtime/helpers/wrapAsyncGenerator\\"));
289
+
290
+ function test() {
291
+ return _test.apply(this, arguments);
292
+ }
293
+
294
+ function _test() {
295
+ _test = (0, _wrapAsyncGenerator2.default)(_regenerator.default.mark(function _callee() {
296
+ return _regenerator.default.wrap(function _callee$(_context) {
297
+ while (1) {
298
+ switch (_context.prev = _context.next) {
299
+ case 0:
300
+ _context.next = 2;
301
+ return \\"ok\\";
302
+
303
+ case 2:
304
+ case \\"end\\":
305
+ return _context.stop();
306
+ }
307
+ }
308
+ }, _callee);
309
+ }));
310
+ return _test.apply(this, arguments);
311
+ }
312
+ });"
313
+ `;
314
+
275
315
  exports[`transforms import/export syntax when experimental flag is on 1`] = `
276
316
  Array [
277
317
  Array [
@@ -1,10 +1,11 @@
1
1
  /**
2
- * Copyright (c) Facebook, Inc. and its affiliates.
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
3
  *
4
4
  * This source code is licensed under the MIT license found in the
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  *
7
7
  * @emails oncall+metro_bundler
8
+ * @flow strict-local
8
9
  * @format
9
10
  */
10
11
 
@@ -22,14 +23,16 @@ jest
22
23
  }))
23
24
  .mock('metro-minify-uglify');
24
25
 
25
- const HermesCompiler = require('metro-hermes-compiler');
26
+ import type {JsTransformerConfig} from '../index';
26
27
 
28
+ const HermesCompiler = require('metro-hermes-compiler');
27
29
  const path = require('path');
28
30
 
29
31
  const babelTransformerPath = require.resolve(
30
32
  'metro-react-native-babel-transformer',
31
33
  );
32
- const transformerContents = require('fs').readFileSync(babelTransformerPath);
34
+ const transformerContents = (() =>
35
+ require('fs').readFileSync(babelTransformerPath))();
33
36
 
34
37
  const HEADER_DEV =
35
38
  '__d(function (global, _$$_REQUIRE, _$$_IMPORT_DEFAULT, _$$_IMPORT_ALL, module, exports, _dependencyMap) {';
@@ -39,18 +42,28 @@ let fs;
39
42
  let mkdirp;
40
43
  let Transformer;
41
44
 
42
- const baseOptions = {
43
- assetExts: [],
45
+ const baseConfig: JsTransformerConfig = {
46
+ allowOptionalDependencies: false,
44
47
  assetPlugins: [],
45
48
  assetRegistryPath: '',
46
49
  asyncRequireModulePath: 'asyncRequire',
47
50
  babelTransformerPath,
48
51
  dynamicDepsInPackages: 'reject',
52
+ enableBabelRCLookup: false,
49
53
  enableBabelRuntime: true,
54
+ experimentalImportBundleSupport: false,
50
55
  globalPrefix: '',
56
+ hermesParser: false,
51
57
  minifierConfig: {},
52
58
  minifierPath: 'minifyModulePath',
53
59
  optimizationSizeLimit: 100000,
60
+ publicPath: '/assets',
61
+ unstable_collectDependenciesPath:
62
+ 'metro/src/ModuleGraph/worker/collectDependencies',
63
+ unstable_dependencyMapReservedName: null,
64
+ unstable_compactOutput: false,
65
+ unstable_disableModuleWrapping: false,
66
+ unstable_disableNormalizePseudoGlobals: false,
54
67
  };
55
68
 
56
69
  beforeEach(() => {
@@ -70,7 +83,7 @@ beforeEach(() => {
70
83
 
71
84
  it('transforms a simple script', async () => {
72
85
  const result = await Transformer.transform(
73
- baseOptions,
86
+ baseConfig,
74
87
  '/root',
75
88
  'local/file.js',
76
89
  'someReallyArbitrary(code)',
@@ -95,7 +108,7 @@ it('transforms a simple script', async () => {
95
108
 
96
109
  it('transforms a simple module', async () => {
97
110
  const result = await Transformer.transform(
98
- baseOptions,
111
+ baseConfig,
99
112
  '/root',
100
113
  'local/file.js',
101
114
  'arbitrary(code)',
@@ -116,7 +129,7 @@ it('transforms a simple module', async () => {
116
129
 
117
130
  it('transforms a module with dependencies', async () => {
118
131
  const contents = [
119
- "'use strict';",
132
+ '"use strict";',
120
133
  'require("./a");',
121
134
  'arbitrary(code);',
122
135
  'const b = require("b");',
@@ -124,7 +137,7 @@ it('transforms a module with dependencies', async () => {
124
137
  ].join('\n');
125
138
 
126
139
  const result = await Transformer.transform(
127
- baseOptions,
140
+ baseConfig,
128
141
  '/root',
129
142
  'local/file.js',
130
143
  contents,
@@ -138,7 +151,7 @@ it('transforms a module with dependencies', async () => {
138
151
  expect(result.output[0].data.code).toBe(
139
152
  [
140
153
  HEADER_DEV,
141
- " 'use strict';",
154
+ ' "use strict";',
142
155
  '',
143
156
  ' var _interopRequireDefault = _$$_REQUIRE(_dependencyMap[0], "@babel/runtime/helpers/interopRequireDefault");',
144
157
  '',
@@ -167,7 +180,7 @@ it('transforms a module with dependencies', async () => {
167
180
 
168
181
  it('transforms an es module with regenerator', async () => {
169
182
  const result = await Transformer.transform(
170
- baseOptions,
183
+ baseConfig,
171
184
  '/root',
172
185
  'local/file.js',
173
186
  'export async function test() {}',
@@ -198,11 +211,44 @@ it('transforms an es module with regenerator', async () => {
198
211
  ]);
199
212
  });
200
213
 
214
+ it('transforms async generators', async () => {
215
+ const result = await Transformer.transform(
216
+ baseConfig,
217
+ '/root',
218
+ 'local/file.js',
219
+ 'export async function* test() { yield "ok"; }',
220
+ {
221
+ dev: true,
222
+ type: 'module',
223
+ },
224
+ );
225
+
226
+ expect(result.output[0].data.code).toMatchSnapshot();
227
+ expect(result.dependencies).toEqual([
228
+ {
229
+ data: expect.objectContaining({asyncType: null}),
230
+ name: '@babel/runtime/helpers/interopRequireDefault',
231
+ },
232
+ {
233
+ data: expect.objectContaining({asyncType: null}),
234
+ name: '@babel/runtime/regenerator',
235
+ },
236
+ {
237
+ data: expect.objectContaining({asyncType: null}),
238
+ name: '@babel/runtime/helpers/awaitAsyncGenerator',
239
+ },
240
+ {
241
+ data: expect.objectContaining({asyncType: null}),
242
+ name: '@babel/runtime/helpers/wrapAsyncGenerator',
243
+ },
244
+ ]);
245
+ });
246
+
201
247
  it('transforms import/export syntax when experimental flag is on', async () => {
202
248
  const contents = ['import c from "./c";'].join('\n');
203
249
 
204
250
  const result = await Transformer.transform(
205
- baseOptions,
251
+ baseConfig,
206
252
  '/root',
207
253
  'local/file.js',
208
254
  contents,
@@ -237,7 +283,7 @@ it('transforms import/export syntax when experimental flag is on', async () => {
237
283
 
238
284
  it('does not add "use strict" on non-modules', async () => {
239
285
  const result = await Transformer.transform(
240
- baseOptions,
286
+ baseConfig,
241
287
  '/root',
242
288
  'node_modules/local/file.js',
243
289
  'module.exports = {};',
@@ -254,6 +300,27 @@ it('does not add "use strict" on non-modules', async () => {
254
300
  );
255
301
  });
256
302
 
303
+ it('preserves require() calls when module wrapping is disabled', async () => {
304
+ const contents = ['require("./c");'].join('\n');
305
+
306
+ const result = await Transformer.transform(
307
+ {
308
+ ...baseConfig,
309
+ unstable_disableModuleWrapping: true,
310
+ },
311
+ '/root',
312
+ 'local/file.js',
313
+ contents,
314
+ {
315
+ dev: true,
316
+ type: 'module',
317
+ },
318
+ );
319
+
320
+ expect(result.output[0].type).toBe('js/module');
321
+ expect(result.output[0].data.code).toBe('require("./c");');
322
+ });
323
+
257
324
  it('reports filename when encountering unsupported dynamic dependency', async () => {
258
325
  const contents = [
259
326
  'require("./a");',
@@ -263,7 +330,7 @@ it('reports filename when encountering unsupported dynamic dependency', async ()
263
330
 
264
331
  try {
265
332
  await Transformer.transform(
266
- baseOptions,
333
+ baseConfig,
267
334
  '/root',
268
335
  'local/file.js',
269
336
  contents,
@@ -283,7 +350,7 @@ it('supports dynamic dependencies from within `node_modules`', async () => {
283
350
  (
284
351
  await Transformer.transform(
285
352
  {
286
- ...baseOptions,
353
+ ...baseConfig,
287
354
  dynamicDepsInPackages: 'throwAtRuntime',
288
355
  },
289
356
  '/root',
@@ -310,7 +377,7 @@ it('minifies the code correctly', async () => {
310
377
  expect(
311
378
  (
312
379
  await Transformer.transform(
313
- baseOptions,
380
+ baseConfig,
314
381
  '/root',
315
382
  'local/file.js',
316
383
  'arbitrary(code);',
@@ -328,7 +395,7 @@ it('minifies a JSON file', async () => {
328
395
  expect(
329
396
  (
330
397
  await Transformer.transform(
331
- baseOptions,
398
+ baseConfig,
332
399
  '/root',
333
400
  'local/file.json',
334
401
  'arbitrary(code);',
@@ -348,9 +415,29 @@ it('minifies a JSON file', async () => {
348
415
  );
349
416
  });
350
417
 
418
+ it('does not wrap a JSON file when disableModuleWrapping is enabled', async () => {
419
+ expect(
420
+ (
421
+ await Transformer.transform(
422
+ {
423
+ ...baseConfig,
424
+ unstable_disableModuleWrapping: true,
425
+ },
426
+ '/root',
427
+ 'local/file.json',
428
+ 'arbitrary(code);',
429
+ {
430
+ dev: true,
431
+ type: 'module',
432
+ },
433
+ )
434
+ ).output[0].data.code,
435
+ ).toBe('module.exports = arbitrary(code);;');
436
+ });
437
+
351
438
  it('transforms a script to JS source and bytecode', async () => {
352
439
  const result = await Transformer.transform(
353
- baseOptions,
440
+ baseConfig,
354
441
  '/root',
355
442
  'local/file.js',
356
443
  'someReallyArbitrary(code)',
@@ -378,3 +465,194 @@ it('transforms a script to JS source and bytecode', async () => {
378
465
  HermesCompiler.validateBytecodeModule(bytecodeOutput.data.bytecode, 0),
379
466
  ).not.toThrow();
380
467
  });
468
+
469
+ it('allows replacing the collectDependencies implementation', async () => {
470
+ jest.mock(
471
+ 'metro-transform-worker/__virtual__/collectModifiedDependencies',
472
+ () =>
473
+ jest.fn((ast, opts) => {
474
+ const metroCoreCollectDependencies = jest.requireActual(
475
+ 'metro/src/ModuleGraph/worker/collectDependencies',
476
+ );
477
+ const collectedDeps = metroCoreCollectDependencies(ast, opts);
478
+ return {
479
+ ...collectedDeps,
480
+ dependencies: collectedDeps.dependencies.map(dep => ({
481
+ ...dep,
482
+ name: 'modified_' + dep.name,
483
+ })),
484
+ };
485
+ }),
486
+ {virtual: true},
487
+ );
488
+
489
+ const config = {
490
+ ...baseConfig,
491
+ unstable_collectDependenciesPath:
492
+ 'metro-transform-worker/__virtual__/collectModifiedDependencies',
493
+ };
494
+ const options = {
495
+ dev: true,
496
+ type: 'module',
497
+ };
498
+ const result = await Transformer.transform(
499
+ config,
500
+ '/root',
501
+ 'local/file.js',
502
+ 'require("foo")',
503
+ options,
504
+ );
505
+
506
+ // $FlowIgnore[cannot-resolve-module] this is a virtual module
507
+ const collectModifiedDependencies = require('metro-transform-worker/__virtual__/collectModifiedDependencies');
508
+ expect(collectModifiedDependencies).toHaveBeenCalledWith(
509
+ expect.objectContaining({type: 'File'}),
510
+ {
511
+ allowOptionalDependencies: config.allowOptionalDependencies,
512
+ asyncRequireModulePath: config.asyncRequireModulePath,
513
+ dynamicRequires: 'reject',
514
+ inlineableCalls: ['_$$_IMPORT_DEFAULT', '_$$_IMPORT_ALL'],
515
+ keepRequireNames: options.dev,
516
+ dependencyMapName: null,
517
+ },
518
+ );
519
+ expect(result.dependencies).toEqual([
520
+ expect.objectContaining({name: 'modified_foo'}),
521
+ ]);
522
+ });
523
+
524
+ it('uses a reserved dependency map name and prevents it from being minified', async () => {
525
+ const result = await Transformer.transform(
526
+ {...baseConfig, unstable_dependencyMapReservedName: 'THE_DEP_MAP'},
527
+ '/root',
528
+ 'local/file.js',
529
+ 'arbitrary(code);',
530
+ {
531
+ dev: false,
532
+ minify: true,
533
+ type: 'module',
534
+ },
535
+ );
536
+ expect(result.output[0].data.code).toMatchInlineSnapshot(`
537
+ "__d(function (g, r, i, a, m, e, THE_DEP_MAP) {
538
+ minified(code);
539
+ });"
540
+ `);
541
+ });
542
+
543
+ it('throws if the reserved dependency map name appears in the input', async () => {
544
+ await expect(
545
+ Transformer.transform(
546
+ {...baseConfig, unstable_dependencyMapReservedName: 'THE_DEP_MAP'},
547
+ '/root',
548
+ 'local/file.js',
549
+ 'arbitrary(code); /* the code is not allowed to mention THE_DEP_MAP, even in a comment */',
550
+ {
551
+ dev: false,
552
+ minify: true,
553
+ type: 'module',
554
+ },
555
+ ),
556
+ ).rejects.toThrowErrorMatchingInlineSnapshot(
557
+ `"Source code contains the reserved string \`THE_DEP_MAP\` at character offset 55"`,
558
+ );
559
+ });
560
+
561
+ it('allows disabling the normalizePseudoGlobals pass when minifying', async () => {
562
+ const result = await Transformer.transform(
563
+ {...baseConfig, unstable_disableNormalizePseudoGlobals: true},
564
+ '/root',
565
+ 'local/file.js',
566
+ 'arbitrary(code);',
567
+ {
568
+ dev: false,
569
+ minify: true,
570
+ type: 'module',
571
+ },
572
+ );
573
+ expect(result.output[0].data.code).toMatchInlineSnapshot(`
574
+ "__d(function (global, _$$_REQUIRE, _$$_IMPORT_DEFAULT, _$$_IMPORT_ALL, module, exports, _dependencyMap) {
575
+ minified(code);
576
+ });"
577
+ `);
578
+ });
579
+
580
+ it('allows emitting compact code when not minifying', async () => {
581
+ const result = await Transformer.transform(
582
+ {...baseConfig, unstable_compactOutput: true},
583
+ '/root',
584
+ 'local/file.js',
585
+ 'arbitrary(code);',
586
+ {
587
+ dev: false,
588
+ minify: false,
589
+ type: 'module',
590
+ },
591
+ );
592
+ expect(result.output[0].data.code).toMatchInlineSnapshot(
593
+ `"__d(function(global,_$$_REQUIRE,_$$_IMPORT_DEFAULT,_$$_IMPORT_ALL,module,exports,_dependencyMap){arbitrary(code);});"`,
594
+ );
595
+ });
596
+
597
+ it('skips minification in Hermes stable transform profile', async () => {
598
+ const result = await Transformer.transform(
599
+ baseConfig,
600
+ '/root',
601
+ 'local/file.js',
602
+ 'arbitrary(code);',
603
+ {
604
+ dev: false,
605
+ minify: true,
606
+ type: 'module',
607
+ unstable_transformProfile: 'hermes-canary',
608
+ },
609
+ );
610
+ expect(result.output[0].data.code).toMatchInlineSnapshot(`
611
+ "__d(function (global, _$$_REQUIRE, _$$_IMPORT_DEFAULT, _$$_IMPORT_ALL, module, exports, _dependencyMap) {
612
+ arbitrary(code);
613
+ });"
614
+ `);
615
+ });
616
+
617
+ it('skips minification in Hermes canary transform profile', async () => {
618
+ const result = await Transformer.transform(
619
+ baseConfig,
620
+ '/root',
621
+ 'local/file.js',
622
+ 'arbitrary(code);',
623
+ {
624
+ dev: false,
625
+ minify: true,
626
+ type: 'module',
627
+ unstable_transformProfile: 'hermes-canary',
628
+ },
629
+ );
630
+ expect(result.output[0].data.code).toMatchInlineSnapshot(`
631
+ "__d(function (global, _$$_REQUIRE, _$$_IMPORT_DEFAULT, _$$_IMPORT_ALL, module, exports, _dependencyMap) {
632
+ arbitrary(code);
633
+ });"
634
+ `);
635
+ });
636
+
637
+ it('counts all line endings correctly', async () => {
638
+ const transformStr = (
639
+ str: $TEMPORARY$string<'one\ntwo\nthree\nfour\nfive\nsix'> | string,
640
+ ) =>
641
+ Transformer.transform(baseConfig, '/root', 'local/file.js', str, {
642
+ dev: false,
643
+ minify: false,
644
+ type: 'module',
645
+ });
646
+
647
+ const differentEndingsResult = await transformStr(
648
+ 'one\rtwo\r\nthree\nfour\u2028five\u2029six',
649
+ );
650
+
651
+ const standardEndingsResult = await transformStr(
652
+ 'one\ntwo\nthree\nfour\nfive\nsix',
653
+ );
654
+
655
+ expect(differentEndingsResult.output[0].data.lineCount).toEqual(
656
+ standardEndingsResult.output[0].data.lineCount,
657
+ );
658
+ });