vxrn 2.10.0 → 2.11.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/config/getBaseVitePlugins.mjs +3 -1
- package/dist/config/getBaseVitePlugins.mjs.map +1 -1
- package/dist/config/getBaseVitePlugins.native.js +3 -1
- package/dist/config/getBaseVitePlugins.native.js.map +1 -1
- package/dist/constants.mjs +23 -1
- package/dist/constants.mjs.map +1 -1
- package/dist/constants.native.js +23 -1
- package/dist/constants.native.js.map +1 -1
- package/dist/exports/build.mjs +6 -4
- package/dist/exports/build.mjs.map +1 -1
- package/dist/exports/build.native.js +6 -4
- package/dist/exports/build.native.js.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.mjs +2 -1
- package/dist/index.native.js +2 -1
- package/dist/plugins/reactNativeDevServer.mjs +7 -5
- package/dist/plugins/reactNativeDevServer.mjs.map +1 -1
- package/dist/plugins/reactNativeDevServer.native.js +8 -5
- package/dist/plugins/reactNativeDevServer.native.js.map +1 -1
- package/dist/rn-commands/bundle/buildBundle.mjs +9 -0
- package/dist/rn-commands/bundle/buildBundle.mjs.map +1 -1
- package/dist/rn-commands/bundle/buildBundle.native.js +9 -0
- package/dist/rn-commands/bundle/buildBundle.native.js.map +1 -1
- package/dist/runtime/native-prelude.mjs +7 -1
- package/dist/runtime/native-prelude.mjs.map +1 -1
- package/dist/runtime/native-prelude.native.js +7 -1
- package/dist/runtime/native-prelude.native.js.map +1 -1
- package/dist/utils/compileNativeDevBytecode.mjs +13 -3
- package/dist/utils/compileNativeDevBytecode.mjs.map +1 -1
- package/dist/utils/compileNativeDevBytecode.native.js +13 -3
- package/dist/utils/compileNativeDevBytecode.native.js.map +1 -1
- package/dist/utils/createNativeDevEngine.mjs +375 -173
- package/dist/utils/createNativeDevEngine.mjs.map +1 -1
- package/dist/utils/createNativeDevEngine.native.js +429 -193
- package/dist/utils/createNativeDevEngine.native.js.map +1 -1
- package/dist/utils/environmentUtils.mjs +1 -1
- package/dist/utils/environmentUtils.mjs.map +1 -1
- package/dist/utils/environmentUtils.native.js +1 -1
- package/dist/utils/environmentUtils.native.js.map +1 -1
- package/dist/utils/scanDepsToOptimize.mjs +37 -15
- package/dist/utils/scanDepsToOptimize.mjs.map +1 -1
- package/dist/utils/scanDepsToOptimize.native.js +45 -15
- package/dist/utils/scanDepsToOptimize.native.js.map +1 -1
- package/package.json +13 -13
- package/src/config/getBaseVitePlugins.ts +2 -0
- package/src/constants.ts +24 -0
- package/src/exports/build.lifecycle.test.ts +2 -1
- package/src/exports/build.ts +11 -4
- package/src/index.ts +1 -0
- package/src/plugins/reactNativeCommonJsPlugin.ts +1 -1
- package/src/plugins/reactNativeDevServer.ts +19 -8
- package/src/rn-commands/bundle/buildBundle.ts +13 -1
- package/src/runtime/native-prelude.ts +7 -1
- package/src/types.ts +1 -1
- package/src/utils/compileNativeDevBytecode.test.ts +8 -8
- package/src/utils/compileNativeDevBytecode.ts +22 -2
- package/src/utils/createNativeDevEngine.test.ts +52 -25
- package/src/utils/createNativeDevEngine.ts +567 -254
- package/src/utils/environmentUtils.ts +6 -1
- package/src/utils/scanDepsToOptimize.test.ts +46 -2
- package/src/utils/scanDepsToOptimize.ts +78 -18
- package/types/config/getBaseVitePlugins.d.ts.map +1 -1
- package/types/constants.d.ts +2 -0
- package/types/constants.d.ts.map +1 -1
- package/types/exports/build.d.ts.map +1 -1
- package/types/index.d.ts +1 -0
- package/types/index.d.ts.map +1 -1
- package/types/plugins/reactNativeDevServer.d.ts.map +1 -1
- package/types/rn-commands/bundle/buildBundle.d.ts.map +1 -1
- package/types/runtime/native-prelude.d.ts.map +1 -1
- package/types/types.d.ts +1 -1
- package/types/types.d.ts.map +1 -1
- package/types/utils/compileNativeDevBytecode.d.ts +2 -1
- package/types/utils/compileNativeDevBytecode.d.ts.map +1 -1
- package/types/utils/createNativeDevEngine.d.ts +11 -5
- package/types/utils/createNativeDevEngine.d.ts.map +1 -1
- package/types/utils/environmentUtils.d.ts.map +1 -1
- package/types/utils/scanDepsToOptimize.d.ts.map +1 -1
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import { createHash } from 'node:crypto'
|
|
10
|
-
import { basename, dirname, extname, join, relative, resolve } from 'node:path'
|
|
10
|
+
import { basename, dirname, extname, join, relative, resolve, sep } from 'node:path'
|
|
11
11
|
import {
|
|
12
12
|
copyFileSync,
|
|
13
13
|
existsSync,
|
|
@@ -28,7 +28,13 @@ import type { DevEngine } from 'rolldown/experimental'
|
|
|
28
28
|
import { loadEnv as loadViteEnv, normalizePath } from 'vite'
|
|
29
29
|
import { shouldStripFlow, transformHermesAsync } from '@vxrn/compiler'
|
|
30
30
|
import { resolvePath } from '@vxrn/resolve'
|
|
31
|
+
import {
|
|
32
|
+
desktopReactNativeFile,
|
|
33
|
+
requireOnePackageRoot,
|
|
34
|
+
type DesktopPlatform,
|
|
35
|
+
} from '@vxrn/utils/desktopReactNativeResolve'
|
|
31
36
|
import { nativeAppDefines } from '@vxrn/utils/nativeAppManifest'
|
|
37
|
+
import type { NativePlatform } from '@vxrn/utils/platformEnv'
|
|
32
38
|
import { withExpoPublicEnvAliases } from '@vxrn/utils/publicEnv'
|
|
33
39
|
import { DEFAULT_ASSET_EXTS } from '../constants/defaults'
|
|
34
40
|
import { getNativePrelude } from '../runtime/native-prelude'
|
|
@@ -64,15 +70,17 @@ export function getHermesSWCIncludes(dev: boolean): string[] {
|
|
|
64
70
|
|
|
65
71
|
export interface NativePluginContext {
|
|
66
72
|
root: string
|
|
67
|
-
platform:
|
|
73
|
+
platform: NativePlatform
|
|
68
74
|
dev: boolean
|
|
75
|
+
command?: 'serve' | 'build'
|
|
76
|
+
assetsDest?: string
|
|
69
77
|
}
|
|
70
78
|
|
|
71
79
|
interface NativeDevEngineOptions {
|
|
72
80
|
root: string
|
|
73
81
|
port: number
|
|
74
82
|
host?: string
|
|
75
|
-
platform:
|
|
83
|
+
platform: NativePlatform
|
|
76
84
|
serverUrl?: string
|
|
77
85
|
plugins?: Plugin[]
|
|
78
86
|
onHmrUpdate?: (update: NativeHmrUpdate) => void
|
|
@@ -109,19 +117,34 @@ interface NativeDevEngineResult {
|
|
|
109
117
|
close: () => Promise<void>
|
|
110
118
|
}
|
|
111
119
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
120
|
+
const NATIVE_PLATFORM_EXTENSIONS: Record<NativePlatform, string[]> = {
|
|
121
|
+
ios: ['.ios.tsx', '.ios.ts', '.ios.jsx', '.ios.js'],
|
|
122
|
+
android: ['.android.tsx', '.android.ts', '.android.jsx', '.android.js'],
|
|
123
|
+
macos: ['.macos.tsx', '.macos.ts', '.macos.jsx', '.macos.js'],
|
|
124
|
+
linux: ['.linux.tsx', '.linux.ts', '.linux.jsx', '.linux.js'],
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const NATIVE_SHARED_EXTENSIONS = [
|
|
128
|
+
'.native.tsx',
|
|
129
|
+
'.native.ts',
|
|
130
|
+
'.native.jsx',
|
|
131
|
+
'.native.js',
|
|
132
|
+
]
|
|
133
|
+
|
|
134
|
+
const PLAIN_EXTENSIONS = ['.tsx', '.ts', '.jsx', '.js', '.mjs', '.cjs', '.json']
|
|
135
|
+
|
|
136
|
+
// shared resolve extensions for native builds. macos and linux do not list .ios:
|
|
137
|
+
// that fallback is only for files inside the react-native package.
|
|
138
|
+
function getResolveExtensions(platform: NativePlatform): string[] {
|
|
139
|
+
return [
|
|
140
|
+
...NATIVE_PLATFORM_EXTENSIONS[platform],
|
|
141
|
+
...NATIVE_SHARED_EXTENSIONS,
|
|
142
|
+
...PLAIN_EXTENSIONS,
|
|
143
|
+
]
|
|
121
144
|
}
|
|
122
145
|
|
|
123
146
|
// shared rolldown resolve config for native builds
|
|
124
|
-
function getNativeResolveConfig(platform:
|
|
147
|
+
function getNativeResolveConfig(platform: NativePlatform) {
|
|
125
148
|
return {
|
|
126
149
|
extensions: getResolveExtensions(platform),
|
|
127
150
|
// Rolldown supplies `import`, `require`, and `default` contextually. Adding
|
|
@@ -138,7 +161,7 @@ function getNativeResolveConfig(platform: 'ios' | 'android') {
|
|
|
138
161
|
|
|
139
162
|
// shared rolldown transform config for native builds
|
|
140
163
|
export function getNativeTransformConfig(
|
|
141
|
-
platform:
|
|
164
|
+
platform: NativePlatform,
|
|
142
165
|
dev: boolean,
|
|
143
166
|
root: string
|
|
144
167
|
) {
|
|
@@ -254,6 +277,128 @@ export function getNativeTransformConfig(
|
|
|
254
277
|
}
|
|
255
278
|
}
|
|
256
279
|
|
|
280
|
+
function desktopReactNativeResolvePlugin(root: string, platform: DesktopPlatform): Plugin {
|
|
281
|
+
const oneRoot = requireOnePackageRoot(root, platform)
|
|
282
|
+
return {
|
|
283
|
+
name: `vxrn:${platform}-react-native-resolve`,
|
|
284
|
+
resolveId(source, importer) {
|
|
285
|
+
if (!importer) return
|
|
286
|
+
const file = desktopReactNativeFile(importer, source, oneRoot, platform)
|
|
287
|
+
if (file) return file
|
|
288
|
+
},
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// third-party packages often ship a component as .ios and .android only.
|
|
293
|
+
// desktop platforms must fail at bundle time when no supported implementation exists.
|
|
294
|
+
const PLATFORM_VARIANT_EXTS = [
|
|
295
|
+
'.ios.tsx',
|
|
296
|
+
'.ios.ts',
|
|
297
|
+
'.ios.jsx',
|
|
298
|
+
'.ios.js',
|
|
299
|
+
'.android.tsx',
|
|
300
|
+
'.android.ts',
|
|
301
|
+
'.android.jsx',
|
|
302
|
+
'.android.js',
|
|
303
|
+
'.web.tsx',
|
|
304
|
+
'.web.ts',
|
|
305
|
+
'.web.jsx',
|
|
306
|
+
'.web.js',
|
|
307
|
+
]
|
|
308
|
+
|
|
309
|
+
function desktopNormalFile(
|
|
310
|
+
target: string,
|
|
311
|
+
source: string,
|
|
312
|
+
platform: DesktopPlatform
|
|
313
|
+
): boolean {
|
|
314
|
+
if (/\.(?:tsx|ts|jsx|js|mjs|cjs|json)$/.test(source)) return existsSync(target)
|
|
315
|
+
const extensions = getResolveExtensions(platform)
|
|
316
|
+
for (const ext of extensions) {
|
|
317
|
+
if (existsSync(target + ext)) return true
|
|
318
|
+
if (existsSync(join(target, 'index' + ext))) return true
|
|
319
|
+
}
|
|
320
|
+
return false
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function desktopVariantSource(target: string): string | null {
|
|
324
|
+
for (const ext of PLATFORM_VARIANT_EXTS) {
|
|
325
|
+
const file = target + ext
|
|
326
|
+
if (existsSync(file)) return file
|
|
327
|
+
}
|
|
328
|
+
return null
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
function desktopPackageImportLabel(variantFile: string): string {
|
|
332
|
+
let dir = dirname(variantFile)
|
|
333
|
+
for (let i = 0; i < 8; i++) {
|
|
334
|
+
const manifest = join(dir, 'package.json')
|
|
335
|
+
if (existsSync(manifest)) {
|
|
336
|
+
try {
|
|
337
|
+
const name = JSON.parse(readFileSync(manifest, 'utf8')).name
|
|
338
|
+
if (typeof name === 'string' && name.length > 0) {
|
|
339
|
+
const rel = relative(dir, variantFile)
|
|
340
|
+
.split('\\')
|
|
341
|
+
.join('/')
|
|
342
|
+
.replace(/\.(?:ios|android|web)\.(?:tsx|ts|jsx|js)$/, '')
|
|
343
|
+
return `${name}/${rel}`
|
|
344
|
+
}
|
|
345
|
+
} catch {
|
|
346
|
+
// keep walking to a readable manifest
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
const parent = dirname(dir)
|
|
350
|
+
if (parent === dir) break
|
|
351
|
+
dir = parent
|
|
352
|
+
}
|
|
353
|
+
return variantFile
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
function desktopUnsupportedImportPlugin(platform: DesktopPlatform): Plugin {
|
|
357
|
+
const label = platform === 'macos' ? 'macOS' : 'Linux'
|
|
358
|
+
return {
|
|
359
|
+
name: `vxrn:${platform}-unsupported-import`,
|
|
360
|
+
onLog(_level, log) {
|
|
361
|
+
// rolldown otherwise warns and emits an external ESM import. native
|
|
362
|
+
// hosts evaluate scripts and cannot load that import at runtime.
|
|
363
|
+
if (log.code === 'UNRESOLVED_IMPORT') {
|
|
364
|
+
this.error({
|
|
365
|
+
...log,
|
|
366
|
+
message: `[vxrn] ${label} bundle cannot resolve an import: ${log.message}`,
|
|
367
|
+
})
|
|
368
|
+
}
|
|
369
|
+
},
|
|
370
|
+
resolveId(source, importer) {
|
|
371
|
+
if (!importer || !source.startsWith('.') || source.includes('\0')) return
|
|
372
|
+
if (source.includes('?') || source.includes('#')) return
|
|
373
|
+
const target = resolve(dirname(importer), source)
|
|
374
|
+
if (desktopNormalFile(target, source, platform)) return
|
|
375
|
+
const variant = desktopVariantSource(target)
|
|
376
|
+
if (!variant) return
|
|
377
|
+
this.error(
|
|
378
|
+
`[vxrn] ${desktopPackageImportLabel(variant)} has no ${label} implementation: cannot resolve import "${source}" from "${importer}". Add a .${platform}, .native or plain file.`
|
|
379
|
+
)
|
|
380
|
+
},
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// built packages import siblings by their full name (./one.native.js), so
|
|
385
|
+
// .macos and .linux resolution never sees them. when the build emitted a
|
|
386
|
+
// .<platform>.native.js beside the target, that platform loads that file
|
|
387
|
+
// instead. the variant itself still imports the shared file.
|
|
388
|
+
function desktopDistVariantPlugin(platform: DesktopPlatform): Plugin {
|
|
389
|
+
return {
|
|
390
|
+
name: `vxrn:${platform}-dist-variant`,
|
|
391
|
+
resolveId(source, importer) {
|
|
392
|
+
if (!importer || !source.startsWith('.') || !source.endsWith('.native.js')) return
|
|
393
|
+
if (source.endsWith(`.${platform}.native.js`)) return
|
|
394
|
+
const target = resolve(dirname(importer), source)
|
|
395
|
+
const variant = target.replace(/\.native\.js$/, `.${platform}.native.js`)
|
|
396
|
+
if (variant === importer || !existsSync(variant)) return
|
|
397
|
+
return variant
|
|
398
|
+
},
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
257
402
|
// shared plugins used by both dev and prod native builds
|
|
258
403
|
function getNativePlugins(
|
|
259
404
|
root: string,
|
|
@@ -264,12 +409,30 @@ function getNativePlugins(
|
|
|
264
409
|
onAsset?: (asset: NativeAssetData) => void,
|
|
265
410
|
sourceMaps = false,
|
|
266
411
|
userPlugins: Plugin[] = [],
|
|
267
|
-
minify = false
|
|
412
|
+
minify = false,
|
|
413
|
+
command: 'serve' | 'build' = 'serve'
|
|
268
414
|
): Plugin[] {
|
|
269
|
-
const context: NativePluginContext = { root, platform, dev }
|
|
270
|
-
|
|
415
|
+
const context: NativePluginContext = { root, platform, dev, command, assetsDest }
|
|
416
|
+
const desktopPlugins =
|
|
417
|
+
platform === 'macos' || platform === 'linux'
|
|
418
|
+
? [
|
|
419
|
+
desktopReactNativeResolvePlugin(root, platform),
|
|
420
|
+
desktopDistVariantPlugin(platform),
|
|
421
|
+
desktopUnsupportedImportPlugin(platform),
|
|
422
|
+
]
|
|
423
|
+
: []
|
|
424
|
+
// evaluate One's factory once, in its existing position after desktop setup.
|
|
425
|
+
const globalPlugins = globalThis.__vxrnAddNativePlugins?.(platform) || []
|
|
426
|
+
const trace: NativeCompilerTrace | undefined =
|
|
427
|
+
dev && command === 'serve'
|
|
428
|
+
? [...globalPlugins, ...userPlugins].find(
|
|
429
|
+
(plugin) => plugin.api?.vxrnNativeCompilerTrace
|
|
430
|
+
)?.api.vxrnNativeCompilerTrace
|
|
431
|
+
: undefined
|
|
432
|
+
const plugins = [
|
|
433
|
+
...desktopPlugins,
|
|
271
434
|
// plugins provided by One (clientTreeShakePlugin for loader removal, etc.)
|
|
272
|
-
...
|
|
435
|
+
...globalPlugins,
|
|
273
436
|
// block .server.* and _middleware.* files from entering the native bundle
|
|
274
437
|
serverFileExclusionPlugin(),
|
|
275
438
|
// guard server-only / client-only / web-only / native-only imports
|
|
@@ -300,7 +463,7 @@ function getNativePlugins(
|
|
|
300
463
|
rnCodegenPlugin({ projectRoot: root }),
|
|
301
464
|
// lower Flow before worklets and react compiler parse the source with oxc.
|
|
302
465
|
flowStripPlugin(),
|
|
303
|
-
vxrnCompilerPlugin(platform, dev, root, sourceMaps),
|
|
466
|
+
vxrnCompilerPlugin(platform, dev, root, sourceMaps, trace),
|
|
304
467
|
mixedNativeModulePlugin(sourceMaps),
|
|
305
468
|
// guard undefined native methods in NativeAnimatedHelper
|
|
306
469
|
nativeAnimatedGuardPlugin(),
|
|
@@ -317,6 +480,83 @@ function getNativePlugins(
|
|
|
317
480
|
].map((plugin: Plugin) =>
|
|
318
481
|
plugin.api?.vxrnNative ? plugin.api.vxrnNative(context) : plugin
|
|
319
482
|
)
|
|
483
|
+
if (!trace) return plugins
|
|
484
|
+
|
|
485
|
+
// temporary fixture-only diagnostics. keep the existing pre hooks in place;
|
|
486
|
+
// observe their returned promises without replacing them or awaiting again.
|
|
487
|
+
const start = (id: string, stage: string) => {
|
|
488
|
+
try {
|
|
489
|
+
const record = trace(id)
|
|
490
|
+
if (!record) return
|
|
491
|
+
const mark = (status: 'start' | 'end' | 'error') => {
|
|
492
|
+
try {
|
|
493
|
+
record(stage, status)
|
|
494
|
+
} catch {
|
|
495
|
+
// capture must not affect the hook.
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
mark('start')
|
|
499
|
+
return mark
|
|
500
|
+
} catch {
|
|
501
|
+
// capture must not affect the hook.
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
const traced = plugins.map((plugin, index) => {
|
|
505
|
+
if (
|
|
506
|
+
plugin.name !== 'one:use-dom-native' &&
|
|
507
|
+
plugin.name !== 'vxrn:rn-codegen' &&
|
|
508
|
+
plugin.name !== 'one:background-computation:native' &&
|
|
509
|
+
plugin.name !== 'one-client-tree-shake'
|
|
510
|
+
)
|
|
511
|
+
return plugin
|
|
512
|
+
const hook = plugin.transform
|
|
513
|
+
if (!hook || typeof hook === 'function' || hook.order !== 'pre') return plugin
|
|
514
|
+
return {
|
|
515
|
+
...plugin,
|
|
516
|
+
transform: {
|
|
517
|
+
...hook,
|
|
518
|
+
handler(this: any, ...args: any[]) {
|
|
519
|
+
const mark = start(args[1], `pre:${index}:${plugin.name}`)
|
|
520
|
+
if (!mark) return hook.handler.apply(this, args)
|
|
521
|
+
try {
|
|
522
|
+
const result = hook.handler.apply(this, args)
|
|
523
|
+
if (result instanceof Promise) {
|
|
524
|
+
// use the native observer, never a user-supplied .then override.
|
|
525
|
+
// its rejection handler consumes only the observer's branch.
|
|
526
|
+
try {
|
|
527
|
+
Promise.prototype.then.call(
|
|
528
|
+
result,
|
|
529
|
+
() => mark('end'),
|
|
530
|
+
() => mark('error')
|
|
531
|
+
)
|
|
532
|
+
} catch {
|
|
533
|
+
// an observer failure must not replace the original promise.
|
|
534
|
+
}
|
|
535
|
+
} else {
|
|
536
|
+
mark('end')
|
|
537
|
+
}
|
|
538
|
+
return result
|
|
539
|
+
} catch (error) {
|
|
540
|
+
mark('error')
|
|
541
|
+
throw error
|
|
542
|
+
}
|
|
543
|
+
},
|
|
544
|
+
},
|
|
545
|
+
}
|
|
546
|
+
})
|
|
547
|
+
return [
|
|
548
|
+
{
|
|
549
|
+
name: 'test-native-hmr-trace-first-pre',
|
|
550
|
+
transform: {
|
|
551
|
+
order: 'pre',
|
|
552
|
+
handler(_code: string, id: string) {
|
|
553
|
+
start(id, 'pre:first')?.('end')
|
|
554
|
+
return null
|
|
555
|
+
},
|
|
556
|
+
},
|
|
557
|
+
},
|
|
558
|
+
...traced,
|
|
559
|
+
]
|
|
320
560
|
}
|
|
321
561
|
|
|
322
562
|
// shared output options for native builds. `minify: false` keeps rolldown's
|
|
@@ -455,13 +695,11 @@ function interopNodeModeSpan(
|
|
|
455
695
|
function isInteropHelperCallee(callee: InteropSyntaxNode): boolean {
|
|
456
696
|
if (callee.type === 'Identifier') {
|
|
457
697
|
return (
|
|
458
|
-
typeof callee['name'] === 'string' &&
|
|
459
|
-
NATIVE_INTEROP_HELPER.test(callee['name'])
|
|
698
|
+
typeof callee['name'] === 'string' && NATIVE_INTEROP_HELPER.test(callee['name'])
|
|
460
699
|
)
|
|
461
700
|
}
|
|
462
701
|
if (
|
|
463
|
-
(callee.type === 'MemberExpression' ||
|
|
464
|
-
callee.type === 'StaticMemberExpression') &&
|
|
702
|
+
(callee.type === 'MemberExpression' || callee.type === 'StaticMemberExpression') &&
|
|
465
703
|
callee['computed'] !== true
|
|
466
704
|
) {
|
|
467
705
|
const property = callee['property']
|
|
@@ -597,6 +835,15 @@ export function postProcessNativeBundle(code: string): string {
|
|
|
597
835
|
return code
|
|
598
836
|
}
|
|
599
837
|
|
|
838
|
+
// the dev prelude ends with this line, which rolldown emits directly before its
|
|
839
|
+
// runtime. the module scope and the runtime transform locate themselves by text
|
|
840
|
+
// vxrn writes because rolldown's module comments change between releases
|
|
841
|
+
// (1.2.15 dropped `//#region`/`//#endregion`).
|
|
842
|
+
export const NATIVE_MODULE_SCOPE_MARKER = '// vxrn: native module scope'
|
|
843
|
+
// the last statement of getHmrRuntimeSource, which closes rolldown's runtime
|
|
844
|
+
export const NATIVE_DEV_RUNTIME_END =
|
|
845
|
+
'globalThis.__rolldown_runtime__ = new ReactNativeDevRuntime();'
|
|
846
|
+
|
|
600
847
|
/**
|
|
601
848
|
* wrap dev module declarations in a function scope. script-level declarations
|
|
602
849
|
* create global properties that can block react native's lazy polyfills.
|
|
@@ -607,10 +854,12 @@ export function postProcessNativeBundle(code: string): string {
|
|
|
607
854
|
* inside this closure so it can reach module bindings and runtime helpers.
|
|
608
855
|
*/
|
|
609
856
|
export function wrapNativeBundleModuleScope(code: string): string {
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
857
|
+
const idx = code.indexOf(NATIVE_MODULE_SCOPE_MARKER)
|
|
858
|
+
if (idx === -1) {
|
|
859
|
+
throw new Error(
|
|
860
|
+
'[vxrn] the dev bundle lost the end of its prelude, so its modules would run at script scope'
|
|
861
|
+
)
|
|
862
|
+
}
|
|
614
863
|
|
|
615
864
|
// the opener shares the marker's line (the rest of that line is a comment) so
|
|
616
865
|
// the wrap costs no lines, and the closer only appends past the last mapped
|
|
@@ -619,22 +868,22 @@ export function wrapNativeBundleModuleScope(code: string): string {
|
|
|
619
868
|
}
|
|
620
869
|
|
|
621
870
|
/**
|
|
622
|
-
* Downlevel class fields in the rolldown runtime for Hermes compatibility.
|
|
871
|
+
* Downlevel class fields in the rolldown dev runtime for Hermes compatibility.
|
|
623
872
|
* The runtime (\0rolldown/runtime.js) is injected directly into the output,
|
|
624
873
|
* bypassing hermesCompatSWCPlugin. We extract just that section (~5KB) and
|
|
625
|
-
* transform it rather than re-parsing the entire 6MB bundle.
|
|
874
|
+
* transform it rather than re-parsing the entire 6MB bundle. Only the dev
|
|
875
|
+
* runtime has classes; the production runtime is plain helper functions.
|
|
626
876
|
*/
|
|
627
877
|
async function downlevelClassFieldsInBundle(code: string): Promise<string> {
|
|
628
|
-
const
|
|
629
|
-
const
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
const endIdx = code.indexOf(endMarker, startIdx)
|
|
635
|
-
if (endIdx === -1) return code
|
|
878
|
+
const markerIdx = code.indexOf(NATIVE_MODULE_SCOPE_MARKER)
|
|
879
|
+
const endIdx = markerIdx === -1 ? -1 : code.indexOf(NATIVE_DEV_RUNTIME_END, markerIdx)
|
|
880
|
+
if (endIdx === -1) {
|
|
881
|
+
throw new Error('[vxrn] could not locate the rolldown runtime in the dev bundle')
|
|
882
|
+
}
|
|
636
883
|
|
|
637
|
-
|
|
884
|
+
// the marker line stays outside the transform so the scope wrapper finds it
|
|
885
|
+
const startIdx = code.indexOf('\n', markerIdx) + 1
|
|
886
|
+
const runtimeEnd = endIdx + NATIVE_DEV_RUNTIME_END.length
|
|
638
887
|
const runtimeSection = code.slice(startIdx, runtimeEnd)
|
|
639
888
|
const originalNewlines = runtimeSection.match(/\n/g)?.length ?? 0
|
|
640
889
|
|
|
@@ -648,16 +897,11 @@ async function downlevelClassFieldsInBundle(code: string): Promise<string> {
|
|
|
648
897
|
if (result.errors?.length) {
|
|
649
898
|
throw new Error(result.errors.map((e) => e.message).join('\n'))
|
|
650
899
|
}
|
|
651
|
-
const
|
|
652
|
-
// SWC minification strips comments, but the dev bundle scope wrapper uses
|
|
653
|
-
// this generated-runtime marker as its structural boundary.
|
|
654
|
-
const transformed = transformedCode.includes(startMarker)
|
|
655
|
-
? transformedCode
|
|
656
|
-
: `${startMarker}\n${transformedCode}`
|
|
900
|
+
const transformed = result.code.trimEnd()
|
|
657
901
|
const transformedNewlines = transformed.match(/\n/g)?.length ?? 0
|
|
658
902
|
if (transformedNewlines > originalNewlines) {
|
|
659
903
|
throw new Error(
|
|
660
|
-
'[vxrn] Hermes runtime transform added lines and would invalidate the
|
|
904
|
+
'[vxrn] Hermes runtime transform added lines and would invalidate the dev source map'
|
|
661
905
|
)
|
|
662
906
|
}
|
|
663
907
|
const linePreservingRuntime =
|
|
@@ -669,14 +913,13 @@ async function downlevelClassFieldsInBundle(code: string): Promise<string> {
|
|
|
669
913
|
* Run the native post-processing passes inside the bundle, where rolldown still
|
|
670
914
|
* hands them unminified output.
|
|
671
915
|
*
|
|
672
|
-
* Rolldown minifies a chunk *after* plugin `renderChunk` hooks, and
|
|
673
|
-
*
|
|
674
|
-
*
|
|
675
|
-
*
|
|
676
|
-
*
|
|
677
|
-
*
|
|
678
|
-
*
|
|
679
|
-
* minified and an unminified bundle the same bundle.
|
|
916
|
+
* Rolldown minifies a chunk *after* plugin `renderChunk` hooks, and the passes
|
|
917
|
+
* read the shape rolldown generates: `normalizeNativeCommonJSInterop` matches
|
|
918
|
+
* `__toESM(require_x(), 1)` by name. Minification mangles that, so running the
|
|
919
|
+
* passes on the finished chunk would silently stop applying them as soon as
|
|
920
|
+
* `--minify` is on — a production bundle whose CommonJS interop differs from
|
|
921
|
+
* the dev one. This hook is what keeps a minified and an unminified bundle the
|
|
922
|
+
* same bundle.
|
|
680
923
|
*
|
|
681
924
|
* `map: null` records what the passes guarantee: they replace syntax in place
|
|
682
925
|
* without moving a line, so rolldown's own map still describes the result.
|
|
@@ -684,8 +927,8 @@ async function downlevelClassFieldsInBundle(code: string): Promise<string> {
|
|
|
684
927
|
function nativeBundlePostProcessPlugin(): Plugin {
|
|
685
928
|
return {
|
|
686
929
|
name: 'vxrn:native-post-process',
|
|
687
|
-
|
|
688
|
-
const processed =
|
|
930
|
+
renderChunk(code) {
|
|
931
|
+
const processed = postProcessNativeBundle(code)
|
|
689
932
|
assertBundleLinesPreserved(code, processed, 'production')
|
|
690
933
|
return { code: processed, map: null }
|
|
691
934
|
},
|
|
@@ -789,7 +1032,7 @@ export async function createNativeDevEngine(
|
|
|
789
1032
|
// generated once per full bundle build, not per edit: rebuildStrategy is
|
|
790
1033
|
// 'never', so Fast Refresh patches never re-enter this path. a dev bundle
|
|
791
1034
|
// is never minified.
|
|
792
|
-
...getNativeOutputOptions(prelude
|
|
1035
|
+
...getNativeOutputOptions(`${prelude}\n${NATIVE_MODULE_SCOPE_MARKER}`, true, false),
|
|
793
1036
|
// open the HMR socket with RN's WebSocket module (not the global, which is
|
|
794
1037
|
// only polyfilled once setup-env has run) and hand it to the runtime,
|
|
795
1038
|
// which owns every message it carries. handed over immediately rather than
|
|
@@ -952,6 +1195,12 @@ try {
|
|
|
952
1195
|
// skipped by the per-file SWC plugin) so old Hermes can parse them
|
|
953
1196
|
code = await downlevelClassFieldsInBundle(code)
|
|
954
1197
|
assertBundleLinesPreserved(chunk.code, code, 'dev')
|
|
1198
|
+
|
|
1199
|
+
// wrap module code in a function scope so top-level `var`s (e.g. RN
|
|
1200
|
+
// fetch.js's `Headers`/`Request`) don't leak as non-configurable
|
|
1201
|
+
// globals and break RN's polyfillGlobal (dev-only redbox). see fn doc.
|
|
1202
|
+
// it only appends past the last mapped line, so it needs no assertion.
|
|
1203
|
+
code = wrapNativeBundleModuleScope(code)
|
|
955
1204
|
} catch (error) {
|
|
956
1205
|
// a post-processing failure is a build failure: without a bundle to
|
|
957
1206
|
// serve, record it and wake the waiting request with its message
|
|
@@ -973,12 +1222,6 @@ try {
|
|
|
973
1222
|
return
|
|
974
1223
|
}
|
|
975
1224
|
|
|
976
|
-
// wrap module code in a function scope so top-level `var`s (e.g. RN
|
|
977
|
-
// fetch.js's `Headers`/`Request`) don't leak as non-configurable
|
|
978
|
-
// globals and break RN's polyfillGlobal (dev-only redbox). see fn doc.
|
|
979
|
-
// it only appends past the last mapped line, so it needs no assertion.
|
|
980
|
-
code = wrapNativeBundleModuleScope(code)
|
|
981
|
-
|
|
982
1225
|
if (!chunk.map) {
|
|
983
1226
|
throw new Error(
|
|
984
1227
|
'[vxrn] rolldown produced no source map for the dev bundle, so /symbolicate cannot resolve a frame'
|
|
@@ -1092,7 +1335,7 @@ try {
|
|
|
1092
1335
|
|
|
1093
1336
|
interface NativeBuildOptions {
|
|
1094
1337
|
root: string
|
|
1095
|
-
platform:
|
|
1338
|
+
platform: NativePlatform
|
|
1096
1339
|
dev?: boolean
|
|
1097
1340
|
serverUrl?: string
|
|
1098
1341
|
entryFile?: string
|
|
@@ -1162,7 +1405,8 @@ export async function buildNativeBundle(
|
|
|
1162
1405
|
undefined,
|
|
1163
1406
|
sourcemap,
|
|
1164
1407
|
userPlugins,
|
|
1165
|
-
minify
|
|
1408
|
+
minify,
|
|
1409
|
+
'build'
|
|
1166
1410
|
),
|
|
1167
1411
|
// last, so it sees what every other plugin produced
|
|
1168
1412
|
nativeBundlePostProcessPlugin(),
|
|
@@ -1217,7 +1461,7 @@ function nativeVirtualEntryPlugin(
|
|
|
1217
1461
|
root: string,
|
|
1218
1462
|
opts: {
|
|
1219
1463
|
dev?: boolean
|
|
1220
|
-
platform:
|
|
1464
|
+
platform: NativePlatform
|
|
1221
1465
|
dynamicRoutes?: boolean
|
|
1222
1466
|
}
|
|
1223
1467
|
): { plugin: Plugin; routes: NativeRouteRegistry } {
|
|
@@ -1236,8 +1480,21 @@ function nativeVirtualEntryPlugin(
|
|
|
1236
1480
|
const setupFileImport = (() => {
|
|
1237
1481
|
const sf = entryConfig.setupFile
|
|
1238
1482
|
if (!sf) return ''
|
|
1239
|
-
//
|
|
1240
|
-
const
|
|
1483
|
+
// string and native apply to every native platform. macos and linux do not borrow the ios file.
|
|
1484
|
+
const perPlatform = sf as {
|
|
1485
|
+
ios?: string
|
|
1486
|
+
android?: string
|
|
1487
|
+
macos?: string
|
|
1488
|
+
linux?: string
|
|
1489
|
+
}
|
|
1490
|
+
const file =
|
|
1491
|
+
typeof sf === 'string'
|
|
1492
|
+
? sf
|
|
1493
|
+
: 'native' in sf
|
|
1494
|
+
? sf.native
|
|
1495
|
+
: opts.platform === 'macos' || opts.platform === 'linux'
|
|
1496
|
+
? perPlatform[opts.platform]
|
|
1497
|
+
: sf.ios
|
|
1241
1498
|
if (!file) return ''
|
|
1242
1499
|
// file:// URL is the canonical specifier; bare Windows absolute path is not
|
|
1243
1500
|
const resolved = pathToFileURL(resolve(root, file)).href
|
|
@@ -1568,7 +1825,7 @@ export function hmrClientNoopPlugin(): Plugin {
|
|
|
1568
1825
|
}
|
|
1569
1826
|
}
|
|
1570
1827
|
|
|
1571
|
-
function nativeIdentityAliasPlugin(platform:
|
|
1828
|
+
function nativeIdentityAliasPlugin(platform: NativePlatform): Plugin {
|
|
1572
1829
|
const packages = new Map<
|
|
1573
1830
|
string,
|
|
1574
1831
|
{ root: string; aliases: Record<string, unknown> } | null
|
|
@@ -1622,10 +1879,7 @@ function nativeIdentityAliasPlugin(platform: 'ios' | 'android'): Plugin {
|
|
|
1622
1879
|
// `react-native` would give native its web build. the browser fallback is for
|
|
1623
1880
|
// packages without a native target: where a package's exports name both,
|
|
1624
1881
|
// resolve it as metro does, without `browser`.
|
|
1625
|
-
function nativeExportsPrecedencePlugin(
|
|
1626
|
-
root: string,
|
|
1627
|
-
platform: 'ios' | 'android'
|
|
1628
|
-
): Plugin {
|
|
1882
|
+
function nativeExportsPrecedencePlugin(root: string, platform: NativePlatform): Plugin {
|
|
1629
1883
|
const manifests = new Map<string, { name?: string; exports?: unknown } | null>()
|
|
1630
1884
|
const readManifest = (directory: string) => {
|
|
1631
1885
|
if (!manifests.has(directory)) {
|
|
@@ -1677,7 +1931,7 @@ function nativeExportsPrecedencePlugin(
|
|
|
1677
1931
|
}
|
|
1678
1932
|
}
|
|
1679
1933
|
|
|
1680
|
-
function nativeDeclarationAliasPlugin(root: string, platform:
|
|
1934
|
+
function nativeDeclarationAliasPlugin(root: string, platform: NativePlatform): Plugin {
|
|
1681
1935
|
const resolvers = new Map<string, import('rolldown/experimental').ResolverFactory>()
|
|
1682
1936
|
return {
|
|
1683
1937
|
name: 'vxrn:declaration-alias',
|
|
@@ -1841,6 +2095,12 @@ function cssStubPlugin(): Plugin {
|
|
|
1841
2095
|
}
|
|
1842
2096
|
}
|
|
1843
2097
|
|
|
2098
|
+
// temporary diagnostic hook consumed by the opt-in HMR test trace. callbacks are
|
|
2099
|
+
// synchronous and best-effort; they must never alter compiler results or errors.
|
|
2100
|
+
export type NativeCompilerTrace = (
|
|
2101
|
+
id: string
|
|
2102
|
+
) => ((stage: string, status: 'start' | 'end' | 'error') => void) | undefined
|
|
2103
|
+
|
|
1844
2104
|
/**
|
|
1845
2105
|
* pipe files through @vxrn/compiler's babel transforms.
|
|
1846
2106
|
* handles reanimated worklet compilation, async generator downleveling,
|
|
@@ -1850,7 +2110,8 @@ export function vxrnCompilerPlugin(
|
|
|
1850
2110
|
platform: string,
|
|
1851
2111
|
dev: boolean,
|
|
1852
2112
|
projectRoot = process.cwd(),
|
|
1853
|
-
sourceMaps = false
|
|
2113
|
+
sourceMaps = false,
|
|
2114
|
+
trace?: NativeCompilerTrace
|
|
1854
2115
|
): Plugin {
|
|
1855
2116
|
let compiler: typeof import('@vxrn/compiler') | null = null
|
|
1856
2117
|
|
|
@@ -1867,148 +2128,151 @@ export function vxrnCompilerPlugin(
|
|
|
1867
2128
|
if (!/\.[cm]?[jt]sx?$/.test(id)) return
|
|
1868
2129
|
if (id.includes('\0') || id.includes('virtual:')) return
|
|
1869
2130
|
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
2131
|
+
let active = 'transform'
|
|
2132
|
+
let failed = false
|
|
2133
|
+
let mark: ((stage: string, status: 'start' | 'end' | 'error') => void) | undefined
|
|
2134
|
+
if (trace) {
|
|
2135
|
+
try {
|
|
2136
|
+
const record = trace(id)
|
|
2137
|
+
if (record) {
|
|
2138
|
+
mark = (stage, status) => {
|
|
2139
|
+
active = status === 'start' ? stage : 'transform'
|
|
2140
|
+
try {
|
|
2141
|
+
record(stage, status)
|
|
2142
|
+
} catch {
|
|
2143
|
+
// capture must not affect compilation.
|
|
2144
|
+
}
|
|
2145
|
+
}
|
|
2146
|
+
}
|
|
2147
|
+
} catch {
|
|
2148
|
+
// capture must not affect compilation.
|
|
2149
|
+
}
|
|
1881
2150
|
}
|
|
2151
|
+
mark?.('transform', 'start')
|
|
2152
|
+
try {
|
|
2153
|
+
const needsRefresh = isRefreshCandidate(id)
|
|
1882
2154
|
|
|
1883
|
-
|
|
2155
|
+
if (!compiler) {
|
|
2156
|
+
mark?.('compiler-import', 'start')
|
|
2157
|
+
compiler = await import('@vxrn/compiler')
|
|
2158
|
+
mark?.('compiler-import', 'end')
|
|
2159
|
+
}
|
|
1884
2160
|
|
|
1885
|
-
|
|
1886
|
-
|
|
2161
|
+
const props = {
|
|
2162
|
+
id,
|
|
2163
|
+
code,
|
|
2164
|
+
projectRoot,
|
|
2165
|
+
development: dev,
|
|
2166
|
+
// the compiler's native gate is ios or android. macos and linux use the
|
|
2167
|
+
// ios transforms. module resolution stays .<platform> and does not follow .ios.
|
|
2168
|
+
environment: (platform === 'android' ? 'android' : 'ios') as 'ios' | 'android',
|
|
2169
|
+
reactForRNVersion: '19' as const,
|
|
2170
|
+
}
|
|
1887
2171
|
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
compiler.isNativeWorkletsEnabled() &&
|
|
1904
|
-
(Boolean(workletEntry) || compiler.shouldTransformWorklets({ id, code }))
|
|
1905
|
-
const compilerPluginIndex =
|
|
1906
|
-
babelOptions?.plugins?.findIndex(
|
|
1907
|
-
(x) => Array.isArray(x) && x[0] === 'babel-plugin-react-compiler'
|
|
1908
|
-
) ?? -1
|
|
1909
|
-
|
|
1910
|
-
if (compilerPluginIndex !== -1) {
|
|
1911
|
-
// preserve automatic worklet candidates before react compiler hoists them.
|
|
1912
|
-
if (useWorklets) {
|
|
1913
|
-
const prepared = compiler.prepareWorkletsForReactCompiler(
|
|
1914
|
-
id,
|
|
1915
|
-
curCode,
|
|
1916
|
-
sourceMaps
|
|
2172
|
+
mark?.('options', 'start')
|
|
2173
|
+
let babelOptions = compiler.getBabelOptions(props)
|
|
2174
|
+
mark?.('options', 'end')
|
|
2175
|
+
|
|
2176
|
+
let curCode = code
|
|
2177
|
+
const intermediateMaps: any[] = []
|
|
2178
|
+
|
|
2179
|
+
const isWorkletPlugin = (entry: any) => {
|
|
2180
|
+
if (!entry) return false
|
|
2181
|
+
const name =
|
|
2182
|
+
typeof entry === 'string' ? entry : Array.isArray(entry) ? entry[0] : null
|
|
2183
|
+
return (
|
|
2184
|
+
typeof name === 'string' &&
|
|
2185
|
+
(name.includes('react-native-worklets') ||
|
|
2186
|
+
name.includes('react-native-reanimated'))
|
|
1917
2187
|
)
|
|
1918
|
-
if (prepared) {
|
|
1919
|
-
curCode = prepared.code
|
|
1920
|
-
if (prepared.map) intermediateMaps.push(prepared.map)
|
|
1921
|
-
}
|
|
1922
2188
|
}
|
|
1923
|
-
const
|
|
1924
|
-
const
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
if (
|
|
1938
|
-
|
|
1939
|
-
|
|
2189
|
+
const workletEntry = babelOptions?.plugins?.find(isWorkletPlugin)
|
|
2190
|
+
const workletPluginOptions = Array.isArray(workletEntry)
|
|
2191
|
+
? workletEntry[1]
|
|
2192
|
+
: undefined
|
|
2193
|
+
const useWorklets =
|
|
2194
|
+
compiler.isNativeWorkletsEnabled() &&
|
|
2195
|
+
(Boolean(workletEntry) || compiler.shouldTransformWorklets({ id, code }))
|
|
2196
|
+
const compilerPluginIndex =
|
|
2197
|
+
babelOptions?.plugins?.findIndex(
|
|
2198
|
+
(x) => Array.isArray(x) && x[0] === 'babel-plugin-react-compiler'
|
|
2199
|
+
) ?? -1
|
|
2200
|
+
|
|
2201
|
+
if (compilerPluginIndex !== -1) {
|
|
2202
|
+
// preserve automatic worklet candidates before react compiler hoists them.
|
|
2203
|
+
if (useWorklets) {
|
|
2204
|
+
mark?.('worklets-prepare', 'start')
|
|
2205
|
+
const prepared = compiler.prepareWorkletsForReactCompiler(
|
|
2206
|
+
id,
|
|
2207
|
+
curCode,
|
|
2208
|
+
sourceMaps
|
|
2209
|
+
)
|
|
2210
|
+
mark?.('worklets-prepare', 'end')
|
|
2211
|
+
if (prepared) {
|
|
2212
|
+
curCode = prepared.code
|
|
2213
|
+
if (prepared.map) intermediateMaps.push(prepared.map)
|
|
2214
|
+
}
|
|
1940
2215
|
}
|
|
1941
|
-
babelOptions!.plugins
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
2216
|
+
const compilerPluginConfig = babelOptions!.plugins![
|
|
2217
|
+
compilerPluginIndex
|
|
2218
|
+
] as any[]
|
|
2219
|
+
const compilerPluginOptions = compilerPluginConfig[1] || {}
|
|
2220
|
+
const useBabelCompiler =
|
|
2221
|
+
compilerPluginOptions.compiler === 'babel' ||
|
|
2222
|
+
process.env.ONE_REACT_COMPILER === 'babel' ||
|
|
2223
|
+
process.env.VXRN_REACT_COMPILER === 'babel'
|
|
2224
|
+
|
|
2225
|
+
if (!useBabelCompiler) {
|
|
2226
|
+
mark?.('oxc-react-compiler', 'start')
|
|
2227
|
+
const compilerOut = await compiler.transformOxcReactCompiler(
|
|
2228
|
+
id,
|
|
2229
|
+
curCode,
|
|
2230
|
+
compilerPluginOptions,
|
|
2231
|
+
sourceMaps
|
|
2232
|
+
)
|
|
2233
|
+
mark?.('oxc-react-compiler', 'end')
|
|
2234
|
+
if (compilerOut?.code) {
|
|
2235
|
+
curCode = compilerOut.code
|
|
2236
|
+
if (sourceMaps && compilerOut.map) intermediateMaps.push(compilerOut.map)
|
|
2237
|
+
}
|
|
2238
|
+
babelOptions!.plugins!.splice(compilerPluginIndex, 1)
|
|
2239
|
+
if (
|
|
2240
|
+
babelOptions!.plugins!.length === 0 &&
|
|
2241
|
+
!babelOptions!.configFile &&
|
|
2242
|
+
!babelOptions!.babelrc
|
|
2243
|
+
) {
|
|
2244
|
+
babelOptions = null
|
|
2245
|
+
}
|
|
1948
2246
|
}
|
|
1949
2247
|
}
|
|
1950
|
-
}
|
|
1951
2248
|
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
if (
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
babelOptions.plugins = babelOptions.plugins.filter(
|
|
1963
|
-
(entry) => !isWorkletPlugin(entry)
|
|
1964
|
-
)
|
|
1965
|
-
if (
|
|
1966
|
-
babelOptions.plugins.length === 0 &&
|
|
1967
|
-
!babelOptions.configFile &&
|
|
1968
|
-
!babelOptions.babelrc
|
|
1969
|
-
) {
|
|
1970
|
-
babelOptions = null
|
|
2249
|
+
if (useWorklets) {
|
|
2250
|
+
mark?.('worklets', 'start')
|
|
2251
|
+
const workletOut = await compiler.transformWorklets(id, curCode, sourceMaps, {
|
|
2252
|
+
projectRoot,
|
|
2253
|
+
...workletPluginOptions,
|
|
2254
|
+
})
|
|
2255
|
+
mark?.('worklets', 'end')
|
|
2256
|
+
if (workletOut?.code) {
|
|
2257
|
+
curCode = workletOut.code
|
|
2258
|
+
if (sourceMaps && workletOut.map) intermediateMaps.push(workletOut.map)
|
|
1971
2259
|
}
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
const remapping = (await import('@jridgewell/remapping')).default
|
|
1983
|
-
finalMap = remapping(intermediateMaps.slice().reverse(), () => null)
|
|
2260
|
+
if (babelOptions?.plugins) {
|
|
2261
|
+
babelOptions.plugins = babelOptions.plugins.filter(
|
|
2262
|
+
(entry) => !isWorkletPlugin(entry)
|
|
2263
|
+
)
|
|
2264
|
+
if (
|
|
2265
|
+
babelOptions.plugins.length === 0 &&
|
|
2266
|
+
!babelOptions.configFile &&
|
|
2267
|
+
!babelOptions.babelrc
|
|
2268
|
+
) {
|
|
2269
|
+
babelOptions = null
|
|
1984
2270
|
}
|
|
1985
2271
|
}
|
|
1986
|
-
return { code: curCode, map: finalMap }
|
|
1987
|
-
}
|
|
1988
|
-
return
|
|
1989
|
-
}
|
|
1990
|
-
|
|
1991
|
-
if (sourceMaps && babelOptions) {
|
|
1992
|
-
// Individual stage maps are collected in intermediateMaps and composed
|
|
1993
|
-
// once via remapping([wrapMap, refreshMap, ...intermediateMaps.reverse()]).
|
|
1994
|
-
// Do not pass intermediateMaps as inputSourceMap to Babel, which would
|
|
1995
|
-
// cause Babel to pre-compose and remapping to double-compose the compiler map.
|
|
1996
|
-
babelOptions = {
|
|
1997
|
-
...babelOptions,
|
|
1998
|
-
sourceMaps: true,
|
|
1999
|
-
sourceFileName: id,
|
|
2000
2272
|
}
|
|
2001
|
-
delete (babelOptions as any).inputSourceMap
|
|
2002
|
-
}
|
|
2003
2273
|
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
if (result?.code) {
|
|
2007
|
-
curCode = result.code
|
|
2008
|
-
if (sourceMaps && result.map) {
|
|
2009
|
-
intermediateMaps.push(result.map)
|
|
2010
|
-
}
|
|
2011
|
-
if (!needsRefresh) {
|
|
2274
|
+
if (!babelOptions && !needsRefresh) {
|
|
2275
|
+
if (curCode !== code) {
|
|
2012
2276
|
let finalMap: any = undefined
|
|
2013
2277
|
if (sourceMaps && intermediateMaps.length > 0) {
|
|
2014
2278
|
if (intermediateMaps.length === 1) {
|
|
@@ -2020,40 +2284,82 @@ export function vxrnCompilerPlugin(
|
|
|
2020
2284
|
}
|
|
2021
2285
|
return { code: curCode, map: finalMap }
|
|
2022
2286
|
}
|
|
2287
|
+
return
|
|
2023
2288
|
}
|
|
2024
|
-
}
|
|
2025
2289
|
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
refresh: {
|
|
2039
|
-
refreshReg: '__vxrnRefreshReg',
|
|
2040
|
-
refreshSig: '__vxrnRefreshSig',
|
|
2041
|
-
},
|
|
2042
|
-
},
|
|
2043
|
-
reactCompiler: false,
|
|
2044
|
-
sourcemap: sourceMaps,
|
|
2045
|
-
})
|
|
2290
|
+
if (sourceMaps && babelOptions) {
|
|
2291
|
+
// Individual stage maps are collected in intermediateMaps and composed
|
|
2292
|
+
// once via remapping([wrapMap, refreshMap, ...intermediateMaps.reverse()]).
|
|
2293
|
+
// Do not pass intermediateMaps as inputSourceMap to Babel, which would
|
|
2294
|
+
// cause Babel to pre-compose and remapping to double-compose the compiler map.
|
|
2295
|
+
babelOptions = {
|
|
2296
|
+
...babelOptions,
|
|
2297
|
+
sourceMaps: true,
|
|
2298
|
+
sourceFileName: id,
|
|
2299
|
+
}
|
|
2300
|
+
delete (babelOptions as any).inputSourceMap
|
|
2301
|
+
}
|
|
2046
2302
|
|
|
2047
|
-
if (
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2303
|
+
if (babelOptions) {
|
|
2304
|
+
mark?.('babel', 'start')
|
|
2305
|
+
const result = await compiler.transformBabel(id, curCode, babelOptions)
|
|
2306
|
+
mark?.('babel', 'end')
|
|
2307
|
+
if (result?.code) {
|
|
2308
|
+
curCode = result.code
|
|
2309
|
+
if (sourceMaps && result.map) {
|
|
2310
|
+
intermediateMaps.push(result.map)
|
|
2311
|
+
}
|
|
2312
|
+
if (!needsRefresh) {
|
|
2313
|
+
let finalMap: any = undefined
|
|
2314
|
+
if (sourceMaps && intermediateMaps.length > 0) {
|
|
2315
|
+
if (intermediateMaps.length === 1) {
|
|
2316
|
+
finalMap = intermediateMaps[0]
|
|
2317
|
+
} else {
|
|
2318
|
+
const remapping = (await import('@jridgewell/remapping')).default
|
|
2319
|
+
finalMap = remapping(intermediateMaps.slice().reverse(), () => null)
|
|
2320
|
+
}
|
|
2321
|
+
}
|
|
2322
|
+
return { code: curCode, map: finalMap }
|
|
2323
|
+
}
|
|
2324
|
+
}
|
|
2053
2325
|
}
|
|
2054
2326
|
|
|
2055
|
-
|
|
2056
|
-
|
|
2327
|
+
if (needsRefresh) {
|
|
2328
|
+
mark?.('refresh-import', 'start')
|
|
2329
|
+
const { transformSync } = await import('oxc-transform-react')
|
|
2330
|
+
mark?.('refresh-import', 'end')
|
|
2331
|
+
const jsxImportSource =
|
|
2332
|
+
compiler.configuration?.enableNativewind && !id.includes('node_modules')
|
|
2333
|
+
? 'nativewind'
|
|
2334
|
+
: 'react'
|
|
2335
|
+
|
|
2336
|
+
mark?.('refresh', 'start')
|
|
2337
|
+
const res = transformSync(id, curCode, {
|
|
2338
|
+
jsx: {
|
|
2339
|
+
development: dev,
|
|
2340
|
+
runtime: 'automatic',
|
|
2341
|
+
importSource: jsxImportSource,
|
|
2342
|
+
refresh: {
|
|
2343
|
+
refreshReg: '__vxrnRefreshReg',
|
|
2344
|
+
refreshSig: '__vxrnRefreshSig',
|
|
2345
|
+
},
|
|
2346
|
+
},
|
|
2347
|
+
reactCompiler: false,
|
|
2348
|
+
sourcemap: sourceMaps,
|
|
2349
|
+
})
|
|
2350
|
+
|
|
2351
|
+
if (res.fatal) {
|
|
2352
|
+
throw new Error(
|
|
2353
|
+
`[vxrn:compiler] oxc react transform failed on ${id}: ${(res.errors ?? [])
|
|
2354
|
+
.map((e: any) => e.message ?? String(e))
|
|
2355
|
+
.join(', ')}`
|
|
2356
|
+
)
|
|
2357
|
+
}
|
|
2358
|
+
|
|
2359
|
+
mark?.('refresh', 'end')
|
|
2360
|
+
|
|
2361
|
+
const escapedId = id.replace(/\\/g, '\\\\').replace(/"/g, '\\"')
|
|
2362
|
+
const header = `var __prevRefreshReg = globalThis.$RefreshReg$;
|
|
2057
2363
|
var __prevRefreshSig = globalThis.$RefreshSig$;
|
|
2058
2364
|
if (globalThis.__ReactRefresh) {
|
|
2059
2365
|
globalThis.$RefreshReg$ = function(type, id) {
|
|
@@ -2066,7 +2372,7 @@ var __vxrnRefreshReg = globalThis.$RefreshReg$;
|
|
|
2066
2372
|
var __vxrnRefreshSig = globalThis.$RefreshSig$;
|
|
2067
2373
|
|
|
2068
2374
|
`
|
|
2069
|
-
|
|
2375
|
+
const footer = `
|
|
2070
2376
|
|
|
2071
2377
|
globalThis.$RefreshReg$ = __prevRefreshReg;
|
|
2072
2378
|
globalThis.$RefreshSig$ = __prevRefreshSig;
|
|
@@ -2078,29 +2384,36 @@ if (import.meta.hot) {
|
|
|
2078
2384
|
});
|
|
2079
2385
|
}
|
|
2080
2386
|
`
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
if (sourceMaps) {
|
|
2085
|
-
const MagicString = (await import('magic-string')).default
|
|
2086
|
-
const ms = new MagicString(res.code)
|
|
2087
|
-
ms.prepend(header)
|
|
2088
|
-
ms.append(footer)
|
|
2089
|
-
out = ms.toString()
|
|
2090
|
-
const wrapMap = ms.generateMap({ source: id, hires: true })
|
|
2091
|
-
|
|
2092
|
-
const allMaps = [
|
|
2093
|
-
wrapMap,
|
|
2094
|
-
res.map,
|
|
2095
|
-
...intermediateMaps.slice().reverse(),
|
|
2096
|
-
].filter(Boolean)
|
|
2097
|
-
const remapping = (await import('@jridgewell/remapping')).default
|
|
2098
|
-
finalMap = remapping(allMaps, () => null)
|
|
2099
|
-
} else {
|
|
2100
|
-
out = header + res.code + footer
|
|
2101
|
-
}
|
|
2387
|
+
let out: string
|
|
2388
|
+
let finalMap: any = undefined
|
|
2102
2389
|
|
|
2103
|
-
|
|
2390
|
+
if (sourceMaps) {
|
|
2391
|
+
const MagicString = (await import('magic-string')).default
|
|
2392
|
+
const ms = new MagicString(res.code)
|
|
2393
|
+
ms.prepend(header)
|
|
2394
|
+
ms.append(footer)
|
|
2395
|
+
out = ms.toString()
|
|
2396
|
+
const wrapMap = ms.generateMap({ source: id, hires: true })
|
|
2397
|
+
|
|
2398
|
+
const allMaps = [
|
|
2399
|
+
wrapMap,
|
|
2400
|
+
res.map,
|
|
2401
|
+
...intermediateMaps.slice().reverse(),
|
|
2402
|
+
].filter(Boolean)
|
|
2403
|
+
const remapping = (await import('@jridgewell/remapping')).default
|
|
2404
|
+
finalMap = remapping(allMaps, () => null)
|
|
2405
|
+
} else {
|
|
2406
|
+
out = header + res.code + footer
|
|
2407
|
+
}
|
|
2408
|
+
|
|
2409
|
+
return { code: out, map: finalMap }
|
|
2410
|
+
}
|
|
2411
|
+
} catch (error) {
|
|
2412
|
+
failed = true
|
|
2413
|
+
if (active !== 'transform') mark?.(active, 'error')
|
|
2414
|
+
throw error
|
|
2415
|
+
} finally {
|
|
2416
|
+
mark?.('transform', failed ? 'error' : 'end')
|
|
2104
2417
|
}
|
|
2105
2418
|
},
|
|
2106
2419
|
}
|
|
@@ -2755,6 +3068,6 @@ class ReactNativeDevRuntime extends BaseDevRuntime {
|
|
|
2755
3068
|
}
|
|
2756
3069
|
}
|
|
2757
3070
|
|
|
2758
|
-
|
|
3071
|
+
${NATIVE_DEV_RUNTIME_END}
|
|
2759
3072
|
`
|
|
2760
3073
|
}
|