vxrn 1.10.6 → 1.11.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 (160) hide show
  1. package/dist/config/getAdditionalViteConfig.js +15 -9
  2. package/dist/config/getAdditionalViteConfig.js.map +1 -1
  3. package/dist/config/getAdditionalViteConfig.mjs +20 -10
  4. package/dist/config/getAdditionalViteConfig.mjs.map +1 -1
  5. package/dist/config/getAdditionalViteConfig.native.js +20 -10
  6. package/dist/config/getAdditionalViteConfig.native.js.map +1 -1
  7. package/dist/config/getBaseViteConfigOnly.js +44 -10
  8. package/dist/config/getBaseViteConfigOnly.js.map +1 -1
  9. package/dist/config/getBaseViteConfigOnly.mjs +34 -11
  10. package/dist/config/getBaseViteConfigOnly.mjs.map +1 -1
  11. package/dist/config/getBaseViteConfigOnly.native.js +34 -11
  12. package/dist/config/getBaseViteConfigOnly.native.js.map +1 -1
  13. package/dist/config/getOptimizeDeps.js +18 -5
  14. package/dist/config/getOptimizeDeps.js.map +1 -1
  15. package/dist/config/getOptimizeDeps.mjs +13 -6
  16. package/dist/config/getOptimizeDeps.mjs.map +1 -1
  17. package/dist/config/getOptimizeDeps.native.js +13 -6
  18. package/dist/config/getOptimizeDeps.native.js.map +1 -1
  19. package/dist/config/getReactNativeBuildConfig.js +3 -9
  20. package/dist/config/getReactNativeBuildConfig.js.map +1 -1
  21. package/dist/config/getReactNativeBuildConfig.mjs +5 -9
  22. package/dist/config/getReactNativeBuildConfig.mjs.map +1 -1
  23. package/dist/config/getReactNativeBuildConfig.native.js +5 -9
  24. package/dist/config/getReactNativeBuildConfig.native.js.map +1 -1
  25. package/dist/config/mergeUserConfig.js +2 -6
  26. package/dist/config/mergeUserConfig.js.map +1 -1
  27. package/dist/config/mergeUserConfig.mjs +2 -6
  28. package/dist/config/mergeUserConfig.mjs.map +1 -1
  29. package/dist/config/mergeUserConfig.native.js +2 -6
  30. package/dist/config/mergeUserConfig.native.js.map +1 -1
  31. package/dist/config/mergeUserConfig.test.js +1 -1
  32. package/dist/config/mergeUserConfig.test.js.map +1 -1
  33. package/dist/config/mergeUserConfig.test.mjs +1 -3
  34. package/dist/config/mergeUserConfig.test.mjs.map +1 -1
  35. package/dist/config/mergeUserConfig.test.native.js +1 -3
  36. package/dist/config/mergeUserConfig.test.native.js.map +1 -1
  37. package/dist/exports/build.js +41 -29
  38. package/dist/exports/build.js.map +1 -1
  39. package/dist/exports/build.mjs +46 -32
  40. package/dist/exports/build.mjs.map +1 -1
  41. package/dist/exports/build.native.js +46 -25
  42. package/dist/exports/build.native.js.map +1 -1
  43. package/dist/exports/createServer.js +5 -2
  44. package/dist/exports/createServer.js.map +1 -1
  45. package/dist/exports/createServer.mjs +5 -3
  46. package/dist/exports/createServer.mjs.map +1 -1
  47. package/dist/exports/createServer.native.js +5 -3
  48. package/dist/exports/createServer.native.js.map +1 -1
  49. package/dist/exports/serve.js +2 -1
  50. package/dist/exports/serve.js.map +1 -1
  51. package/dist/exports/serve.mjs +3 -1
  52. package/dist/exports/serve.mjs.map +1 -1
  53. package/dist/exports/serve.native.js +3 -1
  54. package/dist/exports/serve.native.js.map +1 -1
  55. package/dist/exports/serveStaticAssets.js +3 -2
  56. package/dist/exports/serveStaticAssets.js.map +1 -1
  57. package/dist/exports/serveStaticAssets.mjs +3 -2
  58. package/dist/exports/serveStaticAssets.mjs.map +1 -1
  59. package/dist/exports/serveStaticAssets.native.js +3 -2
  60. package/dist/exports/serveStaticAssets.native.js.map +1 -1
  61. package/dist/patches/builtInDepPatches.js +30 -3
  62. package/dist/patches/builtInDepPatches.js.map +1 -1
  63. package/dist/patches/builtInDepPatches.mjs +25 -3
  64. package/dist/patches/builtInDepPatches.mjs.map +1 -1
  65. package/dist/patches/builtInDepPatches.native.js +27 -3
  66. package/dist/patches/builtInDepPatches.native.js.map +1 -1
  67. package/dist/plugins/defaultDepOptimizePlugin.js +9 -1
  68. package/dist/plugins/defaultDepOptimizePlugin.js.map +1 -1
  69. package/dist/plugins/defaultDepOptimizePlugin.mjs +9 -1
  70. package/dist/plugins/defaultDepOptimizePlugin.mjs.map +1 -1
  71. package/dist/plugins/defaultDepOptimizePlugin.native.js +9 -1
  72. package/dist/plugins/defaultDepOptimizePlugin.native.js.map +1 -1
  73. package/dist/plugins/reactNativeCommonJsPlugin.js +14 -17
  74. package/dist/plugins/reactNativeCommonJsPlugin.js.map +1 -1
  75. package/dist/plugins/reactNativeCommonJsPlugin.mjs +15 -16
  76. package/dist/plugins/reactNativeCommonJsPlugin.mjs.map +1 -1
  77. package/dist/plugins/reactNativeCommonJsPlugin.native.js +16 -20
  78. package/dist/plugins/reactNativeCommonJsPlugin.native.js.map +1 -1
  79. package/dist/plugins/reactNativeDevServer.js +4 -2
  80. package/dist/plugins/reactNativeDevServer.js.map +1 -1
  81. package/dist/plugins/reactNativeDevServer.mjs +6 -2
  82. package/dist/plugins/reactNativeDevServer.mjs.map +1 -1
  83. package/dist/plugins/reactNativeDevServer.native.js +10 -2
  84. package/dist/plugins/reactNativeDevServer.native.js.map +1 -1
  85. package/dist/rn-commands/bundle/buildBundle.js +19 -1
  86. package/dist/rn-commands/bundle/buildBundle.js.map +1 -1
  87. package/dist/rn-commands/bundle/buildBundle.mjs +13 -1
  88. package/dist/rn-commands/bundle/buildBundle.mjs.map +1 -1
  89. package/dist/rn-commands/bundle/buildBundle.native.js +15 -1
  90. package/dist/rn-commands/bundle/buildBundle.native.js.map +1 -1
  91. package/dist/utils/getReactNativeBundle.js +4 -2
  92. package/dist/utils/getReactNativeBundle.js.map +1 -1
  93. package/dist/utils/getReactNativeBundle.mjs +4 -2
  94. package/dist/utils/getReactNativeBundle.mjs.map +1 -1
  95. package/dist/utils/getReactNativeBundle.native.js +4 -2
  96. package/dist/utils/getReactNativeBundle.native.js.map +1 -1
  97. package/dist/utils/getServerEntry.js +2 -2
  98. package/dist/utils/getServerEntry.js.map +1 -1
  99. package/dist/utils/getServerEntry.mjs +1 -1
  100. package/dist/utils/getServerEntry.mjs.map +1 -1
  101. package/dist/utils/getServerEntry.native.js +2 -1
  102. package/dist/utils/getServerEntry.native.js.map +1 -1
  103. package/dist/utils/patches.js +1 -14
  104. package/dist/utils/patches.js.map +1 -1
  105. package/dist/utils/patches.mjs +2 -18
  106. package/dist/utils/patches.mjs.map +1 -1
  107. package/dist/utils/patches.native.js +2 -25
  108. package/dist/utils/patches.native.js.map +1 -1
  109. package/expo-plugin.cjs +45 -0
  110. package/package.json +18 -19
  111. package/src/config/getAdditionalViteConfig.ts +16 -8
  112. package/src/config/getBaseViteConfigOnly.ts +45 -11
  113. package/src/config/getOptimizeDeps.ts +18 -5
  114. package/src/config/getReactNativeBuildConfig.ts +3 -9
  115. package/src/config/mergeUserConfig.test.ts +1 -1
  116. package/src/config/mergeUserConfig.ts +8 -8
  117. package/src/exports/build.ts +47 -35
  118. package/src/exports/createServer.ts +6 -3
  119. package/src/exports/serve.ts +2 -1
  120. package/src/exports/serveStaticAssets.ts +3 -1
  121. package/src/patches/builtInDepPatches.ts +44 -3
  122. package/src/plugins/defaultDepOptimizePlugin.ts +15 -2
  123. package/src/plugins/reactNativeCommonJsPlugin.ts +15 -19
  124. package/src/plugins/reactNativeDevServer.ts +10 -4
  125. package/src/rn-commands/bundle/buildBundle.ts +28 -0
  126. package/src/types.ts +3 -7
  127. package/src/utils/getReactNativeBundle.ts +4 -2
  128. package/src/utils/getServerEntry.ts +2 -2
  129. package/src/utils/patches.ts +2 -35
  130. package/types/config/getAdditionalViteConfig.d.ts.map +1 -1
  131. package/types/config/getBaseViteConfigOnly.d.ts.map +1 -1
  132. package/types/config/getOptimizeDeps.d.ts +6 -3
  133. package/types/config/getOptimizeDeps.d.ts.map +1 -1
  134. package/types/config/getOptionsFilled.d.ts.map +1 -1
  135. package/types/config/getReactNativeBuildConfig.d.ts +8 -10
  136. package/types/config/getReactNativeBuildConfig.d.ts.map +1 -1
  137. package/types/config/getReactNativePlugins.d.ts +1 -4
  138. package/types/config/getReactNativePlugins.d.ts.map +1 -1
  139. package/types/config/mergeUserConfig.d.ts +5 -2
  140. package/types/config/mergeUserConfig.d.ts.map +1 -1
  141. package/types/exports/build.d.ts +2 -1
  142. package/types/exports/build.d.ts.map +1 -1
  143. package/types/exports/createServer.d.ts +2 -1
  144. package/types/exports/createServer.d.ts.map +1 -1
  145. package/types/exports/serve.d.ts +1 -1
  146. package/types/exports/serve.d.ts.map +1 -1
  147. package/types/exports/serveStaticAssets.d.ts +2 -1
  148. package/types/exports/serveStaticAssets.d.ts.map +1 -1
  149. package/types/patches/builtInDepPatches.d.ts.map +1 -1
  150. package/types/plugins/defaultDepOptimizePlugin.d.ts.map +1 -1
  151. package/types/plugins/reactNativeCommonJsPlugin.d.ts.map +1 -1
  152. package/types/plugins/reactNativeDevServer.d.ts.map +1 -1
  153. package/types/rn-commands/bundle/buildBundle.d.ts.map +1 -1
  154. package/types/types.d.ts +3 -2
  155. package/types/types.d.ts.map +1 -1
  156. package/types/utils/getReactNativeBundle.d.ts.map +1 -1
  157. package/types/utils/getServerEntry.d.ts +1 -1
  158. package/types/utils/getServerEntry.d.ts.map +1 -1
  159. package/types/utils/patches.d.ts +0 -4
  160. package/types/utils/patches.d.ts.map +1 -1
@@ -14,7 +14,10 @@ export const dedupe = [
14
14
  'react-dom/client',
15
15
  'react-native-web',
16
16
  '@react-navigation/core',
17
+ '@react-navigation/native',
17
18
  '@react-navigation/elements',
19
+ '@react-navigation/native-stack',
20
+ '@react-navigation/bottom-tabs',
18
21
  '@tamagui/core',
19
22
  '@tamagui/web',
20
23
  '@tamagui/react-native-web',
@@ -87,23 +90,54 @@ export async function getBaseViteConfig(
87
90
  },
88
91
 
89
92
  resolve: {
90
- alias: {
91
- 'react-native/package.json': resolvePath('react-native-web/package.json', root),
92
- 'react-native': resolvePath('react-native-web', root),
93
- 'react-native-safe-area-context': resolvePath('@vxrn/safe-area', root),
94
-
93
+ alias: [
94
+ // deep react-native/Libraries/* paths must be caught BEFORE the general
95
+ // react-native react-native-web alias, since react-native-web doesn't
96
+ // have these native-only subpaths
97
+ {
98
+ find: /^react-native\/Libraries\/.*/,
99
+ replacement: resolvePath(
100
+ '@vxrn/vite-plugin-metro/empty',
101
+ new URL('.', import.meta.url).pathname
102
+ ),
103
+ },
104
+ {
105
+ find: 'react-native/package.json',
106
+ replacement: resolvePath('react-native-web/package.json', root),
107
+ },
108
+ {
109
+ find: 'react-native',
110
+ replacement: resolvePath('react-native-web', root),
111
+ },
112
+ {
113
+ find: 'react-native-safe-area-context',
114
+ replacement: resolvePath('@vxrn/safe-area', root),
115
+ },
95
116
  // bundle size optimizations
96
- 'query-string': resolvePath('@vxrn/query-string', root),
97
- 'url-parse': resolvePath('@vxrn/url-parse', root),
98
- },
117
+ {
118
+ find: 'query-string',
119
+ replacement: resolvePath('@vxrn/query-string', root),
120
+ },
121
+ {
122
+ find: 'url-parse',
123
+ replacement: resolvePath('@vxrn/url-parse', root),
124
+ },
125
+ ],
99
126
 
100
127
  // TODO auto dedupe all include optimize deps?
101
128
  dedupe,
102
129
  },
103
130
 
104
- build: {
105
- commonjsOptions: {
106
- transformMixedEsModules: true,
131
+ build: {},
132
+
133
+ // enable OXC React Refresh so component registration ($RefreshReg$) is
134
+ // injected into TSX/JSX output; the one:react-refresh-web compiler plugin
135
+ // then wraps the output with the runtime preamble and import.meta.hot.accept
136
+ // only enable in dev mode - in production the refresh plugin doesn't run,
137
+ // leaving $RefreshReg$ undefined which crashes client-side JS
138
+ oxc: {
139
+ jsx: {
140
+ refresh: mode === 'development',
107
141
  },
108
142
  },
109
143
  } satisfies Omit<InlineConfig, 'plugins'>
@@ -135,15 +135,28 @@ export function getOptimizeDeps(mode: 'build' | 'serve') {
135
135
  depsToOptimize,
136
136
  optimizeDeps: {
137
137
  include: depsToOptimize,
138
- exclude: ['util', '@swc/wasm', '@swc/core-darwin-arm64', 'moti/author'],
138
+ exclude: [
139
+ 'util',
140
+ '@swc/wasm',
141
+ '@swc/core-darwin-arm64',
142
+ 'moti/author',
143
+ '@expo/log-box',
144
+ ],
139
145
  needsInterop,
140
146
  // Enable lazy optimization - don't wait for all deps before starting server
141
147
  // This allows browser to process requests in parallel for faster initial load
142
148
  holdUntilCrawlEnd: false,
143
- esbuildOptions: {
144
- resolveExtensions: webExtensions,
145
- // Some packages ship JSX in .js files (e.g., react-native-css-interop/dist/doctor.js)
146
- loader: { '.js': 'jsx' },
149
+ rolldownOptions: {
150
+ resolve: {
151
+ extensions: webExtensions,
152
+ },
153
+ // some packages ship JSX in .js files (e.g., react-native-css-interop/dist/doctor.js)
154
+ moduleTypes: { '.js': 'jsx' },
155
+ // react-native packages import native-only exports (TurboModuleRegistry etc.)
156
+ // from react-native, which is aliased to react-native-web on web. react-native-web
157
+ // doesn't export these, so rolldown would error. shimMissingExports creates
158
+ // undefined shims instead, matching esbuild's lenient behavior.
159
+ shimMissingExports: true,
147
160
  },
148
161
  } satisfies UserConfig['optimizeDeps'],
149
162
  }
@@ -214,8 +214,8 @@ export async function getReactNativeBuildConfig(
214
214
 
215
215
  optimizeDeps: {
216
216
  ...optimizeDeps,
217
- esbuildOptions: {
218
- jsx: 'automatic',
217
+ rolldownOptions: {
218
+ moduleTypes: { '.js': 'jsx' },
219
219
  },
220
220
  },
221
221
 
@@ -244,13 +244,7 @@ export async function getReactNativeBuildConfig(
244
244
  build: {
245
245
  ssr: true,
246
246
  minify: false,
247
- commonjsOptions: {
248
- transformMixedEsModules: true,
249
- ignore(id) {
250
- return id === 'react/jsx-runtime' || id === 'react/jsx-dev-runtime'
251
- },
252
- },
253
- rollupOptions: {
247
+ rolldownOptions: {
254
248
  input: options.entries.native,
255
249
  treeshake: false,
256
250
  preserveEntrySignatures: 'strict',
@@ -76,7 +76,7 @@ describe('deepMergeOptimizeDeps', () => {
76
76
  include: [],
77
77
  exclude: ['c'],
78
78
  needsInterop: [],
79
- esbuildOptions: { resolveExtensions: [] },
79
+ rolldownOptions: {},
80
80
  }
81
81
 
82
82
  deepMergeOptimizeDeps(a, b, extra)
@@ -6,8 +6,11 @@ type OptimizeDepsConf = {
6
6
  include: string[]
7
7
  exclude: string[]
8
8
  needsInterop: string[]
9
- esbuildOptions: {
10
- resolveExtensions: string[]
9
+ rolldownOptions?: {
10
+ resolve?: {
11
+ extensions?: string[]
12
+ }
13
+ moduleTypes?: Record<string, string>
11
14
  }
12
15
  }
13
16
 
@@ -45,7 +48,6 @@ export function mergeUserConfig(
45
48
  serverConfig.ssr ||= {}
46
49
  userViteConfig.ssr ||= {}
47
50
  deepMergeOptimizeDeps(serverConfig.ssr, userViteConfig.ssr, optimizeDeps)
48
- deepMergeOptimizeDeps(serverConfig.ssr, userViteConfig, optimizeDeps)
49
51
  }
50
52
 
51
53
  return serverConfig
@@ -107,9 +109,7 @@ export function deepMergeOptimizeDeps(
107
109
  ...(extraDepsOpt?.needsInterop || []),
108
110
  ]).filter((dep) => !excludeSet.has(dep))
109
111
 
110
- a.optimizeDeps.esbuildOptions = {
111
- ...a.optimizeDeps.esbuildOptions,
112
- ...b.optimizeDeps.esbuildOptions,
113
- ...extraDepsOpt?.esbuildOptions,
114
- }
112
+ // don't merge rolldownOptions onto ssr.optimizeDeps it must live on
113
+ // environments.ssr.optimizeDeps to avoid a Vite compat-proxy bug that fires
114
+ // a spurious deprecation warning when both paths carry rolldownOptions
115
115
  }
@@ -1,7 +1,7 @@
1
1
  import FSExtra from 'fs-extra'
2
2
  import { rm } from 'node:fs/promises'
3
3
  import { sep } from 'node:path'
4
- import type { OutputAsset, OutputChunk, RollupOutput } from 'rollup'
4
+ import type { OutputAsset, OutputChunk, RolldownOutput } from 'rolldown'
5
5
  import {
6
6
  loadConfigFromFile,
7
7
  mergeConfig,
@@ -24,15 +24,11 @@ const { existsSync } = FSExtra
24
24
  Error.stackTraceLimit = Number.POSITIVE_INFINITY
25
25
 
26
26
  const disableOptimizationConfig = {
27
- optimizeDeps: {
28
- esbuildOptions: {
29
- minify: false,
30
- },
31
- },
27
+ optimizeDeps: {},
32
28
 
33
29
  build: {
34
30
  minify: false,
35
- rollupOptions: {
31
+ rolldownOptions: {
36
32
  treeshake: false,
37
33
  output: {
38
34
  minifyInternalExports: false,
@@ -83,18 +79,14 @@ export const build = async (optionsIn: VXRNOptions, buildArgs: BuildArgs = {}) =
83
79
  console.error(`\n 🥺 error applying built-in patches`, err)
84
80
  })
85
81
 
82
+ const outDir = userViteConfig?.build?.outDir ?? 'dist'
83
+
86
84
  // clean
87
85
  await Promise.all([
88
86
  (async () => {
89
- // lets always clean dist folder for now to be sure were correct
90
- if (existsSync('dist')) {
91
- await rm('dist', { recursive: true, force: true })
92
- }
93
- })(),
94
- (async () => {
95
- // lets always clean dist folder for now to be sure were correct
96
- if (existsSync('node_modules/.vite')) {
97
- await rm('node_modules/.vite', { recursive: true, force: true })
87
+ // lets always clean output folder for now to be sure were correct
88
+ if (existsSync(outDir)) {
89
+ await rm(outDir, { recursive: true, force: true })
98
90
  }
99
91
  })(),
100
92
  ])
@@ -107,7 +99,7 @@ export const build = async (optionsIn: VXRNOptions, buildArgs: BuildArgs = {}) =
107
99
  { root: options.root },
108
100
  {
109
101
  platform: buildArgs.platform,
110
- bundleOutput: `dist${sep}${buildArgs.platform}.js`,
102
+ bundleOutput: `${outDir}${sep}${buildArgs.platform}.js`,
111
103
  dev: false,
112
104
  entryFile: '',
113
105
  resetCache: true,
@@ -120,6 +112,8 @@ export const build = async (optionsIn: VXRNOptions, buildArgs: BuildArgs = {}) =
120
112
  }
121
113
 
122
114
  const { optimizeDeps } = getOptimizeDeps('build')
115
+ const { rolldownOptions: optimizeDepsRolldownOptions, ...optimizeDepsWithoutRolldown } =
116
+ optimizeDeps
123
117
 
124
118
  let webBuildConfig = mergeConfig(
125
119
  await getBaseViteConfigWithPlugins({
@@ -131,10 +125,18 @@ export const build = async (optionsIn: VXRNOptions, buildArgs: BuildArgs = {}) =
131
125
  clearScreen: false,
132
126
  configFile: false,
133
127
  ...(skipEnv && { envFile: false }),
134
- optimizeDeps,
128
+ // rolldownOptions goes on environments.client to avoid Vite compat-proxy bug
129
+ optimizeDeps: optimizeDepsWithoutRolldown,
130
+ environments: {
131
+ client: {
132
+ optimizeDeps: {
133
+ rolldownOptions: optimizeDepsRolldownOptions,
134
+ },
135
+ },
136
+ },
135
137
  logLevel: 'warn',
136
138
  build: {
137
- rollupOptions: {
139
+ rolldownOptions: {
138
140
  onwarn(warning, defaultHandler) {
139
141
  if (
140
142
  warning.code === 'MODULE_LEVEL_DIRECTIVE' &&
@@ -193,7 +195,7 @@ export const build = async (optionsIn: VXRNOptions, buildArgs: BuildArgs = {}) =
193
195
  } satisfies Plugin
194
196
 
195
197
  let clientOutput
196
- let clientBuildPromise: Promise<RollupOutput> | undefined
198
+ let clientBuildPromise: Promise<RolldownOutput> | undefined
197
199
 
198
200
  if (buildArgs.step !== 'generate') {
199
201
  let clientBuildConfig = mergeConfig(webBuildConfig, {
@@ -208,17 +210,18 @@ export const build = async (optionsIn: VXRNOptions, buildArgs: BuildArgs = {}) =
208
210
 
209
211
  build: {
210
212
  ssrManifest: true,
211
- outDir: 'dist/client',
213
+ outDir: `${outDir}/client`,
212
214
  sourcemap: false,
213
215
  manifest: true,
214
- rollupOptions: {
216
+ rolldownOptions: {
215
217
  input: ['virtual:one-entry'],
216
218
 
217
- // output: {
218
- // manualChunks: {
219
- // preload: ['\0vite/preload-helper.js'],
220
- // },
221
- // },
219
+ output: {
220
+ codeSplitting: {
221
+ // merge tiny chunks to reduce HTTP requests and total chunk count
222
+ minSize: 42_000,
223
+ },
224
+ },
222
225
  },
223
226
  },
224
227
  } satisfies UserConfig)
@@ -231,7 +234,7 @@ export const build = async (optionsIn: VXRNOptions, buildArgs: BuildArgs = {}) =
231
234
  `\n 🔨 build ${options.build?.server !== false ? 'client + server' : 'client'}\n`
232
235
  )
233
236
 
234
- clientBuildPromise = viteBuild(clientBuildConfig) as Promise<RollupOutput>
237
+ clientBuildPromise = viteBuild(clientBuildConfig) as Promise<RolldownOutput>
235
238
  }
236
239
 
237
240
  const serverOptions = options.build?.server
@@ -254,7 +257,15 @@ export const build = async (optionsIn: VXRNOptions, buildArgs: BuildArgs = {}) =
254
257
  // we used to do this i think to make our patching react work?
255
258
  // but stopped working for prod builds due to duplicate react somehow
256
259
  // external: ['react', 'react-dom', 'expo-modules-core'],
257
- optimizeDeps,
260
+ optimizeDeps: optimizeDepsWithoutRolldown,
261
+ },
262
+
263
+ environments: {
264
+ ssr: {
265
+ optimizeDeps: {
266
+ rolldownOptions: optimizeDepsRolldownOptions,
267
+ },
268
+ },
258
269
  },
259
270
 
260
271
  build: {
@@ -262,8 +273,8 @@ export const build = async (optionsIn: VXRNOptions, buildArgs: BuildArgs = {}) =
262
273
  cssCodeSplit: false,
263
274
  ssr: true,
264
275
  sourcemap: false,
265
- outDir: 'dist/server',
266
- rollupOptions: {
276
+ outDir: `${outDir}/server`,
277
+ rolldownOptions: {
267
278
  treeshake: true,
268
279
  // fixes some weird issues with optimizing tamagui and other packages
269
280
  // external: (id) => {
@@ -288,11 +299,11 @@ export const build = async (optionsIn: VXRNOptions, buildArgs: BuildArgs = {}) =
288
299
  serverBuildConfig.ssr!.noExternal = true
289
300
  }
290
301
 
291
- const serverEntry = getServerEntry(options)
302
+ const serverEntry = getServerEntry(options, outDir)
292
303
 
293
304
  let serverOutput: [OutputChunk, ...(OutputChunk | OutputAsset)[]] | undefined
294
305
  let clientManifest
295
- let serverBuildPromise: Promise<RollupOutput> | undefined
306
+ let serverBuildPromise: Promise<RolldownOutput> | undefined
296
307
 
297
308
  if (serverOptions !== false) {
298
309
  if (!clientBuildPromise) {
@@ -307,7 +318,7 @@ export const build = async (optionsIn: VXRNOptions, buildArgs: BuildArgs = {}) =
307
318
  userServerBuildConf
308
319
  ? mergeConfig(serverBuildConfig, userServerBuildConf)
309
320
  : serverBuildConfig
310
- ) as Promise<RollupOutput>
321
+ ) as Promise<RolldownOutput>
311
322
  }
312
323
 
313
324
  // Wait for both builds to complete in parallel
@@ -327,7 +338,7 @@ export const build = async (optionsIn: VXRNOptions, buildArgs: BuildArgs = {}) =
327
338
  }
328
339
 
329
340
  if (serverOptions !== false) {
330
- clientManifest = await FSExtra.readJSON('dist/client/.vite/manifest.json')
341
+ clientManifest = await FSExtra.readJSON(`${outDir}/client/.vite/manifest.json`)
331
342
 
332
343
  // temp fix - react native web is importing non-existent react 19 apis
333
344
  const old = await FSExtra.readFile(serverEntry, 'utf-8')
@@ -346,6 +357,7 @@ export const build = async (optionsIn: VXRNOptions, buildArgs: BuildArgs = {}) =
346
357
  }
347
358
 
348
359
  return {
360
+ outDir,
349
361
  processEnvDefines,
350
362
  options,
351
363
  buildArgs,
@@ -14,7 +14,10 @@ export const applyCompression = (app: Hono, options: VXRNServeOptions) => {
14
14
  export const createProdServer = async (
15
15
  app: Hono,
16
16
  options: VXRNServeOptions,
17
- { skipCompression }: { skipCompression?: boolean } = {}
17
+ {
18
+ skipCompression,
19
+ outDir = 'dist',
20
+ }: { skipCompression?: boolean; outDir?: string } = {}
18
21
  ) => {
19
22
  // when called via serve(), compression is already applied before beforeRegisterRoutes
20
23
  if (!skipCompression) {
@@ -22,7 +25,7 @@ export const createProdServer = async (
22
25
  }
23
26
 
24
27
  app.use('*', async (context, next) => {
25
- return await serveStaticAssets({ context, next })
28
+ return await serveStaticAssets({ context, next, outDir })
26
29
  })
27
30
 
28
31
  app.notFound(async (c) => {
@@ -31,7 +34,7 @@ export const createProdServer = async (
31
34
 
32
35
  while (true) {
33
36
  const response = await serveStatic({
34
- root: './dist/client',
37
+ root: `./${outDir}/client`,
35
38
  path: join(currentDir, '+not-found.html'),
36
39
  })(c, async () => {})
37
40
 
@@ -12,6 +12,7 @@ export const serve = async ({
12
12
  afterRegisterRoutes,
13
13
  beforeRegisterRoutes,
14
14
  app = new Hono(),
15
+ outDir = 'dist',
15
16
  ...optionsIn
16
17
  }: VXRNServeOptions) => {
17
18
  const { getServerOptionsFilled } = await import('../config/getServerOptionsFilled')
@@ -25,7 +26,7 @@ export const serve = async ({
25
26
  }
26
27
 
27
28
  // createProdServer will skip compression since we already applied it
28
- await createProdServer(app, options, { skipCompression: true })
29
+ await createProdServer(app, options, { skipCompression: true, outDir })
29
30
 
30
31
  if (afterRegisterRoutes) {
31
32
  await afterRegisterRoutes(options, app)
@@ -7,14 +7,16 @@ const hashedAssetRe = /[.-][a-zA-Z0-9_-]{8,}\.\w+$/
7
7
  export async function serveStaticAssets({
8
8
  context,
9
9
  next,
10
+ outDir = 'dist',
10
11
  }: {
11
12
  context: Context
12
13
  next?: () => Promise<void>
14
+ outDir?: string
13
15
  }) {
14
16
  let didCallNext = false
15
17
 
16
18
  const response = await serveStatic({
17
- root: './dist/client',
19
+ root: `./${outDir}/client`,
18
20
  onFound: (path, c) => {
19
21
  if (hashedAssetRe.test(path)) {
20
22
  c.header('Cache-Control', 'public, immutable, max-age=31536000')
@@ -2,6 +2,47 @@ import { assertString } from '../utils/assert'
2
2
  import { type DepPatch, bailIfExists, bailIfUnchanged } from '../utils/patches'
3
3
 
4
4
  export const builtInDepPatches: DepPatch[] = [
5
+ // fix broken ESM: NavigationContainer.js uses extensionless relative imports
6
+ // which fail in Node.js ESM (lib/module has "type": "module")
7
+ {
8
+ module: '@react-navigation/native',
9
+ patchFiles: {
10
+ 'lib/module/NavigationContainer.js': (contents) => {
11
+ assertString(contents)
12
+ return contents
13
+ .replace(`from './useBackButton'`, `from './useBackButton.js'`)
14
+ .replace(`from './useDocumentTitle'`, `from './useDocumentTitle.js'`)
15
+ .replace(`from './useLinking'`, `from './useLinking.js'`)
16
+ },
17
+ },
18
+ },
19
+
20
+ // expose internal contexts for SSR-optimized NavigationContainer
21
+ {
22
+ module: '@react-navigation/core',
23
+ patchFiles: {
24
+ 'package.json': (contents) => {
25
+ assertString(contents)
26
+ const pkg = JSON.parse(contents)
27
+ const exports = pkg.exports || {}
28
+ let changed = false
29
+ for (const path of [
30
+ './lib/module/NavigationBuilderContext',
31
+ './lib/module/NavigationStateContext',
32
+ './lib/module/EnsureSingleNavigator',
33
+ ]) {
34
+ if (!exports[path]) {
35
+ exports[path] = path + '.js'
36
+ changed = true
37
+ }
38
+ }
39
+ if (!changed) return
40
+ pkg.exports = exports
41
+ return JSON.stringify(pkg, null, 2)
42
+ },
43
+ },
44
+ },
45
+
5
46
  // react-native-web doesn't export unstable_batchedUpdates but react-native does,
6
47
  // so libraries like @legendapp/list break when aliased to rnw on web
7
48
  {
@@ -300,7 +341,7 @@ ${contents}
300
341
  {
301
342
  module: 'expo',
302
343
  patchFiles: {
303
- version: '>=52.0.0 <55.0.0',
344
+ version: '>=52.0.0 <56.0.0',
304
345
  'src/winter/runtime.native.ts': (contents) => {
305
346
  assertString(contents)
306
347
 
@@ -333,7 +374,7 @@ install('URLSearchParams', () => URLSearchParams);
333
374
  {
334
375
  module: 'expo-modules-core',
335
376
  patchFiles: {
336
- version: '<=3.*',
377
+ version: '<=55.*',
337
378
  'src/**/*.ts': addNoCheck,
338
379
  'src/**/*.tsx': addNoCheck,
339
380
  },
@@ -342,7 +383,7 @@ install('URLSearchParams', () => URLSearchParams);
342
383
  {
343
384
  module: '@expo/cli',
344
385
  patchFiles: {
345
- version: '<=54',
386
+ version: '<=55',
346
387
 
347
388
  'build/src/export/embed/exportEmbedAsync.js': (contents) => {
348
389
  return contents?.replace(
@@ -35,8 +35,21 @@ export function defaultDepOptimizePlugin(): Plugin {
35
35
  // so we just call it here to ensure `optimizeDeps` and `noExternal` configs are set as likely
36
36
  // same as in CLI mode.
37
37
  if (!config.ssr) config.ssr = {}
38
- // pass user config so their excludes are respected
39
- deepMergeOptimizeDeps(config.ssr, config, optimizeDeps)
38
+ // only apply framework deps to ssr, don't merge regular optimizeDeps into ssr
39
+ deepMergeOptimizeDeps(config.ssr, {}, optimizeDeps)
40
+
41
+ // rolldownOptions must go on environments.ssr.optimizeDeps to avoid a Vite
42
+ // compat-proxy bug when both ssr.optimizeDeps and environments.ssr.optimizeDeps
43
+ // carry rolldownOptions
44
+ return {
45
+ environments: {
46
+ ssr: {
47
+ optimizeDeps: {
48
+ rolldownOptions: optimizeDeps.rolldownOptions,
49
+ },
50
+ },
51
+ },
52
+ }
40
53
  },
41
54
  } satisfies Plugin
42
55
  }
@@ -96,7 +96,7 @@ export function reactNativeCommonJsPlugin(options: {
96
96
  // Avoids the extra step of testing Brotli compression, which isn't really pertinent to a file served locally
97
97
  reportCompressedSize: false,
98
98
 
99
- rollupOptions: {
99
+ rolldownOptions: {
100
100
  treeshake: false,
101
101
 
102
102
  output: {
@@ -208,8 +208,8 @@ export function reactNativeCommonJsPlugin(options: {
208
208
  noDiscovery: true,
209
209
  include: undefined,
210
210
 
211
- esbuildOptions: {
212
- loader: {
211
+ rolldownOptions: {
212
+ moduleTypes: {
213
213
  '.js': 'jsx',
214
214
  },
215
215
  },
@@ -232,24 +232,18 @@ export function reactNativeCommonJsPlugin(options: {
232
232
  },
233
233
 
234
234
  optimizeDeps: {
235
- esbuildOptions: {
236
- resolveExtensions: getNativeExtensions('ios'),
235
+ rolldownOptions: {
236
+ resolve: {
237
+ extensions: getNativeExtensions('ios'),
238
+ },
237
239
 
238
240
  plugins: [
239
241
  {
240
242
  name: 'react-native-assets',
241
- setup(build) {
242
- build.onResolve(
243
- {
244
- filter: /\.(png|jpg|gif|webp)$/,
245
- },
246
- async ({ path, namespace }) => {
247
- return {
248
- path: '',
249
- external: true,
250
- }
251
- }
252
- )
243
+ resolveId(source) {
244
+ if (/\.(png|jpg|gif|webp)$/.test(source)) {
245
+ return { id: '', external: true }
246
+ }
253
247
  },
254
248
  },
255
249
  ],
@@ -269,8 +263,10 @@ export function reactNativeCommonJsPlugin(options: {
269
263
  },
270
264
 
271
265
  optimizeDeps: {
272
- esbuildOptions: {
273
- resolveExtensions: getNativeExtensions('android'),
266
+ rolldownOptions: {
267
+ resolve: {
268
+ extensions: getNativeExtensions('android'),
269
+ },
274
270
  },
275
271
  },
276
272
  } satisfies UserConfig),
@@ -213,13 +213,19 @@ export function createReactNativeDevServerPlugin(
213
213
  console.error(
214
214
  `\n\n Note, some errors may happen due to a stale Vite cache, you may want to try re-running with the "--clean" flag`
215
215
  )
216
- res.writeHead(500)
217
- res.end()
216
+ res.writeHead(500, { 'Content-Type': 'text/plain' })
217
+ res.end(err instanceof Error ? err.stack || err.message : String(err))
218
218
  }
219
219
  }
220
220
 
221
- server.middlewares.use('/index.bundle', handleRNBundle)
222
- server.middlewares.use('/.expo/.virtual-metro-entry.bundle', handleRNBundle)
221
+ // handle any .bundle request (expo sdk 55 may use /packages/one/metro-entry.bundle)
222
+ server.middlewares.use((req, res, next) => {
223
+ if (req.url?.split('?')[0].endsWith('.bundle')) {
224
+ handleRNBundle(req, res, next)
225
+ } else {
226
+ next()
227
+ }
228
+ })
223
229
 
224
230
  // Status endpoint
225
231
  server.middlewares.use('/status', (_req, res) => {