peachsim 0.1.2194 → 0.1.2195

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.
Files changed (98) hide show
  1. package/README.md +4 -1
  2. package/cli/commands/control.ts +2 -0
  3. package/cli/commands/kotlin.ts +10 -3
  4. package/cli/commands/package.ts +2 -0
  5. package/cli/native-input.ts +2 -0
  6. package/cli/outbound-endpoints.ts +1 -1
  7. package/dist-lib/agent-daemon-client.cjs +1 -1
  8. package/dist-lib/agent-events.cjs +1 -1
  9. package/dist-lib/attached-projects.cjs +1 -1
  10. package/dist-lib/auth/shared-session.cjs +1 -1
  11. package/dist-lib/backend-origin.cjs +1 -1
  12. package/dist-lib/beta.cjs +1 -1
  13. package/dist-lib/beta.mjs +1 -1
  14. package/dist-lib/bridge-constants.cjs +1 -1
  15. package/dist-lib/bridge-contract-input.cjs +1 -1
  16. package/dist-lib/bridge-contract-input.mjs +1 -1
  17. package/dist-lib/bridge-contract.cjs +1 -1
  18. package/dist-lib/bridge-contract.mjs +1 -1
  19. package/dist-lib/bundle-channel.cjs +986 -785
  20. package/dist-lib/capture-contract.cjs +1 -1
  21. package/dist-lib/capture-contract.mjs +1 -1
  22. package/dist-lib/cloud-contract.cjs +1 -1
  23. package/dist-lib/cloud-contract.mjs +1 -1
  24. package/dist-lib/cloud-test-args.cjs +1 -1
  25. package/dist-lib/cloud-test-args.mjs +1 -1
  26. package/dist-lib/cloud.cjs +1 -1
  27. package/dist-lib/cloud.mjs +1 -1
  28. package/dist-lib/config.cjs +1 -1
  29. package/dist-lib/config.mjs +1 -1
  30. package/dist-lib/detox/index.cjs +1 -1
  31. package/dist-lib/dev-bundle-resolution.cjs +1 -1
  32. package/dist-lib/expo-swift-bundle.cjs +3 -3
  33. package/dist-lib/expo-swift-metadata.cjs +1 -1
  34. package/dist-lib/expo-swift-metadata.mjs +1 -1
  35. package/dist-lib/home-paths.cjs +1 -1
  36. package/dist-lib/host/bridge-host.cjs +991 -790
  37. package/dist-lib/host/fetch-proxy-handler.cjs +2 -2
  38. package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
  39. package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
  40. package/dist-lib/host/replacement-module-handler.cjs +1 -1
  41. package/dist-lib/host/websocket-proxy.cjs +1 -1
  42. package/dist-lib/index.cjs +2 -2
  43. package/dist-lib/jump-to-source-babel.cjs +1 -1
  44. package/dist-lib/jump-to-source-native.cjs +1 -1
  45. package/dist-lib/kotlin-host.cjs +82 -13
  46. package/dist-lib/menu.cjs +1 -1
  47. package/dist-lib/menu.mjs +1 -1
  48. package/dist-lib/metro-fingerprint-registry.cjs +1 -1
  49. package/dist-lib/metro-fingerprint-registry.mjs +1 -1
  50. package/dist-lib/metro-production-bundle.cjs +1 -1
  51. package/dist-lib/metro-production-bundle.mjs +1 -1
  52. package/dist-lib/metro.cjs +2 -2
  53. package/dist-lib/native-app-identity-project.cjs +1 -1
  54. package/dist-lib/native-app-identity-project.mjs +1 -1
  55. package/dist-lib/native-app-identity.cjs +1 -1
  56. package/dist-lib/native-app-identity.mjs +1 -1
  57. package/dist-lib/native-firebase-metadata.cjs +1 -1
  58. package/dist-lib/native-firebase-metadata.mjs +1 -1
  59. package/dist-lib/pdfium-raster.cjs +1 -1
  60. package/dist-lib/pdfium-raster.mjs +1 -1
  61. package/dist-lib/plist.cjs +1 -1
  62. package/dist-lib/plist.mjs +1 -1
  63. package/dist-lib/profiles.cjs +1 -1
  64. package/dist-lib/public-brand.cjs +1 -1
  65. package/dist-lib/public-origins-lib-esm.json +24 -24
  66. package/dist-lib/public-origins-lib.json +65 -62
  67. package/dist-lib/react-native-host-modules.cjs +1 -1
  68. package/dist-lib/react-native-host-modules.mjs +1 -1
  69. package/dist-lib/reap-browser.cjs +1 -1
  70. package/dist-lib/scripts/dev-server-scanner.cjs +1 -1
  71. package/dist-lib/sdk.cjs +1 -1
  72. package/dist-lib/sdk.mjs +1 -1
  73. package/dist-lib/skills.cjs +1513 -1264
  74. package/dist-lib/swift-compile.cjs +1 -1
  75. package/dist-lib/swift-compile.mjs +1 -1
  76. package/dist-lib/swift-package.cjs +1 -1
  77. package/dist-lib/swift-package.mjs +1 -1
  78. package/dist-lib/swift-submit.cjs +1 -1
  79. package/dist-lib/swift-submit.mjs +1 -1
  80. package/dist-lib/swift-toolchain.cjs +1 -1
  81. package/dist-lib/swift.cjs +1 -1
  82. package/dist-lib/vite-swift.cjs +2 -2
  83. package/dist-lib/vite.cjs +2 -2
  84. package/dist-lib/worklet-executable.cjs +1 -1
  85. package/dist-lib/worklet-executable.mjs +1 -1
  86. package/dist-types/packages/peach-engine/src/engine/types.d.ts +1 -0
  87. package/kotlin/sdk.tar.gz +0 -0
  88. package/package.json +1 -1
  89. package/src/android-bridge-dependencies.ts +93 -0
  90. package/src/android-bridge-packages.ts +1 -0
  91. package/src/android-gradle-sources.ts +76 -9
  92. package/src/androidGradleContract.ts +8 -0
  93. package/src/expo-kotlin-host.ts +20 -7
  94. package/src/expoKotlinContract.ts +1 -1
  95. package/src/java-module-abi.ts +7 -1
  96. package/src/kotlin-app.ts +120 -63
  97. package/src/kotlin-host.ts +1 -0
  98. package/src/kotlinSdkContract.ts +8 -1
@@ -1,4 +1,4 @@
1
- /*! peach v0.1.2194 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
1
+ /*! peach v0.1.2195 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
2
2
  let __sootsim_import_meta_url = '', __sootsim_import_meta_dirname = '', __sootsim_import_meta_filename = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; __sootsim_import_meta_dirname = __dirname; __sootsim_import_meta_filename = __filename; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
@@ -1,4 +1,4 @@
1
- /*! peach v0.1.2194 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
1
+ /*! peach v0.1.2195 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
2
2
 
3
3
  // src/swift-compile.ts
4
4
  import {
@@ -1,4 +1,4 @@
1
- /*! peach v0.1.2194 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
1
+ /*! peach v0.1.2195 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
2
2
  let __sootsim_import_meta_url = '', __sootsim_import_meta_dirname = '', __sootsim_import_meta_filename = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; __sootsim_import_meta_dirname = __dirname; __sootsim_import_meta_filename = __filename; } catch {}
3
3
  "use strict";
4
4
  var __defProp = Object.defineProperty;
@@ -1,4 +1,4 @@
1
- /*! peach v0.1.2194 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
1
+ /*! peach v0.1.2195 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
2
2
 
3
3
  // src/swift-package.ts
4
4
  function swiftManifestSettings(target) {
@@ -1,4 +1,4 @@
1
- /*! peach v0.1.2194 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
1
+ /*! peach v0.1.2195 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
2
2
  let __sootsim_import_meta_url = '', __sootsim_import_meta_dirname = '', __sootsim_import_meta_filename = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; __sootsim_import_meta_dirname = __dirname; __sootsim_import_meta_filename = __filename; } catch {}
3
3
  "use strict";
4
4
  var __defProp = Object.defineProperty;
@@ -1,4 +1,4 @@
1
- /*! peach v0.1.2194 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
1
+ /*! peach v0.1.2195 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
2
2
 
3
3
  // src/is-record.ts
4
4
  function isRecord(value) {
@@ -1,4 +1,4 @@
1
- /*! peach v0.1.2194 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
1
+ /*! peach v0.1.2195 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
2
2
  let __sootsim_import_meta_url = '', __sootsim_import_meta_dirname = '', __sootsim_import_meta_filename = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; __sootsim_import_meta_dirname = __dirname; __sootsim_import_meta_filename = __filename; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
@@ -1,4 +1,4 @@
1
- /*! peach v0.1.2194 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
1
+ /*! peach v0.1.2195 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
2
2
  let __sootsim_import_meta_url = '', __sootsim_import_meta_dirname = '', __sootsim_import_meta_filename = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; __sootsim_import_meta_dirname = __dirname; __sootsim_import_meta_filename = __filename; } catch {}
3
3
  "use strict";
4
4
  var __defProp = Object.defineProperty;
@@ -1,4 +1,4 @@
1
- /*! peach v0.1.2194 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
1
+ /*! peach v0.1.2195 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
2
2
  let __sootsim_import_meta_url = '', __sootsim_import_meta_dirname = '', __sootsim_import_meta_filename = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; __sootsim_import_meta_dirname = __dirname; __sootsim_import_meta_filename = __filename; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
@@ -30614,7 +30614,7 @@ function sourceClosureDigest(srcDir, entrypoints) {
30614
30614
  return hash.digest("hex");
30615
30615
  }
30616
30616
  var standinSourceDigest = (srcDir = __sootsim_import_meta_dirname) => sourceClosureDigest(srcDir, STANDIN_DIGEST_ENTRYPOINTS);
30617
- var STANDIN_SOURCE_DIGEST = true ? "d9690faaeece4d15e4d1c3fd98fcb05d93d520f33afa9a5f18fdd043578ad02f" : standinSourceDigest();
30617
+ var STANDIN_SOURCE_DIGEST = true ? "afff2f6ba145885813b507114e72f9218f9a815a257ab2e44f7da376b7bf5cd1" : standinSourceDigest();
30618
30618
 
30619
30619
  // src/swift-standins.ts
30620
30620
  init_define_SWIFT_COVERAGE_SOURCE_DIGESTS();
package/dist-lib/vite.cjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! peach v0.1.2194 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
1
+ /*! peach v0.1.2195 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
2
2
  let __sootsim_import_meta_url = '', __sootsim_import_meta_dirname = '', __sootsim_import_meta_filename = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; __sootsim_import_meta_dirname = __dirname; __sootsim_import_meta_filename = __filename; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
@@ -1909,7 +1909,7 @@ function readIosSdkTree(root) {
1909
1909
 
1910
1910
  // src/swift-standin-digest.ts
1911
1911
  init_define_SWIFT_COVERAGE_SOURCE_DIGESTS();
1912
- var STANDIN_SOURCE_DIGEST = true ? "d9690faaeece4d15e4d1c3fd98fcb05d93d520f33afa9a5f18fdd043578ad02f" : standinSourceDigest();
1912
+ var STANDIN_SOURCE_DIGEST = true ? "afff2f6ba145885813b507114e72f9218f9a815a257ab2e44f7da376b7bf5cd1" : standinSourceDigest();
1913
1913
 
1914
1914
  // src/expo-native-workspace.ts
1915
1915
  init_define_SWIFT_COVERAGE_SOURCE_DIGESTS();
@@ -1,4 +1,4 @@
1
- /*! peach v0.1.2194 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
1
+ /*! peach v0.1.2195 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
2
2
  let __sootsim_import_meta_url = '', __sootsim_import_meta_dirname = '', __sootsim_import_meta_filename = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; __sootsim_import_meta_dirname = __dirname; __sootsim_import_meta_filename = __filename; } catch {}
3
3
  "use strict";
4
4
  var __defProp = Object.defineProperty;
@@ -1,4 +1,4 @@
1
- /*! peach v0.1.2194 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
1
+ /*! peach v0.1.2195 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
2
2
 
3
3
  // src/worklet-executable.ts
4
4
  import { parse } from "acorn";
@@ -445,6 +445,7 @@ export interface SootSimStyle {
445
445
  _lineLeading?: number;
446
446
  _lineAscent?: number;
447
447
  _uikitLabel?: true;
448
+ _uikitLabelCenteredShrink?: true;
448
449
  _textKitLeadingAbove?: true;
449
450
  _coreTextLine?: true;
450
451
  _textViewBlock?: true;
package/kotlin/sdk.tar.gz CHANGED
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "peachsim",
3
- "version": "0.1.2194",
3
+ "version": "0.1.2195",
4
4
  "description": "Vite and Metro plugins, testing drivers, and SDK exports for peach.",
5
5
  "author": "Tamagui LLC",
6
6
  "license": "SEE LICENSE IN LICENSE",
@@ -0,0 +1,93 @@
1
+ import fs from 'node:fs'
2
+ import path from 'node:path'
3
+ import { resolveReactNativeAndroidAutolinking } from './android-autolinking'
4
+ import { resolveAndroidGradleSources } from './android-gradle-sources'
5
+ import { readJavaModuleSpecs } from './java-module-abi'
6
+ import { installKotlinSdkArchive } from './kotlin-sdk'
7
+ import { ensureToolchain, readToolchain } from './kotlin-toolchain'
8
+ import type { BridgePackage, BridgeSelection } from './android-bridge-packages'
9
+
10
+ export async function collectAndroidBridgeDependencies(
11
+ appRoot: string,
12
+ moduleRoot: string,
13
+ ): Promise<BridgeSelection | null> {
14
+ const root = path.join(appRoot, 'android')
15
+ if (!fs.existsSync(path.join(root, 'gradlew'))) return null
16
+ const dependencies = (await resolveReactNativeAndroidAutolinking(appRoot)).flatMap(
17
+ (dependency) =>
18
+ dependency.sourceDir === null || dependency.packageInstance === null
19
+ ? []
20
+ : [
21
+ {
22
+ ...dependency,
23
+ sourceDir: dependency.sourceDir,
24
+ packageInstance: dependency.packageInstance,
25
+ },
26
+ ],
27
+ )
28
+ if (dependencies.length === 0) return null
29
+ const models = await resolveAndroidGradleSources({
30
+ root,
31
+ projects: [
32
+ path.join(root, 'app'),
33
+ ...dependencies.map((dependency) => dependency.sourceDir),
34
+ ],
35
+ variant: 'debug',
36
+ })
37
+ const applicationId = models[0].applicationId
38
+ if (applicationId === null) throw new Error(`${root}/app is not an Android application`)
39
+ const toolchain = readToolchain(moduleRoot)
40
+ const [sdk, ready] = await Promise.all([
41
+ installKotlinSdkArchive(
42
+ fs.readFileSync(path.join(moduleRoot, 'sdk.tar.gz')),
43
+ toolchain.compiler.sha256,
44
+ ),
45
+ ensureToolchain(toolchain),
46
+ ])
47
+ const packages: BridgePackage[] = []
48
+ for (const dependency of dependencies) {
49
+ const model = models.find(
50
+ (entry) => entry.projectDir === fs.realpathSync(dependency.sourceDir),
51
+ )!
52
+ try {
53
+ const className = /^import\s+([\w.]+)\s*;$/.exec(
54
+ dependency.packageImportPath ?? '',
55
+ )?.[1]
56
+ const constructor = /^new\s+([\w.]+)\s*\(\s*\)$/.exec(
57
+ dependency.packageInstance,
58
+ )?.[1]
59
+ if (
60
+ !className ||
61
+ (constructor !== className && constructor !== className.split('.').pop())
62
+ )
63
+ throw new Error(
64
+ 'native package registration requires a constructor without arguments',
65
+ )
66
+ const declarations = await readJavaModuleSpecs(model.java, sdk.root, ready)
67
+ const modules = declarations.specs.flatMap((spec) =>
68
+ spec.name === null
69
+ ? []
70
+ : [
71
+ {
72
+ className: spec.className,
73
+ name: spec.name,
74
+ methods: spec.methods,
75
+ },
76
+ ],
77
+ )
78
+ if (modules.length === 0) continue
79
+ packages.push({
80
+ className,
81
+ modules,
82
+ sources: model.kotlin,
83
+ javaSpecs: model.java,
84
+ unsupported: null,
85
+ })
86
+ } catch (error) {
87
+ console.error(
88
+ `[peach] ${dependency.name} native source is unavailable: ${error instanceof Error ? error.message : String(error)}`,
89
+ )
90
+ }
91
+ }
92
+ return packages.length ? { applicationId, packages } : null
93
+ }
@@ -38,6 +38,7 @@ export interface BridgePackage {
38
38
  modules: BridgeModule[]
39
39
  // the app sources the package's classes need, its own first
40
40
  sources: string[]
41
+ javaSpecs?: string[]
41
42
  // why the package cannot be hosted
42
43
  unsupported: string | null
43
44
  }
@@ -7,6 +7,7 @@ import { PEACH_FORKED_SCRIPT_ENV } from './forked-script-contract'
7
7
  import { isRecord } from './is-record'
8
8
  import { kotlinHome } from './kotlin-toolchain'
9
9
  import type {
10
+ AndroidGradleDependency,
10
11
  AndroidGradleSourceRequest,
11
12
  AndroidGradleSources,
12
13
  } from './androidGradleContract'
@@ -15,8 +16,26 @@ const run = promisify(execFile)
15
16
  const modelQuery = `
16
17
  import groovy.json.JsonOutput
17
18
  import groovy.json.JsonSlurper
19
+ import org.gradle.api.artifacts.FileCollectionDependency
20
+ import org.gradle.api.artifacts.component.ModuleComponentIdentifier
21
+ import org.gradle.api.artifacts.component.ProjectComponentIdentifier
22
+ import org.gradle.api.artifacts.result.ResolvedDependencyResult
23
+ import org.gradle.api.attributes.Category
18
24
 
19
25
  def request = new JsonSlurper().parseText(new String(Base64.decoder.decode('__PEACH_REQUEST__'), 'UTF-8'))
26
+ def selectedVariants = [:]
27
+ gradle.beforeProject { project ->
28
+ def directory = project.projectDir.canonicalPath
29
+ if (project.rootProject.projectDir.canonicalPath != request.root || !request.projects.contains(directory)) return
30
+ ['com.android.application', 'com.android.library'].each { plugin ->
31
+ project.pluginManager.withPlugin(plugin) {
32
+ def components = project.extensions.getByName('androidComponents')
33
+ components.onVariants(components.selector().withName(request.variant)) { variant ->
34
+ selectedVariants[directory] = variant
35
+ }
36
+ }
37
+ }
38
+ }
20
39
  gradle.projectsEvaluated {
21
40
  if (gradle.rootProject.projectDir.canonicalPath != request.root) return
22
41
  def selected = []
@@ -31,19 +50,15 @@ gradle.projectsEvaluated {
31
50
  if (!library && !project.pluginManager.hasPlugin('com.android.application')) {
32
51
  throw new GradleException('Project has no Android application or library plugin: ' + project.path)
33
52
  }
34
- def android = project.extensions.getByName('android')
35
- def variants = library ? android.libraryVariants : android.applicationVariants
36
- def variant = variants.find { it.name == request.variant }
53
+ def variant = selectedVariants[directory]
37
54
  if (variant == null) throw new GradleException('Android variant ' + request.variant + ' is absent: ' + project.path)
38
55
  def generators = []
39
- def buildConfig = variant.generateBuildConfigProvider
40
- if (buildConfig != null) generators.add(buildConfig)
41
56
  if (project.pluginManager.hasPlugin('com.facebook.react')) {
42
57
  generators.add(project.tasks.named('generateCodegenArtifactsFromSchema'))
43
58
  }
44
59
  def kotlin = project.tasks.findByName('compile' + variant.name.capitalize() + 'Kotlin')
45
60
  def kotlinSources = kotlin == null ? project.files() : kotlin.sources
46
- def javaSources = variant.javaCompileProvider.get().source
61
+ def javaSources = project.tasks.named('compile' + variant.name.capitalize() + 'JavaWithJavac').get().source
47
62
  def query = project.tasks.register(request.task) {
48
63
  dependsOn(generators)
49
64
  dependsOn(kotlinSources.buildDependencies, javaSources.buildDependencies)
@@ -51,13 +66,31 @@ gradle.projectsEvaluated {
51
66
  def files = (kotlinSources.files + javaSources.files).unique()
52
67
  def unknown = files.find { !it.name.endsWith('.kt') && !it.name.endsWith('.java') }
53
68
  if (unknown != null) throw new GradleException('Unsupported Android source: ' + unknown)
69
+ def dependencies = [variant.compileConfiguration, variant.runtimeConfiguration].collectMany { configuration ->
70
+ def modules = configuration.incoming.resolutionResult.rootComponent.get().dependencies.findAll { !it.constraint }.collectMany { dependency ->
71
+ if (!(dependency instanceof ResolvedDependencyResult)) throw new GradleException('Unresolved Android dependency: ' + dependency.requested, dependency.failure)
72
+ def category = dependency.resolvedVariant.attributes.getAttribute(Category.CATEGORY_ATTRIBUTE)?.name
73
+ if (category == Category.REGULAR_PLATFORM || category == Category.ENFORCED_PLATFORM) return []
74
+ def id = dependency.selected.id
75
+ if (id instanceof ModuleComponentIdentifier) return [[kind:'module', group:id.group, name:id.module, version:id.version]]
76
+ if (id instanceof ProjectComponentIdentifier) return [[kind:'project', name:id.displayName]]
77
+ throw new GradleException('Unsupported Android dependency component: ' + id.displayName)
78
+ }
79
+ def local = configuration.allDependencies.withType(FileCollectionDependency).collectMany { dependency ->
80
+ dependency.files.files.collect { [kind:'file', name:it.absolutePath] }
81
+ }
82
+ modules + local
83
+ }.unique()
54
84
  new File(request.output, index.toString() + '.json').text = JsonOutput.toJson([
55
85
  projectDir:project.projectDir.canonicalPath,
56
86
  projectPath:project.path,
57
87
  variant:variant.name,
58
- applicationId:library ? null : variant.applicationId,
88
+ applicationId:library ? null : variant.applicationId.get(),
89
+ namespace:variant.namespace.get(),
90
+ targetSdk:library ? null : variant.targetSdk.apiLevel,
59
91
  kotlin:files.findAll { it.name.endsWith('.kt') }.collect { it.absolutePath }.sort(),
60
- java:files.findAll { it.name.endsWith('.java') }.collect { it.absolutePath }.sort()
92
+ java:files.findAll { it.name.endsWith('.java') }.collect { it.absolutePath }.sort(),
93
+ dependencies:dependencies
61
94
  ])
62
95
  }
63
96
  }
@@ -97,6 +130,7 @@ export async function resolveAndroidGradleSources(
97
130
  `${task}All`,
98
131
  '--no-daemon',
99
132
  '--no-configuration-cache',
133
+ '--no-configure-on-demand',
100
134
  '--max-workers=1',
101
135
  '--console=plain',
102
136
  ],
@@ -116,7 +150,13 @@ export async function resolveAndroidGradleSources(
116
150
  value.projectDir !== projectDir ||
117
151
  value.variant !== variant ||
118
152
  typeof value.projectPath !== 'string' ||
119
- (value.applicationId !== null && typeof value.applicationId !== 'string')
153
+ (value.applicationId !== null && typeof value.applicationId !== 'string') ||
154
+ typeof value.namespace !== 'string' ||
155
+ !value.namespace ||
156
+ (value.targetSdk !== null &&
157
+ (typeof value.targetSdk !== 'number' ||
158
+ !Number.isInteger(value.targetSdk) ||
159
+ value.targetSdk <= 0))
120
160
  )
121
161
  throw new Error('Gradle printed an invalid Android project source model')
122
162
  const sources = (field: 'kotlin' | 'java'): string[] => {
@@ -128,13 +168,40 @@ export async function resolveAndroidGradleSources(
128
168
  throw new Error(`Gradle printed invalid Android ${field} source paths`)
129
169
  return list.map(String)
130
170
  }
171
+ if (!Array.isArray(value.dependencies))
172
+ throw new Error('Gradle printed invalid Android dependencies')
173
+ const dependencies = value.dependencies.map(
174
+ (entry: unknown): AndroidGradleDependency => {
175
+ if (isRecord(entry) && typeof entry.name === 'string' && entry.name) {
176
+ if (
177
+ entry.kind === 'module' &&
178
+ typeof entry.group === 'string' &&
179
+ entry.group &&
180
+ typeof entry.version === 'string' &&
181
+ entry.version
182
+ )
183
+ return {
184
+ kind: entry.kind,
185
+ group: entry.group,
186
+ name: entry.name,
187
+ version: entry.version,
188
+ }
189
+ if (entry.kind === 'project' || entry.kind === 'file')
190
+ return { kind: entry.kind, name: entry.name }
191
+ }
192
+ throw new Error('Gradle printed an invalid Android dependency')
193
+ },
194
+ )
131
195
  return {
132
196
  projectDir,
133
197
  projectPath: value.projectPath,
134
198
  variant,
135
199
  applicationId: value.applicationId,
200
+ namespace: value.namespace,
201
+ targetSdk: value.targetSdk,
136
202
  kotlin: sources('kotlin'),
137
203
  java: sources('java'),
204
+ dependencies,
138
205
  }
139
206
  })
140
207
  } finally {
@@ -9,6 +9,14 @@ export interface AndroidGradleSources {
9
9
  projectPath: string
10
10
  variant: string
11
11
  applicationId: string | null
12
+ namespace: string
13
+ targetSdk: number | null
12
14
  kotlin: string[]
13
15
  java: string[]
16
+ dependencies: AndroidGradleDependency[]
14
17
  }
18
+
19
+ export type AndroidGradleDependency =
20
+ | { kind: 'module'; group: string; name: string; version: string }
21
+ | { kind: 'project'; name: string }
22
+ | { kind: 'file'; name: string }
@@ -5,6 +5,7 @@
5
5
  import fs from 'node:fs'
6
6
  import path from 'node:path'
7
7
  import { resolveExpoAndroidAutolinking } from './android-autolinking'
8
+ import { collectAndroidBridgeDependencies } from './android-bridge-dependencies'
8
9
  import {
9
10
  androidAppSources,
10
11
  selectBridgePackages,
@@ -27,7 +28,7 @@ export interface ExpoKotlinModules {
27
28
  sources: string[]
28
29
  javaSpecs?: string[]
29
30
  gradleSources?: AndroidGradleSourceRequest
30
- // the app's own legacy React Native modules, hosted beside the Expo ones
31
+ // React Native package modules, hosted beside the Expo ones
31
32
  bridge: BridgeSelection | null
32
33
  }
33
34
 
@@ -113,8 +114,12 @@ export async function collectExpoKotlinModules(
113
114
  return result.length === 0 && !answeredInstalled ? null : result
114
115
  }
115
116
 
116
- // the app module's ReactPackage classes the application registers
117
+ // native dependency packages followed by the app's own registrations
117
118
  async function collectBridgePackages(appRoot: string): Promise<BridgeSelection | null> {
119
+ const dependencies = await collectAndroidBridgeDependencies(
120
+ appRoot,
121
+ peachKotlinModuleRoot(),
122
+ )
118
123
  const app = androidAppSources(appRoot)
119
124
  if (
120
125
  !app ||
@@ -122,11 +127,14 @@ async function collectBridgePackages(appRoot: string): Promise<BridgeSelection |
122
127
  /\b(?:Base|Turbo)?ReactPackage\b/.test(fs.readFileSync(file, 'utf8')),
123
128
  )
124
129
  )
125
- return null
130
+ return dependencies
126
131
  const { scanBridgeFiles } = await import('./kotlin-host')
127
132
  const files = await scanBridgeFiles(peachKotlinModuleRoot(), app.kotlin)
128
133
  const selection = selectBridgePackages(app.applicationId, files, app.java)
129
- return selection.packages.length === 0 ? null : selection
134
+ const packages = [...(dependencies?.packages ?? []), ...selection.packages]
135
+ return packages.length
136
+ ? { applicationId: dependencies?.applicationId ?? selection.applicationId, packages }
137
+ : null
130
138
  }
131
139
 
132
140
  export async function compileExpoKotlinModules(
@@ -190,22 +198,27 @@ export async function compileExpoKotlinModules(
190
198
  const artifact = await expoKotlinArtifact(root, peachKotlinModuleRoot(), origin, {
191
199
  modules,
192
200
  sources,
201
+ javaSpecs: [...new Set(hosted.flatMap((entry) => entry.javaSpecs ?? []))],
193
202
  bridge: selection && { applicationId: selection.applicationId, packages: hosted },
194
203
  })
195
204
  return result(artifact)
196
205
  } catch (error) {
197
206
  const message = error instanceof Error ? error.message : String(error)
198
207
  // kotlinc names each failing source line; anything else is the host's
199
- const errors = message.match(/^.+\.kt:\d+:\d+: error:.*$/gm)
208
+ const errors = message.match(/^.+\.(?:kt|java):\d+:\d+: error:.*$/gm)
200
209
  if (!errors) throw error
201
210
  const linesFor = (sources: string[]) =>
202
211
  errors.filter((line) => sources.some((source) => line.startsWith(`${source}:`)))
203
- const broken = hosted.filter((entry) => linesFor(entry.sources).length > 0)
212
+ const broken = hosted.filter(
213
+ (entry) => linesFor([...entry.sources, ...(entry.javaSpecs ?? [])]).length > 0,
214
+ )
204
215
  const brokenProjects = projects.filter(
205
216
  (project) => project.installed && linesFor(project.sources).length > 0,
206
217
  )
207
218
  if (broken.length > 0 || brokenProjects.length > 0) {
208
- drop(broken, (entry) => linesFor(entry.sources).join('\n'))
219
+ drop(broken, (entry) =>
220
+ linesFor([...entry.sources, ...(entry.javaSpecs ?? [])]).join('\n'),
221
+ )
209
222
  dropProjects(brokenProjects, (project) => linesFor(project.sources).join('\n'))
210
223
  continue
211
224
  }
@@ -12,7 +12,7 @@ export interface ExpoKotlinCompilationResult {
12
12
  namespaces: string[]
13
13
  module: { url: string; hash: string; compose: boolean } | null
14
14
  failures: { namespace: string; diagnostic: string }[]
15
- // the namespaces the app's own legacy React Native modules answer for; the
15
+ // the namespaces the app's React Native package modules answer for; the
16
16
  // engine installs them as bridge modules, not as Expo modules
17
17
  bridgeModules: string[]
18
18
  }
@@ -44,7 +44,13 @@ export async function readJavaModuleSpecs(
44
44
  ...origins.keys(),
45
45
  ],
46
46
  { maxBuffer: 16 * 1024 * 1024 },
47
- )
47
+ ).catch((cause: unknown) => {
48
+ if (!isRecord(cause) || typeof cause.stderr !== 'string') throw cause
49
+ let diagnostic = cause.stderr
50
+ for (const [temporary, original] of origins)
51
+ diagnostic = diagnostic.replaceAll(temporary, original)
52
+ throw new Error(`Java module read failed:\n${diagnostic.trim()}`, { cause })
53
+ })
48
54
  const specs = stdout
49
55
  .split('\n')
50
56
  .filter(Boolean)