Repository navigation
[napi] Support WebAssembly #796
Description
Activity
It won't make sense anyway. N-API is Node-js-only feature that doesn't work on the web and wasm-bindgen is already doing a good job in this field.
You misunderstood, this issue actually expects to provide an ability to bind wasm-bindgen.
Why though? If you wish to call wasm rust from js you can do that with wasm-bindgen. This library exists to allow running specifically native code. Could you elaborate your intent if we have misunderstood?
It's not possible for
napi-rs1.x to support wasm, but with the procedural macro introduced in 2.0, we can generate code for Rust WASM compiling. For example:#[napi] fn add(a: u32, b: u32) -> u32 { a + b }
We can generate wasm bindgen code for the wasm compile target:
#[wasm_bindgen] fn add(a: u32, b: u32) -> u32 { a + b }
Unless i've missed something, I still don't quite understand the use case for this.
Image you have a library like
crc32:use crc32; #[napi] fn crc32(input: Buffer) -> u32 { crc32::caclulate(input.as_slice()) }
With the WebAssembly support, you can just run
napi build --releaseto build native addons. And runnapi build --release --target wasm32-unknown-unknownto buildwasmlibrary.The code will be totally reused.
So then the use case is to reuse the same code to produce a native bindgen or a wasm bindgen? If so, that sounds like a great feature.
So then the use case is to reuse the same code to produce a native bindgen or a wasm bindgen
Yes, this will be released in
3.0Reacted by hardfist, Seonglae Cho, Brooke Holmes, Dj, Sung Jeon, Niklas Mischkulnig, Javier Viola, Alex Yang, Gaubee, A and 4 moreI'm curious what the plan is for how Tasks will be supported in Wasm. I use
napi-rsto run compute heavy tasks off the main thread and I'm not sure how well that translates to the browser. Would it be on the user's responsibility to ensure that thenapi-rsproduced Wasm is executed in a web worker (so tasks would only are computed off the execution thread in node.js) or wouldnapi-rsproduce an output that would manage a pool of web workers to run shuffle tasks around.Anyways some food for thought as this issue is explored.
I use napi-rs to run compute heavy tasks off the main thread and I'm not sure how well that translates to the browser.
While the output should be able to run in a browser too, I believe the main target here would be server-side executed wasm, in general I assume implementation details like if the output should run in a worker would be up to the user?
Reacted by Niklas Mischkulnig and Alex YangSo then the use case is to reuse the same code to produce a native bindgen or a wasm bindgen
Yes, this will be released in
3.0When is version 3.0 planned to be released?
I kinda got this working a different way - by reimplementing napi in JS so it can be used from WASM: https://github.com/devongovett/napi-wasm. I'm planning on using it in lightningcss to avoid having two separate implementations, which is getting to be more work as we add more features. With this, the existing napi implementation just works when recompiled for WASM.
I ran the napi-rs compat mode test suite using it and it mostly passes, minus some features that don't work in WASM (like threads and fs access). In addition, there are a few other features that don't work because napi-rs uses the
#[ctor]attribute which is currently not supported in wasm targets (see wasm-bindgen/wasm-bindgen#1216). To work around that, I manually implementednapi_register_wasm_v1and registered my exports in there (see example in the readme). But that won't work for other features in the napi bindgen API which use ctors to register things for later use innapi_register_module_v1. Perhaps there is some other way of doing that though.Anyway, maybe this approach is easier than modifying all of napi-rs to have two completely different implementations? Most of what's being done in napi-wasm is similar to what wasm-bindgen generates anyway.
Reacted by LongYinanI created https://github.com/toyobayashi/emnapi for emscripten, well tested by using Node.js official test cases, and it support async work and thread-safe functions on modern browsers (based on emscripten pthreads). I'm not familiar with rust, not sure if it can help.
8 remaining items
Additionally i would like to explain, emnapi's goal is 1) help users port their or existing node-api native addons to wasm with code change as less as possible, 2) runtime behavior matches native Node.js as much as possible.
So I decided to port Node.js node-api source code implementation to JavaScript and C, if you have look into emnapi's source code, you could see most code (both TS and C) matches Nodejs's
src/js_native_api_v8.ccandsrc/node_api.cc. Relying on pthread is reasonable and it is the best way to achieve 2) because I can port part of libuv, do tsfn mutex lock/unlock and cond wait/signal exactly same as nodejs. As most addons are written in C/C++, emscripten is still the first class support target, then wasi-sdk, napi-rs could also be supported if @Brooooooklyn approve emnapi's practice. WASI is the standard, I could not find some reason to avoid it.Is there a plan for getting rid of
#[ctor]in napi? At the moment this blocks us from using the modern napi-rs API in Parcel. We instead have to fall back to the old API if we want to support WASM. Maybe there is a way we could disable ctor for wasm targets and somehow manually call the initialization functions?@devongovett I'm working on it on this branch: https://github.com/napi-rs/napi-rs/tree/emnapi
Cool, thanks. Hopefully it won't require WASI though? Not sure that will work in browsers.
In the meantime, I got it working by patching rust-ctor: devongovett/rust-ctor@30aa204. All that does is mark the ctor functions as
#[no_mangle]in wasm32 targets. Then, I just have to do this from the JS:for (let key in instance.exports) { if (key.startsWith('__napi_register__')) { instance.exports[key](); } }
As long as that is done before calling
napi_register_module_v1it seems to work. 🥳Hopefully it won't require WASI though?
Working in progress, no WASI required for async work and tsfn! Currently emnapi already has another TSFN implementation and single thread async work mock, both are implemented in pure JavaScript and passed node js official test suite. I'll bring real multithreaded async work implementation via WebWorkers in next release. emnapi users can decide themselves to use
async work / tsfn 's C or JS implementation.According to @Brooooooklyn , napi-rs uses tokio, unfortunately they still need WASI, but emnapi itself can avoid WASI soon. My WASI polyfill is well tested in browser as well.
For my use cases, I don't really need support for tokio or async work in WASM so I wouldn't really mind if there were some features of napi-rs that were only for non-WASM targets. It would be great if we could ensure that those features are optional and that large polyfills aren't required unless you explicitly include them. I mainly need the basics to work: calling Rust from JS, and JS from Rust. Once in Rust-land, I'm happy to manage things like async tasks myself.
Reacted by Aura@devongovett I can split changes in abbea85 and merge it before WebAssembly implemented, so that you can call the register functions before
napi_register_module_v1with these changes.Thanks @Brooooooklyn, that would be helpful! I'm also happy to use the ctor patch in the meantime if it's too hard.
@devongovett published
napi-derive@2.12.1for that, you can try it!Reacted by Devon GovettI think basic webassembly support is nearly there:
I compiled code with #[napi] derive into wasm usingcargo build --release --target=wasm32-unknown-unknownbut yarn build fails:
yarn run build --target=wasm32-unknown-unknown Finished release [optimized] target(s) in 0.02s Type Error: Operating system not currently supported or recognized by the build script
with @napi-rs/cli/scripts/index.js:11540:31.
I am ok not having threads and tokio and others since even Wasi runtime doesn't support any of it.
But being able to hook into a browser or native is a killer feature.Reacted by Stephen BelangerClosed in #1669
Reacted by JounQin, Toyo Li, Aura and TwilightReacted by JounQin, Aura and Joël GaleranReacted by JounQin, Toyo Li and Elyse- unpinned this issue
on Nov 7, 2023
By integrating with wasm_bindgen, we can get WASM support.
Once supported, we can automate what this PR does: parcel-bundler/lightningcss#1