@callstack/react-native-brownfield 5.1.0 → 5.1.1
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 +21 -0
- package/android/src/main/java/com/callstack/reactnativebrownfield/ReactNativeBrownfield.kt +22 -4
- package/android/src/main/java/com/callstack/reactnativebrownfield/ReactNativeFragment.kt +3 -1
- package/gradle-plugin/brownfield/build.gradle.kts +7 -0
- package/gradle-plugin/brownfield/gradle/libs.versions.toml +2 -0
- package/gradle-plugin/brownfield/gradle.properties +1 -1
- package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/artifacts/RncTransitiveDependencyDiscoverer.kt +96 -0
- package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/expo/ExpoPublishingHelper.kt +10 -214
- package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/expo/utils/BrownfieldPublishingInfo.kt +7 -0
- package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/plugin/RNBrownfieldPlugin.kt +92 -1
- package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/shared/Constants.kt +12 -1
- package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/{expo/utils/BrownfieldPrimitives.kt → shared/DependencyInfo.kt} +3 -8
- package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/shared/DependencyPublishability.kt +22 -0
- package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/shared/GradleDependencyCollector.kt +44 -0
- package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/shared/PublishingMetadataInjector.kt +181 -0
- package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/shared/TransitiveDependencyFiltering.kt +17 -0
- package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/{expo/utils → shared}/VersionMediatingDependencySet.kt +1 -1
- package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/utils/Extension.kt +11 -0
- package/gradle-plugin/brownfield/src/test/kotlin/com/callstack/react/brownfield/artifacts/RncTransitiveDependencyDiscovererTest.kt +171 -0
- package/gradle-plugin/brownfield/src/test/kotlin/com/callstack/react/brownfield/expo/ExpoPublishingHelperGradleFallbackTest.kt +47 -0
- package/gradle-plugin/brownfield/src/test/kotlin/com/callstack/react/brownfield/plugin/LegacyConsumerConfigurationTest.kt +110 -0
- package/gradle-plugin/brownfield/src/test/kotlin/com/callstack/react/brownfield/shared/DependencyPublishabilityTest.kt +67 -0
- package/gradle-plugin/brownfield/src/test/kotlin/com/callstack/react/brownfield/shared/PublishingMetadataInjectorTest.kt +222 -0
- package/gradle-plugin/brownfield/src/test/kotlin/com/callstack/react/brownfield/shared/TransitiveDependencyFilteringTest.kt +37 -0
- package/gradle-plugin/brownfield/src/test/kotlin/com/callstack/react/brownfield/shared/VersionMediatingDependencySetTest.kt +42 -0
- package/gradle-plugin/brownfield/src/test/kotlin/com/callstack/react/brownfield/utils/ExtensionTest.kt +11 -0
- package/ios/BrownfieldSurfaceRegistry.swift +93 -0
- package/ios/Views/ReactNativeView.swift +34 -5
- package/ios/Views/ReactNativeViewController.swift +120 -1
- package/lib/commonjs/expo-config-plugin/android/utils/constants.js +1 -1
- package/lib/module/expo-config-plugin/android/utils/constants.js +1 -1
- package/lib/typescript/commonjs/src/expo-config-plugin/android/utils/constants.d.ts +2 -2
- package/lib/typescript/module/src/expo-config-plugin/android/utils/constants.d.ts +2 -2
- package/package.json +2 -2
- package/src/expo-config-plugin/android/utils/constants.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# @callstack/react-native-brownfield
|
|
2
2
|
|
|
3
|
+
## 5.1.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#458](https://github.com/callstack/react-native-brownfield/pull/458) [`bf976fd`](https://github.com/callstack/react-native-brownfield/commit/bf976fdebc1abb228c03213c9a1b41d9967d0909) Thanks [@KisaneNeko](https://github.com/KisaneNeko)! - Bump the Brownfield Gradle Plugin version injected by the Expo config plugin to `2.0.0-alpha09`.
|
|
8
|
+
|
|
9
|
+
- [#462](https://github.com/callstack/react-native-brownfield/pull/462) [`2f7d5da`](https://github.com/callstack/react-native-brownfield/commit/2f7d5daf661808fdfdd1caae89236dbdfec3f62e) Thanks [@likevy](https://github.com/likevy)! - fixes [#457](https://github.com/callstack/react-native-brownfield/issues/457), android back callbacks survived RN fragment view destruction, retaining delegates and intercepting back presses after re-entry
|
|
10
|
+
|
|
11
|
+
- [#470](https://github.com/callstack/react-native-brownfield/pull/470) [`09162c2`](https://github.com/callstack/react-native-brownfield/commit/09162c27bd8455b67bc0e2890a4ffe8dae5b9eae) Thanks [@KisaneNeko](https://github.com/KisaneNeko)! - Fix `popToNative()` on iOS closing every React Native screen instead of only the topmost one.
|
|
12
|
+
|
|
13
|
+
`popToNative()` is delivered as an app-wide notification, so every live React Native surface acted on it — including surfaces buried under other screens, and each surface acted twice when hosted by SwiftUI. In a stack such as RN → native → RN, a single call tore down the whole navigation stack, taking native screens with it.
|
|
14
|
+
|
|
15
|
+
A React Native surface now acts only when it is the topmost one on screen, and only once per host.
|
|
16
|
+
|
|
17
|
+
Two consequences worth knowing about:
|
|
18
|
+
- When no React Native surface is on screen — for example one hidden behind a full-screen native modal — `popToNative()` is now a no-op instead of popping the hidden screen. It logs when this happens.
|
|
19
|
+
- `popToNative()` no longer dismisses a modally presented native screen that merely _contains_ a React Native surface. Only a surface that owns a navigation stack entry pops.
|
|
20
|
+
|
|
21
|
+
- Updated dependencies []:
|
|
22
|
+
- @callstack/brownfield-cli@5.1.1
|
|
23
|
+
|
|
3
24
|
## 5.1.0
|
|
4
25
|
|
|
5
26
|
### Minor Changes
|
|
@@ -202,6 +202,19 @@ class ReactNativeBrownfield private constructor(val reactHost: ReactHost) {
|
|
|
202
202
|
moduleName: String,
|
|
203
203
|
reactDelegate: ReactDelegateWrapper? = null,
|
|
204
204
|
launchOptions: Bundle? = null,
|
|
205
|
+
): FrameLayout = createView(activity, moduleName, reactDelegate, launchOptions, activity)
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Use the owner of the view when embedding React Native inside a fragment or
|
|
209
|
+
* another container whose lifetime is shorter than the activity.
|
|
210
|
+
* The four-argument overload retains its original JVM signature.
|
|
211
|
+
*/
|
|
212
|
+
fun createView(
|
|
213
|
+
activity: FragmentActivity?,
|
|
214
|
+
moduleName: String,
|
|
215
|
+
reactDelegate: ReactDelegateWrapper?,
|
|
216
|
+
launchOptions: Bundle?,
|
|
217
|
+
lifecycleOwner: LifecycleOwner?,
|
|
205
218
|
): FrameLayout {
|
|
206
219
|
val reactHost = shared.reactHost
|
|
207
220
|
val resolvedDelegate =
|
|
@@ -215,12 +228,17 @@ class ReactNativeBrownfield private constructor(val reactHost: ReactHost) {
|
|
|
215
228
|
}
|
|
216
229
|
|
|
217
230
|
// Register back press callback
|
|
218
|
-
|
|
231
|
+
if (lifecycleOwner != null) {
|
|
232
|
+
activity?.onBackPressedDispatcher?.addCallback(lifecycleOwner, backPressedCallback)
|
|
233
|
+
}
|
|
219
234
|
// invoked on the last RN screen exit
|
|
220
235
|
resolvedDelegate.setHardwareBackHandler {
|
|
221
236
|
backPressedCallback.isEnabled = false
|
|
222
|
-
|
|
223
|
-
|
|
237
|
+
try {
|
|
238
|
+
activity?.onBackPressedDispatcher?.onBackPressed()
|
|
239
|
+
} finally {
|
|
240
|
+
backPressedCallback.isEnabled = true
|
|
241
|
+
}
|
|
224
242
|
}
|
|
225
243
|
|
|
226
244
|
/**
|
|
@@ -230,7 +248,7 @@ class ReactNativeBrownfield private constructor(val reactHost: ReactHost) {
|
|
|
230
248
|
* In such a case, we set the lifeCycle observer.
|
|
231
249
|
*/
|
|
232
250
|
if (reactDelegate == null) {
|
|
233
|
-
|
|
251
|
+
lifecycleOwner?.lifecycle?.addObserver(getLifeCycleObserver(resolvedDelegate))
|
|
234
252
|
}
|
|
235
253
|
|
|
236
254
|
resolvedDelegate.loadApp()
|
|
@@ -54,7 +54,9 @@ class ReactNativeFragment : ReactFragment(), PermissionAwareActivity {
|
|
|
54
54
|
return ReactNativeBrownfield.shared.createView(
|
|
55
55
|
activity,
|
|
56
56
|
moduleName,
|
|
57
|
-
this.reactDelegate as ReactDelegateWrapper
|
|
57
|
+
this.reactDelegate as ReactDelegateWrapper,
|
|
58
|
+
launchOptions = null,
|
|
59
|
+
lifecycleOwner = viewLifecycleOwner
|
|
58
60
|
)
|
|
59
61
|
}
|
|
60
62
|
|
|
@@ -108,6 +108,13 @@ dependencies {
|
|
|
108
108
|
implementation(libs.common)
|
|
109
109
|
implementation(libs.asm.commons)
|
|
110
110
|
implementation(libs.versioncompare)
|
|
111
|
+
testImplementation(libs.junit.jupiter)
|
|
112
|
+
testImplementation(gradleTestKit())
|
|
113
|
+
testImplementation(kotlin("test"))
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
tasks.test {
|
|
117
|
+
useJUnitPlatform()
|
|
111
118
|
}
|
|
112
119
|
|
|
113
120
|
tasks.named("detekt").configure {
|
|
@@ -6,6 +6,7 @@ agp = "8.5.2"
|
|
|
6
6
|
common = "31.2.2" # do not bump it for now, as it throws an error for incompatible AGP used
|
|
7
7
|
asm-commons = "9.7"
|
|
8
8
|
versioncompare = "1.5.0"
|
|
9
|
+
junit = "5.11.4"
|
|
9
10
|
|
|
10
11
|
[plugins]
|
|
11
12
|
kotlinJvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlinJvm" }
|
|
@@ -17,3 +18,4 @@ agp = { module = "com.android.tools.build:gradle", name = "agp", version.ref = "
|
|
|
17
18
|
common = { module = "com.android.tools:common", version.ref = "common" }
|
|
18
19
|
asm-commons = { module = "org.ow2.asm:asm-commons", version.ref = "asm-commons" }
|
|
19
20
|
versioncompare = { module = "io.github.g00fy2:versioncompare", version.ref = "versioncompare" }
|
|
21
|
+
junit-jupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit" }
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
package com.callstack.react.brownfield.artifacts
|
|
2
|
+
|
|
3
|
+
import com.callstack.react.brownfield.shared.Logging
|
|
4
|
+
import com.callstack.react.brownfield.shared.TRANSITIVE_DEPENDENCY_CONFIG_NAMES
|
|
5
|
+
import com.callstack.react.brownfield.shared.UnresolvedArtifactInfo
|
|
6
|
+
import com.callstack.react.brownfield.shared.VersionMediatingDependencySet
|
|
7
|
+
import com.callstack.react.brownfield.shared.collectPublishableGradleDependencies
|
|
8
|
+
import com.callstack.react.brownfield.shared.isPublishableCoordinate
|
|
9
|
+
import org.gradle.api.Project
|
|
10
|
+
import org.gradle.api.artifacts.Dependency
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Result of [RncTransitiveDependencyDiscoverer.discover].
|
|
14
|
+
*
|
|
15
|
+
* @param dependencies The mediated set of third-party dependencies to inject.
|
|
16
|
+
* @param supersededCoordinates Coordinates the consumer declares itself that an embedded module
|
|
17
|
+
* also needs. The caller must drop their pre-existing POM/module.json entries: [dependencies]
|
|
18
|
+
* already carries the mediated (never-lower) version, so keeping both duplicates or shadows it.
|
|
19
|
+
*/
|
|
20
|
+
data class RncTransitiveDependencyDiscoveryResult(
|
|
21
|
+
val dependencies: VersionMediatingDependencySet,
|
|
22
|
+
val supersededCoordinates: Set<Pair<String, String>>,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Discovers third-party dependencies of the native modules embedded into the fat AAR, for
|
|
27
|
+
* publication into its POM/module metadata. The RNC-CLI counterpart to
|
|
28
|
+
* `ExpoPublishingHelper.appendExpoTransitiveDependenciesFromGradle`.
|
|
29
|
+
*/
|
|
30
|
+
class RncTransitiveDependencyDiscoverer(private val project: Project) {
|
|
31
|
+
fun discover(artifacts: List<UnresolvedArtifactInfo>): RncTransitiveDependencyDiscoveryResult {
|
|
32
|
+
val discovered = VersionMediatingDependencySet()
|
|
33
|
+
val supersededCoordinates = mutableSetOf<Pair<String, String>>()
|
|
34
|
+
|
|
35
|
+
artifacts
|
|
36
|
+
.filter { it.isExpoPublishDependency != true }
|
|
37
|
+
.forEach { artifact -> discoverFromArtifact(artifact, discovered, supersededCoordinates) }
|
|
38
|
+
|
|
39
|
+
return RncTransitiveDependencyDiscoveryResult(discovered, supersededCoordinates)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
private fun discoverFromArtifact(
|
|
43
|
+
artifact: UnresolvedArtifactInfo,
|
|
44
|
+
discovered: VersionMediatingDependencySet,
|
|
45
|
+
supersededCoordinates: MutableSet<Pair<String, String>>,
|
|
46
|
+
) {
|
|
47
|
+
val moduleProject = project.rootProject.findProject(":${artifact.moduleName}")
|
|
48
|
+
if (moduleProject == null) {
|
|
49
|
+
Logging.log(
|
|
50
|
+
"WARNING: Could not discover transitive dependencies for embedded module " +
|
|
51
|
+
"'${artifact.moduleName}' - no Gradle project found at " +
|
|
52
|
+
"':${artifact.moduleName}' in the root project",
|
|
53
|
+
)
|
|
54
|
+
return
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
collectPublishableGradleDependencies(moduleProject).forEach { moduleDependency ->
|
|
58
|
+
val consumerDeclaration = consumerDeclaredDependency(moduleDependency.groupId, moduleDependency.artifactId)
|
|
59
|
+
|
|
60
|
+
if (consumerDeclaration != null) {
|
|
61
|
+
// Supersede on the declaration existing, not on it having a version: a versionless
|
|
62
|
+
// one (BOM/platform) still leaves a stale entry that must be replaced, not doubled.
|
|
63
|
+
supersededCoordinates.add(moduleDependency.groupId to moduleDependency.artifactId)
|
|
64
|
+
|
|
65
|
+
// Module-side versions are already validated upstream; this is the consumer-side
|
|
66
|
+
// equivalent, keeping a dynamic version from winning mediation and being published.
|
|
67
|
+
val consumerVersioned = moduleDependency.copy(version = consumerDeclaration.version)
|
|
68
|
+
if (isPublishableCoordinate(consumerVersioned)) {
|
|
69
|
+
discovered.add(consumerVersioned)
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
discovered.add(moduleDependency)
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* The consumer's own declaration of this coordinate, or null if it doesn't declare it. Returns
|
|
79
|
+
* the [Dependency], not its version, so "declared without a version" stays distinguishable from
|
|
80
|
+
* "not declared" — collapsing the two leaks duplicate POM entries.
|
|
81
|
+
*
|
|
82
|
+
* Note this narrows a dynamic declaration: consumer `appcompat:1.+` against module `1.0.0`
|
|
83
|
+
* publishes `1.0.0`, possibly lower than `1.+` would have resolved to. Intended — a published
|
|
84
|
+
* `1.+` is broken for downstream consumers.
|
|
85
|
+
*/
|
|
86
|
+
private fun consumerDeclaredDependency(
|
|
87
|
+
groupId: String,
|
|
88
|
+
artifactId: String,
|
|
89
|
+
): Dependency? {
|
|
90
|
+
return TRANSITIVE_DEPENDENCY_CONFIG_NAMES.firstNotNullOfOrNull { configName ->
|
|
91
|
+
project.configurations.findByName(configName)?.dependencies?.firstOrNull {
|
|
92
|
+
it.group == groupId && it.name == artifactId
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
@@ -1,20 +1,15 @@
|
|
|
1
1
|
package com.callstack.react.brownfield.expo
|
|
2
2
|
|
|
3
3
|
import com.android.utils.forEach
|
|
4
|
-
import com.callstack.react.brownfield.expo.utils.DependencyInfo
|
|
5
4
|
import com.callstack.react.brownfield.expo.utils.ExpoGradleProjectProjection
|
|
6
5
|
import com.callstack.react.brownfield.expo.utils.LocalMavenUtils
|
|
7
|
-
import com.callstack.react.brownfield.expo.utils.VersionMediatingDependencySet
|
|
8
6
|
import com.callstack.react.brownfield.expo.utils.asExpoGradleProjectProjection
|
|
9
7
|
import com.callstack.react.brownfield.shared.Constants
|
|
8
|
+
import com.callstack.react.brownfield.shared.DependencyInfo
|
|
10
9
|
import com.callstack.react.brownfield.shared.Logging
|
|
11
|
-
import
|
|
12
|
-
import
|
|
13
|
-
import groovy.util.NodeList
|
|
10
|
+
import com.callstack.react.brownfield.shared.VersionMediatingDependencySet
|
|
11
|
+
import com.callstack.react.brownfield.shared.collectPublishableGradleDependencies
|
|
14
12
|
import org.gradle.api.Project
|
|
15
|
-
import org.gradle.api.publish.PublishingExtension
|
|
16
|
-
import org.gradle.api.publish.maven.MavenPublication
|
|
17
|
-
import org.gradle.api.publish.tasks.GenerateModuleMetadata
|
|
18
13
|
import org.w3c.dom.Node
|
|
19
14
|
import java.io.File
|
|
20
15
|
import javax.xml.parsers.DocumentBuilderFactory
|
|
@@ -29,6 +24,11 @@ fun Node.getChildNodeByName(nodeName: String): Node? {
|
|
|
29
24
|
}
|
|
30
25
|
|
|
31
26
|
open class ExpoPublishingHelper(val brownfieldAppProject: Project) {
|
|
27
|
+
/**
|
|
28
|
+
* Discovers *which* Expo projects are publishable, not their transitive dependencies: this runs
|
|
29
|
+
* during `apply()`, when those projects may not have configured their own dependencies yet.
|
|
30
|
+
* [discoverAllExpoTransitiveDependencies] handles that later.
|
|
31
|
+
*/
|
|
32
32
|
fun configure(): List<ExpoGradleProjectProjection> {
|
|
33
33
|
val discoverableExpoProjects = getDiscoverableExpoProjects()
|
|
34
34
|
|
|
@@ -39,29 +39,10 @@ open class ExpoPublishingHelper(val brownfieldAppProject: Project) {
|
|
|
39
39
|
) { it.name },
|
|
40
40
|
)
|
|
41
41
|
|
|
42
|
-
val expoTransitiveDependencies =
|
|
43
|
-
discoverAllExpoTransitiveDependencies(
|
|
44
|
-
expoProjects = discoverableExpoProjects,
|
|
45
|
-
)
|
|
46
|
-
|
|
47
|
-
Logging.log(
|
|
48
|
-
"Collected a total of ${expoTransitiveDependencies.size} unique Expo transitive " +
|
|
49
|
-
"dependencies for brownfield app project publishing",
|
|
50
|
-
)
|
|
51
|
-
expoTransitiveDependencies.forEach {
|
|
52
|
-
Logging.log(
|
|
53
|
-
"(*) dependency ${it.groupId}:${it.artifactId}:${it.version} (scope: ${it.scope}, " +
|
|
54
|
-
"${if (it.optional) "optional" else "required"})",
|
|
55
|
-
)
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
reconfigurePOM(expoTransitiveDependencies)
|
|
59
|
-
reconfigureGradleModuleJSON(expoTransitiveDependencies)
|
|
60
|
-
|
|
61
42
|
return discoverableExpoProjects
|
|
62
43
|
}
|
|
63
44
|
|
|
64
|
-
|
|
45
|
+
internal fun shouldExcludeDependency(
|
|
65
46
|
groupId: String,
|
|
66
47
|
artifactId: String,
|
|
67
48
|
): Boolean {
|
|
@@ -78,174 +59,6 @@ open class ExpoPublishingHelper(val brownfieldAppProject: Project) {
|
|
|
78
59
|
return (isRootProjectArtifact || isExpoArtifact)
|
|
79
60
|
}
|
|
80
61
|
|
|
81
|
-
/**
|
|
82
|
-
* Modifies the generated Gradle Module Metadata file to inject Expo transitive dependencies.
|
|
83
|
-
* @param discoveredExpoTransitiveDependencies Set of DependencyInfo
|
|
84
|
-
* representing Expo transitive dependencies to add.
|
|
85
|
-
*/
|
|
86
|
-
@Suppress("LongMethod")
|
|
87
|
-
protected fun reconfigureGradleModuleJSON(discoveredExpoTransitiveDependencies: VersionMediatingDependencySet) {
|
|
88
|
-
val removeDependenciesFromModuleFileTask =
|
|
89
|
-
brownfieldAppProject.tasks.register("removeDependenciesFromModuleFile")
|
|
90
|
-
removeDependenciesFromModuleFileTask.configure { task ->
|
|
91
|
-
task.doLast {
|
|
92
|
-
val moduleBuildDir = brownfieldAppProject.layout.buildDirectory.get()
|
|
93
|
-
|
|
94
|
-
File("$moduleBuildDir/publications/mavenAar/module.json").run {
|
|
95
|
-
val json = inputStream().use { JsonSlurper().parse(it) as Map<*, *> }
|
|
96
|
-
|
|
97
|
-
discoveredExpoTransitiveDependencies.forEach { dependencyToAdd ->
|
|
98
|
-
@Suppress("UNCHECKED_CAST")
|
|
99
|
-
(json["variants"] as? List<MutableMap<String, Any>>)?.forEach { variant ->
|
|
100
|
-
Logging.log(
|
|
101
|
-
"Injecting dependency to Gradle module JSON for variant " +
|
|
102
|
-
"'${variant["name"]}': ${dependencyToAdd.groupId}:" +
|
|
103
|
-
"${dependencyToAdd.artifactId}:${dependencyToAdd.version}",
|
|
104
|
-
)
|
|
105
|
-
|
|
106
|
-
(variant["dependencies"] as? MutableList<MutableMap<String, Any>>)?.add(
|
|
107
|
-
mutableMapOf<String, Any>(
|
|
108
|
-
"group" to dependencyToAdd.groupId,
|
|
109
|
-
"module" to dependencyToAdd.artifactId,
|
|
110
|
-
).apply {
|
|
111
|
-
dependencyToAdd.version?.let { version ->
|
|
112
|
-
put(
|
|
113
|
-
"version",
|
|
114
|
-
mapOf(
|
|
115
|
-
"requires" to version,
|
|
116
|
-
),
|
|
117
|
-
)
|
|
118
|
-
}
|
|
119
|
-
},
|
|
120
|
-
)
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
@Suppress("UNCHECKED_CAST")
|
|
125
|
-
(json["variants"] as? List<MutableMap<String, Any>>)?.forEach { variant ->
|
|
126
|
-
(variant["dependencies"] as? MutableList<Map<String, Any>>)?.removeAll {
|
|
127
|
-
val group = it["group"] as String
|
|
128
|
-
val module = it["module"] as String
|
|
129
|
-
|
|
130
|
-
val shouldBeExcluded =
|
|
131
|
-
shouldExcludeDependency(
|
|
132
|
-
groupId = group,
|
|
133
|
-
artifactId = module,
|
|
134
|
-
)
|
|
135
|
-
|
|
136
|
-
if (shouldBeExcluded) {
|
|
137
|
-
Logging.log(
|
|
138
|
-
"Removing excluded dependency from Gradle module JSON: $group:$module",
|
|
139
|
-
)
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
shouldBeExcluded
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
writer().use {
|
|
146
|
-
it.write(
|
|
147
|
-
JsonOutput.prettyPrint(
|
|
148
|
-
JsonOutput.toJson(
|
|
149
|
-
json,
|
|
150
|
-
),
|
|
151
|
-
),
|
|
152
|
-
)
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
brownfieldAppProject.tasks.withType(GenerateModuleMetadata::class.java)
|
|
160
|
-
.configureEach {
|
|
161
|
-
it.finalizedBy(removeDependenciesFromModuleFileTask.get())
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
/**
|
|
166
|
-
* Modifies the generated Maven POM file to inject Expo transitive dependencies.
|
|
167
|
-
* @param discoveredExpoTransitiveDependencies Set of DependencyInfo
|
|
168
|
-
* representing Expo transitive dependencies to add.
|
|
169
|
-
*/
|
|
170
|
-
@Suppress("LongMethod")
|
|
171
|
-
protected fun reconfigurePOM(discoveredExpoTransitiveDependencies: VersionMediatingDependencySet) {
|
|
172
|
-
brownfieldAppProject.pluginManager.withPlugin("maven-publish") {
|
|
173
|
-
brownfieldAppProject.extensions.configure(PublishingExtension::class.java) { publishing ->
|
|
174
|
-
publishing.publications.withType(MavenPublication::class.java)
|
|
175
|
-
.configureEach { pub ->
|
|
176
|
-
Logging.log(
|
|
177
|
-
"Configuring POM for publication '${pub.name}' to include Expo transitive dependencies",
|
|
178
|
-
)
|
|
179
|
-
|
|
180
|
-
pub.pom.withXml {
|
|
181
|
-
val root = it.asNode()
|
|
182
|
-
|
|
183
|
-
// below: obtains a view of the <dependencies> node(s)
|
|
184
|
-
// inside the POM XML; in practice, there should be only one such node
|
|
185
|
-
val dependenciesNodeList =
|
|
186
|
-
root.get("dependencies") as NodeList
|
|
187
|
-
val dependenciesNode =
|
|
188
|
-
dependenciesNodeList.first() as groovy.util.Node
|
|
189
|
-
|
|
190
|
-
// below: inject the discovered Expo transitive dependencies
|
|
191
|
-
// into the POM's <dependencies> node
|
|
192
|
-
discoveredExpoTransitiveDependencies.forEach { dependencyToAdd ->
|
|
193
|
-
Logging.log(
|
|
194
|
-
"Injecting dependency to POM: ${dependencyToAdd.groupId}:" +
|
|
195
|
-
"${dependencyToAdd.artifactId}:${dependencyToAdd.version}",
|
|
196
|
-
)
|
|
197
|
-
|
|
198
|
-
val childTags =
|
|
199
|
-
mutableMapOf(
|
|
200
|
-
"groupId" to dependencyToAdd.groupId,
|
|
201
|
-
"artifactId" to dependencyToAdd.artifactId,
|
|
202
|
-
"scope" to dependencyToAdd.scope,
|
|
203
|
-
"optional" to dependencyToAdd.optional.toString(),
|
|
204
|
-
)
|
|
205
|
-
|
|
206
|
-
if (dependencyToAdd.version?.isNotBlank() == true) {
|
|
207
|
-
childTags["version"] = dependencyToAdd.version
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
dependenciesNode.appendNode("dependency").let { newDepNode ->
|
|
211
|
-
childTags.forEach { (tagName, tagValue) ->
|
|
212
|
-
newDepNode.appendNode(tagName, tagValue)
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
// below: filter out dependencies that should be excluded
|
|
218
|
-
dependenciesNode.children()
|
|
219
|
-
.filterIsInstance<groovy.util.Node>()
|
|
220
|
-
.filter { dependency ->
|
|
221
|
-
val groupId =
|
|
222
|
-
(dependency["groupId"] as NodeList).text()
|
|
223
|
-
val artifactId =
|
|
224
|
-
(dependency["artifactId"] as NodeList).text()
|
|
225
|
-
|
|
226
|
-
val shouldBeExcluded =
|
|
227
|
-
shouldExcludeDependency(
|
|
228
|
-
groupId = groupId,
|
|
229
|
-
artifactId = artifactId,
|
|
230
|
-
)
|
|
231
|
-
|
|
232
|
-
if (shouldBeExcluded) {
|
|
233
|
-
Logging.log(
|
|
234
|
-
"Removing excluded dependency from POM: $groupId:$artifactId",
|
|
235
|
-
)
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
shouldBeExcluded
|
|
239
|
-
}
|
|
240
|
-
.forEach { dependency ->
|
|
241
|
-
dependenciesNode.remove(dependency)
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
|
|
249
62
|
fun discoverAllExpoTransitiveDependencies(expoProjects: Iterable<ExpoGradleProjectProjection>): VersionMediatingDependencySet {
|
|
250
63
|
var discoveredExpoTransitiveDependencies = VersionMediatingDependencySet()
|
|
251
64
|
expoProjects.forEach { expoProj ->
|
|
@@ -368,24 +181,7 @@ open class ExpoPublishingHelper(val brownfieldAppProject: Project) {
|
|
|
368
181
|
pkgProject: Project,
|
|
369
182
|
dependencies: VersionMediatingDependencySet,
|
|
370
183
|
) {
|
|
371
|
-
|
|
372
|
-
* Not accounting for variant specific configurations as Expo packages are not
|
|
373
|
-
* using it. Should we face any issues/needs to account for it, we can do it here.
|
|
374
|
-
*/
|
|
375
|
-
listOf("implementation", "api", "runtime").forEach {
|
|
376
|
-
val configuration = pkgProject.configurations.findByName(it)
|
|
377
|
-
configuration?.dependencies?.forEach { dep ->
|
|
378
|
-
if (dep.group != null) {
|
|
379
|
-
dependencies.add(
|
|
380
|
-
DependencyInfo.fromGradleDep(
|
|
381
|
-
groupId = dep.group!!,
|
|
382
|
-
artifactId = dep.name,
|
|
383
|
-
version = dep.version,
|
|
384
|
-
),
|
|
385
|
-
)
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
}
|
|
184
|
+
dependencies.addAll(collectPublishableGradleDependencies(pkgProject))
|
|
389
185
|
}
|
|
390
186
|
|
|
391
187
|
/**
|
|
@@ -3,6 +3,7 @@ package com.callstack.react.brownfield.plugin
|
|
|
3
3
|
import com.android.build.api.variant.LibraryAndroidComponentsExtension
|
|
4
4
|
import com.android.build.api.variant.LibraryVariant
|
|
5
5
|
import com.callstack.react.brownfield.artifacts.ArtifactsResolver
|
|
6
|
+
import com.callstack.react.brownfield.artifacts.RncTransitiveDependencyDiscoverer
|
|
6
7
|
import com.callstack.react.brownfield.expo.ExpoPublishingHelper
|
|
7
8
|
import com.callstack.react.brownfield.expo.utils.ExpoGradleProjectProjection
|
|
8
9
|
import com.callstack.react.brownfield.processors.AssetTaskProcessor
|
|
@@ -15,9 +16,13 @@ import com.callstack.react.brownfield.processors.VariantHelper
|
|
|
15
16
|
import com.callstack.react.brownfield.processors.VariantPackagesProperty
|
|
16
17
|
import com.callstack.react.brownfield.processors.VariantTaskProvider
|
|
17
18
|
import com.callstack.react.brownfield.shared.BaseProject
|
|
19
|
+
import com.callstack.react.brownfield.shared.Constants
|
|
18
20
|
import com.callstack.react.brownfield.shared.Constants.PROJECT_ID
|
|
19
21
|
import com.callstack.react.brownfield.shared.Logging
|
|
22
|
+
import com.callstack.react.brownfield.shared.PublishingMetadataInjector
|
|
20
23
|
import com.callstack.react.brownfield.shared.UnresolvedArtifactInfo
|
|
24
|
+
import com.callstack.react.brownfield.shared.VersionMediatingDependencySet
|
|
25
|
+
import com.callstack.react.brownfield.shared.dropHardExcludedDependencies
|
|
21
26
|
import com.callstack.react.brownfield.utils.AndroidArchiveLibrary
|
|
22
27
|
import com.callstack.react.brownfield.utils.DirectoryManager
|
|
23
28
|
import com.callstack.react.brownfield.utils.Extension
|
|
@@ -54,8 +59,9 @@ class RNBrownfieldPlugin : Plugin<Project> {
|
|
|
54
59
|
}
|
|
55
60
|
|
|
56
61
|
var expoProjects = listOf<ExpoGradleProjectProjection>()
|
|
62
|
+
var expoPublishingHelper: ExpoPublishingHelper? = null
|
|
57
63
|
if (this.isExpoProject) {
|
|
58
|
-
|
|
64
|
+
expoPublishingHelper = ExpoPublishingHelper(brownfieldAppProject = project)
|
|
59
65
|
expoProjects = expoPublishingHelper.configure()
|
|
60
66
|
}
|
|
61
67
|
|
|
@@ -65,6 +71,18 @@ class RNBrownfieldPlugin : Plugin<Project> {
|
|
|
65
71
|
val artifactsResolver = ArtifactsResolver(project, isExpoProject)
|
|
66
72
|
val artifacts = artifactsResolver.processDefaultDependencies(expoProjects)
|
|
67
73
|
|
|
74
|
+
// Registered eagerly: Gradle disallows registering tasks from afterEvaluate. Only the
|
|
75
|
+
// dependency set supplied below has to wait.
|
|
76
|
+
val injector = PublishingMetadataInjector(project)
|
|
77
|
+
|
|
78
|
+
// whenReady, not afterEvaluate: afterEvaluate guarantees only that *this* project is
|
|
79
|
+
// configured, not the embedded module projects we read dependencies from — theirs were
|
|
80
|
+
// empirically still empty at that point. whenReady fires once the whole task graph is
|
|
81
|
+
// resolved (so every embedded module is configured) but before any task runs.
|
|
82
|
+
project.gradle.taskGraph.whenReady {
|
|
83
|
+
configureTransitiveDependencyInjection(project, injector, expoPublishingHelper, expoProjects, artifacts)
|
|
84
|
+
}
|
|
85
|
+
|
|
68
86
|
val variantTaskProvider = VariantTaskProvider(project)
|
|
69
87
|
|
|
70
88
|
val androidComponents = project.extensions.getByType(LibraryAndroidComponentsExtension::class.java)
|
|
@@ -77,6 +95,79 @@ class RNBrownfieldPlugin : Plugin<Project> {
|
|
|
77
95
|
const val EXPO_PROJECT_LOCATOR = ":expo"
|
|
78
96
|
}
|
|
79
97
|
|
|
98
|
+
@Suppress("LongMethod")
|
|
99
|
+
private fun configureTransitiveDependencyInjection(
|
|
100
|
+
project: Project,
|
|
101
|
+
injector: PublishingMetadataInjector,
|
|
102
|
+
expoPublishingHelper: ExpoPublishingHelper?,
|
|
103
|
+
expoProjects: List<ExpoGradleProjectProjection>,
|
|
104
|
+
artifacts: List<UnresolvedArtifactInfo>,
|
|
105
|
+
) {
|
|
106
|
+
val transitiveDeps = VersionMediatingDependencySet()
|
|
107
|
+
var rncSupersededCoordinates: Set<Pair<String, String>> = emptySet()
|
|
108
|
+
|
|
109
|
+
if (isExpoProject && expoPublishingHelper != null) {
|
|
110
|
+
val expoTransitiveDeps = expoPublishingHelper.discoverAllExpoTransitiveDependencies(expoProjects)
|
|
111
|
+
Logging.log("Merged ${expoTransitiveDeps.size} transitive dependencies discovered from Expo")
|
|
112
|
+
expoTransitiveDeps.forEach {
|
|
113
|
+
Logging.log(
|
|
114
|
+
"(*) dependency ${it.groupId}:${it.artifactId}:${it.version} (scope: ${it.scope}, " +
|
|
115
|
+
"${if (it.optional) "optional" else "required"})",
|
|
116
|
+
)
|
|
117
|
+
}
|
|
118
|
+
transitiveDeps.addAll(expoTransitiveDeps)
|
|
119
|
+
}
|
|
120
|
+
// Never on Expo: the path above already covers every embedded module there, and the RNC
|
|
121
|
+
// discoverer doesn't filter by the Expo blacklist, so running both leaks Expo coordinates.
|
|
122
|
+
if (!isExpoProject && extension.experimentalIncludeTransitiveDependencies) {
|
|
123
|
+
val rncDiscovery = RncTransitiveDependencyDiscoverer(project).discover(artifacts)
|
|
124
|
+
Logging.log(
|
|
125
|
+
"Merged ${rncDiscovery.dependencies.size} transitive dependencies discovered by the RNC discoverer",
|
|
126
|
+
)
|
|
127
|
+
transitiveDeps.addAll(rncDiscovery.dependencies)
|
|
128
|
+
rncSupersededCoordinates = rncDiscovery.supersededCoordinates
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (isExpoProject || extension.experimentalIncludeTransitiveDependencies) {
|
|
132
|
+
// Coordinates that must never be published: they're embedded in the AAR, not
|
|
133
|
+
// resolvable from Maven. Distinct from "superseded" coordinates, which ARE re-injected
|
|
134
|
+
// (replacing a stale entry) and so must not be dropped here.
|
|
135
|
+
val hardExcludePredicate: (String, String) -> Boolean = { groupId, artifactId ->
|
|
136
|
+
(expoPublishingHelper?.shouldExcludeDependency(groupId, artifactId) ?: (groupId == project.rootProject.name)) ||
|
|
137
|
+
artifacts.any { it.moduleGroup == groupId && it.moduleName == artifactId }
|
|
138
|
+
}
|
|
139
|
+
dropHardExcludedDependencies(transitiveDeps, hardExcludePredicate)
|
|
140
|
+
|
|
141
|
+
Logging.log(
|
|
142
|
+
"Total of ${transitiveDeps.size} unique transitive dependencies merged for POM/module.json injection",
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
val removalPredicate: (String, String) -> Boolean = { groupId, artifactId ->
|
|
146
|
+
hardExcludePredicate(groupId, artifactId) || rncSupersededCoordinates.contains(groupId to artifactId)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
injector.configure(transitiveDeps, removalPredicate)
|
|
150
|
+
Logging.log("PublishingMetadataInjector ran: injected merged transitive dependencies into POM and Gradle Module Metadata")
|
|
151
|
+
|
|
152
|
+
// Informational only — throwing would recreate the upgrade break that the namespaced
|
|
153
|
+
// task name exists to prevent. `names` avoids realizing the task from whenReady.
|
|
154
|
+
if (project.tasks.names.contains(Constants.LEGACY_CONSUMER_MODULE_METADATA_TASK_NAME)) {
|
|
155
|
+
Logging.log(
|
|
156
|
+
"NOTE: this project still registers a hand-written " +
|
|
157
|
+
"'${Constants.LEGACY_CONSUMER_MODULE_METADATA_TASK_NAME}' task. The plugin now performs " +
|
|
158
|
+
"that removal itself (plus transitive-dependency injection) via " +
|
|
159
|
+
"'${Constants.MODULE_METADATA_POST_PROCESS_TASK_NAME}'. The hand-written task is " +
|
|
160
|
+
"redundant and can be deleted; leaving it in place is harmless.",
|
|
161
|
+
)
|
|
162
|
+
}
|
|
163
|
+
} else {
|
|
164
|
+
Logging.log(
|
|
165
|
+
"PublishingMetadataInjector skipped: project is not an Expo project and " +
|
|
166
|
+
"experimentalIncludeTransitiveDependencies is disabled",
|
|
167
|
+
)
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
80
171
|
private fun initializers() {
|
|
81
172
|
RClassTransformer.project = project
|
|
82
173
|
Logging.project = project
|
package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/shared/Constants.kt
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
package com.callstack.react.brownfield.shared
|
|
2
2
|
|
|
3
|
-
import com.callstack.react.brownfield.expo.utils.DependencyInfo
|
|
4
3
|
import com.callstack.react.brownfield.utils.StringMatcher
|
|
5
4
|
|
|
6
5
|
/**
|
|
@@ -40,6 +39,18 @@ object Constants {
|
|
|
40
39
|
|
|
41
40
|
const val INTERMEDIATES_TEMP_DIR = PLUGIN_NAME
|
|
42
41
|
|
|
42
|
+
/**
|
|
43
|
+
* Plugin-owned task that post-processes generated Gradle Module Metadata.
|
|
44
|
+
*
|
|
45
|
+
* Never rename to the bare [LEGACY_CONSUMER_MODULE_METADATA_TASK_NAME]: the plugin registers
|
|
46
|
+
* during `apply()`, before the consumer's script body, so claiming that name breaks every
|
|
47
|
+
* consumer still following the setup docs with "a task with that name already exists".
|
|
48
|
+
*/
|
|
49
|
+
const val MODULE_METADATA_POST_PROCESS_TASK_NAME = "brownfieldRemoveDependenciesFromModuleFile"
|
|
50
|
+
|
|
51
|
+
/** The name the setup docs tell consumers to register themselves; must stay free for them. */
|
|
52
|
+
const val LEGACY_CONSUMER_MODULE_METADATA_TASK_NAME = "removeDependenciesFromModuleFile"
|
|
53
|
+
|
|
43
54
|
val BROWNFIELD_EXPO_TRANSITIVE_DEPS_WHITELISTED_MODULES_FOR_DISCOVERY =
|
|
44
55
|
setOf("expo-modules-core", "expo-constants", "expo", "expo-updates")
|
|
45
56
|
val BROWNFIELD_EXPO_TRANSITIVE_DEPS_ARTIFACTS_BLACKLIST =
|