@vxrn/vite-plugin-metro 2.0.0-beta.51.1 → 2.0.0-beta.56.1
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/dist/cjs/env/platformEnv.cjs +2 -0
- package/dist/cjs/env/platformEnv.native.cjs +2 -0
- package/dist/cjs/env/platformEnv.native.js +2 -0
- package/dist/cjs/env/platformEnv.native.js.map +1 -1
- package/dist/cjs/index.cjs +0 -2
- package/dist/cjs/index.native.cjs +0 -2
- package/dist/cjs/index.native.js +0 -2
- package/dist/cjs/index.native.js.map +1 -1
- package/dist/cjs/metro-config/getMetroBabelConfigFromViteConfig.cjs +10 -15
- package/dist/cjs/metro-config/getMetroBabelConfigFromViteConfig.native.cjs +27 -48
- package/dist/cjs/metro-config/getMetroBabelConfigFromViteConfig.native.js +27 -48
- package/dist/cjs/metro-config/getMetroBabelConfigFromViteConfig.native.js.map +1 -1
- package/dist/cjs/metro-config/getMetroConfigFromViteConfig.cjs +71 -38
- package/dist/cjs/metro-config/getMetroConfigFromViteConfig.native.cjs +86 -42
- package/dist/cjs/metro-config/getMetroConfigFromViteConfig.native.js +86 -42
- package/dist/cjs/metro-config/getMetroConfigFromViteConfig.native.js.map +1 -1
- package/dist/cjs/transformer/babel-transformer.cjs +53 -4
- package/dist/cjs/transformer/babel-transformer.native.cjs +65 -6
- package/dist/cjs/transformer/babel-transformer.native.js +65 -6
- package/dist/cjs/transformer/babel-transformer.native.js.map +1 -1
- package/dist/cjs/transformer/expoVirtualEnv.cjs +92 -0
- package/dist/cjs/transformer/expoVirtualEnv.native.cjs +113 -0
- package/dist/cjs/transformer/expoVirtualEnv.native.js +115 -0
- package/dist/cjs/transformer/expoVirtualEnv.native.js.map +1 -0
- package/dist/cjs/transformer/loadBabelConfig.cjs +1 -1
- package/dist/cjs/transformer/loadBabelConfig.native.cjs +1 -1
- package/dist/cjs/transformer/loadBabelConfig.native.js +1 -1
- package/dist/cjs/transformer/loadBabelConfig.native.js.map +1 -1
- package/dist/cjs/transformer/metroNativeWorker.cjs +20 -29
- package/dist/cjs/transformer/metroNativeWorker.native.cjs +24 -30
- package/dist/cjs/transformer/metroNativeWorker.native.js +24 -30
- package/dist/cjs/transformer/metroNativeWorker.native.js.map +1 -1
- package/dist/cjs/transformer/transformSync.cjs +3 -3
- package/dist/cjs/transformer/transformSync.native.cjs +3 -3
- package/dist/cjs/transformer/transformSync.native.js +3 -3
- package/dist/cjs/transformer/transformSync.native.js.map +1 -1
- package/dist/cjs/utils/exit.cjs +1 -1
- package/dist/cjs/utils/exit.native.cjs +1 -1
- package/dist/cjs/utils/exit.native.js +1 -1
- package/dist/cjs/utils/exit.native.js.map +1 -1
- package/dist/esm/env/platformEnv.mjs +2 -0
- package/dist/esm/env/platformEnv.mjs.map +1 -1
- package/dist/esm/env/platformEnv.native.js +2 -0
- package/dist/esm/env/platformEnv.native.js.map +1 -1
- package/dist/esm/index.js +1 -2
- package/dist/esm/index.mjs +1 -2
- package/dist/esm/index.native.js +1 -2
- package/dist/esm/metro-config/getMetroBabelConfigFromViteConfig.mjs +11 -15
- package/dist/esm/metro-config/getMetroBabelConfigFromViteConfig.mjs.map +1 -1
- package/dist/esm/metro-config/getMetroBabelConfigFromViteConfig.native.js +28 -48
- package/dist/esm/metro-config/getMetroBabelConfigFromViteConfig.native.js.map +1 -1
- package/dist/esm/metro-config/getMetroConfigFromViteConfig.mjs +71 -38
- package/dist/esm/metro-config/getMetroConfigFromViteConfig.mjs.map +1 -1
- package/dist/esm/metro-config/getMetroConfigFromViteConfig.native.js +86 -42
- package/dist/esm/metro-config/getMetroConfigFromViteConfig.native.js.map +1 -1
- package/dist/esm/transformer/babel-transformer.mjs +133 -96
- package/dist/esm/transformer/babel-transformer.mjs.map +1 -1
- package/dist/esm/transformer/babel-transformer.native.js +147 -103
- package/dist/esm/transformer/babel-transformer.native.js.map +1 -1
- package/dist/esm/transformer/expoVirtualEnv.mjs +67 -0
- package/dist/esm/transformer/expoVirtualEnv.mjs.map +1 -0
- package/dist/esm/transformer/expoVirtualEnv.native.js +86 -0
- package/dist/esm/transformer/expoVirtualEnv.native.js.map +1 -0
- package/dist/esm/transformer/loadBabelConfig.mjs +1 -1
- package/dist/esm/transformer/loadBabelConfig.mjs.map +1 -1
- package/dist/esm/transformer/loadBabelConfig.native.js +1 -1
- package/dist/esm/transformer/loadBabelConfig.native.js.map +1 -1
- package/dist/esm/transformer/metroNativeWorker.mjs +20 -29
- package/dist/esm/transformer/metroNativeWorker.mjs.map +1 -1
- package/dist/esm/transformer/metroNativeWorker.native.js +24 -30
- package/dist/esm/transformer/metroNativeWorker.native.js.map +1 -1
- package/dist/esm/transformer/transformSync.mjs +3 -3
- package/dist/esm/transformer/transformSync.mjs.map +1 -1
- package/dist/esm/transformer/transformSync.native.js +3 -3
- package/dist/esm/transformer/transformSync.native.js.map +1 -1
- package/dist/esm/utils/exit.mjs +1 -1
- package/dist/esm/utils/exit.mjs.map +1 -1
- package/dist/esm/utils/exit.native.js +1 -1
- package/dist/esm/utils/exit.native.js.map +1 -1
- package/package.json +7 -9
- package/src/babel-plugins/import-meta-env-plugin.ts +1 -1
- package/src/env/platformEnv.test.ts +12 -2
- package/src/env/platformEnv.ts +3 -0
- package/src/index.ts +0 -2
- package/src/metro-config/getMetroBabelConfigFromViteConfig.test.ts +19 -25
- package/src/metro-config/getMetroBabelConfigFromViteConfig.ts +8 -27
- package/src/metro-config/getMetroConfigFromViteConfig.test.ts +60 -0
- package/src/metro-config/getMetroConfigFromViteConfig.ts +119 -96
- package/src/plugins/metroPlugin.ts +3 -6
- package/src/transformer/babel-transformer.test.ts +118 -0
- package/src/transformer/babel-transformer.ts +82 -15
- package/src/transformer/expoVirtualEnv.integration.test.ts +106 -0
- package/src/transformer/expoVirtualEnv.test.ts +72 -0
- package/src/transformer/expoVirtualEnv.ts +115 -0
- package/src/transformer/loadBabelConfig.ts +1 -1
- package/src/transformer/metroNativeWorker.test.ts +134 -17
- package/src/transformer/metroNativeWorker.ts +43 -51
- package/src/transformer/transformSync.test.ts +22 -0
- package/src/transformer/transformSync.ts +4 -4
- package/src/utils/exit.ts +2 -2
- package/src/utils/getTerminalReporter.ts +1 -1
- package/src/utils/projectImport.ts +2 -2
- package/types/babel-plugins/import-meta-env-plugin.d.ts +1 -1
- package/types/env/platformEnv.d.ts +1 -0
- package/types/env/platformEnv.d.ts.map +1 -1
- package/types/index.d.ts +0 -2
- package/types/index.d.ts.map +1 -1
- package/types/metro-config/getMetroBabelConfigFromViteConfig.d.ts +1 -1
- package/types/metro-config/getMetroBabelConfigFromViteConfig.d.ts.map +1 -1
- package/types/metro-config/getMetroConfigFromViteConfig.d.ts +2 -2
- package/types/metro-config/getMetroConfigFromViteConfig.d.ts.map +1 -1
- package/types/plugins/metroPlugin.d.ts +2 -4
- package/types/plugins/metroPlugin.d.ts.map +1 -1
- package/types/transformer/babel-transformer.d.ts +10 -1
- package/types/transformer/babel-transformer.d.ts.map +1 -1
- package/types/transformer/expoVirtualEnv.d.ts +8 -0
- package/types/transformer/expoVirtualEnv.d.ts.map +1 -0
- package/types/transformer/metroNativeWorker.d.ts +3 -5
- package/types/transformer/metroNativeWorker.d.ts.map +1 -1
- package/types/utils/projectImport.d.ts +2 -2
- package/dist/cjs/plugins/expoManifestRequestHandlerPlugin.cjs +0 -59
- package/dist/cjs/plugins/expoManifestRequestHandlerPlugin.native.cjs +0 -62
- package/dist/cjs/plugins/expoManifestRequestHandlerPlugin.native.js +0 -64
- package/dist/cjs/plugins/expoManifestRequestHandlerPlugin.native.js.map +0 -1
- package/dist/cjs/utils/patchExpoGoManifestHandlerMiddlewareWithCustomMainModuleName.cjs +0 -35
- package/dist/cjs/utils/patchExpoGoManifestHandlerMiddlewareWithCustomMainModuleName.native.cjs +0 -37
- package/dist/cjs/utils/patchExpoGoManifestHandlerMiddlewareWithCustomMainModuleName.native.js +0 -39
- package/dist/cjs/utils/patchExpoGoManifestHandlerMiddlewareWithCustomMainModuleName.native.js.map +0 -1
- package/dist/esm/plugins/expoManifestRequestHandlerPlugin.mjs +0 -40
- package/dist/esm/plugins/expoManifestRequestHandlerPlugin.mjs.map +0 -1
- package/dist/esm/plugins/expoManifestRequestHandlerPlugin.native.js +0 -41
- package/dist/esm/plugins/expoManifestRequestHandlerPlugin.native.js.map +0 -1
- package/dist/esm/utils/patchExpoGoManifestHandlerMiddlewareWithCustomMainModuleName.mjs +0 -15
- package/dist/esm/utils/patchExpoGoManifestHandlerMiddlewareWithCustomMainModuleName.mjs.map +0 -1
- package/dist/esm/utils/patchExpoGoManifestHandlerMiddlewareWithCustomMainModuleName.native.js +0 -15
- package/dist/esm/utils/patchExpoGoManifestHandlerMiddlewareWithCustomMainModuleName.native.js.map +0 -1
- package/src/plugins/expoManifestRequestHandlerPlugin.ts +0 -88
- package/src/utils/patchExpoGoManifestHandlerMiddlewareWithCustomMainModuleName.ts +0 -28
- package/types/plugins/expoManifestRequestHandlerPlugin.d.ts +0 -14
- package/types/plugins/expoManifestRequestHandlerPlugin.d.ts.map +0 -1
- package/types/utils/patchExpoGoManifestHandlerMiddlewareWithCustomMainModuleName.d.ts +0 -5
- package/types/utils/patchExpoGoManifestHandlerMiddlewareWithCustomMainModuleName.d.ts.map +0 -1
|
@@ -1,24 +1,23 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { searchForWorkspaceRoot, type ResolvedConfig } from 'vite'
|
|
2
2
|
import { spawn } from 'node:child_process'
|
|
3
3
|
import { resolve } from 'node:path'
|
|
4
4
|
import micromatch from 'micromatch'
|
|
5
|
+
import { getDefaultConfig } from '@react-native/metro-config'
|
|
5
6
|
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
// at runtime to ensure they are loaded from the user's project root.
|
|
7
|
+
// metro itself is loaded from the app so its cli and config stay aligned.
|
|
8
|
+
// the base config belongs to this package and matches its react-native peer.
|
|
9
9
|
import type { loadConfig as loadConfigT } from 'metro'
|
|
10
|
-
import type { getDefaultConfig as getDefaultConfigT } from '@expo/metro-config'
|
|
11
10
|
|
|
12
11
|
import { findUserBabelConfig } from '@vxrn/compiler'
|
|
13
12
|
import { projectImport, projectResolve } from '../utils/projectImport'
|
|
14
13
|
import { getTerminalReporter } from '../utils/getTerminalReporter'
|
|
15
|
-
import { patchExpoGoManifestHandlerMiddlewareWithCustomMainModuleName } from '../utils/patchExpoGoManifestHandlerMiddlewareWithCustomMainModuleName'
|
|
16
14
|
import type { MetroPluginOptions } from '../plugins/metroPlugin'
|
|
17
15
|
import type { ExtraConfig, MetroConfigExtended } from './types'
|
|
18
16
|
|
|
19
|
-
type MetroInputConfig = Parameters<typeof loadConfigT>[1]
|
|
17
|
+
type MetroInputConfig = NonNullable<Parameters<typeof loadConfigT>[1]>
|
|
20
18
|
|
|
21
19
|
const WATCHMAN_PROBE_TIMEOUT_MS = 2000
|
|
20
|
+
const expoEnvAdditionalExts = ['env', 'local', 'development']
|
|
22
21
|
const watchmanResponsivePromises = new Map<string, Promise<boolean>>()
|
|
23
22
|
let didWarnAboutWatchmanFallback = false
|
|
24
23
|
const rootIndexBundleRequestPattern = /^(https?:\/\/[^/]+)?\/index\.bundle(?=$|[?#])/
|
|
@@ -32,6 +31,36 @@ const metroWatchExclusions = [
|
|
|
32
31
|
/[/\\]node_modules[/\\]\.vxrn(?:[/\\]|$)/,
|
|
33
32
|
]
|
|
34
33
|
|
|
34
|
+
function enforceWorkspaceVisibility(
|
|
35
|
+
config: MetroInputConfig,
|
|
36
|
+
projectRoot: string
|
|
37
|
+
): MetroInputConfig {
|
|
38
|
+
const workspaceRoot = searchForWorkspaceRoot(projectRoot)
|
|
39
|
+
if (workspaceRoot === projectRoot) return config
|
|
40
|
+
|
|
41
|
+
return {
|
|
42
|
+
...config,
|
|
43
|
+
watchFolders: [...new Set([...(config.watchFolders ?? []), workspaceRoot])],
|
|
44
|
+
resolver: {
|
|
45
|
+
...config.resolver,
|
|
46
|
+
nodeModulesPaths: [
|
|
47
|
+
...new Set([
|
|
48
|
+
...(config.resolver?.nodeModulesPaths ?? []),
|
|
49
|
+
resolve(projectRoot, 'node_modules'),
|
|
50
|
+
resolve(workspaceRoot, 'node_modules'),
|
|
51
|
+
]),
|
|
52
|
+
],
|
|
53
|
+
},
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function getReactNativeDefaultConfig(projectRoot: string): MetroInputConfig {
|
|
58
|
+
return enforceWorkspaceVisibility(
|
|
59
|
+
getDefaultConfig(projectRoot) as MetroInputConfig,
|
|
60
|
+
projectRoot
|
|
61
|
+
)
|
|
62
|
+
}
|
|
63
|
+
|
|
35
64
|
function getPlatformFromBundleUrl(url: string): 'ios' | 'android' {
|
|
36
65
|
const platform = url.match(/[?&]platform=(ios|android)(?:&|$)/)?.[1]
|
|
37
66
|
return platform === 'android' ? 'android' : 'ios'
|
|
@@ -45,13 +74,43 @@ function rewriteMainModuleBundleUrl(
|
|
|
45
74
|
platform: getPlatformFromBundleUrl(url),
|
|
46
75
|
})
|
|
47
76
|
|
|
48
|
-
if (url.includes('/.expo/.virtual-metro-entry.bundle?')) {
|
|
49
|
-
return url.replace('.expo/.virtual-metro-entry', resolvedMainModulePath)
|
|
50
|
-
}
|
|
51
|
-
|
|
52
77
|
return url.replace(rootIndexBundleRequestPattern, `$1/${resolvedMainModulePath}.bundle`)
|
|
53
78
|
}
|
|
54
79
|
|
|
80
|
+
function isBareSpecifier(name: string) {
|
|
81
|
+
return name !== '' && !name.startsWith('.') && !name.startsWith('/')
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// metro parses `/one/metro-entry.bundle` to the entry `./one/metro-entry`
|
|
85
|
+
// relative to the server root, which misses node_modules package lookup.
|
|
86
|
+
function bareMainModuleForRequest(
|
|
87
|
+
moduleName: string,
|
|
88
|
+
mainModuleName: string | undefined
|
|
89
|
+
) {
|
|
90
|
+
if (!mainModuleName || !isBareSpecifier(mainModuleName)) return undefined
|
|
91
|
+
if (moduleName === `./${mainModuleName}`) return mainModuleName
|
|
92
|
+
return undefined
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// defaultConfigOverrides and Metro config loading can replace the resolver.
|
|
96
|
+
// enforce package semantics on the final config while leaving every other
|
|
97
|
+
// request on the resolver selected by that composition.
|
|
98
|
+
function enforceBareMainModuleEntry(config: any, mainModuleName: string | undefined) {
|
|
99
|
+
if (!mainModuleName || !isBareSpecifier(mainModuleName)) return config
|
|
100
|
+
const innerResolveRequest = config?.resolver?.resolveRequest
|
|
101
|
+
return {
|
|
102
|
+
...config,
|
|
103
|
+
resolver: {
|
|
104
|
+
...config?.resolver,
|
|
105
|
+
resolveRequest: (context: any, moduleName: string, platform: string) => {
|
|
106
|
+
const bareMain = bareMainModuleForRequest(moduleName, mainModuleName)
|
|
107
|
+
const resolveRequest = innerResolveRequest || context.resolveRequest
|
|
108
|
+
return resolveRequest(context, bareMain ?? moduleName, platform)
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
55
114
|
async function isWatchmanResponsive(projectRoot: string) {
|
|
56
115
|
let probe = watchmanResponsivePromises.get(projectRoot)
|
|
57
116
|
if (probe) {
|
|
@@ -135,7 +194,7 @@ function resolveNativeTransforms(
|
|
|
135
194
|
* Build the Metro config input WITHOUT calling Metro's `loadConfig`. Returns
|
|
136
195
|
* the same shape Metro `loadConfig` expects as its second argument. Use this
|
|
137
196
|
* from a project's `metro.config.cjs` so the outer `loadConfig` (driven by
|
|
138
|
-
*
|
|
197
|
+
* the Metro CLI) is the only one that runs. this avoids infinite
|
|
139
198
|
* recursion that would happen if the inner pipeline also called `loadConfig`
|
|
140
199
|
* and re-read the same metro.config.cjs.
|
|
141
200
|
*/
|
|
@@ -156,53 +215,20 @@ export async function buildMetroConfigInputFromViteConfig(
|
|
|
156
215
|
)
|
|
157
216
|
}
|
|
158
217
|
|
|
159
|
-
const
|
|
160
|
-
getDefaultConfig: typeof getDefaultConfigT
|
|
161
|
-
}>(projectRoot, '@expo/metro-config')
|
|
162
|
-
|
|
163
|
-
const _defaultConfig: MetroInputConfig = getDefaultConfig(projectRoot) as any
|
|
218
|
+
const _defaultConfig = getReactNativeDefaultConfig(projectRoot)
|
|
164
219
|
|
|
165
220
|
if (mainModuleName) {
|
|
166
|
-
const origRewriteRequestUrl = _defaultConfig!.server
|
|
167
|
-
|
|
168
|
-
const resolveMainModuleName: (p: { platform: 'ios' | 'android' }) => string =
|
|
169
|
-
await (async () => {
|
|
170
|
-
const ExpoGoManifestHandlerMiddleware = (
|
|
171
|
-
await projectImport(
|
|
172
|
-
projectRoot,
|
|
173
|
-
'@expo/cli/build/src/start/server/middleware/ExpoGoManifestHandlerMiddleware.js'
|
|
174
|
-
)
|
|
175
|
-
).default.ExpoGoManifestHandlerMiddleware
|
|
176
|
-
|
|
177
|
-
const manifestHandlerMiddleware = new ExpoGoManifestHandlerMiddleware(
|
|
178
|
-
projectRoot,
|
|
179
|
-
{}
|
|
180
|
-
)
|
|
181
|
-
|
|
182
|
-
patchExpoGoManifestHandlerMiddlewareWithCustomMainModuleName(
|
|
183
|
-
manifestHandlerMiddleware,
|
|
184
|
-
mainModuleName
|
|
185
|
-
)
|
|
186
|
-
|
|
187
|
-
return (p) => {
|
|
188
|
-
return manifestHandlerMiddleware.resolveMainModuleName({
|
|
189
|
-
pkg: { main: mainModuleName },
|
|
190
|
-
platform: p.platform,
|
|
191
|
-
})
|
|
192
|
-
}
|
|
193
|
-
})()
|
|
221
|
+
const origRewriteRequestUrl = _defaultConfig!.server?.rewriteRequestUrl
|
|
222
|
+
const resolveMainModuleName = () => mainModuleName
|
|
194
223
|
|
|
195
224
|
extraConfig.getResolveMainModuleName = resolveMainModuleName
|
|
196
225
|
|
|
197
226
|
// @ts-expect-error Metro 0.83 made this read-only in types but we need to patch it
|
|
198
227
|
_defaultConfig!.server!.rewriteRequestUrl = (url) => {
|
|
199
|
-
if (
|
|
200
|
-
url.includes('/.expo/.virtual-metro-entry.bundle?') ||
|
|
201
|
-
rootIndexBundleRequestPattern.test(url)
|
|
202
|
-
) {
|
|
228
|
+
if (rootIndexBundleRequestPattern.test(url)) {
|
|
203
229
|
return rewriteMainModuleBundleUrl(url, resolveMainModuleName)
|
|
204
230
|
}
|
|
205
|
-
return origRewriteRequestUrl(url)
|
|
231
|
+
return origRewriteRequestUrl?.(url) ?? url
|
|
206
232
|
}
|
|
207
233
|
}
|
|
208
234
|
|
|
@@ -233,6 +259,15 @@ export async function buildMetroConfigInputFromViteConfig(
|
|
|
233
259
|
|
|
234
260
|
const defaultConfig: MetroInputConfig = {
|
|
235
261
|
..._defaultConfig,
|
|
262
|
+
watcher: {
|
|
263
|
+
..._defaultConfig?.watcher,
|
|
264
|
+
additionalExts: [
|
|
265
|
+
...new Set([
|
|
266
|
+
...(_defaultConfig?.watcher?.additionalExts ?? []),
|
|
267
|
+
...expoEnvAdditionalExts,
|
|
268
|
+
]),
|
|
269
|
+
],
|
|
270
|
+
},
|
|
236
271
|
resolver: {
|
|
237
272
|
..._defaultConfig?.resolver,
|
|
238
273
|
useWatchman,
|
|
@@ -269,6 +304,10 @@ export async function buildMetroConfigInputFromViteConfig(
|
|
|
269
304
|
},
|
|
270
305
|
transformer: {
|
|
271
306
|
..._defaultConfig?.transformer,
|
|
307
|
+
// one's router and Expo's optional development env module both use
|
|
308
|
+
// require.context. Metro installs a throwing runtime stub unless this
|
|
309
|
+
// graph feature is enabled.
|
|
310
|
+
unstable_allowRequireContext: true,
|
|
272
311
|
babelTransformerPath: projectResolve(
|
|
273
312
|
projectRoot,
|
|
274
313
|
'@vxrn/vite-plugin-metro/babel-transformer'
|
|
@@ -287,7 +326,14 @@ export async function buildMetroConfigInputFromViteConfig(
|
|
|
287
326
|
: defaultConfigOverrides),
|
|
288
327
|
}
|
|
289
328
|
|
|
290
|
-
return {
|
|
329
|
+
return {
|
|
330
|
+
defaultConfig: enforceBareMainModuleEntry(
|
|
331
|
+
enforceWorkspaceVisibility(merged, projectRoot),
|
|
332
|
+
mainModuleName
|
|
333
|
+
),
|
|
334
|
+
projectRoot,
|
|
335
|
+
extraConfig,
|
|
336
|
+
}
|
|
291
337
|
}
|
|
292
338
|
|
|
293
339
|
export async function getMetroConfigFromViteConfig(
|
|
@@ -313,60 +359,21 @@ export async function getMetroConfigFromViteConfig(
|
|
|
313
359
|
const { loadConfig } = await projectImport<{
|
|
314
360
|
loadConfig: typeof loadConfigT
|
|
315
361
|
}>(projectRoot, 'metro')
|
|
316
|
-
const
|
|
317
|
-
getDefaultConfig: typeof getDefaultConfigT
|
|
318
|
-
}>(projectRoot, '@expo/metro-config')
|
|
319
|
-
|
|
320
|
-
const _defaultConfig: MetroInputConfig = getDefaultConfig(projectRoot) as any
|
|
362
|
+
const _defaultConfig = getReactNativeDefaultConfig(projectRoot)
|
|
321
363
|
|
|
322
364
|
if (mainModuleName) {
|
|
323
|
-
const origRewriteRequestUrl = _defaultConfig!.server
|
|
324
|
-
|
|
325
|
-
// We need to patch Expo's default `config.server.rewriteRequestUrl`
|
|
326
|
-
// to change how URLs like '/.expo/.virtual-metro-entry.bundle?' are
|
|
327
|
-
// rewritten.
|
|
328
|
-
// But since that function is difficult to override, here we borrow
|
|
329
|
-
// the ExpoGoManifestHandlerMiddleware and use it to resolve the
|
|
330
|
-
// URL to the main module name.
|
|
331
|
-
const resolveMainModuleName: (p: { platform: 'ios' | 'android' }) => string =
|
|
332
|
-
await (async () => {
|
|
333
|
-
const ExpoGoManifestHandlerMiddleware = (
|
|
334
|
-
await projectImport(
|
|
335
|
-
projectRoot,
|
|
336
|
-
'@expo/cli/build/src/start/server/middleware/ExpoGoManifestHandlerMiddleware.js'
|
|
337
|
-
)
|
|
338
|
-
).default.ExpoGoManifestHandlerMiddleware
|
|
339
|
-
|
|
340
|
-
const manifestHandlerMiddleware = new ExpoGoManifestHandlerMiddleware(
|
|
341
|
-
projectRoot,
|
|
342
|
-
{}
|
|
343
|
-
)
|
|
344
|
-
|
|
345
|
-
patchExpoGoManifestHandlerMiddlewareWithCustomMainModuleName(
|
|
346
|
-
manifestHandlerMiddleware,
|
|
347
|
-
mainModuleName
|
|
348
|
-
)
|
|
349
|
-
|
|
350
|
-
return (p) => {
|
|
351
|
-
return manifestHandlerMiddleware.resolveMainModuleName({
|
|
352
|
-
pkg: { main: mainModuleName },
|
|
353
|
-
platform: p.platform,
|
|
354
|
-
})
|
|
355
|
-
}
|
|
356
|
-
})()
|
|
365
|
+
const origRewriteRequestUrl = _defaultConfig!.server?.rewriteRequestUrl
|
|
366
|
+
const resolveMainModuleName = () => mainModuleName
|
|
357
367
|
|
|
358
368
|
extraConfig.getResolveMainModuleName = resolveMainModuleName
|
|
359
369
|
|
|
360
370
|
// @ts-expect-error Metro 0.83 made this read-only in types but we need to patch it
|
|
361
371
|
_defaultConfig!.server!.rewriteRequestUrl = (url) => {
|
|
362
|
-
if (
|
|
363
|
-
url.includes('/.expo/.virtual-metro-entry.bundle?') ||
|
|
364
|
-
rootIndexBundleRequestPattern.test(url)
|
|
365
|
-
) {
|
|
372
|
+
if (rootIndexBundleRequestPattern.test(url)) {
|
|
366
373
|
return rewriteMainModuleBundleUrl(url, resolveMainModuleName)
|
|
367
374
|
}
|
|
368
375
|
|
|
369
|
-
return origRewriteRequestUrl(url)
|
|
376
|
+
return origRewriteRequestUrl?.(url) ?? url
|
|
370
377
|
}
|
|
371
378
|
}
|
|
372
379
|
|
|
@@ -397,6 +404,15 @@ export async function getMetroConfigFromViteConfig(
|
|
|
397
404
|
|
|
398
405
|
const defaultConfig: MetroInputConfig = {
|
|
399
406
|
..._defaultConfig,
|
|
407
|
+
watcher: {
|
|
408
|
+
..._defaultConfig?.watcher,
|
|
409
|
+
additionalExts: [
|
|
410
|
+
...new Set([
|
|
411
|
+
...(_defaultConfig?.watcher?.additionalExts ?? []),
|
|
412
|
+
...expoEnvAdditionalExts,
|
|
413
|
+
]),
|
|
414
|
+
],
|
|
415
|
+
},
|
|
400
416
|
resolver: {
|
|
401
417
|
..._defaultConfig?.resolver,
|
|
402
418
|
useWatchman,
|
|
@@ -441,6 +457,10 @@ export async function getMetroConfigFromViteConfig(
|
|
|
441
457
|
},
|
|
442
458
|
transformer: {
|
|
443
459
|
..._defaultConfig?.transformer,
|
|
460
|
+
// one's router and Expo's optional development env module both use
|
|
461
|
+
// require.context. Metro installs a throwing runtime stub unless this
|
|
462
|
+
// graph feature is enabled.
|
|
463
|
+
unstable_allowRequireContext: true,
|
|
444
464
|
babelTransformerPath: projectResolve(
|
|
445
465
|
projectRoot,
|
|
446
466
|
'@vxrn/vite-plugin-metro/babel-transformer'
|
|
@@ -468,7 +488,10 @@ export async function getMetroConfigFromViteConfig(
|
|
|
468
488
|
)
|
|
469
489
|
|
|
470
490
|
return {
|
|
471
|
-
...
|
|
491
|
+
...enforceBareMainModuleEntry(
|
|
492
|
+
enforceWorkspaceVisibility(metroConfig, projectRoot),
|
|
493
|
+
mainModuleName
|
|
494
|
+
),
|
|
472
495
|
...extraConfig,
|
|
473
496
|
} as MetroConfigExtended
|
|
474
497
|
}
|
|
@@ -9,8 +9,7 @@ import { checkAndClearMetroCacheFromVite } from '../utils/metroCacheManager'
|
|
|
9
9
|
|
|
10
10
|
const { debug } = createDebugger('vite-plugin-metro')
|
|
11
11
|
|
|
12
|
-
//
|
|
13
|
-
// We use `projectImport` to dynamically import the actual modules
|
|
12
|
+
// for Metro, we only import types here. `projectImport` loads the actual modules
|
|
14
13
|
// at runtime to ensure they are loaded from the user's project root.
|
|
15
14
|
import type MetroT from 'metro'
|
|
16
15
|
import type { loadConfig as loadConfigT } from 'metro'
|
|
@@ -72,10 +71,8 @@ export type MetroPluginOptions = {
|
|
|
72
71
|
/**
|
|
73
72
|
* Overrides the main module name which is normally defined as the `main` field in `package.json`.
|
|
74
73
|
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
* It can be used to change the entry point of the React Native app without the need of using
|
|
78
|
-
* the `main` field in `package.json`.
|
|
74
|
+
* Changes the React Native entry point without requiring a `main` field in
|
|
75
|
+
* `package.json`.
|
|
79
76
|
*/
|
|
80
77
|
mainModuleName?: string
|
|
81
78
|
/**
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import path from 'node:path'
|
|
2
|
+
import { fileURLToPath } from 'node:url'
|
|
3
|
+
import vm from 'node:vm'
|
|
4
|
+
import { describe, expect, it } from 'vitest'
|
|
5
|
+
|
|
6
|
+
import { transformFromAstSync } from './babel-core'
|
|
7
|
+
// @ts-expect-error: module.exports = has no default export in types, runtime interop provides it
|
|
8
|
+
import babelTransformer from './babel-transformer'
|
|
9
|
+
|
|
10
|
+
const fixturesDir = path.join(
|
|
11
|
+
path.dirname(fileURLToPath(import.meta.url)),
|
|
12
|
+
'__fixtures__'
|
|
13
|
+
)
|
|
14
|
+
const virtualFilename = '/proj/node_modules/expo/virtual/env.js'
|
|
15
|
+
const virtualSource = 'export const env = process.env;'
|
|
16
|
+
|
|
17
|
+
function runBabelTransform(src: string, filename: string, dev: boolean): any {
|
|
18
|
+
// the transformer leaks BABEL_ENV=development when it was previously
|
|
19
|
+
// unset; keep that pre-existing leak out of sibling tests.
|
|
20
|
+
const OLD_BABEL_ENV = process.env.BABEL_ENV
|
|
21
|
+
try {
|
|
22
|
+
return (babelTransformer as any).transform({
|
|
23
|
+
filename,
|
|
24
|
+
src,
|
|
25
|
+
options: {
|
|
26
|
+
projectRoot: fixturesDir,
|
|
27
|
+
dev,
|
|
28
|
+
platform: 'ios',
|
|
29
|
+
customTransformOptions: {
|
|
30
|
+
environment: 'client',
|
|
31
|
+
vite: {
|
|
32
|
+
oneViteMetroBabelConfig: true,
|
|
33
|
+
babelConfig: {
|
|
34
|
+
babelrc: false,
|
|
35
|
+
configFile: false,
|
|
36
|
+
presets: [],
|
|
37
|
+
plugins: [],
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
plugins: [],
|
|
43
|
+
})
|
|
44
|
+
} finally {
|
|
45
|
+
if (OLD_BABEL_ENV === undefined) {
|
|
46
|
+
delete process.env.BABEL_ENV
|
|
47
|
+
} else {
|
|
48
|
+
process.env.BABEL_ENV = OLD_BABEL_ENV
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function codegen(ast: any, filename: string): string {
|
|
54
|
+
const out = transformFromAstSync(ast, virtualSource, {
|
|
55
|
+
filename,
|
|
56
|
+
babelrc: false,
|
|
57
|
+
configFile: false,
|
|
58
|
+
code: true,
|
|
59
|
+
ast: false,
|
|
60
|
+
})
|
|
61
|
+
if (!out?.code) {
|
|
62
|
+
throw new Error('codegen produced no code')
|
|
63
|
+
}
|
|
64
|
+
return out.code
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function runCjs(
|
|
68
|
+
code: string,
|
|
69
|
+
opts: { requireImpl: (...args: any[]) => any; procEnv: Record<string, string> }
|
|
70
|
+
) {
|
|
71
|
+
const module = { exports: {} as any }
|
|
72
|
+
vm.runInNewContext(code, {
|
|
73
|
+
require: opts.requireImpl,
|
|
74
|
+
module,
|
|
75
|
+
exports: module.exports,
|
|
76
|
+
process: { env: opts.procEnv },
|
|
77
|
+
console,
|
|
78
|
+
})
|
|
79
|
+
return module.exports
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const noRequire = () => {
|
|
83
|
+
throw new Error('unexpected require call')
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
describe('babel-transformer optional expo virtual env', () => {
|
|
87
|
+
it('merges .env files over process.env in dev like upstream', () => {
|
|
88
|
+
const result = runBabelTransform(virtualSource, virtualFilename, true)
|
|
89
|
+
expect(result.ast).toBeTruthy()
|
|
90
|
+
const code = codegen(result.ast, virtualFilename)
|
|
91
|
+
expect(code).toContain('require.context')
|
|
92
|
+
expect(code).toContain('.env.development.local')
|
|
93
|
+
const contextModule: any = () => ({
|
|
94
|
+
default: { EXPO_PUBLIC_SENTINEL_DOTENV: 'dotenv-sentinel-value' },
|
|
95
|
+
})
|
|
96
|
+
contextModule.keys = () => ['./.env']
|
|
97
|
+
const requireImpl: any = noRequire
|
|
98
|
+
requireImpl.context = () => contextModule
|
|
99
|
+
const mod = runCjs(code, {
|
|
100
|
+
requireImpl,
|
|
101
|
+
procEnv: { EXPO_PUBLIC_SENTINEL_PROC: 'proc-sentinel-value' },
|
|
102
|
+
})
|
|
103
|
+
expect(mod.env.EXPO_PUBLIC_SENTINEL_DOTENV).toBe('dotenv-sentinel-value')
|
|
104
|
+
expect(mod.env.EXPO_PUBLIC_SENTINEL_PROC).toBe('proc-sentinel-value')
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
it('throws a naming error in production like upstream', () => {
|
|
108
|
+
const result = runBabelTransform(virtualSource, virtualFilename, false)
|
|
109
|
+
expect(result.ast).toBeTruthy()
|
|
110
|
+
const mod = runCjs(codegen(result.ast, virtualFilename), {
|
|
111
|
+
requireImpl: noRequire,
|
|
112
|
+
procEnv: {},
|
|
113
|
+
})
|
|
114
|
+
expect(() => mod.env.EXPO_PUBLIC_ANYTHING).toThrow(
|
|
115
|
+
'not supported in production bundles'
|
|
116
|
+
)
|
|
117
|
+
})
|
|
118
|
+
})
|
|
@@ -5,20 +5,27 @@
|
|
|
5
5
|
* This source code is licensed under the MIT license found in the
|
|
6
6
|
* LICENSE file in the root directory of this source tree.
|
|
7
7
|
*/
|
|
8
|
-
//
|
|
9
|
-
//
|
|
8
|
+
// a fork of the upstream babel-transformer that adds support for web and node
|
|
9
|
+
// environments via `isServer` on the babel caller.
|
|
10
10
|
// See:
|
|
11
11
|
// * https://github.com/facebook/metro/blob/main/packages/metro-babel-transformer/src/index.js
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
import type {
|
|
13
|
+
BabelTransformer,
|
|
14
|
+
BabelTransformerArgs,
|
|
15
|
+
BabelTransformerCacheKeyOptions,
|
|
16
|
+
} from 'metro-babel-transformer'
|
|
14
17
|
import assert from 'node:assert'
|
|
18
|
+
import { createHash } from 'node:crypto'
|
|
19
|
+
import { readFileSync } from 'node:fs'
|
|
20
|
+
import path from 'node:path'
|
|
15
21
|
|
|
16
22
|
import type { TransformOptions } from './babel-core'
|
|
23
|
+
import { substituteExpoVirtualEnvSource } from './expoVirtualEnv'
|
|
17
24
|
import { loadBabelConfig } from './loadBabelConfig'
|
|
18
25
|
import { transformSync } from './transformSync'
|
|
19
26
|
import type { ViteCustomTransformOptions } from './types'
|
|
20
27
|
|
|
21
|
-
export type
|
|
28
|
+
export type MetroBabelCaller = TransformOptions['caller'] & {
|
|
22
29
|
supportsReactCompiler?: boolean
|
|
23
30
|
isReactServer?: boolean
|
|
24
31
|
isHMREnabled?: boolean
|
|
@@ -37,7 +44,7 @@ export type ExpoBabelCaller = TransformOptions['caller'] & {
|
|
|
37
44
|
}
|
|
38
45
|
|
|
39
46
|
const debug = require('debug')(
|
|
40
|
-
'
|
|
47
|
+
'vxrn:metro-config:babel-transformer'
|
|
41
48
|
) as typeof console.log
|
|
42
49
|
|
|
43
50
|
function isCustomTruthy(value: any): boolean {
|
|
@@ -67,7 +74,7 @@ function getBabelCaller({
|
|
|
67
74
|
oneViteMetroBabelConfig,
|
|
68
75
|
}: Pick<BabelTransformerArgs, 'filename' | 'options'> & {
|
|
69
76
|
oneViteMetroBabelConfig: boolean
|
|
70
|
-
}):
|
|
77
|
+
}): MetroBabelCaller {
|
|
71
78
|
const isNodeModule = filename.includes('node_modules')
|
|
72
79
|
const isReactServer = options.customTransformOptions?.environment === 'react-server'
|
|
73
80
|
const isGenericServer = options.customTransformOptions?.environment === 'node'
|
|
@@ -80,7 +87,7 @@ function getBabelCaller({
|
|
|
80
87
|
|
|
81
88
|
if (routerRoot == null) {
|
|
82
89
|
memoizeWarning(
|
|
83
|
-
'Warning: Missing transform.routerRoot option in Metro bundling request, falling back to `app` as routes directory.
|
|
90
|
+
'Warning: Missing transform.routerRoot option in Metro bundling request, falling back to `app` as routes directory.'
|
|
84
91
|
)
|
|
85
92
|
}
|
|
86
93
|
|
|
@@ -119,7 +126,7 @@ function getBabelCaller({
|
|
|
119
126
|
// target environment.
|
|
120
127
|
engine: stringOrUndefined(options.customTransformOptions?.engine),
|
|
121
128
|
|
|
122
|
-
//
|
|
129
|
+
// provide the project root for reading the app babel config
|
|
123
130
|
projectRoot: options.projectRoot,
|
|
124
131
|
oneViteMetroBabelConfig,
|
|
125
132
|
|
|
@@ -146,17 +153,28 @@ function stringOrUndefined(value: unknown): string | undefined {
|
|
|
146
153
|
|
|
147
154
|
const transform: BabelTransformer['transform'] = ({
|
|
148
155
|
filename,
|
|
149
|
-
src,
|
|
156
|
+
src: originalSrc,
|
|
150
157
|
options,
|
|
151
|
-
// `plugins` is used for `functionMapBabelPlugin` from `metro-source-map`.
|
|
158
|
+
// `plugins` is used for `functionMapBabelPlugin` from `metro-source-map`.
|
|
152
159
|
plugins,
|
|
153
160
|
}: BabelTransformerArgs): ReturnType<BabelTransformer['transform']> => {
|
|
161
|
+
// narrow optional Expo compatibility (expo/virtual/env.js and .env files
|
|
162
|
+
// only), shared with the native worker. no-Expo apps pass through byte
|
|
163
|
+
// for byte without resolving any Expo module.
|
|
164
|
+
const src = substituteExpoVirtualEnvSource({
|
|
165
|
+
filename,
|
|
166
|
+
src: originalSrc,
|
|
167
|
+
projectRoot: options.projectRoot,
|
|
168
|
+
dev: options.dev,
|
|
169
|
+
environment: options.customTransformOptions?.environment,
|
|
170
|
+
})
|
|
171
|
+
|
|
154
172
|
const viteCustomTransformOptions = options.customTransformOptions?.vite
|
|
155
173
|
|
|
156
174
|
const customOptionsFromVite: ViteCustomTransformOptions = (() => {
|
|
157
175
|
const c: any = viteCustomTransformOptions
|
|
158
|
-
//
|
|
159
|
-
// customTransformOptions.vite
|
|
176
|
+
// standalone Metro invocations don't set
|
|
177
|
+
// customTransformOptions.vite. the plugins flow entirely through the
|
|
160
178
|
// project's babel.config.cjs in that case. Tolerate the missing field
|
|
161
179
|
// rather than throwing so a single Metro config can serve both the
|
|
162
180
|
// Vite-driven and standalone paths.
|
|
@@ -210,8 +228,8 @@ const transform: BabelTransformer['transform'] = ({
|
|
|
210
228
|
// all (most) of the transforms in their local Babel config.
|
|
211
229
|
// This also helps us keep the transform layers small and focused on a single task. We can also use this to
|
|
212
230
|
// ensure the Babel config caching is more accurate.
|
|
213
|
-
//
|
|
214
|
-
//
|
|
231
|
+
// moving everything babel-related to the preset also keeps the transform
|
|
232
|
+
// requirements explicit for users who opt into babel.
|
|
215
233
|
caller: getBabelCaller({
|
|
216
234
|
filename,
|
|
217
235
|
options,
|
|
@@ -238,8 +256,57 @@ const transform: BabelTransformer['transform'] = ({
|
|
|
238
256
|
}
|
|
239
257
|
}
|
|
240
258
|
|
|
259
|
+
export function getCacheKey(options?: BabelTransformerCacheKeyOptions): string {
|
|
260
|
+
const hash = createHash('sha256')
|
|
261
|
+
hash.update('vxrn-metro-babel-transformer-v1')
|
|
262
|
+
|
|
263
|
+
const projectRoot = options?.projectRoot
|
|
264
|
+
if (!projectRoot) {
|
|
265
|
+
return hash.digest('hex')
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
hash.update(projectRoot)
|
|
269
|
+
|
|
270
|
+
for (const file of [
|
|
271
|
+
'.env',
|
|
272
|
+
'.env.development',
|
|
273
|
+
'.env.local',
|
|
274
|
+
'.env.development.local',
|
|
275
|
+
]) {
|
|
276
|
+
const filename = path.join(projectRoot, file)
|
|
277
|
+
hash.update(filename)
|
|
278
|
+
try {
|
|
279
|
+
hash.update(readFileSync(filename))
|
|
280
|
+
} catch (error) {
|
|
281
|
+
if (
|
|
282
|
+
typeof error === 'object' &&
|
|
283
|
+
error !== null &&
|
|
284
|
+
'code' in error &&
|
|
285
|
+
error.code === 'ENOENT'
|
|
286
|
+
) {
|
|
287
|
+
continue
|
|
288
|
+
}
|
|
289
|
+
throw error
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
hash.update(
|
|
294
|
+
JSON.stringify(
|
|
295
|
+
Object.entries(process.env)
|
|
296
|
+
.filter(
|
|
297
|
+
([key]) =>
|
|
298
|
+
key.startsWith('ONE_PUBLIC_') || key.startsWith('EXPO_PUBLIC_')
|
|
299
|
+
)
|
|
300
|
+
.sort(([left], [right]) => left.localeCompare(right))
|
|
301
|
+
)
|
|
302
|
+
)
|
|
303
|
+
|
|
304
|
+
return hash.digest('hex')
|
|
305
|
+
}
|
|
306
|
+
|
|
241
307
|
const babelTransformer: BabelTransformer = {
|
|
242
308
|
transform,
|
|
309
|
+
getCacheKey,
|
|
243
310
|
}
|
|
244
311
|
|
|
245
312
|
module.exports = babelTransformer
|