Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -34,24 +34,21 @@ abstract class GeneratePackageListTask : DefaultTask() {
JsonUtils.fromAutolinkingConfigJson(autolinkInputFile.get().asFile)
?: error(
"""
RNGP - Autolinking: Could not parse autolinking config file:
${autolinkInputFile.get().asFile.absolutePath}

The file is either missing or not containing valid JSON so the build won't succeed.
"""
.trimIndent()
)
RNGP - Autolinking: Could not parse autolinking config file:
${autolinkInputFile.get().asFile.absolutePath}

The file is either missing or not containing valid JSON so the build won't succeed.
"""
.trimIndent())

val packageName =
model.project?.android?.packageName
?: error(
"RNGP - Autolinking: Could not find project.android.packageName in react-native config output! Could not autolink packages without this field."
)
"RNGP - Autolinking: Could not find project.android.packageName in react-native config output! Could not autolink packages without this field.")

val androidPackages = filterAndroidPackages(model)
val packageImports = composePackageImports(packageName, androidPackages)
val packageClassInstance = composePackageInstance(packageName, androidPackages)
val generatedFileContents = composeFileContent(packageImports, packageClassInstance)
val generatedFileContents = composeFileContent(packageClassInstance)

val outputDir = generatedOutputDirectory.get().asFile
outputDir.mkdirs()
Expand All @@ -61,34 +58,50 @@ abstract class GeneratePackageListTask : DefaultTask() {
}
}

internal fun composePackageImports(
packageName: String,
packages: Map<String, ModelAutolinkingDependenciesPlatformAndroidJson>,
) =
packages.entries.joinToString("\n") { (name, dep) ->
val packageImportPath =
requireNotNull(dep.packageImportPath) {
"RNGP - Autolinking: Missing `packageImportPath` in `config` for dependency $name. This is required to generate the autolinking package list."
}
"// $name\n${interpolateDynamicValues(packageImportPath, packageName)}"
}
/**
* 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 composePackageInstance(
packageName: String,
packages: Map<String, ModelAutolinkingDependenciesPlatformAndroidJson>,
) =
if (packages.isEmpty()) {
""
} else {
",\n " +
packages.entries.joinToString(",\n ") { (name, dep) ->
val packageInstance =
requireNotNull(dep.packageInstance) {
"RNGP - Autolinking: Missing `packageInstance` in `config` for dependency $name. This is required to generate the autolinking package list."
}
interpolateDynamicValues(packageInstance, packageName)
}
}
): String {
if (packages.isEmpty()) {
return ""
}

val instances =
packages.entries.map { (name, dep) ->
val packageInstance =
requireNotNull(dep.packageInstance) {
"RNGP - Autolinking: Missing `packageInstance` in `config` for dependency $name. This is required to generate the autolinking package list."
}
val packageImportPath = dep.packageImportPath
val interpolated = interpolateDynamicValues(packageInstance, packageName)

// Use FQCN to avoid class name collisions between different packages
val fqcn =
extractFqcnFromImport(interpolateDynamicValues(packageImportPath, packageName))
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 {
interpolated
}

// Add comment with package name before each instance
"// $name\n $fqcnInstance"
}

return ",\n " + instances.joinToString(",\n ")
}

internal fun filterAndroidPackages(
model: ModelAutolinkingConfigJson?
Expand All @@ -101,10 +114,8 @@ abstract class GeneratePackageListTask : DefaultTask() {
.associate { it.name to checkNotNull(it.platforms?.android) }
}

internal fun composeFileContent(packageImports: String, packageClassInstance: String): String =
generatedFileContentsTemplate
.replace("{{ packageImports }}", packageImports)
.replace("{{ packageClassInstances }}", packageClassInstance)
internal fun composeFileContent(packageClassInstance: String): String =
generatedFileContentsTemplate.replace("{{ packageClassInstances }}", packageClassInstance)

companion object {
const val GENERATED_FILENAME = "com/facebook/react/PackageList.java"
Expand Down Expand Up @@ -148,8 +159,6 @@ abstract class GeneratePackageListTask : DefaultTask() {
import java.util.Arrays;
import java.util.ArrayList;

{{ packageImports }}

@SuppressWarnings("deprecation")
public class PackageList {
private Application application;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,29 +43,17 @@ class GeneratePackageListTaskTest {
}

@Test
fun composePackageImports_withNoPackages_returnsEmpty() {
fun extractFqcnFromImport_withValidImport_returnsClassName() {
val task = createTestTask<GeneratePackageListTask>()
val packageName = "com.facebook.react"
val result = task.composePackageImports(packageName, emptyMap())
assertThat(result).isEqualTo("")
val result = task.extractFqcnFromImport("import com.facebook.react.APackage;")
assertThat(result).isEqualTo("com.facebook.react.APackage")
}

@Test
fun composePackageImports_withPackages_returnsImportCorrectly() {
fun extractFqcnFromImport_withInvalidImport_returnsNull() {
val task = createTestTask<GeneratePackageListTask>()
val packageName = "com.facebook.react"

val result = task.composePackageImports(packageName, testDependencies)
assertThat(result)
.isEqualTo(
"""
// @react-native/a-package
import com.facebook.react.aPackage;
// @react-native/another-package
import com.facebook.react.anotherPackage;
"""
.trimIndent()
)
val result = task.extractFqcnFromImport("invalid import statement")
assertThat(result).isNull()
}

@Test
Expand All @@ -77,7 +65,7 @@ class GeneratePackageListTaskTest {
}

@Test
fun composePackageInstance_withPackages_returnsImportCorrectly() {
fun composePackageInstance_withPackages_returnsFqcnCorrectly() {
val task = createTestTask<GeneratePackageListTask>()
val packageName = "com.facebook.react"

Expand All @@ -86,8 +74,10 @@ class GeneratePackageListTaskTest {
.isEqualTo(
"""
,
new APackage(),
new AnotherPackage()
// @react-native/a-package
new com.facebook.react.APackage(),
// @react-native/another-package
new com.facebook.react.AnotherPackage()
"""
.trimIndent()
)
Expand Down Expand Up @@ -226,10 +216,8 @@ class GeneratePackageListTaskTest {
@Test
fun composeFileContent_withNoPackages_returnsValidFile() {
val task = createTestTask<GeneratePackageListTask>()
val packageName = "com.facebook.react"
val imports = task.composePackageImports(packageName, emptyMap())
val instance = task.composePackageInstance(packageName, emptyMap())
val result = task.composeFileContent(imports, instance)
val instance = task.composePackageInstance("com.facebook.react", emptyMap())
val result = task.composeFileContent(instance)
// language=java
assertThat(result)
.isEqualTo(
Expand All @@ -246,8 +234,6 @@ class GeneratePackageListTaskTest {
import java.util.Arrays;
import java.util.ArrayList;



@SuppressWarnings("deprecation")
public class PackageList {
private Application application;
Expand Down Expand Up @@ -305,9 +291,8 @@ class GeneratePackageListTaskTest {
fun composeFileContent_withPackages_returnsValidFile() {
val task = createTestTask<GeneratePackageListTask>()
val packageName = "com.facebook.react"
val imports = task.composePackageImports(packageName, testDependencies)
val instance = task.composePackageInstance(packageName, testDependencies)
val result = task.composeFileContent(imports, instance)
val result = task.composeFileContent(instance)
// language=java
assertThat(result)
.isEqualTo(
Expand All @@ -324,11 +309,6 @@ class GeneratePackageListTaskTest {
import java.util.Arrays;
import java.util.ArrayList;

// @react-native/a-package
import com.facebook.react.aPackage;
// @react-native/another-package
import com.facebook.react.anotherPackage;

@SuppressWarnings("deprecation")
public class PackageList {
private Application application;
Expand Down Expand Up @@ -374,8 +354,10 @@ class GeneratePackageListTaskTest {
public ArrayList<ReactPackage> getPackages() {
return new ArrayList<>(Arrays.<ReactPackage>asList(
new MainReactPackage(mConfig),
new APackage(),
new AnotherPackage()
// @react-native/a-package
new com.facebook.react.APackage(),
// @react-native/another-package
new com.facebook.react.AnotherPackage()
));
}
}
Expand All @@ -389,7 +371,7 @@ class GeneratePackageListTaskTest {
"@react-native/a-package" to
ModelAutolinkingDependenciesPlatformAndroidJson(
sourceDir = "./a/directory",
packageImportPath = "import com.facebook.react.aPackage;",
packageImportPath = "import com.facebook.react.APackage;",
packageInstance = "new APackage()",
buildTypes = emptyList(),
libraryName = "aPackage",
Expand All @@ -399,7 +381,7 @@ class GeneratePackageListTaskTest {
"@react-native/another-package" to
ModelAutolinkingDependenciesPlatformAndroidJson(
sourceDir = "./another/directory",
packageImportPath = "import com.facebook.react.anotherPackage;",
packageImportPath = "import com.facebook.react.AnotherPackage;",
packageInstance = "new AnotherPackage()",
buildTypes = emptyList(),
libraryName = "anotherPackage",
Expand Down
Loading