mirror of
https://github.com/nodejs/node.git
synced 2026-10-28 18:25:13 +08:00
doc: copyedit addons.md
* Make the intro less C++-centric * Give a GYP-less build example * Remove the duplicate Node-API documentation * Add an ESM example PR-URL: https://github.com/nodejs/node/pull/62071 Reviewed-By: Chengzhong Wu <legendecas@gmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net>
This commit is contained in:
1 parent
bd3cba5633
commit
e39e1fdce8
1 file changed
+68
-92
+68
-92
@@ -4,22 +4,17 @@
|
||||
|
||||
<!-- type=misc -->
|
||||
|
||||
_Addons_ are dynamically-linked shared objects written in C++. The
|
||||
[`require()`][require] function can load addons as ordinary Node.js modules.
|
||||
Addons provide an interface between JavaScript and C/C++ libraries.
|
||||
_Addons_ are dynamically-linked shared objects that can be loaded via the
|
||||
[`require()`][] function as ordinary Node.js modules.
|
||||
Addons provide a foreign function interface between JavaScript and native code.
|
||||
|
||||
There are three options for implementing addons:
|
||||
|
||||
* Node-API
|
||||
* [Node-API][] (recommended)
|
||||
* `nan` ([Native Abstractions for Node.js][])
|
||||
* direct use of internal V8, libuv, and Node.js libraries
|
||||
|
||||
Unless there is a need for direct access to functionality which is not\
|
||||
exposed by Node-API, use Node-API.
|
||||
Refer to [C/C++ addons with Node-API](n-api.md) for more information on
|
||||
Node-API.
|
||||
|
||||
When not using Node-API, implementing addons becomes more complex, requiring\
|
||||
This rest of this document focuses on the latter, requiring
|
||||
knowledge of multiple components and APIs:
|
||||
|
||||
* [V8][]: the C++ library Node.js uses to provide the
|
||||
@@ -28,7 +23,7 @@ knowledge of multiple components and APIs:
|
||||
`v8.h` header file (`deps/v8/include/v8.h` in the Node.js source
|
||||
tree), and is also available [online][v8-docs].
|
||||
|
||||
* [libuv][]: The C library that implements the Node.js event loop, its worker
|
||||
* [`libuv`][]: The C library that implements the Node.js event loop, its worker
|
||||
threads and all of the asynchronous behaviors of the platform. It also
|
||||
serves as a cross-platform abstraction library, giving easy, POSIX-like
|
||||
access across all major operating systems to many common system tasks, such
|
||||
@@ -87,37 +82,40 @@ void Initialize(Local<Object> exports) {
|
||||
NODE_SET_METHOD(exports, "hello", Method);
|
||||
}
|
||||
|
||||
NODE_MODULE(NODE_GYP_MODULE_NAME, Initialize)
|
||||
NODE_MODULE(NODE_GYP_MODULE_NAME, Initialize) // N.B.: no semi-colon, this is not a function
|
||||
|
||||
} // namespace demo
|
||||
```
|
||||
|
||||
All Node.js addons must export an initialization function following
|
||||
the pattern:
|
||||
On most platforms, the following `Makefile` can get us started:
|
||||
|
||||
```cpp
|
||||
void Initialize(Local<Object> exports);
|
||||
NODE_MODULE(NODE_GYP_MODULE_NAME, Initialize)
|
||||
<!--lint disable no-tabs remark-lint-->
|
||||
|
||||
```bash
|
||||
NODEJS_DEV_ROOT ?= $(shell dirname "$$(command -v node)")/..
|
||||
CXXFLAGS = -std=c++23 -I$(NODEJS_DEV_ROOT)/include/node -fPIC -shared -Wl,-undefined,dynamic_lookup
|
||||
|
||||
hello.node: hello.cc
|
||||
$(CXX) $(CXXFLAGS) -o $@ $<
|
||||
```
|
||||
|
||||
There is no semi-colon after `NODE_MODULE` as it's not a function (see
|
||||
`node.h`).
|
||||
<!--lint enable no-tabs remark-lint-->
|
||||
|
||||
The `module_name` must match the filename of the final binary (excluding
|
||||
the `.node` suffix).
|
||||
Then running the following commands will compile and run the code:
|
||||
|
||||
In the `hello.cc` example, then, the initialization function is `Initialize`
|
||||
and the addon module name is `addon`.
|
||||
```console
|
||||
$ make
|
||||
$ node -p 'require("./hello.node").hello()'
|
||||
world
|
||||
```
|
||||
|
||||
When building addons with `node-gyp`, using the macro `NODE_GYP_MODULE_NAME` as
|
||||
the first parameter of `NODE_MODULE()` will ensure that the name of the final
|
||||
binary will be passed to `NODE_MODULE()`.
|
||||
To integrate with the npm ecosystem, see the [Building][] section.
|
||||
|
||||
### Context-aware addons
|
||||
|
||||
Addons defined with `NODE_MODULE()` can not be loaded in multiple contexts or
|
||||
multiple threads at the same time.
|
||||
|
||||
### Context-aware addons
|
||||
|
||||
There are environments in which Node.js addons may need to be loaded multiple
|
||||
times in multiple contexts. For example, the [Electron][] runtime runs multiple
|
||||
instances of Node.js in a single process. Each instance will have its own
|
||||
@@ -257,8 +255,8 @@ changes:
|
||||
In order to be loaded from multiple Node.js environments,
|
||||
such as a main thread and a Worker thread, an add-on needs to either:
|
||||
|
||||
* Be an Node-API addon, or
|
||||
* Be declared as context-aware using `NODE_MODULE_INIT()` as described above
|
||||
* Be an [Node-API][] addon.
|
||||
* Be declared as context-aware using `NODE_MODULE_INIT()` as described above.
|
||||
|
||||
In order to support [`Worker`][] threads, addons need to clean up any resources
|
||||
they may have allocated when such a thread exits. This can be achieved through
|
||||
@@ -340,7 +338,7 @@ require('./build/Release/addon');
|
||||
Once the source code has been written, it must be compiled into the binary
|
||||
`addon.node` file. To do so, create a file called `binding.gyp` in the
|
||||
top-level of the project describing the build configuration of the module
|
||||
using a JSON-like format. This file is used by [node-gyp][], a tool written
|
||||
using a JSON-like format. This file is used by [`node-gyp`][], a tool written
|
||||
specifically to compile Node.js addons.
|
||||
|
||||
```json
|
||||
@@ -375,7 +373,7 @@ version of `node-gyp` to perform this same set of actions, generating a
|
||||
compiled version of the addon for the user's platform on demand.
|
||||
|
||||
Once built, the binary addon can be used from within Node.js by pointing
|
||||
[`require()`][require] to the built `addon.node` module:
|
||||
[`require()`][] to the built `addon.node` module:
|
||||
|
||||
```js
|
||||
// hello.js
|
||||
@@ -422,18 +420,46 @@ aware of:
|
||||
### Loading addons using `require()`
|
||||
|
||||
The filename extension of the compiled addon binary is `.node` (as opposed
|
||||
to `.dll` or `.so`). The [`require()`][require] function is written to look for
|
||||
to `.dll` or `.so`). The [`require()`][] function is written to look for
|
||||
files with the `.node` file extension and initialize those as dynamically-linked
|
||||
libraries.
|
||||
|
||||
When calling [`require()`][require], the `.node` extension can usually be
|
||||
When calling [`require()`][], the `.node` extension can usually be
|
||||
omitted and Node.js will still find and initialize the addon. One caveat,
|
||||
however, is that Node.js will first attempt to locate and load modules or
|
||||
JavaScript files that happen to share the same base name. For instance, if
|
||||
there is a file `addon.js` in the same directory as the binary `addon.node`,
|
||||
then [`require('addon')`][require] will give precedence to the `addon.js` file
|
||||
then [`require('addon')`][`require()`] will give precedence to the `addon.js` file
|
||||
and load it instead.
|
||||
|
||||
### Loading addons using `import`
|
||||
|
||||
<!-- YAML
|
||||
added:
|
||||
- v23.6.0
|
||||
- v22.20.0
|
||||
-->
|
||||
|
||||
> Stability: 1.0 - Early development
|
||||
|
||||
You can use the [`--experimental-addon-modules`][] flag to enable support for
|
||||
both static `import` and dynamic `import()` to load binary addons.
|
||||
|
||||
If we reuse the Hello World example from earlier, you could do:
|
||||
|
||||
```mjs
|
||||
// hello.mjs
|
||||
import myAddon from './hello.node';
|
||||
// N.B.: import {hello} from './hello.node' would not work
|
||||
|
||||
console.log(myAddon.hello());
|
||||
```
|
||||
|
||||
```console
|
||||
$ node --experimental-addon-modules hello.mjs
|
||||
world
|
||||
```
|
||||
|
||||
## Native abstractions for Node.js
|
||||
|
||||
Each of the examples illustrated in this document directly use the
|
||||
@@ -453,59 +479,7 @@ illustration of how it can be used.
|
||||
|
||||
> Stability: 2 - Stable
|
||||
|
||||
Node-API is an API for building native addons. It is independent from
|
||||
the underlying JavaScript runtime (e.g. V8) and is maintained as part of
|
||||
Node.js itself. This API will be Application Binary Interface (ABI) stable
|
||||
across versions of Node.js. It is intended to insulate addons from
|
||||
changes in the underlying JavaScript engine and allow modules
|
||||
compiled for one version to run on later versions of Node.js without
|
||||
recompilation. Addons are built/packaged with the same approach/tools
|
||||
outlined in this document (node-gyp, etc.). The only difference is the
|
||||
set of APIs that are used by the native code. Instead of using the V8
|
||||
or [Native Abstractions for Node.js][] APIs, the functions available
|
||||
in the Node-API are used.
|
||||
|
||||
Creating and maintaining an addon that benefits from the ABI stability
|
||||
provided by Node-API carries with it certain
|
||||
[implementation considerations][].
|
||||
|
||||
To use Node-API in the above "Hello world" example, replace the content of
|
||||
`hello.cc` with the following. All other instructions remain the same.
|
||||
|
||||
```cpp
|
||||
// hello.cc using Node-API
|
||||
#include <node_api.h>
|
||||
|
||||
namespace demo {
|
||||
|
||||
napi_value Method(napi_env env, napi_callback_info args) {
|
||||
napi_value greeting;
|
||||
napi_status status;
|
||||
|
||||
status = napi_create_string_utf8(env, "world", NAPI_AUTO_LENGTH, &greeting);
|
||||
if (status != napi_ok) return nullptr;
|
||||
return greeting;
|
||||
}
|
||||
|
||||
napi_value init(napi_env env, napi_value exports) {
|
||||
napi_status status;
|
||||
napi_value fn;
|
||||
|
||||
status = napi_create_function(env, nullptr, 0, Method, nullptr, &fn);
|
||||
if (status != napi_ok) return nullptr;
|
||||
|
||||
status = napi_set_named_property(env, exports, "hello", fn);
|
||||
if (status != napi_ok) return nullptr;
|
||||
return exports;
|
||||
}
|
||||
|
||||
NAPI_MODULE(NODE_GYP_MODULE_NAME, init)
|
||||
|
||||
} // namespace demo
|
||||
```
|
||||
|
||||
The functions available and how to use them are documented in
|
||||
[C/C++ addons with Node-API](n-api.md).
|
||||
See [C/C++ addons with Node-API][Node-API].
|
||||
|
||||
## Addon examples
|
||||
|
||||
@@ -1378,18 +1352,20 @@ console.log(result);
|
||||
// Prints: 30
|
||||
```
|
||||
|
||||
[Building]: #building
|
||||
[Electron]: https://electronjs.org/
|
||||
[Embedder's Guide]: https://v8.dev/docs/embed
|
||||
[Linking to libraries included with Node.js]: #linking-to-libraries-included-with-nodejs
|
||||
[Native Abstractions for Node.js]: https://github.com/nodejs/nan
|
||||
[Node-API]: n-api.md
|
||||
[V8]: https://v8.dev/
|
||||
[`--experimental-addon-modules`]: cli.md#--experimental-addon-modules
|
||||
[`Worker`]: worker_threads.md#class-worker
|
||||
[`libuv`]: https://github.com/libuv/libuv
|
||||
[`node-gyp`]: https://github.com/nodejs/node-gyp
|
||||
[`require()`]: modules.md#requireid
|
||||
[bindings]: https://github.com/TooTallNate/node-bindings
|
||||
[download]: https://github.com/nodejs/node-addon-examples
|
||||
[examples]: https://github.com/nodejs/nan/tree/HEAD/examples/
|
||||
[implementation considerations]: n-api.md#implications-of-abi-stability
|
||||
[installation instructions]: https://github.com/nodejs/node-gyp#installation
|
||||
[libuv]: https://github.com/libuv/libuv
|
||||
[node-gyp]: https://github.com/nodejs/node-gyp
|
||||
[require]: modules.md#requireid
|
||||
[v8-docs]: https://v8docs.nodesource.com/
|
||||
Reference in new issue
Block a user