metro-file-map 0.73.1 → 0.73.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -100,7 +100,7 @@ module.exports = async function watchmanCrawl({
100
100
  roots: $ReadOnlyArray<Path>,
101
101
  ): Promise<WatchmanRoots> {
102
102
  perfLogger?.point('watchmanCrawl/getWatchmanRoots_start');
103
- const watchmanRoots = new Map();
103
+ const watchmanRoots = new Map<string, Array<string>>();
104
104
  await Promise.all(
105
105
  roots.map(async (root, index) => {
106
106
  perfLogger?.point(`watchmanCrawl/watchProject_${index}_start`);
@@ -205,8 +205,8 @@ module.exports = async function watchmanCrawl({
205
205
  }
206
206
 
207
207
  let files = data.files;
208
- let removedFiles = new Map();
209
- const changedFiles = new Map();
208
+ let removedFiles = new Map<Path, FileMetaData>();
209
+ const changedFiles = new Map<Path, FileMetaData>();
210
210
  let results: Map<string, WatchmanQueryResponse>;
211
211
  let isFresh = false;
212
212
  let queryError: ?Error;
package/src/index.js CHANGED
@@ -52,15 +52,7 @@ var _rootRelativeCacheKeys = _interopRequireDefault(
52
52
 
53
53
  var _ModuleMap = _interopRequireDefault(require("./ModuleMap"));
54
54
 
55
- var _FSEventsWatcher = _interopRequireDefault(
56
- require("./watchers/FSEventsWatcher")
57
- );
58
-
59
- var _NodeWatcher = _interopRequireDefault(require("./watchers/NodeWatcher"));
60
-
61
- var _WatchmanWatcher = _interopRequireDefault(
62
- require("./watchers/WatchmanWatcher")
63
- );
55
+ var _Watcher = require("./Watcher");
64
56
 
65
57
  var _worker = require("./worker");
66
58
 
@@ -132,16 +124,9 @@ function _interopRequireDefault(obj) {
132
124
  * @format
133
125
  * @oncall react_native
134
126
  */
135
- // $FlowFixMe[untyped-import] - it's a fork: https://github.com/facebook/jest/pull/10919
136
127
  // $FlowFixMe[untyped-import] - jest-regex-util
137
128
  // $FlowFixMe[untyped-import] - jest-worker
138
129
  // $FlowFixMe[untyped-import] - this is a polyfill
139
- const nodeCrawl = require("./crawlers/node");
140
-
141
- const watchmanCrawl = require("./crawlers/watchman");
142
-
143
- const debug = require("debug")("Metro:FileMap");
144
-
145
130
  const DuplicateHasteCandidatesError =
146
131
  _ModuleMap.default.DuplicateHasteCandidatesError;
147
132
  exports.DuplicateHasteCandidatesError = DuplicateHasteCandidatesError;
@@ -150,7 +135,6 @@ exports.DuplicateHasteCandidatesError = DuplicateHasteCandidatesError;
150
135
  // filesystem state and build parameters).
151
136
  const CACHE_BREAKER = "2";
152
137
  const CHANGE_INTERVAL = 30;
153
- const MAX_WAIT_TIME = 240000;
154
138
  const NODE_MODULES = path.sep + "node_modules" + path.sep;
155
139
  const PACKAGE_JSON = path.sep + "package.json";
156
140
  const VCS_DIRECTORIES = [".git", ".hg"]
@@ -309,6 +293,7 @@ class HasteMap extends _events.default {
309
293
  };
310
294
  this._options = {
311
295
  ...buildParameters,
296
+ healthCheck: options.healthCheck,
312
297
  maxWorkers: options.maxWorkers,
313
298
  perfLogger: options.perfLogger,
314
299
  resetCache: options.resetCache,
@@ -337,7 +322,6 @@ class HasteMap extends _events.default {
337
322
  }
338
323
 
339
324
  this._buildPromise = null;
340
- this._watchers = [];
341
325
  this._worker = null;
342
326
  (_this$_options$perfLo = this._options.perfLogger) === null ||
343
327
  _this$_options$perfLo === void 0
@@ -511,7 +495,37 @@ class HasteMap extends _events.default {
511
495
  hasteMap = this._createEmptyMap();
512
496
  }
513
497
 
514
- return this._crawl(hasteMap).then((result) => {
498
+ const {
499
+ computeSha1,
500
+ enableSymlinks,
501
+ extensions,
502
+ forceNodeFilesystemAPI,
503
+ ignorePattern,
504
+ perfLogger,
505
+ roots,
506
+ rootDir,
507
+ watch,
508
+ watchmanDeferStates,
509
+ } = this._options;
510
+ this._watcher = new _Watcher.Watcher({
511
+ abortSignal: this._crawlerAbortController.signal,
512
+ computeSha1,
513
+ console: this._console,
514
+ enableSymlinks,
515
+ extensions,
516
+ forceNodeFilesystemAPI,
517
+ healthCheckFilePrefix: this._options.healthCheck.filePrefix,
518
+ ignore: (path) => this._ignore(path),
519
+ ignorePattern,
520
+ initialData: hasteMap,
521
+ perfLogger,
522
+ roots,
523
+ rootDir,
524
+ useWatchman: await this._shouldUseWatchman(),
525
+ watch,
526
+ watchmanDeferStates,
527
+ });
528
+ return this._watcher.crawl().then((result) => {
515
529
  var _this$_options$perfLo8;
516
530
 
517
531
  (_this$_options$perfLo8 = this._options.perfLogger) === null ||
@@ -893,154 +907,38 @@ class HasteMap extends _events.default {
893
907
 
894
908
  return this._worker;
895
909
  }
896
-
897
- async _crawl(hasteMap) {
898
- var _this$_options$perfLo13;
899
-
900
- (_this$_options$perfLo13 = this._options.perfLogger) === null ||
901
- _this$_options$perfLo13 === void 0
902
- ? void 0
903
- : _this$_options$perfLo13.point("crawl_start");
904
- const options = this._options;
905
-
906
- const ignore = (filePath) => this._ignore(filePath);
907
-
908
- const crawl = (await this._shouldUseWatchman()) ? watchmanCrawl : nodeCrawl;
909
- const crawlerOptions = {
910
- abortSignal: this._crawlerAbortController.signal,
911
- computeSha1: options.computeSha1,
912
- data: hasteMap,
913
- enableSymlinks: options.enableSymlinks,
914
- extensions: options.extensions,
915
- forceNodeFilesystemAPI: options.forceNodeFilesystemAPI,
916
- ignore,
917
- perfLogger: options.perfLogger,
918
- rootDir: options.rootDir,
919
- roots: options.roots,
920
- };
921
-
922
- const retry = (error) => {
923
- if (crawl === watchmanCrawl) {
924
- this._console.warn(
925
- "metro-file-map: Watchman crawl failed. Retrying once with node " +
926
- "crawler.\n" +
927
- " Usually this happens when watchman isn't running. Create an " +
928
- "empty `.watchmanconfig` file in your project's root folder or " +
929
- "initialize a git or hg repository in your project.\n" +
930
- " " +
931
- error.toString()
932
- );
933
-
934
- return nodeCrawl(crawlerOptions).catch((e) => {
935
- throw new Error(
936
- "Crawler retry failed:\n" +
937
- ` Original error: ${error.message}\n` +
938
- ` Retry error: ${e.message}\n`
939
- );
940
- });
941
- }
942
-
943
- throw error;
944
- };
945
-
946
- const logEnd = (result) => {
947
- var _this$_options$perfLo14;
948
-
949
- (_this$_options$perfLo14 = this._options.perfLogger) === null ||
950
- _this$_options$perfLo14 === void 0
951
- ? void 0
952
- : _this$_options$perfLo14.point("crawl_end");
953
- return result;
954
- };
955
-
956
- try {
957
- return crawl(crawlerOptions).catch(retry).then(logEnd);
958
- } catch (error) {
959
- return retry(error).then(logEnd);
960
- }
961
- }
962
910
  /**
963
911
  * Watch mode
964
912
  */
965
913
 
966
914
  async _watch(hasteMap) {
967
- var _this$_options$perfLo15, _this$_options$perfLo17;
915
+ var _this$_options$perfLo13, _this$_options$perfLo15;
968
916
 
969
- (_this$_options$perfLo15 = this._options.perfLogger) === null ||
970
- _this$_options$perfLo15 === void 0
917
+ (_this$_options$perfLo13 = this._options.perfLogger) === null ||
918
+ _this$_options$perfLo13 === void 0
971
919
  ? void 0
972
- : _this$_options$perfLo15.point("watch_start");
920
+ : _this$_options$perfLo13.point("watch_start");
973
921
 
974
922
  if (!this._options.watch) {
975
- var _this$_options$perfLo16;
923
+ var _this$_options$perfLo14;
976
924
 
977
- (_this$_options$perfLo16 = this._options.perfLogger) === null ||
978
- _this$_options$perfLo16 === void 0
925
+ (_this$_options$perfLo14 = this._options.perfLogger) === null ||
926
+ _this$_options$perfLo14 === void 0
979
927
  ? void 0
980
- : _this$_options$perfLo16.point("watch_end");
928
+ : _this$_options$perfLo14.point("watch_end");
981
929
  return;
982
930
  } // In watch mode, we'll only warn about module collisions and we'll retain
983
931
  // all files, even changes to node_modules.
984
932
 
985
933
  this._options.throwOnModuleCollision = false;
986
- this._options.retainAllFiles = true; // WatchmanWatcher > FSEventsWatcher > sane.NodeWatcher
987
-
988
- const WatcherImpl = (await this._shouldUseWatchman())
989
- ? _WatchmanWatcher.default
990
- : _FSEventsWatcher.default.isSupported()
991
- ? _FSEventsWatcher.default
992
- : _NodeWatcher.default;
993
- let watcher = "node";
994
-
995
- if (WatcherImpl === _WatchmanWatcher.default) {
996
- watcher = "watchman";
997
- } else if (WatcherImpl === _FSEventsWatcher.default) {
998
- watcher = "fsevents";
999
- }
1000
-
1001
- debug(`Using watcher: ${watcher}`);
1002
- (_this$_options$perfLo17 = this._options.perfLogger) === null ||
1003
- _this$_options$perfLo17 === void 0
1004
- ? void 0
1005
- : _this$_options$perfLo17.annotate({
1006
- string: {
1007
- watcher,
1008
- },
1009
- });
934
+ this._options.retainAllFiles = true;
1010
935
  const extensions = this._options.extensions;
1011
- const ignorePattern = this._options.ignorePattern;
1012
936
  const rootDir = this._options.rootDir;
1013
937
  let changeQueue = Promise.resolve();
1014
938
  let eventsQueue = []; // We only need to copy the entire haste map once on every "frame".
1015
939
 
1016
940
  let mustCopy = true;
1017
941
 
1018
- const createWatcher = (root) => {
1019
- const watcherOptions = {
1020
- dot: true,
1021
- glob: [
1022
- // Ensure we always include package.json files, which are crucial for
1023
- /// module resolution.
1024
- "**/package.json",
1025
- ...extensions.map((extension) => "**/*." + extension),
1026
- ],
1027
- ignored: ignorePattern,
1028
- watchmanDeferStates: this._options.watchmanDeferStates,
1029
- };
1030
- const watcher = new WatcherImpl(root, watcherOptions);
1031
- return new Promise((resolve, reject) => {
1032
- const rejectTimeout = setTimeout(
1033
- () => reject(new Error("Failed to start watch mode.")),
1034
- MAX_WAIT_TIME
1035
- );
1036
- watcher.once("ready", () => {
1037
- clearTimeout(rejectTimeout);
1038
- watcher.on("all", onChange);
1039
- resolve(watcher);
1040
- });
1041
- });
1042
- };
1043
-
1044
942
  const emitChange = () => {
1045
943
  if (eventsQueue.length) {
1046
944
  mustCopy = true;
@@ -1213,17 +1111,36 @@ class HasteMap extends _events.default {
1213
1111
  };
1214
1112
 
1215
1113
  this._changeInterval = setInterval(emitChange, CHANGE_INTERVAL);
1216
- await Promise.all(this._options.roots.map(createWatcher)).then(
1217
- (watchers) => {
1218
- var _this$_options$perfLo18;
1219
-
1220
- this._watchers = watchers;
1221
- (_this$_options$perfLo18 = this._options.perfLogger) === null ||
1222
- _this$_options$perfLo18 === void 0
1223
- ? void 0
1224
- : _this$_options$perfLo18.point("watch_end");
1225
- }
1114
+ (0, _invariant.default)(
1115
+ this._watcher != null,
1116
+ "Expected _watcher to have been initialised by _buildFileMap"
1226
1117
  );
1118
+ await this._watcher.watch(onChange);
1119
+
1120
+ if (this._options.healthCheck.enabled) {
1121
+ const performHealthCheck = () => {
1122
+ if (!this._watcher) {
1123
+ return;
1124
+ }
1125
+
1126
+ this._watcher
1127
+ .checkHealth(this._options.healthCheck.timeout)
1128
+ .then((result) => {
1129
+ this.emit("healthCheck", result);
1130
+ });
1131
+ };
1132
+
1133
+ performHealthCheck();
1134
+ this._healthCheckInterval = setInterval(
1135
+ performHealthCheck,
1136
+ this._options.healthCheck.interval
1137
+ );
1138
+ }
1139
+
1140
+ (_this$_options$perfLo15 = this._options.perfLogger) === null ||
1141
+ _this$_options$perfLo15 === void 0
1142
+ ? void 0
1143
+ : _this$_options$perfLo15.point("watch_end");
1227
1144
  }
1228
1145
  /**
1229
1146
  * This function should be called when the file under `filePath` is removed
@@ -1290,12 +1207,15 @@ class HasteMap extends _events.default {
1290
1207
  clearInterval(this._changeInterval);
1291
1208
  }
1292
1209
 
1293
- if (!this._watchers.length) {
1210
+ if (this._healthCheckInterval) {
1211
+ clearInterval(this._healthCheckInterval);
1212
+ }
1213
+
1214
+ if (!this._watcher) {
1294
1215
  return;
1295
1216
  }
1296
1217
 
1297
- await Promise.all(this._watchers.map((watcher) => watcher.close()));
1298
- this._watchers = [];
1218
+ await this._watcher.close();
1299
1219
 
1300
1220
  this._crawlerAbortController.abort();
1301
1221
  }
@@ -1326,14 +1246,14 @@ class HasteMap extends _events.default {
1326
1246
  )
1327
1247
  .then(() => true)
1328
1248
  .catch((e) => {
1329
- var _this$_options$perfLo19, _e$message;
1249
+ var _this$_options$perfLo16, _e$message;
1330
1250
 
1331
1251
  // TODO: Advise people to either install Watchman or set
1332
1252
  // `useWatchman: false` here?
1333
- (_this$_options$perfLo19 = this._options.perfLogger) === null ||
1334
- _this$_options$perfLo19 === void 0
1253
+ (_this$_options$perfLo16 = this._options.perfLogger) === null ||
1254
+ _this$_options$perfLo16 === void 0
1335
1255
  ? void 0
1336
- : _this$_options$perfLo19.annotate({
1256
+ : _this$_options$perfLo16.annotate({
1337
1257
  string: {
1338
1258
  watchmanFailedCapabilityCheck:
1339
1259
  (_e$message =
package/src/index.js.flow CHANGED
@@ -15,7 +15,6 @@ import type {
15
15
  CacheManagerFactory,
16
16
  ChangeEvent,
17
17
  Console,
18
- CrawlerOptions,
19
18
  EventsQueue,
20
19
  FileData,
21
20
  FileMetaData,
@@ -31,8 +30,8 @@ import type {
31
30
  SerializableModuleMap,
32
31
  WorkerMetadata,
33
32
  } from './flow-types';
34
- import type {WatcherOptions} from './watchers/common';
35
33
  import type {Stats} from 'graceful-fs';
34
+ import type {HealthCheckResult} from './Watcher';
36
35
 
37
36
  import {DiskCacheManager} from './cache/DiskCacheManager';
38
37
  import H from './constants';
@@ -45,10 +44,7 @@ import getPlatformExtension from './lib/getPlatformExtension';
45
44
  import normalizePathSep from './lib/normalizePathSep';
46
45
  import rootRelativeCacheKeys from './lib/rootRelativeCacheKeys';
47
46
  import HasteModuleMap from './ModuleMap';
48
- import FSEventsWatcher from './watchers/FSEventsWatcher';
49
- // $FlowFixMe[untyped-import] - it's a fork: https://github.com/facebook/jest/pull/10919
50
- import NodeWatcher from './watchers/NodeWatcher';
51
- import WatchmanWatcher from './watchers/WatchmanWatcher';
47
+ import {Watcher} from './Watcher';
52
48
  import {getSha1, worker} from './worker';
53
49
  import EventEmitter from 'events';
54
50
  import invariant from 'invariant';
@@ -60,15 +56,12 @@ import * as path from 'path';
60
56
  // $FlowFixMe[untyped-import] - this is a polyfill
61
57
  import AbortController from 'abort-controller';
62
58
 
63
- const nodeCrawl = require('./crawlers/node');
64
- const watchmanCrawl = require('./crawlers/watchman');
65
- const debug = require('debug')('Metro:FileMap');
66
-
67
59
  export type {
68
60
  BuildParameters,
69
61
  FileData,
70
62
  HasteFS,
71
63
  HasteMap,
64
+ HealthCheckResult,
72
65
  InternalData,
73
66
  ModuleMapData,
74
67
  ModuleMapItem,
@@ -101,10 +94,20 @@ export type InputOptions = $ReadOnly<{
101
94
  watch?: ?boolean,
102
95
  console?: Console,
103
96
  cacheManagerFactory?: ?CacheManagerFactory,
97
+
98
+ healthCheck: HealthCheckOptions,
99
+ }>;
100
+
101
+ type HealthCheckOptions = $ReadOnly<{
102
+ enabled: boolean,
103
+ interval: number,
104
+ timeout: number,
105
+ filePrefix: string,
104
106
  }>;
105
107
 
106
108
  type InternalOptions = {
107
109
  ...BuildParameters,
110
+ healthCheck: HealthCheckOptions,
108
111
  perfLogger: ?PerfLogger,
109
112
  resetCache: ?boolean,
110
113
  maxWorkers: number,
@@ -114,10 +117,6 @@ type InternalOptions = {
114
117
  watchmanDeferStates: $ReadOnlyArray<string>,
115
118
  };
116
119
 
117
- interface Watcher {
118
- close(): Promise<void>;
119
- }
120
-
121
120
  type WorkerInterface = {worker: typeof worker, getSha1: typeof getSha1};
122
121
 
123
122
  export const DuplicateHasteCandidatesError =
@@ -140,7 +139,6 @@ export type {
140
139
  const CACHE_BREAKER = '2';
141
140
 
142
141
  const CHANGE_INTERVAL = 30;
143
- const MAX_WAIT_TIME = 240000;
144
142
  const NODE_MODULES = path.sep + 'node_modules' + path.sep;
145
143
  const PACKAGE_JSON = path.sep + 'package.json';
146
144
  const VCS_DIRECTORIES = ['.git', '.hg']
@@ -237,10 +235,11 @@ export default class HasteMap extends EventEmitter {
237
235
  _changeInterval: ?IntervalID;
238
236
  _console: Console;
239
237
  _options: InternalOptions;
240
- _watchers: Array<Watcher>;
238
+ _watcher: ?Watcher;
241
239
  _worker: ?WorkerInterface;
242
240
  _cacheManager: CacheManager;
243
241
  _crawlerAbortController: typeof AbortController;
242
+ _healthCheckInterval: ?IntervalID;
244
243
 
245
244
  static create(options: InputOptions): HasteMap {
246
245
  return new HasteMap(options);
@@ -297,6 +296,7 @@ export default class HasteMap extends EventEmitter {
297
296
 
298
297
  this._options = {
299
298
  ...buildParameters,
299
+ healthCheck: options.healthCheck,
300
300
  maxWorkers: options.maxWorkers,
301
301
  perfLogger: options.perfLogger,
302
302
  resetCache: options.resetCache,
@@ -322,7 +322,6 @@ export default class HasteMap extends EventEmitter {
322
322
  }
323
323
 
324
324
  this._buildPromise = null;
325
- this._watchers = [];
326
325
  this._worker = null;
327
326
  this._options.perfLogger?.point('constructor_end');
328
327
  this._crawlerAbortController = new AbortController();
@@ -455,7 +454,40 @@ export default class HasteMap extends EventEmitter {
455
454
  } catch {
456
455
  hasteMap = this._createEmptyMap();
457
456
  }
458
- return this._crawl(hasteMap).then(result => {
457
+
458
+ const {
459
+ computeSha1,
460
+ enableSymlinks,
461
+ extensions,
462
+ forceNodeFilesystemAPI,
463
+ ignorePattern,
464
+ perfLogger,
465
+ roots,
466
+ rootDir,
467
+ watch,
468
+ watchmanDeferStates,
469
+ } = this._options;
470
+
471
+ this._watcher = new Watcher({
472
+ abortSignal: this._crawlerAbortController.signal,
473
+ computeSha1,
474
+ console: this._console,
475
+ enableSymlinks,
476
+ extensions,
477
+ forceNodeFilesystemAPI,
478
+ healthCheckFilePrefix: this._options.healthCheck.filePrefix,
479
+ ignore: path => this._ignore(path),
480
+ ignorePattern,
481
+ initialData: hasteMap,
482
+ perfLogger,
483
+ roots,
484
+ rootDir,
485
+ useWatchman: await this._shouldUseWatchman(),
486
+ watch,
487
+ watchmanDeferStates,
488
+ });
489
+
490
+ return this._watcher.crawl().then(result => {
459
491
  this._options.perfLogger?.point('buildFileMap_end');
460
492
  return result;
461
493
  });
@@ -788,66 +820,6 @@ export default class HasteMap extends EventEmitter {
788
820
  return this._worker;
789
821
  }
790
822
 
791
- async _crawl(hasteMap: InternalData): Promise<?(
792
- | Promise<{
793
- changedFiles?: FileData,
794
- hasteMap: InternalData,
795
- removedFiles: FileData,
796
- }>
797
- | {changedFiles?: FileData, hasteMap: InternalData, removedFiles: FileData}
798
- )> {
799
- this._options.perfLogger?.point('crawl_start');
800
- const options = this._options;
801
- const ignore = (filePath: string) => this._ignore(filePath);
802
- const crawl = (await this._shouldUseWatchman()) ? watchmanCrawl : nodeCrawl;
803
- const crawlerOptions: CrawlerOptions = {
804
- abortSignal: this._crawlerAbortController.signal,
805
- computeSha1: options.computeSha1,
806
- data: hasteMap,
807
- enableSymlinks: options.enableSymlinks,
808
- extensions: options.extensions,
809
- forceNodeFilesystemAPI: options.forceNodeFilesystemAPI,
810
- ignore,
811
- perfLogger: options.perfLogger,
812
- rootDir: options.rootDir,
813
- roots: options.roots,
814
- };
815
-
816
- const retry = (error: Error) => {
817
- if (crawl === watchmanCrawl) {
818
- this._console.warn(
819
- 'metro-file-map: Watchman crawl failed. Retrying once with node ' +
820
- 'crawler.\n' +
821
- " Usually this happens when watchman isn't running. Create an " +
822
- "empty `.watchmanconfig` file in your project's root folder or " +
823
- 'initialize a git or hg repository in your project.\n' +
824
- ' ' +
825
- error.toString(),
826
- );
827
- return nodeCrawl(crawlerOptions).catch(e => {
828
- throw new Error(
829
- 'Crawler retry failed:\n' +
830
- ` Original error: ${error.message}\n` +
831
- ` Retry error: ${e.message}\n`,
832
- );
833
- });
834
- }
835
-
836
- throw error;
837
- };
838
-
839
- const logEnd = <T>(result: T): T => {
840
- this._options.perfLogger?.point('crawl_end');
841
- return result;
842
- };
843
-
844
- try {
845
- return crawl(crawlerOptions).catch(retry).then(logEnd);
846
- } catch (error) {
847
- return retry(error).then(logEnd);
848
- }
849
- }
850
-
851
823
  /**
852
824
  * Watch mode
853
825
  */
@@ -863,24 +835,7 @@ export default class HasteMap extends EventEmitter {
863
835
  this._options.throwOnModuleCollision = false;
864
836
  this._options.retainAllFiles = true;
865
837
 
866
- // WatchmanWatcher > FSEventsWatcher > sane.NodeWatcher
867
- const WatcherImpl = (await this._shouldUseWatchman())
868
- ? WatchmanWatcher
869
- : FSEventsWatcher.isSupported()
870
- ? FSEventsWatcher
871
- : NodeWatcher;
872
-
873
- let watcher = 'node';
874
- if (WatcherImpl === WatchmanWatcher) {
875
- watcher = 'watchman';
876
- } else if (WatcherImpl === FSEventsWatcher) {
877
- watcher = 'fsevents';
878
- }
879
- debug(`Using watcher: ${watcher}`);
880
- this._options.perfLogger?.annotate({string: {watcher}});
881
-
882
838
  const extensions = this._options.extensions;
883
- const ignorePattern = this._options.ignorePattern;
884
839
  const rootDir = this._options.rootDir;
885
840
 
886
841
  let changeQueue: Promise<null | void> = Promise.resolve();
@@ -888,34 +843,6 @@ export default class HasteMap extends EventEmitter {
888
843
  // We only need to copy the entire haste map once on every "frame".
889
844
  let mustCopy = true;
890
845
 
891
- const createWatcher = (root: Path): Promise<Watcher> => {
892
- const watcherOptions: WatcherOptions = {
893
- dot: true,
894
- glob: [
895
- // Ensure we always include package.json files, which are crucial for
896
- /// module resolution.
897
- '**/package.json',
898
- ...extensions.map(extension => '**/*.' + extension),
899
- ],
900
- ignored: ignorePattern,
901
- watchmanDeferStates: this._options.watchmanDeferStates,
902
- };
903
- const watcher = new WatcherImpl(root, watcherOptions);
904
-
905
- return new Promise((resolve, reject) => {
906
- const rejectTimeout = setTimeout(
907
- () => reject(new Error('Failed to start watch mode.')),
908
- MAX_WAIT_TIME,
909
- );
910
-
911
- watcher.once('ready', () => {
912
- clearTimeout(rejectTimeout);
913
- watcher.on('all', onChange);
914
- resolve(watcher);
915
- });
916
- });
917
- };
918
-
919
846
  const emitChange = () => {
920
847
  if (eventsQueue.length) {
921
848
  mustCopy = true;
@@ -1076,10 +1003,30 @@ export default class HasteMap extends EventEmitter {
1076
1003
 
1077
1004
  this._changeInterval = setInterval(emitChange, CHANGE_INTERVAL);
1078
1005
 
1079
- await Promise.all(this._options.roots.map(createWatcher)).then(watchers => {
1080
- this._watchers = watchers;
1081
- this._options.perfLogger?.point('watch_end');
1082
- });
1006
+ invariant(
1007
+ this._watcher != null,
1008
+ 'Expected _watcher to have been initialised by _buildFileMap',
1009
+ );
1010
+ await this._watcher.watch(onChange);
1011
+
1012
+ if (this._options.healthCheck.enabled) {
1013
+ const performHealthCheck = () => {
1014
+ if (!this._watcher) {
1015
+ return;
1016
+ }
1017
+ this._watcher
1018
+ .checkHealth(this._options.healthCheck.timeout)
1019
+ .then(result => {
1020
+ this.emit('healthCheck', result);
1021
+ });
1022
+ };
1023
+ performHealthCheck();
1024
+ this._healthCheckInterval = setInterval(
1025
+ performHealthCheck,
1026
+ this._options.healthCheck.interval,
1027
+ );
1028
+ }
1029
+ this._options.perfLogger?.point('watch_end');
1083
1030
  }
1084
1031
 
1085
1032
  /**
@@ -1143,14 +1090,14 @@ export default class HasteMap extends EventEmitter {
1143
1090
  if (this._changeInterval) {
1144
1091
  clearInterval(this._changeInterval);
1145
1092
  }
1093
+ if (this._healthCheckInterval) {
1094
+ clearInterval(this._healthCheckInterval);
1095
+ }
1146
1096
 
1147
- if (!this._watchers.length) {
1097
+ if (!this._watcher) {
1148
1098
  return;
1149
1099
  }
1150
-
1151
- await Promise.all(this._watchers.map(watcher => watcher.close()));
1152
-
1153
- this._watchers = [];
1100
+ await this._watcher.close();
1154
1101
  this._crawlerAbortController.abort();
1155
1102
  }
1156
1103