@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.
Files changed (82) hide show
  1. package/README.md +1 -0
  2. package/build/Watcher.d.ts +53 -0
  3. package/build/Watcher.js +249 -0
  4. package/build/cache/DiskCacheManager.d.ts +25 -0
  5. package/build/cache/DiskCacheManager.js +110 -0
  6. package/build/constants.d.ts +23 -0
  7. package/build/constants.js +27 -0
  8. package/build/crawlers/node/fallback.d.ts +20 -0
  9. package/build/crawlers/node/fallback.js +148 -0
  10. package/build/crawlers/node/index.d.ts +10 -0
  11. package/build/crawlers/node/index.js +152 -0
  12. package/build/crawlers/watchman/index.d.ts +8 -0
  13. package/build/crawlers/watchman/index.js +288 -0
  14. package/build/crawlers/watchman/planQuery.d.ts +31 -0
  15. package/build/crawlers/watchman/planQuery.js +99 -0
  16. package/build/index.d.ts +140 -0
  17. package/build/index.js +836 -0
  18. package/build/lib/FileProcessor.d.ts +42 -0
  19. package/build/lib/FileProcessor.js +180 -0
  20. package/build/lib/FileSystemChangeAggregator.d.ts +18 -0
  21. package/build/lib/FileSystemChangeAggregator.js +114 -0
  22. package/build/lib/RootPathUtils.d.ts +24 -0
  23. package/build/lib/RootPathUtils.js +311 -0
  24. package/build/lib/TreeFS.d.ts +161 -0
  25. package/build/lib/TreeFS.js +1006 -0
  26. package/build/lib/checkWatchmanCapabilities.d.ts +9 -0
  27. package/build/lib/checkWatchmanCapabilities.js +50 -0
  28. package/build/lib/normalizePathSeparatorsToPosix.d.ts +8 -0
  29. package/build/lib/normalizePathSeparatorsToPosix.js +20 -0
  30. package/build/lib/normalizePathSeparatorsToSystem.d.ts +8 -0
  31. package/build/lib/normalizePathSeparatorsToSystem.js +20 -0
  32. package/build/lib/removeOverlappingRoots.d.ts +10 -0
  33. package/build/lib/removeOverlappingRoots.js +37 -0
  34. package/build/lib/rootRelativeCacheKeys.d.ts +11 -0
  35. package/build/lib/rootRelativeCacheKeys.js +56 -0
  36. package/build/lib/sorting.d.ts +8 -0
  37. package/build/lib/sorting.js +31 -0
  38. package/build/plugins/DependencyPlugin.d.ts +23 -0
  39. package/build/plugins/DependencyPlugin.js +61 -0
  40. package/build/plugins/FileDataPlugin.d.ts +28 -0
  41. package/build/plugins/FileDataPlugin.js +46 -0
  42. package/build/plugins/HastePlugin.d.ts +32 -0
  43. package/build/plugins/HastePlugin.js +358 -0
  44. package/build/plugins/MockPlugin.d.ts +27 -0
  45. package/build/plugins/MockPlugin.js +157 -0
  46. package/build/plugins/dependencies/dependencyExtractor.d.ts +7 -0
  47. package/build/plugins/dependencies/dependencyExtractor.js +66 -0
  48. package/build/plugins/dependencies/worker.d.ts +18 -0
  49. package/build/plugins/dependencies/worker.js +30 -0
  50. package/build/plugins/haste/DuplicateHasteCandidatesError.d.ts +14 -0
  51. package/build/plugins/haste/DuplicateHasteCandidatesError.js +51 -0
  52. package/build/plugins/haste/HasteConflictsError.d.ts +12 -0
  53. package/build/plugins/haste/HasteConflictsError.js +49 -0
  54. package/build/plugins/haste/computeConflicts.d.ts +19 -0
  55. package/build/plugins/haste/computeConflicts.js +74 -0
  56. package/build/plugins/haste/getPlatformExtension.d.ts +7 -0
  57. package/build/plugins/haste/getPlatformExtension.js +19 -0
  58. package/build/plugins/haste/worker.d.ts +16 -0
  59. package/build/plugins/haste/worker.js +48 -0
  60. package/build/plugins/mocks/getMockName.d.ts +8 -0
  61. package/build/plugins/mocks/getMockName.js +17 -0
  62. package/build/ts-declarations/fb-watchman.d.ts +20 -0
  63. package/build/ts-declarations/fb-watchman.js +2 -0
  64. package/build/types.d.ts +404 -0
  65. package/build/types.js +8 -0
  66. package/build/watchers/AbstractWatcher.d.ts +30 -0
  67. package/build/watchers/AbstractWatcher.js +97 -0
  68. package/build/watchers/FallbackWatcher.d.ts +18 -0
  69. package/build/watchers/FallbackWatcher.js +411 -0
  70. package/build/watchers/NativeWatcher.d.ts +39 -0
  71. package/build/watchers/NativeWatcher.js +156 -0
  72. package/build/watchers/RecrawlWarning.d.ts +18 -0
  73. package/build/watchers/RecrawlWarning.js +59 -0
  74. package/build/watchers/WatchmanWatcher.d.ts +22 -0
  75. package/build/watchers/WatchmanWatcher.js +263 -0
  76. package/build/watchers/common.d.ts +39 -0
  77. package/build/watchers/common.js +60 -0
  78. package/build/worker.d.ts +24 -0
  79. package/build/worker.js +80 -0
  80. package/build/workerExclusionList.d.ts +8 -0
  81. package/build/workerExclusionList.js +57 -0
  82. package/package.json +54 -0
@@ -0,0 +1,152 @@
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
+ Object.defineProperty(exports, "__esModule", { value: true });
44
+ exports.default = nodeCrawl;
45
+ const fs = __importStar(require("fs"));
46
+ const path = __importStar(require("path"));
47
+ const RootPathUtils_1 = require("../../lib/RootPathUtils");
48
+ function find(roots, extensions, ignore, includeSymlinks, rootDir, console, previousFileSystem, callback) {
49
+ const result = new Map();
50
+ let activeCalls = 0;
51
+ const pathUtils = new RootPathUtils_1.RootPathUtils(rootDir);
52
+ const exts = extensions.reduce((acc, ext) => {
53
+ acc[ext] = true;
54
+ return acc;
55
+ }, {});
56
+ function search(directory, dirNormal, isWithinRoot) {
57
+ activeCalls++;
58
+ fs.readdir(directory, { withFileTypes: true }, (err, entries) => {
59
+ activeCalls--;
60
+ if (err) {
61
+ console.warn(`Error "${err.code ?? err.message}" reading contents of "${directory}", skipping. Add this directory to your ignore list to exclude it.`);
62
+ }
63
+ else {
64
+ for (let idx = 0; idx < entries.length; idx++) {
65
+ const entry = entries[idx];
66
+ const name = entry.name.toString();
67
+ const file = directory + path.sep + name;
68
+ const isSymbolicLink = entry.isSymbolicLink();
69
+ if (ignore(file) || (!includeSymlinks && isSymbolicLink)) {
70
+ continue;
71
+ }
72
+ // Deriving a normal path above the root dir requires slicing off an up-fragment
73
+ // then checking if the target matches the next segment of the root dir. It's therefore
74
+ // easier to fall back to `pathUtils.absoluteToNormal`
75
+ const childNormal = !isWithinRoot
76
+ ? pathUtils.absoluteToNormal(file)
77
+ : dirNormal === ''
78
+ ? name
79
+ : dirNormal + path.sep + name;
80
+ if (entry.isDirectory()) {
81
+ search(file, childNormal, isWithinRoot || childNormal === '');
82
+ continue;
83
+ }
84
+ const ext = path.extname(file).substr(1);
85
+ if (!isSymbolicLink && !exts[ext]) {
86
+ continue;
87
+ }
88
+ const mtime = previousFileSystem?.getMtimeByNormalPath(childNormal);
89
+ if (mtime == null || mtime === 0) {
90
+ // When we're in a cold start or a previous file doesn't exist, we can skip
91
+ // the mtime/size lstat now and treat the file as new
92
+ result.set(childNormal, [null, 0, 0, null, isSymbolicLink ? 1 : 0, null]);
93
+ }
94
+ else {
95
+ activeCalls++;
96
+ fs.lstat(file, (err, stat) => {
97
+ activeCalls--;
98
+ if (!err && stat) {
99
+ result.set(childNormal, [
100
+ stat.mtime.getTime(),
101
+ stat.size,
102
+ 0,
103
+ null,
104
+ isSymbolicLink ? 1 : 0,
105
+ null,
106
+ ]);
107
+ }
108
+ if (activeCalls === 0) {
109
+ callback(result);
110
+ }
111
+ });
112
+ }
113
+ }
114
+ }
115
+ if (activeCalls === 0) {
116
+ callback(result);
117
+ }
118
+ });
119
+ }
120
+ if (roots.length > 0) {
121
+ for (const root of roots) {
122
+ const rootNormal = pathUtils.absoluteToNormal(root);
123
+ const isWithinRoot = !rootNormal.startsWith('..' + path.sep);
124
+ search(root, rootNormal, isWithinRoot);
125
+ }
126
+ }
127
+ else {
128
+ callback(result);
129
+ }
130
+ }
131
+ async function nodeCrawl(options) {
132
+ const { console, previousState, extensions, ignore, rootDir, includeSymlinks, perfLogger, roots, abortSignal, subpath, } = options;
133
+ abortSignal?.throwIfAborted();
134
+ perfLogger?.point('nodeCrawl_start');
135
+ return new Promise((resolve, reject) => {
136
+ const callback = (fileData) => {
137
+ const difference = previousState.fileSystem.getDifference(fileData, {
138
+ subpath,
139
+ });
140
+ perfLogger?.point('nodeCrawl_end');
141
+ try {
142
+ // TODO: Use AbortSignal.reason directly when Flow supports it
143
+ abortSignal?.throwIfAborted();
144
+ }
145
+ catch (e) {
146
+ reject(e);
147
+ }
148
+ resolve(difference);
149
+ };
150
+ find(roots, extensions, ignore, includeSymlinks, rootDir, console, previousState.fileSystem, callback);
151
+ });
152
+ }
@@ -0,0 +1,8 @@
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 { CrawlerOptions, CrawlResult } from '../../types';
8
+ export default function watchmanCrawl({ abortSignal, computeSha1, extensions, ignore, includeSymlinks, onStatus, perfLogger, previousState, rootDir, roots, }: CrawlerOptions): Promise<CrawlResult>;
@@ -0,0 +1,288 @@
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
+ var __importDefault = (this && this.__importDefault) || function (mod) {
42
+ return (mod && mod.__esModule) ? mod : { "default": mod };
43
+ };
44
+ Object.defineProperty(exports, "__esModule", { value: true });
45
+ exports.default = watchmanCrawl;
46
+ const fb_watchman_1 = __importDefault(require("fb-watchman"));
47
+ const invariant_1 = __importDefault(require("invariant"));
48
+ const path = __importStar(require("path"));
49
+ const perf_hooks_1 = require("perf_hooks");
50
+ const planQuery_1 = require("./planQuery");
51
+ const RootPathUtils_1 = require("../../lib/RootPathUtils");
52
+ const normalizePathSeparatorsToPosix_1 = __importDefault(require("../../lib/normalizePathSeparatorsToPosix"));
53
+ const normalizePathSeparatorsToSystem_1 = __importDefault(require("../../lib/normalizePathSeparatorsToSystem"));
54
+ const WATCHMAN_WARNING_INITIAL_DELAY_MILLISECONDS = 10000;
55
+ const WATCHMAN_WARNING_INTERVAL_MILLISECONDS = 20000;
56
+ const watchmanURL = 'https://facebook.github.io/watchman/docs/troubleshooting';
57
+ function makeWatchmanError(error) {
58
+ error.message =
59
+ `Watchman error: ${error.message.trim()}. Make sure watchman ` +
60
+ `is running for this project. See ${watchmanURL}.`;
61
+ return error;
62
+ }
63
+ async function watchmanCrawl({ abortSignal, computeSha1, extensions, ignore, includeSymlinks, onStatus, perfLogger, previousState, rootDir, roots, }) {
64
+ abortSignal?.throwIfAborted();
65
+ const client = new fb_watchman_1.default.Client();
66
+ const pathUtils = new RootPathUtils_1.RootPathUtils(rootDir);
67
+ abortSignal?.addEventListener('abort', () => client.end());
68
+ perfLogger?.point('watchmanCrawl_start');
69
+ const newClocks = new Map();
70
+ let clientError;
71
+ client.on('error', (error) => {
72
+ clientError = makeWatchmanError(error);
73
+ });
74
+ // TODO: Fix to use fb-watchman types
75
+ const cmd = async (command, ...args) => {
76
+ let didLogWatchmanWaitMessage = false;
77
+ const startTime = perf_hooks_1.performance.now();
78
+ const logWatchmanWaitMessage = () => {
79
+ didLogWatchmanWaitMessage = true;
80
+ onStatus({
81
+ type: 'watchman_slow_command',
82
+ timeElapsed: perf_hooks_1.performance.now() - startTime,
83
+ command,
84
+ });
85
+ };
86
+ let intervalOrTimeoutId = setTimeout(() => {
87
+ logWatchmanWaitMessage();
88
+ intervalOrTimeoutId = setInterval(logWatchmanWaitMessage, WATCHMAN_WARNING_INTERVAL_MILLISECONDS);
89
+ }, WATCHMAN_WARNING_INITIAL_DELAY_MILLISECONDS);
90
+ try {
91
+ const response = await new Promise((resolve, reject) => {
92
+ // NOTE: dynamic call of command
93
+ return client.command([command, ...args], (error, result) => error ? reject(makeWatchmanError(error)) : resolve(result));
94
+ });
95
+ if ('warning' in response) {
96
+ onStatus({
97
+ type: 'watchman_warning',
98
+ warning: response.warning,
99
+ command,
100
+ });
101
+ }
102
+ return response;
103
+ }
104
+ finally {
105
+ // NOTE: clearInterval / clearTimeout are interchangeable
106
+ clearInterval(intervalOrTimeoutId);
107
+ if (didLogWatchmanWaitMessage) {
108
+ onStatus({
109
+ type: 'watchman_slow_command_complete',
110
+ timeElapsed: perf_hooks_1.performance.now() - startTime,
111
+ command,
112
+ });
113
+ }
114
+ }
115
+ };
116
+ async function getWatchmanRoots(roots) {
117
+ perfLogger?.point('watchmanCrawl/getWatchmanRoots_start');
118
+ const watchmanRoots = new Map();
119
+ await Promise.all(roots.map(async (root, index) => {
120
+ perfLogger?.point(`watchmanCrawl/watchProject_${index}_start`);
121
+ const response = await cmd('watch-project', root);
122
+ perfLogger?.point(`watchmanCrawl/watchProject_${index}_end`);
123
+ const existing = watchmanRoots.get(response.watch);
124
+ // A root can only be filtered if it was never seen with a
125
+ // relative_path before.
126
+ const canBeFiltered = !existing || existing.directoryFilters.length > 0;
127
+ if (canBeFiltered) {
128
+ if (response.relative_path) {
129
+ watchmanRoots.set(response.watch, {
130
+ watcher: response.watcher,
131
+ directoryFilters: (existing?.directoryFilters || []).concat(response.relative_path),
132
+ });
133
+ }
134
+ else {
135
+ // Make the filter directories an empty array to signal that this
136
+ // root was already seen and needs to be watched for all files or
137
+ // directories.
138
+ watchmanRoots.set(response.watch, {
139
+ watcher: response.watcher,
140
+ directoryFilters: [],
141
+ });
142
+ }
143
+ }
144
+ }));
145
+ perfLogger?.point('watchmanCrawl/getWatchmanRoots_end');
146
+ return watchmanRoots;
147
+ }
148
+ async function queryWatchmanForDirs(rootProjectDirMappings) {
149
+ perfLogger?.point('watchmanCrawl/queryWatchmanForDirs_start');
150
+ const results = new Map();
151
+ let isFresh = false;
152
+ await Promise.all(Array.from(rootProjectDirMappings).map(async ([posixSeparatedRoot, { directoryFilters, watcher }], index) => {
153
+ // Jest is only going to store one type of clock; a string that
154
+ // represents a local clock. However, the Watchman crawler supports
155
+ // a second type of clock that can be written by automation outside of
156
+ // Jest, called an "scm query", which fetches changed files based on
157
+ // source control mergebases. The reason this is necessary is because
158
+ // local clocks are not portable across systems, but scm queries are.
159
+ // By using scm queries, we can create the haste map on a different
160
+ // system and import it, transforming the clock into a local clock.
161
+ const since = previousState.clocks.get((0, normalizePathSeparatorsToPosix_1.default)(pathUtils.absoluteToNormal((0, normalizePathSeparatorsToSystem_1.default)(posixSeparatedRoot))));
162
+ perfLogger?.annotate({
163
+ bool: {
164
+ [`watchmanCrawl/query_${index}_has_clock`]: since != null,
165
+ },
166
+ });
167
+ const { query, queryGenerator } = (0, planQuery_1.planQuery)({
168
+ since,
169
+ extensions,
170
+ directoryFilters,
171
+ includeSha1: computeSha1,
172
+ includeSymlinks,
173
+ });
174
+ perfLogger?.annotate({
175
+ string: {
176
+ [`watchmanCrawl/query_${index}_watcher`]: watcher ?? 'unknown',
177
+ [`watchmanCrawl/query_${index}_generator`]: queryGenerator,
178
+ },
179
+ });
180
+ perfLogger?.point(`watchmanCrawl/query_${index}_start`);
181
+ const response = await cmd('query', posixSeparatedRoot, query);
182
+ perfLogger?.point(`watchmanCrawl/query_${index}_end`);
183
+ // When a source-control query is used, we ignore the "is fresh"
184
+ // response from Watchman because it will be true despite the query
185
+ // being incremental.
186
+ const isSourceControlQuery = typeof since !== 'string' && since?.scm?.['mergebase-with'] != null;
187
+ if (!isSourceControlQuery) {
188
+ isFresh = isFresh || response.is_fresh_instance;
189
+ }
190
+ results.set(posixSeparatedRoot, response);
191
+ }));
192
+ perfLogger?.point('watchmanCrawl/queryWatchmanForDirs_end');
193
+ return {
194
+ isFresh,
195
+ results,
196
+ };
197
+ }
198
+ let removedFiles = new Set();
199
+ let changedFiles = new Map();
200
+ let results;
201
+ let isFresh = false;
202
+ let queryError;
203
+ try {
204
+ const watchmanRoots = await getWatchmanRoots(roots);
205
+ const watchmanFileResults = await queryWatchmanForDirs(watchmanRoots);
206
+ results = watchmanFileResults.results;
207
+ isFresh = watchmanFileResults.isFresh;
208
+ }
209
+ catch (e) {
210
+ queryError = e;
211
+ }
212
+ client.end();
213
+ if (results == null) {
214
+ if (clientError) {
215
+ perfLogger?.annotate({
216
+ string: {
217
+ 'watchmanCrawl/client_error': clientError.message ?? '[message missing]',
218
+ },
219
+ });
220
+ }
221
+ if (queryError) {
222
+ perfLogger?.annotate({
223
+ string: {
224
+ 'watchmanCrawl/query_error': queryError.message ?? '[message missing]',
225
+ },
226
+ });
227
+ }
228
+ perfLogger?.point('watchmanCrawl_end');
229
+ abortSignal?.throwIfAborted();
230
+ throw queryError ?? clientError ?? new Error('Watchman file results missing');
231
+ }
232
+ perfLogger?.point('watchmanCrawl/processResults_start');
233
+ const freshFileData = new Map();
234
+ for (const [watchRoot, response] of results) {
235
+ const fsRoot = (0, normalizePathSeparatorsToSystem_1.default)(watchRoot);
236
+ const relativeFsRoot = pathUtils.absoluteToNormal(fsRoot);
237
+ newClocks.set((0, normalizePathSeparatorsToPosix_1.default)(relativeFsRoot),
238
+ // Ensure we persist only the local clock.
239
+ typeof response.clock === 'string' ? response.clock : response.clock.clock);
240
+ for (const fileData of response.files) {
241
+ const filePath = fsRoot + path.sep + (0, normalizePathSeparatorsToSystem_1.default)(fileData.name);
242
+ const relativeFilePath = pathUtils.absoluteToNormal(filePath);
243
+ if (!fileData.exists) {
244
+ if (!isFresh) {
245
+ removedFiles.add(relativeFilePath);
246
+ }
247
+ // Whether watchman can return exists: false in a fresh instance
248
+ // response is unknown, but there's nothing we need to do in that case.
249
+ }
250
+ else if (!ignore(filePath)) {
251
+ const { mtime_ms, size } = fileData;
252
+ (0, invariant_1.default)(mtime_ms != null && size != null, 'missing file data in watchman response');
253
+ const mtime = typeof mtime_ms === 'number' ? mtime_ms : mtime_ms.toNumber();
254
+ let sha1hex = fileData['content.sha1hex'];
255
+ if (typeof sha1hex !== 'string' || sha1hex.length !== 40) {
256
+ sha1hex = undefined;
257
+ }
258
+ let symlinkInfo = 0;
259
+ if (fileData.type === 'l') {
260
+ symlinkInfo = fileData['symlink_target'] ?? 1;
261
+ }
262
+ if (typeof symlinkInfo === 'string') {
263
+ symlinkInfo = (0, normalizePathSeparatorsToPosix_1.default)(pathUtils.resolveSymlinkToNormal(relativeFilePath, symlinkInfo));
264
+ }
265
+ const nextData = [mtime, size, 0, sha1hex ?? null, symlinkInfo, null];
266
+ // If watchman is fresh, the removed files map starts with all files
267
+ // and we remove them as we verify they still exist.
268
+ if (isFresh) {
269
+ freshFileData.set(relativeFilePath, nextData);
270
+ }
271
+ else {
272
+ changedFiles.set(relativeFilePath, nextData);
273
+ }
274
+ }
275
+ }
276
+ }
277
+ if (isFresh) {
278
+ ({ changedFiles, removedFiles } = previousState.fileSystem.getDifference(freshFileData));
279
+ }
280
+ perfLogger?.point('watchmanCrawl/processResults_end');
281
+ perfLogger?.point('watchmanCrawl_end');
282
+ abortSignal?.throwIfAborted();
283
+ return {
284
+ changedFiles,
285
+ removedFiles,
286
+ clocks: newClocks,
287
+ };
288
+ }
@@ -0,0 +1,31 @@
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
+ type WatchmanExpression = readonly [string, ...any[]];
8
+ type WatchmanQuerySince = string | Readonly<{
9
+ scm: Readonly<{
10
+ 'mergebase-with': string;
11
+ }>;
12
+ }>;
13
+ interface WatchmanQuery {
14
+ fields?: string[];
15
+ expression?: WatchmanExpression;
16
+ since?: WatchmanQuerySince;
17
+ glob?: string[];
18
+ glob_includedotfiles?: boolean;
19
+ suffix?: readonly string[];
20
+ }
21
+ export declare function planQuery({ since, directoryFilters, extensions, includeSha1, includeSymlinks, }: {
22
+ readonly since: WatchmanQuerySince | null | undefined;
23
+ readonly directoryFilters: readonly string[];
24
+ readonly extensions: readonly string[];
25
+ readonly includeSha1: boolean;
26
+ readonly includeSymlinks: boolean;
27
+ }): {
28
+ query: WatchmanQuery;
29
+ queryGenerator: string;
30
+ };
31
+ export {};
@@ -0,0 +1,99 @@
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
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.planQuery = planQuery;
10
+ function planQuery({ since, directoryFilters, extensions, includeSha1, includeSymlinks, }) {
11
+ const fields = ['name', 'exists', 'mtime_ms', 'size'];
12
+ if (includeSha1) {
13
+ fields.push('content.sha1hex');
14
+ }
15
+ /**
16
+ * Note on symlink_target:
17
+ *
18
+ * Watchman supports requesting the symlink_target field, which is
19
+ * *potentially* more efficient if targets can be read from metadata without
20
+ * reading/materialising files. However, at the time of writing, Watchman has
21
+ * issues reporting symlink_target on some backends[1]. Additionally, though
22
+ * the Eden watcher is known to work, it reads links serially[2] on demand[3]
23
+ * - less efficiently than we can do ourselves.
24
+ * [1] https://github.com/facebook/watchman/issues/1084
25
+ * [2] https://github.com/facebook/watchman/blob/v2023.01.02.00/watchman/watcher/eden.cpp#L476-L485
26
+ * [3] https://github.com/facebook/watchman/blob/v2023.01.02.00/watchman/watcher/eden.cpp#L433-L434
27
+ */
28
+ if (includeSymlinks) {
29
+ fields.push('type');
30
+ }
31
+ const allOfTerms = includeSymlinks
32
+ ? [['anyof', ['allof', ['type', 'f'], ['suffix', extensions]], ['type', 'l']]]
33
+ : [['type', 'f']];
34
+ const query = { fields };
35
+ /**
36
+ * Watchman "query planner".
37
+ *
38
+ * Watchman file queries consist of 1 or more generators that feed
39
+ * files through the expression evaluator.
40
+ *
41
+ * Strategy:
42
+ * 1. Select the narrowest possible generator so that the expression
43
+ * evaluator has fewer candidates to process.
44
+ * 2. Evaluate expressions from narrowest to broadest.
45
+ * 3. Don't use an expression to recheck a condition that the
46
+ * generator already guarantees.
47
+ * 4. Compose expressions to avoid combinatorial explosions in the
48
+ * number of terms.
49
+ *
50
+ * The ordering of generators/filters, from narrow to broad, is:
51
+ * - since = O(changes)
52
+ * - glob / dirname = O(files in a subtree of the repo)
53
+ * - suffix = O(files in the repo)
54
+ *
55
+ * We assume that file extensions are ~uniformly distributed in the
56
+ * repo but Haste map projects are focused on a handful of
57
+ * directories. Therefore `glob` < `suffix`.
58
+ */
59
+ let queryGenerator;
60
+ if (since != null) {
61
+ // Prefer the since generator whenever we have a clock
62
+ query.since = since;
63
+ queryGenerator = 'since';
64
+ // Filter on directories using an anyof expression
65
+ if (directoryFilters.length > 0) {
66
+ allOfTerms.push([
67
+ 'anyof',
68
+ ...directoryFilters.map((dir) => ['dirname', dir]),
69
+ ]);
70
+ }
71
+ }
72
+ else if (directoryFilters.length > 0) {
73
+ // Use the `glob` generator and filter only by extension.
74
+ query.glob = directoryFilters.map((directory) => `${directory}/**`);
75
+ query.glob_includedotfiles = true;
76
+ queryGenerator = 'glob';
77
+ }
78
+ else if (!includeSymlinks) {
79
+ // Use the `suffix` generator with no path/extension filtering, as long
80
+ // as we don't need (suffixless) directory symlinks.
81
+ query.suffix = extensions;
82
+ queryGenerator = 'suffix';
83
+ }
84
+ else {
85
+ // Fall back to `all` if we need symlinks and don't have a clock or
86
+ // directory filters.
87
+ queryGenerator = 'all';
88
+ }
89
+ // `includeSymlinks` implies we need (suffixless) directory links. In the
90
+ // case of the `suffix` generator, a suffix expression would be redundant.
91
+ if (!includeSymlinks && queryGenerator !== 'suffix') {
92
+ allOfTerms.push(['suffix', extensions]);
93
+ }
94
+ // If we only have one "all of" expression we can use it directly, otherwise
95
+ // wrap in ['allof', ...expressions]. By construction we should never have
96
+ // length 0.
97
+ query.expression = allOfTerms.length === 1 ? allOfTerms[0] : ['allof', ...allOfTerms];
98
+ return { query, queryGenerator };
99
+ }