metro-file-map 0.72.3 → 0.73.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/package.json +2 -2
- package/src/ModuleMap.js +1 -0
- package/src/ModuleMap.js.flow +1 -0
- package/src/cache/DiskCacheManager.js +8 -2
- package/src/cache/DiskCacheManager.js.flow +9 -2
- package/src/crawlers/node.js +1 -0
- package/src/crawlers/node.js.flow +1 -0
- package/src/crawlers/{watchman.js → watchman/index.js} +44 -130
- package/src/crawlers/{watchman.js.flow → watchman/index.js.flow} +44 -165
- package/src/crawlers/watchman/planQuery.js +113 -0
- package/src/crawlers/watchman/planQuery.js.flow +122 -0
- package/src/flow-types.js +1 -0
- package/src/flow-types.js.flow +4 -1
- package/src/index.js +142 -76
- package/src/index.js.flow +68 -30
- package/src/lib/checkWatchmanCapabilities.js +73 -0
- package/src/lib/checkWatchmanCapabilities.js.flow +67 -0
- package/src/lib/rootRelativeCacheKeys.js +1 -0
- package/src/lib/rootRelativeCacheKeys.js.flow +1 -0
- package/src/watchers/FSEventsWatcher.js +6 -4
- package/src/watchers/FSEventsWatcher.js.flow +11 -6
- package/src/watchers/NodeWatcher.js +1 -0
- package/src/watchers/RecrawlWarning.js +22 -5
- package/src/watchers/RecrawlWarning.js.flow +71 -0
- package/src/watchers/WatchmanWatcher.js +215 -265
- package/src/watchers/WatchmanWatcher.js.flow +309 -0
- package/src/watchers/common.js +62 -35
- package/src/watchers/common.js.flow +143 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "metro-file-map",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.73.0",
|
|
4
4
|
"description": "[Experimental] - 🚇 File crawling, watching and mapping for Metro",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"repository": {
|
|
@@ -30,6 +30,6 @@
|
|
|
30
30
|
"slash": "^3.0.0"
|
|
31
31
|
},
|
|
32
32
|
"optionalDependencies": {
|
|
33
|
-
"fsevents": "^2.
|
|
33
|
+
"fsevents": "^2.3.2"
|
|
34
34
|
}
|
|
35
35
|
}
|
package/src/ModuleMap.js
CHANGED
package/src/ModuleMap.js.flow
CHANGED
|
@@ -29,6 +29,7 @@ function _interopRequireDefault(obj) {
|
|
|
29
29
|
*
|
|
30
30
|
*
|
|
31
31
|
* @format
|
|
32
|
+
* @oncall react_native
|
|
32
33
|
*/
|
|
33
34
|
const DEFAULT_PREFIX = "metro-file-map";
|
|
34
35
|
const DEFAULT_DIRECTORY = (0, _os.tmpdir)();
|
|
@@ -66,9 +67,14 @@ class DiskCacheManager {
|
|
|
66
67
|
return (0, _v.deserialize)(
|
|
67
68
|
(0, _gracefulFs.readFileSync)(this._cachePath)
|
|
68
69
|
);
|
|
69
|
-
} catch {
|
|
70
|
+
} catch (e) {
|
|
71
|
+
if ((e === null || e === void 0 ? void 0 : e.code) === "ENOENT") {
|
|
72
|
+
// Cache file not found - not considered an error.
|
|
73
|
+
return null;
|
|
74
|
+
} // Rethrow anything else.
|
|
70
75
|
|
|
71
|
-
|
|
76
|
+
throw e;
|
|
77
|
+
}
|
|
72
78
|
}
|
|
73
79
|
|
|
74
80
|
async write(dataSnapshot, { changed, removed }) {
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* @flow strict-local
|
|
8
8
|
* @format
|
|
9
|
+
* @oncall react_native
|
|
9
10
|
*/
|
|
10
11
|
|
|
11
12
|
import type {
|
|
@@ -68,8 +69,14 @@ export class DiskCacheManager implements CacheManager {
|
|
|
68
69
|
async read(): Promise<?InternalData> {
|
|
69
70
|
try {
|
|
70
71
|
return deserialize(readFileSync(this._cachePath));
|
|
71
|
-
} catch {
|
|
72
|
-
|
|
72
|
+
} catch (e) {
|
|
73
|
+
if (e?.code === 'ENOENT') {
|
|
74
|
+
// Cache file not found - not considered an error.
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
// Rethrow anything else.
|
|
78
|
+
throw e;
|
|
79
|
+
}
|
|
73
80
|
}
|
|
74
81
|
|
|
75
82
|
async write(
|
package/src/crawlers/node.js
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var _planQuery = require("./planQuery");
|
|
4
4
|
|
|
5
|
-
var
|
|
5
|
+
var _constants = _interopRequireDefault(require("../../constants"));
|
|
6
|
+
|
|
7
|
+
var fastPath = _interopRequireWildcard(require("../../lib/fast_path"));
|
|
6
8
|
|
|
7
9
|
var _normalizePathSep = _interopRequireDefault(
|
|
8
|
-
require("
|
|
10
|
+
require("../../lib/normalizePathSep")
|
|
9
11
|
);
|
|
10
12
|
|
|
13
|
+
var _invariant = _interopRequireDefault(require("invariant"));
|
|
14
|
+
|
|
11
15
|
var path = _interopRequireWildcard(require("path"));
|
|
12
16
|
|
|
13
17
|
function _getRequireWildcardCache(nodeInterop) {
|
|
@@ -64,8 +68,9 @@ function _interopRequireDefault(obj) {
|
|
|
64
68
|
*
|
|
65
69
|
*
|
|
66
70
|
* @format
|
|
71
|
+
* @oncall react_native
|
|
67
72
|
*/
|
|
68
|
-
const watchman = require("fb-watchman");
|
|
73
|
+
const watchman = require("fb-watchman");
|
|
69
74
|
|
|
70
75
|
const WATCHMAN_WARNING_INITIAL_DELAY_MILLISECONDS = 10000;
|
|
71
76
|
const WATCHMAN_WARNING_INTERVAL_MILLISECONDS = 20000;
|
|
@@ -77,68 +82,30 @@ function makeWatchmanError(error) {
|
|
|
77
82
|
`is running for this project. See ${watchmanURL}.`;
|
|
78
83
|
return error;
|
|
79
84
|
}
|
|
80
|
-
/**
|
|
81
|
-
* Wrap watchman capabilityCheck method as a promise.
|
|
82
|
-
*
|
|
83
|
-
* @param client watchman client
|
|
84
|
-
* @param caps capabilities to verify
|
|
85
|
-
* @returns a promise resolving to a list of verified capabilities
|
|
86
|
-
*/
|
|
87
|
-
|
|
88
|
-
async function capabilityCheck(client, caps) {
|
|
89
|
-
return new Promise((resolve, reject) => {
|
|
90
|
-
client.capabilityCheck(
|
|
91
|
-
// @ts-expect-error: incorrectly typed
|
|
92
|
-
caps,
|
|
93
|
-
(error, response) => {
|
|
94
|
-
if (error != null || response == null) {
|
|
95
|
-
reject(
|
|
96
|
-
error !== null && error !== void 0
|
|
97
|
-
? error
|
|
98
|
-
: new Error("capabilityCheck: Response missing")
|
|
99
|
-
);
|
|
100
|
-
} else {
|
|
101
|
-
resolve(response);
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
);
|
|
105
|
-
});
|
|
106
|
-
}
|
|
107
85
|
|
|
108
|
-
module.exports = async function watchmanCrawl(
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
86
|
+
module.exports = async function watchmanCrawl({
|
|
87
|
+
abortSignal,
|
|
88
|
+
computeSha1,
|
|
89
|
+
data,
|
|
90
|
+
enableSymlinks,
|
|
91
|
+
extensions,
|
|
92
|
+
ignore,
|
|
93
|
+
rootDir,
|
|
94
|
+
roots,
|
|
95
|
+
perfLogger,
|
|
96
|
+
}) {
|
|
114
97
|
perfLogger === null || perfLogger === void 0
|
|
115
98
|
? void 0
|
|
116
99
|
: perfLogger.point("watchmanCrawl_start");
|
|
100
|
+
const clocks = data.clocks;
|
|
117
101
|
const client = new watchman.Client();
|
|
118
|
-
|
|
119
|
-
_options$abortSignal === void 0
|
|
120
|
-
? void 0
|
|
121
|
-
: _options$abortSignal.addEventListener("abort", () => client.end());
|
|
122
|
-
perfLogger === null || perfLogger === void 0
|
|
102
|
+
abortSignal === null || abortSignal === void 0
|
|
123
103
|
? void 0
|
|
124
|
-
:
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
// If a required capability is missing then an error will be thrown,
|
|
129
|
-
// we don't need this assertion, so using optional instead.
|
|
130
|
-
optional: ["suffix-set"],
|
|
104
|
+
: abortSignal.addEventListener("abort", () => client.end());
|
|
105
|
+
let clientError;
|
|
106
|
+
client.on("error", (error) => {
|
|
107
|
+
clientError = makeWatchmanError(error);
|
|
131
108
|
});
|
|
132
|
-
const suffixExpression =
|
|
133
|
-
capabilities !== null &&
|
|
134
|
-
capabilities !== void 0 &&
|
|
135
|
-
capabilities.capabilities["suffix-set"] // If available, use the optimized `suffix-set` operation:
|
|
136
|
-
? // https://facebook.github.io/watchman/docs/expr/suffix.html#suffix-set
|
|
137
|
-
["suffix", extensions]
|
|
138
|
-
: ["anyof", ...extensions.map((extension) => ["suffix", extension])];
|
|
139
|
-
let clientError; // $FlowFixMe[prop-missing] - Client is not typed as an EventEmitter
|
|
140
|
-
|
|
141
|
-
client.on("error", (error) => (clientError = makeWatchmanError(error)));
|
|
142
109
|
let didLogWatchmanWaitMessage = false; // $FlowFixMe[unclear-type] - Fix to use fb-watchman types
|
|
143
110
|
|
|
144
111
|
const cmd = async (command, ...args) => {
|
|
@@ -171,18 +138,6 @@ module.exports = async function watchmanCrawl(options) {
|
|
|
171
138
|
}
|
|
172
139
|
};
|
|
173
140
|
|
|
174
|
-
if (options.computeSha1) {
|
|
175
|
-
const { capabilities } = await cmd("list-capabilities");
|
|
176
|
-
|
|
177
|
-
if (capabilities.indexOf("field-content.sha1hex") !== -1) {
|
|
178
|
-
fields.push("content.sha1hex");
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
perfLogger === null || perfLogger === void 0
|
|
183
|
-
? void 0
|
|
184
|
-
: perfLogger.point("watchmanCrawl/negotiateCapabilities_end");
|
|
185
|
-
|
|
186
141
|
async function getWatchmanRoots(roots) {
|
|
187
142
|
perfLogger === null || perfLogger === void 0
|
|
188
143
|
? void 0
|
|
@@ -250,61 +205,13 @@ module.exports = async function watchmanCrawl(options) {
|
|
|
250
205
|
[`watchmanCrawl/query_${index}_has_clock`]: since != null,
|
|
251
206
|
},
|
|
252
207
|
});
|
|
253
|
-
const query = {
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
};
|
|
261
|
-
/**
|
|
262
|
-
* Watchman "query planner".
|
|
263
|
-
*
|
|
264
|
-
* Watchman file queries consist of 1 or more generators that feed
|
|
265
|
-
* files through the expression evaluator.
|
|
266
|
-
*
|
|
267
|
-
* Strategy:
|
|
268
|
-
* 1. Select the narrowest possible generator so that the expression
|
|
269
|
-
* evaluator has fewer candidates to process.
|
|
270
|
-
* 2. Evaluate expressions from narrowest to broadest.
|
|
271
|
-
* 3. Don't use an expression to recheck a condition that the
|
|
272
|
-
* generator already guarantees.
|
|
273
|
-
* 4. Compose expressions to avoid combinatorial explosions in the
|
|
274
|
-
* number of terms.
|
|
275
|
-
*
|
|
276
|
-
* The ordering of generators/filters, from narrow to broad, is:
|
|
277
|
-
* - since = O(changes)
|
|
278
|
-
* - glob / dirname = O(files in a subtree of the repo)
|
|
279
|
-
* - suffix = O(files in the repo)
|
|
280
|
-
*
|
|
281
|
-
* We assume that file extensions are ~uniformly distributed in the
|
|
282
|
-
* repo but Haste map projects are focused on a handful of
|
|
283
|
-
* directories. Therefore `glob` < `suffix`.
|
|
284
|
-
*/
|
|
285
|
-
|
|
286
|
-
let queryGenerator;
|
|
287
|
-
|
|
288
|
-
if (since != null) {
|
|
289
|
-
// Use the `since` generator and filter by both path and extension.
|
|
290
|
-
query.since = since;
|
|
291
|
-
queryGenerator = "since";
|
|
292
|
-
query.expression.push(
|
|
293
|
-
["anyof", ...directoryFilters.map((dir) => ["dirname", dir])],
|
|
294
|
-
suffixExpression
|
|
295
|
-
);
|
|
296
|
-
} else if (directoryFilters.length > 0) {
|
|
297
|
-
// Use the `glob` generator and filter only by extension.
|
|
298
|
-
query.glob = directoryFilters.map((directory) => `${directory}/**`);
|
|
299
|
-
query.glob_includedotfiles = true;
|
|
300
|
-
queryGenerator = "glob";
|
|
301
|
-
query.expression.push(suffixExpression);
|
|
302
|
-
} else {
|
|
303
|
-
// Use the `suffix` generator with no path/extension filtering.
|
|
304
|
-
query.suffix = extensions;
|
|
305
|
-
queryGenerator = "suffix";
|
|
306
|
-
}
|
|
307
|
-
|
|
208
|
+
const { query, queryGenerator } = (0, _planQuery.planQuery)({
|
|
209
|
+
since,
|
|
210
|
+
extensions,
|
|
211
|
+
directoryFilters,
|
|
212
|
+
includeSha1: computeSha1,
|
|
213
|
+
includeSymlinks: enableSymlinks,
|
|
214
|
+
});
|
|
308
215
|
perfLogger === null || perfLogger === void 0
|
|
309
216
|
? void 0
|
|
310
217
|
: perfLogger.annotate({
|
|
@@ -435,6 +342,11 @@ module.exports = async function watchmanCrawl(options) {
|
|
|
435
342
|
);
|
|
436
343
|
|
|
437
344
|
for (const fileData of response.files) {
|
|
345
|
+
if (fileData.symlink_target != null) {
|
|
346
|
+
// TODO: Process symlinks
|
|
347
|
+
continue;
|
|
348
|
+
}
|
|
349
|
+
|
|
438
350
|
const filePath =
|
|
439
351
|
fsRoot + path.sep + (0, _normalizePathSep.default)(fileData.name);
|
|
440
352
|
const relativeFilePath = fastPath.relative(rootDir, filePath);
|
|
@@ -456,11 +368,13 @@ module.exports = async function watchmanCrawl(options) {
|
|
|
456
368
|
}
|
|
457
369
|
}
|
|
458
370
|
} else if (!ignore(filePath)) {
|
|
371
|
+
const { mtime_ms, size } = fileData;
|
|
372
|
+
(0, _invariant.default)(
|
|
373
|
+
mtime_ms != null && size != null,
|
|
374
|
+
"missing file data in watchman response"
|
|
375
|
+
);
|
|
459
376
|
const mtime =
|
|
460
|
-
typeof
|
|
461
|
-
? fileData.mtime_ms
|
|
462
|
-
: fileData.mtime_ms.toNumber();
|
|
463
|
-
const size = fileData.size;
|
|
377
|
+
typeof mtime_ms === "number" ? mtime_ms : mtime_ms.toNumber();
|
|
464
378
|
let sha1hex = fileData["content.sha1hex"];
|
|
465
379
|
|
|
466
380
|
if (typeof sha1hex !== "string" || sha1hex.length !== 40) {
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* @flow strict-local
|
|
8
8
|
* @format
|
|
9
|
+
* @oncall react_native
|
|
9
10
|
*/
|
|
10
11
|
|
|
11
12
|
import type {
|
|
@@ -14,55 +15,20 @@ import type {
|
|
|
14
15
|
FileMetaData,
|
|
15
16
|
InternalData,
|
|
16
17
|
Path,
|
|
17
|
-
} from '
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
import
|
|
21
|
-
import
|
|
18
|
+
} from '../../flow-types';
|
|
19
|
+
import type {WatchmanQueryResponse, WatchmanWatchResponse} from 'fb-watchman';
|
|
20
|
+
|
|
21
|
+
import {planQuery} from './planQuery';
|
|
22
|
+
import H from '../../constants';
|
|
23
|
+
import * as fastPath from '../../lib/fast_path';
|
|
24
|
+
import normalizePathSep from '../../lib/normalizePathSep';
|
|
25
|
+
import invariant from 'invariant';
|
|
22
26
|
import * as path from 'path';
|
|
23
27
|
|
|
24
28
|
const watchman = require('fb-watchman');
|
|
25
29
|
|
|
26
|
-
// $FlowFixMe[unclear-type] - Improve fb-watchman types to cover our uses
|
|
27
|
-
type WatchmanQuery = any;
|
|
28
|
-
|
|
29
30
|
type WatchmanRoots = Map<string, Array<string>>;
|
|
30
31
|
|
|
31
|
-
type WatchmanListCapabilitiesResponse = {
|
|
32
|
-
capabilities: Array<string>,
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
type WatchmanCapabilityCheckResponse = {
|
|
36
|
-
// { 'suffix-set': true }
|
|
37
|
-
capabilities: $ReadOnly<{[string]: boolean}>,
|
|
38
|
-
// '2021.06.07.00'
|
|
39
|
-
version: string,
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
type WatchmanWatchProjectResponse = {
|
|
43
|
-
watch: string,
|
|
44
|
-
relative_path: string,
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
type WatchmanQueryResponse = {
|
|
48
|
-
warning?: string,
|
|
49
|
-
is_fresh_instance: boolean,
|
|
50
|
-
version: string,
|
|
51
|
-
clock:
|
|
52
|
-
| string
|
|
53
|
-
| {
|
|
54
|
-
scm: {'mergebase-with': string, mergebase: string},
|
|
55
|
-
clock: string,
|
|
56
|
-
},
|
|
57
|
-
files: Array<{
|
|
58
|
-
name: string,
|
|
59
|
-
exists: boolean,
|
|
60
|
-
mtime_ms: number | {toNumber: () => number},
|
|
61
|
-
size: number,
|
|
62
|
-
'content.sha1hex'?: string,
|
|
63
|
-
}>,
|
|
64
|
-
};
|
|
65
|
-
|
|
66
32
|
const WATCHMAN_WARNING_INITIAL_DELAY_MILLISECONDS = 10000;
|
|
67
33
|
const WATCHMAN_WARNING_INTERVAL_MILLISECONDS = 20000;
|
|
68
34
|
|
|
@@ -75,65 +41,32 @@ function makeWatchmanError(error: Error): Error {
|
|
|
75
41
|
return error;
|
|
76
42
|
}
|
|
77
43
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
): Promise<
|
|
89
|
-
return new Promise((resolve, reject) => {
|
|
90
|
-
client.capabilityCheck(
|
|
91
|
-
// @ts-expect-error: incorrectly typed
|
|
92
|
-
caps,
|
|
93
|
-
(error, response) => {
|
|
94
|
-
if (error != null || response == null) {
|
|
95
|
-
reject(error ?? new Error('capabilityCheck: Response missing'));
|
|
96
|
-
} else {
|
|
97
|
-
resolve(response);
|
|
98
|
-
}
|
|
99
|
-
},
|
|
100
|
-
);
|
|
101
|
-
});
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
module.exports = async function watchmanCrawl(
|
|
105
|
-
options: CrawlerOptions,
|
|
106
|
-
): Promise<{
|
|
44
|
+
module.exports = async function watchmanCrawl({
|
|
45
|
+
abortSignal,
|
|
46
|
+
computeSha1,
|
|
47
|
+
data,
|
|
48
|
+
enableSymlinks,
|
|
49
|
+
extensions,
|
|
50
|
+
ignore,
|
|
51
|
+
rootDir,
|
|
52
|
+
roots,
|
|
53
|
+
perfLogger,
|
|
54
|
+
}: CrawlerOptions): Promise<{
|
|
107
55
|
changedFiles?: FileData,
|
|
108
56
|
removedFiles: FileData,
|
|
109
57
|
hasteMap: InternalData,
|
|
110
58
|
}> {
|
|
111
|
-
|
|
112
|
-
|
|
59
|
+
perfLogger?.point('watchmanCrawl_start');
|
|
60
|
+
|
|
113
61
|
const clocks = data.clocks;
|
|
114
62
|
|
|
115
|
-
perfLogger?.point('watchmanCrawl_start');
|
|
116
63
|
const client = new watchman.Client();
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
perfLogger?.point('watchmanCrawl/negotiateCapabilities_start');
|
|
120
|
-
// https://facebook.github.io/watchman/docs/capabilities.html
|
|
121
|
-
// Check adds about ~28ms
|
|
122
|
-
const capabilities = await capabilityCheck(client, {
|
|
123
|
-
// If a required capability is missing then an error will be thrown,
|
|
124
|
-
// we don't need this assertion, so using optional instead.
|
|
125
|
-
optional: ['suffix-set'],
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
const suffixExpression = capabilities?.capabilities['suffix-set']
|
|
129
|
-
? // If available, use the optimized `suffix-set` operation:
|
|
130
|
-
// https://facebook.github.io/watchman/docs/expr/suffix.html#suffix-set
|
|
131
|
-
['suffix', extensions]
|
|
132
|
-
: ['anyof', ...extensions.map(extension => ['suffix', extension])];
|
|
64
|
+
abortSignal?.addEventListener('abort', () => client.end());
|
|
133
65
|
|
|
134
66
|
let clientError;
|
|
135
|
-
|
|
136
|
-
|
|
67
|
+
client.on('error', error => {
|
|
68
|
+
clientError = makeWatchmanError(error);
|
|
69
|
+
});
|
|
137
70
|
|
|
138
71
|
let didLogWatchmanWaitMessage = false;
|
|
139
72
|
|
|
@@ -163,18 +96,6 @@ module.exports = async function watchmanCrawl(
|
|
|
163
96
|
}
|
|
164
97
|
};
|
|
165
98
|
|
|
166
|
-
if (options.computeSha1) {
|
|
167
|
-
const {capabilities} = await cmd<WatchmanListCapabilitiesResponse>(
|
|
168
|
-
'list-capabilities',
|
|
169
|
-
);
|
|
170
|
-
|
|
171
|
-
if (capabilities.indexOf('field-content.sha1hex') !== -1) {
|
|
172
|
-
fields.push('content.sha1hex');
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
perfLogger?.point('watchmanCrawl/negotiateCapabilities_end');
|
|
177
|
-
|
|
178
99
|
async function getWatchmanRoots(
|
|
179
100
|
roots: $ReadOnlyArray<Path>,
|
|
180
101
|
): Promise<WatchmanRoots> {
|
|
@@ -183,7 +104,7 @@ module.exports = async function watchmanCrawl(
|
|
|
183
104
|
await Promise.all(
|
|
184
105
|
roots.map(async (root, index) => {
|
|
185
106
|
perfLogger?.point(`watchmanCrawl/watchProject_${index}_start`);
|
|
186
|
-
const response = await cmd<
|
|
107
|
+
const response = await cmd<WatchmanWatchResponse>(
|
|
187
108
|
'watch-project',
|
|
188
109
|
root,
|
|
189
110
|
);
|
|
@@ -235,61 +156,13 @@ module.exports = async function watchmanCrawl(
|
|
|
235
156
|
},
|
|
236
157
|
});
|
|
237
158
|
|
|
238
|
-
const query
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
],
|
|
246
|
-
};
|
|
247
|
-
|
|
248
|
-
/**
|
|
249
|
-
* Watchman "query planner".
|
|
250
|
-
*
|
|
251
|
-
* Watchman file queries consist of 1 or more generators that feed
|
|
252
|
-
* files through the expression evaluator.
|
|
253
|
-
*
|
|
254
|
-
* Strategy:
|
|
255
|
-
* 1. Select the narrowest possible generator so that the expression
|
|
256
|
-
* evaluator has fewer candidates to process.
|
|
257
|
-
* 2. Evaluate expressions from narrowest to broadest.
|
|
258
|
-
* 3. Don't use an expression to recheck a condition that the
|
|
259
|
-
* generator already guarantees.
|
|
260
|
-
* 4. Compose expressions to avoid combinatorial explosions in the
|
|
261
|
-
* number of terms.
|
|
262
|
-
*
|
|
263
|
-
* The ordering of generators/filters, from narrow to broad, is:
|
|
264
|
-
* - since = O(changes)
|
|
265
|
-
* - glob / dirname = O(files in a subtree of the repo)
|
|
266
|
-
* - suffix = O(files in the repo)
|
|
267
|
-
*
|
|
268
|
-
* We assume that file extensions are ~uniformly distributed in the
|
|
269
|
-
* repo but Haste map projects are focused on a handful of
|
|
270
|
-
* directories. Therefore `glob` < `suffix`.
|
|
271
|
-
*/
|
|
272
|
-
let queryGenerator: ?string;
|
|
273
|
-
if (since != null) {
|
|
274
|
-
// Use the `since` generator and filter by both path and extension.
|
|
275
|
-
query.since = since;
|
|
276
|
-
queryGenerator = 'since';
|
|
277
|
-
query.expression.push(
|
|
278
|
-
['anyof', ...directoryFilters.map(dir => ['dirname', dir])],
|
|
279
|
-
suffixExpression,
|
|
280
|
-
);
|
|
281
|
-
} else if (directoryFilters.length > 0) {
|
|
282
|
-
// Use the `glob` generator and filter only by extension.
|
|
283
|
-
query.glob = directoryFilters.map(directory => `${directory}/**`);
|
|
284
|
-
query.glob_includedotfiles = true;
|
|
285
|
-
queryGenerator = 'glob';
|
|
286
|
-
|
|
287
|
-
query.expression.push(suffixExpression);
|
|
288
|
-
} else {
|
|
289
|
-
// Use the `suffix` generator with no path/extension filtering.
|
|
290
|
-
query.suffix = extensions;
|
|
291
|
-
queryGenerator = 'suffix';
|
|
292
|
-
}
|
|
159
|
+
const {query, queryGenerator} = planQuery({
|
|
160
|
+
since,
|
|
161
|
+
extensions,
|
|
162
|
+
directoryFilters,
|
|
163
|
+
includeSha1: computeSha1,
|
|
164
|
+
includeSymlinks: enableSymlinks,
|
|
165
|
+
});
|
|
293
166
|
|
|
294
167
|
perfLogger?.annotate({
|
|
295
168
|
string: {
|
|
@@ -392,6 +265,10 @@ module.exports = async function watchmanCrawl(
|
|
|
392
265
|
);
|
|
393
266
|
|
|
394
267
|
for (const fileData of response.files) {
|
|
268
|
+
if (fileData.symlink_target != null) {
|
|
269
|
+
// TODO: Process symlinks
|
|
270
|
+
continue;
|
|
271
|
+
}
|
|
395
272
|
const filePath = fsRoot + path.sep + normalizePathSep(fileData.name);
|
|
396
273
|
const relativeFilePath = fastPath.relative(rootDir, filePath);
|
|
397
274
|
const existingFileData = data.files.get(relativeFilePath);
|
|
@@ -414,11 +291,13 @@ module.exports = async function watchmanCrawl(
|
|
|
414
291
|
}
|
|
415
292
|
}
|
|
416
293
|
} else if (!ignore(filePath)) {
|
|
294
|
+
const {mtime_ms, size} = fileData;
|
|
295
|
+
invariant(
|
|
296
|
+
mtime_ms != null && size != null,
|
|
297
|
+
'missing file data in watchman response',
|
|
298
|
+
);
|
|
417
299
|
const mtime =
|
|
418
|
-
typeof
|
|
419
|
-
? fileData.mtime_ms
|
|
420
|
-
: fileData.mtime_ms.toNumber();
|
|
421
|
-
const size = fileData.size;
|
|
300
|
+
typeof mtime_ms === 'number' ? mtime_ms : mtime_ms.toNumber();
|
|
422
301
|
|
|
423
302
|
let sha1hex = fileData['content.sha1hex'];
|
|
424
303
|
if (typeof sha1hex !== 'string' || sha1hex.length !== 40) {
|