Componentize.js on node and wasmtime

Overview

This is a test implementation of bytecodealliance/componentize-js

Pre-requisites

  • Node.js and npm are required to build and run this project.

  • Cargo must be installed, following the instructions in the link, for your specific platform.

Please notice that I have implemented this demo with node.js 16.16.0 and npm 8.11.0

Creating the repo from scratch

Create the project folder

mkdir componentize-demo && cd componentize-demo

Now we will initialize this folder as an npm package, running

npm init -y

(we pass the -y flag to skip the interactive questions).

We will then open the generated package.json file and add the following line:

"type": "module"

This is required to enable ES modules in Node.js.

Now we will install the componentize-js package:

npm install --save-dev @bytecodealliance/componentize-js

and we're going to create a new file called componentize.mjs that will be used to generate our Wasm module. Copy and paste the following code to it.

import { componentize } from '@bytecodealliance/componentize-js';
import { readFile, writeFile } from 'node:fs/promises';

const jsSource = await readFile('hello.js', 'utf8');
const witSource = await readFile('hello.wit', 'utf8');

const { component } = await componentize(jsSource, witSource);

await writeFile('hello.component.wasm', component);

Create the WIT and JS files

Now we're going to create a WIT file called hello.wit. WIT stands for WebAssembly Interface Types and it's a file that describes the interface of a WebAssembly module. Copy and paste the following code to it.

default world hello {
  export hello: func(name: string) -> string
}

We are also going to create a JavaScript file called hello.js that will be used to implement the WIT file. This file exports a single function. Copy and paste the function to the file.

export function hello (name) {
  return `Hello ${name}`;
}

Generate the Wasm module

Now we're ready to generate our Wasm module:

node componentize.mjs

You should see the generated binary file hello.component.wasm in your project folder.

Create the Rust project

Now we're going to create a Rust project that will be used to load and run our Wasm module.

For that, we're going to run the following command:

cargo init --bin

at the root of our project, just like we did with npm for Node.js.

This will create a Cargo.toml file and a src folder with a main.rs file inside.

Now we're going to add the following lines to the Cargo.toml file:

[package]
name = "wasmtime-test"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[workspace]

[dependencies]
anyhow = "1.0.65"
async-std = { version = "1.12.0", features = ["attributes"] }
cap-std = "1.0.12"
wasmtime = { git = "https://github.com/bytecodealliance/wasmtime", rev = "299131ae2d6655c49138bfab2c4469650763ef3b", features = ["component-model"] }
wasi-common =  { git = "https://github.com/bytecodealliance/preview2-prototyping", rev = "dd34a00d4386000cd00071cff18b9e3a12788075" }
wasi-cap-std-sync = { git = "https://github.com/bytecodealliance/preview2-prototyping", rev = "dd34a00d4386000cd00071cff18b9e3a12788075" }
wasmtime-wasi-sockets =  { git = "https://github.com/bytecodealliance/preview2-prototyping", rev = "dd34a00d4386000cd00071cff18b9e3a12788075" }
wasmtime-wasi-sockets-sync = { git = "https://github.com/bytecodealliance/preview2-prototyping", rev = "dd34a00d4386000cd00071cff18b9e3a12788075" }

This will add the dependencies we need to run our Wasm module.

We will also go to src/main.rs and add the following code to it:

use anyhow::Result;
use wasi_cap_std_sync::WasiCtxBuilder;
use wasi_common::{wasi, Table, WasiCtx, WasiView};
use wasmtime::{
    component::{Component, Linker},
    Config, Engine, Store, WasmBacktraceDetails,
};
use wasmtime_wasi_sockets::{WasiSocketsCtx, WasiSocketsView};
use wasmtime_wasi_sockets_sync::WasiSocketsCtxBuilder;

wasmtime::component::bindgen!({
    world: "hello",
    path: "hello.wit",
    async: true
});

#[async_std::main]
async fn main() -> Result<()> {
    let builder = WasiCtxBuilder::new().inherit_stdio();
    let mut table = Table::new();
    let wasi = builder.build(&mut table)?;

    let mut config = Config::new();
    config.cache_config_load_default().unwrap();
    config.wasm_backtrace_details(WasmBacktraceDetails::Enable);
    config.wasm_component_model(true);
    config.async_support(true);

    let engine = Engine::new(&config)?;
    let mut linker = Linker::new(&engine);

    let component = Component::from_file(&engine, "hello.component.wasm").unwrap();


    struct CommandCtx {
        table: Table,
        wasi: WasiCtx,
        sockets: WasiSocketsCtx,
    }
    impl WasiView for CommandCtx {
        fn table(&self) -> &Table {
            &self.table
        }
        fn table_mut(&mut self) -> &mut Table {
            &mut self.table
        }
        fn ctx(&self) -> &WasiCtx {
            &self.wasi
        }
        fn ctx_mut(&mut self) -> &mut WasiCtx {
            &mut self.wasi
        }
    }
    let sockets = WasiSocketsCtxBuilder::new()
        .inherit_network(cap_std::ambient_authority())
        .build();
    impl WasiSocketsView for CommandCtx {
        fn table(&self) -> &Table {
            &self.table
        }
        fn table_mut(&mut self) -> &mut Table {
            &mut self.table
        }
        fn ctx(&self) -> &WasiSocketsCtx {
            &self.sockets
        }
        fn ctx_mut(&mut self) -> &mut WasiSocketsCtx {
            &mut self.sockets
        }
    }

    wasi::command::add_to_linker(&mut linker)?;
    let mut store = Store::new(
        &engine,
        CommandCtx {
            table,
            wasi,
            sockets,
        },
    );

    let (instance, _instance) =
        Hello::instantiate_async(&mut store, &component, &linker).await?;

    let res = instance.call_hello(&mut store, "ComponentizeJS").await?;
    println!("{}", res);
    Ok(())
}

This code will load our Wasm module and run it.

Building and running the Rust project

The first thing we need to do, is to build our Rust project. For that, we're going to run the following command:

cargo build --release

Now we're ready to run it.

cargo run ./target/release/componentizejs

After running this command, you should see the following output:

Hello ComponentizeJS

Transpiling and running it in the Node.js

Now we're going to transpile our Wasm module to JavaScript and run it in Node.js. First of all, we need to install additional dependencies:

npm install --save @bytecodealliance/jco @bytecodealliance/preview2-shim

We will also update our package.json file to add the following lines:

"scripts": {
    "transpile": "jco transpile hello.component.wasm -o hello --wasi-shim",
    ...
}

Note I did not install the jco CLI globally, but locally. What this line does is execute jsco to transpile the .wasm component using Typescript interfaces, and generate the moules in a new folder called hello. For that, it uses the flag --wasi-shim to resolve the imports.

Now we can go back to the terminal and run the following command:

npm run transpile

We should see the following output:

Transpiled JS Component Files:

 - hello/hello.component.core.wasm     6.44 MiB
 - hello/hello.component.core2.wasm    30.1 KiB
 - hello/hello.component.d.ts          0.58 KiB
 - hello/hello.component.js            44.2 KiB
 - hello/imports/environment.d.ts      0.09 KiB
 - hello/imports/exit.d.ts             0.16 KiB
 - hello/imports/filesystem.d.ts       3.85 KiB
 - hello/imports/monotonic-clock.d.ts  0.14 KiB
 - hello/imports/preopens.d.ts         0.47 KiB
 - hello/imports/random.d.ts            0.1 KiB
 - hello/imports/streams.d.ts          0.58 KiB
 - hello/imports/wall-clock.d.ts

Those have been saved to a new folder in your project called hello.

Now you can use the REPL to run the following command:

node -e "import('./hello/hello.component.js').then(m => console.log(m.hello('ComponentizeJS')))"

You should see the following output:

Hello ComponentizeJS
You might also like...
Otto: a unified approach to CRDTs and OT

Otto: a unified approach to CRDTs and OT This repo contains tests for otto. otto enables any boring Rust data structure (without Rc, RefCell etc.) to

A simple CLI for creating and managing Solana Lookup Tables.

LUT - A simple CLI for creating and managing Solana Lookup Tables Commands lut create Creates a new LUT using the default keypair in the Solana config

Collect crash reports, triage, and estimate severity.
Collect crash reports, triage, and estimate severity.

CASR: Crash Analysis and Severity Report CASR – collect crash reports, triage, and estimate severity. It is based on ideas from exploitable and apport

Easy-to-use grammar-based black-box fuzzer. Has found dozens of bugs in important targets like Clang, Deno, and rustc.

tree-crasher tree-crasher is an easy-to-use grammar-based black-box fuzzer. It parses a number of input files using tree-sitter grammars, and produces

Wasmtime - Standalone JIT-style runtime for WebAssembly, using Cranelift

wasmtime A standalone runtime for WebAssembly A Bytecode Alliance project Guide | Contributing | Website | Chat Installation The Wasmtime CLI can be i

A demo repo that shows how to use the latest component model feature in wasmtime to implement a key-value capability defined in a WIT file.

Key-Value Component Demo This repo serves as an example of how to use the latest wasm runtime wasmtime and its component-model feature to build and ex

Simple node and rust script to achieve an easy to use bridge between rust and node.js

Node-Rust Bridge Simple rust and node.js script to achieve a bridge between them. Only 1 bridge can be initialized per rust program. But node.js can h

A node and runtime configuration for polkadot node.
A node and runtime configuration for polkadot node.

MANTA NODE This repo is a fresh FRAME-based Substrate node, forked from substrate-developer-hub/substrate-node-templte 🚀 It links to pallet-manta-dap

A minimal library for building compiled Node.js add-ons in Rust via Node-API
A minimal library for building compiled Node.js add-ons in Rust via Node-API

A minimal library for building compiled Node.js add-ons in Rust via Node-API

Nvm - Node Version Manager - POSIX-compliant bash script to manage multiple active node.js versions

Node Version Manager Table of Contents Intro About Installing and Updating Install & Update Script Additional Notes Troubleshooting on Linux Troublesh

Egui node graph is a featureful, customizable library to create node graph applications using egui
Egui node graph is a featureful, customizable library to create node graph applications using egui

Egui node graph is a featureful, customizable library to create node graph applications using egui. The library takes care of presenting a node graph to your users, and allows customizing many aspects of the interaction, creating the semantics you want for your specific application.

Abuse the node.js inspector mechanism in order to force any node.js/electron/v8 based process to execute arbitrary javascript code.
Abuse the node.js inspector mechanism in order to force any node.js/electron/v8 based process to execute arbitrary javascript code.

jscythe abuses the node.js inspector mechanism in order to force any node.js/electron/v8 based process to execute arbitrary javascript code, even if t

Sample lightning node command-line app built on top of Ldk Node (similar to ldk-sample).

ldk-node-sample Sample lightning node command-line app built on top of Ldk Node (similar to ldk-sample ). Installation git clone https://github.com/op

Rust bindings for writing safe and fast native Node.js modules.
Rust bindings for writing safe and fast native Node.js modules.

Rust bindings for writing safe and fast native Node.js modules. Getting started Once you have the platform dependencies installed, getting started is

⋰·⋰ Feeless is a Nano cryptocurrency node, wallet, tools, and Rust crate.

⋰·⋰ Feeless What is Feeless? Feeless is a Nano cryptocurrency node, wallet, tools, and Rust crate. This is not the official project for Nano, only an

Playwright is a rust library to automate Chromium, Firefox and WebKit built on top of Node.js library.

🎭 Playwright for Rust Playwright is a rust library to automate Chromium, Firefox and WebKit built on top of Node.js library. Installation [dependenci

A node API for the dprint TypeScript and JavaScript code formatter

dprint-node A node API for the dprint TypeScript and JavaScript code formatter. It's written in Rust for blazing fast speed. Usage Pass a file path an

finch - a super fast and efficient template rendering engine for node.js

finch A super fast and efficient template rendering engine for node.js, inspired by Handlebars. Usage Finch is very simple to use: Register a template

Fastest lz4 compression library in Node.js, powered by napi-rs and lz4-flex.

Lz4 Fastest lz4 compression library in Node.js, powered by napi-rs and lz4-flex. Install this package yarn add lz4-napi API export function compress:

Owner
Natalia Venditto
Principal Lead JavaScript DX @Microsoft Azure - ex Architect @MongoDB - OSS Contributor @adobe ex- Certified AEM Lead Developer | Angular/Web GDE
Natalia Venditto
Rust Command Line Application for scaffolding Node.js network applications

Thunderstorm.rs ⛈️ Rust Command Line Application for setting up Node.js backend applications. Installation thunderStorm.rs is currently distributed vi

ADEOYE Adefemi 4 Aug 4, 2022
Travis CI and AppVeyor template to test your Rust crate on 5 architectures and publish binary releases of it for Linux, macOS and Windows

trust Travis CI and AppVeyor template to test your Rust crate on 5 architectures and publish binary releases of it for Linux, macOS and Windows Featur

Jorge Aparicio 1.2k Dec 30, 2022
Advanced Fuzzing Library - Slot your Fuzzer together in Rust! Scales across cores and machines. For Windows, Android, MacOS, Linux, no_std, ...

LibAFL, the fuzzer library. Advanced Fuzzing Library - Slot your own fuzzers together and extend their features using Rust. LibAFL is written and main

Advanced Fuzzing League ++ 1.2k Dec 29, 2022
A fast Rust-based safe and thead-friendly grammar-based fuzz generator

Intro fzero is a grammar-based fuzzer that generates a Rust application inspired by the paper "Building Fast Fuzzers" by Rahul Gopinath and Andreas Ze

null 203 Nov 9, 2022
Breaking your Rust code for fun and profit

Breaking your Rust code for fun & profit this is an architecture-preview, not all components are there This is a mutation testing framework for Rust c

null 533 Dec 18, 2022
TestDrive automatically scrapes input/output data from BOJ(Baekjoon Online Judge) and runs tests for your executable binary file!

?? TestDrive What does it do? TestDrive automatically scrapes input/output data from BOJ(Baekjoon Online Judge) and runs tests for your executable bin

Hyeonseok Jung 3 Mar 5, 2022
A series of test cases to evaluate async Rust on the nrf52840 in terms of power usage and ergonomics.

A series of test cases to evaluate async Rust on the nrf52840 in terms of power usage and ergonomics. This is an experiment that uses unstable features only available on nightly rust.

Tweede golf 1 Oct 15, 2021
A tiny, super simple and portable benchmarking library.

benchmark-simple A tiny benchmarking library for Rust. Trivial to use Works pretty much everywhere, including WebAssembly (WASI, but also in-browser)

Frank Denis 3 Dec 26, 2022
Very minimalist tmux status bar that displays used memory and CPU usage.

woot-bar Ultra minimalist status bar that displays used memory and CPU usage woot-bar is made for tmux but it is compatible with anything that eats st

Nicolas Gryman 3 Dec 27, 2022
Notes and whatnot!

Noted CLI & TUI application to take and track notes. Generate Coverage (with cargo-llvm-cov): LCOV: cargo llvm-cov --all-features --workspace --lcov -

Tony B 2 May 3, 2022