From 6d7e6ec1042d1e02b3f6575a624657faac13b3d4 Mon Sep 17 00:00:00 2001 From: Nicola Corti Date: Fri, 19 Jun 2026 15:25:09 +0100 Subject: [PATCH 1/2] fix(android): restore namespace fallback for libraries not applying the React plugin #57038 narrowed configureNamespaceForLibraries from a rootProject-wide sweep to only the project applying `com.facebook.react`. That was necessary because the old code re-ran the sweep from every library, and AGP 9 errors when `finalizeDsl` is registered after a project's DSL has been finalized. The narrowing has a side effect: third-party Android libraries that do NOT apply `com.facebook.react` and still rely on the manifest `package` attribute (e.g. older react-native-linear-gradient) no longer receive a namespace, so they fail under AGP 9 with "Namespace not specified". Restore the broad coverage by sweeping all `com.android.library` subprojects once, from the application project (which applies the plugin a single time, avoiding the repeated-traversal finalizeDsl error from #57038). Guard configureNamespaceForLibraries so it registers its finalizeDsl callback at most once per project, since libraries that DO apply the React plugin are now reachable from both the per-library hook and the app-level sweep. --- .../main/kotlin/com/facebook/react/ReactPlugin.kt | 14 ++++++++++++++ .../facebook/react/utils/AgpConfiguratorUtils.kt | 11 +++++++++++ 2 files changed, 25 insertions(+) diff --git a/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/ReactPlugin.kt b/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/ReactPlugin.kt index f7bd68eeb2e5..1728e5e67d09 100644 --- a/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/ReactPlugin.kt +++ b/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/ReactPlugin.kt @@ -113,6 +113,20 @@ class ReactPlugin : Plugin { configureCodegen(project, extension, rootExtension, isLibrary = false) configureResources(project, extension) configureBuildTypesForApp(project) + + // Apply the namespace fallback to every Android library in the build, including third-party + // libraries that don't apply the `com.facebook.react` plugin and still rely on the manifest + // `package` attribute (which AGP 9 no longer accepts as a namespace). The per-library hook + // below only covers libraries that apply our plugin, so we additionally sweep all library + // subprojects from the app. We do this once, from the application project, because re-running + // a rootProject-wide traversal from every library breaks on AGP 9, which errors when + // `finalizeDsl` is registered after a project's DSL has been finalized. + // See https://github.com/facebook/react-native/pull/57038. + project.rootProject.allprojects { subproject -> + subproject.pluginManager.withPlugin("com.android.library") { + configureNamespaceForLibraries(subproject) + } + } } // Library Only Configuration diff --git a/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/AgpConfiguratorUtils.kt b/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/AgpConfiguratorUtils.kt index 8d2b31f13390..36a52185a6d0 100644 --- a/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/AgpConfiguratorUtils.kt +++ b/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/AgpConfiguratorUtils.kt @@ -106,6 +106,15 @@ internal object AgpConfiguratorUtils { } fun configureNamespaceForLibraries(project: Project) { + // This helper can be reached both from a library's own application of the React plugin and + // from the app-level sweep in ReactPlugin (which also covers libraries that don't apply + // `com.facebook.react`). A project's `finalizeDsl` callback must be registered before AGP + // finalizes its DSL — registering it twice (or after finalization) is a hard error on AGP 9+ — + // so we guard to register the namespace fallback at most once per project. + if (project.extensions.extraProperties.has(NAMESPACE_CONFIGURED_PROPERTY)) { + return + } + project.extensions.extraProperties.set(NAMESPACE_CONFIGURED_PROPERTY, true) project.extensions.getByType(LibraryAndroidComponentsExtension::class.java).finalizeDsl { ext -> if (ext.namespace == null) { val manifestFile = @@ -122,6 +131,8 @@ internal object AgpConfiguratorUtils { } } +private const val NAMESPACE_CONFIGURED_PROPERTY = "com.facebook.react.internal.namespaceConfigured" + const val DEFAULT_DEV_SERVER_PORT = "8081" fun getPackageNameFromManifest(manifest: File): String? { From edd14b0850bfe1024a4287b86c4c01b0d804aab6 Mon Sep 17 00:00:00 2001 From: Nicola Corti Date: Fri, 19 Jun 2026 16:04:31 +0100 Subject: [PATCH 2/2] fix(android): skip already-evaluated projects in the namespace sweep MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The app-level namespace sweep registered a `finalizeDsl` callback on every `com.android.library` subproject. In composite/monorepo builds (e.g. rn-tester, where ReactAndroid is a sibling library) some library projects are already evaluated — and their AGP DSL already finalized — by the time the app applies the sweep, so registering `finalizeDsl` on them fails on AGP 9 with "It is too late to call finalizeDsl". Guard the sweep with `!subproject.state.executed`: only configure projects that haven't been evaluated yet (for which `finalizeDsl` is still valid). In a regular app build the app is evaluated before its libraries (ReactRootProjectPlugin's `evaluationDependsOn(":app")`), so all libraries are still pending and covered. Already-evaluated projects in monorepo builds define their own namespace, so skipping them is safe. Verified against rn-tester: `:packages:rn-tester:android:app:assembleDebug --dry-run` reproduces the crash without this guard and succeeds with it. --- .../main/kotlin/com/facebook/react/ReactPlugin.kt | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/ReactPlugin.kt b/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/ReactPlugin.kt index 1728e5e67d09..4af3f14f34ce 100644 --- a/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/ReactPlugin.kt +++ b/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/ReactPlugin.kt @@ -122,9 +122,19 @@ class ReactPlugin : Plugin { // a rootProject-wide traversal from every library breaks on AGP 9, which errors when // `finalizeDsl` is registered after a project's DSL has been finalized. // See https://github.com/facebook/react-native/pull/57038. + // + // We skip projects that have already been evaluated: AGP finalizes a project's DSL during/ + // after its evaluation, so registering a `finalizeDsl` callback on an already-evaluated + // project is "too late" and fails on AGP 9. In a regular app build the app is evaluated + // before its libraries (see ReactRootProjectPlugin's `evaluationDependsOn(":app")`), so every + // library is still pending here. In composite/monorepo builds (e.g. rn-tester, where + // ReactAndroid is a sibling) some library projects are already evaluated by this point; those + // already define their own namespace, so skipping them is safe. project.rootProject.allprojects { subproject -> subproject.pluginManager.withPlugin("com.android.library") { - configureNamespaceForLibraries(subproject) + if (!subproject.state.executed) { + configureNamespaceForLibraries(subproject) + } } } }