metro-file-map 0.73.1 → 0.73.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "metro-file-map",
3
- "version": "0.73.1",
3
+ "version": "0.73.2",
4
4
  "description": "[Experimental] - 🚇 File crawling, watching and mapping for Metro",
5
5
  "main": "src/index.js",
6
6
  "repository": {
package/src/Watcher.js ADDED
@@ -0,0 +1,176 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true,
5
+ });
6
+ exports.Watcher = void 0;
7
+
8
+ var _watchman = _interopRequireDefault(require("./crawlers/watchman"));
9
+
10
+ var _node = _interopRequireDefault(require("./crawlers/node"));
11
+
12
+ var _WatchmanWatcher = _interopRequireDefault(
13
+ require("./watchers/WatchmanWatcher")
14
+ );
15
+
16
+ var _FSEventsWatcher = _interopRequireDefault(
17
+ require("./watchers/FSEventsWatcher")
18
+ );
19
+
20
+ var _NodeWatcher = _interopRequireDefault(require("./watchers/NodeWatcher"));
21
+
22
+ function _interopRequireDefault(obj) {
23
+ return obj && obj.__esModule ? obj : { default: obj };
24
+ }
25
+
26
+ /**
27
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
28
+ *
29
+ * This source code is licensed under the MIT license found in the
30
+ * LICENSE file in the root directory of this source tree.
31
+ *
32
+ * @format
33
+ *
34
+ */
35
+ // $FlowFixMe[untyped-import] - it's a fork: https://github.com/facebook/jest/pull/10919
36
+ const debug = require("debug")("Metro:Watcher");
37
+
38
+ const MAX_WAIT_TIME = 240000;
39
+
40
+ class Watcher {
41
+ _backends = [];
42
+
43
+ constructor(options) {
44
+ this._options = options;
45
+ }
46
+
47
+ async crawl() {
48
+ var _this$_options$perfLo;
49
+
50
+ (_this$_options$perfLo = this._options.perfLogger) === null ||
51
+ _this$_options$perfLo === void 0
52
+ ? void 0
53
+ : _this$_options$perfLo.point("crawl_start");
54
+ const options = this._options;
55
+
56
+ const ignore = (filePath) => options.ignore(filePath);
57
+
58
+ const crawl = options.useWatchman ? _watchman.default : _node.default;
59
+ const crawlerOptions = {
60
+ abortSignal: options.abortSignal,
61
+ computeSha1: options.computeSha1,
62
+ data: options.initialData,
63
+ enableSymlinks: options.enableSymlinks,
64
+ extensions: options.extensions,
65
+ forceNodeFilesystemAPI: options.forceNodeFilesystemAPI,
66
+ ignore,
67
+ perfLogger: options.perfLogger,
68
+ rootDir: options.rootDir,
69
+ roots: options.roots,
70
+ };
71
+
72
+ const retry = (error) => {
73
+ if (crawl === _watchman.default) {
74
+ options.console.warn(
75
+ "metro-file-map: Watchman crawl failed. Retrying once with node " +
76
+ "crawler.\n" +
77
+ " Usually this happens when watchman isn't running. Create an " +
78
+ "empty `.watchmanconfig` file in your project's root folder or " +
79
+ "initialize a git or hg repository in your project.\n" +
80
+ " " +
81
+ error.toString()
82
+ );
83
+ return (0, _node.default)(crawlerOptions).catch((e) => {
84
+ throw new Error(
85
+ "Crawler retry failed:\n" +
86
+ ` Original error: ${error.message}\n` +
87
+ ` Retry error: ${e.message}\n`
88
+ );
89
+ });
90
+ }
91
+
92
+ throw error;
93
+ };
94
+
95
+ const logEnd = (result) => {
96
+ var _this$_options$perfLo2;
97
+
98
+ (_this$_options$perfLo2 = this._options.perfLogger) === null ||
99
+ _this$_options$perfLo2 === void 0
100
+ ? void 0
101
+ : _this$_options$perfLo2.point("crawl_end");
102
+ return result;
103
+ };
104
+
105
+ try {
106
+ return crawl(crawlerOptions).catch(retry).then(logEnd);
107
+ } catch (error) {
108
+ return retry(error).then(logEnd);
109
+ }
110
+ }
111
+
112
+ async watch(onChange) {
113
+ var _this$_options$perfLo3;
114
+
115
+ const { extensions, ignorePattern, useWatchman } = this._options; // WatchmanWatcher > FSEventsWatcher > sane.NodeWatcher
116
+
117
+ const WatcherImpl = useWatchman
118
+ ? _WatchmanWatcher.default
119
+ : _FSEventsWatcher.default.isSupported()
120
+ ? _FSEventsWatcher.default
121
+ : _NodeWatcher.default;
122
+ let watcher = "node";
123
+
124
+ if (WatcherImpl === _WatchmanWatcher.default) {
125
+ watcher = "watchman";
126
+ } else if (WatcherImpl === _FSEventsWatcher.default) {
127
+ watcher = "fsevents";
128
+ }
129
+
130
+ debug(`Using watcher: ${watcher}`);
131
+ (_this$_options$perfLo3 = this._options.perfLogger) === null ||
132
+ _this$_options$perfLo3 === void 0
133
+ ? void 0
134
+ : _this$_options$perfLo3.annotate({
135
+ string: {
136
+ watcher,
137
+ },
138
+ });
139
+
140
+ const createWatcherBackend = (root) => {
141
+ const watcherOptions = {
142
+ dot: true,
143
+ glob: [
144
+ // Ensure we always include package.json files, which are crucial for
145
+ /// module resolution.
146
+ "**/package.json",
147
+ ...extensions.map((extension) => "**/*." + extension),
148
+ ],
149
+ ignored: ignorePattern,
150
+ watchmanDeferStates: this._options.watchmanDeferStates,
151
+ };
152
+ const watcher = new WatcherImpl(root, watcherOptions);
153
+ return new Promise((resolve, reject) => {
154
+ const rejectTimeout = setTimeout(
155
+ () => reject(new Error("Failed to start watch mode.")),
156
+ MAX_WAIT_TIME
157
+ );
158
+ watcher.once("ready", () => {
159
+ clearTimeout(rejectTimeout);
160
+ watcher.on("all", onChange);
161
+ resolve(watcher);
162
+ });
163
+ });
164
+ };
165
+
166
+ this._backends = await Promise.all(
167
+ this._options.roots.map(createWatcherBackend)
168
+ );
169
+ }
170
+
171
+ async close() {
172
+ await Promise.all(this._backends.map((watcher) => watcher.close()));
173
+ }
174
+ }
175
+
176
+ exports.Watcher = Watcher;
@@ -0,0 +1,186 @@
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
+ * @format
8
+ * @flow strict-local
9
+ */
10
+
11
+ import type {
12
+ Console,
13
+ CrawlerOptions,
14
+ FileData,
15
+ InternalData,
16
+ Path,
17
+ PerfLogger,
18
+ } from './flow-types';
19
+ import type {WatcherOptions as WatcherBackendOptions} from './watchers/common';
20
+ import type {Stats} from 'fs';
21
+
22
+ import watchmanCrawl from './crawlers/watchman';
23
+ import nodeCrawl from './crawlers/node';
24
+ import WatchmanWatcher from './watchers/WatchmanWatcher';
25
+ import FSEventsWatcher from './watchers/FSEventsWatcher';
26
+ // $FlowFixMe[untyped-import] - it's a fork: https://github.com/facebook/jest/pull/10919
27
+ import NodeWatcher from './watchers/NodeWatcher';
28
+
29
+ const debug = require('debug')('Metro:Watcher');
30
+
31
+ const MAX_WAIT_TIME = 240000;
32
+
33
+ type WatcherOptions = {
34
+ abortSignal: AbortSignal,
35
+ computeSha1: boolean,
36
+ console: Console,
37
+ enableSymlinks: boolean,
38
+ extensions: $ReadOnlyArray<string>,
39
+ forceNodeFilesystemAPI: boolean,
40
+ ignore: string => boolean,
41
+ ignorePattern: RegExp,
42
+ initialData: InternalData,
43
+ perfLogger: ?PerfLogger,
44
+ roots: $ReadOnlyArray<string>,
45
+ rootDir: string,
46
+ useWatchman: boolean,
47
+ watch: boolean,
48
+ watchmanDeferStates: $ReadOnlyArray<string>,
49
+ };
50
+
51
+ interface WatcherBackend {
52
+ close(): Promise<void>;
53
+ }
54
+
55
+ export class Watcher {
56
+ _options: WatcherOptions;
57
+ _backends: $ReadOnlyArray<WatcherBackend> = [];
58
+
59
+ constructor(options: WatcherOptions) {
60
+ this._options = options;
61
+ }
62
+
63
+ async crawl(): Promise<?(
64
+ | Promise<{
65
+ changedFiles?: FileData,
66
+ hasteMap: InternalData,
67
+ removedFiles: FileData,
68
+ }>
69
+ | {changedFiles?: FileData, hasteMap: InternalData, removedFiles: FileData}
70
+ )> {
71
+ this._options.perfLogger?.point('crawl_start');
72
+
73
+ const options = this._options;
74
+ const ignore = (filePath: string) => options.ignore(filePath);
75
+ const crawl = options.useWatchman ? watchmanCrawl : nodeCrawl;
76
+ const crawlerOptions: CrawlerOptions = {
77
+ abortSignal: options.abortSignal,
78
+ computeSha1: options.computeSha1,
79
+ data: options.initialData,
80
+ enableSymlinks: options.enableSymlinks,
81
+ extensions: options.extensions,
82
+ forceNodeFilesystemAPI: options.forceNodeFilesystemAPI,
83
+ ignore,
84
+ perfLogger: options.perfLogger,
85
+ rootDir: options.rootDir,
86
+ roots: options.roots,
87
+ };
88
+
89
+ const retry = (error: Error) => {
90
+ if (crawl === watchmanCrawl) {
91
+ options.console.warn(
92
+ 'metro-file-map: Watchman crawl failed. Retrying once with node ' +
93
+ 'crawler.\n' +
94
+ " Usually this happens when watchman isn't running. Create an " +
95
+ "empty `.watchmanconfig` file in your project's root folder or " +
96
+ 'initialize a git or hg repository in your project.\n' +
97
+ ' ' +
98
+ error.toString(),
99
+ );
100
+ return nodeCrawl(crawlerOptions).catch(e => {
101
+ throw new Error(
102
+ 'Crawler retry failed:\n' +
103
+ ` Original error: ${error.message}\n` +
104
+ ` Retry error: ${e.message}\n`,
105
+ );
106
+ });
107
+ }
108
+
109
+ throw error;
110
+ };
111
+
112
+ const logEnd = <T>(result: T): T => {
113
+ this._options.perfLogger?.point('crawl_end');
114
+ return result;
115
+ };
116
+
117
+ try {
118
+ return crawl(crawlerOptions).catch(retry).then(logEnd);
119
+ } catch (error) {
120
+ return retry(error).then(logEnd);
121
+ }
122
+ }
123
+
124
+ async watch(
125
+ onChange: (
126
+ type: string,
127
+ filePath: string,
128
+ root: string,
129
+ stat?: Stats,
130
+ ) => void,
131
+ ) {
132
+ const {extensions, ignorePattern, useWatchman} = this._options;
133
+
134
+ // WatchmanWatcher > FSEventsWatcher > sane.NodeWatcher
135
+ const WatcherImpl = useWatchman
136
+ ? WatchmanWatcher
137
+ : FSEventsWatcher.isSupported()
138
+ ? FSEventsWatcher
139
+ : NodeWatcher;
140
+
141
+ let watcher = 'node';
142
+ if (WatcherImpl === WatchmanWatcher) {
143
+ watcher = 'watchman';
144
+ } else if (WatcherImpl === FSEventsWatcher) {
145
+ watcher = 'fsevents';
146
+ }
147
+ debug(`Using watcher: ${watcher}`);
148
+ this._options.perfLogger?.annotate({string: {watcher}});
149
+
150
+ const createWatcherBackend = (root: Path): Promise<WatcherBackend> => {
151
+ const watcherOptions: WatcherBackendOptions = {
152
+ dot: true,
153
+ glob: [
154
+ // Ensure we always include package.json files, which are crucial for
155
+ /// module resolution.
156
+ '**/package.json',
157
+ ...extensions.map(extension => '**/*.' + extension),
158
+ ],
159
+ ignored: ignorePattern,
160
+ watchmanDeferStates: this._options.watchmanDeferStates,
161
+ };
162
+ const watcher = new WatcherImpl(root, watcherOptions);
163
+
164
+ return new Promise((resolve, reject) => {
165
+ const rejectTimeout = setTimeout(
166
+ () => reject(new Error('Failed to start watch mode.')),
167
+ MAX_WAIT_TIME,
168
+ );
169
+
170
+ watcher.once('ready', () => {
171
+ clearTimeout(rejectTimeout);
172
+ watcher.on('all', onChange);
173
+ resolve(watcher);
174
+ });
175
+ });
176
+ };
177
+
178
+ this._backends = await Promise.all(
179
+ this._options.roots.map(createWatcherBackend),
180
+ );
181
+ }
182
+
183
+ async close() {
184
+ await Promise.all(this._backends.map(watcher => watcher.close()));
185
+ }
186
+ }
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"]
@@ -337,7 +321,6 @@ class HasteMap extends _events.default {
337
321
  }
338
322
 
339
323
  this._buildPromise = null;
340
- this._watchers = [];
341
324
  this._worker = null;
342
325
  (_this$_options$perfLo = this._options.perfLogger) === null ||
343
326
  _this$_options$perfLo === void 0
@@ -511,7 +494,36 @@ class HasteMap extends _events.default {
511
494
  hasteMap = this._createEmptyMap();
512
495
  }
513
496
 
514
- return this._crawl(hasteMap).then((result) => {
497
+ const {
498
+ computeSha1,
499
+ enableSymlinks,
500
+ extensions,
501
+ forceNodeFilesystemAPI,
502
+ ignorePattern,
503
+ perfLogger,
504
+ roots,
505
+ rootDir,
506
+ watch,
507
+ watchmanDeferStates,
508
+ } = this._options;
509
+ this._watcher = new _Watcher.Watcher({
510
+ abortSignal: this._crawlerAbortController.signal,
511
+ computeSha1,
512
+ console: this._console,
513
+ enableSymlinks,
514
+ extensions,
515
+ forceNodeFilesystemAPI,
516
+ ignore: (path) => this._ignore(path),
517
+ ignorePattern,
518
+ initialData: hasteMap,
519
+ perfLogger,
520
+ roots,
521
+ rootDir,
522
+ useWatchman: await this._shouldUseWatchman(),
523
+ watch,
524
+ watchmanDeferStates,
525
+ });
526
+ return this._watcher.crawl().then((result) => {
515
527
  var _this$_options$perfLo8;
516
528
 
517
529
  (_this$_options$perfLo8 = this._options.perfLogger) === null ||
@@ -893,154 +905,38 @@ class HasteMap extends _events.default {
893
905
 
894
906
  return this._worker;
895
907
  }
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
908
  /**
963
909
  * Watch mode
964
910
  */
965
911
 
966
912
  async _watch(hasteMap) {
967
- var _this$_options$perfLo15, _this$_options$perfLo17;
913
+ var _this$_options$perfLo13, _this$_options$perfLo15;
968
914
 
969
- (_this$_options$perfLo15 = this._options.perfLogger) === null ||
970
- _this$_options$perfLo15 === void 0
915
+ (_this$_options$perfLo13 = this._options.perfLogger) === null ||
916
+ _this$_options$perfLo13 === void 0
971
917
  ? void 0
972
- : _this$_options$perfLo15.point("watch_start");
918
+ : _this$_options$perfLo13.point("watch_start");
973
919
 
974
920
  if (!this._options.watch) {
975
- var _this$_options$perfLo16;
921
+ var _this$_options$perfLo14;
976
922
 
977
- (_this$_options$perfLo16 = this._options.perfLogger) === null ||
978
- _this$_options$perfLo16 === void 0
923
+ (_this$_options$perfLo14 = this._options.perfLogger) === null ||
924
+ _this$_options$perfLo14 === void 0
979
925
  ? void 0
980
- : _this$_options$perfLo16.point("watch_end");
926
+ : _this$_options$perfLo14.point("watch_end");
981
927
  return;
982
928
  } // In watch mode, we'll only warn about module collisions and we'll retain
983
929
  // all files, even changes to node_modules.
984
930
 
985
931
  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
- });
932
+ this._options.retainAllFiles = true;
1010
933
  const extensions = this._options.extensions;
1011
- const ignorePattern = this._options.ignorePattern;
1012
934
  const rootDir = this._options.rootDir;
1013
935
  let changeQueue = Promise.resolve();
1014
936
  let eventsQueue = []; // We only need to copy the entire haste map once on every "frame".
1015
937
 
1016
938
  let mustCopy = true;
1017
939
 
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
940
  const emitChange = () => {
1045
941
  if (eventsQueue.length) {
1046
942
  mustCopy = true;
@@ -1213,17 +1109,15 @@ class HasteMap extends _events.default {
1213
1109
  };
1214
1110
 
1215
1111
  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
- }
1112
+ (0, _invariant.default)(
1113
+ this._watcher != null,
1114
+ "Expected _watcher to have been initialised by _buildFileMap"
1226
1115
  );
1116
+ await this._watcher.watch(onChange);
1117
+ (_this$_options$perfLo15 = this._options.perfLogger) === null ||
1118
+ _this$_options$perfLo15 === void 0
1119
+ ? void 0
1120
+ : _this$_options$perfLo15.point("watch_end");
1227
1121
  }
1228
1122
  /**
1229
1123
  * This function should be called when the file under `filePath` is removed
@@ -1290,12 +1184,11 @@ class HasteMap extends _events.default {
1290
1184
  clearInterval(this._changeInterval);
1291
1185
  }
1292
1186
 
1293
- if (!this._watchers.length) {
1187
+ if (!this._watcher) {
1294
1188
  return;
1295
1189
  }
1296
1190
 
1297
- await Promise.all(this._watchers.map((watcher) => watcher.close()));
1298
- this._watchers = [];
1191
+ await this._watcher.close();
1299
1192
 
1300
1193
  this._crawlerAbortController.abort();
1301
1194
  }
@@ -1326,14 +1219,14 @@ class HasteMap extends _events.default {
1326
1219
  )
1327
1220
  .then(() => true)
1328
1221
  .catch((e) => {
1329
- var _this$_options$perfLo19, _e$message;
1222
+ var _this$_options$perfLo16, _e$message;
1330
1223
 
1331
1224
  // TODO: Advise people to either install Watchman or set
1332
1225
  // `useWatchman: false` here?
1333
- (_this$_options$perfLo19 = this._options.perfLogger) === null ||
1334
- _this$_options$perfLo19 === void 0
1226
+ (_this$_options$perfLo16 = this._options.perfLogger) === null ||
1227
+ _this$_options$perfLo16 === void 0
1335
1228
  ? void 0
1336
- : _this$_options$perfLo19.annotate({
1229
+ : _this$_options$perfLo16.annotate({
1337
1230
  string: {
1338
1231
  watchmanFailedCapabilityCheck:
1339
1232
  (_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,7 +30,6 @@ 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';
36
34
 
37
35
  import {DiskCacheManager} from './cache/DiskCacheManager';
@@ -45,10 +43,7 @@ import getPlatformExtension from './lib/getPlatformExtension';
45
43
  import normalizePathSep from './lib/normalizePathSep';
46
44
  import rootRelativeCacheKeys from './lib/rootRelativeCacheKeys';
47
45
  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';
46
+ import {Watcher} from './Watcher';
52
47
  import {getSha1, worker} from './worker';
53
48
  import EventEmitter from 'events';
54
49
  import invariant from 'invariant';
@@ -60,10 +55,6 @@ import * as path from 'path';
60
55
  // $FlowFixMe[untyped-import] - this is a polyfill
61
56
  import AbortController from 'abort-controller';
62
57
 
63
- const nodeCrawl = require('./crawlers/node');
64
- const watchmanCrawl = require('./crawlers/watchman');
65
- const debug = require('debug')('Metro:FileMap');
66
-
67
58
  export type {
68
59
  BuildParameters,
69
60
  FileData,
@@ -114,10 +105,6 @@ type InternalOptions = {
114
105
  watchmanDeferStates: $ReadOnlyArray<string>,
115
106
  };
116
107
 
117
- interface Watcher {
118
- close(): Promise<void>;
119
- }
120
-
121
108
  type WorkerInterface = {worker: typeof worker, getSha1: typeof getSha1};
122
109
 
123
110
  export const DuplicateHasteCandidatesError =
@@ -140,7 +127,6 @@ export type {
140
127
  const CACHE_BREAKER = '2';
141
128
 
142
129
  const CHANGE_INTERVAL = 30;
143
- const MAX_WAIT_TIME = 240000;
144
130
  const NODE_MODULES = path.sep + 'node_modules' + path.sep;
145
131
  const PACKAGE_JSON = path.sep + 'package.json';
146
132
  const VCS_DIRECTORIES = ['.git', '.hg']
@@ -237,7 +223,7 @@ export default class HasteMap extends EventEmitter {
237
223
  _changeInterval: ?IntervalID;
238
224
  _console: Console;
239
225
  _options: InternalOptions;
240
- _watchers: Array<Watcher>;
226
+ _watcher: ?Watcher;
241
227
  _worker: ?WorkerInterface;
242
228
  _cacheManager: CacheManager;
243
229
  _crawlerAbortController: typeof AbortController;
@@ -322,7 +308,6 @@ export default class HasteMap extends EventEmitter {
322
308
  }
323
309
 
324
310
  this._buildPromise = null;
325
- this._watchers = [];
326
311
  this._worker = null;
327
312
  this._options.perfLogger?.point('constructor_end');
328
313
  this._crawlerAbortController = new AbortController();
@@ -455,7 +440,39 @@ export default class HasteMap extends EventEmitter {
455
440
  } catch {
456
441
  hasteMap = this._createEmptyMap();
457
442
  }
458
- return this._crawl(hasteMap).then(result => {
443
+
444
+ const {
445
+ computeSha1,
446
+ enableSymlinks,
447
+ extensions,
448
+ forceNodeFilesystemAPI,
449
+ ignorePattern,
450
+ perfLogger,
451
+ roots,
452
+ rootDir,
453
+ watch,
454
+ watchmanDeferStates,
455
+ } = this._options;
456
+
457
+ this._watcher = new Watcher({
458
+ abortSignal: this._crawlerAbortController.signal,
459
+ computeSha1,
460
+ console: this._console,
461
+ enableSymlinks,
462
+ extensions,
463
+ forceNodeFilesystemAPI,
464
+ ignore: path => this._ignore(path),
465
+ ignorePattern,
466
+ initialData: hasteMap,
467
+ perfLogger,
468
+ roots,
469
+ rootDir,
470
+ useWatchman: await this._shouldUseWatchman(),
471
+ watch,
472
+ watchmanDeferStates,
473
+ });
474
+
475
+ return this._watcher.crawl().then(result => {
459
476
  this._options.perfLogger?.point('buildFileMap_end');
460
477
  return result;
461
478
  });
@@ -788,66 +805,6 @@ export default class HasteMap extends EventEmitter {
788
805
  return this._worker;
789
806
  }
790
807
 
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
808
  /**
852
809
  * Watch mode
853
810
  */
@@ -863,24 +820,7 @@ export default class HasteMap extends EventEmitter {
863
820
  this._options.throwOnModuleCollision = false;
864
821
  this._options.retainAllFiles = true;
865
822
 
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
823
  const extensions = this._options.extensions;
883
- const ignorePattern = this._options.ignorePattern;
884
824
  const rootDir = this._options.rootDir;
885
825
 
886
826
  let changeQueue: Promise<null | void> = Promise.resolve();
@@ -888,34 +828,6 @@ export default class HasteMap extends EventEmitter {
888
828
  // We only need to copy the entire haste map once on every "frame".
889
829
  let mustCopy = true;
890
830
 
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
831
  const emitChange = () => {
920
832
  if (eventsQueue.length) {
921
833
  mustCopy = true;
@@ -1076,10 +988,13 @@ export default class HasteMap extends EventEmitter {
1076
988
 
1077
989
  this._changeInterval = setInterval(emitChange, CHANGE_INTERVAL);
1078
990
 
1079
- await Promise.all(this._options.roots.map(createWatcher)).then(watchers => {
1080
- this._watchers = watchers;
1081
- this._options.perfLogger?.point('watch_end');
1082
- });
991
+ invariant(
992
+ this._watcher != null,
993
+ 'Expected _watcher to have been initialised by _buildFileMap',
994
+ );
995
+ await this._watcher.watch(onChange);
996
+
997
+ this._options.perfLogger?.point('watch_end');
1083
998
  }
1084
999
 
1085
1000
  /**
@@ -1144,13 +1059,10 @@ export default class HasteMap extends EventEmitter {
1144
1059
  clearInterval(this._changeInterval);
1145
1060
  }
1146
1061
 
1147
- if (!this._watchers.length) {
1062
+ if (!this._watcher) {
1148
1063
  return;
1149
1064
  }
1150
-
1151
- await Promise.all(this._watchers.map(watcher => watcher.close()));
1152
-
1153
- this._watchers = [];
1065
+ await this._watcher.close();
1154
1066
  this._crawlerAbortController.abort();
1155
1067
  }
1156
1068