What is the best practice to build an NPM package with wasm in 2022?

Hey all, I've been doing a little bit of rust wasm in the past, mostly via wasm-pack to build hybrid JS-Rust-Wasm web applications. I've always used wasm-pack, and always compiling with --target web since I do not want anything to do with a JS bundler like webpack.

We are almost in 2022, and I'd like to do something I've not done yet, which is create a JS package (npm I guess) wrapping a wasm module exposing a single rust function. The function I want to expose looks like this:

pub fn solve_deps_with<Fetch, L>(
    config: &SomeConfig, 
    fetch_elm_json: Fetch,     // function passed as argument
    list_available_versions: L // function passed as argument
) -> Result<Solution, SomeError> 

where
    Fetch: Fn(&Pkg, Version) -> Result<SomeConfig, Box<dyn Error>>,
    L: Fn(&Pkg) -> Result<Vec<Version>, Box<dyn Error>>,

This means I'll have to specify that those injected functions are extern.

So my question is the following. In late 2021, early 2022, what is the best practice to create a JS package with rust/wasm, that can easily be used via npm by other people? Is it still wasm-pack, is it something else? What other tools should I use to make sure I bundle a package with the minimum size possible? I expect this function to be very small if stripped of all unnecessary stuff from dependencies since it essentially is a pure function doing only computation, where all side effects are brought in via external functions passed as arguments.

1 Like

So I've been using wasm-pack successfully for the time being so it does seem to still be the de-facto tool. For now I'm compiling with

wasm-pack build --target nodejs

This creates a node package, that I can easily import in another node js project. For now, for dev purposes, I have an example usage with the following example/package.json:

{
  "name": "example",
  "version": "1.0.0",
  "main": "index.js",
  "dependencies": {
    "my-wasm-pkg": "file:../pkg"
  }
}

But eventually, when my-wasm-pkg is published on npm, I could replace file:../pkg by a version number.
Then inside my example/index.js I can simply do the following:

// example/index.js
let wasm = require("my-wasm-pkg");
// now use exposed public functions in wasm

One thing I'm still struggling with though, is minimizing the size of the produced wasm module. It seems that there is good advice regarding "shrinking .wasm size" and regarding profiling code side with twiggy. However, all the indications there are very complicated to apply when your wasm package lives in a cargo workspace. In my situation I have a workspace split in 3 such as:

[workspace]
resolver = "2"
members = [
  "my-lib",
  "my-cli",
  "my-wasm-pkg",
]

And in that situation, all indications implying changes of the release profile do not seem to work when those config are put inside workspace/my-wasm-pkg/Cargo.toml with messages of the like:

warning: profiles for the non root package will be ignored, specify profiles at the workspace root

EDIT: I've extracted another dedicated question about this: How to shrink .wasm size in a cargo workspace?

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.