Skip to content
Open
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 @@ -83,18 +83,28 @@ abstract class GeneratePackageListTask : DefaultTask() {
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))
// Use FQCNs to avoid class name collisions between different libraries.
val packageImportPaths = dep.packageImportPaths
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 }
if (packageImportPaths != null) {
packageImportPaths.fold(interpolated) { acc, fqcn ->
val className = fqcn.substringAfterLast('.')
// Negative lookbehind ensures we only replace bare class names,
// not ones already part of a fully qualified name.
acc.replace(Regex("(?<!\\.)\\b${Regex.escape(className)}\\b")) { fqcn }
}
} else {
interpolated
val fqcn =
extractFqcnFromImport(
interpolateDynamicValues(dep.packageImportPath, packageName))
if (fqcn != null) {
val className = fqcn.substringAfterLast('.')
interpolated.replace(Regex("\\b${Regex.escape(className)}\\b")) { fqcn }
} else {
interpolated
}
}

// Add comment with package name before each instance
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,83 @@ class GeneratePackageListTaskTest {
)
}

@Test
fun composePackageInstance_withPackageImportPaths_usesArrayDirectly() {
val task = createTestTask<GeneratePackageListTask>()
val packageName = "com.example.app"

val deps =
mapOf(
"react-native-appsflyer" to
ModelAutolinkingDependenciesPlatformAndroidJson(
sourceDir = "./node_modules/react-native-appsflyer/android",
packageImportPath = "",
packageImportPaths =
listOf(
"com.appsflyer.reactnative.RNAppsFlyerPackage",
"com.appsflyer.reactnative.PCAppsFlyerPackage",
),
packageInstance = "new RNAppsFlyerPackage(),\nnew PCAppsFlyerPackage()",
buildTypes = emptyList(),
),
)

val result = task.composePackageInstance(packageName, deps)
assertThat(result)
.isEqualTo(
"""
,
// react-native-appsflyer
new com.appsflyer.reactnative.RNAppsFlyerPackage(),
new com.appsflyer.reactnative.PCAppsFlyerPackage()
"""
.trimIndent()
)
}

@Test
fun composePackageInstance_withBothFields_prefersPackageImportPaths() {
val task = createTestTask<GeneratePackageListTask>()
val packageName = "com.example.app"

val deps =
mapOf(
"my-library" to
ModelAutolinkingDependenciesPlatformAndroidJson(
sourceDir = "./node_modules/my-library/android",
packageImportPath = "import com.wrong.WrongPackage;",
packageImportPaths = listOf("com.correct.CorrectPackage"),
packageInstance = "new CorrectPackage()",
buildTypes = emptyList(),
),
)

val result = task.composePackageInstance(packageName, deps)
assertThat(result).contains("com.correct.CorrectPackage")
assertThat(result).doesNotContain("com.wrong.WrongPackage")
}

@Test
fun composePackageInstance_withNullPackageImportPaths_fallsBackToSingleFqcn() {
val task = createTestTask<GeneratePackageListTask>()
val packageName = "com.example.app"

val deps =
mapOf(
"my-library" to
ModelAutolinkingDependenciesPlatformAndroidJson(
sourceDir = "./node_modules/my-library/android",
packageImportPath = "import com.legacy.LegacyPackage;",
packageImportPaths = null,
packageInstance = "new LegacyPackage()",
buildTypes = emptyList(),
),
)

val result = task.composePackageInstance(packageName, deps)
assertThat(result).contains("com.legacy.LegacyPackage")
}

@Test
fun interpolateDynamicValues_withNoBuildConfigOrROccurrencies_doesNothing() {
val packageName = "com.facebook.react"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ package com.facebook.react.model
data class ModelAutolinkingDependenciesPlatformAndroidJson(
val sourceDir: String,
val packageImportPath: String,
val packageImportPaths: List<String>? = null,
val packageInstance: String,
val buildTypes: List<String>,
val libraryName: String? = null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,51 @@ class JsonUtilsTest {
.isPureCxxDependency!!
)
.isFalse()
assertThat(
parsed.dependencies!!["@react-native/oss-library-example"]!!
.platforms!!
.android!!
.packageImportPaths
)
.isNull()
}

@Test
fun fromAutolinkingConfigJson_withPackageImportPaths_canParseIt() {
val validJson =
createJsonFile(
"""
{
"reactNativeVersion": "1000.0.0",
"dependencies": {
"@react-native/oss-library-example": {
"root": "./node_modules/@react-native/oss-library-example",
"name": "@react-native/oss-library-example",
"platforms": {
"android": {
"sourceDir": "./node_modules/@react-native/oss-library-example/android",
"packageImportPath": "import com.facebook.react.osslibraryexample.OSSLibraryExamplePackage;",
"packageImportPaths": [
"com.facebook.react.osslibraryexample.OSSLibraryExamplePackage"
],
"packageInstance": "new OSSLibraryExamplePackage()",
"buildTypes": ["debug", "release"]
}
}
}
}
}
"""
.trimIndent()
)
val parsed = JsonUtils.fromAutolinkingConfigJson(validJson)!!

val android =
parsed.dependencies!!["@react-native/oss-library-example"]!!.platforms!!.android!!
assertThat(android.packageImportPaths)
.containsExactly("com.facebook.react.osslibraryexample.OSSLibraryExamplePackage")
assertThat(android.packageImportPath)
.isEqualTo("import com.facebook.react.osslibraryexample.OSSLibraryExamplePackage;")
}

private fun createJsonFile(@Language("JSON") input: String) =
Expand Down