vxrn 1.12.8 → 1.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config/getAdditionalViteConfig.mjs +1 -4
- package/dist/config/getAdditionalViteConfig.mjs.map +1 -1
- package/dist/config/getAdditionalViteConfig.native.js +1 -4
- package/dist/config/getAdditionalViteConfig.native.js.map +1 -1
- package/dist/config/getReactNativePlugins.mjs +1 -5
- package/dist/config/getReactNativePlugins.mjs.map +1 -1
- package/dist/config/getReactNativePlugins.native.js +1 -5
- package/dist/config/getReactNativePlugins.native.js.map +1 -1
- package/dist/exports/build.mjs +5 -0
- package/dist/exports/build.mjs.map +1 -1
- package/dist/exports/build.native.js +5 -0
- package/dist/exports/build.native.js.map +1 -1
- package/dist/exports/createServer.mjs +30 -25
- package/dist/exports/createServer.mjs.map +1 -1
- package/dist/exports/createServer.native.js +6 -3
- package/dist/exports/createServer.native.js.map +1 -1
- package/dist/exports/prebuild.mjs +25 -1
- package/dist/exports/prebuild.mjs.map +1 -1
- package/dist/exports/prebuild.native.js +30 -1
- package/dist/exports/prebuild.native.js.map +1 -1
- package/dist/exports/serve.mjs +2 -2
- package/dist/exports/serve.mjs.map +1 -1
- package/dist/exports/serve.native.js +2 -2
- package/dist/exports/serve.native.js.map +1 -1
- package/dist/exports/serveStaticAssets.mjs +43 -11
- package/dist/exports/serveStaticAssets.mjs.map +1 -1
- package/dist/exports/serveStaticAssets.native.js +69 -6
- package/dist/exports/serveStaticAssets.native.js.map +1 -1
- package/dist/index.js +1 -2
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -2
- package/dist/index.mjs.map +1 -1
- package/dist/index.native.js +1 -2
- package/dist/index.native.js.map +1 -1
- package/dist/patches/builtInDepPatches.mjs +14 -0
- package/dist/patches/builtInDepPatches.mjs.map +1 -1
- package/dist/patches/builtInDepPatches.native.js +14 -0
- package/dist/patches/builtInDepPatches.native.js.map +1 -1
- package/dist/plugins/clientInjectPlugin.mjs +2 -62
- package/dist/plugins/clientInjectPlugin.mjs.map +1 -1
- package/dist/plugins/clientInjectPlugin.native.js +2 -75
- package/dist/plugins/clientInjectPlugin.native.js.map +1 -1
- package/dist/plugins/reactNativeDevAssetPlugin.mjs +1 -3
- package/dist/plugins/reactNativeDevAssetPlugin.mjs.map +1 -1
- package/dist/plugins/reactNativeDevAssetPlugin.native.js +1 -3
- package/dist/plugins/reactNativeDevAssetPlugin.native.js.map +1 -1
- package/dist/plugins/reactNativeDevServer.mjs +55 -77
- package/dist/plugins/reactNativeDevServer.mjs.map +1 -1
- package/dist/plugins/reactNativeDevServer.native.js +73 -84
- package/dist/plugins/reactNativeDevServer.native.js.map +1 -1
- package/dist/plugins/serverExtensions.test.mjs +12 -6
- package/dist/plugins/serverExtensions.test.mjs.map +1 -1
- package/dist/plugins/serverExtensions.test.native.js +12 -6
- package/dist/plugins/serverExtensions.test.native.js.map +1 -1
- package/dist/rn-commands/bundle/buildBundle.mjs +11 -35
- package/dist/rn-commands/bundle/buildBundle.mjs.map +1 -1
- package/dist/rn-commands/bundle/buildBundle.native.js +9 -36
- package/dist/rn-commands/bundle/buildBundle.native.js.map +1 -1
- package/dist/runtime/hmr-client.mjs +125 -0
- package/dist/runtime/hmr-client.mjs.map +1 -0
- package/dist/runtime/hmr-client.native.js +197 -0
- package/dist/runtime/hmr-client.native.js.map +1 -0
- package/dist/runtime/hmr-runtime.mjs +162 -0
- package/dist/runtime/hmr-runtime.mjs.map +1 -0
- package/dist/runtime/hmr-runtime.native.js +348 -0
- package/dist/runtime/hmr-runtime.native.js.map +1 -0
- package/dist/runtime/hmr-types.mjs +2 -0
- package/dist/runtime/hmr-types.mjs.map +1 -0
- package/dist/runtime/hmr-types.native.js +2 -0
- package/dist/runtime/hmr-types.native.js.map +1 -0
- package/dist/runtime/native-prelude.mjs +97 -0
- package/dist/runtime/native-prelude.mjs.map +1 -0
- package/dist/runtime/native-prelude.native.js +97 -0
- package/dist/runtime/native-prelude.native.js.map +1 -0
- package/dist/runtime/react-refresh-utils.mjs +19 -0
- package/dist/runtime/react-refresh-utils.mjs.map +1 -0
- package/dist/runtime/react-refresh-utils.native.js +24 -0
- package/dist/runtime/react-refresh-utils.native.js.map +1 -0
- package/dist/utils/createNativeDevEngine.mjs +661 -0
- package/dist/utils/createNativeDevEngine.mjs.map +1 -0
- package/dist/utils/createNativeDevEngine.native.js +702 -0
- package/dist/utils/createNativeDevEngine.native.js.map +1 -0
- package/dist/utils/patches.mjs +6 -2
- package/dist/utils/patches.mjs.map +1 -1
- package/dist/utils/patches.native.js +6 -2
- package/dist/utils/patches.native.js.map +1 -1
- package/dist/utils/scanDepsToOptimize.mjs +4 -3
- package/dist/utils/scanDepsToOptimize.mjs.map +1 -1
- package/dist/utils/scanDepsToOptimize.native.js +4 -3
- package/dist/utils/scanDepsToOptimize.native.js.map +1 -1
- package/expo-plugin.cjs +122 -0
- package/package.json +15 -19
- package/src/config/getAdditionalViteConfig.ts +1 -3
- package/src/config/getReactNativePlugins.ts +0 -6
- package/src/exports/build.ts +5 -0
- package/src/exports/createServer.ts +7 -2
- package/src/exports/prebuild.ts +45 -0
- package/src/exports/serve.ts +2 -1
- package/src/exports/serveStaticAssets.ts +67 -4
- package/src/index.ts +0 -2
- package/src/patches/builtInDepPatches.ts +29 -0
- package/src/plugins/clientInjectPlugin.ts +2 -109
- package/src/plugins/reactNativeDevAssetPlugin.ts +0 -21
- package/src/plugins/reactNativeDevServer.ts +57 -84
- package/src/plugins/serverExtensions.test.ts +6 -8
- package/src/rn-commands/bundle/buildBundle.ts +9 -62
- package/src/runtime/hmr-client.ts +215 -0
- package/src/runtime/hmr-runtime.ts +276 -0
- package/src/runtime/hmr-types.ts +84 -0
- package/src/runtime/native-prelude.ts +110 -0
- package/src/runtime/react-refresh-utils.ts +36 -0
- package/src/types.ts +22 -4
- package/src/utils/createNativeDevEngine.ts +942 -0
- package/src/utils/patches.ts +36 -18
- package/src/utils/scanDepsToOptimize.ts +2 -3
- package/types/config/getAdditionalViteConfig.d.ts.map +1 -1
- package/types/config/getOptionsFilled.d.ts +2 -18
- package/types/config/getOptionsFilled.d.ts.map +1 -1
- package/types/config/getReactNativePlugins.d.ts.map +1 -1
- package/types/exports/build.d.ts +1 -9
- package/types/exports/build.d.ts.map +1 -1
- package/types/exports/createServer.d.ts.map +1 -1
- package/types/exports/prebuild.d.ts.map +1 -1
- package/types/exports/serve.d.ts +2 -1
- package/types/exports/serve.d.ts.map +1 -1
- package/types/exports/serveStaticAssets.d.ts +12 -1
- package/types/exports/serveStaticAssets.d.ts.map +1 -1
- package/types/index.d.ts +0 -1
- package/types/index.d.ts.map +1 -1
- package/types/patches/builtInDepPatches.d.ts.map +1 -1
- package/types/plugins/clientInjectPlugin.d.ts +1 -7
- package/types/plugins/clientInjectPlugin.d.ts.map +1 -1
- package/types/plugins/reactNativeDevAssetPlugin.d.ts.map +1 -1
- package/types/plugins/reactNativeDevServer.d.ts.map +1 -1
- package/types/rn-commands/bundle/buildBundle.d.ts.map +1 -1
- package/types/runtime/hmr-client.d.ts +40 -0
- package/types/runtime/hmr-client.d.ts.map +1 -0
- package/types/runtime/hmr-runtime.d.ts +69 -0
- package/types/runtime/hmr-runtime.d.ts.map +1 -0
- package/types/runtime/hmr-types.d.ts +76 -0
- package/types/runtime/hmr-types.d.ts.map +1 -0
- package/types/runtime/native-prelude.d.ts +11 -0
- package/types/runtime/native-prelude.d.ts.map +1 -0
- package/types/runtime/react-refresh-utils.d.ts +3 -0
- package/types/runtime/react-refresh-utils.d.ts.map +1 -0
- package/types/types.d.ts +15 -1
- package/types/types.d.ts.map +1 -1
- package/types/utils/createNativeDevEngine.d.ts +42 -0
- package/types/utils/createNativeDevEngine.d.ts.map +1 -0
- package/types/utils/patches.d.ts.map +1 -1
- package/types/utils/scanDepsToOptimize.d.ts.map +1 -1
- package/dist/config/getReactNativeBuildConfig.mjs +0 -200
- package/dist/config/getReactNativeBuildConfig.mjs.map +0 -1
- package/dist/config/getReactNativeBuildConfig.native.js +0 -204
- package/dist/config/getReactNativeBuildConfig.native.js.map +0 -1
- package/dist/plugins/reactNativeHMRPlugin.mjs +0 -120
- package/dist/plugins/reactNativeHMRPlugin.mjs.map +0 -1
- package/dist/plugins/reactNativeHMRPlugin.native.js +0 -151
- package/dist/plugins/reactNativeHMRPlugin.native.js.map +0 -1
- package/dist/utils/filterPluginsForNative.mjs +0 -27
- package/dist/utils/filterPluginsForNative.mjs.map +0 -1
- package/dist/utils/filterPluginsForNative.native.js +0 -33
- package/dist/utils/filterPluginsForNative.native.js.map +0 -1
- package/dist/utils/getReactNativeBundle.mjs +0 -104
- package/dist/utils/getReactNativeBundle.mjs.map +0 -1
- package/dist/utils/getReactNativeBundle.native.js +0 -135
- package/dist/utils/getReactNativeBundle.native.js.map +0 -1
- package/dist/utils/hotUpdateCache.mjs +0 -3
- package/dist/utils/hotUpdateCache.mjs.map +0 -1
- package/dist/utils/hotUpdateCache.native.js +0 -3
- package/dist/utils/hotUpdateCache.native.js.map +0 -1
- package/dist/utils/isBuildingNativeBundle.mjs +0 -6
- package/dist/utils/isBuildingNativeBundle.mjs.map +0 -1
- package/dist/utils/isBuildingNativeBundle.native.js +0 -7
- package/dist/utils/isBuildingNativeBundle.native.js.map +0 -1
- package/dist/utils/swapPrebuiltReactModules.mjs +0 -168
- package/dist/utils/swapPrebuiltReactModules.mjs.map +0 -1
- package/dist/utils/swapPrebuiltReactModules.native.js +0 -181
- package/dist/utils/swapPrebuiltReactModules.native.js.map +0 -1
- package/dist/worker.mjs +0 -55
- package/dist/worker.mjs.map +0 -1
- package/dist/worker.native.js +0 -55
- package/dist/worker.native.js.map +0 -1
- package/react-native-template.js +0 -375
- package/src/config/getReactNativeBuildConfig.ts +0 -349
- package/src/plugins/reactNativeHMRPlugin.ts +0 -237
- package/src/utils/filterPluginsForNative.ts +0 -55
- package/src/utils/getReactNativeBundle.ts +0 -243
- package/src/utils/hotUpdateCache.ts +0 -1
- package/src/utils/isBuildingNativeBundle.ts +0 -7
- package/src/utils/swapPrebuiltReactModules.ts +0 -341
- package/src/worker.ts +0 -90
- package/types/config/getReactNativeBuildConfig.d.ts +0 -72
- package/types/config/getReactNativeBuildConfig.d.ts.map +0 -1
- package/types/plugins/reactNativeHMRPlugin.d.ts +0 -10
- package/types/plugins/reactNativeHMRPlugin.d.ts.map +0 -1
- package/types/utils/filterPluginsForNative.d.ts +0 -8
- package/types/utils/filterPluginsForNative.d.ts.map +0 -1
- package/types/utils/getReactNativeBundle.d.ts +0 -12
- package/types/utils/getReactNativeBundle.d.ts.map +0 -1
- package/types/utils/hotUpdateCache.d.ts +0 -2
- package/types/utils/hotUpdateCache.d.ts.map +0 -1
- package/types/utils/isBuildingNativeBundle.d.ts +0 -3
- package/types/utils/isBuildingNativeBundle.d.ts.map +0 -1
- package/types/utils/swapPrebuiltReactModules.d.ts +0 -9
- package/types/utils/swapPrebuiltReactModules.d.ts.map +0 -1
- package/types/worker.d.ts +0 -13
- package/types/worker.d.ts.map +0 -1
package/src/exports/prebuild.ts
CHANGED
|
@@ -62,6 +62,12 @@ export const prebuild = async ({
|
|
|
62
62
|
)
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
// validate swift 6 workaround was injected into Podfile (outside prebuild try/catch
|
|
66
|
+
// so a validation error doesn't produce a misleading "Is expo listed?" message)
|
|
67
|
+
if (!platform || platform === 'ios') {
|
|
68
|
+
validateSwift6Workaround(root)
|
|
69
|
+
}
|
|
70
|
+
|
|
65
71
|
if (!platform || platform === 'ios') {
|
|
66
72
|
console.info(
|
|
67
73
|
`
|
|
@@ -206,6 +212,45 @@ Android:
|
|
|
206
212
|
}
|
|
207
213
|
}
|
|
208
214
|
|
|
215
|
+
function validateSwift6Workaround(root: string) {
|
|
216
|
+
const podfilePath = path.join(root, 'ios', 'Podfile')
|
|
217
|
+
if (!FSExtra.existsSync(podfilePath)) return
|
|
218
|
+
|
|
219
|
+
// check opt-out
|
|
220
|
+
const propsPath = path.join(root, 'ios', 'Podfile.properties.json')
|
|
221
|
+
if (FSExtra.existsSync(propsPath)) {
|
|
222
|
+
try {
|
|
223
|
+
const props = JSON.parse(FSExtra.readFileSync(propsPath, 'utf8'))
|
|
224
|
+
if (props['one.disableSwift6Workaround'] === 'true') return
|
|
225
|
+
} catch {}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// check version — only needed for <56
|
|
229
|
+
try {
|
|
230
|
+
const emcPkgPath = resolvePath('expo-modules-core/package.json', root)
|
|
231
|
+
const emcVersion = JSON.parse(FSExtra.readFileSync(emcPkgPath, 'utf8')).version
|
|
232
|
+
const major = Number.parseInt(emcVersion.match(/^(\d+)/)?.[1] || '0', 10)
|
|
233
|
+
if (major >= 56) return
|
|
234
|
+
} catch {}
|
|
235
|
+
|
|
236
|
+
const podfile = FSExtra.readFileSync(podfilePath, 'utf8')
|
|
237
|
+
if (!podfile.includes('SWIFT_STRICT_CONCURRENCY')) {
|
|
238
|
+
console.warn(
|
|
239
|
+
colors.yellow(`
|
|
240
|
+
⚠️ Your ios/Podfile is missing the Swift 6 workaround for expo-modules-core.
|
|
241
|
+
Without this, your iOS build will fail with Swift concurrency errors.
|
|
242
|
+
|
|
243
|
+
Fix: add "vxrn/expo-plugin" to your app.json plugins and re-run prebuild.
|
|
244
|
+
|
|
245
|
+
To disable this check: set "one.disableSwift6Workaround": "true"
|
|
246
|
+
in ios/Podfile.properties.json.
|
|
247
|
+
|
|
248
|
+
See: expo/expo#43199
|
|
249
|
+
`)
|
|
250
|
+
)
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
209
254
|
function isMissingCliDependency(error) {
|
|
210
255
|
return (
|
|
211
256
|
error.code === 'MODULE_NOT_FOUND' &&
|
package/src/exports/serve.ts
CHANGED
|
@@ -6,7 +6,8 @@ import { applyCompression, createProdServer } from './createServer'
|
|
|
6
6
|
export { loadEnv } from '../exports/loadEnv'
|
|
7
7
|
export * from '../utils/getServerEntry'
|
|
8
8
|
export { createProdServer, applyCompression } from './createServer'
|
|
9
|
-
export { serveStaticAssets } from './serveStaticAssets'
|
|
9
|
+
export { serveStaticAssets, compileCacheRules } from './serveStaticAssets'
|
|
10
|
+
export type { CompiledCacheRules } from './serveStaticAssets'
|
|
10
11
|
|
|
11
12
|
export const serve = async ({
|
|
12
13
|
afterRegisterRoutes,
|
|
@@ -1,24 +1,87 @@
|
|
|
1
1
|
import { serveStatic } from '@hono/node-server/serve-static'
|
|
2
2
|
import type { Context } from 'hono'
|
|
3
|
+
import micromatch from 'micromatch'
|
|
3
4
|
|
|
4
5
|
// hashed assets can be cached forever, html must revalidate
|
|
5
|
-
const hashedAssetRe = /[.-][a-zA-Z0-9_-]{8,}\.\w+$/
|
|
6
|
+
const hashedAssetRe = /[.-](?=[a-zA-Z0-9_-]*\d)[a-zA-Z0-9_-]{8,}\.\w+$/
|
|
7
|
+
|
|
8
|
+
export type CompiledCacheRules = { re: RegExp; values: string[] }
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* compile a cacheControl config (glob → header) into a single regex.
|
|
12
|
+
* each unique header value gets a capturing group — one regex.test()
|
|
13
|
+
* per request, then the matched group index maps to the header value.
|
|
14
|
+
*/
|
|
15
|
+
export function compileCacheRules(
|
|
16
|
+
cacheControl: Record<string, string>
|
|
17
|
+
): CompiledCacheRules {
|
|
18
|
+
// group patterns by header value
|
|
19
|
+
const groups = new Map<string, string[]>()
|
|
20
|
+
for (const [pattern, value] of Object.entries(cacheControl)) {
|
|
21
|
+
let arr = groups.get(value)
|
|
22
|
+
if (!arr) {
|
|
23
|
+
arr = []
|
|
24
|
+
groups.set(value, arr)
|
|
25
|
+
}
|
|
26
|
+
arr.push(pattern)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// each group becomes a capturing group: (pat1|pat2)|(pat3)
|
|
30
|
+
// matched group index → values[index]
|
|
31
|
+
const values: string[] = []
|
|
32
|
+
const groupSources: string[] = []
|
|
33
|
+
|
|
34
|
+
for (const [value, patterns] of groups) {
|
|
35
|
+
values.push(value)
|
|
36
|
+
const sources = patterns.map((p) => micromatch.makeRe(p).source)
|
|
37
|
+
groupSources.push(`(${sources.join('|')})`)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const re = new RegExp(groupSources.join('|'))
|
|
41
|
+
return { re, values }
|
|
42
|
+
}
|
|
6
43
|
|
|
7
44
|
export async function serveStaticAssets({
|
|
8
45
|
context,
|
|
9
46
|
next,
|
|
10
47
|
outDir = 'dist',
|
|
48
|
+
cacheRules,
|
|
11
49
|
}: {
|
|
12
50
|
context: Context
|
|
13
51
|
next?: () => Promise<void>
|
|
14
52
|
outDir?: string
|
|
53
|
+
cacheRules?: CompiledCacheRules
|
|
15
54
|
}) {
|
|
16
55
|
let didCallNext = false
|
|
17
56
|
|
|
57
|
+
const root = `./${outDir}/client`
|
|
58
|
+
// path.join normalizes "./" away, so pre-compute the normalized prefix
|
|
59
|
+
const rootPrefix = `${outDir}/client/`
|
|
60
|
+
|
|
18
61
|
const response = await serveStatic({
|
|
19
|
-
root
|
|
20
|
-
onFound: (
|
|
21
|
-
|
|
62
|
+
root,
|
|
63
|
+
onFound: (fsPath, c) => {
|
|
64
|
+
// onFound receives the joined fs path (e.g. "dist/client/foo.js")
|
|
65
|
+
// strip root prefix to get the URL-relative path for glob matching
|
|
66
|
+
const path = fsPath.startsWith(rootPrefix)
|
|
67
|
+
? fsPath.slice(rootPrefix.length)
|
|
68
|
+
: fsPath
|
|
69
|
+
|
|
70
|
+
// single regex test for all custom rules
|
|
71
|
+
if (cacheRules) {
|
|
72
|
+
const m = cacheRules.re.exec(path)
|
|
73
|
+
if (m) {
|
|
74
|
+
// find which capturing group matched
|
|
75
|
+
for (let i = 1; i < m.length; i++) {
|
|
76
|
+
if (m[i] !== undefined) {
|
|
77
|
+
c.header('Cache-Control', cacheRules.values[i - 1]!)
|
|
78
|
+
return
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (hashedAssetRe.test(fsPath)) {
|
|
22
85
|
c.header('Cache-Control', 'public, immutable, max-age=31536000')
|
|
23
86
|
} else {
|
|
24
87
|
c.header('Cache-Control', 'public, max-age=0, must-revalidate')
|
package/src/index.ts
CHANGED
|
@@ -28,6 +28,35 @@ export const builtInDepPatches: DepPatch[] = [
|
|
|
28
28
|
},
|
|
29
29
|
},
|
|
30
30
|
|
|
31
|
+
// @react-navigation/core 7.17 renamed createComponentForStaticNavigation to
|
|
32
|
+
// createComponentForStaticConfigDeprecated, breaking @react-navigation/native
|
|
33
|
+
// which still imports the old name. re-export the old name as an alias.
|
|
34
|
+
{
|
|
35
|
+
module: '@react-navigation/core',
|
|
36
|
+
patchFiles: {
|
|
37
|
+
'lib/module/index.js': (contents) => {
|
|
38
|
+
assertString(contents)
|
|
39
|
+
// only patch if the old export is missing and the new one exists
|
|
40
|
+
if (contents.includes('createComponentForStaticNavigation')) return
|
|
41
|
+
if (!contents.includes('createComponentForStaticConfigDeprecated')) return
|
|
42
|
+
return contents.replace(
|
|
43
|
+
/export \{ createComponentForStaticConfigDeprecated as createComponentForStaticConfig,/,
|
|
44
|
+
'export { createComponentForStaticConfigDeprecated as createComponentForStaticConfig, createComponentForStaticConfigDeprecated as createComponentForStaticNavigation,'
|
|
45
|
+
)
|
|
46
|
+
},
|
|
47
|
+
|
|
48
|
+
'lib/commonjs/index.js': (contents) => {
|
|
49
|
+
if (!contents) return
|
|
50
|
+
if (contents.includes('createComponentForStaticNavigation')) return
|
|
51
|
+
if (!contents.includes('createComponentForStaticConfigDeprecated')) return
|
|
52
|
+
return contents.replace(
|
|
53
|
+
/createComponentForStaticConfigDeprecated as createComponentForStaticConfig,/,
|
|
54
|
+
'createComponentForStaticConfigDeprecated as createComponentForStaticConfig, createComponentForStaticConfigDeprecated as createComponentForStaticNavigation,'
|
|
55
|
+
)
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
|
|
31
60
|
// react-native-web doesn't export unstable_batchedUpdates but react-native does,
|
|
32
61
|
// so libraries like @legendapp/list break when aliased to rnw on web
|
|
33
62
|
{
|
|
@@ -1,12 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
import type { HmrOptions, Plugin, ResolvedConfig, UserConfig } from 'vite'
|
|
4
|
-
import { isNativeEnvironment } from '../utils/environmentUtils'
|
|
5
|
-
import { getResolvedConfig, setResolvedConfig } from './getResolvedConfigSubset'
|
|
6
|
-
|
|
7
|
-
let config: ResolvedConfig | null
|
|
8
|
-
|
|
9
|
-
const isObject = (x: any): x is object => x && typeof x === 'object'
|
|
1
|
+
import type { Plugin } from 'vite'
|
|
2
|
+
import { setResolvedConfig } from './getResolvedConfigSubset'
|
|
10
3
|
|
|
11
4
|
export function getServerConfigPlugin() {
|
|
12
5
|
return {
|
|
@@ -22,103 +15,3 @@ export function getServerConfigPlugin() {
|
|
|
22
15
|
},
|
|
23
16
|
} satisfies Plugin
|
|
24
17
|
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* some values used by the client needs to be dynamically injected by the server
|
|
28
|
-
* @server-only
|
|
29
|
-
*/
|
|
30
|
-
export function nativeClientInjectPlugin(): Plugin {
|
|
31
|
-
let injectConfigValues: (code: string) => string
|
|
32
|
-
|
|
33
|
-
return {
|
|
34
|
-
name: 'vite:native-client-inject',
|
|
35
|
-
|
|
36
|
-
async buildStart() {
|
|
37
|
-
const config = getResolvedConfig()
|
|
38
|
-
|
|
39
|
-
const resolvedServerHostname = config.server.host || '127.0.0.1'
|
|
40
|
-
const resolvedServerPort = config.server!.port! || 5173
|
|
41
|
-
const devBase = config.base || '/'
|
|
42
|
-
|
|
43
|
-
const serverHost = `${resolvedServerHostname}:${resolvedServerPort}${devBase}`
|
|
44
|
-
|
|
45
|
-
let hmrConfig = config.server!.hmr
|
|
46
|
-
hmrConfig = isObject(hmrConfig) ? (hmrConfig as HmrOptions) : undefined
|
|
47
|
-
|
|
48
|
-
const host = hmrConfig?.host || null
|
|
49
|
-
const protocol = hmrConfig?.protocol || null
|
|
50
|
-
const timeout = hmrConfig?.timeout || 30000
|
|
51
|
-
const overlay = hmrConfig?.overlay !== false
|
|
52
|
-
const isHmrServerSpecified = !!hmrConfig?.server
|
|
53
|
-
|
|
54
|
-
// hmr.clientPort -> hmr.port
|
|
55
|
-
// -> (24678 if middleware mode and HMR server is not specified) -> new URL(import.meta.url).port
|
|
56
|
-
let port = hmrConfig?.clientPort || hmrConfig?.port || null
|
|
57
|
-
if (config.server.middlewareMode && !isHmrServerSpecified) {
|
|
58
|
-
port ||= 24678
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
let directTarget = hmrConfig?.host || resolvedServerHostname
|
|
62
|
-
directTarget += `:${hmrConfig?.port || resolvedServerPort}`
|
|
63
|
-
directTarget += devBase
|
|
64
|
-
|
|
65
|
-
let hmrBase = devBase
|
|
66
|
-
if (hmrConfig?.path) {
|
|
67
|
-
hmrBase = path.posix.join(hmrBase, hmrConfig.path)
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
const serializedDefines = serializeDefine(config.define || {})
|
|
71
|
-
|
|
72
|
-
const modeReplacement = escapeReplacement(config.mode!)
|
|
73
|
-
const baseReplacement = escapeReplacement(devBase)
|
|
74
|
-
const definesReplacement = () => serializedDefines
|
|
75
|
-
const serverHostReplacement = escapeReplacement(serverHost)
|
|
76
|
-
const hmrProtocolReplacement = escapeReplacement(protocol)
|
|
77
|
-
const hmrHostnameReplacement = escapeReplacement(host)
|
|
78
|
-
const hmrPortReplacement = escapeReplacement(port)
|
|
79
|
-
const hmrDirectTargetReplacement = escapeReplacement(directTarget)
|
|
80
|
-
const hmrBaseReplacement = escapeReplacement(hmrBase)
|
|
81
|
-
const hmrTimeoutReplacement = escapeReplacement(timeout)
|
|
82
|
-
const hmrEnableOverlayReplacement = escapeReplacement(overlay)
|
|
83
|
-
|
|
84
|
-
injectConfigValues = (code: string) => {
|
|
85
|
-
return code
|
|
86
|
-
.replace(`__MODE__`, modeReplacement)
|
|
87
|
-
.replace(/__BASE__/g, baseReplacement)
|
|
88
|
-
.replace(`__DEFINES__`, definesReplacement)
|
|
89
|
-
.replace(`__SERVER_HOST__`, serverHostReplacement)
|
|
90
|
-
.replace(`__HMR_PROTOCOL__`, hmrProtocolReplacement)
|
|
91
|
-
.replace(`__HMR_HOSTNAME__`, hmrHostnameReplacement)
|
|
92
|
-
.replace(`__HMR_PORT__`, hmrPortReplacement)
|
|
93
|
-
.replace(`__HMR_DIRECT_TARGET__`, hmrDirectTargetReplacement)
|
|
94
|
-
.replace(`__HMR_BASE__`, hmrBaseReplacement)
|
|
95
|
-
.replace(`__HMR_TIMEOUT__`, hmrTimeoutReplacement)
|
|
96
|
-
.replace(`__HMR_ENABLE_OVERLAY__`, hmrEnableOverlayReplacement)
|
|
97
|
-
}
|
|
98
|
-
},
|
|
99
|
-
|
|
100
|
-
applyToEnvironment(environment) {
|
|
101
|
-
return isNativeEnvironment(environment)
|
|
102
|
-
},
|
|
103
|
-
|
|
104
|
-
transform(code, id) {
|
|
105
|
-
if (id.includes('vite-native-client/dist/esm/client.')) {
|
|
106
|
-
return injectConfigValues(code)
|
|
107
|
-
}
|
|
108
|
-
},
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
function escapeReplacement(value: string | number | boolean | null) {
|
|
113
|
-
const jsonValue = JSON.stringify(value)
|
|
114
|
-
return () => jsonValue
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
function serializeDefine(define: Record<string, any>): string {
|
|
118
|
-
let res = `{`
|
|
119
|
-
for (const key in define) {
|
|
120
|
-
const val = define[key]
|
|
121
|
-
res += `${JSON.stringify(key)}: ${typeof val === 'string' ? `(${val})` : JSON.stringify(val)}, `
|
|
122
|
-
}
|
|
123
|
-
return res + `}`
|
|
124
|
-
}
|
|
@@ -5,20 +5,6 @@ import type { Plugin, ResolvedConfig } from 'vite'
|
|
|
5
5
|
import colors from 'picocolors'
|
|
6
6
|
import { isNativeEnvironment } from '../utils/environmentUtils'
|
|
7
7
|
|
|
8
|
-
const IMAGE_ASSET_EXTS = [
|
|
9
|
-
'png',
|
|
10
|
-
'jpg',
|
|
11
|
-
'jpeg',
|
|
12
|
-
'bmp',
|
|
13
|
-
'gif',
|
|
14
|
-
'webp',
|
|
15
|
-
'psd',
|
|
16
|
-
'svg',
|
|
17
|
-
'tiff',
|
|
18
|
-
'ktx',
|
|
19
|
-
]
|
|
20
|
-
const IMAGE_ASSET_EXTS_SET = new Set(IMAGE_ASSET_EXTS)
|
|
21
|
-
|
|
22
8
|
const ASSET_DEST_DIR = 'assets'
|
|
23
9
|
/** `/assets` is too common and might conflict with web, using another path for dev server in development. */
|
|
24
10
|
const DEV_ASSET_DEST_PATH = '__vxrn_dev_native_assets'
|
|
@@ -173,10 +159,3 @@ export default asset;
|
|
|
173
159
|
},
|
|
174
160
|
}
|
|
175
161
|
}
|
|
176
|
-
|
|
177
|
-
function isAssetTypeAnImage(
|
|
178
|
-
/** Normally an file extension without the leading dot. */
|
|
179
|
-
type: string
|
|
180
|
-
) {
|
|
181
|
-
return IMAGE_ASSET_EXTS_SET.has(type)
|
|
182
|
-
}
|
|
@@ -5,15 +5,9 @@ import {
|
|
|
5
5
|
removeConnectedNativeClient,
|
|
6
6
|
} from '../utils/connectedNativeClients'
|
|
7
7
|
import type { VXRNOptionsFilled } from '../config/getOptionsFilled'
|
|
8
|
-
import { clearCachedBundle, getReactNativeBundle } from '../utils/getReactNativeBundle'
|
|
9
|
-
import { hotUpdateCache } from '../utils/hotUpdateCache'
|
|
10
8
|
import { URL } from 'node:url'
|
|
11
|
-
import { existsSync } from 'node:fs'
|
|
12
|
-
import { readFile, writeFile } from 'node:fs/promises'
|
|
13
9
|
import { createDevMiddleware } from '@react-native/dev-middleware'
|
|
14
|
-
import {
|
|
15
|
-
import { getCacheDir } from '../utils/getCacheDir'
|
|
16
|
-
import { debounce } from 'perfect-debounce'
|
|
10
|
+
import { createNativeDevEngine } from '../utils/createNativeDevEngine'
|
|
17
11
|
|
|
18
12
|
type ClientMessage = {
|
|
19
13
|
type: 'client-log'
|
|
@@ -26,15 +20,12 @@ export function createReactNativeDevServerPlugin(
|
|
|
26
20
|
Pick<VXRNOptionsFilled, 'cacheDir' | 'debugBundle' | 'debugBundlePaths' | 'entries'>
|
|
27
21
|
>
|
|
28
22
|
): Plugin {
|
|
29
|
-
let hmrSocket: WebSocket | null = null
|
|
30
|
-
|
|
31
23
|
return {
|
|
32
24
|
name: 'vite-plugin-react-native-server',
|
|
33
25
|
|
|
34
26
|
configureServer(server: ViteDevServer) {
|
|
35
27
|
const { host, port } = server.config.server
|
|
36
28
|
const { root } = server.config
|
|
37
|
-
const cacheDir = options?.cacheDir || getCacheDir(root)
|
|
38
29
|
const hmrWSS = new WebSocketServer({ noServer: true })
|
|
39
30
|
const clientWSS = new WebSocketServer({ noServer: true })
|
|
40
31
|
|
|
@@ -49,29 +40,41 @@ export function createReactNativeDevServerPlugin(
|
|
|
49
40
|
|
|
50
41
|
// link up sockets
|
|
51
42
|
server.httpServer?.on('upgrade', (req, socket, head) => {
|
|
43
|
+
const url = req.url || ''
|
|
44
|
+
|
|
52
45
|
// devtools sockets
|
|
53
46
|
for (const endpoint of devToolsSocketEndpoints) {
|
|
54
|
-
if (
|
|
47
|
+
if (url.startsWith(endpoint)) {
|
|
55
48
|
const wss = websocketEndpoints[endpoint]
|
|
56
49
|
wss.handleUpgrade(req, socket, head, (ws) => {
|
|
57
50
|
wss.emit('connection', ws, req)
|
|
58
51
|
})
|
|
52
|
+
return
|
|
59
53
|
}
|
|
60
54
|
}
|
|
61
55
|
|
|
62
|
-
//
|
|
63
|
-
if (
|
|
64
|
-
req.url.startsWith(
|
|
65
|
-
'/__hmr'
|
|
66
|
-
) /* TODO: handle '/__hmr?platform=ios' and android differently */
|
|
67
|
-
) {
|
|
56
|
+
// rolldown HMR socket (used by rolldown dev() HMR client)
|
|
57
|
+
if (url.startsWith('/hot')) {
|
|
68
58
|
hmrWSS.handleUpgrade(req, socket, head, (ws) => {
|
|
59
|
+
// listen for module registration messages from client
|
|
60
|
+
ws.on('message', async (data: any) => {
|
|
61
|
+
try {
|
|
62
|
+
const msg = JSON.parse(data.toString())
|
|
63
|
+
if (msg.type === 'hmr:module-registered' && msg.modules) {
|
|
64
|
+
const currentEngine = devEngines['ios'] || devEngines['android']
|
|
65
|
+
if (currentEngine?.engine) {
|
|
66
|
+
await currentEngine.engine.registerModules('vxrn-dev', msg.modules)
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
} catch {}
|
|
70
|
+
})
|
|
69
71
|
hmrWSS.emit('connection', ws, req)
|
|
70
72
|
})
|
|
73
|
+
return
|
|
71
74
|
}
|
|
72
75
|
|
|
73
76
|
// client socket
|
|
74
|
-
if (
|
|
77
|
+
if (url === '/__client') {
|
|
75
78
|
clientWSS.handleUpgrade(req, socket, head, (ws) => {
|
|
76
79
|
clientWSS.emit('connection', ws, req)
|
|
77
80
|
})
|
|
@@ -131,24 +134,12 @@ export function createReactNativeDevServerPlugin(
|
|
|
131
134
|
android: 'android',
|
|
132
135
|
}
|
|
133
136
|
|
|
134
|
-
//
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
const source = hotUpdateCache.get(file)
|
|
141
|
-
if (!source) {
|
|
142
|
-
console.warn(`No hot source found for`, file)
|
|
143
|
-
res.writeHead(200, { 'Content-Type': 'text/javascript' })
|
|
144
|
-
res.end('')
|
|
145
|
-
return
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
res.writeHead(200, { 'Content-Type': 'text/javascript' })
|
|
149
|
-
res.end(source)
|
|
150
|
-
}
|
|
151
|
-
})
|
|
137
|
+
// rolldown DevEngine instances (per platform)
|
|
138
|
+
const devEngines: Record<
|
|
139
|
+
string,
|
|
140
|
+
Awaited<ReturnType<typeof createNativeDevEngine>> | null
|
|
141
|
+
> = {}
|
|
142
|
+
const devEngineCreating: Record<string, Promise<any> | null> = {}
|
|
152
143
|
|
|
153
144
|
// React Native bundle handler
|
|
154
145
|
const handleRNBundle: Connect.NextHandleFunction = async (req, res) => {
|
|
@@ -162,47 +153,39 @@ export function createReactNativeDevServerPlugin(
|
|
|
162
153
|
|
|
163
154
|
try {
|
|
164
155
|
const bundle = await (async () => {
|
|
165
|
-
if (
|
|
166
|
-
|
|
167
|
-
if (
|
|
168
|
-
|
|
169
|
-
|
|
156
|
+
if (!devEngines[platform]) {
|
|
157
|
+
// prevent duplicate creation from concurrent requests
|
|
158
|
+
if (!devEngineCreating[platform]) {
|
|
159
|
+
devEngineCreating[platform] = (async () => {
|
|
160
|
+
try {
|
|
161
|
+
console.info(`[vxrn] creating rolldown DevEngine for ${platform}...`)
|
|
162
|
+
devEngines[platform] = await createNativeDevEngine({
|
|
163
|
+
root,
|
|
164
|
+
port: port || 8081,
|
|
165
|
+
host: typeof host === 'string' ? host : 'localhost',
|
|
166
|
+
platform,
|
|
167
|
+
serverUrl: `http://${typeof host === 'string' && host !== '0.0.0.0' ? host : 'localhost'}:${port || 8081}`,
|
|
168
|
+
onHmrUpdate: (update) => {
|
|
169
|
+
const msg = JSON.stringify(update)
|
|
170
|
+
hmrWSS.clients.forEach((client: any) => {
|
|
171
|
+
if (client.readyState === 1) {
|
|
172
|
+
client.send(msg)
|
|
173
|
+
}
|
|
174
|
+
})
|
|
175
|
+
},
|
|
176
|
+
})
|
|
177
|
+
console.info(`[vxrn] rolldown DevEngine ready for ${platform}`)
|
|
178
|
+
} catch (err) {
|
|
179
|
+
// clear so next request retries instead of permanently failing
|
|
180
|
+
devEngineCreating[platform] = null
|
|
181
|
+
throw err
|
|
182
|
+
}
|
|
183
|
+
})()
|
|
170
184
|
}
|
|
185
|
+
await devEngineCreating[platform]
|
|
171
186
|
}
|
|
172
187
|
|
|
173
|
-
|
|
174
|
-
root,
|
|
175
|
-
cacheDir,
|
|
176
|
-
server: {
|
|
177
|
-
port: port,
|
|
178
|
-
url: `http://${host}:${port}`,
|
|
179
|
-
},
|
|
180
|
-
entries: { native: options?.entries?.native || './src/entry-native.tsx' },
|
|
181
|
-
...options,
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
let outBundle = process.env.VXRN_WORKER_BUNDLE
|
|
185
|
-
? await runOnWorker('bundle-react-native', {
|
|
186
|
-
options: getRnBundleOptions,
|
|
187
|
-
platform,
|
|
188
|
-
})
|
|
189
|
-
: await getReactNativeBundle(getRnBundleOptions, platform, {
|
|
190
|
-
mode: process.env.RN_SERVE_PROD_BUNDLE ? 'prod' : 'dev',
|
|
191
|
-
})
|
|
192
|
-
|
|
193
|
-
if (server.config.webSocketToken) {
|
|
194
|
-
outBundle = `globalThis.__VITE_WS_TOKEN__ = "${server.config.webSocketToken}";\n${outBundle}`
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
if (options?.debugBundle && options.debugBundlePaths) {
|
|
198
|
-
const path = options.debugBundlePaths[platform]
|
|
199
|
-
if (!existsSync(path)) {
|
|
200
|
-
console.info(` !!! - writing debug bundle to`, path)
|
|
201
|
-
await writeFile(path, outBundle)
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
return outBundle
|
|
188
|
+
return await devEngines[platform]!.getBundle().then((r) => r.code)
|
|
206
189
|
})()
|
|
207
190
|
|
|
208
191
|
res.writeHead(200, { 'Content-Type': 'text/javascript' })
|
|
@@ -238,16 +221,6 @@ export function createReactNativeDevServerPlugin(
|
|
|
238
221
|
res.writeHead(200, { 'Content-Type': 'text/plain' })
|
|
239
222
|
res.end('TODO')
|
|
240
223
|
})
|
|
241
|
-
|
|
242
|
-
// Clear bundle cache on file changes (debounced to avoid CPU spikes during builds)
|
|
243
|
-
const debouncedClearCache = debounce(clearCachedBundle, 100)
|
|
244
|
-
server.watcher.on('change', (path: string) => {
|
|
245
|
-
// Skip clearing cache for dist files to avoid loops during builds
|
|
246
|
-
if (path.includes('/dist/') || path.includes('\\dist\\')) {
|
|
247
|
-
return
|
|
248
|
-
}
|
|
249
|
-
debouncedClearCache()
|
|
250
|
-
})
|
|
251
224
|
},
|
|
252
225
|
}
|
|
253
226
|
}
|
|
@@ -63,26 +63,24 @@ describe('platform-specific-resolve', () => {
|
|
|
63
63
|
vi.restoreAllMocks()
|
|
64
64
|
})
|
|
65
65
|
|
|
66
|
-
it('
|
|
66
|
+
it('stubs .server file when explicitly imported on client', async () => {
|
|
67
67
|
const plugin = await getPlatformResolvePlugin()
|
|
68
68
|
const resolveId = plugin.resolveId as Function
|
|
69
69
|
|
|
70
70
|
const ctx = createMockContext('client', '/src/db.server.ts')
|
|
71
71
|
|
|
72
|
-
await
|
|
73
|
-
|
|
74
|
-
).rejects.toThrow('.server file cannot be imported on client')
|
|
72
|
+
const result = await resolveId.call(ctx, './db.server', '/src/page.tsx', {})
|
|
73
|
+
expect(result).toEqual({ id: '\0server-only-stub:./db.server' })
|
|
75
74
|
})
|
|
76
75
|
|
|
77
|
-
it('
|
|
76
|
+
it('stubs .server file when explicitly imported on ios', async () => {
|
|
78
77
|
const plugin = await getPlatformResolvePlugin()
|
|
79
78
|
const resolveId = plugin.resolveId as Function
|
|
80
79
|
|
|
81
80
|
const ctx = createMockContext('ios', '/src/db.server.ts')
|
|
82
81
|
|
|
83
|
-
await
|
|
84
|
-
|
|
85
|
-
).rejects.toThrow('.server file cannot be imported on ios')
|
|
82
|
+
const result = await resolveId.call(ctx, './db.server', '/src/page.tsx', {})
|
|
83
|
+
expect(result).toEqual({ id: '\0server-only-stub:./db.server' })
|
|
86
84
|
})
|
|
87
85
|
|
|
88
86
|
it('allows .server file import on ssr', async () => {
|