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
|
@@ -2,29 +2,7 @@ import path from 'node:path'
|
|
|
2
2
|
import FSExtra from 'fs-extra'
|
|
3
3
|
import { bundle as metroBundle } from '@vxrn/vite-plugin-metro/rn-commands'
|
|
4
4
|
import { loadEnv } from '../../exports/loadEnv'
|
|
5
|
-
import {
|
|
6
|
-
import { getReactNativeBundle } from '../../utils/getReactNativeBundle'
|
|
7
|
-
|
|
8
|
-
async function transformForHermes(code: string): Promise<string> {
|
|
9
|
-
const babel = await import('@babel/core')
|
|
10
|
-
const result = await babel.transformAsync(code, {
|
|
11
|
-
compact: false,
|
|
12
|
-
retainLines: true,
|
|
13
|
-
assumptions: {
|
|
14
|
-
setPublicClassFields: true,
|
|
15
|
-
privateFieldsAsSymbols: true,
|
|
16
|
-
},
|
|
17
|
-
plugins: [
|
|
18
|
-
'@babel/plugin-transform-class-properties',
|
|
19
|
-
'@babel/plugin-transform-private-methods',
|
|
20
|
-
'@babel/plugin-transform-private-property-in-object',
|
|
21
|
-
'@babel/plugin-transform-classes',
|
|
22
|
-
'@babel/plugin-transform-async-to-generator',
|
|
23
|
-
],
|
|
24
|
-
sourceType: 'script',
|
|
25
|
-
})
|
|
26
|
-
return result?.code || code
|
|
27
|
-
}
|
|
5
|
+
import { buildNativeBundle } from '../../utils/createNativeDevEngine'
|
|
28
6
|
|
|
29
7
|
export type BundleCommandArgs = {
|
|
30
8
|
assetsDest?: string
|
|
@@ -94,51 +72,20 @@ export async function buildBundle(
|
|
|
94
72
|
loadEnv(dev ? 'development' : 'production', root)
|
|
95
73
|
|
|
96
74
|
if (!dev) {
|
|
97
|
-
// Vite will set `process.env.NODE_ENV` to 'development' if it's not set. See: https://github.com/vitejs/vite/blob/v6.0.7/packages/vite/src/node/config.ts#L973-L977.
|
|
98
|
-
// So we need to do this here to make sure that won't break our production build, since some plugins' behavior will be overridden if `NODE_ENV` is set to 'development'.
|
|
99
75
|
process.env.NODE_ENV = 'production'
|
|
100
76
|
}
|
|
101
77
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
// If there's an `app` directory, then we assume that the user is using One.
|
|
105
|
-
// FIXME: should use a better way to detect this.
|
|
106
|
-
const appDir = path.join(root, 'app')
|
|
107
|
-
if (FSExtra.existsSync(appDir) && FSExtra.statSync(appDir).isDirectory()) {
|
|
108
|
-
console.info('One project detected. Using One virtual entry.')
|
|
109
|
-
// TODO: Hardcoded for now to work with one. See `virtualEntryIdNative` in `packages/one/src/vite/virtualEntryPlugin.ts` and also `native: virtualEntryIdNative` in `packages/one/src/cli/run.ts`.
|
|
110
|
-
nativeEntry = 'virtual:one-entry-native'
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
const optionsIn = {
|
|
78
|
+
console.info(`[vxrn] building native bundle for ${platform}...`)
|
|
79
|
+
const result = await buildNativeBundle({
|
|
114
80
|
root,
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
const options = await fillOptions(optionsIn, { mode: dev ? 'dev' : 'prod' })
|
|
120
|
-
let builtBundle = await getReactNativeBundle(options, platform, {
|
|
121
|
-
mode: dev ? 'dev' : 'prod',
|
|
122
|
-
assetsDest,
|
|
123
|
-
useCache: false,
|
|
81
|
+
platform,
|
|
82
|
+
dev,
|
|
124
83
|
})
|
|
84
|
+
const builtBundle = result.code
|
|
125
85
|
|
|
126
|
-
//
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
if (!dev) {
|
|
130
|
-
// TODO: There should be a legitimate way to do this.
|
|
131
|
-
builtBundle = builtBundle.replace(
|
|
132
|
-
'.getEnforcing("DevSettings")',
|
|
133
|
-
'.patched_getEnforcing_DevSettings_will_not_work_in_production'
|
|
134
|
-
)
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
// hermes-compiler 0.14.x doesn't support class expressions or private fields,
|
|
138
|
-
// run babel to downlevel if needed (same as what metro does)
|
|
139
|
-
if (!dev && /\bclass\s+[a-zA-Z]/.test(builtBundle)) {
|
|
140
|
-
console.info('Transforming bundle for Hermes compatibility...')
|
|
141
|
-
builtBundle = await transformForHermes(builtBundle)
|
|
86
|
+
// write sourcemap if available and requested
|
|
87
|
+
if (result.map && args.sourcemapOutput) {
|
|
88
|
+
FSExtra.writeFileSync(args.sourcemapOutput, result.map, { encoding: 'utf8' })
|
|
142
89
|
}
|
|
143
90
|
|
|
144
91
|
console.info(`Writing bundle to ${bundleOutput}...`)
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HMR client for React Native.
|
|
3
|
+
* Ported from rollipop (https://github.com/leegeunhyeok/rollipop)
|
|
4
|
+
*
|
|
5
|
+
* connects to dev server WebSocket, handles HMR messages,
|
|
6
|
+
* and integrates with React Native's DevLoadingView and LogBox.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { HMRClientLogLevel, HMRClientMessage, HMRServerMessage } from './hmr-types'
|
|
10
|
+
|
|
11
|
+
declare var __DEV__: boolean
|
|
12
|
+
|
|
13
|
+
interface HMRClientNativeInterface {
|
|
14
|
+
enable(): void
|
|
15
|
+
disable(): void
|
|
16
|
+
registerBundle(requestUrl: string): void
|
|
17
|
+
log(level: string, data: any[]): void
|
|
18
|
+
setup(
|
|
19
|
+
platform: string,
|
|
20
|
+
bundleEntry: string,
|
|
21
|
+
host: string,
|
|
22
|
+
port: number | string,
|
|
23
|
+
isEnabled: boolean,
|
|
24
|
+
scheme?: string
|
|
25
|
+
): void
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
interface SocketInstance {
|
|
29
|
+
socket: WebSocket
|
|
30
|
+
origin: string
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
class HMRClient implements HMRClientNativeInterface {
|
|
34
|
+
static readonly STARTUP_ERROR = 'Expected HMRClient.setup() call at startup'
|
|
35
|
+
static readonly MAX_PENDING_LOGS = 100
|
|
36
|
+
|
|
37
|
+
private enabled = true
|
|
38
|
+
private _socketHolder: SocketInstance | null = null
|
|
39
|
+
private unavailableMessage: string | null = null
|
|
40
|
+
private compileErrorMessage: string | null = null
|
|
41
|
+
private pendingUpdatesCount = 0
|
|
42
|
+
private readonly pendingLogs: [HMRClientLogLevel, any[]][] = []
|
|
43
|
+
|
|
44
|
+
enable() {
|
|
45
|
+
if (this.unavailableMessage) throw new Error(this.unavailableMessage)
|
|
46
|
+
if (this._socketHolder == null) throw new Error(HMRClient.STARTUP_ERROR)
|
|
47
|
+
this.enabled = true
|
|
48
|
+
this.showCompileErrorIfNeeded()
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
disable() {
|
|
52
|
+
this.enabled = false
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
registerBundle(_requestUrl: string) {
|
|
56
|
+
// no-op for rolldown HMR
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
log(level: HMRClientLogLevel, data: any[]) {
|
|
60
|
+
if (this._socketHolder == null) {
|
|
61
|
+
this.pendingLogs.push([level, data])
|
|
62
|
+
if (this.pendingLogs.length > HMRClient.MAX_PENDING_LOGS) {
|
|
63
|
+
this.pendingLogs.shift()
|
|
64
|
+
}
|
|
65
|
+
return
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
try {
|
|
69
|
+
const stringData = data.map((item) =>
|
|
70
|
+
typeof item === 'string' ? item : JSON.stringify(item, null, 2)
|
|
71
|
+
)
|
|
72
|
+
this.send({ type: 'hmr:log', level, data: stringData })
|
|
73
|
+
} catch {}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
setup(
|
|
77
|
+
platform: string,
|
|
78
|
+
bundleEntry: string,
|
|
79
|
+
host: string,
|
|
80
|
+
port: number | string,
|
|
81
|
+
isEnabled = true,
|
|
82
|
+
protocol = 'http'
|
|
83
|
+
) {
|
|
84
|
+
if (!__DEV__) throw new Error('HMR is only available in development mode')
|
|
85
|
+
if (this._socketHolder != null)
|
|
86
|
+
throw new Error('Cannot initialize HMRClient more than once')
|
|
87
|
+
if (platform == null) throw new Error('Missing required parameter `platform`')
|
|
88
|
+
if (bundleEntry == null) throw new Error('Missing required parameter `bundleEntry`')
|
|
89
|
+
if (host == null) throw new Error('Missing required parameter `host`')
|
|
90
|
+
|
|
91
|
+
const serverHost = port !== null && port !== '' ? `${host}:${port}` : host
|
|
92
|
+
const origin = `${protocol}://${serverHost}`
|
|
93
|
+
const socket = new globalThis.WebSocket(`${origin}/hot`)
|
|
94
|
+
|
|
95
|
+
this._socketHolder = { socket, origin }
|
|
96
|
+
|
|
97
|
+
socket.addEventListener('open', () => {
|
|
98
|
+
socket.send(
|
|
99
|
+
JSON.stringify({
|
|
100
|
+
type: 'hmr:connected',
|
|
101
|
+
bundleEntry,
|
|
102
|
+
platform,
|
|
103
|
+
} satisfies HMRClientMessage)
|
|
104
|
+
)
|
|
105
|
+
this.handleConnection()
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
socket.addEventListener('error', (event) => {
|
|
109
|
+
this.handleConnectionError(
|
|
110
|
+
(event as any).error || new Error('WebSocket error'),
|
|
111
|
+
origin
|
|
112
|
+
)
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
socket.addEventListener('message', (event) => {
|
|
116
|
+
this.handleMessage(event)
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
socket.addEventListener('close', (event) => {
|
|
120
|
+
this.handleClose(event)
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
// connect to rolldown runtime
|
|
124
|
+
if (globalThis.__rolldown_runtime__ != null) {
|
|
125
|
+
globalThis.__rolldown_runtime__.setup(socket, origin)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
this.enabled = isEnabled
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
private send(payload: HMRClientMessage) {
|
|
132
|
+
if (this._socketHolder == null) return
|
|
133
|
+
if (this._socketHolder.socket.readyState === WebSocket.OPEN) {
|
|
134
|
+
this._socketHolder.socket.send(JSON.stringify(payload))
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
private flushEarlyLogs() {
|
|
139
|
+
if (
|
|
140
|
+
this._socketHolder == null ||
|
|
141
|
+
this._socketHolder.socket.readyState !== WebSocket.OPEN
|
|
142
|
+
) {
|
|
143
|
+
return
|
|
144
|
+
}
|
|
145
|
+
for (const [level, data] of this.pendingLogs) {
|
|
146
|
+
this.send({ type: 'hmr:log', level, data })
|
|
147
|
+
}
|
|
148
|
+
this.pendingLogs.length = 0
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
private showCompileErrorIfNeeded() {
|
|
152
|
+
if (this.compileErrorMessage == null) return
|
|
153
|
+
const error = new Error(this.compileErrorMessage)
|
|
154
|
+
this.compileErrorMessage = null
|
|
155
|
+
Object.defineProperty(error, 'preventSymbolication', { value: true })
|
|
156
|
+
throw error
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
private handleConnection() {
|
|
160
|
+
this.flushEarlyLogs()
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
private handleConnectionError(error: Error, origin: string) {
|
|
164
|
+
let msg =
|
|
165
|
+
'Cannot connect to One dev server.\n\n' +
|
|
166
|
+
'Try the following:\n' +
|
|
167
|
+
'- Ensure the dev server is running and on the same network\n'
|
|
168
|
+
|
|
169
|
+
msg += `\nURL: ${origin}\nError: ${error.message}`
|
|
170
|
+
|
|
171
|
+
this.unavailableMessage ??= msg
|
|
172
|
+
this.showCompileErrorIfNeeded()
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
private handleMessage(message: MessageEvent) {
|
|
176
|
+
const data = JSON.parse(String(message.data)) as HMRServerMessage
|
|
177
|
+
|
|
178
|
+
if (!this.enabled && data.type.startsWith('hmr:')) return
|
|
179
|
+
|
|
180
|
+
switch (data.type) {
|
|
181
|
+
case 'hmr:update-start':
|
|
182
|
+
this.pendingUpdatesCount++
|
|
183
|
+
this.compileErrorMessage = null
|
|
184
|
+
break
|
|
185
|
+
|
|
186
|
+
case 'hmr:update':
|
|
187
|
+
break
|
|
188
|
+
|
|
189
|
+
case 'hmr:update-done':
|
|
190
|
+
this.pendingUpdatesCount = Math.max(0, this.pendingUpdatesCount - 1)
|
|
191
|
+
break
|
|
192
|
+
|
|
193
|
+
case 'hmr:error':
|
|
194
|
+
this.compileErrorMessage = data.payload.message
|
|
195
|
+
this.showCompileErrorIfNeeded()
|
|
196
|
+
break
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
private handleClose(event: CloseEvent) {
|
|
201
|
+
const { code, reason } = event
|
|
202
|
+
const isNormalClose = code === 1000 || code === 1005
|
|
203
|
+
const message = isNormalClose
|
|
204
|
+
? 'Disconnected from dev server.'
|
|
205
|
+
: `Disconnected from dev server (${code}: "${reason}").`
|
|
206
|
+
|
|
207
|
+
this.unavailableMessage ??= message + '\n\nReload the app to reconnect.\n'
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const instance = new HMRClient()
|
|
212
|
+
|
|
213
|
+
export default Object.defineProperty(instance, 'default', {
|
|
214
|
+
get: () => instance,
|
|
215
|
+
})
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HMR runtime for rolldown's devMode.
|
|
3
|
+
* Ported from rollipop (https://github.com/leegeunhyeok/rollipop)
|
|
4
|
+
*
|
|
5
|
+
* this file is compiled to a string and passed to rolldown's
|
|
6
|
+
* `devMode.implement` option. rolldown injects it at the top of the bundle
|
|
7
|
+
* and references `__rolldown_runtime__` for module registration and HMR.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type {
|
|
11
|
+
DevRuntime as DefaultDevRuntime,
|
|
12
|
+
DevRuntimeMessenger,
|
|
13
|
+
HMRClientMessage,
|
|
14
|
+
HMRContext,
|
|
15
|
+
HMRCustomMessage,
|
|
16
|
+
HMRServerMessage,
|
|
17
|
+
} from './hmr-types'
|
|
18
|
+
import { enqueueUpdate, isReactRefreshBoundary } from './react-refresh-utils'
|
|
19
|
+
|
|
20
|
+
declare global {
|
|
21
|
+
var __rolldown_runtime__: ReactNativeDevRuntime
|
|
22
|
+
var __turboModuleProxy: (moduleName: string) => any
|
|
23
|
+
var globalEvalWithSourceUrl: (code: string, sourceURL?: string) => void
|
|
24
|
+
var nativeModuleProxy: Record<string, any>
|
|
25
|
+
var __ReactRefresh: any
|
|
26
|
+
var __VXRN_CUSTOM_HMR_HANDLER__:
|
|
27
|
+
| ((socket: WebSocket, message: HMRCustomMessage) => void)
|
|
28
|
+
| undefined
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// DO NOT EDIT THIS CLASS NAME - rolldown references it
|
|
32
|
+
declare const DevRuntime: typeof DefaultDevRuntime
|
|
33
|
+
|
|
34
|
+
var BaseDevRuntime = DevRuntime
|
|
35
|
+
|
|
36
|
+
// simple event emitter (avoids mitt dependency in bundle)
|
|
37
|
+
type Listener = (payload?: unknown) => void
|
|
38
|
+
|
|
39
|
+
class SimpleEmitter {
|
|
40
|
+
private listeners = new Map<string, Set<Listener>>()
|
|
41
|
+
|
|
42
|
+
on(event: string, fn: Listener) {
|
|
43
|
+
if (!this.listeners.has(event)) this.listeners.set(event, new Set())
|
|
44
|
+
this.listeners.get(event)!.add(fn)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
off(event: string, fn: Listener) {
|
|
48
|
+
this.listeners.get(event)?.delete(fn)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
emit(event: string, payload?: unknown) {
|
|
52
|
+
this.listeners.get(event)?.forEach((fn) => fn(payload))
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
class ModuleHotContext implements HMRContext {
|
|
57
|
+
private readonly removeListeners: (() => void)[] = []
|
|
58
|
+
acceptCallbacks: {
|
|
59
|
+
deps: string[]
|
|
60
|
+
fn: (moduleExports: Record<string, any>[]) => void
|
|
61
|
+
}[] = []
|
|
62
|
+
|
|
63
|
+
constructor(
|
|
64
|
+
private moduleId: string,
|
|
65
|
+
private socketHolder: SocketHolder
|
|
66
|
+
) {}
|
|
67
|
+
|
|
68
|
+
get refresh() {
|
|
69
|
+
return globalThis.__ReactRefresh
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
get refreshUtils() {
|
|
73
|
+
return { isReactRefreshBoundary, enqueueUpdate }
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
accept(...args: any[]) {
|
|
77
|
+
if (args.length === 1) {
|
|
78
|
+
this.acceptCallbacks.push({ deps: [this.moduleId], fn: args[0] })
|
|
79
|
+
} else if (args.length === 0) {
|
|
80
|
+
// self-accepting
|
|
81
|
+
} else {
|
|
82
|
+
throw new Error('Invalid arguments for `import.meta.hot.accept`')
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
invalidate() {
|
|
87
|
+
this.socketHolder.send(
|
|
88
|
+
JSON.stringify({
|
|
89
|
+
type: 'hmr:invalidate',
|
|
90
|
+
moduleId: this.moduleId,
|
|
91
|
+
} satisfies HMRClientMessage)
|
|
92
|
+
)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
on(event: string, listener: (...args: any[]) => void) {
|
|
96
|
+
this.socketHolder.on(event, listener)
|
|
97
|
+
this.removeListeners.push(() => this.socketHolder.off(event, listener))
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
off(event: string, listener: (...args: any[]) => void) {
|
|
101
|
+
this.socketHolder.off(event, listener)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
send(type: string, payload?: unknown) {
|
|
105
|
+
this.socketHolder.send(JSON.stringify({ type, payload }))
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
cleanup() {
|
|
109
|
+
for (const removeListener of this.removeListeners) {
|
|
110
|
+
removeListener()
|
|
111
|
+
}
|
|
112
|
+
this.removeListeners.length = 0
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
class SocketHolder {
|
|
117
|
+
private readonly queuedMessages: string[] = []
|
|
118
|
+
private readonly emitter = new SimpleEmitter()
|
|
119
|
+
private _socket: WebSocket | null = null
|
|
120
|
+
private _origin: string | null = null
|
|
121
|
+
|
|
122
|
+
get socket() {
|
|
123
|
+
return this._socket
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
get origin() {
|
|
127
|
+
return this._origin
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
setup(socket: WebSocket, origin: string) {
|
|
131
|
+
this._socket = socket
|
|
132
|
+
this._origin = origin
|
|
133
|
+
if (socket.readyState !== WebSocket.OPEN) {
|
|
134
|
+
socket.addEventListener('open', () => this.flushQueuedMessages(), { once: true })
|
|
135
|
+
} else {
|
|
136
|
+
this.flushQueuedMessages()
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
on(event: string, listener: (payload?: unknown) => void) {
|
|
141
|
+
this.emitter.on(event, listener)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
off(event: string, listener: (payload?: unknown) => void) {
|
|
145
|
+
this.emitter.off(event, listener)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
emit(event: string, payload?: unknown) {
|
|
149
|
+
this.emitter.emit(event, payload)
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
send(message: string) {
|
|
153
|
+
if (this._socket == null || this._socket.readyState !== WebSocket.OPEN) {
|
|
154
|
+
this.queuedMessages.push(message)
|
|
155
|
+
return
|
|
156
|
+
}
|
|
157
|
+
this.flushQueuedMessages()
|
|
158
|
+
this._socket.send(message)
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
flushQueuedMessages() {
|
|
162
|
+
if (this._socket == null) return
|
|
163
|
+
for (const message of this.queuedMessages) {
|
|
164
|
+
this._socket.send(message)
|
|
165
|
+
}
|
|
166
|
+
this.queuedMessages.length = 0
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
close() {
|
|
170
|
+
this._socket?.close()
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
class ReactNativeDevRuntime extends BaseDevRuntime {
|
|
175
|
+
socketHolder: SocketHolder
|
|
176
|
+
moduleHotContexts = new Map<string, ModuleHotContext>()
|
|
177
|
+
moduleHotContextsToBeUpdated = new Map<string, ModuleHotContext>()
|
|
178
|
+
|
|
179
|
+
constructor() {
|
|
180
|
+
const socketHolder = new SocketHolder()
|
|
181
|
+
const messenger: DevRuntimeMessenger = {
|
|
182
|
+
send: (message) => socketHolder.send(JSON.stringify(message)),
|
|
183
|
+
}
|
|
184
|
+
// generate a unique client id for lazy compilation
|
|
185
|
+
const clientId = `rn-${Date.now()}-${Math.random().toString(36).slice(2)}`
|
|
186
|
+
super(messenger, clientId)
|
|
187
|
+
this.socketHolder = socketHolder
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
createModuleHotContext(moduleId: string) {
|
|
191
|
+
const hotContext = new ModuleHotContext(moduleId, this.socketHolder)
|
|
192
|
+
if (this.moduleHotContexts.has(moduleId)) {
|
|
193
|
+
this.moduleHotContextsToBeUpdated.set(moduleId, hotContext)
|
|
194
|
+
} else {
|
|
195
|
+
this.moduleHotContexts.set(moduleId, hotContext)
|
|
196
|
+
}
|
|
197
|
+
return hotContext
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
applyUpdates(boundaries: [string, string][]) {
|
|
201
|
+
for (let [moduleId] of boundaries) {
|
|
202
|
+
const hotContext = this.moduleHotContexts.get(moduleId)
|
|
203
|
+
if (hotContext) {
|
|
204
|
+
hotContext.acceptCallbacks.filter((cb) => {
|
|
205
|
+
cb.fn(this.modules[moduleId].exports)
|
|
206
|
+
})
|
|
207
|
+
hotContext.cleanup()
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
this.moduleHotContextsToBeUpdated.forEach((hotContext, moduleId) => {
|
|
211
|
+
this.moduleHotContexts.set(moduleId, hotContext)
|
|
212
|
+
})
|
|
213
|
+
this.moduleHotContextsToBeUpdated.clear()
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
setup(socket: WebSocket, origin: string) {
|
|
217
|
+
if (this.socketHolder.socket != null) {
|
|
218
|
+
console.warn('[vxrn HMR]: runtime already setup')
|
|
219
|
+
return
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
this.socketHolder.setup(socket, origin)
|
|
223
|
+
|
|
224
|
+
socket.addEventListener('message', (event: MessageEvent) => {
|
|
225
|
+
const message = JSON.parse(event.data) as HMRServerMessage
|
|
226
|
+
|
|
227
|
+
if (isCustomHMRMessage(message)) {
|
|
228
|
+
this.socketHolder.emit(message.type, message.payload)
|
|
229
|
+
globalThis.__VXRN_CUSTOM_HMR_HANDLER__?.(socket, message)
|
|
230
|
+
return
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
switch (message.type) {
|
|
234
|
+
case 'hmr:update':
|
|
235
|
+
this.evaluate(message.code)
|
|
236
|
+
break
|
|
237
|
+
|
|
238
|
+
case 'hmr:reload':
|
|
239
|
+
this.reload()
|
|
240
|
+
break
|
|
241
|
+
}
|
|
242
|
+
})
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
private evaluate(code: string, sourceURL?: string) {
|
|
246
|
+
if (globalThis.globalEvalWithSourceUrl) {
|
|
247
|
+
globalThis.globalEvalWithSourceUrl(code, sourceURL)
|
|
248
|
+
} else {
|
|
249
|
+
eval(code)
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
private reload() {
|
|
254
|
+
const moduleName = 'DevSettings'
|
|
255
|
+
;(globalThis.__turboModuleProxy
|
|
256
|
+
? globalThis.__turboModuleProxy(moduleName)
|
|
257
|
+
: globalThis.nativeModuleProxy[moduleName]
|
|
258
|
+
).reload()
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function isCustomHMRMessage(message: unknown): message is HMRCustomMessage {
|
|
263
|
+
if (typeof message !== 'object' || message == null) return false
|
|
264
|
+
if (
|
|
265
|
+
'type' in message &&
|
|
266
|
+
typeof message.type === 'string' &&
|
|
267
|
+
message.type.startsWith('hmr:')
|
|
268
|
+
) {
|
|
269
|
+
return false
|
|
270
|
+
}
|
|
271
|
+
return true
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
globalThis.__rolldown_runtime__ = new ReactNativeDevRuntime()
|
|
275
|
+
|
|
276
|
+
export type { DevRuntime }
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
export interface HMRContext {
|
|
2
|
+
accept(...args: any[]): void
|
|
3
|
+
invalidate(): void
|
|
4
|
+
on(event: string, listener: (...args: any[]) => void): void
|
|
5
|
+
off(event: string, listener: (...args: any[]) => void): void
|
|
6
|
+
send(type: string, payload?: unknown): void
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export type HMRClientLogLevel =
|
|
10
|
+
| 'trace'
|
|
11
|
+
| 'info'
|
|
12
|
+
| 'warn'
|
|
13
|
+
| 'error'
|
|
14
|
+
| 'log'
|
|
15
|
+
| 'group'
|
|
16
|
+
| 'groupCollapsed'
|
|
17
|
+
| 'groupEnd'
|
|
18
|
+
| 'debug'
|
|
19
|
+
|
|
20
|
+
export type HMRClientMessage =
|
|
21
|
+
| { type: 'hmr:connected'; bundleEntry: string; platform: string }
|
|
22
|
+
| { type: 'hmr:module-registered'; modules: string[] }
|
|
23
|
+
| { type: 'hmr:log'; level: HMRClientLogLevel; data: any[] }
|
|
24
|
+
| { type: 'hmr:invalidate'; moduleId: string }
|
|
25
|
+
|
|
26
|
+
export type HMRServerMessage =
|
|
27
|
+
| { type: 'hmr:update-start' }
|
|
28
|
+
| { type: 'hmr:update-done' }
|
|
29
|
+
| { type: 'hmr:update'; code: string }
|
|
30
|
+
| { type: 'hmr:reload' }
|
|
31
|
+
| { type: 'hmr:error'; payload: HMRServerError }
|
|
32
|
+
|
|
33
|
+
export type HMRCustomMessage = {
|
|
34
|
+
type: string
|
|
35
|
+
payload: unknown
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export type HMRCustomHandler = (socket: WebSocket, message: HMRCustomMessage) => void
|
|
39
|
+
|
|
40
|
+
export interface HMRServerError {
|
|
41
|
+
type: string
|
|
42
|
+
message: string
|
|
43
|
+
errors: { description: string }[]
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface DevRuntimeModule {
|
|
47
|
+
exportsHolder: { exports: any }
|
|
48
|
+
id: string
|
|
49
|
+
exports: any
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface DevRuntimeInterface {
|
|
53
|
+
modules: Record<string, DevRuntimeModule>
|
|
54
|
+
createModuleHotContext(moduleId: string): void
|
|
55
|
+
applyUpdates(boundaries: [string, string][]): void
|
|
56
|
+
registerModule(id: string, exportsHolder: DevRuntimeModule['exportsHolder']): void
|
|
57
|
+
loadExports(id: string): void
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// the base class is provided by rolldown's devMode runtime
|
|
61
|
+
// DO NOT rename - rolldown references this by name
|
|
62
|
+
class DevRuntime implements DevRuntimeInterface {
|
|
63
|
+
clientId!: string
|
|
64
|
+
constructor(messenger: DevRuntimeMessenger, clientId: string) {}
|
|
65
|
+
modules: Record<string, DevRuntimeModule> = {}
|
|
66
|
+
createModuleHotContext(moduleId: string): void {
|
|
67
|
+
throw new Error('createModuleHotContext should be implemented')
|
|
68
|
+
}
|
|
69
|
+
applyUpdates(boundaries: [string, string][]): void {
|
|
70
|
+
throw new Error('applyUpdates should be implemented')
|
|
71
|
+
}
|
|
72
|
+
registerModule(id: string, exportsHolder: DevRuntimeModule['exportsHolder']): void {
|
|
73
|
+
throw new Error('registerModule should be implemented')
|
|
74
|
+
}
|
|
75
|
+
loadExports(id: string): void {
|
|
76
|
+
throw new Error('loadExports should be implemented')
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export type { DevRuntime }
|
|
81
|
+
|
|
82
|
+
export interface DevRuntimeMessenger {
|
|
83
|
+
send(message: HMRClientMessage): void
|
|
84
|
+
}
|