seskar
4.60.0indexedEnhances JavaScript projects by adding useful Gradle plugin functionalities, including automatic key generation for React components, preventing autoboxing in hooks, and supporting various union type representations.
Enhances JavaScript projects by adding useful Gradle plugin functionalities, including automatic key generation for React components, preventing autoboxing in hooks, and supporting various union type representations.
Seskar is a Gradle plugin that provides useful additions for Kotlin/JS projects.
To add Seskar to your project, you need to add the following configuration to your project's build.gradle.kts:
plugins {
kotlin("multiplatform") version "2.4.0"
id("io.github.turansky.seskar") version "4.60.0"
}
Puts a function in a separate JS chunk. The chunk will be loaded when the function is called the first time.
Add the dependency kotlin-js of Kotlin Wrappers to your project.
// root settings.gradle.kts
dependencyResolutionManagement {
repositories {
mavenCentral()
}
versionCatalogs {
create("kotlinWrappers") {
val wrappersVersion = "%LAST_WRAPPERS_VERSION%"
from("org.jetbrains.kotlin-wrappers:kotlin-wrappers-catalog:$wrappersVersion")
}
}
}
// build.gradle.kts
sourceSets {
jsMain {
dependencies {
implementation(kotlinWrappers.js)
}
}
}
// App.kt
suspend fun main() {
console.log("App start!")
val value = if (Random.nextDouble() > 0.5) {
createCalculationWithHeavyLibrary()
} else {
42
}
console.log("Value: $value")
}
// createCalculationWithHeavyLibrary.kt
import js.lazy.Lazy
import js.lazy.LazyFunction
/**
* Function will be located in separate JS chunk
*/
@Lazy
val createCalculationWithHeavyLibrary = LazyFunction<Int> {
val calculator = HeavyCalculator()
calculator.calculate()
}
// Content.kt
@Lazy
val Content = FC {
MyHeavyComponent1()
MyHeavyComponent2()
}
// App.kt
val App = FC {
Header()
Suspense {
Content()
}
Footer()
}
Seskar generates keys for child elements to prevent problems with conditional rendering.
As a result, in the following example Content child state won't be reset after showHeader property change.
val App = FC {
val showHeader = useShowHeader()
if (showHeader)
Header() // generated: key = "@rdk/5"
Content() // generated: key = "@rdk/7"
Footer() // generated: key = "@rdk/8"
}
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.
It also converts Long values to String.
Seskar supports Duration by default.
value class Count(
private val value: Int,
)
val Counter = FC {
val count: Count = useCount()
useEffect(count) {
println("Count changed: $count")
}
}
function Counter() {
var count = useCount()
useEffect(
() => {
println(`Count changed: $count`)
},
// AUTOBOXING
[new Count(count)],
)
}
function Counter() {
var count = useCount()
useEffect(
() => {
println(`Count changed: $count`)
},
// NO AUTOBOXING
[count],
)
}
Use enum constant as union value
// TypeScript
type Align = 'TOP' | 'LEFT' | 'BOTTOM' | 'RIGHT'
// Kotlin
sealed external interface Align {
companion object {
@JsValue("TOP")
val TOP: Align
LEFT: Align
BOTTOM: Align
RIGHT: Align
}
}
println(Align.TOP)
println(Align.LEFT)
// TypeScript
type LayoutOrientation = 'top-to-bottom'
| 'left-to-right'
| 'bottom-to-top'
| 'right-to-left'
// Kotlin
import seskar.js.JsValue
sealed external interface LayoutOrientation {
companion object {
@JsValue()
TOP_TO_BOTTOM: LayoutOrientation
LEFT_TO_RIGHT: LayoutOrientation
bottomToTop: LayoutOrientation
rightToLeft: LayoutOrientation
}
}
// TypeScript
type LayoutOrientation = 'top_to_bottom'
| 'left_to_right'
| 'bottom_to_top'
| 'right_to_left'
// Kotlin
import seskar.js.Case
sealed external interface LayoutOrientation {
companion object {
@JsValue()
TOP_TO_BOTTOM: LayoutOrientation
LEFT_TO_RIGHT: LayoutOrientation
bottomToTop: LayoutOrientation
rightToLeft: LayoutOrientation
}
}
Use String or Int constant as union value
String// TypeScript
type Align = 't' | 'l' | 'b' | 'r'
// Kotlin
import seskar.js.JsValue
sealed external interface CustomAlign {
companion {
TOP: CustomAlign
LEFT: CustomAlign
BOTTOM: CustomAlign
RIGHT: CustomAlign
}
}
println(CustomAlign.TOP)
println(CustomAlign.LEFT)
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 =
=
|
|
|
// Kotlin
import seskar.js.JsRawValue
""
sealed external {
{
NODE: GraphItemType
EDGE: GraphItemType
PORT: GraphItemType
LABEL: GraphItemType
}
}
println(GraphItemType.EDGE)
println(GraphItemType.PORT)
Surfaced from shared tags and platforms — no rankings paid for.