metro 0.71.1 → 0.71.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "metro",
3
- "version": "0.71.1",
3
+ "version": "0.71.2",
4
4
  "description": "🚇 The JavaScript bundler for React Native.",
5
5
  "main": "src/index.js",
6
6
  "bin": "src/cli.js",
@@ -31,27 +31,27 @@
31
31
  "error-stack-parser": "^2.0.6",
32
32
  "fs-extra": "^1.0.0",
33
33
  "graceful-fs": "^4.2.4",
34
- "hermes-parser": "0.6.0",
34
+ "hermes-parser": "0.8.0",
35
35
  "image-size": "^0.6.0",
36
36
  "invariant": "^2.2.4",
37
37
  "jest-worker": "^27.2.0",
38
38
  "lodash.throttle": "^4.1.1",
39
- "metro-babel-transformer": "0.71.1",
40
- "metro-cache": "0.71.1",
41
- "metro-cache-key": "0.71.1",
42
- "metro-config": "0.71.1",
43
- "metro-core": "0.71.1",
44
- "metro-file-map": "0.71.1",
45
- "metro-hermes-compiler": "0.71.1",
46
- "metro-inspector-proxy": "0.71.1",
47
- "metro-minify-uglify": "0.71.1",
48
- "metro-react-native-babel-preset": "0.71.1",
49
- "metro-resolver": "0.71.1",
50
- "metro-runtime": "0.71.1",
51
- "metro-source-map": "0.71.1",
52
- "metro-symbolicate": "0.71.1",
53
- "metro-transform-plugins": "0.71.1",
54
- "metro-transform-worker": "0.71.1",
39
+ "metro-babel-transformer": "0.71.2",
40
+ "metro-cache": "0.71.2",
41
+ "metro-cache-key": "0.71.2",
42
+ "metro-config": "0.71.2",
43
+ "metro-core": "0.71.2",
44
+ "metro-file-map": "0.71.2",
45
+ "metro-hermes-compiler": "0.71.2",
46
+ "metro-inspector-proxy": "0.71.2",
47
+ "metro-minify-uglify": "0.71.2",
48
+ "metro-react-native-babel-preset": "0.71.2",
49
+ "metro-resolver": "0.71.2",
50
+ "metro-runtime": "0.71.2",
51
+ "metro-source-map": "0.71.2",
52
+ "metro-symbolicate": "0.71.2",
53
+ "metro-transform-plugins": "0.71.2",
54
+ "metro-transform-worker": "0.71.2",
55
55
  "mime-types": "^2.1.27",
56
56
  "node-fetch": "^2.2.0",
57
57
  "nullthrows": "^1.1.1",
@@ -70,10 +70,10 @@
70
70
  "babel-jest": "^26.6.3",
71
71
  "dedent": "^0.7.0",
72
72
  "jest-snapshot": "^26.5.2",
73
- "metro-babel-register": "0.71.1",
74
- "metro-memory-fs": "0.71.1",
75
- "metro-react-native-babel-preset": "0.71.1",
76
- "metro-react-native-babel-transformer": "0.71.1",
73
+ "metro-babel-register": "0.71.2",
74
+ "metro-memory-fs": "0.71.2",
75
+ "metro-react-native-babel-preset": "0.71.2",
76
+ "metro-react-native-babel-transformer": "0.71.2",
77
77
  "stack-trace": "^0.0.10"
78
78
  },
79
79
  "license": "MIT"
package/src/Bundler.js CHANGED
@@ -13,8 +13,6 @@ const Transformer = require("./DeltaBundler/Transformer");
13
13
 
14
14
  const DependencyGraph = require("./node-haste/DependencyGraph");
15
15
 
16
- const { EventEmitter } = require("events");
17
-
18
16
  class Bundler {
19
17
  constructor(config, options) {
20
18
  this._depGraph = new DependencyGraph(config, options);
@@ -13,10 +13,10 @@
13
13
  import type {TransformResultWithSource} from './DeltaBundler';
14
14
  import type {TransformOptions} from './DeltaBundler/Worker';
15
15
  import type {ConfigT} from 'metro-config/src/configTypes.flow';
16
+ import type EventEmitter from 'events';
16
17
 
17
18
  const Transformer = require('./DeltaBundler/Transformer');
18
19
  const DependencyGraph = require('./node-haste/DependencyGraph');
19
- const {EventEmitter} = require('events');
20
20
 
21
21
  export type BundlerOptions = $ReadOnly<{
22
22
  hasReducedPerformance?: boolean,
@@ -29,6 +29,12 @@
29
29
  */
30
30
  "use strict";
31
31
 
32
+ var _CountingSet = _interopRequireDefault(require("../lib/CountingSet"));
33
+
34
+ function _interopRequireDefault(obj) {
35
+ return obj && obj.__esModule ? obj : { default: obj };
36
+ }
37
+
32
38
  const invariant = require("invariant");
33
39
 
34
40
  const nullthrows = require("nullthrows"); // TODO: Convert to a Flow enum
@@ -181,7 +187,8 @@ async function processModule(path, graph, delta, options) {
181
187
  options
182
188
  );
183
189
  const previousModule = graph.dependencies.get(path) || {
184
- inverseDependencies: delta.earlyInverseDependencies.get(path) || new Set(),
190
+ inverseDependencies:
191
+ delta.earlyInverseDependencies.get(path) || new _CountingSet.default(),
185
192
  path,
186
193
  };
187
194
  const previousDependencies = previousModule.dependencies || new Map(); // Update the module information.
@@ -298,7 +305,7 @@ async function addDependency(
298
305
  delta.modified.delete(path);
299
306
  }
300
307
 
301
- delta.earlyInverseDependencies.set(path, new Set([parentModule.path]));
308
+ delta.earlyInverseDependencies.set(path, new _CountingSet.default());
302
309
  options.onDependencyAdd();
303
310
  module = await processModule(path, graph, delta, options);
304
311
  options.onDependencyAdded();
@@ -39,6 +39,8 @@ import type {
39
39
  TransformResultDependency,
40
40
  } from './types.flow';
41
41
 
42
+ import CountingSet from '../lib/CountingSet';
43
+
42
44
  const invariant = require('invariant');
43
45
  const nullthrows = require('nullthrows');
44
46
 
@@ -104,7 +106,7 @@ type Delta = $ReadOnly<{
104
106
 
105
107
  // A place to temporarily track inverse dependencies for a module while it is
106
108
  // being processed but has not been added to `graph.dependencies` yet.
107
- earlyInverseDependencies: Map<string, Set<string>>,
109
+ earlyInverseDependencies: Map<string, CountingSet<string>>,
108
110
  }>;
109
111
 
110
112
  type InternalOptions<T> = $ReadOnly<{
@@ -277,7 +279,8 @@ async function processModule<T>(
277
279
  );
278
280
 
279
281
  const previousModule = graph.dependencies.get(path) || {
280
- inverseDependencies: delta.earlyInverseDependencies.get(path) || new Set(),
282
+ inverseDependencies:
283
+ delta.earlyInverseDependencies.get(path) || new CountingSet(),
281
284
  path,
282
285
  };
283
286
  const previousDependencies = previousModule.dependencies || new Map();
@@ -397,7 +400,7 @@ async function addDependency<T>(
397
400
  delta.added.add(path);
398
401
  delta.modified.delete(path);
399
402
  }
400
- delta.earlyInverseDependencies.set(path, new Set([parentModule.path]));
403
+ delta.earlyInverseDependencies.set(path, new CountingSet());
401
404
 
402
405
  options.onDependencyAdd();
403
406
  module = await processModule(path, graph, delta, options);
@@ -8,3 +8,9 @@
8
8
  * @format
9
9
  */
10
10
  "use strict";
11
+
12
+ var _CountingSet = _interopRequireDefault(require("../lib/CountingSet"));
13
+
14
+ function _interopRequireDefault(obj) {
15
+ return obj && obj.__esModule ? obj : { default: obj };
16
+ }
@@ -10,9 +10,12 @@
10
10
 
11
11
  'use strict';
12
12
 
13
+ import type {RequireContextParams} from '../ModuleGraph/worker/collectDependencies';
13
14
  import type {PrivateState} from './graphOperations';
14
15
  import type {JsTransformOptions} from 'metro-transform-worker';
15
16
 
17
+ import CountingSet from '../lib/CountingSet';
18
+
16
19
  export type MixedOutput = {
17
20
  +data: mixed,
18
21
  +type: string,
@@ -48,6 +51,9 @@ export type TransformResultDependency = {
48
51
  +isOptional?: boolean,
49
52
 
50
53
  +locs: $ReadOnlyArray<BabelSourceLocation>,
54
+
55
+ /** Context for requiring a collection of modules. */
56
+ +contextParams?: RequireContextParams,
51
57
  },
52
58
  };
53
59
 
@@ -58,7 +64,7 @@ export type Dependency = {
58
64
 
59
65
  export type Module<T = MixedOutput> = {
60
66
  +dependencies: Map<string, Dependency>,
61
- +inverseDependencies: Set<string>,
67
+ +inverseDependencies: CountingSet<string>,
62
68
  +output: $ReadOnlyArray<T>,
63
69
  +path: string,
64
70
  +getSource: () => Buffer,
@@ -11,8 +11,6 @@
11
11
 
12
12
  const DeltaCalculator = require("./DeltaBundler/DeltaCalculator");
13
13
 
14
- const { EventEmitter } = require("events");
15
-
16
14
  /**
17
15
  * `DeltaBundler` uses the `DeltaTransformer` to build bundle deltas. This
18
16
  * module handles all the transformer instances so it can support multiple
@@ -18,9 +18,9 @@ import type {
18
18
  MixedOutput,
19
19
  Options,
20
20
  } from './DeltaBundler/types.flow';
21
+ import type EventEmitter from 'events';
21
22
 
22
23
  const DeltaCalculator = require('./DeltaBundler/DeltaCalculator');
23
- const {EventEmitter} = require('events');
24
24
 
25
25
  export type {
26
26
  DeltaResult,
package/src/HmrServer.js CHANGED
@@ -11,8 +11,6 @@
11
11
 
12
12
  const hmrJSBundle = require("./DeltaBundler/Serializers/hmrJSBundle");
13
13
 
14
- const IncrementalBundler = require("./IncrementalBundler");
15
-
16
14
  const GraphNotFoundError = require("./IncrementalBundler/GraphNotFoundError");
17
15
 
18
16
  const RevisionNotFoundError = require("./IncrementalBundler/RevisionNotFoundError");
@@ -10,7 +10,7 @@
10
10
 
11
11
  'use strict';
12
12
 
13
- import type {RevisionId} from './IncrementalBundler';
13
+ import type IncrementalBundler, {RevisionId} from './IncrementalBundler';
14
14
  import type {ConfigT} from 'metro-config/src/configTypes.flow';
15
15
  import type {
16
16
  HmrClientMessage,
@@ -20,7 +20,6 @@ import type {
20
20
  } from 'metro-runtime/src/modules/types.flow';
21
21
 
22
22
  const hmrJSBundle = require('./DeltaBundler/Serializers/hmrJSBundle');
23
- const IncrementalBundler = require('./IncrementalBundler');
24
23
  const GraphNotFoundError = require('./IncrementalBundler/GraphNotFoundError');
25
24
  const RevisionNotFoundError = require('./IncrementalBundler/RevisionNotFoundError');
26
25
  const debounceAsyncQueue = require('./lib/debounceAsyncQueue');
@@ -8,4 +8,3 @@
8
8
  * @format
9
9
  */
10
10
  "use strict";
11
- "use strict";
@@ -10,8 +10,6 @@
10
10
 
11
11
  'use strict';
12
12
 
13
- 'use strict';
14
-
15
13
  type ModuleID = string;
16
14
  export type Path = string;
17
15
  type Platform = string;
@@ -63,6 +61,7 @@ type HasteMapOptions = {
63
61
  preferNativePlatform: true,
64
62
  };
65
63
 
64
+ // eslint-disable-next-line no-unused-vars
66
65
  declare class HasteMap {
67
66
  // node-haste/DependencyGraph/HasteMap.js
68
67
  build(): Promise<Object>;
@@ -53,6 +53,7 @@ function collectDependencies(ast, options) {
53
53
  dynamicRequires: options.dynamicRequires,
54
54
  keepRequireNames: options.keepRequireNames,
55
55
  allowOptionalDependencies: options.allowOptionalDependencies,
56
+ unstable_allowRequireContext: options.unstable_allowRequireContext,
56
57
  };
57
58
  const visitor = {
58
59
  CallExpression(path, state) {
@@ -97,6 +98,22 @@ function collectDependencies(ast, options) {
97
98
  splitCondition: args[1],
98
99
  });
99
100
  return;
101
+ } // Match `require.context`
102
+
103
+ if (
104
+ // Feature gate, defaults to `false`.
105
+ state.unstable_allowRequireContext &&
106
+ callee.type === "MemberExpression" && // `require`
107
+ callee.object.type === "Identifier" &&
108
+ callee.object.name === "require" && // `context`
109
+ callee.property.type === "Identifier" &&
110
+ callee.property.name === "context" &&
111
+ !callee.computed && // Ensure `require` refers to the global and not something else.
112
+ !path.scope.getBinding("require")
113
+ ) {
114
+ processRequireContextCall(path, state);
115
+ visited.add(path.node);
116
+ return;
100
117
  }
101
118
 
102
119
  if (
@@ -148,6 +165,146 @@ function collectDependencies(ast, options) {
148
165
  dependencyMapName: nullthrows(state.dependencyMapIdentifier).name,
149
166
  };
150
167
  }
168
+ /** Extract args passed to the `require.context` method. */
169
+
170
+ function getRequireContextArgs(path) {
171
+ const args = path.get("arguments");
172
+ let directory;
173
+
174
+ if (!Array.isArray(args) || args.length < 1) {
175
+ throw new InvalidRequireCallError(path);
176
+ } else {
177
+ const result = args[0].evaluate();
178
+
179
+ if (result.confident && typeof result.value === "string") {
180
+ directory = result.value;
181
+ } else {
182
+ var _result$deopt;
183
+
184
+ throw new InvalidRequireCallError(
185
+ (_result$deopt = result.deopt) !== null && _result$deopt !== void 0
186
+ ? _result$deopt
187
+ : args[0],
188
+ "First argument of `require.context` should be a string denoting the directory to require."
189
+ );
190
+ }
191
+ } // Default to requiring through all directories.
192
+
193
+ let recursive = true;
194
+
195
+ if (args.length > 1) {
196
+ const result = args[1].evaluate();
197
+
198
+ if (result.confident && typeof result.value === "boolean") {
199
+ recursive = result.value;
200
+ } else if (!(result.confident && typeof result.value === "undefined")) {
201
+ var _result$deopt2;
202
+
203
+ throw new InvalidRequireCallError(
204
+ (_result$deopt2 = result.deopt) !== null && _result$deopt2 !== void 0
205
+ ? _result$deopt2
206
+ : args[1],
207
+ "Second argument of `require.context` should be an optional boolean indicating if files should be imported recursively or not."
208
+ );
209
+ }
210
+ } // Default to all files.
211
+
212
+ let filter = {
213
+ pattern: ".*",
214
+ flags: "",
215
+ };
216
+
217
+ if (args.length > 2) {
218
+ // evaluate() to check for undefined (because it's technically a scope lookup)
219
+ // but check the AST for the regex literal, since evaluate() doesn't do regex.
220
+ const result = args[2].evaluate();
221
+ const argNode = args[2].node;
222
+
223
+ if (argNode.type === "RegExpLiteral") {
224
+ // TODO: Handle `new RegExp(...)` -- `argNode.type === 'NewExpression'`
225
+ filter = {
226
+ pattern: argNode.pattern,
227
+ flags: argNode.flags || "",
228
+ };
229
+ } else if (!(result.confident && typeof result.value === "undefined")) {
230
+ throw new InvalidRequireCallError(
231
+ args[2],
232
+ `Third argument of \`require.context\` should be an optional RegExp pattern matching all of the files to import, instead found node of type: ${argNode.type}.`
233
+ );
234
+ }
235
+ } // Default to `sync`.
236
+
237
+ let mode = "sync";
238
+
239
+ if (args.length > 3) {
240
+ const result = args[3].evaluate();
241
+
242
+ if (result.confident && typeof result.value === "string") {
243
+ mode = getContextMode(args[3], result.value);
244
+ } else if (!(result.confident && typeof result.value === "undefined")) {
245
+ var _result$deopt3;
246
+
247
+ throw new InvalidRequireCallError(
248
+ (_result$deopt3 = result.deopt) !== null && _result$deopt3 !== void 0
249
+ ? _result$deopt3
250
+ : args[3],
251
+ 'Fourth argument of `require.context` should be an optional string "mode" denoting how the modules will be resolved.'
252
+ );
253
+ }
254
+ }
255
+
256
+ if (args.length > 4) {
257
+ throw new InvalidRequireCallError(
258
+ path,
259
+ `Too many arguments provided to \`require.context\` call. Expected 4, got: ${args.length}`
260
+ );
261
+ }
262
+
263
+ return [
264
+ directory,
265
+ {
266
+ recursive,
267
+ filter,
268
+ mode,
269
+ },
270
+ ];
271
+ }
272
+
273
+ function getContextMode(path, mode) {
274
+ if (
275
+ mode === "sync" ||
276
+ mode === "eager" ||
277
+ mode === "lazy" ||
278
+ mode === "lazy-once"
279
+ ) {
280
+ return mode;
281
+ }
282
+
283
+ throw new InvalidRequireCallError(
284
+ path,
285
+ `require.context "${mode}" mode is not supported. Expected one of: sync, eager, lazy, lazy-once`
286
+ );
287
+ }
288
+
289
+ function processRequireContextCall(path, state) {
290
+ const [directory, contextParams] = getRequireContextArgs(path);
291
+ const transformer = state.dependencyTransformer;
292
+ const dep = registerDependency(
293
+ state,
294
+ {
295
+ // We basically want to "import" every file in a folder and then filter them out with the given `filter` RegExp.
296
+ name: directory,
297
+ // Capture the matching context
298
+ contextParams,
299
+ asyncType: null,
300
+ optional: isOptionalDependency(directory, path, state),
301
+ },
302
+ path
303
+ ); // require() the generated module representing this context
304
+
305
+ path.get("callee").replaceWith(types.identifier("require"));
306
+ transformer.transformSyncRequire(path, dep, state);
307
+ }
151
308
 
152
309
  function collectImports(path, state) {
153
310
  if (path.node.source) {
@@ -309,10 +466,15 @@ function getModuleNameFromCallArgs(path) {
309
466
  collectDependencies.getModuleNameFromCallArgs = getModuleNameFromCallArgs;
310
467
 
311
468
  class InvalidRequireCallError extends Error {
312
- constructor({ node }) {
469
+ constructor({ node }, message) {
313
470
  const line = node.loc && node.loc.start && node.loc.start.line;
314
471
  super(
315
- `Invalid call at line ${line || "<unknown>"}: ${generate(node).code}`
472
+ [
473
+ `Invalid call at line ${line || "<unknown>"}: ${generate(node).code}`,
474
+ message,
475
+ ]
476
+ .filter(Boolean)
477
+ .join("\n")
316
478
  );
317
479
  }
318
480
  }
@@ -348,9 +510,11 @@ const makeJSResourceTemplate = template.statement(`
348
510
  const DefaultDependencyTransformer = {
349
511
  transformSyncRequire(path, dependency, state) {
350
512
  const moduleIDExpression = createModuleIDExpression(dependency, state);
351
- path.node.arguments = state.keepRequireNames
352
- ? [moduleIDExpression, types.stringLiteral(dependency.name)]
353
- : [moduleIDExpression];
513
+ path.node.arguments = [moduleIDExpression]; // Always add the debug name argument last
514
+
515
+ if (state.keepRequireNames) {
516
+ path.node.arguments.push(types.stringLiteral(dependency.name));
517
+ }
354
518
  },
355
519
 
356
520
  transformImportCall(path, dependency, state) {
@@ -420,12 +584,51 @@ function createModuleIDExpression(dependency, state) {
420
584
  function createModuleNameLiteral(dependency) {
421
585
  return types.stringLiteral(dependency.name);
422
586
  }
587
+ /**
588
+ * Given an import qualifier, return a key used to register the dependency.
589
+ * Generally this return the `ImportQualifier.name` property, but in the case
590
+ * of `require.context` more attributes can be appended to distinguish various combinations that would otherwise conflict.
591
+ *
592
+ * For example, the following case would have collision issues if they all utilized the `name` property:
593
+ * ```
594
+ * require('./foo');
595
+ * require.context('./foo');
596
+ * require.context('./foo', true, /something/);
597
+ * require.context('./foo', false, /something/);
598
+ * require.context('./foo', false, /something/, 'lazy');
599
+ * ```
600
+ *
601
+ * This method should be utilized by `registerDependency`.
602
+ */
603
+
604
+ function getKeyForDependency(qualifier) {
605
+ let key = qualifier.name;
606
+ const { contextParams } = qualifier; // Add extra qualifiers when using `require.context` to prevent collisions.
607
+
608
+ if (contextParams) {
609
+ // NOTE(EvanBacon): Keep this synchronized with `RequireContextParams`, if any other properties are added
610
+ // then this key algorithm should be updated to account for those properties.
611
+ // Example: `./directory__true__/foobar/m__lazy`
612
+ key += [
613
+ "",
614
+ "context",
615
+ String(contextParams.recursive),
616
+ String(contextParams.filter.pattern),
617
+ String(contextParams.filter.flags),
618
+ contextParams.mode, // Join together and append to the name:
619
+ ].join("__");
620
+ }
621
+
622
+ return key;
623
+ }
423
624
 
424
625
  class DefaultModuleDependencyRegistry {
425
626
  _dependencies = new Map();
426
627
 
427
628
  registerDependency(qualifier) {
428
- let dependency = this._dependencies.get(qualifier.name);
629
+ const key = getKeyForDependency(qualifier);
630
+
631
+ let dependency = this._dependencies.get(key);
429
632
 
430
633
  if (dependency == null) {
431
634
  const newDependency = {
@@ -439,15 +642,19 @@ class DefaultModuleDependencyRegistry {
439
642
  newDependency.isOptional = true;
440
643
  }
441
644
 
645
+ if (qualifier.contextParams) {
646
+ newDependency.contextParams = qualifier.contextParams;
647
+ }
648
+
442
649
  dependency = newDependency;
443
650
 
444
- this._dependencies.set(qualifier.name, dependency);
651
+ this._dependencies.set(key, dependency);
445
652
  } else {
446
653
  const original = dependency;
447
654
  dependency = collapseDependencies(original, qualifier);
448
655
 
449
656
  if (original !== dependency) {
450
- this._dependencies.set(qualifier.name, dependency);
657
+ this._dependencies.set(key, dependency);
451
658
  }
452
659
  }
453
660