Handling WASM module in Rescript + NextJS

Hi peeps :slight_smile: Still trying to integrate a few things to evaluate a potential move to rescript :tada:

One of the last two blocking things (the other one is state management but I’ve seen a couple of examples lying around) is the ability to load WASM modules into the react application.

In Nextjs+TS you would do something like this:

onClick: (function () {
  import('wasm').then(wasm => wasm.greet())
}),

or

onClick: (async function () {
  const wasm = await import('wasm')
  wasm.greet()
}),

I manually wrote this inside my compiled .bs.js and both versions work just as expected (wasm package is compiled by wasm-pack which handles the loading of the .wasm itself)

What I’m struggling on is trying to write a Wasm.res binding so that in my component I can just write

  let handleClick = () => {
    Wasm.greet()
  }

If I write the following:

@module("../pkg")
external greet: unit => unit = "greet"

it compiles to

import * as Pkg from "../pkg";

function greet(prim) {
  Pkg.greet();
}

export {
  greet ,
}

which fails with TypeError: Method Promise.prototype.then called on incompatible receiver #<Promise>

I could maybe add a .js to wrap this in, but ideally I’d like to minimise the amount of .js commited to the repo (I am already not a massive fan of having to wrap the compiled .bs.js to support the nextjs 13 file system pages.

I added a sample repo here GitHub - davodesign84/rescript-next please note you might need to replace your node path in /use-client-mod.cjs

Ideas? Any help would be super appreciated :smiley:

You might be interested in this forum post: RFC: Dynamic imports in ReScript.

You could try making import the external function that returns your module…(playground)

module Wasm = {
  type t

  @val external import: string => promise<t> = "import"
  // Whatever the return type is, you could adjust it...
  @send external greet': t => unit = "greet"

  // JS code you want...
  //
  // (async function () {
  //   const wasm = await import('wasm')
  //   wasm.greet()
  // })
  let greet = async () => {
    let wasm = await import("wasm")
    greet'(wasm)
  }
}

You get this:

async function greet(param) {
  var wasm = await import("wasm");
  wasm.greet();
}

Nice! Thanks @Ryan, this does get me indeed closer! However my noobness now gets stuck because Wasm.greet() it’s a unit => promise<unit> and the onClick handler expects a unit => unit. Is there a recommended way to solve this last bit?

EDIT: I’m a little less noob now, I figured out that I can use ->ignore, everything is working perfectly now :smiley:

1 Like