@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,411 @@
|
|
|
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
|
+
* @format
|
|
9
|
+
*/
|
|
10
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
13
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
14
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
15
|
+
}
|
|
16
|
+
Object.defineProperty(o, k2, desc);
|
|
17
|
+
}) : (function(o, m, k, k2) {
|
|
18
|
+
if (k2 === undefined) k2 = k;
|
|
19
|
+
o[k2] = m[k];
|
|
20
|
+
}));
|
|
21
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
22
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
23
|
+
}) : function(o, v) {
|
|
24
|
+
o["default"] = v;
|
|
25
|
+
});
|
|
26
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
27
|
+
var ownKeys = function(o) {
|
|
28
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
29
|
+
var ar = [];
|
|
30
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
31
|
+
return ar;
|
|
32
|
+
};
|
|
33
|
+
return ownKeys(o);
|
|
34
|
+
};
|
|
35
|
+
return function (mod) {
|
|
36
|
+
if (mod && mod.__esModule) return mod;
|
|
37
|
+
var result = {};
|
|
38
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
39
|
+
__setModuleDefault(result, mod);
|
|
40
|
+
return result;
|
|
41
|
+
};
|
|
42
|
+
})();
|
|
43
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
44
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
45
|
+
};
|
|
46
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
47
|
+
const fs_1 = __importDefault(require("fs"));
|
|
48
|
+
const os_1 = __importDefault(require("os"));
|
|
49
|
+
const path_1 = __importDefault(require("path"));
|
|
50
|
+
const AbstractWatcher_1 = require("./AbstractWatcher");
|
|
51
|
+
const common = __importStar(require("./common"));
|
|
52
|
+
// NOTE(@kitten): No typings
|
|
53
|
+
const walker = require('walker');
|
|
54
|
+
const platform = os_1.default.platform();
|
|
55
|
+
const fsPromises = fs_1.default.promises;
|
|
56
|
+
const TOUCH_EVENT = common.TOUCH_EVENT;
|
|
57
|
+
const DELETE_EVENT = common.DELETE_EVENT;
|
|
58
|
+
/**
|
|
59
|
+
* This setting delays all events. It suppresses 'change' events that
|
|
60
|
+
* immediately follow an 'add', and debounces successive 'change' events to
|
|
61
|
+
* only emit the latest.
|
|
62
|
+
*/
|
|
63
|
+
const DEBOUNCE_MS = 100;
|
|
64
|
+
class FallbackWatcher extends AbstractWatcher_1.AbstractWatcher {
|
|
65
|
+
#changeTimers = new Map();
|
|
66
|
+
#dirRegistry = Object.create(null);
|
|
67
|
+
#watched = Object.create(null);
|
|
68
|
+
async startWatching() {
|
|
69
|
+
this.#watchdir(this.root);
|
|
70
|
+
await new Promise((resolve) => {
|
|
71
|
+
recReaddir(this.root, (dir) => {
|
|
72
|
+
this.#watchdir(dir);
|
|
73
|
+
}, (filename) => {
|
|
74
|
+
this.#register(filename, 'f');
|
|
75
|
+
}, (symlink) => {
|
|
76
|
+
this.#register(symlink, 'l');
|
|
77
|
+
}, () => {
|
|
78
|
+
resolve();
|
|
79
|
+
}, this.#checkedEmitError, this.ignored);
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Register files that matches our globs to know what to type of event to
|
|
84
|
+
* emit in the future.
|
|
85
|
+
*
|
|
86
|
+
* Registry looks like the following:
|
|
87
|
+
*
|
|
88
|
+
* dirRegister => Map {
|
|
89
|
+
* dirpath => Map {
|
|
90
|
+
* filename => true
|
|
91
|
+
* }
|
|
92
|
+
* }
|
|
93
|
+
*
|
|
94
|
+
* Return false if ignored or already registered.
|
|
95
|
+
*/
|
|
96
|
+
#register(filepath, type) {
|
|
97
|
+
const dir = path_1.default.dirname(filepath);
|
|
98
|
+
const filename = path_1.default.basename(filepath);
|
|
99
|
+
if (this.#dirRegistry[dir] && this.#dirRegistry[dir][filename]) {
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
const relativePath = path_1.default.relative(this.root, filepath);
|
|
103
|
+
if (this.doIgnore(relativePath) ||
|
|
104
|
+
(type === 'f' && !common.includedByGlob('f', this.globs, this.dot, relativePath))) {
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
if (!this.#dirRegistry[dir]) {
|
|
108
|
+
this.#dirRegistry[dir] = Object.create(null);
|
|
109
|
+
}
|
|
110
|
+
this.#dirRegistry[dir][filename] = true;
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Removes a file from the registry.
|
|
115
|
+
*/
|
|
116
|
+
#unregister(filepath) {
|
|
117
|
+
const dir = path_1.default.dirname(filepath);
|
|
118
|
+
if (this.#dirRegistry[dir]) {
|
|
119
|
+
const filename = path_1.default.basename(filepath);
|
|
120
|
+
delete this.#dirRegistry[dir][filename];
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Removes a dir from the registry, returning all files that were registered
|
|
125
|
+
* under it (recursively).
|
|
126
|
+
*/
|
|
127
|
+
#unregisterDir(dirpath) {
|
|
128
|
+
const removedFiles = [];
|
|
129
|
+
// Find and remove all entries under this directory
|
|
130
|
+
for (const registeredDir of Object.keys(this.#dirRegistry)) {
|
|
131
|
+
if (registeredDir === dirpath || registeredDir.startsWith(dirpath + path_1.default.sep)) {
|
|
132
|
+
// Collect all files in this directory
|
|
133
|
+
for (const filename of Object.keys(this.#dirRegistry[registeredDir])) {
|
|
134
|
+
removedFiles.push(path_1.default.join(registeredDir, filename));
|
|
135
|
+
}
|
|
136
|
+
delete this.#dirRegistry[registeredDir];
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return removedFiles;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Checks if a file or directory exists in the registry.
|
|
143
|
+
*/
|
|
144
|
+
#registered(fullpath) {
|
|
145
|
+
const dir = path_1.default.dirname(fullpath);
|
|
146
|
+
return !!(this.#dirRegistry[fullpath] ||
|
|
147
|
+
(this.#dirRegistry[dir] && this.#dirRegistry[dir][path_1.default.basename(fullpath)]));
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Emit "error" event if it's not an ignorable event
|
|
151
|
+
*/
|
|
152
|
+
#checkedEmitError = (error) => {
|
|
153
|
+
if (!isIgnorableFileError(error)) {
|
|
154
|
+
this.emitError(error);
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
/**
|
|
158
|
+
* Watch a directory.
|
|
159
|
+
*/
|
|
160
|
+
#watchdir = (dir) => {
|
|
161
|
+
if (this.#watched[dir]) {
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
const watcher = fs_1.default.watch(dir, { persistent: true }, (event, filename) => this.#normalizeChange(dir, event, filename));
|
|
165
|
+
this.#watched[dir] = watcher;
|
|
166
|
+
watcher.on('error', this.#checkedEmitError);
|
|
167
|
+
if (this.root !== dir) {
|
|
168
|
+
this.#register(dir, 'd');
|
|
169
|
+
}
|
|
170
|
+
return true;
|
|
171
|
+
};
|
|
172
|
+
/**
|
|
173
|
+
* Stop watching a directory.
|
|
174
|
+
*/
|
|
175
|
+
async #stopWatching(dir) {
|
|
176
|
+
const watcher = this.#watched[dir];
|
|
177
|
+
if (watcher) {
|
|
178
|
+
await new Promise((resolve) => {
|
|
179
|
+
watcher.once('close', () => process.nextTick(resolve));
|
|
180
|
+
watcher.close();
|
|
181
|
+
delete this.#watched[dir];
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* End watching.
|
|
187
|
+
*/
|
|
188
|
+
async stopWatching() {
|
|
189
|
+
await super.stopWatching();
|
|
190
|
+
const promises = Object.keys(this.#watched).map((dir) => this.#stopWatching(dir));
|
|
191
|
+
await Promise.all(promises);
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* On some platforms, as pointed out on the fs docs (most likely just win32)
|
|
195
|
+
* the file argument might be missing from the fs event. Try to detect what
|
|
196
|
+
* change by detecting if something was deleted or the most recent file change.
|
|
197
|
+
*/
|
|
198
|
+
#detectChangedFile(dir, event, callback) {
|
|
199
|
+
if (!this.#dirRegistry[dir]) {
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
let found = false;
|
|
203
|
+
let closest = null;
|
|
204
|
+
let c = 0;
|
|
205
|
+
Object.keys(this.#dirRegistry[dir]).forEach((file, i, arr) => {
|
|
206
|
+
fs_1.default.lstat(path_1.default.join(dir, file), (error, stat) => {
|
|
207
|
+
if (found) {
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
if (error) {
|
|
211
|
+
if (isIgnorableFileError(error)) {
|
|
212
|
+
found = true;
|
|
213
|
+
callback(file);
|
|
214
|
+
}
|
|
215
|
+
else {
|
|
216
|
+
this.emitError(error);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
else {
|
|
220
|
+
if (closest == null || stat.mtime > closest.mtime) {
|
|
221
|
+
closest = { file, mtime: stat.mtime };
|
|
222
|
+
}
|
|
223
|
+
if (arr.length === ++c) {
|
|
224
|
+
callback(closest.file);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Normalize fs events and pass it on to be processed.
|
|
232
|
+
*/
|
|
233
|
+
#normalizeChange(dir, event, file) {
|
|
234
|
+
if (!file) {
|
|
235
|
+
this.#detectChangedFile(dir, event, (actualFile) => {
|
|
236
|
+
if (actualFile) {
|
|
237
|
+
this.#processChange(dir, event, actualFile).catch((error) => {
|
|
238
|
+
this.emitError(error);
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
else {
|
|
244
|
+
this.#processChange(dir, event, path_1.default.normalize(file)).catch((error) => {
|
|
245
|
+
this.emitError(error);
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Process changes.
|
|
251
|
+
*/
|
|
252
|
+
async #processChange(dir, event, file) {
|
|
253
|
+
const fullPath = path_1.default.join(dir, file);
|
|
254
|
+
const relativePath = path_1.default.join(path_1.default.relative(this.root, dir), file);
|
|
255
|
+
const registered = this.#registered(fullPath);
|
|
256
|
+
try {
|
|
257
|
+
const stat = await fsPromises.lstat(fullPath);
|
|
258
|
+
if (stat.isDirectory()) {
|
|
259
|
+
// win32 emits usless change events on dirs.
|
|
260
|
+
if (event === 'change') {
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
if (this.doIgnore(relativePath) ||
|
|
264
|
+
!common.includedByGlob('d', this.globs, this.dot, relativePath)) {
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
recReaddir(path_1.default.resolve(this.root, relativePath), (dir, stats) => {
|
|
268
|
+
if (this.#watchdir(dir)) {
|
|
269
|
+
this.#emitEvent({
|
|
270
|
+
event: TOUCH_EVENT,
|
|
271
|
+
relativePath: path_1.default.relative(this.root, dir),
|
|
272
|
+
metadata: {
|
|
273
|
+
modifiedTime: stats.mtime.getTime(),
|
|
274
|
+
size: stats.size,
|
|
275
|
+
type: 'd',
|
|
276
|
+
},
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
}, (file, stats) => {
|
|
280
|
+
if (this.#register(file, 'f')) {
|
|
281
|
+
this.#emitEvent({
|
|
282
|
+
event: TOUCH_EVENT,
|
|
283
|
+
relativePath: path_1.default.relative(this.root, file),
|
|
284
|
+
metadata: {
|
|
285
|
+
modifiedTime: stats.mtime.getTime(),
|
|
286
|
+
size: stats.size,
|
|
287
|
+
type: 'f',
|
|
288
|
+
},
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
}, (symlink, stats) => {
|
|
292
|
+
if (this.#register(symlink, 'l')) {
|
|
293
|
+
this.emitFileEvent({
|
|
294
|
+
event: TOUCH_EVENT,
|
|
295
|
+
relativePath: path_1.default.relative(this.root, symlink),
|
|
296
|
+
metadata: {
|
|
297
|
+
modifiedTime: stats.mtime.getTime(),
|
|
298
|
+
size: stats.size,
|
|
299
|
+
type: 'l',
|
|
300
|
+
},
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
}, function endCallback() { }, this.#checkedEmitError, this.ignored);
|
|
304
|
+
}
|
|
305
|
+
else {
|
|
306
|
+
const type = common.typeFromStat(stat);
|
|
307
|
+
if (type == null) {
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
const metadata = {
|
|
311
|
+
modifiedTime: stat.mtime.getTime(),
|
|
312
|
+
size: stat.size,
|
|
313
|
+
type,
|
|
314
|
+
};
|
|
315
|
+
if (registered) {
|
|
316
|
+
this.#emitEvent({ event: TOUCH_EVENT, relativePath, metadata });
|
|
317
|
+
}
|
|
318
|
+
else {
|
|
319
|
+
if (this.#register(fullPath, type)) {
|
|
320
|
+
this.#emitEvent({ event: TOUCH_EVENT, relativePath, metadata });
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
catch (error) {
|
|
326
|
+
if (!isIgnorableFileError(error)) {
|
|
327
|
+
this.emitError(error);
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
this.#unregister(fullPath);
|
|
331
|
+
// When a directory is deleted, emit delete events for all files we
|
|
332
|
+
// knew about under that directory
|
|
333
|
+
const removedFiles = this.#unregisterDir(fullPath);
|
|
334
|
+
for (const removedFile of removedFiles) {
|
|
335
|
+
this.#emitEvent({
|
|
336
|
+
event: DELETE_EVENT,
|
|
337
|
+
relativePath: path_1.default.relative(this.root, removedFile),
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
if (registered) {
|
|
341
|
+
this.#emitEvent({ event: DELETE_EVENT, relativePath });
|
|
342
|
+
}
|
|
343
|
+
await this.#stopWatching(fullPath);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* Emits the given event after debouncing, to emit only the latest
|
|
348
|
+
* information when we receive several events in quick succession. E.g.,
|
|
349
|
+
* Linux emits two events for every new file.
|
|
350
|
+
*
|
|
351
|
+
* See also note above for DEBOUNCE_MS.
|
|
352
|
+
*/
|
|
353
|
+
#emitEvent(change) {
|
|
354
|
+
const { event, relativePath } = change;
|
|
355
|
+
const key = event + '-' + relativePath;
|
|
356
|
+
const existingTimer = this.#changeTimers.get(key);
|
|
357
|
+
if (existingTimer) {
|
|
358
|
+
clearTimeout(existingTimer);
|
|
359
|
+
}
|
|
360
|
+
this.#changeTimers.set(key, setTimeout(() => {
|
|
361
|
+
this.#changeTimers.delete(key);
|
|
362
|
+
this.emitFileEvent(change);
|
|
363
|
+
}, DEBOUNCE_MS));
|
|
364
|
+
}
|
|
365
|
+
getPauseReason() {
|
|
366
|
+
return null;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
exports.default = FallbackWatcher;
|
|
370
|
+
/**
|
|
371
|
+
* Determine if a given FS error can be ignored
|
|
372
|
+
*/
|
|
373
|
+
function isIgnorableFileError(error) {
|
|
374
|
+
return (error.code === 'ENOENT' ||
|
|
375
|
+
// Workaround Windows EPERM on watched folder deletion, and when
|
|
376
|
+
// reading locked files (pending further writes or pending deletion).
|
|
377
|
+
// In such cases, we'll receive a subsequent event when the file is
|
|
378
|
+
// deleted or ready to read.
|
|
379
|
+
// https://github.com/facebook/metro/issues/1001
|
|
380
|
+
// https://github.com/nodejs/node-v0.x-archive/issues/4337
|
|
381
|
+
(error.code === 'EPERM' && platform === 'win32'));
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* Traverse a directory recursively calling `callback` on every directory.
|
|
385
|
+
*/
|
|
386
|
+
function recReaddir(dir, dirCallback, fileCallback, symlinkCallback, endCallback, errorCallback, ignored) {
|
|
387
|
+
const walk = walker(dir);
|
|
388
|
+
if (ignored) {
|
|
389
|
+
walk.filterDir((currentDir) => !common.posixPathMatchesPattern(ignored, currentDir));
|
|
390
|
+
}
|
|
391
|
+
walk
|
|
392
|
+
.on('dir', normalizeProxy(dirCallback))
|
|
393
|
+
.on('file', normalizeProxy(fileCallback))
|
|
394
|
+
.on('symlink', normalizeProxy(symlinkCallback))
|
|
395
|
+
.on('error', errorCallback)
|
|
396
|
+
.on('end', () => {
|
|
397
|
+
if (platform === 'win32') {
|
|
398
|
+
setTimeout(endCallback, 1000);
|
|
399
|
+
}
|
|
400
|
+
else {
|
|
401
|
+
endCallback();
|
|
402
|
+
}
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
/**
|
|
406
|
+
* Returns a callback that when called will normalize a path and call the
|
|
407
|
+
* original callback
|
|
408
|
+
*/
|
|
409
|
+
function normalizeProxy(callback) {
|
|
410
|
+
return (filepath, stats) => callback(path_1.default.normalize(filepath), stats);
|
|
411
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
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 { WatcherBackendOptions } from '../types';
|
|
8
|
+
import { AbstractWatcher } from './AbstractWatcher';
|
|
9
|
+
/**
|
|
10
|
+
* NativeWatcher uses Node's native fs.watch API with recursive: true.
|
|
11
|
+
*
|
|
12
|
+
* Supported on macOS (and potentially Windows), because both natively have a
|
|
13
|
+
* concept of recurisve watching, via FSEvents and ReadDirectoryChangesW
|
|
14
|
+
* respectively. Notably Linux lacks this capability at the OS level.
|
|
15
|
+
*
|
|
16
|
+
* Node.js has at times supported the `recursive` option to fs.watch on Linux
|
|
17
|
+
* by walking the directory tree and creating a watcher on each directory, but
|
|
18
|
+
* this fits poorly with the synchronous `watch` API - either it must block for
|
|
19
|
+
* arbitrarily large IO, or it may drop changes after `watch` returns. See:
|
|
20
|
+
* https://github.com/nodejs/node/issues/48437
|
|
21
|
+
*
|
|
22
|
+
* Therefore, we retain a fallback to our own application-level recursive
|
|
23
|
+
* FallbackWatcher for Linux, which has async `startWatching`.
|
|
24
|
+
*
|
|
25
|
+
* On Windows, this watcher could be used in principle, but needs work around
|
|
26
|
+
* some Windows-specific edge cases handled in FallbackWatcher, like
|
|
27
|
+
* deduping file change events, ignoring directory changes, and handling EPERM.
|
|
28
|
+
*/
|
|
29
|
+
export default class NativeWatcher extends AbstractWatcher {
|
|
30
|
+
#private;
|
|
31
|
+
static isSupported(): boolean;
|
|
32
|
+
constructor(dir: string, opts: WatcherBackendOptions);
|
|
33
|
+
startWatching(): Promise<void>;
|
|
34
|
+
/**
|
|
35
|
+
* End watching.
|
|
36
|
+
*/
|
|
37
|
+
stopWatching(): Promise<void>;
|
|
38
|
+
_handleEvent(event: string, relativePath: string | null): Promise<void>;
|
|
39
|
+
}
|
|
@@ -0,0 +1,156 @@
|
|
|
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 __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
9
|
+
if (k2 === undefined) k2 = k;
|
|
10
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
11
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
12
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
13
|
+
}
|
|
14
|
+
Object.defineProperty(o, k2, desc);
|
|
15
|
+
}) : (function(o, m, k, k2) {
|
|
16
|
+
if (k2 === undefined) k2 = k;
|
|
17
|
+
o[k2] = m[k];
|
|
18
|
+
}));
|
|
19
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
20
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
21
|
+
}) : function(o, v) {
|
|
22
|
+
o["default"] = v;
|
|
23
|
+
});
|
|
24
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
25
|
+
var ownKeys = function(o) {
|
|
26
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
27
|
+
var ar = [];
|
|
28
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
29
|
+
return ar;
|
|
30
|
+
};
|
|
31
|
+
return ownKeys(o);
|
|
32
|
+
};
|
|
33
|
+
return function (mod) {
|
|
34
|
+
if (mod && mod.__esModule) return mod;
|
|
35
|
+
var result = {};
|
|
36
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
37
|
+
__setModuleDefault(result, mod);
|
|
38
|
+
return result;
|
|
39
|
+
};
|
|
40
|
+
})();
|
|
41
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
42
|
+
const fs_1 = require("fs");
|
|
43
|
+
const os_1 = require("os");
|
|
44
|
+
const path = __importStar(require("path"));
|
|
45
|
+
const AbstractWatcher_1 = require("./AbstractWatcher");
|
|
46
|
+
const common_1 = require("./common");
|
|
47
|
+
const debug = require('debug')('Metro:NativeWatcher');
|
|
48
|
+
const TOUCH_EVENT = 'touch';
|
|
49
|
+
const DELETE_EVENT = 'delete';
|
|
50
|
+
const RECRAWL_EVENT = 'recrawl';
|
|
51
|
+
/**
|
|
52
|
+
* NativeWatcher uses Node's native fs.watch API with recursive: true.
|
|
53
|
+
*
|
|
54
|
+
* Supported on macOS (and potentially Windows), because both natively have a
|
|
55
|
+
* concept of recurisve watching, via FSEvents and ReadDirectoryChangesW
|
|
56
|
+
* respectively. Notably Linux lacks this capability at the OS level.
|
|
57
|
+
*
|
|
58
|
+
* Node.js has at times supported the `recursive` option to fs.watch on Linux
|
|
59
|
+
* by walking the directory tree and creating a watcher on each directory, but
|
|
60
|
+
* this fits poorly with the synchronous `watch` API - either it must block for
|
|
61
|
+
* arbitrarily large IO, or it may drop changes after `watch` returns. See:
|
|
62
|
+
* https://github.com/nodejs/node/issues/48437
|
|
63
|
+
*
|
|
64
|
+
* Therefore, we retain a fallback to our own application-level recursive
|
|
65
|
+
* FallbackWatcher for Linux, which has async `startWatching`.
|
|
66
|
+
*
|
|
67
|
+
* On Windows, this watcher could be used in principle, but needs work around
|
|
68
|
+
* some Windows-specific edge cases handled in FallbackWatcher, like
|
|
69
|
+
* deduping file change events, ignoring directory changes, and handling EPERM.
|
|
70
|
+
*/
|
|
71
|
+
class NativeWatcher extends AbstractWatcher_1.AbstractWatcher {
|
|
72
|
+
#fsWatcher;
|
|
73
|
+
static isSupported() {
|
|
74
|
+
return (0, os_1.platform)() === 'darwin';
|
|
75
|
+
}
|
|
76
|
+
// eslint-disable-next-line @typescript-eslint/no-useless-constructor
|
|
77
|
+
constructor(dir, opts) {
|
|
78
|
+
// NOTE(@kitten): `!NativeWatcher.isSupported` was always truthy, so omitting check here
|
|
79
|
+
super(dir, opts);
|
|
80
|
+
}
|
|
81
|
+
async startWatching() {
|
|
82
|
+
this.#fsWatcher = (0, fs_1.watch)(this.root, {
|
|
83
|
+
// Don't hold the process open if we forget to close()
|
|
84
|
+
persistent: false,
|
|
85
|
+
// FSEvents or ReadDirectoryChangesW should mean this is cheap and
|
|
86
|
+
// ~instant on macOS or Windows.
|
|
87
|
+
recursive: true,
|
|
88
|
+
}, (event, relativePath) => {
|
|
89
|
+
this._handleEvent(event, relativePath).catch((error) => {
|
|
90
|
+
this.emitError(error);
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
debug('Watching %s', this.root);
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* End watching.
|
|
97
|
+
*/
|
|
98
|
+
async stopWatching() {
|
|
99
|
+
await super.stopWatching();
|
|
100
|
+
if (this.#fsWatcher) {
|
|
101
|
+
this.#fsWatcher.close();
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
async _handleEvent(event, relativePath) {
|
|
105
|
+
if (relativePath == null) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
const absolutePath = path.resolve(this.root, relativePath);
|
|
109
|
+
if (this.doIgnore(relativePath)) {
|
|
110
|
+
debug('Ignoring event "%s" on %s (root: %s)', event, relativePath, this.root);
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
debug('Handling event "%s" on %s (root: %s)', event, relativePath, this.root);
|
|
114
|
+
try {
|
|
115
|
+
const stat = await fs_1.promises.lstat(absolutePath);
|
|
116
|
+
const type = (0, common_1.typeFromStat)(stat);
|
|
117
|
+
// Ignore files of an unrecognized type
|
|
118
|
+
if (!type) {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
if (!(0, common_1.includedByGlob)(type, this.globs, this.dot, relativePath)) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
// For directory "rename" events, notify that we need a recrawl since we
|
|
125
|
+
// wont' receive events for unmodified files underneath a moved (or
|
|
126
|
+
// cloned) directory. Renames are fired by the OS on moves, clones, and
|
|
127
|
+
// creations. We ignore "change" events because they indiciate a change
|
|
128
|
+
// to directory metadata, rather than its path or existence.
|
|
129
|
+
if (type === 'd' && event === 'rename') {
|
|
130
|
+
debug('Directory rename detected on %s, requesting recrawl', relativePath);
|
|
131
|
+
this.emitFileEvent({
|
|
132
|
+
event: RECRAWL_EVENT,
|
|
133
|
+
relativePath,
|
|
134
|
+
});
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
this.emitFileEvent({
|
|
138
|
+
event: TOUCH_EVENT,
|
|
139
|
+
relativePath,
|
|
140
|
+
metadata: {
|
|
141
|
+
type,
|
|
142
|
+
modifiedTime: stat.mtime.getTime(),
|
|
143
|
+
size: stat.size,
|
|
144
|
+
},
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
catch (error) {
|
|
148
|
+
if (error?.code !== 'ENOENT') {
|
|
149
|
+
this.emitError(error);
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
this.emitFileEvent({ event: DELETE_EVENT, relativePath });
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
exports.default = NativeWatcher;
|
|
@@ -0,0 +1,18 @@
|
|
|
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
|
+
* Originally vendored from
|
|
8
|
+
* https://github.com/amasad/sane/blob/64ff3a870c42e84f744086884bf55a4f9c22d376/src/utils/recrawl-warning-dedupe.js
|
|
9
|
+
*/
|
|
10
|
+
export default class RecrawlWarning {
|
|
11
|
+
static RECRAWL_WARNINGS: RecrawlWarning[];
|
|
12
|
+
static REGEXP: RegExp;
|
|
13
|
+
root: string;
|
|
14
|
+
count: number;
|
|
15
|
+
constructor(root: string, count: number);
|
|
16
|
+
static findByRoot(root: string): RecrawlWarning | undefined;
|
|
17
|
+
static isRecrawlWarningDupe(warningMessage: unknown): boolean;
|
|
18
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
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
|
+
* Originally vendored from
|
|
9
|
+
* https://github.com/amasad/sane/blob/64ff3a870c42e84f744086884bf55a4f9c22d376/src/utils/recrawl-warning-dedupe.js
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
class RecrawlWarning {
|
|
13
|
+
static RECRAWL_WARNINGS = [];
|
|
14
|
+
static REGEXP = /Recrawled this watch (\d+) times?, most recently because:\n([^:]+)/;
|
|
15
|
+
root;
|
|
16
|
+
count;
|
|
17
|
+
constructor(root, count) {
|
|
18
|
+
this.root = root;
|
|
19
|
+
this.count = count;
|
|
20
|
+
}
|
|
21
|
+
static findByRoot(root) {
|
|
22
|
+
for (let i = 0; i < this.RECRAWL_WARNINGS.length; i++) {
|
|
23
|
+
const warning = this.RECRAWL_WARNINGS[i];
|
|
24
|
+
if (warning.root === root) {
|
|
25
|
+
return warning;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return undefined;
|
|
29
|
+
}
|
|
30
|
+
static isRecrawlWarningDupe(warningMessage) {
|
|
31
|
+
if (typeof warningMessage !== 'string') {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
const match = warningMessage.match(this.REGEXP);
|
|
35
|
+
if (!match) {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
const count = Number(match[1]);
|
|
39
|
+
const root = match[2];
|
|
40
|
+
const warning = this.findByRoot(root);
|
|
41
|
+
if (warning) {
|
|
42
|
+
// only keep the highest count, assume count to either stay the same or
|
|
43
|
+
// increase.
|
|
44
|
+
if (warning.count >= count) {
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
// update the existing warning to the latest (highest) count
|
|
49
|
+
warning.count = count;
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
this.RECRAWL_WARNINGS.push(new RecrawlWarning(root, count));
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
exports.default = RecrawlWarning;
|