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

https://github.com/mpadge/wasm-next

Demo nextjs repo with rust WebAssembly
https://github.com/mpadge/wasm-next

nextjs wasm wasm-bindgen

Last synced: 6 months ago
JSON representation

Demo nextjs repo with rust WebAssembly

Awesome Lists containing this project

README

        

# nextjs, WebAssembly, and wasm-bindgen

This repository demonstrates how to access WebAssembly compiled from rust in a
nextjs frontend, both with and without
[`wasm-bindgen`](https://github.com/rustwasm/wasm-bindgen). The result is
currently in action at https://wasm-next-xi.vercel.app, which shows three
output panels from three difference WebAssembly interfaces:

## 1. Simple WebAssembly processing of single numeric inputs

The first interface is a slightly modified version of the nextjs example at
https://github.com/vercel/next.js/tree/canary/examples/with-webassembly,
including a WebAssembly module generated from a rust crate, instead of the
simple `.rs` file used in the Vercel example.

The crate is defined in [the `/wasm`
directory](https://github.com/mpadge/wasm-next/tree/main/wasm), and built with
the [npm script, `npm run
build:wasm`](https://github.com/mpadge/wasm-next/blob/main/package.json#L6).
This command compiles the WebAssembly binary module in the [`./pkg`
directory](https://github.com/mpadge/wasm-next/tree/main/pkg), where this
`./pkg` location must also be specified in
[`next.config.js`](https://github.com/mpadge/wasm-next/blob/main/next.config.js).
All of the files, including the compiled binaries, are then committed with this
repository, and the whole site built with `npm run build`. (Compiling binaries
on a server requires the community-supported [rust
runtime](https://github.com/vercel-community/rust).)

## 2. WebAssembly processing of vectors

The second example uses standard WebAssembly interfaces to accept two input
vectors, and returns the result of adding each pair of input elements. The main
rust function for this is [`mult_two` in
`wasm/src/lib.rs`](https://github.com/mpadge/wasm-next/blob/main/wasm/src/lib.rs).
This function demonstrates the standard procedure to pass vectors between
TypeScript and Rust: as a pointer to the start of the vector in memory, and an
integer defining the length of the vector. The vectors may then be assembled in
rust as on [lines 16-17 of
`wasm/src/lib.rs`](https://github.com/mpadge/wasm-next/blob/main/wasm/src/lib.rs#L16-L17).
The length of the return vector must be stored in rust as a global variable,
which can then be accessed using the function
[`get_result_len()`](https://github.com/mpadge/wasm-next/blob/main/wasm/src/lib.rs#L34-L36).

The interface to these two WebAssembly functions from TypeScript is
demonstrated in
[`src/components/WasmVectorMult.tsx`](https://github.com/mpadge/wasm-next/blob/main/src/components/WasmVectorMult.tsx),
which demonstrates how the compiled WebAssembly binary module must be
[explicitly
imported](https://github.com/mpadge/wasm-next/blob/main/src/components/WasmVectorMult.tsx#L22)
in order to access its functions.

## 3. nextjs, WebAssembly, and wasm-bindgen

The previous example demonstrates some of the intricacies of passing complex,
variable-length objects between TypeScript and WebAssembly. The
[`wasm-bindgen`](https://github.com/rustwasm/wasm-bindgen) crate provides a
cleaner interface for passing complex objects between TypeScript and
WebAssembly. The final component here uses
[`wasm-bindgen`](https://github.com/rustwasm/wasm-bindgen) to read two local
JSON files [bundled with this
repository](https://github.com/mpadge/wasm-next/tree/main/public/data) and
containing columns of numeric values, to extract a specified column from each
of those files, and to compute pairwise average values.

The TypeScript interface using `wasm-bindgen` is in
[`src/components/WasmBindGen.tsx`](https://github.com/mpadge/wasm-next/blob/main/src/components/WasmBindGen.tsx),
where [Line 41](
https://github.com/mpadge/wasm-next/blob/main/src/components/WasmBindGen.tsx#L41)
demonstrates that the compiled WebAssembly module is accessed in this case by an
asynchronous `fetch` call (equivalent to `await import` calls in the previous
two examples). These calls in nextjs can only access public URLs, which means
that the WebAssembly binary must be accessible from the `./public`
directory of this repository. The [`package.json`
file](https://github.com/mpadge/wasm-next/blob/main/package.json) includes a
final command to copy the compiled binary from the `./pkg` directory [across to
`./public/pkg`](https://github.com/mpadge/wasm-next/blob/main/package.json#L6).
Note that the binary must be copied, not moved, so that copies of the compiled
binary must be held both in the internal `./pkg` directory, and mirrored in the
`./public/pkg` directory. (Alternative approaches that avoid this duplication
require manually editing [the `testcrate.js`
file](https://github.com/mpadge/wasm-next/blob/main/pkg/testcrate.js) each time
it is automatically re-generated by `wasm-pack`.)

The
[`WasmBindGen.tsx`](https://github.com/mpadge/wasm-next/blob/main/src/components/WasmBindGen.tsx)
file uses two main react effects, one to load the JSON files into the module,
and the second to pass the associated data to WebAssembly and wait for the
response. The JSON data are converted to strings in TypeScript before passing
to rust, allowing [`wasm-bindgen`](https://github.com/rustwasm/wasm-bindgen) to
use [generic `&str`
objects](https://github.com/mpadge/wasm-next/blob/main/wasm/src/lib.rs#L66),
rather than explicit pointers to memory addresses and object lengths. And that,
finally, is the whole point of using `wasm-bindgen`: to avoid the kind of
explicit interaction with underlying memory that was necessary in the previous
vector example.

One final and important point is that WebAssembly interfaces, with or without
`wasm-bindgen`, are generally defined in a `.js` file in the WebAssembly build
directory (in this case,
[`testcrate.js`](https://github.com/mpadge/wasm-next/blob/main/pkg/testcrate.js)).
These interfaces are automatically generated by `wasm-pack`, and must be
imported into any JavaScript file in which they are used. The previous two
examples imported functions [directory
from](https://github.com/mpadge/wasm-next/blob/main/src/components/WasmAddTwo.tsx#L13)
the [WebAssembly
binary](https://github.com/mpadge/wasm-next/blob/main/src/components/WasmVectorMult.tsx#L22).
JavaScript interfaces to the `wasm-bindgen` functions defined in
[`wasm/src/lib.rs`](https://github.com/mpadge/wasm-next/blob/main/wasm/src/lib.rs)
are automatically generated in
[`/pkg/testcrate.js`](https://github.com/mpadge/wasm-next/blob/main/pkg/testcrate.js),
and may be imported and used as in the [first line of `WasmBindGen.tsx`](
https://github.com/mpadge/wasm-next/blob/main/src/components/WasmBindGen.tsx#L1):
```{js}
import * as wasm_js from "@/../pkg/testcrate.js";
```
The binary module itself must then also be initalised, and its memory usage
synchronised with the JavaScript code, with [Line 47 of `WasmBindGen.tsx`](
https://github.com/mpadge/wasm-next/blob/main/src/components/WasmBindGen.tsx#L47):
```{js}
const wasm_binary = wasm_js.initSync(bytes);
```
The two environments have been named here to explicitly demonstrate that
`initSync` is defined in the JavaScript interface, `testcrate.js`, which may
then be called to load and synchronise the WebAssembly module as `wasm_binary`.
Although `wasm_binary` is not used any further in this example, this
initialisation is necessary to enable the module to access memory, and if
necessary this memory can be subsequently accessed as `wasm_binary.memory`.

## 4. Error: webpack.cache Caching failed

This repository has been unarchived (Nov 2024) to resolve a new :bug: that has
arisen:

```
[webpack.cache.PackFileCacheStrategy] Caching failed for pack: Error: Unable to snapshot resolve dependencies
```

This error has been reported [on
StackOverflow](https://stackoverflow.com/questions/73306304/w-webpack-cache-packfilecachestrategy-caching-failed-for-pack-error-unable),
on [nextjs](https://github.com/vercel/next.js/issues/27650), and on [webpack
itself](https://github.com/webpack/webpack/issues/11908). Solution for the
moment, as documented in
[issue#4](https://github.com/mpadge/wasm-next/issues/4) is just to downgrade to
Node v22, and not (yet) use v23.