@react-native/gradle-plugin 0.82.0-rc.0 → 0.82.0-rc.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@react-native/gradle-plugin",
3
- "version": "0.82.0-rc.0",
3
+ "version": "0.82.0-rc.2",
4
4
  "description": "Gradle Plugin for React Native",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -28,6 +28,7 @@ import com.facebook.react.utils.DependencyUtils.readVersionAndGroupStrings
28
28
  import com.facebook.react.utils.JdkConfiguratorUtils.configureJavaToolChains
29
29
  import com.facebook.react.utils.JsonUtils
30
30
  import com.facebook.react.utils.NdkConfiguratorUtils.configureReactNativeNdk
31
+ import com.facebook.react.utils.ProjectUtils.isHermesV1Enabled
31
32
  import com.facebook.react.utils.ProjectUtils.needsCodegenFromPackageJson
32
33
  import com.facebook.react.utils.findPackageJsonFile
33
34
  import java.io.File
@@ -54,6 +55,10 @@ class ReactPlugin : Plugin<Project> {
54
55
  project,
55
56
  )
56
57
 
58
+ if (project.rootProject.isHermesV1Enabled) {
59
+ rootExtension.hermesV1Enabled.set(true)
60
+ }
61
+
57
62
  // App Only Configuration
58
63
  project.pluginManager.withPlugin("com.android.application") {
59
64
  // We wire the root extension with the values coming from the app (either user populated or
@@ -66,10 +71,12 @@ class ReactPlugin : Plugin<Project> {
66
71
  project.afterEvaluate {
67
72
  val reactNativeDir = extension.reactNativeDir.get().asFile
68
73
  val propertiesFile = File(reactNativeDir, "ReactAndroid/gradle.properties")
69
- val versionAndGroupStrings = readVersionAndGroupStrings(propertiesFile)
70
- val versionString = versionAndGroupStrings.first
71
- val groupString = versionAndGroupStrings.second
72
- configureDependencies(project, versionString, groupString)
74
+ val hermesVersionPropertiesFile =
75
+ File(reactNativeDir, "sdks/hermes-engine/version.properties")
76
+ val versionAndGroupStrings =
77
+ readVersionAndGroupStrings(propertiesFile, hermesVersionPropertiesFile)
78
+ val hermesV1Enabled = rootExtension.hermesV1Enabled.get()
79
+ configureDependencies(project, versionAndGroupStrings, hermesV1Enabled)
73
80
  configureRepositories(project)
74
81
  }
75
82
 
@@ -11,6 +11,7 @@ import javax.inject.Inject
11
11
  import org.gradle.api.Project
12
12
  import org.gradle.api.file.DirectoryProperty
13
13
  import org.gradle.api.provider.ListProperty
14
+ import org.gradle.api.provider.Property
14
15
 
15
16
  /**
16
17
  * A private extension we set on the rootProject to make easier to share values at execution time
@@ -57,4 +58,6 @@ abstract class PrivateReactExtension @Inject constructor(project: Project) {
57
58
 
58
59
  val codegenDir: DirectoryProperty =
59
60
  objects.directoryProperty().convention(root.dir("node_modules/@react-native/codegen"))
61
+
62
+ val hermesV1Enabled: Property<Boolean> = objects.property(Boolean::class.java).convention(false)
60
63
  }
@@ -94,7 +94,12 @@ abstract class BundleHermesCTask : DefaultTask() {
94
94
  runCommand(bundleCommand)
95
95
 
96
96
  if (hermesEnabled.get()) {
97
- val detectedHermesCommand = detectOSAwareHermesCommand(root.get().asFile, hermesCommand.get())
97
+ val hermesV1Enabled =
98
+ if (project.rootProject.hasProperty("hermesV1Enabled"))
99
+ project.rootProject.findProperty("hermesV1Enabled") == "true"
100
+ else false
101
+ val detectedHermesCommand =
102
+ detectOSAwareHermesCommand(root.get().asFile, hermesCommand.get(), hermesV1Enabled)
98
103
  val bytecodeFile = File("${bundleFile}.hbc")
99
104
  val outputSourceMap = resolveOutputSourceMap(bundleAssetFilename)
100
105
  val compilerSourceMap = resolveCompilerSourceMap(bundleAssetFilename)
@@ -7,12 +7,15 @@
7
7
 
8
8
  package com.facebook.react.utils
9
9
 
10
- import com.facebook.react.utils.PropertyUtils.DEFAULT_INTERNAL_PUBLISHING_GROUP
10
+ import com.facebook.react.utils.PropertyUtils.DEFAULT_INTERNAL_HERMES_PUBLISHING_GROUP
11
+ import com.facebook.react.utils.PropertyUtils.DEFAULT_INTERNAL_REACT_PUBLISHING_GROUP
11
12
  import com.facebook.react.utils.PropertyUtils.EXCLUSIVE_ENTEPRISE_REPOSITORY
12
13
  import com.facebook.react.utils.PropertyUtils.INCLUDE_JITPACK_REPOSITORY
13
14
  import com.facebook.react.utils.PropertyUtils.INCLUDE_JITPACK_REPOSITORY_DEFAULT
14
- import com.facebook.react.utils.PropertyUtils.INTERNAL_PUBLISHING_GROUP
15
+ import com.facebook.react.utils.PropertyUtils.INTERNAL_HERMES_PUBLISHING_GROUP
16
+ import com.facebook.react.utils.PropertyUtils.INTERNAL_HERMES_V1_VERSION_NAME
15
17
  import com.facebook.react.utils.PropertyUtils.INTERNAL_REACT_NATIVE_MAVEN_LOCAL_REPO
18
+ import com.facebook.react.utils.PropertyUtils.INTERNAL_REACT_PUBLISHING_GROUP
16
19
  import com.facebook.react.utils.PropertyUtils.INTERNAL_USE_HERMES_NIGHTLY
17
20
  import com.facebook.react.utils.PropertyUtils.INTERNAL_VERSION_NAME
18
21
  import com.facebook.react.utils.PropertyUtils.SCOPED_EXCLUSIVE_ENTEPRISE_REPOSITORY
@@ -25,6 +28,14 @@ import org.gradle.api.artifacts.repositories.MavenArtifactRepository
25
28
 
26
29
  internal object DependencyUtils {
27
30
 
31
+ internal data class Coordinates(
32
+ val versionString: String,
33
+ val hermesVersionString: String,
34
+ val hermesV1VersionString: String,
35
+ val reactGroupString: String = DEFAULT_INTERNAL_REACT_PUBLISHING_GROUP,
36
+ val hermesGroupString: String = DEFAULT_INTERNAL_HERMES_PUBLISHING_GROUP,
37
+ )
38
+
28
39
  /**
29
40
  * This method takes care of configuring the repositories{} block for both the app and all the 3rd
30
41
  * party libraries which are auto-linked.
@@ -95,14 +106,20 @@ internal object DependencyUtils {
95
106
  * This method takes care of configuring the resolution strategy for both the app and all the 3rd
96
107
  * party libraries which are auto-linked. Specifically it takes care of:
97
108
  * - Forcing the react-android/hermes-android version to the one specified in the package.json
98
- * - Substituting `react-native` with `react-android` and `hermes-engine` with `hermes-android`.
109
+ * - Substituting `react-native` with `react-android` and `hermes-engine` with `hermes-android`
110
+ * - Selecting between the classic Hermes and Hermes V1
99
111
  */
100
112
  fun configureDependencies(
101
113
  project: Project,
102
- versionString: String,
103
- groupString: String = DEFAULT_INTERNAL_PUBLISHING_GROUP,
114
+ coordinates: Coordinates,
115
+ hermesV1Enabled: Boolean = false,
104
116
  ) {
105
- if (versionString.isBlank()) return
117
+ if (
118
+ coordinates.versionString.isBlank() ||
119
+ (!hermesV1Enabled && coordinates.hermesVersionString.isBlank()) ||
120
+ (hermesV1Enabled && coordinates.hermesV1VersionString.isBlank())
121
+ )
122
+ return
106
123
  project.rootProject.allprojects { eachProject ->
107
124
  eachProject.configurations.all { configuration ->
108
125
  // Here we set a dependencySubstitution for both react-native and hermes-engine as those
@@ -110,53 +127,67 @@ internal object DependencyUtils {
110
127
  // This allows users to import libraries that are still using
111
128
  // implementation("com.facebook.react:react-native:+") and resolve the right dependency.
112
129
  configuration.resolutionStrategy.dependencySubstitution {
113
- getDependencySubstitutions(versionString, groupString).forEach { (module, dest, reason) ->
130
+ getDependencySubstitutions(coordinates, hermesV1Enabled).forEach { (module, dest, reason)
131
+ ->
114
132
  it.substitute(it.module(module)).using(it.module(dest)).because(reason)
115
133
  }
116
134
  }
117
135
  configuration.resolutionStrategy.force(
118
- "${groupString}:react-android:${versionString}",
136
+ "${coordinates.reactGroupString}:react-android:${coordinates.versionString}",
119
137
  )
120
138
  if (!(eachProject.findProperty(INTERNAL_USE_HERMES_NIGHTLY) as? String).toBoolean()) {
121
139
  // Contributors only: The hermes-engine version is forced only if the user has
122
140
  // not opted into using nightlies for local development.
123
- configuration.resolutionStrategy.force("${groupString}:hermes-android:${versionString}")
141
+ configuration.resolutionStrategy.force(
142
+ // TODO: T237406039 update coordinates
143
+ if (hermesV1Enabled)
144
+ "${coordinates.hermesGroupString}:hermes-android:${coordinates.hermesV1VersionString}"
145
+ else
146
+ "${coordinates.reactGroupString}:hermes-android:${coordinates.hermesVersionString}"
147
+ )
124
148
  }
125
149
  }
126
150
  }
127
151
  }
128
152
 
129
153
  internal fun getDependencySubstitutions(
130
- versionString: String,
131
- groupString: String = DEFAULT_INTERNAL_PUBLISHING_GROUP,
154
+ coordinates: Coordinates,
155
+ hermesV1Enabled: Boolean = false,
132
156
  ): List<Triple<String, String, String>> {
157
+ // TODO: T231755027 update coordinates and versioning
133
158
  val dependencySubstitution = mutableListOf<Triple<String, String, String>>()
159
+ // TODO: T237406039 update coordinates
160
+ val hermesVersionString =
161
+ if (hermesV1Enabled)
162
+ "${coordinates.hermesGroupString}:hermes-android:${coordinates.hermesV1VersionString}"
163
+ else "${coordinates.reactGroupString}:hermes-android:${coordinates.hermesVersionString}"
134
164
  dependencySubstitution.add(
135
165
  Triple(
136
166
  "com.facebook.react:react-native",
137
- "${groupString}:react-android:${versionString}",
167
+ "${coordinates.reactGroupString}:react-android:${coordinates.versionString}",
138
168
  "The react-native artifact was deprecated in favor of react-android due to https://github.com/facebook/react-native/issues/35210.",
139
169
  )
140
170
  )
141
171
  dependencySubstitution.add(
142
172
  Triple(
143
173
  "com.facebook.react:hermes-engine",
144
- "${groupString}:hermes-android:${versionString}",
174
+ hermesVersionString,
145
175
  "The hermes-engine artifact was deprecated in favor of hermes-android due to https://github.com/facebook/react-native/issues/35210.",
146
176
  )
147
177
  )
148
- if (groupString != DEFAULT_INTERNAL_PUBLISHING_GROUP) {
178
+ if (coordinates.reactGroupString != DEFAULT_INTERNAL_REACT_PUBLISHING_GROUP) {
149
179
  dependencySubstitution.add(
150
180
  Triple(
151
181
  "com.facebook.react:react-android",
152
- "${groupString}:react-android:${versionString}",
182
+ "${coordinates.reactGroupString}:react-android:${coordinates.versionString}",
153
183
  "The react-android dependency was modified to use the correct Maven group.",
154
184
  )
155
185
  )
186
+ // TODO: T237406039 update coordinates
156
187
  dependencySubstitution.add(
157
188
  Triple(
158
189
  "com.facebook.react:hermes-android",
159
- "${groupString}:hermes-android:${versionString}",
190
+ hermesVersionString,
160
191
  "The hermes-android dependency was modified to use the correct Maven group.",
161
192
  )
162
193
  )
@@ -164,7 +195,7 @@ internal object DependencyUtils {
164
195
  return dependencySubstitution
165
196
  }
166
197
 
167
- fun readVersionAndGroupStrings(propertiesFile: File): Pair<String, String> {
198
+ fun readVersionAndGroupStrings(propertiesFile: File, hermesVersionFile: File): Coordinates {
168
199
  val reactAndroidProperties = Properties()
169
200
  propertiesFile.inputStream().use { reactAndroidProperties.load(it) }
170
201
  val versionStringFromFile = (reactAndroidProperties[INTERNAL_VERSION_NAME] as? String).orEmpty()
@@ -176,10 +207,27 @@ internal object DependencyUtils {
176
207
  versionStringFromFile
177
208
  }
178
209
  // Returns Maven group for repos using different group for Maven artifacts
179
- val groupString =
180
- reactAndroidProperties[INTERNAL_PUBLISHING_GROUP] as? String
181
- ?: DEFAULT_INTERNAL_PUBLISHING_GROUP
182
- return Pair(versionString, groupString)
210
+ val reactGroupString =
211
+ reactAndroidProperties[INTERNAL_REACT_PUBLISHING_GROUP] as? String
212
+ ?: DEFAULT_INTERNAL_REACT_PUBLISHING_GROUP
213
+ val hermesGroupString =
214
+ reactAndroidProperties[INTERNAL_HERMES_PUBLISHING_GROUP] as? String
215
+ ?: DEFAULT_INTERNAL_HERMES_PUBLISHING_GROUP
216
+ // TODO: T237406039 read both versions from the same file
217
+ val hermesVersionProperties = Properties()
218
+ hermesVersionFile.inputStream().use { hermesVersionProperties.load(it) }
219
+
220
+ val hermesVersion = versionString
221
+ val hermesV1Version =
222
+ (hermesVersionProperties[INTERNAL_HERMES_V1_VERSION_NAME] as? String).orEmpty()
223
+
224
+ return Coordinates(
225
+ versionString,
226
+ hermesVersion,
227
+ hermesV1Version,
228
+ reactGroupString,
229
+ hermesGroupString,
230
+ )
183
231
  }
184
232
 
185
233
  fun Project.mavenRepoFromUrl(
@@ -122,11 +122,16 @@ private fun detectCliFile(reactNativeRoot: File, preconfiguredCliFile: File?): F
122
122
  * used if the user is building Hermes from source.
123
123
  * 3. The file located in `node_modules/react-native/sdks/hermesc/%OS-BIN%/hermesc` where `%OS-BIN%`
124
124
  * is substituted with the correct OS arch. This will be used if the user is using a precompiled
125
- * hermes-engine package.
125
+ * hermes-engine package. Or, if the user has opted in to use Hermes V1, the used file will be
126
+ * located in `node_modules/hermes-compiler/%OS-BIN%/hermesc` where `%OS-BIN%` is substituted
127
+ * with the correct OS arch.
126
128
  * 4. Fails otherwise
127
129
  */
128
- internal fun detectOSAwareHermesCommand(projectRoot: File, hermesCommand: String): String {
129
- // 1. If the project specifies a Hermes command, don't second guess it.
130
+ internal fun detectOSAwareHermesCommand(
131
+ projectRoot: File,
132
+ hermesCommand: String,
133
+ hermesV1Enabled: Boolean = false,
134
+ ): String { // 1. If the project specifies a Hermes command, don't second guess it.
130
135
  if (hermesCommand.isNotBlank()) {
131
136
  val osSpecificHermesCommand =
132
137
  if ("%OS-BIN%" in hermesCommand) {
@@ -146,9 +151,13 @@ internal fun detectOSAwareHermesCommand(projectRoot: File, hermesCommand: String
146
151
  return builtHermesc.cliPath(projectRoot)
147
152
  }
148
153
 
149
- // 3. If the react-native contains a pre-built hermesc, use it.
154
+ // 3. If Hermes V1 is enabled, use hermes-compiler from npm, otherwise, if the
155
+ // react-native contains a pre-built hermesc, use it.
156
+ // TODO: T237406039 use hermes-compiler from npm for both
157
+ val hermesCPath = if (hermesV1Enabled) HERMES_COMPILER_NPM_DIR else HERMESC_IN_REACT_NATIVE_DIR
150
158
  val prebuiltHermesPath =
151
- HERMESC_IN_REACT_NATIVE_DIR.plus(getHermesCBin())
159
+ hermesCPath
160
+ .plus(getHermesCBin())
152
161
  .replace("%OS-BIN%", getHermesOSBin())
153
162
  // Execution on Windows fails with / as separator
154
163
  .replace('/', File.separatorChar)
@@ -233,6 +242,7 @@ internal fun readPackageJsonFile(
233
242
  return packageJson?.let { JsonUtils.fromPackageJson(it) }
234
243
  }
235
244
 
245
+ private const val HERMES_COMPILER_NPM_DIR = "node_modules/hermes-compiler/%OS-BIN%/"
236
246
  private const val HERMESC_IN_REACT_NATIVE_DIR = "node_modules/react-native/sdks/hermesc/%OS-BIN%/"
237
247
  private const val HERMESC_BUILT_FROM_SOURCE_DIR =
238
248
  "node_modules/react-native/ReactAndroid/hermes-engine/build/hermes/bin/"
@@ -13,14 +13,17 @@ import com.facebook.react.utils.KotlinStdlibCompatUtils.lowercaseCompat
13
13
  import com.facebook.react.utils.KotlinStdlibCompatUtils.toBooleanStrictOrNullCompat
14
14
  import com.facebook.react.utils.PropertyUtils.EDGE_TO_EDGE_ENABLED
15
15
  import com.facebook.react.utils.PropertyUtils.HERMES_ENABLED
16
+ import com.facebook.react.utils.PropertyUtils.HERMES_V1_ENABLED
16
17
  import com.facebook.react.utils.PropertyUtils.REACT_NATIVE_ARCHITECTURES
17
18
  import com.facebook.react.utils.PropertyUtils.SCOPED_EDGE_TO_EDGE_ENABLED
18
19
  import com.facebook.react.utils.PropertyUtils.SCOPED_HERMES_ENABLED
20
+ import com.facebook.react.utils.PropertyUtils.SCOPED_HERMES_V1_ENABLED
19
21
  import com.facebook.react.utils.PropertyUtils.SCOPED_REACT_NATIVE_ARCHITECTURES
20
22
  import com.facebook.react.utils.PropertyUtils.SCOPED_USE_THIRD_PARTY_JSC
21
23
  import com.facebook.react.utils.PropertyUtils.USE_THIRD_PARTY_JSC
22
24
  import org.gradle.api.Project
23
25
  import org.gradle.api.file.DirectoryProperty
26
+ import org.jetbrains.kotlin.gradle.plugin.extraProperties
24
27
 
25
28
  internal object ProjectUtils {
26
29
 
@@ -68,6 +71,17 @@ internal object ProjectUtils {
68
71
  (project.hasProperty(SCOPED_USE_THIRD_PARTY_JSC) &&
69
72
  project.property(SCOPED_USE_THIRD_PARTY_JSC).toString().toBoolean())
70
73
 
74
+ internal val Project.isHermesV1Enabled: Boolean
75
+ get() =
76
+ (project.hasProperty(HERMES_V1_ENABLED) &&
77
+ project.property(HERMES_V1_ENABLED).toString().toBoolean()) ||
78
+ (project.hasProperty(SCOPED_HERMES_V1_ENABLED) &&
79
+ project.property(SCOPED_HERMES_V1_ENABLED).toString().toBoolean()) ||
80
+ (project.extraProperties.has(HERMES_V1_ENABLED) &&
81
+ project.extraProperties.get(HERMES_V1_ENABLED).toString().toBoolean()) ||
82
+ (project.extraProperties.has(SCOPED_HERMES_V1_ENABLED) &&
83
+ project.extraProperties.get(SCOPED_HERMES_V1_ENABLED).toString().toBoolean())
84
+
71
85
  internal fun Project.needsCodegenFromPackageJson(rootProperty: DirectoryProperty): Boolean {
72
86
  val parsedPackageJson = readPackageJsonFile(this, rootProperty)
73
87
  return needsCodegenFromPackageJson(parsedPackageJson)
@@ -18,6 +18,10 @@ object PropertyUtils {
18
18
  const val HERMES_ENABLED = "hermesEnabled"
19
19
  const val SCOPED_HERMES_ENABLED = "react.hermesEnabled"
20
20
 
21
+ /** Public property that toggles Hermes V1 */
22
+ const val HERMES_V1_ENABLED = "hermesV1Enabled"
23
+ const val SCOPED_HERMES_V1_ENABLED = "react.hermesV1Enabled"
24
+
21
25
  /** Public property that toggles edge-to-edge */
22
26
  const val EDGE_TO_EDGE_ENABLED = "edgeToEdgeEnabled"
23
27
  const val SCOPED_EDGE_TO_EDGE_ENABLED = "react.edgeToEdgeEnabled"
@@ -68,9 +72,17 @@ object PropertyUtils {
68
72
  const val INTERNAL_USE_HERMES_NIGHTLY = "react.internal.useHermesNightly"
69
73
 
70
74
  /** Internal property used to override the publishing group for the React Native artifacts. */
71
- const val INTERNAL_PUBLISHING_GROUP = "react.internal.publishingGroup"
72
- const val DEFAULT_INTERNAL_PUBLISHING_GROUP = "com.facebook.react"
75
+ const val INTERNAL_REACT_PUBLISHING_GROUP = "react.internal.publishingGroup"
76
+ const val INTERNAL_HERMES_PUBLISHING_GROUP = "react.internal.hermesPublishingGroup"
77
+ const val DEFAULT_INTERNAL_REACT_PUBLISHING_GROUP = "com.facebook.react"
78
+ const val DEFAULT_INTERNAL_HERMES_PUBLISHING_GROUP = "com.facebook.hermes"
73
79
 
74
80
  /** Internal property used to control the version name of React Native */
75
81
  const val INTERNAL_VERSION_NAME = "VERSION_NAME"
82
+
83
+ /**
84
+ * Internal property, shared with iOS, used to control the version name of Hermes Engine. This is
85
+ * stored in sdks/hermes-engine/version.properties
86
+ */
87
+ const val INTERNAL_HERMES_V1_VERSION_NAME = "HERMES_V1_VERSION_NAME"
76
88
  }
@@ -275,71 +275,160 @@ class DependencyUtilsTest {
275
275
  fun configureDependencies_withEmptyVersion_doesNothing() {
276
276
  val project = createProject()
277
277
 
278
- configureDependencies(project, "")
278
+ configureDependencies(project, DependencyUtils.Coordinates("", "", ""))
279
279
 
280
280
  assertThat(project.configurations.first().resolutionStrategy.forcedModules.isEmpty()).isTrue()
281
281
  }
282
282
 
283
283
  @Test
284
- fun configureDependencies_withVersionString_appliesResolutionStrategy() {
284
+ fun configureDependencies_withVersionString_appliesResolutionStrategy_withClassicHermes() {
285
285
  val project = createProject()
286
286
 
287
- configureDependencies(project, "1.2.3")
287
+ configureDependencies(project, DependencyUtils.Coordinates("1.2.3", "4.5.6", "7.8.9"))
288
288
 
289
289
  val forcedModules = project.configurations.first().resolutionStrategy.forcedModules
290
290
  assertThat(forcedModules.any { it.toString() == "com.facebook.react:react-android:1.2.3" })
291
291
  .isTrue()
292
- assertThat(forcedModules.any { it.toString() == "com.facebook.react:hermes-android:1.2.3" })
292
+ assertThat(forcedModules.any { it.toString() == "com.facebook.react:hermes-android:4.5.6" })
293
293
  .isTrue()
294
294
  }
295
295
 
296
296
  @Test
297
- fun configureDependencies_withVersionString_appliesOnAllProjects() {
297
+ fun configureDependencies_withVersionString_appliesResolutionStrategy_withHermesV1() {
298
+ val project = createProject()
299
+
300
+ configureDependencies(
301
+ project,
302
+ DependencyUtils.Coordinates("1.2.3", "4.5.6", "7.8.9"),
303
+ hermesV1Enabled = true,
304
+ )
305
+
306
+ val forcedModules = project.configurations.first().resolutionStrategy.forcedModules
307
+ assertThat(forcedModules.any { it.toString() == "com.facebook.react:react-android:1.2.3" })
308
+ .isTrue()
309
+ assertThat(forcedModules.any { it.toString() == "com.facebook.hermes:hermes-android:7.8.9" })
310
+ .isTrue()
311
+ }
312
+
313
+ @Test
314
+ fun configureDependencies_withVersionString_appliesOnAllProjects_withClassicHermes() {
298
315
  val rootProject = ProjectBuilder.builder().build()
299
316
  val appProject = ProjectBuilder.builder().withName("app").withParent(rootProject).build()
300
317
  val libProject = ProjectBuilder.builder().withName("lib").withParent(rootProject).build()
301
318
  appProject.plugins.apply("com.android.application")
302
319
  libProject.plugins.apply("com.android.library")
303
320
 
304
- configureDependencies(appProject, "1.2.3")
321
+ configureDependencies(appProject, DependencyUtils.Coordinates("1.2.3", "4.5.6", "7.8.9"))
305
322
 
306
323
  val appForcedModules = appProject.configurations.first().resolutionStrategy.forcedModules
307
324
  val libForcedModules = libProject.configurations.first().resolutionStrategy.forcedModules
308
325
  assertThat(appForcedModules.any { it.toString() == "com.facebook.react:react-android:1.2.3" })
309
326
  .isTrue()
310
- assertThat(appForcedModules.any { it.toString() == "com.facebook.react:hermes-android:1.2.3" })
327
+ assertThat(appForcedModules.any { it.toString() == "com.facebook.react:hermes-android:4.5.6" })
311
328
  .isTrue()
312
329
  assertThat(libForcedModules.any { it.toString() == "com.facebook.react:react-android:1.2.3" })
313
330
  .isTrue()
314
- assertThat(libForcedModules.any { it.toString() == "com.facebook.react:hermes-android:1.2.3" })
331
+ assertThat(libForcedModules.any { it.toString() == "com.facebook.react:hermes-android:4.5.6" })
315
332
  .isTrue()
316
333
  }
317
334
 
318
335
  @Test
319
- fun configureDependencies_withVersionStringAndGroupString_appliesOnAllProjects() {
336
+ fun configureDependencies_withVersionString_appliesOnAllProjects_withHermesV1() {
320
337
  val rootProject = ProjectBuilder.builder().build()
321
338
  val appProject = ProjectBuilder.builder().withName("app").withParent(rootProject).build()
322
339
  val libProject = ProjectBuilder.builder().withName("lib").withParent(rootProject).build()
323
340
  appProject.plugins.apply("com.android.application")
324
341
  libProject.plugins.apply("com.android.library")
325
342
 
326
- configureDependencies(appProject, "1.2.3", "io.github.test")
343
+ configureDependencies(
344
+ appProject,
345
+ DependencyUtils.Coordinates("1.2.3", "4.5.6", "7.8.9"),
346
+ hermesV1Enabled = true,
347
+ )
348
+
349
+ val appForcedModules = appProject.configurations.first().resolutionStrategy.forcedModules
350
+ val libForcedModules = libProject.configurations.first().resolutionStrategy.forcedModules
351
+ assertThat(appForcedModules.any { it.toString() == "com.facebook.react:react-android:1.2.3" })
352
+ .isTrue()
353
+ assertThat(appForcedModules.any { it.toString() == "com.facebook.hermes:hermes-android:7.8.9" })
354
+ .isTrue()
355
+ assertThat(libForcedModules.any { it.toString() == "com.facebook.react:react-android:1.2.3" })
356
+ .isTrue()
357
+ assertThat(libForcedModules.any { it.toString() == "com.facebook.hermes:hermes-android:7.8.9" })
358
+ .isTrue()
359
+ }
360
+
361
+ @Test
362
+ fun configureDependencies_withVersionStringAndGroupString_appliesOnAllProjects_withClassicHermes() {
363
+ val rootProject = ProjectBuilder.builder().build()
364
+ val appProject = ProjectBuilder.builder().withName("app").withParent(rootProject).build()
365
+ val libProject = ProjectBuilder.builder().withName("lib").withParent(rootProject).build()
366
+ appProject.plugins.apply("com.android.application")
367
+ libProject.plugins.apply("com.android.library")
368
+
369
+ configureDependencies(
370
+ appProject,
371
+ DependencyUtils.Coordinates(
372
+ "1.2.3",
373
+ "4.5.6",
374
+ "7.8.9",
375
+ "io.github.test",
376
+ "io.github.test.hermes",
377
+ ),
378
+ )
327
379
 
328
380
  val appForcedModules = appProject.configurations.first().resolutionStrategy.forcedModules
329
381
  val libForcedModules = libProject.configurations.first().resolutionStrategy.forcedModules
330
382
  assertThat(appForcedModules.any { it.toString() == "io.github.test:react-android:1.2.3" })
331
383
  .isTrue()
332
- assertThat(appForcedModules.any { it.toString() == "io.github.test:hermes-android:1.2.3" })
384
+ assertThat(appForcedModules.any { it.toString() == "io.github.test:hermes-android:4.5.6" })
333
385
  .isTrue()
334
386
  assertThat(libForcedModules.any { it.toString() == "io.github.test:react-android:1.2.3" })
335
387
  .isTrue()
336
- assertThat(libForcedModules.any { it.toString() == "io.github.test:hermes-android:1.2.3" })
388
+ assertThat(libForcedModules.any { it.toString() == "io.github.test:hermes-android:4.5.6" })
337
389
  .isTrue()
338
390
  }
339
391
 
340
392
  @Test
341
- fun getDependencySubstitutions_withDefaultGroup_substitutesCorrectly() {
342
- val dependencySubstitutions = getDependencySubstitutions("0.42.0")
393
+ fun configureDependencies_withVersionStringAndGroupString_appliesOnAllProjects_withHermesV1() {
394
+ val rootProject = ProjectBuilder.builder().build()
395
+ val appProject = ProjectBuilder.builder().withName("app").withParent(rootProject).build()
396
+ val libProject = ProjectBuilder.builder().withName("lib").withParent(rootProject).build()
397
+ appProject.plugins.apply("com.android.application")
398
+ libProject.plugins.apply("com.android.library")
399
+
400
+ configureDependencies(
401
+ appProject,
402
+ DependencyUtils.Coordinates(
403
+ "1.2.3",
404
+ "4.5.6",
405
+ "7.8.9",
406
+ "io.github.test",
407
+ "io.github.test.hermes",
408
+ ),
409
+ hermesV1Enabled = true,
410
+ )
411
+
412
+ val appForcedModules = appProject.configurations.first().resolutionStrategy.forcedModules
413
+ val libForcedModules = libProject.configurations.first().resolutionStrategy.forcedModules
414
+ assertThat(appForcedModules.any { it.toString() == "io.github.test:react-android:1.2.3" })
415
+ .isTrue()
416
+ assertThat(
417
+ appForcedModules.any { it.toString() == "io.github.test.hermes:hermes-android:7.8.9" }
418
+ )
419
+ .isTrue()
420
+ assertThat(libForcedModules.any { it.toString() == "io.github.test:react-android:1.2.3" })
421
+ .isTrue()
422
+ assertThat(
423
+ libForcedModules.any { it.toString() == "io.github.test.hermes:hermes-android:7.8.9" }
424
+ )
425
+ .isTrue()
426
+ }
427
+
428
+ @Test
429
+ fun getDependencySubstitutions_withDefaultGroup_substitutesCorrectly_withClassicHermes() {
430
+ val dependencySubstitutions =
431
+ getDependencySubstitutions(DependencyUtils.Coordinates("0.42.0", "0.42.0", "0.43.0"))
343
432
 
344
433
  assertThat("com.facebook.react:react-native").isEqualTo(dependencySubstitutions[0].first)
345
434
  assertThat("com.facebook.react:react-android:0.42.0")
@@ -358,8 +447,41 @@ class DependencyUtilsTest {
358
447
  }
359
448
 
360
449
  @Test
361
- fun getDependencySubstitutions_withCustomGroup_substitutesCorrectly() {
362
- val dependencySubstitutions = getDependencySubstitutions("0.42.0", "io.github.test")
450
+ fun getDependencySubstitutions_withDefaultGroup_substitutesCorrectly_withHermesV1() {
451
+ val dependencySubstitutions =
452
+ getDependencySubstitutions(
453
+ DependencyUtils.Coordinates("0.42.0", "0.42.0", "0.43.0"),
454
+ hermesV1Enabled = true,
455
+ )
456
+
457
+ assertThat("com.facebook.react:react-native").isEqualTo(dependencySubstitutions[0].first)
458
+ assertThat("com.facebook.react:react-android:0.42.0")
459
+ .isEqualTo(dependencySubstitutions[0].second)
460
+ assertThat(
461
+ "The react-native artifact was deprecated in favor of react-android due to https://github.com/facebook/react-native/issues/35210."
462
+ )
463
+ .isEqualTo(dependencySubstitutions[0].third)
464
+ assertThat("com.facebook.react:hermes-engine").isEqualTo(dependencySubstitutions[1].first)
465
+ assertThat("com.facebook.hermes:hermes-android:0.43.0")
466
+ .isEqualTo(dependencySubstitutions[1].second)
467
+ assertThat(
468
+ "The hermes-engine artifact was deprecated in favor of hermes-android due to https://github.com/facebook/react-native/issues/35210."
469
+ )
470
+ .isEqualTo(dependencySubstitutions[1].third)
471
+ }
472
+
473
+ @Test
474
+ fun getDependencySubstitutions_withCustomGroup_substitutesCorrectly_withClassicHermes() {
475
+ val dependencySubstitutions =
476
+ getDependencySubstitutions(
477
+ DependencyUtils.Coordinates(
478
+ "0.42.0",
479
+ "0.42.0",
480
+ "0.43.0",
481
+ "io.github.test",
482
+ "io.github.test.hermes",
483
+ )
484
+ )
363
485
 
364
486
  assertThat("com.facebook.react:react-native").isEqualTo(dependencySubstitutions[0].first)
365
487
  assertThat("io.github.test:react-android:0.42.0").isEqualTo(dependencySubstitutions[0].second)
@@ -383,6 +505,44 @@ class DependencyUtilsTest {
383
505
  .isEqualTo(dependencySubstitutions[3].third)
384
506
  }
385
507
 
508
+ @Test
509
+ fun getDependencySubstitutions_withCustomGroup_substitutesCorrectly_withHermesV1() {
510
+ val dependencySubstitutions =
511
+ getDependencySubstitutions(
512
+ DependencyUtils.Coordinates(
513
+ "0.42.0",
514
+ "0.42.0",
515
+ "0.43.0",
516
+ "io.github.test",
517
+ "io.github.test.hermes",
518
+ ),
519
+ hermesV1Enabled = true,
520
+ )
521
+
522
+ assertThat("com.facebook.react:react-native").isEqualTo(dependencySubstitutions[0].first)
523
+ assertThat("io.github.test:react-android:0.42.0").isEqualTo(dependencySubstitutions[0].second)
524
+ assertThat(
525
+ "The react-native artifact was deprecated in favor of react-android due to https://github.com/facebook/react-native/issues/35210."
526
+ )
527
+ .isEqualTo(dependencySubstitutions[0].third)
528
+ assertThat("com.facebook.react:hermes-engine").isEqualTo(dependencySubstitutions[1].first)
529
+ assertThat("io.github.test.hermes:hermes-android:0.43.0")
530
+ .isEqualTo(dependencySubstitutions[1].second)
531
+ assertThat(
532
+ "The hermes-engine artifact was deprecated in favor of hermes-android due to https://github.com/facebook/react-native/issues/35210."
533
+ )
534
+ .isEqualTo(dependencySubstitutions[1].third)
535
+ assertThat("com.facebook.react:react-android").isEqualTo(dependencySubstitutions[2].first)
536
+ assertThat("io.github.test:react-android:0.42.0").isEqualTo(dependencySubstitutions[2].second)
537
+ assertThat("The react-android dependency was modified to use the correct Maven group.")
538
+ .isEqualTo(dependencySubstitutions[2].third)
539
+ assertThat("com.facebook.react:hermes-android").isEqualTo(dependencySubstitutions[3].first)
540
+ assertThat("io.github.test.hermes:hermes-android:0.43.0")
541
+ .isEqualTo(dependencySubstitutions[3].second)
542
+ assertThat("The hermes-android dependency was modified to use the correct Maven group.")
543
+ .isEqualTo(dependencySubstitutions[3].third)
544
+ }
545
+
386
546
  @Test
387
547
  fun readVersionString_withCorrectVersionString_returnsIt() {
388
548
  val propertiesFile =
@@ -396,9 +556,25 @@ class DependencyUtilsTest {
396
556
  )
397
557
  }
398
558
 
399
- val versionString = readVersionAndGroupStrings(propertiesFile).first
559
+ val hermesVersionFile =
560
+ tempFolder.newFile("version.properties").apply {
561
+ writeText(
562
+ """
563
+ HERMES_V1_VERSION_NAME=1000.0.0
564
+ ANOTHER_PROPERTY=true
565
+ """
566
+ .trimIndent()
567
+ )
568
+ }
569
+
570
+ val strings = readVersionAndGroupStrings(propertiesFile, hermesVersionFile)
571
+ val versionString = strings.versionString
572
+ val hermesVersionString = strings.hermesVersionString
573
+ val hermesV1VersionString = strings.hermesV1VersionString
400
574
 
401
575
  assertThat(versionString).isEqualTo("1000.0.0")
576
+ assertThat(hermesVersionString).isEqualTo("1000.0.0")
577
+ assertThat(hermesV1VersionString).isEqualTo("1000.0.0")
402
578
  }
403
579
 
404
580
  @Test
@@ -408,15 +584,33 @@ class DependencyUtilsTest {
408
584
  writeText(
409
585
  """
410
586
  VERSION_NAME=0.0.0-20221101-2019-cfe811ab1
587
+ HERMES_VERSION_NAME=0.12.0-commitly-20221101-2019-cfe811ab1
588
+ HERMES_V1_VERSION_NAME=250829098.0.0-stable
589
+ ANOTHER_PROPERTY=true
590
+ """
591
+ .trimIndent()
592
+ )
593
+ }
594
+
595
+ val hermesVersionFile =
596
+ tempFolder.newFile("version.properties").apply {
597
+ writeText(
598
+ """
599
+ HERMES_V1_VERSION_NAME=250829098.0.0-stable
411
600
  ANOTHER_PROPERTY=true
412
601
  """
413
602
  .trimIndent()
414
603
  )
415
604
  }
416
605
 
417
- val versionString = readVersionAndGroupStrings(propertiesFile).first
606
+ val strings = readVersionAndGroupStrings(propertiesFile, hermesVersionFile)
607
+ val versionString = strings.versionString
608
+ val hermesVersionString = strings.hermesVersionString
609
+ val hermesV1VersionString = strings.hermesV1VersionString
418
610
 
419
611
  assertThat(versionString).isEqualTo("0.0.0-20221101-2019-cfe811ab1-SNAPSHOT")
612
+ assertThat(hermesVersionString).isEqualTo("0.0.0-20221101-2019-cfe811ab1-SNAPSHOT")
613
+ assertThat(hermesV1VersionString).isEqualTo("250829098.0.0-stable")
420
614
  }
421
615
 
422
616
  @Test
@@ -431,8 +625,23 @@ class DependencyUtilsTest {
431
625
  )
432
626
  }
433
627
 
434
- val versionString = readVersionAndGroupStrings(propertiesFile).first
628
+ val hermesVersionFile =
629
+ tempFolder.newFile("version.properties").apply {
630
+ writeText(
631
+ """
632
+ ANOTHER_PROPERTY=true
633
+ """
634
+ .trimIndent()
635
+ )
636
+ }
637
+
638
+ val strings = readVersionAndGroupStrings(propertiesFile, hermesVersionFile)
639
+ val versionString = strings.versionString
640
+ val hermesVersionString = strings.hermesVersionString
641
+ val hermesV1VersionString = strings.hermesV1VersionString
435
642
  assertThat(versionString).isEqualTo("")
643
+ assertThat(hermesVersionString).isEqualTo("")
644
+ assertThat(hermesV1VersionString).isEqualTo("")
436
645
  }
437
646
 
438
647
  @Test
@@ -448,8 +657,24 @@ class DependencyUtilsTest {
448
657
  )
449
658
  }
450
659
 
451
- val versionString = readVersionAndGroupStrings(propertiesFile).first
660
+ val hermesVersionFile =
661
+ tempFolder.newFile("version.properties").apply {
662
+ writeText(
663
+ """
664
+ HERMES_V1_VERSION_NAME=
665
+ ANOTHER_PROPERTY=true
666
+ """
667
+ .trimIndent()
668
+ )
669
+ }
670
+
671
+ val strings = readVersionAndGroupStrings(propertiesFile, hermesVersionFile)
672
+ val versionString = strings.versionString
673
+ val hermesVersionString = strings.hermesVersionString
674
+ val hermesV1VersionString = strings.hermesV1VersionString
452
675
  assertThat(versionString).isEqualTo("")
676
+ assertThat(hermesVersionString).isEqualTo("")
677
+ assertThat(hermesV1VersionString).isEqualTo("")
453
678
  }
454
679
 
455
680
  @Test
@@ -459,15 +684,30 @@ class DependencyUtilsTest {
459
684
  writeText(
460
685
  """
461
686
  react.internal.publishingGroup=io.github.test
687
+ react.internal.hermesPublishingGroup=io.github.test
688
+ ANOTHER_PROPERTY=true
689
+ """
690
+ .trimIndent()
691
+ )
692
+ }
693
+
694
+ val hermesVersionFile =
695
+ tempFolder.newFile("version.properties").apply {
696
+ writeText(
697
+ """
698
+ HERMES_V1_VERSION_NAME=
462
699
  ANOTHER_PROPERTY=true
463
700
  """
464
701
  .trimIndent()
465
702
  )
466
703
  }
467
704
 
468
- val groupString = readVersionAndGroupStrings(propertiesFile).second
705
+ val strings = readVersionAndGroupStrings(propertiesFile, hermesVersionFile)
706
+ val reactGroupString = strings.reactGroupString
707
+ val hermesGroupString = strings.hermesGroupString
469
708
 
470
- assertThat(groupString).isEqualTo("io.github.test")
709
+ assertThat(reactGroupString).isEqualTo("io.github.test")
710
+ assertThat(hermesGroupString).isEqualTo("io.github.test")
471
711
  }
472
712
 
473
713
  @Test
@@ -482,9 +722,23 @@ class DependencyUtilsTest {
482
722
  )
483
723
  }
484
724
 
485
- val groupString = readVersionAndGroupStrings(propertiesFile).second
725
+ val hermesVersionFile =
726
+ tempFolder.newFile("version.properties").apply {
727
+ writeText(
728
+ """
729
+ HERMES_V1_VERSION_NAME=
730
+ ANOTHER_PROPERTY=true
731
+ """
732
+ .trimIndent()
733
+ )
734
+ }
735
+
736
+ val strings = readVersionAndGroupStrings(propertiesFile, hermesVersionFile)
737
+ val reactGroupString = strings.reactGroupString
738
+ val hermesGroupString = strings.hermesGroupString
486
739
 
487
- assertThat(groupString).isEqualTo("com.facebook.react")
740
+ assertThat(reactGroupString).isEqualTo("com.facebook.react")
741
+ assertThat(hermesGroupString).isEqualTo("com.facebook.hermes")
488
742
  }
489
743
 
490
744
  @Test
@@ -162,6 +162,16 @@ class PathUtilsTest {
162
162
  assertThat(detectOSAwareHermesCommand(tempFolder.root, "")).isEqualTo(expected.toString())
163
163
  }
164
164
 
165
+ @Test
166
+ @WithOs(OS.MAC)
167
+ fun detectOSAwareHermesCommand_withHermesV1Enabled() {
168
+ tempFolder.newFolder("node_modules/hermes-compiler/osx-bin/")
169
+ val expected = tempFolder.newFile("node_modules/hermes-compiler/osx-bin/hermesc")
170
+
171
+ assertThat(detectOSAwareHermesCommand(tempFolder.root, "", hermesV1Enabled = true))
172
+ .isEqualTo(expected.toString())
173
+ }
174
+
165
175
  @Test(expected = IllegalStateException::class)
166
176
  @WithOs(OS.MAC)
167
177
  fun detectOSAwareHermesCommand_failsIfNotFound() {