jsaddle-wasm: Run JSaddle @JSM@ with the GHC WASM backend

[ javascript, library, web ] [ Propose Tags ]

Run JSaddle JSM with the GHC WASM backend.


[Skip to Readme]

Modules

[Index] [Quick Jump]

Downloads

Maintainer's Corner

Package maintainers

For package maintainers and hackage trustees

Candidates

  • No Candidates
Versions [RSS] 0.0.0.0
Change log CHANGELOG.md
Dependencies aeson (>=2 && <2.3), async (>=2.2 && <2.3), base (>=4.16 && <5), bytestring (>=0.11 && <0.13), ghc-experimental (>=0.1 && <0.2), jsaddle (>=0.9 && <0.10), stm (>=2.5 && <2.6) [details]
License CC0-1.0
Author amesgen
Maintainer amesgen@amesgen.de
Category Web, Javascript
Home page https://github.com/amesgen/jsaddle-wasm
Bug tracker https://github.com/amesgen/jsaddle-wasm/issues
Source repo head: git clone https://github.com/amesgen/jsaddle-wasm
Uploaded by amesgen at 2024-10-20T13:44:33Z
Distributions
Downloads 14 total (14 in the last 30 days)
Rating (no votes yet) [estimated by Bayesian average]
Your Rating
  • λ
  • λ
  • λ
Status Docs uploaded by user
Build status unknown [no reports yet]

Readme for jsaddle-wasm-0.0.0.0

[back to package description]

jsaddle-wasm

CI [Hackage](https://hackage.haskell.org/package/jsaddle-wasm Haddocks)

Run JSaddle JSM actions with the GHC WASM backend.

This can for example be used to compile and run Miso or Reflex apps in the browser.

[!IMPORTANT] This project is in an early stage.

Examples

Miso

Several Miso examples: https://github.com/tweag/ghc-wasm-miso-examples

How to use

Install a WASM-enabled GHC with support for the WASM JSFFI from ghc-wasm-meta (GHC 9.10 or newer).

Assuming you built your application as an app :: JSM ():

import Language.Javascript.JSaddle.Wasm qualified as JSaddle.Wasm

foreign export javascript "hs_start" main :: IO ()

main :: IO ()
main = JSaddle.Wasm.run app

Build the WASM binary with the following GHC options:

ghc-options: -no-hs-main -optl-mexec-model=reactor "-optl-Wl,--export=hs_start"

Now, run the post-linker script as described in the GHC User's Guide; we will call the resulting JavaScript file ghc_wasm_jsffi.js.

Then, following the GHC User's Guide, you can run the WASM binary in the browser via e.g. browser_wasi_shim:

import { WASI, OpenFile, File, ConsoleStdout } from "@bjorn3/browser_wasi_shim";
import ghc_wasm_jsffi from "./ghc_wasm_jsffi.js";

const fds = [
  new OpenFile(new File([])), // stdin
  ConsoleStdout.lineBuffered((msg) => console.log(`[WASI stdout] ${msg}`)),
  ConsoleStdout.lineBuffered((msg) => console.warn(`[WASI stderr] ${msg}`)),
];
const options = { debug: false };
const wasi = new WASI([], [], fds, options);

const instance_exports = {};
const { instance } = await WebAssembly.instantiateStreaming(fetch("app.wasm"), {
  wasi_snapshot_preview1: wasi.wasiImport,
  ghc_wasm_jsffi: ghc_wasm_jsffi(instance_exports),
});
Object.assign(instance_exports, instance.exports);

wasi.initialize(instance);
await instance.exports.hs_start();

Potential future work

  • Take a closer look at synchronous callbacks (no special handling currently, but basic things like stopPropagation already seem to work fine).
  • Testing (e.g. via Selenium).
  • Add logging/stats.
  • Performance/benchmarking (not clear that this is actually a bottleneck for most applications).
    • Optimize existing command-based implementation.

      • Reuse buffers
      • Use a serialization format more efficient than JSON.
    • Implement ghcjs-dom API directly via the WASM JS FFI.

      This would involve creating a ghcjs-dom-wasm package by adapting the FFI import syntax from ghcjs-dom-jsffi/ghcjs-dom-javascript appropriately.

      Currently, the generic ghcjs-dom-jsaddle seems to work fine, so it seems sensible to wait with this until benchmarks or other concerns motivate this.