metro 0.74.1 → 0.75.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "metro",
3
- "version": "0.74.1",
3
+ "version": "0.75.1",
4
4
  "description": "🚇 The JavaScript bundler for React Native.",
5
5
  "main": "src/index.js",
6
6
  "bin": "src/cli.js",
@@ -35,23 +35,23 @@
35
35
  "invariant": "^2.2.4",
36
36
  "jest-worker": "^27.2.0",
37
37
  "lodash.throttle": "^4.1.1",
38
- "metro-babel-transformer": "0.74.1",
39
- "metro-cache": "0.74.1",
40
- "metro-cache-key": "0.74.1",
41
- "metro-config": "0.74.1",
42
- "metro-core": "0.74.1",
43
- "metro-file-map": "0.74.1",
44
- "metro-hermes-compiler": "0.74.1",
45
- "metro-inspector-proxy": "0.74.1",
46
- "metro-minify-terser": "0.74.1",
47
- "metro-minify-uglify": "0.74.1",
48
- "metro-react-native-babel-preset": "0.74.1",
49
- "metro-resolver": "0.74.1",
50
- "metro-runtime": "0.74.1",
51
- "metro-source-map": "0.74.1",
52
- "metro-symbolicate": "0.74.1",
53
- "metro-transform-plugins": "0.74.1",
54
- "metro-transform-worker": "0.74.1",
38
+ "metro-babel-transformer": "0.75.1",
39
+ "metro-cache": "0.75.1",
40
+ "metro-cache-key": "0.75.1",
41
+ "metro-config": "0.75.1",
42
+ "metro-core": "0.75.1",
43
+ "metro-file-map": "0.75.1",
44
+ "metro-hermes-compiler": "0.75.1",
45
+ "metro-inspector-proxy": "0.75.1",
46
+ "metro-minify-terser": "0.75.1",
47
+ "metro-minify-uglify": "0.75.1",
48
+ "metro-react-native-babel-preset": "0.75.1",
49
+ "metro-resolver": "0.75.1",
50
+ "metro-runtime": "0.75.1",
51
+ "metro-source-map": "0.75.1",
52
+ "metro-symbolicate": "0.75.1",
53
+ "metro-transform-plugins": "0.75.1",
54
+ "metro-transform-worker": "0.75.1",
55
55
  "mime-types": "^2.1.27",
56
56
  "node-fetch": "^2.2.0",
57
57
  "nullthrows": "^1.1.1",
@@ -70,13 +70,16 @@
70
70
  "dedent": "^0.7.0",
71
71
  "jest-snapshot": "^26.5.2",
72
72
  "jest-snapshot-serializer-raw": "^1.2.0",
73
- "metro-babel-register": "0.74.1",
74
- "metro-memory-fs": "0.74.1",
75
- "metro-react-native-babel-preset": "0.74.1",
76
- "metro-react-native-babel-transformer": "0.74.1",
73
+ "metro-babel-register": "0.75.1",
74
+ "metro-memory-fs": "0.75.1",
75
+ "metro-react-native-babel-preset": "0.75.1",
76
+ "metro-react-native-babel-transformer": "0.75.1",
77
77
  "mock-req": "^0.2.0",
78
78
  "mock-res": "^0.6.0",
79
79
  "stack-trace": "^0.0.10"
80
80
  },
81
- "license": "MIT"
81
+ "license": "MIT",
82
+ "engines": {
83
+ "node": ">=14.17.0"
84
+ }
82
85
  }
@@ -11,7 +11,11 @@
11
11
 
12
12
  "use strict";
13
13
 
14
+ var _path = _interopRequireDefault(require("path"));
14
15
  var _Graph = require("./Graph");
16
+ function _interopRequireDefault(obj) {
17
+ return obj && obj.__esModule ? obj : { default: obj };
18
+ }
15
19
  const debug = require("debug")("Metro:DeltaCalculator");
16
20
  const { EventEmitter } = require("events");
17
21
 
@@ -25,6 +29,7 @@ class DeltaCalculator extends EventEmitter {
25
29
  _deletedFiles = new Set();
26
30
  _modifiedFiles = new Set();
27
31
  _addedFiles = new Set();
32
+ _requiresReset = false;
28
33
  constructor(entryPoints, changeEventSource, options) {
29
34
  super();
30
35
  this._options = options;
@@ -61,6 +66,7 @@ class DeltaCalculator extends EventEmitter {
61
66
  * which contain the modified/added modules and the removed modules.
62
67
  */
63
68
  async getDelta({ reset, shallow }) {
69
+ debug("Calculating delta (reset: %s, shallow: %s)", reset, shallow);
64
70
  // If there is already a build in progress, wait until it finish to start
65
71
  // processing a new one (delta server doesn't support concurrent builds).
66
72
  if (this._currentBuildPromise) {
@@ -76,6 +82,21 @@ class DeltaCalculator extends EventEmitter {
76
82
  this._deletedFiles = new Set();
77
83
  const addedFiles = this._addedFiles;
78
84
  this._addedFiles = new Set();
85
+ const requiresReset = this._requiresReset;
86
+ this._requiresReset = false;
87
+
88
+ // Revisit all files if changes require a graph reset - resolutions may be
89
+ // invalidated but we don't yet know which. This should be optimized in the
90
+ // future.
91
+ if (requiresReset) {
92
+ const markModified = (file) => {
93
+ if (!addedFiles.has(file) && !deletedFiles.has(file)) {
94
+ modifiedFiles.add(file);
95
+ }
96
+ };
97
+ this._graph.dependencies.forEach((_, key) => markModified(key));
98
+ this._graph.entryPoints.forEach(markModified);
99
+ }
79
100
 
80
101
  // Concurrent requests should reuse the same bundling process. To do so,
81
102
  // this method stores the promise as an instance variable, and then it's
@@ -148,6 +169,16 @@ class DeltaCalculator extends EventEmitter {
148
169
  */
149
170
  _handleFileChange = ({ type, filePath, metadata }, logger) => {
150
171
  debug("Handling %s: %s (type: %s)", type, filePath, metadata.type);
172
+ if (
173
+ metadata.type === "l" ||
174
+ (this._options.unstable_enablePackageExports &&
175
+ filePath.endsWith(_path.default.sep + "package.json"))
176
+ ) {
177
+ this._requiresReset = true;
178
+ this.emit("change", {
179
+ logger,
180
+ });
181
+ }
151
182
  let state;
152
183
  if (this._deletedFiles.has(filePath)) {
153
184
  state = "deleted";
@@ -209,13 +240,13 @@ class DeltaCalculator extends EventEmitter {
209
240
  // If a file has been deleted, we want to invalidate any other file that
210
241
  // depends on it, so we can process it and correctly return an error.
211
242
  deletedFiles.forEach((filePath) => {
212
- for (const path of this._graph.getModifiedModulesForDeletedPath(
243
+ for (const modifiedModulePath of this._graph.getModifiedModulesForDeletedPath(
213
244
  filePath
214
245
  )) {
215
246
  // Only mark the inverse dependency as modified if it's not already
216
247
  // marked as deleted (in that case we can just ignore it).
217
- if (!deletedFiles.has(path)) {
218
- modifiedFiles.add(path);
248
+ if (!deletedFiles.has(modifiedModulePath)) {
249
+ modifiedFiles.add(modifiedModulePath);
219
250
  }
220
251
  }
221
252
  });
@@ -11,6 +11,7 @@
11
11
 
12
12
  'use strict';
13
13
 
14
+ import path from 'path';
14
15
  import {Graph} from './Graph';
15
16
  import type {DeltaResult, Options} from './types.flow';
16
17
  import type {RootPerfLogger} from 'metro-config';
@@ -33,6 +34,7 @@ class DeltaCalculator<T> extends EventEmitter {
33
34
  _deletedFiles: Set<string> = new Set();
34
35
  _modifiedFiles: Set<string> = new Set();
35
36
  _addedFiles: Set<string> = new Set();
37
+ _requiresReset = false;
36
38
 
37
39
  _graph: Graph<T>;
38
40
 
@@ -87,6 +89,7 @@ class DeltaCalculator<T> extends EventEmitter {
87
89
  shallow: boolean,
88
90
  ...
89
91
  }): Promise<DeltaResult<T>> {
92
+ debug('Calculating delta (reset: %s, shallow: %s)', reset, shallow);
90
93
  // If there is already a build in progress, wait until it finish to start
91
94
  // processing a new one (delta server doesn't support concurrent builds).
92
95
  if (this._currentBuildPromise) {
@@ -102,6 +105,21 @@ class DeltaCalculator<T> extends EventEmitter {
102
105
  this._deletedFiles = new Set();
103
106
  const addedFiles = this._addedFiles;
104
107
  this._addedFiles = new Set();
108
+ const requiresReset = this._requiresReset;
109
+ this._requiresReset = false;
110
+
111
+ // Revisit all files if changes require a graph reset - resolutions may be
112
+ // invalidated but we don't yet know which. This should be optimized in the
113
+ // future.
114
+ if (requiresReset) {
115
+ const markModified = (file: string) => {
116
+ if (!addedFiles.has(file) && !deletedFiles.has(file)) {
117
+ modifiedFiles.add(file);
118
+ }
119
+ };
120
+ this._graph.dependencies.forEach((_, key) => markModified(key));
121
+ this._graph.entryPoints.forEach(markModified);
122
+ }
105
123
 
106
124
  // Concurrent requests should reuse the same bundling process. To do so,
107
125
  // this method stores the promise as an instance variable, and then it's
@@ -190,6 +208,14 @@ class DeltaCalculator<T> extends EventEmitter {
190
208
  logger: ?RootPerfLogger,
191
209
  ): mixed => {
192
210
  debug('Handling %s: %s (type: %s)', type, filePath, metadata.type);
211
+ if (
212
+ metadata.type === 'l' ||
213
+ (this._options.unstable_enablePackageExports &&
214
+ filePath.endsWith(path.sep + 'package.json'))
215
+ ) {
216
+ this._requiresReset = true;
217
+ this.emit('change', {logger});
218
+ }
193
219
  let state: void | 'deleted' | 'modified' | 'added';
194
220
  if (this._deletedFiles.has(filePath)) {
195
221
  state = 'deleted';
@@ -259,13 +285,13 @@ class DeltaCalculator<T> extends EventEmitter {
259
285
  // If a file has been deleted, we want to invalidate any other file that
260
286
  // depends on it, so we can process it and correctly return an error.
261
287
  deletedFiles.forEach((filePath: string) => {
262
- for (const path of this._graph.getModifiedModulesForDeletedPath(
288
+ for (const modifiedModulePath of this._graph.getModifiedModulesForDeletedPath(
263
289
  filePath,
264
290
  )) {
265
291
  // Only mark the inverse dependency as modified if it's not already
266
292
  // marked as deleted (in that case we can just ignore it).
267
- if (!deletedFiles.has(path)) {
268
- modifiedFiles.add(path);
293
+ if (!deletedFiles.has(modifiedModulePath)) {
294
+ modifiedFiles.add(modifiedModulePath);
269
295
  }
270
296
  }
271
297
  });
@@ -78,6 +78,7 @@ class WorkerFarm {
78
78
  return new JestWorker(workerPath, {
79
79
  computeWorkerKey: this._config.stickyWorkers
80
80
  ? // $FlowFixMe[method-unbinding] added when improving typing for this parameters
81
+ // $FlowFixMe[incompatible-call]
81
82
  this._computeWorkerKey
82
83
  : undefined,
83
84
  exposedMethods,
@@ -120,6 +120,7 @@ class WorkerFarm {
120
120
  return new JestWorker(workerPath, {
121
121
  computeWorkerKey: this._config.stickyWorkers
122
122
  ? // $FlowFixMe[method-unbinding] added when improving typing for this parameters
123
+ // $FlowFixMe[incompatible-call]
123
124
  this._computeWorkerKey
124
125
  : undefined,
125
126
  exposedMethods,
@@ -138,6 +138,7 @@ export type Options<T = MixedOutput> = {
138
138
  +onProgress: ?(numProcessed: number, total: number) => mixed,
139
139
  +experimentalImportBundleSupport: boolean,
140
140
  +unstable_allowRequireContext: boolean,
141
+ +unstable_enablePackageExports: boolean,
141
142
  +shallow: boolean,
142
143
  };
143
144
 
@@ -81,6 +81,8 @@ class IncrementalBundler {
81
81
  this._config.server.experimentalImportBundleSupport,
82
82
  unstable_allowRequireContext:
83
83
  this._config.transformer.unstable_allowRequireContext,
84
+ unstable_enablePackageExports:
85
+ this._config.resolver.unstable_enablePackageExports,
84
86
  shallow: otherOptions.shallow,
85
87
  });
86
88
  this._config.serializer.experimentalSerializerHook(graph, {
@@ -123,6 +125,8 @@ class IncrementalBundler {
123
125
  this._config.server.experimentalImportBundleSupport,
124
126
  unstable_allowRequireContext:
125
127
  this._config.transformer.unstable_allowRequireContext,
128
+ unstable_enablePackageExports:
129
+ this._config.resolver.unstable_enablePackageExports,
126
130
  shallow: otherOptions.shallow,
127
131
  }
128
132
  );
@@ -130,6 +130,8 @@ class IncrementalBundler {
130
130
  this._config.server.experimentalImportBundleSupport,
131
131
  unstable_allowRequireContext:
132
132
  this._config.transformer.unstable_allowRequireContext,
133
+ unstable_enablePackageExports:
134
+ this._config.resolver.unstable_enablePackageExports,
133
135
  shallow: otherOptions.shallow,
134
136
  });
135
137
 
@@ -176,6 +178,8 @@ class IncrementalBundler {
176
178
  this._config.server.experimentalImportBundleSupport,
177
179
  unstable_allowRequireContext:
178
180
  this._config.transformer.unstable_allowRequireContext,
181
+ unstable_enablePackageExports:
182
+ this._config.resolver.unstable_enablePackageExports,
179
183
  shallow: otherOptions.shallow,
180
184
  },
181
185
  );
package/src/Server.js CHANGED
@@ -392,7 +392,7 @@ class Server {
392
392
  bundlePerfLogger:
393
393
  (_this$_config$unstabl =
394
394
  (_this$_config$unstabl2 = (_this$_config = this._config)
395
- .unstable_perfLogger) === null ||
395
+ .unstable_perfLoggerFactory) === null ||
396
396
  _this$_config$unstabl2 === void 0
397
397
  ? void 0
398
398
  : _this$_config$unstabl2.call(
@@ -668,6 +668,11 @@ class Server {
668
668
  });
669
669
  const revPromise = this._bundler.getRevisionByGraphId(graphId);
670
670
  bundlePerfLogger.point("resolvingAndTransformingDependencies_start");
671
+ bundlePerfLogger.annotate({
672
+ bool: {
673
+ initial_build: revPromise == null,
674
+ },
675
+ });
671
676
  const { delta, revision } = await (revPromise != null
672
677
  ? this._bundler.updateGraph(await revPromise, false)
673
678
  : this._bundler.initializeGraph(
@@ -519,7 +519,7 @@ class Server {
519
519
  await this._processBundleRequest(req, res, options, {
520
520
  buildNumber,
521
521
  bundlePerfLogger:
522
- this._config.unstable_perfLogger?.('BUNDLING_REQUEST', {
522
+ this._config.unstable_perfLoggerFactory?.('BUNDLING_REQUEST', {
523
523
  key: buildNumber,
524
524
  }) ?? noopLogger,
525
525
  });
@@ -832,6 +832,11 @@ class Server {
832
832
  const revPromise = this._bundler.getRevisionByGraphId(graphId);
833
833
 
834
834
  bundlePerfLogger.point('resolvingAndTransformingDependencies_start');
835
+ bundlePerfLogger.annotate({
836
+ bool: {
837
+ initial_build: revPromise == null,
838
+ },
839
+ });
835
840
  const {delta, revision} = await (revPromise != null
836
841
  ? this._bundler.updateGraph(await revPromise, false)
837
842
  : this._bundler.initializeGraph(
@@ -13,6 +13,7 @@ var _utils = require("./utils");
13
13
 
14
14
  function main() {
15
15
  return (0, _utils.copyContextToObject)(
16
+ // $FlowFixMe[underconstrained-implicit-instantiation]
16
17
  require.context("./subdir", undefined, undefined, "sync")
17
18
  );
18
19
  }
@@ -15,6 +15,7 @@ declare var require: RequireWithContext;
15
15
 
16
16
  function main(): mixed {
17
17
  return copyContextToObject(
18
+ // $FlowFixMe[underconstrained-implicit-instantiation]
18
19
  require.context('./subdir', undefined, undefined, 'sync'),
19
20
  );
20
21
  }
@@ -203,6 +203,9 @@ class TerminalReporter {
203
203
  case "global_cache_disabled":
204
204
  this._logCacheDisabled(event.reason);
205
205
  break;
206
+ case "resolver_warning":
207
+ this._logWarning(event.message);
208
+ break;
206
209
  case "transform_cache_reset":
207
210
  reporting.logWarning(this.terminal, "the transform cache was reset.");
208
211
  break;
@@ -360,6 +363,9 @@ class TerminalReporter {
360
363
  e
361
364
  );
362
365
  }
366
+ _logWarning(message) {
367
+ reporting.logWarning(this.terminal, message);
368
+ }
363
369
  _logWatcherHealthCheckResult(result) {
364
370
  var _result$pauseReason, _this$_prevHealthChec;
365
371
  // Don't be spammy; only report changes in status.
@@ -260,6 +260,9 @@ class TerminalReporter {
260
260
  case 'global_cache_disabled':
261
261
  this._logCacheDisabled(event.reason);
262
262
  break;
263
+ case 'resolver_warning':
264
+ this._logWarning(event.message);
265
+ break;
263
266
  case 'transform_cache_reset':
264
267
  reporting.logWarning(this.terminal, 'the transform cache was reset.');
265
268
  break;
@@ -435,6 +438,10 @@ class TerminalReporter {
435
438
  );
436
439
  }
437
440
 
441
+ _logWarning(message: string): void {
442
+ reporting.logWarning(this.terminal, message);
443
+ }
444
+
438
445
  _logWatcherHealthCheckResult(result: HealthCheckResult) {
439
446
  // Don't be spammy; only report changes in status.
440
447
  if (
@@ -60,6 +60,7 @@ function formatBundlingError(error) {
60
60
  return {
61
61
  type: "ResourceNotFoundError",
62
62
  // $FlowFixMe[missing-empty-array-annot]
63
+ // $FlowFixMe[incompatible-return]
63
64
  errors: [],
64
65
  message: error.message,
65
66
  };
@@ -67,6 +68,7 @@ function formatBundlingError(error) {
67
68
  return {
68
69
  type: "GraphNotFoundError",
69
70
  // $FlowFixMe[missing-empty-array-annot]
71
+ // $FlowFixMe[incompatible-return]
70
72
  errors: [],
71
73
  message: error.message,
72
74
  };
@@ -74,6 +76,7 @@ function formatBundlingError(error) {
74
76
  return {
75
77
  type: "RevisionNotFoundError",
76
78
  // $FlowFixMe[missing-empty-array-annot]
79
+ // $FlowFixMe[incompatible-return]
77
80
  errors: [],
78
81
  message: error.message,
79
82
  };
@@ -75,6 +75,7 @@ function formatBundlingError(error: CustomError): FormattedError {
75
75
  return {
76
76
  type: 'ResourceNotFoundError',
77
77
  // $FlowFixMe[missing-empty-array-annot]
78
+ // $FlowFixMe[incompatible-return]
78
79
  errors: [],
79
80
  message: error.message,
80
81
  };
@@ -82,6 +83,7 @@ function formatBundlingError(error: CustomError): FormattedError {
82
83
  return {
83
84
  type: 'GraphNotFoundError',
84
85
  // $FlowFixMe[missing-empty-array-annot]
86
+ // $FlowFixMe[incompatible-return]
85
87
  errors: [],
86
88
  message: error.message,
87
89
  };
@@ -89,6 +91,7 @@ function formatBundlingError(error: CustomError): FormattedError {
89
91
  return {
90
92
  type: 'RevisionNotFoundError',
91
93
  // $FlowFixMe[missing-empty-array-annot]
94
+ // $FlowFixMe[incompatible-return]
92
95
  errors: [],
93
96
  message: error.message,
94
97
  };
@@ -59,6 +59,8 @@ async function getPrependedScripts(
59
59
  onProgress: null,
60
60
  experimentalImportBundleSupport:
61
61
  config.server.experimentalImportBundleSupport,
62
+ unstable_enablePackageExports:
63
+ config.resolver.unstable_enablePackageExports,
62
64
  shallow: false,
63
65
  }
64
66
  );
@@ -69,6 +69,8 @@ async function getPrependedScripts(
69
69
  onProgress: null,
70
70
  experimentalImportBundleSupport:
71
71
  config.server.experimentalImportBundleSupport,
72
+ unstable_enablePackageExports:
73
+ config.resolver.unstable_enablePackageExports,
72
74
  shallow: false,
73
75
  },
74
76
  );
@@ -127,6 +127,10 @@ export type ReportableEvent =
127
127
  mode: 'BRIDGE' | 'NOBRIDGE',
128
128
  ...
129
129
  }
130
+ | {
131
+ type: 'resolver_warning',
132
+ message: string,
133
+ }
130
134
  | {
131
135
  type: 'transformer_load_started',
132
136
  }
@@ -67,6 +67,8 @@ async function calcTransformerOptions(
67
67
  config.server.experimentalImportBundleSupport,
68
68
  unstable_allowRequireContext:
69
69
  config.transformer.unstable_allowRequireContext,
70
+ unstable_enablePackageExports:
71
+ config.resolver.unstable_enablePackageExports,
70
72
  shallow: false,
71
73
  });
72
74
  return Array.from(dependencies.keys());
@@ -90,6 +90,8 @@ async function calcTransformerOptions(
90
90
  config.server.experimentalImportBundleSupport,
91
91
  unstable_allowRequireContext:
92
92
  config.transformer.unstable_allowRequireContext,
93
+ unstable_enablePackageExports:
94
+ config.resolver.unstable_enablePackageExports,
93
95
  shallow: false,
94
96
  });
95
97
 
@@ -15,6 +15,7 @@ const { codeFrameColumns } = require("@babel/code-frame");
15
15
  const fs = require("fs");
16
16
  const invariant = require("invariant");
17
17
  const Resolver = require("metro-resolver");
18
+ const createDefaultContext = require("metro-resolver/src/createDefaultContext");
18
19
  const path = require("path");
19
20
  const util = require("util");
20
21
  class ModuleResolver {
@@ -52,53 +53,6 @@ class ModuleResolver {
52
53
  }
53
54
  return emptyModule;
54
55
  }
55
- _redirectRequire(fromModule, modulePath) {
56
- const moduleCache = this._options.moduleCache;
57
- try {
58
- if (modulePath.startsWith(".")) {
59
- const fromPackage = fromModule.getPackage();
60
- if (fromPackage) {
61
- // We need to convert the module path from module-relative to
62
- // package-relative, so that we can easily match it against the
63
- // "browser" map (where all paths are relative to the package root)
64
- const fromPackagePath =
65
- "./" +
66
- path.relative(
67
- path.dirname(fromPackage.path),
68
- path.resolve(path.dirname(fromModule.path), modulePath)
69
- );
70
- let redirectedPath = fromPackage.redirectRequire(
71
- fromPackagePath,
72
- this._options.mainFields
73
- );
74
-
75
- // Since the redirected path is still relative to the package root,
76
- // we have to transform it back to be module-relative (as it
77
- // originally was)
78
- if (redirectedPath !== false) {
79
- redirectedPath =
80
- "./" +
81
- path.relative(
82
- path.dirname(fromModule.path),
83
- path.resolve(path.dirname(fromPackage.path), redirectedPath)
84
- );
85
- }
86
- return redirectedPath;
87
- }
88
- } else {
89
- const pck = path.isAbsolute(modulePath)
90
- ? moduleCache.getPackageOf(modulePath)
91
- : fromModule.getPackage();
92
- if (pck) {
93
- return pck.redirectRequire(modulePath, this._options.mainFields);
94
- }
95
- }
96
- } catch (err) {
97
- // Do nothing. The standard module cache does not trigger any error, but
98
- // the ModuleGraph one does, if the module does not exist.
99
- }
100
- return modulePath;
101
- }
102
56
  resolveDependency(
103
57
  fromModule,
104
58
  moduleName,
@@ -106,29 +60,56 @@ class ModuleResolver {
106
60
  platform,
107
61
  resolverOptions
108
62
  ) {
63
+ const {
64
+ disableHierarchicalLookup,
65
+ doesFileExist,
66
+ extraNodeModules,
67
+ isAssetFile,
68
+ mainFields,
69
+ nodeModulesPaths,
70
+ preferNativePlatform,
71
+ resolveAsset,
72
+ resolveRequest,
73
+ sourceExts,
74
+ unstable_conditionNames,
75
+ unstable_conditionsByPlatform,
76
+ unstable_enablePackageExports,
77
+ unstable_getRealPath,
78
+ } = this._options;
109
79
  try {
110
80
  var _resolverOptions$cust;
111
81
  const result = Resolver.resolve(
112
- {
113
- ...this._options,
82
+ createDefaultContext({
83
+ allowHaste,
84
+ disableHierarchicalLookup,
85
+ doesFileExist,
86
+ extraNodeModules,
87
+ isAssetFile,
88
+ mainFields,
89
+ nodeModulesPaths,
90
+ preferNativePlatform,
91
+ resolveAsset,
92
+ resolveRequest,
93
+ sourceExts,
94
+ unstable_conditionNames,
95
+ unstable_conditionsByPlatform,
96
+ unstable_enablePackageExports,
97
+ unstable_getRealPath,
98
+ unstable_logWarning: this._logWarning,
114
99
  customResolverOptions:
115
100
  (_resolverOptions$cust = resolverOptions.customResolverOptions) !==
116
101
  null && _resolverOptions$cust !== void 0
117
102
  ? _resolverOptions$cust
118
103
  : {},
119
104
  originModulePath: fromModule.path,
120
- redirectModulePath: (modulePath) =>
121
- this._redirectRequire(fromModule, modulePath),
122
- allowHaste,
123
- platform,
124
105
  resolveHasteModule: (name) =>
125
106
  this._options.getHasteModulePath(name, platform),
126
107
  resolveHastePackage: (name) =>
127
108
  this._options.getHastePackagePath(name, platform),
128
- getPackageMainPath: this._getPackageMainPath,
129
- unstable_enablePackageExports:
130
- this._options.unstable_enablePackageExports,
131
- },
109
+ getPackage: this._getPackage,
110
+ getPackageForModule: (modulePath) =>
111
+ this._getPackageForModule(fromModule, modulePath),
112
+ }),
132
113
  moduleName,
133
114
  platform
134
115
  );
@@ -176,9 +157,29 @@ class ModuleResolver {
176
157
  throw error;
177
158
  }
178
159
  }
179
- _getPackageMainPath = (packageJsonPath) => {
180
- const package_ = this._options.moduleCache.getPackage(packageJsonPath);
181
- return package_.getMain(this._options.mainFields);
160
+ _getPackage = (packageJsonPath) => {
161
+ try {
162
+ return this._options.moduleCache.getPackage(packageJsonPath).read();
163
+ } catch (e) {
164
+ // Do nothing. The standard module cache does not trigger any error, but
165
+ // the ModuleGraph one does, if the module does not exist.
166
+ }
167
+ return null;
168
+ };
169
+ _getPackageForModule = (fromModule, modulePath) => {
170
+ let pkg;
171
+ try {
172
+ pkg = this._options.moduleCache.getPackageOf(modulePath);
173
+ } catch (e) {
174
+ // Do nothing. The standard module cache does not trigger any error, but
175
+ // the ModuleGraph one does, if the module does not exist.
176
+ }
177
+ return pkg != null
178
+ ? {
179
+ rootPath: path.dirname(pkg.path),
180
+ packageJson: pkg.read(),
181
+ }
182
+ : null;
182
183
  };
183
184
 
184
185
  /**
@@ -204,6 +205,12 @@ class ModuleResolver {
204
205
  throw new Error("invalid type");
205
206
  }
206
207
  }
208
+ _logWarning = (message) => {
209
+ this._options.reporter.update({
210
+ type: "resolver_warning",
211
+ message,
212
+ });
213
+ };
207
214
  _removeRoot(candidates) {
208
215
  if (candidates.filePathPrefix) {
209
216
  candidates.filePathPrefix = path.relative(
@@ -15,29 +15,29 @@ import type {
15
15
  CustomResolver,
16
16
  DoesFileExist,
17
17
  FileCandidates,
18
+ GetRealPath,
18
19
  IsAssetFile,
19
20
  Resolution,
20
21
  ResolveAsset,
21
22
  } from 'metro-resolver';
23
+ import type {ResolverInputOptions} from '../../shared/types.flow';
24
+ import type {PackageInfo, PackageJson} from 'metro-resolver/src/types';
22
25
 
23
26
  const {codeFrameColumns} = require('@babel/code-frame');
24
27
  const fs = require('fs');
25
28
  const invariant = require('invariant');
26
29
  const Resolver = require('metro-resolver');
30
+ const createDefaultContext = require('metro-resolver/src/createDefaultContext');
27
31
  const path = require('path');
28
32
  const util = require('util');
29
- import type {ResolverInputOptions} from '../../shared/types.flow';
30
33
  import type {BundlerResolution} from '../../DeltaBundler/types.flow';
34
+ import type {Reporter} from '../../lib/reporting';
31
35
 
32
36
  export type DirExistsFn = (filePath: string) => boolean;
33
37
 
34
38
  export type Packageish = interface {
35
39
  path: string,
36
- redirectRequire(
37
- toModuleName: string,
38
- mainFields: $ReadOnlyArray<string>,
39
- ): string | false,
40
- getMain(mainFields: $ReadOnlyArray<string>): string,
40
+ read(): PackageJson,
41
41
  };
42
42
 
43
43
  export type Moduleish = interface {
@@ -68,6 +68,7 @@ type Options<TPackage> = $ReadOnly<{
68
68
  nodeModulesPaths: $ReadOnlyArray<string>,
69
69
  preferNativePlatform: boolean,
70
70
  projectRoot: string,
71
+ reporter: Reporter,
71
72
  resolveAsset: ResolveAsset,
72
73
  resolveRequest: ?CustomResolver,
73
74
  sourceExts: $ReadOnlyArray<string>,
@@ -76,6 +77,7 @@ type Options<TPackage> = $ReadOnly<{
76
77
  [platform: string]: $ReadOnlyArray<string>,
77
78
  }>,
78
79
  unstable_enablePackageExports: boolean,
80
+ unstable_getRealPath: ?GetRealPath,
79
81
  }>;
80
82
 
81
83
  class ModuleResolver<TPackage: Packageish> {
@@ -117,59 +119,6 @@ class ModuleResolver<TPackage: Packageish> {
117
119
  return emptyModule;
118
120
  }
119
121
 
120
- _redirectRequire(fromModule: Moduleish, modulePath: string): string | false {
121
- const moduleCache = this._options.moduleCache;
122
- try {
123
- if (modulePath.startsWith('.')) {
124
- const fromPackage = fromModule.getPackage();
125
-
126
- if (fromPackage) {
127
- // We need to convert the module path from module-relative to
128
- // package-relative, so that we can easily match it against the
129
- // "browser" map (where all paths are relative to the package root)
130
- const fromPackagePath =
131
- './' +
132
- path.relative(
133
- path.dirname(fromPackage.path),
134
- path.resolve(path.dirname(fromModule.path), modulePath),
135
- );
136
-
137
- let redirectedPath = fromPackage.redirectRequire(
138
- fromPackagePath,
139
- this._options.mainFields,
140
- );
141
-
142
- // Since the redirected path is still relative to the package root,
143
- // we have to transform it back to be module-relative (as it
144
- // originally was)
145
- if (redirectedPath !== false) {
146
- redirectedPath =
147
- './' +
148
- path.relative(
149
- path.dirname(fromModule.path),
150
- path.resolve(path.dirname(fromPackage.path), redirectedPath),
151
- );
152
- }
153
-
154
- return redirectedPath;
155
- }
156
- } else {
157
- const pck = path.isAbsolute(modulePath)
158
- ? moduleCache.getPackageOf(modulePath)
159
- : fromModule.getPackage();
160
-
161
- if (pck) {
162
- return pck.redirectRequire(modulePath, this._options.mainFields);
163
- }
164
- }
165
- } catch (err) {
166
- // Do nothing. The standard module cache does not trigger any error, but
167
- // the ModuleGraph one does, if the module does not exist.
168
- }
169
-
170
- return modulePath;
171
- }
172
-
173
122
  resolveDependency(
174
123
  fromModule: Moduleish,
175
124
  moduleName: string,
@@ -177,24 +126,52 @@ class ModuleResolver<TPackage: Packageish> {
177
126
  platform: string | null,
178
127
  resolverOptions: ResolverInputOptions,
179
128
  ): BundlerResolution {
129
+ const {
130
+ disableHierarchicalLookup,
131
+ doesFileExist,
132
+ extraNodeModules,
133
+ isAssetFile,
134
+ mainFields,
135
+ nodeModulesPaths,
136
+ preferNativePlatform,
137
+ resolveAsset,
138
+ resolveRequest,
139
+ sourceExts,
140
+ unstable_conditionNames,
141
+ unstable_conditionsByPlatform,
142
+ unstable_enablePackageExports,
143
+ unstable_getRealPath,
144
+ } = this._options;
145
+
180
146
  try {
181
147
  const result = Resolver.resolve(
182
- {
183
- ...this._options,
148
+ createDefaultContext({
149
+ allowHaste,
150
+ disableHierarchicalLookup,
151
+ doesFileExist,
152
+ extraNodeModules,
153
+ isAssetFile,
154
+ mainFields,
155
+ nodeModulesPaths,
156
+ preferNativePlatform,
157
+ resolveAsset,
158
+ resolveRequest,
159
+ sourceExts,
160
+ unstable_conditionNames,
161
+ unstable_conditionsByPlatform,
162
+ unstable_enablePackageExports,
163
+ unstable_getRealPath,
164
+ unstable_logWarning: this._logWarning,
184
165
  customResolverOptions: resolverOptions.customResolverOptions ?? {},
185
166
  originModulePath: fromModule.path,
186
- redirectModulePath: (modulePath: string) =>
187
- this._redirectRequire(fromModule, modulePath),
188
- allowHaste,
189
- platform,
190
167
  resolveHasteModule: (name: string) =>
191
168
  this._options.getHasteModulePath(name, platform),
192
169
  resolveHastePackage: (name: string) =>
193
170
  this._options.getHastePackagePath(name, platform),
194
- getPackageMainPath: this._getPackageMainPath,
195
- unstable_enablePackageExports:
196
- this._options.unstable_enablePackageExports,
197
- },
171
+ getPackage: this._getPackage,
172
+ getPackageForModule: (modulePath: string) =>
173
+ this._getPackageForModule(fromModule, modulePath),
174
+ }),
198
175
  moduleName,
199
176
  platform,
200
177
  );
@@ -245,9 +222,36 @@ class ModuleResolver<TPackage: Packageish> {
245
222
  }
246
223
  }
247
224
 
248
- _getPackageMainPath = (packageJsonPath: string): string => {
249
- const package_ = this._options.moduleCache.getPackage(packageJsonPath);
250
- return package_.getMain(this._options.mainFields);
225
+ _getPackage = (packageJsonPath: string): ?PackageJson => {
226
+ try {
227
+ return this._options.moduleCache.getPackage(packageJsonPath).read();
228
+ } catch (e) {
229
+ // Do nothing. The standard module cache does not trigger any error, but
230
+ // the ModuleGraph one does, if the module does not exist.
231
+ }
232
+
233
+ return null;
234
+ };
235
+
236
+ _getPackageForModule = (
237
+ fromModule: Moduleish,
238
+ modulePath: string,
239
+ ): ?PackageInfo => {
240
+ let pkg;
241
+
242
+ try {
243
+ pkg = this._options.moduleCache.getPackageOf(modulePath);
244
+ } catch (e) {
245
+ // Do nothing. The standard module cache does not trigger any error, but
246
+ // the ModuleGraph one does, if the module does not exist.
247
+ }
248
+
249
+ return pkg != null
250
+ ? {
251
+ rootPath: path.dirname(pkg.path),
252
+ packageJson: pkg.read(),
253
+ }
254
+ : null;
251
255
  };
252
256
 
253
257
  /**
@@ -271,6 +275,13 @@ class ModuleResolver<TPackage: Packageish> {
271
275
  }
272
276
  }
273
277
 
278
+ _logWarning = (message: string): void => {
279
+ this._options.reporter.update({
280
+ type: 'resolver_warning',
281
+ message,
282
+ });
283
+ };
284
+
274
285
  _removeRoot(candidates: FileCandidates): FileCandidates {
275
286
  if (candidates.filePathPrefix) {
276
287
  candidates.filePathPrefix = path.relative(
@@ -108,7 +108,7 @@ function createHasteMap(config, options) {
108
108
  : options.cacheFilePrefix,
109
109
  });
110
110
  },
111
- perfLoggerFactory: config.unstable_perfLogger,
111
+ perfLoggerFactory: config.unstable_perfLoggerFactory,
112
112
  computeDependencies,
113
113
  computeSha1: true,
114
114
  dependencyExtractor: config.resolver.dependencyExtractor,
@@ -66,7 +66,7 @@ function createHasteMap(
66
66
  config.fileMapCacheDirectory ?? config.hasteMapCacheDirectory,
67
67
  cacheFilePrefix: options?.cacheFilePrefix,
68
68
  })),
69
- perfLoggerFactory: config.unstable_perfLogger,
69
+ perfLoggerFactory: config.unstable_perfLoggerFactory,
70
70
  computeDependencies,
71
71
  computeSha1: true,
72
72
  dependencyExtractor: config.resolver.dependencyExtractor,
@@ -150,14 +150,24 @@ class DependencyGraph extends EventEmitter {
150
150
  nodeModulesPaths: this._config.resolver.nodeModulesPaths,
151
151
  preferNativePlatform: true,
152
152
  projectRoot: this._config.projectRoot,
153
+ reporter: this._config.reporter,
153
154
  resolveAsset: (dirPath, assetName, extension) => {
154
155
  const basePath = dirPath + path.sep + assetName;
155
- const assets = [
156
+ let assets = [
156
157
  basePath + extension,
157
158
  ...this._config.resolver.assetResolutions.map(
158
159
  (resolution) => basePath + "@" + resolution + "x" + extension
159
160
  ),
160
- ].filter((candidate) => this._fileSystem.exists(candidate));
161
+ ];
162
+ if (this._config.resolver.unstable_enableSymlinks) {
163
+ assets = assets
164
+ .map((candidate) => this._fileSystem.getRealPath(candidate))
165
+ .filter(Boolean);
166
+ } else {
167
+ assets = assets.filter((candidate) =>
168
+ this._fileSystem.exists(candidate)
169
+ );
170
+ }
161
171
  return assets.length ? assets : null;
162
172
  },
163
173
  resolveRequest: this._config.resolver.resolveRequest,
@@ -167,6 +177,9 @@ class DependencyGraph extends EventEmitter {
167
177
  this._config.resolver.unstable_conditionsByPlatform,
168
178
  unstable_enablePackageExports:
169
179
  this._config.resolver.unstable_enablePackageExports,
180
+ unstable_getRealPath: this._config.resolver.unstable_enableSymlinks
181
+ ? (path) => this._fileSystem.getRealPath(path)
182
+ : null,
170
183
  });
171
184
  }
172
185
  _createModuleCache() {
@@ -186,14 +199,18 @@ class DependencyGraph extends EventEmitter {
186
199
  const containerName =
187
200
  splitIndex !== -1 ? filename.slice(0, splitIndex + 4) : filename;
188
201
 
189
- // TODO Calling realpath allows us to get a hash for a given path even when
202
+ // Prior to unstable_enableSymlinks:
203
+ // Calling realpath allows us to get a hash for a given path even when
190
204
  // it's a symlink to a file, which prevents Metro from crashing in such a
191
205
  // case. However, it doesn't allow Metro to track changes to the target file
192
206
  // of the symlink. We should fix this by implementing a symlink map into
193
207
  // Metro (or maybe by implementing those "extra transformation sources" we've
194
208
  // been talking about for stuff like CSS or WASM).
195
-
196
- const resolvedPath = fs.realpathSync(containerName);
209
+ //
210
+ // This is unnecessary with a symlink-aware fileSystem implementation.
211
+ const resolvedPath = this._config.resolver.unstable_enableSymlinks
212
+ ? containerName
213
+ : fs.realpathSync(containerName);
197
214
  const sha1 = this._fileSystem.getSha1(resolvedPath);
198
215
  if (!sha1) {
199
216
  throw new ReferenceError(`SHA-1 for file ${filename} (${resolvedPath}) is not computed.
@@ -197,14 +197,26 @@ class DependencyGraph extends EventEmitter {
197
197
  nodeModulesPaths: this._config.resolver.nodeModulesPaths,
198
198
  preferNativePlatform: true,
199
199
  projectRoot: this._config.projectRoot,
200
+ reporter: this._config.reporter,
200
201
  resolveAsset: (dirPath: string, assetName: string, extension: string) => {
201
202
  const basePath = dirPath + path.sep + assetName;
202
- const assets = [
203
+ let assets = [
203
204
  basePath + extension,
204
205
  ...this._config.resolver.assetResolutions.map(
205
206
  resolution => basePath + '@' + resolution + 'x' + extension,
206
207
  ),
207
- ].filter(candidate => this._fileSystem.exists(candidate));
208
+ ];
209
+
210
+ if (this._config.resolver.unstable_enableSymlinks) {
211
+ assets = assets
212
+ .map(candidate => this._fileSystem.getRealPath(candidate))
213
+ .filter(Boolean);
214
+ } else {
215
+ assets = assets.filter(candidate =>
216
+ this._fileSystem.exists(candidate),
217
+ );
218
+ }
219
+
208
220
  return assets.length ? assets : null;
209
221
  },
210
222
  resolveRequest: this._config.resolver.resolveRequest,
@@ -214,6 +226,9 @@ class DependencyGraph extends EventEmitter {
214
226
  this._config.resolver.unstable_conditionsByPlatform,
215
227
  unstable_enablePackageExports:
216
228
  this._config.resolver.unstable_enablePackageExports,
229
+ unstable_getRealPath: this._config.resolver.unstable_enableSymlinks
230
+ ? path => this._fileSystem.getRealPath(path)
231
+ : null,
217
232
  });
218
233
  }
219
234
 
@@ -236,14 +251,19 @@ class DependencyGraph extends EventEmitter {
236
251
  const containerName =
237
252
  splitIndex !== -1 ? filename.slice(0, splitIndex + 4) : filename;
238
253
 
239
- // TODO Calling realpath allows us to get a hash for a given path even when
254
+ // Prior to unstable_enableSymlinks:
255
+ // Calling realpath allows us to get a hash for a given path even when
240
256
  // it's a symlink to a file, which prevents Metro from crashing in such a
241
257
  // case. However, it doesn't allow Metro to track changes to the target file
242
258
  // of the symlink. We should fix this by implementing a symlink map into
243
259
  // Metro (or maybe by implementing those "extra transformation sources" we've
244
260
  // been talking about for stuff like CSS or WASM).
261
+ //
262
+ // This is unnecessary with a symlink-aware fileSystem implementation.
263
+ const resolvedPath = this._config.resolver.unstable_enableSymlinks
264
+ ? containerName
265
+ : fs.realpathSync(containerName);
245
266
 
246
- const resolvedPath = fs.realpathSync(containerName);
247
267
  const sha1 = this._fileSystem.getSha1(resolvedPath);
248
268
 
249
269
  if (!sha1) {
@@ -19,85 +19,9 @@ class Package {
19
19
  this._root = path.dirname(this.path);
20
20
  this._content = null;
21
21
  }
22
-
23
- /**
24
- * The `browser` field and replacement behavior is specified in
25
- * https://github.com/defunctzombie/package-browser-field-spec.
26
- */
27
- getMain(mainFields) {
28
- const json = this.read();
29
- let main;
30
- for (const name of mainFields) {
31
- if (typeof json[name] === "string") {
32
- main = json[name];
33
- break;
34
- }
35
- }
36
-
37
- // flowlint-next-line sketchy-null-string:off
38
- if (!main) {
39
- main = "index";
40
- }
41
- const replacements = getReplacements(json, mainFields);
42
- if (replacements) {
43
- const variants = [main];
44
- if (main.slice(0, 2) === "./") {
45
- variants.push(main.slice(2));
46
- } else {
47
- variants.push("./" + main);
48
- }
49
- for (const variant of variants) {
50
- const winner =
51
- replacements[variant] ||
52
- replacements[variant + ".js"] ||
53
- replacements[variant + ".json"] ||
54
- replacements[variant.replace(/(\.js|\.json)$/, "")];
55
- if (winner) {
56
- main = winner;
57
- break;
58
- }
59
- }
60
- }
61
- return path.join(this._root, main);
62
- }
63
22
  invalidate() {
64
23
  this._content = null;
65
24
  }
66
- redirectRequire(name, mainFields) {
67
- const json = this.read();
68
- const replacements = getReplacements(json, mainFields);
69
- if (!replacements) {
70
- return name;
71
- }
72
- if (!name.startsWith(".") && !path.isAbsolute(name)) {
73
- const replacement = replacements[name];
74
- // support exclude with "someDependency": false
75
- return replacement === false ? false : replacement || name;
76
- }
77
- let relPath =
78
- "./" + path.relative(this._root, path.resolve(this._root, name));
79
- if (path.sep !== "/") {
80
- relPath = relPath.replace(new RegExp("\\" + path.sep, "g"), "/");
81
- }
82
- let redirect = replacements[relPath];
83
-
84
- // false is a valid value
85
- if (redirect == null) {
86
- redirect = replacements[relPath + ".js"];
87
- if (redirect == null) {
88
- redirect = replacements[relPath + ".json"];
89
- }
90
- }
91
-
92
- // support exclude with "./someFile": false
93
- if (redirect === false) {
94
- return false;
95
- }
96
- if (redirect) {
97
- return path.join(this._root, redirect);
98
- }
99
- return name;
100
- }
101
25
  read() {
102
26
  if (this._content == null) {
103
27
  this._content = JSON.parse(fs.readFileSync(this.path, "utf8"));
@@ -105,20 +29,4 @@ class Package {
105
29
  return this._content;
106
30
  }
107
31
  }
108
- function getReplacements(pkg, mainFields) {
109
- const replacements = mainFields
110
- .map((name) => {
111
- // If the field is a string, that doesn't mean we want to redirect the
112
- // `main` file itself to anything else. See the spec.
113
- if (!pkg[name] || typeof pkg[name] === "string") {
114
- return null;
115
- }
116
- return pkg[name];
117
- })
118
- .filter(Boolean);
119
- if (!replacements.length) {
120
- return null;
121
- }
122
- return Object.assign({}, ...replacements.reverse());
123
- }
124
32
  module.exports = Package;
@@ -11,22 +11,16 @@
11
11
 
12
12
  'use strict';
13
13
 
14
+ import type {PackageJson} from 'metro-resolver/src/types';
15
+
14
16
  const fs = require('fs');
15
17
  const path = require('path');
16
18
 
17
- type PackageContent = {
18
- name: string,
19
- 'react-native': mixed,
20
- browser: mixed,
21
- main: ?string,
22
- ...
23
- };
24
-
25
19
  class Package {
26
20
  path: string;
27
21
 
28
22
  _root: string;
29
- _content: ?PackageContent;
23
+ _content: ?PackageJson;
30
24
 
31
25
  constructor({file}: {file: string, ...}) {
32
26
  this.path = path.resolve(file);
@@ -34,104 +28,11 @@ class Package {
34
28
  this._content = null;
35
29
  }
36
30
 
37
- /**
38
- * The `browser` field and replacement behavior is specified in
39
- * https://github.com/defunctzombie/package-browser-field-spec.
40
- */
41
- getMain(mainFields: $ReadOnlyArray<string>): string {
42
- const json = this.read();
43
-
44
- let main;
45
-
46
- for (const name of mainFields) {
47
- if (typeof json[name] === 'string') {
48
- main = json[name];
49
- break;
50
- }
51
- }
52
-
53
- // flowlint-next-line sketchy-null-string:off
54
- if (!main) {
55
- main = 'index';
56
- }
57
-
58
- const replacements = getReplacements(json, mainFields);
59
- if (replacements) {
60
- const variants = [main];
61
- if (main.slice(0, 2) === './') {
62
- variants.push(main.slice(2));
63
- } else {
64
- variants.push('./' + main);
65
- }
66
-
67
- for (const variant of variants) {
68
- const winner =
69
- replacements[variant] ||
70
- replacements[variant + '.js'] ||
71
- replacements[variant + '.json'] ||
72
- replacements[variant.replace(/(\.js|\.json)$/, '')];
73
-
74
- if (winner) {
75
- main = winner;
76
- break;
77
- }
78
- }
79
- }
80
-
81
- return path.join(this._root, main);
82
- }
83
-
84
31
  invalidate() {
85
32
  this._content = null;
86
33
  }
87
34
 
88
- redirectRequire(
89
- name: string,
90
- mainFields: $ReadOnlyArray<string>,
91
- ): string | false {
92
- const json = this.read();
93
- const replacements = getReplacements(json, mainFields);
94
-
95
- if (!replacements) {
96
- return name;
97
- }
98
-
99
- if (!name.startsWith('.') && !path.isAbsolute(name)) {
100
- const replacement = replacements[name];
101
- // support exclude with "someDependency": false
102
- return replacement === false ? false : replacement || name;
103
- }
104
-
105
- let relPath =
106
- './' + path.relative(this._root, path.resolve(this._root, name));
107
-
108
- if (path.sep !== '/') {
109
- relPath = relPath.replace(new RegExp('\\' + path.sep, 'g'), '/');
110
- }
111
-
112
- let redirect = replacements[relPath];
113
-
114
- // false is a valid value
115
- if (redirect == null) {
116
- redirect = replacements[relPath + '.js'];
117
- if (redirect == null) {
118
- redirect = replacements[relPath + '.json'];
119
- }
120
- }
121
-
122
- // support exclude with "./someFile": false
123
- if (redirect === false) {
124
- return false;
125
- }
126
-
127
- if (redirect) {
128
- return path.join(this._root, redirect);
129
- }
130
-
131
- return name;
132
- }
133
-
134
- read(): PackageContent {
35
+ read(): PackageJson {
135
36
  if (this._content == null) {
136
37
  this._content = JSON.parse(fs.readFileSync(this.path, 'utf8'));
137
38
  }
@@ -139,27 +40,4 @@ class Package {
139
40
  }
140
41
  }
141
42
 
142
- function getReplacements(
143
- pkg: PackageContent,
144
- mainFields: $ReadOnlyArray<string>,
145
- ): ?{[string]: string | false, ...} {
146
- const replacements = mainFields
147
- .map((name: string) => {
148
- // If the field is a string, that doesn't mean we want to redirect the
149
- // `main` file itself to anything else. See the spec.
150
- if (!pkg[name] || typeof pkg[name] === 'string') {
151
- return null;
152
- }
153
-
154
- return pkg[name];
155
- })
156
- .filter(Boolean);
157
-
158
- if (!replacements.length) {
159
- return null;
160
- }
161
-
162
- return Object.assign({}, ...replacements.reverse());
163
- }
164
-
165
43
  module.exports = Package;