@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
package/README.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# `@expo/metro-file-map`
|
|
@@ -0,0 +1,53 @@
|
|
|
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 EventEmitter from 'events';
|
|
8
|
+
import type { Console, CrawlerOptions, CrawlResult, PerfLogger, WatcherBackendChangeEvent } from './types';
|
|
9
|
+
interface WatcherOptions {
|
|
10
|
+
abortSignal: AbortSignal;
|
|
11
|
+
computeSha1: boolean;
|
|
12
|
+
console: Console;
|
|
13
|
+
enableSymlinks: boolean;
|
|
14
|
+
extensions: readonly string[];
|
|
15
|
+
/** @deprecated */
|
|
16
|
+
forceNodeFilesystemAPI?: boolean;
|
|
17
|
+
healthCheckFilePrefix: string;
|
|
18
|
+
ignoreForCrawl: (filePath: string) => boolean;
|
|
19
|
+
ignorePatternForWatch: RegExp;
|
|
20
|
+
previousState: CrawlerOptions['previousState'];
|
|
21
|
+
perfLogger: PerfLogger | undefined | null;
|
|
22
|
+
roots: readonly string[];
|
|
23
|
+
rootDir: string;
|
|
24
|
+
useWatchman: boolean;
|
|
25
|
+
watch: boolean;
|
|
26
|
+
watchmanDeferStates: readonly string[];
|
|
27
|
+
}
|
|
28
|
+
export type HealthCheckResult = {
|
|
29
|
+
type: 'error';
|
|
30
|
+
timeout: number;
|
|
31
|
+
error: Error;
|
|
32
|
+
watcher: string | undefined | null;
|
|
33
|
+
} | {
|
|
34
|
+
type: 'success';
|
|
35
|
+
timeout: number;
|
|
36
|
+
timeElapsed: number;
|
|
37
|
+
watcher: string | undefined | null;
|
|
38
|
+
} | {
|
|
39
|
+
type: 'timeout';
|
|
40
|
+
timeout: number;
|
|
41
|
+
watcher: string | undefined | null;
|
|
42
|
+
pauseReason: string | undefined | null;
|
|
43
|
+
};
|
|
44
|
+
export declare class Watcher extends EventEmitter {
|
|
45
|
+
#private;
|
|
46
|
+
constructor(options: WatcherOptions);
|
|
47
|
+
crawl(): Promise<CrawlResult>;
|
|
48
|
+
recrawl(subpath: string, currentFileSystem: CrawlerOptions['previousState']['fileSystem']): Promise<CrawlResult>;
|
|
49
|
+
watch(onChange: (change: WatcherBackendChangeEvent) => void): Promise<void>;
|
|
50
|
+
close(): Promise<void>;
|
|
51
|
+
checkHealth(timeout: number): Promise<HealthCheckResult>;
|
|
52
|
+
}
|
|
53
|
+
export {};
|
package/build/Watcher.js
ADDED
|
@@ -0,0 +1,249 @@
|
|
|
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.Watcher = void 0;
|
|
13
|
+
const events_1 = __importDefault(require("events"));
|
|
14
|
+
const fs_1 = __importDefault(require("fs"));
|
|
15
|
+
const path_1 = __importDefault(require("path"));
|
|
16
|
+
const perf_hooks_1 = require("perf_hooks");
|
|
17
|
+
const node_1 = __importDefault(require("./crawlers/node"));
|
|
18
|
+
const watchman_1 = __importDefault(require("./crawlers/watchman"));
|
|
19
|
+
const FallbackWatcher_1 = __importDefault(require("./watchers/FallbackWatcher"));
|
|
20
|
+
const NativeWatcher_1 = __importDefault(require("./watchers/NativeWatcher"));
|
|
21
|
+
const WatchmanWatcher_1 = __importDefault(require("./watchers/WatchmanWatcher"));
|
|
22
|
+
const common_1 = require("./watchers/common");
|
|
23
|
+
const debug = require('debug')('Metro:Watcher');
|
|
24
|
+
const MAX_WAIT_TIME = 240000;
|
|
25
|
+
let nextInstanceId = 0;
|
|
26
|
+
class Watcher extends events_1.default {
|
|
27
|
+
#activeWatcher;
|
|
28
|
+
#backends = [];
|
|
29
|
+
#instanceId;
|
|
30
|
+
#nextHealthCheckId = 0;
|
|
31
|
+
#options;
|
|
32
|
+
#pendingHealthChecks = new Map();
|
|
33
|
+
constructor(options) {
|
|
34
|
+
super();
|
|
35
|
+
this.#options = options;
|
|
36
|
+
this.#instanceId = nextInstanceId++;
|
|
37
|
+
}
|
|
38
|
+
async crawl() {
|
|
39
|
+
this.#options.perfLogger?.point('crawl_start');
|
|
40
|
+
const options = this.#options;
|
|
41
|
+
const result = await this.#crawl({
|
|
42
|
+
previousState: options.previousState,
|
|
43
|
+
roots: options.roots,
|
|
44
|
+
useWatchman: options.useWatchman,
|
|
45
|
+
});
|
|
46
|
+
this.#options.perfLogger?.point('crawl_end');
|
|
47
|
+
return result;
|
|
48
|
+
}
|
|
49
|
+
async recrawl(subpath, currentFileSystem) {
|
|
50
|
+
return this.#crawl({
|
|
51
|
+
previousState: {
|
|
52
|
+
clocks: new Map(),
|
|
53
|
+
fileSystem: currentFileSystem,
|
|
54
|
+
},
|
|
55
|
+
roots: [path_1.default.join(this.#options.rootDir, subpath)],
|
|
56
|
+
subpath,
|
|
57
|
+
useWatchman: false,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
async #crawl(crawlOptions) {
|
|
61
|
+
const options = this.#options;
|
|
62
|
+
const { useWatchman, subpath } = crawlOptions;
|
|
63
|
+
const ignoreForCrawl = (filePath) => options.ignoreForCrawl(filePath) ||
|
|
64
|
+
path_1.default.basename(filePath).startsWith(this.#options.healthCheckFilePrefix);
|
|
65
|
+
const crawl = useWatchman ? watchman_1.default : node_1.default;
|
|
66
|
+
let crawler = crawl === watchman_1.default ? 'watchman' : 'node';
|
|
67
|
+
options.abortSignal.throwIfAborted();
|
|
68
|
+
const crawlerOptions = {
|
|
69
|
+
abortSignal: options.abortSignal,
|
|
70
|
+
computeSha1: options.computeSha1,
|
|
71
|
+
console: options.console,
|
|
72
|
+
includeSymlinks: options.enableSymlinks,
|
|
73
|
+
extensions: options.extensions,
|
|
74
|
+
forceNodeFilesystemAPI: options.forceNodeFilesystemAPI,
|
|
75
|
+
ignore: ignoreForCrawl,
|
|
76
|
+
onStatus: (status) => {
|
|
77
|
+
this.emit('status', status);
|
|
78
|
+
},
|
|
79
|
+
perfLogger: options.perfLogger,
|
|
80
|
+
previousState: crawlOptions.previousState,
|
|
81
|
+
rootDir: options.rootDir,
|
|
82
|
+
roots: crawlOptions.roots,
|
|
83
|
+
subpath,
|
|
84
|
+
};
|
|
85
|
+
debug('Crawling roots: %s with %s crawler.', crawlOptions.roots, crawler);
|
|
86
|
+
let delta;
|
|
87
|
+
try {
|
|
88
|
+
delta = await crawl(crawlerOptions);
|
|
89
|
+
}
|
|
90
|
+
catch (firstError) {
|
|
91
|
+
if (crawl !== watchman_1.default) {
|
|
92
|
+
throw firstError;
|
|
93
|
+
}
|
|
94
|
+
crawler = 'node';
|
|
95
|
+
options.console.warn('metro-file-map: Watchman crawl failed. Retrying once with node ' +
|
|
96
|
+
'crawler.\n' +
|
|
97
|
+
" Usually this happens when watchman isn't running. Create an " +
|
|
98
|
+
"empty `.watchmanconfig` file in your project's root folder or " +
|
|
99
|
+
'initialize a git or hg repository in your project.\n' +
|
|
100
|
+
' ' +
|
|
101
|
+
firstError.toString());
|
|
102
|
+
try {
|
|
103
|
+
delta = await (0, node_1.default)(crawlerOptions);
|
|
104
|
+
}
|
|
105
|
+
catch (retryError) {
|
|
106
|
+
throw new Error('Crawler retry failed:\n' +
|
|
107
|
+
` Original error: ${firstError.message}\n` +
|
|
108
|
+
` Retry error: ${retryError.message}\n`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
debug('Crawler "%s" returned %d added/modified, %d removed, %d clock(s).', crawler, delta.changedFiles.size, delta.removedFiles.size, 'clocks' in delta ? (delta.clocks?.size ?? 0) : 0);
|
|
112
|
+
return delta;
|
|
113
|
+
}
|
|
114
|
+
async watch(onChange) {
|
|
115
|
+
const { extensions, ignorePatternForWatch, useWatchman } = this.#options;
|
|
116
|
+
// WatchmanWatcher > NativeWatcher > FallbackWatcher
|
|
117
|
+
const WatcherImpl = (useWatchman
|
|
118
|
+
? WatchmanWatcher_1.default
|
|
119
|
+
: NativeWatcher_1.default.isSupported()
|
|
120
|
+
? NativeWatcher_1.default
|
|
121
|
+
: FallbackWatcher_1.default);
|
|
122
|
+
let watcher = 'fallback';
|
|
123
|
+
if (WatcherImpl === WatchmanWatcher_1.default) {
|
|
124
|
+
watcher = 'watchman';
|
|
125
|
+
}
|
|
126
|
+
else if (WatcherImpl === NativeWatcher_1.default) {
|
|
127
|
+
watcher = 'native';
|
|
128
|
+
}
|
|
129
|
+
debug(`Using watcher: ${watcher}`);
|
|
130
|
+
this.#options.perfLogger?.annotate({ string: { watcher } });
|
|
131
|
+
this.#activeWatcher = watcher;
|
|
132
|
+
const createWatcherBackend = (root) => {
|
|
133
|
+
const watcherOptions = {
|
|
134
|
+
dot: true,
|
|
135
|
+
globs: [
|
|
136
|
+
// Ensure we always include package.json files, which are crucial for
|
|
137
|
+
/// module resolution.
|
|
138
|
+
'**/package.json',
|
|
139
|
+
// Ensure we always watch any health check files
|
|
140
|
+
'**/' + this.#options.healthCheckFilePrefix + '*',
|
|
141
|
+
...extensions.map((extension) => '**/*.' + extension),
|
|
142
|
+
],
|
|
143
|
+
ignored: ignorePatternForWatch,
|
|
144
|
+
watchmanDeferStates: this.#options.watchmanDeferStates,
|
|
145
|
+
};
|
|
146
|
+
const watcher = new WatcherImpl(root, watcherOptions);
|
|
147
|
+
return new Promise(async (resolve, reject) => {
|
|
148
|
+
const rejectTimeout = setTimeout(() => reject(new Error('Failed to start watch mode.')), MAX_WAIT_TIME);
|
|
149
|
+
watcher.onFileEvent((change) => {
|
|
150
|
+
const basename = path_1.default.basename(change.relativePath);
|
|
151
|
+
if (basename.startsWith(this.#options.healthCheckFilePrefix)) {
|
|
152
|
+
if (change.event === common_1.TOUCH_EVENT) {
|
|
153
|
+
debug('Observed possible health check cookie: %s in %s', change.relativePath, root);
|
|
154
|
+
this.#handleHealthCheckObservation(basename);
|
|
155
|
+
}
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
// Watchman handles recrawls internally - receiving a recrawl event
|
|
159
|
+
// when using Watchman would indicate a bug. Log an error and ignore.
|
|
160
|
+
if (change.event === 'recrawl' && useWatchman) {
|
|
161
|
+
this.#options.console.error('metro-file-map: Received unexpected recrawl event while using ' +
|
|
162
|
+
'Watchman. Watchman recrawls are not implemented.');
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
onChange(change);
|
|
166
|
+
});
|
|
167
|
+
await watcher.startWatching();
|
|
168
|
+
clearTimeout(rejectTimeout);
|
|
169
|
+
resolve(watcher);
|
|
170
|
+
});
|
|
171
|
+
};
|
|
172
|
+
this.#backends = await Promise.all(this.#options.roots.map(createWatcherBackend));
|
|
173
|
+
}
|
|
174
|
+
#handleHealthCheckObservation(basename) {
|
|
175
|
+
const resolveHealthCheck = this.#pendingHealthChecks.get(basename);
|
|
176
|
+
if (!resolveHealthCheck) {
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
resolveHealthCheck();
|
|
180
|
+
}
|
|
181
|
+
async close() {
|
|
182
|
+
await Promise.all(this.#backends.map((watcher) => watcher.stopWatching()));
|
|
183
|
+
this.#activeWatcher = null;
|
|
184
|
+
}
|
|
185
|
+
async checkHealth(timeout) {
|
|
186
|
+
const healthCheckId = this.#nextHealthCheckId++;
|
|
187
|
+
if (healthCheckId === Number.MAX_SAFE_INTEGER) {
|
|
188
|
+
this.#nextHealthCheckId = 0;
|
|
189
|
+
}
|
|
190
|
+
const watcher = this.#activeWatcher;
|
|
191
|
+
const basename = this.#options.healthCheckFilePrefix +
|
|
192
|
+
'-' +
|
|
193
|
+
process.pid +
|
|
194
|
+
'-' +
|
|
195
|
+
this.#instanceId +
|
|
196
|
+
'-' +
|
|
197
|
+
healthCheckId;
|
|
198
|
+
const healthCheckPath = path_1.default.join(this.#options.rootDir, basename);
|
|
199
|
+
let result;
|
|
200
|
+
const timeoutPromise = new Promise((resolve) => setTimeout(resolve, timeout)).then(() => {
|
|
201
|
+
if (!result) {
|
|
202
|
+
result = {
|
|
203
|
+
type: 'timeout',
|
|
204
|
+
pauseReason: this.#backends[0]?.getPauseReason(),
|
|
205
|
+
timeout,
|
|
206
|
+
watcher,
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
const startTime = perf_hooks_1.performance.now();
|
|
211
|
+
debug('Creating health check cookie: %s', healthCheckPath);
|
|
212
|
+
const creationPromise = fs_1.default.promises
|
|
213
|
+
.writeFile(healthCheckPath, String(startTime))
|
|
214
|
+
.catch((error) => {
|
|
215
|
+
if (!result) {
|
|
216
|
+
result = {
|
|
217
|
+
type: 'error',
|
|
218
|
+
error,
|
|
219
|
+
timeout,
|
|
220
|
+
watcher,
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
});
|
|
224
|
+
const observationPromise = new Promise((resolve) => {
|
|
225
|
+
this.#pendingHealthChecks.set(basename, resolve);
|
|
226
|
+
}).then(() => {
|
|
227
|
+
if (!result) {
|
|
228
|
+
result = {
|
|
229
|
+
type: 'success',
|
|
230
|
+
timeElapsed: perf_hooks_1.performance.now() - startTime,
|
|
231
|
+
timeout,
|
|
232
|
+
watcher,
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
await Promise.race([timeoutPromise, creationPromise.then(() => observationPromise)]);
|
|
237
|
+
this.#pendingHealthChecks.delete(basename);
|
|
238
|
+
// Chain a deletion to the creation promise (which may not have even settled yet!),
|
|
239
|
+
// don't await it, and swallow errors. This is just best-effort cleanup.
|
|
240
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
241
|
+
creationPromise.then(() => fs_1.default.promises.unlink(healthCheckPath).catch(() => { }));
|
|
242
|
+
debug('Health check result: %o', result);
|
|
243
|
+
if (result == null) {
|
|
244
|
+
throw new Error('health check result was not set by any promise branch');
|
|
245
|
+
}
|
|
246
|
+
return result;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
exports.Watcher = Watcher;
|
|
@@ -0,0 +1,25 @@
|
|
|
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 { BuildParameters, CacheData, CacheManager, CacheManagerFactoryOptions, CacheManagerWriteOptions } from '../types';
|
|
8
|
+
interface AutoSaveOptions {
|
|
9
|
+
readonly debounceMs: number;
|
|
10
|
+
}
|
|
11
|
+
interface DiskCacheConfig {
|
|
12
|
+
readonly autoSave?: Partial<AutoSaveOptions> | boolean | undefined;
|
|
13
|
+
readonly cacheFilePrefix?: string | undefined | null;
|
|
14
|
+
readonly cacheDirectory?: string | undefined | null;
|
|
15
|
+
}
|
|
16
|
+
export declare class DiskCacheManager implements CacheManager {
|
|
17
|
+
#private;
|
|
18
|
+
constructor({ buildParameters }: CacheManagerFactoryOptions, { autoSave, cacheDirectory, cacheFilePrefix }: DiskCacheConfig);
|
|
19
|
+
static getCacheFilePath(buildParameters: BuildParameters, cacheFilePrefix?: string | null, cacheDirectory?: string | null): string;
|
|
20
|
+
getCacheFilePath(): string;
|
|
21
|
+
read(): Promise<CacheData | undefined | null>;
|
|
22
|
+
write(getSnapshot: () => CacheData, { changedSinceCacheRead, eventSource, onWriteError }: CacheManagerWriteOptions): Promise<void>;
|
|
23
|
+
end(): Promise<void>;
|
|
24
|
+
}
|
|
25
|
+
export {};
|
|
@@ -0,0 +1,110 @@
|
|
|
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.DiskCacheManager = void 0;
|
|
13
|
+
const fs_1 = require("fs");
|
|
14
|
+
const os_1 = require("os");
|
|
15
|
+
const path_1 = __importDefault(require("path"));
|
|
16
|
+
const timers_1 = require("timers");
|
|
17
|
+
const v8_1 = require("v8");
|
|
18
|
+
const rootRelativeCacheKeys_1 = __importDefault(require("../lib/rootRelativeCacheKeys"));
|
|
19
|
+
const debug = require('debug')('Metro:FileMapCache');
|
|
20
|
+
const DEFAULT_PREFIX = 'metro-file-map';
|
|
21
|
+
const DEFAULT_DIRECTORY = (0, os_1.tmpdir)();
|
|
22
|
+
const DEFAULT_AUTO_SAVE_DEBOUNCE_MS = 5000;
|
|
23
|
+
// NOTE(@kitten): We're incompatible with Metro, so need our own naming
|
|
24
|
+
const FIXED_PREFIX = 'expo';
|
|
25
|
+
class DiskCacheManager {
|
|
26
|
+
#autoSaveOpts;
|
|
27
|
+
#cachePath;
|
|
28
|
+
#debounceTimeout = null;
|
|
29
|
+
#writePromise = Promise.resolve();
|
|
30
|
+
#hasUnwrittenChanges = false;
|
|
31
|
+
#tryWrite;
|
|
32
|
+
#stopListening;
|
|
33
|
+
constructor({ buildParameters }, { autoSave = {}, cacheDirectory, cacheFilePrefix }) {
|
|
34
|
+
this.#cachePath = DiskCacheManager.getCacheFilePath(buildParameters, cacheFilePrefix, cacheDirectory);
|
|
35
|
+
// Normalise auto-save options.
|
|
36
|
+
if (autoSave) {
|
|
37
|
+
const { debounceMs = DEFAULT_AUTO_SAVE_DEBOUNCE_MS } = autoSave === true ? {} : autoSave;
|
|
38
|
+
this.#autoSaveOpts = { debounceMs };
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
static getCacheFilePath(buildParameters, cacheFilePrefix, cacheDirectory) {
|
|
42
|
+
const { rootDirHash, relativeConfigHash } = (0, rootRelativeCacheKeys_1.default)(buildParameters);
|
|
43
|
+
return path_1.default.join(cacheDirectory ?? DEFAULT_DIRECTORY, `${cacheFilePrefix ?? DEFAULT_PREFIX}-${FIXED_PREFIX}-${rootDirHash}-${relativeConfigHash}`);
|
|
44
|
+
}
|
|
45
|
+
getCacheFilePath() {
|
|
46
|
+
return this.#cachePath;
|
|
47
|
+
}
|
|
48
|
+
async read() {
|
|
49
|
+
try {
|
|
50
|
+
return (0, v8_1.deserialize)(await fs_1.promises.readFile(this.#cachePath));
|
|
51
|
+
}
|
|
52
|
+
catch (e) {
|
|
53
|
+
if (e?.code === 'ENOENT') {
|
|
54
|
+
// Cache file not found - not considered an error.
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
// Rethrow anything else.
|
|
58
|
+
throw e;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
async write(getSnapshot, { changedSinceCacheRead, eventSource, onWriteError }) {
|
|
62
|
+
// Initialise a writer function using a promise queue to ensure writes are
|
|
63
|
+
// sequenced.
|
|
64
|
+
// eslint-disable-next-line no-multi-assign
|
|
65
|
+
const tryWrite = (this.#tryWrite = () => {
|
|
66
|
+
this.#writePromise = this.#writePromise
|
|
67
|
+
.then(async () => {
|
|
68
|
+
if (!this.#hasUnwrittenChanges) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const data = getSnapshot();
|
|
72
|
+
this.#hasUnwrittenChanges = false;
|
|
73
|
+
await fs_1.promises.writeFile(this.#cachePath, (0, v8_1.serialize)(data));
|
|
74
|
+
debug('Written cache to %s', this.#cachePath);
|
|
75
|
+
})
|
|
76
|
+
.catch(onWriteError);
|
|
77
|
+
return this.#writePromise;
|
|
78
|
+
});
|
|
79
|
+
// Set up auto-save on changes, if enabled.
|
|
80
|
+
if (this.#autoSaveOpts) {
|
|
81
|
+
const autoSave = this.#autoSaveOpts;
|
|
82
|
+
this.#stopListening?.();
|
|
83
|
+
this.#stopListening = eventSource.onChange(() => {
|
|
84
|
+
this.#hasUnwrittenChanges = true;
|
|
85
|
+
if (this.#debounceTimeout) {
|
|
86
|
+
this.#debounceTimeout.refresh();
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
this.#debounceTimeout = (0, timers_1.setTimeout)(() => tryWrite(), autoSave.debounceMs).unref();
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
// Write immediately if state has changed since the cache was read.
|
|
94
|
+
if (changedSinceCacheRead) {
|
|
95
|
+
this.#hasUnwrittenChanges = true;
|
|
96
|
+
await tryWrite();
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
async end() {
|
|
100
|
+
// Clear any timers
|
|
101
|
+
if (this.#debounceTimeout) {
|
|
102
|
+
(0, timers_1.clearTimeout)(this.#debounceTimeout);
|
|
103
|
+
}
|
|
104
|
+
// Remove event listeners
|
|
105
|
+
this.#stopListening?.();
|
|
106
|
+
// Flush unwritten changes to disk (no-op if no changes)
|
|
107
|
+
await this.#tryWrite?.();
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
exports.DiskCacheManager = DiskCacheManager;
|
|
@@ -0,0 +1,23 @@
|
|
|
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
|
+
export interface HType {
|
|
8
|
+
readonly MTIME: 0;
|
|
9
|
+
readonly SIZE: 1;
|
|
10
|
+
readonly VISITED: 2;
|
|
11
|
+
readonly SHA1: 3;
|
|
12
|
+
readonly SYMLINK: 4;
|
|
13
|
+
readonly PLUGINDATA: 5;
|
|
14
|
+
readonly PATH: 0;
|
|
15
|
+
readonly TYPE: 1;
|
|
16
|
+
readonly MODULE: 0;
|
|
17
|
+
readonly PACKAGE: 1;
|
|
18
|
+
readonly GENERIC_PLATFORM: 'g';
|
|
19
|
+
readonly NATIVE_PLATFORM: 'native';
|
|
20
|
+
}
|
|
21
|
+
export type HTypeValue = 0 | 1 | 2 | 3 | 4 | 5 | 'g' | 'native';
|
|
22
|
+
declare const H: HType;
|
|
23
|
+
export default H;
|
|
@@ -0,0 +1,27 @@
|
|
|
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
|
+
const H = {
|
|
10
|
+
/* file map attributes */
|
|
11
|
+
MTIME: 0,
|
|
12
|
+
SIZE: 1,
|
|
13
|
+
VISITED: 2,
|
|
14
|
+
SHA1: 3,
|
|
15
|
+
SYMLINK: 4,
|
|
16
|
+
PLUGINDATA: 5,
|
|
17
|
+
/* module map attributes */
|
|
18
|
+
PATH: 0,
|
|
19
|
+
TYPE: 1,
|
|
20
|
+
/* module types */
|
|
21
|
+
MODULE: 0,
|
|
22
|
+
PACKAGE: 1,
|
|
23
|
+
/* platforms */
|
|
24
|
+
GENERIC_PLATFORM: 'g',
|
|
25
|
+
NATIVE_PLATFORM: 'native',
|
|
26
|
+
};
|
|
27
|
+
exports.default = H;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { RootPathUtils } from '../../lib/RootPathUtils';
|
|
2
|
+
import type { FallbackFilesystem, IgnoreMatcher } from '../../types';
|
|
3
|
+
type FallbackFilesystemOptions = {
|
|
4
|
+
rootPathUtils: RootPathUtils;
|
|
5
|
+
extensions: readonly string[];
|
|
6
|
+
ignore: IgnoreMatcher;
|
|
7
|
+
includeSymlinks: boolean;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Create a FallbackFilesystem that synchronously queries the real filesystem.
|
|
11
|
+
*
|
|
12
|
+
* - `lookup` uses lstatSync to check a single path (for traversal).
|
|
13
|
+
* - `readdir` uses readdirSync to list directory contents (for enumeration).
|
|
14
|
+
*
|
|
15
|
+
* Both methods apply the same filtering as the node crawler: ignore patterns,
|
|
16
|
+
* extension filtering, and symlink inclusion.
|
|
17
|
+
*/
|
|
18
|
+
export default function createFallbackFilesystem(opts: FallbackFilesystemOptions): FallbackFilesystem;
|
|
19
|
+
export declare function shouldFallbackCrawlDir(canonicalPath: string): boolean;
|
|
20
|
+
export {};
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.default = createFallbackFilesystem;
|
|
7
|
+
exports.shouldFallbackCrawlDir = shouldFallbackCrawlDir;
|
|
8
|
+
const fs_1 = __importDefault(require("fs"));
|
|
9
|
+
const path_1 = __importDefault(require("path"));
|
|
10
|
+
const normalizePathSeparatorsToPosix_1 = __importDefault(require("../../lib/normalizePathSeparatorsToPosix"));
|
|
11
|
+
const readdirMarker = Symbol.for('fallbackDir');
|
|
12
|
+
function markDir(dirNode) {
|
|
13
|
+
dirNode[readdirMarker] = true;
|
|
14
|
+
}
|
|
15
|
+
function isMarkedDir(dirNode) {
|
|
16
|
+
return !!dirNode[readdirMarker];
|
|
17
|
+
}
|
|
18
|
+
function isDirectory(node) {
|
|
19
|
+
return node instanceof Map;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Create a FallbackFilesystem that synchronously queries the real filesystem.
|
|
23
|
+
*
|
|
24
|
+
* - `lookup` uses lstatSync to check a single path (for traversal).
|
|
25
|
+
* - `readdir` uses readdirSync to list directory contents (for enumeration).
|
|
26
|
+
*
|
|
27
|
+
* Both methods apply the same filtering as the node crawler: ignore patterns,
|
|
28
|
+
* extension filtering, and symlink inclusion.
|
|
29
|
+
*/
|
|
30
|
+
function createFallbackFilesystem(opts) {
|
|
31
|
+
const { rootPathUtils, extensions, ignore, includeSymlinks } = opts;
|
|
32
|
+
const exts = extensions.reduce((acc, ext) => {
|
|
33
|
+
acc[ext] = true;
|
|
34
|
+
return acc;
|
|
35
|
+
}, {});
|
|
36
|
+
function readdir(_normalPath, absolutePath, dirNode) {
|
|
37
|
+
if (dirNode != null && isMarkedDir(dirNode)) {
|
|
38
|
+
return dirNode;
|
|
39
|
+
}
|
|
40
|
+
let dirEntries;
|
|
41
|
+
try {
|
|
42
|
+
dirEntries = fs_1.default.readdirSync(absolutePath, { withFileTypes: true });
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
const result = dirNode ?? new Map();
|
|
48
|
+
for (const entry of dirEntries) {
|
|
49
|
+
const name = entry.name.toString();
|
|
50
|
+
const childAbsolutePath = absolutePath + path_1.default.sep + name;
|
|
51
|
+
if (ignore(childAbsolutePath)) {
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
if (entry.isDirectory()) {
|
|
55
|
+
if (!result.has(name)) {
|
|
56
|
+
result.set(name, new Map());
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
else if (entry.isSymbolicLink()) {
|
|
60
|
+
// We can skip reading the symlink target here, since it'll be read lazily
|
|
61
|
+
if (includeSymlinks && !result.has(name)) {
|
|
62
|
+
result.set(name, [null, 0, 0, null, 1, null]);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
else if (entry.isFile()) {
|
|
66
|
+
const ext = path_1.default.extname(name).slice(1);
|
|
67
|
+
if (exts[ext] && !result.has(name)) {
|
|
68
|
+
result.set(name, [null, 0, 0, null, 0, null]);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
markDir(result);
|
|
73
|
+
return result;
|
|
74
|
+
}
|
|
75
|
+
return {
|
|
76
|
+
readdir,
|
|
77
|
+
lookup(normalPath, absolutePath, prevNode) {
|
|
78
|
+
if (ignore(absolutePath)) {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
let stat;
|
|
82
|
+
try {
|
|
83
|
+
stat = fs_1.default.lstatSync(absolutePath);
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
if (stat.isDirectory()) {
|
|
89
|
+
const dirNode = isDirectory(prevNode) ? prevNode : null;
|
|
90
|
+
return shouldFallbackCrawlDir(absolutePath)
|
|
91
|
+
? readdir(normalPath, absolutePath, dirNode)
|
|
92
|
+
: (dirNode ?? new Map());
|
|
93
|
+
}
|
|
94
|
+
else if (stat.isSymbolicLink()) {
|
|
95
|
+
if (!includeSymlinks) {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
try {
|
|
99
|
+
// We might as well read the symlink target here and assume it'll be used
|
|
100
|
+
const symlinkTarget = fs_1.default.readlinkSync(absolutePath);
|
|
101
|
+
// Cached value should be in posix format
|
|
102
|
+
const target = (0, normalizePathSeparatorsToPosix_1.default)(rootPathUtils.resolveSymlinkToNormal(normalPath, symlinkTarget));
|
|
103
|
+
return [stat.mtime.getTime(), stat.size, 0, null, target, null];
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
else if (stat.isFile()) {
|
|
110
|
+
// Check extension — symlinks bypass this check (same as node crawler)
|
|
111
|
+
const ext = path_1.default.extname(absolutePath).slice(1);
|
|
112
|
+
if (!exts[ext]) {
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
return [stat.mtime.getTime(), stat.size, 0, null, 0, null];
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
// Whether a directory at the given canonical path should be eagerly
|
|
126
|
+
// populated via readdir. Returns false for directories that are typically
|
|
127
|
+
// too large (node_modules) or not useful (.git, .hg, etc.) to enumerate.
|
|
128
|
+
function shouldFallbackCrawlDir(canonicalPath) {
|
|
129
|
+
const lastSepIdx = canonicalPath.lastIndexOf(path_1.default.sep);
|
|
130
|
+
const baseStart = lastSepIdx + 1;
|
|
131
|
+
const baseLen = canonicalPath.length - baseStart;
|
|
132
|
+
if (baseLen === 2 &&
|
|
133
|
+
canonicalPath.charCodeAt(baseStart) === 46 /*'.'*/ &&
|
|
134
|
+
canonicalPath.charCodeAt(baseStart + 1) === 46 /*'.'*/) {
|
|
135
|
+
// '..' is the parent-of-rootDir indirection, not a hidden directory.
|
|
136
|
+
return true;
|
|
137
|
+
}
|
|
138
|
+
else if (canonicalPath.charCodeAt(baseStart) === 46 /*'.'*/) {
|
|
139
|
+
// starts with '.'
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
else if (baseLen === 12 && canonicalPath.startsWith('node_modules', baseStart)) {
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
return true;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
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
|
+
* @format
|
|
8
|
+
*/
|
|
9
|
+
import type { CrawlerOptions, CrawlResult } from '../../types';
|
|
10
|
+
export default function nodeCrawl(options: CrawlerOptions): Promise<CrawlResult>;
|