metro-file-map 0.83.4 → 0.83.6
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 +3 -2
- package/src/Watcher.d.ts +74 -0
- package/src/Watcher.js +68 -48
- package/src/Watcher.js.flow +84 -51
- package/src/cache/DiskCacheManager.d.ts +49 -0
- package/src/cache/DiskCacheManager.js +1 -5
- package/src/constants.d.ts +22 -0
- package/src/crawlers/node/hasNativeFindSupport.d.ts +19 -0
- package/src/crawlers/node/index.d.ts +21 -0
- package/src/crawlers/node/index.js +6 -10
- package/src/crawlers/node/index.js.flow +8 -6
- package/src/crawlers/watchman/index.d.ts +23 -0
- package/src/crawlers/watchman/index.js +2 -9
- package/src/crawlers/watchman/index.js.flow +2 -6
- package/src/flow-types.d.ts +460 -0
- package/src/flow-types.js.flow +89 -29
- package/src/index.d.ts +182 -0
- package/src/index.js +148 -132
- package/src/index.js.flow +200 -155
- package/src/lib/FileProcessor.d.ts +60 -0
- package/src/lib/FileProcessor.js +1 -5
- package/src/lib/FileSystemChangeAggregator.d.ts +40 -0
- package/src/lib/FileSystemChangeAggregator.js +89 -0
- package/src/lib/FileSystemChangeAggregator.js.flow +143 -0
- package/src/lib/RootPathUtils.d.ts +30 -0
- package/src/lib/RootPathUtils.js +2 -9
- package/src/lib/TreeFS.d.ts +174 -0
- package/src/lib/TreeFS.js +68 -21
- package/src/lib/TreeFS.js.flow +89 -16
- package/src/lib/checkWatchmanCapabilities.d.ts +20 -0
- package/src/lib/normalizePathSeparatorsToPosix.d.ts +20 -0
- package/src/lib/normalizePathSeparatorsToPosix.js +1 -4
- package/src/lib/normalizePathSeparatorsToSystem.d.ts +20 -0
- package/src/lib/normalizePathSeparatorsToSystem.js +1 -4
- package/src/lib/rootRelativeCacheKeys.d.ts +24 -0
- package/src/lib/rootRelativeCacheKeys.js +1 -5
- package/src/lib/sorting.d.ts +23 -0
- package/src/plugins/DependencyPlugin.d.ts +52 -0
- package/src/plugins/DependencyPlugin.js +1 -3
- package/src/plugins/DependencyPlugin.js.flow +1 -16
- package/src/plugins/HastePlugin.d.ts +69 -0
- package/src/plugins/HastePlugin.js +12 -16
- package/src/plugins/HastePlugin.js.flow +12 -12
- package/src/plugins/MockPlugin.d.ts +48 -0
- package/src/plugins/MockPlugin.js +18 -25
- package/src/plugins/MockPlugin.js.flow +18 -22
- package/src/plugins/dependencies/dependencyExtractor.d.ts +1 -1
- package/src/plugins/haste/DuplicateHasteCandidatesError.d.ts +31 -0
- package/src/plugins/haste/DuplicateHasteCandidatesError.js +1 -5
- package/src/plugins/haste/HasteConflictsError.d.ts +23 -0
- package/src/plugins/haste/HasteConflictsError.js +1 -5
- package/src/plugins/haste/computeConflicts.d.ts +34 -0
- package/src/plugins/haste/computeConflicts.js +1 -5
- package/src/plugins/haste/getPlatformExtension.d.ts +21 -0
- package/src/plugins/mocks/getMockName.d.ts +20 -0
- package/src/plugins/mocks/getMockName.js +1 -4
- package/src/watchers/AbstractWatcher.d.ts +41 -0
- package/src/watchers/AbstractWatcher.js +2 -9
- package/src/watchers/FallbackWatcher.d.ts +28 -0
- package/src/watchers/FallbackWatcher.js +21 -12
- package/src/watchers/FallbackWatcher.js.flow +28 -5
- package/src/watchers/NativeWatcher.d.ts +55 -0
- package/src/watchers/NativeWatcher.js +28 -9
- package/src/watchers/NativeWatcher.js.flow +33 -6
- package/src/watchers/RecrawlWarning.d.ts +32 -0
- package/src/watchers/WatchmanWatcher.d.ts +34 -0
- package/src/watchers/WatchmanWatcher.js +2 -9
- package/src/watchers/common.d.ts +70 -0
- package/src/watchers/common.js +7 -6
- package/src/watchers/common.js.flow +1 -0
package/src/index.js.flow
CHANGED
|
@@ -17,12 +17,13 @@ import type {
|
|
|
17
17
|
CacheManagerFactory,
|
|
18
18
|
CacheManagerFactoryOptions,
|
|
19
19
|
CanonicalPath,
|
|
20
|
+
ChangedFileMetadata,
|
|
20
21
|
ChangeEvent,
|
|
21
22
|
ChangeEventClock,
|
|
22
23
|
ChangeEventMetadata,
|
|
23
24
|
Console,
|
|
24
25
|
CrawlerOptions,
|
|
25
|
-
|
|
26
|
+
CrawlResult,
|
|
26
27
|
FileData,
|
|
27
28
|
FileMapPlugin,
|
|
28
29
|
FileMapPluginWorker,
|
|
@@ -31,6 +32,7 @@ import type {
|
|
|
31
32
|
HasteMapData,
|
|
32
33
|
HasteMapItem,
|
|
33
34
|
HType,
|
|
35
|
+
InputFileMapPlugin,
|
|
34
36
|
MutableFileSystem,
|
|
35
37
|
Path,
|
|
36
38
|
PerfLogger,
|
|
@@ -44,6 +46,7 @@ import {DiskCacheManager} from './cache/DiskCacheManager';
|
|
|
44
46
|
import H from './constants';
|
|
45
47
|
import checkWatchmanCapabilities from './lib/checkWatchmanCapabilities';
|
|
46
48
|
import {FileProcessor} from './lib/FileProcessor';
|
|
49
|
+
import {FileSystemChangeAggregator} from './lib/FileSystemChangeAggregator';
|
|
47
50
|
import normalizePathSeparatorsToPosix from './lib/normalizePathSeparatorsToPosix';
|
|
48
51
|
import normalizePathSeparatorsToSystem from './lib/normalizePathSeparatorsToSystem';
|
|
49
52
|
import {RootPathUtils} from './lib/RootPathUtils';
|
|
@@ -52,7 +55,6 @@ import {Watcher} from './Watcher';
|
|
|
52
55
|
import EventEmitter from 'events';
|
|
53
56
|
import {promises as fsPromises} from 'fs';
|
|
54
57
|
import invariant from 'invariant';
|
|
55
|
-
import nullthrows from 'nullthrows';
|
|
56
58
|
import * as path from 'path';
|
|
57
59
|
import {performance} from 'perf_hooks';
|
|
58
60
|
|
|
@@ -69,6 +71,7 @@ export type {
|
|
|
69
71
|
FileSystem,
|
|
70
72
|
HasteMapData,
|
|
71
73
|
HasteMapItem,
|
|
74
|
+
InputFileMapPlugin,
|
|
72
75
|
};
|
|
73
76
|
|
|
74
77
|
export type InputOptions = Readonly<{
|
|
@@ -77,7 +80,7 @@ export type InputOptions = Readonly<{
|
|
|
77
80
|
extensions: ReadonlyArray<string>,
|
|
78
81
|
forceNodeFilesystemAPI?: ?boolean,
|
|
79
82
|
ignorePattern?: ?RegExp,
|
|
80
|
-
plugins?: ReadonlyArray<
|
|
83
|
+
plugins?: ReadonlyArray<InputFileMapPlugin>,
|
|
81
84
|
retainAllFiles: boolean,
|
|
82
85
|
rootDir: string,
|
|
83
86
|
roots: ReadonlyArray<string>,
|
|
@@ -111,12 +114,24 @@ type InternalOptions = Readonly<{
|
|
|
111
114
|
watchmanDeferStates: ReadonlyArray<string>,
|
|
112
115
|
}>;
|
|
113
116
|
|
|
114
|
-
// $FlowFixMe[unclear-type] Plugin types cannot be known statically
|
|
115
|
-
type AnyFileMapPlugin = FileMapPlugin<any, any>;
|
|
116
117
|
type IndexedPlugin = Readonly<{
|
|
117
|
-
|
|
118
|
+
// $FlowFixMe[unclear-type] Plugin types cannot be known statically
|
|
119
|
+
plugin: FileMapPlugin<any, any>,
|
|
118
120
|
dataIdx: ?number,
|
|
119
121
|
}>;
|
|
122
|
+
type InternalEnqueuedEvent = Readonly<
|
|
123
|
+
| {
|
|
124
|
+
clock: ?ChangeEventClock,
|
|
125
|
+
relativeFilePath: string,
|
|
126
|
+
metadata: FileMetadata,
|
|
127
|
+
type: 'touch',
|
|
128
|
+
}
|
|
129
|
+
| {
|
|
130
|
+
clock: ?ChangeEventClock,
|
|
131
|
+
relativeFilePath: string,
|
|
132
|
+
type: 'delete',
|
|
133
|
+
},
|
|
134
|
+
>;
|
|
120
135
|
|
|
121
136
|
export {DiskCacheManager} from './cache/DiskCacheManager';
|
|
122
137
|
export {default as DependencyPlugin} from './plugins/DependencyPlugin';
|
|
@@ -421,7 +436,7 @@ export default class FileMap extends EventEmitter {
|
|
|
421
436
|
};
|
|
422
437
|
},
|
|
423
438
|
fileIterator: opts =>
|
|
424
|
-
|
|
439
|
+
mapIterable(
|
|
425
440
|
fileSystem.metadataIterator(opts),
|
|
426
441
|
({baseName, canonicalPath, metadata}) => ({
|
|
427
442
|
baseName,
|
|
@@ -437,7 +452,13 @@ export default class FileMap extends EventEmitter {
|
|
|
437
452
|
]);
|
|
438
453
|
|
|
439
454
|
// Update `fileSystem` and plugins based on the file delta.
|
|
440
|
-
await this.#applyFileDelta(
|
|
455
|
+
const actualChanges = await this.#applyFileDelta(
|
|
456
|
+
fileSystem,
|
|
457
|
+
plugins,
|
|
458
|
+
fileDelta,
|
|
459
|
+
);
|
|
460
|
+
|
|
461
|
+
const changeSize = actualChanges.getSize();
|
|
441
462
|
|
|
442
463
|
// Validate plugins before persisting them.
|
|
443
464
|
plugins.forEach(({plugin}) => plugin.assertValid());
|
|
@@ -447,14 +468,9 @@ export default class FileMap extends EventEmitter {
|
|
|
447
468
|
fileSystem,
|
|
448
469
|
watchmanClocks,
|
|
449
470
|
plugins,
|
|
450
|
-
|
|
451
|
-
fileDelta.removedFiles,
|
|
452
|
-
);
|
|
453
|
-
debug(
|
|
454
|
-
'Finished mapping files (%d changes, %d removed).',
|
|
455
|
-
fileDelta.changedFiles.size,
|
|
456
|
-
fileDelta.removedFiles.size,
|
|
471
|
+
changeSize > 0,
|
|
457
472
|
);
|
|
473
|
+
debug('Finished mapping files (%d changes).', changeSize);
|
|
458
474
|
|
|
459
475
|
await this.#watch(fileSystem, watchmanClocks, plugins);
|
|
460
476
|
return {fileSystem};
|
|
@@ -492,11 +508,7 @@ export default class FileMap extends EventEmitter {
|
|
|
492
508
|
*/
|
|
493
509
|
async #buildFileDelta(
|
|
494
510
|
previousState: CrawlerOptions['previousState'],
|
|
495
|
-
): Promise<{
|
|
496
|
-
removedFiles: Set<CanonicalPath>,
|
|
497
|
-
changedFiles: FileData,
|
|
498
|
-
clocks?: WatchmanClocks,
|
|
499
|
-
}> {
|
|
511
|
+
): Promise<CrawlResult> {
|
|
500
512
|
this.#startupPerfLogger?.point('buildFileDelta_start');
|
|
501
513
|
|
|
502
514
|
const {
|
|
@@ -540,10 +552,9 @@ export default class FileMap extends EventEmitter {
|
|
|
540
552
|
|
|
541
553
|
watcher.on('status', status => this.emit('status', status));
|
|
542
554
|
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
});
|
|
555
|
+
const result = await watcher.crawl();
|
|
556
|
+
this.#startupPerfLogger?.point('buildFileDelta_end');
|
|
557
|
+
return result;
|
|
547
558
|
}
|
|
548
559
|
|
|
549
560
|
#maybeReadLink(normalPath: Path, fileMetadata: FileMetadata): ?Promise<void> {
|
|
@@ -568,25 +579,20 @@ export default class FileMap extends EventEmitter {
|
|
|
568
579
|
removedFiles: ReadonlySet<CanonicalPath>,
|
|
569
580
|
clocks?: WatchmanClocks,
|
|
570
581
|
}>,
|
|
571
|
-
): Promise<
|
|
582
|
+
): Promise<FileSystemChangeAggregator> {
|
|
572
583
|
this.#startupPerfLogger?.point('applyFileDelta_start');
|
|
573
584
|
const {changedFiles, removedFiles} = delta;
|
|
574
585
|
this.#startupPerfLogger?.point('applyFileDelta_preprocess_start');
|
|
575
|
-
const missingFiles: Set<string> = new Set();
|
|
576
|
-
|
|
577
586
|
// Remove files first so that we don't mistake moved modules
|
|
578
587
|
// modules as duplicates.
|
|
579
588
|
this.#startupPerfLogger?.point('applyFileDelta_remove_start');
|
|
580
|
-
const
|
|
589
|
+
const changeAggregator = new FileSystemChangeAggregator();
|
|
581
590
|
for (const relativeFilePath of removedFiles) {
|
|
582
|
-
|
|
583
|
-
if (metadata) {
|
|
584
|
-
removed.push([relativeFilePath, metadata]);
|
|
585
|
-
}
|
|
591
|
+
fileSystem.remove(relativeFilePath, changeAggregator);
|
|
586
592
|
}
|
|
587
593
|
this.#startupPerfLogger?.point('applyFileDelta_remove_end');
|
|
588
594
|
|
|
589
|
-
const readLinkPromises = [];
|
|
595
|
+
const readLinkPromises: Array<Promise<void>> = [];
|
|
590
596
|
const readLinkErrors: Array<{
|
|
591
597
|
normalFilePath: string,
|
|
592
598
|
error: Error & {code?: string},
|
|
@@ -606,9 +612,9 @@ export default class FileMap extends EventEmitter {
|
|
|
606
612
|
const maybeReadLink = this.#maybeReadLink(normalFilePath, fileData);
|
|
607
613
|
if (maybeReadLink) {
|
|
608
614
|
readLinkPromises.push(
|
|
609
|
-
maybeReadLink.catch(error =>
|
|
610
|
-
readLinkErrors.push({normalFilePath, error})
|
|
611
|
-
),
|
|
615
|
+
maybeReadLink.catch(error => {
|
|
616
|
+
readLinkErrors.push({normalFilePath, error});
|
|
617
|
+
}),
|
|
612
618
|
);
|
|
613
619
|
}
|
|
614
620
|
}
|
|
@@ -647,43 +653,32 @@ export default class FileMap extends EventEmitter {
|
|
|
647
653
|
/* $FlowFixMe[incompatible-type] Error exposed after improved typing of
|
|
648
654
|
* Array.{includes,indexOf,lastIndexOf} */
|
|
649
655
|
if (['ENOENT', 'EACCESS'].includes(error.code)) {
|
|
650
|
-
|
|
656
|
+
delta.changedFiles.delete(normalFilePath);
|
|
657
|
+
fileSystem.remove(normalFilePath, changeAggregator);
|
|
651
658
|
} else {
|
|
652
659
|
// Anything else is fatal.
|
|
653
660
|
throw error;
|
|
654
661
|
}
|
|
655
662
|
}
|
|
656
|
-
|
|
657
|
-
changedFiles.delete(relativeFilePath);
|
|
658
|
-
const metadata = fileSystem.remove(relativeFilePath);
|
|
659
|
-
if (metadata) {
|
|
660
|
-
removed.push([relativeFilePath, metadata]);
|
|
661
|
-
}
|
|
662
|
-
}
|
|
663
|
+
|
|
663
664
|
this.#startupPerfLogger?.point('applyFileDelta_missing_end');
|
|
664
665
|
|
|
665
666
|
this.#startupPerfLogger?.point('applyFileDelta_add_start');
|
|
666
|
-
fileSystem.bulkAddOrModify(changedFiles);
|
|
667
|
+
fileSystem.bulkAddOrModify(changedFiles, changeAggregator);
|
|
667
668
|
this.#startupPerfLogger?.point('applyFileDelta_add_end');
|
|
668
669
|
|
|
669
670
|
this.#startupPerfLogger?.point('applyFileDelta_updatePlugins_start');
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
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
|
-
}),
|
|
684
|
-
]);
|
|
671
|
+
this.#plugins.forEach(({plugin, dataIdx}) => {
|
|
672
|
+
plugin.onChanged(
|
|
673
|
+
changeAggregator.getMappedView(
|
|
674
|
+
dataIdx != null ? metadata => metadata[dataIdx] : () => null,
|
|
675
|
+
),
|
|
676
|
+
);
|
|
677
|
+
});
|
|
685
678
|
this.#startupPerfLogger?.point('applyFileDelta_updatePlugins_end');
|
|
686
679
|
this.#startupPerfLogger?.point('applyFileDelta_end');
|
|
680
|
+
|
|
681
|
+
return changeAggregator;
|
|
687
682
|
}
|
|
688
683
|
|
|
689
684
|
/**
|
|
@@ -693,8 +688,7 @@ export default class FileMap extends EventEmitter {
|
|
|
693
688
|
fileSystem: FileSystem,
|
|
694
689
|
clocks: WatchmanClocks,
|
|
695
690
|
plugins: ReadonlyArray<IndexedPlugin>,
|
|
696
|
-
|
|
697
|
-
removed: Set<CanonicalPath>,
|
|
691
|
+
changedSinceCacheRead: boolean,
|
|
698
692
|
) {
|
|
699
693
|
this.#startupPerfLogger?.point('persist_start');
|
|
700
694
|
await this.#cacheManager.write(
|
|
@@ -709,7 +703,7 @@ export default class FileMap extends EventEmitter {
|
|
|
709
703
|
),
|
|
710
704
|
}),
|
|
711
705
|
{
|
|
712
|
-
changedSinceCacheRead
|
|
706
|
+
changedSinceCacheRead,
|
|
713
707
|
eventSource: {
|
|
714
708
|
onChange: cb => {
|
|
715
709
|
// Inform the cache about changes to internal state, including:
|
|
@@ -748,20 +742,68 @@ export default class FileMap extends EventEmitter {
|
|
|
748
742
|
const hasWatchedExtension = (filePath: string) =>
|
|
749
743
|
this.#options.extensions.some(ext => filePath.endsWith(ext));
|
|
750
744
|
|
|
751
|
-
let changeQueue: Promise<null | void> = Promise.resolve();
|
|
752
745
|
let nextEmit: ?{
|
|
753
|
-
|
|
746
|
+
events: Array<InternalEnqueuedEvent>,
|
|
754
747
|
firstEventTimestamp: number,
|
|
755
748
|
firstEnqueuedTimestamp: number,
|
|
756
749
|
} = null;
|
|
757
750
|
|
|
758
751
|
const emitChange = () => {
|
|
759
|
-
if (nextEmit == null
|
|
752
|
+
if (nextEmit == null) {
|
|
760
753
|
// Nothing to emit
|
|
761
754
|
return;
|
|
762
755
|
}
|
|
763
|
-
const {
|
|
764
|
-
|
|
756
|
+
const {events, firstEventTimestamp, firstEnqueuedTimestamp} = nextEmit;
|
|
757
|
+
|
|
758
|
+
const changeAggregator = new FileSystemChangeAggregator();
|
|
759
|
+
|
|
760
|
+
// Process a sequence of events. Note that preserving ordering is
|
|
761
|
+
// important here - a file may be both removed and added in the same
|
|
762
|
+
// batch.
|
|
763
|
+
// `changeAggregator` flattens this over time into the net change from
|
|
764
|
+
// this sequence.
|
|
765
|
+
for (const event of events) {
|
|
766
|
+
const {relativeFilePath, clock} = event;
|
|
767
|
+
if (event.type === 'delete') {
|
|
768
|
+
fileSystem.remove(relativeFilePath, changeAggregator);
|
|
769
|
+
} else {
|
|
770
|
+
fileSystem.addOrModify(
|
|
771
|
+
relativeFilePath,
|
|
772
|
+
event.metadata,
|
|
773
|
+
changeAggregator,
|
|
774
|
+
);
|
|
775
|
+
}
|
|
776
|
+
this.#updateClock(clocks, clock);
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
const changeSize = changeAggregator.getSize();
|
|
780
|
+
|
|
781
|
+
if (changeSize === 0) {
|
|
782
|
+
// We had events, but they've exactly cancelled each other out, reset
|
|
783
|
+
// so that timers are correct for the next change.
|
|
784
|
+
nextEmit = null;
|
|
785
|
+
return;
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
const _netChange = changeAggregator.getView();
|
|
789
|
+
this.#plugins.forEach(({plugin, dataIdx}) => {
|
|
790
|
+
plugin.onChanged(
|
|
791
|
+
changeAggregator.getMappedView(
|
|
792
|
+
dataIdx != null ? metadata => metadata[dataIdx] : () => null,
|
|
793
|
+
),
|
|
794
|
+
);
|
|
795
|
+
});
|
|
796
|
+
|
|
797
|
+
const toPublicMetadata = (
|
|
798
|
+
metadata: Readonly<FileMetadata>,
|
|
799
|
+
): ChangedFileMetadata => ({
|
|
800
|
+
isSymlink: metadata[H.SYMLINK] !== 0,
|
|
801
|
+
modifiedTime: metadata[H.MTIME] ?? null,
|
|
802
|
+
});
|
|
803
|
+
|
|
804
|
+
const changesWithMetadata =
|
|
805
|
+
changeAggregator.getMappedView(toPublicMetadata);
|
|
806
|
+
|
|
765
807
|
const hmrPerfLogger = this.#options.perfLoggerFactory?.('HMR', {
|
|
766
808
|
key: this.#getNextChangeID(),
|
|
767
809
|
});
|
|
@@ -771,21 +813,24 @@ export default class FileMap extends EventEmitter {
|
|
|
771
813
|
timestamp: firstEnqueuedTimestamp,
|
|
772
814
|
});
|
|
773
815
|
hmrPerfLogger.point('waitingForChangeInterval_end');
|
|
774
|
-
hmrPerfLogger.annotate({
|
|
775
|
-
int: {eventsQueueLength: eventsQueue.length},
|
|
776
|
-
});
|
|
816
|
+
hmrPerfLogger.annotate({int: {changeSize}});
|
|
777
817
|
hmrPerfLogger.point('fileChange_start');
|
|
778
818
|
}
|
|
779
819
|
const changeEvent: ChangeEvent = {
|
|
780
|
-
|
|
820
|
+
changes: changesWithMetadata,
|
|
781
821
|
logger: hmrPerfLogger,
|
|
822
|
+
rootDir: this.#options.rootDir,
|
|
782
823
|
};
|
|
783
824
|
this.emit('change', changeEvent);
|
|
784
825
|
nextEmit = null;
|
|
785
826
|
};
|
|
786
827
|
|
|
828
|
+
let changeQueue: Promise<null | void> = Promise.resolve();
|
|
829
|
+
|
|
787
830
|
const onChange = (change: WatcherBackendChangeEvent) => {
|
|
831
|
+
// Recrawl events bypass normal filtering - they trigger a full subdirectory scan
|
|
788
832
|
if (
|
|
833
|
+
change.event !== 'recrawl' &&
|
|
789
834
|
change.metadata &&
|
|
790
835
|
// Ignore all directory events
|
|
791
836
|
(change.metadata.type === 'd' ||
|
|
@@ -811,73 +856,38 @@ export default class FileMap extends EventEmitter {
|
|
|
811
856
|
|
|
812
857
|
const relativeFilePath =
|
|
813
858
|
this.#pathUtils.absoluteToNormal(absoluteFilePath);
|
|
814
|
-
const linkStats = fileSystem.linkStats(relativeFilePath);
|
|
815
|
-
|
|
816
|
-
// The file has been accessed, not modified. If the modified time is
|
|
817
|
-
// null, then it is assumed that the watcher does not have capabilities
|
|
818
|
-
// to detect modified time, and change processing proceeds.
|
|
819
|
-
if (
|
|
820
|
-
change.event === 'touch' &&
|
|
821
|
-
linkStats != null &&
|
|
822
|
-
change.metadata.modifiedTime != null &&
|
|
823
|
-
linkStats.modifiedTime === change.metadata.modifiedTime
|
|
824
|
-
) {
|
|
825
|
-
return;
|
|
826
|
-
}
|
|
827
|
-
|
|
828
|
-
// Emitted events, unlike memoryless backend events, specify 'add' or
|
|
829
|
-
// 'change' instead of 'touch'.
|
|
830
|
-
const eventTypeToEmit =
|
|
831
|
-
change.event === 'touch'
|
|
832
|
-
? linkStats == null
|
|
833
|
-
? 'add'
|
|
834
|
-
: 'change'
|
|
835
|
-
: 'delete';
|
|
836
859
|
|
|
837
860
|
const onChangeStartTime = performance.timeOrigin + performance.now();
|
|
838
861
|
|
|
862
|
+
const enqueueEvent = (event: InternalEnqueuedEvent) => {
|
|
863
|
+
nextEmit ??= {
|
|
864
|
+
events: [],
|
|
865
|
+
firstEnqueuedTimestamp: performance.timeOrigin + performance.now(),
|
|
866
|
+
firstEventTimestamp: onChangeStartTime,
|
|
867
|
+
};
|
|
868
|
+
nextEmit.events.push(event);
|
|
869
|
+
};
|
|
870
|
+
|
|
839
871
|
changeQueue = changeQueue
|
|
840
872
|
.then(async () => {
|
|
841
873
|
// If we get duplicate events for the same file, ignore them.
|
|
842
874
|
if (
|
|
843
875
|
nextEmit != null &&
|
|
844
|
-
nextEmit.
|
|
876
|
+
nextEmit.events.find(
|
|
845
877
|
event =>
|
|
846
|
-
event.type ===
|
|
847
|
-
event.
|
|
878
|
+
event.type === change.event &&
|
|
879
|
+
event.relativeFilePath === relativeFilePath &&
|
|
848
880
|
((!event.metadata && !change.metadata) ||
|
|
849
881
|
(event.metadata &&
|
|
850
882
|
change.metadata &&
|
|
851
|
-
event.metadata.
|
|
883
|
+
event.metadata[H.MTIME] != null &&
|
|
852
884
|
change.metadata.modifiedTime != null &&
|
|
853
|
-
event.metadata.
|
|
854
|
-
change.metadata.modifiedTime)),
|
|
885
|
+
event.metadata[H.MTIME] === change.metadata.modifiedTime)),
|
|
855
886
|
)
|
|
856
887
|
) {
|
|
857
888
|
return null;
|
|
858
889
|
}
|
|
859
890
|
|
|
860
|
-
const linkStats = fileSystem.linkStats(relativeFilePath);
|
|
861
|
-
|
|
862
|
-
const enqueueEvent = (metadata: ChangeEventMetadata) => {
|
|
863
|
-
const event = {
|
|
864
|
-
filePath: absoluteFilePath,
|
|
865
|
-
metadata,
|
|
866
|
-
type: eventTypeToEmit,
|
|
867
|
-
};
|
|
868
|
-
if (nextEmit == null) {
|
|
869
|
-
nextEmit = {
|
|
870
|
-
eventsQueue: [event],
|
|
871
|
-
firstEnqueuedTimestamp:
|
|
872
|
-
performance.timeOrigin + performance.now(),
|
|
873
|
-
firstEventTimestamp: onChangeStartTime,
|
|
874
|
-
};
|
|
875
|
-
} else {
|
|
876
|
-
nextEmit.eventsQueue.push(event);
|
|
877
|
-
}
|
|
878
|
-
return null;
|
|
879
|
-
};
|
|
880
|
-
|
|
881
891
|
// If the file was added or modified,
|
|
882
892
|
// parse it and update the file map.
|
|
883
893
|
if (change.event === 'touch') {
|
|
@@ -907,17 +917,12 @@ export default class FileMap extends EventEmitter {
|
|
|
907
917
|
},
|
|
908
918
|
);
|
|
909
919
|
}
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
fileMetadata[dataIdx],
|
|
917
|
-
)
|
|
918
|
-
: plugin.onNewOrModifiedFile(relativeFilePath),
|
|
919
|
-
);
|
|
920
|
-
enqueueEvent(change.metadata);
|
|
920
|
+
enqueueEvent({
|
|
921
|
+
clock: change.clock,
|
|
922
|
+
relativeFilePath,
|
|
923
|
+
metadata: fileMetadata,
|
|
924
|
+
type: change.event,
|
|
925
|
+
});
|
|
921
926
|
} catch (e) {
|
|
922
927
|
if (!['ENOENT', 'EACCESS'].includes(e.code)) {
|
|
923
928
|
throw e;
|
|
@@ -930,26 +935,68 @@ export default class FileMap extends EventEmitter {
|
|
|
930
935
|
// point.
|
|
931
936
|
}
|
|
932
937
|
} else if (change.event === 'delete') {
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
938
|
+
enqueueEvent({
|
|
939
|
+
clock: change.clock,
|
|
940
|
+
relativeFilePath,
|
|
941
|
+
type: 'delete',
|
|
942
|
+
});
|
|
943
|
+
} else if (change.event === 'recrawl') {
|
|
944
|
+
// Recrawl event: flush pending changes and re-crawl the directory
|
|
945
|
+
emitChange();
|
|
946
|
+
|
|
947
|
+
// The relativePath is relative to the watcher root (change.root),
|
|
948
|
+
// but we need a path relative to rootDir for the recrawl.
|
|
949
|
+
const absoluteDirPath = path.join(
|
|
950
|
+
change.root,
|
|
951
|
+
normalizePathSeparatorsToSystem(change.relativePath),
|
|
952
|
+
);
|
|
953
|
+
const subpath = this.#pathUtils.absoluteToNormal(absoluteDirPath);
|
|
954
|
+
|
|
955
|
+
// Crawl the specific subdirectory
|
|
956
|
+
const watcher = this.#watcher;
|
|
957
|
+
invariant(watcher != null, 'Watcher must be initialized');
|
|
958
|
+
const crawlResult = await watcher.recrawl(subpath, fileSystem);
|
|
959
|
+
|
|
960
|
+
// Skip if no changes
|
|
961
|
+
if (
|
|
962
|
+
crawlResult.changedFiles.size === 0 &&
|
|
963
|
+
crawlResult.removedFiles.size === 0
|
|
964
|
+
) {
|
|
936
965
|
return null;
|
|
937
966
|
}
|
|
938
|
-
|
|
939
|
-
//
|
|
940
|
-
const
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
? plugin.onRemovedFile(relativeFilePath, metadata[dataIdx])
|
|
945
|
-
: plugin.onRemovedFile(relativeFilePath),
|
|
967
|
+
|
|
968
|
+
// Reuse the same batch processing logic as build()
|
|
969
|
+
const recrawlChangeAggregator = await this.#applyFileDelta(
|
|
970
|
+
fileSystem,
|
|
971
|
+
this.#plugins,
|
|
972
|
+
crawlResult,
|
|
946
973
|
);
|
|
947
974
|
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
975
|
+
// Update clock if provided
|
|
976
|
+
this.#updateClock(clocks, change.clock);
|
|
977
|
+
|
|
978
|
+
// Skip emit if no changes after processing
|
|
979
|
+
if (recrawlChangeAggregator.getSize() === 0) {
|
|
980
|
+
return null;
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
// Emit changes directly
|
|
984
|
+
const toPublicMetadata = (
|
|
985
|
+
metadata: Readonly<FileMetadata>,
|
|
986
|
+
): ChangedFileMetadata => ({
|
|
987
|
+
isSymlink: metadata[H.SYMLINK] !== 0,
|
|
988
|
+
modifiedTime: metadata[H.MTIME] ?? null,
|
|
952
989
|
});
|
|
990
|
+
|
|
991
|
+
const changesWithMetadata =
|
|
992
|
+
recrawlChangeAggregator.getMappedView(toPublicMetadata);
|
|
993
|
+
|
|
994
|
+
const changeEvent: ChangeEvent = {
|
|
995
|
+
changes: changesWithMetadata,
|
|
996
|
+
logger: null,
|
|
997
|
+
rootDir: this.#options.rootDir,
|
|
998
|
+
};
|
|
999
|
+
this.emit('change', changeEvent);
|
|
953
1000
|
} else {
|
|
954
1001
|
throw new Error(
|
|
955
1002
|
`metro-file-map: Unrecognized event type from watcher: ${change.event}`,
|
|
@@ -1060,11 +1107,9 @@ export default class FileMap extends EventEmitter {
|
|
|
1060
1107
|
}
|
|
1061
1108
|
|
|
1062
1109
|
// TODO: Replace with it.map() from Node 22+
|
|
1063
|
-
const
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
}
|
|
1070
|
-
})();
|
|
1110
|
+
const mapIterable: <T, S>(Iterable<T>, (T) => S) => Iterator<S> = (it, fn) =>
|
|
1111
|
+
(function* mapped() {
|
|
1112
|
+
for (const item of it) {
|
|
1113
|
+
yield fn(item);
|
|
1114
|
+
}
|
|
1115
|
+
})();
|
|
@@ -0,0 +1,60 @@
|
|
|
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
|
+
* @noformat
|
|
8
|
+
* @oncall react_native
|
|
9
|
+
* @generated SignedSource<<2ea213f753eef5de14cb8a27f68b9fa2>>
|
|
10
|
+
*
|
|
11
|
+
* This file was translated from Flow by scripts/generateTypeScriptDefinitions.js
|
|
12
|
+
* Original file: packages/metro-file-map/src/lib/FileProcessor.js
|
|
13
|
+
* To regenerate, run:
|
|
14
|
+
* js1 build metro-ts-defs (internal) OR
|
|
15
|
+
* yarn run build-ts-defs (OSS)
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import type {
|
|
19
|
+
FileMapPluginWorker,
|
|
20
|
+
FileMetadata,
|
|
21
|
+
PerfLogger,
|
|
22
|
+
} from '../flow-types';
|
|
23
|
+
|
|
24
|
+
type ProcessFileRequest = Readonly<{
|
|
25
|
+
/**
|
|
26
|
+
* Populate metadata[H.SHA1] with the SHA1 of the file's contents.
|
|
27
|
+
*/
|
|
28
|
+
computeSha1: boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Only if processing has already required reading the file's contents, return
|
|
31
|
+
* the contents as a Buffer - null otherwise. Not supported for batches.
|
|
32
|
+
*/
|
|
33
|
+
maybeReturnContent: boolean;
|
|
34
|
+
}>;
|
|
35
|
+
interface MaybeCodedError extends Error {
|
|
36
|
+
code?: string;
|
|
37
|
+
}
|
|
38
|
+
export declare class FileProcessor {
|
|
39
|
+
constructor(
|
|
40
|
+
opts: Readonly<{
|
|
41
|
+
maxFilesPerWorker?: null | undefined | number;
|
|
42
|
+
maxWorkers: number;
|
|
43
|
+
pluginWorkers: null | undefined | ReadonlyArray<FileMapPluginWorker>;
|
|
44
|
+
perfLogger: null | undefined | PerfLogger;
|
|
45
|
+
rootDir: string;
|
|
46
|
+
}>,
|
|
47
|
+
);
|
|
48
|
+
processBatch(
|
|
49
|
+
files: ReadonlyArray<[string, FileMetadata]>,
|
|
50
|
+
req: ProcessFileRequest,
|
|
51
|
+
): Promise<{
|
|
52
|
+
errors: Array<{normalFilePath: string; error: MaybeCodedError}>;
|
|
53
|
+
}>;
|
|
54
|
+
processRegularFile(
|
|
55
|
+
normalPath: string,
|
|
56
|
+
fileMetadata: FileMetadata,
|
|
57
|
+
req: ProcessFileRequest,
|
|
58
|
+
): null | undefined | {content: null | undefined | Buffer};
|
|
59
|
+
end(): Promise<void>;
|
|
60
|
+
}
|
package/src/lib/FileProcessor.js
CHANGED
|
@@ -10,11 +10,7 @@ var _RootPathUtils = require("./RootPathUtils");
|
|
|
10
10
|
var _jestWorker = require("jest-worker");
|
|
11
11
|
var _path = require("path");
|
|
12
12
|
function _interopRequireDefault(e) {
|
|
13
|
-
return e && e.__esModule
|
|
14
|
-
? e
|
|
15
|
-
: {
|
|
16
|
-
default: e,
|
|
17
|
-
};
|
|
13
|
+
return e && e.__esModule ? e : { default: e };
|
|
18
14
|
}
|
|
19
15
|
const debug = require("debug")("Metro:FileMap");
|
|
20
16
|
const NODE_MODULES_SEP = "node_modules" + _path.sep;
|