metro 0.71.0 → 0.71.3

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.
Files changed (57) hide show
  1. package/package.json +23 -22
  2. package/src/Bundler.js +0 -2
  3. package/src/Bundler.js.flow +1 -1
  4. package/src/DeltaBundler/DeltaCalculator.js +2 -0
  5. package/src/DeltaBundler/DeltaCalculator.js.flow +2 -0
  6. package/src/DeltaBundler/Worker.flow.js +78 -0
  7. package/src/DeltaBundler/Worker.flow.js.flow +121 -0
  8. package/src/DeltaBundler/Worker.js +8 -66
  9. package/src/DeltaBundler/Worker.js.flow +8 -107
  10. package/src/DeltaBundler/WorkerFarm.js.flow +2 -2
  11. package/src/DeltaBundler/__fixtures__/hasteImpl.js +4 -0
  12. package/src/DeltaBundler/graphOperations.js +382 -222
  13. package/src/DeltaBundler/graphOperations.js.flow +404 -232
  14. package/src/DeltaBundler/types.flow.js +6 -0
  15. package/src/DeltaBundler/types.flow.js.flow +7 -1
  16. package/src/DeltaBundler.js +0 -2
  17. package/src/DeltaBundler.js.flow +1 -1
  18. package/src/HmrServer.js +0 -2
  19. package/src/HmrServer.js.flow +1 -2
  20. package/src/ModuleGraph/node-haste/node-haste.flow.js +0 -1
  21. package/src/ModuleGraph/node-haste/node-haste.flow.js.flow +1 -2
  22. package/src/ModuleGraph/node-haste/node-haste.js.flow +7 -2
  23. package/src/ModuleGraph/output/util.js.flow +2 -2
  24. package/src/ModuleGraph/silent-console.js +5 -4
  25. package/src/ModuleGraph/silent-console.js.flow +8 -4
  26. package/src/ModuleGraph/worker/collectDependencies.js +215 -8
  27. package/src/ModuleGraph/worker/collectDependencies.js.flow +233 -13
  28. package/src/Server.js +2 -0
  29. package/src/Server.js.flow +9 -2
  30. package/src/cli.js +5 -0
  31. package/src/cli.js.flow +5 -0
  32. package/src/commands/build.js +4 -3
  33. package/src/commands/build.js.flow +3 -1
  34. package/src/commands/serve.js +3 -3
  35. package/src/commands/serve.js.flow +3 -1
  36. package/src/index.flow.js +392 -0
  37. package/src/index.flow.js.flow +480 -0
  38. package/src/index.js +8 -380
  39. package/src/index.js.flow +8 -466
  40. package/src/lib/CountingSet.js +116 -0
  41. package/src/lib/CountingSet.js.flow +126 -0
  42. package/src/lib/JsonReporter.js +0 -2
  43. package/src/lib/JsonReporter.js.flow +1 -1
  44. package/src/lib/getAppendScripts.js +10 -4
  45. package/src/lib/getAppendScripts.js.flow +6 -4
  46. package/src/lib/getPreludeCode.js +19 -1
  47. package/src/lib/getPreludeCode.js.flow +15 -0
  48. package/src/lib/getPrependedScripts.js +10 -2
  49. package/src/lib/getPrependedScripts.js.flow +11 -2
  50. package/src/lib/reporting.js +0 -2
  51. package/src/lib/reporting.js.flow +2 -1
  52. package/src/node-haste/DependencyGraph/ModuleResolution.js +15 -3
  53. package/src/node-haste/DependencyGraph/ModuleResolution.js.flow +15 -0
  54. package/src/node-haste/DependencyGraph/createHasteMap.js +78 -19
  55. package/src/node-haste/DependencyGraph/createHasteMap.js.flow +14 -9
  56. package/src/node-haste/DependencyGraph.js +2 -2
  57. package/src/node-haste/DependencyGraph.js.flow +4 -2
@@ -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>;
@@ -56,7 +56,7 @@ const NULL_MODULE: Moduleish = {
56
56
  };
57
57
 
58
58
  const NODE_MODULES = path.sep + 'node_modules' + path.sep;
59
- const isNodeModules = file => file.includes(NODE_MODULES);
59
+ const isNodeModules = (file: string) => file.includes(NODE_MODULES);
60
60
 
61
61
  // This function maps the ModuleGraph data structure to metro-file-map's ModuleMap
62
62
  const createModuleMap = ({
@@ -64,6 +64,11 @@ const createModuleMap = ({
64
64
  moduleCache,
65
65
  sourceExts,
66
66
  platforms,
67
+ }: {
68
+ files: Array<string>,
69
+ moduleCache: ModuleCache,
70
+ platforms: void | $ReadOnlyArray<string>,
71
+ sourceExts: Extensions,
67
72
  }): ModuleMapData => {
68
73
  const platformSet = new Set(
69
74
  (platforms ?? defaults.platforms).concat([NATIVE_PLATFORM]),
@@ -142,7 +147,7 @@ exports.createResolveFn = function (options: ResolveOptions): ResolveFn {
142
147
  );
143
148
 
144
149
  const assetExtensions = new Set(assetExts.map(asset => '.' + asset));
145
- const isAssetFile = file => assetExtensions.has(path.extname(file));
150
+ const isAssetFile = (file: string) => assetExtensions.has(path.extname(file));
146
151
 
147
152
  const moduleResolver = new ModuleResolver({
148
153
  dirExists: (filePath: string): boolean => hasteFS.dirExists(filePath),
@@ -62,11 +62,11 @@ type InlineModuleIdsOptions = $ReadOnly<{
62
62
  }>;
63
63
 
64
64
  // TS detection conditions copied from metro-react-native-babel-preset
65
- function isTypeScriptSource(fileName) {
65
+ function isTypeScriptSource(fileName: string) {
66
66
  return !!fileName && fileName.endsWith('.ts');
67
67
  }
68
68
 
69
- function isTSXSource(fileName) {
69
+ function isTSXSource(fileName: string) {
70
70
  return !!fileName && fileName.endsWith('.tsx');
71
71
  }
72
72
 
@@ -12,11 +12,12 @@
12
12
  const { Console } = require("console");
13
13
 
14
14
  const { Writable } = require("stream");
15
- /* $FlowFixMe(>=0.97.0 site=react_native_fb) This comment suppresses an error
16
- * found when Flow v0.97 was deployed. To see the error delete this comment and
17
- * run Flow. */
18
15
 
19
- const write = (_, __, callback) => callback();
16
+ const write = (_, __, callback) =>
17
+ /* $FlowFixMe(>=0.97.0 site=react_native_fb) This comment suppresses an error
18
+ * found when Flow v0.97 was deployed. To see the error delete this comment and
19
+ * run Flow. */
20
+ callback();
20
21
 
21
22
  module.exports = new Console(
22
23
  new Writable({
@@ -13,8 +13,12 @@
13
13
  const {Console} = require('console');
14
14
  const {Writable} = require('stream');
15
15
 
16
- /* $FlowFixMe(>=0.97.0 site=react_native_fb) This comment suppresses an error
17
- * found when Flow v0.97 was deployed. To see the error delete this comment and
18
- * run Flow. */
19
- const write = (_, __, callback) => callback();
16
+ const write = (
17
+ _: Buffer | string | Array<{chunk: Buffer | string, encoding: string, ...}>,
18
+ __: string | ((error?: Error) => void),
19
+ callback: void | ((error?: Error) => void),
20
+ /* $FlowFixMe(>=0.97.0 site=react_native_fb) This comment suppresses an error
21
+ * found when Flow v0.97 was deployed. To see the error delete this comment and
22
+ * run Flow. */
23
+ ) => callback();
20
24
  module.exports = (new Console(new Writable({write, writev: write})): Console);
@@ -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