Skip to content

Currently you can use napi-rs like this, more work is working in progress.

Note: WASI target require rust nightly toolchain.

toml
[package]
edition = "2021"
name = "binding"
version = "0.0.0"

# We should build binary for WASI reactor
# https://github.com/rust-lang/rust/pull/79997
# https://github.com/WebAssembly/WASI/issues/24
# for wasm
[[bin]]
name = "binding"
path = "src/main.rs"

# for native
# [lib]
# name = "binding"
# path = "src/lib.rs"
# crate-type = ["cdylib"]

[dependencies]
napi = { version = "2.12.1", default-features = false, features = ["napi8"] }
napi-sys = { version = "2.2.3", features = ["napi8"] }
napi-derive = "2.12.2"

[build-dependencies]
napi-build = "2.0.1"

[profile.release]
strip = "symbols"
[package]
edition = "2021"
name = "binding"
version = "0.0.0"

# We should build binary for WASI reactor
# https://github.com/rust-lang/rust/pull/79997
# https://github.com/WebAssembly/WASI/issues/24
# for wasm
[[bin]]
name = "binding"
path = "src/main.rs"

# for native
# [lib]
# name = "binding"
# path = "src/lib.rs"
# crate-type = ["cdylib"]

[dependencies]
napi = { version = "2.12.1", default-features = false, features = ["napi8"] }
napi-sys = { version = "2.2.3", features = ["napi8"] }
napi-derive = "2.12.2"

[build-dependencies]
napi-build = "2.0.1"

[profile.release]
strip = "symbols"
toml
[build]
target = [
  "wasm32-unknown-unknown",
  "wasm32-wasi"
]

[target.wasm32-unknown-unknown]
rustflags = [
  "-L./node_modules/emnapi/lib/wasm32",
  "-lemnapi",
  "-ldlmalloc", # "-lemmalloc",
  "-C", "link-arg=--no-entry",
  "-C", "link-arg=--initial-memory=16777216",
  "-C", "link-arg=--export-dynamic",
  "-C", "link-arg=--export=malloc",
  "-C", "link-arg=--export=free",
  "-C", "link-arg=--export=napi_register_wasm_v1",
  "-C", "link-arg=--export-table",
  "-C", "link-arg=--import-undefined",
]

[target.wasm32-wasi]
rustflags = [
  "-L./node_modules/emnapi/lib/wasm32-wasi",
  "-lemnapi",
  "-C", "link-arg=--initial-memory=16777216",
  "-C", "link-arg=--export-dynamic",
  "-C", "link-arg=--export=malloc",
  "-C", "link-arg=--export=free",
  "-C", "link-arg=--export=napi_register_wasm_v1",
  "-C", "link-arg=--export-table",
  "-C", "link-arg=--import-undefined",
  "-Z", "wasi-exec-model=reactor", # +nightly
]
[build]
target = [
  "wasm32-unknown-unknown",
  "wasm32-wasi"
]

[target.wasm32-unknown-unknown]
rustflags = [
  "-L./node_modules/emnapi/lib/wasm32",
  "-lemnapi",
  "-ldlmalloc", # "-lemmalloc",
  "-C", "link-arg=--no-entry",
  "-C", "link-arg=--initial-memory=16777216",
  "-C", "link-arg=--export-dynamic",
  "-C", "link-arg=--export=malloc",
  "-C", "link-arg=--export=free",
  "-C", "link-arg=--export=napi_register_wasm_v1",
  "-C", "link-arg=--export-table",
  "-C", "link-arg=--import-undefined",
]

[target.wasm32-wasi]
rustflags = [
  "-L./node_modules/emnapi/lib/wasm32-wasi",
  "-lemnapi",
  "-C", "link-arg=--initial-memory=16777216",
  "-C", "link-arg=--export-dynamic",
  "-C", "link-arg=--export=malloc",
  "-C", "link-arg=--export=free",
  "-C", "link-arg=--export=napi_register_wasm_v1",
  "-C", "link-arg=--export-table",
  "-C", "link-arg=--import-undefined",
  "-Z", "wasi-exec-model=reactor", # +nightly
]
rust
#![no_main]

use napi_derive::napi;

#[napi]
fn fibonacci(n: u32) -> u32 {
  match n {
    1 | 2 => 1,
    _ => fibonacci(n - 1) + fibonacci(n - 2),
  }
}
#![no_main]

use napi_derive::napi;

#[napi]
fn fibonacci(n: u32) -> u32 {
  match n {
    1 | 2 => 1,
    _ => fibonacci(n - 1) + fibonacci(n - 2),
  }
}
js
const fs = require('fs')
const path = require('path')
const useWASI = false

let wasi
if (useWASI) {
  const { WASI } = require('wasi')
  wasi = new WASI({ /* ... */ })
}

const { instantiateNapiModule } = require('@emnapi/core')

const wasmBuffer = useWASI
  ? fs.readFileSync(path.join(__dirname, './target/wasm32-wasi/release/binding.wasm'))
  : fs.readFileSync(path.join(__dirname, './target/wasm32-unknown-unknown/release/binding.wasm'))

instantiateNapiModule(wasmBuffer, {
  context: require('@emnapi/runtime').getDefaultContext(),
  wasi,
  beforeInit ({ instance }) {
    for (const sym in instance.exports) {
      if (sym.startsWith('__napi_register__')) {
        instance.exports[sym]()
      }
    }
  },
  overwriteImports (importObject) {
    importObject.env = {
      ...importObject.env,
      ...importObject.napi,
      ...importObject.emnapi
    }
  }
}).then(({ napiModule }) => {
  const binding = napiModule.exports
  // output: 5
  console.log(binding.fibonacci(5))
})
const fs = require('fs')
const path = require('path')
const useWASI = false

let wasi
if (useWASI) {
  const { WASI } = require('wasi')
  wasi = new WASI({ /* ... */ })
}

const { instantiateNapiModule } = require('@emnapi/core')

const wasmBuffer = useWASI
  ? fs.readFileSync(path.join(__dirname, './target/wasm32-wasi/release/binding.wasm'))
  : fs.readFileSync(path.join(__dirname, './target/wasm32-unknown-unknown/release/binding.wasm'))

instantiateNapiModule(wasmBuffer, {
  context: require('@emnapi/runtime').getDefaultContext(),
  wasi,
  beforeInit ({ instance }) {
    for (const sym in instance.exports) {
      if (sym.startsWith('__napi_register__')) {
        instance.exports[sym]()
      }
    }
  },
  overwriteImports (importObject) {
    importObject.env = {
      ...importObject.env,
      ...importObject.napi,
      ...importObject.emnapi
    }
  }
}).then(({ napiModule }) => {
  const binding = napiModule.exports
  // output: 5
  console.log(binding.fibonacci(5))
})

Released under the MIT License.