Skip to content

Rollup plugin for loading wasm files created through wasm-bindgen

License

Notifications You must be signed in to change notification settings

Alorel/rollup-plugin-wasm-bindgen-web

Repository files navigation

rollup-plugin-wasm-bindgen-web

CI Coverage Status Language grade: JavaScript

Note: semantic-release starts at v1, but this plugin is in early alpha and should be considered unstable and prone to breaking changes, although any such changes will follow semantic versioning.


Table of Contents

What is this

This is a rollup plugin for importing wasm files generated by wasm-bindgen; it's an early proof of concept and may or may not handle all scenarios.

Installation

Add the registry to .npmrc:

@alorel:registry=https://npm.pkg.github.com

Then install it:

npm install @alorel/rollup-plugin-wasm-bindgen-web

Requirements & basic setup

  • wasm-bindgen must output in web mode
  • the resulting js file must be imported via dynamic import, i.e. import('./foo')

Given a Rust input like this:

use wasm_bindgen::prelude::*;

#[wasm_bindgen(js_name = multiplyByTwo)]
pub fn multiply_by_two(num: i32) -> i32 {
    num * 2
}

And a build command sequence like this:

cargo build --release --target wasm32-unknown-unknown;
wasm-bindgen target/wasm32-unknown-unknown/release/my_lib.wasm --out-name my_lib --target web;

You'd produce four files: my_lib.js, my_lib.d.ts, my_lib_bg.wasm, my_lib_bg.wasm.d.ts

The file you want to be import()ing is my_lib.js; it contains an asynchronous initialiser function that the plugin will call and await on before giving you the import's response.

The transformation

Whenever the plugin sees that you're importing the js file of your wasm-bindgen output, it'll emit the associated wasm file as an asset and reformat your import:

import('./wasm/my_lib')
  .then(lib => console.log(lib.multiplyByTwo(2), "That'll be 4"));

will get transformed into

import __wasmBindgenRollupPluginDynamicImportLoader from '@alorel/rollup-plugin-wasm-bindgen-web/autoinit-wasm-import';

__wasmBindgenRollupPluginDynamicImportLoader(import('./wasm/my_lib'), 'urlOfWasmFile.wasm')
  .then(lib => {/* ... */
  });

Where the loader returns the following:

Promise
  .all([dynamicImport, fetch(url)])
  .then(([wasmModule, response]) => (
    wasmModule.default(response)
      .then(() => wasmModule)
  ));

The plugin config

import {wasmBindgen} from '@alorel/rollup-plugin-wasm-bindgen-web';

export default {
  // ... your config
  plugins: [
    wasmBindgen(config)
  ]
}

Where config is the following interface:

/** Standard Rollup filter */
interface Filter {
  exclude?: FilterPattern;

  include?: FilterPattern;
}

interface Opts {

  /**
   * When set to true, adds a __getWasm() function to the import() response which returns what the original
   * init function returned
   * @default false
   */
  exposeWasm?: boolean;

  /**
   * JS source files to look for dynamic imports in.
   * @default {include: /\.[jt]sx?$/}
   */
  jsFilter?: Filter;

  /**
   * Whether to include a source map in the generated code
   * @default true
   */
  sourceMap?: boolean;

  /**
   * Filter to match dynamically imported wasm-bindgen output files
   * @default Don't match anything
   */
  wasmFilter?: Filter;
}

Filtering

There are two sets of files to match. Using the my_lib example from earlier,

  • wasmFilter should match my_lib.js
  • jsFilter should match files that'll contain the dynamic import()s

Raw wasm output

By default, the response of the original init function is hidden. You can turn the exposeWasm option on to append a __getWasm function to the module which will return it. If you're using Typescript, you can correct your typings by adding the following to your Rust source files (constant name doesn't matter):

#[wasm_bindgen(typescript_custom_section)]
const TS_APPEND_CONTENT: &'static str = r#"
export function __getWasm(): InitOutput;
"#;