metro-file-map 0.84.2 → 0.84.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.
Files changed (37) hide show
  1. package/package.json +1 -1
  2. package/src/Watcher.d.ts +6 -9
  3. package/src/Watcher.js +66 -39
  4. package/src/Watcher.js.flow +84 -51
  5. package/src/crawlers/node/index.d.ts +3 -5
  6. package/src/crawlers/node/index.js +4 -1
  7. package/src/crawlers/node/index.js.flow +8 -6
  8. package/src/crawlers/watchman/index.d.ts +5 -12
  9. package/src/crawlers/watchman/index.js.flow +2 -6
  10. package/src/flow-types.d.ts +81 -32
  11. package/src/flow-types.js.flow +89 -29
  12. package/src/index.d.ts +4 -4
  13. package/src/index.js +145 -120
  14. package/src/index.js.flow +199 -149
  15. package/src/lib/FileSystemChangeAggregator.d.ts +40 -0
  16. package/src/lib/FileSystemChangeAggregator.js +89 -0
  17. package/src/lib/FileSystemChangeAggregator.js.flow +143 -0
  18. package/src/lib/TreeFS.d.ts +16 -8
  19. package/src/lib/TreeFS.js +67 -16
  20. package/src/lib/TreeFS.js.flow +89 -16
  21. package/src/plugins/DependencyPlugin.d.ts +2 -13
  22. package/src/plugins/DependencyPlugin.js +1 -3
  23. package/src/plugins/DependencyPlugin.js.flow +1 -16
  24. package/src/plugins/HastePlugin.d.ts +3 -11
  25. package/src/plugins/HastePlugin.js +11 -11
  26. package/src/plugins/HastePlugin.js.flow +12 -12
  27. package/src/plugins/MockPlugin.d.ts +3 -5
  28. package/src/plugins/MockPlugin.js +17 -20
  29. package/src/plugins/MockPlugin.js.flow +18 -22
  30. package/src/watchers/FallbackWatcher.js +19 -3
  31. package/src/watchers/FallbackWatcher.js.flow +28 -5
  32. package/src/watchers/NativeWatcher.d.ts +2 -2
  33. package/src/watchers/NativeWatcher.js +27 -5
  34. package/src/watchers/NativeWatcher.js.flow +33 -6
  35. package/src/watchers/common.d.ts +3 -1
  36. package/src/watchers/common.js +6 -1
  37. package/src/watchers/common.js.flow +1 -0
package/src/index.js CHANGED
@@ -40,6 +40,7 @@ var _checkWatchmanCapabilities = _interopRequireDefault(
40
40
  require("./lib/checkWatchmanCapabilities"),
41
41
  );
42
42
  var _FileProcessor = require("./lib/FileProcessor");
43
+ var _FileSystemChangeAggregator = require("./lib/FileSystemChangeAggregator");
43
44
  var _normalizePathSeparatorsToPosix = _interopRequireDefault(
44
45
  require("./lib/normalizePathSeparatorsToPosix"),
45
46
  );
@@ -52,7 +53,6 @@ var _Watcher = require("./Watcher");
52
53
  var _events = _interopRequireDefault(require("events"));
53
54
  var _fs = require("fs");
54
55
  var _invariant = _interopRequireDefault(require("invariant"));
55
- var _nullthrows = _interopRequireDefault(require("nullthrows"));
56
56
  var path = _interopRequireWildcard(require("path"));
57
57
  var _perf_hooks = require("perf_hooks");
58
58
  var _DependencyPlugin = _interopRequireDefault(
@@ -270,7 +270,7 @@ class FileMap extends _events.default {
270
270
  };
271
271
  },
272
272
  fileIterator: (opts) =>
273
- mapIterator(
273
+ mapIterable(
274
274
  fileSystem.metadataIterator(opts),
275
275
  ({ baseName, canonicalPath, metadata }) => ({
276
276
  baseName,
@@ -284,21 +284,21 @@ class FileMap extends _events.default {
284
284
  ),
285
285
  ),
286
286
  ]);
287
- await this.#applyFileDelta(fileSystem, plugins, fileDelta);
287
+ const actualChanges = await this.#applyFileDelta(
288
+ fileSystem,
289
+ plugins,
290
+ fileDelta,
291
+ );
292
+ const changeSize = actualChanges.getSize();
288
293
  plugins.forEach(({ plugin }) => plugin.assertValid());
289
294
  const watchmanClocks = new Map(fileDelta.clocks ?? []);
290
295
  await this.#takeSnapshotAndPersist(
291
296
  fileSystem,
292
297
  watchmanClocks,
293
298
  plugins,
294
- fileDelta.changedFiles,
295
- fileDelta.removedFiles,
296
- );
297
- debug(
298
- "Finished mapping files (%d changes, %d removed).",
299
- fileDelta.changedFiles.size,
300
- fileDelta.removedFiles.size,
299
+ changeSize > 0,
301
300
  );
301
+ debug("Finished mapping files (%d changes).", changeSize);
302
302
  await this.#watch(fileSystem, watchmanClocks, plugins);
303
303
  return {
304
304
  fileSystem,
@@ -368,10 +368,9 @@ class FileMap extends _events.default {
368
368
  });
369
369
  const watcher = this.#watcher;
370
370
  watcher.on("status", (status) => this.emit("status", status));
371
- return watcher.crawl().then((result) => {
372
- this.#startupPerfLogger?.point("buildFileDelta_end");
373
- return result;
374
- });
371
+ const result = await watcher.crawl();
372
+ this.#startupPerfLogger?.point("buildFileDelta_end");
373
+ return result;
375
374
  }
376
375
  #maybeReadLink(normalPath, fileMetadata) {
377
376
  if (fileMetadata[_constants.default.SYMLINK] === 1) {
@@ -388,14 +387,11 @@ class FileMap extends _events.default {
388
387
  this.#startupPerfLogger?.point("applyFileDelta_start");
389
388
  const { changedFiles, removedFiles } = delta;
390
389
  this.#startupPerfLogger?.point("applyFileDelta_preprocess_start");
391
- const missingFiles = new Set();
392
390
  this.#startupPerfLogger?.point("applyFileDelta_remove_start");
393
- const removed = [];
391
+ const changeAggregator =
392
+ new _FileSystemChangeAggregator.FileSystemChangeAggregator();
394
393
  for (const relativeFilePath of removedFiles) {
395
- const metadata = fileSystem.remove(relativeFilePath);
396
- if (metadata) {
397
- removed.push([relativeFilePath, metadata]);
398
- }
394
+ fileSystem.remove(relativeFilePath, changeAggregator);
399
395
  }
400
396
  this.#startupPerfLogger?.point("applyFileDelta_remove_end");
401
397
  const readLinkPromises = [];
@@ -411,12 +407,12 @@ class FileMap extends _events.default {
411
407
  const maybeReadLink = this.#maybeReadLink(normalFilePath, fileData);
412
408
  if (maybeReadLink) {
413
409
  readLinkPromises.push(
414
- maybeReadLink.catch((error) =>
410
+ maybeReadLink.catch((error) => {
415
411
  readLinkErrors.push({
416
412
  normalFilePath,
417
413
  error,
418
- }),
419
- ),
414
+ });
415
+ }),
420
416
  );
421
417
  }
422
418
  }
@@ -441,37 +437,34 @@ class FileMap extends _events.default {
441
437
  readLinkErrors,
442
438
  )) {
443
439
  if (["ENOENT", "EACCESS"].includes(error.code)) {
444
- missingFiles.add(normalFilePath);
440
+ delta.changedFiles.delete(normalFilePath);
441
+ fileSystem.remove(normalFilePath, changeAggregator);
445
442
  } else {
446
443
  throw error;
447
444
  }
448
445
  }
449
- for (const relativeFilePath of missingFiles) {
450
- changedFiles.delete(relativeFilePath);
451
- const metadata = fileSystem.remove(relativeFilePath);
452
- if (metadata) {
453
- removed.push([relativeFilePath, metadata]);
454
- }
455
- }
456
446
  this.#startupPerfLogger?.point("applyFileDelta_missing_end");
457
447
  this.#startupPerfLogger?.point("applyFileDelta_add_start");
458
- fileSystem.bulkAddOrModify(changedFiles);
448
+ fileSystem.bulkAddOrModify(changedFiles, changeAggregator);
459
449
  this.#startupPerfLogger?.point("applyFileDelta_add_end");
460
450
  this.#startupPerfLogger?.point("applyFileDelta_updatePlugins_start");
461
- plugins.forEach(({ plugin, dataIdx }) => {
462
- const mapFn =
463
- dataIdx != null
464
- ? ([relativePath, fileData]) => [relativePath, fileData[dataIdx]]
465
- : ([relativePath, fileData]) => [relativePath, null];
466
- plugin.bulkUpdate({
467
- addedOrModified: mapIterator(changedFiles.entries(), mapFn),
468
- removed: mapIterator(removed.values(), mapFn),
469
- });
451
+ this.#plugins.forEach(({ plugin, dataIdx }) => {
452
+ plugin.onChanged(
453
+ changeAggregator.getMappedView(
454
+ dataIdx != null ? (metadata) => metadata[dataIdx] : () => null,
455
+ ),
456
+ );
470
457
  });
471
458
  this.#startupPerfLogger?.point("applyFileDelta_updatePlugins_end");
472
459
  this.#startupPerfLogger?.point("applyFileDelta_end");
460
+ return changeAggregator;
473
461
  }
474
- async #takeSnapshotAndPersist(fileSystem, clocks, plugins, changed, removed) {
462
+ async #takeSnapshotAndPersist(
463
+ fileSystem,
464
+ clocks,
465
+ plugins,
466
+ changedSinceCacheRead,
467
+ ) {
475
468
  this.#startupPerfLogger?.point("persist_start");
476
469
  await this.#cacheManager.write(
477
470
  () => ({
@@ -485,7 +478,7 @@ class FileMap extends _events.default {
485
478
  ),
486
479
  }),
487
480
  {
488
- changedSinceCacheRead: changed.size + removed.size > 0,
481
+ changedSinceCacheRead,
489
482
  eventSource: {
490
483
  onChange: (cb) => {
491
484
  this.on("change", cb);
@@ -511,14 +504,46 @@ class FileMap extends _events.default {
511
504
  }
512
505
  const hasWatchedExtension = (filePath) =>
513
506
  this.#options.extensions.some((ext) => filePath.endsWith(ext));
514
- let changeQueue = Promise.resolve();
515
507
  let nextEmit = null;
516
508
  const emitChange = () => {
517
- if (nextEmit == null || nextEmit.eventsQueue.length === 0) {
509
+ if (nextEmit == null) {
510
+ return;
511
+ }
512
+ const { events, firstEventTimestamp, firstEnqueuedTimestamp } = nextEmit;
513
+ const changeAggregator =
514
+ new _FileSystemChangeAggregator.FileSystemChangeAggregator();
515
+ for (const event of events) {
516
+ const { relativeFilePath, clock } = event;
517
+ if (event.type === "delete") {
518
+ fileSystem.remove(relativeFilePath, changeAggregator);
519
+ } else {
520
+ fileSystem.addOrModify(
521
+ relativeFilePath,
522
+ event.metadata,
523
+ changeAggregator,
524
+ );
525
+ }
526
+ this.#updateClock(clocks, clock);
527
+ }
528
+ const changeSize = changeAggregator.getSize();
529
+ if (changeSize === 0) {
530
+ nextEmit = null;
518
531
  return;
519
532
  }
520
- const { eventsQueue, firstEventTimestamp, firstEnqueuedTimestamp } =
521
- nextEmit;
533
+ const _netChange = changeAggregator.getView();
534
+ this.#plugins.forEach(({ plugin, dataIdx }) => {
535
+ plugin.onChanged(
536
+ changeAggregator.getMappedView(
537
+ dataIdx != null ? (metadata) => metadata[dataIdx] : () => null,
538
+ ),
539
+ );
540
+ });
541
+ const toPublicMetadata = (metadata) => ({
542
+ isSymlink: metadata[_constants.default.SYMLINK] !== 0,
543
+ modifiedTime: metadata[_constants.default.MTIME] ?? null,
544
+ });
545
+ const changesWithMetadata =
546
+ changeAggregator.getMappedView(toPublicMetadata);
522
547
  const hmrPerfLogger = this.#options.perfLoggerFactory?.("HMR", {
523
548
  key: this.#getNextChangeID(),
524
549
  });
@@ -532,20 +557,23 @@ class FileMap extends _events.default {
532
557
  hmrPerfLogger.point("waitingForChangeInterval_end");
533
558
  hmrPerfLogger.annotate({
534
559
  int: {
535
- eventsQueueLength: eventsQueue.length,
560
+ changeSize,
536
561
  },
537
562
  });
538
563
  hmrPerfLogger.point("fileChange_start");
539
564
  }
540
565
  const changeEvent = {
541
- eventsQueue,
566
+ changes: changesWithMetadata,
542
567
  logger: hmrPerfLogger,
568
+ rootDir: this.#options.rootDir,
543
569
  };
544
570
  this.emit("change", changeEvent);
545
571
  nextEmit = null;
546
572
  };
573
+ let changeQueue = Promise.resolve();
547
574
  const onChange = (change) => {
548
575
  if (
576
+ change.event !== "recrawl" &&
549
577
  change.metadata &&
550
578
  (change.metadata.type === "d" ||
551
579
  (change.metadata.type === "f" &&
@@ -563,62 +591,36 @@ class FileMap extends _events.default {
563
591
  }
564
592
  const relativeFilePath =
565
593
  this.#pathUtils.absoluteToNormal(absoluteFilePath);
566
- const linkStats = fileSystem.linkStats(relativeFilePath);
567
- if (
568
- change.event === "touch" &&
569
- linkStats != null &&
570
- change.metadata.modifiedTime != null &&
571
- linkStats.modifiedTime === change.metadata.modifiedTime
572
- ) {
573
- return;
574
- }
575
- const eventTypeToEmit =
576
- change.event === "touch"
577
- ? linkStats == null
578
- ? "add"
579
- : "change"
580
- : "delete";
581
594
  const onChangeStartTime =
582
595
  _perf_hooks.performance.timeOrigin + _perf_hooks.performance.now();
596
+ const enqueueEvent = (event) => {
597
+ nextEmit ??= {
598
+ events: [],
599
+ firstEnqueuedTimestamp:
600
+ _perf_hooks.performance.timeOrigin + _perf_hooks.performance.now(),
601
+ firstEventTimestamp: onChangeStartTime,
602
+ };
603
+ nextEmit.events.push(event);
604
+ };
583
605
  changeQueue = changeQueue
584
606
  .then(async () => {
585
607
  if (
586
608
  nextEmit != null &&
587
- nextEmit.eventsQueue.find(
609
+ nextEmit.events.find(
588
610
  (event) =>
589
- event.type === eventTypeToEmit &&
590
- event.filePath === absoluteFilePath &&
611
+ event.type === change.event &&
612
+ event.relativeFilePath === relativeFilePath &&
591
613
  ((!event.metadata && !change.metadata) ||
592
614
  (event.metadata &&
593
615
  change.metadata &&
594
- event.metadata.modifiedTime != null &&
616
+ event.metadata[_constants.default.MTIME] != null &&
595
617
  change.metadata.modifiedTime != null &&
596
- event.metadata.modifiedTime ===
618
+ event.metadata[_constants.default.MTIME] ===
597
619
  change.metadata.modifiedTime)),
598
620
  )
599
621
  ) {
600
622
  return null;
601
623
  }
602
- const linkStats = fileSystem.linkStats(relativeFilePath);
603
- const enqueueEvent = (metadata) => {
604
- const event = {
605
- filePath: absoluteFilePath,
606
- metadata,
607
- type: eventTypeToEmit,
608
- };
609
- if (nextEmit == null) {
610
- nextEmit = {
611
- eventsQueue: [event],
612
- firstEnqueuedTimestamp:
613
- _perf_hooks.performance.timeOrigin +
614
- _perf_hooks.performance.now(),
615
- firstEventTimestamp: onChangeStartTime,
616
- };
617
- } else {
618
- nextEmit.eventsQueue.push(event);
619
- }
620
- return null;
621
- };
622
624
  if (change.event === "touch") {
623
625
  (0, _invariant.default)(
624
626
  change.metadata.size != null,
@@ -645,40 +647,65 @@ class FileMap extends _events.default {
645
647
  },
646
648
  );
647
649
  }
648
- fileSystem.addOrModify(relativeFilePath, fileMetadata);
649
- this.#updateClock(clocks, change.clock);
650
- plugins.forEach(({ plugin, dataIdx }) =>
651
- dataIdx != null
652
- ? plugin.onNewOrModifiedFile(
653
- relativeFilePath,
654
- fileMetadata[dataIdx],
655
- )
656
- : plugin.onNewOrModifiedFile(relativeFilePath),
657
- );
658
- enqueueEvent(change.metadata);
650
+ enqueueEvent({
651
+ clock: change.clock,
652
+ relativeFilePath,
653
+ metadata: fileMetadata,
654
+ type: change.event,
655
+ });
659
656
  } catch (e) {
660
657
  if (!["ENOENT", "EACCESS"].includes(e.code)) {
661
658
  throw e;
662
659
  }
663
660
  }
664
661
  } else if (change.event === "delete") {
665
- if (linkStats == null) {
662
+ enqueueEvent({
663
+ clock: change.clock,
664
+ relativeFilePath,
665
+ type: "delete",
666
+ });
667
+ } else if (change.event === "recrawl") {
668
+ emitChange();
669
+ const absoluteDirPath = path.join(
670
+ change.root,
671
+ (0, _normalizePathSeparatorsToSystem.default)(
672
+ change.relativePath,
673
+ ),
674
+ );
675
+ const subpath = this.#pathUtils.absoluteToNormal(absoluteDirPath);
676
+ const watcher = this.#watcher;
677
+ (0, _invariant.default)(
678
+ watcher != null,
679
+ "Watcher must be initialized",
680
+ );
681
+ const crawlResult = await watcher.recrawl(subpath, fileSystem);
682
+ if (
683
+ crawlResult.changedFiles.size === 0 &&
684
+ crawlResult.removedFiles.size === 0
685
+ ) {
666
686
  return null;
667
687
  }
668
- const metadata = (0, _nullthrows.default)(
669
- fileSystem.remove(relativeFilePath),
688
+ const recrawlChangeAggregator = await this.#applyFileDelta(
689
+ fileSystem,
690
+ this.#plugins,
691
+ crawlResult,
670
692
  );
671
693
  this.#updateClock(clocks, change.clock);
672
- plugins.forEach(({ plugin, dataIdx }) =>
673
- dataIdx != null
674
- ? plugin.onRemovedFile(relativeFilePath, metadata[dataIdx])
675
- : plugin.onRemovedFile(relativeFilePath),
676
- );
677
- enqueueEvent({
678
- modifiedTime: null,
679
- size: null,
680
- type: linkStats.fileType,
694
+ if (recrawlChangeAggregator.getSize() === 0) {
695
+ return null;
696
+ }
697
+ const toPublicMetadata = (metadata) => ({
698
+ isSymlink: metadata[_constants.default.SYMLINK] !== 0,
699
+ modifiedTime: metadata[_constants.default.MTIME] ?? null,
681
700
  });
701
+ const changesWithMetadata =
702
+ recrawlChangeAggregator.getMappedView(toPublicMetadata);
703
+ const changeEvent = {
704
+ changes: changesWithMetadata,
705
+ logger: null,
706
+ rootDir: this.#options.rootDir,
707
+ };
708
+ this.emit("change", changeEvent);
682
709
  } else {
683
710
  throw new Error(
684
711
  `metro-file-map: Unrecognized event type from watcher: ${change.event}`,
@@ -778,11 +805,9 @@ class FileMap extends _events.default {
778
805
  static H = _constants.default;
779
806
  }
780
807
  exports.default = FileMap;
781
- const mapIterator = (it, fn) =>
782
- "map" in it
783
- ? it.map(fn)
784
- : (function* mapped() {
785
- for (const item of it) {
786
- yield fn(item);
787
- }
788
- })();
808
+ const mapIterable = (it, fn) =>
809
+ (function* mapped() {
810
+ for (const item of it) {
811
+ yield fn(item);
812
+ }
813
+ })();