Skip to content

White-Wind-LLC/komap

Repository files navigation

Kotlin (KMP) mapper codegen via KSP

Lightweight annotation-based library for generating type-safe mappers in Kotlin Multiplatform using KSP 2. The komap-annotations module is KMP-ready, while code generation is performed via KSP in commonMain (metadata) using the JVM processor komap-processor.

Features

  • KMP-first: generate from commonMain for JVM/JS/Native, no platform-specific APIs
  • Bidirectional mapping via @Komap(from = [...], to = [...])
  • Property renaming: @MapName("sourceName")
  • Ignore parameters: @IgnoreInMapping
  • Custom converters: @KomapProvider(qualifier = ...) + @MapQualifier("...")
  • Construction via factories: @KomapFactory(qualifier = ...) with selection through factoryQualifiers
  • Collections and defaults: overloads for Iterable; missing values are exposed as parameters/lambdas

Installation

Prereqs: Kotlin 2.2.0, KSP 2.2.0-2.0.2, repository mavenCentral().

// settings.gradle.kts
pluginManagement {
    repositories { google(); gradlePluginPortal(); mavenCentral() }
}
// build.gradle.kts (module with your models)
plugins {
    kotlin("multiplatform")
    id("com.google.devtools.ksp")
}

kotlin {
    jvm()
    js { nodejs() }
    // add other targets as needed

    sourceSets {
        val commonMain by getting {
            // Include KSP generated sources
            kotlin.srcDir("build/generated/ksp/metadata/commonMain/kotlin")
            dependencies {
                implementation("ua.wwind.komap:komap-annotations:1.0.0")
            }
        }
    }
}

dependencies {
    // Wire the KSP processor for common metadata
    add("kspCommonMainMetadata", "ua.wwind.komap:komap-processor:1.0.0")
}

Generate code:

./gradlew kspCommonMainKotlinMetadata

Generated sources live in build/generated/ksp/metadata/commonMain/kotlin.

Quick start

// API model
data class ApiUser(
    val id: String,
    val name: String,
    val role: String,
)

// Domain model with annotations
@Komap(to = [ApiUser::class])
data class User(
    @MapName(name = "id") val userId: String,
    val name: String,
    @IgnoreInMapping val role: String, // not read from source; becomes a mapper parameter
)

// After generation you get:
// fun User.toApiUser(role: String): ApiUser
// fun Iterable<User>.toApiUser(provideRole: (User) -> String): List<ApiUser>

Usage:

val user = User(userId = "42", name = "Ada", role = "ADMIN")
val api = user.toApiUser(role = "USER")
val list = listOf(user).toApiUser { it.role }

Custom converters

// Example: Email String
@KomapProvider(qualifier = "plain")
fun Email.toStringPlain(): String = this.value

@KomapProvider
fun String.toEmail(): Email = Email(this)

// Use the selected provider
data class Profile(
    @MapQualifier("plain") val email: String,
)

Factories

// Must be in the same module as the target class
@KomapFactory(qualifier = "fromName")
fun ApiUser(name: String, id: String, role: String): ApiUser = ApiUser(id, name, role)

@Komap(to = [ApiUser::class], factoryQualifiers = ["fromName"])
data class User(
    @MapName("id") val userId: String,
    val name: String,
    @IgnoreInMapping val role: String,
)

The processor selects a factory by matching return type and provided factoryQualifiers.

Supported targets

  • Annotations (komap-annotations): Android (Library), JVM, JS (Node), WASM JS, Linux X64/ARM64, macOS X64/ARM64, Windows (mingwX64), iOS X64/ARM64/Simulator
  • Processor (komap-processor): JVM (via KSP in metadata for KMP)

Examples

See the komap-samples module:

  • basic/BasicSample.kt — basic mappings and collections
  • factory/FactorySample.kt — factories with qualifiers
  • providers/ — examples of @KomapProvider

Run generation in the sample:

./gradlew :komap-samples:kspCommonMainKotlinMetadata

License

Apache License 2.0. See POM details and the repository page: White Wind LLC — komap.

Contributing

PRs and discussions are welcome. Please keep the code style and add small examples to komap-samples for new features.