{"id":15647374,"url":"https://github.com/turansky/seskar","last_synced_at":"2025-04-09T17:25:00.937Z","repository":{"id":37253061,"uuid":"279976108","full_name":"turansky/seskar","owner":"turansky","description":"Kotlin/JS sugar","archived":false,"fork":false,"pushed_at":"2024-10-29T12:07:55.000Z","size":2221,"stargazers_count":53,"open_issues_count":0,"forks_count":7,"subscribers_count":4,"default_branch":"master","last_synced_at":"2024-10-29T14:35:59.122Z","etag":null,"topics":["dataclass","equals","gradle-plugin","hashcode","kotlin"],"latest_commit_sha":null,"homepage":"","language":"Kotlin","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/turansky.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-07-15T20:53:03.000Z","updated_at":"2024-10-29T12:07:58.000Z","dependencies_parsed_at":"2023-10-12T06:11:24.506Z","dependency_job_id":"103befaa-6814-4670-bc7a-537cd2d70272","html_url":"https://github.com/turansky/seskar","commit_stats":{"total_commits":503,"total_committers":3,"mean_commits":"167.66666666666666","dds":0.005964214711729587,"last_synced_commit":"9168c5253a3c38f2e679d7b69e1b783bdaba9488"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/turansky%2Fseskar","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/turansky%2Fseskar/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/turansky%2Fseskar/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/turansky%2Fseskar/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/turansky","download_url":"https://codeload.github.com/turansky/seskar/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248075757,"owners_count":21043645,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dataclass","equals","gradle-plugin","hashcode","kotlin"],"created_at":"2024-10-03T12:19:01.825Z","updated_at":"2025-04-09T17:25:00.900Z","avatar_url":"https://github.com/turansky.png","language":"Kotlin","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![CI Status](https://github.com/turansky/seskar/workflows/CI/badge.svg)](https://github.com/turansky/seskar/actions)\n[![CI Status](https://github.com/turansky/seskar/workflows/gradle%20plugin/badge.svg)](https://github.com/turansky/seskar/actions)\n[![Gradle Plugin Portal](https://img.shields.io/gradle-plugin-portal/v/io.github.turansky.seskar?logo=gradle)](https://plugins.gradle.org/plugin/io.github.turansky.seskar)\n[![Maven Central](https://img.shields.io/maven-central/v/io.github.turansky.seskar/seskar-core?logo=apache-maven)](https://search.maven.org/artifact/io.github.turansky.seskar/seskar-core)\n[![Kotlin](https://img.shields.io/badge/kotlin-2.1.20-blue.svg?logo=kotlin)](http://kotlinlang.org)\n\n# Seskar\n\nSeskar is a Gradle plugin that provides useful additions for Kotlin/JS projects.\n\n## Setup\n\nTo add Seskar to your project, you need to add the following configuration to your project's `build.gradle.kts`:\n\n```kotlin\nplugins {\n  kotlin(\"multiplatform\") version \"2.1.20\"\n  id(\"io.github.turansky.seskar\") version \"4.7.0\"\n}\n```\n\n### Kotlin/JS requirements\n\n* Target -`es2015`\n  * [example](https://github.com/JetBrains/kotlin-wrappers/blob/fdc8fb9b8ac2b13ba151449e13977a0327e7e3df/examples/buildSrc/src/main/kotlin/kotlin-conventions.gradle.kts#L35)\n* Granularity - `per-file`\n  * [example](https://github.com/JetBrains/kotlin-wrappers/blob/fdc8fb9b8ac2b13ba151449e13977a0327e7e3df/examples/gradle.properties#L7)\n\n## Lazy functions\n\n```kotlin\n// App.kt\nsuspend fun main() {\n    console.log(\"App start!\")\n  \n    val value = if (Random.nextDouble() \u003e 0.5) {\n        createCalculationWithHeavyLibrary()\n    } else {\n        42\n    }\n\n    console.log(\"Value: $value\")\n}\n\n// createCalculationWithHeavyLibrary.kt\n/**\n * - Function will be located in separate JS chunk\n * - Chunk will be loaded when function will be called first time\n */ \n@Lazy\nval createCalculationWithHeavyLibrary = LazyFunction\u003cInt\u003e {\n    val calculator = HeavyCalculator()\n    calculator.calculate()\n}\n```\n\n## React\n\n#### Lazy components\n\n```kotlin\n// Content.kt\n@Lazy\nval Content = FC {\n    MyHeavyComponent1()\n    MyHeavyComponent2()\n}\n\n// App.kt\nval App = FC {\n    Header()\n\n    Suspense {\n        Content()\n    }\n\n    Footer()\n}\n```\n\n##### Examples\n\n2. [Lazy app](/tests/react-lazy-components/app)\n2. [Kotlin Wrappers Example](https://github.com/JetBrains/kotlin-wrappers/tree/master/examples/react-lazy-modules-webpack)\n\n#### Conditional rendering\n\nSeskar generates keys for child elements to prevent problems with conditional rendering.\nAs a result, in the following example `Content` child state won't be reset after `showHeader` property change.\n\n```kotlin\nval App = FC {\n    val showHeader = useShowHeader()\n\n    if (showHeader)\n        Header() // generated: key = \"@rdk/5\"\n\n    Content()    // generated: key = \"@rdk/7\"\n    Footer()     // generated: key = \"@rdk/8\"\n}\n```\n\n#### Dependencies\n\nWhen a project uses the Kotlin/JS compiler, `value classes` are autoboxed. If a `value class` is used as a dependency\nof a React hook (e.g., in `useMemo`, `useState` or `useEffect`), a new class will be created on every rendering pass,\nwhich causes infinite re-rendering.\n\nTo prevent this, Seskar disables autoboxing for `value class` dependencies in hooks.\nIt also converts `Long` values to `String`.\n\nSeskar supports `Duration` by default.\n\n##### Example\n\n```kotlin\nvalue class Count(\n    private val value: Int,\n)\n\nval Counter = FC {\n    val count: Count = useCount()\n\n    useEffect(count) {\n        println(\"Count changed: $count\")\n    }\n}\n```\n\n##### Without plugin\n\n```javascript\nfunction Counter() {\n    var count = useCount()\n\n    useEffect(\n        () =\u003e {\n            println(`Count changed: $count`)\n        },\n        // AUTOBOXING\n        [new Count(count)],\n    )\n}\n```\n\n##### With plugin\n\n```javascript\nfunction Counter() {\n    var count = useCount()\n\n    useEffect(\n        () =\u003e {\n            println(`Count changed: $count`)\n        },\n        // NO AUTOBOXING\n        [count],\n    )\n}\n```\n\n## Unions\n\n#### AS-IS\n\nUse enum constant as union value\n\n```typescript\n// TypeScript\ntype Align = 'TOP' | 'LEFT' | 'BOTTOM' | 'RIGHT'\n```\n\n```kotlin\n// Kotlin\n\nsealed external interface Align {\n    companion object {\n        @JsValue(\"TOP\")\n        val TOP: Align\n\n        @JsValue(\"LEFT\")\n        val LEFT: Align\n\n        @JsValue(\"BOTTOM\")\n        val BOTTOM: Align\n\n        @JsValue(\"RIGHT\")\n        val RIGHT: Align\n    }\n}\n\nprintln(Align.TOP)  // 'TOP'\nprintln(Align.LEFT) // 'LEFT'\n```\n\n#### Kebab case\n\n```typescript\n// TypeScript\ntype LayoutOrientation = 'top-to-bottom'\n    | 'left-to-right'\n    | 'bottom-to-top'\n    | 'right-to-left'\n```\n\n```kotlin\n// Kotlin\nimport seskar.js.JsValue\n\nsealed external interface LayoutOrientation {\n    companion object {\n        @JsValue(\"top-to-bottom\")\n        val TOP_TO_BOTTOM: LayoutOrientation\n\n        @JsValue(\"left-to-right\")\n        val LEFT_TO_RIGHT: LayoutOrientation\n\n        @JsValue(\"bottom-to-top\")\n        val bottomToTop: LayoutOrientation\n\n        @JsValue(\"right-to-left\")\n        val rightToLeft: LayoutOrientation\n    }\n}\n```\n\n#### Snake case\n\n```typescript\n// TypeScript\ntype LayoutOrientation = 'top_to_bottom'\n    | 'left_to_right'\n    | 'bottom_to_top'\n    | 'right_to_left'\n```\n\n```kotlin\n// Kotlin\nimport seskar.js.Case\n\nsealed external interface LayoutOrientation {\n    companion object {\n        @JsValue(\"top_to_bottom\")\n        val TOP_TO_BOTTOM: LayoutOrientation\n\n        @JsValue(\"left_to_right\")\n        val LEFT_TO_RIGHT: LayoutOrientation\n\n        @JsValue(\"bottom_to_top\")\n        val bottomToTop: LayoutOrientation\n\n        @JsValue(\"right_to_left\")\n        val rightToLeft: LayoutOrientation\n    }\n}\n```\n\n#### Custom\n\nUse `String` or `Int` constant as union value\n\n##### `String`\n\n```typescript\n// TypeScript\ntype Align = 't' | 'l' | 'b' | 'r'\n```\n\n```kotlin\n// Kotlin\nimport seskar.js.JsValue\n\nsealed external interface CustomAlign {\n    companion object {\n        @JsValue(\"t\")\n        val TOP: CustomAlign\n\n        @JsValue(\"l\")\n        val LEFT: CustomAlign\n\n        @JsValue(\"b\")\n        val BOTTOM: CustomAlign\n\n        @JsValue(\"r\")\n        val RIGHT: CustomAlign\n    }\n}\n\nprintln(CustomAlign.TOP)  // 't'\nprintln(CustomAlign.LEFT) // 'l'\n```\n\n##### `Int`\n\n```typescript\n// TypeScript\ntype GRAPH_ITEM_TYPE_NODE = 1\ntype GRAPH_ITEM_TYPE_EDGE = 2\ntype GRAPH_ITEM_TYPE_PORT = 3\ntype GRAPH_ITEM_TYPE_LABEL = 4\n\ntype GraphItemType = GRAPH_ITEM_TYPE_NODE\n    | GRAPH_ITEM_TYPE_EDGE\n    | GRAPH_ITEM_TYPE_PORT\n    | GRAPH_ITEM_TYPE_LABEL\n```\n\n```kotlin\n// Kotlin\nimport seskar.js.JsRawValue\n\n\"\"\nsealed external interface GraphItemType {\n    companion object {\n      @JsRawValue(\"1\")\n        val NODE: GraphItemType\n\n      @JsRawValue(\"2\")\n        val EDGE: GraphItemType\n\n      @JsRawValue(\"4\")\n        val PORT: GraphItemType\n\n      @JsRawValue(\"8\")\n        val LABEL: GraphItemType\n    }\n}\n\nprintln(GraphItemType.EDGE) // 2\nprintln(GraphItemType.PORT) // 4\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fturansky%2Fseskar","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fturansky%2Fseskar","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fturansky%2Fseskar/lists"}