Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/dflemstr/rust-native-wasm-loader


https://github.com/dflemstr/rust-native-wasm-loader

loader rust webpack-loader

Last synced: about 2 months ago
JSON representation

Awesome Lists containing this project

README

        

# `rust-native-wasm-loader`

[![Build Status](https://travis-ci.org/dflemstr/rust-native-wasm-loader.svg?branch=master)](https://travis-ci.org/dflemstr/rust-native-wasm-loader) [![rust-native-wasm-loader](https://img.shields.io/npm/dt/rust-native-wasm-loader.svg)](https://www.npmjs.com/package/rust-native-wasm-loader) [![npm](https://img.shields.io/npm/v/rust-native-wasm-loader.svg)](https://www.npmjs.com/package/rust-native-wasm-loader)

This is a webpack loader that loads Rust code as a WebAssembly module. It uses the native Rust
support for compiling to `wasm32` and does not require Emscripten.

* [rust-native-wasm-loader](#rust-native-wasm-loader)
* [Usage](#usage)
* [Short version](#short-version)
* [Available loader options](#available-loader-options)
* [Long version](#long-version)
* [wasm-bindgen experimental support](#wasm-bindgen-experimental-support)
* [cargo-web experimental support](#cargo-web-experimental-support)

# Usage

If you already know how to use Rust and Webpack, read the "Short version" of this section. If you
want a full example, read the "Long version."

## Short version

Add both this loader and `wasm-loader` to your Webpack loaders in `webpack.config.js`:

```js
module.exports = {
// ...
module: {
rules: [
{
test: /\.rs$/,
use: [{
loader: 'wasm-loader'
}, {
loader: 'rust-native-wasm-loader',
options: {
release: true
}
}]
}
]
}
}
```

Then, specify that your Rust library should be a `cdylib` in `Cargo.toml`:

```toml
[lib]
crate-type = ["cdylib"]
```

Now you can import any functions marked with `#[no_mangle]` as WebAssembly functions:

```js
import loadWasm from './path/to/rustlib/src/lib.rs'

loadWasm().then(result => {
const add = result.instance.exports['add'];
console.log('return value was', add(2, 3));
});
```

## Available loader options

- `release`: `boolean`; whether to compile the WebAssembly module in debug or release mode;
defaults to `false`.
- `gc`: `boolean`; whether to run `wasm-gc` on the WebAssembly output. Reduces binary size but
requires installing [wasm-gc][]. Defaults to `false` and is a no-op in `wasmBindgen` or
`cargoWeb` mode.
- `target`: `string`; the Rust target to use; this defaults to `wasm32-unknown-unknown`
- `wasmBindgen`: `boolean` or `object`; use `wasm-bindgen` to post-process the wasm file. This
probably means that you need to chain this loader with `babel-loader` as well since
`wasm-bindgen` outputs ES6 (or typescript).
- `wasmBindgen.debug`: `boolean`; run `wasm-bindgen` in debug mode.
- `wasmBindgen.wasm2es6js`: `boolean`; use `wasm2es6js` to inline the wasm file into generated
Javascript. Useful if webpack is not configured to load wasm files via some other loader.
- `wasmBindgen.typescript`: `boolean`; emit a typescript declaration file as part of the
build. This file will automatically be referenced, and in a way that `ts-loader` will pick
it up but it's still possible to treat the output from this loader like a normal Javascript
module compatible with `babel-loader`.
- `cargoWeb`: `boolean` or `object`; use `cargo-web` to compile the project instead of only
building a `wasm` module. Defaults to `false`.
- `cargoWeb.name`: `string`; the file name to use for emitting the wasm file for `cargo-web`,
e.g. `'static/wasm/[name].[hash:8].wasm'`.
- `cargoWeb.regExp`: `string`; a regexp to extract additional variables for use in `name`.

## Long version

First, you need Rust installed. The easiest way is to follow the instructions at [rustup.rs][].

Then, you need to add support for WebAssembly cross-compilation. At the time of writing, this
requires using the `nightly` compiler:

```text
rustup toolchain install nightly
rustup target add wasm32-unknown-unknown --toolchain nightly
```

The next step is to integrate a cargo/node project. Let's assume we don't already have one, so we
create one:

```text
cargo init add
cd add
```

If nightly is not your system default toolchain, create a file named `rust-toolchain` containing
the toolchain name you want to associate with the project:

```
echo nightly > rust-toolchain
```

We can add the Rust code that should be available in the WebAssembly module to `src/lib.rs`. All
functions that should be reachable from WebAssembly should be marked with `#[no_mangle]`:

```rust
#[no_mangle]
pub fn add(a: i32, b: i32) -> i32 {
eprintln!("add({:?}, {:?}) was called", a, b);
a + b
}
```

Then, specify that your Rust library should be a `cdylib` in `Cargo.toml`:

```toml
[lib]
crate-type = ["cdylib"]
```

Now you can actually start to use this loader. This loader itself does not create Javascript code
for loading a WebAssembly module, so you need to compose it with another loader, like `wasm-loader`:

```text
yarn init
yarn add --dev webpack
yarn add --dev rust-native-wasm-loader wasm-loader
```

The loaders can be registered the usual way by adding them to your `webpack.config.js`:

```js
const path = require('path');

module.exports = {
entry: './src/index.js',
output: {
filename: 'index.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.rs$/,
use: [{
loader: 'wasm-loader'
}, {
loader: 'rust-native-wasm-loader',
options: {
release: true
}
}]
}
]
}
};
```

You can now import the WebAssembly module by using the main `.rs` file from your Cargo project
(`lib.rs` or `main.rs`); e.g. from your `index.js`:

```js
import loadAdd from './lib.rs'

loadAdd().then(result => {
const add = result.instance.exports['add'];
console.log('return value was', add(2, 3));
});
```

You can now run webpack and the resulting code from node.js or a browser:

```text
$ yarn run webpack
$ node dist/index.js
return value was 5
```

# `wasm-bindgen` experimental support

You can use experimental `wasm-bindgen` support with the following options:

```js
{
test: /\.rs$/,
use: [
{
loader: 'babel-loader',
options: {
compact: true,
}
},
{
loader: 'rust-native-wasm-loader',
options: {
release: true,
wasmBindgen: {
wasm2es6js: true,
},
}
}
]
}
```

and edit your `lib.rs`:

```rust
extern crate wasm_bindgen;
use wasm_bindgen::prelude::*;

#[wasm_bindgen]
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
```

The loader now uses `wasm-bindgen` to build the project. If you are using webpack 4, it has experimental native support for importing WASM files, so you probably
don't need the `wasm2es6js` flag. If you are using webpack 3 (or bundling for Chrome and it's 4K limit on main thread WASM), it is needed in order to inline
the loading of the wasm file correctly. By using `wasm2es6js`, the loader returns a normal Javascript module that can
be loaded like so:

```js
import { add, wasmBooted } from './path/to/rustlib/src/lib.rs'

wasmBooted.then(() => {
console.log('return value was', add(2, 3));
});
```

# `cargo-web` experimental support

You can use experimental `cargo-web` support with the following options:

```js
{
loader: 'rust-native-wasm-loader',
options: {
cargoWeb: true,
name: 'static/wasm/[name].[hash:8].wasm'
}
}
```

The loader now uses `cargo-web` to build the project, and as a result needs to emit the wasm file
separately. The loader now returns a normal Javascript module that can be loaded like so:

```js
import loadWasm from './path/to/rustlib/src/lib.rs'

loadWasm.then(module => {
console.log('return value was', module.add(2, 3));
});
```

[rustup.rs]: https://rustup.rs/
[wasm-gc]: https://github.com/alexcrichton/wasm-gc