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.
package/src/index.js.flow CHANGED
@@ -1,52 +1,59 @@
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
- * @flow
7
+ * @flow strict-local
8
8
  * @format
9
9
  */
10
10
 
11
11
  'use strict';
12
12
 
13
- const HermesCompiler = require('metro-hermes-compiler');
14
- const JsFileWrapping = require('metro/src/ModuleGraph/worker/JsFileWrapping');
13
+ import type {
14
+ BabelTransformer,
15
+ BabelTransformerArgs,
16
+ CustomTransformOptions,
17
+ TransformProfile,
18
+ } from 'metro-babel-transformer';
19
+ import type {
20
+ HermesCompilerResult,
21
+ Options as HermesCompilerOptions,
22
+ } from 'metro-hermes-compiler';
23
+ import type {
24
+ BasicSourceMap,
25
+ FBSourceFunctionMap,
26
+ MetroSourceMapSegmentTuple,
27
+ } from 'metro-source-map';
28
+ import type {TransformResultDependency} from 'metro/src/DeltaBundler';
29
+ import type {AllowOptionalDependencies} from 'metro/src/DeltaBundler/types.flow.js';
30
+ import type {
31
+ DependencyTransformer,
32
+ DynamicRequiresBehavior,
33
+ } from 'metro/src/ModuleGraph/worker/collectDependencies';
34
+ import typeof CollectDependenciesFn from 'metro/src/ModuleGraph/worker/collectDependencies';
15
35
 
16
- const babylon = require('@babel/parser');
17
- const collectDependencies = require('metro/src/ModuleGraph/worker/collectDependencies');
18
- const generateImportNames = require('metro/src/ModuleGraph/worker/generateImportNames');
19
- const generate = require('@babel/generator').default;
20
- const getCacheKey = require('metro-cache-key');
21
36
  const getMinifier = require('./utils/getMinifier');
22
- const metroTransformPlugins = require('metro-transform-plugins');
23
37
  const {transformFromAstSync} = require('@babel/core');
24
- const {stableHash} = require('metro-cache');
38
+ const generate = require('@babel/generator').default;
39
+ const babylon = require('@babel/parser');
25
40
  const types = require('@babel/types');
26
- const countLines = require('metro/src/lib/countLines');
27
- const nullthrows = require('nullthrows');
28
-
41
+ const {stableHash} = require('metro-cache');
42
+ const getCacheKey = require('metro-cache-key');
43
+ const HermesCompiler = require('metro-hermes-compiler');
29
44
  const {
30
45
  fromRawMappings,
31
46
  toBabelSegments,
32
47
  toSegmentTuple,
33
48
  } = require('metro-source-map');
34
- import type {TransformResultDependency} from 'metro/src/DeltaBundler';
35
- import type {AllowOptionalDependencies} from 'metro/src/DeltaBundler/types.flow.js';
36
- import type {DynamicRequiresBehavior} from 'metro/src/ModuleGraph/worker/collectDependencies';
37
- import type {
38
- BasicSourceMap,
39
- FBSourceFunctionMap,
40
- MetroSourceMapSegmentTuple,
41
- } from 'metro-source-map';
42
- import type {
43
- HermesCompilerResult,
44
- Options as HermesCompilerOptions,
45
- } from 'metro-hermes-compiler';
46
- import type {
47
- CustomTransformOptions,
48
- TransformProfile,
49
- } from 'metro-babel-transformer';
49
+ const metroTransformPlugins = require('metro-transform-plugins');
50
+ const countLines = require('metro/src/lib/countLines');
51
+ const {
52
+ InvalidRequireCallError: InternalInvalidRequireCallError,
53
+ } = require('metro/src/ModuleGraph/worker/collectDependencies');
54
+ const generateImportNames = require('metro/src/ModuleGraph/worker/generateImportNames');
55
+ const JsFileWrapping = require('metro/src/ModuleGraph/worker/JsFileWrapping');
56
+ const nullthrows = require('nullthrows');
50
57
 
51
58
  type MinifierConfig = $ReadOnly<{[string]: mixed, ...}>;
52
59
 
@@ -65,7 +72,9 @@ export type MinifierResult = {
65
72
  ...
66
73
  };
67
74
 
68
- export type Minifier = MinifierOptions => MinifierResult;
75
+ export type Minifier = MinifierOptions =>
76
+ | MinifierResult
77
+ | Promise<MinifierResult>;
69
78
 
70
79
  export type Type = 'script' | 'module' | 'asset';
71
80
 
@@ -79,11 +88,17 @@ export type JsTransformerConfig = $ReadOnly<{|
79
88
  enableBabelRuntime: boolean,
80
89
  experimentalImportBundleSupport: boolean,
81
90
  globalPrefix: string,
91
+ hermesParser: boolean,
82
92
  minifierConfig: MinifierConfig,
83
93
  minifierPath: string,
84
94
  optimizationSizeLimit: number,
85
95
  publicPath: string,
86
96
  allowOptionalDependencies: AllowOptionalDependencies,
97
+ unstable_collectDependenciesPath: string,
98
+ unstable_dependencyMapReservedName: ?string,
99
+ unstable_disableModuleWrapping: boolean,
100
+ unstable_disableNormalizePseudoGlobals: boolean,
101
+ unstable_compactOutput: boolean,
87
102
  |}>;
88
103
 
89
104
  export type {CustomTransformOptions} from 'metro-babel-transformer';
@@ -104,6 +119,44 @@ export type JsTransformOptions = $ReadOnly<{|
104
119
  unstable_transformProfile: TransformProfile,
105
120
  |}>;
106
121
 
122
+ export type BytecodeFileType =
123
+ | 'bytecode/module'
124
+ | 'bytecode/module/asset'
125
+ | 'bytecode/script';
126
+
127
+ opaque type Path = string;
128
+
129
+ type BaseFile = $ReadOnly<{
130
+ code: string,
131
+ filename: Path,
132
+ inputFileSize: number,
133
+ }>;
134
+
135
+ type AssetFile = $ReadOnly<{
136
+ ...BaseFile,
137
+ type: 'asset',
138
+ }>;
139
+
140
+ type JSFileType = 'js/script' | 'js/module' | 'js/module/asset';
141
+
142
+ type JSFile = $ReadOnly<{
143
+ ...BaseFile,
144
+ ast?: ?BabelNodeFile,
145
+ type: JSFileType,
146
+ functionMap: FBSourceFunctionMap | null,
147
+ }>;
148
+
149
+ type JSONFile = {
150
+ ...BaseFile,
151
+ type: Type,
152
+ };
153
+
154
+ type TransformationContext = $ReadOnly<{
155
+ config: JsTransformerConfig,
156
+ projectRoot: Path,
157
+ options: JsTransformOptions,
158
+ }>;
159
+
107
160
  export type JsOutput = $ReadOnly<{|
108
161
  data: $ReadOnly<{|
109
162
  code: string,
@@ -111,18 +164,23 @@ export type JsOutput = $ReadOnly<{|
111
164
  map: Array<MetroSourceMapSegmentTuple>,
112
165
  functionMap: ?FBSourceFunctionMap,
113
166
  |}>,
114
- type: string,
167
+ type: JSFileType,
115
168
  |}>;
116
169
 
117
170
  export type BytecodeOutput = $ReadOnly<{|
118
171
  data: HermesCompilerResult,
119
- type: 'bytecode/module' | 'bytecode/module/asset' | 'bytecode/script',
172
+ type: BytecodeFileType,
120
173
  |}>;
121
174
 
122
- type Result = {|
175
+ type DependencySplitCondition = $PropertyType<
176
+ $PropertyType<TransformResultDependency, 'data'>,
177
+ 'splitCondition',
178
+ >;
179
+
180
+ type TransformResponse = $ReadOnly<{
123
181
  dependencies: $ReadOnlyArray<TransformResultDependency>,
124
182
  output: $ReadOnlyArray<JsOutput | BytecodeOutput>,
125
- |};
183
+ }>;
126
184
 
127
185
  function getDynamicDepsBehavior(
128
186
  inPackages: DynamicRequiresBehavior,
@@ -162,7 +220,7 @@ const minifyCode = async (
162
220
  const minify = getMinifier(config.minifierPath);
163
221
 
164
222
  try {
165
- const minified = minify({
223
+ const minified = await minify({
166
224
  code,
167
225
  map: sourceMap,
168
226
  filename,
@@ -188,10 +246,11 @@ const minifyCode = async (
188
246
  };
189
247
 
190
248
  const compileToBytecode = (
191
- code: string,
249
+ rawCode: string,
192
250
  type: string,
193
251
  options: HermesCompilerOptions,
194
252
  ): HermesCompilerResult => {
253
+ let code = rawCode;
195
254
  if (type.startsWith('js/module')) {
196
255
  const index = code.lastIndexOf(')');
197
256
  code =
@@ -202,175 +261,97 @@ const compileToBytecode = (
202
261
  return HermesCompiler.compile(code, options);
203
262
  };
204
263
 
264
+ const disabledDependencyTransformer: DependencyTransformer<mixed> = {
265
+ transformSyncRequire: () => void 0,
266
+ transformImportCall: () => void 0,
267
+ transformJSResource: () => void 0,
268
+ transformPrefetch: () => void 0,
269
+ transformIllegalDynamicRequire: () => void 0,
270
+ };
271
+
205
272
  class InvalidRequireCallError extends Error {
206
- innerError: collectDependencies.InvalidRequireCallError;
273
+ innerError: InternalInvalidRequireCallError;
207
274
  filename: string;
208
275
 
209
- constructor(
210
- innerError: collectDependencies.InvalidRequireCallError,
211
- filename: string,
212
- ) {
276
+ constructor(innerError: InternalInvalidRequireCallError, filename: string) {
213
277
  super(`${filename}:${innerError.message}`);
214
278
  this.innerError = innerError;
215
279
  this.filename = filename;
216
280
  }
217
281
  }
218
282
 
219
- module.exports = {
220
- transform: async (
221
- config: JsTransformerConfig,
222
- projectRoot: string,
223
- filename: string,
224
- data: Buffer,
225
- options: JsTransformOptions,
226
- ): Promise<Result> => {
227
- const sourceCode = data.toString('utf8');
228
- let type = 'js/module';
229
- let bytecodeType = 'bytecode/module';
283
+ async function transformJS(
284
+ file: JSFile,
285
+ {config, options, projectRoot}: TransformationContext,
286
+ ): Promise<TransformResponse> {
287
+ // Transformers can output null ASTs (if they ignore the file). In that case
288
+ // we need to parse the module source code to get their AST.
289
+ let ast = file.ast ?? babylon.parse(file.code, {sourceType: 'unambiguous'});
230
290
 
231
- if (options.type === 'asset') {
232
- type = 'js/module/asset';
233
- bytecodeType = 'bytecode/module/asset';
234
- }
235
- if (options.type === 'script') {
236
- type = 'js/script';
237
- bytecodeType = 'bytecode/script';
238
- }
291
+ const {importDefault, importAll} = generateImportNames(ast);
239
292
 
240
- if (filename.endsWith('.json')) {
241
- let code = JsFileWrapping.wrapJson(sourceCode, config.globalPrefix);
242
- let map = [];
243
-
244
- if (options.minify) {
245
- ({map, code} = await minifyCode(
246
- config,
247
- projectRoot,
248
- filename,
249
- code,
250
- sourceCode,
251
- map,
252
- ));
253
- }
293
+ // Add "use strict" if the file was parsed as a module, and the directive did
294
+ // not exist yet.
295
+ const {directives} = ast.program;
254
296
 
255
- const output = [
256
- {
257
- data: {code, lineCount: countLines(code), map, functionMap: null},
258
- type,
259
- },
260
- ];
261
- if (options.runtimeBytecodeVersion) {
262
- output.push({
263
- data: (compileToBytecode(code, type, {
264
- sourceURL: filename,
265
- sourceMap: fromRawMappings([
266
- {
267
- code,
268
- source: sourceCode,
269
- map,
270
- functionMap: null,
271
- path: filename,
272
- },
273
- ]).toString(),
274
- }): HermesCompilerResult),
275
- type: bytecodeType,
276
- });
277
- }
297
+ if (
298
+ ast.program.sourceType === 'module' &&
299
+ directives != null &&
300
+ directives.findIndex(d => d.value.value === 'use strict') === -1
301
+ ) {
302
+ directives.push(types.directive(types.directiveLiteral('use strict')));
303
+ }
278
304
 
279
- return {
280
- dependencies: [],
281
- output,
282
- };
283
- }
305
+ // Perform the import-export transform (in case it's still needed), then
306
+ // fold requires and perform constant folding (if in dev).
307
+ const plugins = [];
308
+ const babelPluginOpts = {
309
+ ...options,
310
+ inlineableCalls: [importDefault, importAll],
311
+ importDefault,
312
+ importAll,
313
+ };
314
+
315
+ if (options.experimentalImportSupport === true) {
316
+ plugins.push([metroTransformPlugins.importExportPlugin, babelPluginOpts]);
317
+ }
284
318
 
285
- const transformerArgs = {
286
- filename,
287
- options: {
288
- ...options,
289
- enableBabelRCLookup: config.enableBabelRCLookup,
290
- enableBabelRuntime: config.enableBabelRuntime,
291
- globalPrefix: config.globalPrefix,
292
- // Inline requires are now performed at a secondary step. We cannot
293
- // unfortunately remove it from the internal transformer, since this one
294
- // is used by other tooling, and this would affect it.
295
- inlineRequires: false,
296
- nonInlinedRequires: [],
297
- projectRoot,
298
- publicPath: config.publicPath,
319
+ if (options.inlineRequires) {
320
+ plugins.push([
321
+ // $FlowFixMe[untyped-import] untyped module
322
+ require('babel-preset-fbjs/plugins/inline-requires'),
323
+ {
324
+ ...babelPluginOpts,
325
+ ignoredRequires: options.nonInlinedRequires,
299
326
  },
300
- plugins: [],
301
- src: sourceCode,
302
- };
303
-
304
- let transformResult;
305
-
306
- if (type === 'js/module/asset') {
307
- const assetTransformer = require('./utils/assetTransformer');
308
-
309
- transformResult = {
310
- ...(await assetTransformer.transform(
311
- transformerArgs,
312
- config.assetRegistryPath,
313
- config.assetPlugins,
314
- )),
315
- functionMap: null,
316
- };
317
- } else {
318
- // $FlowFixMe TODO t26372934 Plugin system
319
- const transformer: Transformer<*> = require(config.babelTransformerPath);
320
- transformResult = await transformer.transform(transformerArgs);
321
- }
322
-
323
- // Transformers can output null ASTs (if they ignore the file). In that case
324
- // we need to parse the module source code to get their AST.
325
- let ast =
326
- transformResult.ast ||
327
- babylon.parse(sourceCode, {sourceType: 'unambiguous'});
328
-
329
- const {importDefault, importAll} = generateImportNames(ast);
330
-
331
- // Add "use strict" if the file was parsed as a module, and the directive did
332
- // not exist yet.
333
- const {directives} = ast.program;
334
-
335
- if (
336
- ast.program.sourceType === 'module' &&
337
- // $FlowFixMe[incompatible-use]
338
- directives.findIndex(d => d.value.value === 'use strict') === -1
339
- ) {
340
- // $FlowFixMe[incompatible-use]
341
- directives.push(types.directive(types.directiveLiteral('use strict')));
342
- }
343
-
344
- // Perform the import-export transform (in case it's still needed), then
345
- // fold requires and perform constant folding (if in dev).
346
- const plugins = [];
347
- const opts = {
348
- ...options,
349
- inlineableCalls: [importDefault, importAll],
350
- importDefault,
351
- importAll,
352
- };
353
-
354
- if (options.experimentalImportSupport) {
355
- plugins.push([metroTransformPlugins.importExportPlugin, opts]);
356
- }
357
-
358
- if (options.inlineRequires) {
359
- plugins.push([
360
- require('babel-preset-fbjs/plugins/inline-requires'),
361
- {
362
- ...opts,
363
- ignoredRequires: options.nonInlinedRequires,
364
- },
365
- ]);
366
- }
367
-
368
- if (!options.dev) {
369
- plugins.push([metroTransformPlugins.constantFoldingPlugin, opts]);
370
- }
371
-
372
- plugins.push([metroTransformPlugins.inlinePlugin, opts]);
327
+ ]);
328
+ }
373
329
 
330
+ plugins.push([metroTransformPlugins.inlinePlugin, babelPluginOpts]);
331
+
332
+ ast = nullthrows(
333
+ transformFromAstSync(ast, '', {
334
+ ast: true,
335
+ babelrc: false,
336
+ code: false,
337
+ configFile: false,
338
+ comments: false,
339
+ filename: file.filename,
340
+ plugins,
341
+ sourceMaps: false,
342
+ // Not-Cloning the input AST here should be safe because other code paths above this call
343
+ // are mutating the AST as well and no code is depending on the original AST.
344
+ // However, switching the flag to false caused issues with ES Modules if `experimentalImportSupport` isn't used https://github.com/facebook/metro/issues/641
345
+ // either because one of the plugins is doing something funky or Babel messes up some caches.
346
+ // Make sure to test the above mentioned case before flipping the flag back to false.
347
+ cloneInputAst: true,
348
+ }).ast,
349
+ );
350
+
351
+ if (!options.dev) {
352
+ // Run the constant folding plugin in its own pass, avoiding race conditions
353
+ // with other plugins that have exit() visitors on Program (e.g. the ESM
354
+ // transform).
374
355
  ast = nullthrows(
375
356
  transformFromAstSync(ast, '', {
376
357
  ast: true,
@@ -378,53 +359,57 @@ module.exports = {
378
359
  code: false,
379
360
  configFile: false,
380
361
  comments: false,
381
- compact: false,
382
- filename,
383
- plugins,
362
+ filename: file.filename,
363
+ plugins: [
364
+ [metroTransformPlugins.constantFoldingPlugin, babelPluginOpts],
365
+ ],
384
366
  sourceMaps: false,
385
- // Not-Cloning the input AST here should be safe because other code paths above this call
386
- // are mutating the AST as well and no code is depending on the original AST.
387
- // However, switching the flag to false caused issues with ES Modules if `experimentalImportSupport` isn't used https://github.com/facebook/metro/issues/641
388
- // either because one of the plugins is doing something funky or Babel messes up some caches.
389
- // Make sure to test the above mentioned case before flipping the flag back to false.
390
- cloneInputAst: true,
367
+ cloneInputAst: false,
391
368
  }).ast,
392
369
  );
370
+ }
393
371
 
394
- let dependencyMapName = '';
395
- let dependencies;
396
- let wrappedAst;
397
-
398
- // If the module to transform is a script (meaning that is not part of the
399
- // dependency graph and it code will just be prepended to the bundle modules),
400
- // we need to wrap it differently than a commonJS module (also, scripts do
401
- // not have dependencies).
402
- if (type === 'js/script') {
403
- dependencies = [];
404
- wrappedAst = JsFileWrapping.wrapPolyfill(ast);
405
- } else {
406
- try {
407
- const opts = {
408
- asyncRequireModulePath: config.asyncRequireModulePath,
409
- dynamicRequires: getDynamicDepsBehavior(
410
- config.dynamicDepsInPackages,
411
- filename,
412
- ),
413
- inlineableCalls: [importDefault, importAll],
414
- keepRequireNames: options.dev,
415
- allowOptionalDependencies: config.allowOptionalDependencies,
416
- };
417
- ({ast, dependencies, dependencyMapName} = collectDependencies(
418
- ast,
419
- opts,
420
- ));
421
- } catch (error) {
422
- if (error instanceof collectDependencies.InvalidRequireCallError) {
423
- throw new InvalidRequireCallError(error, filename);
424
- }
425
- throw error;
372
+ let dependencyMapName = '';
373
+ let dependencies;
374
+ let wrappedAst;
375
+
376
+ // If the module to transform is a script (meaning that is not part of the
377
+ // dependency graph and it code will just be prepended to the bundle modules),
378
+ // we need to wrap it differently than a commonJS module (also, scripts do
379
+ // not have dependencies).
380
+ if (file.type === 'js/script') {
381
+ dependencies = [];
382
+ wrappedAst = JsFileWrapping.wrapPolyfill(ast);
383
+ } else {
384
+ try {
385
+ const opts = {
386
+ asyncRequireModulePath: config.asyncRequireModulePath,
387
+ dependencyTransformer:
388
+ config.unstable_disableModuleWrapping === true
389
+ ? disabledDependencyTransformer
390
+ : undefined,
391
+ dynamicRequires: getDynamicDepsBehavior(
392
+ config.dynamicDepsInPackages,
393
+ file.filename,
394
+ ),
395
+ inlineableCalls: [importDefault, importAll],
396
+ keepRequireNames: options.dev,
397
+ allowOptionalDependencies: config.allowOptionalDependencies,
398
+ dependencyMapName: config.unstable_dependencyMapReservedName,
399
+ };
400
+ // $FlowFixMe[unsupported-syntax] dynamic require
401
+ const collectDependencies: CollectDependenciesFn<DependencySplitCondition> = require(config.unstable_collectDependenciesPath);
402
+ ({ast, dependencies, dependencyMapName} = collectDependencies(ast, opts));
403
+ } catch (error) {
404
+ if (error instanceof InternalInvalidRequireCallError) {
405
+ throw new InvalidRequireCallError(error, file.filename);
426
406
  }
407
+ throw error;
408
+ }
427
409
 
410
+ if (config.unstable_disableModuleWrapping === true) {
411
+ wrappedAst = ast;
412
+ } else {
428
413
  ({ast: wrappedAst} = JsFileWrapping.wrapModule(
429
414
  ast,
430
415
  importDefault,
@@ -433,79 +418,327 @@ module.exports = {
433
418
  config.globalPrefix,
434
419
  ));
435
420
  }
421
+ }
436
422
 
437
- const reserved =
438
- options.minify && data.length <= config.optimizationSizeLimit
439
- ? metroTransformPlugins.normalizePseudoGlobals(wrappedAst)
440
- : [];
423
+ const minify =
424
+ options.minify &&
425
+ options.unstable_transformProfile !== 'hermes-canary' &&
426
+ options.unstable_transformProfile !== 'hermes-stable';
441
427
 
442
- const result = generate(
443
- wrappedAst,
444
- {
445
- comments: false,
446
- compact: false,
447
- filename,
448
- retainLines: false,
449
- sourceFileName: filename,
450
- sourceMaps: true,
451
- },
452
- sourceCode,
428
+ const reserved = [];
429
+ if (config.unstable_dependencyMapReservedName != null) {
430
+ reserved.push(config.unstable_dependencyMapReservedName);
431
+ }
432
+ if (
433
+ minify &&
434
+ file.inputFileSize <= config.optimizationSizeLimit &&
435
+ !config.unstable_disableNormalizePseudoGlobals
436
+ ) {
437
+ reserved.push(
438
+ ...metroTransformPlugins.normalizePseudoGlobals(wrappedAst, {
439
+ reservedNames: reserved,
440
+ }),
453
441
  );
442
+ }
454
443
 
455
- let map = result.rawMappings ? result.rawMappings.map(toSegmentTuple) : [];
456
- let code = result.code;
444
+ const result = generate(
445
+ wrappedAst,
446
+ {
447
+ comments: false,
448
+ compact: config.unstable_compactOutput,
449
+ filename: file.filename,
450
+ retainLines: false,
451
+ sourceFileName: file.filename,
452
+ sourceMaps: true,
453
+ },
454
+ file.code,
455
+ );
456
+
457
+ let map = result.rawMappings ? result.rawMappings.map(toSegmentTuple) : [];
458
+ let code = result.code;
459
+
460
+ if (minify) {
461
+ ({map, code} = await minifyCode(
462
+ config,
463
+ projectRoot,
464
+ file.filename,
465
+ result.code,
466
+ file.code,
467
+ map,
468
+ reserved,
469
+ ));
470
+ }
457
471
 
458
- if (options.minify) {
459
- ({map, code} = await minifyCode(
460
- config,
461
- projectRoot,
462
- filename,
463
- result.code,
464
- sourceCode,
472
+ const output = [
473
+ {
474
+ data: {
475
+ code,
476
+ lineCount: countLines(code),
465
477
  map,
466
- reserved,
467
- ));
478
+ functionMap: file.functionMap,
479
+ },
480
+ type: file.type,
481
+ },
482
+ ];
483
+
484
+ if (options.runtimeBytecodeVersion != null) {
485
+ output.push({
486
+ data: (compileToBytecode(code, file.type, {
487
+ sourceURL: file.filename,
488
+ sourceMap: fromRawMappings([
489
+ {
490
+ code,
491
+ source: file.code,
492
+ map,
493
+ functionMap: null,
494
+ path: file.filename,
495
+ },
496
+ ]).toString(),
497
+ }): HermesCompilerResult),
498
+ type: getBytecodeFileType(file.type),
499
+ });
500
+ }
501
+
502
+ return {
503
+ dependencies,
504
+ output,
505
+ };
506
+ }
507
+
508
+ /**
509
+ * Transforms an asset file
510
+ */
511
+ async function transformAsset(
512
+ file: AssetFile,
513
+ context: TransformationContext,
514
+ ): Promise<TransformResponse> {
515
+ const assetTransformer = require('./utils/assetTransformer');
516
+ const {assetRegistryPath, assetPlugins} = context.config;
517
+
518
+ const result = await assetTransformer.transform(
519
+ getBabelTransformArgs(file, context),
520
+ assetRegistryPath,
521
+ assetPlugins,
522
+ );
523
+
524
+ const jsFile = {
525
+ ...file,
526
+ type: 'js/module/asset',
527
+ ast: result.ast,
528
+ functionMap: null,
529
+ };
530
+
531
+ return transformJS(jsFile, context);
532
+ }
533
+
534
+ /**
535
+ * Transforms a JavaScript file with Babel before processing the file with
536
+ * the generic JavaScript transformation.
537
+ */
538
+ async function transformJSWithBabel(
539
+ file: JSFile,
540
+ context: TransformationContext,
541
+ ): Promise<TransformResponse> {
542
+ const {babelTransformerPath} = context.config;
543
+ // $FlowFixMe[unsupported-syntax] dynamic require
544
+ const transformer: BabelTransformer = require(babelTransformerPath);
545
+
546
+ const transformResult = await transformer.transform(
547
+ getBabelTransformArgs(file, context),
548
+ );
549
+
550
+ const jsFile: JSFile = {
551
+ ...file,
552
+ ast: transformResult.ast,
553
+ functionMap: transformResult.functionMap ?? null,
554
+ };
555
+
556
+ return await transformJS(jsFile, context);
557
+ }
558
+
559
+ async function transformJSON(
560
+ file: JSONFile,
561
+ {options, config, projectRoot}: TransformationContext,
562
+ ): Promise<TransformResponse> {
563
+ let code =
564
+ config.unstable_disableModuleWrapping === true
565
+ ? JsFileWrapping.jsonToCommonJS(file.code)
566
+ : JsFileWrapping.wrapJson(file.code, config.globalPrefix);
567
+ let map = [];
568
+
569
+ // TODO: When we can reuse transformJS for JSON, we should not derive `minify` separately.
570
+ const minify =
571
+ options.minify &&
572
+ options.unstable_transformProfile !== 'hermes-canary' &&
573
+ options.unstable_transformProfile !== 'hermes-stable';
574
+
575
+ if (minify) {
576
+ ({map, code} = await minifyCode(
577
+ config,
578
+ projectRoot,
579
+ file.filename,
580
+ code,
581
+ file.code,
582
+ map,
583
+ ));
584
+ }
585
+
586
+ let jsType: JSFileType;
587
+
588
+ if (file.type === 'asset') {
589
+ jsType = 'js/module/asset';
590
+ } else if (file.type === 'script') {
591
+ jsType = 'js/script';
592
+ } else {
593
+ jsType = 'js/module';
594
+ }
595
+
596
+ const output = [
597
+ {
598
+ data: {code, lineCount: countLines(code), map, functionMap: null},
599
+ type: jsType,
600
+ },
601
+ ];
602
+
603
+ if (options.runtimeBytecodeVersion != null) {
604
+ output.push({
605
+ data: (compileToBytecode(code, jsType, {
606
+ sourceURL: file.filename,
607
+ sourceMap: fromRawMappings([
608
+ {
609
+ code,
610
+ source: file.code,
611
+ map,
612
+ functionMap: null,
613
+ path: file.filename,
614
+ },
615
+ ]).toString(),
616
+ }): HermesCompilerResult),
617
+ type: getBytecodeFileType(jsType),
618
+ });
619
+ }
620
+
621
+ return {
622
+ dependencies: [],
623
+ output,
624
+ };
625
+ }
626
+
627
+ /**
628
+ * Returns the bytecode type for a file type
629
+ */
630
+ function getBytecodeFileType(type: JSFileType): BytecodeFileType {
631
+ switch (type) {
632
+ case 'js/module/asset':
633
+ return 'bytecode/module/asset';
634
+ case 'js/script':
635
+ return 'bytecode/script';
636
+ default:
637
+ (type: 'js/module');
638
+ return 'bytecode/module';
639
+ }
640
+ }
641
+
642
+ function getBabelTransformArgs(
643
+ file: $ReadOnly<{filename: Path, code: string, ...}>,
644
+ {options, config, projectRoot}: TransformationContext,
645
+ ): BabelTransformerArgs {
646
+ return {
647
+ filename: file.filename,
648
+ options: {
649
+ ...options,
650
+ enableBabelRCLookup: config.enableBabelRCLookup,
651
+ enableBabelRuntime: config.enableBabelRuntime,
652
+ globalPrefix: config.globalPrefix,
653
+ hermesParser: config.hermesParser,
654
+ // Inline requires are now performed at a secondary step. We cannot
655
+ // unfortunately remove it from the internal transformer, since this one
656
+ // is used by other tooling, and this would affect it.
657
+ inlineRequires: false,
658
+ nonInlinedRequires: [],
659
+ projectRoot,
660
+ publicPath: config.publicPath,
661
+ },
662
+ plugins: [],
663
+ src: file.code,
664
+ };
665
+ }
666
+
667
+ module.exports = {
668
+ transform: async (
669
+ config: JsTransformerConfig,
670
+ projectRoot: string,
671
+ filename: string,
672
+ data: Buffer,
673
+ options: JsTransformOptions,
674
+ ): Promise<TransformResponse> => {
675
+ const context: TransformationContext = {
676
+ config,
677
+ projectRoot,
678
+ options,
679
+ };
680
+ const sourceCode = data.toString('utf8');
681
+
682
+ const {unstable_dependencyMapReservedName} = config;
683
+ if (unstable_dependencyMapReservedName != null) {
684
+ const position = sourceCode.indexOf(unstable_dependencyMapReservedName);
685
+ if (position > -1) {
686
+ throw new SyntaxError(
687
+ 'Source code contains the reserved string `' +
688
+ unstable_dependencyMapReservedName +
689
+ '` at character offset ' +
690
+ position,
691
+ );
692
+ }
468
693
  }
469
694
 
470
- const output = [
471
- {
472
- data: {
473
- code,
474
- lineCount: countLines(code),
475
- map,
476
- functionMap: transformResult.functionMap,
477
- },
478
- type,
479
- },
480
- ];
481
-
482
- if (options.runtimeBytecodeVersion) {
483
- output.push({
484
- data: (compileToBytecode(code, type, {
485
- sourceURL: filename,
486
- sourceMap: fromRawMappings([
487
- {code, source: sourceCode, map, functionMap: null, path: filename},
488
- ]).toString(),
489
- }): HermesCompilerResult),
490
- type: bytecodeType,
491
- });
695
+ if (filename.endsWith('.json')) {
696
+ const jsonFile: JSONFile = {
697
+ filename,
698
+ inputFileSize: data.length,
699
+ code: sourceCode,
700
+ type: options.type,
701
+ };
702
+
703
+ return await transformJSON(jsonFile, context);
492
704
  }
493
705
 
494
- return {
495
- dependencies,
496
- output,
706
+ if (options.type === 'asset') {
707
+ const file: AssetFile = {
708
+ filename,
709
+ inputFileSize: data.length,
710
+ code: sourceCode,
711
+ type: options.type,
712
+ };
713
+
714
+ return await transformAsset(file, context);
715
+ }
716
+
717
+ const file: JSFile = {
718
+ filename,
719
+ inputFileSize: data.length,
720
+ code: sourceCode,
721
+ type: options.type === 'script' ? 'js/script' : 'js/module',
722
+ functionMap: null,
497
723
  };
724
+
725
+ return await transformJSWithBabel(file, context);
498
726
  },
499
727
 
500
728
  getCacheKey: (config: JsTransformerConfig): string => {
501
- const {babelTransformerPath, minifierPath, ...remainingConfig} = config;
729
+ const {
730
+ babelTransformerPath,
731
+ minifierPath,
732
+ unstable_collectDependenciesPath,
733
+ ...remainingConfig
734
+ } = config;
502
735
 
503
736
  const filesKey = getCacheKey([
504
737
  require.resolve(babelTransformerPath),
505
738
  require.resolve(minifierPath),
506
739
  require.resolve('./utils/getMinifier'),
507
740
  require.resolve('./utils/assetTransformer'),
508
- require.resolve('metro/src/ModuleGraph/worker/collectDependencies'),
741
+ require.resolve(unstable_collectDependenciesPath),
509
742
  require.resolve('metro/src/ModuleGraph/worker/generateImportNames'),
510
743
  require.resolve('metro/src/ModuleGraph/worker/JsFileWrapping'),
511
744
  ...metroTransformPlugins.getTransformPluginCacheKeyFiles(),