peachsim 0.1.2186 → 0.1.2188
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-lib/agent-daemon-client.cjs +1 -1
- package/dist-lib/agent-events.cjs +1 -1
- package/dist-lib/attached-projects.cjs +1 -1
- package/dist-lib/auth/shared-session.cjs +1 -1
- package/dist-lib/backend-origin.cjs +1 -1
- package/dist-lib/beta.cjs +1 -1
- package/dist-lib/beta.mjs +1 -1
- package/dist-lib/bridge-constants.cjs +1 -1
- package/dist-lib/bridge-contract-input.cjs +1 -1
- package/dist-lib/bridge-contract-input.mjs +1 -1
- package/dist-lib/bridge-contract.cjs +1 -1
- package/dist-lib/bridge-contract.mjs +1 -1
- package/dist-lib/bundle-channel.cjs +2708 -2383
- package/dist-lib/capture-contract.cjs +1 -1
- package/dist-lib/capture-contract.mjs +1 -1
- package/dist-lib/cloud-contract.cjs +1 -1
- package/dist-lib/cloud-contract.mjs +1 -1
- package/dist-lib/cloud-test-args.cjs +1 -1
- package/dist-lib/cloud-test-args.mjs +1 -1
- package/dist-lib/cloud.cjs +1 -1
- package/dist-lib/cloud.mjs +1 -1
- package/dist-lib/config.cjs +1 -1
- package/dist-lib/config.mjs +1 -1
- package/dist-lib/detox/index.cjs +1 -1
- package/dist-lib/dev-bundle-resolution.cjs +1 -1
- package/dist-lib/expo-swift-bundle.cjs +3229 -3222
- package/dist-lib/expo-swift-metadata.cjs +1 -1
- package/dist-lib/expo-swift-metadata.mjs +1 -1
- package/dist-lib/home-paths.cjs +1 -1
- package/dist-lib/host/bridge-host.cjs +4540 -4215
- package/dist-lib/host/fetch-proxy-handler.cjs +8 -7
- package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
- package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
- package/dist-lib/host/replacement-module-handler.cjs +1 -1
- package/dist-lib/host/websocket-proxy.cjs +1 -1
- package/dist-lib/index.cjs +431 -281
- package/dist-lib/jump-to-source-babel.cjs +1 -1
- package/dist-lib/jump-to-source-native.cjs +1 -1
- package/dist-lib/kotlin-host.cjs +645 -284
- package/dist-lib/menu.cjs +1 -1
- package/dist-lib/menu.mjs +1 -1
- package/dist-lib/metro-fingerprint-registry.cjs +1 -1
- package/dist-lib/metro-fingerprint-registry.mjs +1 -1
- package/dist-lib/metro-production-bundle.cjs +1 -1
- package/dist-lib/metro-production-bundle.mjs +1 -1
- package/dist-lib/metro.cjs +732 -582
- package/dist-lib/native-app-identity-project.cjs +3 -3
- package/dist-lib/native-app-identity-project.mjs +3 -3
- package/dist-lib/native-app-identity.cjs +1 -1
- package/dist-lib/native-app-identity.mjs +1 -1
- package/dist-lib/native-firebase-metadata.cjs +1 -1
- package/dist-lib/native-firebase-metadata.mjs +1 -1
- package/dist-lib/pdfium-raster.cjs +1 -1
- package/dist-lib/pdfium-raster.mjs +1 -1
- package/dist-lib/plist.cjs +1 -1
- package/dist-lib/plist.mjs +1 -1
- package/dist-lib/profiles.cjs +1 -1
- package/dist-lib/public-brand.cjs +1 -1
- package/dist-lib/public-origins-lib-esm.json +24 -24
- package/dist-lib/public-origins-lib.json +99 -59
- package/dist-lib/react-native-host-modules.cjs +1 -1
- package/dist-lib/react-native-host-modules.mjs +1 -1
- package/dist-lib/reap-browser.cjs +1 -1
- package/dist-lib/scripts/dev-server-scanner.cjs +1 -1
- package/dist-lib/sdk.cjs +1 -1
- package/dist-lib/sdk.mjs +1 -1
- package/dist-lib/skills.cjs +5198 -4873
- package/dist-lib/swift-compile.cjs +2 -2
- package/dist-lib/swift-compile.mjs +2 -2
- package/dist-lib/swift-package.cjs +1 -1
- package/dist-lib/swift-package.mjs +1 -1
- package/dist-lib/swift-submit.cjs +1 -1
- package/dist-lib/swift-submit.mjs +1 -1
- package/dist-lib/swift-toolchain.cjs +3 -1
- package/dist-lib/swift.cjs +1 -1
- package/dist-lib/vite-swift.cjs +4 -4
- package/dist-lib/vite.cjs +8 -7
- package/dist-lib/worklet-executable.cjs +1 -1
- package/dist-lib/worklet-executable.mjs +1 -1
- package/dist-types/packages/peach-engine/src/engine/node.d.ts +1 -1
- package/kotlin/sdk.tar.gz +0 -0
- package/package.json +1 -1
- package/sdk/ios/System/Library/Frameworks/CFNetwork.framework/Headers/CFNetwork.h +10 -0
- package/src/android-autolinking.ts +45 -4
- package/src/android-bridge-packages.ts +2 -2
- package/src/android-gradle-sources.ts +143 -0
- package/src/androidAutolinkingContract.ts +8 -0
- package/src/androidGradleContract.ts +14 -0
- package/src/expo-kotlin-host.ts +3 -0
- package/src/java-module-abi.ts +145 -0
- package/src/javaAbiContract.ts +14 -0
- package/src/kotlin-host.ts +55 -3
- package/src/nitro-source.ts +2 -167
- package/src/react-native-autolinking.ts +153 -0
- package/src/swift-distribution-contract.ts +72 -0
- package/src/swift-toolchain.ts +1 -1
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { execFile } from 'node:child_process'
|
|
2
|
+
import { createHash } from 'node:crypto'
|
|
3
|
+
import fs from 'node:fs'
|
|
4
|
+
import path from 'node:path'
|
|
5
|
+
import { promisify } from 'node:util'
|
|
6
|
+
import { readBridgeMethod } from './android-bridge-packages'
|
|
7
|
+
import { isRecord } from './is-record'
|
|
8
|
+
import { kotlinHome } from './kotlin-toolchain'
|
|
9
|
+
import type { JavaModuleSpec, JavaModuleSpecs } from './javaAbiContract'
|
|
10
|
+
import type { KotlinToolchainInstallation } from './kotlinSdkContract'
|
|
11
|
+
|
|
12
|
+
const run = promisify(execFile)
|
|
13
|
+
export const JAVA_BINDINGS_INITIALIZER = 'initializeJavaBindings'
|
|
14
|
+
|
|
15
|
+
export async function readJavaModuleSpecs(
|
|
16
|
+
files: string[],
|
|
17
|
+
sdkRoot: string,
|
|
18
|
+
toolchain: KotlinToolchainInstallation,
|
|
19
|
+
): Promise<JavaModuleSpecs> {
|
|
20
|
+
const digest = createHash('sha256')
|
|
21
|
+
const parent = path.join(kotlinHome(), 'java-specs')
|
|
22
|
+
fs.mkdirSync(parent, { recursive: true })
|
|
23
|
+
const work = fs.mkdtempSync(path.join(parent, '.read-'))
|
|
24
|
+
try {
|
|
25
|
+
const origins = new Map<string, string>()
|
|
26
|
+
for (const [index, file] of files.entries()) {
|
|
27
|
+
const bytes = fs.readFileSync(file)
|
|
28
|
+
digest.update(file).update('\0').update(bytes).update('\0')
|
|
29
|
+
const target = path.join(work, String(index), path.basename(file))
|
|
30
|
+
fs.mkdirSync(path.dirname(target))
|
|
31
|
+
fs.writeFileSync(target, bytes)
|
|
32
|
+
origins.set(target, file)
|
|
33
|
+
}
|
|
34
|
+
const { stdout } = await run(
|
|
35
|
+
path.join(toolchain.javaHome, 'bin', 'java'),
|
|
36
|
+
[
|
|
37
|
+
'-cp',
|
|
38
|
+
[
|
|
39
|
+
path.join(sdkRoot, 'tools.jar'),
|
|
40
|
+
path.resolve(path.dirname(toolchain.kotlinc), '../lib/kotlin-compiler.jar'),
|
|
41
|
+
path.resolve(path.dirname(toolchain.kotlinc), '../lib/kotlin-stdlib.jar'),
|
|
42
|
+
].join(path.delimiter),
|
|
43
|
+
'dev.peach.tools.javaabi.JavaModuleSpecsKt',
|
|
44
|
+
...origins.keys(),
|
|
45
|
+
],
|
|
46
|
+
{ maxBuffer: 16 * 1024 * 1024 },
|
|
47
|
+
)
|
|
48
|
+
const specs = stdout
|
|
49
|
+
.split('\n')
|
|
50
|
+
.filter(Boolean)
|
|
51
|
+
.map((line): JavaModuleSpec => {
|
|
52
|
+
const row: unknown = JSON.parse(line)
|
|
53
|
+
if (
|
|
54
|
+
!isRecord(row) ||
|
|
55
|
+
typeof row.file !== 'string' ||
|
|
56
|
+
typeof row.className !== 'string' ||
|
|
57
|
+
(row.name !== null && typeof row.name !== 'string') ||
|
|
58
|
+
typeof row.source !== 'string' ||
|
|
59
|
+
!Array.isArray(row.methods)
|
|
60
|
+
)
|
|
61
|
+
throw new Error('Java module reader printed an invalid spec')
|
|
62
|
+
const file = origins.get(row.file)
|
|
63
|
+
if (!file) throw new Error('Java module reader named an unknown input')
|
|
64
|
+
return {
|
|
65
|
+
file,
|
|
66
|
+
className: row.className,
|
|
67
|
+
name: row.name,
|
|
68
|
+
source: row.source,
|
|
69
|
+
methods: row.methods.map(readBridgeMethod),
|
|
70
|
+
}
|
|
71
|
+
})
|
|
72
|
+
return { sha256: digest.digest('hex'), specs }
|
|
73
|
+
} finally {
|
|
74
|
+
fs.rmSync(work, { recursive: true, force: true })
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export async function compileJavaModuleAbi(input: {
|
|
79
|
+
specs: JavaModuleSpecs
|
|
80
|
+
work: string
|
|
81
|
+
hash: string
|
|
82
|
+
toolchain: KotlinToolchainInstallation
|
|
83
|
+
libraries: string[]
|
|
84
|
+
sdkRoot: string
|
|
85
|
+
sdkLibrary: string
|
|
86
|
+
language: string
|
|
87
|
+
}): Promise<string> {
|
|
88
|
+
const { specs, work, hash, toolchain, libraries, sdkRoot, sdkLibrary, language } = input
|
|
89
|
+
const directory = path.join(work, 'java-abi')
|
|
90
|
+
fs.mkdirSync(directory)
|
|
91
|
+
const sources = specs.specs.map((spec, index) => {
|
|
92
|
+
const target = path.join(directory, `Spec${index}.kt`)
|
|
93
|
+
fs.writeFileSync(target, spec.source)
|
|
94
|
+
return target
|
|
95
|
+
})
|
|
96
|
+
const initializer = path.join(directory, 'Bindings.kt')
|
|
97
|
+
fs.writeFileSync(
|
|
98
|
+
initializer,
|
|
99
|
+
[
|
|
100
|
+
'package dev.peach.jni',
|
|
101
|
+
`internal fun ${JAVA_BINDINGS_INITIALIZER}() { error("Java bindings require the compiler plugin") }`,
|
|
102
|
+
].join('\n'),
|
|
103
|
+
)
|
|
104
|
+
const name = `peach-java-abi-${hash}`
|
|
105
|
+
await run(
|
|
106
|
+
toolchain.kotlinc,
|
|
107
|
+
[
|
|
108
|
+
'-J-Xmx2g',
|
|
109
|
+
'-Xwasm-target=js',
|
|
110
|
+
'-libraries',
|
|
111
|
+
libraries.join(path.delimiter),
|
|
112
|
+
`-Xplugin=${path.join(sdkRoot, 'plugin.jar')}`,
|
|
113
|
+
`-Xfriend-modules=${sdkLibrary}`,
|
|
114
|
+
'-language-version',
|
|
115
|
+
language,
|
|
116
|
+
'-api-version',
|
|
117
|
+
language,
|
|
118
|
+
'-nowarn',
|
|
119
|
+
'-ir-output-dir',
|
|
120
|
+
directory,
|
|
121
|
+
'-ir-output-name',
|
|
122
|
+
name,
|
|
123
|
+
...sources,
|
|
124
|
+
initializer,
|
|
125
|
+
],
|
|
126
|
+
{ maxBuffer: 16 * 1024 * 1024 },
|
|
127
|
+
)
|
|
128
|
+
const library = path.join(directory, 'java-abi.klib')
|
|
129
|
+
await run(
|
|
130
|
+
path.join(toolchain.javaHome, 'bin', 'java'),
|
|
131
|
+
[
|
|
132
|
+
'-cp',
|
|
133
|
+
[
|
|
134
|
+
path.join(sdkRoot, 'tools.jar'),
|
|
135
|
+
path.resolve(path.dirname(toolchain.kotlinc), '../lib/kotlin-compiler.jar'),
|
|
136
|
+
path.resolve(path.dirname(toolchain.kotlinc), '../lib/kotlin-stdlib.jar'),
|
|
137
|
+
].join(path.delimiter),
|
|
138
|
+
'dev.peach.tools.platform.JavaPlatformTypesKt',
|
|
139
|
+
path.join(directory, `${name}.klib`),
|
|
140
|
+
library,
|
|
141
|
+
],
|
|
142
|
+
{ maxBuffer: 16 * 1024 * 1024 },
|
|
143
|
+
)
|
|
144
|
+
return library
|
|
145
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { BridgeMethod } from './android-bridge-packages'
|
|
2
|
+
|
|
3
|
+
export interface JavaModuleSpec {
|
|
4
|
+
file: string
|
|
5
|
+
className: string
|
|
6
|
+
name: string | null
|
|
7
|
+
source: string
|
|
8
|
+
methods: BridgeMethod[]
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface JavaModuleSpecs {
|
|
12
|
+
sha256: string
|
|
13
|
+
specs: JavaModuleSpec[]
|
|
14
|
+
}
|
package/src/kotlin-host.ts
CHANGED
|
@@ -20,6 +20,11 @@ import {
|
|
|
20
20
|
} from './android-bridge-packages'
|
|
21
21
|
import { rnxHomeDir } from './home-paths'
|
|
22
22
|
import { isRecord } from './is-record'
|
|
23
|
+
import {
|
|
24
|
+
compileJavaModuleAbi,
|
|
25
|
+
JAVA_BINDINGS_INITIALIZER,
|
|
26
|
+
readJavaModuleSpecs,
|
|
27
|
+
} from './java-module-abi'
|
|
23
28
|
import { kotlinInstanceSource } from './kotlin-instance-source'
|
|
24
29
|
import { installKotlinSdkArchive } from './kotlin-sdk'
|
|
25
30
|
import {
|
|
@@ -131,11 +136,12 @@ function entrySource(ids: string[]): string {
|
|
|
131
136
|
'',
|
|
132
137
|
'@OptIn(kotlin.js.ExperimentalJsExport::class)',
|
|
133
138
|
'@JsExport',
|
|
134
|
-
'fun peachSetAndroidDeviceInfo(model: String, release: String, apiLevel: Int, locale: String, timeZone: String, settings: String) {',
|
|
139
|
+
'fun peachSetAndroidDeviceInfo(model: String, release: String, apiLevel: Int, locale: String, timeZone: String, settings: String, properties: String) {',
|
|
135
140
|
' Build.setDevice(model, release, apiLevel)',
|
|
136
141
|
' java.util.Locale.setDefault(java.util.Locale.forLanguageTag(locale))',
|
|
137
142
|
' java.util.TimeZone.setDevice(timeZone)',
|
|
138
143
|
' dev.peach.android.ProcessSettings.configure(settings)',
|
|
144
|
+
' android.os.SystemProperties.configure(properties)',
|
|
139
145
|
'}',
|
|
140
146
|
'',
|
|
141
147
|
'@OptIn(kotlin.js.ExperimentalJsExport::class, kotlinx.coroutines.DelicateCoroutinesApi::class)',
|
|
@@ -703,6 +709,25 @@ export async function expoKotlinArtifact(
|
|
|
703
709
|
origin: string,
|
|
704
710
|
modules: ExpoKotlinModules,
|
|
705
711
|
): Promise<KotlinArtifact & { compose: boolean }> {
|
|
712
|
+
if (modules.gradleSources) {
|
|
713
|
+
const { resolveAndroidGradleSources } = await import('./android-gradle-sources')
|
|
714
|
+
const projects = await resolveAndroidGradleSources(modules.gradleSources)
|
|
715
|
+
modules = {
|
|
716
|
+
...modules,
|
|
717
|
+
sources: [
|
|
718
|
+
...new Set([
|
|
719
|
+
...modules.sources,
|
|
720
|
+
...projects.flatMap((project) => project.kotlin),
|
|
721
|
+
]),
|
|
722
|
+
],
|
|
723
|
+
javaSpecs: [
|
|
724
|
+
...new Set([
|
|
725
|
+
...(modules.javaSpecs ?? []),
|
|
726
|
+
...projects.flatMap((project) => project.java),
|
|
727
|
+
]),
|
|
728
|
+
],
|
|
729
|
+
}
|
|
730
|
+
}
|
|
706
731
|
const toolchain = readToolchain(moduleRoot)
|
|
707
732
|
const language = appKotlinLanguage(root)
|
|
708
733
|
const scanned = await scanExpoTypes(toolchain, moduleRoot, modules.sources)
|
|
@@ -731,11 +756,12 @@ export async function expoKotlinArtifact(
|
|
|
731
756
|
' ))',
|
|
732
757
|
'}',
|
|
733
758
|
'',
|
|
734
|
-
'@JsExport fun peachSetAndroidDeviceInfo(model: String, release: String, apiLevel: Int, locale: String, timeZone: String, settings: String) {',
|
|
759
|
+
'@JsExport fun peachSetAndroidDeviceInfo(model: String, release: String, apiLevel: Int, locale: String, timeZone: String, settings: String, properties: String) {',
|
|
735
760
|
' android.os.Build.setDevice(model, release, apiLevel)',
|
|
736
761
|
' java.util.Locale.setDefault(java.util.Locale.forLanguageTag(locale))',
|
|
737
762
|
' java.util.TimeZone.setDevice(timeZone)',
|
|
738
763
|
' dev.peach.android.ProcessSettings.configure(settings)',
|
|
764
|
+
' android.os.SystemProperties.configure(properties)',
|
|
739
765
|
' PeachExpoRuntime.configureProcessLocale(locale)',
|
|
740
766
|
'}',
|
|
741
767
|
'',
|
|
@@ -757,6 +783,7 @@ export async function expoKotlinArtifact(
|
|
|
757
783
|
toolchain,
|
|
758
784
|
language,
|
|
759
785
|
sources: modules.sources.filter((file) => !rewritten.has(file)),
|
|
786
|
+
javaSpecs: modules.javaSpecs,
|
|
760
787
|
generated: {
|
|
761
788
|
...types.generated,
|
|
762
789
|
'PeachExpoEntry.kt': entry,
|
|
@@ -1109,6 +1136,7 @@ async function compileKotlin(
|
|
|
1109
1136
|
toolchain: KotlinToolchain
|
|
1110
1137
|
language: string
|
|
1111
1138
|
sources: string[]
|
|
1139
|
+
javaSpecs?: string[]
|
|
1112
1140
|
generated: Record<string, string>
|
|
1113
1141
|
// the app source a generated file rewrites, which its diagnostics name
|
|
1114
1142
|
origins?: Record<string, string>
|
|
@@ -1137,10 +1165,14 @@ async function compileKotlin(
|
|
|
1137
1165
|
usesCompose,
|
|
1138
1166
|
usesSerialization = false,
|
|
1139
1167
|
viewModels,
|
|
1168
|
+
javaSpecs = [],
|
|
1140
1169
|
} = input
|
|
1141
1170
|
const sdk = await loadKotlinSdk(moduleRoot, toolchain)
|
|
1142
1171
|
const plugin = path.join(sdk.root, 'plugin.jar')
|
|
1143
1172
|
const sdkLibrary = path.join(sdk.root, `${sdkProfile}.klib`)
|
|
1173
|
+
const java = javaSpecs.length
|
|
1174
|
+
? await readJavaModuleSpecs(javaSpecs, sdk.root, await ensureToolchain(toolchain))
|
|
1175
|
+
: null
|
|
1144
1176
|
const generated = {
|
|
1145
1177
|
...sourceGenerated,
|
|
1146
1178
|
'PeachBindingsInitialization.kt': [
|
|
@@ -1152,6 +1184,7 @@ async function compileKotlin(
|
|
|
1152
1184
|
'@JsExport fun peachInitializeModuleBindings() {',
|
|
1153
1185
|
' if (!initialized) {',
|
|
1154
1186
|
' requireIncomingBindings()',
|
|
1187
|
+
...(java ? [` ${JAVA_BINDINGS_INITIALIZER}()`] : []),
|
|
1155
1188
|
' initializeApplicationIncomingBindings()',
|
|
1156
1189
|
...(sourceGenerated['PeachViews.kt']
|
|
1157
1190
|
? [' initializeApplicationViews()']
|
|
@@ -1175,6 +1208,7 @@ async function compileKotlin(
|
|
|
1175
1208
|
sdk: sdk.sha256,
|
|
1176
1209
|
sdkProfile,
|
|
1177
1210
|
usesSerialization,
|
|
1211
|
+
java: java?.sha256,
|
|
1178
1212
|
}),
|
|
1179
1213
|
)
|
|
1180
1214
|
for (const { file, bytes } of inputs)
|
|
@@ -1210,6 +1244,8 @@ async function compileKotlin(
|
|
|
1210
1244
|
}
|
|
1211
1245
|
fs.mkdirSync(buildsDir(), { recursive: true })
|
|
1212
1246
|
const work = fs.mkdtempSync(path.join(buildsDir(), `${hash}-`))
|
|
1247
|
+
const javaLibrary = java ? path.join(work, 'java-abi', 'java-abi.klib') : null
|
|
1248
|
+
if (javaLibrary) libraries.push(javaLibrary)
|
|
1213
1249
|
const generatedDir = path.join(work, 'generated')
|
|
1214
1250
|
fs.mkdirSync(generatedDir)
|
|
1215
1251
|
for (const [name, text] of Object.entries(generated))
|
|
@@ -1234,7 +1270,7 @@ async function compileKotlin(
|
|
|
1234
1270
|
[
|
|
1235
1271
|
...common,
|
|
1236
1272
|
`-Xplugin=${plugin}`,
|
|
1237
|
-
`-Xfriend-modules=${sdkLibrary}`,
|
|
1273
|
+
`-Xfriend-modules=${[sdkLibrary, ...(javaLibrary ? [javaLibrary] : [])].join(path.delimiter)}`,
|
|
1238
1274
|
...(usesCompose
|
|
1239
1275
|
? [
|
|
1240
1276
|
`-Xplugin=${path.resolve(path.dirname(kotlinc), '../lib/compose-compiler-plugin.jar')}`,
|
|
@@ -1261,6 +1297,17 @@ async function compileKotlin(
|
|
|
1261
1297
|
{ maxBuffer: 16 * 1024 * 1024 },
|
|
1262
1298
|
)
|
|
1263
1299
|
try {
|
|
1300
|
+
if (java)
|
|
1301
|
+
await compileJavaModuleAbi({
|
|
1302
|
+
specs: java,
|
|
1303
|
+
work,
|
|
1304
|
+
hash,
|
|
1305
|
+
toolchain: ready,
|
|
1306
|
+
libraries: libraries.filter((library) => library !== javaLibrary),
|
|
1307
|
+
sdkRoot: sdk.root,
|
|
1308
|
+
sdkLibrary,
|
|
1309
|
+
language,
|
|
1310
|
+
})
|
|
1264
1311
|
// the compiler resolves the app's classes and constructors: a first
|
|
1265
1312
|
// klib's ABI names the view models the generated factory builds. an app
|
|
1266
1313
|
// whose sources never name ViewModel declares none and skips it
|
|
@@ -1397,6 +1444,11 @@ async function compileKotlin(
|
|
|
1397
1444
|
output = output
|
|
1398
1445
|
.replaceAll(target, file)
|
|
1399
1446
|
.replaceAll(path.relative(process.cwd(), target), file)
|
|
1447
|
+
for (const [index, spec] of (java?.specs ?? []).entries())
|
|
1448
|
+
output = output.replaceAll(
|
|
1449
|
+
path.join(work, 'java-abi', `Spec${index}.kt`),
|
|
1450
|
+
spec.file,
|
|
1451
|
+
)
|
|
1400
1452
|
throw new Error(
|
|
1401
1453
|
`kotlin compile failed:\n${output.trim() || (error instanceof Error ? error.message : String(error))}`,
|
|
1402
1454
|
)
|
package/src/nitro-source.ts
CHANGED
|
@@ -1,15 +1,13 @@
|
|
|
1
|
-
import { execFile } from 'node:child_process'
|
|
2
1
|
import { createHash } from 'node:crypto'
|
|
3
2
|
import fs from 'node:fs'
|
|
4
3
|
import { createRequire } from 'node:module'
|
|
5
4
|
import path from 'node:path'
|
|
6
|
-
import { promisify } from 'node:util'
|
|
7
|
-
import { PEACH_FORKED_SCRIPT_ENV } from './forked-script-contract'
|
|
8
5
|
import {
|
|
9
6
|
nitroDependencyIncludes,
|
|
10
7
|
nitroDependencyReader,
|
|
11
8
|
prepareNitroDependencies,
|
|
12
9
|
} from './nitro-dependencies'
|
|
10
|
+
import { reactNativeAutolinkingConfig } from './react-native-autolinking'
|
|
13
11
|
import { projectSourceKey, readPackageFile } from './swift-source-file'
|
|
14
12
|
import { MAX_SWIFT_COMPILE_SOURCE_BYTES } from './swift-submit'
|
|
15
13
|
import type { NitroView } from './nitroContract'
|
|
@@ -33,24 +31,6 @@ export type NitroSourceOptions = { packageRoots: string[]; answers?: NitroAnswer
|
|
|
33
31
|
}
|
|
34
32
|
)
|
|
35
33
|
|
|
36
|
-
const run = promisify(execFile)
|
|
37
|
-
|
|
38
|
-
// an autolinker that exits 0 with unparseable stdout names what it printed,
|
|
39
|
-
// so a truncated or empty pipe read is distinguishable from a bad command.
|
|
40
|
-
function autolinkerJson(
|
|
41
|
-
command: string,
|
|
42
|
-
output: { stdout: string; stderr: string },
|
|
43
|
-
): unknown {
|
|
44
|
-
try {
|
|
45
|
-
return JSON.parse(output.stdout)
|
|
46
|
-
} catch (cause) {
|
|
47
|
-
throw new Error(
|
|
48
|
-
`${command} exited 0 with ${output.stdout.length} bytes of stdout that is not JSON (${cause instanceof Error ? cause.message : cause}); stderr: ${output.stderr.trim().slice(0, 500) || '(empty)'}; stdout: ${output.stdout.slice(0, 200) || '(empty)'}`,
|
|
49
|
-
{ cause },
|
|
50
|
-
)
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
34
|
type NitroPackage = { name: string; version: string; root: string }
|
|
55
35
|
|
|
56
36
|
export const nitroProviderTarget = (provider: Pick<NitroPackage, 'name'>) =>
|
|
@@ -378,150 +358,6 @@ export function selectNitroProviders(
|
|
|
378
358
|
.filter(({ selected }) => selected.length > 0)
|
|
379
359
|
}
|
|
380
360
|
|
|
381
|
-
// whether the app's installed CLI advertises a command or flag. old Expo
|
|
382
|
-
// autolinkers predate `react-native-config` and old React Native CLIs predate
|
|
383
|
-
// `config --platform`, so both are probed rather than assumed.
|
|
384
|
-
// the cli runs from the app root, as its real command does: react-native cli
|
|
385
|
-
// 6 exits 1 with no output when it finds no project in its cwd
|
|
386
|
-
async function cliAdvertises(
|
|
387
|
-
cli: string,
|
|
388
|
-
args: string[],
|
|
389
|
-
token: string,
|
|
390
|
-
appRoot: string,
|
|
391
|
-
signal?: AbortSignal,
|
|
392
|
-
): Promise<boolean> {
|
|
393
|
-
const { stdout, stderr } = await run('node', [cli, ...args], {
|
|
394
|
-
cwd: appRoot,
|
|
395
|
-
signal,
|
|
396
|
-
timeout: 30_000,
|
|
397
|
-
maxBuffer: 1 * 1024 * 1024,
|
|
398
|
-
env: { ...process.env, [PEACH_FORKED_SCRIPT_ENV]: '1' },
|
|
399
|
-
})
|
|
400
|
-
if (!stdout.trim())
|
|
401
|
-
throw new Error(
|
|
402
|
-
`${cli} ${args.join(' ')} returned empty help output${stderr.trim() ? `: ${stderr.trim()}` : ''}`,
|
|
403
|
-
)
|
|
404
|
-
return stdout.includes(token)
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
function declaresDependency(appRoot: string, name: string): boolean {
|
|
408
|
-
const manifest: unknown = JSON.parse(
|
|
409
|
-
fs.readFileSync(path.join(appRoot, 'package.json'), 'utf8'),
|
|
410
|
-
)
|
|
411
|
-
return (
|
|
412
|
-
object(manifest) &&
|
|
413
|
-
['dependencies', 'devDependencies'].some((field) => {
|
|
414
|
-
const group = manifest[field]
|
|
415
|
-
return object(group) && Object.hasOwn(group, name)
|
|
416
|
-
})
|
|
417
|
-
)
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
async function autolinkingConfig(
|
|
421
|
-
appRoot: string,
|
|
422
|
-
appRequire: NodeJS.Require,
|
|
423
|
-
signal?: AbortSignal,
|
|
424
|
-
): Promise<unknown> {
|
|
425
|
-
// a pnpm app reaches the autolinker only through expo's own dependencies
|
|
426
|
-
const expoManifest = appRequire.resolve
|
|
427
|
-
.paths('expo')
|
|
428
|
-
?.map((directory) => path.join(directory, 'expo/package.json'))
|
|
429
|
-
.find((manifest) => fs.existsSync(manifest))
|
|
430
|
-
const expoRequire = expoManifest
|
|
431
|
-
? createRequire(fs.realpathSync(expoManifest))
|
|
432
|
-
: appRequire
|
|
433
|
-
// the app's autolinking contract is the command its Podfile hands
|
|
434
|
-
// use_native_modules!: expo's react-native-config, or the React Native CLI
|
|
435
|
-
// when it passes none (expo's loader ignores react-native.config.cjs, which
|
|
436
|
-
// the CLI reads). a gitignored ios project is prebuild output, so without a
|
|
437
|
-
// Podfile the prebuild that would write it answers: expo's for an app that
|
|
438
|
-
// declares expo, One's bare use_native_modules! otherwise. an Expo Podfile
|
|
439
|
-
// holds both commands and runs the CLI's when EXPO_USE_COMMUNITY_AUTOLINKING
|
|
440
|
-
// is 1.
|
|
441
|
-
const podfile = path.join(appRoot, 'ios/Podfile')
|
|
442
|
-
const asksExpo =
|
|
443
|
-
process.env.EXPO_USE_COMMUNITY_AUTOLINKING !== '1' &&
|
|
444
|
-
(fs.existsSync(podfile)
|
|
445
|
-
? fs.readFileSync(podfile, 'utf8').includes('expo-modules-autolinking')
|
|
446
|
-
: declaresDependency(appRoot, 'expo'))
|
|
447
|
-
const expoAutolinker = asksExpo
|
|
448
|
-
? [appRequire, expoRequire]
|
|
449
|
-
.flatMap((req) => req.resolve.paths('expo-modules-autolinking') ?? [])
|
|
450
|
-
.map((directory) => path.join(directory, 'expo-modules-autolinking/package.json'))
|
|
451
|
-
.find((manifest) => fs.existsSync(manifest))
|
|
452
|
-
: undefined
|
|
453
|
-
if (expoAutolinker) {
|
|
454
|
-
const cli = path.join(path.dirname(expoAutolinker), 'bin/expo-modules-autolinking.js')
|
|
455
|
-
// autolinkers before the `react-native-config` command (Expo SDK 48 ships
|
|
456
|
-
// 1.2.0) cannot answer the React Native dependency map; those apps fall
|
|
457
|
-
// through to their own `react-native config`, the command their Podfile's
|
|
458
|
-
// use_native_modules! runs.
|
|
459
|
-
if (await cliAdvertises(cli, ['--help'], 'react-native-config', appRoot, signal)) {
|
|
460
|
-
const args = [
|
|
461
|
-
cli,
|
|
462
|
-
'react-native-config',
|
|
463
|
-
'--json',
|
|
464
|
-
'--platform',
|
|
465
|
-
'ios',
|
|
466
|
-
'--project-root',
|
|
467
|
-
appRoot,
|
|
468
|
-
]
|
|
469
|
-
return autolinkerJson(
|
|
470
|
-
'expo-modules-autolinking react-native-config',
|
|
471
|
-
await run('node', args, {
|
|
472
|
-
timeout: 30_000,
|
|
473
|
-
maxBuffer: 8 * 1024 * 1024,
|
|
474
|
-
env: { ...process.env, [PEACH_FORKED_SCRIPT_ENV]: '1' },
|
|
475
|
-
// expo's react-native.config.js calls findProjectRootSync() on the
|
|
476
|
-
// process cwd and ignores --project-root. the corpus bridge starts
|
|
477
|
-
// outside the app, where that walk finds no package.json.
|
|
478
|
-
cwd: appRoot,
|
|
479
|
-
signal,
|
|
480
|
-
}),
|
|
481
|
-
)
|
|
482
|
-
}
|
|
483
|
-
}
|
|
484
|
-
// the app's own `react-native config`, the command its Podfile runs, so any
|
|
485
|
-
// CLI release answers through one interface
|
|
486
|
-
// One's generated Podfile resolves the community CLI from vxrn.
|
|
487
|
-
const cliRequire = declaresDependency(appRoot, 'one')
|
|
488
|
-
? createRequire(
|
|
489
|
-
createRequire(appRequire.resolve('one/package.json')).resolve(
|
|
490
|
-
'vxrn/package.json',
|
|
491
|
-
),
|
|
492
|
-
)
|
|
493
|
-
: appRequire
|
|
494
|
-
const cliManifest = cliRequire.resolve('@react-native-community/cli/package.json')
|
|
495
|
-
const cli: unknown = JSON.parse(fs.readFileSync(cliManifest, 'utf8'))
|
|
496
|
-
const bins = object(cli) && object(cli.bin) ? cli.bin : undefined
|
|
497
|
-
// the bin was renamed from `react-native` to `rnc-cli` along the way; both
|
|
498
|
-
// point at the same entry.
|
|
499
|
-
const bin = [bins?.['rnc-cli'], bins?.['react-native']].find(
|
|
500
|
-
(entry): entry is string => typeof entry === 'string',
|
|
501
|
-
)
|
|
502
|
-
if (bin === undefined) throw new Error(`${cliManifest} declares no CLI bin`)
|
|
503
|
-
const cliPath = path.join(path.dirname(cliManifest), bin)
|
|
504
|
-
const configArgs = (await cliAdvertises(
|
|
505
|
-
cliPath,
|
|
506
|
-
['config', '--help'],
|
|
507
|
-
'--platform',
|
|
508
|
-
appRoot,
|
|
509
|
-
signal,
|
|
510
|
-
))
|
|
511
|
-
? ['config', '--platform', 'ios']
|
|
512
|
-
: ['config']
|
|
513
|
-
return autolinkerJson(
|
|
514
|
-
`react-native ${configArgs.join(' ')}`,
|
|
515
|
-
await run('node', [cliPath, ...configArgs], {
|
|
516
|
-
cwd: appRoot,
|
|
517
|
-
signal,
|
|
518
|
-
timeout: 30_000,
|
|
519
|
-
maxBuffer: 16 * 1024 * 1024,
|
|
520
|
-
env: { ...process.env, [PEACH_FORKED_SCRIPT_ENV]: '1', NO_COLOR: '1' },
|
|
521
|
-
}),
|
|
522
|
-
)
|
|
523
|
-
}
|
|
524
|
-
|
|
525
361
|
/** the packages react native's iOS autolinking links into the app that ship a
|
|
526
362
|
* generated Nitro iOS initializer. The app's own autolinker resolves the
|
|
527
363
|
* dependencies, so local roots from react-native.config and platform exclusions
|
|
@@ -541,8 +377,7 @@ export async function iosAutolinkingConfig(
|
|
|
541
377
|
appRoot: string,
|
|
542
378
|
signal?: AbortSignal,
|
|
543
379
|
): Promise<unknown> {
|
|
544
|
-
|
|
545
|
-
return autolinkingConfig(root, createRequire(path.join(root, 'package.json')), signal)
|
|
380
|
+
return reactNativeAutolinkingConfig(appRoot, 'ios', signal)
|
|
546
381
|
}
|
|
547
382
|
|
|
548
383
|
/** every dependency React Native iOS autolinking links into the app, with the
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { execFile } from 'node:child_process'
|
|
2
|
+
import fs from 'node:fs'
|
|
3
|
+
import { createRequire } from 'node:module'
|
|
4
|
+
import path from 'node:path'
|
|
5
|
+
import { promisify } from 'node:util'
|
|
6
|
+
import { PEACH_FORKED_SCRIPT_ENV } from './forked-script-contract'
|
|
7
|
+
import { isRecord } from './is-record'
|
|
8
|
+
|
|
9
|
+
const run = promisify(execFile)
|
|
10
|
+
function autolinkerJson(
|
|
11
|
+
command: string,
|
|
12
|
+
output: { stdout: string; stderr: string },
|
|
13
|
+
): unknown {
|
|
14
|
+
try {
|
|
15
|
+
return JSON.parse(output.stdout)
|
|
16
|
+
} catch (cause) {
|
|
17
|
+
throw new Error(
|
|
18
|
+
`${command} exited 0 with ${output.stdout.length} bytes of stdout that is not JSON (${cause instanceof Error ? cause.message : cause}); stderr: ${output.stderr.trim().slice(0, 500) || '(empty)'}; stdout: ${output.stdout.slice(0, 200) || '(empty)'}`,
|
|
19
|
+
{ cause },
|
|
20
|
+
)
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// old CLI releases lack platform flags and Expo’s React Native command.
|
|
25
|
+
async function cliAdvertises(
|
|
26
|
+
cli: string,
|
|
27
|
+
args: string[],
|
|
28
|
+
token: string,
|
|
29
|
+
appRoot: string,
|
|
30
|
+
signal?: AbortSignal,
|
|
31
|
+
): Promise<boolean> {
|
|
32
|
+
const { stdout, stderr } = await run('node', [cli, ...args], {
|
|
33
|
+
cwd: appRoot,
|
|
34
|
+
signal,
|
|
35
|
+
timeout: 30_000,
|
|
36
|
+
maxBuffer: 1 * 1024 * 1024,
|
|
37
|
+
env: { ...process.env, [PEACH_FORKED_SCRIPT_ENV]: '1' },
|
|
38
|
+
})
|
|
39
|
+
if (!stdout.trim())
|
|
40
|
+
throw new Error(
|
|
41
|
+
`${cli} ${args.join(' ')} returned empty help output${stderr.trim() ? `: ${stderr.trim()}` : ''}`,
|
|
42
|
+
)
|
|
43
|
+
return stdout.includes(token)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function declaresDependency(appRoot: string, name: string): boolean {
|
|
47
|
+
const manifest: unknown = JSON.parse(
|
|
48
|
+
fs.readFileSync(path.join(appRoot, 'package.json'), 'utf8'),
|
|
49
|
+
)
|
|
50
|
+
return (
|
|
51
|
+
isRecord(manifest) &&
|
|
52
|
+
['dependencies', 'devDependencies'].some((field) => {
|
|
53
|
+
const group = manifest[field]
|
|
54
|
+
return isRecord(group) && Object.hasOwn(group, name)
|
|
55
|
+
})
|
|
56
|
+
)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export async function reactNativeAutolinkingConfig(
|
|
60
|
+
appRoot: string,
|
|
61
|
+
platform: 'ios' | 'android',
|
|
62
|
+
signal?: AbortSignal,
|
|
63
|
+
): Promise<unknown> {
|
|
64
|
+
appRoot = fs.realpathSync(appRoot)
|
|
65
|
+
const appRequire = createRequire(path.join(appRoot, 'package.json'))
|
|
66
|
+
const expoManifest = appRequire.resolve
|
|
67
|
+
.paths('expo')
|
|
68
|
+
?.map((directory) => path.join(directory, 'expo/package.json'))
|
|
69
|
+
.find((manifest) => fs.existsSync(manifest))
|
|
70
|
+
const expoRequire = expoManifest
|
|
71
|
+
? createRequire(fs.realpathSync(expoManifest))
|
|
72
|
+
: appRequire
|
|
73
|
+
const nativeFile = (
|
|
74
|
+
platform === 'ios'
|
|
75
|
+
? ['ios/Podfile']
|
|
76
|
+
: ['android/settings.gradle', 'android/settings.gradle.kts']
|
|
77
|
+
)
|
|
78
|
+
.map((name) => path.join(appRoot, name))
|
|
79
|
+
.find((file) => fs.existsSync(file))
|
|
80
|
+
const nativeSource = nativeFile ? fs.readFileSync(nativeFile, 'utf8') : null
|
|
81
|
+
const asksExpo =
|
|
82
|
+
process.env.EXPO_USE_COMMUNITY_AUTOLINKING !== '1' &&
|
|
83
|
+
(nativeSource === null
|
|
84
|
+
? declaresDependency(appRoot, 'expo')
|
|
85
|
+
: nativeSource.includes('expo-modules-autolinking') ||
|
|
86
|
+
(platform === 'android' &&
|
|
87
|
+
nativeSource.includes('expoAutolinking.rnConfigCommand')))
|
|
88
|
+
const expoAutolinker = asksExpo
|
|
89
|
+
? [appRequire, expoRequire]
|
|
90
|
+
.flatMap((req) => req.resolve.paths('expo-modules-autolinking') ?? [])
|
|
91
|
+
.map((directory) => path.join(directory, 'expo-modules-autolinking/package.json'))
|
|
92
|
+
.find((manifest) => fs.existsSync(manifest))
|
|
93
|
+
: undefined
|
|
94
|
+
if (expoAutolinker) {
|
|
95
|
+
const cli = path.join(path.dirname(expoAutolinker), 'bin/expo-modules-autolinking.js')
|
|
96
|
+
if (await cliAdvertises(cli, ['--help'], 'react-native-config', appRoot, signal)) {
|
|
97
|
+
const args = [
|
|
98
|
+
cli,
|
|
99
|
+
'react-native-config',
|
|
100
|
+
'--json',
|
|
101
|
+
'--platform',
|
|
102
|
+
platform,
|
|
103
|
+
'--project-root',
|
|
104
|
+
appRoot,
|
|
105
|
+
]
|
|
106
|
+
return autolinkerJson(
|
|
107
|
+
'expo-modules-autolinking react-native-config',
|
|
108
|
+
await run('node', args, {
|
|
109
|
+
timeout: 30_000,
|
|
110
|
+
maxBuffer: 8 * 1024 * 1024,
|
|
111
|
+
env: { ...process.env, [PEACH_FORKED_SCRIPT_ENV]: '1' },
|
|
112
|
+
// expo configuration resolves its project from cwd.
|
|
113
|
+
cwd: appRoot,
|
|
114
|
+
signal,
|
|
115
|
+
}),
|
|
116
|
+
)
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
const cliRequire = declaresDependency(appRoot, 'one')
|
|
120
|
+
? createRequire(
|
|
121
|
+
createRequire(appRequire.resolve('one/package.json')).resolve(
|
|
122
|
+
'vxrn/package.json',
|
|
123
|
+
),
|
|
124
|
+
)
|
|
125
|
+
: appRequire
|
|
126
|
+
const cliManifest = cliRequire.resolve('@react-native-community/cli/package.json')
|
|
127
|
+
const cli: unknown = JSON.parse(fs.readFileSync(cliManifest, 'utf8'))
|
|
128
|
+
const bins = isRecord(cli) && isRecord(cli.bin) ? cli.bin : undefined
|
|
129
|
+
const bin = [bins?.['rnc-cli'], bins?.['react-native']].find(
|
|
130
|
+
(entry): entry is string => typeof entry === 'string',
|
|
131
|
+
)
|
|
132
|
+
if (bin === undefined) throw new Error(`${cliManifest} declares no CLI bin`)
|
|
133
|
+
const cliPath = path.join(path.dirname(cliManifest), bin)
|
|
134
|
+
const configArgs = (await cliAdvertises(
|
|
135
|
+
cliPath,
|
|
136
|
+
['config', '--help'],
|
|
137
|
+
'--platform',
|
|
138
|
+
appRoot,
|
|
139
|
+
signal,
|
|
140
|
+
))
|
|
141
|
+
? ['config', '--platform', platform]
|
|
142
|
+
: ['config']
|
|
143
|
+
return autolinkerJson(
|
|
144
|
+
`react-native ${configArgs.join(' ')}`,
|
|
145
|
+
await run('node', [cliPath, ...configArgs], {
|
|
146
|
+
cwd: appRoot,
|
|
147
|
+
signal,
|
|
148
|
+
timeout: 30_000,
|
|
149
|
+
maxBuffer: 16 * 1024 * 1024,
|
|
150
|
+
env: { ...process.env, [PEACH_FORKED_SCRIPT_ENV]: '1', NO_COLOR: '1' },
|
|
151
|
+
}),
|
|
152
|
+
)
|
|
153
|
+
}
|