expo-brownfield 55.0.13 → 55.0.14

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/CHANGELOG.md CHANGED
@@ -10,6 +10,16 @@
10
10
 
11
11
  ### 💡 Others
12
12
 
13
+ ## 55.0.14 — 2026-03-11
14
+
15
+ ### 🎉 New features
16
+
17
+ - Align using custom components between the platforms ([#43633](https://github.com/expo/expo/pull/43633) by [@pmleczek](https://github.com/pmleczek))
18
+
19
+ ### 💡 Others
20
+
21
+ - [android] set react native version for all published artifacts ([#43693](https://github.com/expo/expo/pull/43693) by [@pmleczek](https://github.com/pmleczek))
22
+
13
23
  ## 55.0.13 — 2026-03-05
14
24
 
15
25
  ### 🛠 Breaking changes
@@ -4,7 +4,7 @@ plugins {
4
4
  }
5
5
 
6
6
  group = 'expo.modules.brownfield'
7
- version = '55.0.13'
7
+ version = '55.0.14'
8
8
 
9
9
  expoModule {
10
10
  canBePublished false
@@ -14,7 +14,7 @@ android {
14
14
  namespace "expo.modules.brownfield"
15
15
  defaultConfig {
16
16
  versionCode 1
17
- versionName '55.0.13'
17
+ versionName '55.0.14'
18
18
  }
19
19
  }
20
20
 
@@ -179,11 +179,14 @@ internal fun PublishingExtension.createPublication(
179
179
  from: String,
180
180
  project: Project,
181
181
  libraryExtension: LibraryExtension,
182
- rnVersion: String?,
183
- hermesVersion: String?,
184
182
  ) {
183
+ val rnVersion = getReactNativeVersion(project)
184
+ val hermesVersion = getHermesVersion(project)
185
+
186
+ val isBrownfieldProject = project.plugins.hasPlugin("expo-brownfield-setup")
187
+
185
188
  val _artifactId =
186
- if (rnVersion != null) {
189
+ if (isBrownfieldProject) {
187
190
  project.name
188
191
  } else {
189
192
  requireNotNull(libraryExtension.namespace)
@@ -198,9 +201,7 @@ internal fun PublishingExtension.createPublication(
198
201
 
199
202
  pom.withXml { xml ->
200
203
  removeReactNativeDependencyPom(xml)
201
- if (rnVersion != null && hermesVersion != null) {
202
- setReactNativeVersionPom(xml, rnVersion, hermesVersion)
203
- }
204
+ setReactNativeVersionPom(xml, rnVersion, hermesVersion)
204
205
  }
205
206
  }
206
207
  }
@@ -30,24 +30,11 @@ internal fun setupPublishing(project: Project) {
30
30
  return@afterEvaluate
31
31
  }
32
32
 
33
- val rnVersion =
34
- if (project.name == configExtension.libraryName.get()) {
35
- getReactNativeVersion(project)
36
- } else {
37
- null
38
- }
39
- val hermesVersion =
40
- if (project.name == configExtension.libraryName.get()) {
41
- getHermesVersion(project)
42
- } else {
43
- null
44
- }
45
-
46
33
  variants.forEach { variant ->
47
- publicationExtension.createPublication(variant, project, libraryExtension, rnVersion, hermesVersion)
34
+ publicationExtension.createPublication(variant, project, libraryExtension)
48
35
  }
49
36
 
50
- createModuleRelatedTasks(project, rnVersion, hermesVersion)
37
+ createModuleRelatedTasks(project)
51
38
  setupRepositories(publicationExtension, project, configExtension)
52
39
  }
53
40
  }
@@ -89,13 +76,11 @@ internal fun setupRepositories(
89
76
  * @param project The project to remove react-native dependency from.
90
77
  * @param isBrownfieldProject Whether the project is the brownfield project.
91
78
  */
92
- internal fun createModuleRelatedTasks(project: Project, rnVersion: String?, hermesVersion: String?) {
79
+ internal fun createModuleRelatedTasks(project: Project) {
93
80
  val variants = listOf("brownfieldDebug", "brownfieldRelease", "brownfieldAll")
94
81
  variants.forEach { variant ->
95
82
  createRemoveReactNativeDependencyModuleTask(project, variant)
96
- if (rnVersion != null && hermesVersion != null) {
97
- createSetReactNativeVersionModuleTask(project, variant, rnVersion, hermesVersion)
98
- }
83
+ createSetReactNativeVersionModuleTask(project, variant)
99
84
  }
100
85
  }
101
86
 
@@ -147,9 +132,10 @@ internal fun createRemoveReactNativeDependencyModuleTask(project: Project, varia
147
132
  internal fun createSetReactNativeVersionModuleTask(
148
133
  project: Project,
149
134
  variant: String,
150
- rnVersion: String,
151
- hermesVersion: String,
152
135
  ) {
136
+ val rnVersion = getReactNativeVersion(project)
137
+ val hermesVersion = getHermesVersion(project)
138
+
153
139
  val setVersionTask =
154
140
  project.tasks.register("setRNDependencyVersionInModuleFile$variant") { task ->
155
141
  task.doLast {
@@ -8,8 +8,12 @@ import java.io.File
8
8
  import org.gradle.api.Project
9
9
  import org.gradle.api.XmlProvider
10
10
  import org.gradle.api.publish.PublishingExtension
11
+ import org.gradle.api.plugins.ExtraPropertiesExtension
11
12
  import org.json.JSONObject
12
13
 
14
+ private const val EXPO_BROWNFIELD_RN_VERSION_KEY = "expoBrownfieldRnVersion"
15
+ private const val EXPO_BROWNFIELD_HERMES_VERSION_KEY = "expoBrownfieldHermesVersion"
16
+
13
17
  /**
14
18
  * Find the app project in the root project.
15
19
  *
@@ -105,13 +109,31 @@ internal fun getConfigExtension(project: Project): ExpoPublishExtension {
105
109
  }
106
110
 
107
111
  /**
108
- * Get the React Native version for the project.
112
+ * Return cached React Native version for the project.
113
+ *
114
+ * @param project The project to get the React Native version for.
115
+ * @return The cached React Native version for the project.
116
+ */
117
+ internal fun getReactNativeVersion(project: Project): String {
118
+ val rootProject = project.rootProject
119
+ val ext = rootProject.extensions.getByType(ExtraPropertiesExtension::class.java)
120
+ if (!ext.has(EXPO_BROWNFIELD_RN_VERSION_KEY)) {
121
+ val version = getReactNativeVersionInternal(project)
122
+ ext.set(EXPO_BROWNFIELD_RN_VERSION_KEY, version)
123
+ return version
124
+ }
125
+
126
+ return ext.get(EXPO_BROWNFIELD_RN_VERSION_KEY) as String
127
+ }
128
+
129
+ /**
130
+ * Get the React Native version from the project.
109
131
  *
110
132
  * @param project The project to get the React Native version for.
111
133
  * @return The React Native version for the project.
112
134
  * @throws IllegalStateException if the React Native version cannot be inferred.
113
135
  */
114
- internal fun getReactNativeVersion(project: Project): String {
136
+ internal fun getReactNativeVersionInternal(project: Project): String {
115
137
  return try {
116
138
  val process =
117
139
  ProcessBuilder("node", "--print", "require('react-native/package.json').version")
@@ -134,30 +156,48 @@ internal fun getReactNativeVersion(project: Project): String {
134
156
  }
135
157
  }
136
158
 
137
- /**
138
- * Get the Hermes version for the project.
139
- *
140
- * @param project The project to get the Hermes version for.
141
- * @return The Hermes version for the project.
142
- * @throws IllegalStateException if the Hermes version cannot be inferred.
143
- */
144
- internal fun getHermesVersion(project: Project): String {
145
- val process =
146
- ProcessBuilder("node", "--print", "require('hermes-compiler/package.json').version")
147
- .directory(project.rootProject.projectDir)
148
- .redirectErrorStream(true)
149
- .start()
159
+ /**
160
+ * Return cached Hermes version for the project.
161
+ *
162
+ * @param project The project to get the Hermes version for.
163
+ * @return The cached Hermes version for the project.
164
+ */
165
+ internal fun getHermesVersion(project: Project): String {
166
+ val rootProject = project.rootProject
167
+ val ext = rootProject.extensions.getByType(ExtraPropertiesExtension::class.java)
168
+ if (!ext.has(EXPO_BROWNFIELD_HERMES_VERSION_KEY)) {
169
+ val version = getHermesVersionInternal(project)
170
+ ext.set(EXPO_BROWNFIELD_HERMES_VERSION_KEY, version)
171
+ return version
172
+ }
150
173
 
151
- val version = process.inputStream.bufferedReader().readText().trim()
152
- process.waitFor()
174
+ return ext.get(EXPO_BROWNFIELD_HERMES_VERSION_KEY) as String
175
+ }
153
176
 
154
- if (process.exitValue() == 0 && version.isNotEmpty()) {
155
- return version
156
- }
177
+ /**
178
+ * Get the Hermes version for the project.
179
+ *
180
+ * @param project The project to get the Hermes version for.
181
+ * @return The Hermes version for the project.
182
+ * @throws IllegalStateException if the Hermes version cannot be inferred.
183
+ */
184
+ internal fun getHermesVersionInternal(project: Project): String {
185
+ val process =
186
+ ProcessBuilder("node", "--print", "require('hermes-compiler/package.json').version")
187
+ .directory(project.rootProject.projectDir)
188
+ .redirectErrorStream(true)
189
+ .start()
190
+
191
+ val version = process.inputStream.bufferedReader().readText().trim()
192
+ process.waitFor()
157
193
 
158
- throw IllegalStateException("Failed to infer Hermes version via Node")
194
+ if (process.exitValue() == 0 && version.isNotEmpty()) {
195
+ return version
159
196
  }
160
197
 
198
+ throw IllegalStateException("Failed to infer Hermes version via Node")
199
+ }
200
+
161
201
  /**
162
202
  * Get the React Native version from the package.json file.
163
203
  *
@@ -219,6 +259,9 @@ internal fun removeReactNativeDependencyPom(xml: XmlProvider) {
219
259
  if (dependency.groupId() == "com.facebook.react" && dependency.artifactId() == "react-native") {
220
260
  toRemove.add(dependency)
221
261
  }
262
+ if (dependency.groupId() == "com.facebook.react" && dependency.artifactId() == "hermes-android") {
263
+ toRemove.add(dependency)
264
+ }
222
265
  }
223
266
 
224
267
  val dependenciesNode = xml.dependenciesNode()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-brownfield",
3
- "version": "55.0.13",
3
+ "version": "55.0.14",
4
4
  "description": "Toolkit and APIs for adding brownfield setup to Expo projects",
5
5
  "main": "./build/index.js",
6
6
  "types": "./build/index.d.ts",
@@ -67,5 +67,5 @@
67
67
  "expo": "*",
68
68
  "react": "*"
69
69
  },
70
- "gitHead": "756404b0eb18d441b54c7136b4142349193f554b"
70
+ "gitHead": "bcdd2c239f8a92cdf5140e35cde768352630acd6"
71
71
  }
@@ -15,17 +15,18 @@ class ReactNativeFragment : Fragment() {
15
15
  container: ViewGroup?,
16
16
  savedInstanceState: Bundle?,
17
17
  ): FrameLayout {
18
+ val rootComponent = arguments?.getString("rootComponentName") ?: "main"
18
19
  return ReactNativeViewFactory.createFrameLayout(
19
20
  requireContext(),
20
21
  requireActivity(),
21
- RootComponent.Main,
22
+ rootComponent,
22
23
  )
23
24
  }
24
25
 
25
26
  companion object {
26
27
  private const val TAG = "ReactNativeFragment"
27
28
 
28
- fun createFragmentHost(activity: Activity): ViewGroup {
29
+ fun createFragmentHost(activity: Activity, rootComponent: String = "main"): ViewGroup {
29
30
  val layout =
30
31
  object : FrameLayout(activity) {
31
32
  init {
@@ -33,7 +34,7 @@ class ReactNativeFragment : Fragment() {
33
34
  }
34
35
  }
35
36
 
36
- val fragment = createAndCommit(activity, layout)
37
+ val fragment = createAndCommit(activity, layout, rootComponent)
37
38
 
38
39
  return layout
39
40
  }
@@ -41,10 +42,13 @@ class ReactNativeFragment : Fragment() {
41
42
  internal fun createAndCommit(
42
43
  activity: Activity,
43
44
  container: ViewGroup,
45
+ rootComponent: String = "main",
44
46
  ): ReactNativeFragment {
45
47
  val fragmentManager = (activity as FragmentActivity).supportFragmentManager
46
48
 
47
- val fragment = ReactNativeFragment()
49
+ val fragment = ReactNativeFragment().apply {
50
+ arguments = Bundle().apply { putString("rootComponentName", rootComponent) }
51
+ }
48
52
 
49
53
  fragmentManager.commit(true) {
50
54
  setReorderingAllowed(true)
@@ -64,9 +64,9 @@ class ReactNativeHostManager {
64
64
  }
65
65
  }
66
66
 
67
- fun Activity.showReactNativeFragment() {
67
+ fun Activity.showReactNativeFragment(rootComponent: String = "main") {
68
68
  ReactNativeHostManager.shared.initialize(this.application)
69
- val fragment = ReactNativeFragment.createFragmentHost(this)
69
+ val fragment = ReactNativeFragment.createFragmentHost(this, rootComponent)
70
70
  setContentView(fragment)
71
71
  setUpNativeBackHandling()
72
72
  }
@@ -10,19 +10,15 @@ import com.facebook.react.ReactDelegate
10
10
  import com.facebook.react.ReactInstanceManager
11
11
  import com.facebook.react.ReactRootView
12
12
 
13
- enum class RootComponent(val key: String) {
14
- Main("main")
15
- }
16
-
17
13
  object ReactNativeViewFactory {
18
14
  fun createFrameLayout(
19
15
  context: Context,
20
16
  activity: FragmentActivity,
21
- rootComponent: RootComponent,
17
+ rootComponent: String,
22
18
  launchOptions: Bundle? = null,
23
19
  ): FrameLayout {
24
20
  val reactHost = ReactNativeHostManager.shared.getReactHost()
25
- val reactDelegate = ReactDelegate(activity, reactHost!!, rootComponent.key, launchOptions)
21
+ val reactDelegate = ReactDelegate(activity, reactHost!!, rootComponent, launchOptions)
26
22
 
27
23
  activity.lifecycle.addObserver(
28
24
  object : DefaultLifecycleObserver {
@@ -37,7 +37,7 @@ public class ReactNativeHostManager {
37
37
  * Creates the React Native view using RCTReactNativeFactory
38
38
  */
39
39
  public func loadView(
40
- moduleName: String,
40
+ moduleName: String = "main",
41
41
  initialProps: [AnyHashable: Any]?,
42
42
  launchOptions: [AnyHashable: Any]?
43
43
  ) throws -> UIView {
@@ -24,7 +24,7 @@ public struct ReactNativeView: View {
24
24
  var launchOptions: [AnyHashable: Any]? = [:]
25
25
 
26
26
  public init(
27
- moduleName: String,
27
+ moduleName: String = "main",
28
28
  initialProps: [AnyHashable: Any] = [:],
29
29
  launchOptions: [AnyHashable: Any] = [:],
30
30
  ) {
@@ -8,7 +8,7 @@ public class ReactNativeViewController: UIViewController {
8
8
 
9
9
  @objc
10
10
  public init(
11
- moduleName: String,
11
+ moduleName: String = "main",
12
12
  initialProps: [AnyHashable: Any]? = nil,
13
13
  launchOptions: [AnyHashable: Any]? = nil
14
14
  ) {
@@ -36,9 +36,9 @@ index a9fbbe7d70..f7561327e7 100644
36
36
  }
37
37
  }
38
38
 
39
- fun Activity.showReactNativeFragment() {
39
+ fun Activity.showReactNativeFragment(rootComponent: String = "main") {
40
40
  ReactNativeHostManager.shared.initialize(this.application)
41
- - val fragment = ReactNativeFragment.createFragmentHost(this)
41
+ - val fragment = ReactNativeFragment.createFragmentHost(this, rootComponent)
42
42
  - setContentView(fragment)
43
43
  +
44
44
  + if (BuildConfig.DEBUG) {
@@ -51,7 +51,7 @@ index a9fbbe7d70..f7561327e7 100644
51
51
  + val reactNativeView = ReactNativeViewFactory.createFrameLayout(
52
52
  + this,
53
53
  + this as androidx.fragment.app.FragmentActivity,
54
- + RootComponent.Main
54
+ + rootComponent
55
55
  + )
56
56
  + fragmentHost.addView(reactNativeView, ViewGroup.LayoutParams.MATCH_PARENT)
57
57
  + setContentView(fragmentHost)
@@ -59,7 +59,7 @@ index a9fbbe7d70..f7561327e7 100644
59
59
  + }
60
60
  + } else {
61
61
  + setContentView(
62
- + ReactNativeFragment.createFragmentHost(this)
62
+ + ReactNativeFragment.createFragmentHost(this, rootComponent)
63
63
  + )
64
64
  + }
65
65
  +