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
|
@@ -4,7 +4,8 @@ import { dirname, isAbsolute, join } from 'node:path'
|
|
|
4
4
|
import { statSync } from 'node:fs'
|
|
5
5
|
import { rolldown, type Plugin } from 'rolldown'
|
|
6
6
|
import { parseSync } from 'oxc-parser'
|
|
7
|
-
import {
|
|
7
|
+
import { createBackgroundWorkletModule } from '../backgroundComputation'
|
|
8
|
+
import { prepareBackgroundWorkletModule } from '../backgroundComputationNative'
|
|
8
9
|
import { hermesCompatSWCPlugin, hermesLoopsPlugin } from '../utils/createNativeDevEngine'
|
|
9
10
|
|
|
10
11
|
/**
|
|
@@ -40,7 +41,12 @@ export function workletImportsPlugin(imports: Record<string, readonly string[]>)
|
|
|
40
41
|
const instance: Plugin = {
|
|
41
42
|
name: 'vxrn:worklet-imports:bundle',
|
|
42
43
|
async resolveId(source, importer, options) {
|
|
43
|
-
const
|
|
44
|
+
const marker = '?one-background='
|
|
45
|
+
const selected = source.indexOf(marker)
|
|
46
|
+
const names =
|
|
47
|
+
selected < 0
|
|
48
|
+
? selections.get(source)
|
|
49
|
+
: [decodeURIComponent(source.slice(selected + marker.length))]
|
|
44
50
|
if (!names) return
|
|
45
51
|
if (!names.length)
|
|
46
52
|
throw new Error(`[worklet imports] no exports selected for ${source}`)
|
|
@@ -48,7 +54,11 @@ export function workletImportsPlugin(imports: Record<string, readonly string[]>)
|
|
|
48
54
|
throw new Error(`[worklet imports] ${source} requires a static ESM import`)
|
|
49
55
|
}
|
|
50
56
|
rejectRuntimeDependency(source)
|
|
51
|
-
const resolved = await this.resolve(
|
|
57
|
+
const resolved = await this.resolve(
|
|
58
|
+
selected < 0 ? source : source.slice(0, selected),
|
|
59
|
+
importer,
|
|
60
|
+
{ skipSelf: true }
|
|
61
|
+
)
|
|
52
62
|
if (
|
|
53
63
|
!resolved ||
|
|
54
64
|
resolved.external ||
|
|
@@ -173,7 +183,7 @@ export function workletImportsPlugin(imports: Record<string, readonly string[]>)
|
|
|
173
183
|
)
|
|
174
184
|
}
|
|
175
185
|
// also cover closure-bearing loops in helpers emitted by the bundler.
|
|
176
|
-
code =
|
|
186
|
+
code = chunk.code
|
|
177
187
|
for (const file of await bundle.watchFiles) {
|
|
178
188
|
if (isAbsolute(file)) outerContext.addWatchFile(file)
|
|
179
189
|
}
|
|
@@ -181,40 +191,12 @@ export function workletImportsPlugin(imports: Record<string, readonly string[]>)
|
|
|
181
191
|
await bundle.close()
|
|
182
192
|
}
|
|
183
193
|
|
|
184
|
-
const
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
let namespace = cache.get(${loader});
|
|
189
|
-
if (namespace === undefined) {
|
|
190
|
-
${code}
|
|
191
|
-
namespace = __oneWorkletBundle;
|
|
192
|
-
cache.set(${loader}, namespace);
|
|
193
|
-
}
|
|
194
|
-
return namespace;
|
|
195
|
-
}`
|
|
196
|
-
const parsed = parseSync(id, loaderCode, { lang: 'js' })
|
|
197
|
-
if (parsed.errors.length) throw new Error(parsed.errors[0].message)
|
|
198
|
-
const captures = getClosureVariables(
|
|
199
|
-
parsed.program.body[0],
|
|
200
|
-
new Set([...JS_GLOBALS, 'arguments'])
|
|
194
|
+
const moduleSource = createBackgroundWorkletModule(
|
|
195
|
+
code,
|
|
196
|
+
selected.names,
|
|
197
|
+
selected.key
|
|
201
198
|
)
|
|
202
|
-
|
|
203
|
-
throw new Error(
|
|
204
|
-
`[worklet imports] ${selected.entry} requires unsupported runtime globals: ${captures.join(', ')}`
|
|
205
|
-
)
|
|
206
|
-
}
|
|
207
|
-
return `${loaderCode}\n${selected.names
|
|
208
|
-
.map(
|
|
209
|
-
(name, index) => `
|
|
210
|
-
function __oneExport${index}(...args) {
|
|
211
|
-
'worklet';
|
|
212
|
-
return (0, ${loader}().__export${index})(...args);
|
|
213
|
-
}
|
|
214
|
-
export { __oneExport${index} as ${JSON.stringify(name)} };
|
|
215
|
-
`
|
|
216
|
-
)
|
|
217
|
-
.join('\n')}`
|
|
199
|
+
return prepareBackgroundWorkletModule(moduleSource, selected.entry)
|
|
218
200
|
},
|
|
219
201
|
}
|
|
220
202
|
return {
|
|
@@ -93,17 +93,19 @@ export async function buildBundle(
|
|
|
93
93
|
|
|
94
94
|
// write sourcemap if available and requested
|
|
95
95
|
if (result.map && args.sourcemapOutput) {
|
|
96
|
-
FSExtra.
|
|
96
|
+
FSExtra.outputFileSync(args.sourcemapOutput, result.map, { encoding: 'utf8' })
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
console.info(`Writing bundle to ${bundleOutput}...`)
|
|
100
|
-
FSExtra.
|
|
100
|
+
FSExtra.outputFileSync(bundleOutput, builtBundle, { encoding: bundleEncoding })
|
|
101
101
|
console.info('Done.')
|
|
102
102
|
|
|
103
103
|
// Prevent the process not getting exited for some unknown reason.
|
|
104
104
|
// If the process is not exited, it might hang the native build process.
|
|
105
|
+
// unref so a clean build exits as soon as its work is done; the timer only
|
|
106
|
+
// fires when something else still holds the event loop.
|
|
105
107
|
setTimeout(() => {
|
|
106
108
|
console.info('Exiting process to prevent hanging.')
|
|
107
109
|
process.exit()
|
|
108
|
-
}, 30000)
|
|
110
|
+
}, 30000).unref()
|
|
109
111
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { swiftPackageDirOf, swiftPackageDirectories, swiftPackageId } from './utils/swiftPackageId'
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { chmod, mkdir, mkdtemp, rm, symlink, writeFile } from 'node:fs/promises'
|
|
2
|
+
import { createRequire } from 'node:module'
|
|
3
|
+
import { tmpdir } from 'node:os'
|
|
4
|
+
import { dirname, join } from 'node:path'
|
|
5
|
+
import { afterEach, beforeEach, expect, it } from 'vitest'
|
|
6
|
+
import { nativeDevBytecodeCompiler } from './compileNativeDevBytecode'
|
|
7
|
+
|
|
8
|
+
const savedHermescPath = process.env.HERMESC_PATH
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
delete process.env.HERMESC_PATH
|
|
11
|
+
})
|
|
12
|
+
afterEach(() => {
|
|
13
|
+
if (savedHermescPath === undefined) delete process.env.HERMESC_PATH
|
|
14
|
+
else process.env.HERMESC_PATH = savedHermescPath
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
it.each(['prebuilt', 'source'])(
|
|
18
|
+
'shares compiled bytes and retries failures with %s hermes',
|
|
19
|
+
async (mode) => {
|
|
20
|
+
const root = await mkdtemp(join(tmpdir(), 'vxrn-dev-bytecode-test-'))
|
|
21
|
+
const require = createRequire(import.meta.url)
|
|
22
|
+
const compilerRoot = dirname(require.resolve('hermes-compiler/package.json'))
|
|
23
|
+
const host = { darwin: 'osx-bin', linux: 'linux64-bin', win32: 'win64-bin' }[
|
|
24
|
+
process.platform
|
|
25
|
+
]!
|
|
26
|
+
const pod = join(root, 'ios/Pods/hermes-engine/destroot/bin')
|
|
27
|
+
try {
|
|
28
|
+
await mkdir(pod, { recursive: true })
|
|
29
|
+
await symlink(
|
|
30
|
+
join(
|
|
31
|
+
compilerRoot,
|
|
32
|
+
'hermesc',
|
|
33
|
+
host,
|
|
34
|
+
process.platform === 'win32' ? 'hermesc.exe' : 'hermesc'
|
|
35
|
+
),
|
|
36
|
+
join(pod, 'hermesc')
|
|
37
|
+
)
|
|
38
|
+
const specs = join(root, 'ios/Pods/Local Podspecs')
|
|
39
|
+
await mkdir(specs, { recursive: true })
|
|
40
|
+
await writeFile(
|
|
41
|
+
join(specs, 'hermes-engine.podspec.json'),
|
|
42
|
+
JSON.stringify({
|
|
43
|
+
subspecs: [{ name: mode === 'prebuilt' ? 'Pre-built' : 'Hermes' }],
|
|
44
|
+
user_target_xcconfig: {
|
|
45
|
+
HERMES_CLI_PATH: '${PODS_ROOT}/hermes-engine/destroot/bin/hermesc',
|
|
46
|
+
},
|
|
47
|
+
})
|
|
48
|
+
)
|
|
49
|
+
const compiler = nativeDevBytecodeCompiler(root)
|
|
50
|
+
const compile = (bundle: { code: string; map: string }) =>
|
|
51
|
+
compiler(bundle, 'http://localhost:8081/index.bundle?platform=ios')
|
|
52
|
+
const first = { code: 'globalThis.answer = 42;', map: '' }
|
|
53
|
+
const pending = compile(first)
|
|
54
|
+
expect(compile(first)).toBe(pending)
|
|
55
|
+
const bytes = await pending
|
|
56
|
+
expect(bytes.subarray(0, 8).toString('hex')).toBe('c61fbc03c103191f')
|
|
57
|
+
expect(await compile(first)).toBe(bytes)
|
|
58
|
+
const changed = await compile({ code: 'globalThis.answer = 43;', map: '' })
|
|
59
|
+
expect(changed.equals(bytes)).toBe(false)
|
|
60
|
+
const invalid = { code: 'function {', map: '' }
|
|
61
|
+
const failed = compile(invalid)
|
|
62
|
+
await expect(failed).rejects.toThrow()
|
|
63
|
+
const retried = compile(invalid)
|
|
64
|
+
expect(retried).not.toBe(failed)
|
|
65
|
+
await expect(retried).rejects.toThrow()
|
|
66
|
+
} finally {
|
|
67
|
+
await rm(root, { recursive: true, force: true })
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
function hostHermesc(): string {
|
|
73
|
+
const require = createRequire(import.meta.url)
|
|
74
|
+
const compilerRoot = dirname(require.resolve('hermes-compiler/package.json'))
|
|
75
|
+
const host = { darwin: 'osx-bin', linux: 'linux64-bin', win32: 'win64-bin' }[
|
|
76
|
+
process.platform
|
|
77
|
+
]!
|
|
78
|
+
return join(
|
|
79
|
+
compilerRoot,
|
|
80
|
+
'hermesc',
|
|
81
|
+
host,
|
|
82
|
+
process.platform === 'win32' ? 'hermesc.exe' : 'hermesc'
|
|
83
|
+
)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
it('uses HERMESC_PATH without any ios/Pods', async () => {
|
|
87
|
+
const root = await mkdtemp(join(tmpdir(), 'vxrn-dev-bytecode-override-'))
|
|
88
|
+
process.env.HERMESC_PATH = hostHermesc()
|
|
89
|
+
try {
|
|
90
|
+
const compiler = nativeDevBytecodeCompiler(root)
|
|
91
|
+
const bytes = await compiler(
|
|
92
|
+
{ code: 'globalThis.answer = 42;', map: '' },
|
|
93
|
+
'http://localhost:8081/index.bundle?platform=ios'
|
|
94
|
+
)
|
|
95
|
+
expect(bytes.subarray(0, 8).toString('hex')).toBe('c61fbc03c103191f')
|
|
96
|
+
} finally {
|
|
97
|
+
await rm(root, { recursive: true, force: true })
|
|
98
|
+
}
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
it('treats empty HERMESC_PATH as unset', async () => {
|
|
102
|
+
const root = await mkdtemp(join(tmpdir(), 'vxrn-dev-bytecode-empty-'))
|
|
103
|
+
process.env.HERMESC_PATH = ' '
|
|
104
|
+
try {
|
|
105
|
+
expect(() => nativeDevBytecodeCompiler(root)).toThrow(
|
|
106
|
+
/hermes-engine\.podspec\.json/
|
|
107
|
+
)
|
|
108
|
+
expect(() => nativeDevBytecodeCompiler(root)).not.toThrow(/HERMESC_PATH is set/)
|
|
109
|
+
} finally {
|
|
110
|
+
await rm(root, { recursive: true, force: true })
|
|
111
|
+
}
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
it('loudly rejects a HERMESC_PATH that points nowhere', async () => {
|
|
115
|
+
const root = await mkdtemp(join(tmpdir(), 'vxrn-dev-bytecode-invalid-'))
|
|
116
|
+
process.env.HERMESC_PATH = join(root, 'no-such-hermesc')
|
|
117
|
+
try {
|
|
118
|
+
expect(() => nativeDevBytecodeCompiler(root)).toThrow(
|
|
119
|
+
/HERMESC_PATH is set but points nowhere/
|
|
120
|
+
)
|
|
121
|
+
} finally {
|
|
122
|
+
await rm(root, { recursive: true, force: true })
|
|
123
|
+
}
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
// windows has no executable bit, so this case is posix-only.
|
|
127
|
+
it.runIf(process.platform !== 'win32')(
|
|
128
|
+
'loudly rejects a non-executable HERMESC_PATH',
|
|
129
|
+
async () => {
|
|
130
|
+
const root = await mkdtemp(join(tmpdir(), 'vxrn-dev-bytecode-noexec-'))
|
|
131
|
+
const plain = join(root, 'not-executable')
|
|
132
|
+
await writeFile(plain, 'x')
|
|
133
|
+
await chmod(plain, 0o644)
|
|
134
|
+
process.env.HERMESC_PATH = plain
|
|
135
|
+
try {
|
|
136
|
+
expect(() => nativeDevBytecodeCompiler(root)).toThrow(
|
|
137
|
+
/HERMESC_PATH is not executable/
|
|
138
|
+
)
|
|
139
|
+
} finally {
|
|
140
|
+
await rm(root, { recursive: true, force: true })
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
it('loudly rejects a root without ios/Pods when the override is unset', async () => {
|
|
146
|
+
const root = await mkdtemp(join(tmpdir(), 'vxrn-dev-bytecode-nopods-'))
|
|
147
|
+
try {
|
|
148
|
+
expect(() => nativeDevBytecodeCompiler(root)).toThrow(
|
|
149
|
+
/hermes-engine\.podspec\.json.*HERMESC_PATH/s
|
|
150
|
+
)
|
|
151
|
+
} finally {
|
|
152
|
+
await rm(root, { recursive: true, force: true })
|
|
153
|
+
}
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
it('loudly rejects a malformed podspec when the override is unset', async () => {
|
|
157
|
+
const root = await mkdtemp(join(tmpdir(), 'vxrn-dev-bytecode-malformed-'))
|
|
158
|
+
try {
|
|
159
|
+
const specs = join(root, 'ios/Pods/Local Podspecs')
|
|
160
|
+
await mkdir(specs, { recursive: true })
|
|
161
|
+
await writeFile(join(specs, 'hermes-engine.podspec.json'), '{oops')
|
|
162
|
+
expect(() => nativeDevBytecodeCompiler(root)).toThrow(
|
|
163
|
+
/hermes-engine\.podspec\.json/
|
|
164
|
+
)
|
|
165
|
+
} finally {
|
|
166
|
+
await rm(root, { recursive: true, force: true })
|
|
167
|
+
}
|
|
168
|
+
})
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { accessSync, constants, readFileSync, statSync } from 'node:fs'
|
|
2
|
+
import { execFile } from 'node:child_process'
|
|
3
|
+
import { mkdir, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises'
|
|
4
|
+
import { tmpdir } from 'node:os'
|
|
5
|
+
import { dirname, join } from 'node:path'
|
|
6
|
+
import { promisify } from 'node:util'
|
|
7
|
+
import type { NativeDevBundle } from './createNativeDevEngine'
|
|
8
|
+
|
|
9
|
+
const execute = promisify(execFile)
|
|
10
|
+
|
|
11
|
+
// explicit compiler ownership for split layouts, where the dev server root
|
|
12
|
+
// is not the native project (the iOS test job serves tests/test while the
|
|
13
|
+
// container is built from tests/rn-test-container). empty means unset.
|
|
14
|
+
function useHermescOverride(): string | undefined {
|
|
15
|
+
const override = process.env.HERMESC_PATH?.trim()
|
|
16
|
+
if (!override) return undefined
|
|
17
|
+
let stat: ReturnType<typeof statSync> | undefined
|
|
18
|
+
try {
|
|
19
|
+
stat = statSync(override)
|
|
20
|
+
} catch {
|
|
21
|
+
stat = undefined
|
|
22
|
+
}
|
|
23
|
+
if (!stat?.isFile()) {
|
|
24
|
+
throw new Error(
|
|
25
|
+
`[vxrn] HERMESC_PATH is set but points nowhere: ${override}. ` +
|
|
26
|
+
`Point it at the hermesc binary the native build used, or unset it to resolve the pod compiler.`
|
|
27
|
+
)
|
|
28
|
+
}
|
|
29
|
+
try {
|
|
30
|
+
accessSync(override, constants.X_OK)
|
|
31
|
+
} catch {
|
|
32
|
+
throw new Error(
|
|
33
|
+
`[vxrn] HERMESC_PATH is not executable: ${override}. ` +
|
|
34
|
+
`Point it at the hermesc binary the native build used, or unset it to resolve the pod compiler.`
|
|
35
|
+
)
|
|
36
|
+
}
|
|
37
|
+
return override
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// the pod compiler matches the actual native vm, including rn's prebuilt
|
|
41
|
+
// hermes replacement. the npm compiler can target a different bytecode version.
|
|
42
|
+
function resolvePodHermesc(root: string): string {
|
|
43
|
+
const pods = join(root, 'ios/Pods')
|
|
44
|
+
const podspecPath = join(pods, 'Local Podspecs/hermes-engine.podspec.json')
|
|
45
|
+
let podspec: {
|
|
46
|
+
subspecs: { name: string }[]
|
|
47
|
+
user_target_xcconfig?: { HERMES_CLI_PATH?: string }
|
|
48
|
+
}
|
|
49
|
+
try {
|
|
50
|
+
podspec = JSON.parse(readFileSync(podspecPath, 'utf8'))
|
|
51
|
+
} catch (error) {
|
|
52
|
+
throw new Error(
|
|
53
|
+
`[vxrn] native iOS dev bytecode needs the pod hermesc but ${root} has no usable native project: ` +
|
|
54
|
+
`cannot read ${podspecPath} (${error instanceof Error ? error.message : String(error)}). ` +
|
|
55
|
+
`Run prebuild and pod install for this app, or set HERMESC_PATH to the hermesc the native build used.`
|
|
56
|
+
)
|
|
57
|
+
}
|
|
58
|
+
if (!Array.isArray(podspec.subspecs)) {
|
|
59
|
+
throw new Error(
|
|
60
|
+
`[vxrn] native iOS dev bytecode needs the pod hermesc but ${podspecPath} declares no subspecs. ` +
|
|
61
|
+
`Reinstall pods for this app, or set HERMESC_PATH to the hermesc the native build used.`
|
|
62
|
+
)
|
|
63
|
+
}
|
|
64
|
+
const prebuilt = podspec.subspecs.some(
|
|
65
|
+
(spec: { name: string }) => spec.name === 'Pre-built'
|
|
66
|
+
)
|
|
67
|
+
if (prebuilt) {
|
|
68
|
+
return join(pods, 'hermes-engine/destroot/bin/hermesc')
|
|
69
|
+
}
|
|
70
|
+
const cliPath = podspec.user_target_xcconfig?.HERMES_CLI_PATH
|
|
71
|
+
if (!cliPath) {
|
|
72
|
+
throw new Error(
|
|
73
|
+
`[vxrn] native iOS dev bytecode needs the pod hermesc but ${podspecPath} declares no HERMES_CLI_PATH. ` +
|
|
74
|
+
`Reinstall pods for this app, or set HERMESC_PATH to the hermesc the native build used.`
|
|
75
|
+
)
|
|
76
|
+
}
|
|
77
|
+
return cliPath.replace(/\$\(PODS_ROOT\)|\$\{PODS_ROOT\}/g, pods)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// compile once per emitted bundle. the device can start a fresh hermes context
|
|
81
|
+
// without compiling every cold route's module closures on its js thread.
|
|
82
|
+
export function nativeDevBytecodeCompiler(root: string) {
|
|
83
|
+
const compiler = useHermescOverride() ?? resolvePodHermesc(root)
|
|
84
|
+
const compiled = new WeakMap<NativeDevBundle, Map<string, Promise<Buffer>>>()
|
|
85
|
+
|
|
86
|
+
return (bundle: NativeDevBundle, sourceURL: string): Promise<Buffer> => {
|
|
87
|
+
let urls = compiled.get(bundle)
|
|
88
|
+
if (!urls) compiled.set(bundle, (urls = new Map()))
|
|
89
|
+
const existing = urls.get(sourceURL)
|
|
90
|
+
if (existing) return existing
|
|
91
|
+
const result = (async () => {
|
|
92
|
+
const directory = await mkdtemp(join(tmpdir(), 'vxrn-dev-hermes-'))
|
|
93
|
+
try {
|
|
94
|
+
// hermesc records its input argument as the debug filename. use the
|
|
95
|
+
// served js url as a relative filename so stacks and devtools retain
|
|
96
|
+
// the original bundle coordinates without a second source map.
|
|
97
|
+
const source = join(directory, sourceURL)
|
|
98
|
+
await mkdir(dirname(source), { recursive: true })
|
|
99
|
+
const output = join(directory, 'index.hbc')
|
|
100
|
+
await writeFile(source, bundle.code)
|
|
101
|
+
await execute(
|
|
102
|
+
compiler,
|
|
103
|
+
['-Og', '-g', '-emit-binary', '-out', output, sourceURL],
|
|
104
|
+
{ cwd: directory, maxBuffer: 16 * 1024 * 1024 }
|
|
105
|
+
)
|
|
106
|
+
return await readFile(output)
|
|
107
|
+
} finally {
|
|
108
|
+
await rm(directory, { recursive: true, force: true })
|
|
109
|
+
}
|
|
110
|
+
})().catch((error) => {
|
|
111
|
+
urls.delete(sourceURL)
|
|
112
|
+
throw error
|
|
113
|
+
})
|
|
114
|
+
urls.set(sourceURL, result)
|
|
115
|
+
return result
|
|
116
|
+
}
|
|
117
|
+
}
|