yawkat
02/17/2025, 9:03 PMEdoardo Luppi
02/17/2025, 9:09 PMwasmJs project from a js one. What does the web-app do with the WASM code?Edoardo Luppi
02/17/2025, 9:11 PMyawkat
02/17/2025, 9:12 PMyawkat
02/17/2025, 9:13 PMyawkat
02/17/2025, 9:15 PMEdoardo Luppi
02/17/2025, 9:15 PMexternal bindings in the js project, for the `@JsExport`ed functions in the wasmJs project is what you're looking for I believe.
You'll then have to do some Gradle magic to depend on the common-wasm module, and include its output in the final JS bundle.yawkat
02/17/2025, 9:16 PMyawkat
02/17/2025, 9:17 PMEdoardo Luppi
02/17/2025, 9:19 PMEdoardo Luppi
02/17/2025, 9:24 PMEdoardo Luppi
02/17/2025, 9:28 PMexternal declarations to load the exported WASM functions through the JS wrapper.yawkat
02/17/2025, 9:36 PM@JsModule("kcd2dicesim-common-wasm-wasm-js")yawkat
02/17/2025, 9:36 PMyawkat
02/17/2025, 9:36 PMyawkat
02/17/2025, 9:43 PM@file:JsModule("kcd2dicesim-common-wasm-wasm-js")
@file:JsNonModule
but i would really only like the former. would it be better to fix this on the js side by fixing the import or on the wasm side by making the export non-module?yawkat
02/17/2025, 9:44 PMUncaught TypeError: wasmCalculateEv is not a function
think webpack-internal:///./kotlin/kcd2dicesim-web-app.js:684
lambda_5 webpack-internal:///./kotlin/kcd2dicesim-web-app.js:430
lambda webpack-internal:///./kotlin/kvision.js:12638
invokeHandler webpack-internal:///../../node_modules/snabbdom/build/modules/eventlisteners.js:8
handleEvent webpack-internal:///../../node_modules/snabbdom/build/modules/eventlisteners.js:22
handler webpack-internal:///../../node_modules/snabbdom/build/modules/eventlisteners.js:27
and looking at the js, wasmCalculateEv is just called bare, not on a module like i think it should be?Edoardo Luppi
02/17/2025, 9:45 PMjs() capability, and make the js tasks depend on the wasmJs ones, than move the outputted WASM binary into js resources.
At this point you can depend directly from web-app to common-wasm using js source sets.
but the javascript external functions try to use a non-module function. it's forcing me to useDid you set
useEsModules() in the web-app project?yawkat
02/17/2025, 9:46 PMuseEsModules seems to have broken a bunch of other things 🙂yawkat
02/17/2025, 9:47 PMEdoardo Luppi
02/17/2025, 9:47 PMyawkat
02/17/2025, 9:47 PMEdoardo Luppi
02/17/2025, 9:48 PMyawkat
02/17/2025, 9:49 PMyawkat
02/17/2025, 9:51 PMyawkat
02/17/2025, 9:51 PMEdoardo Luppi
02/17/2025, 9:52 PM- web-app
js()
- wasm-module
wasmJs()
js() <-- This simply wraps what is outputted by the wasmJs tasks.
You basically do the wrapping work here, instead of doing it in web-app
You then have the following in your web-app Gradle script:
jsMain {
dependencies {
implementation(project(":wasm-module"))
}
}yawkat
02/17/2025, 9:52 PMEdoardo Luppi
02/17/2025, 9:54 PMwasm-module you could even play with expect-actual to mandate functions' implementations between js and wasmJs source sets, ensuring signatures stay the same.yawkat
02/17/2025, 9:55 PMyawkat
02/17/2025, 9:56 PMEdoardo Luppi
02/17/2025, 9:57 PMwasm-module:js would still be IR that is consumed by the web-app:js compiler after allYes, but the
js functions' implementations would load and call the WASM binary - through the JS wrapper - that you now have in your js resources, thanks to having a dependency to the wasmJs tasksEdoardo Luppi
02/17/2025, 9:58 PMyawkat
02/17/2025, 9:58 PMyawkat
02/17/2025, 9:58 PMEdoardo Luppi
02/17/2025, 9:58 PMwasmJs compilation tasksEdoardo Luppi
02/17/2025, 10:01 PMEdoardo Luppi
02/17/2025, 10:02 PMyawkat
02/17/2025, 10:02 PMyawkat
02/17/2025, 10:03 PMyawkat
02/17/2025, 10:04 PMimport { instantiate } from './kcd2dicesim-common-wasm-wasm-js.uninstantiated.mjs';
const exports = (await instantiate({
})).exports;
export const {
wasmCalculateEv,
wasmGetMoveKeep,
wasmGetMoveShouldContinue,
memory,
_initialize,
startUnitTests
} = exports;yawkat
02/17/2025, 10:08 PMEdoardo Luppi
02/17/2025, 10:08 PMjs source set you'll simply use @JsModule("wasm-wrapper.js"), there is no need to use useEsModules() or useCommonJs() because you're simply building a multiplatform library.
In the web-app side, on the other hand, you'll be somewhat forced to use useEsModules(), as that's how the JS wrapper exports those functions.yawkat
02/17/2025, 10:09 PMyawkat
02/17/2025, 10:10 PMEdoardo Luppi
02/17/2025, 10:11 PMEdoardo Luppi
02/17/2025, 10:13 PMyawkat
02/17/2025, 10:14 PMyawkat
02/17/2025, 10:15 PMEdoardo Luppi
02/17/2025, 10:17 PMVampire
02/17/2025, 10:58 PMEdoardo Luppi
02/18/2025, 1:19 AMyawkat
02/18/2025, 10:28 AMimport {
wasmCalculateEv,
wasmGetMoveKeep,
wasmGetMoveShouldContinue
} from "./kcd2dicesim-common-wasm-wasm-js.mjs";
window.wasmCalculateEv = wasmCalculateEv;
window.wasmGetMoveKeep = wasmGetMoveKeep;
window.wasmGetMoveShouldContinue = wasmGetMoveShouldContinue;
ugly but it does the job.yawkat
02/18/2025, 12:39 PMval commonWasm = project.parent!!.project("common-wasm")
kotlin {
...
sourceSets["jsMain"].resources.srcDir(commonWasm.layout.buildDirectory.dir("dist/wasmJs/productionLibrary").get().asFile)
}
tasks.named("jsProcessResources").configure {
dependsOn(commonWasm.tasks.named("wasmJsNodeProductionLibraryDistribution"))
}yawkat
02/18/2025, 12:40 PMEdoardo Luppi
02/18/2025, 12:42 PMyawkat
02/18/2025, 12:43 PMVampire
02/18/2025, 11:16 PMwhat's the best way to have task X from subproject A, depend on task Y from subproject B.A direct dependency? Never. Well if it really is just a "run that task before this task, *but I don't need any output of it*", then you could do it with
dependsOn(":path:to:the:task").
But never ever ever ever ever try to do it like @yawkat did it now by reaching into the project model of other projects.
That is highly discouraged and unsafe, especially when combined with then configuring some output directories as dependencies manually like @yawkat did.
All this is highly fragile, unsafe, and discouraged.
If you need things from another project in the same build, either create a feature variant with that stuff or if it is only build-internal with only an outgoing configuration that is requested explicitly.
If that is properly set up, you depend on that other project and either request the according feature variant or the according configuration and all necessary tasks run automatically when necessary.
See this for more in-depth information: https://docs.gradle.org/current/userguide/cross_project_publications.htmlEdoardo Luppi
02/18/2025, 11:29 PMval instrumentedJars by configurations.creating ...
It looks familiar to what is used in KGP, so I'd say the answer might be yes.
I also recall seeing attributes mentioned when discussing multiple js() targets, to distinguish themVampire
02/18/2025, 11:35 PMVampire
02/18/2025, 11:45 PMidea-debugger-enhancer and add Kotlin support and actually make it work with latest IJ versions 😉)