developit / microbundle

📦 Zero-configuration bundler for tiny modules.
https://npm.im/microbundle
MIT License
8.05k stars 362 forks source link

Bundling a module that depends on WASM resources #1042

Closed donmccurdy closed 1 year ago

donmccurdy commented 1 year ago

I'm interested in bundling a library that depends on WebAssembly/WASM resources. The WASM portion of the project is compiled from AssemblyScript. AssemblyScript generates JS bindings with "node" or "web" targets using fs.readFile or fetch(...) respectively. I'm not sure those options are appropriate for building a library that runs on the web, as opposed to an application, so it's my expectation that I'll have to write my JS bindings by hand anyway, with separate node.js and bundler/web-oriented entry points.

With that background — is there a recommended or supported way to load WASM binaries, in a library module built with Microbundle? Ideally, I think I want the compiled import published to NPM to end up looking like:

import * as wasm from 'build.wasm';

Alternatively, there's @rollup/plugin-wasm which inlines a base64 string. That's more widely compatible, but at the cost of +33% size and a large performance hit. Base64 is probably my second choice for those reasons.

Does Microbundle support one of these options, or something else? And/or, would the project be open to PRs for support if that is currently missing?

Thanks!

donmccurdy commented 1 year ago

On further reading, this might be the pattern I should be using:

https://web.dev/bundling-non-js-resources/#universal-pattern-for-browsers-and-bundlers

donmccurdy commented 1 year ago

I've arrived at a solution here that seems to be working well, and hasn't required any changes to Microbundle. In order to support a broad range of targets, I'm running Microbundle three times:

  1. Node.js target, using fs.readFile to load the WASM binary.
  2. Web+Bundler target, using fetch() to load the WASM binary.
  3. Compatibility target for old bundlers, using an inline Data URI to load the WASM Binary.

I use the --alias flag to choose among the appropriate loading methods, and --define to inline the WASM Data URI. Full configuration:

https://github.com/donmccurdy/keyframe-resample-wasm/blob/480911d7c12ced870e37b599e9dc8d64200731a4/package.json#L8-L39

If this solution seems like a fair one and no changes are needed to Microbundle, please feel free to close this issue. :)

rschristian commented 1 year ago

Sorry for the lack of response.

Indeed, that seems like a pretty good solution, and doesn't require anything too hacky to get around Microbundle's lack of configuration. Using Rollup directly you could move that into a replace plugin or something, though I'm not sure that'd even be an improvement over what you have already.

Maybe it'd be neat to extend the functionality of --target to help out in situations like this, though I'm not sure what that'd look like.

donmccurdy commented 1 year ago

I'm still learning more about AssemblyScript, but so far very happy with how this is working. Will wait to see if the current packaging process works for my project's end-users, and hopefully write up a blog post after that. No changes needed, thanks!