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
package/src/exports/build.ts
CHANGED
|
@@ -77,14 +77,19 @@ export const build = async (optionsIn: VXRNOptions, buildArgs: BuildArgs = {}) =
|
|
|
77
77
|
|
|
78
78
|
const outDir = userViteConfig?.build?.outDir ?? 'dist'
|
|
79
79
|
|
|
80
|
-
const
|
|
80
|
+
const platform = buildArgs.platform
|
|
81
|
+
const isNativeBuild =
|
|
82
|
+
platform === 'ios' ||
|
|
83
|
+
platform === 'android' ||
|
|
84
|
+
platform === 'macos' ||
|
|
85
|
+
platform === 'linux'
|
|
81
86
|
|
|
82
87
|
// generation reuses client assets and their manifest, but rebuilds the server
|
|
83
88
|
const cleanDir =
|
|
84
89
|
buildArgs.step === 'generate' && !isNativeBuild ? `${outDir}/server` : outDir
|
|
85
90
|
await rm(cleanDir, { recursive: true, force: true })
|
|
86
91
|
|
|
87
|
-
if (
|
|
92
|
+
if (isNativeBuild) {
|
|
88
93
|
const { buildBundle } = await import('../rn-commands/bundle/buildBundle')
|
|
89
94
|
|
|
90
95
|
return buildBundle(
|
|
@@ -94,8 +99,10 @@ export const build = async (optionsIn: VXRNOptions, buildArgs: BuildArgs = {}) =
|
|
|
94
99
|
// default (./src/entry-native.tsx) exists only in plain vxrn apps.
|
|
95
100
|
{ root: options.root, vxrnEntries: optionsIn.entries },
|
|
96
101
|
{
|
|
97
|
-
platform
|
|
98
|
-
bundleOutput: `${outDir}${sep}${
|
|
102
|
+
platform,
|
|
103
|
+
bundleOutput: `${outDir}${sep}${platform}.js`,
|
|
104
|
+
// every native build emits imported assets beside its bundle
|
|
105
|
+
assetsDest: outDir,
|
|
99
106
|
dev: false,
|
|
100
107
|
entryFile: '',
|
|
101
108
|
resetCache: true,
|
package/src/index.ts
CHANGED
|
@@ -22,6 +22,7 @@ export * from './utils/environmentUtils'
|
|
|
22
22
|
export * from './utils/getServerEntry'
|
|
23
23
|
export * from './utils/nearestPackageJson'
|
|
24
24
|
export * from './utils/swiftPackageId'
|
|
25
|
+
export { resolveNpmHermesc } from './utils/compileNativeDevBytecode'
|
|
25
26
|
|
|
26
27
|
export * from './plugins/rollupRemoveUnusedImports'
|
|
27
28
|
export * from './plugins/autoDepOptimizePlugin'
|
|
@@ -12,7 +12,7 @@ import { isNativeEnvironment } from '../utils/environmentUtils'
|
|
|
12
12
|
* * https://reactnative.dev/docs/platform-specific-code#platform-specific-extensions
|
|
13
13
|
* * https://v5.vite.dev/config/shared-options.html#resolve-extensions
|
|
14
14
|
*/
|
|
15
|
-
const getNativeExtensions = (platform: 'ios' | 'android') => {
|
|
15
|
+
const getNativeExtensions = (platform: 'ios' | 'android' | 'macos' | 'linux') => {
|
|
16
16
|
return [
|
|
17
17
|
`.${platform}.tsx`,
|
|
18
18
|
`.${platform}.ts`,
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
expoClientFromNativeApp,
|
|
6
6
|
type NativeAppManifest,
|
|
7
7
|
} from '@vxrn/utils/nativeAppManifest'
|
|
8
|
+
import type { NativePlatform } from '@vxrn/utils/platformEnv'
|
|
8
9
|
import {
|
|
9
10
|
addConnectedNativeClient,
|
|
10
11
|
removeConnectedNativeClient,
|
|
@@ -35,7 +36,7 @@ type ClientMessage = {
|
|
|
35
36
|
|
|
36
37
|
type NativeHmrSocket = WebSocket & {
|
|
37
38
|
vxrnClientId: string
|
|
38
|
-
vxrnPlatform:
|
|
39
|
+
vxrnPlatform: NativePlatform
|
|
39
40
|
}
|
|
40
41
|
|
|
41
42
|
// metro's asset server answers `/assets/<path>` for any asset file inside the
|
|
@@ -102,9 +103,11 @@ export function createReactNativeDevServerPlugin(
|
|
|
102
103
|
const hmrWSS = new WebSocketServer({ noServer: true })
|
|
103
104
|
const clientWSS = new WebSocketServer({ noServer: true })
|
|
104
105
|
const messageWSS = createMessageSocket()
|
|
105
|
-
const validPlatforms: Record<string,
|
|
106
|
+
const validPlatforms: Record<string, NativePlatform | undefined> = {
|
|
106
107
|
ios: 'ios',
|
|
107
108
|
android: 'android',
|
|
109
|
+
macos: 'macos',
|
|
110
|
+
linux: 'linux',
|
|
108
111
|
}
|
|
109
112
|
const devEngines: Record<
|
|
110
113
|
string,
|
|
@@ -112,8 +115,10 @@ export function createReactNativeDevServerPlugin(
|
|
|
112
115
|
> = {}
|
|
113
116
|
const devEngineCreating: Record<string, Promise<unknown> | null> = {}
|
|
114
117
|
const warnedProdBundleRequest = new Set<string>()
|
|
115
|
-
const pendingReloadPlatforms = new Set<
|
|
116
|
-
|
|
118
|
+
const pendingReloadPlatforms = new Set<NativePlatform>()
|
|
119
|
+
const compileBytecode: Partial<
|
|
120
|
+
Record<'ios' | 'macos', ReturnType<typeof nativeDevBytecodeCompiler>>
|
|
121
|
+
> = {}
|
|
117
122
|
|
|
118
123
|
const devToolsSocketEndpoints = ['/inspector/device', '/inspector/debug']
|
|
119
124
|
const reactNativeDevToolsUrl = `http://${typeof host === 'string' && host !== '0.0.0.0' ? host : 'localhost'}:${getBoundPort(server)}`
|
|
@@ -491,7 +496,7 @@ export function createReactNativeDevServerPlugin(
|
|
|
491
496
|
})
|
|
492
497
|
})
|
|
493
498
|
|
|
494
|
-
const getDevEngine = async (platform:
|
|
499
|
+
const getDevEngine = async (platform: NativePlatform) => {
|
|
495
500
|
while (!devEngines[platform]) {
|
|
496
501
|
// prevent duplicate creation from concurrent requests
|
|
497
502
|
if (!devEngineCreating[platform]) {
|
|
@@ -612,9 +617,15 @@ export function createReactNativeDevServerPlugin(
|
|
|
612
617
|
try {
|
|
613
618
|
const bundle = await (await getDevEngine(platform)).getBundle()
|
|
614
619
|
let body: string | Buffer = bundle.code
|
|
615
|
-
if (
|
|
616
|
-
|
|
617
|
-
|
|
620
|
+
if (
|
|
621
|
+
(platform === 'ios' || platform === 'macos') &&
|
|
622
|
+
url.searchParams.get('bytecode') === 'hermes'
|
|
623
|
+
) {
|
|
624
|
+
const compile = (compileBytecode[platform] ||= nativeDevBytecodeCompiler(
|
|
625
|
+
root,
|
|
626
|
+
platform
|
|
627
|
+
))
|
|
628
|
+
body = await compile(
|
|
618
629
|
bundle,
|
|
619
630
|
`${url.origin}/index.bundle?platform=${platform}`
|
|
620
631
|
)
|
|
@@ -2,6 +2,7 @@ import FSExtra from 'fs-extra'
|
|
|
2
2
|
import { clearTransformCache } from '@vxrn/compiler'
|
|
3
3
|
import type { BundleCommandArgs } from '@vxrn/vite-plugin-metro/rn-commands'
|
|
4
4
|
import { resolveConfig } from 'vite'
|
|
5
|
+
import { dirname, join, resolve } from 'node:path'
|
|
5
6
|
import { loadEnv } from '../../exports/loadEnv'
|
|
6
7
|
import { buildNativeBundle } from '../../utils/createNativeDevEngine'
|
|
7
8
|
|
|
@@ -14,7 +15,7 @@ export async function buildBundle(
|
|
|
14
15
|
bundleImpl: any = null
|
|
15
16
|
): Promise<void> {
|
|
16
17
|
const {
|
|
17
|
-
platform,
|
|
18
|
+
platform,
|
|
18
19
|
dev,
|
|
19
20
|
bundleOutput,
|
|
20
21
|
// React Native's `--bundle-encoding` names a Node Buffer encoding.
|
|
@@ -92,6 +93,17 @@ export async function buildBundle(
|
|
|
92
93
|
})
|
|
93
94
|
const builtBundle = result.code
|
|
94
95
|
|
|
96
|
+
// gradle sends drawable resources to assetsDest but packages arbitrary files
|
|
97
|
+
// from the bundle directory. DOM pages must share that assets root with JS.
|
|
98
|
+
if (platform === 'android' && assetsDest) {
|
|
99
|
+
const source = join(assetsDest, 'one-dom')
|
|
100
|
+
const destination = join(dirname(bundleOutput), 'one-dom')
|
|
101
|
+
if (resolve(source) !== resolve(destination)) {
|
|
102
|
+
FSExtra.removeSync(destination)
|
|
103
|
+
if (FSExtra.existsSync(source)) FSExtra.moveSync(source, destination)
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
95
107
|
// write sourcemap if available and requested
|
|
96
108
|
if (result.map && args.sourcemapOutput) {
|
|
97
109
|
FSExtra.outputFileSync(args.sourcemapOutput, result.map, { encoding: 'utf8' })
|
|
@@ -33,7 +33,13 @@ global.performance = global.performance || { now: function() { return Date.now()
|
|
|
33
33
|
// hermes can expose addEventListener without the matching removal API. browser
|
|
34
34
|
// libraries use addEventListener as their capability check, then crash during
|
|
35
35
|
// cleanup. keep the incomplete host API from advertising browser event support.
|
|
36
|
-
|
|
36
|
+
// the bundle runs at script scope, so a module's own top-level
|
|
37
|
+
// function addEventListener declaration is also this global; only a native host function is cleared.
|
|
38
|
+
if (
|
|
39
|
+
typeof global.addEventListener === 'function' &&
|
|
40
|
+
typeof global.removeEventListener !== 'function' &&
|
|
41
|
+
Function.prototype.toString.call(global.addEventListener).indexOf('[native code]') !== -1
|
|
42
|
+
) {
|
|
37
43
|
global.addEventListener = undefined;
|
|
38
44
|
}
|
|
39
45
|
|
package/src/types.ts
CHANGED
|
@@ -46,7 +46,7 @@ it.each(['prebuilt', 'source'])(
|
|
|
46
46
|
},
|
|
47
47
|
})
|
|
48
48
|
)
|
|
49
|
-
const compiler = nativeDevBytecodeCompiler(root)
|
|
49
|
+
const compiler = nativeDevBytecodeCompiler(root, 'ios')
|
|
50
50
|
const compile = (bundle: { code: string; map: string }) =>
|
|
51
51
|
compiler(bundle, 'http://localhost:8081/index.bundle?platform=ios')
|
|
52
52
|
const first = { code: 'globalThis.answer = 42;', map: '' }
|
|
@@ -87,7 +87,7 @@ it('uses HERMESC_PATH without any ios/Pods', async () => {
|
|
|
87
87
|
const root = await mkdtemp(join(tmpdir(), 'vxrn-dev-bytecode-override-'))
|
|
88
88
|
process.env.HERMESC_PATH = hostHermesc()
|
|
89
89
|
try {
|
|
90
|
-
const compiler = nativeDevBytecodeCompiler(root)
|
|
90
|
+
const compiler = nativeDevBytecodeCompiler(root, 'ios')
|
|
91
91
|
const bytes = await compiler(
|
|
92
92
|
{ code: 'globalThis.answer = 42;', map: '' },
|
|
93
93
|
'http://localhost:8081/index.bundle?platform=ios'
|
|
@@ -102,10 +102,10 @@ it('treats empty HERMESC_PATH as unset', async () => {
|
|
|
102
102
|
const root = await mkdtemp(join(tmpdir(), 'vxrn-dev-bytecode-empty-'))
|
|
103
103
|
process.env.HERMESC_PATH = ' '
|
|
104
104
|
try {
|
|
105
|
-
expect(() => nativeDevBytecodeCompiler(root)).toThrow(
|
|
105
|
+
expect(() => nativeDevBytecodeCompiler(root, 'ios')).toThrow(
|
|
106
106
|
/hermes-engine\.podspec\.json/
|
|
107
107
|
)
|
|
108
|
-
expect(() => nativeDevBytecodeCompiler(root)).not.toThrow(/HERMESC_PATH is set/)
|
|
108
|
+
expect(() => nativeDevBytecodeCompiler(root, 'ios')).not.toThrow(/HERMESC_PATH is set/)
|
|
109
109
|
} finally {
|
|
110
110
|
await rm(root, { recursive: true, force: true })
|
|
111
111
|
}
|
|
@@ -115,7 +115,7 @@ it('loudly rejects a HERMESC_PATH that points nowhere', async () => {
|
|
|
115
115
|
const root = await mkdtemp(join(tmpdir(), 'vxrn-dev-bytecode-invalid-'))
|
|
116
116
|
process.env.HERMESC_PATH = join(root, 'no-such-hermesc')
|
|
117
117
|
try {
|
|
118
|
-
expect(() => nativeDevBytecodeCompiler(root)).toThrow(
|
|
118
|
+
expect(() => nativeDevBytecodeCompiler(root, 'ios')).toThrow(
|
|
119
119
|
/HERMESC_PATH is set but points nowhere/
|
|
120
120
|
)
|
|
121
121
|
} finally {
|
|
@@ -133,7 +133,7 @@ it.runIf(process.platform !== 'win32')(
|
|
|
133
133
|
await chmod(plain, 0o644)
|
|
134
134
|
process.env.HERMESC_PATH = plain
|
|
135
135
|
try {
|
|
136
|
-
expect(() => nativeDevBytecodeCompiler(root)).toThrow(
|
|
136
|
+
expect(() => nativeDevBytecodeCompiler(root, 'ios')).toThrow(
|
|
137
137
|
/HERMESC_PATH is not executable/
|
|
138
138
|
)
|
|
139
139
|
} finally {
|
|
@@ -145,7 +145,7 @@ it.runIf(process.platform !== 'win32')(
|
|
|
145
145
|
it('loudly rejects a root without ios/Pods when the override is unset', async () => {
|
|
146
146
|
const root = await mkdtemp(join(tmpdir(), 'vxrn-dev-bytecode-nopods-'))
|
|
147
147
|
try {
|
|
148
|
-
expect(() => nativeDevBytecodeCompiler(root)).toThrow(
|
|
148
|
+
expect(() => nativeDevBytecodeCompiler(root, 'ios')).toThrow(
|
|
149
149
|
/hermes-engine\.podspec\.json.*HERMESC_PATH/s
|
|
150
150
|
)
|
|
151
151
|
} finally {
|
|
@@ -159,7 +159,7 @@ it('loudly rejects a malformed podspec when the override is unset', async () =>
|
|
|
159
159
|
const specs = join(root, 'ios/Pods/Local Podspecs')
|
|
160
160
|
await mkdir(specs, { recursive: true })
|
|
161
161
|
await writeFile(join(specs, 'hermes-engine.podspec.json'), '{oops')
|
|
162
|
-
expect(() => nativeDevBytecodeCompiler(root)).toThrow(
|
|
162
|
+
expect(() => nativeDevBytecodeCompiler(root, 'ios')).toThrow(
|
|
163
163
|
/hermes-engine\.podspec\.json/
|
|
164
164
|
)
|
|
165
165
|
} finally {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { accessSync, constants, readFileSync, statSync } from 'node:fs'
|
|
2
2
|
import { execFile } from 'node:child_process'
|
|
3
|
+
import { createRequire } from 'node:module'
|
|
3
4
|
import { mkdir, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises'
|
|
4
5
|
import { tmpdir } from 'node:os'
|
|
5
6
|
import { dirname, join } from 'node:path'
|
|
@@ -77,10 +78,29 @@ function resolvePodHermesc(root: string): string {
|
|
|
77
78
|
return cliPath.replace(/\$\(PODS_ROOT\)|\$\{PODS_ROOT\}/g, pods)
|
|
78
79
|
}
|
|
79
80
|
|
|
81
|
+
// through react-native's own dependency graph: it pins the compiler whose
|
|
82
|
+
// bytecode its hermes vm runs. the macos dev server and builds without a
|
|
83
|
+
// hermes-engine pod compile with it.
|
|
84
|
+
export function resolveNpmHermesc(root: string): string {
|
|
85
|
+
const require = createRequire(join(root, 'package.json'))
|
|
86
|
+
const reactNativeJson = require.resolve('react-native/package.json')
|
|
87
|
+
const nested = createRequire(reactNativeJson)
|
|
88
|
+
const compilerJson = nested.resolve('hermes-compiler/package.json')
|
|
89
|
+
const dir = compilerJson.slice(0, -'package.json'.length)
|
|
90
|
+
const bin =
|
|
91
|
+
process.platform === 'darwin'
|
|
92
|
+
? 'osx-bin'
|
|
93
|
+
: process.platform === 'win32'
|
|
94
|
+
? 'win64-bin'
|
|
95
|
+
: 'linux64-bin'
|
|
96
|
+
return join(dir, 'hermesc', bin, process.platform === 'win32' ? 'hermesc.exe' : 'hermesc')
|
|
97
|
+
}
|
|
98
|
+
|
|
80
99
|
// compile once per emitted bundle. the device can start a fresh hermes context
|
|
81
100
|
// without compiling every cold route's module closures on its js thread.
|
|
82
|
-
export function nativeDevBytecodeCompiler(root: string) {
|
|
83
|
-
const compiler =
|
|
101
|
+
export function nativeDevBytecodeCompiler(root: string, platform: 'ios' | 'macos') {
|
|
102
|
+
const compiler =
|
|
103
|
+
useHermescOverride() ?? (platform === 'ios' ? resolvePodHermesc(root) : resolveNpmHermesc(root))
|
|
84
104
|
const compiled = new WeakMap<NativeDevBundle, Map<string, Promise<Buffer>>>()
|
|
85
105
|
|
|
86
106
|
return (bundle: NativeDevBundle, sourceURL: string): Promise<Buffer> => {
|
|
@@ -32,6 +32,8 @@ import {
|
|
|
32
32
|
normalizeNativeCommonJSInterop,
|
|
33
33
|
postProcessNativeBundle,
|
|
34
34
|
vxrnCompilerPlugin,
|
|
35
|
+
NATIVE_DEV_RUNTIME_END,
|
|
36
|
+
NATIVE_MODULE_SCOPE_MARKER,
|
|
35
37
|
wrapNativeBundleModuleScope,
|
|
36
38
|
type NativePluginContext,
|
|
37
39
|
} from './createNativeDevEngine'
|
|
@@ -218,8 +220,20 @@ describe.each(['ios', 'android'] as const)('native plugin adapters on %s', (plat
|
|
|
218
220
|
}
|
|
219
221
|
runInNewContext(code, context)
|
|
220
222
|
expect(Reflect.get(context, 'nativePluginResult')).toEqual([
|
|
221
|
-
{
|
|
222
|
-
|
|
223
|
+
{
|
|
224
|
+
name: 'global',
|
|
225
|
+
root,
|
|
226
|
+
platform,
|
|
227
|
+
dev: mode === 'dev',
|
|
228
|
+
command: mode === 'dev' ? 'serve' : 'build',
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
name: 'direct',
|
|
232
|
+
root,
|
|
233
|
+
platform,
|
|
234
|
+
dev: mode === 'dev',
|
|
235
|
+
command: mode === 'dev' ? 'serve' : 'build',
|
|
236
|
+
},
|
|
223
237
|
'plain',
|
|
224
238
|
])
|
|
225
239
|
for (const factory of factories) {
|
|
@@ -227,6 +241,7 @@ describe.each(['ios', 'android'] as const)('native plugin adapters on %s', (plat
|
|
|
227
241
|
root,
|
|
228
242
|
platform,
|
|
229
243
|
dev: mode === 'dev',
|
|
244
|
+
command: mode === 'dev' ? 'serve' : 'build',
|
|
230
245
|
})
|
|
231
246
|
}
|
|
232
247
|
} finally {
|
|
@@ -727,8 +742,9 @@ describe('native prelude', () => {
|
|
|
727
742
|
})
|
|
728
743
|
|
|
729
744
|
it('does not advertise a host event API that cannot remove listeners', () => {
|
|
745
|
+
// a bound function prints as [native code], like a host function
|
|
730
746
|
const context = {
|
|
731
|
-
addEventListener() {},
|
|
747
|
+
addEventListener: function () {}.bind(null),
|
|
732
748
|
}
|
|
733
749
|
|
|
734
750
|
runInNewContext(getNativePrelude({ dev: false, platform: 'ios' }), context)
|
|
@@ -736,6 +752,15 @@ describe('native prelude', () => {
|
|
|
736
752
|
expect(Reflect.get(context, 'addEventListener')).toBeUndefined()
|
|
737
753
|
})
|
|
738
754
|
|
|
755
|
+
it('keeps a javascript addEventListener without a removal API', () => {
|
|
756
|
+
const addEventListener = function addEventListener() {}
|
|
757
|
+
const context = { addEventListener }
|
|
758
|
+
|
|
759
|
+
runInNewContext(getNativePrelude({ dev: false, platform: 'ios' }), context)
|
|
760
|
+
|
|
761
|
+
expect(Reflect.get(context, 'addEventListener')).toBe(addEventListener)
|
|
762
|
+
})
|
|
763
|
+
|
|
739
764
|
it('preserves a complete host event API', () => {
|
|
740
765
|
const addEventListener = () => {}
|
|
741
766
|
const removeEventListener = () => {}
|
|
@@ -1708,30 +1733,32 @@ describe('getNativeTransformConfig platform env defines', () => {
|
|
|
1708
1733
|
})
|
|
1709
1734
|
|
|
1710
1735
|
describe('wrapNativeBundleModuleScope', () => {
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
const prelude =
|
|
1736
|
+
it.each([
|
|
1737
|
+
['rolldown 1.2.15', '// \\0rolldown/runtime.js'],
|
|
1738
|
+
['rolldown 1.2.13', '//#region \\0rolldown/runtime.js'],
|
|
1739
|
+
])('wraps module code after the prelude with %s module comments', (_, header) => {
|
|
1740
|
+
const prelude = `globalThis.global = globalThis;\nglobalThis.__DEV__ = true;\n${NATIVE_MODULE_SCOPE_MARKER}\n`
|
|
1716
1741
|
// a top-level `var Headers` in a script becomes a non-configurable global,
|
|
1717
1742
|
// which is the exact leak that breaks RN's polyfillGlobal in dev
|
|
1718
|
-
const body = `${
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
expect(
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1743
|
+
const body = `${header}\nvar fetch_hot, fetch$1, Headers, Request, Response$1;\nglobalThis.__rolldown_runtime__ = {};\n`
|
|
1744
|
+
const input = prelude + body
|
|
1745
|
+
|
|
1746
|
+
const out = wrapNativeBundleModuleScope(input)
|
|
1747
|
+
|
|
1748
|
+
const context = createContext({})
|
|
1749
|
+
runInContext(out, context)
|
|
1750
|
+
expect(runInContext('globalThis.__DEV__', context)).toBe(true)
|
|
1751
|
+
expect(
|
|
1752
|
+
runInContext('Object.getOwnPropertyDescriptor(globalThis, "Headers")', context)
|
|
1753
|
+
).toBeUndefined()
|
|
1754
|
+
// the opener shares the marker line, so every module keeps its mapped line
|
|
1755
|
+
const lineOf = (code: string) =>
|
|
1756
|
+
code.split('\n').indexOf('var fetch_hot, fetch$1, Headers, Request, Response$1;')
|
|
1757
|
+
expect(lineOf(out)).toBe(lineOf(input))
|
|
1730
1758
|
})
|
|
1731
1759
|
|
|
1732
|
-
it('
|
|
1733
|
-
|
|
1734
|
-
expect(wrapNativeBundleModuleScope(input)).toBe(input)
|
|
1760
|
+
it('fails the build when the prelude marker is missing', () => {
|
|
1761
|
+
expect(() => wrapNativeBundleModuleScope('var x = 1;\n')).toThrow('script scope')
|
|
1735
1762
|
})
|
|
1736
1763
|
})
|
|
1737
1764
|
|
|
@@ -3507,9 +3534,9 @@ describe('native dev bundle post-processing failures', () => {
|
|
|
3507
3534
|
// downleveled, which throws inside handleOutput.
|
|
3508
3535
|
return {
|
|
3509
3536
|
code: [
|
|
3510
|
-
|
|
3537
|
+
NATIVE_MODULE_SCOPE_MARKER,
|
|
3511
3538
|
'class Poison { x = 1; y = 2; z = 3; }',
|
|
3512
|
-
|
|
3539
|
+
NATIVE_DEV_RUNTIME_END,
|
|
3513
3540
|
'globalThis.__postprocessProbe = new Poison();',
|
|
3514
3541
|
'',
|
|
3515
3542
|
].join('\n'),
|