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.
Files changed (208) hide show
  1. package/dist/config/getAdditionalViteConfig.mjs +1 -4
  2. package/dist/config/getAdditionalViteConfig.mjs.map +1 -1
  3. package/dist/config/getAdditionalViteConfig.native.js +1 -4
  4. package/dist/config/getAdditionalViteConfig.native.js.map +1 -1
  5. package/dist/config/getReactNativePlugins.mjs +1 -5
  6. package/dist/config/getReactNativePlugins.mjs.map +1 -1
  7. package/dist/config/getReactNativePlugins.native.js +1 -5
  8. package/dist/config/getReactNativePlugins.native.js.map +1 -1
  9. package/dist/exports/build.mjs +5 -0
  10. package/dist/exports/build.mjs.map +1 -1
  11. package/dist/exports/build.native.js +5 -0
  12. package/dist/exports/build.native.js.map +1 -1
  13. package/dist/exports/createServer.mjs +30 -25
  14. package/dist/exports/createServer.mjs.map +1 -1
  15. package/dist/exports/createServer.native.js +6 -3
  16. package/dist/exports/createServer.native.js.map +1 -1
  17. package/dist/exports/prebuild.mjs +25 -1
  18. package/dist/exports/prebuild.mjs.map +1 -1
  19. package/dist/exports/prebuild.native.js +30 -1
  20. package/dist/exports/prebuild.native.js.map +1 -1
  21. package/dist/exports/serve.mjs +2 -2
  22. package/dist/exports/serve.mjs.map +1 -1
  23. package/dist/exports/serve.native.js +2 -2
  24. package/dist/exports/serve.native.js.map +1 -1
  25. package/dist/exports/serveStaticAssets.mjs +43 -11
  26. package/dist/exports/serveStaticAssets.mjs.map +1 -1
  27. package/dist/exports/serveStaticAssets.native.js +69 -6
  28. package/dist/exports/serveStaticAssets.native.js.map +1 -1
  29. package/dist/index.js +1 -2
  30. package/dist/index.js.map +1 -1
  31. package/dist/index.mjs +1 -2
  32. package/dist/index.mjs.map +1 -1
  33. package/dist/index.native.js +1 -2
  34. package/dist/index.native.js.map +1 -1
  35. package/dist/patches/builtInDepPatches.mjs +14 -0
  36. package/dist/patches/builtInDepPatches.mjs.map +1 -1
  37. package/dist/patches/builtInDepPatches.native.js +14 -0
  38. package/dist/patches/builtInDepPatches.native.js.map +1 -1
  39. package/dist/plugins/clientInjectPlugin.mjs +2 -62
  40. package/dist/plugins/clientInjectPlugin.mjs.map +1 -1
  41. package/dist/plugins/clientInjectPlugin.native.js +2 -75
  42. package/dist/plugins/clientInjectPlugin.native.js.map +1 -1
  43. package/dist/plugins/reactNativeDevAssetPlugin.mjs +1 -3
  44. package/dist/plugins/reactNativeDevAssetPlugin.mjs.map +1 -1
  45. package/dist/plugins/reactNativeDevAssetPlugin.native.js +1 -3
  46. package/dist/plugins/reactNativeDevAssetPlugin.native.js.map +1 -1
  47. package/dist/plugins/reactNativeDevServer.mjs +55 -77
  48. package/dist/plugins/reactNativeDevServer.mjs.map +1 -1
  49. package/dist/plugins/reactNativeDevServer.native.js +73 -84
  50. package/dist/plugins/reactNativeDevServer.native.js.map +1 -1
  51. package/dist/plugins/serverExtensions.test.mjs +12 -6
  52. package/dist/plugins/serverExtensions.test.mjs.map +1 -1
  53. package/dist/plugins/serverExtensions.test.native.js +12 -6
  54. package/dist/plugins/serverExtensions.test.native.js.map +1 -1
  55. package/dist/rn-commands/bundle/buildBundle.mjs +11 -35
  56. package/dist/rn-commands/bundle/buildBundle.mjs.map +1 -1
  57. package/dist/rn-commands/bundle/buildBundle.native.js +9 -36
  58. package/dist/rn-commands/bundle/buildBundle.native.js.map +1 -1
  59. package/dist/runtime/hmr-client.mjs +125 -0
  60. package/dist/runtime/hmr-client.mjs.map +1 -0
  61. package/dist/runtime/hmr-client.native.js +197 -0
  62. package/dist/runtime/hmr-client.native.js.map +1 -0
  63. package/dist/runtime/hmr-runtime.mjs +162 -0
  64. package/dist/runtime/hmr-runtime.mjs.map +1 -0
  65. package/dist/runtime/hmr-runtime.native.js +348 -0
  66. package/dist/runtime/hmr-runtime.native.js.map +1 -0
  67. package/dist/runtime/hmr-types.mjs +2 -0
  68. package/dist/runtime/hmr-types.mjs.map +1 -0
  69. package/dist/runtime/hmr-types.native.js +2 -0
  70. package/dist/runtime/hmr-types.native.js.map +1 -0
  71. package/dist/runtime/native-prelude.mjs +97 -0
  72. package/dist/runtime/native-prelude.mjs.map +1 -0
  73. package/dist/runtime/native-prelude.native.js +97 -0
  74. package/dist/runtime/native-prelude.native.js.map +1 -0
  75. package/dist/runtime/react-refresh-utils.mjs +19 -0
  76. package/dist/runtime/react-refresh-utils.mjs.map +1 -0
  77. package/dist/runtime/react-refresh-utils.native.js +24 -0
  78. package/dist/runtime/react-refresh-utils.native.js.map +1 -0
  79. package/dist/utils/createNativeDevEngine.mjs +661 -0
  80. package/dist/utils/createNativeDevEngine.mjs.map +1 -0
  81. package/dist/utils/createNativeDevEngine.native.js +702 -0
  82. package/dist/utils/createNativeDevEngine.native.js.map +1 -0
  83. package/dist/utils/patches.mjs +6 -2
  84. package/dist/utils/patches.mjs.map +1 -1
  85. package/dist/utils/patches.native.js +6 -2
  86. package/dist/utils/patches.native.js.map +1 -1
  87. package/dist/utils/scanDepsToOptimize.mjs +4 -3
  88. package/dist/utils/scanDepsToOptimize.mjs.map +1 -1
  89. package/dist/utils/scanDepsToOptimize.native.js +4 -3
  90. package/dist/utils/scanDepsToOptimize.native.js.map +1 -1
  91. package/expo-plugin.cjs +122 -0
  92. package/package.json +15 -19
  93. package/src/config/getAdditionalViteConfig.ts +1 -3
  94. package/src/config/getReactNativePlugins.ts +0 -6
  95. package/src/exports/build.ts +5 -0
  96. package/src/exports/createServer.ts +7 -2
  97. package/src/exports/prebuild.ts +45 -0
  98. package/src/exports/serve.ts +2 -1
  99. package/src/exports/serveStaticAssets.ts +67 -4
  100. package/src/index.ts +0 -2
  101. package/src/patches/builtInDepPatches.ts +29 -0
  102. package/src/plugins/clientInjectPlugin.ts +2 -109
  103. package/src/plugins/reactNativeDevAssetPlugin.ts +0 -21
  104. package/src/plugins/reactNativeDevServer.ts +57 -84
  105. package/src/plugins/serverExtensions.test.ts +6 -8
  106. package/src/rn-commands/bundle/buildBundle.ts +9 -62
  107. package/src/runtime/hmr-client.ts +215 -0
  108. package/src/runtime/hmr-runtime.ts +276 -0
  109. package/src/runtime/hmr-types.ts +84 -0
  110. package/src/runtime/native-prelude.ts +110 -0
  111. package/src/runtime/react-refresh-utils.ts +36 -0
  112. package/src/types.ts +22 -4
  113. package/src/utils/createNativeDevEngine.ts +942 -0
  114. package/src/utils/patches.ts +36 -18
  115. package/src/utils/scanDepsToOptimize.ts +2 -3
  116. package/types/config/getAdditionalViteConfig.d.ts.map +1 -1
  117. package/types/config/getOptionsFilled.d.ts +2 -18
  118. package/types/config/getOptionsFilled.d.ts.map +1 -1
  119. package/types/config/getReactNativePlugins.d.ts.map +1 -1
  120. package/types/exports/build.d.ts +1 -9
  121. package/types/exports/build.d.ts.map +1 -1
  122. package/types/exports/createServer.d.ts.map +1 -1
  123. package/types/exports/prebuild.d.ts.map +1 -1
  124. package/types/exports/serve.d.ts +2 -1
  125. package/types/exports/serve.d.ts.map +1 -1
  126. package/types/exports/serveStaticAssets.d.ts +12 -1
  127. package/types/exports/serveStaticAssets.d.ts.map +1 -1
  128. package/types/index.d.ts +0 -1
  129. package/types/index.d.ts.map +1 -1
  130. package/types/patches/builtInDepPatches.d.ts.map +1 -1
  131. package/types/plugins/clientInjectPlugin.d.ts +1 -7
  132. package/types/plugins/clientInjectPlugin.d.ts.map +1 -1
  133. package/types/plugins/reactNativeDevAssetPlugin.d.ts.map +1 -1
  134. package/types/plugins/reactNativeDevServer.d.ts.map +1 -1
  135. package/types/rn-commands/bundle/buildBundle.d.ts.map +1 -1
  136. package/types/runtime/hmr-client.d.ts +40 -0
  137. package/types/runtime/hmr-client.d.ts.map +1 -0
  138. package/types/runtime/hmr-runtime.d.ts +69 -0
  139. package/types/runtime/hmr-runtime.d.ts.map +1 -0
  140. package/types/runtime/hmr-types.d.ts +76 -0
  141. package/types/runtime/hmr-types.d.ts.map +1 -0
  142. package/types/runtime/native-prelude.d.ts +11 -0
  143. package/types/runtime/native-prelude.d.ts.map +1 -0
  144. package/types/runtime/react-refresh-utils.d.ts +3 -0
  145. package/types/runtime/react-refresh-utils.d.ts.map +1 -0
  146. package/types/types.d.ts +15 -1
  147. package/types/types.d.ts.map +1 -1
  148. package/types/utils/createNativeDevEngine.d.ts +42 -0
  149. package/types/utils/createNativeDevEngine.d.ts.map +1 -0
  150. package/types/utils/patches.d.ts.map +1 -1
  151. package/types/utils/scanDepsToOptimize.d.ts.map +1 -1
  152. package/dist/config/getReactNativeBuildConfig.mjs +0 -200
  153. package/dist/config/getReactNativeBuildConfig.mjs.map +0 -1
  154. package/dist/config/getReactNativeBuildConfig.native.js +0 -204
  155. package/dist/config/getReactNativeBuildConfig.native.js.map +0 -1
  156. package/dist/plugins/reactNativeHMRPlugin.mjs +0 -120
  157. package/dist/plugins/reactNativeHMRPlugin.mjs.map +0 -1
  158. package/dist/plugins/reactNativeHMRPlugin.native.js +0 -151
  159. package/dist/plugins/reactNativeHMRPlugin.native.js.map +0 -1
  160. package/dist/utils/filterPluginsForNative.mjs +0 -27
  161. package/dist/utils/filterPluginsForNative.mjs.map +0 -1
  162. package/dist/utils/filterPluginsForNative.native.js +0 -33
  163. package/dist/utils/filterPluginsForNative.native.js.map +0 -1
  164. package/dist/utils/getReactNativeBundle.mjs +0 -104
  165. package/dist/utils/getReactNativeBundle.mjs.map +0 -1
  166. package/dist/utils/getReactNativeBundle.native.js +0 -135
  167. package/dist/utils/getReactNativeBundle.native.js.map +0 -1
  168. package/dist/utils/hotUpdateCache.mjs +0 -3
  169. package/dist/utils/hotUpdateCache.mjs.map +0 -1
  170. package/dist/utils/hotUpdateCache.native.js +0 -3
  171. package/dist/utils/hotUpdateCache.native.js.map +0 -1
  172. package/dist/utils/isBuildingNativeBundle.mjs +0 -6
  173. package/dist/utils/isBuildingNativeBundle.mjs.map +0 -1
  174. package/dist/utils/isBuildingNativeBundle.native.js +0 -7
  175. package/dist/utils/isBuildingNativeBundle.native.js.map +0 -1
  176. package/dist/utils/swapPrebuiltReactModules.mjs +0 -168
  177. package/dist/utils/swapPrebuiltReactModules.mjs.map +0 -1
  178. package/dist/utils/swapPrebuiltReactModules.native.js +0 -181
  179. package/dist/utils/swapPrebuiltReactModules.native.js.map +0 -1
  180. package/dist/worker.mjs +0 -55
  181. package/dist/worker.mjs.map +0 -1
  182. package/dist/worker.native.js +0 -55
  183. package/dist/worker.native.js.map +0 -1
  184. package/react-native-template.js +0 -375
  185. package/src/config/getReactNativeBuildConfig.ts +0 -349
  186. package/src/plugins/reactNativeHMRPlugin.ts +0 -237
  187. package/src/utils/filterPluginsForNative.ts +0 -55
  188. package/src/utils/getReactNativeBundle.ts +0 -243
  189. package/src/utils/hotUpdateCache.ts +0 -1
  190. package/src/utils/isBuildingNativeBundle.ts +0 -7
  191. package/src/utils/swapPrebuiltReactModules.ts +0 -341
  192. package/src/worker.ts +0 -90
  193. package/types/config/getReactNativeBuildConfig.d.ts +0 -72
  194. package/types/config/getReactNativeBuildConfig.d.ts.map +0 -1
  195. package/types/plugins/reactNativeHMRPlugin.d.ts +0 -10
  196. package/types/plugins/reactNativeHMRPlugin.d.ts.map +0 -1
  197. package/types/utils/filterPluginsForNative.d.ts +0 -8
  198. package/types/utils/filterPluginsForNative.d.ts.map +0 -1
  199. package/types/utils/getReactNativeBundle.d.ts +0 -12
  200. package/types/utils/getReactNativeBundle.d.ts.map +0 -1
  201. package/types/utils/hotUpdateCache.d.ts +0 -2
  202. package/types/utils/hotUpdateCache.d.ts.map +0 -1
  203. package/types/utils/isBuildingNativeBundle.d.ts +0 -3
  204. package/types/utils/isBuildingNativeBundle.d.ts.map +0 -1
  205. package/types/utils/swapPrebuiltReactModules.d.ts +0 -9
  206. package/types/utils/swapPrebuiltReactModules.d.ts.map +0 -1
  207. package/types/worker.d.ts +0 -13
  208. 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 { fillOptions } from '../../config/getOptionsFilled'
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
- let nativeEntry: string | undefined = undefined
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
- host: '0.0.0.0', // TODO: Hardcoded for now.
116
- entries: nativeEntry ? { native: nativeEntry } : {},
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
- // Assuming we are not enabling this on native as it will break anyway.
127
- builtBundle = builtBundle.replace(/process\.env\.VXRN_REACT_19/g, 'false')
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
+ }