@granite-js/vitest 1.0.22 → 1.0.24
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/CHANGELOG.md +8 -0
- package/README.md +15 -9
- package/dist/index.d.ts +34 -2
- package/dist/index.js +9 -420
- package/dist/mirror-e92G5HdG.js +484 -0
- package/dist/reactNativeRuntime.js +44 -70
- package/package.json +1 -1
- package/dist/assets-DoucBOz0.js +0 -16
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
|
@@ -21,31 +21,37 @@ export default defineConfig({
|
|
|
21
21
|
});
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
-
`reactNative()` injects the React Native
|
|
24
|
+
`reactNative()` injects the React Native manifest resolver, the Vitest `node` environment, `globals: true`, Jest-like test globs, the packaged React Native runtime/setup files, and Jest-preset-style asset mocks.
|
|
25
25
|
|
|
26
|
-
##
|
|
26
|
+
## Transform Cache
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
React Native sources are stored as a content-addressed flat transform cache:
|
|
29
29
|
|
|
30
30
|
- cache root: `<cacheDir>/vitest-react-native-cache`
|
|
31
|
-
-
|
|
31
|
+
- transformed objects: `<cacheDir>/vitest-react-native-cache/entries/<cache-key>/objects/<content-hash>.<ext>`
|
|
32
|
+
- manifest: `<cacheDir>/vitest-react-native-cache/entries/<cache-key>/manifest.json`
|
|
32
33
|
- metadata: `<cacheDir>/vitest-react-native-cache/entries/<cache-key>/meta.json`
|
|
33
34
|
|
|
34
35
|
By default, `cacheDir` is Vitest's `.vitest` directory unless your `vitest.config.*` overrides it.
|
|
35
36
|
|
|
36
37
|
The cache key is derived from:
|
|
37
38
|
|
|
38
|
-
- the
|
|
39
|
+
- the `react-native` and installed RN-family allowlist package contents
|
|
39
40
|
- the local mirror/transform implementation
|
|
40
41
|
- transform dependency versions
|
|
41
42
|
|
|
42
|
-
If the cache key matches, `reactNative()` reuses the existing
|
|
43
|
+
If the cache key matches, `reactNative()` reuses the existing transform cache immediately instead of rebuilding it.
|
|
44
|
+
Only one completed React Native transform cache entry is retained per cache directory. When the cache key changes,
|
|
45
|
+
the previous entry is removed before the new cache is transformed and materialized.
|
|
43
46
|
|
|
44
|
-
|
|
47
|
+
The cache intentionally does not materialize package trees such as `packages/react-native/package.json`.
|
|
48
|
+
React Native package and relative imports are resolved through `manifest.json` and then redirected to flat
|
|
49
|
+
object files, so file scanners do not discover duplicate package boundaries inside the cache.
|
|
50
|
+
|
|
51
|
+
Local garbage collection runs opportunistically while resolving the transform cache:
|
|
45
52
|
|
|
46
|
-
- stale entries older than 7 days are removed
|
|
47
53
|
- temporary entries left behind by interrupted builds are removed
|
|
48
|
-
-
|
|
54
|
+
- inactive completed entries are removed
|
|
49
55
|
|
|
50
56
|
## React Native Jest Preset Parity
|
|
51
57
|
|
package/dist/index.d.ts
CHANGED
|
@@ -7,11 +7,43 @@ declare const REACT_NATIVE_ASSET_EXTENSIONS: readonly ["bmp", "gif", "jpg", "jpe
|
|
|
7
7
|
declare const DEFAULT_PLATFORM = "ios";
|
|
8
8
|
declare const GRANITE_VITEST_RN_CACHE_DIRECTORY = "vitest-react-native-cache";
|
|
9
9
|
declare const GRANITE_VITEST_RN_CACHE_ENTRIES_DIRECTORY = "entries";
|
|
10
|
+
declare const GRANITE_VITEST_RN_OBJECTS_DIRECTORY = "objects";
|
|
11
|
+
declare const GRANITE_VITEST_RN_MANIFEST_FILENAME = "manifest.json";
|
|
10
12
|
declare const GRANITE_VITEST_RN_PACKAGES_DIRECTORY = "packages";
|
|
11
13
|
declare const REACT_NATIVE_PLATFORMS: readonly ["android", "ios", "native"];
|
|
14
|
+
type ReactNativePackageEntry = {
|
|
15
|
+
packageName: string;
|
|
16
|
+
packageRoot: string;
|
|
17
|
+
requireRoot: string;
|
|
18
|
+
};
|
|
19
|
+
type ReactNativeTransformCacheManifest = {
|
|
20
|
+
cacheKey: string;
|
|
21
|
+
createdAt: string;
|
|
22
|
+
lastAccessedAt: string;
|
|
23
|
+
objectsRoot: string;
|
|
24
|
+
packageRoots: string[];
|
|
25
|
+
packages: ReactNativePackageEntry[];
|
|
26
|
+
sourceToObject: Record<string, string>;
|
|
27
|
+
objectToSource: Record<string, string>;
|
|
28
|
+
transformDependencyVersions: {
|
|
29
|
+
flowRemoveTypes: string;
|
|
30
|
+
};
|
|
31
|
+
transformImplementationHash: string;
|
|
32
|
+
};
|
|
33
|
+
type ReactNativeTransformCache = {
|
|
34
|
+
entryRoot: string;
|
|
35
|
+
manifest: ReactNativeTransformCacheManifest;
|
|
36
|
+
manifestPath: string;
|
|
37
|
+
objectsRoot: string;
|
|
38
|
+
};
|
|
39
|
+
type ReactNativeResolvedModule = {
|
|
40
|
+
objectPath: string;
|
|
41
|
+
sourcePath: string;
|
|
42
|
+
};
|
|
12
43
|
declare function resolvePackageRoot(packageName: string, requireRoot: string): string;
|
|
13
44
|
declare function resolveReactNativePackageRoots(workspaceRoot: string): string[];
|
|
14
|
-
declare function
|
|
45
|
+
declare function resolveReactNativeModuleFromManifest(request: string, importer: string | undefined, manifest: ReactNativeTransformCacheManifest): ReactNativeResolvedModule | null;
|
|
46
|
+
declare function buildReactNativeTransformCache(workspaceRoot: string, resolvedCacheDir?: string): Promise<ReactNativeTransformCache>;
|
|
15
47
|
declare function buildReactNativeMirror(workspaceRoot: string, resolvedCacheDir?: string): Promise<string>;
|
|
16
48
|
//#endregion
|
|
17
49
|
//#region src/transpile.d.ts
|
|
@@ -33,4 +65,4 @@ declare const _default: {
|
|
|
33
65
|
resolveReactNativeSetupFiles: typeof resolveReactNativeSetupFiles;
|
|
34
66
|
};
|
|
35
67
|
//#endregion
|
|
36
|
-
export { DEFAULT_PLATFORM, GRANITE_VITEST_RN_CACHE_DIRECTORY, GRANITE_VITEST_RN_CACHE_ENTRIES_DIRECTORY, GRANITE_VITEST_RN_PACKAGES_DIRECTORY, JEST_LIKE_TEST_PATTERNS, REACT_NATIVE_ASSET_EXTENSIONS, REACT_NATIVE_EXPORT_CONDITIONS, REACT_NATIVE_PLATFORMS, REACT_NATIVE_RESOLVE_EXTENSIONS, REACT_NATIVE_TRANSFORM_ALLOWLIST, REACT_NATIVE_TRANSFORM_EXTENSIONS, buildReactNativeMirror, _default as default, reactNative, resolvePackageRoot, resolveReactNativePackageRoots, resolveReactNativeSetupFiles, shouldInlineReactNativeDependency, shouldTransformReactNativeFile
|
|
68
|
+
export { DEFAULT_PLATFORM, GRANITE_VITEST_RN_CACHE_DIRECTORY, GRANITE_VITEST_RN_CACHE_ENTRIES_DIRECTORY, GRANITE_VITEST_RN_MANIFEST_FILENAME, GRANITE_VITEST_RN_OBJECTS_DIRECTORY, GRANITE_VITEST_RN_PACKAGES_DIRECTORY, JEST_LIKE_TEST_PATTERNS, REACT_NATIVE_ASSET_EXTENSIONS, REACT_NATIVE_EXPORT_CONDITIONS, REACT_NATIVE_PLATFORMS, REACT_NATIVE_RESOLVE_EXTENSIONS, REACT_NATIVE_TRANSFORM_ALLOWLIST, REACT_NATIVE_TRANSFORM_EXTENSIONS, buildReactNativeMirror, buildReactNativeTransformCache, _default as default, reactNative, resolvePackageRoot, resolveReactNativeModuleFromManifest, resolveReactNativePackageRoots, resolveReactNativeSetupFiles, shouldInlineReactNativeDependency, shouldTransformReactNativeFile };
|
package/dist/index.js
CHANGED
|
@@ -1,395 +1,9 @@
|
|
|
1
|
-
import { n as
|
|
2
|
-
import { createRequire } from "node:module";
|
|
1
|
+
import { _ as shouldTransformReactNativeFile, a as GRANITE_VITEST_RN_OBJECTS_DIRECTORY, c as buildReactNativeMirror, d as resolvePackageRoot, f as resolveReactNativeModuleFromManifest, g as shouldInlineReactNativeDependency, h as REACT_NATIVE_TRANSFORM_EXTENSIONS, i as GRANITE_VITEST_RN_MANIFEST_FILENAME, l as buildReactNativeTransformCache, m as REACT_NATIVE_TRANSFORM_ALLOWLIST, n as GRANITE_VITEST_RN_CACHE_DIRECTORY, o as GRANITE_VITEST_RN_PACKAGES_DIRECTORY, p as resolveReactNativePackageRoots, r as GRANITE_VITEST_RN_CACHE_ENTRIES_DIRECTORY, s as REACT_NATIVE_PLATFORMS, t as DEFAULT_PLATFORM, v as REACT_NATIVE_ASSET_EXTENSIONS, y as REACT_NATIVE_ASSET_MODULE_ID_PATTERN } from "./mirror-e92G5HdG.js";
|
|
3
2
|
import fs from "node:fs";
|
|
4
3
|
import path from "node:path";
|
|
5
4
|
import { fileURLToPath } from "node:url";
|
|
6
5
|
import * as vite from "vitest/node";
|
|
7
|
-
import { createHash } from "node:crypto";
|
|
8
|
-
import flowRemoveTypes from "flow-remove-types";
|
|
9
6
|
|
|
10
|
-
//#region src/transpile.ts
|
|
11
|
-
const REACT_NATIVE_TRANSFORM_EXTENSIONS = [
|
|
12
|
-
".js",
|
|
13
|
-
".jsx",
|
|
14
|
-
".ts",
|
|
15
|
-
".tsx"
|
|
16
|
-
];
|
|
17
|
-
const REACT_NATIVE_TRANSFORM_ALLOWLIST = [
|
|
18
|
-
"react-native",
|
|
19
|
-
"jest-react-native",
|
|
20
|
-
"@react-native",
|
|
21
|
-
"@react-native-community"
|
|
22
|
-
];
|
|
23
|
-
function normalizePath(filename) {
|
|
24
|
-
return filename.replace(/\\/g, "/");
|
|
25
|
-
}
|
|
26
|
-
function isWithinPackageRoots(filename, packageRoots) {
|
|
27
|
-
const normalizedFilename = normalizePath(path.resolve(filename));
|
|
28
|
-
return packageRoots.some((packageRoot) => {
|
|
29
|
-
const normalizedPackageRoot = normalizePath(path.resolve(packageRoot));
|
|
30
|
-
return normalizedFilename === normalizedPackageRoot || normalizedFilename.startsWith(`${normalizedPackageRoot}/`);
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
function shouldTransformReactNativeFile(filename, packageRoots = []) {
|
|
34
|
-
const normalized = normalizePath(filename);
|
|
35
|
-
const isReactNativeSource = packageRoots.length > 0 ? isWithinPackageRoots(normalized, packageRoots) : normalized.includes("/react-native/") || normalized.includes("/@react-native/");
|
|
36
|
-
return /\.(?:js|jsx|ts|tsx)$/.test(normalized) && !normalized.includes("/Renderer/implementations/") && isReactNativeSource;
|
|
37
|
-
}
|
|
38
|
-
function shouldInlineReactNativeDependency(filename, packageRoots = []) {
|
|
39
|
-
const normalized = normalizePath(filename);
|
|
40
|
-
if (packageRoots.length > 0) return isWithinPackageRoots(normalized, packageRoots);
|
|
41
|
-
return REACT_NATIVE_TRANSFORM_ALLOWLIST.some((allowlistEntry) => normalized.includes(`/${allowlistEntry}/`));
|
|
42
|
-
}
|
|
43
|
-
async function transformReactNativeSource(_sourcePath, source) {
|
|
44
|
-
return flowRemoveTypes(source, {
|
|
45
|
-
all: true,
|
|
46
|
-
pretty: true,
|
|
47
|
-
removeEmptyImports: true
|
|
48
|
-
}).toString();
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
//#endregion
|
|
52
|
-
//#region src/mirror.ts
|
|
53
|
-
const DEFAULT_PLATFORM = "ios";
|
|
54
|
-
const GRANITE_VITEST_RN_CACHE_DIRECTORY = "vitest-react-native-cache";
|
|
55
|
-
const GRANITE_VITEST_RN_CACHE_ENTRIES_DIRECTORY = "entries";
|
|
56
|
-
const GRANITE_VITEST_RN_PACKAGES_DIRECTORY = "packages";
|
|
57
|
-
const REACT_NATIVE_PLATFORMS = [
|
|
58
|
-
"android",
|
|
59
|
-
"ios",
|
|
60
|
-
"native"
|
|
61
|
-
];
|
|
62
|
-
const GRANITE_VITEST_RN_CACHE_GC_LOCK_FILENAME = ".gc.lock";
|
|
63
|
-
const GRANITE_VITEST_RN_CACHE_METADATA_FILENAME = "meta.json";
|
|
64
|
-
const GRANITE_VITEST_RN_CACHE_TMP_PREFIX = ".tmp-";
|
|
65
|
-
const GRANITE_VITEST_RN_CACHE_MAX_AGE_MS = 10080 * 60 * 1e3;
|
|
66
|
-
const GRANITE_VITEST_RN_CACHE_MAX_SIZE_BYTES = 1024 * 1024 * 1024;
|
|
67
|
-
const GRANITE_VITEST_RN_TEMP_ENTRY_MAX_AGE_MS = 3600 * 1e3;
|
|
68
|
-
const DEFAULT_VITEST_CACHE_DIRECTORY = ".vitest";
|
|
69
|
-
const MIRROR_CACHE_KEY_VERSION = "v1";
|
|
70
|
-
const currentMirrorModulePath = fs.realpathSync(fileURLToPath(import.meta.url));
|
|
71
|
-
const currentMirrorModuleDirectory = path.dirname(currentMirrorModulePath);
|
|
72
|
-
function getFlowRemoveTypesVersion() {
|
|
73
|
-
const flowRemoveTypesPackageJsonPath = createRequire(import.meta.url).resolve("flow-remove-types/package.json");
|
|
74
|
-
return JSON.parse(fs.readFileSync(flowRemoveTypesPackageJsonPath, "utf8")).version ?? "unknown";
|
|
75
|
-
}
|
|
76
|
-
function getPackageManifestDependencyNames(packageManifest) {
|
|
77
|
-
return Object.keys({
|
|
78
|
-
...packageManifest.dependencies ?? {},
|
|
79
|
-
...packageManifest.devDependencies ?? {},
|
|
80
|
-
...packageManifest.optionalDependencies ?? {},
|
|
81
|
-
...packageManifest.peerDependencies ?? {}
|
|
82
|
-
}).sort();
|
|
83
|
-
}
|
|
84
|
-
function shouldMirrorReactNativeDependency(packageName) {
|
|
85
|
-
return packageName === "jest-react-native" || packageName.startsWith("@react-native/") || packageName.startsWith("@react-native-community/");
|
|
86
|
-
}
|
|
87
|
-
function hashDirectoryContents(hasher, rootPath, currentPath = rootPath) {
|
|
88
|
-
const entries = fs.readdirSync(currentPath, { withFileTypes: true }).sort((left, right) => left.name.localeCompare(right.name));
|
|
89
|
-
for (const entry of entries) {
|
|
90
|
-
const entryPath = path.join(currentPath, entry.name);
|
|
91
|
-
if (entry.isDirectory()) {
|
|
92
|
-
hashDirectoryContents(hasher, rootPath, entryPath);
|
|
93
|
-
continue;
|
|
94
|
-
}
|
|
95
|
-
if (!entry.isFile()) continue;
|
|
96
|
-
hasher.update(path.relative(rootPath, entryPath));
|
|
97
|
-
hasher.update("\0");
|
|
98
|
-
hasher.update(fs.readFileSync(entryPath));
|
|
99
|
-
hasher.update("\0");
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
function hashImplementationFiles() {
|
|
103
|
-
const hasher = createHash("sha256");
|
|
104
|
-
const extension = path.extname(currentMirrorModulePath);
|
|
105
|
-
const implementationFiles = [currentMirrorModulePath, path.join(currentMirrorModuleDirectory, `transpile${extension}`)].filter((implementationFile, index, allFiles) => {
|
|
106
|
-
if (!fs.existsSync(implementationFile)) return false;
|
|
107
|
-
return allFiles.indexOf(implementationFile) === index;
|
|
108
|
-
}).sort();
|
|
109
|
-
for (const implementationFile of implementationFiles) {
|
|
110
|
-
hasher.update(path.basename(implementationFile));
|
|
111
|
-
hasher.update("\0");
|
|
112
|
-
hasher.update(fs.readFileSync(implementationFile));
|
|
113
|
-
hasher.update("\0");
|
|
114
|
-
}
|
|
115
|
-
return hasher.digest("hex");
|
|
116
|
-
}
|
|
117
|
-
function collectMirroredReactNativePackageNames(workspacePackageManifest, reactNativePackageManifest) {
|
|
118
|
-
const packageNames = /* @__PURE__ */ new Set();
|
|
119
|
-
for (const dependencyName of Object.keys(reactNativePackageManifest.dependencies ?? {})) if (dependencyName.startsWith("@react-native/")) packageNames.add(dependencyName);
|
|
120
|
-
for (const dependencyName of getPackageManifestDependencyNames(workspacePackageManifest)) if (dependencyName !== "react-native" && shouldMirrorReactNativeDependency(dependencyName)) packageNames.add(dependencyName);
|
|
121
|
-
return [...packageNames].sort();
|
|
122
|
-
}
|
|
123
|
-
function getMirroredReactNativePackages(workspaceRoot) {
|
|
124
|
-
const reactNativeRoot = resolvePackageRoot("react-native", workspaceRoot);
|
|
125
|
-
const workspacePackageManifest = JSON.parse(fs.readFileSync(path.join(workspaceRoot, "package.json"), "utf8"));
|
|
126
|
-
const reactNativePackageManifest = JSON.parse(fs.readFileSync(path.join(reactNativeRoot, "package.json"), "utf8"));
|
|
127
|
-
const workspaceDependencyNames = new Set(getPackageManifestDependencyNames(workspacePackageManifest));
|
|
128
|
-
return collectMirroredReactNativePackageNames(workspacePackageManifest, reactNativePackageManifest).map((packageName) => ({
|
|
129
|
-
packageName,
|
|
130
|
-
requireRoot: workspaceDependencyNames.has(packageName) ? workspaceRoot : reactNativeRoot
|
|
131
|
-
}));
|
|
132
|
-
}
|
|
133
|
-
function computeReactNativeMirrorCacheKey(workspaceRoot) {
|
|
134
|
-
const hasher = createHash("sha256");
|
|
135
|
-
const packageRoots = resolveReactNativePackageRoots(workspaceRoot).sort();
|
|
136
|
-
const transformImplementationHash = hashImplementationFiles();
|
|
137
|
-
const transformDependencyVersions = { flowRemoveTypes: getFlowRemoveTypesVersion() };
|
|
138
|
-
hasher.update(`granite-vitest-rn-cache:${MIRROR_CACHE_KEY_VERSION}`);
|
|
139
|
-
hasher.update("\0");
|
|
140
|
-
hasher.update(`flow-remove-types:${transformDependencyVersions.flowRemoveTypes}`);
|
|
141
|
-
hasher.update("\0");
|
|
142
|
-
hasher.update(`transform:${transformImplementationHash}`);
|
|
143
|
-
hasher.update("\0");
|
|
144
|
-
for (const packageRoot of packageRoots) {
|
|
145
|
-
hasher.update(path.basename(packageRoot));
|
|
146
|
-
hasher.update("\0");
|
|
147
|
-
hashDirectoryContents(hasher, packageRoot);
|
|
148
|
-
}
|
|
149
|
-
return {
|
|
150
|
-
cacheKey: hasher.digest("hex"),
|
|
151
|
-
packageRoots,
|
|
152
|
-
transformDependencyVersions,
|
|
153
|
-
transformImplementationHash
|
|
154
|
-
};
|
|
155
|
-
}
|
|
156
|
-
function getDirectorySize(targetPath) {
|
|
157
|
-
const stat = fs.statSync(targetPath);
|
|
158
|
-
if (stat.isFile()) return stat.size;
|
|
159
|
-
if (!stat.isDirectory()) return 0;
|
|
160
|
-
return fs.readdirSync(targetPath, { withFileTypes: true }).reduce((totalSize, entry) => {
|
|
161
|
-
const entryPath = path.join(targetPath, entry.name);
|
|
162
|
-
if (entry.isDirectory() || entry.isFile()) return totalSize + getDirectorySize(entryPath);
|
|
163
|
-
return totalSize;
|
|
164
|
-
}, 0);
|
|
165
|
-
}
|
|
166
|
-
function readMirrorCacheMetadata(entryRoot) {
|
|
167
|
-
const metadataPath = path.join(entryRoot, GRANITE_VITEST_RN_CACHE_METADATA_FILENAME);
|
|
168
|
-
if (!fs.existsSync(metadataPath)) return null;
|
|
169
|
-
try {
|
|
170
|
-
return JSON.parse(fs.readFileSync(metadataPath, "utf8"));
|
|
171
|
-
} catch {
|
|
172
|
-
return null;
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
function withMirrorCacheGcLock(workspaceRoot, resolvedCacheDir = path.join(workspaceRoot, DEFAULT_VITEST_CACHE_DIRECTORY), callback) {
|
|
176
|
-
const cacheRoot = path.join(resolvedCacheDir, GRANITE_VITEST_RN_CACHE_DIRECTORY);
|
|
177
|
-
const lockPath = path.join(cacheRoot, GRANITE_VITEST_RN_CACHE_GC_LOCK_FILENAME);
|
|
178
|
-
fs.mkdirSync(cacheRoot, { recursive: true });
|
|
179
|
-
let lockHandle = null;
|
|
180
|
-
try {
|
|
181
|
-
lockHandle = fs.openSync(lockPath, "wx");
|
|
182
|
-
} catch (error) {
|
|
183
|
-
if (error.code === "EEXIST") return;
|
|
184
|
-
throw error;
|
|
185
|
-
}
|
|
186
|
-
try {
|
|
187
|
-
callback();
|
|
188
|
-
} finally {
|
|
189
|
-
if (lockHandle != null) {
|
|
190
|
-
fs.closeSync(lockHandle);
|
|
191
|
-
fs.rmSync(lockPath, {
|
|
192
|
-
force: true,
|
|
193
|
-
recursive: true
|
|
194
|
-
});
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
function gcReactNativeMirrorCache(workspaceRoot, activeCacheKey, resolvedCacheDir = path.join(workspaceRoot, DEFAULT_VITEST_CACHE_DIRECTORY)) {
|
|
199
|
-
withMirrorCacheGcLock(workspaceRoot, resolvedCacheDir, () => {
|
|
200
|
-
const entriesRoot = path.join(resolvedCacheDir, GRANITE_VITEST_RN_CACHE_DIRECTORY, GRANITE_VITEST_RN_CACHE_ENTRIES_DIRECTORY);
|
|
201
|
-
if (!fs.existsSync(entriesRoot)) return;
|
|
202
|
-
const now = Date.now();
|
|
203
|
-
const retainedEntries = [];
|
|
204
|
-
for (const entry of fs.readdirSync(entriesRoot, { withFileTypes: true })) {
|
|
205
|
-
if (!entry.isDirectory()) continue;
|
|
206
|
-
const entryRoot = path.join(entriesRoot, entry.name);
|
|
207
|
-
if (entry.name.startsWith(GRANITE_VITEST_RN_CACHE_TMP_PREFIX)) {
|
|
208
|
-
if (now - fs.statSync(entryRoot).mtimeMs > GRANITE_VITEST_RN_TEMP_ENTRY_MAX_AGE_MS) fs.rmSync(entryRoot, {
|
|
209
|
-
force: true,
|
|
210
|
-
recursive: true
|
|
211
|
-
});
|
|
212
|
-
continue;
|
|
213
|
-
}
|
|
214
|
-
if (entry.name === activeCacheKey) {
|
|
215
|
-
const activeMetadata = readMirrorCacheMetadata(entryRoot);
|
|
216
|
-
retainedEntries.push({
|
|
217
|
-
cacheKey: entry.name,
|
|
218
|
-
entryRoot,
|
|
219
|
-
lastAccessedAtMs: activeMetadata == null ? now : Date.parse(activeMetadata.lastAccessedAt),
|
|
220
|
-
sizeBytes: activeMetadata?.sizeBytes ?? getDirectorySize(path.join(entryRoot, GRANITE_VITEST_RN_PACKAGES_DIRECTORY))
|
|
221
|
-
});
|
|
222
|
-
continue;
|
|
223
|
-
}
|
|
224
|
-
const metadata = readMirrorCacheMetadata(entryRoot);
|
|
225
|
-
const packagesRoot = path.join(entryRoot, GRANITE_VITEST_RN_PACKAGES_DIRECTORY);
|
|
226
|
-
if (metadata == null || !fs.existsSync(packagesRoot)) {
|
|
227
|
-
fs.rmSync(entryRoot, {
|
|
228
|
-
force: true,
|
|
229
|
-
recursive: true
|
|
230
|
-
});
|
|
231
|
-
continue;
|
|
232
|
-
}
|
|
233
|
-
const lastAccessedAtMs = Date.parse(metadata.lastAccessedAt);
|
|
234
|
-
if (Number.isNaN(lastAccessedAtMs) || now - lastAccessedAtMs > GRANITE_VITEST_RN_CACHE_MAX_AGE_MS) {
|
|
235
|
-
fs.rmSync(entryRoot, {
|
|
236
|
-
force: true,
|
|
237
|
-
recursive: true
|
|
238
|
-
});
|
|
239
|
-
continue;
|
|
240
|
-
}
|
|
241
|
-
retainedEntries.push({
|
|
242
|
-
cacheKey: entry.name,
|
|
243
|
-
entryRoot,
|
|
244
|
-
lastAccessedAtMs,
|
|
245
|
-
sizeBytes: metadata.sizeBytes
|
|
246
|
-
});
|
|
247
|
-
}
|
|
248
|
-
let totalSizeBytes = retainedEntries.reduce((totalSize, entry) => totalSize + entry.sizeBytes, 0);
|
|
249
|
-
for (const entry of retainedEntries.filter((cacheEntry) => cacheEntry.cacheKey !== activeCacheKey).sort((left, right) => left.lastAccessedAtMs - right.lastAccessedAtMs)) {
|
|
250
|
-
if (totalSizeBytes <= GRANITE_VITEST_RN_CACHE_MAX_SIZE_BYTES) break;
|
|
251
|
-
fs.rmSync(entry.entryRoot, {
|
|
252
|
-
force: true,
|
|
253
|
-
recursive: true
|
|
254
|
-
});
|
|
255
|
-
totalSizeBytes -= entry.sizeBytes;
|
|
256
|
-
}
|
|
257
|
-
});
|
|
258
|
-
}
|
|
259
|
-
function resolvePackageRoot(packageName, requireRoot) {
|
|
260
|
-
const packageJsonPath = createRequire(path.join(requireRoot, "package.json")).resolve(`${packageName}/package.json`);
|
|
261
|
-
return path.dirname(packageJsonPath);
|
|
262
|
-
}
|
|
263
|
-
function resolveReactNativePackageRoots(workspaceRoot) {
|
|
264
|
-
return [resolvePackageRoot("react-native", workspaceRoot), ...getMirroredReactNativePackages(workspaceRoot).map(({ packageName, requireRoot }) => resolvePackageRoot(packageName, requireRoot))];
|
|
265
|
-
}
|
|
266
|
-
async function mirrorFile(sourcePath, destinationPath, packageRoots) {
|
|
267
|
-
fs.mkdirSync(path.dirname(destinationPath), { recursive: true });
|
|
268
|
-
if (shouldTransformReactNativeFile(sourcePath, packageRoots)) {
|
|
269
|
-
const source = fs.readFileSync(sourcePath, "utf8");
|
|
270
|
-
try {
|
|
271
|
-
const transformed = await transformReactNativeSource(sourcePath, source);
|
|
272
|
-
fs.writeFileSync(destinationPath, transformed);
|
|
273
|
-
return;
|
|
274
|
-
} catch {
|
|
275
|
-
fs.writeFileSync(destinationPath, source);
|
|
276
|
-
return;
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
fs.copyFileSync(sourcePath, destinationPath);
|
|
280
|
-
}
|
|
281
|
-
async function mirrorTree(sourceRoot, destinationRoot, packageRoots) {
|
|
282
|
-
const entries = fs.readdirSync(sourceRoot, { withFileTypes: true }).sort((left, right) => left.name.localeCompare(right.name));
|
|
283
|
-
for (const entry of entries) {
|
|
284
|
-
const sourcePath = path.join(sourceRoot, entry.name);
|
|
285
|
-
const destinationPath = path.join(destinationRoot, entry.name);
|
|
286
|
-
if (entry.isDirectory()) {
|
|
287
|
-
await mirrorTree(sourcePath, destinationPath, packageRoots);
|
|
288
|
-
continue;
|
|
289
|
-
}
|
|
290
|
-
if (!entry.isFile()) continue;
|
|
291
|
-
await mirrorFile(sourcePath, destinationPath, packageRoots);
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
function synthesizeDefaultPlatformFiles(destinationRoot) {
|
|
295
|
-
const platformFallbackOrder = [
|
|
296
|
-
DEFAULT_PLATFORM,
|
|
297
|
-
"native",
|
|
298
|
-
...REACT_NATIVE_PLATFORMS.filter((platform) => platform !== DEFAULT_PLATFORM && platform !== "native")
|
|
299
|
-
];
|
|
300
|
-
const platformPriority = new Map(platformFallbackOrder.map((platform, index) => [platform, index]));
|
|
301
|
-
const fallbackCandidates = [];
|
|
302
|
-
function collectCandidates(currentRoot) {
|
|
303
|
-
const entries = fs.readdirSync(currentRoot, { withFileTypes: true });
|
|
304
|
-
for (const entry of entries) {
|
|
305
|
-
const entryPath = path.join(currentRoot, entry.name);
|
|
306
|
-
if (entry.isDirectory()) {
|
|
307
|
-
collectCandidates(entryPath);
|
|
308
|
-
continue;
|
|
309
|
-
}
|
|
310
|
-
const match = entry.name.match(new RegExp(`^(.*)\\.(${REACT_NATIVE_PLATFORMS.join("|")})\\.(js|jsx|ts|tsx)$`));
|
|
311
|
-
if (match == null) continue;
|
|
312
|
-
const [, basename, platform, extension] = match;
|
|
313
|
-
if (basename == null || platform == null || extension == null) continue;
|
|
314
|
-
fallbackCandidates.push({
|
|
315
|
-
sourcePath: entryPath,
|
|
316
|
-
fallbackPath: path.join(currentRoot, `${basename}.${extension}`),
|
|
317
|
-
priority: platformPriority.get(platform) ?? platformFallbackOrder.length
|
|
318
|
-
});
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
collectCandidates(destinationRoot);
|
|
322
|
-
fallbackCandidates.sort((left, right) => left.priority - right.priority).forEach(({ sourcePath, fallbackPath }) => {
|
|
323
|
-
if (!fs.existsSync(fallbackPath)) fs.copyFileSync(sourcePath, fallbackPath);
|
|
324
|
-
});
|
|
325
|
-
}
|
|
326
|
-
async function buildReactNativeMirror(workspaceRoot, resolvedCacheDir = path.join(workspaceRoot, DEFAULT_VITEST_CACHE_DIRECTORY)) {
|
|
327
|
-
const cacheEntry = computeReactNativeMirrorCacheKey(workspaceRoot);
|
|
328
|
-
const entriesRoot = path.join(resolvedCacheDir, GRANITE_VITEST_RN_CACHE_DIRECTORY, GRANITE_VITEST_RN_CACHE_ENTRIES_DIRECTORY);
|
|
329
|
-
const entryRoot = path.join(entriesRoot, cacheEntry.cacheKey);
|
|
330
|
-
const packagesRoot = path.join(entryRoot, GRANITE_VITEST_RN_PACKAGES_DIRECTORY);
|
|
331
|
-
const metadataPath = path.join(entryRoot, GRANITE_VITEST_RN_CACHE_METADATA_FILENAME);
|
|
332
|
-
fs.mkdirSync(entriesRoot, { recursive: true });
|
|
333
|
-
if (fs.existsSync(entryRoot) && (!fs.existsSync(packagesRoot) || !fs.existsSync(metadataPath))) fs.rmSync(entryRoot, {
|
|
334
|
-
force: true,
|
|
335
|
-
recursive: true
|
|
336
|
-
});
|
|
337
|
-
if (fs.existsSync(packagesRoot) && fs.existsSync(metadataPath)) {
|
|
338
|
-
const metadata = readMirrorCacheMetadata(entryRoot);
|
|
339
|
-
if (metadata != null) {
|
|
340
|
-
metadata.lastAccessedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
341
|
-
fs.writeFileSync(metadataPath, JSON.stringify(metadata, null, 2));
|
|
342
|
-
}
|
|
343
|
-
gcReactNativeMirrorCache(workspaceRoot, cacheEntry.cacheKey, resolvedCacheDir);
|
|
344
|
-
return packagesRoot;
|
|
345
|
-
}
|
|
346
|
-
const temporaryEntryRoot = path.join(entriesRoot, `${GRANITE_VITEST_RN_CACHE_TMP_PREFIX}${process.pid}-${Date.now()}-${cacheEntry.cacheKey}`);
|
|
347
|
-
const temporaryPackagesRoot = path.join(temporaryEntryRoot, GRANITE_VITEST_RN_PACKAGES_DIRECTORY);
|
|
348
|
-
const temporaryMetadataPath = path.join(temporaryEntryRoot, GRANITE_VITEST_RN_CACHE_METADATA_FILENAME);
|
|
349
|
-
fs.rmSync(temporaryEntryRoot, {
|
|
350
|
-
force: true,
|
|
351
|
-
recursive: true
|
|
352
|
-
});
|
|
353
|
-
fs.mkdirSync(temporaryPackagesRoot, { recursive: true });
|
|
354
|
-
try {
|
|
355
|
-
await mirrorTree(resolvePackageRoot("react-native", workspaceRoot), path.join(temporaryPackagesRoot, "react-native"), cacheEntry.packageRoots);
|
|
356
|
-
for (const { packageName, requireRoot } of getMirroredReactNativePackages(workspaceRoot)) await mirrorTree(resolvePackageRoot(packageName, requireRoot), path.join(temporaryPackagesRoot, ...packageName.split("/")), cacheEntry.packageRoots);
|
|
357
|
-
synthesizeDefaultPlatformFiles(temporaryPackagesRoot);
|
|
358
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
359
|
-
fs.writeFileSync(temporaryMetadataPath, JSON.stringify({
|
|
360
|
-
cacheKey: cacheEntry.cacheKey,
|
|
361
|
-
createdAt: now,
|
|
362
|
-
lastAccessedAt: now,
|
|
363
|
-
packageRoots: cacheEntry.packageRoots,
|
|
364
|
-
sizeBytes: getDirectorySize(temporaryPackagesRoot),
|
|
365
|
-
transformDependencyVersions: cacheEntry.transformDependencyVersions,
|
|
366
|
-
transformImplementationHash: cacheEntry.transformImplementationHash
|
|
367
|
-
}, null, 2));
|
|
368
|
-
try {
|
|
369
|
-
fs.renameSync(temporaryEntryRoot, entryRoot);
|
|
370
|
-
} catch (error) {
|
|
371
|
-
const errorCode = error.code;
|
|
372
|
-
if (errorCode !== "EEXIST" && errorCode !== "ENOTEMPTY") throw error;
|
|
373
|
-
}
|
|
374
|
-
} finally {
|
|
375
|
-
fs.rmSync(temporaryEntryRoot, {
|
|
376
|
-
force: true,
|
|
377
|
-
recursive: true
|
|
378
|
-
});
|
|
379
|
-
}
|
|
380
|
-
if (fs.existsSync(packagesRoot) && fs.existsSync(metadataPath)) {
|
|
381
|
-
const metadata = readMirrorCacheMetadata(entryRoot);
|
|
382
|
-
if (metadata != null) {
|
|
383
|
-
metadata.lastAccessedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
384
|
-
fs.writeFileSync(metadataPath, JSON.stringify(metadata, null, 2));
|
|
385
|
-
}
|
|
386
|
-
gcReactNativeMirrorCache(workspaceRoot, cacheEntry.cacheKey, resolvedCacheDir);
|
|
387
|
-
return packagesRoot;
|
|
388
|
-
}
|
|
389
|
-
throw new Error(`Failed to materialize React Native mirror cache at ${entryRoot}`);
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
//#endregion
|
|
393
7
|
//#region src/reactNative.ts
|
|
394
8
|
const PLATFORM_RESOLUTION_ORDER = [
|
|
395
9
|
DEFAULT_PLATFORM,
|
|
@@ -429,49 +43,20 @@ function resolveReactNativeSetupFiles() {
|
|
|
429
43
|
return [path.join(currentDirectory, `reactNativeRuntime${extension}`), path.join(currentDirectory, `setup${extension}`)];
|
|
430
44
|
}
|
|
431
45
|
function reactNative() {
|
|
46
|
+
let reactNativeTransformCache = null;
|
|
432
47
|
return {
|
|
433
48
|
enforce: "pre",
|
|
434
49
|
name: "granite-react-native",
|
|
435
50
|
async config(conf) {
|
|
436
51
|
const workspaceRoot = conf.root ?? conf.test?.root ?? process.cwd();
|
|
437
52
|
const cacheDir = conf.cacheDir ?? ".vitest";
|
|
438
|
-
|
|
53
|
+
reactNativeTransformCache = await buildReactNativeTransformCache(workspaceRoot, path.isAbsolute(cacheDir) ? cacheDir : path.join(workspaceRoot, cacheDir));
|
|
439
54
|
const resolve = {
|
|
440
|
-
alias: [
|
|
441
|
-
{
|
|
442
|
-
find: /^react-native$/,
|
|
443
|
-
replacement: path.join(reactNativeMirrorRoot, "react-native", "index.js")
|
|
444
|
-
},
|
|
445
|
-
{
|
|
446
|
-
find: /^react-native\/(.*)$/,
|
|
447
|
-
replacement: path.join(reactNativeMirrorRoot, "react-native", "$1")
|
|
448
|
-
},
|
|
449
|
-
{
|
|
450
|
-
find: /^@react-native\/(.*)$/,
|
|
451
|
-
replacement: path.join(reactNativeMirrorRoot, "@react-native", "$1")
|
|
452
|
-
},
|
|
453
|
-
{
|
|
454
|
-
find: /^@react-native-community\/([^/]+)$/,
|
|
455
|
-
replacement: path.join(reactNativeMirrorRoot, "@react-native-community", "$1")
|
|
456
|
-
},
|
|
457
|
-
{
|
|
458
|
-
find: /^@react-native-community\/([^/]+)\/(.*)$/,
|
|
459
|
-
replacement: path.join(reactNativeMirrorRoot, "@react-native-community", "$1", "$2")
|
|
460
|
-
},
|
|
461
|
-
{
|
|
462
|
-
find: /^jest-react-native$/,
|
|
463
|
-
replacement: path.join(reactNativeMirrorRoot, "jest-react-native")
|
|
464
|
-
},
|
|
465
|
-
{
|
|
466
|
-
find: /^jest-react-native\/(.*)$/,
|
|
467
|
-
replacement: path.join(reactNativeMirrorRoot, "jest-react-native", "$1")
|
|
468
|
-
}
|
|
469
|
-
],
|
|
470
55
|
conditions: [...REACT_NATIVE_EXPORT_CONDITIONS],
|
|
471
56
|
extensions: [...REACT_NATIVE_RESOLVE_EXTENSIONS]
|
|
472
57
|
};
|
|
473
58
|
const commonConfig = {
|
|
474
|
-
define: { "globalThis.
|
|
59
|
+
define: { "globalThis.__GRANITE_VITEST_RN_CACHE_MANIFEST__": JSON.stringify(reactNativeTransformCache.manifestPath) },
|
|
475
60
|
resolve,
|
|
476
61
|
test: {
|
|
477
62
|
environment: "node",
|
|
@@ -495,6 +80,10 @@ function reactNative() {
|
|
|
495
80
|
...commonConfig
|
|
496
81
|
};
|
|
497
82
|
},
|
|
83
|
+
resolveId(id, importer) {
|
|
84
|
+
if (reactNativeTransformCache == null) return;
|
|
85
|
+
return resolveReactNativeModuleFromManifest(id, importer, reactNativeTransformCache.manifest)?.objectPath;
|
|
86
|
+
},
|
|
498
87
|
load(id) {
|
|
499
88
|
if (!REACT_NATIVE_ASSET_MODULE_ID_PATTERN.test(id)) return;
|
|
500
89
|
const asset = { testUri: path.relative(currentDirectory, id.replace(/[?#].*$/, "")).replace(/\\/g, "/") };
|
|
@@ -515,4 +104,4 @@ var src_default = {
|
|
|
515
104
|
};
|
|
516
105
|
|
|
517
106
|
//#endregion
|
|
518
|
-
export { DEFAULT_PLATFORM, GRANITE_VITEST_RN_CACHE_DIRECTORY, GRANITE_VITEST_RN_CACHE_ENTRIES_DIRECTORY, GRANITE_VITEST_RN_PACKAGES_DIRECTORY, JEST_LIKE_TEST_PATTERNS, REACT_NATIVE_ASSET_EXTENSIONS, REACT_NATIVE_EXPORT_CONDITIONS, REACT_NATIVE_PLATFORMS, REACT_NATIVE_RESOLVE_EXTENSIONS, REACT_NATIVE_TRANSFORM_ALLOWLIST, REACT_NATIVE_TRANSFORM_EXTENSIONS, buildReactNativeMirror, src_default as default, reactNative, resolvePackageRoot, resolveReactNativePackageRoots, resolveReactNativeSetupFiles, shouldInlineReactNativeDependency, shouldTransformReactNativeFile
|
|
107
|
+
export { DEFAULT_PLATFORM, GRANITE_VITEST_RN_CACHE_DIRECTORY, GRANITE_VITEST_RN_CACHE_ENTRIES_DIRECTORY, GRANITE_VITEST_RN_MANIFEST_FILENAME, GRANITE_VITEST_RN_OBJECTS_DIRECTORY, GRANITE_VITEST_RN_PACKAGES_DIRECTORY, JEST_LIKE_TEST_PATTERNS, REACT_NATIVE_ASSET_EXTENSIONS, REACT_NATIVE_EXPORT_CONDITIONS, REACT_NATIVE_PLATFORMS, REACT_NATIVE_RESOLVE_EXTENSIONS, REACT_NATIVE_TRANSFORM_ALLOWLIST, REACT_NATIVE_TRANSFORM_EXTENSIONS, buildReactNativeMirror, buildReactNativeTransformCache, src_default as default, reactNative, resolvePackageRoot, resolveReactNativeModuleFromManifest, resolveReactNativePackageRoots, resolveReactNativeSetupFiles, shouldInlineReactNativeDependency, shouldTransformReactNativeFile };
|
|
@@ -0,0 +1,484 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { createHash } from "node:crypto";
|
|
6
|
+
import flowRemoveTypes from "flow-remove-types";
|
|
7
|
+
|
|
8
|
+
//#region src/assets.ts
|
|
9
|
+
const REACT_NATIVE_ASSET_EXTENSIONS = [
|
|
10
|
+
"bmp",
|
|
11
|
+
"gif",
|
|
12
|
+
"jpg",
|
|
13
|
+
"jpeg",
|
|
14
|
+
"mp4",
|
|
15
|
+
"png",
|
|
16
|
+
"psd",
|
|
17
|
+
"svg",
|
|
18
|
+
"webp"
|
|
19
|
+
];
|
|
20
|
+
const REACT_NATIVE_ASSET_MODULE_ID_PATTERN = new RegExp(`\\.(${REACT_NATIVE_ASSET_EXTENSIONS.join("|")})(?:[?#].*)?$`, "i");
|
|
21
|
+
|
|
22
|
+
//#endregion
|
|
23
|
+
//#region src/transpile.ts
|
|
24
|
+
const REACT_NATIVE_TRANSFORM_EXTENSIONS = [
|
|
25
|
+
".js",
|
|
26
|
+
".jsx",
|
|
27
|
+
".ts",
|
|
28
|
+
".tsx"
|
|
29
|
+
];
|
|
30
|
+
const REACT_NATIVE_TRANSFORM_ALLOWLIST = [
|
|
31
|
+
"react-native",
|
|
32
|
+
"jest-react-native",
|
|
33
|
+
"@react-native",
|
|
34
|
+
"@react-native-community"
|
|
35
|
+
];
|
|
36
|
+
function normalizePath$1(filename) {
|
|
37
|
+
return filename.replace(/\\/g, "/");
|
|
38
|
+
}
|
|
39
|
+
function isWithinPackageRoots(filename, packageRoots) {
|
|
40
|
+
const normalizedFilename = normalizePath$1(path.resolve(filename));
|
|
41
|
+
return packageRoots.some((packageRoot) => {
|
|
42
|
+
const normalizedPackageRoot = normalizePath$1(path.resolve(packageRoot));
|
|
43
|
+
return normalizedFilename === normalizedPackageRoot || normalizedFilename.startsWith(`${normalizedPackageRoot}/`);
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
function shouldTransformReactNativeFile(filename, packageRoots = []) {
|
|
47
|
+
const normalized = normalizePath$1(filename);
|
|
48
|
+
const isReactNativeSource = packageRoots.length > 0 ? isWithinPackageRoots(normalized, packageRoots) : normalized.includes("/react-native/") || normalized.includes("/@react-native/");
|
|
49
|
+
return /\.(?:js|jsx|ts|tsx)$/.test(normalized) && !normalized.includes("/Renderer/implementations/") && isReactNativeSource;
|
|
50
|
+
}
|
|
51
|
+
function shouldInlineReactNativeDependency(filename, packageRoots = []) {
|
|
52
|
+
const normalized = normalizePath$1(filename);
|
|
53
|
+
if (packageRoots.length > 0) return isWithinPackageRoots(normalized, packageRoots);
|
|
54
|
+
return REACT_NATIVE_TRANSFORM_ALLOWLIST.some((allowlistEntry) => normalized.includes(`/${allowlistEntry}/`));
|
|
55
|
+
}
|
|
56
|
+
async function transformReactNativeSource(_sourcePath, source) {
|
|
57
|
+
return flowRemoveTypes(source, {
|
|
58
|
+
all: true,
|
|
59
|
+
pretty: true,
|
|
60
|
+
removeEmptyImports: true
|
|
61
|
+
}).toString();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
//#endregion
|
|
65
|
+
//#region src/mirror.ts
|
|
66
|
+
const DEFAULT_PLATFORM = "ios";
|
|
67
|
+
const GRANITE_VITEST_RN_CACHE_DIRECTORY = "vitest-react-native-cache";
|
|
68
|
+
const GRANITE_VITEST_RN_CACHE_ENTRIES_DIRECTORY = "entries";
|
|
69
|
+
const GRANITE_VITEST_RN_OBJECTS_DIRECTORY = "objects";
|
|
70
|
+
const GRANITE_VITEST_RN_MANIFEST_FILENAME = "manifest.json";
|
|
71
|
+
const GRANITE_VITEST_RN_PACKAGES_DIRECTORY = "packages";
|
|
72
|
+
const REACT_NATIVE_PLATFORMS = [
|
|
73
|
+
"android",
|
|
74
|
+
"ios",
|
|
75
|
+
"native"
|
|
76
|
+
];
|
|
77
|
+
const GRANITE_VITEST_RN_CACHE_GC_LOCK_FILENAME = ".gc.lock";
|
|
78
|
+
const GRANITE_VITEST_RN_CACHE_METADATA_FILENAME = "meta.json";
|
|
79
|
+
const GRANITE_VITEST_RN_CACHE_TMP_PREFIX = ".tmp-";
|
|
80
|
+
const GRANITE_VITEST_RN_TEMP_ENTRY_MAX_AGE_MS = 3600 * 1e3;
|
|
81
|
+
const DEFAULT_VITEST_CACHE_DIRECTORY = ".vitest";
|
|
82
|
+
const MIRROR_CACHE_KEY_VERSION = "v2-flat";
|
|
83
|
+
const PLATFORM_EXTENSION_SUFFIXES = [
|
|
84
|
+
"tsx",
|
|
85
|
+
"ts",
|
|
86
|
+
"jsx",
|
|
87
|
+
"js"
|
|
88
|
+
];
|
|
89
|
+
const FALLBACK_EXTENSIONS = [
|
|
90
|
+
...[
|
|
91
|
+
DEFAULT_PLATFORM,
|
|
92
|
+
"native",
|
|
93
|
+
...REACT_NATIVE_PLATFORMS.filter((platform) => platform !== DEFAULT_PLATFORM && platform !== "native")
|
|
94
|
+
].flatMap((platform) => PLATFORM_EXTENSION_SUFFIXES.map((extension) => `.${platform}.${extension}`)),
|
|
95
|
+
".tsx",
|
|
96
|
+
".ts",
|
|
97
|
+
".jsx",
|
|
98
|
+
".js",
|
|
99
|
+
".json",
|
|
100
|
+
...REACT_NATIVE_ASSET_EXTENSIONS.map((extension) => `.${extension}`)
|
|
101
|
+
];
|
|
102
|
+
const currentMirrorModulePath = fs.realpathSync(fileURLToPath(import.meta.url));
|
|
103
|
+
const currentMirrorModuleDirectory = path.dirname(currentMirrorModulePath);
|
|
104
|
+
function normalizePath(filename) {
|
|
105
|
+
return path.resolve(filename.replace(/[?#].*$/, ""));
|
|
106
|
+
}
|
|
107
|
+
function getFlowRemoveTypesVersion() {
|
|
108
|
+
const flowRemoveTypesPackageJsonPath = createRequire(import.meta.url).resolve("flow-remove-types/package.json");
|
|
109
|
+
return JSON.parse(fs.readFileSync(flowRemoveTypesPackageJsonPath, "utf8")).version ?? "unknown";
|
|
110
|
+
}
|
|
111
|
+
function getPackageManifestDependencyNames(packageManifest) {
|
|
112
|
+
return Object.keys({
|
|
113
|
+
...packageManifest.dependencies ?? {},
|
|
114
|
+
...packageManifest.devDependencies ?? {},
|
|
115
|
+
...packageManifest.optionalDependencies ?? {},
|
|
116
|
+
...packageManifest.peerDependencies ?? {}
|
|
117
|
+
}).sort();
|
|
118
|
+
}
|
|
119
|
+
function shouldMirrorReactNativeDependency(packageName) {
|
|
120
|
+
return packageName === "jest-react-native" || packageName.startsWith("@react-native/") || packageName.startsWith("@react-native-community/");
|
|
121
|
+
}
|
|
122
|
+
function hashDirectoryContents(hasher, rootPath, currentPath = rootPath) {
|
|
123
|
+
const entries = fs.readdirSync(currentPath, { withFileTypes: true }).sort((left, right) => left.name.localeCompare(right.name));
|
|
124
|
+
for (const entry of entries) {
|
|
125
|
+
const entryPath = path.join(currentPath, entry.name);
|
|
126
|
+
if (entry.isDirectory()) {
|
|
127
|
+
hashDirectoryContents(hasher, rootPath, entryPath);
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
if (!entry.isFile()) continue;
|
|
131
|
+
hasher.update(path.relative(rootPath, entryPath));
|
|
132
|
+
hasher.update("\0");
|
|
133
|
+
hasher.update(fs.readFileSync(entryPath));
|
|
134
|
+
hasher.update("\0");
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
function hashImplementationFiles() {
|
|
138
|
+
const hasher = createHash("sha256");
|
|
139
|
+
const extension = path.extname(currentMirrorModulePath);
|
|
140
|
+
const implementationFiles = [currentMirrorModulePath, path.join(currentMirrorModuleDirectory, `transpile${extension}`)].filter((implementationFile, index, allFiles) => {
|
|
141
|
+
if (!fs.existsSync(implementationFile)) return false;
|
|
142
|
+
return allFiles.indexOf(implementationFile) === index;
|
|
143
|
+
}).sort();
|
|
144
|
+
for (const implementationFile of implementationFiles) {
|
|
145
|
+
hasher.update(path.basename(implementationFile));
|
|
146
|
+
hasher.update("\0");
|
|
147
|
+
hasher.update(fs.readFileSync(implementationFile));
|
|
148
|
+
hasher.update("\0");
|
|
149
|
+
}
|
|
150
|
+
return hasher.digest("hex");
|
|
151
|
+
}
|
|
152
|
+
function collectMirroredReactNativePackageNames(workspacePackageManifest, reactNativePackageManifest) {
|
|
153
|
+
const packageNames = /* @__PURE__ */ new Set();
|
|
154
|
+
for (const dependencyName of Object.keys(reactNativePackageManifest.dependencies ?? {})) if (dependencyName.startsWith("@react-native/")) packageNames.add(dependencyName);
|
|
155
|
+
for (const dependencyName of getPackageManifestDependencyNames(workspacePackageManifest)) if (dependencyName !== "react-native" && shouldMirrorReactNativeDependency(dependencyName)) packageNames.add(dependencyName);
|
|
156
|
+
return [...packageNames].sort();
|
|
157
|
+
}
|
|
158
|
+
function getMirroredReactNativePackages(workspaceRoot) {
|
|
159
|
+
const reactNativeRoot = resolvePackageRoot("react-native", workspaceRoot);
|
|
160
|
+
const workspacePackageManifest = JSON.parse(fs.readFileSync(path.join(workspaceRoot, "package.json"), "utf8"));
|
|
161
|
+
const reactNativePackageManifest = JSON.parse(fs.readFileSync(path.join(reactNativeRoot, "package.json"), "utf8"));
|
|
162
|
+
const workspaceDependencyNames = new Set(getPackageManifestDependencyNames(workspacePackageManifest));
|
|
163
|
+
return collectMirroredReactNativePackageNames(workspacePackageManifest, reactNativePackageManifest).map((packageName) => ({
|
|
164
|
+
packageName,
|
|
165
|
+
requireRoot: workspaceDependencyNames.has(packageName) ? workspaceRoot : reactNativeRoot
|
|
166
|
+
}));
|
|
167
|
+
}
|
|
168
|
+
function getReactNativePackageEntries(workspaceRoot) {
|
|
169
|
+
return [{
|
|
170
|
+
packageName: "react-native",
|
|
171
|
+
packageRoot: resolvePackageRoot("react-native", workspaceRoot),
|
|
172
|
+
requireRoot: workspaceRoot
|
|
173
|
+
}, ...getMirroredReactNativePackages(workspaceRoot).map(({ packageName, requireRoot }) => ({
|
|
174
|
+
packageName,
|
|
175
|
+
packageRoot: resolvePackageRoot(packageName, requireRoot),
|
|
176
|
+
requireRoot
|
|
177
|
+
}))];
|
|
178
|
+
}
|
|
179
|
+
function computeReactNativeTransformCacheKey(workspaceRoot) {
|
|
180
|
+
const hasher = createHash("sha256");
|
|
181
|
+
const packageRoots = resolveReactNativePackageRoots(workspaceRoot).sort();
|
|
182
|
+
const transformImplementationHash = hashImplementationFiles();
|
|
183
|
+
const transformDependencyVersions = { flowRemoveTypes: getFlowRemoveTypesVersion() };
|
|
184
|
+
hasher.update(`granite-vitest-rn-cache:${MIRROR_CACHE_KEY_VERSION}`);
|
|
185
|
+
hasher.update("\0");
|
|
186
|
+
hasher.update(`flow-remove-types:${transformDependencyVersions.flowRemoveTypes}`);
|
|
187
|
+
hasher.update("\0");
|
|
188
|
+
hasher.update(`transform:${transformImplementationHash}`);
|
|
189
|
+
hasher.update("\0");
|
|
190
|
+
for (const packageRoot of packageRoots) {
|
|
191
|
+
hasher.update(path.basename(packageRoot));
|
|
192
|
+
hasher.update("\0");
|
|
193
|
+
hashDirectoryContents(hasher, packageRoot);
|
|
194
|
+
}
|
|
195
|
+
return {
|
|
196
|
+
cacheKey: hasher.digest("hex"),
|
|
197
|
+
packageRoots,
|
|
198
|
+
transformDependencyVersions,
|
|
199
|
+
transformImplementationHash
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
function getDirectorySize(targetPath) {
|
|
203
|
+
const stat = fs.statSync(targetPath);
|
|
204
|
+
if (stat.isFile()) return stat.size;
|
|
205
|
+
if (!stat.isDirectory()) return 0;
|
|
206
|
+
return fs.readdirSync(targetPath, { withFileTypes: true }).reduce((totalSize, entry) => {
|
|
207
|
+
const entryPath = path.join(targetPath, entry.name);
|
|
208
|
+
if (entry.isDirectory() || entry.isFile()) return totalSize + getDirectorySize(entryPath);
|
|
209
|
+
return totalSize;
|
|
210
|
+
}, 0);
|
|
211
|
+
}
|
|
212
|
+
function readMirrorCacheMetadata(entryRoot) {
|
|
213
|
+
const metadataPath = path.join(entryRoot, GRANITE_VITEST_RN_CACHE_METADATA_FILENAME);
|
|
214
|
+
if (!fs.existsSync(metadataPath)) return null;
|
|
215
|
+
try {
|
|
216
|
+
return JSON.parse(fs.readFileSync(metadataPath, "utf8"));
|
|
217
|
+
} catch {
|
|
218
|
+
return null;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
function gcReactNativeMirrorCache(workspaceRoot, activeCacheKey, resolvedCacheDir = path.join(workspaceRoot, DEFAULT_VITEST_CACHE_DIRECTORY)) {
|
|
222
|
+
gcReactNativeMirrorCacheAsync(activeCacheKey, resolvedCacheDir).catch(() => void 0);
|
|
223
|
+
}
|
|
224
|
+
async function gcReactNativeMirrorCacheAsync(activeCacheKey, resolvedCacheDir) {
|
|
225
|
+
const cacheRoot = path.join(resolvedCacheDir, GRANITE_VITEST_RN_CACHE_DIRECTORY);
|
|
226
|
+
const lockPath = path.join(cacheRoot, GRANITE_VITEST_RN_CACHE_GC_LOCK_FILENAME);
|
|
227
|
+
await fs.promises.mkdir(cacheRoot, { recursive: true });
|
|
228
|
+
let lockHandle = null;
|
|
229
|
+
try {
|
|
230
|
+
lockHandle = await fs.promises.open(lockPath, "wx");
|
|
231
|
+
} catch (error) {
|
|
232
|
+
if (error.code === "EEXIST") return;
|
|
233
|
+
throw error;
|
|
234
|
+
}
|
|
235
|
+
try {
|
|
236
|
+
const entriesRoot = path.join(resolvedCacheDir, GRANITE_VITEST_RN_CACHE_DIRECTORY, GRANITE_VITEST_RN_CACHE_ENTRIES_DIRECTORY);
|
|
237
|
+
if (!await fs.promises.access(entriesRoot).then(() => true).catch(() => false)) return;
|
|
238
|
+
const now = Date.now();
|
|
239
|
+
const removeEntryPromises = [];
|
|
240
|
+
for (const entry of await fs.promises.readdir(entriesRoot, { withFileTypes: true })) {
|
|
241
|
+
if (!entry.isDirectory()) continue;
|
|
242
|
+
const entryRoot = path.join(entriesRoot, entry.name);
|
|
243
|
+
if (entry.name.startsWith(GRANITE_VITEST_RN_CACHE_TMP_PREFIX)) {
|
|
244
|
+
const stat = await fs.promises.stat(entryRoot).catch(() => null);
|
|
245
|
+
if ((stat == null ? 0 : now - stat.mtimeMs) > GRANITE_VITEST_RN_TEMP_ENTRY_MAX_AGE_MS) removeEntryPromises.push(fs.promises.rm(entryRoot, {
|
|
246
|
+
force: true,
|
|
247
|
+
recursive: true
|
|
248
|
+
}));
|
|
249
|
+
continue;
|
|
250
|
+
}
|
|
251
|
+
if (entry.name === activeCacheKey) continue;
|
|
252
|
+
removeEntryPromises.push(fs.promises.rm(entryRoot, {
|
|
253
|
+
force: true,
|
|
254
|
+
recursive: true
|
|
255
|
+
}));
|
|
256
|
+
}
|
|
257
|
+
await Promise.allSettled(removeEntryPromises);
|
|
258
|
+
} catch {} finally {
|
|
259
|
+
if (lockHandle != null) {
|
|
260
|
+
await lockHandle.close().catch(() => void 0);
|
|
261
|
+
await fs.promises.rm(lockPath, {
|
|
262
|
+
force: true,
|
|
263
|
+
recursive: true
|
|
264
|
+
}).catch(() => void 0);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
function resolvePackageRoot(packageName, requireRoot) {
|
|
269
|
+
const packageJsonPath = createRequire(path.join(requireRoot, "package.json")).resolve(`${packageName}/package.json`);
|
|
270
|
+
return path.dirname(packageJsonPath);
|
|
271
|
+
}
|
|
272
|
+
function resolveReactNativePackageRoots(workspaceRoot) {
|
|
273
|
+
return getReactNativePackageEntries(workspaceRoot).map(({ packageRoot }) => packageRoot);
|
|
274
|
+
}
|
|
275
|
+
function readPackageManifest(packageRoot) {
|
|
276
|
+
return JSON.parse(fs.readFileSync(path.join(packageRoot, "package.json"), "utf8"));
|
|
277
|
+
}
|
|
278
|
+
function findPackageForRequest(request, manifest) {
|
|
279
|
+
return manifest.packages.sort((left, right) => right.packageName.length - left.packageName.length).find(({ packageName }) => request === packageName || request.startsWith(`${packageName}/`));
|
|
280
|
+
}
|
|
281
|
+
function getPackageSubpath(request, packageName) {
|
|
282
|
+
if (request === packageName) return null;
|
|
283
|
+
return request.slice(packageName.length + 1);
|
|
284
|
+
}
|
|
285
|
+
function getObjectForSourcePath(sourcePath, manifest) {
|
|
286
|
+
const normalizedSourcePath = normalizePath(sourcePath);
|
|
287
|
+
const objectPath = manifest.sourceToObject[normalizedSourcePath];
|
|
288
|
+
if (objectPath == null) return null;
|
|
289
|
+
return {
|
|
290
|
+
objectPath,
|
|
291
|
+
sourcePath: normalizedSourcePath
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
function resolvePackageEntrySourcePath(packageEntry, manifest) {
|
|
295
|
+
const packageManifest = readPackageManifest(packageEntry.packageRoot);
|
|
296
|
+
const entryPath = packageEntry.packageName === "react-native" ? "index.js" : packageManifest["react-native"] ?? packageManifest.module ?? packageManifest.main ?? "index.js";
|
|
297
|
+
if (typeof entryPath === "string") return resolveSourcePathCandidate(path.join(packageEntry.packageRoot, entryPath), manifest);
|
|
298
|
+
return resolveSourcePathCandidate(path.join(packageEntry.packageRoot, "index"), manifest);
|
|
299
|
+
}
|
|
300
|
+
function resolveSourcePathCandidate(candidatePath, manifest) {
|
|
301
|
+
const normalizedCandidatePath = normalizePath(candidatePath);
|
|
302
|
+
if (fs.existsSync(normalizedCandidatePath)) {
|
|
303
|
+
const stat = fs.statSync(normalizedCandidatePath);
|
|
304
|
+
if (stat.isFile()) return getObjectForSourcePath(normalizedCandidatePath, manifest);
|
|
305
|
+
if (stat.isDirectory()) {
|
|
306
|
+
const packageJsonPath = path.join(normalizedCandidatePath, "package.json");
|
|
307
|
+
if (fs.existsSync(packageJsonPath)) {
|
|
308
|
+
const packageManifest = readPackageManifest(normalizedCandidatePath);
|
|
309
|
+
const entryPath = packageManifest["react-native"] ?? packageManifest.module ?? packageManifest.main ?? "index.js";
|
|
310
|
+
if (typeof entryPath === "string") {
|
|
311
|
+
const resolvedEntry = resolveSourcePathCandidate(path.join(normalizedCandidatePath, entryPath), manifest);
|
|
312
|
+
if (resolvedEntry != null) return resolvedEntry;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
return resolveSourcePathCandidate(path.join(normalizedCandidatePath, "index"), manifest);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
if (path.extname(normalizedCandidatePath).length > 0) return null;
|
|
319
|
+
for (const extension of FALLBACK_EXTENSIONS) {
|
|
320
|
+
const resolved = getObjectForSourcePath(`${normalizedCandidatePath}${extension}`, manifest);
|
|
321
|
+
if (resolved != null) return resolved;
|
|
322
|
+
}
|
|
323
|
+
return null;
|
|
324
|
+
}
|
|
325
|
+
function resolveReactNativeModuleFromManifest(request, importer, manifest) {
|
|
326
|
+
if (request.startsWith(".") && importer != null) {
|
|
327
|
+
const importerSourcePath = manifest.objectToSource[normalizePath(importer)];
|
|
328
|
+
if (importerSourcePath == null) return null;
|
|
329
|
+
return resolveSourcePathCandidate(path.resolve(path.dirname(importerSourcePath), request), manifest);
|
|
330
|
+
}
|
|
331
|
+
const packageEntry = findPackageForRequest(request, manifest);
|
|
332
|
+
if (packageEntry == null) return null;
|
|
333
|
+
const subpath = getPackageSubpath(request, packageEntry.packageName);
|
|
334
|
+
if (subpath == null) return resolvePackageEntrySourcePath(packageEntry, manifest);
|
|
335
|
+
return resolveSourcePathCandidate(path.join(packageEntry.packageRoot, subpath), manifest);
|
|
336
|
+
}
|
|
337
|
+
async function writeObjectFile(sourcePath, objectsRoot, packageRoots, manifest) {
|
|
338
|
+
const normalizedSourcePath = normalizePath(sourcePath);
|
|
339
|
+
const sourceBuffer = fs.readFileSync(normalizedSourcePath);
|
|
340
|
+
const extension = path.extname(normalizedSourcePath) || ".js";
|
|
341
|
+
let outputBuffer = sourceBuffer;
|
|
342
|
+
if (shouldTransformReactNativeFile(normalizedSourcePath, packageRoots)) try {
|
|
343
|
+
const transformed = await transformReactNativeSource(normalizedSourcePath, sourceBuffer.toString("utf8"));
|
|
344
|
+
outputBuffer = Buffer.from(transformed);
|
|
345
|
+
} catch {
|
|
346
|
+
outputBuffer = sourceBuffer;
|
|
347
|
+
}
|
|
348
|
+
const objectHash = createHash("sha256").update(normalizedSourcePath).update("\0").update(outputBuffer).digest("hex");
|
|
349
|
+
const objectPath = path.join(objectsRoot, `${objectHash}${extension}`);
|
|
350
|
+
if (!fs.existsSync(objectPath)) fs.writeFileSync(objectPath, outputBuffer);
|
|
351
|
+
manifest.sourceToObject[normalizedSourcePath] = objectPath;
|
|
352
|
+
manifest.objectToSource[objectPath] = normalizedSourcePath;
|
|
353
|
+
}
|
|
354
|
+
async function writePackageTreeObjects(sourceRoot, objectsRoot, packageRoots, manifest) {
|
|
355
|
+
const entries = fs.readdirSync(sourceRoot, { withFileTypes: true }).sort((left, right) => left.name.localeCompare(right.name));
|
|
356
|
+
for (const entry of entries) {
|
|
357
|
+
const sourcePath = path.join(sourceRoot, entry.name);
|
|
358
|
+
if (entry.isDirectory()) {
|
|
359
|
+
await writePackageTreeObjects(sourcePath, objectsRoot, packageRoots, manifest);
|
|
360
|
+
continue;
|
|
361
|
+
}
|
|
362
|
+
if (!entry.isFile()) continue;
|
|
363
|
+
await writeObjectFile(sourcePath, objectsRoot, packageRoots, manifest);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
function readTransformCacheManifest(manifestPath) {
|
|
367
|
+
return JSON.parse(fs.readFileSync(manifestPath, "utf8"));
|
|
368
|
+
}
|
|
369
|
+
function loadReactNativeTransformCacheManifest(manifestPath) {
|
|
370
|
+
return readTransformCacheManifest(manifestPath);
|
|
371
|
+
}
|
|
372
|
+
async function buildReactNativeTransformCache(workspaceRoot, resolvedCacheDir = path.join(workspaceRoot, DEFAULT_VITEST_CACHE_DIRECTORY)) {
|
|
373
|
+
const cacheEntry = computeReactNativeTransformCacheKey(workspaceRoot);
|
|
374
|
+
const entriesRoot = path.join(resolvedCacheDir, GRANITE_VITEST_RN_CACHE_DIRECTORY, GRANITE_VITEST_RN_CACHE_ENTRIES_DIRECTORY);
|
|
375
|
+
const entryRoot = path.join(entriesRoot, cacheEntry.cacheKey);
|
|
376
|
+
const objectsRoot = path.join(entryRoot, GRANITE_VITEST_RN_OBJECTS_DIRECTORY);
|
|
377
|
+
const manifestPath = path.join(entryRoot, GRANITE_VITEST_RN_MANIFEST_FILENAME);
|
|
378
|
+
const metadataPath = path.join(entryRoot, GRANITE_VITEST_RN_CACHE_METADATA_FILENAME);
|
|
379
|
+
fs.mkdirSync(entriesRoot, { recursive: true });
|
|
380
|
+
if (fs.existsSync(entryRoot) && (!fs.existsSync(objectsRoot) || !fs.existsSync(manifestPath))) fs.rmSync(entryRoot, {
|
|
381
|
+
force: true,
|
|
382
|
+
recursive: true
|
|
383
|
+
});
|
|
384
|
+
gcReactNativeMirrorCache(workspaceRoot, cacheEntry.cacheKey, resolvedCacheDir);
|
|
385
|
+
if (fs.existsSync(objectsRoot) && fs.existsSync(manifestPath) && fs.existsSync(metadataPath)) {
|
|
386
|
+
const manifest = readTransformCacheManifest(manifestPath);
|
|
387
|
+
const metadata = readMirrorCacheMetadata(entryRoot);
|
|
388
|
+
const lastAccessedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
389
|
+
manifest.lastAccessedAt = lastAccessedAt;
|
|
390
|
+
fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
|
|
391
|
+
if (metadata != null) {
|
|
392
|
+
metadata.lastAccessedAt = lastAccessedAt;
|
|
393
|
+
fs.writeFileSync(metadataPath, JSON.stringify(metadata, null, 2));
|
|
394
|
+
}
|
|
395
|
+
return {
|
|
396
|
+
entryRoot,
|
|
397
|
+
manifest,
|
|
398
|
+
manifestPath,
|
|
399
|
+
objectsRoot
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
const temporaryEntryRoot = path.join(entriesRoot, `${GRANITE_VITEST_RN_CACHE_TMP_PREFIX}${process.pid}-${Date.now()}-${cacheEntry.cacheKey}`);
|
|
403
|
+
const temporaryObjectsRoot = path.join(temporaryEntryRoot, GRANITE_VITEST_RN_OBJECTS_DIRECTORY);
|
|
404
|
+
const temporaryManifestPath = path.join(temporaryEntryRoot, GRANITE_VITEST_RN_MANIFEST_FILENAME);
|
|
405
|
+
const temporaryMetadataPath = path.join(temporaryEntryRoot, GRANITE_VITEST_RN_CACHE_METADATA_FILENAME);
|
|
406
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
407
|
+
const packages = getReactNativePackageEntries(workspaceRoot);
|
|
408
|
+
const manifest = {
|
|
409
|
+
cacheKey: cacheEntry.cacheKey,
|
|
410
|
+
createdAt: now,
|
|
411
|
+
lastAccessedAt: now,
|
|
412
|
+
objectsRoot,
|
|
413
|
+
packageRoots: cacheEntry.packageRoots,
|
|
414
|
+
packages,
|
|
415
|
+
sourceToObject: {},
|
|
416
|
+
objectToSource: {},
|
|
417
|
+
transformDependencyVersions: cacheEntry.transformDependencyVersions,
|
|
418
|
+
transformImplementationHash: cacheEntry.transformImplementationHash
|
|
419
|
+
};
|
|
420
|
+
fs.rmSync(temporaryEntryRoot, {
|
|
421
|
+
force: true,
|
|
422
|
+
recursive: true
|
|
423
|
+
});
|
|
424
|
+
fs.mkdirSync(temporaryObjectsRoot, { recursive: true });
|
|
425
|
+
try {
|
|
426
|
+
const temporaryManifest = {
|
|
427
|
+
...manifest,
|
|
428
|
+
objectsRoot: temporaryObjectsRoot
|
|
429
|
+
};
|
|
430
|
+
for (const { packageRoot } of packages) await writePackageTreeObjects(packageRoot, temporaryObjectsRoot, cacheEntry.packageRoots, temporaryManifest);
|
|
431
|
+
const finalizedManifest = {
|
|
432
|
+
...temporaryManifest,
|
|
433
|
+
objectsRoot,
|
|
434
|
+
sourceToObject: Object.fromEntries(Object.entries(temporaryManifest.sourceToObject).map(([sourcePath, objectPath]) => [sourcePath, path.join(objectsRoot, path.basename(objectPath))])),
|
|
435
|
+
objectToSource: Object.fromEntries(Object.entries(temporaryManifest.objectToSource).map(([objectPath, sourcePath]) => [path.join(objectsRoot, path.basename(objectPath)), sourcePath]))
|
|
436
|
+
};
|
|
437
|
+
fs.writeFileSync(temporaryManifestPath, JSON.stringify(finalizedManifest, null, 2));
|
|
438
|
+
fs.writeFileSync(temporaryMetadataPath, JSON.stringify({
|
|
439
|
+
cacheKey: cacheEntry.cacheKey,
|
|
440
|
+
createdAt: now,
|
|
441
|
+
lastAccessedAt: now,
|
|
442
|
+
packageRoots: cacheEntry.packageRoots,
|
|
443
|
+
sizeBytes: getDirectorySize(temporaryObjectsRoot),
|
|
444
|
+
transformDependencyVersions: cacheEntry.transformDependencyVersions,
|
|
445
|
+
transformImplementationHash: cacheEntry.transformImplementationHash
|
|
446
|
+
}, null, 2));
|
|
447
|
+
try {
|
|
448
|
+
fs.renameSync(temporaryEntryRoot, entryRoot);
|
|
449
|
+
} catch (error) {
|
|
450
|
+
const errorCode = error.code;
|
|
451
|
+
if (errorCode !== "EEXIST" && errorCode !== "ENOTEMPTY") throw error;
|
|
452
|
+
}
|
|
453
|
+
} finally {
|
|
454
|
+
fs.rmSync(temporaryEntryRoot, {
|
|
455
|
+
force: true,
|
|
456
|
+
recursive: true
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
if (fs.existsSync(objectsRoot) && fs.existsSync(manifestPath) && fs.existsSync(metadataPath)) {
|
|
460
|
+
const manifest = readTransformCacheManifest(manifestPath);
|
|
461
|
+
const metadata = readMirrorCacheMetadata(entryRoot);
|
|
462
|
+
const lastAccessedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
463
|
+
manifest.lastAccessedAt = lastAccessedAt;
|
|
464
|
+
fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
|
|
465
|
+
if (metadata != null) {
|
|
466
|
+
metadata.lastAccessedAt = lastAccessedAt;
|
|
467
|
+
fs.writeFileSync(metadataPath, JSON.stringify(metadata, null, 2));
|
|
468
|
+
}
|
|
469
|
+
gcReactNativeMirrorCache(workspaceRoot, cacheEntry.cacheKey, resolvedCacheDir);
|
|
470
|
+
return {
|
|
471
|
+
entryRoot,
|
|
472
|
+
manifest,
|
|
473
|
+
manifestPath,
|
|
474
|
+
objectsRoot
|
|
475
|
+
};
|
|
476
|
+
}
|
|
477
|
+
throw new Error(`Failed to materialize React Native transform cache at ${entryRoot}`);
|
|
478
|
+
}
|
|
479
|
+
async function buildReactNativeMirror(workspaceRoot, resolvedCacheDir = path.join(workspaceRoot, DEFAULT_VITEST_CACHE_DIRECTORY)) {
|
|
480
|
+
return (await buildReactNativeTransformCache(workspaceRoot, resolvedCacheDir)).objectsRoot;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
//#endregion
|
|
484
|
+
export { shouldTransformReactNativeFile as _, GRANITE_VITEST_RN_OBJECTS_DIRECTORY as a, buildReactNativeMirror as c, resolvePackageRoot as d, resolveReactNativeModuleFromManifest as f, shouldInlineReactNativeDependency as g, REACT_NATIVE_TRANSFORM_EXTENSIONS as h, GRANITE_VITEST_RN_MANIFEST_FILENAME as i, buildReactNativeTransformCache as l, REACT_NATIVE_TRANSFORM_ALLOWLIST as m, GRANITE_VITEST_RN_CACHE_DIRECTORY as n, GRANITE_VITEST_RN_PACKAGES_DIRECTORY as o, resolveReactNativePackageRoots as p, GRANITE_VITEST_RN_CACHE_ENTRIES_DIRECTORY as r, REACT_NATIVE_PLATFORMS as s, DEFAULT_PLATFORM as t, loadReactNativeTransformCacheManifest as u, REACT_NATIVE_ASSET_EXTENSIONS as v, REACT_NATIVE_ASSET_MODULE_ID_PATTERN as y };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { f as resolveReactNativeModuleFromManifest, u as loadReactNativeTransformCacheManifest, y as REACT_NATIVE_ASSET_MODULE_ID_PATTERN } from "./mirror-e92G5HdG.js";
|
|
2
2
|
import { n as installNativeModuleProxy, r as installReactNativeGlobals, t as defineGlobalProperty } from "./runtimeBootstrap-CHWitCG1.js";
|
|
3
3
|
import Module, { createRequire } from "node:module";
|
|
4
4
|
import path from "node:path";
|
|
@@ -9,11 +9,16 @@ import { vi } from "vitest";
|
|
|
9
9
|
const runtimeGlobals = globalThis;
|
|
10
10
|
const runtimeDirectory = path.dirname(fileURLToPath(import.meta.url));
|
|
11
11
|
const runtimeRequire = createRequire(import.meta.url);
|
|
12
|
-
function
|
|
13
|
-
if (typeof globalThis.
|
|
14
|
-
throw new Error("[@granite-js/vitest] reactNativeRuntime requires reactNative() in vitest.config to set
|
|
12
|
+
function resolveReactNativeManifestPath() {
|
|
13
|
+
if (typeof globalThis.__GRANITE_VITEST_RN_CACHE_MANIFEST__ === "string" && globalThis.__GRANITE_VITEST_RN_CACHE_MANIFEST__.length > 0) return path.resolve(globalThis.__GRANITE_VITEST_RN_CACHE_MANIFEST__);
|
|
14
|
+
throw new Error("[@granite-js/vitest] reactNativeRuntime requires reactNative() in vitest.config to set __GRANITE_VITEST_RN_CACHE_MANIFEST__.");
|
|
15
|
+
}
|
|
16
|
+
const reactNativeTransformCacheManifest = loadReactNativeTransformCacheManifest(resolveReactNativeManifestPath());
|
|
17
|
+
function resolveReactNativeObjectPath(request, importer, manifest = reactNativeTransformCacheManifest) {
|
|
18
|
+
const resolved = resolveReactNativeModuleFromManifest(request, importer, manifest);
|
|
19
|
+
if (resolved == null) throw new Error(`[@granite-js/vitest] Failed to resolve React Native module: ${request}`);
|
|
20
|
+
return resolved.objectPath;
|
|
15
21
|
}
|
|
16
|
-
const reactNativeMirrorRoot = resolveReactNativeMirrorRoot();
|
|
17
22
|
let nextNativeTag = 1;
|
|
18
23
|
function createMockNativeMethods() {
|
|
19
24
|
return {
|
|
@@ -1625,83 +1630,52 @@ function patchReactNativeResolution() {
|
|
|
1625
1630
|
const moduleWithPrivateResolver = Module;
|
|
1626
1631
|
const originalResolveFilename = moduleWithPrivateResolver._resolveFilename;
|
|
1627
1632
|
const originalLoad = moduleWithPrivateResolver._load;
|
|
1628
|
-
const reactNativeIndexPath =
|
|
1629
|
-
const nativeComponentRegistryMockPath = path.join(reactNativeMirrorRoot, "react-native", "Libraries", "NativeComponent", "NativeComponentRegistry.js");
|
|
1630
|
-
const requireNativeComponentMockPath = path.join(reactNativeMirrorRoot, "react-native", "Libraries", "ReactNative", "requireNativeComponent.js");
|
|
1631
|
-
const initializeCoreMockPath = path.join(reactNativeMirrorRoot, "react-native", "Libraries", "Core", "InitializeCore.js");
|
|
1632
|
-
const nativeExceptionsManagerMockPath = path.join(reactNativeMirrorRoot, "react-native", "Libraries", "Core", "NativeExceptionsManager.js");
|
|
1633
|
-
const accessibilityInfoMockPath = path.join(reactNativeMirrorRoot, "react-native", "Libraries", "Components", "AccessibilityInfo", "AccessibilityInfo.js");
|
|
1634
|
-
const clipboardMockPath = path.join(reactNativeMirrorRoot, "react-native", "Libraries", "Components", "Clipboard", "Clipboard.js");
|
|
1635
|
-
const codegenNativeCommandsMockPath = path.join(reactNativeMirrorRoot, "react-native", "Libraries", "Utilities", "codegenNativeCommands.js");
|
|
1636
|
-
const codegenNativeComponentMockPath = path.join(reactNativeMirrorRoot, "react-native", "Libraries", "Utilities", "codegenNativeComponent.js");
|
|
1637
|
-
const refreshControlMockPath = path.join(reactNativeMirrorRoot, "react-native", "Libraries", "Components", "RefreshControl", "RefreshControl.js");
|
|
1638
|
-
const activityIndicatorMockPath = path.join(reactNativeMirrorRoot, "react-native", "Libraries", "Components", "ActivityIndicator", "ActivityIndicator.js");
|
|
1639
|
-
const imageMockPath = path.join(reactNativeMirrorRoot, "react-native", "Libraries", "Image", "Image.js");
|
|
1640
|
-
const textMockPath = path.join(reactNativeMirrorRoot, "react-native", "Libraries", "Text", "Text.js");
|
|
1641
|
-
const textInputMockPath = path.join(reactNativeMirrorRoot, "react-native", "Libraries", "Components", "TextInput", "TextInput.js");
|
|
1642
|
-
const modalMockPath = path.join(reactNativeMirrorRoot, "react-native", "Libraries", "Modal", "Modal.js");
|
|
1643
|
-
const viewMockPath = path.join(reactNativeMirrorRoot, "react-native", "Libraries", "Components", "View", "View.js");
|
|
1644
|
-
const scrollViewMockPath = path.join(reactNativeMirrorRoot, "react-native", "Libraries", "Components", "ScrollView", "ScrollView.js");
|
|
1645
|
-
const nativeModulesMockPath = path.join(reactNativeMirrorRoot, "react-native", "Libraries", "BatchedBridge", "NativeModules.js");
|
|
1646
|
-
const uiManagerMockPath = path.join(reactNativeMirrorRoot, "react-native", "Libraries", "ReactNative", "UIManager.js");
|
|
1647
|
-
const appStateMockPath = path.join(reactNativeMirrorRoot, "react-native", "Libraries", "AppState", "AppState.js");
|
|
1648
|
-
const linkingMockPath = path.join(reactNativeMirrorRoot, "react-native", "Libraries", "Linking", "Linking.js");
|
|
1649
|
-
const vibrationMockPath = path.join(reactNativeMirrorRoot, "react-native", "Libraries", "Vibration", "Vibration.js");
|
|
1650
|
-
const viewNativeComponentMockPath = path.join(reactNativeMirrorRoot, "react-native", "Libraries", "Components", "View", "ViewNativeComponent.js");
|
|
1651
|
-
const useColorSchemeMockPath = path.join(reactNativeMirrorRoot, "react-native", "Libraries", "Utilities", "useColorScheme.js");
|
|
1652
|
-
const platformMockPath = path.join(reactNativeMirrorRoot, "react-native", "Libraries", "Utilities", "Platform.js");
|
|
1653
|
-
const styleSheetMockPath = path.join(reactNativeMirrorRoot, "react-native", "Libraries", "StyleSheet", "StyleSheet.js");
|
|
1633
|
+
const reactNativeIndexPath = resolveReactNativeObjectPath("react-native");
|
|
1654
1634
|
const sharedMockModules = getSharedReactNativeMockModules();
|
|
1655
|
-
const
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1635
|
+
const sharedMockModuleByPath = new Map([
|
|
1636
|
+
[resolveReactNativeObjectPath("react-native/Libraries/NativeComponent/NativeComponentRegistry.js"), sharedMockModules.nativeComponentRegistryModule],
|
|
1637
|
+
[resolveReactNativeObjectPath("react-native/Libraries/ReactNative/requireNativeComponent.js"), sharedMockModules.requireNativeComponentModule],
|
|
1638
|
+
[resolveReactNativeObjectPath("react-native/Libraries/Core/InitializeCore.js"), sharedMockModules.initializeCoreModule],
|
|
1639
|
+
[resolveReactNativeObjectPath("react-native/Libraries/Core/NativeExceptionsManager.js"), sharedMockModules.nativeExceptionsManagerModule],
|
|
1640
|
+
[resolveReactNativeObjectPath("react-native/Libraries/Components/AccessibilityInfo/AccessibilityInfo.js"), sharedMockModules.accessibilityInfoModule],
|
|
1641
|
+
[resolveReactNativeObjectPath("react-native/Libraries/Components/Clipboard/Clipboard.js"), sharedMockModules.clipboardModule],
|
|
1642
|
+
[resolveReactNativeObjectPath("react-native/Libraries/Utilities/codegenNativeCommands.js"), sharedMockModules.codegenNativeCommandsModule],
|
|
1643
|
+
[resolveReactNativeObjectPath("react-native/Libraries/Utilities/codegenNativeComponent.js"), sharedMockModules.codegenNativeComponentModule],
|
|
1644
|
+
[resolveReactNativeObjectPath("react-native/Libraries/Components/RefreshControl/RefreshControl.js"), sharedMockModules.refreshControlModule],
|
|
1645
|
+
[resolveReactNativeObjectPath("react-native/Libraries/Components/ActivityIndicator/ActivityIndicator.js"), sharedMockModules.activityIndicatorModule],
|
|
1646
|
+
[resolveReactNativeObjectPath("react-native/Libraries/Image/Image.js"), sharedMockModules.imageModule],
|
|
1647
|
+
[resolveReactNativeObjectPath("react-native/Libraries/Text/Text.js"), sharedMockModules.textModule],
|
|
1648
|
+
[resolveReactNativeObjectPath("react-native/Libraries/Components/TextInput/TextInput.js"), sharedMockModules.textInputModule],
|
|
1649
|
+
[resolveReactNativeObjectPath("react-native/Libraries/Modal/Modal.js"), sharedMockModules.modalModule],
|
|
1650
|
+
[resolveReactNativeObjectPath("react-native/Libraries/Components/View/View.js"), sharedMockModules.viewModule],
|
|
1651
|
+
[resolveReactNativeObjectPath("react-native/Libraries/Components/ScrollView/ScrollView.js"), sharedMockModules.scrollViewModule],
|
|
1652
|
+
[resolveReactNativeObjectPath("react-native/Libraries/BatchedBridge/NativeModules.js"), sharedMockModules.nativeModulesModule],
|
|
1653
|
+
[resolveReactNativeObjectPath("react-native/Libraries/ReactNative/UIManager.js"), sharedMockModules.uiManagerModule],
|
|
1654
|
+
[resolveReactNativeObjectPath("react-native/Libraries/AppState/AppState.js"), sharedMockModules.appStateModule],
|
|
1655
|
+
[resolveReactNativeObjectPath("react-native/Libraries/Linking/Linking.js"), sharedMockModules.linkingModule],
|
|
1656
|
+
[resolveReactNativeObjectPath("react-native/Libraries/Vibration/Vibration.js"), sharedMockModules.vibrationModule],
|
|
1657
|
+
[resolveReactNativeObjectPath("react-native/Libraries/Components/View/ViewNativeComponent.js"), sharedMockModules.viewNativeComponentModule],
|
|
1658
|
+
[resolveReactNativeObjectPath("react-native/Libraries/Utilities/useColorScheme.js"), sharedMockModules.useColorSchemeModule],
|
|
1659
|
+
[resolveReactNativeObjectPath("react-native/Libraries/Utilities/Platform.js"), sharedMockModules.platformModule],
|
|
1660
|
+
[resolveReactNativeObjectPath("react-native/Libraries/StyleSheet/StyleSheet.js"), sharedMockModules.styleSheetModule]
|
|
1661
|
+
]);
|
|
1662
|
+
const isReactNativePackageManifestRequest = (request) => request === "react-native/package.json" || request === "jest-react-native/package.json" || request.startsWith("@react-native/") && request.endsWith("/package.json") || request.startsWith("@react-native-community/") && request.endsWith("/package.json");
|
|
1662
1663
|
moduleWithPrivateResolver._resolveFilename = function patchedResolveFilename(request, parent, isMain, options) {
|
|
1663
|
-
if (
|
|
1664
|
-
|
|
1665
|
-
if (
|
|
1666
|
-
if (request.startsWith("@react-native/")) return originalResolveFilename.call(this, path.join(reactNativeMirrorRoot, request), parent, isMain, options);
|
|
1667
|
-
const mirroredPackageRequest = tryResolveMirroredPackageRequest(request);
|
|
1668
|
-
if (mirroredPackageRequest != null) return originalResolveFilename.call(this, mirroredPackageRequest, parent, isMain, options);
|
|
1664
|
+
if (isReactNativePackageManifestRequest(request)) return originalResolveFilename.call(this, request, parent, isMain, options);
|
|
1665
|
+
const resolved = resolveReactNativeModuleFromManifest(request, parent?.filename, reactNativeTransformCacheManifest);
|
|
1666
|
+
if (resolved != null) return resolved.objectPath;
|
|
1669
1667
|
return originalResolveFilename.call(this, request, parent, isMain, options);
|
|
1670
1668
|
};
|
|
1671
1669
|
moduleWithPrivateResolver._load = function patchedLoad(request, parent, isMain) {
|
|
1672
1670
|
const resolved = moduleWithPrivateResolver._resolveFilename(request, parent, isMain);
|
|
1673
1671
|
if (resolved === reactNativeIndexPath) return getSharedReactNativeExports();
|
|
1674
|
-
if (resolved
|
|
1675
|
-
if (resolved === requireNativeComponentMockPath) return sharedMockModules.requireNativeComponentModule;
|
|
1676
|
-
if (resolved === initializeCoreMockPath) return sharedMockModules.initializeCoreModule;
|
|
1677
|
-
if (resolved === nativeExceptionsManagerMockPath) return sharedMockModules.nativeExceptionsManagerModule;
|
|
1678
|
-
if (resolved === accessibilityInfoMockPath) return sharedMockModules.accessibilityInfoModule;
|
|
1679
|
-
if (resolved === clipboardMockPath) return sharedMockModules.clipboardModule;
|
|
1680
|
-
if (resolved === codegenNativeCommandsMockPath) return sharedMockModules.codegenNativeCommandsModule;
|
|
1681
|
-
if (resolved === codegenNativeComponentMockPath) return sharedMockModules.codegenNativeComponentModule;
|
|
1682
|
-
if (resolved === refreshControlMockPath) return sharedMockModules.refreshControlModule;
|
|
1683
|
-
if (resolved === activityIndicatorMockPath) return sharedMockModules.activityIndicatorModule;
|
|
1684
|
-
if (resolved === imageMockPath) return sharedMockModules.imageModule;
|
|
1685
|
-
if (resolved === textMockPath) return sharedMockModules.textModule;
|
|
1686
|
-
if (resolved === textInputMockPath) return sharedMockModules.textInputModule;
|
|
1687
|
-
if (resolved === modalMockPath) return sharedMockModules.modalModule;
|
|
1688
|
-
if (resolved === viewMockPath) return sharedMockModules.viewModule;
|
|
1689
|
-
if (resolved === scrollViewMockPath) return sharedMockModules.scrollViewModule;
|
|
1690
|
-
if (resolved === nativeModulesMockPath) return sharedMockModules.nativeModulesModule;
|
|
1691
|
-
if (resolved === uiManagerMockPath) return sharedMockModules.uiManagerModule;
|
|
1692
|
-
if (resolved === appStateMockPath) return sharedMockModules.appStateModule;
|
|
1693
|
-
if (resolved === linkingMockPath) return sharedMockModules.linkingModule;
|
|
1694
|
-
if (resolved === vibrationMockPath) return sharedMockModules.vibrationModule;
|
|
1695
|
-
if (resolved === viewNativeComponentMockPath) return sharedMockModules.viewNativeComponentModule;
|
|
1696
|
-
if (resolved === useColorSchemeMockPath) return sharedMockModules.useColorSchemeModule;
|
|
1697
|
-
if (resolved === platformMockPath) return sharedMockModules.platformModule;
|
|
1698
|
-
if (resolved === styleSheetMockPath) return sharedMockModules.styleSheetModule;
|
|
1672
|
+
if (sharedMockModuleByPath.has(resolved)) return sharedMockModuleByPath.get(resolved);
|
|
1699
1673
|
if (REACT_NATIVE_ASSET_MODULE_ID_PATTERN.test(resolved)) return { testUri: path.relative(runtimeDirectory, resolved.replace(/[?#].*$/, "")).replace(/\\/g, "/") };
|
|
1700
1674
|
return originalLoad.call(this, request, parent, isMain);
|
|
1701
1675
|
};
|
|
1702
1676
|
runtimeGlobals.__rntlVitestRnResolverPatched__ = true;
|
|
1703
1677
|
}
|
|
1704
|
-
runtimeRequire(
|
|
1678
|
+
runtimeRequire(resolveReactNativeObjectPath("@react-native/js-polyfills/error-guard.js"));
|
|
1705
1679
|
vi.mock("react-native", () => getSharedReactNativeExports());
|
|
1706
1680
|
installReactNativeGlobals(runtimeGlobals);
|
|
1707
1681
|
installNativeModuleProxy(runtimeGlobals);
|
package/package.json
CHANGED
package/dist/assets-DoucBOz0.js
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
//#region src/assets.ts
|
|
2
|
-
const REACT_NATIVE_ASSET_EXTENSIONS = [
|
|
3
|
-
"bmp",
|
|
4
|
-
"gif",
|
|
5
|
-
"jpg",
|
|
6
|
-
"jpeg",
|
|
7
|
-
"mp4",
|
|
8
|
-
"png",
|
|
9
|
-
"psd",
|
|
10
|
-
"svg",
|
|
11
|
-
"webp"
|
|
12
|
-
];
|
|
13
|
-
const REACT_NATIVE_ASSET_MODULE_ID_PATTERN = new RegExp(`\\.(${REACT_NATIVE_ASSET_EXTENSIONS.join("|")})(?:[?#].*)?$`, "i");
|
|
14
|
-
|
|
15
|
-
//#endregion
|
|
16
|
-
export { REACT_NATIVE_ASSET_MODULE_ID_PATTERN as n, REACT_NATIVE_ASSET_EXTENSIONS as t };
|