Skip to content

elide-tools/seskar

 
 

Repository files navigation

CI Status CI Status Gradle Plugin Portal Maven Central Kotlin

Seskar

Seskar is a gradle plugin that provides useful additions for Kotlin/JS projects.

Setup

To add Seskar to your project, you need to the following configuration to your project's build.gradle.kts:

plugins {
    kotlin("multiplatform") version "1.9.10"
    id("io.github.turansky.seskar") version "2.3.0"
}

// browser target
dependencies {
    implementation("io.github.turansky.seskar:seskar-core:2.3.0")
}

React

Conditional rendering

Seskar generates keys for child elements to prevent problems with conditional rendering. As result in following example Content child state won't reset after showHeader property change.

val App = FC {
    val showHeader = useShowHeader()
    
    if (showHeader) 
        Header() // genarated: key = "@rdk/5"
        
    Content()    // genarated: key = "@rdk/7"
    Footer()     // genarated: key = "@rdk/8"
}

Dependencies

When a project uses the Kotlin/JS compiler, value classes are autoboxed. If a value class is used as a dependency of a react hook (e.g., in useMemo, useState or useEffect), a new class will be created on every rendering pass, which causes infinite re-rendering.

To prevent this, Seskar disables autoboxing for value class dependencies in hooks. Also, it converts Long values to String.

Seskar supports Duration by default.

Example
value class Count(
    private val value: Int,
)

val Counter = FC {
    val count: Count = useCount()
    
    useEffect(count) {
        println("Count changed: $count")
    }
}
Without plugin
function Counter() { 
    var count = useCount()
    
    useEffect(
        () => {
            println(`Count changed: $count`)
        },
        // AUTOBOXING
        [ new Count(count) ],
    )
}
With plugin
function Counter() { 
    var count = useCount()
    
    useEffect(
        () => {
            println(`Count changed: $count`)
        },
        // NO AUTOBOXING
        [ count ],
    )
}

Unions

AS-IS

Use enum constant as union value

// TypeScript
type Align = 'TOP' | 'LEFT' | 'BOTTOM' | 'RIGHT'
// Kotlin
import seskar.js.JsUnion

@JsUnion
sealed external interface Align {
    companion object {
        val TOP: Align
        val LEFT: Align
        val BOTTOM: Align
        val RIGHT: Align
    }
}

println(Align.TOP)  // 'TOP'
println(Align.LEFT) // 'LEFT'

Custom

Use String or Int constant as union value

String
// TypeScript
type Align = 't' | 'l' | 'b' | 'r'
// Kotlin
import seskar.js.JsUnion
import seskar.js.JsValue

@JsUnion
sealed external interface CustomAlign {
    companion object {
        @JsValue("t")
        val TOP: CustomAlign

        @JsValue("l")
        val LEFT: CustomAlign

        @JsValue("b")
        val BOTTOM: CustomAlign

        @JsValue("r")
        val RIGHT: CustomAlign
    }
}

println(CustomAlign.TOP)  // 't'
println(CustomAlign.LEFT) // 'l'
Int
// TypeScript
type GRAPH_ITEM_TYPE_NODE = 1
type GRAPH_ITEM_TYPE_EDGE = 2
type GRAPH_ITEM_TYPE_PORT = 3
type GRAPH_ITEM_TYPE_LABEL = 4

type GraphItemType = GRAPH_ITEM_TYPE_NODE
    | GRAPH_ITEM_TYPE_EDGE
    | GRAPH_ITEM_TYPE_PORT
    | GRAPH_ITEM_TYPE_LABEL
// Kotlin
import seskar.js.JsIntValue
import seskar.js.JsUnion

@JsUnion
sealed external interface GraphItemType {
    companion object {
        @JsIntValue(1)
        val NODE: GraphItemType

        @JsIntValue(2)
        val EDGE: GraphItemType

        @JsIntValue(4)
        val PORT: GraphItemType

        @JsIntValue(8)
        val LABEL: GraphItemType
    }
}

println(GraphItemType.EDGE) // 2
println(GraphItemType.PORT) // 4

Releases

No releases published

Packages

No packages published

Languages

  • Kotlin 99.5%
  • Shell 0.5%