Runing main after emscriptem onRuntimeInitialized

I’m trying to load a the starter Purescript app in the emscriptem generated module callback. Although my problem is specific to this setup, I think it can be generalised to being able to run Purescript main after a callback. Emscriptem calls a Module.onRuntimeInitialized () if defined once it has loaded WASM and done all its setup.

I’m trying the below:

index.html

...
<script src="../output/index.js"></script>
  <script>
    var Module = {};
    Module.onRuntimeInitialized = () => {
      Main.main
    };
  </script>
  <script async src="../assets/output.js" ></script>
  
...

Main.purs is nothing but:

main :: Effect Unit
main = do
  log "🍝"

Here output.js is the emcc js output. It all works fine with a non-Purescript page.

I’ve built with spago bundle-module, used parcel and am serving with a normal web server. The error I see is

Uncaught ReferenceError: module is not defined

from the generated Javascript

module.exports = PS["Main"];

Oh an confusingly parcel told me to add the below to my HTML (which didn’t change anything)

<script async src="../assets/output.js"  type="module">

Is this method of calling a PS module even possible? Or should I be using FFI instead to handle the onRuntimeInitialized callback?

Thanks

Sumit

If it’s isn’t a type can you try

Module.onRuntimeInitialized = () => {
  Main.main();
};

instead?


Usually I “require” the purescript module in the index.js for Parcel but I think it should work as you did as well.

the type="module" stuff was introduced with with parcel lately - to be honest: I have no clue why but it works for me.

So just to confirm you have an index.js that “requires” the Purescript index.js? So your HTML references a hand coded index.js which is in the HTML?

If so, this is not what I’m doing so will give it a try. Thanks.

I usually have a index.js like this:

import { main } from '../output/Main';

main();

that is used in the index.html:

<body>
    <script src="index.js" defer></script>
</body>

(older version of parcel here - you’ll need the type=module stuff)

This is for development, where I’ll use the compiled purescript modules (it works a bit like Hot-Module-Reloading this way … well minus the persisted model)

For production I’ll compile the purescript using spago bundle-app (I had not big success with further tree-shaking/minification so I usually don’t bother) and change the index.js to

import { main } from '../dce-output/Main'

main()

(or something similar)

in your case you’ll need your wait/continuation so I’d try adding this to the index.js or reference ../output/Main from your index.html and try there - no matter what usually you start your app by calling main()

1 Like

Ah what a palaver but I’ve got it working…

I had to build the library correctly using

 emcc -s EXPORT_ES6=1 -s MODULARIZE=1 libblst.a -o assets/output.js

Then I used the method you’ve described above:

import { main } from '../output/Main';

import Module from '../assets/output';

let instance = Module({
  onRuntimeInitialized() {
    console.log("hello");
    main();
  }
});
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Emscriptem</title>

</head>

<body>
  <script defer src="index.js" type="module"></script>
</body>

</html>

And it worked. Parcelv2 even copied the output.wasm to the correct location which it wasn’t doing before. I’ll need to see if I can get a spago build-module version working.

Thanks for the pointers.

3 Likes