@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,358 @@
|
|
|
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
|
+
const path_1 = __importDefault(require("path"));
|
|
13
|
+
const constants_1 = __importDefault(require("../constants"));
|
|
14
|
+
const RootPathUtils_1 = require("../lib/RootPathUtils");
|
|
15
|
+
const sorting_1 = require("../lib/sorting");
|
|
16
|
+
const DuplicateHasteCandidatesError_1 = require("./haste/DuplicateHasteCandidatesError");
|
|
17
|
+
const HasteConflictsError_1 = require("./haste/HasteConflictsError");
|
|
18
|
+
const getPlatformExtension_1 = __importDefault(require("./haste/getPlatformExtension"));
|
|
19
|
+
const EMPTY_OBJ = {};
|
|
20
|
+
const EMPTY_MAP = new Map();
|
|
21
|
+
const PACKAGE_JSON = /(?:[/\\]|^)package\.json$/;
|
|
22
|
+
// Periodically yield to the event loop to allow parallel I/O, etc.
|
|
23
|
+
// Based on 200k files taking up to 800ms => max 40ms between yields.
|
|
24
|
+
const YIELD_EVERY_NUM_HASTE_FILES = 10000;
|
|
25
|
+
class HastePlugin {
|
|
26
|
+
name = 'haste';
|
|
27
|
+
#console;
|
|
28
|
+
#duplicates = new Map();
|
|
29
|
+
#enableHastePackages;
|
|
30
|
+
#failValidationOnConflicts;
|
|
31
|
+
#getModuleNameByPath;
|
|
32
|
+
#hasteImplModulePath;
|
|
33
|
+
#map = new Map();
|
|
34
|
+
#pathUtils;
|
|
35
|
+
#perfLogger;
|
|
36
|
+
#platforms;
|
|
37
|
+
constructor(options) {
|
|
38
|
+
this.#console = options.console ?? globalThis.console;
|
|
39
|
+
this.#enableHastePackages = options.enableHastePackages;
|
|
40
|
+
this.#hasteImplModulePath = options.hasteImplModulePath;
|
|
41
|
+
this.#perfLogger = options.perfLogger;
|
|
42
|
+
this.#platforms = options.platforms;
|
|
43
|
+
this.#pathUtils = new RootPathUtils_1.RootPathUtils(options.rootDir);
|
|
44
|
+
this.#failValidationOnConflicts = options.failValidationOnConflicts;
|
|
45
|
+
}
|
|
46
|
+
async initialize({ files }) {
|
|
47
|
+
this.#perfLogger?.point('constructHasteMap_start');
|
|
48
|
+
let hasteFiles = 0;
|
|
49
|
+
for (const { baseName, canonicalPath, pluginData: hasteId } of files.fileIterator({
|
|
50
|
+
// Symlinks and node_modules are never Haste modules or packages.
|
|
51
|
+
includeNodeModules: false,
|
|
52
|
+
includeSymlinks: false,
|
|
53
|
+
})) {
|
|
54
|
+
if (hasteId == null) {
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
this.setModule(hasteId, [
|
|
58
|
+
canonicalPath,
|
|
59
|
+
this.#enableHastePackages && baseName === 'package.json' ? constants_1.default.PACKAGE : constants_1.default.MODULE,
|
|
60
|
+
]);
|
|
61
|
+
if (++hasteFiles % YIELD_EVERY_NUM_HASTE_FILES === 0) {
|
|
62
|
+
await new Promise(setImmediate);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
this.#getModuleNameByPath = (mixedPath) => {
|
|
66
|
+
const result = files.lookup(mixedPath);
|
|
67
|
+
return result.exists && result.type === 'f' && typeof result.pluginData === 'string'
|
|
68
|
+
? result.pluginData
|
|
69
|
+
: null;
|
|
70
|
+
};
|
|
71
|
+
this.#perfLogger?.point('constructHasteMap_end');
|
|
72
|
+
this.#perfLogger?.annotate({ int: { hasteFiles } });
|
|
73
|
+
}
|
|
74
|
+
getSerializableSnapshot() {
|
|
75
|
+
// Haste is not serialised, but built from traversing the file metadata
|
|
76
|
+
// on each run. This turns out to have comparable performance to
|
|
77
|
+
// serialisation, at least when Haste is dense, and makes for a much
|
|
78
|
+
// smaller cache.
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
getModule(name, platform, supportsNativePlatform, type) {
|
|
82
|
+
const module = this.#getModuleMetadata(name, platform, !!supportsNativePlatform);
|
|
83
|
+
if (module && module[constants_1.default.TYPE] === (type ?? constants_1.default.MODULE)) {
|
|
84
|
+
const modulePath = module[constants_1.default.PATH];
|
|
85
|
+
return modulePath && this.#pathUtils.normalToAbsolute(modulePath);
|
|
86
|
+
}
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
getModuleNameByPath(mixedPath) {
|
|
90
|
+
if (this.#getModuleNameByPath == null) {
|
|
91
|
+
throw new Error('HastePlugin has not been initialized before getModuleNameByPath');
|
|
92
|
+
}
|
|
93
|
+
return this.#getModuleNameByPath(mixedPath) ?? null;
|
|
94
|
+
}
|
|
95
|
+
getPackage(name, platform, _supportsNativePlatform) {
|
|
96
|
+
return this.getModule(name, platform, null, constants_1.default.PACKAGE);
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* When looking up a module's data, we walk through each eligible platform for
|
|
100
|
+
* the query. For each platform, we want to check if there are known
|
|
101
|
+
* duplicates for that name+platform pair. The duplication logic normally
|
|
102
|
+
* removes elements from the `map` object, but we want to check upfront to be
|
|
103
|
+
* extra sure. If metadata exists both in the `duplicates` object and the
|
|
104
|
+
* `map`, this would be a bug.
|
|
105
|
+
*/
|
|
106
|
+
#getModuleMetadata(name, platform, supportsNativePlatform) {
|
|
107
|
+
const map = this.#map.get(name) || EMPTY_OBJ;
|
|
108
|
+
const dupMap = this.#duplicates.get(name) || EMPTY_MAP;
|
|
109
|
+
if (platform != null) {
|
|
110
|
+
this.#assertNoDuplicates(name, platform, supportsNativePlatform, dupMap.get(platform));
|
|
111
|
+
if (map[platform] != null) {
|
|
112
|
+
return map[platform];
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
if (supportsNativePlatform) {
|
|
116
|
+
this.#assertNoDuplicates(name, constants_1.default.NATIVE_PLATFORM, supportsNativePlatform, dupMap.get(constants_1.default.NATIVE_PLATFORM));
|
|
117
|
+
if (map[constants_1.default.NATIVE_PLATFORM]) {
|
|
118
|
+
return map[constants_1.default.NATIVE_PLATFORM];
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
this.#assertNoDuplicates(name, constants_1.default.GENERIC_PLATFORM, supportsNativePlatform, dupMap.get(constants_1.default.GENERIC_PLATFORM));
|
|
122
|
+
if (map[constants_1.default.GENERIC_PLATFORM]) {
|
|
123
|
+
return map[constants_1.default.GENERIC_PLATFORM];
|
|
124
|
+
}
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
#assertNoDuplicates(name, platform, supportsNativePlatform, relativePathSet) {
|
|
128
|
+
if (relativePathSet == null) {
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
const duplicates = new Map();
|
|
132
|
+
for (const [relativePath, type] of relativePathSet) {
|
|
133
|
+
const duplicatePath = this.#pathUtils.normalToAbsolute(relativePath);
|
|
134
|
+
duplicates.set(duplicatePath, type);
|
|
135
|
+
}
|
|
136
|
+
throw new DuplicateHasteCandidatesError_1.DuplicateHasteCandidatesError(name, platform, supportsNativePlatform, duplicates);
|
|
137
|
+
}
|
|
138
|
+
onChanged(delta) {
|
|
139
|
+
// Process removals first so that moves aren't treated as duplicates.
|
|
140
|
+
for (const [canonicalPath, maybeHasteId] of delta.removedFiles) {
|
|
141
|
+
this.#onRemovedFile(canonicalPath, maybeHasteId);
|
|
142
|
+
}
|
|
143
|
+
for (const [canonicalPath, maybeHasteId] of delta.addedFiles) {
|
|
144
|
+
this.#onNewFile(canonicalPath, maybeHasteId);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
#onNewFile(canonicalPath, id) {
|
|
148
|
+
if (id == null) {
|
|
149
|
+
// Not a Haste module or package
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
const module = [
|
|
153
|
+
canonicalPath,
|
|
154
|
+
this.#enableHastePackages && path_1.default.basename(canonicalPath) === 'package.json'
|
|
155
|
+
? constants_1.default.PACKAGE
|
|
156
|
+
: constants_1.default.MODULE,
|
|
157
|
+
];
|
|
158
|
+
this.setModule(id, module);
|
|
159
|
+
}
|
|
160
|
+
setModule(id, module) {
|
|
161
|
+
let hasteMapItem = this.#map.get(id);
|
|
162
|
+
if (!hasteMapItem) {
|
|
163
|
+
hasteMapItem = Object.create(null);
|
|
164
|
+
this.#map.set(id, hasteMapItem);
|
|
165
|
+
}
|
|
166
|
+
const platform = (0, getPlatformExtension_1.default)(module[constants_1.default.PATH], this.#platforms) || constants_1.default.GENERIC_PLATFORM;
|
|
167
|
+
const existingModule = hasteMapItem[platform];
|
|
168
|
+
if (existingModule && existingModule[constants_1.default.PATH] !== module[constants_1.default.PATH]) {
|
|
169
|
+
if (this.#console) {
|
|
170
|
+
this.#console.warn([
|
|
171
|
+
'metro-file-map: Haste module naming collision: ' + id,
|
|
172
|
+
' The following files share their name; please adjust your hasteImpl:',
|
|
173
|
+
' * <rootDir>' + path_1.default.sep + existingModule[constants_1.default.PATH],
|
|
174
|
+
' * <rootDir>' + path_1.default.sep + module[constants_1.default.PATH],
|
|
175
|
+
'',
|
|
176
|
+
].join('\n'));
|
|
177
|
+
}
|
|
178
|
+
// We do NOT want consumers to use a module that is ambiguous.
|
|
179
|
+
delete hasteMapItem[platform];
|
|
180
|
+
if (Object.keys(hasteMapItem).length === 0) {
|
|
181
|
+
this.#map.delete(id);
|
|
182
|
+
}
|
|
183
|
+
let dupsByPlatform = this.#duplicates.get(id);
|
|
184
|
+
if (dupsByPlatform == null) {
|
|
185
|
+
dupsByPlatform = new Map();
|
|
186
|
+
this.#duplicates.set(id, dupsByPlatform);
|
|
187
|
+
}
|
|
188
|
+
const dups = new Map([
|
|
189
|
+
[module[constants_1.default.PATH], module[constants_1.default.TYPE]],
|
|
190
|
+
[existingModule[constants_1.default.PATH], existingModule[constants_1.default.TYPE]],
|
|
191
|
+
]);
|
|
192
|
+
dupsByPlatform.set(platform, dups);
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
const dupsByPlatform = this.#duplicates.get(id);
|
|
196
|
+
if (dupsByPlatform != null) {
|
|
197
|
+
const dups = dupsByPlatform.get(platform);
|
|
198
|
+
if (dups != null) {
|
|
199
|
+
dups.set(module[constants_1.default.PATH], module[constants_1.default.TYPE]);
|
|
200
|
+
}
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
hasteMapItem[platform] = module;
|
|
204
|
+
}
|
|
205
|
+
#onRemovedFile(canonicalPath, moduleName) {
|
|
206
|
+
if (moduleName == null) {
|
|
207
|
+
// Not a Haste module or package
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
const platform = (0, getPlatformExtension_1.default)(canonicalPath, this.#platforms) || constants_1.default.GENERIC_PLATFORM;
|
|
211
|
+
const hasteMapItem = this.#map.get(moduleName);
|
|
212
|
+
if (hasteMapItem != null) {
|
|
213
|
+
delete hasteMapItem[platform];
|
|
214
|
+
if (Object.keys(hasteMapItem).length === 0) {
|
|
215
|
+
this.#map.delete(moduleName);
|
|
216
|
+
}
|
|
217
|
+
else {
|
|
218
|
+
this.#map.set(moduleName, hasteMapItem);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
this.#recoverDuplicates(moduleName, canonicalPath);
|
|
222
|
+
}
|
|
223
|
+
assertValid() {
|
|
224
|
+
if (!this.#failValidationOnConflicts) {
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
const conflicts = this.computeConflicts();
|
|
228
|
+
if (conflicts.length > 0) {
|
|
229
|
+
throw new HasteConflictsError_1.HasteConflictsError(conflicts);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* This function should be called when the file under `filePath` is removed
|
|
234
|
+
* or changed. When that happens, we want to figure out if that file was
|
|
235
|
+
* part of a group of files that had the same ID. If it was, we want to
|
|
236
|
+
* remove it from the group. Furthermore, if there is only one file
|
|
237
|
+
* remaining in the group, then we want to restore that single file as the
|
|
238
|
+
* correct resolution for its ID, and cleanup the duplicates index.
|
|
239
|
+
*/
|
|
240
|
+
#recoverDuplicates(moduleName, relativeFilePath) {
|
|
241
|
+
let dupsByPlatform = this.#duplicates.get(moduleName);
|
|
242
|
+
if (dupsByPlatform == null) {
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
const platform = (0, getPlatformExtension_1.default)(relativeFilePath, this.#platforms) || constants_1.default.GENERIC_PLATFORM;
|
|
246
|
+
let dups = dupsByPlatform.get(platform);
|
|
247
|
+
if (dups == null) {
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
dupsByPlatform = new Map(dupsByPlatform);
|
|
251
|
+
this.#duplicates.set(moduleName, dupsByPlatform);
|
|
252
|
+
dups = new Map(dups);
|
|
253
|
+
dupsByPlatform.set(platform, dups);
|
|
254
|
+
dups.delete(relativeFilePath);
|
|
255
|
+
if (dups.size !== 1) {
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
const uniqueModule = dups.entries().next().value;
|
|
259
|
+
if (!uniqueModule) {
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
let dedupMap = this.#map.get(moduleName);
|
|
263
|
+
if (dedupMap == null) {
|
|
264
|
+
dedupMap = Object.create(null);
|
|
265
|
+
this.#map.set(moduleName, dedupMap);
|
|
266
|
+
}
|
|
267
|
+
dedupMap[platform] = uniqueModule;
|
|
268
|
+
dupsByPlatform.delete(platform);
|
|
269
|
+
if (dupsByPlatform.size === 0) {
|
|
270
|
+
this.#duplicates.delete(moduleName);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
computeConflicts() {
|
|
274
|
+
const conflicts = [];
|
|
275
|
+
// Add literal duplicates tracked in the #duplicates map
|
|
276
|
+
for (const [id, dupsByPlatform] of this.#duplicates.entries()) {
|
|
277
|
+
for (const [platform, conflictingModules] of dupsByPlatform) {
|
|
278
|
+
conflicts.push({
|
|
279
|
+
absolutePaths: [...conflictingModules.keys()]
|
|
280
|
+
.map((modulePath) => this.#pathUtils.normalToAbsolute(modulePath))
|
|
281
|
+
// Sort for ease of testing
|
|
282
|
+
.sort(),
|
|
283
|
+
id,
|
|
284
|
+
platform: platform === constants_1.default.GENERIC_PLATFORM ? null : platform,
|
|
285
|
+
type: 'duplicate',
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
// Add cases of "shadowing at a distance": a module with a platform suffix and
|
|
290
|
+
// a module with a lower priority platform suffix (or no suffix), in different
|
|
291
|
+
// directories.
|
|
292
|
+
for (const [id, data] of this.#map) {
|
|
293
|
+
const conflictPaths = new Set();
|
|
294
|
+
const basePaths = [];
|
|
295
|
+
for (const basePlatform of [constants_1.default.NATIVE_PLATFORM, constants_1.default.GENERIC_PLATFORM]) {
|
|
296
|
+
if (data[basePlatform] == null) {
|
|
297
|
+
continue;
|
|
298
|
+
}
|
|
299
|
+
const basePath = data[basePlatform][0];
|
|
300
|
+
basePaths.push(basePath);
|
|
301
|
+
const basePathDir = path_1.default.dirname(basePath);
|
|
302
|
+
// Find all platforms that can shadow basePlatform
|
|
303
|
+
// Given that X.(specific platform).js > x.native.js > X.js
|
|
304
|
+
// and basePlatform is either 'native' or generic (no platform).
|
|
305
|
+
for (const platform of Object.keys(data)) {
|
|
306
|
+
if (platform === basePlatform || platform === constants_1.default.GENERIC_PLATFORM /* lowest priority */) {
|
|
307
|
+
continue;
|
|
308
|
+
}
|
|
309
|
+
const platformPath = data[platform][0];
|
|
310
|
+
if (path_1.default.dirname(platformPath) !== basePathDir) {
|
|
311
|
+
conflictPaths.add(platformPath);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
if (conflictPaths.size) {
|
|
316
|
+
conflicts.push({
|
|
317
|
+
absolutePaths: [...new Set([...conflictPaths, ...basePaths])]
|
|
318
|
+
.map((modulePath) => this.#pathUtils.normalToAbsolute(modulePath))
|
|
319
|
+
// Sort for ease of testing
|
|
320
|
+
.sort(),
|
|
321
|
+
id,
|
|
322
|
+
platform: null,
|
|
323
|
+
type: 'shadowing',
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
// Sort for ease of testing
|
|
328
|
+
conflicts.sort((0, sorting_1.chainComparators)((a, b) => (0, sorting_1.compareStrings)(a.type, b.type), (a, b) => (0, sorting_1.compareStrings)(a.id, b.id), (a, b) => (0, sorting_1.compareStrings)(a.platform, b.platform)));
|
|
329
|
+
return conflicts;
|
|
330
|
+
}
|
|
331
|
+
getCacheKey() {
|
|
332
|
+
return JSON.stringify([
|
|
333
|
+
this.#enableHastePackages,
|
|
334
|
+
this.#hasteImplModulePath != null ? require(this.#hasteImplModulePath).getCacheKey() : null,
|
|
335
|
+
[...this.#platforms].sort(),
|
|
336
|
+
]);
|
|
337
|
+
}
|
|
338
|
+
getWorker() {
|
|
339
|
+
return {
|
|
340
|
+
worker: {
|
|
341
|
+
modulePath: require.resolve('./haste/worker.js'),
|
|
342
|
+
setupArgs: {
|
|
343
|
+
hasteImplModulePath: this.#hasteImplModulePath ?? null,
|
|
344
|
+
},
|
|
345
|
+
},
|
|
346
|
+
filter: ({ isNodeModules, normalPath }) => {
|
|
347
|
+
if (isNodeModules) {
|
|
348
|
+
return false;
|
|
349
|
+
}
|
|
350
|
+
if (PACKAGE_JSON.test(normalPath)) {
|
|
351
|
+
return this.#enableHastePackages;
|
|
352
|
+
}
|
|
353
|
+
return this.#hasteImplModulePath != null;
|
|
354
|
+
},
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
exports.default = HastePlugin;
|
|
@@ -0,0 +1,27 @@
|
|
|
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 { Console, FileMapPlugin, FileMapPluginInitOptions, FileMapPluginWorker, MockMap as IMockMap, Path, RawMockMap, ReadonlyFileSystemChanges } from '../types';
|
|
8
|
+
export declare const CACHE_VERSION = 2;
|
|
9
|
+
export interface MockMapOptions {
|
|
10
|
+
readonly console: Console;
|
|
11
|
+
readonly mocksPattern: RegExp;
|
|
12
|
+
readonly rawMockMap?: RawMockMap;
|
|
13
|
+
readonly rootDir: Path;
|
|
14
|
+
readonly throwOnModuleCollision: boolean;
|
|
15
|
+
}
|
|
16
|
+
export default class MockPlugin implements FileMapPlugin<RawMockMap, undefined>, IMockMap {
|
|
17
|
+
#private;
|
|
18
|
+
readonly name: 'mocks';
|
|
19
|
+
constructor({ console, mocksPattern, rawMockMap, rootDir, throwOnModuleCollision, }: MockMapOptions);
|
|
20
|
+
initialize({ files, pluginState }: FileMapPluginInitOptions<RawMockMap>): Promise<void>;
|
|
21
|
+
getMockModule(name: string): Path | undefined | null;
|
|
22
|
+
onChanged(delta: ReadonlyFileSystemChanges<undefined | null>): void;
|
|
23
|
+
getSerializableSnapshot(): RawMockMap;
|
|
24
|
+
assertValid(): void;
|
|
25
|
+
getCacheKey(): string;
|
|
26
|
+
getWorker(): FileMapPluginWorker | undefined | null;
|
|
27
|
+
}
|
|
@@ -0,0 +1,157 @@
|
|
|
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.CACHE_VERSION = void 0;
|
|
13
|
+
const path_1 = __importDefault(require("path"));
|
|
14
|
+
const RootPathUtils_1 = require("../lib/RootPathUtils");
|
|
15
|
+
const normalizePathSeparatorsToPosix_1 = __importDefault(require("../lib/normalizePathSeparatorsToPosix"));
|
|
16
|
+
const normalizePathSeparatorsToSystem_1 = __importDefault(require("../lib/normalizePathSeparatorsToSystem"));
|
|
17
|
+
const getMockName_1 = __importDefault(require("./mocks/getMockName"));
|
|
18
|
+
exports.CACHE_VERSION = 2;
|
|
19
|
+
class MockPlugin {
|
|
20
|
+
name = 'mocks';
|
|
21
|
+
#mocksPattern;
|
|
22
|
+
#raw;
|
|
23
|
+
#pathUtils;
|
|
24
|
+
#console;
|
|
25
|
+
#throwOnModuleCollision;
|
|
26
|
+
constructor({ console, mocksPattern, rawMockMap = {
|
|
27
|
+
duplicates: new Map(),
|
|
28
|
+
mocks: new Map(),
|
|
29
|
+
version: exports.CACHE_VERSION,
|
|
30
|
+
}, rootDir, throwOnModuleCollision, }) {
|
|
31
|
+
this.#mocksPattern = mocksPattern;
|
|
32
|
+
if (rawMockMap.version !== exports.CACHE_VERSION) {
|
|
33
|
+
throw new Error('Incompatible state passed to MockPlugin');
|
|
34
|
+
}
|
|
35
|
+
this.#raw = rawMockMap;
|
|
36
|
+
this.#console = console;
|
|
37
|
+
this.#pathUtils = new RootPathUtils_1.RootPathUtils(rootDir);
|
|
38
|
+
this.#throwOnModuleCollision = throwOnModuleCollision;
|
|
39
|
+
}
|
|
40
|
+
async initialize({ files, pluginState }) {
|
|
41
|
+
if (pluginState != null && pluginState.version === this.#raw.version) {
|
|
42
|
+
// Use cached state directly if available
|
|
43
|
+
this.#raw = pluginState;
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
// Otherwise, traverse all files to rebuild
|
|
47
|
+
for (const { canonicalPath } of files.fileIterator({
|
|
48
|
+
includeNodeModules: false,
|
|
49
|
+
includeSymlinks: false,
|
|
50
|
+
})) {
|
|
51
|
+
this.#onFileAdded(canonicalPath);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
getMockModule(name) {
|
|
56
|
+
const mockPosixRelativePath = this.#raw.mocks.get(name) || this.#raw.mocks.get(name + '/index');
|
|
57
|
+
if (typeof mockPosixRelativePath !== 'string') {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
return this.#pathUtils.normalToAbsolute((0, normalizePathSeparatorsToSystem_1.default)(mockPosixRelativePath));
|
|
61
|
+
}
|
|
62
|
+
onChanged(delta) {
|
|
63
|
+
// Process removals first so that moves aren't treated as duplicates.
|
|
64
|
+
for (const [canonicalPath] of delta.removedFiles) {
|
|
65
|
+
this.#onFileRemoved(canonicalPath);
|
|
66
|
+
}
|
|
67
|
+
for (const [canonicalPath] of delta.addedFiles) {
|
|
68
|
+
this.#onFileAdded(canonicalPath);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
#onFileAdded(canonicalPath) {
|
|
72
|
+
const absoluteFilePath = this.#pathUtils.normalToAbsolute(canonicalPath);
|
|
73
|
+
if (!this.#mocksPattern.test(absoluteFilePath)) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
const mockName = (0, getMockName_1.default)(absoluteFilePath);
|
|
77
|
+
const posixRelativePath = (0, normalizePathSeparatorsToPosix_1.default)(canonicalPath);
|
|
78
|
+
const existingMockPosixPath = this.#raw.mocks.get(mockName);
|
|
79
|
+
if (existingMockPosixPath != null) {
|
|
80
|
+
if (existingMockPosixPath !== posixRelativePath) {
|
|
81
|
+
let duplicates = this.#raw.duplicates.get(mockName);
|
|
82
|
+
if (duplicates == null) {
|
|
83
|
+
duplicates = new Set([existingMockPosixPath, posixRelativePath]);
|
|
84
|
+
this.#raw.duplicates.set(mockName, duplicates);
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
duplicates.add(posixRelativePath);
|
|
88
|
+
}
|
|
89
|
+
this.#console.warn(this.#getMessageForDuplicates(mockName, duplicates));
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
// If there are duplicates and we don't throw, the latest mock wins.
|
|
93
|
+
// This is to preserve backwards compatibility, but it's unpredictable.
|
|
94
|
+
this.#raw.mocks.set(mockName, posixRelativePath);
|
|
95
|
+
}
|
|
96
|
+
#onFileRemoved(canonicalPath) {
|
|
97
|
+
const absoluteFilePath = this.#pathUtils.normalToAbsolute(canonicalPath);
|
|
98
|
+
if (!this.#mocksPattern.test(absoluteFilePath)) {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
const mockName = (0, getMockName_1.default)(absoluteFilePath);
|
|
102
|
+
const duplicates = this.#raw.duplicates.get(mockName);
|
|
103
|
+
if (duplicates != null) {
|
|
104
|
+
const posixRelativePath = (0, normalizePathSeparatorsToPosix_1.default)(canonicalPath);
|
|
105
|
+
duplicates.delete(posixRelativePath);
|
|
106
|
+
if (duplicates.size === 1) {
|
|
107
|
+
this.#raw.duplicates.delete(mockName);
|
|
108
|
+
}
|
|
109
|
+
// Set the mock to a remaining duplicate. Should never be empty.
|
|
110
|
+
// Size was checked as 1 above, so this is always defined
|
|
111
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
112
|
+
const remaining = duplicates.values().next().value;
|
|
113
|
+
this.#raw.mocks.set(mockName, remaining);
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
this.#raw.mocks.delete(mockName);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
getSerializableSnapshot() {
|
|
120
|
+
return {
|
|
121
|
+
duplicates: new Map([...this.#raw.duplicates].map(([k, v]) => [k, new Set(v)])),
|
|
122
|
+
mocks: new Map(this.#raw.mocks),
|
|
123
|
+
version: this.#raw.version,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
assertValid() {
|
|
127
|
+
if (!this.#throwOnModuleCollision) {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
// Throw an aggregate error for each duplicate.
|
|
131
|
+
const errors = [];
|
|
132
|
+
for (const [mockName, relativePosixPaths] of this.#raw.duplicates) {
|
|
133
|
+
errors.push(this.#getMessageForDuplicates(mockName, relativePosixPaths));
|
|
134
|
+
}
|
|
135
|
+
if (errors.length > 0) {
|
|
136
|
+
throw new Error(`Mock map has ${errors.length} error${errors.length > 1 ? 's' : ''}:\n${errors.join('\n')}`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
#getMessageForDuplicates(mockName, relativePosixPaths) {
|
|
140
|
+
return ('Duplicate manual mock found for `' +
|
|
141
|
+
mockName +
|
|
142
|
+
'`:\n' +
|
|
143
|
+
[...relativePosixPaths]
|
|
144
|
+
.map((relativePosixPath) => ' * <rootDir>' +
|
|
145
|
+
path_1.default.sep +
|
|
146
|
+
this.#pathUtils.absoluteToNormal((0, normalizePathSeparatorsToSystem_1.default)(relativePosixPath)) +
|
|
147
|
+
'\n')
|
|
148
|
+
.join(''));
|
|
149
|
+
}
|
|
150
|
+
getCacheKey() {
|
|
151
|
+
return this.#mocksPattern.source.replaceAll('\\\\', '\\/') + ',' + this.#mocksPattern.flags;
|
|
152
|
+
}
|
|
153
|
+
getWorker() {
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
exports.default = MockPlugin;
|
|
@@ -0,0 +1,66 @@
|
|
|
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.extract = extract;
|
|
10
|
+
const NOT_A_DOT = '(?<!\\.\\s*)';
|
|
11
|
+
const CAPTURE_STRING_LITERAL = (pos) => `([\`'"])([^'"\`]*?)(?:\\${pos})`;
|
|
12
|
+
const WORD_SEPARATOR = '\\b';
|
|
13
|
+
const LEFT_PARENTHESIS = '\\(';
|
|
14
|
+
const RIGHT_PARENTHESIS = '\\)';
|
|
15
|
+
const WHITESPACE = '\\s*';
|
|
16
|
+
const OPTIONAL_COMMA = '(:?,\\s*)?';
|
|
17
|
+
function createRegExp(parts, flags) {
|
|
18
|
+
return new RegExp(parts.join(''), flags);
|
|
19
|
+
}
|
|
20
|
+
function alternatives(...parts) {
|
|
21
|
+
return `(?:${parts.join('|')})`;
|
|
22
|
+
}
|
|
23
|
+
function functionCallStart(...names) {
|
|
24
|
+
return [
|
|
25
|
+
NOT_A_DOT,
|
|
26
|
+
WORD_SEPARATOR,
|
|
27
|
+
alternatives(...names),
|
|
28
|
+
WHITESPACE,
|
|
29
|
+
LEFT_PARENTHESIS,
|
|
30
|
+
WHITESPACE,
|
|
31
|
+
];
|
|
32
|
+
}
|
|
33
|
+
const BLOCK_COMMENT_RE = /\/\*[^]*?\*\//g;
|
|
34
|
+
const LINE_COMMENT_RE = /\/\/.*/g;
|
|
35
|
+
const REQUIRE_OR_DYNAMIC_IMPORT_RE = createRegExp([
|
|
36
|
+
...functionCallStart('require', 'import'),
|
|
37
|
+
CAPTURE_STRING_LITERAL(1),
|
|
38
|
+
WHITESPACE,
|
|
39
|
+
OPTIONAL_COMMA,
|
|
40
|
+
RIGHT_PARENTHESIS,
|
|
41
|
+
], 'g');
|
|
42
|
+
const IMPORT_OR_EXPORT_RE = createRegExp([
|
|
43
|
+
'\\b(?:import|export)\\s+(?!type(?:of)?\\s+)(?:[^\'"]+\\s+from\\s+)?',
|
|
44
|
+
CAPTURE_STRING_LITERAL(1),
|
|
45
|
+
], 'g');
|
|
46
|
+
const JEST_EXTENSIONS_RE = createRegExp([
|
|
47
|
+
...functionCallStart('jest\\s*\\.\\s*(?:requireActual|requireMock|genMockFromModule|createMockFromModule)'),
|
|
48
|
+
CAPTURE_STRING_LITERAL(1),
|
|
49
|
+
WHITESPACE,
|
|
50
|
+
OPTIONAL_COMMA,
|
|
51
|
+
RIGHT_PARENTHESIS,
|
|
52
|
+
], 'g');
|
|
53
|
+
function extract(code) {
|
|
54
|
+
const dependencies = new Set();
|
|
55
|
+
const addDependency = (match, _, dep) => {
|
|
56
|
+
dependencies.add(dep);
|
|
57
|
+
return match;
|
|
58
|
+
};
|
|
59
|
+
code
|
|
60
|
+
.replace(BLOCK_COMMENT_RE, '')
|
|
61
|
+
.replace(LINE_COMMENT_RE, '')
|
|
62
|
+
.replace(IMPORT_OR_EXPORT_RE, addDependency)
|
|
63
|
+
.replace(REQUIRE_OR_DYNAMIC_IMPORT_RE, addDependency)
|
|
64
|
+
.replace(JEST_EXTENSIONS_RE, addDependency);
|
|
65
|
+
return dependencies;
|
|
66
|
+
}
|
|
@@ -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
|
+
* @format
|
|
8
|
+
*/
|
|
9
|
+
import type { MetadataWorker, V8Serializable, WorkerMessage } from '../../types';
|
|
10
|
+
export default class DependencyExtractorWorker implements MetadataWorker {
|
|
11
|
+
#private;
|
|
12
|
+
constructor({ dependencyExtractor }: Readonly<{
|
|
13
|
+
dependencyExtractor: string | null;
|
|
14
|
+
}>);
|
|
15
|
+
processFile(data: WorkerMessage, utils: {
|
|
16
|
+
readonly getContent: () => Promise<Buffer>;
|
|
17
|
+
}): Promise<V8Serializable>;
|
|
18
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
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
|
+
'use strict';
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
const dependencyExtractor_1 = require("./dependencyExtractor");
|
|
12
|
+
class DependencyExtractorWorker {
|
|
13
|
+
#dependencyExtractor;
|
|
14
|
+
constructor({ dependencyExtractor }) {
|
|
15
|
+
if (dependencyExtractor != null) {
|
|
16
|
+
const mod = require(dependencyExtractor);
|
|
17
|
+
this.#dependencyExtractor = mod.__esModule === true && 'default' in mod ? mod.default : mod;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
async processFile(data, utils) {
|
|
21
|
+
const content = (await utils.getContent()).toString();
|
|
22
|
+
const { filePath } = data;
|
|
23
|
+
const dependencies = this.#dependencyExtractor != null
|
|
24
|
+
? this.#dependencyExtractor.extract(content, filePath, dependencyExtractor_1.extract)
|
|
25
|
+
: (0, dependencyExtractor_1.extract)(content);
|
|
26
|
+
// Return as array (PerFileData type)
|
|
27
|
+
return Array.from(dependencies);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
exports.default = DependencyExtractorWorker;
|
|
@@ -0,0 +1,14 @@
|
|
|
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 { DuplicatesSet } from '../../types';
|
|
8
|
+
export declare class DuplicateHasteCandidatesError extends Error {
|
|
9
|
+
hasteName: string;
|
|
10
|
+
platform: string | null;
|
|
11
|
+
supportsNativePlatform: boolean;
|
|
12
|
+
duplicatesSet: DuplicatesSet;
|
|
13
|
+
constructor(name: string, platform: string, supportsNativePlatform: boolean, duplicatesSet: DuplicatesSet);
|
|
14
|
+
}
|