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