Skip to content

Commit 6d1550a

Browse files
committed
docs(website): add template system documentation page
1 parent d238f62 commit 6d1550a

File tree

29 files changed

+1185
-133
lines changed

29 files changed

+1185
-133
lines changed

svg-to-compose-gradle-plugin/src/main/kotlin/dev/tonholo/s2c/gradle/tasks/ParseSvgToComposeIconTask.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,7 @@ internal abstract class ParseSvgToComposeIconTask @Inject constructor(private va
316316
makeInternal.set(iconConfiguration.iconVisibility.get() == IconVisibility.Internal)
317317
excludePattern.set(iconConfiguration.exclude.orNull?.pattern)
318318
kmpPreview.set(isKmp)
319-
templateFilePath.set(iconConfiguration.templateFile.orNull?.asFile?.absolutePath)
319+
templateFilePath.set(iconConfiguration.templateFile.orNull)
320320
resultFilePath.set(resultFile.absolutePath)
321321
this.bridgeToken.set(bridgeToken)
322322
tempDirPath.set(temporaryDir.resolve("worker-${path.name.hashCode()}").absolutePath)

svg-to-compose-gradle-plugin/src/main/kotlin/dev/tonholo/s2c/gradle/tasks/worker/IconParsingParameters.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package dev.tonholo.s2c.gradle.tasks.worker
22

3+
import org.gradle.api.file.RegularFileProperty
34
import org.gradle.api.provider.Property
45
import org.gradle.workers.WorkParameters
56

@@ -24,7 +25,7 @@ internal interface IconParsingParameters : WorkParameters {
2425
val kmpPreview: Property<Boolean>
2526
val recursive: Property<Boolean>
2627
val maxDepth: Property<Int>
27-
val templateFilePath: Property<String>
28+
val templateFilePath: RegularFileProperty
2829
val resultFilePath: Property<String>
2930
val bridgeToken: Property<String>
3031
}

svg-to-compose-gradle-plugin/src/main/kotlin/dev/tonholo/s2c/gradle/tasks/worker/IconParsingWorkAction.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import dev.tonholo.s2c.error.ParserException
55
import dev.tonholo.s2c.gradle.internal.service.S2cWorkerBridge
66
import dev.tonholo.s2c.parser.ParserConfig
77
import dev.tonholo.s2c.parser.config.TemplateConfig
8+
import okio.Path.Companion.toOkioPath
89
import okio.Path.Companion.toPath
910
import org.gradle.workers.WorkAction
1011
import java.util.UUID
@@ -80,8 +81,8 @@ internal abstract class IconParsingWorkAction : WorkAction<IconParsingParameters
8081
silent = true,
8182
keepTempFolder = true,
8283
template = TemplateConfig(
83-
configPath = templateFilePath.orNull?.toPath(),
84-
noDiscovery = templateFilePath.orNull == null,
84+
configPath = templateFilePath.orNull?.asFile?.toOkioPath(),
85+
noDiscovery = !templateFilePath.isPresent,
8586
),
8687
)
8788
}

svg-to-compose/src/commonMain/kotlin/dev/tonholo/s2c/Converter.kt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import dev.tonholo.s2c.ConversionStep.Parsing
88
import dev.tonholo.s2c.domain.FileType
99
import dev.tonholo.s2c.domain.IconFileContents
1010
import dev.tonholo.s2c.emitter.CodeEmitterFactory
11+
import dev.tonholo.s2c.emitter.template.config.TemplateEmitterConfig
1112
import dev.tonholo.s2c.optimizer.ContentOptimizer
1213
import dev.tonholo.s2c.parser.ContentParser
1314
import dev.tonholo.s2c.parser.ParserConfig
@@ -27,7 +28,7 @@ import kotlinx.coroutines.flow.flow
2728
* allowing consumers to track progress and handle intermediate states such as optimization,
2829
* parsing, code generation, completion, or errors.
2930
*/
30-
fun interface Converter {
31+
interface Converter {
3132
/**
3233
* Converts vector icon content into Kotlin Compose code by processing it through optimization,
3334
* parsing, and code generation stages.
@@ -51,6 +52,7 @@ fun interface Converter {
5152
* @param config the parser configuration containing package name, theme, and other generation options
5253
* @param fileType the type of vector file being converted (defaults to SVG)
5354
* @param optimizer optional optimizer to preprocess the content before parsing
55+
* @param templateEmitterConfig optional template configuration for custom code generation output
5456
* @return a Flow of ConversionStep instances representing the progress and result of the conversion
5557
*/
5658
fun convert(
@@ -59,6 +61,7 @@ fun interface Converter {
5961
config: ParserConfig,
6062
fileType: FileType,
6163
optimizer: ContentOptimizer?,
64+
templateEmitterConfig: TemplateEmitterConfig? = null,
6265
): Flow<ConversionStep>
6366
}
6467

@@ -73,6 +76,7 @@ class DefaultConverter(
7376
config: ParserConfig,
7477
fileType: FileType,
7578
optimizer: ContentOptimizer?,
79+
templateEmitterConfig: TemplateEmitterConfig?,
7680
): Flow<ConversionStep> = flow {
7781
try {
7882
val optimizedContent = if (optimizer != null) {
@@ -88,7 +92,7 @@ class DefaultConverter(
8892
val iconContents = parser.parse(optimizedContent, iconName, config)
8993

9094
emit(Generating("Generating Kotlin code..."))
91-
val emitter = codeEmitterFactory.create()
95+
val emitter = codeEmitterFactory.create(templateEmitterConfig = templateEmitterConfig)
9296
val kotlinCode = emitter.emit(iconContents)
9397

9498
emit(

svg-to-compose/src/commonMain/kotlin/dev/tonholo/s2c/emitter/NodeChunker.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@ import kotlin.math.roundToInt
1515
* @property chunkFunctions The chunk function nodes extracted from [nodes],
1616
* or `null` when no chunking was needed.
1717
*/
18-
data class ChunkResult(val nodes: List<ImageVectorNode>, val chunkFunctions: List<ImageVectorNode.ChunkFunction>?)
18+
internal data class ChunkResult(
19+
val nodes: List<ImageVectorNode>,
20+
val chunkFunctions: List<ImageVectorNode.ChunkFunction>?,
21+
)
1922

2023
/**
2124
* Splits an icon's nodes into [ImageVectorNode.ChunkFunction]s when the

svg-to-compose/src/commonMain/kotlin/dev/tonholo/s2c/emitter/imagevector/ImageVectorEmitter.kt

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ class ImageVectorEmitter(private val logger: Logger, private val formatConfig: F
100100
}
101101

102102
private fun buildIconPropertyName(contents: IconFileContents): String = when {
103-
contents.receiverType?.isNotEmpty() == true -> {
103+
!contents.receiverType.isNullOrEmpty() -> {
104104
val receiverType = contents.receiverType.removeSuffix(".")
105105
"$receiverType.${contents.iconName.pascalCase()}"
106106
}
@@ -173,4 +173,19 @@ class ImageVectorEmitter(private val logger: Logger, private val formatConfig: F
173173
appendLine(preview.trimMargin())
174174
}
175175
}
176+
177+
internal companion object {
178+
/** Imports required by the default preview snippet. */
179+
val PREVIEW_IMPORTS: Set<String> = setOf(
180+
"androidx.compose.foundation.Image",
181+
"androidx.compose.foundation.layout.Arrangement",
182+
"androidx.compose.foundation.layout.Column",
183+
"androidx.compose.foundation.layout.width",
184+
"androidx.compose.foundation.layout.height",
185+
"androidx.compose.ui.Alignment",
186+
"androidx.compose.ui.Modifier",
187+
"androidx.compose.ui.tooling.preview.Preview",
188+
"androidx.compose.runtime.Composable",
189+
)
190+
}
176191
}

svg-to-compose/src/commonMain/kotlin/dev/tonholo/s2c/emitter/template/TemplateContext.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ internal class TemplateContext(
3333
private val _collectedImports: MutableSet<String> = initialImports.toMutableSet()
3434

3535
/** Immutable snapshot of the accumulated imports. */
36-
val collectedImports: Set<String> get() = _collectedImports
36+
val collectedImports: Set<String> get() = _collectedImports.toSet()
3737

3838
/** Registers a single import path. */
3939
fun addImport(importPath: String) {
@@ -69,7 +69,7 @@ internal class TemplateContext(
6969
fun forIcon(contents: IconFileContents, config: TemplateEmitterConfig, iconBody: String): TemplateContext {
7070
val receiverName = config.definitions.receiver?.name
7171
val iconPropertyName = when {
72-
contents.receiverType?.isNotEmpty() == true -> {
72+
!contents.receiverType.isNullOrEmpty() -> {
7373
val receiverType = contents.receiverType.removeSuffix(".")
7474
"$receiverType.${contents.iconName.pascalCase()}"
7575
}

svg-to-compose/src/commonMain/kotlin/dev/tonholo/s2c/emitter/template/TemplateEmitter.kt

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -125,19 +125,7 @@ internal class TemplateEmitter(
125125
}
126126

127127
private companion object {
128-
// These mirror the imports that ImageVectorEmitter adds for its preview
129-
// snippet. If the default preview format changes, this set must be updated.
130-
val PREVIEW_IMPORTS = setOf(
131-
"androidx.compose.foundation.Image",
132-
"androidx.compose.foundation.layout.Arrangement",
133-
"androidx.compose.foundation.layout.Column",
134-
"androidx.compose.foundation.layout.width",
135-
"androidx.compose.foundation.layout.height",
136-
"androidx.compose.ui.Alignment",
137-
"androidx.compose.ui.Modifier",
138-
"androidx.compose.ui.tooling.preview.Preview",
139-
"androidx.compose.runtime.Composable",
140-
)
128+
val PREVIEW_IMPORTS: Set<String> = ImageVectorEmitter.PREVIEW_IMPORTS
141129
}
142130

143131
private fun buildChunkFunctionsContent(chunkFunctions: List<ImageVectorNode.ChunkFunction>?): String {

svg-to-compose/src/commonTest/kotlin/dev/tonholo/s2c/emitter/NodeChunkerTest.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,8 @@ class NodeChunkerTest {
6161
val icon = createIcon(nodeCount = NODE_COUNT_ABOVE_THRESHOLD)
6262
val result = chunker.chunkIfNeeded(icon, defaultNameResolver)
6363

64-
val allChunkedNodes = result.chunkFunctions!!.flatMap { it.nodes }
64+
val chunkFunctions = requireNotNull(result.chunkFunctions) { "chunkFunctions should be present" }
65+
val allChunkedNodes = chunkFunctions.flatMap { it.nodes }
6566
assertEquals(icon.nodes.size, allChunkedNodes.size)
6667
}
6768

@@ -74,7 +75,8 @@ class NodeChunkerTest {
7475

7576
val result = chunker.chunkIfNeeded(icon, nameResolver)
7677

77-
val names = result.chunkFunctions!!.map { it.functionName }
78+
val chunkFunctions = requireNotNull(result.chunkFunctions) { "chunkFunctions should be present" }
79+
val names = chunkFunctions.map { it.functionName }
7880
names.forEachIndexed { idx, name ->
7981
assertEquals("customChunk${idx + 1}", name)
8082
}

svg-to-compose/src/commonTest/kotlin/dev/tonholo/s2c/emitter/template/TemplateEmitterTest.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -286,10 +286,10 @@ class TemplateEmitterTest {
286286
definitions = config.definitions.imports,
287287
fragments = config.fragments,
288288
)
289-
val result = PlaceholderResolver.resolve(
290-
config.fragments["chunk_function_name"]!!,
291-
context,
292-
)
289+
val fragment = requireNotNull(config.fragments["chunk_function_name"]) {
290+
"chunk_function_name fragment must be present in config"
291+
}
292+
val result = PlaceholderResolver.resolve(fragment, context)
293293
assertEquals("testIconPart3", result)
294294
}
295295

0 commit comments

Comments
 (0)