metro 0.72.1 → 0.72.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.
Files changed (43) hide show
  1. package/package.json +23 -21
  2. package/src/DeltaBundler/Serializers/baseBytecodeBundle.js +3 -2
  3. package/src/DeltaBundler/Serializers/baseBytecodeBundle.js.flow +2 -1
  4. package/src/DeltaBundler/Serializers/helpers/bytecode.js +2 -2
  5. package/src/DeltaBundler/Serializers/helpers/bytecode.js.flow +2 -1
  6. package/src/DeltaBundler.js.flow +1 -1
  7. package/src/HmrServer.js +9 -5
  8. package/src/HmrServer.js.flow +9 -4
  9. package/src/IncrementalBundler.js +16 -4
  10. package/src/IncrementalBundler.js.flow +17 -4
  11. package/src/ModuleGraph/node-haste/Package.js.flow +5 -5
  12. package/src/ModuleGraph/node-haste/node-haste.js +8 -4
  13. package/src/ModuleGraph/node-haste/node-haste.js.flow +21 -14
  14. package/src/ModuleGraph/output/util.js +2 -4
  15. package/src/ModuleGraph/output/util.js.flow +1 -2
  16. package/src/ModuleGraph/types.flow.js.flow +28 -5
  17. package/src/Server.js +86 -34
  18. package/src/Server.js.flow +106 -36
  19. package/src/index.flow.js +16 -8
  20. package/src/index.flow.js.flow +14 -8
  21. package/src/lib/RamBundleParser.js +1 -0
  22. package/src/lib/RamBundleParser.js.flow +1 -0
  23. package/src/lib/bundleToBytecode.js +3 -2
  24. package/src/lib/bundleToBytecode.js.flow +2 -2
  25. package/src/lib/getGraphId.js +16 -3
  26. package/src/lib/getGraphId.js.flow +10 -10
  27. package/src/lib/getPrependedScripts.js +13 -5
  28. package/src/lib/getPrependedScripts.js.flow +6 -1
  29. package/src/lib/parseCustomResolverOptions.js +26 -0
  30. package/src/lib/parseCustomResolverOptions.js.flow +38 -0
  31. package/src/lib/parseOptionsFromUrl.js +3 -0
  32. package/src/lib/parseOptionsFromUrl.js.flow +2 -0
  33. package/src/lib/splitBundleOptions.js +3 -0
  34. package/src/lib/splitBundleOptions.js.flow +3 -0
  35. package/src/lib/transformHelpers.js +27 -13
  36. package/src/lib/transformHelpers.js.flow +27 -7
  37. package/src/node-haste/DependencyGraph/ModuleResolution.js +18 -2
  38. package/src/node-haste/DependencyGraph/ModuleResolution.js.flow +5 -0
  39. package/src/node-haste/DependencyGraph.js +34 -9
  40. package/src/node-haste/DependencyGraph.js.flow +49 -11
  41. package/src/node-haste/Module.js +1 -0
  42. package/src/node-haste/Module.js.flow +1 -0
  43. package/src/shared/types.flow.js.flow +7 -0
@@ -0,0 +1,26 @@
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
+ * @emails oncall+metro_bundler
8
+ * @format
9
+ *
10
+ */
11
+ "use strict";
12
+
13
+ const nullthrows = require("nullthrows");
14
+
15
+ const PREFIX = "resolver.";
16
+
17
+ module.exports = function parseCustomResolverOptions(urlObj) {
18
+ const customResolverOptions = Object.create(null);
19
+ const query = nullthrows(urlObj.query);
20
+ Object.keys(query).forEach((key) => {
21
+ if (key.startsWith(PREFIX)) {
22
+ customResolverOptions[key.substr(PREFIX.length)] = query[key];
23
+ }
24
+ });
25
+ return customResolverOptions;
26
+ };
@@ -0,0 +1,38 @@
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
+ * @emails oncall+metro_bundler
8
+ * @format
9
+ * @flow strict-local
10
+ */
11
+
12
+ 'use strict';
13
+
14
+ import type {CustomResolverOptions} from '../../../metro-resolver/src/types';
15
+
16
+ const nullthrows = require('nullthrows');
17
+
18
+ const PREFIX = 'resolver.';
19
+
20
+ module.exports = function parseCustomResolverOptions(urlObj: {
21
+ +query?: {[string]: string, ...},
22
+ ...
23
+ }): CustomResolverOptions {
24
+ const customResolverOptions: {
25
+ __proto__: null,
26
+ [string]: mixed,
27
+ ...
28
+ } = Object.create(null);
29
+ const query = nullthrows(urlObj.query);
30
+
31
+ Object.keys(query).forEach((key: string) => {
32
+ if (key.startsWith(PREFIX)) {
33
+ customResolverOptions[key.substr(PREFIX.length)] = query[key];
34
+ }
35
+ });
36
+
37
+ return customResolverOptions;
38
+ };
@@ -11,6 +11,8 @@
11
11
 
12
12
  const parsePlatformFilePath = require("../node-haste/lib/parsePlatformFilePath");
13
13
 
14
+ const parseCustomResolverOptions = require("./parseCustomResolverOptions");
15
+
14
16
  const parseCustomTransformOptions = require("./parseCustomTransformOptions");
15
17
 
16
18
  const nullthrows = require("nullthrows");
@@ -60,6 +62,7 @@ module.exports = function parseOptionsFromUrl(
60
62
  bundleType,
61
63
  runtimeBytecodeVersion:
62
64
  bytecodeVersion === runtimeBytecodeVersion ? bytecodeVersion : null,
65
+ customResolverOptions: parseCustomResolverOptions(parsedURL),
63
66
  customTransformOptions: parseCustomTransformOptions(parsedURL),
64
67
  dev: getBoolean(query, "dev", true),
65
68
  entryFile: pathname.replace(/^(?:\.?\/)?/, "./").replace(/\.[^/.]+$/, ""),
@@ -14,6 +14,7 @@ import type {BundleOptions} from '../shared/types.flow';
14
14
  import type {TransformProfile} from 'metro-babel-transformer';
15
15
 
16
16
  const parsePlatformFilePath = require('../node-haste/lib/parsePlatformFilePath');
17
+ const parseCustomResolverOptions = require('./parseCustomResolverOptions');
17
18
  const parseCustomTransformOptions = require('./parseCustomTransformOptions');
18
19
  const nullthrows = require('nullthrows');
19
20
  const path = require('path');
@@ -68,6 +69,7 @@ module.exports = function parseOptionsFromUrl(
68
69
  bundleType,
69
70
  runtimeBytecodeVersion:
70
71
  bytecodeVersion === runtimeBytecodeVersion ? bytecodeVersion : null,
72
+ customResolverOptions: parseCustomResolverOptions(parsedURL),
71
73
  customTransformOptions: parseCustomTransformOptions(parsedURL),
72
74
  dev: getBoolean(query, 'dev', true),
73
75
  entryFile: pathname.replace(/^(?:\.?\/)?/, './').replace(/\.[^/.]+$/, ''),
@@ -15,6 +15,9 @@
15
15
  function splitBundleOptions(options) {
16
16
  return {
17
17
  entryFile: options.entryFile,
18
+ resolverOptions: {
19
+ customResolverOptions: options.customResolverOptions,
20
+ },
18
21
  transformOptions: {
19
22
  customTransformOptions: options.customTransformOptions,
20
23
  dev: options.dev,
@@ -18,6 +18,9 @@ import type {BundleOptions, SplitBundleOptions} from '../shared/types.flow';
18
18
  function splitBundleOptions(options: BundleOptions): SplitBundleOptions {
19
19
  return {
20
20
  entryFile: options.entryFile,
21
+ resolverOptions: {
22
+ customResolverOptions: options.customResolverOptions,
23
+ },
21
24
  transformOptions: {
22
25
  customTransformOptions: options.customTransformOptions,
23
26
  dev: options.dev,
@@ -20,7 +20,8 @@ async function calcTransformerOptions(
20
20
  bundler,
21
21
  deltaBundler,
22
22
  config,
23
- options
23
+ options,
24
+ resolverOptions
24
25
  ) {
25
26
  const baseOptions = {
26
27
  customTransformOptions: options.customTransformOptions,
@@ -42,11 +43,19 @@ async function calcTransformerOptions(
42
43
 
43
44
  const getDependencies = async (path) => {
44
45
  const dependencies = await deltaBundler.getDependencies([path], {
45
- resolve: await getResolveDependencyFn(bundler, options.platform),
46
- transform: await getTransformFn([path], bundler, deltaBundler, config, {
47
- ...options,
48
- minify: false,
49
- }),
46
+ resolve: await getResolveDependencyFn(
47
+ bundler,
48
+ options.platform,
49
+ resolverOptions
50
+ ),
51
+ transform: await getTransformFn(
52
+ [path],
53
+ bundler,
54
+ deltaBundler,
55
+ config,
56
+ { ...options, minify: false },
57
+ resolverOptions
58
+ ),
50
59
  transformOptions: options,
51
60
  onProgress: null,
52
61
  experimentalImportBundleSupport:
@@ -92,14 +101,16 @@ async function getTransformFn(
92
101
  bundler,
93
102
  deltaBundler,
94
103
  config,
95
- options
104
+ options,
105
+ resolverOptions
96
106
  ) {
97
107
  const { inlineRequires, ...transformOptions } = await calcTransformerOptions(
98
108
  entryFiles,
99
109
  bundler,
100
110
  deltaBundler,
101
111
  config,
102
- options
112
+ options,
113
+ resolverOptions
103
114
  );
104
115
  return async (modulePath, requireContext) => {
105
116
  let templateBuffer;
@@ -153,12 +164,15 @@ function getType(type, filePath, assetExts) {
153
164
  return "module";
154
165
  }
155
166
 
156
- async function getResolveDependencyFn(bundler, platform) {
167
+ async function getResolveDependencyFn(bundler, platform, resolverOptions) {
157
168
  const dependencyGraph = await await bundler.getDependencyGraph();
158
- return (
159
- from,
160
- to // $FlowFixMe[incompatible-call]
161
- ) => dependencyGraph.resolveDependency(from, to, platform);
169
+ return (from, to) =>
170
+ dependencyGraph.resolveDependency(
171
+ from,
172
+ to,
173
+ platform !== null && platform !== void 0 ? platform : null,
174
+ resolverOptions
175
+ );
162
176
  }
163
177
 
164
178
  module.exports = {
@@ -21,6 +21,7 @@ import type {RequireContext} from './contextModule';
21
21
  import {getContextModuleTemplate} from './contextModuleTemplates';
22
22
 
23
23
  const path = require('path');
24
+ import type {ResolverInputOptions} from '../shared/types.flow';
24
25
 
25
26
  type InlineRequiresRaw = {+blockList: {[string]: true, ...}, ...} | boolean;
26
27
 
@@ -37,6 +38,7 @@ async function calcTransformerOptions(
37
38
  deltaBundler: DeltaBundler<>,
38
39
  config: ConfigT,
39
40
  options: TransformInputOptions,
41
+ resolverOptions: ResolverInputOptions,
40
42
  ): Promise<TransformOptionsWithRawInlines> {
41
43
  const baseOptions = {
42
44
  customTransformOptions: options.customTransformOptions,
@@ -62,11 +64,22 @@ async function calcTransformerOptions(
62
64
 
63
65
  const getDependencies = async (path: string) => {
64
66
  const dependencies = await deltaBundler.getDependencies([path], {
65
- resolve: await getResolveDependencyFn(bundler, options.platform),
66
- transform: await getTransformFn([path], bundler, deltaBundler, config, {
67
- ...options,
68
- minify: false,
69
- }),
67
+ resolve: await getResolveDependencyFn(
68
+ bundler,
69
+ options.platform,
70
+ resolverOptions,
71
+ ),
72
+ transform: await getTransformFn(
73
+ [path],
74
+ bundler,
75
+ deltaBundler,
76
+ config,
77
+ {
78
+ ...options,
79
+ minify: false,
80
+ },
81
+ resolverOptions,
82
+ ),
70
83
  transformOptions: options,
71
84
  onProgress: null,
72
85
  experimentalImportBundleSupport:
@@ -114,6 +127,7 @@ async function getTransformFn(
114
127
  deltaBundler: DeltaBundler<>,
115
128
  config: ConfigT,
116
129
  options: TransformInputOptions,
130
+ resolverOptions: ResolverInputOptions,
117
131
  ): Promise<TransformFn<>> {
118
132
  const {inlineRequires, ...transformOptions} = await calcTransformerOptions(
119
133
  entryFiles,
@@ -121,6 +135,7 @@ async function getTransformFn(
121
135
  deltaBundler,
122
136
  config,
123
137
  options,
138
+ resolverOptions,
124
139
  );
125
140
 
126
141
  return async (modulePath: string, requireContext: ?RequireContext) => {
@@ -186,12 +201,17 @@ function getType(
186
201
  async function getResolveDependencyFn(
187
202
  bundler: Bundler,
188
203
  platform: ?string,
204
+ resolverOptions: ResolverInputOptions,
189
205
  ): Promise<(from: string, to: string) => string> {
190
206
  const dependencyGraph = await await bundler.getDependencyGraph();
191
207
 
192
208
  return (from: string, to: string) =>
193
- // $FlowFixMe[incompatible-call]
194
- dependencyGraph.resolveDependency(from, to, platform);
209
+ dependencyGraph.resolveDependency(
210
+ from,
211
+ to,
212
+ platform ?? null,
213
+ resolverOptions,
214
+ );
195
215
  }
196
216
 
197
217
  module.exports = {
@@ -24,6 +24,7 @@ const util = require("util");
24
24
  class ModuleResolver {
25
25
  // A module representing the project root, used as the origin when resolving `emptyModulePath`.
26
26
  // An empty module, the result of resolving `emptyModulePath` from the project root.
27
+ // $FlowFixMe[missing-local-annot]
27
28
  constructor(options) {
28
29
  this._options = options;
29
30
  const { projectRoot, moduleCache } = this._options;
@@ -50,7 +51,9 @@ class ModuleResolver {
50
51
  this._projectRootFakeModule,
51
52
  this._options.emptyModulePath,
52
53
  false,
53
- null
54
+ null,
55
+ /* resolverOptions */
56
+ {}
54
57
  );
55
58
  this._cachedEmptyModule = emptyModule;
56
59
  }
@@ -110,11 +113,24 @@ class ModuleResolver {
110
113
  return modulePath;
111
114
  }
112
115
 
113
- resolveDependency(fromModule, moduleName, allowHaste, platform) {
116
+ resolveDependency(
117
+ fromModule,
118
+ moduleName,
119
+ allowHaste,
120
+ platform,
121
+ resolverOptions
122
+ ) {
114
123
  try {
124
+ var _resolverOptions$cust;
125
+
115
126
  const result = Resolver.resolve(
116
127
  {
117
128
  ...this._options,
129
+ customResolverOptions:
130
+ (_resolverOptions$cust = resolverOptions.customResolverOptions) !==
131
+ null && _resolverOptions$cust !== void 0
132
+ ? _resolverOptions$cust
133
+ : {},
118
134
  originModulePath: fromModule.path,
119
135
  redirectModulePath: (modulePath) =>
120
136
  this._redirectRequire(fromModule, modulePath),
@@ -26,6 +26,7 @@ const invariant = require('invariant');
26
26
  const Resolver = require('metro-resolver');
27
27
  const path = require('path');
28
28
  const util = require('util');
29
+ import type {ResolverInputOptions} from '../../shared/types.flow';
29
30
 
30
31
  export type DirExistsFn = (filePath: string) => boolean;
31
32
 
@@ -78,6 +79,7 @@ class ModuleResolver<TModule: Moduleish, TPackage: Packageish> {
78
79
  // An empty module, the result of resolving `emptyModulePath` from the project root.
79
80
  _cachedEmptyModule: ?TModule;
80
81
 
82
+ // $FlowFixMe[missing-local-annot]
81
83
  constructor(options: Options<TModule, TPackage>) {
82
84
  this._options = options;
83
85
  const {projectRoot, moduleCache} = this._options;
@@ -102,6 +104,7 @@ class ModuleResolver<TModule: Moduleish, TPackage: Packageish> {
102
104
  this._options.emptyModulePath,
103
105
  false,
104
106
  null,
107
+ /* resolverOptions */ {},
105
108
  );
106
109
  this._cachedEmptyModule = emptyModule;
107
110
  }
@@ -166,11 +169,13 @@ class ModuleResolver<TModule: Moduleish, TPackage: Packageish> {
166
169
  moduleName: string,
167
170
  allowHaste: boolean,
168
171
  platform: string | null,
172
+ resolverOptions: ResolverInputOptions,
169
173
  ): TModule {
170
174
  try {
171
175
  const result = Resolver.resolve(
172
176
  {
173
177
  ...this._options,
178
+ customResolverOptions: resolverOptions.customResolverOptions ?? {},
174
179
  originModulePath: fromModule.path,
175
180
  redirectModulePath: (modulePath: string) =>
176
181
  this._redirectRequire(fromModule, modulePath),
@@ -11,6 +11,8 @@ var _metroFileMap = require("metro-file-map");
11
11
  *
12
12
  * @format
13
13
  */
14
+ const canonicalize = require("metro-core/src/canonicalize");
15
+
14
16
  const createHasteMap = require("./DependencyGraph/createHasteMap");
15
17
 
16
18
  const { ModuleResolver } = require("./DependencyGraph/ModuleResolution");
@@ -34,8 +36,9 @@ const nullthrows = require("nullthrows");
34
36
  const path = require("path");
35
37
 
36
38
  const { DuplicateHasteCandidatesError } = _metroFileMap.ModuleMap;
39
+ const NULL_PLATFORM = Symbol();
37
40
 
38
- function getOrCreate(map, field) {
41
+ function getOrCreateMap(map, field) {
39
42
  let subMap = map.get(field);
40
43
 
41
44
  if (!subMap) {
@@ -218,22 +221,43 @@ class DependencyGraph extends EventEmitter {
218
221
  from,
219
222
  to,
220
223
  platform,
224
+ resolverOptions, // TODO: Fold assumeFlatNodeModules into resolverOptions and add to graphId
221
225
  { assumeFlatNodeModules } = {
222
226
  assumeFlatNodeModules: false,
223
227
  }
224
228
  ) {
229
+ var _JSON$stringify, _resolverOptions$cust;
230
+
225
231
  const isSensitiveToOriginFolder = // Resolution is always relative to the origin folder unless we assume a flat node_modules
226
232
  !assumeFlatNodeModules || // Path requests are resolved relative to the origin folder
227
233
  to.includes("/") ||
228
234
  to === "." ||
229
235
  to === ".." || // Preserve standard assumptions under node_modules
230
- from.includes(path.sep + "node_modules" + path.sep);
231
- const mapByDirectory = getOrCreate(
232
- this._resolutionCache,
233
- isSensitiveToOriginFolder ? path.dirname(from) : ""
236
+ from.includes(path.sep + "node_modules" + path.sep); // Compound key for the resolver cache
237
+
238
+ const resolverOptionsKey =
239
+ (_JSON$stringify = JSON.stringify(
240
+ (_resolverOptions$cust = resolverOptions.customResolverOptions) !==
241
+ null && _resolverOptions$cust !== void 0
242
+ ? _resolverOptions$cust
243
+ : {},
244
+ canonicalize
245
+ )) !== null && _JSON$stringify !== void 0
246
+ ? _JSON$stringify
247
+ : "";
248
+ const originKey = isSensitiveToOriginFolder ? path.dirname(from) : "";
249
+ const targetKey = to;
250
+ const platformKey =
251
+ platform !== null && platform !== void 0 ? platform : NULL_PLATFORM; // Traverse the resolver cache, which is a tree of maps
252
+
253
+ const mapByResolverOptions = this._resolutionCache;
254
+ const mapByOrigin = getOrCreateMap(
255
+ mapByResolverOptions,
256
+ resolverOptionsKey
234
257
  );
235
- const mapByPlatform = getOrCreate(mapByDirectory, to);
236
- let modulePath = mapByPlatform.get(platform);
258
+ const mapByTarget = getOrCreateMap(mapByOrigin, originKey);
259
+ const mapByPlatform = getOrCreateMap(mapByTarget, targetKey);
260
+ let modulePath = mapByPlatform.get(platformKey);
237
261
 
238
262
  if (!modulePath) {
239
263
  try {
@@ -241,7 +265,8 @@ class DependencyGraph extends EventEmitter {
241
265
  this._moduleCache.getModule(from),
242
266
  to,
243
267
  true,
244
- platform
268
+ platform,
269
+ resolverOptions
245
270
  ).path;
246
271
  } catch (error) {
247
272
  if (error instanceof DuplicateHasteCandidatesError) {
@@ -260,7 +285,7 @@ class DependencyGraph extends EventEmitter {
260
285
  }
261
286
  }
262
287
 
263
- mapByPlatform.set(platform, modulePath);
288
+ mapByPlatform.set(platformKey, modulePath);
264
289
  return modulePath;
265
290
  }
266
291
 
@@ -15,6 +15,7 @@ import type Module from './Module';
15
15
 
16
16
  import {ModuleMap as MetroFileMapModuleMap} from 'metro-file-map';
17
17
 
18
+ const canonicalize = require('metro-core/src/canonicalize');
18
19
  const createHasteMap = require('./DependencyGraph/createHasteMap');
19
20
  const {ModuleResolver} = require('./DependencyGraph/ModuleResolution');
20
21
  const ModuleCache = require('./ModuleCache');
@@ -28,13 +29,16 @@ const {
28
29
  const {InvalidPackageError} = require('metro-resolver');
29
30
  const nullthrows = require('nullthrows');
30
31
  const path = require('path');
32
+ import type {ResolverInputOptions} from '../shared/types.flow';
31
33
 
32
34
  const {DuplicateHasteCandidatesError} = MetroFileMapModuleMap;
33
35
 
34
- function getOrCreate<T>(
35
- map: Map<string, Map<string, T>>,
36
+ const NULL_PLATFORM = Symbol();
37
+
38
+ function getOrCreateMap<T>(
39
+ map: Map<string | symbol, Map<string | symbol, T>>,
36
40
  field: string,
37
- ): Map<string, T> {
41
+ ): Map<string | symbol, T> {
38
42
  let subMap = map.get(field);
39
43
  if (!subMap) {
40
44
  subMap = new Map();
@@ -51,7 +55,23 @@ class DependencyGraph extends EventEmitter {
51
55
  _moduleCache: ModuleCache;
52
56
  _moduleMap: MetroFileMapModuleMap;
53
57
  _moduleResolver: ModuleResolver<Module, Package>;
54
- _resolutionCache: Map<string, Map<string, Map<string, string>>>;
58
+ _resolutionCache: Map<
59
+ // Custom resolver options
60
+ string | symbol,
61
+ Map<
62
+ // Origin folder
63
+ string | symbol,
64
+ Map<
65
+ // Dependency name
66
+ string | symbol,
67
+ Map<
68
+ // Platform
69
+ string | symbol,
70
+ string,
71
+ >,
72
+ >,
73
+ >,
74
+ >;
55
75
  _readyPromise: Promise<void>;
56
76
 
57
77
  constructor(
@@ -241,7 +261,10 @@ class DependencyGraph extends EventEmitter {
241
261
  resolveDependency(
242
262
  from: string,
243
263
  to: string,
244
- platform: string,
264
+ platform: string | null,
265
+ resolverOptions: ResolverInputOptions,
266
+
267
+ // TODO: Fold assumeFlatNodeModules into resolverOptions and add to graphId
245
268
  {assumeFlatNodeModules}: {assumeFlatNodeModules: boolean} = {
246
269
  assumeFlatNodeModules: false,
247
270
  },
@@ -255,12 +278,26 @@ class DependencyGraph extends EventEmitter {
255
278
  to === '..' ||
256
279
  // Preserve standard assumptions under node_modules
257
280
  from.includes(path.sep + 'node_modules' + path.sep);
258
- const mapByDirectory = getOrCreate(
259
- this._resolutionCache,
260
- isSensitiveToOriginFolder ? path.dirname(from) : '',
281
+
282
+ // Compound key for the resolver cache
283
+ const resolverOptionsKey =
284
+ JSON.stringify(
285
+ resolverOptions.customResolverOptions ?? {},
286
+ canonicalize,
287
+ ) ?? '';
288
+ const originKey = isSensitiveToOriginFolder ? path.dirname(from) : '';
289
+ const targetKey = to;
290
+ const platformKey = platform ?? NULL_PLATFORM;
291
+
292
+ // Traverse the resolver cache, which is a tree of maps
293
+ const mapByResolverOptions = this._resolutionCache;
294
+ const mapByOrigin = getOrCreateMap(
295
+ mapByResolverOptions,
296
+ resolverOptionsKey,
261
297
  );
262
- const mapByPlatform = getOrCreate(mapByDirectory, to);
263
- let modulePath = mapByPlatform.get(platform);
298
+ const mapByTarget = getOrCreateMap(mapByOrigin, originKey);
299
+ const mapByPlatform = getOrCreateMap(mapByTarget, targetKey);
300
+ let modulePath = mapByPlatform.get(platformKey);
264
301
 
265
302
  if (!modulePath) {
266
303
  try {
@@ -269,6 +306,7 @@ class DependencyGraph extends EventEmitter {
269
306
  to,
270
307
  true,
271
308
  platform,
309
+ resolverOptions,
272
310
  ).path;
273
311
  } catch (error) {
274
312
  if (error instanceof DuplicateHasteCandidatesError) {
@@ -285,7 +323,7 @@ class DependencyGraph extends EventEmitter {
285
323
  }
286
324
  }
287
325
 
288
- mapByPlatform.set(platform, modulePath);
326
+ mapByPlatform.set(platformKey, modulePath);
289
327
  return modulePath;
290
328
  }
291
329
 
@@ -12,6 +12,7 @@
12
12
  const isAbsolutePath = require("absolute-path");
13
13
 
14
14
  class Module {
15
+ // $FlowFixMe[missing-local-annot]
15
16
  constructor(file, moduleCache) {
16
17
  if (!isAbsolutePath(file)) {
17
18
  throw new Error("Expected file to be absolute path but got " + file);
@@ -21,6 +21,7 @@ class Module {
21
21
  _moduleCache: ModuleCache;
22
22
  _sourceCode: ?string;
23
23
 
24
+ // $FlowFixMe[missing-local-annot]
24
25
  constructor(file: string, moduleCache: ModuleCache) {
25
26
  if (!isAbsolutePath(file)) {
26
27
  throw new Error('Expected file to be absolute path but got ' + file);
@@ -23,6 +23,7 @@ import type {
23
23
  CustomTransformOptions,
24
24
  MinifierOptions,
25
25
  } from 'metro-transform-worker';
26
+ import type {CustomResolverOptions} from 'metro-resolver';
26
27
 
27
28
  type BundleType =
28
29
  | 'bundle'
@@ -41,6 +42,7 @@ type MetroSourceMapOrMappings =
41
42
 
42
43
  export type BundleOptions = {
43
44
  bundleType: BundleType,
45
+ +customResolverOptions: CustomResolverOptions,
44
46
  customTransformOptions: CustomTransformOptions,
45
47
  dev: boolean,
46
48
  entryFile: string,
@@ -61,6 +63,10 @@ export type BundleOptions = {
61
63
  ...
62
64
  };
63
65
 
66
+ export type ResolverInputOptions = $ReadOnly<{
67
+ customResolverOptions?: CustomResolverOptions,
68
+ }>;
69
+
64
70
  export type SerializerOptions = {
65
71
  +sourceMapUrl: ?string,
66
72
  +sourceUrl: ?string,
@@ -77,6 +83,7 @@ export type GraphOptions = {
77
83
  // Stricter representation of BundleOptions.
78
84
  export type SplitBundleOptions = {
79
85
  +entryFile: string,
86
+ +resolverOptions: ResolverInputOptions,
80
87
  +transformOptions: TransformInputOptions,
81
88
  +serializerOptions: SerializerOptions,
82
89
  +graphOptions: GraphOptions,