@callstack/react-native-brownfield 3.11.0 → 3.13.0

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.
Files changed (77) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/gradle-plugin/brownfield/build.gradle.kts +134 -0
  3. package/gradle-plugin/brownfield/config/detekt/detekt.yml +936 -0
  4. package/gradle-plugin/brownfield/gradle/libs.versions.toml +19 -0
  5. package/gradle-plugin/brownfield/gradle/wrapper/gradle-wrapper.jar +0 -0
  6. package/gradle-plugin/brownfield/gradle/wrapper/gradle-wrapper.properties +7 -0
  7. package/gradle-plugin/brownfield/gradle.properties +14 -0
  8. package/gradle-plugin/brownfield/gradlew +249 -0
  9. package/gradle-plugin/brownfield/gradlew.bat +92 -0
  10. package/gradle-plugin/brownfield/settings.gradle +7 -0
  11. package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/artifacts/ArtifactsResolver.kt +132 -0
  12. package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/exceptions/NameSpaceNotFound.kt +4 -0
  13. package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/exceptions/TaskNotFound.kt +4 -0
  14. package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/expo/ExpoPublishingHelper.kt +432 -0
  15. package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/expo/utils/BrownfieldPrimitives.kt +31 -0
  16. package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/expo/utils/ExpoProjectionPrimitives.kt +22 -0
  17. package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/expo/utils/LocalMavenUtils.kt +83 -0
  18. package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/expo/utils/ReflectionUtils.kt +79 -0
  19. package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/expo/utils/VersionMediatingDependencySet.kt +100 -0
  20. package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/plugin/CustomDependencyResolver.kt +50 -0
  21. package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/plugin/ProjectConfigurations.kt +107 -0
  22. package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/plugin/RClassTransformer.kt +131 -0
  23. package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/plugin/RNBrownfieldPlugin.kt +171 -0
  24. package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/plugin/RNSourceSets.kt +149 -0
  25. package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/processors/AssetTaskProcessor.kt +29 -0
  26. package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/processors/ExplodeTaskProvider.kt +63 -0
  27. package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/processors/JNILibsProcessor.kt +165 -0
  28. package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/processors/ManifestTaskProcessor.kt +74 -0
  29. package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/processors/MergeProcessor.kt +55 -0
  30. package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/processors/ProguardProcessor.kt +61 -0
  31. package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/processors/ResourceTaskProcessor.kt +26 -0
  32. package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/processors/VariantHelper.kt +61 -0
  33. package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/processors/VariantPackagesProperty.kt +21 -0
  34. package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/processors/VariantTaskProvider.kt +103 -0
  35. package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/shared/BaseProject.kt +17 -0
  36. package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/shared/BundleTaskProvider.kt +46 -0
  37. package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/shared/Constants.kt +58 -0
  38. package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/shared/ExplodeAarTask.kt +72 -0
  39. package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/shared/GradleILogger.kt +59 -0
  40. package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/shared/Logging.kt +24 -0
  41. package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/shared/ModuleInfo.kt +22 -0
  42. package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/utils/AndroidArchiveLibrary.kt +77 -0
  43. package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/utils/DirectoryManager.kt +20 -0
  44. package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/utils/Extension.kt +44 -0
  45. package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/utils/StringExtensions.kt +5 -0
  46. package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/utils/StringMatcher.kt +21 -0
  47. package/gradle-plugin/brownfield/src/main/kotlin/com/callstack/react/brownfield/utils/Utils.kt +59 -0
  48. package/ios/Expo/ExpoHostRuntime.swift +3 -2
  49. package/ios/ReactNativeBrownfield.swift +1 -1
  50. package/ios/Vanilla/ReactNativeHostRuntime.swift +5 -2
  51. package/ios/swiftpm/Sources/BrownfieldBundleSupport/BrownfieldBundleURLResolver.swift +11 -5
  52. package/ios/swiftpm/Tests/BrownfieldBundleSupportTests/BrownfieldBundleURLResolverTests.swift +17 -5
  53. package/lib/commonjs/expo-config-plugin/android/utils/constants.js +1 -1
  54. package/lib/commonjs/expo-config-plugin/android/utils/constants.js.map +1 -1
  55. package/lib/commonjs/expo-config-plugin/ios/xcodeHelpers.js +2 -1
  56. package/lib/commonjs/expo-config-plugin/ios/xcodeHelpers.js.map +1 -1
  57. package/lib/commonjs/index.js.map +1 -1
  58. package/lib/module/expo-config-plugin/android/utils/constants.js +1 -1
  59. package/lib/module/expo-config-plugin/android/utils/constants.js.map +1 -1
  60. package/lib/module/expo-config-plugin/ios/xcodeHelpers.js +2 -1
  61. package/lib/module/expo-config-plugin/ios/xcodeHelpers.js.map +1 -1
  62. package/lib/module/index.js.map +1 -1
  63. package/lib/typescript/commonjs/src/expo-config-plugin/android/utils/constants.d.ts +2 -2
  64. package/lib/typescript/commonjs/src/expo-config-plugin/android/utils/constants.d.ts.map +1 -1
  65. package/lib/typescript/commonjs/src/expo-config-plugin/ios/xcodeHelpers.d.ts.map +1 -1
  66. package/lib/typescript/commonjs/src/index.d.ts +1 -0
  67. package/lib/typescript/commonjs/src/index.d.ts.map +1 -1
  68. package/lib/typescript/module/src/expo-config-plugin/android/utils/constants.d.ts +2 -2
  69. package/lib/typescript/module/src/expo-config-plugin/android/utils/constants.d.ts.map +1 -1
  70. package/lib/typescript/module/src/expo-config-plugin/ios/xcodeHelpers.d.ts.map +1 -1
  71. package/lib/typescript/module/src/index.d.ts +1 -0
  72. package/lib/typescript/module/src/index.d.ts.map +1 -1
  73. package/package.json +6 -4
  74. package/scripts/sync-gradle-plugin-source.js +58 -0
  75. package/src/expo-config-plugin/android/utils/constants.ts +1 -1
  76. package/src/expo-config-plugin/ios/xcodeHelpers.ts +14 -0
  77. package/src/index.ts +2 -0
@@ -0,0 +1,21 @@
1
+ package com.callstack.react.brownfield.processors
2
+
3
+ import org.gradle.api.Project
4
+ import org.gradle.api.provider.MapProperty
5
+
6
+ object VariantPackagesProperty {
7
+ private lateinit var properties: MapProperty<String, List<String>>
8
+
9
+ fun getVariantPackagesProperty(): MapProperty<String, List<String>> {
10
+ return properties
11
+ }
12
+
13
+ fun setVariantPackagesProperty(project: Project) {
14
+ @Suppress("UNCHECKED_CAST")
15
+ properties =
16
+ project.objects.mapProperty(
17
+ String::class.java,
18
+ List::class.java as Class<List<String>>,
19
+ )
20
+ }
21
+ }
@@ -0,0 +1,103 @@
1
+ package com.callstack.react.brownfield.processors
2
+
3
+ import com.android.build.gradle.internal.tasks.factory.dependsOn
4
+ import com.callstack.react.brownfield.exceptions.TaskNotFound
5
+ import com.callstack.react.brownfield.shared.ExplodeAarTask
6
+ import com.callstack.react.brownfield.utils.AndroidArchiveLibrary
7
+ import com.callstack.react.brownfield.utils.DirectoryManager
8
+ import com.callstack.react.brownfield.utils.Extension
9
+ import com.callstack.react.brownfield.utils.Utils
10
+ import com.callstack.react.brownfield.utils.capitalized
11
+ import org.gradle.api.Project
12
+ import org.gradle.api.Task
13
+ import org.gradle.api.UnknownTaskException
14
+ import org.gradle.api.tasks.TaskProvider
15
+ import java.io.File
16
+
17
+ class VariantTaskProvider(val project: Project) {
18
+ fun bundleTaskProvider(
19
+ project: Project,
20
+ variantName: String,
21
+ ): TaskProvider<Task> {
22
+ var bundleTaskPath = "bundle${variantName.capitalized()}"
23
+ return try {
24
+ project.tasks.named(bundleTaskPath)
25
+ } catch (_: UnknownTaskException) {
26
+ bundleTaskPath += "Aar"
27
+ project.tasks.named(bundleTaskPath)
28
+ }
29
+ }
30
+
31
+ fun processDataBinding(
32
+ bundleTask: TaskProvider<Task>,
33
+ aarLibraries: Collection<AndroidArchiveLibrary>,
34
+ variantName: String,
35
+ ) {
36
+ bundleTask.configure { task ->
37
+ task.doLast {
38
+ aarLibraries.forEach {
39
+ val dataBindingFolder = it.getDataBindingFolder()
40
+ if (dataBindingFolder.exists()) {
41
+ val filePath = getReBundleFilePath(dataBindingFolder.name, variantName)
42
+ File(filePath).mkdirs()
43
+ project.copy { copyTask ->
44
+ copyTask.from(dataBindingFolder)
45
+ copyTask.into(filePath)
46
+ }
47
+ }
48
+
49
+ val dataBindingLogFolder = it.getDataBindingLogFolder()
50
+ if (dataBindingLogFolder.exists()) {
51
+ val filePath = getReBundleFilePath(dataBindingLogFolder.name, variantName)
52
+ File(filePath).mkdirs()
53
+ project.copy { copyTask ->
54
+ copyTask.from(dataBindingLogFolder)
55
+ copyTask.into(filePath)
56
+ }
57
+ }
58
+ }
59
+ }
60
+ }
61
+ }
62
+
63
+ private fun getReBundleFilePath(
64
+ folderName: String,
65
+ variantName: String,
66
+ ) = "${DirectoryManager.getReBundleDirectory(
67
+ variantName,
68
+ ).path}/$folderName"
69
+
70
+ fun preBuildTaskByVariant(
71
+ variantName: String,
72
+ buildTypeName: String,
73
+ isVariantDebuggable: Boolean,
74
+ explodeAarTask: TaskProvider<ExplodeAarTask>,
75
+ ) {
76
+ val preBuildTaskPath = "pre${variantName.capitalized()}Build"
77
+ val preBuildTask = project.tasks.named(preBuildTaskPath)
78
+
79
+ if (!preBuildTask.isPresent) {
80
+ throw TaskNotFound("Can not find $preBuildTaskPath task")
81
+ }
82
+
83
+ preBuildTask.dependsOn(explodeAarTask)
84
+
85
+ val bundledAssetsVariantName =
86
+ Utils.getBundledAssetsVariantName(
87
+ variantName = variantName,
88
+ buildTypeName = buildTypeName,
89
+ isDebuggable = isVariantDebuggable,
90
+ )
91
+ val capitalizedBundledAssetsVariantName = bundledAssetsVariantName.capitalized()
92
+
93
+ val projectExt = project.extensions.getByType(Extension::class.java)
94
+ val appProject = project.rootProject.project(projectExt.appProjectName)
95
+ preBuildTask.dependsOn("${appProject.path}:createBundle${capitalizedBundledAssetsVariantName}JsAndAssets")
96
+
97
+ if (Utils.isExpoProject(project)) {
98
+ preBuildTask.dependsOn(
99
+ "${appProject.path}:create${capitalizedBundledAssetsVariantName}UpdatesResources",
100
+ )
101
+ }
102
+ }
103
+ }
@@ -0,0 +1,17 @@
1
+ package com.callstack.react.brownfield.shared
2
+
3
+ import org.gradle.api.Project
4
+ import org.gradle.api.file.Directory
5
+
6
+ open class BaseProject {
7
+ private var _project: Project? = null
8
+
9
+ var project: Project
10
+ get() = checkNotNull(_project) { "Project has not been initialized" }
11
+ set(value) {
12
+ _project = value
13
+ }
14
+
15
+ val buildDir: Directory
16
+ get() = project.layout.buildDirectory.get()
17
+ }
@@ -0,0 +1,46 @@
1
+ @file:Suppress("DEPRECATION")
2
+
3
+ package com.callstack.react.brownfield.shared
4
+
5
+ import com.android.build.gradle.LibraryExtension
6
+ import com.android.build.gradle.api.LibraryVariant
7
+ import com.callstack.react.brownfield.processors.VariantTaskProvider
8
+ import org.gradle.api.Project
9
+ import org.gradle.api.Task
10
+ import org.gradle.api.tasks.TaskProvider
11
+
12
+ class BundleTaskProvider(private val variantTaskProvider: VariantTaskProvider) {
13
+ fun getBundleTask(
14
+ project: Project,
15
+ variant: LibraryVariant,
16
+ ): TaskProvider<Task>? {
17
+ val androidExtension = project.extensions.getByType(LibraryExtension::class.java)
18
+
19
+ // Find the first variant in the library that matches our criteria
20
+ val matchedVariant =
21
+ androidExtension.libraryVariants.find { libraryVariant ->
22
+ // 1. Try Simple Match
23
+ if (libraryVariant.name == variant.name || libraryVariant.name == variant.buildType.name) {
24
+ return@find true
25
+ }
26
+
27
+ // 2. Try Dimension Strategy Match
28
+ val flavor = variant.productFlavors.firstOrNull() ?: variant.mergedFlavor
29
+ val strategies =
30
+ runCatching {
31
+ androidExtension.productFlavors.getByName(flavor.name).missingDimensionStrategies
32
+ }.getOrNull() ?: return@find false
33
+
34
+ strategies.any { (dimension, strategy) ->
35
+ val fallbacks = listOf(strategy.requested) + strategy.fallbacks
36
+ val libFlavor = libraryVariant.productFlavors.firstOrNull() ?: libraryVariant.mergedFlavor
37
+
38
+ dimension == libFlavor.dimension &&
39
+ fallbacks.contains(libFlavor.name) &&
40
+ variant.buildType.name == libraryVariant.buildType.name
41
+ }
42
+ }
43
+
44
+ return matchedVariant?.let { variantTaskProvider.bundleTaskProvider(project, it.name) }
45
+ }
46
+ }
@@ -0,0 +1,58 @@
1
+ package com.callstack.react.brownfield.shared
2
+
3
+ import com.callstack.react.brownfield.expo.utils.DependencyInfo
4
+ import com.callstack.react.brownfield.utils.StringMatcher
5
+
6
+ /**
7
+ * A condition that checks if a certain Expo version meets specific criteria
8
+ * and returns a set of DependencyInfo objects if applicable.
9
+ */
10
+ fun interface ExpoVersionConditionalDepSet {
11
+ /**
12
+ * Gets the dependency set if it is applicable for the given Expo version.
13
+ * @param expoVersion The Expo version to check.
14
+ * @return A set of DependencyInfo objects if the condition is met; otherwise, null.
15
+ */
16
+ fun getIfApplicable(expoVersion: String): Set<DependencyInfo>?
17
+ }
18
+
19
+ data class ArtifactMatcher(
20
+ val groupId: StringMatcher? = null,
21
+ val artifactId: StringMatcher? = null,
22
+ ) {
23
+ fun matches(
24
+ groupId: String,
25
+ artifactId: String,
26
+ ): Boolean {
27
+ val groupMatches = groupIdMatcher()?.matches(groupId) ?: true
28
+ val artifactMatches = artifactIdMatcher()?.matches(artifactId) ?: true
29
+ return groupMatches && artifactMatches
30
+ }
31
+
32
+ private fun groupIdMatcher(): StringMatcher? = groupId
33
+
34
+ private fun artifactIdMatcher(): StringMatcher? = artifactId
35
+ }
36
+
37
+ object Constants {
38
+ const val PROJECT_ID = "com.callstack.react.brownfield"
39
+ const val PLUGIN_NAME = "react-brownfield"
40
+
41
+ const val RE_BUNDLE_FOLDER = "aar_rebundle"
42
+ const val INTERMEDIATES_TEMP_DIR = PLUGIN_NAME
43
+
44
+ val BROWNFIELD_EXPO_TRANSITIVE_DEPS_WHITELISTED_MODULES_FOR_DISCOVERY =
45
+ setOf("expo-modules-core", "expo-constants", "expo", "expo-updates")
46
+ val BROWNFIELD_EXPO_TRANSITIVE_DEPS_ARTIFACTS_BLACKLIST =
47
+ setOf(
48
+ // below: groupIds of Expo components in node_modules
49
+ ArtifactMatcher(groupId = StringMatcher.literal("host.exp.exponent")),
50
+ ArtifactMatcher(groupId = StringMatcher.literal("BareExpo")),
51
+ ArtifactMatcher(groupId = StringMatcher.regex("expo.*")),
52
+ // below: a broken transitive dependency that has no version specified and thus breaks the build
53
+ ArtifactMatcher(
54
+ groupId = StringMatcher.literal("org.jetbrains.kotlin"),
55
+ artifactId = StringMatcher.literal("kotlin-build-tools-impl"),
56
+ ),
57
+ )
58
+ }
@@ -0,0 +1,72 @@
1
+ package com.callstack.react.brownfield.shared
2
+
3
+ import com.callstack.react.brownfield.processors.VariantHelper
4
+ import com.callstack.react.brownfield.utils.AndroidArchiveLibrary
5
+ import com.callstack.react.brownfield.utils.DirectoryManager
6
+ import org.gradle.api.DefaultTask
7
+ import org.gradle.api.provider.ListProperty
8
+ import org.gradle.api.provider.Property
9
+ import org.gradle.api.tasks.Input
10
+ import org.gradle.api.tasks.Internal
11
+ import org.gradle.api.tasks.TaskAction
12
+
13
+ abstract class ExplodeAarTask : DefaultTask() {
14
+ @get:Internal
15
+ abstract val inputArtifacts: ListProperty<UnresolvedArtifactInfo>
16
+
17
+ @get:Input
18
+ abstract val variantName: Property<String>
19
+
20
+ @get:Input
21
+ abstract val minifyEnabled: Property<Boolean>
22
+
23
+ @TaskAction
24
+ fun run() {
25
+ val artifacts = inputArtifacts.get()
26
+ val resolvedVariantName = variantName.get()
27
+
28
+ val variantHelper = VariantHelper()
29
+ variantHelper.project = project
30
+
31
+ // classes-merge
32
+ variantHelper.classesMergeTaskDoFirst(
33
+ DirectoryManager.getMergeClassDirectory(
34
+ resolvedVariantName,
35
+ ),
36
+ resolvedVariantName,
37
+ )
38
+
39
+ val aarLibraries = mutableListOf<AndroidArchiveLibrary>()
40
+ artifacts.forEach { art ->
41
+ val artifactFile =
42
+ requireNotNull(art.file) {
43
+ "Missing AAR file for artifact ${art.moduleGroup}:${art.moduleName}:${art.moduleVersion} " +
44
+ "while exploding variant $resolvedVariantName. Ensure the artifact is resolved " +
45
+ "or the dependency is configured to produce an AAR before running this task."
46
+ }
47
+
48
+ val archiveLibrary =
49
+ AndroidArchiveLibrary(
50
+ this.project,
51
+ art,
52
+ resolvedVariantName,
53
+ )
54
+
55
+ aarLibraries.add(archiveLibrary)
56
+
57
+ // explode-aar
58
+ val zipFolder = archiveLibrary.getExplodedAarRootDir()
59
+ zipFolder.mkdirs()
60
+
61
+ project.copy {
62
+ zipFolder.deleteRecursively()
63
+ it.from(project.zipTree(artifactFile))
64
+ it.into(zipFolder)
65
+ }
66
+ }
67
+
68
+ // classes-merge
69
+ val mergeClassesOutputDir = DirectoryManager.getMergeClassDirectory(resolvedVariantName)
70
+ variantHelper.classesMergeTaskDoLast(mergeClassesOutputDir, aarLibraries, mutableListOf(), resolvedVariantName, minifyEnabled.get())
71
+ }
72
+ }
@@ -0,0 +1,59 @@
1
+ package com.callstack.react.brownfield.shared
2
+
3
+ import com.android.utils.ILogger
4
+ import org.gradle.api.logging.Logger
5
+ import java.util.IllegalFormatException
6
+
7
+ class GradleILogger(
8
+ private val logger: Logger,
9
+ ) : ILogger {
10
+ override fun error(
11
+ throwable: Throwable?,
12
+ msgFormat: String?,
13
+ vararg args: Any?,
14
+ ) {
15
+ val message = formatMessage(msgFormat, args)
16
+ logger.error(message, throwable)
17
+ }
18
+
19
+ override fun warning(
20
+ msgFormat: String?,
21
+ vararg args: Any?,
22
+ ) {
23
+ logger.warn(formatMessage(msgFormat, args))
24
+ }
25
+
26
+ override fun info(
27
+ msgFormat: String?,
28
+ vararg args: Any?,
29
+ ) {
30
+ logger.info(formatMessage(msgFormat, args))
31
+ }
32
+
33
+ override fun verbose(
34
+ msgFormat: String?,
35
+ vararg args: Any?,
36
+ ) {
37
+ logger.debug(formatMessage(msgFormat, args))
38
+ }
39
+
40
+ @Suppress("SpreadOperator") // Required for Java vararg interop with String.format.
41
+ private fun formatMessage(
42
+ msgFormat: String?,
43
+ args: Array<out Any?>,
44
+ ): String {
45
+ if (msgFormat == null) {
46
+ return ""
47
+ }
48
+
49
+ return try {
50
+ if (args.isEmpty()) {
51
+ msgFormat
52
+ } else {
53
+ String.format(msgFormat, *args)
54
+ }
55
+ } catch (_: IllegalFormatException) {
56
+ msgFormat
57
+ }
58
+ }
59
+ }
@@ -0,0 +1,24 @@
1
+ package com.callstack.react.brownfield.shared
2
+
3
+ import com.callstack.react.brownfield.shared.Constants.PLUGIN_NAME
4
+
5
+ object Logging : BaseProject() {
6
+ fun error(
7
+ message: Any,
8
+ error: Throwable,
9
+ ) {
10
+ project.logger.error(getFormattedMessage(message), error)
11
+ }
12
+
13
+ fun info(message: Any) {
14
+ project.logger.info(getFormattedMessage(message))
15
+ }
16
+
17
+ fun log(message: Any) {
18
+ println(getFormattedMessage(message))
19
+ }
20
+
21
+ private fun getFormattedMessage(message: Any): String {
22
+ return " $PLUGIN_NAME :: $message"
23
+ }
24
+ }
@@ -0,0 +1,22 @@
1
+ package com.callstack.react.brownfield.shared
2
+
3
+ interface ModuleInfo {
4
+ val moduleGroup: String
5
+ val moduleName: String
6
+ val moduleVersion: String
7
+
8
+ val file: String?
9
+
10
+ val type: String
11
+ get() = "aar"
12
+
13
+ val isExpoPublishDependency: Boolean?
14
+ }
15
+
16
+ data class UnresolvedArtifactInfo(
17
+ override val moduleGroup: String,
18
+ override val moduleName: String,
19
+ override val moduleVersion: String,
20
+ override val file: String?,
21
+ override val isExpoPublishDependency: Boolean?,
22
+ ) : ModuleInfo
@@ -0,0 +1,77 @@
1
+ package com.callstack.react.brownfield.utils
2
+
3
+ import com.callstack.react.brownfield.shared.UnresolvedArtifactInfo
4
+ import org.gradle.api.Project
5
+ import java.io.File
6
+
7
+ class AndroidArchiveLibrary(
8
+ private val project: Project,
9
+ artifact: UnresolvedArtifactInfo,
10
+ private val variantName: String,
11
+ ) {
12
+ private var packageName: String? = null
13
+ private val artifact: UnresolvedArtifactInfo =
14
+ requireNotNull(artifact.takeIf { it.type == "aar" }) {
15
+ "Only Aar is accepted as an artifact"
16
+ }
17
+
18
+ fun getExplodedAarRootDir(): File {
19
+ val explodedRootDir = File("${project.layout.buildDirectory.get()}/intermediates/exploded-aar")
20
+ return File(explodedRootDir, "${artifact.moduleGroup}/${artifact.moduleName}/${artifact.moduleVersion}/$variantName")
21
+ }
22
+
23
+ @Synchronized
24
+ fun getPackageName(): String {
25
+ if (packageName != null) return packageName!!
26
+
27
+ packageName = getNameSpaceFromBuildGradle()
28
+ return packageName!!
29
+ }
30
+
31
+ private fun getNameSpaceFromBuildGradle(): String {
32
+ if (artifact.isExpoPublishDependency == true) {
33
+ return artifact.moduleGroup
34
+ }
35
+
36
+ val subProj = project.rootProject.project(":${artifact.moduleName}")
37
+ val buildFile = subProj.buildFile // points to build.gradle or build.gradle.kts
38
+
39
+ if (!buildFile.exists()) {
40
+ error("build.gradle file does not exist for ${artifact.moduleName}")
41
+ }
42
+
43
+ val text = buildFile.readText()
44
+
45
+ // Regex to match: namespace = "com.example.rnscreens"
46
+ val regex = Regex("""namespace\s*=?\s*["']([^"']+)["']""")
47
+ val match = regex.find(text)
48
+
49
+ val namespace =
50
+ match?.groupValues?.get(1)
51
+ ?: error("No namespace found in ${buildFile.path}")
52
+
53
+ return namespace
54
+ }
55
+
56
+ fun getManifestFile() = File(getExplodedAarRootDir(), "AndroidManifest.xml")
57
+
58
+ fun getAssetsDir(): File = File(getExplodedAarRootDir(), "assets")
59
+
60
+ fun getJniDir(): File = File(getExplodedAarRootDir(), "jni")
61
+
62
+ fun getLibsDir(): File = File(getExplodedAarRootDir(), "libs")
63
+
64
+ fun getClassesJarFile(): File = File(getExplodedAarRootDir(), "classes.jar")
65
+
66
+ fun getLocalJars(): Collection<File> {
67
+ return getLibsDir().listFiles()?.filter { it.isFile && it.name.endsWith(".jar") } ?: emptyList()
68
+ }
69
+
70
+ fun getResDir(): File = File(getExplodedAarRootDir(), "res")
71
+
72
+ fun getProguardRules(): File = File(getExplodedAarRootDir(), "proguard.txt")
73
+
74
+ fun getDataBindingFolder(): File = File(getExplodedAarRootDir(), "data-binding")
75
+
76
+ fun getDataBindingLogFolder(): File = File(getExplodedAarRootDir(), "data-binding-base-class-log")
77
+ }
@@ -0,0 +1,20 @@
1
+ package com.callstack.react.brownfield.utils
2
+
3
+ import com.callstack.react.brownfield.shared.BaseProject
4
+ import com.callstack.react.brownfield.shared.Constants.INTERMEDIATES_TEMP_DIR
5
+ import com.callstack.react.brownfield.shared.Constants.RE_BUNDLE_FOLDER
6
+ import java.io.File
7
+
8
+ object DirectoryManager : BaseProject() {
9
+ fun getMergeClassDirectory(variantName: String): File {
10
+ return project.file("$buildDir/intermediates/$INTERMEDIATES_TEMP_DIR/merge_classes/$variantName")
11
+ }
12
+
13
+ fun getKotlinMetaDirectory(variantName: String): File {
14
+ return project.file("$buildDir/tmp/kotlin-classes/$variantName/META-INF")
15
+ }
16
+
17
+ fun getReBundleDirectory(variantName: String): File {
18
+ return project.file("$buildDir/outputs/$RE_BUNDLE_FOLDER/$variantName")
19
+ }
20
+ }
@@ -0,0 +1,44 @@
1
+ package com.callstack.react.brownfield.utils
2
+
3
+ open class Extension {
4
+ companion object {
5
+ const val NAME = "reactBrownfield"
6
+ }
7
+
8
+ /**
9
+ * Name of the module using `com.android.application`
10
+ * For eg, app
11
+ *
12
+ * Default value is `app`
13
+ */
14
+ var appProjectName = "app"
15
+
16
+ /**
17
+ * List of dynamic libs (.so) files that you wish to bundle with
18
+ * the aar.
19
+ *
20
+ * By default, only `libappmodules.so` and `libreact_codegen_*.so` are
21
+ * bundled.
22
+ */
23
+ var dynamicLibs = listOf<String>()
24
+
25
+ /**
26
+ * Whether to use stripped .so files.
27
+ *
28
+ * Default is `true`.
29
+ */
30
+ var useStrippedSoFiles = true
31
+
32
+ @Deprecated(
33
+ message =
34
+ "This property is deprecated and will be removed in a future release." +
35
+ "The successor is useStrippedSoFiles, which is by default true.",
36
+ replaceWith = ReplaceWith("useStrippedSoFiles"),
37
+ level = DeprecationLevel.WARNING,
38
+ )
39
+ var experimentalUseStrippedSoFiles
40
+ get() = useStrippedSoFiles
41
+ set(value) {
42
+ useStrippedSoFiles = value
43
+ }
44
+ }
@@ -0,0 +1,5 @@
1
+ package com.callstack.react.brownfield.utils
2
+
3
+ fun String.capitalized(): String {
4
+ return this.replaceFirstChar(Char::titlecase)
5
+ }
@@ -0,0 +1,21 @@
1
+ package com.callstack.react.brownfield.utils
2
+
3
+ sealed class StringMatcher {
4
+ abstract fun matches(input: String): Boolean
5
+
6
+ data class Literal(private val value: String) : StringMatcher() {
7
+ override fun matches(input: String): Boolean = input == value
8
+ }
9
+
10
+ data class Pattern(private val regex: Regex) : StringMatcher() {
11
+ override fun matches(input: String): Boolean = regex.matches(input)
12
+ }
13
+
14
+ companion object {
15
+ fun literal(value: String): StringMatcher = Literal(value)
16
+
17
+ fun regex(pattern: String): StringMatcher = Pattern(Regex(pattern))
18
+
19
+ fun regex(regex: Regex): StringMatcher = Pattern(regex)
20
+ }
21
+ }
@@ -0,0 +1,59 @@
1
+ package com.callstack.react.brownfield.utils
2
+
3
+ import com.callstack.react.brownfield.plugin.RNBrownfieldPlugin.Companion.EXPO_PROJECT_LOCATOR
4
+ import org.gradle.api.Project
5
+ import java.io.File
6
+
7
+ object Utils {
8
+ fun mergeFiles(
9
+ inputFiles: Collection<File>?,
10
+ output: File,
11
+ ) {
12
+ if (inputFiles.isNullOrEmpty()) return
13
+
14
+ val existingFiles = inputFiles.filter { it.exists() }
15
+
16
+ if (existingFiles.isEmpty()) return
17
+
18
+ if (!output.exists()) {
19
+ output.createNewFile()
20
+ }
21
+
22
+ existingFiles.forEach { file ->
23
+ output.appendText("\n${file.readText(Charsets.UTF_8)}\n")
24
+ }
25
+ }
26
+
27
+ fun isExpoProject(project: Project): Boolean {
28
+ return project.findProject(EXPO_PROJECT_LOCATOR) != null
29
+ }
30
+
31
+ fun getBundledAssetsVariantName(
32
+ variantName: String?,
33
+ buildTypeName: String?,
34
+ isDebuggable: Boolean,
35
+ ): String {
36
+ require(!variantName.isNullOrEmpty()) {
37
+ "getBundledAssetsVariantName: Variant name cannot be empty"
38
+ }
39
+
40
+ require(!buildTypeName.isNullOrEmpty()) {
41
+ "getBundledAssetsVariantName: Build Type cannot be empty"
42
+ }
43
+
44
+ if (!isDebuggable) {
45
+ return variantName
46
+ }
47
+
48
+ if (variantName == buildTypeName) {
49
+ return "release"
50
+ }
51
+
52
+ val capitalizedBuildTypeName = buildTypeName.capitalized()
53
+ return if (variantName.endsWith(capitalizedBuildTypeName)) {
54
+ "${variantName.removeSuffix(capitalizedBuildTypeName)}Release"
55
+ } else {
56
+ "release"
57
+ }
58
+ }
59
+ }
@@ -102,7 +102,7 @@ final class ExpoHostRuntime {
102
102
  }
103
103
 
104
104
  /**
105
- * Prefer the embedded JavaScript bundle instead of Metro when this framework is built in Debug.
105
+ * Use the embedded JavaScript bundle as a Debug fallback when Metro is unavailable.
106
106
  * Default value: false
107
107
  */
108
108
  public var preferEmbeddedBundleInDebug: Bool = false {
@@ -232,7 +232,8 @@ class ExpoHostRuntimeDelegate: ExpoReactNativeFactoryDelegate {
232
232
  bundleURLOverride: nil,
233
233
  metroURL: {
234
234
  RCTBundleURLProvider.sharedSettings().jsBundleURL(
235
- forBundleRoot: entryFile
235
+ forBundleRoot: entryFile,
236
+ fallbackURLProvider: { nil }
236
237
  )
237
238
  }
238
239
  )