Improving wasm imports with wrangler and Cloudflare Workers

WebAssembly (wasm) has been supported by Cloudflare Workers since 2018. Enabling WebAssembly support has extended the functionality of Workers so much in the last 5 years, enabling use-cases that simply weren't feasible in JavaScript beforehand.

However, the workflow and DX for importing wasm into your Worker via wrangler has been less than ideal, especially when working with third-party wasm libraries.

TL;DR

You can now import wasm files in wrangler >= 3.15.0 like this:

import wasm from '@resvg/resvg-wasm/index_bg.wasm'

instead of like this:

import wasm from '../../node_modules/@resvg/resvg-wasm/index_bg.wasm'

Prior art

The most common solutions in the past for importing wasm from a third party library would be to either:

  • copy the bundled .wasm file out of the node_modules directory and into your own src (etc.) dir for easy import
  • or, import using a relative path

The first option felt very hacky to me, so I opted for the second. Some folks were even making this a part of their build step, and while this felt a little better, it still felt unnecessary to me.

Initial code

My initial code ended up looking something like this:

import wasm from '../../node_modules/@resvg/resvg-wasm/index_bg.wasm'

It worked well, and was better than copying the wasm file into my own src directory, but it still felt a little unergonomic and flimsy. Looking into the package some more, I saw it had entrypoints setup via exports in their package.json, so I hoped I would be able to make use of this and simply import from @resvg/resvg-wasm/index_bg.wasm, however this did not work and resulted in an error like this from wrangler:

✘ [ERROR] ENOENT: no such file or directory, open 'E:\<path>\src\@resvg\resvg-wasm' [plugin wrangler-module-collector]

For some reason, it was being treated as a relative path.

Investigation

plugin wrangler-module-collector gave me some insight that this was a custom esbuild plugin wrangler was using, so I started investigating there. I quickly found the custom esbuild plugin wrangler had implemented in their source:

workers-sdk/packages/wrangler/src/deployment-bundle/module-collection.ts at 2526794f214e730f7f88a8146ef24f50c2caf8f6 · cloudflare/workers-sdk
⛅️ Home to Wrangler, the CLI for Cloudflare Workers® - cloudflare/workers-sdk

This plugin was handling all imports to **/*.wasm, so that they'd be bundled into the Worker as needed, but was treating every path as a relative one.

I figured it should be pretty easy to add npm resolution support to this so I could import @resvg/resvg-wasm/index_bg.wasm as originally desired, so I dove in and started hacking away at Wrangler.

Implementation

Wrangler is part of Cloudflare's workers-sdk repository, powered by Turbo and pnpm, so it was relatively straight forward to jump in and start working. They have a detailed CONTRIBUTING guide too, which I was already somewhat familiar with.

I was looking for an easy way to use the entrypoint exposed via exports in the package, without having to re-implement all of that logic myself - there's so much build tooling in the JavaScript ecosystem, so someone had to have built something for this already. I quickly ran across resolve.exports, a package used by so much of JS ecosystem including Vite, Jest, and much more, so I was confident it would work well here.

Submitting my changes

You can find my full Pull Request below:

feat: support npm resolution for file imports by Cherry · Pull Request #4135 · cloudflare/workers-sdk
Fixes #4098. What this PR solves / how to test: Previously when importing static files (such as wasm) from an npm package, you had to path directly to it, such as : import wasm from ’../../node_mod…

I was very happy with how it turned out, and after a little back and forth with the awesome wrangler team, it was well tested, and ready for merge.

Final code

As of wrangler 3.15.0, you can now simply import wasm files from packages, assuming they setup an entrypoint via exports in their package.json like this:

import wasm from '@resvg/resvg-wasm/index_bg.wasm'

A small win, but much nicer ergonomics and developer experience when working with wasm in Cloudflare Workers! 🎉

You've successfully subscribed to James Ross
Great! Next, complete checkout for full access to James Ross
Welcome back! You've successfully signed in.
Success! Your account is fully activated, you now have access to all content.