Rust + WebAssembly + JavaScript
Tags: javascript, rust, webpack, typescript
Published: 2021-11-10 23:09:57


### Introduction

WebAssembly is the new craze when it comes to frontend & backend capabilities. The part that has me excited about it is the concept that you can use the same library for both frontend & backend. (provided the backend is in Rust or NodeJs.)

> [Skip to source](https://github.com/mbround18/webpack-wasm-template)

### Getting Started

> Expected Prerequisites:
> - [Rust](https://www.rust-lang.org/learn/get-started)
> - [NodeJs](https://nodejs.org/en/)
> - [Yarn](https://yarnpkg.com/getting-started/install)

First, we need to establish the goal of this little tutorial. This tutorial is to give a little demonstration of how to simply get Webpack to compile our Rust code into WebAssembly and import it on the frontend. To get started, lets create a new folder and enter it:

```sh
mkdir -p ./wasm-demo
cd ./wasm-demo
```

Now with this empty folder, lets start by adding the necessary files to get up and running.  

#### Setting up Yarn

> [If you are curious why Yarn 2+, click here!](https://snyk.io/blog/yarn-2-intro/)

For the yarn init, you can simply select all the defaults. 
```sh
yarn init 
``` 
We are converting this project to a Yarn 2+ for speed.
```sh
yarn set version berry
```

#### Getting our rust crate created

```sh
cargo new \
  --edition 2021 \
  --lib \
  --name hello-world \
  ./hello-world
```

#### Adding the root `Cargo.toml`

Now, create a new file called `Cargo.toml` in the root of your project. The contents of the `Cargo.toml` should be a simple workspace:

```toml
[workspace]
members = [
  "hello-world"
]
```
We create a `Cargo.toml` at the root of the project to allow us to have multiple crates in a given repository and manage them all together. 

#### Finally, adding JavaScript dependencies 

**Development dependencies**
```sh
yarn add -D \
  webpack \
  webpack-cli \
  webpack-dev-server \
  @wasm-tool/wasm-pack-plugin \
  html-webpack-plugin
```

### Configuring Webpack

Getting the Webpack configurations to be just right can be tedious for any project. However, with Rust & WebAssembly it can be relatively easy! Lets start by creating the files we will need to get running:

#### Setting up the basic files

Our application directory:
```sh
mkdir -p ./src
```

Webpack config file:
```sh
touch ./webpack.config.js
```

Basic application file: 
```sh
touch ./src/index.js
```

#### Editing the webpack config

```js
// Path lets us map out where files should be:
const path = require("path");

// HtmlWebpackPlugin will let us inject html into our app
const HtmlWebpackPlugin = require('html-webpack-plugin');

// WasmPackPlugin does the magic of building our application
const WasmPackPlugin = require('@wasm-tool/wasm-pack-plugin');

module.exports = {
    experiments: {
        // This allows WebAssembly to be bundled
        asyncWebAssembly: true
    },
    // Its good practice to specify the mode
    // But this can also be done via `--mode`
    mode: process.env.NODE_ENV || "development",
    // This entry should be pathing to the index.js
    entry: path.join(__dirname, "src/index.js"),
    output: {
        // Always clean your output folder!
        // Otherwise, you can end up with dangling code when developing.
        clean: true,
        path: path.resolve(__dirname, './dist'),
        filename: 'bundle.js',
    },
    plugins: [
        // We point our WasmPackPlugin to the location of the
        // the crates `Cargo.toml` file. Never the root file.
        new WasmPackPlugin({
            crateDirectory: path.join(__dirname, "hello-world")
        }),
        // Some basic boiler plate, 
        // Device width meta lets us make it mobile friendly. 
        new HtmlWebpackPlugin({
            meta: {viewport: 'width=device-width, initial-scale=1, shrink-to-fit=no'},
            // This will be used later
            templateContent: "<div id='app'></div>",
            inject: "body"
        })
    ],
    resolve: {
        // A little overkill for our tutorial but useful.
        extensions: [".ts", ".tsx", ".js", ".jsx", '.mts', '.mjs', '...'],
    }
}
```

### Getting our Rust code ready

#### Adding Dependencies 

In order to compile to WebAssembly, we need to make a few adjustments. First, we will need to install the build tools:
```sh
cargo install wasm-pack
```

Next, we will need to modify the `hello-world/Cargo.toml` file:

```toml
[package]
name = "hello-world"
version = "0.1.0"
edition = "2021"

# We add cdylib and rlib to tell rust
# it needs to build for WebAssembly and Rust
[lib]
crate-type = ["cdylib", "rlib"]

[dependencies]
# wasm-bindgen builds the JavaScript bindings
# Take a look at the following link for more info:
# https://rustwasm.github.io/docs/wasm-bindgen/
wasm-bindgen = "0"
```

#### Adding a main function

Now, we edit the file at `hello-world/src/lib.rs`:

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

// The main function which will be referenced in JavaScript
#[wasm_bindgen]
pub fn main() -> String {
    // Returning a string to use in JavaScript land.
    String::from("Hello, world!")
}
```

#### Building to ensure it works

At the root of the project, run the following command to make sure the rust code is good:

```sh
cargo build
```

You should get a success build: 
![Successful Build](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w22h44hsh1on81a99pr7.png)
 

### Getting our JavaScript code ready

Now that we have our Rust code ready to rock and roll, lets get our JavaScript ready by editing the `src/index.js` file:


```js
/**
 * When importing a Rust WebAssembly crate,
 * you must always add a /pkg to the import like below.
 * This is because, when webpack builds the crate it will output the contents into a pkg folder.
 * If you wish to manually build, you can use the command `wasm-pack build --target web` inside the `hello-world` folder
 */
import * as helloWorld from '../hello-world/pkg'


document.getElementById("app").innerText =
    /**
     * Now we can simply call the function `main` from our Rust package :)
     */
    helloWorld.main();

```

### Running the code

Now we have all of our code set up and ready to go! If we simply run: 

```sh
yarn webpack serve
```

It should build the project successfully and give you a web address you can use to access your application! look for `loopback` and click that url or open it up in your browser: 
![Loopback](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ghg7wmxazukete2e0bw6.png)

Once the application is open in your browser, it should look a little something like this:

![Successful App](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yl4vmjdoo5r2uyuqz1gi.png)
  


### Links

[Click here to take a look at the source!](https://github.com/mbround18/webpack-wasm-template/tree/main)