Bringing in a Web Devs Tool Chain
Up until now we’ve been writing our Go code and then using the
go build command to generate our WebAssembly bundle, and sure, this works fine, does what we need it to do, but it doesn’t really fit with how we web developers would be approaching it.
I prefer webpack so I decided to look at incorporating it into my process by writing a custom Loader.
A Quick Intro to webpack
If you’re unfamiliar with webpack you really should check out their docs as I won’t do it justice here. Instead I want to focus on the core part of webpack that we need to leverage and how it works.
And tell webpack to use the right loader when it finds a
*.go file to hopefully generate what we need it to generate.
Ultimately, our goal is to be able to write something like this:
Now let’s look at how we achieve this.
Creating a Loader
TL;DR: If you don’t really want to see the process you can just check out the source code for the loader and install it into your own project.
go build, not the file contents.
But never fear, the loader has a Loader API that we can leverage, and the first thing is that we want to get
resourcePath which gives us the full path to the file. Fantastic, now we are able to send that over to
Generating WASM in our Loader
We’re going to need to execute
go build in our loader, and to do that we can use
child_process to spawn it.
But before that we’ll need to find the path to the
go binary and for that we’ll use the
GOROOT environment variable (that we learnt about in the first post).
Finally we’re going to use
execFile which is asynchronous, and we’ll have to tell webpack that this loader is
I’m also creating the environment variables (in
opts) that sets the appropriate
GOARCH for WASM.
For the file that we generate, I’ll just append
.wasm to the end of the resource that we’re processing. This means that we should be fine writing to disk, but some error handling on the writability of the disk could be useful…
We’re successfully generating our WASM file but it’s a) dropped in what’s likely our
src folder, not where the rest of the webpack bundles will go and b) we still have to write a bunch of code to use it.
- The WebAssembly loader
Well I think this is something that webpack should do for us, we don’t want to have to write that code ourselves!
We’re going to build up a large string template to send back to webpack, starting with the bootstrapper for WebAssembly:
This code will be inserted into our bundle and used when we
import wasm from './main.go', but that only starts up the WASM runtime, what about accessing the stuff we registered?
I decided that I want to enforce the callback pattern from the last post, and that means we’ll need to return something, but what the heck should we return? We’ve got no idea what the names of the functions from Go will be, so how do we know what to return to the
If you’ve ever done programming with Ruby you may have come across the
method_missing method on
BasicObject which you can use to do metaprogramming. In C# you can do a similar thing with the DLR.
But if you haven’t come across this, basically it’s a special function that gets executed on an object when there are no members of it that match, a last ditch attempt to handle an error before it is thrown.
method_missing from Ruby.
Here’s a basic example:
And we’ll see:
> "captured call to foo" > "foo" > "captured call to bar" > "method_missing"
So we can capture all calls and do something with them before, after or completely replace them.
And we’re going to use that to wrap WASM with our callback pattern:
Because we register stuff on the global object our proxy is actually of a blank object, since we don’t really want to proxy
window, and anyway we can just ignore the
target that the proxy receives anyway.
Putting it all together
It’s time to put together our template that we’ll give back to webpack, and that will be executed when you import a Go file:
Ok, it’s a little more advanced that the few snippets above, but let me explain some of the additions:
- Since we are asynchronously loading the WASM file using
fetchthere is the possibility that we’d try and use an exported function before it’s been made available. This would most likely happen if you have a large bundle and/or a slow network connection, so I’ve introduced a
sleepfunction which uses
requestAnimationFrameas a sleeper (so chucking stuff in the event loop) and waiting until the WASM initialization function completes and sets
- I’ve aliased the global that we’re working with so you can use the generated code in Node.js or a browser
- I’m not exposing it as a callback pattern, instead I’m exposing it as a
Promise, meaning you can
- I added some error handling, if you call a function that can’t be found the
- It also supports setting values not just functions from Go
Finishing our Loader
Generating WASM file? ✔
Time to combine all of this together so that we can actually run the Loader.
Remember how we generated the WASM file into the same location on disk as the original
.go file? Well that’s fine to output as
go build requires, but we actually want it to go with the rest of the webpack output. To do this we use the
emitFile method on the loader context, providing it the contents of the file as a
Buffer. That’s why I use
readFileSync to get the file into memory, then I
unlinkSync to delete it from disk, since the original output isn’t needed anymore.
Finally I generate a
require statement to the
wasm_exec.js file that is bundled with the loader (I had to make a minor change to it so it worked with webpack). You’ll see this message in the debugging console:
../lib/wasm_exec.js 9:19-26 Critical dependency: require function is used in a way in which dependencies cannot be statically extracted
This is because the
wasm_exec.js file is being added as a
require statement to webpack but we’re not explicitly exporting anything from it (since it just augments the
global scope), meaning webpack is unsure what we’re actually using in there and it can’t undertake tree shaking to remove unneeded code (and thus optimise the application).
All the code for the loader is on GitHub and I’ve published the loader on npm as
golang-wasm-async-loader. GitHub contains a (works on my machine) example of it in action if you’d like to try it out.
Bonus Round: Ditching Globals and Improving the Go Experience
The astute observer among you will have looked at the source code published to GitHub and noticed it’s not quite what I posted above.
One thing that’s constantly irked me with the stuff I’d read from Go on how to work with WASM is that everything seems to use
js.Global as a dumping place for their functions/values/etc. and that is rather unpleasant because you shouldn’t pollute
So the loader’s GitHub repository also contains a Go package called
This means I can write some code like so:
gobridge.RegisterCallback and not worry about working with
And that latter part is important because I don’t want to dump everything on global, I want to namespace it.
Now our Go code can use that, via the
gobridge and we don’t have to worry about trashing anything on
window in the browser!