peachsim 0.1.2185 → 0.1.2187
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/cli/commands/cpu-profile.ts +6 -2
- package/cli/commands/shell-url.ts +3 -6
- 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 +2755 -2385
- 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 +2 -2
- package/dist-lib/dev-bundle-resolution.cjs +1 -1
- package/dist-lib/expo-swift-bundle.cjs +3265 -3223
- package/dist-lib/expo-swift-metadata.cjs +2 -1
- package/dist-lib/expo-swift-metadata.mjs +2 -1
- package/dist-lib/home-paths.cjs +1 -1
- package/dist-lib/host/bridge-host.cjs +4587 -4217
- 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 +474 -283
- 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 +647 -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 +775 -584
- package/dist-lib/native-app-identity-project.cjs +45 -4
- package/dist-lib/native-app-identity-project.mjs +45 -4
- package/dist-lib/native-app-identity.cjs +40 -2
- package/dist-lib/native-app-identity.mjs +40 -2
- 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 +5257 -4878
- 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 +1 -1
- package/dist-lib/swift.cjs +1 -1
- package/dist-lib/vite-swift.cjs +3 -3
- 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/render-worker/compositor-backend.d.ts +1 -0
- package/kotlin/sdk.tar.gz +0 -0
- package/package.json +1 -1
- package/runtime-companion.json +1 -1
- package/sdk/ios/System/Library/Frameworks/CFNetwork.framework/Headers/CFNetwork.h +10 -0
- package/sdk/ios/System/Library/Frameworks/UIKit.framework/Headers/UIImageAsset.h +1 -1
- 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/expo-native-workspace.ts +7 -1
- package/src/java-module-abi.ts +145 -0
- package/src/javaAbiContract.ts +14 -0
- package/src/kotlin-host.ts +57 -3
- package/src/native-app-identity-project.ts +10 -2
- package/src/native-app-identity.ts +15 -1
- package/src/nativeAppIconContract.ts +37 -0
- package/src/nitro-source.ts +2 -167
- package/src/react-native-autolinking.ts +153 -0
- package/src/swift-installed-targets.ts +8 -0
|
@@ -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,10 +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) {',
|
|
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)',
|
|
143
|
+
' dev.peach.android.ProcessSettings.configure(settings)',
|
|
144
|
+
' android.os.SystemProperties.configure(properties)',
|
|
138
145
|
'}',
|
|
139
146
|
'',
|
|
140
147
|
'@OptIn(kotlin.js.ExperimentalJsExport::class, kotlinx.coroutines.DelicateCoroutinesApi::class)',
|
|
@@ -702,6 +709,25 @@ export async function expoKotlinArtifact(
|
|
|
702
709
|
origin: string,
|
|
703
710
|
modules: ExpoKotlinModules,
|
|
704
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
|
+
}
|
|
705
731
|
const toolchain = readToolchain(moduleRoot)
|
|
706
732
|
const language = appKotlinLanguage(root)
|
|
707
733
|
const scanned = await scanExpoTypes(toolchain, moduleRoot, modules.sources)
|
|
@@ -730,10 +756,12 @@ export async function expoKotlinArtifact(
|
|
|
730
756
|
' ))',
|
|
731
757
|
'}',
|
|
732
758
|
'',
|
|
733
|
-
'@JsExport fun peachSetAndroidDeviceInfo(model: String, release: String, apiLevel: Int, locale: String, timeZone: String) {',
|
|
759
|
+
'@JsExport fun peachSetAndroidDeviceInfo(model: String, release: String, apiLevel: Int, locale: String, timeZone: String, settings: String, properties: String) {',
|
|
734
760
|
' android.os.Build.setDevice(model, release, apiLevel)',
|
|
735
761
|
' java.util.Locale.setDefault(java.util.Locale.forLanguageTag(locale))',
|
|
736
762
|
' java.util.TimeZone.setDevice(timeZone)',
|
|
763
|
+
' dev.peach.android.ProcessSettings.configure(settings)',
|
|
764
|
+
' android.os.SystemProperties.configure(properties)',
|
|
737
765
|
' PeachExpoRuntime.configureProcessLocale(locale)',
|
|
738
766
|
'}',
|
|
739
767
|
'',
|
|
@@ -755,6 +783,7 @@ export async function expoKotlinArtifact(
|
|
|
755
783
|
toolchain,
|
|
756
784
|
language,
|
|
757
785
|
sources: modules.sources.filter((file) => !rewritten.has(file)),
|
|
786
|
+
javaSpecs: modules.javaSpecs,
|
|
758
787
|
generated: {
|
|
759
788
|
...types.generated,
|
|
760
789
|
'PeachExpoEntry.kt': entry,
|
|
@@ -1107,6 +1136,7 @@ async function compileKotlin(
|
|
|
1107
1136
|
toolchain: KotlinToolchain
|
|
1108
1137
|
language: string
|
|
1109
1138
|
sources: string[]
|
|
1139
|
+
javaSpecs?: string[]
|
|
1110
1140
|
generated: Record<string, string>
|
|
1111
1141
|
// the app source a generated file rewrites, which its diagnostics name
|
|
1112
1142
|
origins?: Record<string, string>
|
|
@@ -1135,10 +1165,14 @@ async function compileKotlin(
|
|
|
1135
1165
|
usesCompose,
|
|
1136
1166
|
usesSerialization = false,
|
|
1137
1167
|
viewModels,
|
|
1168
|
+
javaSpecs = [],
|
|
1138
1169
|
} = input
|
|
1139
1170
|
const sdk = await loadKotlinSdk(moduleRoot, toolchain)
|
|
1140
1171
|
const plugin = path.join(sdk.root, 'plugin.jar')
|
|
1141
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
|
|
1142
1176
|
const generated = {
|
|
1143
1177
|
...sourceGenerated,
|
|
1144
1178
|
'PeachBindingsInitialization.kt': [
|
|
@@ -1150,6 +1184,7 @@ async function compileKotlin(
|
|
|
1150
1184
|
'@JsExport fun peachInitializeModuleBindings() {',
|
|
1151
1185
|
' if (!initialized) {',
|
|
1152
1186
|
' requireIncomingBindings()',
|
|
1187
|
+
...(java ? [` ${JAVA_BINDINGS_INITIALIZER}()`] : []),
|
|
1153
1188
|
' initializeApplicationIncomingBindings()',
|
|
1154
1189
|
...(sourceGenerated['PeachViews.kt']
|
|
1155
1190
|
? [' initializeApplicationViews()']
|
|
@@ -1173,6 +1208,7 @@ async function compileKotlin(
|
|
|
1173
1208
|
sdk: sdk.sha256,
|
|
1174
1209
|
sdkProfile,
|
|
1175
1210
|
usesSerialization,
|
|
1211
|
+
java: java?.sha256,
|
|
1176
1212
|
}),
|
|
1177
1213
|
)
|
|
1178
1214
|
for (const { file, bytes } of inputs)
|
|
@@ -1208,6 +1244,8 @@ async function compileKotlin(
|
|
|
1208
1244
|
}
|
|
1209
1245
|
fs.mkdirSync(buildsDir(), { recursive: true })
|
|
1210
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)
|
|
1211
1249
|
const generatedDir = path.join(work, 'generated')
|
|
1212
1250
|
fs.mkdirSync(generatedDir)
|
|
1213
1251
|
for (const [name, text] of Object.entries(generated))
|
|
@@ -1232,7 +1270,7 @@ async function compileKotlin(
|
|
|
1232
1270
|
[
|
|
1233
1271
|
...common,
|
|
1234
1272
|
`-Xplugin=${plugin}`,
|
|
1235
|
-
`-Xfriend-modules=${sdkLibrary}`,
|
|
1273
|
+
`-Xfriend-modules=${[sdkLibrary, ...(javaLibrary ? [javaLibrary] : [])].join(path.delimiter)}`,
|
|
1236
1274
|
...(usesCompose
|
|
1237
1275
|
? [
|
|
1238
1276
|
`-Xplugin=${path.resolve(path.dirname(kotlinc), '../lib/compose-compiler-plugin.jar')}`,
|
|
@@ -1259,6 +1297,17 @@ async function compileKotlin(
|
|
|
1259
1297
|
{ maxBuffer: 16 * 1024 * 1024 },
|
|
1260
1298
|
)
|
|
1261
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
|
+
})
|
|
1262
1311
|
// the compiler resolves the app's classes and constructors: a first
|
|
1263
1312
|
// klib's ABI names the view models the generated factory builds. an app
|
|
1264
1313
|
// whose sources never name ViewModel declares none and skips it
|
|
@@ -1395,6 +1444,11 @@ async function compileKotlin(
|
|
|
1395
1444
|
output = output
|
|
1396
1445
|
.replaceAll(target, file)
|
|
1397
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
|
+
)
|
|
1398
1452
|
throw new Error(
|
|
1399
1453
|
`kotlin compile failed:\n${output.trim() || (error instanceof Error ? error.message : String(error))}`,
|
|
1400
1454
|
)
|
|
@@ -10,7 +10,10 @@ import {
|
|
|
10
10
|
readGoogleSignInConfiguration,
|
|
11
11
|
selectNativeAppIdentityReader,
|
|
12
12
|
} from './native-app-identity'
|
|
13
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
resolveNativeAppIconArtwork,
|
|
15
|
+
resolveNativeBundleIcons,
|
|
16
|
+
} from './nativeAppIconContract'
|
|
14
17
|
import { parsePlistSource } from './plist'
|
|
15
18
|
import { NATIVE_GRAPH_SDK_VERSION } from './swift-objc-standin'
|
|
16
19
|
import {
|
|
@@ -91,11 +94,15 @@ export function readNativeAppIdentityFromXcodeTarget(
|
|
|
91
94
|
}
|
|
92
95
|
const alternateSetting =
|
|
93
96
|
settings?.('ASSETCATALOG_COMPILER_ALTERNATE_APPICON_NAMES') ?? ''
|
|
97
|
+
const catalogAlternateNames: string[] = []
|
|
94
98
|
for (const match of alternateSetting
|
|
95
99
|
.replace(/\\"/g, '"')
|
|
96
100
|
.matchAll(/"([^"]*)"|([^\s(),]+)/g)) {
|
|
97
101
|
const val = match[1] ?? match[2]
|
|
98
|
-
if (val)
|
|
102
|
+
if (val) {
|
|
103
|
+
alternateIconNames.add(val)
|
|
104
|
+
catalogAlternateNames.push(val)
|
|
105
|
+
}
|
|
99
106
|
}
|
|
100
107
|
|
|
101
108
|
const permissionUsageDescriptions: Record<string, unknown> = {}
|
|
@@ -204,6 +211,7 @@ export function readNativeAppIdentityFromXcodeTarget(
|
|
|
204
211
|
ipadSupportedInterfaceOrientations,
|
|
205
212
|
...(defaultScreenOrientationMask ? { defaultScreenOrientationMask } : {}),
|
|
206
213
|
alternateIconNames: [...alternateIconNames],
|
|
214
|
+
bundleIcons: resolveNativeBundleIcons(icons, catalogAlternateNames, (value) => value),
|
|
207
215
|
appIconArtwork: resolveNativeAppIconArtwork(
|
|
208
216
|
app.resources,
|
|
209
217
|
(file) => fs.readFileSync(file, 'utf8'),
|
|
@@ -13,6 +13,7 @@ import { nativeAppIdentityFromAndroidManifest } from './android-app-identity'
|
|
|
13
13
|
import { isRecord } from './is-record'
|
|
14
14
|
import {
|
|
15
15
|
resolveNativeAppIconArtwork,
|
|
16
|
+
resolveNativeBundleIcons,
|
|
16
17
|
isNativeAppIconArtwork,
|
|
17
18
|
readNativeAppIconArtwork,
|
|
18
19
|
type NativeAppIconArtwork,
|
|
@@ -87,6 +88,7 @@ export interface NativeAppIdentity {
|
|
|
87
88
|
rtcAppGroupIdentifier?: string
|
|
88
89
|
screenShareExtensionBundleId?: string
|
|
89
90
|
alternateIconNames?: readonly string[]
|
|
91
|
+
bundleIcons?: Readonly<Record<string, unknown>>
|
|
90
92
|
appIconArtwork?: NativeAppIconArtwork
|
|
91
93
|
// resolved Reanimated compile flags from the native build inputs.
|
|
92
94
|
reanimatedStaticFeatureFlags?: Readonly<Record<string, boolean>>
|
|
@@ -183,6 +185,7 @@ export function isNativeAppIdentity(value: unknown): value is NativeAppIdentity
|
|
|
183
185
|
)
|
|
184
186
|
return false
|
|
185
187
|
if (value.googleSignIn !== undefined && !isRecord(value.googleSignIn)) return false
|
|
188
|
+
if (value.bundleIcons !== undefined && !isRecord(value.bundleIcons)) return false
|
|
186
189
|
const descriptions = value.permissionUsageDescriptions
|
|
187
190
|
if (
|
|
188
191
|
value.reanimatedStaticFeatureFlags !== undefined &&
|
|
@@ -355,6 +358,9 @@ export function readNativeAppIdentity(value: unknown): NativeAppIdentity | null
|
|
|
355
358
|
...(value.alternateIconNames === undefined
|
|
356
359
|
? {}
|
|
357
360
|
: { alternateIconNames: [...value.alternateIconNames] }),
|
|
361
|
+
...(value.bundleIcons === undefined
|
|
362
|
+
? {}
|
|
363
|
+
: { bundleIcons: structuredClone(value.bundleIcons) }),
|
|
358
364
|
...(value.androidManifestPermissions === undefined
|
|
359
365
|
? {}
|
|
360
366
|
: {
|
|
@@ -1003,10 +1009,13 @@ function nativeAppIdentityFromIos(
|
|
|
1003
1009
|
)
|
|
1004
1010
|
if (alternateSetting === null)
|
|
1005
1011
|
throw new Error('native alternate icon setting is unresolved')
|
|
1012
|
+
const catalogAlternateNames: string[] = []
|
|
1006
1013
|
for (const match of alternateSetting
|
|
1007
1014
|
.replace(/\\"/g, '"')
|
|
1008
1015
|
.matchAll(/"([^"]*)"|([^\s(),]+)/g)) {
|
|
1009
|
-
|
|
1016
|
+
const name = match[1] ?? match[2]
|
|
1017
|
+
alternateIconNames.add(name)
|
|
1018
|
+
catalogAlternateNames.push(name)
|
|
1010
1019
|
}
|
|
1011
1020
|
// swift casts the whole configured value to [String]; a mixed array grants none.
|
|
1012
1021
|
const intentIds = dictionary.OneAppIntentIdentifiers
|
|
@@ -1208,6 +1217,11 @@ function nativeAppIdentityFromIos(
|
|
|
1208
1217
|
? {}
|
|
1209
1218
|
: { defaultScreenOrientationMask }),
|
|
1210
1219
|
alternateIconNames: [...alternateIconNames],
|
|
1220
|
+
bundleIcons: resolveNativeBundleIcons(icons, catalogAlternateNames, (value) => {
|
|
1221
|
+
const resolved = resolveBuildSetting(value, settings)
|
|
1222
|
+
if (resolved === null) throw new Error('native bundle icon reference is unresolved')
|
|
1223
|
+
return resolved
|
|
1224
|
+
}),
|
|
1211
1225
|
...(resourceUri
|
|
1212
1226
|
? {
|
|
1213
1227
|
appIconArtwork: resolveNativeAppIconArtwork(
|
|
@@ -6,6 +6,43 @@ export interface NativeAppIconArtwork {
|
|
|
6
6
|
alternates: Record<string, SwiftImageVariants>
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
+
export function resolveNativeBundleIcons(
|
|
10
|
+
icons: unknown,
|
|
11
|
+
catalogNames: readonly string[],
|
|
12
|
+
resolveReference: (value: string) => string,
|
|
13
|
+
): Record<string, unknown> | undefined {
|
|
14
|
+
if (!isRecord(icons) && catalogNames.length === 0) return undefined
|
|
15
|
+
const resolve = (value: unknown): unknown => {
|
|
16
|
+
if (typeof value === 'string') return resolveReference(value)
|
|
17
|
+
if (Array.isArray(value)) return value.map(resolve)
|
|
18
|
+
if (isRecord(value))
|
|
19
|
+
return Object.fromEntries(
|
|
20
|
+
Object.entries(value).map(([key, entry]) => [
|
|
21
|
+
resolveReference(key),
|
|
22
|
+
resolve(entry),
|
|
23
|
+
]),
|
|
24
|
+
)
|
|
25
|
+
return value
|
|
26
|
+
}
|
|
27
|
+
const dictionary = isRecord(icons)
|
|
28
|
+
? Object.fromEntries(
|
|
29
|
+
Object.entries(icons).map(([key, value]) => [key, resolve(value)]),
|
|
30
|
+
)
|
|
31
|
+
: {}
|
|
32
|
+
if (catalogNames.length === 0) return dictionary
|
|
33
|
+
return {
|
|
34
|
+
...dictionary,
|
|
35
|
+
CFBundleAlternateIcons: {
|
|
36
|
+
...Object.fromEntries(
|
|
37
|
+
catalogNames.map((name) => [name, { CFBundleIconName: name }]),
|
|
38
|
+
),
|
|
39
|
+
...(isRecord(dictionary.CFBundleAlternateIcons)
|
|
40
|
+
? dictionary.CFBundleAlternateIcons
|
|
41
|
+
: {}),
|
|
42
|
+
},
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
9
46
|
export function isNativeAppIconArtwork(value: unknown): value is NativeAppIconArtwork {
|
|
10
47
|
return (
|
|
11
48
|
isRecord(value) &&
|
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
|