metro-file-map 0.83.3 → 0.84.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/package.json +4 -3
  2. package/src/Watcher.js +59 -52
  3. package/src/Watcher.js.flow +39 -39
  4. package/src/cache/DiskCacheManager.js.flow +3 -3
  5. package/src/constants.js +9 -8
  6. package/src/constants.js.flow +6 -18
  7. package/src/crawlers/node/index.js +27 -25
  8. package/src/crawlers/node/index.js.flow +6 -8
  9. package/src/crawlers/watchman/index.js +26 -22
  10. package/src/crawlers/watchman/index.js.flow +3 -4
  11. package/src/crawlers/watchman/planQuery.d.ts +24 -0
  12. package/src/crawlers/watchman/planQuery.js.flow +4 -4
  13. package/src/flow-types.js.flow +125 -87
  14. package/src/index.js +267 -235
  15. package/src/index.js.flow +269 -275
  16. package/src/lib/FileProcessor.js +76 -54
  17. package/src/lib/FileProcessor.js.flow +93 -72
  18. package/src/lib/RootPathUtils.js +25 -21
  19. package/src/lib/RootPathUtils.js.flow +4 -4
  20. package/src/lib/TreeFS.js +72 -77
  21. package/src/lib/TreeFS.js.flow +104 -124
  22. package/src/lib/checkWatchmanCapabilities.js.flow +4 -4
  23. package/src/lib/dependencyExtractor.d.ts +14 -0
  24. package/src/lib/normalizePathSeparatorsToPosix.js +25 -21
  25. package/src/lib/normalizePathSeparatorsToPosix.js.flow +3 -3
  26. package/src/lib/normalizePathSeparatorsToSystem.js +25 -21
  27. package/src/lib/normalizePathSeparatorsToSystem.js.flow +3 -3
  28. package/src/lib/rootRelativeCacheKeys.js +0 -20
  29. package/src/lib/rootRelativeCacheKeys.js.flow +1 -23
  30. package/src/plugins/DependencyPlugin.js +75 -0
  31. package/src/plugins/DependencyPlugin.js.flow +144 -0
  32. package/src/plugins/HastePlugin.js +83 -38
  33. package/src/plugins/HastePlugin.js.flow +105 -51
  34. package/src/plugins/MockPlugin.js +7 -4
  35. package/src/plugins/MockPlugin.js.flow +24 -15
  36. package/src/plugins/dependencies/dependencyExtractor.d.ts +14 -0
  37. package/src/{lib → plugins/dependencies}/dependencyExtractor.js.flow +3 -6
  38. package/src/plugins/dependencies/worker.d.ts +24 -0
  39. package/src/plugins/dependencies/worker.js +24 -0
  40. package/src/plugins/dependencies/worker.js.flow +53 -0
  41. package/src/plugins/haste/HasteConflictsError.js.flow +2 -2
  42. package/src/plugins/haste/computeConflicts.js +2 -1
  43. package/src/plugins/haste/computeConflicts.js.flow +11 -12
  44. package/src/plugins/haste/getPlatformExtension.js.flow +2 -2
  45. package/src/plugins/haste/worker.d.ts +24 -0
  46. package/src/plugins/haste/worker.js +35 -0
  47. package/src/plugins/haste/worker.js.flow +64 -0
  48. package/src/plugins/mocks/getMockName.js +27 -23
  49. package/src/plugins/mocks/getMockName.js.flow +2 -4
  50. package/src/watchers/AbstractWatcher.js +27 -22
  51. package/src/watchers/AbstractWatcher.js.flow +6 -5
  52. package/src/watchers/FallbackWatcher.js +88 -84
  53. package/src/watchers/FallbackWatcher.js.flow +65 -65
  54. package/src/watchers/NativeWatcher.js +25 -21
  55. package/src/watchers/NativeWatcher.js.flow +3 -3
  56. package/src/watchers/RecrawlWarning.js.flow +1 -1
  57. package/src/watchers/WatchmanWatcher.js +61 -53
  58. package/src/watchers/WatchmanWatcher.js.flow +39 -38
  59. package/src/watchers/common.js.flow +5 -5
  60. package/src/worker.d.ts +36 -0
  61. package/src/worker.js +16 -58
  62. package/src/worker.js.flow +19 -69
  63. package/src/workerExclusionList.d.ts +12 -0
  64. package/src/workerExclusionList.js.flow +1 -1
  65. package/src/Watcher.d.ts +0 -24
  66. package/src/cache/DiskCacheManager.d.ts +0 -38
  67. package/src/flow-types.d.ts +0 -353
  68. package/src/index.d.ts +0 -97
  69. package/src/lib/DuplicateHasteCandidatesError.d.ts +0 -24
  70. /package/src/{lib → plugins/dependencies}/dependencyExtractor.js +0 -0
package/src/index.js.flow CHANGED
@@ -25,6 +25,7 @@ import type {
25
25
  EventsQueue,
26
26
  FileData,
27
27
  FileMapPlugin,
28
+ FileMapPluginWorker,
28
29
  FileMetadata,
29
30
  FileSystem,
30
31
  HasteMapData,
@@ -47,8 +48,6 @@ import normalizePathSeparatorsToPosix from './lib/normalizePathSeparatorsToPosix
47
48
  import normalizePathSeparatorsToSystem from './lib/normalizePathSeparatorsToSystem';
48
49
  import {RootPathUtils} from './lib/RootPathUtils';
49
50
  import TreeFS from './lib/TreeFS';
50
- import HastePlugin from './plugins/HastePlugin';
51
- import MockPlugin from './plugins/MockPlugin';
52
51
  import {Watcher} from './Watcher';
53
52
  import EventEmitter from 'events';
54
53
  import {promises as fsPromises} from 'fs';
@@ -72,26 +71,16 @@ export type {
72
71
  HasteMapItem,
73
72
  };
74
73
 
75
- export type InputOptions = $ReadOnly<{
76
- computeDependencies?: ?boolean,
74
+ export type InputOptions = Readonly<{
77
75
  computeSha1?: ?boolean,
78
- enableHastePackages?: boolean,
79
76
  enableSymlinks?: ?boolean,
80
- enableWorkerThreads?: ?boolean,
81
- extensions: $ReadOnlyArray<string>,
77
+ extensions: ReadonlyArray<string>,
82
78
  forceNodeFilesystemAPI?: ?boolean,
83
79
  ignorePattern?: ?RegExp,
84
- mocksPattern?: ?string,
85
- platforms: $ReadOnlyArray<string>,
86
- plugins?: $ReadOnlyArray<FileMapPlugin<>>,
80
+ plugins?: ReadonlyArray<AnyFileMapPlugin>,
87
81
  retainAllFiles: boolean,
88
82
  rootDir: string,
89
- roots: $ReadOnlyArray<string>,
90
- skipPackageJson?: ?boolean,
91
-
92
- // Module paths that should export a 'getCacheKey' method
93
- dependencyExtractor?: ?string,
94
- hasteImplModulePath?: ?string,
83
+ roots: ReadonlyArray<string>,
95
84
 
96
85
  cacheManagerFactory?: ?CacheManagerFactory,
97
86
  console?: Console,
@@ -100,30 +89,38 @@ export type InputOptions = $ReadOnly<{
100
89
  maxWorkers: number,
101
90
  perfLoggerFactory?: ?PerfLoggerFactory,
102
91
  resetCache?: ?boolean,
103
- throwOnModuleCollision?: ?boolean,
104
92
  useWatchman?: ?boolean,
105
93
  watch?: ?boolean,
106
- watchmanDeferStates?: $ReadOnlyArray<string>,
94
+ watchmanDeferStates?: ReadonlyArray<string>,
107
95
  }>;
108
96
 
109
- type HealthCheckOptions = $ReadOnly<{
97
+ type HealthCheckOptions = Readonly<{
110
98
  enabled: boolean,
111
99
  interval: number,
112
100
  timeout: number,
113
101
  filePrefix: string,
114
102
  }>;
115
103
 
116
- type InternalOptions = $ReadOnly<{
104
+ type InternalOptions = Readonly<{
117
105
  ...BuildParameters,
118
106
  healthCheck: HealthCheckOptions,
119
107
  perfLoggerFactory: ?PerfLoggerFactory,
120
108
  resetCache: ?boolean,
121
109
  useWatchman: boolean,
122
110
  watch: boolean,
123
- watchmanDeferStates: $ReadOnlyArray<string>,
111
+ watchmanDeferStates: ReadonlyArray<string>,
112
+ }>;
113
+
114
+ // $FlowFixMe[unclear-type] Plugin types cannot be known statically
115
+ type AnyFileMapPlugin = FileMapPlugin<any, any>;
116
+ type IndexedPlugin = Readonly<{
117
+ plugin: AnyFileMapPlugin,
118
+ dataIdx: ?number,
124
119
  }>;
125
120
 
126
121
  export {DiskCacheManager} from './cache/DiskCacheManager';
122
+ export {default as DependencyPlugin} from './plugins/DependencyPlugin';
123
+ export type {DependencyPluginOptions} from './plugins/DependencyPlugin';
127
124
  export {DuplicateHasteCandidatesError} from './plugins/haste/DuplicateHasteCandidatesError';
128
125
  export {HasteConflictsError} from './plugins/haste/HasteConflictsError';
129
126
  export {default as HastePlugin} from './plugins/HastePlugin';
@@ -143,12 +140,11 @@ export type {
143
140
  // This should be bumped whenever a code change to `metro-file-map` itself
144
141
  // would cause a change to the cache data structure and/or content (for a given
145
142
  // filesystem state and build parameters).
146
- const CACHE_BREAKER = '10';
143
+ const CACHE_BREAKER = '11';
147
144
 
148
145
  const CHANGE_INTERVAL = 30;
149
146
 
150
147
  const NODE_MODULES = path.sep + 'node_modules' + path.sep;
151
- const PACKAGE_JSON = path.sep + 'package.json';
152
148
  const VCS_DIRECTORIES = /[/\\]\.(git|hg)[/\\]/.source;
153
149
  const WATCHMAN_REQUIRED_CAPABILITIES = [
154
150
  'field-content.sha1hex',
@@ -165,7 +161,7 @@ const WATCHMAN_REQUIRED_CAPABILITIES = [
165
161
  * hundreds of thousands of files. This implementation is scalable and provides
166
162
  * predictable performance.
167
163
  *
168
- * Because the haste map creation and synchronization is critical to startup
164
+ * Because the file map creation and synchronization is critical to startup
169
165
  * performance and most tasks are blocked by I/O this class makes heavy use of
170
166
  * synchronous operations. It uses worker processes for parallelizing file
171
167
  * access and metadata extraction.
@@ -214,7 +210,7 @@ const WATCHMAN_REQUIRED_CAPABILITIES = [
214
210
  * representation is similar to `['flatMap', 3421, 42, 1, []]` to save storage space
215
211
  * and reduce parse and write time of a big JSON blob.
216
212
  *
217
- * The HasteMap is created as follows:
213
+ * The FileMap is created as follows:
218
214
  * 1. read data from the cache or create an empty structure.
219
215
  *
220
216
  * 2. crawl the file system.
@@ -223,36 +219,34 @@ const WATCHMAN_REQUIRED_CAPABILITIES = [
223
219
  * * if watchman is available: get file system delta changes.
224
220
  * * if watchman is unavailable: crawl the entire file system.
225
221
  * * build metadata objects for every file. This builds the `files` part of
226
- * the `HasteMap`.
222
+ * the `FileMap`.
227
223
  *
228
- * 3. parse and extract metadata from changed files.
224
+ * 3. visit and extract metadata from changed files, including sha1,
225
+ * depedendencies, and any plugins.
229
226
  * * this is done in parallel over worker processes to improve performance.
230
- * * the worst case is to parse all files.
227
+ * * the worst case is to visit all files.
231
228
  * * the best case is no file system access and retrieving all data from
232
229
  * the cache.
233
230
  * * the average case is a small number of changed files.
234
231
  *
235
- * 4. serialize the new `HasteMap` in a cache file.
232
+ * 4. serialize the new `FileMap` in a cache file.
236
233
  *
237
234
  */
238
235
  export default class FileMap extends EventEmitter {
239
- _buildPromise: ?Promise<BuildResult>;
240
- _canUseWatchmanPromise: Promise<boolean>;
241
- _changeID: number;
242
- _changeInterval: ?IntervalID;
243
- _fileProcessor: FileProcessor;
244
- _console: Console;
245
- _options: InternalOptions;
246
- _pathUtils: RootPathUtils;
247
- _watcher: ?Watcher;
248
- _cacheManager: CacheManager;
249
- _crawlerAbortController: AbortController;
250
- _healthCheckInterval: ?IntervalID;
251
- _startupPerfLogger: ?PerfLogger;
252
-
253
- #hastePlugin: HastePlugin;
254
- #mockPlugin: ?MockPlugin = null;
255
- #plugins: $ReadOnlyArray<FileMapPlugin<>>;
236
+ #buildPromise: ?Promise<BuildResult>;
237
+ +#cacheManager: CacheManager;
238
+ #canUseWatchmanPromise: Promise<boolean>;
239
+ #changeID: number;
240
+ #changeInterval: ?IntervalID;
241
+ +#console: Console;
242
+ +#crawlerAbortController: AbortController;
243
+ +#fileProcessor: FileProcessor;
244
+ #healthCheckInterval: ?IntervalID;
245
+ +#options: InternalOptions;
246
+ +#pathUtils: RootPathUtils;
247
+ +#plugins: ReadonlyArray<IndexedPlugin>;
248
+ +#startupPerfLogger: ?PerfLogger;
249
+ #watcher: ?Watcher;
256
250
 
257
251
  static create(options: InputOptions): FileMap {
258
252
  return new FileMap(options);
@@ -262,9 +256,9 @@ export default class FileMap extends EventEmitter {
262
256
  super();
263
257
 
264
258
  if (options.perfLoggerFactory) {
265
- this._startupPerfLogger =
259
+ this.#startupPerfLogger =
266
260
  options.perfLoggerFactory?.('START_UP').subSpan('fileMap') ?? null;
267
- this._startupPerfLogger?.point('constructor_start');
261
+ this.#startupPerfLogger?.point('constructor_start');
268
262
  }
269
263
 
270
264
  // Add VCS_DIRECTORIES to provided ignorePattern
@@ -285,56 +279,39 @@ export default class FileMap extends EventEmitter {
285
279
  ignorePattern = new RegExp(VCS_DIRECTORIES);
286
280
  }
287
281
 
288
- this._console = options.console || global.console;
289
- const throwOnModuleCollision = Boolean(options.throwOnModuleCollision);
290
-
291
- const enableHastePackages = options.enableHastePackages ?? true;
292
-
293
- this.#hastePlugin = new HastePlugin({
294
- console: this._console,
295
- enableHastePackages,
296
- perfLogger: this._startupPerfLogger,
297
- platforms: new Set(options.platforms),
298
- rootDir: options.rootDir,
299
- failValidationOnConflicts: throwOnModuleCollision,
300
- });
282
+ this.#console = options.console || global.console;
301
283
 
302
- const plugins: Array<FileMapPlugin<$FlowFixMe>> = [this.#hastePlugin];
284
+ let dataSlot: number = H.PLUGINDATA;
303
285
 
304
- if (options.mocksPattern != null && options.mocksPattern !== '') {
305
- this.#mockPlugin = new MockPlugin({
306
- console: this._console,
307
- mocksPattern: new RegExp(options.mocksPattern),
308
- rootDir: options.rootDir,
309
- throwOnModuleCollision,
286
+ const indexedPlugins: Array<IndexedPlugin> = [];
287
+ const pluginWorkers: Array<FileMapPluginWorker> = [];
288
+ const plugins = options.plugins ?? [];
289
+ for (const plugin of plugins) {
290
+ const maybeWorker = plugin.getWorker();
291
+ indexedPlugins.push({
292
+ plugin,
293
+ dataIdx: maybeWorker != null ? dataSlot++ : null,
310
294
  });
311
- plugins.push(this.#mockPlugin);
295
+ if (maybeWorker != null) {
296
+ pluginWorkers.push(maybeWorker);
297
+ }
312
298
  }
313
-
314
- this.#plugins = plugins;
299
+ this.#plugins = indexedPlugins;
315
300
 
316
301
  const buildParameters: BuildParameters = {
317
- computeDependencies:
318
- options.computeDependencies == null
319
- ? true
320
- : options.computeDependencies,
302
+ cacheBreaker: CACHE_BREAKER,
321
303
  computeSha1: options.computeSha1 || false,
322
- dependencyExtractor: options.dependencyExtractor ?? null,
323
- enableHastePackages,
324
304
  enableSymlinks: options.enableSymlinks || false,
325
305
  extensions: options.extensions,
326
306
  forceNodeFilesystemAPI: !!options.forceNodeFilesystemAPI,
327
- hasteImplModulePath: options.hasteImplModulePath,
328
307
  ignorePattern,
329
- plugins: options.plugins ?? [],
308
+ plugins,
330
309
  retainAllFiles: options.retainAllFiles,
331
310
  rootDir: options.rootDir,
332
311
  roots: Array.from(new Set(options.roots)),
333
- skipPackageJson: !!options.skipPackageJson,
334
- cacheBreaker: CACHE_BREAKER,
335
312
  };
336
313
 
337
- this._options = {
314
+ this.#options = {
338
315
  ...buildParameters,
339
316
  healthCheck: options.healthCheck,
340
317
  perfLoggerFactory: options.perfLoggerFactory,
@@ -347,33 +324,31 @@ export default class FileMap extends EventEmitter {
347
324
  const cacheFactoryOptions: CacheManagerFactoryOptions = {
348
325
  buildParameters,
349
326
  };
350
- this._cacheManager = options.cacheManagerFactory
327
+ this.#cacheManager = options.cacheManagerFactory
351
328
  ? options.cacheManagerFactory.call(null, cacheFactoryOptions)
352
329
  : new DiskCacheManager(cacheFactoryOptions, {});
353
330
 
354
- this._fileProcessor = new FileProcessor({
355
- dependencyExtractor: buildParameters.dependencyExtractor,
356
- enableHastePackages: buildParameters.enableHastePackages,
357
- enableWorkerThreads: options.enableWorkerThreads ?? false,
358
- hasteImplModulePath: buildParameters.hasteImplModulePath,
331
+ this.#fileProcessor = new FileProcessor({
359
332
  maxFilesPerWorker: options.maxFilesPerWorker,
360
333
  maxWorkers: options.maxWorkers,
361
- perfLogger: this._startupPerfLogger,
334
+ perfLogger: this.#startupPerfLogger,
335
+ pluginWorkers,
336
+ rootDir: options.rootDir,
362
337
  });
363
338
 
364
- this._buildPromise = null;
365
- this._pathUtils = new RootPathUtils(options.rootDir);
366
- this._startupPerfLogger?.point('constructor_end');
367
- this._crawlerAbortController = new AbortController();
368
- this._changeID = 0;
339
+ this.#buildPromise = null;
340
+ this.#pathUtils = new RootPathUtils(options.rootDir);
341
+ this.#startupPerfLogger?.point('constructor_end');
342
+ this.#crawlerAbortController = new AbortController();
343
+ this.#changeID = 0;
369
344
  }
370
345
 
371
346
  build(): Promise<BuildResult> {
372
- this._startupPerfLogger?.point('build_start');
373
- if (!this._buildPromise) {
374
- this._buildPromise = (async () => {
347
+ this.#startupPerfLogger?.point('build_start');
348
+ if (!this.#buildPromise) {
349
+ this.#buildPromise = (async () => {
375
350
  let initialData: ?CacheData;
376
- if (this._options.resetCache !== true) {
351
+ if (this.#options.resetCache !== true) {
377
352
  initialData = await this.read();
378
353
  }
379
354
  if (!initialData) {
@@ -382,23 +357,22 @@ export default class FileMap extends EventEmitter {
382
357
  debug('Cache loaded (%d clock(s))', initialData.clocks.size);
383
358
  }
384
359
 
385
- const rootDir = this._options.rootDir;
386
- this._startupPerfLogger?.point('constructFileSystem_start');
360
+ const rootDir = this.#options.rootDir;
361
+ this.#startupPerfLogger?.point('constructFileSystem_start');
387
362
  const processFile: ProcessFileFunction = (
388
- absolutePath,
363
+ normalPath,
389
364
  metadata,
390
365
  opts,
391
366
  ) => {
392
- const result = this._fileProcessor.processRegularFile(
393
- absolutePath,
367
+ const result = this.#fileProcessor.processRegularFile(
368
+ normalPath,
394
369
  metadata,
395
370
  {
396
371
  computeSha1: opts.computeSha1,
397
- computeDependencies: false,
398
372
  maybeReturnContent: true,
399
373
  },
400
374
  );
401
- debug('Lazily processed file: %s', absolutePath);
375
+ debug('Lazily processed file: %s', normalPath);
402
376
  // Emit an event to inform caches that there is new data to save.
403
377
  this.emit('metadata');
404
378
  return result?.content;
@@ -406,16 +380,16 @@ export default class FileMap extends EventEmitter {
406
380
  const fileSystem =
407
381
  initialData != null
408
382
  ? TreeFS.fromDeserializedSnapshot({
409
- rootDir,
410
383
  // Typed `mixed` because we've read this from an external
411
384
  // source. It'd be too expensive to validate at runtime, so
412
385
  // trust our cache manager that this is correct.
413
386
  // $FlowFixMe[incompatible-type]
414
387
  fileSystemData: initialData.fileSystemData,
415
388
  processFile,
389
+ rootDir,
416
390
  })
417
- : new TreeFS({rootDir, processFile});
418
- this._startupPerfLogger?.point('constructFileSystem_end');
391
+ : new TreeFS({processFile, rootDir});
392
+ this.#startupPerfLogger?.point('constructFileSystem_end');
419
393
 
420
394
  const plugins = this.#plugins;
421
395
 
@@ -423,28 +397,53 @@ export default class FileMap extends EventEmitter {
423
397
  // crawling to build a diff of current state vs cached. `fileSystem`
424
398
  // is not mutated during either operation.
425
399
  const [fileDelta] = await Promise.all([
426
- this._buildFileDelta({
427
- fileSystem,
400
+ this.#buildFileDelta({
428
401
  clocks: initialData?.clocks ?? new Map(),
402
+ fileSystem,
429
403
  }),
430
404
  Promise.all(
431
- plugins.map(plugin =>
405
+ plugins.map(({plugin, dataIdx}) =>
432
406
  plugin.initialize({
433
- files: fileSystem,
407
+ files: {
408
+ lookup: mixedPath => {
409
+ const result = fileSystem.lookup(mixedPath);
410
+ if (!result.exists) {
411
+ return {exists: false};
412
+ }
413
+ if (result.type === 'd') {
414
+ return {exists: true, type: 'd'};
415
+ }
416
+ return {
417
+ exists: true,
418
+ type: 'f',
419
+ pluginData:
420
+ dataIdx != null ? result.metadata[dataIdx] : null,
421
+ };
422
+ },
423
+ fileIterator: opts =>
424
+ mapIterator(
425
+ fileSystem.metadataIterator(opts),
426
+ ({baseName, canonicalPath, metadata}) => ({
427
+ baseName,
428
+ canonicalPath,
429
+ pluginData: dataIdx != null ? metadata[dataIdx] : null,
430
+ }),
431
+ ),
432
+ },
434
433
  pluginState: initialData?.plugins.get(plugin.name),
435
434
  }),
436
435
  ),
437
436
  ),
438
437
  ]);
439
438
 
440
- // Update `fileSystem`, `hasteMap` and `mocks` based on the file delta.
441
- await this._applyFileDelta(fileSystem, plugins, fileDelta);
439
+ // Update `fileSystem` and plugins based on the file delta.
440
+ await this.#applyFileDelta(fileSystem, plugins, fileDelta);
442
441
 
443
- // Validate the mock and Haste maps before persisting them.
444
- plugins.forEach(plugin => plugin.assertValid());
442
+ // Validate plugins before persisting them.
443
+ plugins.forEach(({plugin}) => plugin.assertValid());
445
444
 
446
445
  const watchmanClocks = new Map(fileDelta.clocks ?? []);
447
- await this._takeSnapshotAndPersist(
446
+ await this.#takeSnapshotAndPersist(
448
447
  fileSystem,
449
448
  watchmanClocks,
450
449
  plugins,
@@ -457,16 +456,12 @@ export default class FileMap extends EventEmitter {
457
456
  fileDelta.removedFiles.size,
458
457
  );
459
458
 
460
- await this._watch(fileSystem, watchmanClocks, plugins);
461
- return {
462
- fileSystem,
463
- hasteMap: this.#hastePlugin,
464
- mockMap: this.#mockPlugin,
465
- };
459
+ await this.#watch(fileSystem, watchmanClocks, plugins);
460
+ return {fileSystem};
466
461
  })();
467
462
  }
468
- return this._buildPromise.then(result => {
469
- this._startupPerfLogger?.point('build_end');
463
+ return this.#buildPromise.then(result => {
464
+ this.#startupPerfLogger?.point('build_end');
470
465
  return result;
471
466
  });
472
467
  }
@@ -476,33 +471,33 @@ export default class FileMap extends EventEmitter {
476
471
  */
477
472
  async read(): Promise<?CacheData> {
478
473
  let data: ?CacheData;
479
- this._startupPerfLogger?.point('read_start');
474
+ this.#startupPerfLogger?.point('read_start');
480
475
  try {
481
- data = await this._cacheManager.read();
476
+ data = await this.#cacheManager.read();
482
477
  } catch (e) {
483
- this._console.warn(
478
+ this.#console.warn(
484
479
  'Error while reading cache, falling back to a full crawl:\n',
485
480
  e,
486
481
  );
487
- this._startupPerfLogger?.annotate({
482
+ this.#startupPerfLogger?.annotate({
488
483
  string: {cacheReadError: e.toString()},
489
484
  });
490
485
  }
491
- this._startupPerfLogger?.point('read_end');
486
+ this.#startupPerfLogger?.point('read_end');
492
487
  return data;
493
488
  }
494
489
 
495
490
  /**
496
491
  * 2. crawl the file system.
497
492
  */
498
- async _buildFileDelta(
493
+ async #buildFileDelta(
499
494
  previousState: CrawlerOptions['previousState'],
500
495
  ): Promise<{
501
496
  removedFiles: Set<CanonicalPath>,
502
497
  changedFiles: FileData,
503
498
  clocks?: WatchmanClocks,
504
499
  }> {
505
- this._startupPerfLogger?.point('buildFileDelta_start');
500
+ this.#startupPerfLogger?.point('buildFileDelta_start');
506
501
 
507
502
  const {
508
503
  computeSha1,
@@ -515,16 +510,16 @@ export default class FileMap extends EventEmitter {
515
510
  rootDir,
516
511
  watch,
517
512
  watchmanDeferStates,
518
- } = this._options;
513
+ } = this.#options;
519
514
 
520
- this._watcher = new Watcher({
521
- abortSignal: this._crawlerAbortController.signal,
515
+ this.#watcher = new Watcher({
516
+ abortSignal: this.#crawlerAbortController.signal,
522
517
  computeSha1,
523
- console: this._console,
518
+ console: this.#console,
524
519
  enableSymlinks,
525
520
  extensions,
526
521
  forceNodeFilesystemAPI,
527
- healthCheckFilePrefix: this._options.healthCheck.filePrefix,
522
+ healthCheckFilePrefix: this.#options.healthCheck.filePrefix,
528
523
  // TODO: Refactor out the two different ignore strategies here.
529
524
  ignoreForCrawl: filePath => {
530
525
  const ignoreMatched = ignorePattern.test(filePath);
@@ -533,53 +528,55 @@ export default class FileMap extends EventEmitter {
533
528
  );
534
529
  },
535
530
  ignorePatternForWatch: ignorePattern,
536
- perfLogger: this._startupPerfLogger,
531
+ perfLogger: this.#startupPerfLogger,
537
532
  previousState,
538
- roots,
539
533
  rootDir,
540
- useWatchman: await this._shouldUseWatchman(),
534
+ roots,
535
+ useWatchman: await this.#shouldUseWatchman(),
541
536
  watch,
542
537
  watchmanDeferStates,
543
538
  });
544
- const watcher = this._watcher;
539
+ const watcher = this.#watcher;
545
540
 
546
541
  watcher.on('status', status => this.emit('status', status));
547
542
 
548
543
  return watcher.crawl().then(result => {
549
- this._startupPerfLogger?.point('buildFileDelta_end');
544
+ this.#startupPerfLogger?.point('buildFileDelta_end');
550
545
  return result;
551
546
  });
552
547
  }
553
548
 
554
- _maybeReadLink(filePath: Path, fileMetadata: FileMetadata): ?Promise<void> {
549
+ #maybeReadLink(normalPath: Path, fileMetadata: FileMetadata): ?Promise<void> {
555
550
  // If we only need to read a link, it's more efficient to do it in-band
556
551
  // (with async file IO) than to have the overhead of worker IO.
557
552
  if (fileMetadata[H.SYMLINK] === 1) {
558
- return fsPromises.readlink(filePath).then(symlinkTarget => {
559
- fileMetadata[H.VISITED] = 1;
560
- fileMetadata[H.SYMLINK] = symlinkTarget;
561
- });
553
+ return fsPromises
554
+ .readlink(this.#pathUtils.normalToAbsolute(normalPath))
555
+ .then(symlinkTarget => {
556
+ fileMetadata[H.VISITED] = 1;
557
+ fileMetadata[H.SYMLINK] = symlinkTarget;
558
+ });
562
559
  }
563
560
  return null;
564
561
  }
565
562
 
566
- async _applyFileDelta(
563
+ async #applyFileDelta(
567
564
  fileSystem: MutableFileSystem,
568
- plugins: $ReadOnlyArray<FileMapPlugin<>>,
569
- delta: $ReadOnly<{
565
+ plugins: ReadonlyArray<IndexedPlugin>,
566
+ delta: Readonly<{
570
567
  changedFiles: FileData,
571
- removedFiles: $ReadOnlySet<CanonicalPath>,
568
+ removedFiles: ReadonlySet<CanonicalPath>,
572
569
  clocks?: WatchmanClocks,
573
570
  }>,
574
571
  ): Promise<void> {
575
- this._startupPerfLogger?.point('applyFileDelta_start');
572
+ this.#startupPerfLogger?.point('applyFileDelta_start');
576
573
  const {changedFiles, removedFiles} = delta;
577
- this._startupPerfLogger?.point('applyFileDelta_preprocess_start');
574
+ this.#startupPerfLogger?.point('applyFileDelta_preprocess_start');
578
575
  const missingFiles: Set<string> = new Set();
579
576
 
580
- // Remove files first so that we don't mistake moved mocks or Haste
577
+ // Remove files first so that we don't mistake moved modules
581
578
  // modules as duplicates.
582
- this._startupPerfLogger?.point('applyFileDelta_remove_start');
579
+ this.#startupPerfLogger?.point('applyFileDelta_remove_start');
583
580
  const removed: Array<[string, FileMetadata]> = [];
584
581
  for (const relativeFilePath of removedFiles) {
585
582
  const metadata = fileSystem.remove(relativeFilePath);
@@ -587,77 +584,52 @@ export default class FileMap extends EventEmitter {
587
584
  removed.push([relativeFilePath, metadata]);
588
585
  }
589
586
  }
590
- this._startupPerfLogger?.point('applyFileDelta_remove_end');
587
+ this.#startupPerfLogger?.point('applyFileDelta_remove_end');
591
588
 
592
589
  const readLinkPromises = [];
593
590
  const readLinkErrors: Array<{
594
- absolutePath: string,
591
+ normalFilePath: string,
595
592
  error: Error & {code?: string},
596
593
  }> = [];
597
594
  const filesToProcess: Array<[string, FileMetadata]> = [];
598
595
 
599
- for (const [relativeFilePath, fileData] of changedFiles) {
596
+ for (const [normalFilePath, fileData] of changedFiles) {
600
597
  // A crawler may preserve the H.VISITED flag to indicate that the file
601
598
  // contents are unchaged and it doesn't need visiting again.
602
599
  if (fileData[H.VISITED] === 1) {
603
600
  continue;
604
601
  }
605
602
 
606
- if (
607
- this._options.skipPackageJson &&
608
- relativeFilePath.endsWith(PACKAGE_JSON)
609
- ) {
610
- continue;
611
- }
612
-
613
- if (
614
- fileData[H.SYMLINK] === 0 &&
615
- !this._options.computeDependencies &&
616
- !this._options.computeSha1 &&
617
- this._options.hasteImplModulePath == null &&
618
- !(
619
- this._options.enableHastePackages &&
620
- relativeFilePath.endsWith(PACKAGE_JSON)
621
- )
622
- ) {
623
- // Nothing to process
624
- continue;
625
- }
626
-
627
- // SHA-1, if requested, should already be present thanks to the crawler.
628
- const absolutePath = this._pathUtils.normalToAbsolute(relativeFilePath);
629
-
630
603
  if (fileData[H.SYMLINK] === 0) {
631
- filesToProcess.push([absolutePath, fileData]);
604
+ filesToProcess.push([normalFilePath, fileData]);
632
605
  } else {
633
- const maybeReadLink = this._maybeReadLink(absolutePath, fileData);
606
+ const maybeReadLink = this.#maybeReadLink(normalFilePath, fileData);
634
607
  if (maybeReadLink) {
635
608
  readLinkPromises.push(
636
609
  maybeReadLink.catch(error =>
637
- readLinkErrors.push({absolutePath, error}),
610
+ readLinkErrors.push({normalFilePath, error}),
638
611
  ),
639
612
  );
640
613
  }
641
614
  }
642
615
  }
643
- this._startupPerfLogger?.point('applyFileDelta_preprocess_end');
616
+ this.#startupPerfLogger?.point('applyFileDelta_preprocess_end');
644
617
 
645
618
  debug(
646
- 'Visiting %d added/modified files and %d symlinks.',
619
+ 'Found %d added/modified files and %d symlinks.',
647
620
  filesToProcess.length,
648
621
  readLinkPromises.length,
649
622
  );
650
623
 
651
- this._startupPerfLogger?.point('applyFileDelta_process_start');
624
+ this.#startupPerfLogger?.point('applyFileDelta_process_start');
652
625
  const [batchResult] = await Promise.all([
653
- this._fileProcessor.processBatch(filesToProcess, {
654
- computeSha1: this._options.computeSha1,
655
- computeDependencies: this._options.computeDependencies,
626
+ this.#fileProcessor.processBatch(filesToProcess, {
627
+ computeSha1: this.#options.computeSha1,
656
628
  maybeReturnContent: false,
657
629
  }),
658
630
  Promise.all(readLinkPromises),
659
631
  ]);
660
- this._startupPerfLogger?.point('applyFileDelta_process_end');
632
+ this.#startupPerfLogger?.point('applyFileDelta_process_end');
661
633
 
662
634
  // It's possible that a file could be deleted between being seen by the
663
635
  // crawler and our attempt to process it. For our purposes, this is
@@ -668,14 +640,14 @@ export default class FileMap extends EventEmitter {
668
640
  // Treat the file accordingly - don't add it to `FileSystem`, and remove
669
641
  // it if it already exists. We're not emitting events at this point in
670
642
  // startup, so there's nothing more to do.
671
- this._startupPerfLogger?.point('applyFileDelta_missing_start');
672
- for (const {absolutePath, error} of batchResult.errors.concat(
643
+ this.#startupPerfLogger?.point('applyFileDelta_missing_start');
644
+ for (const {normalFilePath, error} of batchResult.errors.concat(
673
645
  readLinkErrors,
674
646
  )) {
675
647
  /* $FlowFixMe[incompatible-type] Error exposed after improved typing of
676
648
  * Array.{includes,indexOf,lastIndexOf} */
677
649
  if (['ENOENT', 'EACCESS'].includes(error.code)) {
678
- missingFiles.add(this._pathUtils.absoluteToNormal(absolutePath));
650
+ missingFiles.add(normalFilePath);
679
651
  } else {
680
652
  // Anything else is fatal.
681
653
  throw error;
@@ -688,42 +660,49 @@ export default class FileMap extends EventEmitter {
688
660
  removed.push([relativeFilePath, metadata]);
689
661
  }
690
662
  }
691
- this._startupPerfLogger?.point('applyFileDelta_missing_end');
663
+ this.#startupPerfLogger?.point('applyFileDelta_missing_end');
692
664
 
693
- this._startupPerfLogger?.point('applyFileDelta_add_start');
665
+ this.#startupPerfLogger?.point('applyFileDelta_add_start');
694
666
  fileSystem.bulkAddOrModify(changedFiles);
695
- this._startupPerfLogger?.point('applyFileDelta_add_end');
667
+ this.#startupPerfLogger?.point('applyFileDelta_add_end');
668
+
669
+ this.#startupPerfLogger?.point('applyFileDelta_updatePlugins_start');
696
670
 
697
- this._startupPerfLogger?.point('applyFileDelta_updatePlugins_start');
698
671
  await Promise.all([
699
- plugins.map(plugin =>
700
- plugin.bulkUpdate({
701
- addedOrModified: changedFiles,
702
- removed,
703
- }),
704
- ),
672
+ plugins.map(({plugin, dataIdx}) => {
673
+ const mapFn: (
674
+ [CanonicalPath, FileMetadata],
675
+ ) => [CanonicalPath, unknown] =
676
+ dataIdx != null
677
+ ? ([relativePath, fileData]) => [relativePath, fileData[dataIdx]]
678
+ : ([relativePath, fileData]) => [relativePath, null];
679
+ return plugin.bulkUpdate({
680
+ addedOrModified: mapIterator(changedFiles.entries(), mapFn),
681
+ removed: mapIterator(removed.values(), mapFn),
682
+ });
683
+ }),
705
684
  ]);
706
- this._startupPerfLogger?.point('applyFileDelta_updatePlugins_end');
707
- this._startupPerfLogger?.point('applyFileDelta_end');
685
+ this.#startupPerfLogger?.point('applyFileDelta_updatePlugins_end');
686
+ this.#startupPerfLogger?.point('applyFileDelta_end');
708
687
  }
709
688
 
710
689
  /**
711
690
  * 4. Serialize a snapshot of our raw data via the configured cache manager
712
691
  */
713
- async _takeSnapshotAndPersist(
692
+ async #takeSnapshotAndPersist(
714
693
  fileSystem: FileSystem,
715
694
  clocks: WatchmanClocks,
716
- plugins: $ReadOnlyArray<FileMapPlugin<>>,
695
+ plugins: ReadonlyArray<IndexedPlugin>,
717
696
  changed: FileData,
718
697
  removed: Set<CanonicalPath>,
719
698
  ) {
720
- this._startupPerfLogger?.point('persist_start');
721
- await this._cacheManager.write(
699
+ this.#startupPerfLogger?.point('persist_start');
700
+ await this.#cacheManager.write(
722
701
  () => ({
723
- fileSystemData: fileSystem.getSerializableSnapshot(),
724
702
  clocks: new Map(clocks),
703
+ fileSystemData: fileSystem.getSerializableSnapshot(),
725
704
  plugins: new Map(
726
- plugins.map(plugin => [
705
+ plugins.map(({plugin}) => [
727
706
  plugin.name,
728
707
  plugin.getSerializableSnapshot(),
729
708
  ]),
@@ -745,29 +724,29 @@ export default class FileMap extends EventEmitter {
745
724
  },
746
725
  },
747
726
  onWriteError: error => {
748
- this._console.warn('[metro-file-map] Cache write error\n:', error);
727
+ this.#console.warn('[metro-file-map] Cache write error\n:', error);
749
728
  },
750
729
  },
751
730
  );
752
- this._startupPerfLogger?.point('persist_end');
731
+ this.#startupPerfLogger?.point('persist_end');
753
732
  }
754
733
 
755
734
  /**
756
735
  * Watch mode
757
736
  */
758
- async _watch(
737
+ async #watch(
759
738
  fileSystem: MutableFileSystem,
760
739
  clocks: WatchmanClocks,
761
- plugins: $ReadOnlyArray<FileMapPlugin<>>,
740
+ plugins: ReadonlyArray<IndexedPlugin>,
762
741
  ): Promise<void> {
763
- this._startupPerfLogger?.point('watch_start');
764
- if (!this._options.watch) {
765
- this._startupPerfLogger?.point('watch_end');
742
+ this.#startupPerfLogger?.point('watch_start');
743
+ if (!this.#options.watch) {
744
+ this.#startupPerfLogger?.point('watch_end');
766
745
  return;
767
746
  }
768
747
 
769
748
  const hasWatchedExtension = (filePath: string) =>
770
- this._options.extensions.some(ext => filePath.endsWith(ext));
749
+ this.#options.extensions.some(ext => filePath.endsWith(ext));
771
750
 
772
751
  let changeQueue: Promise<null | void> = Promise.resolve();
773
752
  let nextEmit: ?{
@@ -783,8 +762,8 @@ export default class FileMap extends EventEmitter {
783
762
  }
784
763
  const {eventsQueue, firstEventTimestamp, firstEnqueuedTimestamp} =
785
764
  nextEmit;
786
- const hmrPerfLogger = this._options.perfLoggerFactory?.('HMR', {
787
- key: this._getNextChangeID(),
765
+ const hmrPerfLogger = this.#options.perfLoggerFactory?.('HMR', {
766
+ key: this.#getNextChangeID(),
788
767
  });
789
768
  if (hmrPerfLogger != null) {
790
769
  hmrPerfLogger.start({timestamp: firstEventTimestamp});
@@ -798,8 +777,8 @@ export default class FileMap extends EventEmitter {
798
777
  hmrPerfLogger.point('fileChange_start');
799
778
  }
800
779
  const changeEvent: ChangeEvent = {
801
- logger: hmrPerfLogger,
802
780
  eventsQueue,
781
+ logger: hmrPerfLogger,
803
782
  };
804
783
  this.emit('change', changeEvent);
805
784
  nextEmit = null;
@@ -814,7 +793,7 @@ export default class FileMap extends EventEmitter {
814
793
  (change.metadata.type === 'f' &&
815
794
  !hasWatchedExtension(change.relativePath)) ||
816
795
  // Don't emit events relating to symlinks if enableSymlinks: false
817
- (!this._options.enableSymlinks && change.metadata?.type === 'l'))
796
+ (!this.#options.enableSymlinks && change.metadata?.type === 'l'))
818
797
  ) {
819
798
  return;
820
799
  }
@@ -826,12 +805,12 @@ export default class FileMap extends EventEmitter {
826
805
 
827
806
  // Ignore files (including symlinks) whose path matches ignorePattern
828
807
  // (we don't ignore node_modules in watch mode)
829
- if (this._options.ignorePattern.test(absoluteFilePath)) {
808
+ if (this.#options.ignorePattern.test(absoluteFilePath)) {
830
809
  return;
831
810
  }
832
811
 
833
812
  const relativeFilePath =
834
- this._pathUtils.absoluteToNormal(absoluteFilePath);
813
+ this.#pathUtils.absoluteToNormal(absoluteFilePath);
835
814
  const linkStats = fileSystem.linkStats(relativeFilePath);
836
815
 
837
816
  // The file has been accessed, not modified. If the modified time is
@@ -889,9 +868,9 @@ export default class FileMap extends EventEmitter {
889
868
  if (nextEmit == null) {
890
869
  nextEmit = {
891
870
  eventsQueue: [event],
892
- firstEventTimestamp: onChangeStartTime,
893
871
  firstEnqueuedTimestamp:
894
872
  performance.timeOrigin + performance.now(),
873
+ firstEventTimestamp: onChangeStartTime,
895
874
  };
896
875
  } else {
897
876
  nextEmit.eventsQueue.push(event);
@@ -900,7 +879,7 @@ export default class FileMap extends EventEmitter {
900
879
  };
901
880
 
902
881
  // If the file was added or modified,
903
- // parse it and update the haste map.
882
+ // parse it and update the file map.
904
883
  if (change.event === 'touch') {
905
884
  invariant(
906
885
  change.metadata.size != null,
@@ -910,30 +889,33 @@ export default class FileMap extends EventEmitter {
910
889
  change.metadata.modifiedTime,
911
890
  change.metadata.size,
912
891
  0,
913
- '',
914
892
  null,
915
893
  change.metadata.type === 'l' ? 1 : 0,
916
- '',
894
+ null,
917
895
  ];
918
896
 
919
897
  try {
920
898
  if (change.metadata.type === 'l') {
921
- await this._maybeReadLink(absoluteFilePath, fileMetadata);
899
+ await this.#maybeReadLink(relativeFilePath, fileMetadata);
922
900
  } else {
923
- await this._fileProcessor.processRegularFile(
924
- absoluteFilePath,
901
+ await this.#fileProcessor.processRegularFile(
902
+ relativeFilePath,
925
903
  fileMetadata,
926
904
  {
927
- computeSha1: this._options.computeSha1,
928
- computeDependencies: this._options.computeDependencies,
905
+ computeSha1: this.#options.computeSha1,
929
906
  maybeReturnContent: false,
930
907
  },
931
908
  );
932
909
  }
933
910
  fileSystem.addOrModify(relativeFilePath, fileMetadata);
934
- this._updateClock(clocks, change.clock);
935
- plugins.forEach(plugin =>
936
- plugin.onNewOrModifiedFile(relativeFilePath, fileMetadata),
911
+ this.#updateClock(clocks, change.clock);
912
+ plugins.forEach(({plugin, dataIdx}) =>
913
+ dataIdx != null
914
+ ? plugin.onNewOrModifiedFile(
915
+ relativeFilePath,
916
+ fileMetadata[dataIdx],
917
+ )
918
+ : plugin.onNewOrModifiedFile(relativeFilePath),
937
919
  );
938
920
  enqueueEvent(change.metadata);
939
921
  } catch (e) {
@@ -956,9 +938,11 @@ export default class FileMap extends EventEmitter {
956
938
  // We've already checked linkStats != null above, so the file
957
939
  // exists in the file map and remove should always return metadata.
958
940
  const metadata = nullthrows(fileSystem.remove(relativeFilePath));
959
- this._updateClock(clocks, change.clock);
960
- plugins.forEach(plugin =>
961
- plugin.onRemovedFile(relativeFilePath, metadata),
941
+ this.#updateClock(clocks, change.clock);
942
+ plugins.forEach(({plugin, dataIdx}) =>
943
+ dataIdx != null
944
+ ? plugin.onRemovedFile(relativeFilePath, metadata[dataIdx])
945
+ : plugin.onRemovedFile(relativeFilePath),
962
946
  );
963
947
 
964
948
  enqueueEvent({
@@ -974,68 +958,68 @@ export default class FileMap extends EventEmitter {
974
958
  return null;
975
959
  })
976
960
  .catch((error: Error) => {
977
- this._console.error(
961
+ this.#console.error(
978
962
  `metro-file-map: watch error:\n ${error.stack}\n`,
979
963
  );
980
964
  });
981
965
  };
982
966
 
983
- this._changeInterval = setInterval(emitChange, CHANGE_INTERVAL);
967
+ this.#changeInterval = setInterval(emitChange, CHANGE_INTERVAL);
984
968
 
985
969
  invariant(
986
- this._watcher != null,
987
- 'Expected _watcher to have been initialised by build()',
970
+ this.#watcher != null,
971
+ 'Expected #watcher to have been initialised by build()',
988
972
  );
989
- await this._watcher.watch(onChange);
973
+ await this.#watcher.watch(onChange);
990
974
 
991
- if (this._options.healthCheck.enabled) {
975
+ if (this.#options.healthCheck.enabled) {
992
976
  const performHealthCheck = () => {
993
- if (!this._watcher) {
977
+ if (!this.#watcher) {
994
978
  return;
995
979
  }
996
980
  // $FlowFixMe[unused-promise]
997
- this._watcher
998
- .checkHealth(this._options.healthCheck.timeout)
981
+ this.#watcher
982
+ .checkHealth(this.#options.healthCheck.timeout)
999
983
  .then(result => {
1000
984
  this.emit('healthCheck', result);
1001
985
  });
1002
986
  };
1003
987
  performHealthCheck();
1004
- this._healthCheckInterval = setInterval(
988
+ this.#healthCheckInterval = setInterval(
1005
989
  performHealthCheck,
1006
- this._options.healthCheck.interval,
990
+ this.#options.healthCheck.interval,
1007
991
  );
1008
992
  }
1009
- this._startupPerfLogger?.point('watch_end');
993
+ this.#startupPerfLogger?.point('watch_end');
1010
994
  }
1011
995
 
1012
996
  async end(): Promise<void> {
1013
- if (this._changeInterval) {
1014
- clearInterval(this._changeInterval);
997
+ if (this.#changeInterval) {
998
+ clearInterval(this.#changeInterval);
1015
999
  }
1016
- if (this._healthCheckInterval) {
1017
- clearInterval(this._healthCheckInterval);
1000
+ if (this.#healthCheckInterval) {
1001
+ clearInterval(this.#healthCheckInterval);
1018
1002
  }
1019
1003
 
1020
- this._crawlerAbortController.abort();
1004
+ this.#crawlerAbortController.abort();
1021
1005
 
1022
1006
  await Promise.all([
1023
- this._fileProcessor.end(),
1024
- this._watcher?.close(),
1025
- this._cacheManager.end(),
1007
+ this.#fileProcessor.end(),
1008
+ this.#watcher?.close(),
1009
+ this.#cacheManager.end(),
1026
1010
  ]);
1027
1011
  }
1028
1012
 
1029
- async _shouldUseWatchman(): Promise<boolean> {
1030
- if (!this._options.useWatchman) {
1013
+ async #shouldUseWatchman(): Promise<boolean> {
1014
+ if (!this.#options.useWatchman) {
1031
1015
  return false;
1032
1016
  }
1033
- if (!this._canUseWatchmanPromise) {
1034
- this._canUseWatchmanPromise = checkWatchmanCapabilities(
1017
+ if (!this.#canUseWatchmanPromise) {
1018
+ this.#canUseWatchmanPromise = checkWatchmanCapabilities(
1035
1019
  WATCHMAN_REQUIRED_CAPABILITIES,
1036
1020
  )
1037
1021
  .then(({version}) => {
1038
- this._startupPerfLogger?.annotate({
1022
+ this.#startupPerfLogger?.annotate({
1039
1023
  string: {
1040
1024
  watchmanVersion: version,
1041
1025
  },
@@ -1045,7 +1029,7 @@ export default class FileMap extends EventEmitter {
1045
1029
  .catch(e => {
1046
1030
  // TODO: Advise people to either install Watchman or set
1047
1031
  // `useWatchman: false` here?
1048
- this._startupPerfLogger?.annotate({
1032
+ this.#startupPerfLogger?.annotate({
1049
1033
  string: {
1050
1034
  watchmanFailedCapabilityCheck: e?.message ?? '[missing]',
1051
1035
  },
@@ -1053,24 +1037,34 @@ export default class FileMap extends EventEmitter {
1053
1037
  return false;
1054
1038
  });
1055
1039
  }
1056
- return this._canUseWatchmanPromise;
1040
+ return this.#canUseWatchmanPromise;
1057
1041
  }
1058
1042
 
1059
- _getNextChangeID(): number {
1060
- if (this._changeID >= Number.MAX_SAFE_INTEGER) {
1061
- this._changeID = 0;
1043
+ #getNextChangeID(): number {
1044
+ if (this.#changeID >= Number.MAX_SAFE_INTEGER) {
1045
+ this.#changeID = 0;
1062
1046
  }
1063
- return ++this._changeID;
1047
+ return ++this.#changeID;
1064
1048
  }
1065
1049
 
1066
- _updateClock(clocks: WatchmanClocks, newClock?: ?ChangeEventClock): void {
1050
+ #updateClock(clocks: WatchmanClocks, newClock?: ?ChangeEventClock): void {
1067
1051
  if (newClock == null) {
1068
1052
  return;
1069
1053
  }
1070
1054
  const [absoluteWatchRoot, clockSpec] = newClock;
1071
- const relativeFsRoot = this._pathUtils.absoluteToNormal(absoluteWatchRoot);
1055
+ const relativeFsRoot = this.#pathUtils.absoluteToNormal(absoluteWatchRoot);
1072
1056
  clocks.set(normalizePathSeparatorsToPosix(relativeFsRoot), clockSpec);
1073
1057
  }
1074
1058
 
1075
1059
  static H: HType = H;
1076
1060
  }
1061
+
1062
+ // TODO: Replace with it.map() from Node 22+
1063
+ const mapIterator: <T, S>(Iterator<T>, (T) => S) => Iterable<S> = (it, fn) =>
1064
+ 'map' in it
1065
+ ? it.map(fn)
1066
+ : (function* mapped() {
1067
+ for (const item of it) {
1068
+ yield fn(item);
1069
+ }
1070
+ })();