• Stars
    star
    154
  • Rank 241,303 (Top 5 %)
  • Language
    JavaScript
  • Created almost 2 years ago
  • Updated over 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

An implementation of the napi API for WASM.

napi-wasm

An implementation of the napi API for WASM. Enables using some native Node modules in browsers and other environments.

Setup

To use napi-wasm, there are a few requirements:

  1. Configure your linker to export an indirect function table. With ldd, this is the --export-table flag. This enables JavaScript to call callback functions registered by WASM. It is exposed in the WebAssembly exports as __indirect_function_table.
  2. Export a function from your WASM build named napi_register_module_v1 (Node's default), or napi_register_wasm_v1 for WASM-specific builds. This is called during initialization to setup the exports object for your module. It receives an environment and an exports object pointer as arguments, which you can add properties to.
  3. Include a function named napi_wasm_malloc in your WASM build. This is called from JavaScript by napi-wasm to allocate memory in the WASM heap. It should accept a uint32 size argument indicating the number of bytes to allocate, and return a uint8 pointer to allocated memory.
  4. Compile for the wasm32-unknown-unknown target.

In Rust

The above steps should apply for any programming language, but here's an example in Rust. First, define a napi_wasm_malloc function so JavaScript can allocate memory in the WASM heap using the default allocator.

use std::alloc::{alloc, Layout};

#[no_mangle]
pub extern "C" fn napi_wasm_malloc(size: usize) -> *mut u8 {
  let align = std::mem::align_of::<usize>();
  if let Ok(layout) = Layout::from_size_align(size, align) {
    unsafe {
      if layout.size() > 0 {
        let ptr = alloc(layout);
        if !ptr.is_null() {
          return ptr;
        }
      } else {
        return align as *mut u8;
      }
    }
  }

  std::process::abort();
}

Next, implement napi_register_wasm_v1 to register your module exports. We'll use the napi-rs bindings in this example to make it a bit nicer than calling C APIs directly. Note that the napi-rs #[module_exports] macro currently doesn't work in WASM because Rust doesn't support ctor setup functions in WASM targets yet, so we'll need to do this manually.

use napi::{Env, JsObject, NapiValue};

#[no_mangle]
pub unsafe extern "C" fn napi_register_wasm_v1(raw_env: napi::sys::napi_env, raw_exports: napi::sys::napi_value) {
  let env = Env::from_raw(raw_env);
  let exports = JsObject::from_raw_unchecked(raw_env, raw_exports);

  exports.create_named_method("transform", transform);
}

#[js_function(1)]
fn transform(ctx: CallContext) -> napi::Result<JsUnknown> {
  // ...
}

To compile, you need to export a function table and use the correct target.

 RUSTFLAGS="-C link-arg=--export-table" cargo build --target wasm32-unknown-unknown

This will output a file in target/wasm32-unknown-unknown/debug/YOUR_CRATE.wasm which you can load in a JavaScript environment.

You can also put the rust flags in a .cargo/config.toml file so you don't need to provide the environment variable each time you run cargo build.

[target.wasm32-unknown-unknown]
rustflags = ["-C", "link-arg=--export-table"]

Loading

To load a WASM file and initialize a napi environment, you'll need to import the napi-wasm package. You instantiate a WASM module as usual, providing napi as the env import key. This provides the napi functions for your WASM module to use.

Then, pass the WASM instance to the Environment constructor to setup a napi environment. This will call napi_register_wasm_v1 or napi_register_module_v1 to setup the exports object. Then you can call functions on the exports object as you would in Node.

import { Environment, napi } from 'napi-wasm';

// Construct a URL and instantiate a WebAssembly module as usual.
const url = new URL('path/to/lib.wasm', import.meta.url);
const { instance } = await WebAssembly.instantiateStreaming(fetch(url), {
  env: napi
});

// Create an environment.
let env = new Environment(instance);
let exports = env.exports;

// Use exports as usual!
exports.transform({
  // ...
});

When you are done with an Environment, call the destroy() function to clean up memory.

More Repositories

1

regexgen

Generate regular expressions that match a set of strings
JavaScript
3,240
star
2

node-wkhtmltopdf

A wrapper for the wkhtmltopdf HTML to PDF converter using WebKit
JavaScript
602
star
3

dprint-node

A node API for the dprint TypeScript and JavaScript code formatter
Rust
449
star
4

reader

An API Compatible Replacement for Google Reader
JavaScript
355
star
5

glob-match

An extremely fast glob matching library in Rust.
Rust
276
star
6

tree-sitter-highlight

A syntax highlighter for Node powered by Tree Sitter. Written in Rust.
Rust
215
star
7

slang

A collection of utility functions for working with strings in JavaScript in the browser or Node
JavaScript
170
star
8

exif-reader

A small EXIF image metadata reader
JavaScript
145
star
9

protobuf-jsonschema

Compiles Protobuf IDL to JSON Schema
JavaScript
111
star
10

blob-stream

A Node-style writable stream for HTML5 Blobs
JavaScript
110
star
11

jpg-stream

A streaming JPEG encoder and decoder
C++
79
star
12

pi-christmas-lights

🎄 Sync Christmas lights to music with a Raspberry Pi and the Web Audio API
JavaScript
74
star
13

vue-hooks

Experiment to shim the React Hooks API in Vue 3
Vue
74
star
14

importer

Deprecated: File importing for CoffeeScript and JavaScript
CoffeeScript
60
star
15

apple-color-emoji

Replace emoji in strings with images from the Apple Color Emoji font
JavaScript
59
star
16

protobuf-validator

Validates objects against protocol buffer schemas
JavaScript
57
star
17

gif-stream

A streaming GIF encoder and decoder
JavaScript
52
star
18

svelte-hooks

HTML
51
star
19

atom-jade

Jade TextMate bundle converted for Atom
HTML
51
star
20

fontkit-demo

A variable fonts demo using fontkit
JavaScript
50
star
21

coffeepack

An implementation of the MessagePack serialization format in CoffeeScript for Node.js and the browser.
CoffeeScript
50
star
22

browserify-istanbul

A browserify transform for the istanbul code coverage tool
JavaScript
50
star
23

SNNeuralNet

A neural network library for Objective-C
Objective-C
41
star
24

react-aria-components

Prototype of a higher level component-based API on top of React Aria
JavaScript
34
star
25

pics

Ties together streaming image encoders and decoders with a nice API
JavaScript
34
star
26

contenteditable-emoji

A blog post/demo on how to support emoji cross browser inside contenteditable
JavaScript
34
star
27

to-ast

Converts JavaScript objects to equivalent ASTs
JavaScript
33
star
28

bundler-algorithm

Experimenting with a faster bundling algorithm
Rust
31
star
29

png-stream

A streaming PNG encoder and decoder
JavaScript
29
star
30

neuquant

JavaScript port of the NeuQuant image quantization algorithm
JavaScript
26
star
31

svgkit

An SVG renderer for PDFKit
JavaScript
23
star
32

color-generator

Generates colors based on the golden ratio
JavaScript
20
star
33

qunit-cli

A Node module that adds colorful CLI support for the QUnit testing framework
JavaScript
18
star
34

pixel-stream

A base transform stream class for image pixel data
JavaScript
18
star
35

glsl.js

A glsl to asm.js compiler
JavaScript
18
star
36

react-aria-tailwind

A set of components using React Aria and Tailwind
TypeScript
18
star
37

resizer-stream

A streaming image resizer
JavaScript
17
star
38

spellchecker.js

A spellchecker in CoffeeScript/JavaScript based on Hunspell
CoffeeScript
17
star
39

bmp.js

A BMP decoder in JS for the HTML5 Canvas element
CoffeeScript
14
star
40

zipcode

Node module to easily lookup city and state for a US zipcode
JavaScript
14
star
41

bittorrent.js

A work in progress BitTorrent client in JavaScript
CoffeeScript
13
star
42

standards

Ideas for HTML/CSS/JavaScript standards from the community
12
star
43

color-transform

Streaming image color space transforms
JavaScript
11
star
44

concat-frames

Concatenate a pixel-stream into an array of frames
JavaScript
11
star
45

rsp-gatsby

JavaScript
9
star
46

rsp-next

Test app using React Spectrum with Next.js
JavaScript
9
star
47

lzw-stream

A streaming LZW encoder and decoder in JavaScript
JavaScript
9
star
48

browserify-optional

A browserify transform that allows optional dependencies in try..catch blocks
JavaScript
9
star
49

jQuery-DB

A JavaScript data store queried by jQuery-like selectors
JavaScript
8
star
50

bench-napi

C++
8
star
51

wc-hooks

JavaScript
8
star
52

atom-stylus

Stylus TextMate bundle converted for Atom
CoffeeScript
6
star
53

snoozr

Snoozr is an app for sleeping
Objective-C
6
star
54

shared-object

Multi threaded shared memory objects in Node
C++
5
star
55

babel-plugin-transform-glob-import

Import multiple files at once with globs
JavaScript
5
star
56

node-coreaudio

Apple Audio Unit bindings for Node.js
C++
5
star
57

sourcemaps

C++
4
star
58

babel-plugin-transform-async-super

Transform super calls inside async class methods for Node 6
JavaScript
4
star
59

workspace-registry

Virtual NPM registry for Yarn workspaces
JavaScript
3
star
60

is.js

JavaScript function that returns whether an object is of any of the given types
JavaScript
3
star
61

node-impermium

A Node.js API client for impermium
JavaScript
3
star
62

worker-experiment

Experimental Rust-based worker farm for node
Rust
3
star
63

rust-threadlocal-bug

C
2
star
64

next-swc-helpers-repro

JavaScript
1
star
65

private-react

JavaScript
1
star
66

napi-repro

JavaScript
1
star
67

babel-plugin-multidimensional-array

Fast multidimensional typed arrays with a nice syntax
JavaScript
1
star