From 54576d8decb6d49ee8f7e3b2d91e37288a413106 Mon Sep 17 00:00:00 2001 From: nullsanya <000sanya.000sanya@gmail.com> Date: Thu, 3 Apr 2025 21:22:55 +1000 Subject: [PATCH 01/12] implement support of wasm/js for browser --- common/build.gradle | 7 + .../micro_utils/common/ByteArrayDataView.kt | 15 +++ .../micro_utils/common/CopyToClipboard.kt | 13 ++ .../common/CopyToClipboardImage.kt | 31 +++++ .../common/HTMLElementDomChanged.kt | 63 +++++++++ .../micro_utils/common/HtmlElementOnScreen.kt | 43 ++++++ .../common/IntersectionObserver.kt | 127 ++++++++++++++++++ .../dev/inmo/micro_utils/common/IsOverflow.kt | 12 ++ .../dev/inmo/micro_utils/common/JSMPPFile.kt | 58 ++++++++ .../inmo/micro_utils/common/OnClickOutside.kt | 41 ++++++ .../dev/inmo/micro_utils/common/OpenLink.kt | 8 ++ .../inmo/micro_utils/common/ResizeObserver.kt | 56 ++++++++ .../dev/inmo/micro_utils/common/SelectFile.kt | 30 +++++ .../micro_utils/common/TriggerDownload.kt | 14 ++ .../dev/inmo/micro_utils/common/Visibility.kt | 48 +++++++ .../dev/inmo/micro_utils/common/toFixed.kt | 11 ++ gradle/libs.versions.toml | 3 + ...sAndroidLinuxMingwLinuxArm64Project.gradle | 9 ++ ...sAndroidLinuxMingwLinuxArm64Project.gradle | 9 ++ .../mppJvmJsLinuxMingwProject.gradle | 9 ++ ...pProjectWithSerializationAndCompose.gradle | 9 ++ .../ktor/client/ActualTemporalUpload.kt | 63 +++++++++ .../ktor/client/ActualUniUpload.kt | 97 +++++++++++++ ktor/common/build.gradle | 6 + .../ktor/common/ActualMPPFileInput.kt | 7 + repos/common/tests/build.gradle | 4 + startup/plugin/build.gradle | 5 + .../kotlin/StartPluginSerializer.wasm.kt | 13 ++ 28 files changed, 811 insertions(+) create mode 100644 common/src/wasmJsMain/kotlin/dev/inmo/micro_utils/common/ByteArrayDataView.kt create mode 100644 common/src/wasmJsMain/kotlin/dev/inmo/micro_utils/common/CopyToClipboard.kt create mode 100644 common/src/wasmJsMain/kotlin/dev/inmo/micro_utils/common/CopyToClipboardImage.kt create mode 100644 common/src/wasmJsMain/kotlin/dev/inmo/micro_utils/common/HTMLElementDomChanged.kt create mode 100644 common/src/wasmJsMain/kotlin/dev/inmo/micro_utils/common/HtmlElementOnScreen.kt create mode 100644 common/src/wasmJsMain/kotlin/dev/inmo/micro_utils/common/IntersectionObserver.kt create mode 100644 common/src/wasmJsMain/kotlin/dev/inmo/micro_utils/common/IsOverflow.kt create mode 100644 common/src/wasmJsMain/kotlin/dev/inmo/micro_utils/common/JSMPPFile.kt create mode 100644 common/src/wasmJsMain/kotlin/dev/inmo/micro_utils/common/OnClickOutside.kt create mode 100644 common/src/wasmJsMain/kotlin/dev/inmo/micro_utils/common/OpenLink.kt create mode 100644 common/src/wasmJsMain/kotlin/dev/inmo/micro_utils/common/ResizeObserver.kt create mode 100644 common/src/wasmJsMain/kotlin/dev/inmo/micro_utils/common/SelectFile.kt create mode 100644 common/src/wasmJsMain/kotlin/dev/inmo/micro_utils/common/TriggerDownload.kt create mode 100644 common/src/wasmJsMain/kotlin/dev/inmo/micro_utils/common/Visibility.kt create mode 100644 common/src/wasmJsMain/kotlin/dev/inmo/micro_utils/common/toFixed.kt create mode 100644 ktor/client/src/wasmJsMain/kotlin/dev/inmo/micro_utils/ktor/client/ActualTemporalUpload.kt create mode 100644 ktor/client/src/wasmJsMain/kotlin/dev/inmo/micro_utils/ktor/client/ActualUniUpload.kt create mode 100644 ktor/common/src/wasmJsMain/kotlin/dev/inmo/micro_utils/ktor/common/ActualMPPFileInput.kt create mode 100644 startup/plugin/src/wasmJsMain/kotlin/StartPluginSerializer.wasm.kt diff --git a/common/build.gradle b/common/build.gradle index eea0236fc9a..942a4c89148 100644 --- a/common/build.gradle +++ b/common/build.gradle @@ -29,5 +29,12 @@ kotlin { api libs.okio } } + + wasmJsMain { + dependencies { + api libs.kotlinx.browser + implementation libs.kt.coroutines + } + } } } diff --git a/common/src/wasmJsMain/kotlin/dev/inmo/micro_utils/common/ByteArrayDataView.kt b/common/src/wasmJsMain/kotlin/dev/inmo/micro_utils/common/ByteArrayDataView.kt new file mode 100644 index 00000000000..6a151cc8947 --- /dev/null +++ b/common/src/wasmJsMain/kotlin/dev/inmo/micro_utils/common/ByteArrayDataView.kt @@ -0,0 +1,15 @@ +package dev.inmo.micro_utils.common + +import org.khronos.webgl.* + +fun DataView.toByteArray() = ByteArray(this.byteLength) { + getInt8(it) +} + +fun ArrayBuffer.toByteArray() = Int8Array(this).toByteArray() + +fun ByteArray.toDataView() = DataView(ArrayBuffer(size)).also { + forEachIndexed { i, byte -> it.setInt8(i, byte) } +} + +fun ByteArray.toArrayBuffer() = toDataView().buffer diff --git a/common/src/wasmJsMain/kotlin/dev/inmo/micro_utils/common/CopyToClipboard.kt b/common/src/wasmJsMain/kotlin/dev/inmo/micro_utils/common/CopyToClipboard.kt new file mode 100644 index 00000000000..3f1d30ed0f6 --- /dev/null +++ b/common/src/wasmJsMain/kotlin/dev/inmo/micro_utils/common/CopyToClipboard.kt @@ -0,0 +1,13 @@ +package dev.inmo.micro_utils.common + +import kotlinx.browser.window + +fun copyToClipboard(text: String): Boolean { + return runCatching { + window.navigator.clipboard.writeText( + text + ) + }.onFailure { + it.printStackTrace() + }.isSuccess +} diff --git a/common/src/wasmJsMain/kotlin/dev/inmo/micro_utils/common/CopyToClipboardImage.kt b/common/src/wasmJsMain/kotlin/dev/inmo/micro_utils/common/CopyToClipboardImage.kt new file mode 100644 index 00000000000..f5b6f2a4287 --- /dev/null +++ b/common/src/wasmJsMain/kotlin/dev/inmo/micro_utils/common/CopyToClipboardImage.kt @@ -0,0 +1,31 @@ +package dev.inmo.micro_utils.common + +import kotlinx.browser.window +import kotlinx.coroutines.await +import org.w3c.fetch.Response +import org.w3c.files.Blob +import org.w3c.files.BlobPropertyBag + +external class ClipboardItem(data: JsAny?) : JsAny + +fun createBlobData(blob: Blob): JsAny = js("""({[blob.type]: blob})""") + +inline fun Blob.convertToClipboardItem(): ClipboardItem { + return ClipboardItem(createBlobData(this)) +} + +suspend fun copyImageURLToClipboard(imageUrl: String): Boolean { + return runCatching { + val response = window.fetch(imageUrl).await() + val blob = response.blob().await() + val data = arrayOf( + Blob( + arrayOf(blob).toJsArray().unsafeCast(), + BlobPropertyBag("image/png") + ).convertToClipboardItem() + ).toJsArray() + window.navigator.clipboard.write(data.unsafeCast()) + }.onFailure { + it.printStackTrace() + }.isSuccess +} diff --git a/common/src/wasmJsMain/kotlin/dev/inmo/micro_utils/common/HTMLElementDomChanged.kt b/common/src/wasmJsMain/kotlin/dev/inmo/micro_utils/common/HTMLElementDomChanged.kt new file mode 100644 index 00000000000..08ed4dcc4cc --- /dev/null +++ b/common/src/wasmJsMain/kotlin/dev/inmo/micro_utils/common/HTMLElementDomChanged.kt @@ -0,0 +1,63 @@ +package dev.inmo.micro_utils.common + +import kotlinx.browser.document +import org.w3c.dom.* + +private fun createMutationObserverInit(childList: Boolean, subtree: Boolean): JsAny = js("({childList, subtree})") + +fun Node.onRemoved(block: () -> Unit): MutationObserver { + lateinit var observer: MutationObserver + + observer = MutationObserver { _, _ -> + fun checkIfRemoved(node: Node): Boolean { + return node.parentNode != document && (node.parentNode ?.let { checkIfRemoved(it) } ?: true) + } + + if (checkIfRemoved(this)) { + observer.disconnect() + block() + } + } + + observer.observe(document, createMutationObserverInit(childList = true, subtree = true).unsafeCast()) + return observer +} + +fun Element.onVisibilityChanged(block: IntersectionObserverEntry.(Float, IntersectionObserver) -> Unit): IntersectionObserver { + var previousIntersectionRatio = -1f + val observer = IntersectionObserver { entries, observer -> + entries.toArray().forEach { + if (previousIntersectionRatio.toDouble() != it.intersectionRatio.toDouble()) { + previousIntersectionRatio = it.intersectionRatio.toDouble().toFloat() + it.block(previousIntersectionRatio, observer) + } + } + } + + observer.observe(this) + return observer +} + +fun Element.onVisible(block: Element.(IntersectionObserver) -> Unit) { + var previous = -1f + onVisibilityChanged { intersectionRatio, observer -> + if (previous != intersectionRatio) { + if (intersectionRatio > 0 && previous == 0f) { + block(observer) + } + previous = intersectionRatio + } + } +} + +fun Element.onInvisible(block: Element.(IntersectionObserver) -> Unit): IntersectionObserver { + var previous = -1f + return onVisibilityChanged { intersectionRatio, observer -> + if (previous != intersectionRatio) { + if (intersectionRatio == 0f && previous != 0f) { + block(observer) + } + previous = intersectionRatio + } + } +} diff --git a/common/src/wasmJsMain/kotlin/dev/inmo/micro_utils/common/HtmlElementOnScreen.kt b/common/src/wasmJsMain/kotlin/dev/inmo/micro_utils/common/HtmlElementOnScreen.kt new file mode 100644 index 00000000000..93a27d3812f --- /dev/null +++ b/common/src/wasmJsMain/kotlin/dev/inmo/micro_utils/common/HtmlElementOnScreen.kt @@ -0,0 +1,43 @@ +package dev.inmo.micro_utils.common + +import kotlinx.browser.window +import org.w3c.dom.DOMRect +import org.w3c.dom.Element + +val DOMRect.isOnScreenByLeftEdge: Boolean + get() = left >= 0 && left <= window.innerWidth +inline val Element.isOnScreenByLeftEdge + get() = getBoundingClientRect().isOnScreenByLeftEdge + +val DOMRect.isOnScreenByRightEdge: Boolean + get() = right >= 0 && right <= window.innerWidth +inline val Element.isOnScreenByRightEdge + get() = getBoundingClientRect().isOnScreenByRightEdge + +internal val DOMRect.isOnScreenHorizontally: Boolean + get() = isOnScreenByLeftEdge || isOnScreenByRightEdge + + +val DOMRect.isOnScreenByTopEdge: Boolean + get() = top >= 0 && top <= window.innerHeight +inline val Element.isOnScreenByTopEdge + get() = getBoundingClientRect().isOnScreenByTopEdge + +val DOMRect.isOnScreenByBottomEdge: Boolean + get() = bottom >= 0 && bottom <= window.innerHeight +inline val Element.isOnScreenByBottomEdge + get() = getBoundingClientRect().isOnScreenByBottomEdge + +internal val DOMRect.isOnScreenVertically: Boolean + get() = isOnScreenByLeftEdge || isOnScreenByRightEdge + + +val DOMRect.isOnScreenFully: Boolean + get() = isOnScreenByLeftEdge && isOnScreenByTopEdge && isOnScreenByRightEdge && isOnScreenByBottomEdge +val Element.isOnScreenFully: Boolean + get() = getBoundingClientRect().isOnScreenFully + +val DOMRect.isOnScreen: Boolean + get() = isOnScreenFully || (isOnScreenHorizontally && isOnScreenVertically) +inline val Element.isOnScreen: Boolean + get() = getBoundingClientRect().isOnScreen diff --git a/common/src/wasmJsMain/kotlin/dev/inmo/micro_utils/common/IntersectionObserver.kt b/common/src/wasmJsMain/kotlin/dev/inmo/micro_utils/common/IntersectionObserver.kt new file mode 100644 index 00000000000..c4f4d9cbb4e --- /dev/null +++ b/common/src/wasmJsMain/kotlin/dev/inmo/micro_utils/common/IntersectionObserver.kt @@ -0,0 +1,127 @@ +package dev.inmo.micro_utils.common + +import org.w3c.dom.DOMRectReadOnly +import org.w3c.dom.Element + +external interface IntersectionObserverOptions: JsAny { + /** + * An Element or Document object which is an ancestor of the intended target, whose bounding rectangle will be + * considered the viewport. Any part of the target not visible in the visible area of the root is not considered + * visible. + */ + var root: Element? + + /** + * A string which specifies a set of offsets to add to the root's bounding_box when calculating intersections, + * effectively shrinking or growing the root for calculation purposes. The syntax is approximately the same as that + * for the CSS margin property; see The root element and root margin in Intersection Observer API for more + * information on how the margin works and the syntax. The default is "0px 0px 0px 0px". + */ + var rootMargin: String? + + /** + * Either a single number or an array of numbers between 0.0 and 1.0, specifying a ratio of intersection area to + * total bounding box area for the observed target. A value of 0.0 means that even a single visible pixel counts as + * the target being visible. 1.0 means that the entire target element is visible. See Thresholds in Intersection + * Observer API for a more in-depth description of how thresholds are used. The default is a threshold of 0.0. + */ + var threshold: JsArray? +} + +private fun createEmptyJsObject(): JsAny = js("{}") + +fun IntersectionObserverOptions( + block: IntersectionObserverOptions.() -> Unit = {} +): IntersectionObserverOptions = createEmptyJsObject().unsafeCast().apply(block) + +external interface IntersectionObserverEntry: JsAny { + /** + * Returns the bounds rectangle of the target element as a DOMRectReadOnly. The bounds are computed as described in + * the documentation for Element.getBoundingClientRect(). + */ + val boundingClientRect: DOMRectReadOnly + + /** + * Returns the ratio of the intersectionRect to the boundingClientRect. + */ + val intersectionRatio: JsNumber + + /** + * Returns a DOMRectReadOnly representing the target's visible area. + */ + val intersectionRect: DOMRectReadOnly + + /** + * A Boolean value which is true if the target element intersects with the intersection observer's root. If this is + * true, then, the IntersectionObserverEntry describes a transition into a state of intersection; if it's false, + * then you know the transition is from intersecting to not-intersecting. + */ + val isIntersecting: Boolean + + /** + * Returns a DOMRectReadOnly for the intersection observer's root. + */ + val rootBounds: DOMRectReadOnly + + /** + * The Element whose intersection with the root changed. + */ + val target: Element + + /** + * A DOMHighResTimeStamp indicating the time at which the intersection was recorded, relative to the + * IntersectionObserver's time origin. + */ + val time: Double +} + +typealias IntersectionObserverCallback = (entries: JsArray, observer: IntersectionObserver) -> Unit + +/** + * This is just an implementation from [this commentary](https://youtrack.jetbrains.com/issue/KT-43157#focus=Comments-27-4498582.0-0) + * of Kotlin JS issue related to the absence of [Intersection Observer API](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver) + */ +external class IntersectionObserver(callback: IntersectionObserverCallback): JsAny { + constructor(callback: IntersectionObserverCallback, options: IntersectionObserverOptions) + + /** + * The Element or Document whose bounds are used as the bounding box when testing for intersection. If no root value + * was passed to the constructor or its value is null, the top-level document's viewport is used. + */ + val root: Element + + /** + * An offset rectangle applied to the root's bounding box when calculating intersections, effectively shrinking or + * growing the root for calculation purposes. The value returned by this property may not be the same as the one + * specified when calling the constructor as it may be changed to match internal requirements. Each offset can be + * expressed in pixels (px) or as a percentage (%). The default is "0px 0px 0px 0px". + */ + val rootMargin: String + + /** + * A list of thresholds, sorted in increasing numeric order, where each threshold is a ratio of intersection area to + * bounding box area of an observed target. Notifications for a target are generated when any of the thresholds are + * crossed for that target. If no value was passed to the constructor, 0 is used. + */ + val thresholds: JsArray + + /** + * Stops the IntersectionObserver object from observing any target. + */ + fun disconnect() + + /** + * Tells the IntersectionObserver a target element to observe. + */ + fun observe(targetElement: Element) + + /** + * Returns an array of IntersectionObserverEntry objects for all observed targets. + */ + fun takeRecords(): JsArray + + /** + * Tells the IntersectionObserver to stop observing a particular target element. + */ + fun unobserve(targetElement: Element) +} diff --git a/common/src/wasmJsMain/kotlin/dev/inmo/micro_utils/common/IsOverflow.kt b/common/src/wasmJsMain/kotlin/dev/inmo/micro_utils/common/IsOverflow.kt new file mode 100644 index 00000000000..28412eaa0b0 --- /dev/null +++ b/common/src/wasmJsMain/kotlin/dev/inmo/micro_utils/common/IsOverflow.kt @@ -0,0 +1,12 @@ +package dev.inmo.micro_utils.common + +import org.w3c.dom.Element + +inline val Element.isOverflowWidth + get() = scrollWidth > clientWidth + +inline val Element.isOverflowHeight + get() = scrollHeight > clientHeight + +inline val Element.isOverflow + get() = isOverflowHeight || isOverflowWidth diff --git a/common/src/wasmJsMain/kotlin/dev/inmo/micro_utils/common/JSMPPFile.kt b/common/src/wasmJsMain/kotlin/dev/inmo/micro_utils/common/JSMPPFile.kt new file mode 100644 index 00000000000..cd5ad68ae73 --- /dev/null +++ b/common/src/wasmJsMain/kotlin/dev/inmo/micro_utils/common/JSMPPFile.kt @@ -0,0 +1,58 @@ +package dev.inmo.micro_utils.common + +import kotlinx.coroutines.await +import org.khronos.webgl.ArrayBuffer +import org.w3c.dom.ErrorEvent +import org.w3c.files.* +import kotlin.js.Promise + +/** + * @suppress + */ +actual typealias MPPFile = File + +private fun createJsError(message: String): JsAny = js("Error(message)") + +fun MPPFile.readBytesPromise() = Promise { success, failure -> + val reader = FileReader() + reader.onload = { + success((reader.result as ArrayBuffer)) + } + reader.onerror = { + val message = (it as ErrorEvent).message + failure(createJsError(message)) + } + reader.readAsArrayBuffer(this) +} + +fun MPPFile.readBytes(): ByteArray { + val reader = FileReaderSync() + return reader.readAsArrayBuffer(this).toByteArray() +} + +private suspend fun MPPFile.dirtyReadBytes(): ByteArray = readBytesPromise().await().toByteArray() + +/** + * @suppress + */ +actual val MPPFile.filename: FileName + get() = FileName(name) +/** + * @suppress + */ +actual val MPPFile.filesize: Long + get() = jsNumberToBigInt(size).toLong() +/** + * @suppress + */ +@Warning("That is not optimized version of bytes allocator. Use asyncBytesAllocator everywhere you can") +actual val MPPFile.bytesAllocatorSync: ByteArrayAllocator + get() = ::readBytes +/** + * @suppress + */ +@Warning("That is not optimized version of bytes allocator. Use asyncBytesAllocator everywhere you can") +actual val MPPFile.bytesAllocator: SuspendByteArrayAllocator + get() = ::dirtyReadBytes + +private fun jsNumberToBigInt(number: JsNumber): JsBigInt = js("BigInt(number)") diff --git a/common/src/wasmJsMain/kotlin/dev/inmo/micro_utils/common/OnClickOutside.kt b/common/src/wasmJsMain/kotlin/dev/inmo/micro_utils/common/OnClickOutside.kt new file mode 100644 index 00000000000..481f879ff27 --- /dev/null +++ b/common/src/wasmJsMain/kotlin/dev/inmo/micro_utils/common/OnClickOutside.kt @@ -0,0 +1,41 @@ +package dev.inmo.micro_utils.common + +import kotlinx.browser.document +import org.w3c.dom.* +import org.w3c.dom.events.Event +import org.w3c.dom.events.EventListener + +private fun createEventListener(listener: (Event) -> Unit): JsAny = js("({handleEvent: listener})") + +fun Element.onActionOutside(type: String, options: AddEventListenerOptions? = null, callback: (Event) -> Unit): EventListener { + lateinit var observer: MutationObserver + val listener = createEventListener { it: Event -> + val elementsToCheck = mutableListOf(this@onActionOutside) + while (it.target != this@onActionOutside && elementsToCheck.isNotEmpty()) { + val childrenGettingElement = elementsToCheck.removeFirst() + for (i in 0 until childrenGettingElement.childElementCount) { + elementsToCheck.add(childrenGettingElement.children[i] ?: continue) + } + } + if (elementsToCheck.isEmpty()) { + callback(it) + } + }.unsafeCast() + + if (options == null) { + document.addEventListener(type, listener) + } else { + document.addEventListener(type, listener, options) + } + observer = onRemoved { + if (options == null) { + document.removeEventListener(type, listener) + } else { + document.removeEventListener(type, listener, options) + } + observer.disconnect() + } + return listener +} + +fun Element.onClickOutside(options: AddEventListenerOptions? = null, callback: (Event) -> Unit) = onActionOutside("click", options, callback) diff --git a/common/src/wasmJsMain/kotlin/dev/inmo/micro_utils/common/OpenLink.kt b/common/src/wasmJsMain/kotlin/dev/inmo/micro_utils/common/OpenLink.kt new file mode 100644 index 00000000000..24315e95559 --- /dev/null +++ b/common/src/wasmJsMain/kotlin/dev/inmo/micro_utils/common/OpenLink.kt @@ -0,0 +1,8 @@ +package dev.inmo.micro_utils.common + +import kotlinx.browser.window + +fun openLink(link: String, target: String = "_blank", features: String = "") { + window.open(link, target, features) ?.focus() +} + diff --git a/common/src/wasmJsMain/kotlin/dev/inmo/micro_utils/common/ResizeObserver.kt b/common/src/wasmJsMain/kotlin/dev/inmo/micro_utils/common/ResizeObserver.kt new file mode 100644 index 00000000000..ee9d4ab7411 --- /dev/null +++ b/common/src/wasmJsMain/kotlin/dev/inmo/micro_utils/common/ResizeObserver.kt @@ -0,0 +1,56 @@ +package dev.inmo.micro_utils.common + +import org.w3c.dom.* + +external class ResizeObserver( + callback: (JsArray, ResizeObserver) -> Unit +): JsAny { + fun observe(target: Element, options: JsAny = definedExternally) + + fun unobserve(target: Element) + + fun disconnect() +} + +private fun createObserveOptions(jsBox: JsString?): JsAny = js("({box: jsBox})") + +external interface ResizeObserverSize: JsAny { + val blockSize: Float + val inlineSize: Float +} + +external interface ResizeObserverEntry: JsAny { + val borderBoxSize: JsArray + val contentBoxSize: JsArray + val devicePixelContentBoxSize: JsArray + val contentRect: DOMRectReadOnly + val target: Element +} + +fun ResizeObserver.observe(target: Element, options: ResizeObserverObserveOptions) = observe( + target, + createObserveOptions(options.box?.name?.toJsString()) +) + +class ResizeObserverObserveOptions( + val box: Box? = null +) { + sealed interface Box { + val name: String + + object Content : Box { + override val name: String + get() = "content-box" + } + + object Border : Box { + override val name: String + get() = "border-box" + } + + object DevicePixelContent : Box { + override val name: String + get() = "device-pixel-content-box" + } + } +} diff --git a/common/src/wasmJsMain/kotlin/dev/inmo/micro_utils/common/SelectFile.kt b/common/src/wasmJsMain/kotlin/dev/inmo/micro_utils/common/SelectFile.kt new file mode 100644 index 00000000000..c2af68ad32d --- /dev/null +++ b/common/src/wasmJsMain/kotlin/dev/inmo/micro_utils/common/SelectFile.kt @@ -0,0 +1,30 @@ +package dev.inmo.micro_utils.common + +import kotlinx.browser.document +import kotlinx.dom.createElement +import org.w3c.dom.HTMLElement +import org.w3c.dom.HTMLInputElement +import org.w3c.files.get + +fun selectFile( + inputSetup: (HTMLInputElement) -> Unit = {}, + onFailure: (Throwable) -> Unit = {}, + onFile: (MPPFile) -> Unit +) { + (document.createElement("input") { + (this as HTMLInputElement).apply { + type = "file" + onchange = { + runCatching { + files ?.get(0) ?: error("File must not be null") + }.onSuccess { + onFile(it) + }.onFailure { + onFailure(it) + } + } + inputSetup(this) + } + } as HTMLElement).click() +} + diff --git a/common/src/wasmJsMain/kotlin/dev/inmo/micro_utils/common/TriggerDownload.kt b/common/src/wasmJsMain/kotlin/dev/inmo/micro_utils/common/TriggerDownload.kt new file mode 100644 index 00000000000..369050cf196 --- /dev/null +++ b/common/src/wasmJsMain/kotlin/dev/inmo/micro_utils/common/TriggerDownload.kt @@ -0,0 +1,14 @@ +package dev.inmo.micro_utils.common + +import kotlinx.browser.document +import org.w3c.dom.HTMLAnchorElement + +fun triggerDownloadFile(filename: String, fileLink: String) { + val hiddenElement = document.createElement("a") as HTMLAnchorElement + + hiddenElement.href = fileLink + hiddenElement.target = "_blank" + hiddenElement.download = filename + hiddenElement.click() +} + diff --git a/common/src/wasmJsMain/kotlin/dev/inmo/micro_utils/common/Visibility.kt b/common/src/wasmJsMain/kotlin/dev/inmo/micro_utils/common/Visibility.kt new file mode 100644 index 00000000000..991b9843901 --- /dev/null +++ b/common/src/wasmJsMain/kotlin/dev/inmo/micro_utils/common/Visibility.kt @@ -0,0 +1,48 @@ +package dev.inmo.micro_utils.common + +import kotlinx.browser.window +import org.w3c.dom.Element +import org.w3c.dom.css.CSSStyleDeclaration + +sealed class Visibility +data object Visible : Visibility() +data object Invisible : Visibility() +data object Gone : Visibility() + +var CSSStyleDeclaration.visibilityState: Visibility + get() = when { + display == "none" -> Gone + visibility == "hidden" -> Invisible + else -> Visible + } + set(value) { + when (value) { + Visible -> { + if (display == "none") { + display = "initial" + } + visibility = "visible" + } + Invisible -> { + if (display == "none") { + display = "initial" + } + visibility = "hidden" + } + Gone -> { + display = "none" + } + } + } +inline var Element.visibilityState: Visibility + get() = window.getComputedStyle(this).visibilityState + set(value) { + window.getComputedStyle(this).visibilityState = value + } + +inline val Element.isVisible: Boolean + get() = visibilityState == Visible +inline val Element.isInvisible: Boolean + get() = visibilityState == Invisible +inline val Element.isGone: Boolean + get() = visibilityState == Gone diff --git a/common/src/wasmJsMain/kotlin/dev/inmo/micro_utils/common/toFixed.kt b/common/src/wasmJsMain/kotlin/dev/inmo/micro_utils/common/toFixed.kt new file mode 100644 index 00000000000..3de164e37d2 --- /dev/null +++ b/common/src/wasmJsMain/kotlin/dev/inmo/micro_utils/common/toFixed.kt @@ -0,0 +1,11 @@ +package dev.inmo.micro_utils.common + +actual fun Float.fixed(signs: Int): Float { + return jsToFixed(toDouble().toJsNumber(), signs.coerceIn(FixedSignsRange)).toString().toFloat() +} + +actual fun Double.fixed(signs: Int): Double { + return jsToFixed(toJsNumber(), signs.coerceIn(FixedSignsRange)).toString().toDouble() +} + +private fun jsToFixed(number: JsNumber, signs: Int): JsString = js("number.toFixed(signs)") diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 0f32dc7d040..b41c06d18c3 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -4,6 +4,8 @@ kt = "2.1.20" kt-serialization = "1.8.0" kt-coroutines = "1.10.1" +kotlinx-browser = "0.3" + kslog = "1.4.1" jb-compose = "1.7.3" @@ -55,6 +57,7 @@ kt-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-and kt-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "kt-coroutines" } kt-coroutines-debug = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-debug", version.ref = "kt-coroutines" } +kotlinx-browser = { module = "org.jetbrains.kotlinx:kotlinx-browser-wasm-js", version.ref = "kotlinx-browser" } ktor-io = { module = "io.ktor:ktor-io", version.ref = "ktor" } ktor-serialization-kotlinx-json = { module = "io.ktor:ktor-serialization-kotlinx-json", version.ref = "ktor" } diff --git a/gradle/templates/mppComposeJvmJsAndroidLinuxMingwLinuxArm64Project.gradle b/gradle/templates/mppComposeJvmJsAndroidLinuxMingwLinuxArm64Project.gradle index 5e2606e4b50..3d804a813fb 100644 --- a/gradle/templates/mppComposeJvmJsAndroidLinuxMingwLinuxArm64Project.gradle +++ b/gradle/templates/mppComposeJvmJsAndroidLinuxMingwLinuxArm64Project.gradle @@ -27,6 +27,10 @@ kotlin { } } } + wasmJs { + browser() + nodejs() + } androidTarget { publishAllLibraryVariants() compilations.all { @@ -91,6 +95,11 @@ kotlin { implementation kotlin('test-js') } } + wasmJsTest { + dependencies { + implementation kotlin('test-wasm-js') + } + } nativeMain.dependsOn commonMain linuxX64Main.dependsOn nativeMain mingwX64Main.dependsOn nativeMain diff --git a/gradle/templates/mppJvmJsAndroidLinuxMingwLinuxArm64Project.gradle b/gradle/templates/mppJvmJsAndroidLinuxMingwLinuxArm64Project.gradle index 574b69a0107..e626931c55b 100644 --- a/gradle/templates/mppJvmJsAndroidLinuxMingwLinuxArm64Project.gradle +++ b/gradle/templates/mppJvmJsAndroidLinuxMingwLinuxArm64Project.gradle @@ -27,6 +27,10 @@ kotlin { } } } + wasmJs { + browser() + nodejs() + } androidTarget { publishAllLibraryVariants() compilations.all { @@ -71,6 +75,11 @@ kotlin { implementation kotlin('test-js') } } + wasmJsTest { + dependencies { + implementation kotlin('test-wasm-js') + } + } nativeMain.dependsOn commonMain linuxX64Main.dependsOn nativeMain mingwX64Main.dependsOn nativeMain diff --git a/gradle/templates/mppJvmJsLinuxMingwProject.gradle b/gradle/templates/mppJvmJsLinuxMingwProject.gradle index b62c12dc548..1e0ac854f87 100644 --- a/gradle/templates/mppJvmJsLinuxMingwProject.gradle +++ b/gradle/templates/mppJvmJsLinuxMingwProject.gradle @@ -27,6 +27,10 @@ kotlin { } } } + wasmJs { + browser() + nodejs() + } linuxX64() mingwX64() @@ -55,6 +59,11 @@ kotlin { implementation kotlin('test-js') } } + wasmJsTest { + dependencies { + implementation kotlin('test-wasm-js') + } + } nativeMain.dependsOn commonMain linuxX64Main.dependsOn nativeMain diff --git a/gradle/templates/mppProjectWithSerializationAndCompose.gradle b/gradle/templates/mppProjectWithSerializationAndCompose.gradle index 0a5c853bf56..4ee03b48c2a 100644 --- a/gradle/templates/mppProjectWithSerializationAndCompose.gradle +++ b/gradle/templates/mppProjectWithSerializationAndCompose.gradle @@ -27,6 +27,10 @@ kotlin { } } } + wasmJs { + browser() + nodejs() + } androidTarget { publishAllLibraryVariants() compilations.all { @@ -72,6 +76,11 @@ kotlin { implementation kotlin('test-js') } } + wasmJsTest { + dependencies { + implementation kotlin('test-wasm-js') + } + } androidUnitTest { dependencies { implementation kotlin('test-junit') diff --git a/ktor/client/src/wasmJsMain/kotlin/dev/inmo/micro_utils/ktor/client/ActualTemporalUpload.kt b/ktor/client/src/wasmJsMain/kotlin/dev/inmo/micro_utils/ktor/client/ActualTemporalUpload.kt new file mode 100644 index 00000000000..be3d2d7c538 --- /dev/null +++ b/ktor/client/src/wasmJsMain/kotlin/dev/inmo/micro_utils/ktor/client/ActualTemporalUpload.kt @@ -0,0 +1,63 @@ +package dev.inmo.micro_utils.ktor.client + +import dev.inmo.micro_utils.common.MPPFile +import dev.inmo.micro_utils.coroutines.LinkedSupervisorJob +import dev.inmo.micro_utils.coroutines.launchLoggingDropExceptions +import dev.inmo.micro_utils.ktor.common.TemporalFileId +import io.ktor.client.HttpClient +import io.ktor.client.content.* +import kotlinx.coroutines.* +import org.w3c.xhr.* +import org.w3c.xhr.XMLHttpRequest.Companion.DONE + +suspend fun tempUpload( + fullTempUploadDraftPath: String, + file: MPPFile, + onUpload: ProgressListener +): TemporalFileId { + val formData = FormData() + val answer = CompletableDeferred(currentCoroutineContext().job) + val subscope = CoroutineScope(currentCoroutineContext().LinkedSupervisorJob()) + + formData.append( + "data", + file + ) + + val request = XMLHttpRequest() + request.responseType = XMLHttpRequestResponseType.TEXT + request.upload.onprogress = { + subscope.launchLoggingDropExceptions { onUpload.onProgress(it.loaded.toString().toLong(), it.total.toString().toLong()) } + } + request.onload = { + if (request.status == 200.toShort()) { + answer.complete(TemporalFileId(request.responseText)) + } else { + answer.completeExceptionally(Exception("Something went wrong: $it")) + } + } + request.onerror = { + answer.completeExceptionally(Exception("Something went wrong: $it")) + } + request.open("POST", fullTempUploadDraftPath, true) + request.send(formData) + + answer.invokeOnCompletion { + runCatching { + if (request.readyState != DONE) { + request.abort() + } + } + } + + return answer.await().also { + subscope.cancel() + } +} + + +actual suspend fun HttpClient.tempUpload( + fullTempUploadDraftPath: String, + file: MPPFile, + onUpload: ProgressListener +): TemporalFileId = dev.inmo.micro_utils.ktor.client.tempUpload(fullTempUploadDraftPath, file, onUpload) diff --git a/ktor/client/src/wasmJsMain/kotlin/dev/inmo/micro_utils/ktor/client/ActualUniUpload.kt b/ktor/client/src/wasmJsMain/kotlin/dev/inmo/micro_utils/ktor/client/ActualUniUpload.kt new file mode 100644 index 00000000000..60ac8ca1b9e --- /dev/null +++ b/ktor/client/src/wasmJsMain/kotlin/dev/inmo/micro_utils/ktor/client/ActualUniUpload.kt @@ -0,0 +1,97 @@ +package dev.inmo.micro_utils.ktor.client + +import dev.inmo.micro_utils.common.MPPFile +import dev.inmo.micro_utils.coroutines.LinkedSupervisorJob +import dev.inmo.micro_utils.coroutines.launchLoggingDropExceptions +import io.ktor.client.HttpClient +import io.ktor.client.content.* +import io.ktor.http.Headers +import kotlinx.coroutines.CompletableDeferred +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.cancel +import kotlinx.coroutines.currentCoroutineContext +import kotlinx.coroutines.job +import kotlinx.io.readByteArray +import kotlinx.serialization.DeserializationStrategy +import kotlinx.serialization.StringFormat +import kotlinx.serialization.encodeToString +import org.khronos.webgl.toInt8Array +import org.w3c.files.Blob +import org.w3c.xhr.FormData +import org.w3c.xhr.TEXT +import org.w3c.xhr.XMLHttpRequest +import org.w3c.xhr.XMLHttpRequestResponseType + +/** + * Will execute submitting of multipart data request + * + * @param data [Map] where keys will be used as names for multipart parts and values as values. If you will pass + * [dev.inmo.micro_utils.common.MPPFile] (File from JS or JVM platform). Also you may pass [UniUploadFileInfo] as value + * in case you wish to pass other source of multipart binary data than regular file + * @suppress + */ +actual suspend fun HttpClient.uniUpload( + url: String, + data: Map, + resultDeserializer: DeserializationStrategy, + headers: Headers, + stringFormat: StringFormat, + onUpload: ProgressListener +): T? { + val formData = FormData() + val answer = CompletableDeferred(currentCoroutineContext().job) + val subscope = CoroutineScope(currentCoroutineContext().LinkedSupervisorJob()) + + data.forEach { (k, v) -> + when (v) { + is MPPFile -> formData.append( + k, + v + ) + is UniUploadFileInfo -> formData.append( + k, + Blob(arrayOf(v.inputAllocator().readByteArray().toInt8Array()).toJsArray().unsafeCast()), + v.fileName.name + ) + else -> formData.append( + k, + stringFormat.encodeToString(v) + ) + } + } + + val request = XMLHttpRequest() + headers.forEach { s, strings -> + request.setRequestHeader(s, strings.joinToString()) + } + request.responseType = XMLHttpRequestResponseType.TEXT + request.upload.onprogress = { + subscope.launchLoggingDropExceptions { onUpload.onProgress(it.loaded.toString().toLong(), it.total.toString().toLong()) } + } + request.onload = { + if (request.status == 200.toShort()) { + answer.complete( + stringFormat.decodeFromString(resultDeserializer, request.responseText) + ) + } else { + answer.completeExceptionally(Exception("Something went wrong: $it")) + } + } + request.onerror = { + answer.completeExceptionally(Exception("Something went wrong: $it")) + } + request.open("POST", url, true) + request.send(formData) + + answer.invokeOnCompletion { + runCatching { + if (request.readyState != XMLHttpRequest.DONE) { + request.abort() + } + } + } + + return answer.await().also { + subscope.cancel() + } +} diff --git a/ktor/common/build.gradle b/ktor/common/build.gradle index 2e044e41e55..be8b564ad28 100644 --- a/ktor/common/build.gradle +++ b/ktor/common/build.gradle @@ -16,5 +16,11 @@ kotlin { api libs.ktor.io } } + + wasmJsMain { + dependencies { + + } + } } } diff --git a/ktor/common/src/wasmJsMain/kotlin/dev/inmo/micro_utils/ktor/common/ActualMPPFileInput.kt b/ktor/common/src/wasmJsMain/kotlin/dev/inmo/micro_utils/ktor/common/ActualMPPFileInput.kt new file mode 100644 index 00000000000..8144362a2d9 --- /dev/null +++ b/ktor/common/src/wasmJsMain/kotlin/dev/inmo/micro_utils/ktor/common/ActualMPPFileInput.kt @@ -0,0 +1,7 @@ +package dev.inmo.micro_utils.ktor.common + +import dev.inmo.micro_utils.common.* +import io.ktor.utils.io.core.ByteReadPacket +import io.ktor.utils.io.core.Input + +actual fun MPPFile.input(): Input = ByteReadPacket(readBytes()) diff --git a/repos/common/tests/build.gradle b/repos/common/tests/build.gradle index 22158b48a9f..173e538b797 100644 --- a/repos/common/tests/build.gradle +++ b/repos/common/tests/build.gradle @@ -19,6 +19,10 @@ kotlin { browser() nodejs() } + wasmJs { + browser() + nodejs() + } androidTarget { compilations.all { kotlinOptions { diff --git a/startup/plugin/build.gradle b/startup/plugin/build.gradle index aadfac944d7..239fb6c0dea 100644 --- a/startup/plugin/build.gradle +++ b/startup/plugin/build.gradle @@ -21,6 +21,11 @@ kotlin { api libs.uuid } } + wasmJsMain { + dependencies { + api libs.uuid + } + } linuxX64Main { dependencies { api libs.uuid diff --git a/startup/plugin/src/wasmJsMain/kotlin/StartPluginSerializer.wasm.kt b/startup/plugin/src/wasmJsMain/kotlin/StartPluginSerializer.wasm.kt new file mode 100644 index 00000000000..db3d48e3e5e --- /dev/null +++ b/startup/plugin/src/wasmJsMain/kotlin/StartPluginSerializer.wasm.kt @@ -0,0 +1,13 @@ +package dev.inmo.micro_utils.startup.plugin + +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder + +internal actual fun alternativeDeserialize(decoder: Decoder): StartPlugin? { + return null +} + +internal actual fun alternativeSerialize( + encoder: Encoder, + value: StartPlugin +): Boolean = false \ No newline at end of file From 97bb6d09364333e459b3abaa7af25fc133aa56ad Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Fri, 4 Apr 2025 20:06:44 +0600 Subject: [PATCH 02/12] start 0.25.5 --- CHANGELOG.md | 2 ++ gradle.properties | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e62db63770..7ce523441e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ # Changelog +## 0.25.5 + ## 0.25.4 * `Versions`: diff --git a/gradle.properties b/gradle.properties index 87d42ae962b..1b495d7f670 100644 --- a/gradle.properties +++ b/gradle.properties @@ -15,5 +15,5 @@ crypto_js_version=4.1.1 # Project data group=dev.inmo -version=0.25.4 -android_code_version=294 +version=0.25.5 +android_code_version=295 From 5dff373d5f636815ce43722302a9dc7f8bfc122f Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Fri, 4 Apr 2025 20:29:40 +0600 Subject: [PATCH 03/12] refactor: rename templates and optimize wasmJsMain blocks --- colors/build.gradle | 2 +- colors/common/build.gradle | 2 +- common/build.gradle | 4 ++-- common/compose/build.gradle | 2 +- coroutines/build.gradle | 2 +- coroutines/compose/build.gradle | 2 +- crypto/build.gradle | 2 +- fsm/common/build.gradle | 2 +- fsm/repos/common/build.gradle | 2 +- ...AndroidLinuxMingwLinuxArm64Project.gradle} | 0 ...AndroidLinuxMingwLinuxArm64Project.gradle} | 0 ...=> mppJvmJsWasmJsLinuxMingwProject.gradle} | 0 koin/build.gradle | 2 +- koin/generator/test/build.gradle | 2 +- ksp/classcasts/build.gradle | 2 +- ksp/classcasts/generator/test/build.gradle | 2 +- ksp/sealed/build.gradle | 2 +- ksp/sealed/generator/test/build.gradle | 2 +- ksp/variations/build.gradle | 2 +- ksp/variations/generator/test/build.gradle | 2 +- ktor/client/build.gradle | 2 +- ktor/common/build.gradle | 8 +------ language_codes/build.gradle | 2 +- matrix/build.gradle | 2 +- mime_types/build.gradle | 2 +- pagination/common/build.gradle | 2 +- pagination/compose/build.gradle | 2 +- pagination/ktor/common/build.gradle | 2 +- repos/cache/build.gradle | 2 +- repos/common/build.gradle | 2 +- repos/generator/test/build.gradle | 2 +- repos/inmemory/build.gradle | 2 +- repos/ktor/client/build.gradle | 2 +- repos/ktor/common/build.gradle | 2 +- resources/build.gradle | 2 +- safe_wrapper/build.gradle | 2 +- selector/common/build.gradle | 2 +- serialization/base64/build.gradle | 2 +- serialization/encapsulator/build.gradle | 2 +- serialization/mapper/build.gradle | 2 +- serialization/typed_serializer/build.gradle | 2 +- startup/launcher/build.gradle | 2 +- startup/plugin/build.gradle | 22 +------------------ startup/template/client/build.gradle | 2 +- startup/template/common/build.gradle | 2 +- transactions/build.gradle | 2 +- 46 files changed, 44 insertions(+), 70 deletions(-) rename gradle/templates/{mppComposeJvmJsAndroidLinuxMingwLinuxArm64Project.gradle => mppComposeJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project.gradle} (100%) rename gradle/templates/{mppJvmJsAndroidLinuxMingwLinuxArm64Project.gradle => mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project.gradle} (100%) rename gradle/templates/{mppJvmJsLinuxMingwProject.gradle => mppJvmJsWasmJsLinuxMingwProject.gradle} (100%) diff --git a/colors/build.gradle b/colors/build.gradle index f7c1a7cecb3..edc807db7ea 100644 --- a/colors/build.gradle +++ b/colors/build.gradle @@ -4,7 +4,7 @@ plugins { id "com.android.library" } -apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project" +apply from: "$mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project" kotlin { sourceSets { diff --git a/colors/common/build.gradle b/colors/common/build.gradle index 9e169e84113..a2e3bb55ba4 100644 --- a/colors/common/build.gradle +++ b/colors/common/build.gradle @@ -4,4 +4,4 @@ plugins { id "com.android.library" } -apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project" +apply from: "$mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project" diff --git a/common/build.gradle b/common/build.gradle index 942a4c89148..32056af686b 100644 --- a/common/build.gradle +++ b/common/build.gradle @@ -4,7 +4,7 @@ plugins { id "com.android.library" } -apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project" +apply from: "$mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project" kotlin { sourceSets { @@ -33,7 +33,7 @@ kotlin { wasmJsMain { dependencies { api libs.kotlinx.browser - implementation libs.kt.coroutines + api libs.kt.coroutines } } } diff --git a/common/compose/build.gradle b/common/compose/build.gradle index eb6e9137d55..9a5b52e22b2 100644 --- a/common/compose/build.gradle +++ b/common/compose/build.gradle @@ -6,7 +6,7 @@ plugins { alias(libs.plugins.kt.jb.compose) } -apply from: "$mppComposeJvmJsAndroidLinuxMingwLinuxArm64Project" +apply from: "$mppComposeJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project" kotlin { sourceSets { diff --git a/coroutines/build.gradle b/coroutines/build.gradle index 5b5d02e9f95..a4c6a2a18e6 100644 --- a/coroutines/build.gradle +++ b/coroutines/build.gradle @@ -4,7 +4,7 @@ plugins { id "com.android.library" } -apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project" +apply from: "$mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project" kotlin { sourceSets { diff --git a/coroutines/compose/build.gradle b/coroutines/compose/build.gradle index 7f5f7aa1677..3e93c9f3895 100644 --- a/coroutines/compose/build.gradle +++ b/coroutines/compose/build.gradle @@ -6,7 +6,7 @@ plugins { alias(libs.plugins.kt.jb.compose) } -apply from: "$mppComposeJvmJsAndroidLinuxMingwLinuxArm64Project" +apply from: "$mppComposeJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project" kotlin { sourceSets { diff --git a/crypto/build.gradle b/crypto/build.gradle index 7c018861781..c0ed52a829d 100644 --- a/crypto/build.gradle +++ b/crypto/build.gradle @@ -4,7 +4,7 @@ plugins { id "com.android.library" } -apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project" +apply from: "$mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project" kotlin { sourceSets { diff --git a/fsm/common/build.gradle b/fsm/common/build.gradle index 4aa30d4a72e..483a2b0eaff 100644 --- a/fsm/common/build.gradle +++ b/fsm/common/build.gradle @@ -4,7 +4,7 @@ plugins { id "com.android.library" } -apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project" +apply from: "$mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project" kotlin { sourceSets { diff --git a/fsm/repos/common/build.gradle b/fsm/repos/common/build.gradle index 262cba815e9..022d10790af 100644 --- a/fsm/repos/common/build.gradle +++ b/fsm/repos/common/build.gradle @@ -4,7 +4,7 @@ plugins { id "com.android.library" } -apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project" +apply from: "$mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project" kotlin { sourceSets { diff --git a/gradle/templates/mppComposeJvmJsAndroidLinuxMingwLinuxArm64Project.gradle b/gradle/templates/mppComposeJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project.gradle similarity index 100% rename from gradle/templates/mppComposeJvmJsAndroidLinuxMingwLinuxArm64Project.gradle rename to gradle/templates/mppComposeJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project.gradle diff --git a/gradle/templates/mppJvmJsAndroidLinuxMingwLinuxArm64Project.gradle b/gradle/templates/mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project.gradle similarity index 100% rename from gradle/templates/mppJvmJsAndroidLinuxMingwLinuxArm64Project.gradle rename to gradle/templates/mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project.gradle diff --git a/gradle/templates/mppJvmJsLinuxMingwProject.gradle b/gradle/templates/mppJvmJsWasmJsLinuxMingwProject.gradle similarity index 100% rename from gradle/templates/mppJvmJsLinuxMingwProject.gradle rename to gradle/templates/mppJvmJsWasmJsLinuxMingwProject.gradle diff --git a/koin/build.gradle b/koin/build.gradle index 829d2ea11d7..3631b59151f 100644 --- a/koin/build.gradle +++ b/koin/build.gradle @@ -4,7 +4,7 @@ plugins { id "com.android.library" } -apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project" +apply from: "$mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project" kotlin { sourceSets { diff --git a/koin/generator/test/build.gradle b/koin/generator/test/build.gradle index 1c8f42c9caa..8c9bbc239d5 100644 --- a/koin/generator/test/build.gradle +++ b/koin/generator/test/build.gradle @@ -5,7 +5,7 @@ plugins { id "com.google.devtools.ksp" } -apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project" +apply from: "$mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project" kotlin { diff --git a/ksp/classcasts/build.gradle b/ksp/classcasts/build.gradle index 9e169e84113..a2e3bb55ba4 100644 --- a/ksp/classcasts/build.gradle +++ b/ksp/classcasts/build.gradle @@ -4,4 +4,4 @@ plugins { id "com.android.library" } -apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project" +apply from: "$mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project" diff --git a/ksp/classcasts/generator/test/build.gradle b/ksp/classcasts/generator/test/build.gradle index 4f6203b4006..bbae41dde1b 100644 --- a/ksp/classcasts/generator/test/build.gradle +++ b/ksp/classcasts/generator/test/build.gradle @@ -7,7 +7,7 @@ plugins { ext.do_publish = false -apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project" +apply from: "$mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project" kotlin { diff --git a/ksp/sealed/build.gradle b/ksp/sealed/build.gradle index 9e169e84113..a2e3bb55ba4 100644 --- a/ksp/sealed/build.gradle +++ b/ksp/sealed/build.gradle @@ -4,4 +4,4 @@ plugins { id "com.android.library" } -apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project" +apply from: "$mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project" diff --git a/ksp/sealed/generator/test/build.gradle b/ksp/sealed/generator/test/build.gradle index 24de402973e..ab28fb2bbcc 100644 --- a/ksp/sealed/generator/test/build.gradle +++ b/ksp/sealed/generator/test/build.gradle @@ -7,7 +7,7 @@ plugins { ext.do_publish = false -apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project" +apply from: "$mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project" kotlin { diff --git a/ksp/variations/build.gradle b/ksp/variations/build.gradle index 9e169e84113..a2e3bb55ba4 100644 --- a/ksp/variations/build.gradle +++ b/ksp/variations/build.gradle @@ -4,4 +4,4 @@ plugins { id "com.android.library" } -apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project" +apply from: "$mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project" diff --git a/ksp/variations/generator/test/build.gradle b/ksp/variations/generator/test/build.gradle index b610e624073..00d90fe74a9 100644 --- a/ksp/variations/generator/test/build.gradle +++ b/ksp/variations/generator/test/build.gradle @@ -7,7 +7,7 @@ plugins { ext.do_publish = false -apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project" +apply from: "$mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project" kotlin { diff --git a/ktor/client/build.gradle b/ktor/client/build.gradle index 454da1ff3b2..684e71044e6 100644 --- a/ktor/client/build.gradle +++ b/ktor/client/build.gradle @@ -4,7 +4,7 @@ plugins { id "com.android.library" } -apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project" +apply from: "$mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project" kotlin { sourceSets { diff --git a/ktor/common/build.gradle b/ktor/common/build.gradle index be8b564ad28..e7667860720 100644 --- a/ktor/common/build.gradle +++ b/ktor/common/build.gradle @@ -4,7 +4,7 @@ plugins { id "com.android.library" } -apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project" +apply from: "$mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project" kotlin { sourceSets { @@ -16,11 +16,5 @@ kotlin { api libs.ktor.io } } - - wasmJsMain { - dependencies { - - } - } } } diff --git a/language_codes/build.gradle b/language_codes/build.gradle index 9e169e84113..a2e3bb55ba4 100644 --- a/language_codes/build.gradle +++ b/language_codes/build.gradle @@ -4,4 +4,4 @@ plugins { id "com.android.library" } -apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project" +apply from: "$mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project" diff --git a/matrix/build.gradle b/matrix/build.gradle index 9e169e84113..a2e3bb55ba4 100644 --- a/matrix/build.gradle +++ b/matrix/build.gradle @@ -4,4 +4,4 @@ plugins { id "com.android.library" } -apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project" +apply from: "$mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project" diff --git a/mime_types/build.gradle b/mime_types/build.gradle index 9e169e84113..a2e3bb55ba4 100644 --- a/mime_types/build.gradle +++ b/mime_types/build.gradle @@ -4,4 +4,4 @@ plugins { id "com.android.library" } -apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project" +apply from: "$mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project" diff --git a/pagination/common/build.gradle b/pagination/common/build.gradle index 683dae26159..cbe5931cc81 100644 --- a/pagination/common/build.gradle +++ b/pagination/common/build.gradle @@ -4,7 +4,7 @@ plugins { id "com.android.library" } -apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project" +apply from: "$mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project" kotlin { sourceSets { diff --git a/pagination/compose/build.gradle b/pagination/compose/build.gradle index df1a5bb3725..bb7862242db 100644 --- a/pagination/compose/build.gradle +++ b/pagination/compose/build.gradle @@ -6,7 +6,7 @@ plugins { alias(libs.plugins.kt.jb.compose) } -apply from: "$mppComposeJvmJsAndroidLinuxMingwLinuxArm64Project" +apply from: "$mppComposeJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project" kotlin { sourceSets { diff --git a/pagination/ktor/common/build.gradle b/pagination/ktor/common/build.gradle index d32ba92503d..d19e4186c66 100644 --- a/pagination/ktor/common/build.gradle +++ b/pagination/ktor/common/build.gradle @@ -4,7 +4,7 @@ plugins { id "com.android.library" } -apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project" +apply from: "$mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project" kotlin { sourceSets { diff --git a/repos/cache/build.gradle b/repos/cache/build.gradle index 483fc698661..1608282c11f 100644 --- a/repos/cache/build.gradle +++ b/repos/cache/build.gradle @@ -4,7 +4,7 @@ plugins { id "com.android.library" } -apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project" +apply from: "$mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project" kotlin { sourceSets { diff --git a/repos/common/build.gradle b/repos/common/build.gradle index 73a30291df9..8c34d5fe839 100644 --- a/repos/common/build.gradle +++ b/repos/common/build.gradle @@ -4,7 +4,7 @@ plugins { id "com.android.library" } -apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project" +apply from: "$mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project" kotlin { sourceSets { diff --git a/repos/generator/test/build.gradle b/repos/generator/test/build.gradle index 20c11632a17..d548d6e10b3 100644 --- a/repos/generator/test/build.gradle +++ b/repos/generator/test/build.gradle @@ -5,7 +5,7 @@ plugins { id "com.google.devtools.ksp" } -apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project" +apply from: "$mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project" kotlin { diff --git a/repos/inmemory/build.gradle b/repos/inmemory/build.gradle index 33b989eaf48..bdf6a7ecfe1 100644 --- a/repos/inmemory/build.gradle +++ b/repos/inmemory/build.gradle @@ -4,7 +4,7 @@ plugins { id "com.android.library" } -apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project" +apply from: "$mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project" kotlin { sourceSets { diff --git a/repos/ktor/client/build.gradle b/repos/ktor/client/build.gradle index c8cd17d20b6..5906cd72a10 100644 --- a/repos/ktor/client/build.gradle +++ b/repos/ktor/client/build.gradle @@ -4,7 +4,7 @@ plugins { id "com.android.library" } -apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project" +apply from: "$mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project" kotlin { sourceSets { diff --git a/repos/ktor/common/build.gradle b/repos/ktor/common/build.gradle index 46ccf649378..793de002335 100644 --- a/repos/ktor/common/build.gradle +++ b/repos/ktor/common/build.gradle @@ -4,7 +4,7 @@ plugins { id "com.android.library" } -apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project" +apply from: "$mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project" kotlin { sourceSets { diff --git a/resources/build.gradle b/resources/build.gradle index b73240fb2a1..3d99c758f28 100644 --- a/resources/build.gradle +++ b/resources/build.gradle @@ -4,7 +4,7 @@ plugins { id "com.android.library" } -apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project" +apply from: "$mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project" kotlin { sourceSets { diff --git a/safe_wrapper/build.gradle b/safe_wrapper/build.gradle index baeb8b29275..c70b4fc57cf 100644 --- a/safe_wrapper/build.gradle +++ b/safe_wrapper/build.gradle @@ -4,7 +4,7 @@ plugins { id "com.android.library" } -apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project" +apply from: "$mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project" kotlin { sourceSets { diff --git a/selector/common/build.gradle b/selector/common/build.gradle index da59911dabf..eddc68fc40f 100644 --- a/selector/common/build.gradle +++ b/selector/common/build.gradle @@ -4,7 +4,7 @@ plugins { id "com.android.library" } -apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project" +apply from: "$mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project" kotlin { sourceSets { diff --git a/serialization/base64/build.gradle b/serialization/base64/build.gradle index 3072e4eb5d0..0007fbefc2a 100644 --- a/serialization/base64/build.gradle +++ b/serialization/base64/build.gradle @@ -4,7 +4,7 @@ plugins { id "com.android.library" } -apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project" +apply from: "$mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project" kotlin { sourceSets { diff --git a/serialization/encapsulator/build.gradle b/serialization/encapsulator/build.gradle index 9e169e84113..a2e3bb55ba4 100644 --- a/serialization/encapsulator/build.gradle +++ b/serialization/encapsulator/build.gradle @@ -4,4 +4,4 @@ plugins { id "com.android.library" } -apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project" +apply from: "$mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project" diff --git a/serialization/mapper/build.gradle b/serialization/mapper/build.gradle index 9e169e84113..a2e3bb55ba4 100644 --- a/serialization/mapper/build.gradle +++ b/serialization/mapper/build.gradle @@ -4,4 +4,4 @@ plugins { id "com.android.library" } -apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project" +apply from: "$mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project" diff --git a/serialization/typed_serializer/build.gradle b/serialization/typed_serializer/build.gradle index 9e169e84113..a2e3bb55ba4 100644 --- a/serialization/typed_serializer/build.gradle +++ b/serialization/typed_serializer/build.gradle @@ -4,4 +4,4 @@ plugins { id "com.android.library" } -apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project" +apply from: "$mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project" diff --git a/startup/launcher/build.gradle b/startup/launcher/build.gradle index b9943d52852..977be4d1037 100644 --- a/startup/launcher/build.gradle +++ b/startup/launcher/build.gradle @@ -4,7 +4,7 @@ plugins { id "com.google.devtools.ksp" } -apply from: "$mppJvmJsLinuxMingwProject" +apply from: "$mppJvmJsWasmJsLinuxMingwProject" kotlin { jvm { diff --git a/startup/plugin/build.gradle b/startup/plugin/build.gradle index 239fb6c0dea..9d6e499ddd7 100644 --- a/startup/plugin/build.gradle +++ b/startup/plugin/build.gradle @@ -3,7 +3,7 @@ plugins { id "org.jetbrains.kotlin.plugin.serialization" } -apply from: "$mppJvmJsLinuxMingwProject" +apply from: "$mppJvmJsWasmJsLinuxMingwProject" kotlin { sourceSets { @@ -16,25 +16,5 @@ kotlin { api libs.uuid } } - jsMain { - dependencies { - api libs.uuid - } - } - wasmJsMain { - dependencies { - api libs.uuid - } - } - linuxX64Main { - dependencies { - api libs.uuid - } - } - mingwX64Main { - dependencies { - api libs.uuid - } - } } } diff --git a/startup/template/client/build.gradle b/startup/template/client/build.gradle index 6c8676b9c55..9cd774ee9d7 100644 --- a/startup/template/client/build.gradle +++ b/startup/template/client/build.gradle @@ -5,7 +5,7 @@ plugins { alias(libs.plugins.compose) } -apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project" +apply from: "$mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project" kotlin { sourceSets { diff --git a/startup/template/common/build.gradle b/startup/template/common/build.gradle index 9e169e84113..a2e3bb55ba4 100644 --- a/startup/template/common/build.gradle +++ b/startup/template/common/build.gradle @@ -4,4 +4,4 @@ plugins { id "com.android.library" } -apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project" +apply from: "$mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project" diff --git a/transactions/build.gradle b/transactions/build.gradle index 9e169e84113..a2e3bb55ba4 100644 --- a/transactions/build.gradle +++ b/transactions/build.gradle @@ -4,4 +4,4 @@ plugins { id "com.android.library" } -apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project" +apply from: "$mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project" From 8c5678e26f6cc3bd6391f47a00267763ffbf8673 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Fri, 4 Apr 2025 21:46:36 +0600 Subject: [PATCH 04/12] pre-fix of tests for wasmjs --- ...smJsAndroidLinuxMingwLinuxArm64Project.gradle | 16 ++++++++++++++-- ...smJsAndroidLinuxMingwLinuxArm64Project.gradle | 16 ++++++++++++++-- .../mppJvmJsWasmJsLinuxMingwProject.gradle | 16 ++++++++++++++-- .../mppProjectWithSerializationAndCompose.gradle | 16 ++++++++++++++-- 4 files changed, 56 insertions(+), 8 deletions(-) diff --git a/gradle/templates/mppComposeJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project.gradle b/gradle/templates/mppComposeJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project.gradle index 3d804a813fb..e760748c7df 100644 --- a/gradle/templates/mppComposeJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project.gradle +++ b/gradle/templates/mppComposeJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project.gradle @@ -28,8 +28,20 @@ kotlin { } } wasmJs { - browser() - nodejs() + browser { + testTask { + useKarma { + timeout = Duration.ofSeconds(240) + } + } + } + nodejs { + testTask { + useKarma { + timeout = Duration.ofSeconds(240) + } + } + } } androidTarget { publishAllLibraryVariants() diff --git a/gradle/templates/mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project.gradle b/gradle/templates/mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project.gradle index e626931c55b..94f6dfd0b66 100644 --- a/gradle/templates/mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project.gradle +++ b/gradle/templates/mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project.gradle @@ -28,8 +28,20 @@ kotlin { } } wasmJs { - browser() - nodejs() + browser { + testTask { + useKarma { + timeout = Duration.ofSeconds(240) + } + } + } + nodejs { + testTask { + useKarma { + timeout = Duration.ofSeconds(240) + } + } + } } androidTarget { publishAllLibraryVariants() diff --git a/gradle/templates/mppJvmJsWasmJsLinuxMingwProject.gradle b/gradle/templates/mppJvmJsWasmJsLinuxMingwProject.gradle index 1e0ac854f87..e4a4c41267c 100644 --- a/gradle/templates/mppJvmJsWasmJsLinuxMingwProject.gradle +++ b/gradle/templates/mppJvmJsWasmJsLinuxMingwProject.gradle @@ -28,8 +28,20 @@ kotlin { } } wasmJs { - browser() - nodejs() + browser { + testTask { + useKarma { + timeout = Duration.ofSeconds(240) + } + } + } + nodejs { + testTask { + useKarma { + timeout = Duration.ofSeconds(240) + } + } + } } linuxX64() mingwX64() diff --git a/gradle/templates/mppProjectWithSerializationAndCompose.gradle b/gradle/templates/mppProjectWithSerializationAndCompose.gradle index 4ee03b48c2a..455cf774601 100644 --- a/gradle/templates/mppProjectWithSerializationAndCompose.gradle +++ b/gradle/templates/mppProjectWithSerializationAndCompose.gradle @@ -28,8 +28,20 @@ kotlin { } } wasmJs { - browser() - nodejs() + browser { + testTask { + useKarma { + timeout = Duration.ofSeconds(240) + } + } + } + nodejs { + testTask { + useKarma { + timeout = Duration.ofSeconds(240) + } + } + } } androidTarget { publishAllLibraryVariants() From 4586ad65b5aa6e1e055fa526182d986ee74a2180 Mon Sep 17 00:00:00 2001 From: nullsanya <000sanya.000sanya@gmail.com> Date: Sat, 5 Apr 2025 12:45:51 +1000 Subject: [PATCH 05/12] fix test running for wasm/js browser --- gradle/karma.config.d/timeout.js | 7 +++++++ ...oseJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project.gradle | 7 +++---- ...mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project.gradle | 7 +++---- gradle/templates/mppJvmJsWasmJsLinuxMingwProject.gradle | 7 +++---- .../templates/mppProjectWithSerializationAndCompose.gradle | 7 +++---- 5 files changed, 19 insertions(+), 16 deletions(-) create mode 100644 gradle/karma.config.d/timeout.js diff --git a/gradle/karma.config.d/timeout.js b/gradle/karma.config.d/timeout.js new file mode 100644 index 00000000000..d724d972347 --- /dev/null +++ b/gradle/karma.config.d/timeout.js @@ -0,0 +1,7 @@ +config.set({ + client: { + mocha: { + timeout: 240000 + } + } +}); \ No newline at end of file diff --git a/gradle/templates/mppComposeJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project.gradle b/gradle/templates/mppComposeJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project.gradle index e760748c7df..f6bfef2b2e0 100644 --- a/gradle/templates/mppComposeJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project.gradle +++ b/gradle/templates/mppComposeJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project.gradle @@ -31,15 +31,14 @@ kotlin { browser { testTask { useKarma { - timeout = Duration.ofSeconds(240) + useChromeHeadless() + useConfigDirectory(rootProject.relativeProjectPath("gradle/karma.config.d")) } } } nodejs { testTask { - useKarma { - timeout = Duration.ofSeconds(240) - } + enabled = false } } } diff --git a/gradle/templates/mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project.gradle b/gradle/templates/mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project.gradle index 94f6dfd0b66..236d1ff0235 100644 --- a/gradle/templates/mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project.gradle +++ b/gradle/templates/mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project.gradle @@ -31,15 +31,14 @@ kotlin { browser { testTask { useKarma { - timeout = Duration.ofSeconds(240) + useChromeHeadless() + useConfigDirectory(rootProject.relativeProjectPath("gradle/karma.config.d")) } } } nodejs { testTask { - useKarma { - timeout = Duration.ofSeconds(240) - } + enabled = false } } } diff --git a/gradle/templates/mppJvmJsWasmJsLinuxMingwProject.gradle b/gradle/templates/mppJvmJsWasmJsLinuxMingwProject.gradle index e4a4c41267c..50f19cb734f 100644 --- a/gradle/templates/mppJvmJsWasmJsLinuxMingwProject.gradle +++ b/gradle/templates/mppJvmJsWasmJsLinuxMingwProject.gradle @@ -31,15 +31,14 @@ kotlin { browser { testTask { useKarma { - timeout = Duration.ofSeconds(240) + useChromeHeadless() + useConfigDirectory(rootProject.relativeProjectPath("gradle/karma.config.d")) } } } nodejs { testTask { - useKarma { - timeout = Duration.ofSeconds(240) - } + enabled = false } } } diff --git a/gradle/templates/mppProjectWithSerializationAndCompose.gradle b/gradle/templates/mppProjectWithSerializationAndCompose.gradle index 455cf774601..538e0de749e 100644 --- a/gradle/templates/mppProjectWithSerializationAndCompose.gradle +++ b/gradle/templates/mppProjectWithSerializationAndCompose.gradle @@ -31,15 +31,14 @@ kotlin { browser { testTask { useKarma { - timeout = Duration.ofSeconds(240) + useChromeHeadless() + useConfigDirectory(rootProject.relativeProjectPath("gradle/karma.config.d")) } } } nodejs { testTask { - useKarma { - timeout = Duration.ofSeconds(240) - } + enabled = false } } } From 5f93706d915dcf3d18b8be7d29f138cbff6f95be Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Fri, 11 Apr 2025 22:39:18 +0600 Subject: [PATCH 06/12] Small performance optimization of MutableMap.applyDiff --- CHANGELOG.md | 3 +++ .../kotlin/dev/inmo/micro_utils/common/MapDiff.kt | 6 ++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ce523441e9..a86146be12e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ ## 0.25.5 +* `Common`: + * Small performance optimization of `MutableMap.applyDiff` + ## 0.25.4 * `Versions`: diff --git a/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/MapDiff.kt b/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/MapDiff.kt index dc275c347d1..c3d5bfed2e9 100644 --- a/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/MapDiff.kt +++ b/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/MapDiff.kt @@ -82,13 +82,11 @@ fun MutableMap.applyDiff( mapDiff: MapDiff ) { mapDiff.apply { - removed.keys.forEach { remove(it) } + keys.removeAll(removed.keys) changed.forEach { (k, oldNew) -> put(k, oldNew.second) } - added.forEach { (k, new) -> - put(k, new) - } + putAll(added) } } From 50a90d6e96e47e2db6d14086179e90f84a1d8f53 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Fri, 11 Apr 2025 22:45:54 +0600 Subject: [PATCH 07/12] update dependencies --- CHANGELOG.md | 4 ++++ gradle/libs.versions.toml | 12 ++++++------ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a86146be12e..6084fb823f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## 0.25.5 +* `Versions`: + * `Exposed`: `0.60.0` -> `0.61.0` + * `Serialization`: `1.8.0` -> `1.8.1` + * `Coroutines`: `1.10.1` -> `1.10.2` * `Common`: * Small performance optimization of `MutableMap.applyDiff` diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 28915893e8f..630964d7234 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,15 +1,15 @@ [versions] kt = "2.1.20" -kt-serialization = "1.8.0" -kt-coroutines = "1.10.1" +kt-serialization = "1.8.1" +kt-coroutines = "1.10.2" kotlinx-browser = "0.3" kslog = "1.4.1" jb-compose = "1.7.3" -jb-exposed = "0.60.0" +jb-exposed = "0.61.0" jb-dokka = "2.0.0" sqlite = "3.49.1.0" @@ -33,7 +33,7 @@ versions = "0.51.0" android-gradle = "8.7.+" dexcount = "4.0.0" -android-coreKtx = "1.15.0" +android-coreKtx = "1.16.0" android-recyclerView = "1.4.0" android-appCompat = "1.7.0" android-fragment = "1.8.6" @@ -41,8 +41,8 @@ android-espresso = "3.6.1" android-test = "1.2.1" android-props-minSdk = "21" -android-props-compileSdk = "35" -android-props-buildTools = "35.0.0" +android-props-compileSdk = "36" +android-props-buildTools = "36.0.0" [libraries] From 0ff149a3d302ad96863ad684bfe85186e25f4663 Mon Sep 17 00:00:00 2001 From: nullsanya <000sanya.000sanya@gmail.com> Date: Mon, 14 Apr 2025 12:44:00 +1000 Subject: [PATCH 08/12] fix test running for wasm/js node --- ...omposeJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project.gradle | 4 +++- .../mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project.gradle | 4 +++- gradle/templates/mppJvmJsWasmJsLinuxMingwProject.gradle | 4 +++- gradle/templates/mppProjectWithSerializationAndCompose.gradle | 4 +++- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/gradle/templates/mppComposeJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project.gradle b/gradle/templates/mppComposeJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project.gradle index f6bfef2b2e0..9c7873cb00c 100644 --- a/gradle/templates/mppComposeJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project.gradle +++ b/gradle/templates/mppComposeJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project.gradle @@ -38,7 +38,9 @@ kotlin { } nodejs { testTask { - enabled = false + timeout = Duration.ofSeconds(240) + nodeJsArgs.add("--unhandled-rejections=warn") + nodeJsArgs.add("--trace-warnings") } } } diff --git a/gradle/templates/mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project.gradle b/gradle/templates/mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project.gradle index 236d1ff0235..269241694d2 100644 --- a/gradle/templates/mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project.gradle +++ b/gradle/templates/mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project.gradle @@ -38,7 +38,9 @@ kotlin { } nodejs { testTask { - enabled = false + timeout = Duration.ofSeconds(240) + nodeJsArgs.add("--unhandled-rejections=warn") + nodeJsArgs.add("--trace-warnings") } } } diff --git a/gradle/templates/mppJvmJsWasmJsLinuxMingwProject.gradle b/gradle/templates/mppJvmJsWasmJsLinuxMingwProject.gradle index 50f19cb734f..cfe86191061 100644 --- a/gradle/templates/mppJvmJsWasmJsLinuxMingwProject.gradle +++ b/gradle/templates/mppJvmJsWasmJsLinuxMingwProject.gradle @@ -38,7 +38,9 @@ kotlin { } nodejs { testTask { - enabled = false + timeout = Duration.ofSeconds(240) + nodeJsArgs.add("--unhandled-rejections=warn") + nodeJsArgs.add("--trace-warnings") } } } diff --git a/gradle/templates/mppProjectWithSerializationAndCompose.gradle b/gradle/templates/mppProjectWithSerializationAndCompose.gradle index 538e0de749e..df9b6b3ccf7 100644 --- a/gradle/templates/mppProjectWithSerializationAndCompose.gradle +++ b/gradle/templates/mppProjectWithSerializationAndCompose.gradle @@ -38,7 +38,9 @@ kotlin { } nodejs { testTask { - enabled = false + timeout = Duration.ofSeconds(240) + nodeJsArgs.add("--unhandled-rejections=warn") + nodeJsArgs.add("--trace-warnings") } } } From 979d0ee4ca8f75060a2549bf19f96234534d3c5e Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Mon, 14 Apr 2025 09:43:07 +0600 Subject: [PATCH 09/12] new model of templating --- gradle/templates/addCompose.gradle | 9 ++ gradle/templates/addComposeForAndroid.gradle | 9 ++ gradle/templates/addComposeForDesktop.gradle | 15 ++ gradle/templates/addComposeForJs.gradle | 9 ++ gradle/templates/defaultProject.gradle | 24 +++ .../defaultProjectWithSerialization.gradle | 25 ++++ gradle/templates/enableMPPAndroid.gradle | 31 ++++ gradle/templates/enableMPPJs.gradle | 30 ++++ gradle/templates/enableMPPJvm.gradle | 21 +++ gradle/templates/enableMPPNativeArm64.gradle | 8 + gradle/templates/enableMPPNativeX64.gradle | 10 ++ gradle/templates/enableMPPWasmJs.gradle | 27 ++++ gradle/templates/mppAndroidProject.gradle | 41 +---- ...sAndroidLinuxMingwLinuxArm64Project.gradle | 140 ++---------------- gradle/templates/mppJavaProject.gradle | 41 +---- ...sAndroidLinuxMingwLinuxArm64Project.gradle | 116 +-------------- .../mppJvmJsWasmJsLinuxMingwProject.gradle | 96 +----------- ...pProjectWithSerializationAndCompose.gradle | 128 ++-------------- gradle/wrapper/gradle-wrapper.properties | 2 +- 19 files changed, 258 insertions(+), 524 deletions(-) create mode 100644 gradle/templates/addCompose.gradle create mode 100644 gradle/templates/addComposeForAndroid.gradle create mode 100644 gradle/templates/addComposeForDesktop.gradle create mode 100644 gradle/templates/addComposeForJs.gradle create mode 100644 gradle/templates/defaultProject.gradle create mode 100644 gradle/templates/defaultProjectWithSerialization.gradle create mode 100644 gradle/templates/enableMPPAndroid.gradle create mode 100644 gradle/templates/enableMPPJs.gradle create mode 100644 gradle/templates/enableMPPJvm.gradle create mode 100644 gradle/templates/enableMPPNativeArm64.gradle create mode 100644 gradle/templates/enableMPPNativeX64.gradle create mode 100644 gradle/templates/enableMPPWasmJs.gradle diff --git a/gradle/templates/addCompose.gradle b/gradle/templates/addCompose.gradle new file mode 100644 index 00000000000..bab421e7bab --- /dev/null +++ b/gradle/templates/addCompose.gradle @@ -0,0 +1,9 @@ +kotlin { + sourceSets { + commonMain { + dependencies { + implementation compose.runtime + } + } + } +} \ No newline at end of file diff --git a/gradle/templates/addComposeForAndroid.gradle b/gradle/templates/addComposeForAndroid.gradle new file mode 100644 index 00000000000..5dedc38ca31 --- /dev/null +++ b/gradle/templates/addComposeForAndroid.gradle @@ -0,0 +1,9 @@ +kotlin { + sourceSets { + androidUnitTest { + dependencies { + implementation compose.uiTest + } + } + } +} diff --git a/gradle/templates/addComposeForDesktop.gradle b/gradle/templates/addComposeForDesktop.gradle new file mode 100644 index 00000000000..06293f13343 --- /dev/null +++ b/gradle/templates/addComposeForDesktop.gradle @@ -0,0 +1,15 @@ +kotlin { + sourceSets { + jvmMain { + dependencies { + implementation compose.desktop.currentOs + } + } + jvmTest { + dependencies { + implementation kotlin('test-junit') + implementation compose.uiTest + } + } + } +} diff --git a/gradle/templates/addComposeForJs.gradle b/gradle/templates/addComposeForJs.gradle new file mode 100644 index 00000000000..c9936d9c62f --- /dev/null +++ b/gradle/templates/addComposeForJs.gradle @@ -0,0 +1,9 @@ +kotlin { + sourceSets { + jsMain { + dependencies { + implementation compose.web.core + } + } + } +} diff --git a/gradle/templates/defaultProject.gradle b/gradle/templates/defaultProject.gradle new file mode 100644 index 00000000000..810e5cce72b --- /dev/null +++ b/gradle/templates/defaultProject.gradle @@ -0,0 +1,24 @@ +project.version = "$version" +project.group = "$group" + +kotlin { + sourceSets { + commonMain { + dependencies { + implementation kotlin('stdlib') + } + } + commonTest { + dependencies { + implementation kotlin('test-common') + implementation kotlin('test-annotations-common') + implementation libs.kt.coroutines.test + } + } + } +} + +java { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 +} diff --git a/gradle/templates/defaultProjectWithSerialization.gradle b/gradle/templates/defaultProjectWithSerialization.gradle new file mode 100644 index 00000000000..ce506197bf9 --- /dev/null +++ b/gradle/templates/defaultProjectWithSerialization.gradle @@ -0,0 +1,25 @@ +project.version = "$version" +project.group = "$group" + +kotlin { + sourceSets { + commonMain { + dependencies { + implementation kotlin('stdlib') + api libs.kt.serialization + } + } + commonTest { + dependencies { + implementation kotlin('test-common') + implementation kotlin('test-annotations-common') + implementation libs.kt.coroutines.test + } + } + } +} + +java { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 +} diff --git a/gradle/templates/enableMPPAndroid.gradle b/gradle/templates/enableMPPAndroid.gradle new file mode 100644 index 00000000000..6e735f74752 --- /dev/null +++ b/gradle/templates/enableMPPAndroid.gradle @@ -0,0 +1,31 @@ +kotlin { + androidTarget { + publishAllLibraryVariants() + compilations.all { + kotlinOptions { + jvmTarget = "17" + } + } + } + + sourceSets { + androidUnitTest { + dependencies { + implementation kotlin('test-junit') + implementation libs.android.test.junit + implementation libs.android.espresso + } + } + androidInstrumentedTest { + dependencies { + implementation kotlin('test-junit') + implementation libs.android.test.junit + implementation libs.android.espresso + } + } + + androidMain.dependsOn jvmMain + } +} + +apply from: "$defaultAndroidSettings" diff --git a/gradle/templates/enableMPPJs.gradle b/gradle/templates/enableMPPJs.gradle new file mode 100644 index 00000000000..e376278fb6c --- /dev/null +++ b/gradle/templates/enableMPPJs.gradle @@ -0,0 +1,30 @@ +kotlin { + js (IR) { + browser { + testTask { + useMocha { + timeout = "240000" + } + } + } + nodejs { + testTask { + useMocha { + timeout = "240000" + } + } + } + } + + sourceSets { + jsMain { + dependencies { + } + } + jsTest { + dependencies { + implementation kotlin('test-js') + } + } + } +} diff --git a/gradle/templates/enableMPPJvm.gradle b/gradle/templates/enableMPPJvm.gradle new file mode 100644 index 00000000000..28abd02ecf2 --- /dev/null +++ b/gradle/templates/enableMPPJvm.gradle @@ -0,0 +1,21 @@ +kotlin { + jvm { + compilations.main { + kotlinOptions { + jvmTarget = "17" + } + } + } + + sourceSets { + jvmMain { + dependencies { + } + } + jvmTest { + dependencies { + implementation kotlin('test-junit') + } + } + } +} diff --git a/gradle/templates/enableMPPNativeArm64.gradle b/gradle/templates/enableMPPNativeArm64.gradle new file mode 100644 index 00000000000..191519d3f2c --- /dev/null +++ b/gradle/templates/enableMPPNativeArm64.gradle @@ -0,0 +1,8 @@ +kotlin { + linuxArm64() + + sourceSets { + nativeMain.dependsOn commonMain + linuxArm64Main.dependsOn nativeMain + } +} diff --git a/gradle/templates/enableMPPNativeX64.gradle b/gradle/templates/enableMPPNativeX64.gradle new file mode 100644 index 00000000000..e656b94b37e --- /dev/null +++ b/gradle/templates/enableMPPNativeX64.gradle @@ -0,0 +1,10 @@ +kotlin { + linuxX64() + mingwX64() + + sourceSets { + nativeMain.dependsOn commonMain + linuxX64Main.dependsOn nativeMain + mingwX64Main.dependsOn nativeMain + } +} diff --git a/gradle/templates/enableMPPWasmJs.gradle b/gradle/templates/enableMPPWasmJs.gradle new file mode 100644 index 00000000000..d670f42ebaf --- /dev/null +++ b/gradle/templates/enableMPPWasmJs.gradle @@ -0,0 +1,27 @@ +kotlin { + wasmJs { + browser { + testTask { + useKarma { + useChromeHeadless() + useConfigDirectory(rootProject.relativeProjectPath("gradle/karma.config.d")) + } + } + } + nodejs { + testTask { + timeout = Duration.ofSeconds(240) + nodeJsArgs.add("--unhandled-rejections=warn") + nodeJsArgs.add("--trace-warnings") + } + } + } + + sourceSets { + wasmJsTest { + dependencies { + implementation kotlin('test-wasm-js') + } + } + } +} diff --git a/gradle/templates/mppAndroidProject.gradle b/gradle/templates/mppAndroidProject.gradle index 3173408b142..4d4ece2fdf0 100644 --- a/gradle/templates/mppAndroidProject.gradle +++ b/gradle/templates/mppAndroidProject.gradle @@ -1,37 +1,6 @@ -project.version = "$version" -project.group = "$group" - +apply from: "$defaultProject" +apply from: "$enableMPPJvm" +apply from: "$enableMPPJs" +apply from: "$enableMPPWasmJs" +apply from: "$enableMPPAndroid" apply from: "$publish" - -kotlin { - androidTarget { - publishAllLibraryVariants() - compilations.all { - kotlinOptions { - jvmTarget = "17" - } - } - } - - sourceSets { - commonMain { - dependencies { - implementation kotlin('stdlib') - } - } - commonTest { - dependencies { - implementation kotlin('test-common') - implementation kotlin('test-annotations-common') - implementation libs.kt.coroutines.test - } - } - } -} - -apply from: "$defaultAndroidSettings" - -java { - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 -} diff --git a/gradle/templates/mppComposeJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project.gradle b/gradle/templates/mppComposeJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project.gradle index 9c7873cb00c..d3c1db07e7d 100644 --- a/gradle/templates/mppComposeJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project.gradle +++ b/gradle/templates/mppComposeJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project.gradle @@ -1,130 +1,12 @@ -project.version = "$version" -project.group = "$group" - +apply from: "$defaultProjectWithSerialization" +apply from: "$enableMPPJvm" +apply from: "$enableMPPJs" +apply from: "$enableMPPWasmJs" +apply from: "$enableMPPAndroid" +apply from: "$enableMPPNativeX64" +apply from: "$enableMPPNativeArm64" +apply from: "$addCompose" +apply from: "$addComposeForAndroid" +apply from: "$addComposeForDesktop" +apply from: "$addComposeForJs" apply from: "$publish" - -kotlin { - jvm { - compilations.main { - kotlinOptions { - jvmTarget = "17" - } - } - } - js (IR) { - browser { - testTask { - useMocha { - timeout = "240000" - } - } - } - nodejs { - testTask { - useMocha { - timeout = "240000" - } - } - } - } - wasmJs { - browser { - testTask { - useKarma { - useChromeHeadless() - useConfigDirectory(rootProject.relativeProjectPath("gradle/karma.config.d")) - } - } - } - nodejs { - testTask { - timeout = Duration.ofSeconds(240) - nodeJsArgs.add("--unhandled-rejections=warn") - nodeJsArgs.add("--trace-warnings") - } - } - } - androidTarget { - publishAllLibraryVariants() - compilations.all { - kotlinOptions { - jvmTarget = "17" - } - } - } - linuxX64() - mingwX64() - linuxArm64() - - sourceSets { - commonMain { - dependencies { - implementation kotlin('stdlib') - implementation compose.runtime - api libs.kt.serialization - } - } - commonTest { - dependencies { - implementation kotlin('test-common') - implementation kotlin('test-annotations-common') - implementation libs.kt.coroutines.test - } - } - - androidUnitTest { - dependencies { - implementation kotlin('test-junit') - implementation libs.android.test.junit - implementation libs.android.espresso - implementation compose.uiTest - } - } - androidInstrumentedTest { - dependencies { - implementation kotlin('test-junit') - implementation libs.android.test.junit - implementation libs.android.espresso - } - } - jvmMain { - dependencies { - implementation compose.desktop.currentOs - } - } - jvmTest { - dependencies { - implementation kotlin('test-junit') - implementation compose.uiTest - } - } - jsMain { - dependencies { - implementation compose.web.core - } - } - jsTest { - dependencies { - implementation kotlin('test-js') - } - } - wasmJsTest { - dependencies { - implementation kotlin('test-wasm-js') - } - } - nativeMain.dependsOn commonMain - linuxX64Main.dependsOn nativeMain - mingwX64Main.dependsOn nativeMain - linuxArm64Main.dependsOn nativeMain - - androidMain.dependsOn jvmMain - } -} - -apply from: "$defaultAndroidSettings" - -java { - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 -} diff --git a/gradle/templates/mppJavaProject.gradle b/gradle/templates/mppJavaProject.gradle index d4a050d1499..5b2e90f368f 100644 --- a/gradle/templates/mppJavaProject.gradle +++ b/gradle/templates/mppJavaProject.gradle @@ -1,40 +1,3 @@ -project.version = "$version" -project.group = "$group" - +apply from: "$defaultProject" +apply from: "$enableMPPJvm" apply from: "$publish" - -kotlin { - jvm { - compilations.main { - kotlinOptions { - jvmTarget = "17" - } - } - } - - sourceSets { - commonMain { - dependencies { - implementation kotlin('stdlib') - } - } - commonTest { - dependencies { - implementation kotlin('test-common') - implementation kotlin('test-annotations-common') - implementation libs.kt.coroutines.test - } - } - - jvmTest { - dependencies { - implementation kotlin('test-junit') - } - } - } -} - -java { - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 -} diff --git a/gradle/templates/mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project.gradle b/gradle/templates/mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project.gradle index 269241694d2..41bc490fc0b 100644 --- a/gradle/templates/mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project.gradle +++ b/gradle/templates/mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project.gradle @@ -1,110 +1,8 @@ -project.version = "$version" -project.group = "$group" - +apply from: "$defaultProjectWithSerialization" +apply from: "$enableMPPJvm" +apply from: "$enableMPPJs" +apply from: "$enableMPPWasmJs" +apply from: "$enableMPPAndroid" +apply from: "$enableMPPNativeX64" +apply from: "$enableMPPNativeArm64" apply from: "$publish" - -kotlin { - jvm { - compilations.main { - kotlinOptions { - jvmTarget = "17" - } - } - } - js (IR) { - browser { - testTask { - useMocha { - timeout = "240000" - } - } - } - nodejs { - testTask { - useMocha { - timeout = "240000" - } - } - } - } - wasmJs { - browser { - testTask { - useKarma { - useChromeHeadless() - useConfigDirectory(rootProject.relativeProjectPath("gradle/karma.config.d")) - } - } - } - nodejs { - testTask { - timeout = Duration.ofSeconds(240) - nodeJsArgs.add("--unhandled-rejections=warn") - nodeJsArgs.add("--trace-warnings") - } - } - } - androidTarget { - publishAllLibraryVariants() - compilations.all { - kotlinOptions { - jvmTarget = "17" - } - } - } - linuxX64() - mingwX64() - linuxArm64() - - sourceSets { - commonMain { - dependencies { - implementation kotlin('stdlib') - api libs.kt.serialization - } - } - commonTest { - dependencies { - implementation kotlin('test') - implementation kotlin('test-annotations-common') - implementation libs.kt.coroutines.test - } - } - - androidUnitTest { - dependencies { - implementation kotlin('test-junit') - implementation libs.android.test.junit - implementation libs.android.espresso - } - } - jvmTest { - dependencies { - implementation kotlin('test-junit') - } - } - jsTest { - dependencies { - implementation kotlin('test-js') - } - } - wasmJsTest { - dependencies { - implementation kotlin('test-wasm-js') - } - } - nativeMain.dependsOn commonMain - linuxX64Main.dependsOn nativeMain - mingwX64Main.dependsOn nativeMain - linuxArm64Main.dependsOn nativeMain - - androidMain.dependsOn jvmMain - } -} - -apply from: "$defaultAndroidSettings" - -java { - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 -} diff --git a/gradle/templates/mppJvmJsWasmJsLinuxMingwProject.gradle b/gradle/templates/mppJvmJsWasmJsLinuxMingwProject.gradle index cfe86191061..d175019a855 100644 --- a/gradle/templates/mppJvmJsWasmJsLinuxMingwProject.gradle +++ b/gradle/templates/mppJvmJsWasmJsLinuxMingwProject.gradle @@ -1,92 +1,6 @@ -project.version = "$version" -project.group = "$group" - +apply from: "$defaultProjectWithSerialization" +apply from: "$enableMPPJvm" +apply from: "$enableMPPJs" +apply from: "$enableMPPWasmJs" +apply from: "$enableMPPNativeX64" apply from: "$publish" - -kotlin { - jvm { - compilations.main { - kotlinOptions { - jvmTarget = "17" - } - } - } - js (IR) { - browser { - testTask { - useMocha { - timeout = "240000" - } - } - } - nodejs { - testTask { - useMocha { - timeout = "240000" - } - } - } - } - wasmJs { - browser { - testTask { - useKarma { - useChromeHeadless() - useConfigDirectory(rootProject.relativeProjectPath("gradle/karma.config.d")) - } - } - } - nodejs { - testTask { - timeout = Duration.ofSeconds(240) - nodeJsArgs.add("--unhandled-rejections=warn") - nodeJsArgs.add("--trace-warnings") - } - } - } - linuxX64() - mingwX64() - - sourceSets { - commonMain { - dependencies { - implementation kotlin('stdlib') - api libs.kt.serialization - } - } - commonTest { - dependencies { - implementation kotlin('test-common') - implementation kotlin('test-annotations-common') - implementation libs.kt.coroutines.test - } - } - - jvmTest { - dependencies { - implementation kotlin('test-junit') - } - } - jsTest { - dependencies { - implementation kotlin('test-js') - } - } - wasmJsTest { - dependencies { - implementation kotlin('test-wasm-js') - } - } - - nativeMain.dependsOn commonMain - linuxX64Main.dependsOn nativeMain - mingwX64Main.dependsOn nativeMain - - androidMain.dependsOn jvmMain - } -} - -java { - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 -} diff --git a/gradle/templates/mppProjectWithSerializationAndCompose.gradle b/gradle/templates/mppProjectWithSerializationAndCompose.gradle index df9b6b3ccf7..e92ba3322f6 100644 --- a/gradle/templates/mppProjectWithSerializationAndCompose.gradle +++ b/gradle/templates/mppProjectWithSerializationAndCompose.gradle @@ -1,120 +1,10 @@ -project.version = "$version" -project.group = "$group" - +apply from: "$defaultProjectWithSerialization" +apply from: "$enableMPPJvm" +apply from: "$enableMPPJs" +apply from: "$enableMPPWasmJs" +apply from: "$enableMPPAndroid" +apply from: "$addCompose" +apply from: "$addComposeForAndroid" +apply from: "$addComposeForDesktop" +apply from: "$addComposeForJs" apply from: "$publish" - -kotlin { - jvm { - compilations.main { - kotlinOptions { - jvmTarget = "17" - } - } - } - js (IR) { - browser { - testTask { - useMocha { - timeout = "240000" - } - } - } - nodejs { - testTask { - useMocha { - timeout = "240000" - } - } - } - } - wasmJs { - browser { - testTask { - useKarma { - useChromeHeadless() - useConfigDirectory(rootProject.relativeProjectPath("gradle/karma.config.d")) - } - } - } - nodejs { - testTask { - timeout = Duration.ofSeconds(240) - nodeJsArgs.add("--unhandled-rejections=warn") - nodeJsArgs.add("--trace-warnings") - } - } - } - androidTarget { - publishAllLibraryVariants() - compilations.all { - kotlinOptions { - jvmTarget = "17" - } - } - } - - sourceSets { - commonMain { - dependencies { - implementation kotlin('stdlib') - implementation compose.runtime - api libs.kt.serialization - } - } - commonTest { - dependencies { - implementation kotlin('test-common') - implementation kotlin('test-annotations-common') - implementation libs.kt.coroutines.test - } - } - jvmMain { - dependencies { - implementation compose.desktop.currentOs - } - } - jvmTest { - dependencies { - implementation kotlin('test-junit') - implementation compose.uiTest - } - } - jsMain { - dependencies { - implementation compose.web.core - } - } - jsTest { - dependencies { - implementation kotlin('test-js') - } - } - wasmJsTest { - dependencies { - implementation kotlin('test-wasm-js') - } - } - androidUnitTest { - dependencies { - implementation kotlin('test-junit') - implementation libs.android.test.junit - implementation libs.android.espresso - implementation compose.uiTest - } - } - } -} - -apply from: "$defaultAndroidSettings" - -java { - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 -} - - -//compose { -// if (composePluginKotlinVersion != null && !composePluginKotlinVersion.isEmpty()) { -// kotlinCompilerPlugin.set(composePluginKotlinVersion) -// } -//} diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index e382118b03d..2733ed5dc3c 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.12.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists From 5ec0a46089a3262215ce17cc04ebc194f3d1be27 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Mon, 14 Apr 2025 09:53:07 +0600 Subject: [PATCH 10/12] add KDocs for actor and safeActor --- .../dev/inmo/micro_utils/coroutines/Actor.kt | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/Actor.kt b/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/Actor.kt index 13478c85960..59b24fbe877 100644 --- a/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/Actor.kt +++ b/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/Actor.kt @@ -4,6 +4,15 @@ import kotlinx.coroutines.* import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.consumeAsFlow +/** + * Creates an actor using coroutines that processes incoming messages of type [T]. + * An actor is a computational entity that processes messages sequentially in response to messages it receives. + * + * @param T The type of messages this actor will process + * @param channelCapacity The capacity of the [Channel] used for message delibery to the actor. Defaults to [Channel.UNLIMITED] + * @param block The processing logic to be applied to each received message + * @return A [Channel] that can be used to send messages to this actor or cancel it + */ fun CoroutineScope.actor( channelCapacity: Int = Channel.UNLIMITED, block: suspend (T) -> Unit @@ -13,6 +22,16 @@ fun CoroutineScope.actor( return channel } +/** + * Creates a safe actor that catches and handles exceptions during message processing. + * This variant wraps the processing logic in a safety mechanism to prevent actor failure due to exceptions. + * + * @param T The type of messages this actor will process + * @param channelCapacity The capacity of the [Channel] used for message processing. Defaults to [Channel.UNLIMITED] + * @param onException Handler for exceptions that occur during message processing. Defaults to [defaultSafelyExceptionHandler] + * @param block The processing logic to be applied to each received message + * @return A [Channel] that can be used to send messages to this actor + */ inline fun CoroutineScope.safeActor( channelCapacity: Int = Channel.UNLIMITED, noinline onException: ExceptionHandler = defaultSafelyExceptionHandler, From 9c463d03383699ae473a58b7a12fbec5ca1cbb7d Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Mon, 14 Apr 2025 10:03:26 +0600 Subject: [PATCH 11/12] add kdocs to koin common module --- .../kotlin/FactoryWithRandomQualifier.kt | 10 +++- .../kotlin/FactoryWithStringQualifier.kt | 10 ++++ koin/src/commonMain/kotlin/GetAllDistinct.kt | 15 ++++++ koin/src/commonMain/kotlin/GetAny.kt | 17 +++++++ .../commonMain/kotlin/GetWithDefinition.kt | 48 +++++++++++++++++++ koin/src/commonMain/kotlin/RandomQualifier.kt | 6 +++ .../kotlin/SingleWithRandomQualifier.kt | 10 +++- .../kotlin/SingleWithStringQualifier.kt | 10 ++++ 8 files changed, 122 insertions(+), 4 deletions(-) diff --git a/koin/src/commonMain/kotlin/FactoryWithRandomQualifier.kt b/koin/src/commonMain/kotlin/FactoryWithRandomQualifier.kt index e00675e1ff8..c5705d30c3f 100644 --- a/koin/src/commonMain/kotlin/FactoryWithRandomQualifier.kt +++ b/koin/src/commonMain/kotlin/FactoryWithRandomQualifier.kt @@ -4,8 +4,14 @@ import org.koin.core.definition.Definition import org.koin.core.module.Module /** - * Will be useful in case you need to declare some singles with one type several types, but need to separate them and do - * not care about how :) + * Declares a factory with a random qualifier in the Koin module. + * This is useful when you need to declare multiple factory definitions of the same type + * but want them to be uniquely identifiable without manually specifying qualifiers. + * Unlike singles, factories create a new instance each time they are requested. + * + * @param T The type of instance to be created by the factory + * @param definition The definition function that creates new instances + * @return A Koin definition for the factory with a random qualifier */ inline fun Module.factoryWithRandomQualifier( noinline definition: Definition diff --git a/koin/src/commonMain/kotlin/FactoryWithStringQualifier.kt b/koin/src/commonMain/kotlin/FactoryWithStringQualifier.kt index 0bf7301b107..f7d51d05ea2 100644 --- a/koin/src/commonMain/kotlin/FactoryWithStringQualifier.kt +++ b/koin/src/commonMain/kotlin/FactoryWithStringQualifier.kt @@ -4,6 +4,16 @@ import org.koin.core.definition.Definition import org.koin.core.module.Module import org.koin.core.qualifier.StringQualifier +/** + * Declares a factory with a string qualifier in the Koin module. + * This is a convenience function that wraps the string qualifier in a [StringQualifier]. + * Unlike singles, factories create a new instance each time they are requested. + * + * @param T The type of instance to be created by the factory + * @param qualifier The string value to be used as a qualifier + * @param definition The definition function that creates new instances + * @return A Koin definition for the factory with the specified string qualifier + */ inline fun Module.factory( qualifier: String, noinline definition: Definition diff --git a/koin/src/commonMain/kotlin/GetAllDistinct.kt b/koin/src/commonMain/kotlin/GetAllDistinct.kt index 59a610217cb..67743437368 100644 --- a/koin/src/commonMain/kotlin/GetAllDistinct.kt +++ b/koin/src/commonMain/kotlin/GetAllDistinct.kt @@ -3,6 +3,21 @@ package dev.inmo.micro_utils.koin import org.koin.core.Koin import org.koin.core.scope.Scope +/** + * Retrieves all instances of type [T] from the current [Scope] and returns them as a distinct list. + * This function is useful when you want to avoid duplicate instances of the same type. + * + * @param T The type of instances to retrieve + * @return A list of distinct instances of type [T] + */ inline fun Scope.getAllDistinct() = getAll().distinct() + +/** + * Retrieves all instances of type [T] from the [Koin] container and returns them as a distinct list. + * This function is useful when you want to avoid duplicate instances of the same type. + * + * @param T The type of instances to retrieve + * @return A list of distinct instances of type [T] + */ inline fun Koin.getAllDistinct() = getAll().distinct() diff --git a/koin/src/commonMain/kotlin/GetAny.kt b/koin/src/commonMain/kotlin/GetAny.kt index 351a1272fe0..ed26b32c7ce 100644 --- a/koin/src/commonMain/kotlin/GetAny.kt +++ b/koin/src/commonMain/kotlin/GetAny.kt @@ -3,6 +3,23 @@ package dev.inmo.micro_utils.koin import org.koin.core.Koin import org.koin.core.scope.Scope +/** + * Retrieves the first available instance of type [T] from the current scope. + * This is useful when you need any instance of a type and don't care which one. + * + * @param T The type of instance to retrieve + * @return The first available instance of type [T] + * @throws NoSuchElementException if no instances of type [T] are available + */ inline fun Scope.getAny() = getAll().first() + +/** + * Retrieves the first available instance of type [T] from the Koin container. + * This is useful when you need any instance of a type and don't care which one. + * + * @param T The type of instance to retrieve + * @return The first available instance of type [T] + * @throws NoSuchElementException if no instances of type [T] are available + */ inline fun Koin.getAny() = getAll().first() diff --git a/koin/src/commonMain/kotlin/GetWithDefinition.kt b/koin/src/commonMain/kotlin/GetWithDefinition.kt index 32cc88bad9e..3be5f9ac912 100644 --- a/koin/src/commonMain/kotlin/GetWithDefinition.kt +++ b/koin/src/commonMain/kotlin/GetWithDefinition.kt @@ -7,33 +7,81 @@ import org.koin.core.instance.InstanceFactory import org.koin.core.parameter.ParametersDefinition import org.koin.core.scope.Scope +/** + * Retrieves an instance of type [T] from the Koin container using a [BeanDefinition]. + * + * @param T The type of instance to retrieve + * @param definition The bean definition to use for instance retrieval + * @param parameters Optional parameters to pass to the instance constructor + * @return An instance of type [T] + */ fun Koin.get(definition: BeanDefinition, parameters: ParametersDefinition? = null): T = get( definition.primaryType, definition.qualifier, parameters ) +/** + * Retrieves an instance of type [T] from the Koin container using an [InstanceFactory]. + * + * @param T The type of instance to retrieve + * @param definition The instance factory to use for instance retrieval + * @param parameters Optional parameters to pass to the instance constructor + * @return An instance of type [T] + */ fun Koin.get(definition: InstanceFactory, parameters: ParametersDefinition? = null): T = get( definition.beanDefinition, parameters ) +/** + * Retrieves an instance of type [T] from the Koin container using a [KoinDefinition]. + * + * @param T The type of instance to retrieve + * @param definition The Koin definition to use for instance retrieval + * @param parameters Optional parameters to pass to the instance constructor + * @return An instance of type [T] + */ fun Koin.get(definition: KoinDefinition, parameters: ParametersDefinition? = null): T = get( definition.factory, parameters ) +/** + * Retrieves an instance of type [T] from the current scope using a [BeanDefinition]. + * + * @param T The type of instance to retrieve + * @param definition The bean definition to use for instance retrieval + * @param parameters Optional parameters to pass to the instance constructor + * @return An instance of type [T] + */ fun Scope.get(definition: BeanDefinition, parameters: ParametersDefinition? = null): T = get( definition.primaryType, definition.qualifier, parameters ) +/** + * Retrieves an instance of type [T] from the current scope using an [InstanceFactory]. + * + * @param T The type of instance to retrieve + * @param definition The instance factory to use for instance retrieval + * @param parameters Optional parameters to pass to the instance constructor + * @return An instance of type [T] + */ fun Scope.get(definition: InstanceFactory, parameters: ParametersDefinition? = null): T = get( definition.beanDefinition, parameters ) +/** + * Retrieves an instance of type [T] from the current scope using a [KoinDefinition]. + * + * @param T The type of instance to retrieve + * @param definition The Koin definition to use for instance retrieval + * @param parameters Optional parameters to pass to the instance constructor + * @return An instance of type [T] + */ fun Scope.get(definition: KoinDefinition, parameters: ParametersDefinition? = null): T = get( definition.factory, parameters diff --git a/koin/src/commonMain/kotlin/RandomQualifier.kt b/koin/src/commonMain/kotlin/RandomQualifier.kt index 9b5b1a39c62..64d124bd5b0 100644 --- a/koin/src/commonMain/kotlin/RandomQualifier.kt +++ b/koin/src/commonMain/kotlin/RandomQualifier.kt @@ -3,4 +3,10 @@ package dev.inmo.micro_utils.koin import com.benasher44.uuid.uuid4 import org.koin.core.qualifier.StringQualifier +/** + * Creates a [StringQualifier] with a random string value. + * + * @param randomFun A function that generates a random string. By default, it uses UUID4 string representation. + * @return A [StringQualifier] with a randomly generated string value + */ fun RandomQualifier(randomFun: () -> String = { uuid4().toString() }) = StringQualifier(randomFun()) diff --git a/koin/src/commonMain/kotlin/SingleWithRandomQualifier.kt b/koin/src/commonMain/kotlin/SingleWithRandomQualifier.kt index ef7068076e2..255ffaf9785 100644 --- a/koin/src/commonMain/kotlin/SingleWithRandomQualifier.kt +++ b/koin/src/commonMain/kotlin/SingleWithRandomQualifier.kt @@ -4,8 +4,14 @@ import org.koin.core.definition.Definition import org.koin.core.module.Module /** - * Will be useful in case you need to declare some singles with one type several types, but need to separate them and do - * not care about how :) + * Declares a single instance with a random qualifier in the Koin module. + * This is useful when you need to declare multiple instances of the same type + * but want them to be uniquely identifiable without manually specifying qualifiers. + * + * @param T The type of instance to be created + * @param createdAtStart Whether the instance should be created when the Koin module starts (default: false) + * @param definition The definition function that creates the instance + * @return A Koin definition for the single instance with a random qualifier */ inline fun Module.singleWithRandomQualifier( createdAtStart: Boolean = false, diff --git a/koin/src/commonMain/kotlin/SingleWithStringQualifier.kt b/koin/src/commonMain/kotlin/SingleWithStringQualifier.kt index cdde37efcca..5c16569130d 100644 --- a/koin/src/commonMain/kotlin/SingleWithStringQualifier.kt +++ b/koin/src/commonMain/kotlin/SingleWithStringQualifier.kt @@ -4,6 +4,16 @@ import org.koin.core.definition.Definition import org.koin.core.module.Module import org.koin.core.qualifier.StringQualifier +/** + * Declares a single instance with a string qualifier in the Koin module. + * This is a convenience function that wraps the string qualifier in a [StringQualifier]. + * + * @param T The type of instance to be created + * @param qualifier The string value to be used as a qualifier + * @param createdAtStart Whether the instance should be created when the Koin module starts (default: false) + * @param definition The definition function that creates the instance + * @return A Koin definition for the single instance with the specified string qualifier + */ inline fun Module.single( qualifier: String, createdAtStart: Boolean = false, From 041f35ed6ca1ff219e0e829434c59318adb983fa Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Mon, 14 Apr 2025 10:09:33 +0600 Subject: [PATCH 12/12] add templates declaration in README.md --- README.md | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/README.md b/README.md index b813b2c758d..8c1bea754c7 100644 --- a/README.md +++ b/README.md @@ -35,3 +35,51 @@ Most of complex modules are built with next hierarchy: * `common` part contains routes which are common for clients and servers * `client` submodule contains clients which are usually using `UnifiedRequester` to make requests using routes from `ktor/common` module and some internal logic of requests * `server` submodule (in most cases `JVM`-only) contains some extensions for `Route` instances which usually will give opportunity to proxy internet requests from `ktor/client` realization to some proxy object + +## Gradle Templates + +All templates can be used by applying them in your project's build.gradle files using the `apply from` directive. For example: + +```gradle +apply from: "$defaultProject" +``` + +In the sample has been used `defaultProject.gradle` as a basic template. + +The project includes a collection of Gradle templates to simplify project setup and configuration. These templates are located in the `gradle/templates` directory and can be used to quickly set up different types of projects: + +### Project Setup Templates + +* `defaultProject.gradle` (usage `apply from: "$defaultProject"`) - Basic project configuration +* `defaultProjectWithSerialization.gradle` (usage `apply from: "$defaultProjectWithSerialization"`) - Project configuration with Kotlin Serialization support +* `mppJavaProject.gradle` (usage `apply from: "$mppJavaProject"`) - Multiplatform project with Java support +* `mppAndroidProject.gradle` (usage `apply from: "$mppAndroidProject"`) - Multiplatform project with Android support + +### Multiplatform Configuration Templates + +* `enableMPPAndroid.gradle` (usage `apply from: "$enableMPPAndroid"`) - Enable Android target in multiplatform project +* `enableMPPJs.gradle` (usage `apply from: "$enableMPPJs"`) - Enable JavaScript target in multiplatform project +* `enableMPPJvm.gradle` (usage `apply from: "$enableMPPJvm"`) - Enable JVM target in multiplatform project +* `enableMPPNativeArm64.gradle` (usage `apply from: "$enableMPPNativeArm64"`) - Enable ARM64 native target +* `enableMPPNativeX64.gradle` (usage `apply from: "$enableMPPNativeX64"`) - Enable x64 native target +* `enableMPPWasmJs.gradle` (usage `apply from: "$enableMPPWasmJs"`) - Enable WebAssembly JavaScript target + +### Compose Integration Templates + +* `addCompose.gradle` (usage `apply from: "$addCompose"`) - Basic Compose configuration +* `addComposeForAndroid.gradle` (usage `apply from: "$addComposeForAndroid"`) - Compose configuration for Android +* `addComposeForDesktop.gradle` (usage `apply from: "$addComposeForDesktop"`) - Compose configuration for Desktop +* `addComposeForJs.gradle` (usage `apply from: "$addComposeForJs"`) - Compose configuration for JavaScript + +### Publishing Templates + +* `publish.gradle` (usage `apply from: "$publish"`) - General publishing configuration +* `publish_jvm.gradle` (usage `apply from: "$publish_jvm"`) - JVM-specific publishing configuration +* `publish.kpsb` and `publish_jvm.kpsb` (usage `apply from: "$publish_kpsb"` and `apply from: "$publish_jvm_kpsb"`) - Publishing configuration for Kotlin Multiplatform and JVM + +### Combined Project Templates + +* `mppJvmJsWasmJsLinuxMingwProject.gradle` (usage `apply from: "$mppJvmJsWasmJsLinuxMingwProject"`) - Multiplatform project for JVM, JS, Wasm, Linux, and MinGW +* `mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project.gradle` (usage `apply from: "$mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project"`) - Multiplatform project with additional Android and ARM64 support +* `mppComposeJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project.gradle` (usage `apply from: "$mppComposeJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project"`) - Multiplatform project with Compose support +* `mppProjectWithSerializationAndCompose.gradle` (usage `apply from: "$mppProjectWithSerializationAndCompose"`) - Multiplatform project with both Serialization and Compose support