Skip to content

Commit 0f86553

Browse files
committed
[Gradle] Add automatic handling of full qualified imports for conflicting icon names
1 parent 98429ee commit 0f86553

File tree

3 files changed

+165
-6
lines changed

3 files changed

+165
-6
lines changed

tools/gradle-plugin/CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
# Gradle plugin changelog
22

3+
## Unreleased
4+
5+
### Added
6+
7+
- Automatically handle full qualified imports for icons that conflict with reserved Compose qualified names (`Brush`,
8+
`Color`, `Offset`)
9+
310
## [0.2.0] - 2025-12-08
411

512
### Added

tools/gradle-plugin/src/main/kotlin/io/github/composegears/valkyrie/gradle/internal/task/GenerateImageVectorsTask.kt

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ package io.github.composegears.valkyrie.gradle.internal.task
33
import io.github.composegears.valkyrie.generator.core.IconPack
44
import io.github.composegears.valkyrie.generator.iconpack.IconPackGenerator
55
import io.github.composegears.valkyrie.generator.iconpack.IconPackGeneratorConfig
6+
import io.github.composegears.valkyrie.generator.jvm.imagevector.FullQualifiedImports
7+
import io.github.composegears.valkyrie.generator.jvm.imagevector.FullQualifiedImports.Companion.reservedComposeQualifiers
68
import io.github.composegears.valkyrie.generator.jvm.imagevector.ImageVectorGenerator
79
import io.github.composegears.valkyrie.generator.jvm.imagevector.ImageVectorGeneratorConfig
810
import io.github.composegears.valkyrie.generator.jvm.imagevector.OutputFormat
@@ -11,6 +13,7 @@ import io.github.composegears.valkyrie.gradle.IconPackExtension
1113
import io.github.composegears.valkyrie.gradle.NestedPack
1214
import io.github.composegears.valkyrie.parser.unified.ParserType
1315
import io.github.composegears.valkyrie.parser.unified.SvgXmlParser
16+
import io.github.composegears.valkyrie.parser.unified.util.IconNameFormatter
1417
import io.github.composegears.valkyrie.sdk.core.extensions.writeToKt
1518
import java.io.File
1619
import kotlinx.io.files.Path
@@ -98,14 +101,32 @@ internal abstract class GenerateImageVectorsTask : DefaultTask() {
98101
outputDirectory.deleteRecursively() // make sure nothing is left over from previous run
99102
outputDirectory.mkdirs()
100103

104+
// Detect icons with names conflicting with reserved Compose qualifiers
105+
val fullQualifiedNames = iconFiles.files
106+
.map { IconNameFormatter.format(name = it.name) }
107+
.filter { reservedComposeQualifiers.contains(it) }
108+
109+
if (fullQualifiedNames.isNotEmpty()) {
110+
logger.lifecycle(
111+
"Found icons names that conflict with reserved Compose qualifiers. " +
112+
"Full qualified import will be used for: \"${fullQualifiedNames.joinToString(", ")}\"",
113+
)
114+
}
115+
101116
if (iconPack.isPresent && iconPack.get().targetSourceSet.get() == sourceSet.get()) {
102117
generateIconPack(outputDirectory = outputDirectory)
103118
}
104119

105120
if (iconPack.isPresent) {
106-
generateIconsWithIconPack(outputDirectory = outputDirectory)
121+
generateIconsWithIconPack(
122+
outputDirectory = outputDirectory,
123+
fullQualifiedNames = fullQualifiedNames,
124+
)
107125
} else {
108-
generateIconsWithoutPack(outputDirectory = outputDirectory)
126+
generateIconsWithoutPack(
127+
outputDirectory = outputDirectory,
128+
fullQualifiedNames = fullQualifiedNames,
129+
)
109130
}
110131

111132
val executionTime = System.currentTimeMillis() - startTime
@@ -142,7 +163,7 @@ internal abstract class GenerateImageVectorsTask : DefaultTask() {
142163
}
143164
}
144165

145-
private fun generateIconsWithoutPack(outputDirectory: File) {
166+
private fun generateIconsWithoutPack(outputDirectory: File, fullQualifiedNames: List<String>) {
146167
val packageName = packageName.get()
147168

148169
if (iconFiles.isEmpty) {
@@ -157,7 +178,13 @@ internal abstract class GenerateImageVectorsTask : DefaultTask() {
157178
useFlatPackage = false,
158179
)
159180

160-
val config = basicConfig
181+
val config = basicConfig.copy(
182+
fullQualifiedImports = FullQualifiedImports(
183+
brush = "Brush" in fullQualifiedNames,
184+
color = "Color" in fullQualifiedNames,
185+
offset = "Offset" in fullQualifiedNames,
186+
),
187+
)
161188
var convertedCount = 0
162189
iconFiles.files.forEach { file ->
163190
runCatching {
@@ -175,14 +202,21 @@ internal abstract class GenerateImageVectorsTask : DefaultTask() {
175202
logger.lifecycle("Generated $convertedCount ImageVector ${iconWord(convertedCount)} in package \"$packageName\"")
176203
}
177204

178-
private fun generateIconsWithIconPack(outputDirectory: File) {
205+
private fun generateIconsWithIconPack(outputDirectory: File, fullQualifiedNames: List<String>) {
179206
val packageName = packageName.get()
180207

181208
val pack = iconPack.get()
182209
val nestedPacks = pack.nestedPacks.get()
183210
val useFlatPackage = pack.useFlatPackage.get()
184211

185-
val config = basicConfig.copy(packName = pack.name.get())
212+
val config = basicConfig.copy(
213+
packName = pack.name.get(),
214+
fullQualifiedImports = FullQualifiedImports(
215+
brush = "Brush" in fullQualifiedNames,
216+
color = "Color" in fullQualifiedNames,
217+
offset = "Offset" in fullQualifiedNames,
218+
),
219+
)
186220

187221
if (iconFiles.isEmpty) {
188222
logger.lifecycle("No icon files to process for ImageVector generation")
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
package io.github.composegears.valkyrie.gradle
2+
3+
import assertk.assertThat
4+
import assertk.assertions.contains
5+
import assertk.assertions.doesNotContain
6+
import assertk.assertions.exists
7+
import io.github.composegears.valkyrie.gradle.common.CommonGradleTest
8+
import io.github.composegears.valkyrie.gradle.common.GENERATED_SOURCES_DIR
9+
import io.github.composegears.valkyrie.gradle.internal.TASK_NAME
10+
import java.nio.file.Path
11+
import kotlin.io.path.copyTo
12+
import kotlin.io.path.createDirectories
13+
import kotlin.io.path.readText
14+
import kotlin.io.path.writeText
15+
import org.junit.jupiter.api.Test
16+
import org.junit.jupiter.api.io.TempDir
17+
18+
class FullQualifiedNamesTest : CommonGradleTest() {
19+
20+
@Test
21+
fun `test full qualified imports for Brush icon`(@TempDir root: Path) {
22+
root.writeSettingsFile()
23+
24+
root.resolve("build.gradle.kts").writeText(
25+
"""
26+
plugins {
27+
kotlin("multiplatform")
28+
id("io.github.composegears.valkyrie")
29+
}
30+
valkyrie {
31+
packageName = "test.icons"
32+
}
33+
kotlin {
34+
jvm()
35+
}
36+
""".trimIndent(),
37+
)
38+
39+
val sourceDir = RESOURCES_DIR_XML.toPath()
40+
val resourceDir = root.resolve("src/commonMain/valkyrieResources")
41+
resourceDir.createDirectories()
42+
sourceDir.resolve("ic_brush.xml").copyTo(resourceDir.resolve("ic_brush.xml"))
43+
44+
val result = runTask(root, TASK_NAME)
45+
46+
assertThat(result.output).contains("Found icons names that conflict with reserved Compose qualifiers. Full qualified import will be used for: \"Brush\"")
47+
48+
val ktPath = root.resolve("${GENERATED_SOURCES_DIR}/commonMain/test/icons/Brush.kt")
49+
assertThat(ktPath).exists()
50+
assertThat(ktPath.readText()).doesNotContain("import androidx.compose.ui.graphics.Brush")
51+
}
52+
53+
@Test
54+
fun `test full qualified imports for Color icon`(@TempDir root: Path) {
55+
root.writeSettingsFile()
56+
57+
root.resolve("build.gradle.kts").writeText(
58+
"""
59+
plugins {
60+
kotlin("multiplatform")
61+
id("io.github.composegears.valkyrie")
62+
}
63+
valkyrie {
64+
packageName = "test.icons"
65+
}
66+
kotlin {
67+
jvm()
68+
}
69+
""".trimIndent(),
70+
)
71+
72+
val sourceDir = RESOURCES_DIR_XML.toPath()
73+
val resourceDir = root.resolve("src/commonMain/valkyrieResources")
74+
resourceDir.createDirectories()
75+
sourceDir.resolve("ic_brush.xml").copyTo(resourceDir.resolve("ic_color.xml"))
76+
77+
val result = runTask(root, TASK_NAME)
78+
79+
assertThat(result.output).contains("Found icons names that conflict with reserved Compose qualifiers. Full qualified import will be used for: \"Color\"")
80+
81+
val ktPath = root.resolve("${GENERATED_SOURCES_DIR}/commonMain/test/icons/Color.kt")
82+
assertThat(ktPath).exists()
83+
assertThat(ktPath.readText()).doesNotContain("import androidx.compose.ui.graphics.Color")
84+
}
85+
86+
@Test
87+
fun `test full qualified imports for Offset icon`(@TempDir root: Path) {
88+
root.writeSettingsFile()
89+
90+
root.resolve("build.gradle.kts").writeText(
91+
"""
92+
plugins {
93+
kotlin("multiplatform")
94+
id("io.github.composegears.valkyrie")
95+
}
96+
valkyrie {
97+
packageName = "test.icons"
98+
}
99+
kotlin {
100+
jvm()
101+
}
102+
""".trimIndent(),
103+
)
104+
105+
val sourceDir = RESOURCES_DIR_XML.toPath()
106+
val resourceDir = root.resolve("src/commonMain/valkyrieResources")
107+
resourceDir.createDirectories()
108+
sourceDir.resolve("ic_brush.xml").copyTo(resourceDir.resolve("ic_offset.xml"))
109+
110+
val result = runTask(root, TASK_NAME)
111+
112+
assertThat(result.output).contains("Found icons names that conflict with reserved Compose qualifiers. Full qualified import will be used for: \"Offset\"")
113+
114+
val ktPath = root.resolve("${GENERATED_SOURCES_DIR}/commonMain/test/icons/Offset.kt")
115+
assertThat(ktPath).exists()
116+
assertThat(ktPath.readText()).doesNotContain("import androidx.compose.ui.graphics.Offset")
117+
}
118+
}

0 commit comments

Comments
 (0)