@expo/metro-file-map 55.0.0-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.
- package/README.md +1 -0
- package/build/Watcher.d.ts +53 -0
- package/build/Watcher.js +249 -0
- package/build/cache/DiskCacheManager.d.ts +25 -0
- package/build/cache/DiskCacheManager.js +110 -0
- package/build/constants.d.ts +23 -0
- package/build/constants.js +27 -0
- package/build/crawlers/node/fallback.d.ts +20 -0
- package/build/crawlers/node/fallback.js +148 -0
- package/build/crawlers/node/index.d.ts +10 -0
- package/build/crawlers/node/index.js +152 -0
- package/build/crawlers/watchman/index.d.ts +8 -0
- package/build/crawlers/watchman/index.js +288 -0
- package/build/crawlers/watchman/planQuery.d.ts +31 -0
- package/build/crawlers/watchman/planQuery.js +99 -0
- package/build/index.d.ts +140 -0
- package/build/index.js +836 -0
- package/build/lib/FileProcessor.d.ts +42 -0
- package/build/lib/FileProcessor.js +180 -0
- package/build/lib/FileSystemChangeAggregator.d.ts +18 -0
- package/build/lib/FileSystemChangeAggregator.js +114 -0
- package/build/lib/RootPathUtils.d.ts +24 -0
- package/build/lib/RootPathUtils.js +311 -0
- package/build/lib/TreeFS.d.ts +161 -0
- package/build/lib/TreeFS.js +1006 -0
- package/build/lib/checkWatchmanCapabilities.d.ts +9 -0
- package/build/lib/checkWatchmanCapabilities.js +50 -0
- package/build/lib/normalizePathSeparatorsToPosix.d.ts +8 -0
- package/build/lib/normalizePathSeparatorsToPosix.js +20 -0
- package/build/lib/normalizePathSeparatorsToSystem.d.ts +8 -0
- package/build/lib/normalizePathSeparatorsToSystem.js +20 -0
- package/build/lib/removeOverlappingRoots.d.ts +10 -0
- package/build/lib/removeOverlappingRoots.js +37 -0
- package/build/lib/rootRelativeCacheKeys.d.ts +11 -0
- package/build/lib/rootRelativeCacheKeys.js +56 -0
- package/build/lib/sorting.d.ts +8 -0
- package/build/lib/sorting.js +31 -0
- package/build/plugins/DependencyPlugin.d.ts +23 -0
- package/build/plugins/DependencyPlugin.js +61 -0
- package/build/plugins/FileDataPlugin.d.ts +28 -0
- package/build/plugins/FileDataPlugin.js +46 -0
- package/build/plugins/HastePlugin.d.ts +32 -0
- package/build/plugins/HastePlugin.js +358 -0
- package/build/plugins/MockPlugin.d.ts +27 -0
- package/build/plugins/MockPlugin.js +157 -0
- package/build/plugins/dependencies/dependencyExtractor.d.ts +7 -0
- package/build/plugins/dependencies/dependencyExtractor.js +66 -0
- package/build/plugins/dependencies/worker.d.ts +18 -0
- package/build/plugins/dependencies/worker.js +30 -0
- package/build/plugins/haste/DuplicateHasteCandidatesError.d.ts +14 -0
- package/build/plugins/haste/DuplicateHasteCandidatesError.js +51 -0
- package/build/plugins/haste/HasteConflictsError.d.ts +12 -0
- package/build/plugins/haste/HasteConflictsError.js +49 -0
- package/build/plugins/haste/computeConflicts.d.ts +19 -0
- package/build/plugins/haste/computeConflicts.js +74 -0
- package/build/plugins/haste/getPlatformExtension.d.ts +7 -0
- package/build/plugins/haste/getPlatformExtension.js +19 -0
- package/build/plugins/haste/worker.d.ts +16 -0
- package/build/plugins/haste/worker.js +48 -0
- package/build/plugins/mocks/getMockName.d.ts +8 -0
- package/build/plugins/mocks/getMockName.js +17 -0
- package/build/ts-declarations/fb-watchman.d.ts +20 -0
- package/build/ts-declarations/fb-watchman.js +2 -0
- package/build/types.d.ts +404 -0
- package/build/types.js +8 -0
- package/build/watchers/AbstractWatcher.d.ts +30 -0
- package/build/watchers/AbstractWatcher.js +97 -0
- package/build/watchers/FallbackWatcher.d.ts +18 -0
- package/build/watchers/FallbackWatcher.js +411 -0
- package/build/watchers/NativeWatcher.d.ts +39 -0
- package/build/watchers/NativeWatcher.js +156 -0
- package/build/watchers/RecrawlWarning.d.ts +18 -0
- package/build/watchers/RecrawlWarning.js +59 -0
- package/build/watchers/WatchmanWatcher.d.ts +22 -0
- package/build/watchers/WatchmanWatcher.js +263 -0
- package/build/watchers/common.d.ts +39 -0
- package/build/watchers/common.js +60 -0
- package/build/worker.d.ts +24 -0
- package/build/worker.js +80 -0
- package/build/workerExclusionList.d.ts +8 -0
- package/build/workerExclusionList.js +57 -0
- package/package.json +54 -0
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*/
|
|
8
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
9
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.RootPathUtils = void 0;
|
|
13
|
+
exports.getAncestorOfRootIdx = getAncestorOfRootIdx;
|
|
14
|
+
exports.pathsToPattern = pathsToPattern;
|
|
15
|
+
const invariant_1 = __importDefault(require("invariant"));
|
|
16
|
+
const path_1 = __importDefault(require("path"));
|
|
17
|
+
const normalizePathSeparatorsToSystem_1 = __importDefault(require("./normalizePathSeparatorsToSystem"));
|
|
18
|
+
/**
|
|
19
|
+
* This module provides path utility functions - similar to `node:path` -
|
|
20
|
+
* optimised for Metro's use case (many paths, few roots) under assumptions
|
|
21
|
+
* typically safe to make within Metro - namely:
|
|
22
|
+
*
|
|
23
|
+
* - All input path separators must be system-native.
|
|
24
|
+
* - Double/redundant separators like '/foo//bar' are not supported.
|
|
25
|
+
* - All characters except separators are assumed to be valid in path segments.
|
|
26
|
+
*
|
|
27
|
+
* - A "well-formed" path is any path following the rules above.
|
|
28
|
+
* - A "normal" path is a root-relative well-formed path with no redundant
|
|
29
|
+
* indirections. Normal paths have no leading './`, and the normal path of
|
|
30
|
+
* the root is the empty string.
|
|
31
|
+
*
|
|
32
|
+
* Output and input paths are at least well-formed (normal where indicated by
|
|
33
|
+
* naming).
|
|
34
|
+
*
|
|
35
|
+
* Trailing path separators are preserved, except for fs roots in
|
|
36
|
+
* normalToAbsolute (fs roots always have a trailing separator), and the
|
|
37
|
+
* project root in absoluteToNormal and relativeToNormal (the project root is
|
|
38
|
+
* always the empty string, and is always a directory, so a trailing separator
|
|
39
|
+
* is redundant).
|
|
40
|
+
*
|
|
41
|
+
* As of Node 20, absoluteToNormal is ~8x faster than `path.relative` and
|
|
42
|
+
* `normalToAbsolute` is ~20x faster than `path.resolve`, benchmarked on the
|
|
43
|
+
* real inputs from building FB's product graph. Some well-formed inputs
|
|
44
|
+
* (e.g., /project/./foo/../bar), are handled but not optimised, and we fall
|
|
45
|
+
* back to `node:path` equivalents in those cases.
|
|
46
|
+
*/
|
|
47
|
+
const UP_FRAGMENT_SEP = '..' + path_1.default.sep;
|
|
48
|
+
const SEP_UP_FRAGMENT = path_1.default.sep + '..';
|
|
49
|
+
const UP_FRAGMENT_SEP_LENGTH = UP_FRAGMENT_SEP.length;
|
|
50
|
+
const CURRENT_FRAGMENT = '.' + path_1.default.sep;
|
|
51
|
+
class RootPathUtils {
|
|
52
|
+
#rootDir;
|
|
53
|
+
#rootDirnames;
|
|
54
|
+
#rootParts;
|
|
55
|
+
#rootDepth;
|
|
56
|
+
constructor(rootDir) {
|
|
57
|
+
this.#rootDir = rootDir;
|
|
58
|
+
const rootDirnames = [];
|
|
59
|
+
for (let next = rootDir, previous = null; previous !== next; previous = next, next = path_1.default.dirname(next)) {
|
|
60
|
+
rootDirnames.push(next);
|
|
61
|
+
}
|
|
62
|
+
this.#rootDirnames = rootDirnames;
|
|
63
|
+
this.#rootDepth = rootDirnames.length - 1;
|
|
64
|
+
const rootParts = rootDir.split(path_1.default.sep);
|
|
65
|
+
// If rootDir is a filesystem root (C:\ or /), it will end in a separator and
|
|
66
|
+
// give a spurious empty entry at the end of rootParts.
|
|
67
|
+
if (this.#rootDepth === 0) {
|
|
68
|
+
rootParts.pop();
|
|
69
|
+
}
|
|
70
|
+
this.#rootParts = rootParts;
|
|
71
|
+
}
|
|
72
|
+
getBasenameOfNthAncestor(n) {
|
|
73
|
+
return this.#rootParts[this.#rootParts.length - 1 - n];
|
|
74
|
+
}
|
|
75
|
+
getParts() {
|
|
76
|
+
return this.#rootParts;
|
|
77
|
+
}
|
|
78
|
+
// absolutePath may be any well-formed absolute path.
|
|
79
|
+
absoluteToNormal(absolutePath) {
|
|
80
|
+
let endOfMatchingPrefix = 0;
|
|
81
|
+
let lastMatchingPartIdx = 0;
|
|
82
|
+
for (let nextPart = this.#rootParts[0], nextLength = nextPart.length; nextPart != null &&
|
|
83
|
+
// Check that absolutePath is equal to nextPart + '/' or ends with
|
|
84
|
+
// nextPart, starting from endOfMatchingPrefix.
|
|
85
|
+
absolutePath.startsWith(nextPart, endOfMatchingPrefix) &&
|
|
86
|
+
(absolutePath.length === endOfMatchingPrefix + nextLength ||
|
|
87
|
+
absolutePath[endOfMatchingPrefix + nextLength] === path_1.default.sep);) {
|
|
88
|
+
// Move our matching pointer forward and load the next part.
|
|
89
|
+
endOfMatchingPrefix += nextLength + 1;
|
|
90
|
+
nextPart = this.#rootParts[++lastMatchingPartIdx];
|
|
91
|
+
nextLength = nextPart?.length ?? 0;
|
|
92
|
+
}
|
|
93
|
+
// If our root is /project/root and we're given /project/bar/foo.js, we
|
|
94
|
+
// have matched up to '/project', and will need to return a path
|
|
95
|
+
// beginning '../' (one prepended indirection, to go up from 'root').
|
|
96
|
+
//
|
|
97
|
+
// If we're given /project/../project2/otherroot, we have one level of
|
|
98
|
+
// indirection up to prepend in the same way as above. There's another
|
|
99
|
+
// explicit indirection already present in the input - we'll account for
|
|
100
|
+
// that in tryCollapseIndirectionsInSuffix.
|
|
101
|
+
const upIndirectionsToPrepend = this.#rootParts.length - lastMatchingPartIdx;
|
|
102
|
+
return (this.#tryCollapseIndirectionsInSuffix(absolutePath, endOfMatchingPrefix, upIndirectionsToPrepend)?.collapsedPath ?? this.#slowAbsoluteToNormal(absolutePath));
|
|
103
|
+
}
|
|
104
|
+
#slowAbsoluteToNormal(absolutePath) {
|
|
105
|
+
const endsWithSep = absolutePath.endsWith(path_1.default.sep);
|
|
106
|
+
const result = path_1.default.relative(this.#rootDir, absolutePath);
|
|
107
|
+
return endsWithSep && !result.endsWith(path_1.default.sep) ? result + path_1.default.sep : result;
|
|
108
|
+
}
|
|
109
|
+
// `normalPath` is assumed to be normal (root-relative, no redundant
|
|
110
|
+
// indirection), per the definition above.
|
|
111
|
+
normalToAbsolute(normalPath) {
|
|
112
|
+
let left = this.#rootDir;
|
|
113
|
+
let i = 0;
|
|
114
|
+
let pos = 0;
|
|
115
|
+
while (normalPath.startsWith(UP_FRAGMENT_SEP, pos) ||
|
|
116
|
+
(normalPath.endsWith('..') && normalPath.length === 2 + pos)) {
|
|
117
|
+
left = this.#rootDirnames[i === this.#rootDepth ? this.#rootDepth : ++i];
|
|
118
|
+
pos += UP_FRAGMENT_SEP_LENGTH;
|
|
119
|
+
}
|
|
120
|
+
const right = pos === 0 ? normalPath : normalPath.slice(pos);
|
|
121
|
+
if (right.length === 0) {
|
|
122
|
+
return left;
|
|
123
|
+
}
|
|
124
|
+
// left may already end in a path separator only if it is a filesystem root,
|
|
125
|
+
// '/' or 'X:\'.
|
|
126
|
+
if (i === this.#rootDepth) {
|
|
127
|
+
return left + right;
|
|
128
|
+
}
|
|
129
|
+
return left + path_1.default.sep + right;
|
|
130
|
+
}
|
|
131
|
+
relativeToNormal(relativePath) {
|
|
132
|
+
return (this.#tryCollapseIndirectionsInSuffix(relativePath, 0, 0)?.collapsedPath ??
|
|
133
|
+
path_1.default.relative(this.#rootDir, path_1.default.join(this.#rootDir, relativePath)));
|
|
134
|
+
}
|
|
135
|
+
resolveSymlinkToNormal(symlinkNormalPath, readlinkResult) {
|
|
136
|
+
let target = (0, normalizePathSeparatorsToSystem_1.default)(readlinkResult);
|
|
137
|
+
// WARN: This only applies to Windows + Node 20 case, where the value is completely
|
|
138
|
+
// unnormalized and a trailing slash may be returned
|
|
139
|
+
if (target[target.length - 1] === path_1.default.sep) {
|
|
140
|
+
target = target.slice(0, -1);
|
|
141
|
+
}
|
|
142
|
+
if (path_1.default.isAbsolute(target)) {
|
|
143
|
+
return this.absoluteToNormal(target);
|
|
144
|
+
}
|
|
145
|
+
// Resolve relative to the symlink's containing directory, expressed as
|
|
146
|
+
// a root-relative (possibly non-normal) path, then normalize
|
|
147
|
+
const sepIdx = symlinkNormalPath.lastIndexOf(path_1.default.sep);
|
|
148
|
+
const rootRelativeTarget = sepIdx === -1 ? target : symlinkNormalPath.slice(0, sepIdx) + path_1.default.sep + target;
|
|
149
|
+
return this.relativeToNormal(rootRelativeTarget);
|
|
150
|
+
}
|
|
151
|
+
// If a path is a direct ancestor of the project root (or the root itself),
|
|
152
|
+
// return a number with the degrees of separation, e.g. root=0, parent=1,..
|
|
153
|
+
// or null otherwise.
|
|
154
|
+
getAncestorOfRootIdx(normalPath) {
|
|
155
|
+
if (normalPath === '') {
|
|
156
|
+
return 0;
|
|
157
|
+
}
|
|
158
|
+
if (normalPath === '..') {
|
|
159
|
+
return 1;
|
|
160
|
+
}
|
|
161
|
+
// Otherwise a *normal* path is only a root ancestor if it is a sequence of
|
|
162
|
+
// '../' segments followed by '..', so the length tells us the number of
|
|
163
|
+
// up fragments.
|
|
164
|
+
if (normalPath.endsWith(SEP_UP_FRAGMENT)) {
|
|
165
|
+
return (normalPath.length + 1) / 3;
|
|
166
|
+
}
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
// Takes a normal and relative path, and joins them efficiently into a normal
|
|
170
|
+
// path, including collapsing trailing '..' in the first part with leading
|
|
171
|
+
// project root segments in the relative part.
|
|
172
|
+
joinNormalToRelative(normalPath, relativePath) {
|
|
173
|
+
if (normalPath === '') {
|
|
174
|
+
return { collapsedSegments: 0, normalPath: relativePath };
|
|
175
|
+
}
|
|
176
|
+
if (relativePath === '') {
|
|
177
|
+
return { collapsedSegments: 0, normalPath };
|
|
178
|
+
}
|
|
179
|
+
const left = normalPath + path_1.default.sep;
|
|
180
|
+
const rawPath = left + relativePath;
|
|
181
|
+
if (normalPath === '..' || normalPath.endsWith(SEP_UP_FRAGMENT)) {
|
|
182
|
+
const collapsed = this.#tryCollapseIndirectionsInSuffix(rawPath, 0, 0);
|
|
183
|
+
(0, invariant_1.default)(collapsed != null, 'Failed to collapse');
|
|
184
|
+
return {
|
|
185
|
+
collapsedSegments: collapsed.collapsedSegments,
|
|
186
|
+
normalPath: collapsed.collapsedPath,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
return {
|
|
190
|
+
collapsedSegments: 0,
|
|
191
|
+
normalPath: rawPath,
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
relative(from, to) {
|
|
195
|
+
return path_1.default.relative(from, to);
|
|
196
|
+
}
|
|
197
|
+
// Internal: Tries to collapse sequences like `../root/foo` for root
|
|
198
|
+
// `/project/root` down to the normal 'foo'.
|
|
199
|
+
#tryCollapseIndirectionsInSuffix(fullPath, // A string ending with the relative path to process
|
|
200
|
+
startOfRelativePart, // Index of the start of part to process
|
|
201
|
+
implicitUpIndirections // 0=root-relative, 1=dirname(root)-relative...
|
|
202
|
+
) {
|
|
203
|
+
let totalUpIndirections = implicitUpIndirections;
|
|
204
|
+
let collapsedSegments = 0;
|
|
205
|
+
// Allow any sequence of indirection fragments at the start of the
|
|
206
|
+
// unmatched suffix e.g /project/[../../foo], but bail out to Node's
|
|
207
|
+
// path.relative if we find a possible indirection after any later segment,
|
|
208
|
+
// or on any "./" that isn't a "../".
|
|
209
|
+
for (let pos = startOfRelativePart;; pos += UP_FRAGMENT_SEP_LENGTH) {
|
|
210
|
+
const nextIndirection = fullPath.indexOf(CURRENT_FRAGMENT, pos);
|
|
211
|
+
if (nextIndirection === -1) {
|
|
212
|
+
// If we have any indirections, they may "collapse" if a subsequent
|
|
213
|
+
// segment re-enters a directory we had previously exited, e.g:
|
|
214
|
+
// /project/root/../root/foo should collapse to /project/root/foo' and
|
|
215
|
+
// return foo, not ../root/foo.
|
|
216
|
+
//
|
|
217
|
+
// We match each segment following redirections, in turn, against the
|
|
218
|
+
// part of the root path they may collapse into, and break on the first
|
|
219
|
+
// mismatch.
|
|
220
|
+
while (totalUpIndirections > 0) {
|
|
221
|
+
const segmentToMaybeCollapse = this.#rootParts[this.#rootParts.length - totalUpIndirections];
|
|
222
|
+
if (fullPath.startsWith(segmentToMaybeCollapse, pos) &&
|
|
223
|
+
// The following character should be either a separator or end of
|
|
224
|
+
// string
|
|
225
|
+
(fullPath.length === segmentToMaybeCollapse.length + pos ||
|
|
226
|
+
fullPath[segmentToMaybeCollapse.length + pos] === path_1.default.sep)) {
|
|
227
|
+
pos += segmentToMaybeCollapse.length + 1;
|
|
228
|
+
collapsedSegments++;
|
|
229
|
+
totalUpIndirections--;
|
|
230
|
+
}
|
|
231
|
+
else {
|
|
232
|
+
break;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
// After collapsing we may have no more segments remaining (following
|
|
236
|
+
// '..' indirections). Ensure that we don't drop or add a trailing
|
|
237
|
+
// separator in this case by taking .slice(pos-1). In any other case,
|
|
238
|
+
// we know that fullPath[pos] is a separator.
|
|
239
|
+
if (pos >= fullPath.length) {
|
|
240
|
+
return {
|
|
241
|
+
collapsedPath: totalUpIndirections > 0
|
|
242
|
+
? UP_FRAGMENT_SEP.repeat(totalUpIndirections - 1) + '..' + fullPath.slice(pos - 1)
|
|
243
|
+
: '',
|
|
244
|
+
collapsedSegments,
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
const right = pos > 0 ? fullPath.slice(pos) : fullPath;
|
|
248
|
+
if (right === '..' && totalUpIndirections >= this.#rootParts.length - 1) {
|
|
249
|
+
// If we have no right side (or an indirection that would take us
|
|
250
|
+
// below the root), just ensure we don't include a trailing separtor.
|
|
251
|
+
return {
|
|
252
|
+
collapsedPath: UP_FRAGMENT_SEP.repeat(totalUpIndirections).slice(0, -1),
|
|
253
|
+
collapsedSegments,
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
// Optimisation for the common case, saves a concatenation.
|
|
257
|
+
if (totalUpIndirections === 0) {
|
|
258
|
+
return { collapsedPath: right, collapsedSegments };
|
|
259
|
+
}
|
|
260
|
+
return {
|
|
261
|
+
collapsedPath: UP_FRAGMENT_SEP.repeat(totalUpIndirections) + right,
|
|
262
|
+
collapsedSegments,
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
// Cap the number of indirections at the total number of root segments.
|
|
266
|
+
// File systems treat '..' at the root as '.'.
|
|
267
|
+
if (totalUpIndirections < this.#rootParts.length - 1) {
|
|
268
|
+
totalUpIndirections++;
|
|
269
|
+
}
|
|
270
|
+
if (nextIndirection !== pos + 1 || // Fallback when ./ later in the path, or leading
|
|
271
|
+
fullPath[pos] !== '.' // and for anything other than a leading ../
|
|
272
|
+
) {
|
|
273
|
+
return null;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
exports.RootPathUtils = RootPathUtils;
|
|
279
|
+
function getAncestorOfRootIdx(normalPath) {
|
|
280
|
+
let pos = 0;
|
|
281
|
+
while (normalPath.startsWith(UP_FRAGMENT_SEP, pos)) {
|
|
282
|
+
pos += UP_FRAGMENT_SEP_LENGTH;
|
|
283
|
+
}
|
|
284
|
+
if (normalPath.length === pos + 2 &&
|
|
285
|
+
normalPath.charCodeAt(pos) === 46 &&
|
|
286
|
+
normalPath.charCodeAt(pos + 1) === 46) {
|
|
287
|
+
return pos / UP_FRAGMENT_SEP_LENGTH + 1;
|
|
288
|
+
}
|
|
289
|
+
return pos / UP_FRAGMENT_SEP_LENGTH;
|
|
290
|
+
}
|
|
291
|
+
function pathsToPattern(paths, pathUtils) {
|
|
292
|
+
if (paths.length === 0) {
|
|
293
|
+
return null;
|
|
294
|
+
}
|
|
295
|
+
const pathsPatterns = paths.map((input) => {
|
|
296
|
+
let pattern = pathUtils.absoluteToNormal(input);
|
|
297
|
+
// When pattern is '' (root === rootDir), match any normal path that
|
|
298
|
+
// doesn't escape the root via '..' indirections.
|
|
299
|
+
if (pattern === '') {
|
|
300
|
+
return `(?!\\.\\.(?:\\${path_1.default.sep}|$))`;
|
|
301
|
+
}
|
|
302
|
+
// Append separator so that 'src' matches 'src/foo' but not 'src2'.
|
|
303
|
+
if (!pattern.endsWith(path_1.default.sep)) {
|
|
304
|
+
pattern += path_1.default.sep;
|
|
305
|
+
}
|
|
306
|
+
// Escape all regex-special characters.
|
|
307
|
+
// eslint-disable-next-line no-useless-escape
|
|
308
|
+
return pattern.replace(/[\-\[\]\{\}\(\)\*\+\?\.\\\^\$\|\/]/g, '\\$&');
|
|
309
|
+
});
|
|
310
|
+
return new RegExp(`^(?:${pathsPatterns.join('|')})`);
|
|
311
|
+
}
|
|
@@ -0,0 +1,161 @@
|
|
|
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
|
+
import type { CacheData, FallbackFilesystem, FileData, FileMetadata, FileStats, FileSystemListener, LookupResult, MutableFileSystem, Path, ProcessFileFunction } from '../types';
|
|
8
|
+
type DirectoryNode = Map<string, MixedNode | null>;
|
|
9
|
+
type FileNode = FileMetadata;
|
|
10
|
+
type MixedNode = FileNode | DirectoryNode;
|
|
11
|
+
interface DeserializedSnapshotInput {
|
|
12
|
+
rootDir: string;
|
|
13
|
+
fileSystemData: DirectoryNode;
|
|
14
|
+
processFile: ProcessFileFunction;
|
|
15
|
+
fallbackFilesystem?: FallbackFilesystem | null | undefined;
|
|
16
|
+
roots?: readonly string[];
|
|
17
|
+
serverRoot?: string | null | undefined;
|
|
18
|
+
}
|
|
19
|
+
interface TreeFSOptions {
|
|
20
|
+
rootDir: Path;
|
|
21
|
+
files?: FileData;
|
|
22
|
+
processFile: ProcessFileFunction;
|
|
23
|
+
fallbackFilesystem?: FallbackFilesystem | null | undefined;
|
|
24
|
+
roots?: readonly string[];
|
|
25
|
+
serverRoot?: string | null | undefined;
|
|
26
|
+
}
|
|
27
|
+
interface MatchFilesOptions {
|
|
28
|
+
readonly filter?: RegExp | null;
|
|
29
|
+
readonly filterCompareAbsolute?: boolean;
|
|
30
|
+
readonly filterComparePosix?: boolean;
|
|
31
|
+
readonly follow?: boolean;
|
|
32
|
+
readonly recursive?: boolean;
|
|
33
|
+
readonly rootDir?: Path | null;
|
|
34
|
+
}
|
|
35
|
+
interface MetadataIteratorOptions {
|
|
36
|
+
readonly includeSymlinks: boolean;
|
|
37
|
+
readonly includeNodeModules: boolean;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* OVERVIEW:
|
|
41
|
+
*
|
|
42
|
+
* TreeFS is Metro's in-memory representation of the file system. It is
|
|
43
|
+
* structured as a tree of non-empty maps and leaves (tuples), with the root
|
|
44
|
+
* node representing the given `rootDir`, typically Metro's _project root_
|
|
45
|
+
* (not a filesystem root). Map keys are path segments, and branches outside
|
|
46
|
+
* the project root are accessed via `'..'`.
|
|
47
|
+
*
|
|
48
|
+
* EXAMPLE:
|
|
49
|
+
*
|
|
50
|
+
* For a root dir '/data/project', the file '/data/other/app/index.js' would
|
|
51
|
+
* have metadata at #rootNode.get('..').get('other').get('app').get('index.js')
|
|
52
|
+
*
|
|
53
|
+
* SERIALISATION:
|
|
54
|
+
*
|
|
55
|
+
* #rootNode is designed to be directly serialisable and directly portable (for
|
|
56
|
+
* a given project) between different root directories and operating systems.
|
|
57
|
+
*
|
|
58
|
+
* SYMLINKS:
|
|
59
|
+
*
|
|
60
|
+
* Symlinks are represented as nodes whose metadata contains their literal
|
|
61
|
+
* target. Literal targets are resolved to normal paths at runtime, and cached.
|
|
62
|
+
* If a symlink is encountered during traversal, we restart traversal at the
|
|
63
|
+
* root node targeting join(normal symlink target, remaining path suffix).
|
|
64
|
+
*
|
|
65
|
+
* NODE TYPES:
|
|
66
|
+
*
|
|
67
|
+
* - A directory (including a parent directory at '..') is represented by a
|
|
68
|
+
* `Map` of basenames to any other node type.
|
|
69
|
+
* - A file is represented by an `Array` (tuple) of metadata, of which:
|
|
70
|
+
* - A regular file has node[H.SYMLINK] === 0
|
|
71
|
+
* - A symlink has node[H.SYMLINK] === 1 or
|
|
72
|
+
* typeof node[H.SYMLINK] === 'string', where a string is the literal
|
|
73
|
+
* content of the symlink (i.e. from readlink), if known.
|
|
74
|
+
*
|
|
75
|
+
* TERMINOLOGY:
|
|
76
|
+
*
|
|
77
|
+
* - mixedPath
|
|
78
|
+
* A root-relative or absolute path
|
|
79
|
+
* - relativePath
|
|
80
|
+
* A root-relative path
|
|
81
|
+
* - normalPath
|
|
82
|
+
* A root-relative, normalised path (no extraneous '.' or '..'), may have a
|
|
83
|
+
* single trailing slash
|
|
84
|
+
* - canonicalPath
|
|
85
|
+
* A root-relative, normalised, real path (no symlinks in dirname), never has
|
|
86
|
+
* a trailing slash
|
|
87
|
+
*/
|
|
88
|
+
export default class TreeFS implements MutableFileSystem {
|
|
89
|
+
#private;
|
|
90
|
+
constructor(opts: TreeFSOptions);
|
|
91
|
+
getSerializableSnapshot(): CacheData['fileSystemData'];
|
|
92
|
+
static fromDeserializedSnapshot(args: DeserializedSnapshotInput): TreeFS;
|
|
93
|
+
getSize(mixedPath: Path): number | null;
|
|
94
|
+
getDifference(files: FileData, options?: {
|
|
95
|
+
/**
|
|
96
|
+
* Only consider files under this normal subdirectory when computing
|
|
97
|
+
* removedFiles. If not provided, all files in the file system are
|
|
98
|
+
* considered.
|
|
99
|
+
*/
|
|
100
|
+
readonly subpath?: string;
|
|
101
|
+
}): {
|
|
102
|
+
changedFiles: FileData;
|
|
103
|
+
removedFiles: Set<string>;
|
|
104
|
+
};
|
|
105
|
+
getMtimeByNormalPath(normalPath: Path): number | null;
|
|
106
|
+
getSha1(mixedPath: Path): string | null;
|
|
107
|
+
getOrComputeSha1(mixedPath: Path): Promise<{
|
|
108
|
+
sha1: string;
|
|
109
|
+
content?: Buffer;
|
|
110
|
+
} | null | undefined>;
|
|
111
|
+
exists(mixedPath: Path): boolean;
|
|
112
|
+
lookup(mixedPath: Path): LookupResult;
|
|
113
|
+
getAllFiles(): Path[];
|
|
114
|
+
linkStats(mixedPath: Path): FileStats | null;
|
|
115
|
+
/**
|
|
116
|
+
* Given a search context, return a list of file paths matching the query.
|
|
117
|
+
* The query matches against normalized paths which start with `./`,
|
|
118
|
+
* for example: `a/b.js` -> `./a/b.js`
|
|
119
|
+
*/
|
|
120
|
+
matchFiles(opts: MatchFilesOptions): Generator<Path>;
|
|
121
|
+
addOrModify(mixedPath: Path, metadata: FileMetadata, changeListener?: FileSystemListener): void;
|
|
122
|
+
bulkAddOrModify(addedOrModifiedFiles: FileData, changeListener?: FileSystemListener): void;
|
|
123
|
+
remove(mixedPath: Path, changeListener?: FileSystemListener): void;
|
|
124
|
+
/**
|
|
125
|
+
* Given a start path (which need not exist), a subpath and type, and
|
|
126
|
+
* optionally a 'breakOnSegment', performs the following:
|
|
127
|
+
*
|
|
128
|
+
* X = mixedStartPath
|
|
129
|
+
* do
|
|
130
|
+
* if basename(X) === opts.breakOnSegment
|
|
131
|
+
* return null
|
|
132
|
+
* if X + subpath exists and has type opts.subpathType
|
|
133
|
+
* return {
|
|
134
|
+
* absolutePath: realpath(X + subpath)
|
|
135
|
+
* containerRelativePath: relative(mixedStartPath, X)
|
|
136
|
+
* }
|
|
137
|
+
* X = dirname(X)
|
|
138
|
+
* while X !== dirname(X)
|
|
139
|
+
*
|
|
140
|
+
* If opts.invalidatedBy is given, collects all absolute, real paths that if
|
|
141
|
+
* added or removed may invalidate this result.
|
|
142
|
+
*
|
|
143
|
+
* Useful for finding the closest package scope (subpath: package.json,
|
|
144
|
+
* type f, breakOnSegment: node_modules) or closest potential package root
|
|
145
|
+
* (subpath: node_modules/pkg, type: d) in Node.js resolution.
|
|
146
|
+
*/
|
|
147
|
+
hierarchicalLookup(mixedStartPath: string, subpath: string, opts: {
|
|
148
|
+
breakOnSegment: string | null | undefined;
|
|
149
|
+
invalidatedBy: Set<string> | null | undefined;
|
|
150
|
+
subpathType: 'f' | 'd';
|
|
151
|
+
}): {
|
|
152
|
+
absolutePath: string;
|
|
153
|
+
containerRelativePath: string;
|
|
154
|
+
} | null | undefined;
|
|
155
|
+
metadataIterator(opts: MetadataIteratorOptions): Generator<{
|
|
156
|
+
baseName: string;
|
|
157
|
+
canonicalPath: string;
|
|
158
|
+
metadata: FileMetadata;
|
|
159
|
+
}>;
|
|
160
|
+
}
|
|
161
|
+
export {};
|