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
|
@@ -0,0 +1,40 @@
|
|
|
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<<5feda1b197530a9a5fdbc57200633ac5>>
|
|
10
|
+
*
|
|
11
|
+
* This file was translated from Flow by scripts/generateTypeScriptDefinitions.js
|
|
12
|
+
* Original file: packages/metro-file-map/src/lib/FileSystemChangeAggregator.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
|
+
CanonicalPath,
|
|
20
|
+
FileMetadata,
|
|
21
|
+
FileSystemListener,
|
|
22
|
+
ReadonlyFileSystemChanges,
|
|
23
|
+
} from '../flow-types';
|
|
24
|
+
|
|
25
|
+
export declare class FileSystemChangeAggregator implements FileSystemListener {
|
|
26
|
+
directoryAdded(canonicalPath: CanonicalPath): void;
|
|
27
|
+
directoryRemoved(canonicalPath: CanonicalPath): void;
|
|
28
|
+
fileAdded(canonicalPath: CanonicalPath, data: FileMetadata): void;
|
|
29
|
+
fileModified(
|
|
30
|
+
canonicalPath: CanonicalPath,
|
|
31
|
+
oldData: FileMetadata,
|
|
32
|
+
newData: FileMetadata,
|
|
33
|
+
): void;
|
|
34
|
+
fileRemoved(canonicalPath: CanonicalPath, data: FileMetadata): void;
|
|
35
|
+
getSize(): number;
|
|
36
|
+
getView(): ReadonlyFileSystemChanges<FileMetadata>;
|
|
37
|
+
getMappedView<T>(
|
|
38
|
+
metadataMapFn: (metadata: FileMetadata) => T,
|
|
39
|
+
): ReadonlyFileSystemChanges<T>;
|
|
40
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true,
|
|
5
|
+
});
|
|
6
|
+
exports.FileSystemChangeAggregator = void 0;
|
|
7
|
+
class FileSystemChangeAggregator {
|
|
8
|
+
#addedDirectories = new Set();
|
|
9
|
+
#removedDirectories = new Set();
|
|
10
|
+
#addedFiles = new Map();
|
|
11
|
+
#modifiedFiles = new Map();
|
|
12
|
+
#removedFiles = new Map();
|
|
13
|
+
#initialMetadata = new Map();
|
|
14
|
+
directoryAdded(canonicalPath) {
|
|
15
|
+
if (!this.#removedDirectories.delete(canonicalPath)) {
|
|
16
|
+
this.#addedDirectories.add(canonicalPath);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
directoryRemoved(canonicalPath) {
|
|
20
|
+
if (!this.#addedDirectories.delete(canonicalPath)) {
|
|
21
|
+
this.#removedDirectories.add(canonicalPath);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
fileAdded(canonicalPath, data) {
|
|
25
|
+
if (this.#removedFiles.delete(canonicalPath)) {
|
|
26
|
+
this.#modifiedFiles.set(canonicalPath, data);
|
|
27
|
+
} else {
|
|
28
|
+
this.#addedFiles.set(canonicalPath, data);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
fileModified(canonicalPath, oldData, newData) {
|
|
32
|
+
if (this.#addedFiles.has(canonicalPath)) {
|
|
33
|
+
this.#addedFiles.set(canonicalPath, newData);
|
|
34
|
+
} else {
|
|
35
|
+
if (!this.#initialMetadata.has(canonicalPath)) {
|
|
36
|
+
this.#initialMetadata.set(canonicalPath, oldData);
|
|
37
|
+
}
|
|
38
|
+
this.#modifiedFiles.set(canonicalPath, newData);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
fileRemoved(canonicalPath, data) {
|
|
42
|
+
if (!this.#addedFiles.delete(canonicalPath)) {
|
|
43
|
+
let initialData = this.#initialMetadata.get(canonicalPath);
|
|
44
|
+
if (!initialData) {
|
|
45
|
+
initialData = data;
|
|
46
|
+
this.#initialMetadata.set(canonicalPath, initialData);
|
|
47
|
+
}
|
|
48
|
+
this.#modifiedFiles.delete(canonicalPath);
|
|
49
|
+
this.#removedFiles.set(canonicalPath, initialData);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
getSize() {
|
|
53
|
+
return (
|
|
54
|
+
this.#addedDirectories.size +
|
|
55
|
+
this.#removedDirectories.size +
|
|
56
|
+
this.#addedFiles.size +
|
|
57
|
+
this.#modifiedFiles.size +
|
|
58
|
+
this.#removedFiles.size
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
getView() {
|
|
62
|
+
return {
|
|
63
|
+
addedDirectories: this.#addedDirectories,
|
|
64
|
+
removedDirectories: this.#removedDirectories,
|
|
65
|
+
addedFiles: this.#addedFiles,
|
|
66
|
+
modifiedFiles: this.#modifiedFiles,
|
|
67
|
+
removedFiles: this.#removedFiles,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
getMappedView(metadataMapFn) {
|
|
71
|
+
return {
|
|
72
|
+
addedDirectories: this.#addedDirectories,
|
|
73
|
+
removedDirectories: this.#removedDirectories,
|
|
74
|
+
addedFiles: mapIterable(this.#addedFiles, metadataMapFn),
|
|
75
|
+
modifiedFiles: mapIterable(this.#modifiedFiles, metadataMapFn),
|
|
76
|
+
removedFiles: mapIterable(this.#removedFiles, metadataMapFn),
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
exports.FileSystemChangeAggregator = FileSystemChangeAggregator;
|
|
81
|
+
function mapIterable(map, metadataMapFn) {
|
|
82
|
+
return {
|
|
83
|
+
*[Symbol.iterator]() {
|
|
84
|
+
for (const [path, metadata] of map) {
|
|
85
|
+
yield [path, metadataMapFn(metadata)];
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
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
|
+
* @flow strict-local
|
|
8
|
+
* @format
|
|
9
|
+
* @oncall react_native
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import type {
|
|
13
|
+
CanonicalPath,
|
|
14
|
+
FileMetadata,
|
|
15
|
+
FileSystemListener,
|
|
16
|
+
ReadonlyFileSystemChanges,
|
|
17
|
+
} from '../flow-types';
|
|
18
|
+
|
|
19
|
+
export class FileSystemChangeAggregator implements FileSystemListener {
|
|
20
|
+
// Mutually exclusive with removedDirectories
|
|
21
|
+
+#addedDirectories: Set<CanonicalPath> = new Set();
|
|
22
|
+
// Mutually exclusive with addedDirectories
|
|
23
|
+
+#removedDirectories: Set<CanonicalPath> = new Set();
|
|
24
|
+
|
|
25
|
+
// Mutually exclusive with modified and removed files
|
|
26
|
+
+#addedFiles: Map<CanonicalPath, FileMetadata> = new Map();
|
|
27
|
+
// Mutually exclusive with added and removed files
|
|
28
|
+
+#modifiedFiles: Map<CanonicalPath, FileMetadata> = new Map();
|
|
29
|
+
// Mutually exclusive with added and modified files
|
|
30
|
+
+#removedFiles: Map<CanonicalPath, FileMetadata> = new Map();
|
|
31
|
+
|
|
32
|
+
// Removed files must be paired with the file's metadata the last time it was
|
|
33
|
+
// observable by consumers - ie, immediately *before* this batch. To report
|
|
34
|
+
// this accurately with minimal overhead, we'll note the current metadata of
|
|
35
|
+
// a file the first time it is modified or removed within a batch. If it is
|
|
36
|
+
// re-added, modified and removed again, we still have the initial metadata.
|
|
37
|
+
// This is particularly important if, say, a regular file is replaced by a
|
|
38
|
+
// symlink, or vice-versa.
|
|
39
|
+
+#initialMetadata: Map<CanonicalPath, FileMetadata> = new Map();
|
|
40
|
+
|
|
41
|
+
directoryAdded(canonicalPath: CanonicalPath): void {
|
|
42
|
+
// Only add to newDirectories if this directory wasn't previously removed
|
|
43
|
+
// (i.e., it's truly new). If it was removed and re-added, the net effect
|
|
44
|
+
// is no directory change.
|
|
45
|
+
if (!this.#removedDirectories.delete(canonicalPath)) {
|
|
46
|
+
this.#addedDirectories.add(canonicalPath);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
directoryRemoved(canonicalPath: CanonicalPath): void {
|
|
51
|
+
if (!this.#addedDirectories.delete(canonicalPath)) {
|
|
52
|
+
this.#removedDirectories.add(canonicalPath);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
fileAdded(canonicalPath: CanonicalPath, data: FileMetadata): void {
|
|
57
|
+
if (this.#removedFiles.delete(canonicalPath)) {
|
|
58
|
+
// File was removed then re-added in the same batch - treat as modification
|
|
59
|
+
this.#modifiedFiles.set(canonicalPath, data);
|
|
60
|
+
} else {
|
|
61
|
+
// New file
|
|
62
|
+
this.#addedFiles.set(canonicalPath, data);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
fileModified(
|
|
67
|
+
canonicalPath: CanonicalPath,
|
|
68
|
+
oldData: FileMetadata,
|
|
69
|
+
newData: FileMetadata,
|
|
70
|
+
): void {
|
|
71
|
+
if (this.#addedFiles.has(canonicalPath)) {
|
|
72
|
+
// File did not exist before this batch. Further modification only
|
|
73
|
+
// updates metadata
|
|
74
|
+
this.#addedFiles.set(canonicalPath, newData);
|
|
75
|
+
} else {
|
|
76
|
+
if (!this.#initialMetadata.has(canonicalPath)) {
|
|
77
|
+
this.#initialMetadata.set(canonicalPath, oldData);
|
|
78
|
+
}
|
|
79
|
+
this.#modifiedFiles.set(canonicalPath, newData);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
fileRemoved(canonicalPath: CanonicalPath, data: FileMetadata): void {
|
|
84
|
+
// Check if this file was added in the same batch
|
|
85
|
+
if (!this.#addedFiles.delete(canonicalPath)) {
|
|
86
|
+
let initialData = this.#initialMetadata.get(canonicalPath);
|
|
87
|
+
if (!initialData) {
|
|
88
|
+
initialData = data;
|
|
89
|
+
this.#initialMetadata.set(canonicalPath, initialData);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// File was not added in this batch, so add to removed with last metadata
|
|
93
|
+
this.#modifiedFiles.delete(canonicalPath);
|
|
94
|
+
this.#removedFiles.set(canonicalPath, initialData);
|
|
95
|
+
}
|
|
96
|
+
// else: File was added then removed in the same batch - no net change
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
getSize(): number {
|
|
100
|
+
return (
|
|
101
|
+
this.#addedDirectories.size +
|
|
102
|
+
this.#removedDirectories.size +
|
|
103
|
+
this.#addedFiles.size +
|
|
104
|
+
this.#modifiedFiles.size +
|
|
105
|
+
this.#removedFiles.size
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
getView(): ReadonlyFileSystemChanges<FileMetadata> {
|
|
110
|
+
return {
|
|
111
|
+
addedDirectories: this.#addedDirectories,
|
|
112
|
+
removedDirectories: this.#removedDirectories,
|
|
113
|
+
addedFiles: this.#addedFiles,
|
|
114
|
+
modifiedFiles: this.#modifiedFiles,
|
|
115
|
+
removedFiles: this.#removedFiles,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
getMappedView<T>(
|
|
120
|
+
metadataMapFn: (metadata: FileMetadata) => T,
|
|
121
|
+
): ReadonlyFileSystemChanges<T> {
|
|
122
|
+
return {
|
|
123
|
+
addedDirectories: this.#addedDirectories,
|
|
124
|
+
removedDirectories: this.#removedDirectories,
|
|
125
|
+
addedFiles: mapIterable(this.#addedFiles, metadataMapFn),
|
|
126
|
+
modifiedFiles: mapIterable(this.#modifiedFiles, metadataMapFn),
|
|
127
|
+
removedFiles: mapIterable(this.#removedFiles, metadataMapFn),
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function mapIterable<T>(
|
|
133
|
+
map: Map<CanonicalPath, FileMetadata>,
|
|
134
|
+
metadataMapFn: (metadata: FileMetadata) => T,
|
|
135
|
+
): Iterable<Readonly<[CanonicalPath, T]>> {
|
|
136
|
+
return {
|
|
137
|
+
*[Symbol.iterator](): Iterator<Readonly<[CanonicalPath, T]>> {
|
|
138
|
+
for (const [path, metadata] of map) {
|
|
139
|
+
yield [path, metadataMapFn(metadata)];
|
|
140
|
+
}
|
|
141
|
+
},
|
|
142
|
+
};
|
|
143
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
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
|
+
* @generated SignedSource<<5ecdb559fce5f5c6ed50df6e4eaebf20>>
|
|
9
|
+
*
|
|
10
|
+
* This file was translated from Flow by scripts/generateTypeScriptDefinitions.js
|
|
11
|
+
* Original file: packages/metro-file-map/src/lib/RootPathUtils.js
|
|
12
|
+
* To regenerate, run:
|
|
13
|
+
* js1 build metro-ts-defs (internal) OR
|
|
14
|
+
* yarn run build-ts-defs (OSS)
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
export declare class RootPathUtils {
|
|
18
|
+
constructor(rootDir: string);
|
|
19
|
+
getBasenameOfNthAncestor(n: number): string;
|
|
20
|
+
getParts(): ReadonlyArray<string>;
|
|
21
|
+
absoluteToNormal(absolutePath: string): string;
|
|
22
|
+
normalToAbsolute(normalPath: string): string;
|
|
23
|
+
relativeToNormal(relativePath: string): string;
|
|
24
|
+
getAncestorOfRootIdx(normalPath: string): null | undefined | number;
|
|
25
|
+
joinNormalToRelative(
|
|
26
|
+
normalPath: string,
|
|
27
|
+
relativePath: string,
|
|
28
|
+
): {normalPath: string; collapsedSegments: number};
|
|
29
|
+
relative(from: string, to: string): string;
|
|
30
|
+
}
|
package/src/lib/RootPathUtils.js
CHANGED
|
@@ -14,10 +14,7 @@ function _interopRequireWildcard(e, t) {
|
|
|
14
14
|
if (!t && e && e.__esModule) return e;
|
|
15
15
|
var o,
|
|
16
16
|
i,
|
|
17
|
-
f = {
|
|
18
|
-
__proto__: null,
|
|
19
|
-
default: e,
|
|
20
|
-
};
|
|
17
|
+
f = { __proto__: null, default: e };
|
|
21
18
|
if (null === e || ("object" != typeof e && "function" != typeof e))
|
|
22
19
|
return f;
|
|
23
20
|
if ((o = t ? n : r)) {
|
|
@@ -37,11 +34,7 @@ function _interopRequireWildcard(e, t) {
|
|
|
37
34
|
})(e, t);
|
|
38
35
|
}
|
|
39
36
|
function _interopRequireDefault(e) {
|
|
40
|
-
return e && e.__esModule
|
|
41
|
-
? e
|
|
42
|
-
: {
|
|
43
|
-
default: e,
|
|
44
|
-
};
|
|
37
|
+
return e && e.__esModule ? e : { default: e };
|
|
45
38
|
}
|
|
46
39
|
const UP_FRAGMENT_SEP = ".." + path.sep;
|
|
47
40
|
const SEP_UP_FRAGMENT = path.sep + "..";
|
|
@@ -0,0 +1,174 @@
|
|
|
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
|
+
* @generated SignedSource<<65a3c4140d459a56b8c949e52b32ea1b>>
|
|
9
|
+
*
|
|
10
|
+
* This file was translated from Flow by scripts/generateTypeScriptDefinitions.js
|
|
11
|
+
* Original file: packages/metro-file-map/src/lib/TreeFS.js
|
|
12
|
+
* To regenerate, run:
|
|
13
|
+
* js1 build metro-ts-defs (internal) OR
|
|
14
|
+
* yarn run build-ts-defs (OSS)
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import type {
|
|
18
|
+
CacheData,
|
|
19
|
+
FileData,
|
|
20
|
+
FileMetadata,
|
|
21
|
+
FileStats,
|
|
22
|
+
FileSystemListener,
|
|
23
|
+
LookupResult,
|
|
24
|
+
MutableFileSystem,
|
|
25
|
+
Path,
|
|
26
|
+
ProcessFileFunction,
|
|
27
|
+
} from '../flow-types';
|
|
28
|
+
|
|
29
|
+
type DirectoryNode = Map<string, MixedNode>;
|
|
30
|
+
type FileNode = FileMetadata;
|
|
31
|
+
type MixedNode = FileNode | DirectoryNode;
|
|
32
|
+
type DeserializedSnapshotInput = {
|
|
33
|
+
rootDir: string;
|
|
34
|
+
fileSystemData: DirectoryNode;
|
|
35
|
+
processFile: ProcessFileFunction;
|
|
36
|
+
};
|
|
37
|
+
type TreeFSOptions = {
|
|
38
|
+
rootDir: Path;
|
|
39
|
+
files?: FileData;
|
|
40
|
+
processFile: ProcessFileFunction;
|
|
41
|
+
};
|
|
42
|
+
type MatchFilesOptions = Readonly<{
|
|
43
|
+
filter?: null | undefined | RegExp;
|
|
44
|
+
filterCompareAbsolute?: boolean;
|
|
45
|
+
filterComparePosix?: boolean;
|
|
46
|
+
follow?: boolean;
|
|
47
|
+
recursive?: boolean;
|
|
48
|
+
rootDir?: null | undefined | Path;
|
|
49
|
+
}>;
|
|
50
|
+
type MetadataIteratorOptions = Readonly<{
|
|
51
|
+
includeSymlinks: boolean;
|
|
52
|
+
includeNodeModules: boolean;
|
|
53
|
+
}>;
|
|
54
|
+
/**
|
|
55
|
+
* OVERVIEW:
|
|
56
|
+
*
|
|
57
|
+
* TreeFS is Metro's in-memory representation of the file system. It is
|
|
58
|
+
* structured as a tree of non-empty maps and leaves (tuples), with the root
|
|
59
|
+
* node representing the given `rootDir`, typically Metro's _project root_
|
|
60
|
+
* (not a filesystem root). Map keys are path segments, and branches outside
|
|
61
|
+
* the project root are accessed via `'..'`.
|
|
62
|
+
*
|
|
63
|
+
* EXAMPLE:
|
|
64
|
+
*
|
|
65
|
+
* For a root dir '/data/project', the file '/data/other/app/index.js' would
|
|
66
|
+
* have metadata at #rootNode.get('..').get('other').get('app').get('index.js')
|
|
67
|
+
*
|
|
68
|
+
* SERIALISATION:
|
|
69
|
+
*
|
|
70
|
+
* #rootNode is designed to be directly serialisable and directly portable (for
|
|
71
|
+
* a given project) between different root directories and operating systems.
|
|
72
|
+
*
|
|
73
|
+
* SYMLINKS:
|
|
74
|
+
*
|
|
75
|
+
* Symlinks are represented as nodes whose metadata contains their literal
|
|
76
|
+
* target. Literal targets are resolved to normal paths at runtime, and cached.
|
|
77
|
+
* If a symlink is encountered during traversal, we restart traversal at the
|
|
78
|
+
* root node targeting join(normal symlink target, remaining path suffix).
|
|
79
|
+
*
|
|
80
|
+
* NODE TYPES:
|
|
81
|
+
*
|
|
82
|
+
* - A directory (including a parent directory at '..') is represented by a
|
|
83
|
+
* `Map` of basenames to any other node type.
|
|
84
|
+
* - A file is represented by an `Array` (tuple) of metadata, of which:
|
|
85
|
+
* - A regular file has node[H.SYMLINK] === 0
|
|
86
|
+
* - A symlink has node[H.SYMLINK] === 1 or
|
|
87
|
+
* typeof node[H.SYMLINK] === 'string', where a string is the literal
|
|
88
|
+
* content of the symlink (i.e. from readlink), if known.
|
|
89
|
+
*
|
|
90
|
+
* TERMINOLOGY:
|
|
91
|
+
*
|
|
92
|
+
* - mixedPath
|
|
93
|
+
* A root-relative or absolute path
|
|
94
|
+
* - relativePath
|
|
95
|
+
* A root-relative path
|
|
96
|
+
* - normalPath
|
|
97
|
+
* A root-relative, normalised path (no extraneous '.' or '..'), may have a
|
|
98
|
+
* single trailing slash
|
|
99
|
+
* - canonicalPath
|
|
100
|
+
* A root-relative, normalised, real path (no symlinks in dirname), never has
|
|
101
|
+
* a trailing slash
|
|
102
|
+
*/
|
|
103
|
+
declare class TreeFS implements MutableFileSystem {
|
|
104
|
+
constructor(opts: TreeFSOptions);
|
|
105
|
+
getSerializableSnapshot(): CacheData['fileSystemData'];
|
|
106
|
+
static fromDeserializedSnapshot(args: DeserializedSnapshotInput): TreeFS;
|
|
107
|
+
getSize(mixedPath: Path): null | undefined | number;
|
|
108
|
+
getDifference(
|
|
109
|
+
files: FileData,
|
|
110
|
+
options?: Readonly<{subpath?: string}>,
|
|
111
|
+
): {changedFiles: FileData; removedFiles: Set<string>};
|
|
112
|
+
getSha1(mixedPath: Path): null | undefined | string;
|
|
113
|
+
getOrComputeSha1(
|
|
114
|
+
mixedPath: Path,
|
|
115
|
+
): Promise<null | undefined | {sha1: string; content?: Buffer}>;
|
|
116
|
+
exists(mixedPath: Path): boolean;
|
|
117
|
+
lookup(mixedPath: Path): LookupResult;
|
|
118
|
+
getAllFiles(): Array<Path>;
|
|
119
|
+
linkStats(mixedPath: Path): null | undefined | FileStats;
|
|
120
|
+
/**
|
|
121
|
+
* Given a search context, return a list of file paths matching the query.
|
|
122
|
+
* The query matches against normalized paths which start with `./`,
|
|
123
|
+
* for example: `a/b.js` -> `./a/b.js`
|
|
124
|
+
*/
|
|
125
|
+
matchFiles(opts: MatchFilesOptions): Iterable<Path>;
|
|
126
|
+
addOrModify(
|
|
127
|
+
mixedPath: Path,
|
|
128
|
+
metadata: FileMetadata,
|
|
129
|
+
changeListener?: FileSystemListener,
|
|
130
|
+
): void;
|
|
131
|
+
bulkAddOrModify(
|
|
132
|
+
addedOrModifiedFiles: FileData,
|
|
133
|
+
changeListener?: FileSystemListener,
|
|
134
|
+
): void;
|
|
135
|
+
remove(mixedPath: Path, changeListener?: FileSystemListener): void;
|
|
136
|
+
/**
|
|
137
|
+
* Given a start path (which need not exist), a subpath and type, and
|
|
138
|
+
* optionally a 'breakOnSegment', performs the following:
|
|
139
|
+
*
|
|
140
|
+
* X = mixedStartPath
|
|
141
|
+
* do
|
|
142
|
+
* if basename(X) === opts.breakOnSegment
|
|
143
|
+
* return null
|
|
144
|
+
* if X + subpath exists and has type opts.subpathType
|
|
145
|
+
* return {
|
|
146
|
+
* absolutePath: realpath(X + subpath)
|
|
147
|
+
* containerRelativePath: relative(mixedStartPath, X)
|
|
148
|
+
* }
|
|
149
|
+
* X = dirname(X)
|
|
150
|
+
* while X !== dirname(X)
|
|
151
|
+
*
|
|
152
|
+
* If opts.invalidatedBy is given, collects all absolute, real paths that if
|
|
153
|
+
* added or removed may invalidate this result.
|
|
154
|
+
*
|
|
155
|
+
* Useful for finding the closest package scope (subpath: package.json,
|
|
156
|
+
* type f, breakOnSegment: node_modules) or closest potential package root
|
|
157
|
+
* (subpath: node_modules/pkg, type: d) in Node.js resolution.
|
|
158
|
+
*/
|
|
159
|
+
hierarchicalLookup(
|
|
160
|
+
mixedStartPath: string,
|
|
161
|
+
subpath: string,
|
|
162
|
+
opts: {
|
|
163
|
+
breakOnSegment: null | undefined | string;
|
|
164
|
+
invalidatedBy: null | undefined | Set<string>;
|
|
165
|
+
subpathType: 'f' | 'd';
|
|
166
|
+
},
|
|
167
|
+
): null | undefined | {absolutePath: string; containerRelativePath: string};
|
|
168
|
+
metadataIterator(opts: MetadataIteratorOptions): Iterator<{
|
|
169
|
+
baseName: string;
|
|
170
|
+
canonicalPath: string;
|
|
171
|
+
metadata: FileMetadata;
|
|
172
|
+
}>;
|
|
173
|
+
}
|
|
174
|
+
export default TreeFS;
|
package/src/lib/TreeFS.js
CHANGED
|
@@ -9,11 +9,7 @@ var _RootPathUtils = require("./RootPathUtils");
|
|
|
9
9
|
var _invariant = _interopRequireDefault(require("invariant"));
|
|
10
10
|
var _path = _interopRequireDefault(require("path"));
|
|
11
11
|
function _interopRequireDefault(e) {
|
|
12
|
-
return e && e.__esModule
|
|
13
|
-
? e
|
|
14
|
-
: {
|
|
15
|
-
default: e,
|
|
16
|
-
};
|
|
12
|
+
return e && e.__esModule ? e : { default: e };
|
|
17
13
|
}
|
|
18
14
|
function isDirectory(node) {
|
|
19
15
|
return node instanceof Map;
|
|
@@ -52,13 +48,33 @@ class TreeFS {
|
|
|
52
48
|
const fileMetadata = this.#getFileData(mixedPath);
|
|
53
49
|
return (fileMetadata && fileMetadata[_constants.default.SIZE]) ?? null;
|
|
54
50
|
}
|
|
55
|
-
getDifference(files) {
|
|
51
|
+
getDifference(files, options) {
|
|
56
52
|
const changedFiles = new Map(files);
|
|
57
53
|
const removedFiles = new Set();
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
54
|
+
const subpath = options?.subpath;
|
|
55
|
+
let rootNode = this.#rootNode;
|
|
56
|
+
let prefix = "";
|
|
57
|
+
if (subpath != null && subpath !== "") {
|
|
58
|
+
const lookupResult = this.#lookupByNormalPath(subpath, {
|
|
59
|
+
followLeaf: true,
|
|
60
|
+
});
|
|
61
|
+
if (!lookupResult.exists || !isDirectory(lookupResult.node)) {
|
|
62
|
+
return {
|
|
63
|
+
changedFiles,
|
|
64
|
+
removedFiles,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
rootNode = lookupResult.node;
|
|
68
|
+
prefix = lookupResult.canonicalPath;
|
|
69
|
+
}
|
|
70
|
+
for (const { canonicalPath, metadata } of this.#metadataIterator(
|
|
71
|
+
rootNode,
|
|
72
|
+
{
|
|
73
|
+
includeNodeModules: true,
|
|
74
|
+
includeSymlinks: true,
|
|
75
|
+
},
|
|
76
|
+
prefix,
|
|
77
|
+
)) {
|
|
62
78
|
const newMetadata = files.get(canonicalPath);
|
|
63
79
|
if (newMetadata) {
|
|
64
80
|
if (isRegularFile(newMetadata) !== isRegularFile(metadata)) {
|
|
@@ -257,11 +273,12 @@ class TreeFS {
|
|
|
257
273
|
}
|
|
258
274
|
}
|
|
259
275
|
}
|
|
260
|
-
addOrModify(mixedPath, metadata) {
|
|
276
|
+
addOrModify(mixedPath, metadata, changeListener) {
|
|
261
277
|
const normalPath = this.#normalizePath(mixedPath);
|
|
262
278
|
const parentDirNode = this.#lookupByNormalPath(
|
|
263
279
|
_path.default.dirname(normalPath),
|
|
264
280
|
{
|
|
281
|
+
changeListener,
|
|
265
282
|
makeDirectories: true,
|
|
266
283
|
},
|
|
267
284
|
);
|
|
@@ -275,9 +292,9 @@ class TreeFS {
|
|
|
275
292
|
_path.default.sep +
|
|
276
293
|
_path.default.basename(normalPath),
|
|
277
294
|
);
|
|
278
|
-
this.bulkAddOrModify(new Map([[canonicalPath, metadata]]));
|
|
295
|
+
this.bulkAddOrModify(new Map([[canonicalPath, metadata]]), changeListener);
|
|
279
296
|
}
|
|
280
|
-
bulkAddOrModify(addedOrModifiedFiles) {
|
|
297
|
+
bulkAddOrModify(addedOrModifiedFiles, changeListener) {
|
|
281
298
|
let lastDir;
|
|
282
299
|
let directoryNode;
|
|
283
300
|
for (const [normalPath, metadata] of addedOrModifiedFiles) {
|
|
@@ -287,6 +304,7 @@ class TreeFS {
|
|
|
287
304
|
lastSepIdx === -1 ? normalPath : normalPath.slice(lastSepIdx + 1);
|
|
288
305
|
if (directoryNode == null || dirname !== lastDir) {
|
|
289
306
|
const lookup = this.#lookupByNormalPath(dirname, {
|
|
307
|
+
changeListener,
|
|
290
308
|
followLeaf: false,
|
|
291
309
|
makeDirectories: true,
|
|
292
310
|
});
|
|
@@ -305,30 +323,53 @@ class TreeFS {
|
|
|
305
323
|
lastDir = dirname;
|
|
306
324
|
directoryNode = lookup.node;
|
|
307
325
|
}
|
|
326
|
+
if (changeListener != null) {
|
|
327
|
+
const existingNode = directoryNode.get(basename);
|
|
328
|
+
if (existingNode != null) {
|
|
329
|
+
(0, _invariant.default)(
|
|
330
|
+
!isDirectory(existingNode),
|
|
331
|
+
"Detected addition or modification of file %s, but it is tracked as a non-empty directory",
|
|
332
|
+
normalPath,
|
|
333
|
+
);
|
|
334
|
+
changeListener.fileModified(normalPath, existingNode, metadata);
|
|
335
|
+
} else {
|
|
336
|
+
changeListener.fileAdded(normalPath, metadata);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
308
339
|
directoryNode.set(basename, metadata);
|
|
309
340
|
}
|
|
310
341
|
}
|
|
311
|
-
remove(mixedPath) {
|
|
342
|
+
remove(mixedPath, changeListener) {
|
|
312
343
|
const normalPath = this.#normalizePath(mixedPath);
|
|
313
344
|
const result = this.#lookupByNormalPath(normalPath, {
|
|
314
345
|
followLeaf: false,
|
|
315
346
|
});
|
|
316
347
|
if (!result.exists) {
|
|
317
|
-
return
|
|
348
|
+
return;
|
|
318
349
|
}
|
|
319
350
|
const { parentNode, canonicalPath, node } = result;
|
|
320
351
|
if (isDirectory(node) && node.size > 0) {
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
352
|
+
for (const basename of node.keys()) {
|
|
353
|
+
this.remove(
|
|
354
|
+
canonicalPath + _path.default.sep + basename,
|
|
355
|
+
changeListener,
|
|
356
|
+
);
|
|
357
|
+
}
|
|
358
|
+
return;
|
|
324
359
|
}
|
|
325
360
|
if (parentNode != null) {
|
|
361
|
+
if (changeListener != null) {
|
|
362
|
+
if (isDirectory(node)) {
|
|
363
|
+
changeListener.directoryRemoved(canonicalPath);
|
|
364
|
+
} else {
|
|
365
|
+
changeListener.fileRemoved(canonicalPath, node);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
326
368
|
parentNode.delete(_path.default.basename(canonicalPath));
|
|
327
369
|
if (parentNode.size === 0 && parentNode !== this.#rootNode) {
|
|
328
|
-
this.remove(_path.default.dirname(canonicalPath));
|
|
370
|
+
this.remove(_path.default.dirname(canonicalPath), changeListener);
|
|
329
371
|
}
|
|
330
372
|
}
|
|
331
|
-
return isDirectory(node) ? null : node;
|
|
332
373
|
}
|
|
333
374
|
#lookupByNormalPath(
|
|
334
375
|
requestedNormalPath,
|
|
@@ -342,7 +383,7 @@ class TreeFS {
|
|
|
342
383
|
let fromIdx = opts.start?.pathIdx ?? 0;
|
|
343
384
|
let parentNode = opts.start?.node ?? this.#rootNode;
|
|
344
385
|
let ancestorOfRootIdx = opts.start?.ancestorOfRootIdx ?? 0;
|
|
345
|
-
const collectAncestors = opts
|
|
386
|
+
const { collectAncestors, changeListener } = opts;
|
|
346
387
|
let unseenPathFromIdx = 0;
|
|
347
388
|
while (targetNormalPath.length > fromIdx) {
|
|
348
389
|
const nextSepIdx = targetNormalPath.indexOf(_path.default.sep, fromIdx);
|
|
@@ -373,6 +414,12 @@ class TreeFS {
|
|
|
373
414
|
}
|
|
374
415
|
segmentNode = new Map();
|
|
375
416
|
if (opts.makeDirectories === true) {
|
|
417
|
+
if (changeListener != null) {
|
|
418
|
+
const canonicalPath = isLastSegment
|
|
419
|
+
? targetNormalPath
|
|
420
|
+
: targetNormalPath.slice(0, fromIdx - 1);
|
|
421
|
+
changeListener.directoryAdded(canonicalPath);
|
|
422
|
+
}
|
|
376
423
|
parentNode.set(segmentName, segmentNode);
|
|
377
424
|
}
|
|
378
425
|
}
|