From aea82e914ef8abdffc617cca6ab7e94e9b3dc554 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20Ro=C5=BCniata?= <56474758+krozniata@users.noreply.github.com> Date: Thu, 13 Nov 2025 12:22:36 +0100 Subject: [PATCH 1/6] chore: update assets & res `srcDirs` to match RN paths --- .../com/callstack/react/brownfield/plugin/RNSourceSets.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle-plugins/react/brownfield/src/main/kotlin/com/callstack/react/brownfield/plugin/RNSourceSets.kt b/gradle-plugins/react/brownfield/src/main/kotlin/com/callstack/react/brownfield/plugin/RNSourceSets.kt index 3eda0cd5..1b246457 100644 --- a/gradle-plugins/react/brownfield/src/main/kotlin/com/callstack/react/brownfield/plugin/RNSourceSets.kt +++ b/gradle-plugins/react/brownfield/src/main/kotlin/com/callstack/react/brownfield/plugin/RNSourceSets.kt @@ -45,8 +45,8 @@ object RNSourceSets { private fun configureSourceSets() { androidExtension.sourceSets.getByName("main") { - it.assets.srcDirs("$appBuildDir/generated/assets/createBundleReleaseJsAndAssets") - it.res.srcDirs("$appBuildDir/generated/res/createBundleReleaseJsAndAssets") + it.assets.srcDirs("$appBuildDir/generated/assets/react/release") + it.res.srcDirs("$appBuildDir/generated/res/react/release") it.java.srcDirs("$moduleBuildDir/generated/autolinking/src/main/java") } From 4e7f465acfa433a17f33519849ca297370a7aad2 Mon Sep 17 00:00:00 2001 From: artus9033 Date: Thu, 13 Nov 2025 14:41:36 +0100 Subject: [PATCH 2/6] feat: detect path to generated JS assets & res in Gradle plugin --- .../react/brownfield/plugin/RNSourceSets.kt | 40 +++++++++++++++++-- .../react/brownfield/shared/Constants.kt | 5 +++ .../react/brownfield/shared/Logging.kt | 6 +++ 3 files changed, 47 insertions(+), 4 deletions(-) diff --git a/gradle-plugins/react/brownfield/src/main/kotlin/com/callstack/react/brownfield/plugin/RNSourceSets.kt b/gradle-plugins/react/brownfield/src/main/kotlin/com/callstack/react/brownfield/plugin/RNSourceSets.kt index 1b246457..410a189c 100644 --- a/gradle-plugins/react/brownfield/src/main/kotlin/com/callstack/react/brownfield/plugin/RNSourceSets.kt +++ b/gradle-plugins/react/brownfield/src/main/kotlin/com/callstack/react/brownfield/plugin/RNSourceSets.kt @@ -2,6 +2,8 @@ package com.callstack.react.brownfield.plugin import com.android.build.gradle.LibraryExtension import com.callstack.react.brownfield.exceptions.NameSpaceNotFound +import com.callstack.react.brownfield.shared.Constants +import com.callstack.react.brownfield.shared.Logging import com.callstack.react.brownfield.utils.Extension import com.callstack.react.brownfield.utils.Utils import org.gradle.api.Project @@ -43,10 +45,36 @@ object RNSourceSets { configureTasks() } + private fun genAssetsPath(bundlePathSegment: String): String { + return "${appBuildDir}/generated/assets/$bundlePathSegment" + } + private fun configureSourceSets() { androidExtension.sourceSets.getByName("main") { - it.assets.srcDirs("$appBuildDir/generated/assets/react/release") - it.res.srcDirs("$appBuildDir/generated/res/react/release") + // path on RN >= 0.82.x + val rn082UpAssetsDir = + File(genAssetsPath(Constants.RN_0_82_UP_BUNDLE_PATH_SEGMENT)) + val rn082UpAssetsDirExists = rn082UpAssetsDir.exists() + // path on RN <= 0.81.x + val rn081DownAssetsDir = + File(genAssetsPath(Constants.RN_0_81_DOWN_BUNDLE_PATH_SEGMENT)) + val rn081DownAssetsDirExists = rn081DownAssetsDir.exists() + + if (rn081DownAssetsDirExists && rn082UpAssetsDirExists) { + Logging.warn( + "Two RN autogenerated asset paths exist: ${rn082UpAssetsDir.path} (RN >= 0.82) and ${rn081DownAssetsDir.path}" + + "(Rock project or RN < 0.82). The newer path will be used (${rn082UpAssetsDir.path})." + ) + Logging.warn( + "It is likely that you have upgraded RN versions and should clean your app's " + + "build directory and rebuild just once in order for this warning to disappear." + ) + } + + val bundlePathSegment = + if (rn082UpAssetsDirExists) Constants.RN_0_82_UP_BUNDLE_PATH_SEGMENT else Constants.RN_0_81_DOWN_BUNDLE_PATH_SEGMENT + it.assets.srcDirs(genAssetsPath(bundlePathSegment)) + it.res.srcDirs("$appBuildDir/generated/res/react/$bundlePathSegment") it.java.srcDirs("$moduleBuildDir/generated/autolinking/src/main/java") } @@ -61,7 +89,8 @@ object RNSourceSets { private fun getLibraryNameSpace(): String { val nameSpace = androidExtension.namespace - return nameSpace ?: throw NameSpaceNotFound("namespace must be defined in your android library build.gradle") + return nameSpace + ?: throw NameSpaceNotFound("namespace must be defined in your android library build.gradle") } private fun patchRNEntryPoint( @@ -77,7 +106,10 @@ object RNSourceSets { val rnEntryPointTask = appProject.tasks.findByName(rnEntryPointTaskName) ?: return task.dependsOn(rnEntryPointTask) - val sourceFile = File(moduleBuildDir.toString(), "$path/com/facebook/react/ReactNativeApplicationEntryPoint.java") + val sourceFile = File( + moduleBuildDir.toString(), + "$path/com/facebook/react/ReactNativeApplicationEntryPoint.java" + ) task.doLast { if (sourceFile.exists()) { var content = sourceFile.readText() diff --git a/gradle-plugins/react/brownfield/src/main/kotlin/com/callstack/react/brownfield/shared/Constants.kt b/gradle-plugins/react/brownfield/src/main/kotlin/com/callstack/react/brownfield/shared/Constants.kt index eb3c80c0..7e01225b 100644 --- a/gradle-plugins/react/brownfield/src/main/kotlin/com/callstack/react/brownfield/shared/Constants.kt +++ b/gradle-plugins/react/brownfield/src/main/kotlin/com/callstack/react/brownfield/shared/Constants.kt @@ -6,4 +6,9 @@ object Constants { const val RE_BUNDLE_FOLDER = "aar_rebundle" const val INTERMEDIATES_TEMP_DIR = PLUGIN_NAME + + // path segments for specific RN version range which need to be appended to generated/res/ + // and generated/assets/ to obtain path to resources produced by the proper build phase + const val RN_0_82_UP_BUNDLE_PATH_SEGMENT = "react/release" + const val RN_0_81_DOWN_BUNDLE_PATH_SEGMENT = "createBundleReleaseJsAndAssets" } diff --git a/gradle-plugins/react/brownfield/src/main/kotlin/com/callstack/react/brownfield/shared/Logging.kt b/gradle-plugins/react/brownfield/src/main/kotlin/com/callstack/react/brownfield/shared/Logging.kt index a5b54a4c..7da74fc3 100644 --- a/gradle-plugins/react/brownfield/src/main/kotlin/com/callstack/react/brownfield/shared/Logging.kt +++ b/gradle-plugins/react/brownfield/src/main/kotlin/com/callstack/react/brownfield/shared/Logging.kt @@ -10,6 +10,12 @@ object Logging : BaseProject() { project.logger.error(getFormattedMessage(message), error) } + fun warn( + message: Any, + ) { + project.logger.warn(getFormattedMessage(message)) + } + fun info(message: Any) { project.logger.info(getFormattedMessage(message)) } From 4f69675c5462e3e612dc735c3c867788092ecb39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20Ro=C5=BCniata?= <56474758+krozniata@users.noreply.github.com> Date: Thu, 13 Nov 2025 15:34:17 +0100 Subject: [PATCH 3/6] use multiple srcDirs --- .../react/brownfield/plugin/RNSourceSets.kt | 40 ++----------------- 1 file changed, 4 insertions(+), 36 deletions(-) diff --git a/gradle-plugins/react/brownfield/src/main/kotlin/com/callstack/react/brownfield/plugin/RNSourceSets.kt b/gradle-plugins/react/brownfield/src/main/kotlin/com/callstack/react/brownfield/plugin/RNSourceSets.kt index 410a189c..507f2d19 100644 --- a/gradle-plugins/react/brownfield/src/main/kotlin/com/callstack/react/brownfield/plugin/RNSourceSets.kt +++ b/gradle-plugins/react/brownfield/src/main/kotlin/com/callstack/react/brownfield/plugin/RNSourceSets.kt @@ -2,8 +2,6 @@ package com.callstack.react.brownfield.plugin import com.android.build.gradle.LibraryExtension import com.callstack.react.brownfield.exceptions.NameSpaceNotFound -import com.callstack.react.brownfield.shared.Constants -import com.callstack.react.brownfield.shared.Logging import com.callstack.react.brownfield.utils.Extension import com.callstack.react.brownfield.utils.Utils import org.gradle.api.Project @@ -45,36 +43,10 @@ object RNSourceSets { configureTasks() } - private fun genAssetsPath(bundlePathSegment: String): String { - return "${appBuildDir}/generated/assets/$bundlePathSegment" - } - private fun configureSourceSets() { androidExtension.sourceSets.getByName("main") { - // path on RN >= 0.82.x - val rn082UpAssetsDir = - File(genAssetsPath(Constants.RN_0_82_UP_BUNDLE_PATH_SEGMENT)) - val rn082UpAssetsDirExists = rn082UpAssetsDir.exists() - // path on RN <= 0.81.x - val rn081DownAssetsDir = - File(genAssetsPath(Constants.RN_0_81_DOWN_BUNDLE_PATH_SEGMENT)) - val rn081DownAssetsDirExists = rn081DownAssetsDir.exists() - - if (rn081DownAssetsDirExists && rn082UpAssetsDirExists) { - Logging.warn( - "Two RN autogenerated asset paths exist: ${rn082UpAssetsDir.path} (RN >= 0.82) and ${rn081DownAssetsDir.path}" + - "(Rock project or RN < 0.82). The newer path will be used (${rn082UpAssetsDir.path})." - ) - Logging.warn( - "It is likely that you have upgraded RN versions and should clean your app's " + - "build directory and rebuild just once in order for this warning to disappear." - ) - } - - val bundlePathSegment = - if (rn082UpAssetsDirExists) Constants.RN_0_82_UP_BUNDLE_PATH_SEGMENT else Constants.RN_0_81_DOWN_BUNDLE_PATH_SEGMENT - it.assets.srcDirs(genAssetsPath(bundlePathSegment)) - it.res.srcDirs("$appBuildDir/generated/res/react/$bundlePathSegment") + it.assets.srcDirs("$appBuildDir/generated/assets/createBundleReleaseJsAndAssets", "$appBuildDir/generated/assets/react/release") + it.res.srcDirs("$appBuildDir/generated/res/createBundleReleaseJsAndAssets", "$appBuildDir/generated/res/react/release") it.java.srcDirs("$moduleBuildDir/generated/autolinking/src/main/java") } @@ -89,8 +61,7 @@ object RNSourceSets { private fun getLibraryNameSpace(): String { val nameSpace = androidExtension.namespace - return nameSpace - ?: throw NameSpaceNotFound("namespace must be defined in your android library build.gradle") + return nameSpace ?: throw NameSpaceNotFound("namespace must be defined in your android library build.gradle") } private fun patchRNEntryPoint( @@ -106,10 +77,7 @@ object RNSourceSets { val rnEntryPointTask = appProject.tasks.findByName(rnEntryPointTaskName) ?: return task.dependsOn(rnEntryPointTask) - val sourceFile = File( - moduleBuildDir.toString(), - "$path/com/facebook/react/ReactNativeApplicationEntryPoint.java" - ) + val sourceFile = File(moduleBuildDir.toString(), "$path/com/facebook/react/ReactNativeApplicationEntryPoint.java") task.doLast { if (sourceFile.exists()) { var content = sourceFile.readText() From 2e48008347da910f985a307f05e1a096887372e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20Ro=C5=BCniata?= <56474758+krozniata@users.noreply.github.com> Date: Thu, 13 Nov 2025 15:35:30 +0100 Subject: [PATCH 4/6] remove --- .../com/callstack/react/brownfield/shared/Constants.kt | 5 ----- .../kotlin/com/callstack/react/brownfield/shared/Logging.kt | 6 ------ 2 files changed, 11 deletions(-) diff --git a/gradle-plugins/react/brownfield/src/main/kotlin/com/callstack/react/brownfield/shared/Constants.kt b/gradle-plugins/react/brownfield/src/main/kotlin/com/callstack/react/brownfield/shared/Constants.kt index 7e01225b..eb3c80c0 100644 --- a/gradle-plugins/react/brownfield/src/main/kotlin/com/callstack/react/brownfield/shared/Constants.kt +++ b/gradle-plugins/react/brownfield/src/main/kotlin/com/callstack/react/brownfield/shared/Constants.kt @@ -6,9 +6,4 @@ object Constants { const val RE_BUNDLE_FOLDER = "aar_rebundle" const val INTERMEDIATES_TEMP_DIR = PLUGIN_NAME - - // path segments for specific RN version range which need to be appended to generated/res/ - // and generated/assets/ to obtain path to resources produced by the proper build phase - const val RN_0_82_UP_BUNDLE_PATH_SEGMENT = "react/release" - const val RN_0_81_DOWN_BUNDLE_PATH_SEGMENT = "createBundleReleaseJsAndAssets" } diff --git a/gradle-plugins/react/brownfield/src/main/kotlin/com/callstack/react/brownfield/shared/Logging.kt b/gradle-plugins/react/brownfield/src/main/kotlin/com/callstack/react/brownfield/shared/Logging.kt index 7da74fc3..a5b54a4c 100644 --- a/gradle-plugins/react/brownfield/src/main/kotlin/com/callstack/react/brownfield/shared/Logging.kt +++ b/gradle-plugins/react/brownfield/src/main/kotlin/com/callstack/react/brownfield/shared/Logging.kt @@ -10,12 +10,6 @@ object Logging : BaseProject() { project.logger.error(getFormattedMessage(message), error) } - fun warn( - message: Any, - ) { - project.logger.warn(getFormattedMessage(message)) - } - fun info(message: Any) { project.logger.info(getFormattedMessage(message)) } From b6b790770ee5eb878532bc2c70c02f1bcaf9ff72 Mon Sep 17 00:00:00 2001 From: artus9033 Date: Thu, 13 Nov 2025 15:38:46 +0100 Subject: [PATCH 5/6] refactor: changes after CR --- .../react/brownfield/plugin/RNSourceSets.kt | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/gradle-plugins/react/brownfield/src/main/kotlin/com/callstack/react/brownfield/plugin/RNSourceSets.kt b/gradle-plugins/react/brownfield/src/main/kotlin/com/callstack/react/brownfield/plugin/RNSourceSets.kt index 507f2d19..6f5ed274 100644 --- a/gradle-plugins/react/brownfield/src/main/kotlin/com/callstack/react/brownfield/plugin/RNSourceSets.kt +++ b/gradle-plugins/react/brownfield/src/main/kotlin/com/callstack/react/brownfield/plugin/RNSourceSets.kt @@ -44,10 +44,16 @@ object RNSourceSets { } private fun configureSourceSets() { - androidExtension.sourceSets.getByName("main") { - it.assets.srcDirs("$appBuildDir/generated/assets/createBundleReleaseJsAndAssets", "$appBuildDir/generated/assets/react/release") - it.res.srcDirs("$appBuildDir/generated/res/createBundleReleaseJsAndAssets", "$appBuildDir/generated/res/react/release") - it.java.srcDirs("$moduleBuildDir/generated/autolinking/src/main/java") + androidExtension.sourceSets.getByName("main") { sourceSet -> + for (bundlePathSegment in listOf( + "createBundleReleaseJsAndAssets", // outputs for RN <= 0.81 + "react/release" // outputs for RN >= 0.82 + )) { + sourceSet.assets.srcDirs("${appBuildDir}/generated/assets/$bundlePathSegment") + sourceSet.res.srcDirs("$appBuildDir/generated/res/$bundlePathSegment") + } + + sourceSet.java.srcDirs("$moduleBuildDir/generated/autolinking/src/main/java") } androidExtension.sourceSets.getByName("release") { From 176364533c9351a582ab7f5e98abcbbb9ee4b7f7 Mon Sep 17 00:00:00 2001 From: artus9033 Date: Thu, 13 Nov 2025 15:56:06 +0100 Subject: [PATCH 6/6] docs: document changes --- README.md | 4 ++++ docs/TROUBLESHOOTING.md | 9 +++++++++ 2 files changed, 13 insertions(+) create mode 100644 docs/TROUBLESHOOTING.md diff --git a/README.md b/README.md index 018267b9..693c321a 100644 --- a/README.md +++ b/README.md @@ -226,6 +226,10 @@ React Native Brownfield is an open source project and will always remain free to Like the project? ⚛️ [Join the team](https://callstack.com/careers/?utm_campaign=Senior_RN&utm_source=github&utm_medium=readme) who does amazing stuff for clients and drives React Native Open Source! 🔥 +## Troubleshooting + +For troubleshooting common issues, please refer to [TROUBLESHOOTING.md](docs/TROUBLESHOOTING.md). + ## Contributors Thanks goes to these wonderful people ([emoji key](https://github.com/kentcdodds/all-contributors#emoji-key)): diff --git a/docs/TROUBLESHOOTING.md b/docs/TROUBLESHOOTING.md new file mode 100644 index 00000000..4c2cc85b --- /dev/null +++ b/docs/TROUBLESHOOTING.md @@ -0,0 +1,9 @@ +This section provides a troubleshooting guide for common issues encountered when using the `react-native-brownfield` library. If you face any problems not covered here, please refer to the official documentation or reach out to the community for support. + +## [Android] `Error: duplicate resources` during `:app:mergeReleaseAssets` + +An error like `Error: duplicate resources` during the `:app:mergeReleaseAssets` phase may occur if you have upgraded your React Native version from a version less than `0.82.0`, to a version greater than or equal to (>=) `0.82.0`. This is because RN 0.82.0 changed the path to which the JS bundle is written to from `build/generated/assets/createBundleReleaseJsAndAssets/` to `build/generated/assets/react/release/`, and analogously changed the path for `res/createBundleReleaseJsAndAssets/`. The brownfield Gradle plugin adds both directories to the source sets, potentially causing a conflict of artifacts. To fix this, just once clean your build directory (precisely, the `app/build/` directory) and rebuild the project. All subsequent builds should work fine. + +## [iOS] `No script URL provided` in Release configuration + +If you encounter this error, most likely you have missed a setup step and are missing `ReactNativeBrownfield.shared.bundle = ReactNativeBundle` before your call to `startReactNative`.