vxrn 1.26.0 → 2.0.0-beta.33.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.
- package/dist/patches/builtInDepPatches.mjs +23 -42
- package/dist/patches/builtInDepPatches.mjs.map +1 -1
- package/dist/patches/builtInDepPatches.native.js +23 -42
- package/dist/patches/builtInDepPatches.native.js.map +1 -1
- package/dist/plugins/reactNativeDevServer.mjs +88 -42
- package/dist/plugins/reactNativeDevServer.mjs.map +1 -1
- package/dist/plugins/reactNativeDevServer.native.js +113 -44
- package/dist/plugins/reactNativeDevServer.native.js.map +1 -1
- package/dist/rn-commands/bundle/buildBundle.mjs +29 -10
- package/dist/rn-commands/bundle/buildBundle.mjs.map +1 -1
- package/dist/rn-commands/bundle/buildBundle.native.js +29 -10
- package/dist/rn-commands/bundle/buildBundle.native.js.map +1 -1
- package/dist/utils/createNativeDevEngine.mjs +288 -95
- package/dist/utils/createNativeDevEngine.mjs.map +1 -1
- package/dist/utils/createNativeDevEngine.native.js +329 -95
- package/dist/utils/createNativeDevEngine.native.js.map +1 -1
- package/dist/utils/createNativeDevEngine.test.mjs +285 -23
- package/dist/utils/createNativeDevEngine.test.mjs.map +1 -1
- package/dist/utils/createNativeDevEngine.test.native.js +319 -23
- package/dist/utils/createNativeDevEngine.test.native.js.map +1 -1
- package/dist/utils/patches.test.mjs.map +1 -1
- package/dist/utils/patches.test.native.js.map +1 -1
- package/dist/utils/scanDepsToOptimize.mjs +1 -3
- package/dist/utils/scanDepsToOptimize.mjs.map +1 -1
- package/dist/utils/scanDepsToOptimize.native.js +1 -3
- package/dist/utils/scanDepsToOptimize.native.js.map +1 -1
- package/dist/utils/symbolicateNativeStack.mjs +66 -0
- package/dist/utils/symbolicateNativeStack.mjs.map +1 -0
- package/dist/utils/symbolicateNativeStack.native.js +70 -0
- package/dist/utils/symbolicateNativeStack.native.js.map +1 -0
- package/dist/utils/symbolicateNativeStack.test.mjs +96 -0
- package/dist/utils/symbolicateNativeStack.test.mjs.map +1 -0
- package/dist/utils/symbolicateNativeStack.test.native.js +96 -0
- package/dist/utils/symbolicateNativeStack.test.native.js.map +1 -0
- package/expo-plugin.cjs +12 -6
- package/package.json +13 -12
- package/src/patches/builtInDepPatches.ts +24 -48
- package/src/plugins/reactNativeDevServer.ts +124 -52
- package/src/rn-commands/bundle/buildBundle.ts +28 -9
- package/src/utils/createNativeDevEngine.test.ts +581 -193
- package/src/utils/createNativeDevEngine.ts +506 -123
- package/src/utils/patches.test.ts +1 -4
- package/src/utils/scanDepsToOptimize.ts +0 -3
- package/src/utils/symbolicateNativeStack.test.ts +78 -0
- package/src/utils/symbolicateNativeStack.ts +129 -0
- package/types/patches/builtInDepPatches.d.ts.map +1 -1
- package/types/plugins/reactNativeDevServer.d.ts.map +1 -1
- package/types/rn-commands/bundle/buildBundle.d.ts.map +1 -1
- package/types/utils/createNativeDevEngine.d.ts +22 -4
- package/types/utils/createNativeDevEngine.d.ts.map +1 -1
- package/types/utils/scanDepsToOptimize.d.ts.map +1 -1
- package/types/utils/symbolicateNativeStack.d.ts +46 -0
- package/types/utils/symbolicateNativeStack.d.ts.map +1 -0
- package/types/utils/symbolicateNativeStack.test.d.ts +2 -0
- package/types/utils/symbolicateNativeStack.test.d.ts.map +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vxrn",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0-beta.33.1",
|
|
4
4
|
"sideEffects": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -65,16 +65,16 @@
|
|
|
65
65
|
"@jridgewell/trace-mapping": "^0.3.25",
|
|
66
66
|
"@react-native/codegen": "^0.86.2",
|
|
67
67
|
"@react-native/dev-middleware": "^0.86.2",
|
|
68
|
-
"@vxrn/compiler": "
|
|
69
|
-
"@vxrn/debug": "
|
|
70
|
-
"@vxrn/query-string": "
|
|
71
|
-
"@vxrn/resolve": "
|
|
72
|
-
"@vxrn/safe-area": "
|
|
73
|
-
"@vxrn/url-parse": "
|
|
74
|
-
"@vxrn/utils": "
|
|
75
|
-
"@vxrn/vendor": "
|
|
76
|
-
"@vxrn/vite-flow": "
|
|
77
|
-
"@vxrn/vite-plugin-metro": "
|
|
68
|
+
"@vxrn/compiler": "2.0.0-beta.33.1",
|
|
69
|
+
"@vxrn/debug": "2.0.0-beta.33.1",
|
|
70
|
+
"@vxrn/query-string": "2.0.0-beta.33.1",
|
|
71
|
+
"@vxrn/resolve": "2.0.0-beta.33.1",
|
|
72
|
+
"@vxrn/safe-area": "2.0.0-beta.33.1",
|
|
73
|
+
"@vxrn/url-parse": "2.0.0-beta.33.1",
|
|
74
|
+
"@vxrn/utils": "2.0.0-beta.33.1",
|
|
75
|
+
"@vxrn/vendor": "2.0.0-beta.33.1",
|
|
76
|
+
"@vxrn/vite-flow": "2.0.0-beta.33.1",
|
|
77
|
+
"@vxrn/vite-plugin-metro": "2.0.0-beta.33.1",
|
|
78
78
|
"citty": "^0.1.6",
|
|
79
79
|
"dotenv": "^17.2.1",
|
|
80
80
|
"dotenv-expand": "^12.0.2",
|
|
@@ -95,13 +95,14 @@
|
|
|
95
95
|
"perfect-debounce": "^1.0.0",
|
|
96
96
|
"picocolors": "^1.0.0",
|
|
97
97
|
"pkg-types": "^1.0.3",
|
|
98
|
-
"rolldown": "^1.2.
|
|
98
|
+
"rolldown": "^1.2.8",
|
|
99
99
|
"semver": "^7.6.3",
|
|
100
100
|
"vite": "^8.2.2",
|
|
101
101
|
"ws": "^8.21.0"
|
|
102
102
|
},
|
|
103
103
|
"devDependencies": {
|
|
104
104
|
"@biomejs/biome": "2.3.3",
|
|
105
|
+
"@jridgewell/gen-mapping": "^0.3.5",
|
|
105
106
|
"@react-native-community/cli": "^20.2.0",
|
|
106
107
|
"@tamagui/build": "2.6.2",
|
|
107
108
|
"@types/find-node-modules": "^2.1.2",
|
|
@@ -268,36 +268,20 @@ ${contents}
|
|
|
268
268
|
},
|
|
269
269
|
},
|
|
270
270
|
|
|
271
|
-
// fix RNGH crash on RN 0.
|
|
271
|
+
// fix RNGH crash on RN 0.86+ new architecture
|
|
272
272
|
// getShadowNodeFromRef crashes when findHostInstance_DEPRECATED returns null
|
|
273
273
|
{
|
|
274
274
|
module: 'react-native-gesture-handler',
|
|
275
275
|
patchFiles: {
|
|
276
|
-
|
|
277
|
-
// exact unsafe line in all three targets, so the cap silently dropped the
|
|
278
|
-
// crash guard rather than retiring it. each replacement is a no-op when
|
|
279
|
-
// its target is absent, so an uncapped range retires itself.
|
|
280
|
-
version: '>=2.0.0',
|
|
276
|
+
version: '>=3.0.0',
|
|
281
277
|
|
|
282
278
|
'lib/module/getShadowNodeFromRef.js': (contents) => {
|
|
283
279
|
return contents?.replace(
|
|
284
|
-
`
|
|
285
|
-
`const hostInstance = findHostInstance_DEPRECATED(ref);
|
|
286
|
-
if (hostInstance === null || hostInstance === undefined) {
|
|
287
|
-
return null;
|
|
288
|
-
}
|
|
289
|
-
const internalHandle = getInternalInstanceHandleFromPublicInstance(hostInstance);
|
|
290
|
-
if (internalHandle === null || internalHandle === undefined) {
|
|
291
|
-
return null;
|
|
292
|
-
}
|
|
293
|
-
return internalHandle.stateNode?.node ?? null;`
|
|
294
|
-
)
|
|
295
|
-
},
|
|
280
|
+
`const hostInstance = isAlreadyHostInstance ? ref : findHostInstance_DEPRECATED(ref);
|
|
296
281
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
`
|
|
300
|
-
`const hostInstance = findHostInstance_DEPRECATED(ref);
|
|
282
|
+
// @ts-ignore Fabric
|
|
283
|
+
return getInternalInstanceHandleFromPublicInstance(hostInstance).stateNode.node;`,
|
|
284
|
+
`const hostInstance = isAlreadyHostInstance ? ref : findHostInstance_DEPRECATED(ref);
|
|
301
285
|
if (hostInstance === null || hostInstance === undefined) {
|
|
302
286
|
return null;
|
|
303
287
|
}
|
|
@@ -312,10 +296,16 @@ ${contents}
|
|
|
312
296
|
// metro uses src/ directly via "react-native" field in package.json
|
|
313
297
|
'src/getShadowNodeFromRef.ts': (contents) => {
|
|
314
298
|
return contents?.replace(
|
|
315
|
-
`
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
299
|
+
`const hostInstance = isAlreadyHostInstance
|
|
300
|
+
? ref
|
|
301
|
+
: findHostInstance_DEPRECATED(ref);
|
|
302
|
+
|
|
303
|
+
// @ts-ignore Fabric
|
|
304
|
+
return getInternalInstanceHandleFromPublicInstance(hostInstance).stateNode
|
|
305
|
+
.node;`,
|
|
306
|
+
`const hostInstance = isAlreadyHostInstance
|
|
307
|
+
? ref
|
|
308
|
+
: findHostInstance_DEPRECATED(ref);
|
|
319
309
|
if (hostInstance === null || hostInstance === undefined) {
|
|
320
310
|
return null;
|
|
321
311
|
}
|
|
@@ -507,10 +497,6 @@ install('URLSearchParams', () => URLSearchParams);
|
|
|
507
497
|
{
|
|
508
498
|
module: 'expo-video',
|
|
509
499
|
patchFiles: {
|
|
510
|
-
'build/index.js': (contents) => {
|
|
511
|
-
// bad type export that can't be auto-fixed
|
|
512
|
-
return contents?.replace(`export { VideoThumbnail } from './VideoThumbnail';`, ``)
|
|
513
|
-
},
|
|
514
500
|
'build/**/*.js': ['jsx'],
|
|
515
501
|
},
|
|
516
502
|
},
|
|
@@ -632,17 +618,13 @@ export const addCustomSourceTransformer = resolveAssetSource.addCustomSourceTran
|
|
|
632
618
|
},
|
|
633
619
|
|
|
634
620
|
// a void TurboModule method that throws an Obj-C NSException crashes the app
|
|
635
|
-
//
|
|
636
|
-
//
|
|
637
|
-
//
|
|
638
|
-
// single-threaded VM. only safe to build the JS error when the method runs
|
|
639
|
-
// sync (on the JS thread); otherwise log. mirrors the isSync guard added for
|
|
640
|
-
// value-returning methods in facebook/react-native#50193 (which never covered
|
|
641
|
-
// the void path). see tests/test/IOS_PROD_CRASH.md
|
|
621
|
+
// when React Native rethrows it on the TurboModule queue. there is no JS
|
|
622
|
+
// caller for an async void method to receive an error, so log it instead.
|
|
623
|
+
// see tests/test/IOS_PROD_CRASH.md
|
|
642
624
|
{
|
|
643
625
|
module: 'react-native',
|
|
644
626
|
patchFiles: {
|
|
645
|
-
version: '>=0.
|
|
627
|
+
version: '>=0.86.0',
|
|
646
628
|
|
|
647
629
|
'ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModule.mm': (
|
|
648
630
|
contents
|
|
@@ -652,20 +634,14 @@ export const addCustomSourceTransformer = resolveAssetSource.addCustomSourceTran
|
|
|
652
634
|
` @try {
|
|
653
635
|
[inv invokeWithTarget:strongModule];
|
|
654
636
|
} @catch (NSException *exception) {
|
|
655
|
-
|
|
637
|
+
// Void methods are always async, re-throw instead of converting to
|
|
638
|
+
// JSError, same as the async branch in performMethodInvocation.
|
|
639
|
+
@throw exception;
|
|
656
640
|
} @finally {`,
|
|
657
641
|
` @try {
|
|
658
642
|
[inv invokeWithTarget:strongModule];
|
|
659
643
|
} @catch (NSException *exception) {
|
|
660
|
-
|
|
661
|
-
// sync void methods run on the JS thread — safe to build the JS error.
|
|
662
|
-
throw convertNSExceptionToJSError(runtime, exception, std::string{moduleName}, methodNameStr);
|
|
663
|
-
} else {
|
|
664
|
-
// async void methods run on the TurboModule method queue; building a JS
|
|
665
|
-
// error here touches the Hermes runtime off the JS thread and corrupts
|
|
666
|
-
// the VM (SIGSEGV). there is no JS caller to receive it, so log instead.
|
|
667
|
-
RCTLogError(@"Exception in %s.%s: %@", moduleName, methodNameStr.c_str(), exception.reason);
|
|
668
|
-
}
|
|
644
|
+
RCTLogError(@"Exception in %s.%s: %@", moduleName, methodNameStr.c_str(), exception.reason);
|
|
669
645
|
} @finally {`
|
|
670
646
|
)
|
|
671
647
|
},
|
|
@@ -11,6 +11,12 @@ import { readFile } from 'node:fs/promises'
|
|
|
11
11
|
import { createDevMiddleware } from '@react-native/dev-middleware'
|
|
12
12
|
import { createNativeDevEngine } from '../utils/createNativeDevEngine'
|
|
13
13
|
import { getBoundPort } from '../utils/getBoundPort'
|
|
14
|
+
import {
|
|
15
|
+
getNativeFramePlatform,
|
|
16
|
+
isNativeBundleFrame,
|
|
17
|
+
symbolicateNativeStack,
|
|
18
|
+
type NativeStackFrame,
|
|
19
|
+
} from '../utils/symbolicateNativeStack'
|
|
14
20
|
|
|
15
21
|
type ClientMessage = {
|
|
16
22
|
type: 'client-log'
|
|
@@ -77,6 +83,7 @@ export function createReactNativeDevServerPlugin(
|
|
|
77
83
|
> = {}
|
|
78
84
|
const devEngineCreating: Record<string, Promise<unknown> | null> = {}
|
|
79
85
|
const warnedProdBundleRequest = new Set<string>()
|
|
86
|
+
const pendingReloadPlatforms = new Set<'ios' | 'android'>()
|
|
80
87
|
|
|
81
88
|
const devToolsSocketEndpoints = ['/inspector/device', '/inspector/debug']
|
|
82
89
|
const reactNativeDevToolsUrl = `http://${host}:${getBoundPort(server)}`
|
|
@@ -204,8 +211,11 @@ export function createReactNativeDevServerPlugin(
|
|
|
204
211
|
return
|
|
205
212
|
}
|
|
206
213
|
addConnectedNativeClient()
|
|
214
|
+
if (pendingReloadPlatforms.has(socket.vxrnPlatform)) {
|
|
215
|
+
socket.send(JSON.stringify({ type: 'hmr:reload' }))
|
|
216
|
+
}
|
|
207
217
|
|
|
208
|
-
socket.on('message',
|
|
218
|
+
socket.on('message', (message) => {
|
|
209
219
|
const value = message.toString()
|
|
210
220
|
if (value === 'ping') {
|
|
211
221
|
socket.send('pong')
|
|
@@ -219,8 +229,9 @@ export function createReactNativeDevServerPlugin(
|
|
|
219
229
|
return
|
|
220
230
|
}
|
|
221
231
|
if (update.type === 'hmr:invalidate') {
|
|
222
|
-
|
|
223
|
-
|
|
232
|
+
// the runtime could not accept an update, so restart it. the bundle
|
|
233
|
+
// it fetches on the way back settles the engine's pending work
|
|
234
|
+
// first, so it lands on the current output.
|
|
224
235
|
socket.send(JSON.stringify({ type: 'hmr:reload' }))
|
|
225
236
|
}
|
|
226
237
|
})
|
|
@@ -267,6 +278,55 @@ export function createReactNativeDevServerPlugin(
|
|
|
267
278
|
})
|
|
268
279
|
})
|
|
269
280
|
|
|
281
|
+
const getDevEngine = async (platform: 'ios' | 'android') => {
|
|
282
|
+
while (!devEngines[platform]) {
|
|
283
|
+
// prevent duplicate creation from concurrent requests
|
|
284
|
+
if (!devEngineCreating[platform]) {
|
|
285
|
+
const creating = (async () => {
|
|
286
|
+
try {
|
|
287
|
+
console.info(`[vxrn] creating rolldown DevEngine for ${platform}...`)
|
|
288
|
+
devEngines[platform] = await createNativeDevEngine({
|
|
289
|
+
root,
|
|
290
|
+
port: getBoundPort(server),
|
|
291
|
+
host: typeof host === 'string' ? host : 'localhost',
|
|
292
|
+
platform,
|
|
293
|
+
serverUrl: `http://${typeof host === 'string' && host !== '0.0.0.0' ? host : 'localhost'}:${getBoundPort(server)}`,
|
|
294
|
+
onHmrUpdate: (update) => {
|
|
295
|
+
const msg = JSON.stringify(update)
|
|
296
|
+
// an update with no clientId is for every client on the
|
|
297
|
+
// platform: an error, or a reload after a full rebuild
|
|
298
|
+
const target = 'clientId' in update ? update.clientId : undefined
|
|
299
|
+
if (update.type === 'hmr:reload' && !target) {
|
|
300
|
+
// keep this pending until the rebuilt bundle is served.
|
|
301
|
+
// a previous app instance can leave a live socket while
|
|
302
|
+
// its replacement is still mounting and connecting.
|
|
303
|
+
pendingReloadPlatforms.add(platform)
|
|
304
|
+
}
|
|
305
|
+
hmrWSS.clients.forEach((client) => {
|
|
306
|
+
const nativeClient = client as NativeHmrSocket
|
|
307
|
+
if (
|
|
308
|
+
nativeClient.readyState === 1 &&
|
|
309
|
+
nativeClient.vxrnPlatform === platform &&
|
|
310
|
+
(!target || nativeClient.vxrnClientId === target)
|
|
311
|
+
) {
|
|
312
|
+
client.send(msg)
|
|
313
|
+
}
|
|
314
|
+
})
|
|
315
|
+
},
|
|
316
|
+
})
|
|
317
|
+
console.info(`[vxrn] rolldown DevEngine ready for ${platform}`)
|
|
318
|
+
} finally {
|
|
319
|
+
// clear so a failed creation can be retried.
|
|
320
|
+
devEngineCreating[platform] = null
|
|
321
|
+
}
|
|
322
|
+
})()
|
|
323
|
+
devEngineCreating[platform] = creating
|
|
324
|
+
}
|
|
325
|
+
await devEngineCreating[platform]
|
|
326
|
+
}
|
|
327
|
+
return devEngines[platform]!
|
|
328
|
+
}
|
|
329
|
+
|
|
270
330
|
// React Native bundle handler
|
|
271
331
|
const handleRNBundle: Connect.NextHandleFunction = async (req, res) => {
|
|
272
332
|
const url = new URL(req.url!, `http://${req.headers.host}`)
|
|
@@ -281,7 +341,10 @@ export function createReactNativeDevServerPlugin(
|
|
|
281
341
|
// here would mean a second engine per platform. production native bundles
|
|
282
342
|
// come from the build command instead. say so rather than quietly serving
|
|
283
343
|
// dev bytes to something that asked for production ones.
|
|
284
|
-
if (
|
|
344
|
+
if (
|
|
345
|
+
url.searchParams.get('dev') === 'false' &&
|
|
346
|
+
!warnedProdBundleRequest.has(platform)
|
|
347
|
+
) {
|
|
285
348
|
warnedProdBundleRequest.add(platform)
|
|
286
349
|
console.warn(
|
|
287
350
|
`[vxrn] ${platform} bundle requested with dev=false, but the dev server only builds dev bundles. Serving a dev bundle. Use the build command for a production one.`
|
|
@@ -289,50 +352,20 @@ export function createReactNativeDevServerPlugin(
|
|
|
289
352
|
}
|
|
290
353
|
|
|
291
354
|
try {
|
|
292
|
-
const bundle = await (
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
const msg = JSON.stringify(update)
|
|
307
|
-
hmrWSS.clients.forEach((client) => {
|
|
308
|
-
const nativeClient = client as NativeHmrSocket
|
|
309
|
-
if (
|
|
310
|
-
nativeClient.readyState === 1 &&
|
|
311
|
-
nativeClient.vxrnPlatform === platform &&
|
|
312
|
-
(update.type === 'hmr:error' ||
|
|
313
|
-
nativeClient.vxrnClientId === update.clientId)
|
|
314
|
-
) {
|
|
315
|
-
client.send(msg)
|
|
316
|
-
}
|
|
317
|
-
})
|
|
318
|
-
},
|
|
319
|
-
})
|
|
320
|
-
console.info(`[vxrn] rolldown DevEngine ready for ${platform}`)
|
|
321
|
-
} catch (err) {
|
|
322
|
-
// clear so next request retries instead of permanently failing
|
|
323
|
-
devEngineCreating[platform] = null
|
|
324
|
-
throw err
|
|
325
|
-
}
|
|
326
|
-
})()
|
|
327
|
-
}
|
|
328
|
-
await devEngineCreating[platform]
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
return await devEngines[platform]!.getBundle().then((r) => r.code)
|
|
332
|
-
})()
|
|
333
|
-
|
|
334
|
-
res.writeHead(200, { 'Content-Type': 'text/javascript' })
|
|
335
|
-
res.end(bundle)
|
|
355
|
+
const bundle = await (await getDevEngine(platform)).getBundle()
|
|
356
|
+
// a client that connects after this response starts from the current
|
|
357
|
+
// route map and does not need the pending reload intended for the
|
|
358
|
+
// previous runtime.
|
|
359
|
+
res.once('finish', () => pendingReloadPlatforms.delete(platform))
|
|
360
|
+
|
|
361
|
+
// a DevSettings reload requests this exact URL again. native URL
|
|
362
|
+
// loading may otherwise reuse the first response and restart the app
|
|
363
|
+
// on a route map from before a file was added or removed.
|
|
364
|
+
res.writeHead(200, {
|
|
365
|
+
'Cache-Control': 'no-store',
|
|
366
|
+
'Content-Type': 'text/javascript',
|
|
367
|
+
})
|
|
368
|
+
res.end(bundle.code)
|
|
336
369
|
} catch (err) {
|
|
337
370
|
console.error(` Error building React Native bundle`)
|
|
338
371
|
console.error(err)
|
|
@@ -353,10 +386,49 @@ export function createReactNativeDevServerPlugin(
|
|
|
353
386
|
}
|
|
354
387
|
})
|
|
355
388
|
|
|
356
|
-
//
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
389
|
+
// resolve a device stack back to authored files through the dev bundle's
|
|
390
|
+
// source map. react native posts here from LogBox and from console traces.
|
|
391
|
+
server.middlewares.use('/symbolicate', async (req, res) => {
|
|
392
|
+
try {
|
|
393
|
+
const chunks: Buffer[] = []
|
|
394
|
+
for await (const chunk of req) chunks.push(chunk as Buffer)
|
|
395
|
+
const body: { stack?: NativeStackFrame[] } = JSON.parse(
|
|
396
|
+
Buffer.concat(chunks).toString('utf8')
|
|
397
|
+
)
|
|
398
|
+
const stack = body.stack
|
|
399
|
+
if (!Array.isArray(stack)) {
|
|
400
|
+
throw new Error('expected a `stack` array in the request body')
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
const bundleFrame = stack.find((frame) => isNativeBundleFrame(frame.file))
|
|
404
|
+
if (!bundleFrame) {
|
|
405
|
+
// nothing in this stack came from the bundle, so there is nothing to
|
|
406
|
+
// resolve. hand it straight back rather than inventing frames.
|
|
407
|
+
res.writeHead(200, { 'Content-Type': 'application/json' })
|
|
408
|
+
res.end(JSON.stringify({ stack, codeFrame: null }))
|
|
409
|
+
return
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
const framePlatform = getNativeFramePlatform(bundleFrame.file)
|
|
413
|
+
const platform = validPlatforms[framePlatform ?? '']
|
|
414
|
+
if (!platform) {
|
|
415
|
+
throw new Error(
|
|
416
|
+
`could not tell which platform "${bundleFrame.file}" was bundled for`
|
|
417
|
+
)
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
const bundle = await (await getDevEngine(platform)).getBundle()
|
|
421
|
+
res.writeHead(200, { 'Content-Type': 'application/json' })
|
|
422
|
+
res.end(JSON.stringify(symbolicateNativeStack(stack, bundle.map)))
|
|
423
|
+
} catch (err) {
|
|
424
|
+
console.error(`[vxrn] symbolicate failed`, err)
|
|
425
|
+
res.writeHead(500, { 'Content-Type': 'application/json' })
|
|
426
|
+
res.end(
|
|
427
|
+
JSON.stringify({
|
|
428
|
+
error: err instanceof Error ? err.message : String(err),
|
|
429
|
+
})
|
|
430
|
+
)
|
|
431
|
+
}
|
|
360
432
|
})
|
|
361
433
|
},
|
|
362
434
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import path from 'node:path'
|
|
2
1
|
import FSExtra from 'fs-extra'
|
|
2
|
+
import { clearTransformCache } from '@vxrn/compiler'
|
|
3
3
|
import {
|
|
4
4
|
bundle as metroBundle,
|
|
5
5
|
type BundleCommandArgs,
|
|
@@ -18,15 +18,29 @@ export async function buildBundle(
|
|
|
18
18
|
const {
|
|
19
19
|
platform, // Android is not supported yet.
|
|
20
20
|
dev,
|
|
21
|
-
// bundleEncoding, // Not supported, we are using utf8.
|
|
22
|
-
// sourcemapUseAbsolutePath,
|
|
23
|
-
// unstableTransformProfile,
|
|
24
|
-
// resetCache, // Currently we are not using cache for production builds.
|
|
25
|
-
// readGlobalCache,
|
|
26
|
-
// entryFile, // Not supported. With VxRN, we are using a static entry file.
|
|
27
21
|
bundleOutput,
|
|
22
|
+
// React Native's `--bundle-encoding` names a Node Buffer encoding.
|
|
23
|
+
bundleEncoding = 'utf8',
|
|
28
24
|
assetsDest,
|
|
29
|
-
// minify
|
|
25
|
+
// `--minify` overrides React Native's default of "minify unless this is a
|
|
26
|
+
// dev bundle". undefined means the flag was not passed.
|
|
27
|
+
minify,
|
|
28
|
+
// `--reset-cache` drops the compiler's transform cache, the only cache a
|
|
29
|
+
// rolldown production build reads.
|
|
30
|
+
resetCache,
|
|
31
|
+
// `--entry-file` names the React Native root module. One generates its own
|
|
32
|
+
// native entry from the route tree instead, and __vxrnNativeEntryFile is
|
|
33
|
+
// the supported way to point the build somewhere else.
|
|
34
|
+
// `--read-global-cache` / `--max-workers` / `--config` / `--transformer` /
|
|
35
|
+
// `--resolver-option` configure Metro's global cache, worker pool and
|
|
36
|
+
// resolver. A rolldown production build has none of them: it transforms
|
|
37
|
+
// from source in-process. React Native's own bundle command ignores
|
|
38
|
+
// `--read-global-cache` too.
|
|
39
|
+
// `--unstable-transform-profile` picks a JS engine to target. This pipeline
|
|
40
|
+
// always downlevels for Hermes, which is what every profile it accepts
|
|
41
|
+
// ('default', 'hermes', 'hermes-canary') can run.
|
|
42
|
+
// `--sourcemap-use-absolute-path`, `--sourcemap-sources-root`,
|
|
43
|
+
// `--asset-catalog-dest` and `--indexed-ram-bundle` are not implemented.
|
|
30
44
|
} = args
|
|
31
45
|
|
|
32
46
|
const { root } = ctx
|
|
@@ -53,6 +67,10 @@ export async function buildBundle(
|
|
|
53
67
|
process.env.NODE_ENV = 'production'
|
|
54
68
|
}
|
|
55
69
|
|
|
70
|
+
if (resetCache) {
|
|
71
|
+
clearTransformCache()
|
|
72
|
+
}
|
|
73
|
+
|
|
56
74
|
console.info(`[vxrn] building native bundle for ${platform}...`)
|
|
57
75
|
const nativeEntryFile = (globalThis as { __vxrnNativeEntryFile?: unknown })
|
|
58
76
|
.__vxrnNativeEntryFile
|
|
@@ -67,6 +85,7 @@ export async function buildBundle(
|
|
|
67
85
|
serverUrl: process.env.ONE_SERVER_URL,
|
|
68
86
|
assetsDest,
|
|
69
87
|
sourcemap: !!args.sourcemapOutput,
|
|
88
|
+
minify,
|
|
70
89
|
})
|
|
71
90
|
const builtBundle = result.code
|
|
72
91
|
|
|
@@ -76,7 +95,7 @@ export async function buildBundle(
|
|
|
76
95
|
}
|
|
77
96
|
|
|
78
97
|
console.info(`Writing bundle to ${bundleOutput}...`)
|
|
79
|
-
FSExtra.writeFileSync(bundleOutput, builtBundle, { encoding:
|
|
98
|
+
FSExtra.writeFileSync(bundleOutput, builtBundle, { encoding: bundleEncoding })
|
|
80
99
|
console.info('Done.')
|
|
81
100
|
|
|
82
101
|
// Prevent the process not getting exited for some unknown reason.
|