metro 0.71.1 → 0.72.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.
Files changed (56) hide show
  1. package/package.json +22 -22
  2. package/src/Assets.js +3 -2
  3. package/src/Assets.js.flow +3 -2
  4. package/src/Bundler.js +0 -2
  5. package/src/Bundler.js.flow +1 -1
  6. package/src/DeltaBundler/DeltaCalculator.js +2 -0
  7. package/src/DeltaBundler/DeltaCalculator.js.flow +2 -0
  8. package/src/DeltaBundler/WorkerFarm.js.flow +2 -2
  9. package/src/DeltaBundler/graphOperations.js +50 -47
  10. package/src/DeltaBundler/graphOperations.js.flow +46 -52
  11. package/src/DeltaBundler/types.flow.js +6 -0
  12. package/src/DeltaBundler/types.flow.js.flow +12 -3
  13. package/src/DeltaBundler.js +0 -2
  14. package/src/DeltaBundler.js.flow +1 -1
  15. package/src/HmrServer.js +0 -2
  16. package/src/HmrServer.js.flow +1 -2
  17. package/src/ModuleGraph/node-haste/node-haste.flow.js +0 -1
  18. package/src/ModuleGraph/node-haste/node-haste.flow.js.flow +1 -2
  19. package/src/ModuleGraph/node-haste/node-haste.js +12 -3
  20. package/src/ModuleGraph/node-haste/node-haste.js.flow +34 -7
  21. package/src/ModuleGraph/output/util.js +1 -0
  22. package/src/ModuleGraph/output/util.js.flow +3 -2
  23. package/src/ModuleGraph/silent-console.js +5 -4
  24. package/src/ModuleGraph/silent-console.js.flow +8 -4
  25. package/src/ModuleGraph/worker/collectDependencies.js +229 -33
  26. package/src/ModuleGraph/worker/collectDependencies.js.flow +250 -48
  27. package/src/Server.js +4 -0
  28. package/src/Server.js.flow +11 -2
  29. package/src/cli-utils.js.flow +1 -1
  30. package/src/commands/build.js +1 -2
  31. package/src/commands/build.js.flow +6 -9
  32. package/src/commands/dependencies.js +1 -1
  33. package/src/commands/serve.js +2 -1
  34. package/src/commands/serve.js.flow +7 -8
  35. package/src/index.flow.js +11 -8
  36. package/src/index.flow.js.flow +10 -7
  37. package/src/lib/CountingSet.js +116 -0
  38. package/src/lib/CountingSet.js.flow +126 -0
  39. package/src/lib/JsonReporter.js +0 -2
  40. package/src/lib/JsonReporter.js.flow +1 -1
  41. package/src/lib/getAppendScripts.js +10 -4
  42. package/src/lib/getAppendScripts.js.flow +6 -4
  43. package/src/lib/getPreludeCode.js +19 -1
  44. package/src/lib/getPreludeCode.js.flow +15 -0
  45. package/src/lib/getPrependedScripts.js +10 -2
  46. package/src/lib/getPrependedScripts.js.flow +11 -2
  47. package/src/lib/reporting.js +0 -2
  48. package/src/lib/reporting.js.flow +2 -1
  49. package/src/node-haste/DependencyGraph/createHasteMap.js +8 -1
  50. package/src/node-haste/DependencyGraph/createHasteMap.js.flow +9 -2
  51. package/src/node-haste/DependencyGraph.js +2 -2
  52. package/src/node-haste/DependencyGraph.js.flow +4 -2
  53. package/src/shared/output/bundle.flow.js +67 -0
  54. package/src/shared/output/bundle.flow.js.flow +89 -0
  55. package/src/shared/output/bundle.js +8 -55
  56. package/src/shared/output/bundle.js.flow +8 -75
@@ -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>;
@@ -47,7 +47,13 @@ const NODE_MODULES = path.sep + "node_modules" + path.sep;
47
47
 
48
48
  const isNodeModules = (file) => file.includes(NODE_MODULES); // This function maps the ModuleGraph data structure to metro-file-map's ModuleMap
49
49
 
50
- const createModuleMap = ({ files, moduleCache, sourceExts, platforms }) => {
50
+ const createModuleMap = ({
51
+ files,
52
+ moduleCache,
53
+ sourceExts,
54
+ additionalExts,
55
+ platforms,
56
+ }) => {
51
57
  const platformSet = new Set(
52
58
  (platforms !== null && platforms !== void 0
53
59
  ? platforms
@@ -62,11 +68,12 @@ const createModuleMap = ({ files, moduleCache, sourceExts, platforms }) => {
62
68
 
63
69
  let id;
64
70
  let module;
71
+ const fileExt = path.extname(filePath).substr(1);
65
72
 
66
73
  if (filePath.endsWith(PACKAGE_JSON)) {
67
74
  module = moduleCache.getPackage(filePath);
68
75
  id = module.data.name;
69
- } else if (sourceExts.indexOf(path.extname(filePath).substr(1)) !== -1) {
76
+ } else if (sourceExts.has(fileExt) || additionalExts.has(fileExt)) {
70
77
  module = moduleCache.getModule(filePath);
71
78
  id = module.name;
72
79
  }
@@ -107,6 +114,7 @@ exports.createResolveFn = function (options) {
107
114
  extraNodeModules,
108
115
  transformedFiles,
109
116
  sourceExts,
117
+ additionalExts,
110
118
  platform,
111
119
  platforms,
112
120
  } = options;
@@ -146,7 +154,8 @@ exports.createResolveFn = function (options) {
146
154
  map: createModuleMap({
147
155
  files,
148
156
  moduleCache,
149
- sourceExts,
157
+ sourceExts: new Set(sourceExts),
158
+ additionalExts: new Set(additionalExts),
150
159
  platforms,
151
160
  }),
152
161
  mocks: new Map(),
@@ -10,7 +10,7 @@
10
10
 
11
11
  import type {Moduleish} from '../../node-haste/DependencyGraph/ModuleResolution';
12
12
  import type {ResolveFn, TransformedCodeFile} from '../types.flow';
13
- import type {Extensions, Path} from './node-haste.flow';
13
+ import type {Path} from './node-haste.flow';
14
14
  import type {ModuleMapData, ModuleMapItem} from 'metro-file-map';
15
15
  import type {CustomResolver} from 'metro-resolver';
16
16
 
@@ -27,7 +27,19 @@ const defaults = require('metro-config/src/defaults/defaults');
27
27
  const path = require('path');
28
28
 
29
29
  type ResolveOptions = {
30
- assetExts: Extensions,
30
+ /**
31
+ * (Used by the resolver) The extensions tried (in order) to implicitly
32
+ * locate a source file.
33
+ */
34
+ sourceExts: $ReadOnlyArray<string>,
35
+
36
+ /**
37
+ * The additional extensions to include in the file map as source files that
38
+ * can be explicitly imported.
39
+ */
40
+ additionalExts: $ReadOnlyArray<string>,
41
+
42
+ assetExts: $ReadOnlyArray<string>,
31
43
  assetResolutions: $ReadOnlyArray<string>,
32
44
  +disableHierarchicalLookup: boolean,
33
45
  +emptyModulePath: string,
@@ -37,7 +49,6 @@ type ResolveOptions = {
37
49
  +platform: string,
38
50
  platforms?: $ReadOnlyArray<string>,
39
51
  resolveRequest?: ?CustomResolver,
40
- +sourceExts: Extensions,
41
52
  transformedFiles: {[path: Path]: TransformedCodeFile, ...},
42
53
  };
43
54
 
@@ -56,14 +67,21 @@ const NULL_MODULE: Moduleish = {
56
67
  };
57
68
 
58
69
  const NODE_MODULES = path.sep + 'node_modules' + path.sep;
59
- const isNodeModules = file => file.includes(NODE_MODULES);
70
+ const isNodeModules = (file: string) => file.includes(NODE_MODULES);
60
71
 
61
72
  // This function maps the ModuleGraph data structure to metro-file-map's ModuleMap
62
73
  const createModuleMap = ({
63
74
  files,
64
75
  moduleCache,
65
76
  sourceExts,
77
+ additionalExts,
66
78
  platforms,
79
+ }: {
80
+ files: Array<string>,
81
+ moduleCache: ModuleCache,
82
+ sourceExts: $ReadOnlySet<string>,
83
+ additionalExts: $ReadOnlySet<string>,
84
+ platforms: void | $ReadOnlyArray<string>,
67
85
  }): ModuleMapData => {
68
86
  const platformSet = new Set(
69
87
  (platforms ?? defaults.platforms).concat([NATIVE_PLATFORM]),
@@ -77,10 +95,12 @@ const createModuleMap = ({
77
95
  }
78
96
  let id;
79
97
  let module;
98
+ const fileExt = path.extname(filePath).substr(1);
99
+
80
100
  if (filePath.endsWith(PACKAGE_JSON)) {
81
101
  module = moduleCache.getPackage(filePath);
82
102
  id = module.data.name;
83
- } else if (sourceExts.indexOf(path.extname(filePath).substr(1)) !== -1) {
103
+ } else if (sourceExts.has(fileExt) || additionalExts.has(fileExt)) {
84
104
  module = moduleCache.getModule(filePath);
85
105
  id = module.name;
86
106
  }
@@ -123,6 +143,7 @@ exports.createResolveFn = function (options: ResolveOptions): ResolveFn {
123
143
  extraNodeModules,
124
144
  transformedFiles,
125
145
  sourceExts,
146
+ additionalExts,
126
147
  platform,
127
148
  platforms,
128
149
  } = options;
@@ -142,7 +163,7 @@ exports.createResolveFn = function (options: ResolveOptions): ResolveFn {
142
163
  );
143
164
 
144
165
  const assetExtensions = new Set(assetExts.map(asset => '.' + asset));
145
- const isAssetFile = file => assetExtensions.has(path.extname(file));
166
+ const isAssetFile = (file: string) => assetExtensions.has(path.extname(file));
146
167
 
147
168
  const moduleResolver = new ModuleResolver({
148
169
  dirExists: (filePath: string): boolean => hasteFS.dirExists(filePath),
@@ -156,7 +177,13 @@ exports.createResolveFn = function (options: ResolveOptions): ResolveFn {
156
177
  moduleCache,
157
178
  moduleMap: new ModuleMap({
158
179
  duplicates: new Map(),
159
- map: createModuleMap({files, moduleCache, sourceExts, platforms}),
180
+ map: createModuleMap({
181
+ files,
182
+ moduleCache,
183
+ sourceExts: new Set(sourceExts),
184
+ additionalExts: new Set(additionalExts),
185
+ platforms,
186
+ }),
160
187
  mocks: new Map(),
161
188
  rootDir: '',
162
189
  }),
@@ -181,6 +181,7 @@ function inlineModuleIds(
181
181
  ? parseSync(code, babelConfig)
182
182
  : HermesParser.parse(code, {
183
183
  babel: true,
184
+ // $FlowFixMe[prop-missing]
184
185
  sourceType: babelConfig.sourceType,
185
186
  });
186
187
  const ast = nullthrows(
@@ -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
 
@@ -181,6 +181,7 @@ function inlineModuleIds(
181
181
  ? parseSync(code, babelConfig)
182
182
  : HermesParser.parse(code, {
183
183
  babel: true,
184
+ // $FlowFixMe[prop-missing]
184
185
  sourceType: babelConfig.sourceType,
185
186
  });
186
187
 
@@ -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);
@@ -9,6 +9,8 @@
9
9
  */
10
10
  "use strict";
11
11
 
12
+ const crypto = require("crypto");
13
+
12
14
  const generate = require("@babel/generator").default;
13
15
 
14
16
  const template = require("@babel/template").default;
@@ -53,6 +55,7 @@ function collectDependencies(ast, options) {
53
55
  dynamicRequires: options.dynamicRequires,
54
56
  keepRequireNames: options.keepRequireNames,
55
57
  allowOptionalDependencies: options.allowOptionalDependencies,
58
+ unstable_allowRequireContext: options.unstable_allowRequireContext,
56
59
  };
57
60
  const visitor = {
58
61
  CallExpression(path, state) {
@@ -97,6 +100,22 @@ function collectDependencies(ast, options) {
97
100
  splitCondition: args[1],
98
101
  });
99
102
  return;
103
+ } // Match `require.context`
104
+
105
+ if (
106
+ // Feature gate, defaults to `false`.
107
+ state.unstable_allowRequireContext &&
108
+ callee.type === "MemberExpression" && // `require`
109
+ callee.object.type === "Identifier" &&
110
+ callee.object.name === "require" && // `context`
111
+ callee.property.type === "Identifier" &&
112
+ callee.property.name === "context" &&
113
+ !callee.computed && // Ensure `require` refers to the global and not something else.
114
+ !path.scope.getBinding("require")
115
+ ) {
116
+ processRequireContextCall(path, state);
117
+ visited.add(path.node);
118
+ return;
100
119
  }
101
120
 
102
121
  if (
@@ -148,6 +167,146 @@ function collectDependencies(ast, options) {
148
167
  dependencyMapName: nullthrows(state.dependencyMapIdentifier).name,
149
168
  };
150
169
  }
170
+ /** Extract args passed to the `require.context` method. */
171
+
172
+ function getRequireContextArgs(path) {
173
+ const args = path.get("arguments");
174
+ let directory;
175
+
176
+ if (!Array.isArray(args) || args.length < 1) {
177
+ throw new InvalidRequireCallError(path);
178
+ } else {
179
+ const result = args[0].evaluate();
180
+
181
+ if (result.confident && typeof result.value === "string") {
182
+ directory = result.value;
183
+ } else {
184
+ var _result$deopt;
185
+
186
+ throw new InvalidRequireCallError(
187
+ (_result$deopt = result.deopt) !== null && _result$deopt !== void 0
188
+ ? _result$deopt
189
+ : args[0],
190
+ "First argument of `require.context` should be a string denoting the directory to require."
191
+ );
192
+ }
193
+ } // Default to requiring through all directories.
194
+
195
+ let recursive = true;
196
+
197
+ if (args.length > 1) {
198
+ const result = args[1].evaluate();
199
+
200
+ if (result.confident && typeof result.value === "boolean") {
201
+ recursive = result.value;
202
+ } else if (!(result.confident && typeof result.value === "undefined")) {
203
+ var _result$deopt2;
204
+
205
+ throw new InvalidRequireCallError(
206
+ (_result$deopt2 = result.deopt) !== null && _result$deopt2 !== void 0
207
+ ? _result$deopt2
208
+ : args[1],
209
+ "Second argument of `require.context` should be an optional boolean indicating if files should be imported recursively or not."
210
+ );
211
+ }
212
+ } // Default to all files.
213
+
214
+ let filter = {
215
+ pattern: ".*",
216
+ flags: "",
217
+ };
218
+
219
+ if (args.length > 2) {
220
+ // evaluate() to check for undefined (because it's technically a scope lookup)
221
+ // but check the AST for the regex literal, since evaluate() doesn't do regex.
222
+ const result = args[2].evaluate();
223
+ const argNode = args[2].node;
224
+
225
+ if (argNode.type === "RegExpLiteral") {
226
+ // TODO: Handle `new RegExp(...)` -- `argNode.type === 'NewExpression'`
227
+ filter = {
228
+ pattern: argNode.pattern,
229
+ flags: argNode.flags || "",
230
+ };
231
+ } else if (!(result.confident && typeof result.value === "undefined")) {
232
+ throw new InvalidRequireCallError(
233
+ args[2],
234
+ `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}.`
235
+ );
236
+ }
237
+ } // Default to `sync`.
238
+
239
+ let mode = "sync";
240
+
241
+ if (args.length > 3) {
242
+ const result = args[3].evaluate();
243
+
244
+ if (result.confident && typeof result.value === "string") {
245
+ mode = getContextMode(args[3], result.value);
246
+ } else if (!(result.confident && typeof result.value === "undefined")) {
247
+ var _result$deopt3;
248
+
249
+ throw new InvalidRequireCallError(
250
+ (_result$deopt3 = result.deopt) !== null && _result$deopt3 !== void 0
251
+ ? _result$deopt3
252
+ : args[3],
253
+ 'Fourth argument of `require.context` should be an optional string "mode" denoting how the modules will be resolved.'
254
+ );
255
+ }
256
+ }
257
+
258
+ if (args.length > 4) {
259
+ throw new InvalidRequireCallError(
260
+ path,
261
+ `Too many arguments provided to \`require.context\` call. Expected 4, got: ${args.length}`
262
+ );
263
+ }
264
+
265
+ return [
266
+ directory,
267
+ {
268
+ recursive,
269
+ filter,
270
+ mode,
271
+ },
272
+ ];
273
+ }
274
+
275
+ function getContextMode(path, mode) {
276
+ if (
277
+ mode === "sync" ||
278
+ mode === "eager" ||
279
+ mode === "lazy" ||
280
+ mode === "lazy-once"
281
+ ) {
282
+ return mode;
283
+ }
284
+
285
+ throw new InvalidRequireCallError(
286
+ path,
287
+ `require.context "${mode}" mode is not supported. Expected one of: sync, eager, lazy, lazy-once`
288
+ );
289
+ }
290
+
291
+ function processRequireContextCall(path, state) {
292
+ const [directory, contextParams] = getRequireContextArgs(path);
293
+ const transformer = state.dependencyTransformer;
294
+ const dep = registerDependency(
295
+ state,
296
+ {
297
+ // We basically want to "import" every file in a folder and then filter them out with the given `filter` RegExp.
298
+ name: directory,
299
+ // Capture the matching context
300
+ contextParams,
301
+ asyncType: null,
302
+ optional: isOptionalDependency(directory, path, state),
303
+ },
304
+ path
305
+ ); // require() the generated module representing this context
306
+
307
+ path.get("callee").replaceWith(types.identifier("require"));
308
+ transformer.transformSyncRequire(path, dep, state);
309
+ }
151
310
 
152
311
  function collectImports(path, state) {
153
312
  if (path.node.source) {
@@ -309,10 +468,15 @@ function getModuleNameFromCallArgs(path) {
309
468
  collectDependencies.getModuleNameFromCallArgs = getModuleNameFromCallArgs;
310
469
 
311
470
  class InvalidRequireCallError extends Error {
312
- constructor({ node }) {
471
+ constructor({ node }, message) {
313
472
  const line = node.loc && node.loc.start && node.loc.start.line;
314
473
  super(
315
- `Invalid call at line ${line || "<unknown>"}: ${generate(node).code}`
474
+ [
475
+ `Invalid call at line ${line || "<unknown>"}: ${generate(node).code}`,
476
+ message,
477
+ ]
478
+ .filter(Boolean)
479
+ .join("\n")
316
480
  );
317
481
  }
318
482
  }
@@ -348,9 +512,11 @@ const makeJSResourceTemplate = template.statement(`
348
512
  const DefaultDependencyTransformer = {
349
513
  transformSyncRequire(path, dependency, state) {
350
514
  const moduleIDExpression = createModuleIDExpression(dependency, state);
351
- path.node.arguments = state.keepRequireNames
352
- ? [moduleIDExpression, types.stringLiteral(dependency.name)]
353
- : [moduleIDExpression];
515
+ path.node.arguments = [moduleIDExpression]; // Always add the debug name argument last
516
+
517
+ if (state.keepRequireNames) {
518
+ path.node.arguments.push(types.stringLiteral(dependency.name));
519
+ }
354
520
  },
355
521
 
356
522
  transformImportCall(path, dependency, state) {
@@ -420,12 +586,58 @@ function createModuleIDExpression(dependency, state) {
420
586
  function createModuleNameLiteral(dependency) {
421
587
  return types.stringLiteral(dependency.name);
422
588
  }
589
+ /**
590
+ * Given an import qualifier, return a key used to register the dependency.
591
+ * Generally this return the `ImportQualifier.name` property, but more
592
+ * attributes can be appended to distinguish various combinations that would
593
+ * otherwise conflict.
594
+ *
595
+ * For example, the following case would have collision issues if they all utilized the `name` property:
596
+ * ```
597
+ * require('./foo');
598
+ * require.context('./foo');
599
+ * require.context('./foo', true, /something/);
600
+ * require.context('./foo', false, /something/);
601
+ * require.context('./foo', false, /something/, 'lazy');
602
+ * ```
603
+ *
604
+ * This method should be utilized by `registerDependency`.
605
+ */
606
+
607
+ function getKeyForDependency(qualifier) {
608
+ let key = qualifier.name;
609
+ const { asyncType } = qualifier;
610
+
611
+ if (asyncType) {
612
+ key += ["", asyncType].join("\0");
613
+ }
614
+
615
+ const { contextParams } = qualifier; // Add extra qualifiers when using `require.context` to prevent collisions.
616
+
617
+ if (contextParams) {
618
+ // NOTE(EvanBacon): Keep this synchronized with `RequireContextParams`, if any other properties are added
619
+ // then this key algorithm should be updated to account for those properties.
620
+ // Example: `./directory__true__/foobar/m__lazy`
621
+ key += [
622
+ "",
623
+ "context",
624
+ String(contextParams.recursive),
625
+ String(contextParams.filter.pattern),
626
+ String(contextParams.filter.flags),
627
+ contextParams.mode, // Join together and append to the name:
628
+ ].join("\0");
629
+ }
630
+
631
+ return key;
632
+ }
423
633
 
424
634
  class DefaultModuleDependencyRegistry {
425
635
  _dependencies = new Map();
426
636
 
427
637
  registerDependency(qualifier) {
428
- let dependency = this._dependencies.get(qualifier.name);
638
+ const key = getKeyForDependency(qualifier);
639
+
640
+ let dependency = this._dependencies.get(key);
429
641
 
430
642
  if (dependency == null) {
431
643
  const newDependency = {
@@ -433,24 +645,28 @@ class DefaultModuleDependencyRegistry {
433
645
  asyncType: qualifier.asyncType,
434
646
  locs: [],
435
647
  index: this._dependencies.size,
648
+ key: crypto.createHash("sha1").update(key).digest("base64"),
436
649
  };
437
650
 
438
651
  if (qualifier.optional) {
439
652
  newDependency.isOptional = true;
440
653
  }
441
654
 
442
- dependency = newDependency;
655
+ if (qualifier.contextParams) {
656
+ newDependency.contextParams = qualifier.contextParams;
657
+ }
443
658
 
444
- this._dependencies.set(qualifier.name, dependency);
659
+ dependency = newDependency;
445
660
  } else {
446
- const original = dependency;
447
- dependency = collapseDependencies(original, qualifier);
448
-
449
- if (original !== dependency) {
450
- this._dependencies.set(qualifier.name, dependency);
661
+ if (dependency.isOptional && !qualifier.optional) {
662
+ // A previously optionally required dependency was required non-optionally.
663
+ // Mark it non optional for the whole module
664
+ dependency = { ...dependency, isOptional: false };
451
665
  }
452
666
  }
453
667
 
668
+ this._dependencies.set(key, dependency);
669
+
454
670
  return dependency;
455
671
  }
456
672
 
@@ -459,24 +675,4 @@ class DefaultModuleDependencyRegistry {
459
675
  }
460
676
  }
461
677
 
462
- function collapseDependencies(dependency, qualifier) {
463
- let collapsed = dependency; // A previously optionally required dependency was required non-optionaly.
464
- // Mark it non optional for the whole module
465
-
466
- if (collapsed.isOptional && !qualifier.optional) {
467
- collapsed = { ...dependency, isOptional: false };
468
- } // A previously asynchronously (or prefetch) required module was required synchronously.
469
- // Make the dependency sync.
470
-
471
- if (collapsed.asyncType != null && qualifier.asyncType == null) {
472
- collapsed = { ...dependency, asyncType: null };
473
- } // A prefetched dependency was required async in the module. Mark it as async.
474
-
475
- if (collapsed.asyncType === "prefetch" && qualifier.asyncType === "async") {
476
- collapsed = { ...dependency, asyncType: "async" };
477
- }
478
-
479
- return collapsed;
480
- }
481
-
482
678
  module.exports = collectDependencies;