metro-transform-worker 0.70.2 → 0.71.1

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.real/index.js DELETED
@@ -1,754 +0,0 @@
1
- /**
2
- * Copyright (c) Meta Platforms, Inc. and 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 strict-local
8
- * @format
9
- */
10
-
11
- 'use strict';
12
-
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';
35
-
36
- const getMinifier = require('./utils/getMinifier');
37
- const {transformFromAstSync} = require('@babel/core');
38
- const generate = require('@babel/generator').default;
39
- const babylon = require('@babel/parser');
40
- const types = require('@babel/types');
41
- const {stableHash} = require('metro-cache');
42
- const getCacheKey = require('metro-cache-key');
43
- const HermesCompiler = require('metro-hermes-compiler');
44
- const {
45
- fromRawMappings,
46
- toBabelSegments,
47
- toSegmentTuple,
48
- } = require('metro-source-map');
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');
57
-
58
- type MinifierConfig = $ReadOnly<{[string]: mixed, ...}>;
59
-
60
- export type MinifierOptions = {
61
- code: string,
62
- map: ?BasicSourceMap,
63
- filename: string,
64
- reserved: $ReadOnlyArray<string>,
65
- config: MinifierConfig,
66
- ...
67
- };
68
-
69
- export type MinifierResult = {
70
- code: string,
71
- map?: BasicSourceMap,
72
- ...
73
- };
74
-
75
- export type Minifier = MinifierOptions =>
76
- | MinifierResult
77
- | Promise<MinifierResult>;
78
-
79
- export type Type = 'script' | 'module' | 'asset';
80
-
81
- export type JsTransformerConfig = $ReadOnly<{|
82
- assetPlugins: $ReadOnlyArray<string>,
83
- assetRegistryPath: string,
84
- asyncRequireModulePath: string,
85
- babelTransformerPath: string,
86
- dynamicDepsInPackages: DynamicRequiresBehavior,
87
- enableBabelRCLookup: boolean,
88
- enableBabelRuntime: boolean | string,
89
- experimentalImportBundleSupport: boolean,
90
- globalPrefix: string,
91
- hermesParser: boolean,
92
- minifierConfig: MinifierConfig,
93
- minifierPath: string,
94
- optimizationSizeLimit: number,
95
- publicPath: string,
96
- allowOptionalDependencies: AllowOptionalDependencies,
97
- unstable_collectDependenciesPath: string,
98
- unstable_dependencyMapReservedName: ?string,
99
- unstable_disableModuleWrapping: boolean,
100
- unstable_disableNormalizePseudoGlobals: boolean,
101
- unstable_compactOutput: boolean,
102
- |}>;
103
-
104
- export type {CustomTransformOptions} from 'metro-babel-transformer';
105
-
106
- export type JsTransformOptions = $ReadOnly<{|
107
- customTransformOptions?: CustomTransformOptions,
108
- dev: boolean,
109
- experimentalImportSupport?: boolean,
110
- hot: boolean,
111
- inlinePlatform: boolean,
112
- inlineRequires: boolean,
113
- minify: boolean,
114
- nonInlinedRequires?: $ReadOnlyArray<string>,
115
- platform: ?string,
116
- runtimeBytecodeVersion: ?number,
117
- type: Type,
118
- unstable_disableES6Transforms?: boolean,
119
- unstable_transformProfile: TransformProfile,
120
- |}>;
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
-
160
- export type JsOutput = $ReadOnly<{|
161
- data: $ReadOnly<{|
162
- code: string,
163
- lineCount: number,
164
- map: Array<MetroSourceMapSegmentTuple>,
165
- functionMap: ?FBSourceFunctionMap,
166
- |}>,
167
- type: JSFileType,
168
- |}>;
169
-
170
- export type BytecodeOutput = $ReadOnly<{|
171
- data: HermesCompilerResult,
172
- type: BytecodeFileType,
173
- |}>;
174
-
175
- type DependencySplitCondition = $PropertyType<
176
- $PropertyType<TransformResultDependency, 'data'>,
177
- 'splitCondition',
178
- >;
179
-
180
- type TransformResponse = $ReadOnly<{
181
- dependencies: $ReadOnlyArray<TransformResultDependency>,
182
- output: $ReadOnlyArray<JsOutput | BytecodeOutput>,
183
- }>;
184
-
185
- function getDynamicDepsBehavior(
186
- inPackages: DynamicRequiresBehavior,
187
- filename: string,
188
- ): DynamicRequiresBehavior {
189
- switch (inPackages) {
190
- case 'reject':
191
- return 'reject';
192
- case 'throwAtRuntime':
193
- const isPackage = /(?:^|[/\\])node_modules[/\\]/.test(filename);
194
- return isPackage ? inPackages : 'reject';
195
- default:
196
- (inPackages: empty);
197
- throw new Error(
198
- `invalid value for dynamic deps behavior: \`${inPackages}\``,
199
- );
200
- }
201
- }
202
-
203
- const minifyCode = async (
204
- config: JsTransformerConfig,
205
- projectRoot: string,
206
- filename: string,
207
- code: string,
208
- source: string,
209
- map: Array<MetroSourceMapSegmentTuple>,
210
- reserved?: $ReadOnlyArray<string> = [],
211
- ): Promise<{
212
- code: string,
213
- map: Array<MetroSourceMapSegmentTuple>,
214
- ...
215
- }> => {
216
- const sourceMap = fromRawMappings([
217
- {code, source, map, functionMap: null, path: filename},
218
- ]).toMap(undefined, {});
219
-
220
- const minify = getMinifier(config.minifierPath);
221
-
222
- try {
223
- const minified = await minify({
224
- code,
225
- map: sourceMap,
226
- filename,
227
- reserved,
228
- config: config.minifierConfig,
229
- });
230
-
231
- return {
232
- code: minified.code,
233
- map: minified.map
234
- ? toBabelSegments(minified.map).map(toSegmentTuple)
235
- : [],
236
- };
237
- } catch (error) {
238
- if (error.constructor.name === 'JS_Parse_Error') {
239
- throw new Error(
240
- `${error.message} in file ${filename} at ${error.line}:${error.col}`,
241
- );
242
- }
243
-
244
- throw error;
245
- }
246
- };
247
-
248
- const compileToBytecode = (
249
- rawCode: string,
250
- type: string,
251
- options: HermesCompilerOptions,
252
- ): HermesCompilerResult => {
253
- let code = rawCode;
254
- if (type.startsWith('js/module')) {
255
- const index = code.lastIndexOf(')');
256
- code =
257
- code.slice(0, index) +
258
- ',$$METRO_D[0],$$METRO_D[1],$$METRO_D[2]' +
259
- code.slice(index);
260
- }
261
- return HermesCompiler.compile(code, options);
262
- };
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
-
272
- class InvalidRequireCallError extends Error {
273
- innerError: InternalInvalidRequireCallError;
274
- filename: string;
275
-
276
- constructor(innerError: InternalInvalidRequireCallError, filename: string) {
277
- super(`${filename}:${innerError.message}`);
278
- this.innerError = innerError;
279
- this.filename = filename;
280
- }
281
- }
282
-
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'});
290
-
291
- const {importDefault, importAll} = generateImportNames(ast);
292
-
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;
296
-
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
- }
304
-
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
- }
318
-
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,
326
- },
327
- ]);
328
- }
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).
355
- ast = nullthrows(
356
- transformFromAstSync(ast, '', {
357
- ast: true,
358
- babelrc: false,
359
- code: false,
360
- configFile: false,
361
- comments: false,
362
- filename: file.filename,
363
- plugins: [
364
- [metroTransformPlugins.constantFoldingPlugin, babelPluginOpts],
365
- ],
366
- sourceMaps: false,
367
- cloneInputAst: false,
368
- }).ast,
369
- );
370
- }
371
-
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);
406
- }
407
- throw error;
408
- }
409
-
410
- if (config.unstable_disableModuleWrapping === true) {
411
- wrappedAst = ast;
412
- } else {
413
- ({ast: wrappedAst} = JsFileWrapping.wrapModule(
414
- ast,
415
- importDefault,
416
- importAll,
417
- dependencyMapName,
418
- config.globalPrefix,
419
- ));
420
- }
421
- }
422
-
423
- const minify =
424
- options.minify &&
425
- options.unstable_transformProfile !== 'hermes-canary' &&
426
- options.unstable_transformProfile !== 'hermes-stable';
427
-
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
- }),
441
- );
442
- }
443
-
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
- }
471
-
472
- const output = [
473
- {
474
- data: {
475
- code,
476
- lineCount: countLines(code),
477
- map,
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
- }
693
- }
694
-
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);
704
- }
705
-
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,
723
- };
724
-
725
- return await transformJSWithBabel(file, context);
726
- },
727
-
728
- getCacheKey: (config: JsTransformerConfig): string => {
729
- const {
730
- babelTransformerPath,
731
- minifierPath,
732
- unstable_collectDependenciesPath,
733
- ...remainingConfig
734
- } = config;
735
-
736
- const filesKey = getCacheKey([
737
- require.resolve(babelTransformerPath),
738
- require.resolve(minifierPath),
739
- require.resolve('./utils/getMinifier'),
740
- require.resolve('./utils/assetTransformer'),
741
- require.resolve(unstable_collectDependenciesPath),
742
- require.resolve('metro/src/ModuleGraph/worker/generateImportNames'),
743
- require.resolve('metro/src/ModuleGraph/worker/JsFileWrapping'),
744
- ...metroTransformPlugins.getTransformPluginCacheKeyFiles(),
745
- ]);
746
-
747
- const babelTransformer = require(babelTransformerPath);
748
- return [
749
- filesKey,
750
- stableHash(remainingConfig).toString('hex'),
751
- babelTransformer.getCacheKey ? babelTransformer.getCacheKey() : '',
752
- ].join('$');
753
- },
754
- };