vxrn 2.0.0-beta.99.1 → 2.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (210) hide show
  1. package/dist/backgroundComputation.mjs +145 -0
  2. package/dist/backgroundComputation.mjs.map +1 -0
  3. package/dist/backgroundComputation.native.js +163 -0
  4. package/dist/backgroundComputation.native.js.map +1 -0
  5. package/dist/backgroundComputationNative.mjs +16 -0
  6. package/dist/backgroundComputationNative.mjs.map +1 -0
  7. package/dist/backgroundComputationNative.native.js +18 -0
  8. package/dist/backgroundComputationNative.native.js.map +1 -0
  9. package/dist/config/getBaseViteConfigOnly.mjs +6 -10
  10. package/dist/config/getBaseViteConfigOnly.mjs.map +1 -1
  11. package/dist/config/getBaseViteConfigOnly.native.js +6 -10
  12. package/dist/config/getBaseViteConfigOnly.native.js.map +1 -1
  13. package/dist/config/getOptimizeDeps.mjs +2 -1
  14. package/dist/config/getOptimizeDeps.mjs.map +1 -1
  15. package/dist/config/getOptimizeDeps.native.js +2 -1
  16. package/dist/config/getOptimizeDeps.native.js.map +1 -1
  17. package/dist/config/getViteServerConfig.mjs +13 -0
  18. package/dist/config/getViteServerConfig.mjs.map +1 -1
  19. package/dist/config/getViteServerConfig.native.js +15 -1
  20. package/dist/config/getViteServerConfig.native.js.map +1 -1
  21. package/dist/constants/defaults.mjs +4 -0
  22. package/dist/constants/defaults.mjs.map +1 -1
  23. package/dist/constants/defaults.native.js +4 -0
  24. package/dist/constants/defaults.native.js.map +1 -1
  25. package/dist/exports/build.native.js +1 -1
  26. package/dist/exports/build.native.js.map +1 -1
  27. package/dist/exports/clean.native.js +1 -1
  28. package/dist/exports/clean.native.js.map +1 -1
  29. package/dist/exports/createServer.native.js +2 -2
  30. package/dist/exports/createServer.native.js.map +1 -1
  31. package/dist/exports/dev.mjs +3 -0
  32. package/dist/exports/dev.mjs.map +1 -1
  33. package/dist/exports/dev.native.js +6 -3
  34. package/dist/exports/dev.native.js.map +1 -1
  35. package/dist/exports/loadEnv.native.js +1 -1
  36. package/dist/exports/loadEnv.native.js.map +1 -1
  37. package/dist/exports/patch.native.js +1 -1
  38. package/dist/exports/patch.native.js.map +1 -1
  39. package/dist/exports/prebuild.mjs +0 -22
  40. package/dist/exports/prebuild.mjs.map +1 -1
  41. package/dist/exports/prebuild.native.js +1 -23
  42. package/dist/exports/prebuild.native.js.map +1 -1
  43. package/dist/exports/prebuildWithoutExpo.mjs +1116 -178
  44. package/dist/exports/prebuildWithoutExpo.mjs.map +1 -1
  45. package/dist/exports/prebuildWithoutExpo.native.js +1592 -243
  46. package/dist/exports/prebuildWithoutExpo.native.js.map +1 -1
  47. package/dist/exports/runAndroid.native.js +1 -1
  48. package/dist/exports/runAndroid.native.js.map +1 -1
  49. package/dist/exports/runIos.native.js +1 -1
  50. package/dist/exports/runIos.native.js.map +1 -1
  51. package/dist/exports/serve.native.js +1 -1
  52. package/dist/exports/serve.native.js.map +1 -1
  53. package/dist/exports/serveStaticAssets.native.js +1 -1
  54. package/dist/exports/serveStaticAssets.native.js.map +1 -1
  55. package/dist/index.js +4 -1
  56. package/dist/index.mjs +4 -1
  57. package/dist/index.native.js +4 -1
  58. package/dist/native-source-contract.mjs +3 -0
  59. package/dist/native-source-contract.native.js +3 -0
  60. package/dist/patches/builtInDepPatches.mjs +0 -13
  61. package/dist/patches/builtInDepPatches.mjs.map +1 -1
  62. package/dist/patches/builtInDepPatches.native.js +0 -13
  63. package/dist/patches/builtInDepPatches.native.js.map +1 -1
  64. package/dist/plugins/autoDepOptimizePlugin.mjs +3 -2
  65. package/dist/plugins/autoDepOptimizePlugin.mjs.map +1 -1
  66. package/dist/plugins/autoDepOptimizePlugin.native.js +6 -3
  67. package/dist/plugins/autoDepOptimizePlugin.native.js.map +1 -1
  68. package/dist/plugins/backgroundComputationPlugin.mjs +27 -0
  69. package/dist/plugins/backgroundComputationPlugin.mjs.map +1 -0
  70. package/dist/plugins/backgroundComputationPlugin.native.js +32 -0
  71. package/dist/plugins/backgroundComputationPlugin.native.js.map +1 -0
  72. package/dist/plugins/reactNativeCommonJsPlugin.native.js +3 -3
  73. package/dist/plugins/reactNativeCommonJsPlugin.native.js.map +1 -1
  74. package/dist/plugins/reactNativeDevAssetPlugin.mjs +2 -1
  75. package/dist/plugins/reactNativeDevAssetPlugin.mjs.map +1 -1
  76. package/dist/plugins/reactNativeDevAssetPlugin.native.js +3 -2
  77. package/dist/plugins/reactNativeDevAssetPlugin.native.js.map +1 -1
  78. package/dist/plugins/reactNativeDevServer.mjs +87 -24
  79. package/dist/plugins/reactNativeDevServer.mjs.map +1 -1
  80. package/dist/plugins/reactNativeDevServer.native.js +162 -45
  81. package/dist/plugins/reactNativeDevServer.native.js.map +1 -1
  82. package/dist/plugins/workletImportsPlugin.mjs +9 -30
  83. package/dist/plugins/workletImportsPlugin.mjs.map +1 -1
  84. package/dist/plugins/workletImportsPlugin.native.js +10 -35
  85. package/dist/plugins/workletImportsPlugin.native.js.map +1 -1
  86. package/dist/rn-commands/bundle/buildBundle.mjs +3 -3
  87. package/dist/rn-commands/bundle/buildBundle.mjs.map +1 -1
  88. package/dist/rn-commands/bundle/buildBundle.native.js +3 -3
  89. package/dist/rn-commands/bundle/buildBundle.native.js.map +1 -1
  90. package/dist/runtime/hmr-client.native.js +3 -7
  91. package/dist/runtime/hmr-client.native.js.map +1 -1
  92. package/dist/runtime/hmr-runtime.native.js +32 -40
  93. package/dist/runtime/hmr-runtime.native.js.map +1 -1
  94. package/dist/serve/node.native.js +2 -2
  95. package/dist/serve/node.native.js.map +1 -1
  96. package/dist/swift-package-id.mjs +3 -0
  97. package/dist/swift-package-id.native.js +3 -0
  98. package/dist/user-interface/index.native.js +6 -6
  99. package/dist/user-interface/index.native.js.map +1 -1
  100. package/dist/utils/compileNativeDevBytecode.mjs +93 -0
  101. package/dist/utils/compileNativeDevBytecode.mjs.map +1 -0
  102. package/dist/utils/compileNativeDevBytecode.native.js +98 -0
  103. package/dist/utils/compileNativeDevBytecode.native.js.map +1 -0
  104. package/dist/utils/createNativeDevEngine.mjs +227 -20
  105. package/dist/utils/createNativeDevEngine.mjs.map +1 -1
  106. package/dist/utils/createNativeDevEngine.native.js +249 -37
  107. package/dist/utils/createNativeDevEngine.native.js.map +1 -1
  108. package/dist/utils/execSync.native.js +1 -1
  109. package/dist/utils/execSync.native.js.map +1 -1
  110. package/dist/utils/getServerEntry.native.js +2 -2
  111. package/dist/utils/getServerEntry.native.js.map +1 -1
  112. package/dist/utils/nativeRun.mjs +32 -16
  113. package/dist/utils/nativeRun.mjs.map +1 -1
  114. package/dist/utils/nativeRun.native.js +43 -18
  115. package/dist/utils/nativeRun.native.js.map +1 -1
  116. package/dist/utils/nativeSourceContract.mjs +1823 -0
  117. package/dist/utils/nativeSourceContract.mjs.map +1 -0
  118. package/dist/utils/nativeSourceContract.native.js +2425 -0
  119. package/dist/utils/nativeSourceContract.native.js.map +1 -0
  120. package/dist/utils/patches.native.js +15 -24
  121. package/dist/utils/patches.native.js.map +1 -1
  122. package/dist/utils/printServerUrls.native.js +1 -1
  123. package/dist/utils/printServerUrls.native.js.map +1 -1
  124. package/dist/utils/scanDepsToOptimize.mjs +7 -1
  125. package/dist/utils/scanDepsToOptimize.mjs.map +1 -1
  126. package/dist/utils/scanDepsToOptimize.native.js +27 -2
  127. package/dist/utils/scanDepsToOptimize.native.js.map +1 -1
  128. package/dist/utils/swiftPackageId.mjs +31 -2
  129. package/dist/utils/swiftPackageId.mjs.map +1 -1
  130. package/dist/utils/swiftPackageId.native.js +50 -2
  131. package/dist/utils/swiftPackageId.native.js.map +1 -1
  132. package/dist/vxrn-vite-plugin.native.js +1 -1
  133. package/dist/vxrn-vite-plugin.native.js.map +1 -1
  134. package/expo-plugin.cjs +792 -2
  135. package/native-project-patches.cjs +201 -10
  136. package/package.json +34 -15
  137. package/src/backgroundComputation.ts +220 -0
  138. package/src/backgroundComputationNative.ts +23 -0
  139. package/src/config/getBaseViteConfigOnly.ts +8 -9
  140. package/src/config/getOptimizeDeps.ts +3 -1
  141. package/src/config/getViteServerConfig.ts +16 -0
  142. package/src/config/glbAssets.test.ts +228 -0
  143. package/src/constants/defaults.ts +6 -0
  144. package/src/exports/dev.ts +5 -1
  145. package/src/exports/expoPlugin.test.mjs +337 -15
  146. package/src/exports/prebuild.ts +0 -31
  147. package/src/exports/prebuildWithoutExpo.test.ts +1588 -48
  148. package/src/exports/prebuildWithoutExpo.ts +1573 -213
  149. package/src/index.ts +8 -0
  150. package/src/native-source-contract.ts +16 -0
  151. package/src/patches/builtInDepPatches.ts +0 -21
  152. package/src/plugins/autoDepOptimizePlugin.ts +7 -2
  153. package/src/plugins/backgroundComputationPlugin.test.ts +77 -0
  154. package/src/plugins/backgroundComputationPlugin.ts +29 -0
  155. package/src/plugins/reactNativeDevAssetPlugin.ts +5 -1
  156. package/src/plugins/reactNativeDevServer.test.ts +74 -0
  157. package/src/plugins/reactNativeDevServer.ts +145 -28
  158. package/src/plugins/rnCodegenPlugin.test.ts +30 -0
  159. package/src/plugins/workletImportsPlugin.ts +19 -37
  160. package/src/rn-commands/bundle/buildBundle.ts +5 -3
  161. package/src/swift-package-id.ts +1 -0
  162. package/src/utils/compileNativeDevBytecode.test.ts +168 -0
  163. package/src/utils/compileNativeDevBytecode.ts +117 -0
  164. package/src/utils/createNativeDevEngine.test.ts +643 -18
  165. package/src/utils/createNativeDevEngine.ts +278 -48
  166. package/src/utils/nativeRun.test.ts +125 -23
  167. package/src/utils/nativeRun.ts +51 -23
  168. package/src/utils/nativeSourceArtifacts.test.ts +39 -0
  169. package/src/utils/nativeSourceContract.test.ts +600 -0
  170. package/src/utils/nativeSourceContract.ts +1897 -0
  171. package/src/utils/scanDepsToOptimize.test.ts +80 -0
  172. package/src/utils/scanDepsToOptimize.ts +18 -7
  173. package/src/utils/swiftPackageId.ts +25 -1
  174. package/types/backgroundComputation.d.ts +28 -0
  175. package/types/backgroundComputation.d.ts.map +1 -0
  176. package/types/backgroundComputationNative.d.ts +2 -0
  177. package/types/backgroundComputationNative.d.ts.map +1 -0
  178. package/types/config/getBaseViteConfigOnly.d.ts.map +1 -1
  179. package/types/config/getOptimizeDeps.d.ts.map +1 -1
  180. package/types/config/getViteServerConfig.d.ts.map +1 -1
  181. package/types/constants/defaults.d.ts.map +1 -1
  182. package/types/exports/dev.d.ts.map +1 -1
  183. package/types/exports/prebuild.d.ts.map +1 -1
  184. package/types/exports/prebuildWithoutExpo.d.ts +6 -0
  185. package/types/exports/prebuildWithoutExpo.d.ts.map +1 -1
  186. package/types/index.d.ts +2 -0
  187. package/types/index.d.ts.map +1 -1
  188. package/types/native-source-contract.d.ts +2 -0
  189. package/types/native-source-contract.d.ts.map +1 -0
  190. package/types/patches/builtInDepPatches.d.ts.map +1 -1
  191. package/types/plugins/autoDepOptimizePlugin.d.ts.map +1 -1
  192. package/types/plugins/backgroundComputationPlugin.d.ts +3 -0
  193. package/types/plugins/backgroundComputationPlugin.d.ts.map +1 -0
  194. package/types/plugins/reactNativeDevAssetPlugin.d.ts.map +1 -1
  195. package/types/plugins/reactNativeDevServer.d.ts.map +1 -1
  196. package/types/plugins/workletImportsPlugin.d.ts.map +1 -1
  197. package/types/rn-commands/bundle/buildBundle.d.ts.map +1 -1
  198. package/types/swift-package-id.d.ts +2 -0
  199. package/types/swift-package-id.d.ts.map +1 -0
  200. package/types/utils/compileNativeDevBytecode.d.ts +3 -0
  201. package/types/utils/compileNativeDevBytecode.d.ts.map +1 -0
  202. package/types/utils/createNativeDevEngine.d.ts +7 -22
  203. package/types/utils/createNativeDevEngine.d.ts.map +1 -1
  204. package/types/utils/nativeRun.d.ts.map +1 -1
  205. package/types/utils/nativeSourceContract.d.ts +121 -0
  206. package/types/utils/nativeSourceContract.d.ts.map +1 -0
  207. package/types/utils/scanDepsToOptimize.d.ts +1 -0
  208. package/types/utils/scanDepsToOptimize.d.ts.map +1 -1
  209. package/types/utils/swiftPackageId.d.ts +1 -0
  210. package/types/utils/swiftPackageId.d.ts.map +1 -1
@@ -1,5 +1,5 @@
1
1
  import { existsSync, realpathSync } from 'node:fs'
2
- import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises'
2
+ import { mkdir, mkdtemp, readFile, rm, symlink, writeFile } from 'node:fs/promises'
3
3
  import { tmpdir } from 'node:os'
4
4
  import { join } from 'node:path'
5
5
  import { createContext, runInContext, runInNewContext } from 'node:vm'
@@ -28,6 +28,108 @@ import {
28
28
  type NativePluginContext,
29
29
  } from './createNativeDevEngine'
30
30
 
31
+ it.each(['edit', 'create', 'delete'] as const)(
32
+ 'loads routes and applies %s HMR through symlinked node_modules',
33
+ async (change) => {
34
+ const workspace = await mkdtemp(join(tmpdir(), 'vxrn-native-route-symlink-'))
35
+ const root = join(workspace, 'packages/shell')
36
+ await mkdir(join(workspace, 'node_modules'), { recursive: true })
37
+ await mkdir(join(root, 'app'), { recursive: true })
38
+ await symlink('../../node_modules', join(root, 'node_modules'))
39
+ await writeFile(join(root, 'app/_layout.tsx'), "export default 'layout'")
40
+ await writeFile(
41
+ join(root, 'app/index.tsx'),
42
+ "export default 'index'; globalThis.routeEdit = 'index'; if (import.meta.hot) import.meta.hot.accept()"
43
+ )
44
+ if (change === 'delete') {
45
+ await writeFile(join(root, 'app/[id].tsx'), "export default 'dynamic'")
46
+ }
47
+ let resolveUpdate!: (update: any) => void
48
+ const nextUpdate = () =>
49
+ new Promise<any>((resolve) => {
50
+ resolveUpdate = resolve
51
+ })
52
+ let native: Awaited<ReturnType<typeof createNativeDevEngine>> | undefined
53
+ try {
54
+ native = await createNativeDevEngine({
55
+ root,
56
+ platform: 'ios',
57
+ port: 0,
58
+ plugins: [
59
+ {
60
+ name: 'route-registry-fixture',
61
+ transform(code, id) {
62
+ if (id.endsWith('/__virtual-native-entry.tsx')) {
63
+ // retain the generated registry import and its actual HMR acceptance.
64
+ const registryImport = code.match(/import \{ routes \} from [^;]+;/)![0]
65
+ const accept = code.slice(code.lastIndexOf('if (import.meta.hot)'))
66
+ return `${registryImport}
67
+ globalThis.routeMap = routes
68
+ ${accept}`
69
+ }
70
+ },
71
+ },
72
+ ],
73
+ onHmrUpdate(update) {
74
+ resolveUpdate(update)
75
+ },
76
+ })
77
+ const context: any = {
78
+ console: { ...console, error() {} },
79
+ setTimeout,
80
+ clearTimeout,
81
+ __VXRN_UPDATE_NATIVE_ROUTES__(routes: any) {
82
+ context.routeMap = routes
83
+ },
84
+ }
85
+ runInNewContext((await native.getBundle()).code, context)
86
+ expect(Object.keys(context.routeMap).sort()).toEqual(
87
+ (change === 'delete'
88
+ ? ['/app/_layout.tsx', '/app/index.tsx', '/app/[id].tsx']
89
+ : ['/app/_layout.tsx', '/app/index.tsx']
90
+ ).sort()
91
+ )
92
+ expect((await context.routeMap['/app/index.tsx']()).default).toBe('index')
93
+ const runtime = context.__rolldown_runtime__
94
+ await native.engine.registerClient(runtime.clientId)
95
+ const apply = async (pending: Promise<any>) => {
96
+ const update = await pending
97
+ expect(update.type).toBe('hmr:update')
98
+ expect(runtime.applyHmrUpdate(update.code, update.changedIds, update.seq)).toBe(
99
+ true
100
+ )
101
+ await native!.engine.ensureCurrentBuildFinish()
102
+ }
103
+ const pending = nextUpdate()
104
+ if (change === 'edit') {
105
+ expect(context.routeEdit).toBe('index')
106
+ await writeFile(
107
+ join(root, 'app/index.tsx'),
108
+ "export default 'edited'; globalThis.routeEdit = 'edited'; if (import.meta.hot) import.meta.hot.accept()"
109
+ )
110
+ await apply(pending)
111
+ expect(context.routeEdit).toBe('edited')
112
+ } else if (change === 'create') {
113
+ expect(context.routeMap['/app/[id].tsx']).toBeUndefined()
114
+ await writeFile(join(root, 'app/[id].tsx'), "export default 'dynamic'")
115
+ await apply(pending)
116
+ expect((await context.routeMap['/app/[id].tsx']()).default).toBe('dynamic')
117
+ } else {
118
+ expect((await context.routeMap['/app/[id].tsx']()).default).toBe('dynamic')
119
+ await rm(join(root, 'app/[id].tsx'))
120
+ await apply(pending)
121
+ expect(Object.keys(context.routeMap).sort()).toEqual([
122
+ '/app/_layout.tsx',
123
+ '/app/index.tsx',
124
+ ])
125
+ }
126
+ } finally {
127
+ await native?.close()
128
+ await rm(workspace, { recursive: true, force: true })
129
+ }
130
+ }
131
+ )
132
+
31
133
  describe.each(['ios', 'android'] as const)('native plugin adapters on %s', (platform) => {
32
134
  it.each(['dev', 'build'] as const)(
33
135
  'binds configured plugins to the %s bundle',
@@ -120,6 +222,79 @@ describe.each(['ios', 'android'] as const)('native plugin adapters on %s', (plat
120
222
  )
121
223
  })
122
224
 
225
+ it.each(['react-native-nitro-modules', 'react-native-worklets'])(
226
+ 'shares the app native singleton for %s across nested installs',
227
+ async (packageName) => {
228
+ const root = await mkdtemp(join(tmpdir(), 'vxrn-native-singleton-'))
229
+ const nested = join(root, 'node_modules/transitive')
230
+ for (const [parent, label] of [
231
+ [root, 'app'],
232
+ [nested, 'nested'],
233
+ ]) {
234
+ const directory = join(parent, 'node_modules', packageName)
235
+ await mkdir(directory, { recursive: true })
236
+ await writeFile(
237
+ join(directory, 'package.json'),
238
+ JSON.stringify({
239
+ name: packageName,
240
+ type: 'module',
241
+ exports: {
242
+ '.': { 'react-native': './native.js', default: './web.js' },
243
+ './shared': './native.js',
244
+ },
245
+ })
246
+ )
247
+ await writeFile(
248
+ join(directory, 'native.js'),
249
+ `export const singleton = { owner: '${label}' }`
250
+ )
251
+ await writeFile(
252
+ join(directory, 'web.js'),
253
+ `throw new Error('web export reached native')`
254
+ )
255
+ }
256
+ await writeFile(join(root, 'package.json'), '{}')
257
+ await writeFile(
258
+ join(nested, 'package.json'),
259
+ JSON.stringify({ name: 'transitive', type: 'module', main: 'index.js' })
260
+ )
261
+ await writeFile(
262
+ join(nested, 'index.js'),
263
+ `export { singleton } from '${packageName}/shared'`
264
+ )
265
+ await writeFile(
266
+ join(root, 'entry.js'),
267
+ `
268
+ import { singleton } from '${packageName}'
269
+ import { singleton as nested } from 'transitive'
270
+ globalThis.singletonResult = [singleton.owner, singleton === nested]
271
+ `
272
+ )
273
+ let native: Awaited<ReturnType<typeof createNativeDevEngine>> | undefined
274
+ try {
275
+ native = await createNativeDevEngine({
276
+ root,
277
+ platform: 'ios',
278
+ port: 0,
279
+ plugins: [
280
+ {
281
+ name: 'singleton-entry',
282
+ transform(_code, id) {
283
+ if (id.endsWith('/__virtual-native-entry.tsx')) return "import './entry.js'"
284
+ },
285
+ },
286
+ ],
287
+ })
288
+ const context = { console, setTimeout, clearTimeout }
289
+ runInNewContext((await native.getBundle()).code, context)
290
+ expect(Reflect.get(context, 'singletonResult')).toEqual(['app', true])
291
+ } finally {
292
+ await native?.close()
293
+ await rm(root, { recursive: true, force: true })
294
+ }
295
+ }
296
+ )
297
+
123
298
  const nativeTransformProbe = `
124
299
  export const transformProbe = () => {
125
300
  'worklet'
@@ -672,6 +847,56 @@ if (import.meta.hot) import.meta.hot.accept(() => {})
672
847
  })
673
848
 
674
849
  describe('native Rolldown HMR runtime', () => {
850
+ it('opens the HMR socket on the origin the device loaded the bundle from', async () => {
851
+ const root = await mkdtemp(join(tmpdir(), 'vxrn-native-hmr-origin-'))
852
+ // an emulator behind `adb reverse tcp:8081 tcp:<port>` loads the bundle from
853
+ // localhost:8081, where the server's own port is unreachable
854
+ await writeFile(
855
+ join(root, 'WebSocket.js'),
856
+ `export default class WebSocket {
857
+ constructor(url) { globalThis.hmrSocketUrl = url }
858
+ addEventListener() {}
859
+ }`
860
+ )
861
+ await writeFile(
862
+ join(root, 'getDevServer.js'),
863
+ `export default function getDevServer() {
864
+ return { url: 'http://localhost:8081/', bundleLoadedFromServer: true }
865
+ }`
866
+ )
867
+ await writeFile(
868
+ join(root, 'entry.mjs'),
869
+ `import WebSocket from './WebSocket.js'
870
+ import getDevServer from './getDevServer.js'
871
+ globalThis.hostModules = [WebSocket, getDevServer]`
872
+ )
873
+ const native = await createNativeDevEngine({
874
+ root,
875
+ port: 52278,
876
+ platform: 'android',
877
+ plugins: [
878
+ {
879
+ name: 'native-hmr-origin-entry',
880
+ transform(_code, id) {
881
+ if (id.endsWith('/__virtual-native-entry.tsx')) return `import './entry.mjs'`
882
+ },
883
+ },
884
+ ],
885
+ })
886
+ try {
887
+ const context: any = { console, setTimeout, clearTimeout }
888
+ runInNewContext((await native.getBundle()).code, context)
889
+ expect(context.hmrSocketUrl).toBe(
890
+ `ws://localhost:8081/hot?platform=android&clientId=${encodeURIComponent(
891
+ context.__rolldown_runtime__.clientId
892
+ )}`
893
+ )
894
+ } finally {
895
+ await native.close()
896
+ await rm(root, { recursive: true, force: true })
897
+ }
898
+ })
899
+
675
900
  it.each([true, false])(
676
901
  'keeps CommonJS default components callable through the native HMR wire path (Babel=%s)',
677
902
  async (babel) => {
@@ -1223,6 +1448,82 @@ describe('getHermesSWCIncludes', () => {
1223
1448
  expect(result.code).not.toContain('async function *')
1224
1449
  })
1225
1450
 
1451
+ it.each([true, false])(
1452
+ 'lowers complete class hierarchies for Hermes (dev=%s)',
1453
+ async (dev) => {
1454
+ const plugin = hermesCompatSWCPlugin(dev)
1455
+ const result = await Reflect.apply(plugin.transform as Function, undefined, [
1456
+ `class Base { value = 2; getValue() { return this.value } }
1457
+ class Derived extends Base { extra = 3; getValue() { return super.getValue() + this.extra } }
1458
+ globalThis.__classProbe = new Derived().getValue()`,
1459
+ '/project/classes.ts',
1460
+ ])
1461
+ expect(result.code).not.toMatch(/\bclass\s+(?:Base|Derived|extends)/)
1462
+ const context: any = {}
1463
+ runInNewContext(result.code, context)
1464
+ expect(context.__classProbe).toBe(5)
1465
+ }
1466
+ )
1467
+
1468
+ it('lowers constructor block bindings before production name mangling', async () => {
1469
+ const plugin = hermesCompatSWCPlugin(false)
1470
+ const result = await Reflect.apply(plugin.transform as Function, undefined, [
1471
+ `class Client {
1472
+ constructor(options) {
1473
+ if (options.dsn) { let url = options.url; this.url = url }
1474
+ this.callbacks = []
1475
+ for (let index = 0; index < 2; index++) this.callbacks.push(() => index)
1476
+ }
1477
+ }
1478
+ globalThis.__constructorScope = new Client({dsn: true, url: 3})`,
1479
+ '/project/constructor-scope.js',
1480
+ ])
1481
+ expect(result.code).not.toMatch(/\b(?:let|const)\s/)
1482
+ const context: any = {}
1483
+ runInNewContext(result.code, context)
1484
+ expect(context.__constructorScope.url).toBe(3)
1485
+ const values = Array.from(context.__constructorScope.callbacks, (callback: any) =>
1486
+ callback()
1487
+ )
1488
+ expect(values).toEqual([0, 1])
1489
+ })
1490
+
1491
+ it.each([true, false])(
1492
+ 'preserves class-free parameter shadowing for Hermes (dev=%s)',
1493
+ async (dev) => {
1494
+ const plugin = hermesCompatSWCPlugin(dev)
1495
+ const result = await Reflect.apply(plugin.transform as Function, undefined, [
1496
+ `function createStore(config) {
1497
+ if (!globalThis.factory) {
1498
+ const config = { install() { globalThis.factory = value => value.id } }
1499
+ config.install()
1500
+ }
1501
+ return globalThis.factory(config)
1502
+ }
1503
+ globalThis.__storeId = createStore({id: 'device'})`,
1504
+ '/project/class-free-storage.js',
1505
+ ])
1506
+ expect(result.code).not.toMatch(/\b(?:let|const)\s/)
1507
+ const context: any = {}
1508
+ runInNewContext(result.code, context)
1509
+ expect(context.__storeId).toBe('device')
1510
+ }
1511
+ )
1512
+
1513
+ it('lowers classes in large prebuilt modules', async () => {
1514
+ const plugin = hermesCompatSWCPlugin(false)
1515
+ const result = await Reflect.apply(plugin.transform as Function, undefined, [
1516
+ '/*' +
1517
+ ' '.repeat(500_000) +
1518
+ '*/ class Large { value = 7 } globalThis.__largeClass = new Large().value',
1519
+ '/project/prebuilt.js',
1520
+ ])
1521
+ expect(result.code).not.toMatch(/\bclass\s+Large/)
1522
+ const context: any = {}
1523
+ runInNewContext(result.code, context)
1524
+ expect(context.__largeClass).toBe(7)
1525
+ })
1526
+
1226
1527
  it('downlevels private fields, public class fields, and static blocks for Hermes', async () => {
1227
1528
  const plugin = hermesCompatSWCPlugin(true)
1228
1529
  if (typeof plugin.transform !== 'function') {
@@ -1423,7 +1724,11 @@ describe('native required transform failures', () => {
1423
1724
  const spy = vi
1424
1725
  .spyOn(compiler, 'transformWorklets')
1425
1726
  .mockRejectedValue(new Error('NATIVE_TRANSFORM_NEGATIVE_CONTROL'))
1426
- compiler.configureVXRNCompilerPlugin({ enableReanimated: true })
1727
+ compiler.configureVXRNCompilerPlugin({
1728
+ enableReanimated: true,
1729
+ enableNativeWorklets: true,
1730
+ })
1731
+ expect(compiler.isNativeWorkletsEnabled()).toBe(true)
1427
1732
 
1428
1733
  try {
1429
1734
  const plugin = vxrnCompilerPlugin('ios', dev, testRoot)
@@ -1439,7 +1744,10 @@ describe('native required transform failures', () => {
1439
1744
  ).rejects.toThrow('NATIVE_TRANSFORM_NEGATIVE_CONTROL')
1440
1745
  } finally {
1441
1746
  spy.mockRestore()
1442
- compiler.configureVXRNCompilerPlugin({ enableReanimated: false })
1747
+ compiler.configureVXRNCompilerPlugin({
1748
+ enableReanimated: false,
1749
+ enableNativeWorklets: false,
1750
+ })
1443
1751
  await rm(testRoot, { recursive: true, force: true })
1444
1752
  }
1445
1753
  }
@@ -1451,7 +1759,11 @@ describe('native required transform failures', () => {
1451
1759
  const spy = vi
1452
1760
  .spyOn(compiler, 'transformWorklets')
1453
1761
  .mockRejectedValue(new Error('NATIVE_TRANSFORM_NEGATIVE_CONTROL'))
1454
- compiler.configureVXRNCompilerPlugin({ enableReanimated: true })
1762
+ compiler.configureVXRNCompilerPlugin({
1763
+ enableReanimated: true,
1764
+ enableNativeWorklets: true,
1765
+ })
1766
+ expect(compiler.isNativeWorkletsEnabled()).toBe(true)
1455
1767
  let resolveOutput!: (output: unknown) => void
1456
1768
  const output = new Promise<unknown>((resolve) => {
1457
1769
  resolveOutput = resolve
@@ -1474,7 +1786,10 @@ describe('native required transform failures', () => {
1474
1786
  } finally {
1475
1787
  spy.mockRestore()
1476
1788
  await engine.close()
1477
- compiler.configureVXRNCompilerPlugin({ enableReanimated: false })
1789
+ compiler.configureVXRNCompilerPlugin({
1790
+ enableReanimated: false,
1791
+ enableNativeWorklets: false,
1792
+ })
1478
1793
  await rm(testRoot, { recursive: true, force: true })
1479
1794
  }
1480
1795
  })
@@ -1485,7 +1800,11 @@ describe('native required transform failures', () => {
1485
1800
  const spy = vi
1486
1801
  .spyOn(compiler, 'transformWorklets')
1487
1802
  .mockRejectedValue(new Error('NATIVE_TRANSFORM_NEGATIVE_CONTROL'))
1488
- compiler.configureVXRNCompilerPlugin({ enableReanimated: true })
1803
+ compiler.configureVXRNCompilerPlugin({
1804
+ enableReanimated: true,
1805
+ enableNativeWorklets: true,
1806
+ })
1807
+ expect(compiler.isNativeWorkletsEnabled()).toBe(true)
1489
1808
 
1490
1809
  try {
1491
1810
  await expect(
@@ -1497,7 +1816,10 @@ describe('native required transform failures', () => {
1497
1816
  ).rejects.toThrow('NATIVE_TRANSFORM_NEGATIVE_CONTROL')
1498
1817
  } finally {
1499
1818
  spy.mockRestore()
1500
- compiler.configureVXRNCompilerPlugin({ enableReanimated: false })
1819
+ compiler.configureVXRNCompilerPlugin({
1820
+ enableReanimated: false,
1821
+ enableNativeWorklets: false,
1822
+ })
1501
1823
  await rm(testRoot, { recursive: true, force: true })
1502
1824
  }
1503
1825
  })
@@ -1519,7 +1841,11 @@ describe('native required transform failures', () => {
1519
1841
  it('returns maps for every required production transform', async () => {
1520
1842
  const testRoot = await createWorkletsProject(false)
1521
1843
  const compiler = await import('@vxrn/compiler')
1522
- compiler.configureVXRNCompilerPlugin({ enableReanimated: true })
1844
+ compiler.configureVXRNCompilerPlugin({
1845
+ enableReanimated: true,
1846
+ enableNativeWorklets: true,
1847
+ })
1848
+ expect(compiler.isNativeWorkletsEnabled()).toBe(true)
1523
1849
 
1524
1850
  try {
1525
1851
  const compilerPlugin = vxrnCompilerPlugin('ios', false, testRoot, true)
@@ -1542,7 +1868,10 @@ describe('native required transform failures', () => {
1542
1868
  ])
1543
1869
  expect(hermesResult.map).toBeTruthy()
1544
1870
  } finally {
1545
- compiler.configureVXRNCompilerPlugin({ enableReanimated: false })
1871
+ compiler.configureVXRNCompilerPlugin({
1872
+ enableReanimated: false,
1873
+ enableNativeWorklets: false,
1874
+ })
1546
1875
  await rm(testRoot, { recursive: true, force: true })
1547
1876
  }
1548
1877
  })
@@ -1672,7 +2001,7 @@ export async function* probe() { throw new Error("MARKER"); }
1672
2001
  }
1673
2002
  })
1674
2003
 
1675
- it('transforms worklets via native Rust/Wasm SWC with zero Babel execution and accurate sourcemaps', async () => {
2004
+ it('transforms worklets via One OXC with zero Babel execution and accurate sourcemaps', async () => {
1676
2005
  const { TraceMap, originalPositionFor } = await import('@jridgewell/trace-mapping')
1677
2006
  const compiler = await import('@vxrn/compiler')
1678
2007
  const { vi } = await import('vitest')
@@ -1734,7 +2063,7 @@ export function Box() {
1734
2063
  }
1735
2064
  })
1736
2065
 
1737
- it('retains the existing Babel backend by default when enableNativeWorklets is false for auto-detected Reanimated and ordinary configured worklets plugin', async () => {
2066
+ it('respects an explicitly configured Babel plugin when One worklet compilation is disabled', async () => {
1738
2067
  const compiler = await import('@vxrn/compiler')
1739
2068
  const { vi } = await import('vitest')
1740
2069
  const babelSpy = vi.spyOn(compiler, 'transformBabel').mockResolvedValue({
@@ -1742,7 +2071,7 @@ export function Box() {
1742
2071
  } as any)
1743
2072
  const workletSpy = vi.spyOn(compiler, 'transformWorklets')
1744
2073
 
1745
- // enableReanimated is auto-detected, but enableNativeWorklets is NOT explicitly enabled
2074
+ // this tests explicit Babel configuration, not automatic backend selection.
1746
2075
  compiler.configureVXRNCompilerPlugin({
1747
2076
  enableReanimated: true,
1748
2077
  enableNativeWorklets: false,
@@ -1764,7 +2093,7 @@ export function Box() {
1764
2093
  const result = await Reflect.apply(plugin.transform, undefined, [inputCode, id])
1765
2094
 
1766
2095
  expect(result).toBeDefined()
1767
- // Retained existing Babel backend: Babel was called with the plugin
2096
+ // the caller explicitly supplied this Babel plugin.
1768
2097
  expect(babelSpy).toHaveBeenCalledWith(
1769
2098
  id,
1770
2099
  inputCode,
@@ -1772,7 +2101,7 @@ export function Box() {
1772
2101
  plugins: expect.arrayContaining(['react-native-reanimated/plugin']),
1773
2102
  })
1774
2103
  )
1775
- // Native SWC was NOT called
2104
+ // One compilation was disabled by the caller.
1776
2105
  expect(workletSpy).not.toHaveBeenCalled()
1777
2106
  } finally {
1778
2107
  babelSpy.mockRestore()
@@ -1785,7 +2114,7 @@ export function Box() {
1785
2114
  }
1786
2115
  })
1787
2116
 
1788
- it('intercepts tuple worklet plugins with options, executes native SWC with options, and skips Babel when enableNativeWorklets is true', async () => {
2117
+ it('intercepts tuple worklet plugins with options, executes One OXC with options, and skips Babel when enableNativeWorklets is true', async () => {
1789
2118
  const compiler = await import('@vxrn/compiler')
1790
2119
  const { vi } = await import('vitest')
1791
2120
  const babelSpy = vi.spyOn(compiler, 'transformBabel')
@@ -1872,8 +2201,14 @@ globalThis.minifyProbe = describeHeader()`
1872
2201
  }
1873
2202
 
1874
2203
  function runFixture(code: string) {
1875
- const context: Record<string, unknown> = { console }
1876
- runInNewContext(code, context)
2204
+ const context = createContext({ console })
2205
+ runInContext(code, context)
2206
+ expect(
2207
+ runInContext(
2208
+ 'Object.getOwnPropertyDescriptor(globalThis, "describeHeader")',
2209
+ context
2210
+ )
2211
+ ).toBeUndefined()
1877
2212
  return Reflect.get(context, 'minifyProbe')
1878
2213
  }
1879
2214
 
@@ -1931,6 +2266,198 @@ globalThis.minifyProbe = describeHeader()`
1931
2266
  })
1932
2267
 
1933
2268
  describe('native production assets', () => {
2269
+ it.each(['ios', 'android'] as const)(
2270
+ 'preserves CommonJS package main exports on %s',
2271
+ async (platform) => {
2272
+ const root = await mkdtemp(join(tmpdir(), 'vxrn-native-cjs-main-'))
2273
+ try {
2274
+ const directory = join(root, 'node_modules', 'native-punycode')
2275
+ await mkdir(directory, { recursive: true })
2276
+ await writeFile(
2277
+ join(directory, 'package.json'),
2278
+ JSON.stringify({
2279
+ name: 'native-punycode',
2280
+ main: './cjs.js',
2281
+ module: './esm.mjs',
2282
+ })
2283
+ )
2284
+ await writeFile(
2285
+ join(directory, 'cjs.js'),
2286
+ `module.exports = { ucs2: { decode: value => Array.from(value) } }`
2287
+ )
2288
+ await writeFile(
2289
+ join(directory, 'esm.mjs'),
2290
+ `export default { ucs2: { decode: value => Array.from(value) } }`
2291
+ )
2292
+ await writeFile(
2293
+ join(root, 'entry.js'),
2294
+ `import imported from 'native-punycode'
2295
+ const required = require('native-punycode')
2296
+ globalThis.decoded = [required.ucs2.decode('ab').join(','), imported.ucs2.decode('cd').join(',')]`
2297
+ )
2298
+ const result = await buildNativeBundle({ root, platform, entryFile: 'entry.js' })
2299
+ const context = { console }
2300
+ runInNewContext(result.code, context)
2301
+ expect(Reflect.get(context, 'decoded')).toEqual(['a,b', 'c,d'])
2302
+ } finally {
2303
+ await rm(root, { recursive: true, force: true })
2304
+ }
2305
+ }
2306
+ )
2307
+
2308
+ it('uses browser fallbacks and mappings after React Native overrides', async () => {
2309
+ const root = await mkdtemp(join(tmpdir(), 'vxrn-native-browser-'))
2310
+ try {
2311
+ for (const [name, manifest] of Object.entries({
2312
+ mapped: { main: './node.js', browser: { './node.js': './browser.js' } },
2313
+ identity: {
2314
+ main: './index.js',
2315
+ browser: { './node': './browser' },
2316
+ 'react-native': { './node': './node' },
2317
+ },
2318
+ fallback: { main: './node.js', browser: './browser.js' },
2319
+ conditional: { exports: { browser: './browser.js', default: './node.js' } },
2320
+ both: {
2321
+ exports: {
2322
+ browser: './browser.js',
2323
+ 'react-native': './native.js',
2324
+ default: './node.js',
2325
+ },
2326
+ },
2327
+ native: {
2328
+ main: './node.js',
2329
+ browser: './browser.js',
2330
+ 'react-native': './native.js',
2331
+ },
2332
+ })) {
2333
+ const directory = join(root, 'node_modules', name)
2334
+ await mkdir(directory, { recursive: true })
2335
+ await writeFile(
2336
+ join(directory, 'package.json'),
2337
+ JSON.stringify({ name, ...manifest })
2338
+ )
2339
+ await writeFile(join(directory, 'index.js'), `module.exports = require('./node')`)
2340
+ for (const target of ['node', 'browser', 'native']) {
2341
+ await writeFile(join(directory, `${target}.js`), `module.exports = '${target}'`)
2342
+ }
2343
+ }
2344
+ await writeFile(
2345
+ join(root, 'entry.js'),
2346
+ `globalThis.targets = [require('mapped'), require('fallback'), require('conditional'), require('both'), require('native'), require('identity')]`
2347
+ )
2348
+ const result = await buildNativeBundle({
2349
+ root,
2350
+ platform: 'ios',
2351
+ entryFile: 'entry.js',
2352
+ })
2353
+ const context = { console }
2354
+ runInNewContext(result.code, context)
2355
+ expect(Reflect.get(context, 'targets')).toEqual([
2356
+ 'browser',
2357
+ 'browser',
2358
+ 'browser',
2359
+ 'native',
2360
+ 'native',
2361
+ 'node',
2362
+ ])
2363
+ } finally {
2364
+ await rm(root, { recursive: true, force: true })
2365
+ }
2366
+ })
2367
+
2368
+ it('uses runtime exports for declaration aliases while preserving source aliases', async () => {
2369
+ const root = await mkdtemp(join(tmpdir(), 'vxrn-native-declaration-alias-'))
2370
+ const packageRoot = join(root, 'node_modules/example')
2371
+ await mkdir(packageRoot, { recursive: true })
2372
+ await writeFile(
2373
+ join(packageRoot, 'package.json'),
2374
+ JSON.stringify({
2375
+ name: 'example',
2376
+ type: 'module',
2377
+ exports: { './value': { import: './esm.js', require: './cjs.cjs' } },
2378
+ })
2379
+ )
2380
+ await writeFile(join(packageRoot, 'esm.js'), 'export const value = 42')
2381
+ await writeFile(join(packageRoot, 'cjs.cjs'), 'exports.value = 43')
2382
+ await writeFile(join(root, 'runtime.js'), 'export const value = 44')
2383
+ await writeFile(
2384
+ join(root, 'declaration.d.ts'),
2385
+ 'export class Value { constructor(); }'
2386
+ )
2387
+ await writeFile(
2388
+ join(root, 'tsconfig.json'),
2389
+ JSON.stringify({
2390
+ compilerOptions: {
2391
+ paths: {
2392
+ 'example/value': ['./declaration.d.ts'],
2393
+ '@runtime': ['./runtime.js'],
2394
+ },
2395
+ },
2396
+ })
2397
+ )
2398
+ await writeFile(
2399
+ join(root, 'entry.ts'),
2400
+ `import { value } from 'example/value'; import { value as local } from '@runtime'; globalThis.aliasValues = [value, require('example/value').value, local]`
2401
+ )
2402
+ try {
2403
+ const result = await buildNativeBundle({
2404
+ root,
2405
+ platform: 'ios',
2406
+ entryFile: 'entry.ts',
2407
+ })
2408
+ const context = { console }
2409
+ runInNewContext(result.code, context)
2410
+ expect(Reflect.get(context, 'aliasValues')).toEqual([42, 43, 44])
2411
+ } finally {
2412
+ await rm(root, { recursive: true, force: true })
2413
+ }
2414
+ })
2415
+
2416
+ it.each(['legacy', 'exported'])(
2417
+ 'shares the %s React Native asset registry with library imports',
2418
+ async (registryKind) => {
2419
+ const root = await mkdtemp(join(tmpdir(), 'vxrn-native-registry-'))
2420
+ const rnRoot = join(root, 'node_modules/react-native')
2421
+ const registryRoot = join(root, 'node_modules/@react-native/assets-registry')
2422
+ await mkdir(join(rnRoot, 'src'), { recursive: true })
2423
+ await mkdir(registryRoot, { recursive: true })
2424
+ await writeFile(
2425
+ join(rnRoot, 'package.json'),
2426
+ JSON.stringify({ name: 'react-native', main: 'index.js' })
2427
+ )
2428
+ await writeFile(join(rnRoot, 'index.js'), 'export {}')
2429
+ await writeFile(
2430
+ join(registryRoot, 'package.json'),
2431
+ JSON.stringify({ name: '@react-native/assets-registry' })
2432
+ )
2433
+ const registryFile =
2434
+ registryKind === 'exported'
2435
+ ? join(rnRoot, 'src/asset-registry.js')
2436
+ : join(registryRoot, 'registry.js')
2437
+ await writeFile(
2438
+ registryFile,
2439
+ 'const assets = []; export function registerAsset(asset) { return assets.push(asset) }; export function getAssetByID(id) { return assets[id - 1] }'
2440
+ )
2441
+ await writeFile(join(root, 'icon.png'), 'icon')
2442
+ await writeFile(
2443
+ join(root, 'entry.js'),
2444
+ `import icon from './icon.png'; import { getAssetByID } from '@react-native/assets-registry/registry'; globalThis.registryAsset = getAssetByID(icon)`
2445
+ )
2446
+ try {
2447
+ const result = await buildNativeBundle({
2448
+ root,
2449
+ entryFile: 'entry.js',
2450
+ platform: 'ios',
2451
+ })
2452
+ const context = createContext({ console })
2453
+ runInContext(result.code, context)
2454
+ expect(context.registryAsset.name).toBe('icon')
2455
+ } finally {
2456
+ await rm(root, { recursive: true, force: true })
2457
+ }
2458
+ }
2459
+ )
2460
+
1934
2461
  it('registers scale siblings and keeps monorepo assets inside assetsDest', async () => {
1935
2462
  const testRoot = await mkdtemp(join(tmpdir(), 'vxrn-native-assets-'))
1936
2463
  const appRoot = join(testRoot, 'workspace/apps/native-app')
@@ -1942,6 +2469,8 @@ describe('native production assets', () => {
1942
2469
  await writeFile(join(appAssets, 'icon.png'), 'icon-1x')
1943
2470
  await writeFile(join(appAssets, 'icon@2x.png'), 'icon-2x')
1944
2471
  await writeFile(join(appAssets, 'icon@3x.png'), 'icon-3x')
2472
+ const animationBytes = Buffer.from([0x50, 0x4b, 0xff, 0x00])
2473
+ await writeFile(join(appAssets, 'animation.lottie'), animationBytes)
1945
2474
  await writeFile(join(packageAssets, 'back.png'), 'back-1x')
1946
2475
  await writeFile(join(packageAssets, 'back@2x.png'), 'back-2x')
1947
2476
  await writeFile(
@@ -1949,7 +2478,8 @@ describe('native production assets', () => {
1949
2478
  `
1950
2479
  import icon from './assets/icon.png'
1951
2480
  import back from '../../node_modules/example/assets/back.png'
1952
- globalThis.__nativeAssetProbe = [icon, back]
2481
+ import animation from './assets/animation.lottie'
2482
+ globalThis.__nativeAssetProbe = [icon, back, animation]
1953
2483
  `
1954
2484
  )
1955
2485
 
@@ -2002,7 +2532,11 @@ globalThis.__nativeAssetProbe = [icon, back]
2002
2532
  expect(registered.map((asset) => [asset.name, asset.scales])).toEqual([
2003
2533
  ['icon', [1, 2, 3]],
2004
2534
  ['back', [1, 2]],
2535
+ ['animation', [1]],
2005
2536
  ])
2537
+ expect(await readFile(join(assetsDest, 'assets/assets/animation.lottie'))).toEqual(
2538
+ animationBytes
2539
+ )
2006
2540
  for (const file of ['icon.png', 'icon@2x.png', 'icon@3x.png']) {
2007
2541
  expect(existsSync(join(assetsDest, 'assets/assets', file))).toBe(true)
2008
2542
  }
@@ -2187,6 +2721,36 @@ globalThis.__vxrnConditionalExportProbe = helper()
2187
2721
  })
2188
2722
 
2189
2723
  describe('native Flow sources', () => {
2724
+ it('strips typed arrows and class fields without a Flow pragma or type import', async () => {
2725
+ const root = await mkdtemp(join(tmpdir(), 'vxrn-native-unmarked-flow-'))
2726
+ await writeFile(
2727
+ join(root, 'entry.js'),
2728
+ `
2729
+ const URI = { isFileURI: (uri: string): boolean => uri.startsWith('file://') }
2730
+ class Response {
2731
+ taskId: string;
2732
+ lengthComputable: boolean = false;
2733
+ onabort: (event: string) => void = () => {};
2734
+ constructor(id: string) { this.taskId = id }
2735
+ }
2736
+ function filePath(path: string) { return new Response(path).taskId }
2737
+ globalThis.unmarkedFlowResult = URI.isFileURI(filePath('file://'))
2738
+ `
2739
+ )
2740
+ try {
2741
+ const result = await buildNativeBundle({
2742
+ root,
2743
+ platform: 'ios',
2744
+ entryFile: 'entry.js',
2745
+ })
2746
+ const context = { console }
2747
+ runInNewContext(result.code, context)
2748
+ expect(Reflect.get(context, 'unmarkedFlowResult')).toBe(true)
2749
+ } finally {
2750
+ await rm(root, { recursive: true, force: true })
2751
+ }
2752
+ })
2753
+
2190
2754
  it.each(['', `/* ${'license text '.repeat(150)} */\n`])(
2191
2755
  'strips third-party Flow following a license header (%#)',
2192
2756
  async (license) => {
@@ -2358,3 +2922,64 @@ describe('postProcessNativeBundle', () => {
2358
2922
  expect(out.split('\n')[4]).toBe('var b = 2;')
2359
2923
  })
2360
2924
  })
2925
+
2926
+ describe('native package context identity', () => {
2927
+ it.each(['@tamagui/web', '@tamagui/adapt'])(
2928
+ 'shares %s contexts with dependencies containing a second package copy',
2929
+ async (packageName) => {
2930
+ const root = realpathSync(await mkdtemp(join(tmpdir(), 'vxrn-native-context-')))
2931
+ const consumer = join(root, 'node_modules/consumer')
2932
+ const packages = [
2933
+ join(root, `node_modules/${packageName}`),
2934
+ join(consumer, `node_modules/${packageName}`),
2935
+ ]
2936
+ for (const packageRoot of packages) {
2937
+ await mkdir(packageRoot, { recursive: true })
2938
+ await writeFile(
2939
+ join(packageRoot, 'package.json'),
2940
+ JSON.stringify({
2941
+ name: packageName,
2942
+ type: 'module',
2943
+ exports: {
2944
+ '.': { 'react-native': './native.js', default: './web.js' },
2945
+ './internal-runtime': './native.js',
2946
+ },
2947
+ })
2948
+ )
2949
+ await writeFile(
2950
+ join(packageRoot, 'native.js'),
2951
+ 'export const context = { platform: "native" }'
2952
+ )
2953
+ await writeFile(
2954
+ join(packageRoot, 'web.js'),
2955
+ 'export const context = { platform: "web" }'
2956
+ )
2957
+ }
2958
+ await writeFile(
2959
+ join(consumer, 'package.json'),
2960
+ JSON.stringify({ name: 'consumer', main: 'index.js', type: 'module' })
2961
+ )
2962
+ await writeFile(
2963
+ join(consumer, 'index.js'),
2964
+ `export { context } from '${packageName}/internal-runtime'`
2965
+ )
2966
+ await writeFile(
2967
+ join(root, 'entry.js'),
2968
+ `import { context } from '${packageName}'; import { context as nested } from 'consumer'; globalThis.contextIdentity = [context === nested, context.platform]`
2969
+ )
2970
+ try {
2971
+ const bundle = await buildNativeBundle({
2972
+ root,
2973
+ platform: 'ios',
2974
+ entryFile: 'entry.js',
2975
+ dev: false,
2976
+ })
2977
+ const context: any = {}
2978
+ runInNewContext(bundle.code, context)
2979
+ expect(context.contextIdentity).toEqual([true, 'native'])
2980
+ } finally {
2981
+ await rm(root, { recursive: true, force: true })
2982
+ }
2983
+ }
2984
+ )
2985
+ })