Skip to content

Latest commit

 

History

History
75 lines (52 loc) · 3.53 KB

README.md

File metadata and controls

75 lines (52 loc) · 3.53 KB

esbuild-plugin-wat

This is a plugin for esbuild which allows you to import .wasm (WebAssembly) and .wat (WebAssembly text format) files.

Both files types will resolve in a default export which is a Uint8Array holding the Wasm binary. It can be directly passed into WebAssembly.instantiate() or WebAssembly.compile().

yarn add esbuild-plugin-wat

Example:

import exampleWasm from 'example.wat';
let {instance} = await WebAssembly.instantiate(exampleWasm);

The bundle produced by esbuild will look similar to this:

var example_default = __toBinary('AGFzbQEAAAABDAJgAn9/AGA...'); // <-- Wasm binary gets inlined as base64
var {instance} = await WebAssembly.instantiate(example_default);

.wat is converted to .wasm with wabt (and cached, for performance).

Usage

// esbuild.config.js // Or:
// esbuild.config.mjs // Use if you get a set "type": "module" error
import {build} from 'esbuild';
import watPlugin from 'esbuild-plugin-wat';

build({
  /* ... */
  plugins: [watPlugin()],
});

Optionally, you can pass a configuration object which currently supports three options:

watPlugin({
  loader: 'file', // how esbuild should load the .wasm file. Default: 'binary'
  inlineFunctions: true, // optimize wasm by inlining all functions. Default: false
  wasmFeatures: {simd: false}, // selectively disable wasm features
  bundle: true // experimental: bundle multiple wat files using import syntax. Default: false
});

The loader option directly translates into choosing an esbuild loader for the .wasm file. For example, instead of "binary" (the default) you could use "base64" if you want to do the base64-decoding yourself, or "file" if you don't want to inline the binary and rather want to fetch it from a separate file.

If inlineFunctions is true, we use binaryen to inline all Wasm functions. This is the only binary optimization so far that I identified as useful when writing raw .wat. If you compile to Wasm from a different language, you will most likely have your own optimization pipeline and you can ignore this option.

The wasmFeatures are passed to parseWat() from wabt.js, see WasmFeatures in the wabt API. Contrary to wabt.js, we enable all features by default (since supporting additional language features at compile time is unlikely to break code not using that feature), so passing true for a feature does nothing and passing false disables it.

Bundling

The experimental bundle option allows you to combine multiple wasm/wat files into one. This is achieved with the following import syntax:

;; main.wat
(import "./util.wat" "some_function" (func $f (param i32) (result i32)))

The statement above tells our bundler to look for the file "./util.wat" at a path relative to main.wat, take the code for "some_function" and include it into our bundled wat (the import statement is removed).

Note that this is normal, spec-compliant wasm syntax, which requires two strings to specify an import. The original intended usage is to specify imports that are dynamically supplied by the host runtime. However, here we instead statically link the wasm modules together.

This enables two things:

  • Split code into multiple files when writing raw wat
  • When importing .wasm, allows to pack code compiled from different languages into one wasm binary