Replies: 1 comment 6 replies
-
@shirakaba thank you for posting this detailed and thoughtful question. I agree with pretty much everything you said and I think that enabling code written for Node-API to work with Hermes would be beneficial for the ecosystem. I have expressed a similar sentiment in our discussions with @vmoroz. Can Node-API Be Maintained by HermesReally, the core question that must be answered first is whether the Node-API implementation can be be maintained by the Hermes team. We have discussed that question at length internally and our conclusion is that at this time it wouldn't be practical. We have no expertise in Node-API; more importantly, we have no internal usages of it. It is very difficult to maintain something that you don't use. We wouldn't be able to diagnose problems well, answer questions, etc. At the same time, having to keep Node-API working while changing our internal API would be a continuous burden. Unfortunately it also looks like neither JSI nor Node-API can be implemented efficiently on top of the other one, to save some work. There need to be separate implementations, existing in parallel, simultaneously accessing the same Hermes runtime using the internal APIs. Sincere question (since I haven't followed this topic closely): are Node-API implementations for other JavaScript engines maintained by the engines themselves, or by separate organizations? Are Other Solutions PossibleWith all of that said, I am hopeful that we can find a satisfactory solution. I see three separate issues that need to be solved:
I believe that the first issue is solved by a change in the Hermes release model that we will be making soon. Hermes will transition to a fixed six month release cadence, where the just released version is frozen and stable. This means that a Node-API implementation doesn't need to track a continuously changing target, but instead one changing at a fixed and predictable times. We are aware of the second issue and I am hoping that we can change JSI can to support that. @neildhar can you comment on this topic? The last issue is really the most interesting and challenging, particularly the "symbols not exported by the .so" part. It seems like this would have to be an additional Hermes target, build from the Hermes source + the Node API plugin source. I think we perhaps could add extension points for this in the Hermes build scripts? Any ideas on this issue (and not only, obviously) are welcome. |
Beta Was this translation helpful? Give feedback.
-
(post by @shirakaba moved to top level from #1072 (comment))
Node-API
Node-API was originally just an API for building native addons for Node.js, but has since been decoupled from Node.js (despite the name), and now serves as a universal standard for interacting with native code from any JS engine that implements Node-API (which is most of them).
Contrast to JSI
It may seem that JSI fulfils this need already. However, Node-API has a much wider feature scope, providing a broad range of low-level APIs much like the V8 APIs. It's so flexible that NodeGUI uses it to create idiomatic JS bindings to the whole of Qt, for example.
It also has a much better ecosystem story than JSI – today, many packages containing Node-API code are distributed through npm. These packages can be run directly by any runtime that implements Node-API, e.g. Node.js, Deno, or Bun. They're also easy to run headless (unlike most JSI libraries, which are only set up to be run via a React Native app), allowing for rapid development (no need to spin up a Android/iOS simulator).
Unlike JSI with its C++-based API, Node-API has an ABI-stable C API, meaning native modules using Node-API could be safely distributed as precompiled binaries, allowing for faster builds. I understand this could be solved by rewriting the JSI internals in C, of course.
Contrast to WebAssembly
This discussion is a bit of a distraction, so feel free to skip it.
See details
Putting aside the fact that WASM isn't supported in Hermes at present (#395 (comment)), let's compare the two.
There is some overlap in use-cases between Node-API and WebAssembly, but Node-API is infinitely more flexible because it is not sandboxed. While WebAssembly can be used to compile a self-contained library (one that you have all the source code for, and no dependencies beyond the C standard library) for use in JS, it cannot access system libraries like Core Foundation, which is my main interest.
An example of a Node-API module
For the curious, here's what Node-API involves. The full docs are here.
See details
Library developers would commonly write a Node-API module in C or C++, though there are other language bindings to it, too. Here's an example of a basic module written in C++:
From userland, it is consumed just like any other Node module:
Concrete use-case
We (the NativeScript TSC) would like to bring NativeScript's ability to access native APIs to other JS engines/runtimes. In particular, we'd like to bring it to Hermes so that React Native could access the whole iOS/Android SDK directly from JS.
For an example of how much time it could save compared to writing a native module, here's what it's like to access the iOS batteryLevel API via JavaScript:
No need to open Xcode and make a native module, no need to recompile any native code, and no need to write any bindings (all bindings to the iOS SDK are ready-generated and fully typed).
To make NativeScript embeddable into other JS engines/runtimes, we've been rewriting it using Node-API. It's already successfully working using @vmoroz's fork of Hermes for React Native Windows (that adds Node-API support), so if only Hermes could support Node-API officially – whether in its core or via a plugin – we could bring it to React Native iOS/Android as well.
We hope this could become the last native module ever written 🥹
Beta Was this translation helpful? Give feedback.
All reactions