metro-transform-worker 0.63.0 → 0.64.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.
@@ -0,0 +1,517 @@
1
+ /**
2
+ * Copyright (c) Facebook, Inc. and its affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @flow
8
+ * @format
9
+ */
10
+
11
+ 'use strict';
12
+
13
+ const HermesCompiler = require('metro-hermes-compiler');
14
+ const JsFileWrapping = require('metro/src/ModuleGraph/worker/JsFileWrapping');
15
+
16
+ const assetTransformer = require('./utils/assetTransformer');
17
+ const babylon = require('@babel/parser');
18
+ const collectDependencies = require('metro/src/ModuleGraph/worker/collectDependencies');
19
+ const generateImportNames = require('metro/src/ModuleGraph/worker/generateImportNames');
20
+ const nullthrows = require('nullthrows');
21
+ const generate = require('@babel/generator').default;
22
+ const getCacheKey = require('metro-cache-key');
23
+ const getMinifier = require('./utils/getMinifier');
24
+ const {
25
+ constantFoldingPlugin,
26
+ getTransformPluginCacheKeyFiles,
27
+ importExportPlugin,
28
+ inlinePlugin,
29
+ normalizePseudoGlobals,
30
+ } = require('metro-transform-plugins');
31
+ const inlineRequiresPlugin = require('babel-preset-fbjs/plugins/inline-requires');
32
+ const {transformFromAstSync} = require('@babel/core');
33
+ const {stableHash} = require('metro-cache');
34
+ const types = require('@babel/types');
35
+ const countLines = require('metro/src/lib/countLines');
36
+
37
+ const {
38
+ fromRawMappings,
39
+ toBabelSegments,
40
+ toSegmentTuple,
41
+ } = require('metro-source-map');
42
+ import type {TransformResultDependency} from 'metro/src/DeltaBundler';
43
+ import type {AllowOptionalDependencies} from 'metro/src/DeltaBundler/types.flow.js';
44
+ import type {DynamicRequiresBehavior} from 'metro/src/ModuleGraph/worker/collectDependencies';
45
+ import type {
46
+ BasicSourceMap,
47
+ FBSourceFunctionMap,
48
+ MetroSourceMapSegmentTuple,
49
+ } from 'metro-source-map';
50
+ import type {
51
+ HermesCompilerResult,
52
+ Options as HermesCompilerOptions,
53
+ } from 'metro-hermes-compiler';
54
+ import type {
55
+ CustomTransformOptions,
56
+ TransformProfile,
57
+ } from 'metro-babel-transformer';
58
+
59
+ type MinifierConfig = $ReadOnly<{[string]: mixed, ...}>;
60
+
61
+ export type MinifierOptions = {
62
+ code: string,
63
+ map: ?BasicSourceMap,
64
+ filename: string,
65
+ reserved: $ReadOnlyArray<string>,
66
+ config: MinifierConfig,
67
+ ...
68
+ };
69
+
70
+ export type MinifierResult = {
71
+ code: string,
72
+ map?: BasicSourceMap,
73
+ ...
74
+ };
75
+
76
+ export type Minifier = MinifierOptions => MinifierResult;
77
+
78
+ export type Type = 'script' | 'module' | 'asset';
79
+
80
+ export type JsTransformerConfig = $ReadOnly<{|
81
+ assetPlugins: $ReadOnlyArray<string>,
82
+ assetRegistryPath: string,
83
+ asyncRequireModulePath: string,
84
+ babelTransformerPath: string,
85
+ dynamicDepsInPackages: DynamicRequiresBehavior,
86
+ enableBabelRCLookup: boolean,
87
+ enableBabelRuntime: boolean,
88
+ experimentalImportBundleSupport: boolean,
89
+ globalPrefix: string,
90
+ minifierConfig: MinifierConfig,
91
+ minifierPath: string,
92
+ optimizationSizeLimit: number,
93
+ publicPath: string,
94
+ allowOptionalDependencies: AllowOptionalDependencies,
95
+ |}>;
96
+
97
+ export type {CustomTransformOptions} from 'metro-babel-transformer';
98
+
99
+ export type JsTransformOptions = $ReadOnly<{|
100
+ customTransformOptions?: CustomTransformOptions,
101
+ dev: boolean,
102
+ experimentalImportSupport?: boolean,
103
+ hot: boolean,
104
+ inlinePlatform: boolean,
105
+ inlineRequires: boolean,
106
+ minify: boolean,
107
+ nonInlinedRequires?: $ReadOnlyArray<string>,
108
+ platform: ?string,
109
+ runtimeBytecodeVersion: ?number,
110
+ type: Type,
111
+ unstable_disableES6Transforms?: boolean,
112
+ unstable_transformProfile: TransformProfile,
113
+ |}>;
114
+
115
+ export type JsOutput = $ReadOnly<{|
116
+ data: $ReadOnly<{|
117
+ code: string,
118
+ lineCount: number,
119
+ map: Array<MetroSourceMapSegmentTuple>,
120
+ functionMap: ?FBSourceFunctionMap,
121
+ |}>,
122
+ type: string,
123
+ |}>;
124
+
125
+ export type BytecodeOutput = $ReadOnly<{|
126
+ data: HermesCompilerResult,
127
+ type: 'bytecode/module' | 'bytecode/module/asset' | 'bytecode/script',
128
+ |}>;
129
+
130
+ type Result = {|
131
+ dependencies: $ReadOnlyArray<TransformResultDependency>,
132
+ output: $ReadOnlyArray<JsOutput | BytecodeOutput>,
133
+ |};
134
+
135
+ function getDynamicDepsBehavior(
136
+ inPackages: DynamicRequiresBehavior,
137
+ filename: string,
138
+ ): DynamicRequiresBehavior {
139
+ switch (inPackages) {
140
+ case 'reject':
141
+ return 'reject';
142
+ case 'throwAtRuntime':
143
+ const isPackage = /(?:^|[/\\])node_modules[/\\]/.test(filename);
144
+ return isPackage ? inPackages : 'reject';
145
+ default:
146
+ (inPackages: empty);
147
+ throw new Error(
148
+ `invalid value for dynamic deps behavior: \`${inPackages}\``,
149
+ );
150
+ }
151
+ }
152
+
153
+ const minifyCode = async (
154
+ config: JsTransformerConfig,
155
+ projectRoot: string,
156
+ filename: string,
157
+ code: string,
158
+ source: string,
159
+ map: Array<MetroSourceMapSegmentTuple>,
160
+ reserved?: $ReadOnlyArray<string> = [],
161
+ ): Promise<{
162
+ code: string,
163
+ map: Array<MetroSourceMapSegmentTuple>,
164
+ ...
165
+ }> => {
166
+ const sourceMap = fromRawMappings([
167
+ {code, source, map, functionMap: null, path: filename},
168
+ ]).toMap(undefined, {});
169
+
170
+ const minify = getMinifier(config.minifierPath);
171
+
172
+ try {
173
+ const minified = minify({
174
+ code,
175
+ map: sourceMap,
176
+ filename,
177
+ reserved,
178
+ config: config.minifierConfig,
179
+ });
180
+
181
+ return {
182
+ code: minified.code,
183
+ map: minified.map
184
+ ? toBabelSegments(minified.map).map(toSegmentTuple)
185
+ : [],
186
+ };
187
+ } catch (error) {
188
+ if (error.constructor.name === 'JS_Parse_Error') {
189
+ throw new Error(
190
+ `${error.message} in file ${filename} at ${error.line}:${error.col}`,
191
+ );
192
+ }
193
+
194
+ throw error;
195
+ }
196
+ };
197
+
198
+ const compileToBytecode = (
199
+ code: string,
200
+ type: string,
201
+ options: HermesCompilerOptions,
202
+ ): HermesCompilerResult => {
203
+ if (type.startsWith('js/module')) {
204
+ const index = code.lastIndexOf(')');
205
+ code =
206
+ code.slice(0, index) +
207
+ ',$$METRO_D[0],$$METRO_D[1],$$METRO_D[2]' +
208
+ code.slice(index);
209
+ }
210
+ return HermesCompiler.compile(code, options);
211
+ };
212
+
213
+ class InvalidRequireCallError extends Error {
214
+ innerError: collectDependencies.InvalidRequireCallError;
215
+ filename: string;
216
+
217
+ constructor(
218
+ innerError: collectDependencies.InvalidRequireCallError,
219
+ filename: string,
220
+ ) {
221
+ super(`${filename}:${innerError.message}`);
222
+ this.innerError = innerError;
223
+ this.filename = filename;
224
+ }
225
+ }
226
+
227
+ module.exports = {
228
+ transform: async (
229
+ config: JsTransformerConfig,
230
+ projectRoot: string,
231
+ filename: string,
232
+ data: Buffer,
233
+ options: JsTransformOptions,
234
+ ): Promise<Result> => {
235
+ const sourceCode = data.toString('utf8');
236
+ let type = 'js/module';
237
+ let bytecodeType = 'bytecode/module';
238
+
239
+ if (options.type === 'asset') {
240
+ type = 'js/module/asset';
241
+ bytecodeType = 'bytecode/module/asset';
242
+ }
243
+ if (options.type === 'script') {
244
+ type = 'js/script';
245
+ bytecodeType = 'bytecode/script';
246
+ }
247
+
248
+ if (filename.endsWith('.json')) {
249
+ let code = JsFileWrapping.wrapJson(sourceCode, config.globalPrefix);
250
+ let map = [];
251
+
252
+ if (options.minify) {
253
+ ({map, code} = await minifyCode(
254
+ config,
255
+ projectRoot,
256
+ filename,
257
+ code,
258
+ sourceCode,
259
+ map,
260
+ ));
261
+ }
262
+
263
+ const output = [
264
+ {
265
+ data: {code, lineCount: countLines(code), map, functionMap: null},
266
+ type,
267
+ },
268
+ ];
269
+ if (options.runtimeBytecodeVersion) {
270
+ output.push({
271
+ data: (compileToBytecode(code, type, {
272
+ sourceURL: filename,
273
+ sourceMap: fromRawMappings([
274
+ {
275
+ code,
276
+ source: sourceCode,
277
+ map,
278
+ functionMap: null,
279
+ path: filename,
280
+ },
281
+ ]).toString(),
282
+ }): HermesCompilerResult),
283
+ type: bytecodeType,
284
+ });
285
+ }
286
+
287
+ return {
288
+ dependencies: [],
289
+ output,
290
+ };
291
+ }
292
+
293
+ // $FlowFixMe TODO t26372934 Plugin system
294
+ const transformer: Transformer<*> = require(config.babelTransformerPath);
295
+
296
+ const transformerArgs = {
297
+ filename,
298
+ options: {
299
+ ...options,
300
+ enableBabelRCLookup: config.enableBabelRCLookup,
301
+ enableBabelRuntime: config.enableBabelRuntime,
302
+ globalPrefix: config.globalPrefix,
303
+ // Inline requires are now performed at a secondary step. We cannot
304
+ // unfortunately remove it from the internal transformer, since this one
305
+ // is used by other tooling, and this would affect it.
306
+ inlineRequires: false,
307
+ nonInlinedRequires: [],
308
+ projectRoot,
309
+ publicPath: config.publicPath,
310
+ },
311
+ plugins: [],
312
+ src: sourceCode,
313
+ };
314
+
315
+ const transformResult =
316
+ type === 'js/module/asset'
317
+ ? {
318
+ ...(await assetTransformer.transform(
319
+ transformerArgs,
320
+ config.assetRegistryPath,
321
+ config.assetPlugins,
322
+ )),
323
+ functionMap: null,
324
+ }
325
+ : await transformer.transform(transformerArgs);
326
+
327
+ // Transformers can ouptut null ASTs (if they ignore the file). In that case
328
+ // we need to parse the module source code to get their AST.
329
+ let ast =
330
+ transformResult.ast ||
331
+ babylon.parse(sourceCode, {sourceType: 'unambiguous'});
332
+
333
+ const {importDefault, importAll} = generateImportNames(ast);
334
+
335
+ // Add "use strict" if the file was parsed as a module, and the directive did
336
+ // not exist yet.
337
+ const {directives} = ast.program;
338
+
339
+ if (
340
+ ast.program.sourceType === 'module' &&
341
+ directives.findIndex(d => d.value.value === 'use strict') === -1
342
+ ) {
343
+ directives.push(types.directive(types.directiveLiteral('use strict')));
344
+ }
345
+
346
+ // Perform the import-export transform (in case it's still needed), then
347
+ // fold requires and perform constant folding (if in dev).
348
+ const plugins = [];
349
+ const opts = {
350
+ ...options,
351
+ inlineableCalls: [importDefault, importAll],
352
+ importDefault,
353
+ importAll,
354
+ };
355
+
356
+ if (options.experimentalImportSupport) {
357
+ plugins.push([importExportPlugin, opts]);
358
+ }
359
+
360
+ if (options.inlineRequires) {
361
+ plugins.push([
362
+ inlineRequiresPlugin,
363
+ {
364
+ ...opts,
365
+ ignoredRequires: options.nonInlinedRequires,
366
+ },
367
+ ]);
368
+ }
369
+
370
+ if (!options.dev) {
371
+ plugins.push([constantFoldingPlugin, opts]);
372
+ }
373
+
374
+ plugins.push([inlinePlugin, opts]);
375
+
376
+ ast = nullthrows(
377
+ transformFromAstSync(ast, '', {
378
+ ast: true,
379
+ babelrc: false,
380
+ code: false,
381
+ configFile: false,
382
+ comments: false,
383
+ compact: false,
384
+ filename,
385
+ plugins,
386
+ sourceMaps: false,
387
+ }).ast,
388
+ );
389
+
390
+ let dependencyMapName = '';
391
+ let dependencies;
392
+ let wrappedAst;
393
+
394
+ // If the module to transform is a script (meaning that is not part of the
395
+ // dependency graph and it code will just be prepended to the bundle modules),
396
+ // we need to wrap it differently than a commonJS module (also, scripts do
397
+ // not have dependencies).
398
+ if (type === 'js/script') {
399
+ dependencies = [];
400
+ wrappedAst = JsFileWrapping.wrapPolyfill(ast);
401
+ } else {
402
+ try {
403
+ const opts = {
404
+ asyncRequireModulePath: config.asyncRequireModulePath,
405
+ dynamicRequires: getDynamicDepsBehavior(
406
+ config.dynamicDepsInPackages,
407
+ filename,
408
+ ),
409
+ inlineableCalls: [importDefault, importAll],
410
+ keepRequireNames: options.dev,
411
+ allowOptionalDependencies: config.allowOptionalDependencies,
412
+ };
413
+ ({ast, dependencies, dependencyMapName} = collectDependencies(
414
+ ast,
415
+ opts,
416
+ ));
417
+ } catch (error) {
418
+ if (error instanceof collectDependencies.InvalidRequireCallError) {
419
+ throw new InvalidRequireCallError(error, filename);
420
+ }
421
+ throw error;
422
+ }
423
+
424
+ ({ast: wrappedAst} = JsFileWrapping.wrapModule(
425
+ ast,
426
+ importDefault,
427
+ importAll,
428
+ dependencyMapName,
429
+ config.globalPrefix,
430
+ ));
431
+ }
432
+
433
+ const reserved =
434
+ options.minify && data.length <= config.optimizationSizeLimit
435
+ ? normalizePseudoGlobals(wrappedAst)
436
+ : [];
437
+
438
+ const result = generate(
439
+ wrappedAst,
440
+ {
441
+ comments: false,
442
+ compact: false,
443
+ filename,
444
+ retainLines: false,
445
+ sourceFileName: filename,
446
+ sourceMaps: true,
447
+ },
448
+ sourceCode,
449
+ );
450
+
451
+ let map = result.rawMappings ? result.rawMappings.map(toSegmentTuple) : [];
452
+ let code = result.code;
453
+
454
+ if (options.minify) {
455
+ ({map, code} = await minifyCode(
456
+ config,
457
+ projectRoot,
458
+ filename,
459
+ result.code,
460
+ sourceCode,
461
+ map,
462
+ reserved,
463
+ ));
464
+ }
465
+
466
+ const output = [
467
+ {
468
+ data: {
469
+ code,
470
+ lineCount: countLines(code),
471
+ map,
472
+ functionMap: transformResult.functionMap,
473
+ },
474
+ type,
475
+ },
476
+ ];
477
+
478
+ if (options.runtimeBytecodeVersion) {
479
+ output.push({
480
+ data: (compileToBytecode(code, type, {
481
+ sourceURL: filename,
482
+ sourceMap: fromRawMappings([
483
+ {code, source: sourceCode, map, functionMap: null, path: filename},
484
+ ]).toString(),
485
+ }): HermesCompilerResult),
486
+ type: bytecodeType,
487
+ });
488
+ }
489
+
490
+ return {
491
+ dependencies,
492
+ output,
493
+ };
494
+ },
495
+
496
+ getCacheKey: (config: JsTransformerConfig): string => {
497
+ const {babelTransformerPath, minifierPath, ...remainingConfig} = config;
498
+
499
+ const filesKey = getCacheKey([
500
+ require.resolve(babelTransformerPath),
501
+ require.resolve(minifierPath),
502
+ require.resolve('./utils/getMinifier'),
503
+ require.resolve('./utils/assetTransformer'),
504
+ require.resolve('metro/src/ModuleGraph/worker/collectDependencies'),
505
+ require.resolve('metro/src/ModuleGraph/worker/generateImportNames'),
506
+ require.resolve('metro/src/ModuleGraph/worker/JsFileWrapping'),
507
+ ...getTransformPluginCacheKeyFiles(),
508
+ ]);
509
+
510
+ const babelTransformer = require(babelTransformerPath);
511
+ return [
512
+ filesKey,
513
+ stableHash(remainingConfig).toString('hex'),
514
+ babelTransformer.getCacheKey ? babelTransformer.getCacheKey() : '',
515
+ ].join('$');
516
+ },
517
+ };
@@ -4,47 +4,85 @@
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
+ *
8
8
  * @format
9
9
  */
10
+ "use strict";
10
11
 
11
- 'use strict';
12
-
13
- const path = require('path');
12
+ function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
13
+ try {
14
+ var info = gen[key](arg);
15
+ var value = info.value;
16
+ } catch (error) {
17
+ reject(error);
18
+ return;
19
+ }
20
+ if (info.done) {
21
+ resolve(value);
22
+ } else {
23
+ Promise.resolve(value).then(_next, _throw);
24
+ }
25
+ }
14
26
 
15
- const {getAssetData} = require('metro/src/Assets');
16
- const {generateAssetCodeFileAst} = require('metro/src/Bundler/util');
27
+ function _asyncToGenerator(fn) {
28
+ return function() {
29
+ var self = this,
30
+ args = arguments;
31
+ return new Promise(function(resolve, reject) {
32
+ var gen = fn.apply(self, args);
33
+ function _next(value) {
34
+ asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);
35
+ }
36
+ function _throw(err) {
37
+ asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err);
38
+ }
39
+ _next(undefined);
40
+ });
41
+ };
42
+ }
17
43
 
18
- import type {Ast} from '@babel/core';
19
- import type {BabelTransformerArgs} from 'metro-babel-transformer';
44
+ const path = require("path");
20
45
 
21
- async function transform(
22
- {filename, options, src}: BabelTransformerArgs,
23
- assetRegistryPath: string,
24
- assetDataPlugins: $ReadOnlyArray<string>,
25
- ): Promise<{ast: Ast, ...}> {
26
- options = options || {
27
- platform: '',
28
- projectRoot: '',
29
- inlineRequires: false,
30
- minify: false,
31
- };
46
+ const _require = require("metro/src/Assets"),
47
+ getAssetData = _require.getAssetData;
32
48
 
33
- const absolutePath = path.resolve(options.projectRoot, filename);
49
+ const _require2 = require("metro/src/Bundler/util"),
50
+ generateAssetCodeFileAst = _require2.generateAssetCodeFileAst;
34
51
 
35
- const data = await getAssetData(
36
- absolutePath,
37
- filename,
38
- assetDataPlugins,
39
- options.platform,
40
- options.publicPath,
41
- );
52
+ function transform(_x, _x2, _x3) {
53
+ return _transform.apply(this, arguments);
54
+ }
42
55
 
43
- return {
44
- ast: generateAssetCodeFileAst(assetRegistryPath, data),
45
- };
56
+ function _transform() {
57
+ _transform = _asyncToGenerator(function*(
58
+ _ref,
59
+ assetRegistryPath,
60
+ assetDataPlugins
61
+ ) {
62
+ let filename = _ref.filename,
63
+ options = _ref.options,
64
+ src = _ref.src;
65
+ options = options || {
66
+ platform: "",
67
+ projectRoot: "",
68
+ inlineRequires: false,
69
+ minify: false
70
+ };
71
+ const absolutePath = path.resolve(options.projectRoot, filename);
72
+ const data = yield getAssetData(
73
+ absolutePath,
74
+ filename,
75
+ assetDataPlugins,
76
+ options.platform,
77
+ options.publicPath
78
+ );
79
+ return {
80
+ ast: generateAssetCodeFileAst(assetRegistryPath, data)
81
+ };
82
+ });
83
+ return _transform.apply(this, arguments);
46
84
  }
47
85
 
48
86
  module.exports = {
49
- transform,
87
+ transform
50
88
  };
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Copyright (c) Facebook, Inc. and its affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @flow
8
+ * @format
9
+ */
10
+
11
+ 'use strict';
12
+
13
+ const path = require('path');
14
+
15
+ const {getAssetData} = require('metro/src/Assets');
16
+ const {generateAssetCodeFileAst} = require('metro/src/Bundler/util');
17
+
18
+ import type {File} from '@babel/types';
19
+ import type {BabelTransformerArgs} from 'metro-babel-transformer';
20
+
21
+ async function transform(
22
+ {filename, options, src}: BabelTransformerArgs,
23
+ assetRegistryPath: string,
24
+ assetDataPlugins: $ReadOnlyArray<string>,
25
+ ): Promise<{ast: File, ...}> {
26
+ options = options || {
27
+ platform: '',
28
+ projectRoot: '',
29
+ inlineRequires: false,
30
+ minify: false,
31
+ };
32
+
33
+ const absolutePath = path.resolve(options.projectRoot, filename);
34
+
35
+ const data = await getAssetData(
36
+ absolutePath,
37
+ filename,
38
+ assetDataPlugins,
39
+ options.platform,
40
+ options.publicPath,
41
+ );
42
+
43
+ return {
44
+ ast: generateAssetCodeFileAst(assetRegistryPath, data),
45
+ };
46
+ }
47
+
48
+ module.exports = {
49
+ transform,
50
+ };
@@ -4,15 +4,12 @@
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 strict-local
7
+ *
8
8
  * @format
9
9
  */
10
+ "use strict";
10
11
 
11
- 'use strict';
12
-
13
- import type {Minifier} from '../index.js';
14
-
15
- function getMinifier(minifierPath: string): Minifier {
12
+ function getMinifier(minifierPath) {
16
13
  // Note: minifierPath should be an absolute path OR a module name here!
17
14
  // The options allow relative paths but they HAVE to be normalized at
18
15
  // any entry point that accepts them...
@@ -24,7 +21,7 @@ function getMinifier(minifierPath: string): Minifier {
24
21
  'A problem occurred while trying to fetch the minifier. Path: "' +
25
22
  minifierPath +
26
23
  '", error message: ' +
27
- e.message,
24
+ e.message
28
25
  );
29
26
  }
30
27
  }