diff --git a/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/tasks/GeneratePackageListTask.kt b/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/tasks/GeneratePackageListTask.kt index 3bc2386901a..31e2130d87b 100644 --- a/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/tasks/GeneratePackageListTask.kt +++ b/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/tasks/GeneratePackageListTask.kt @@ -64,10 +64,19 @@ abstract class GeneratePackageListTask : DefaultTask() { * Extracts the fully qualified class name from an import statement. E.g., "import * com.foo.bar.MyClass;" -> "com.foo.bar.MyClass" */ - internal fun extractFqcnFromImport(importStatement: String): String? { - val match = Regex("import\\s+([\\w.]+)\\s*;").find(importStatement) - return match?.groupValues?.get(1) - } + internal fun extractFqcnFromImport(importStatement: String): String? = + extractFqcsFromImports(importStatement).firstOrNull() + + /** + * Extracts the fully qualified class name from every import statement. E.g., "import + * com.foo.bar.MyClass;\nimport com.foo.baz.MyOtherClass;" -> ["com.foo.bar.MyClass", + * "com.foo.baz.MyOtherClass"] + */ + internal fun extractFqcsFromImports(importStatements: String): List = + Regex("import\\s+([\\w.]+)\\s*;") + .findAll(importStatements) + .map { it.groupValues[1] } + .toList() internal fun composePackageInstance( packageName: String, @@ -87,14 +96,19 @@ abstract class GeneratePackageListTask : DefaultTask() { val interpolated = interpolateDynamicValues(packageInstance, packageName) // Use FQCN to avoid class name collisions between different packages - val fqcn = extractFqcnFromImport(interpolateDynamicValues(packageImportPath, packageName)) + val interpolatedImportPath = interpolateDynamicValues(packageImportPath, packageName) + val fqcnByClassName = + extractFqcsFromImports(interpolatedImportPath) + .associateBy { it.substringAfterLast('.') } val fqcnInstance = - if (fqcn != null) { - val className = fqcn.substringAfterLast('.') - // Replace the short class name with FQCN in the instance - interpolated.replace(Regex("\\b${Regex.escape(className)}\\b")) { fqcn } - } else { + if (fqcnByClassName.isEmpty()) { interpolated + } else { + val classNamePattern = fqcnByClassName.keys.joinToString("|") { Regex.escape(it) } + // Replace every short class name with its FQCN in the instance + interpolated.replace(Regex("\\b(?:$classNamePattern)\\b")) { match -> + fqcnByClassName.getValue(match.value) + } } // Add comment with package name before each instance diff --git a/packages/gradle-plugin/react-native-gradle-plugin/src/test/kotlin/com/facebook/react/tasks/GeneratePackageListTaskTest.kt b/packages/gradle-plugin/react-native-gradle-plugin/src/test/kotlin/com/facebook/react/tasks/GeneratePackageListTaskTest.kt index 789d7a75df2..ee3d4203443 100644 --- a/packages/gradle-plugin/react-native-gradle-plugin/src/test/kotlin/com/facebook/react/tasks/GeneratePackageListTaskTest.kt +++ b/packages/gradle-plugin/react-native-gradle-plugin/src/test/kotlin/com/facebook/react/tasks/GeneratePackageListTaskTest.kt @@ -83,6 +83,42 @@ class GeneratePackageListTaskTest { ) } + @Test + fun composePackageInstance_withMultipleImportsInOneDependency_returnsFqcnForEachInstance() { + val task = createTestTask() + val packageName = "com.facebook.react" + + val result = + task.composePackageInstance( + packageName, + mapOf( + "@react-native/appsflyer" to + ModelAutolinkingDependenciesPlatformAndroidJson( + sourceDir = "./appsflyer/directory", + packageImportPath = + """ + import com.appsflyer.reactnative.RNAppsFlyerPackage; + import com.appsflyer.reactnative.PCAppsFlyerPackage; + """ + .trimIndent(), + packageInstance = + """ + new RNAppsFlyerPackage(), + new PCAppsFlyerPackage() + """ + .trimIndent(), + buildTypes = emptyList(), + ), + ), + ) + + assertThat(result) + .contains("new com.appsflyer.reactnative.RNAppsFlyerPackage()") + .contains("new com.appsflyer.reactnative.PCAppsFlyerPackage()") + .doesNotContain("new RNAppsFlyerPackage()") + .doesNotContain("new PCAppsFlyerPackage()") + } + @Test fun interpolateDynamicValues_withNoBuildConfigOrROccurrencies_doesNothing() { val packageName = "com.facebook.react"