| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 699a621 commit 8d63a17
1 file changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -4,22 +4,17 @@ | |||
| 4 | 4 | ||
| 5 | 5 | <!-- type=misc --> | |
| 6 | 6 | ||
| 7 | - _Addons_ are dynamically-linked shared objects written in C++. The | ||
| 8 | - [`require()`][require] function can load addons as ordinary Node.js modules. | ||
| 9 | - Addons provide an interface between JavaScript and C/C++ libraries. | ||
| 7 | + _Addons_ are dynamically-linked shared objects that can be loaded via the | ||
| 8 | + [`require()`][] function as ordinary Node.js modules. | ||
| 9 | + Addons provide a foreign function interface between JavaScript and native code. | ||
| 10 | 10 | ||
| 11 | 11 | There are three options for implementing addons: | |
| 12 | 12 | ||
| 13 | - * Node-API | ||
| 13 | + * [Node-API][] (recommended) | ||
| 14 | 14 | * `nan` ([Native Abstractions for Node.js][]) | |
| 15 | 15 | * direct use of internal V8, libuv, and Node.js libraries | |
| 16 | 16 | ||
| 17 | - Unless there is a need for direct access to functionality which is not\ | ||
| 18 | - exposed by Node-API, use Node-API. | ||
| 19 | - Refer to [C/C++ addons with Node-API](n-api.md) for more information on | ||
| 20 | - Node-API. | ||
| 21 | - | ||
| 22 | - When not using Node-API, implementing addons becomes more complex, requiring\ | ||
| 17 | + This rest of this document focuses on the latter, requiring | ||
| 23 | 18 | knowledge of multiple components and APIs: | |
| 24 | 19 | ||
| 25 | 20 | * [V8][]: the C++ library Node.js uses to provide the | |
@@ -28,7 +23,7 @@ knowledge of multiple components and APIs: | |||
| 28 | 23 | `v8.h` header file (`deps/v8/include/v8.h` in the Node.js source | |
| 29 | 24 | tree), and is also available [online][v8-docs]. | |
| 30 | 25 | ||
| 31 | - * [libuv][]: The C library that implements the Node.js event loop, its worker | ||
| 26 | + * [`libuv`][]: The C library that implements the Node.js event loop, its worker | ||
| 32 | 27 | threads and all of the asynchronous behaviors of the platform. It also | |
| 33 | 28 | serves as a cross-platform abstraction library, giving easy, POSIX-like | |
| 34 | 29 | access across all major operating systems to many common system tasks, such | |
@@ -87,37 +82,40 @@ void Initialize(Local<Object> exports) { | |||
| 87 | 82 | NODE_SET_METHOD(exports, "hello", Method); | |
| 88 | 83 | } | |
| 89 | 84 | ||
| 90 | - NODE_MODULE(NODE_GYP_MODULE_NAME, Initialize) | ||
| 85 | + NODE_MODULE(NODE_GYP_MODULE_NAME, Initialize) // N.B.: no semi-colon, this is not a function | ||
| 91 | 86 | ||
| 92 | 87 | } // namespace demo | |
| 93 | 88 | ``` | |
| 94 | 89 | ||
| 95 | - All Node.js addons must export an initialization function following | ||
| 96 | - the pattern: | ||
| 90 | + On most platforms, the following `Makefile` can get us started: | ||
| 97 | 91 | ||
| 98 | - ```cpp | ||
| 99 | - void Initialize(Local<Object> exports); | ||
| 100 | - NODE_MODULE(NODE_GYP_MODULE_NAME, Initialize) | ||
| 92 | + <!--lint disable no-tabs remark-lint--> | ||
| 93 | + | ||
| 94 | + ```bash | ||
| 95 | + NODEJS_DEV_ROOT ?= $(shell dirname "$$(command -v node)")/.. | ||
| 96 | + CXXFLAGS = -std=c++23 -I$(NODEJS_DEV_ROOT)/include/node -fPIC -shared -Wl,-undefined,dynamic_lookup | ||
| 97 | + | ||
| 98 | + hello.node: hello.cc | ||
| 99 | + $(CXX) $(CXXFLAGS) -o $@ $< | ||
| 101 | 100 | ``` | |
| 102 | 101 | ||
| 103 | - There is no semi-colon after `NODE_MODULE` as it's not a function (see | ||
| 104 | - `node.h`). | ||
| 102 | + <!--lint enable no-tabs remark-lint--> | ||
| 105 | 103 | ||
| 106 | - The `module_name` must match the filename of the final binary (excluding | ||
| 107 | - the `.node` suffix). | ||
| 104 | + Then running the following commands will compile and run the code: | ||
| 108 | 105 | ||
| 109 | - In the `hello.cc` example, then, the initialization function is `Initialize` | ||
| 110 | - and the addon module name is `addon`. | ||
| 106 | + ```console | ||
| 107 | + $ make | ||
| 108 | + $ node -p 'require("./hello.node").hello()' | ||
| 109 | + world | ||
| 110 | + ``` | ||
| 111 | + | ||
| 112 | + To integrate with the npm ecosystem, see the [Building][] section. | ||
| 111 | 113 | ||
| 112 | - When building addons with `node-gyp`, using the macro `NODE_GYP_MODULE_NAME` as | ||
| 113 | - the first parameter of `NODE_MODULE()` will ensure that the name of the final | ||
| 114 | - binary will be passed to `NODE_MODULE()`. | ||
| 114 | + ### Context-aware addons | ||
| 115 | 115 | ||
| 116 | 116 | Addons defined with `NODE_MODULE()` can not be loaded in multiple contexts or | |
| 117 | 117 | multiple threads at the same time. | |
| 118 | 118 | ||
| 119 | - ### Context-aware addons | ||
| 120 | - | ||
| 121 | 119 | There are environments in which Node.js addons may need to be loaded multiple | |
| 122 | 120 | times in multiple contexts. For example, the [Electron][] runtime runs multiple | |
| 123 | 121 | instances of Node.js in a single process. Each instance will have its own | |
@@ -257,8 +255,8 @@ changes: | |||
| 257 | 255 | In order to be loaded from multiple Node.js environments, | |
| 258 | 256 | such as a main thread and a Worker thread, an add-on needs to either: | |
| 259 | 257 | ||
| 260 | - * Be an Node-API addon, or | ||
| 261 | - * Be declared as context-aware using `NODE_MODULE_INIT()` as described above | ||
| 258 | + * Be an [Node-API][] addon. | ||
| 259 | + * Be declared as context-aware using `NODE_MODULE_INIT()` as described above. | ||
| 262 | 260 | ||
| 263 | 261 | In order to support [`Worker`][] threads, addons need to clean up any resources | |
| 264 | 262 | they may have allocated when such a thread exits. This can be achieved through | |
@@ -340,7 +338,7 @@ require('./build/Release/addon'); | |||
| 340 | 338 | Once the source code has been written, it must be compiled into the binary | |
| 341 | 339 | `addon.node` file. To do so, create a file called `binding.gyp` in the | |
| 342 | 340 | top-level of the project describing the build configuration of the module | |
| 343 | - using a JSON-like format. This file is used by [node-gyp][], a tool written | ||
| 341 | + using a JSON-like format. This file is used by [`node-gyp`][], a tool written | ||
| 344 | 342 | specifically to compile Node.js addons. | |
| 345 | 343 | ||
| 346 | 344 | ```json | |
@@ -375,7 +373,7 @@ version of `node-gyp` to perform this same set of actions, generating a | |||
| 375 | 373 | compiled version of the addon for the user's platform on demand. | |
| 376 | 374 | ||
| 377 | 375 | Once built, the binary addon can be used from within Node.js by pointing | |
| 378 | - [`require()`][require] to the built `addon.node` module: | ||
| 376 | + [`require()`][] to the built `addon.node` module: | ||
| 379 | 377 | ||
| 380 | 378 | ```js | |
| 381 | 379 | // hello.js | |
@@ -422,18 +420,46 @@ aware of: | |||
| 422 | 420 | ### Loading addons using `require()` | |
| 423 | 421 | ||
| 424 | 422 | The filename extension of the compiled addon binary is `.node` (as opposed | |
| 425 | - to `.dll` or `.so`). The [`require()`][require] function is written to look for | ||
| 423 | + to `.dll` or `.so`). The [`require()`][] function is written to look for | ||
| 426 | 424 | files with the `.node` file extension and initialize those as dynamically-linked | |
| 427 | 425 | libraries. | |
| 428 | 426 | ||
| 429 | - When calling [`require()`][require], the `.node` extension can usually be | ||
| 427 | + When calling [`require()`][], the `.node` extension can usually be | ||
| 430 | 428 | omitted and Node.js will still find and initialize the addon. One caveat, | |
| 431 | 429 | however, is that Node.js will first attempt to locate and load modules or | |
| 432 | 430 | JavaScript files that happen to share the same base name. For instance, if | |
| 433 | 431 | there is a file `addon.js` in the same directory as the binary `addon.node`, | |
| 434 | - then [`require('addon')`][require] will give precedence to the `addon.js` file | ||
| 432 | + then [`require('addon')`][`require()`] will give precedence to the `addon.js` file | ||
| 435 | 433 | and load it instead. | |
| 436 | 434 | ||
| 435 | + ### Loading addons using `import` | ||
| 436 | + | ||
| 437 | + <!-- YAML | ||
| 438 | + added: | ||
| 439 | + - v23.6.0 | ||
| 440 | + - v22.20.0 | ||
| 441 | + --> | ||
| 442 | + | ||
| 443 | + > Stability: 1.0 - Early development | ||
| 444 | + | ||
| 445 | + You can use the [`--experimental-addon-modules`][] flag to enable support for | ||
| 446 | + both static `import` and dynamic `import()` to load binary addons. | ||
| 447 | + | ||
| 448 | + If we reuse the Hello World example from earlier, you could do: | ||
| 449 | + | ||
| 450 | + ```mjs | ||
| 451 | + // hello.mjs | ||
| 452 | + import myAddon from './hello.node'; | ||
| 453 | + // N.B.: import {hello} from './hello.node' would not work | ||
| 454 | + | ||
| 455 | + console.log(myAddon.hello()); | ||
| 456 | + ``` | ||
| 457 | + | ||
| 458 | + ```console | ||
| 459 | + $ node --experimental-addon-modules hello.mjs | ||
| 460 | + world | ||
| 461 | + ``` | ||
| 462 | + | ||
| 437 | 463 | ## Native abstractions for Node.js | |
| 438 | 464 | ||
| 439 | 465 | Each of the examples illustrated in this document directly use the | |
@@ -453,59 +479,7 @@ illustration of how it can be used. | |||
| 453 | 479 | ||
| 454 | 480 | > Stability: 2 - Stable | |
| 455 | 481 | ||
| 456 | - Node-API is an API for building native addons. It is independent from | ||
| 457 | - the underlying JavaScript runtime (e.g. V8) and is maintained as part of | ||
| 458 | - Node.js itself. This API will be Application Binary Interface (ABI) stable | ||
| 459 | - across versions of Node.js. It is intended to insulate addons from | ||
| 460 | - changes in the underlying JavaScript engine and allow modules | ||
| 461 | - compiled for one version to run on later versions of Node.js without | ||
| 462 | - recompilation. Addons are built/packaged with the same approach/tools | ||
| 463 | - outlined in this document (node-gyp, etc.). The only difference is the | ||
| 464 | - set of APIs that are used by the native code. Instead of using the V8 | ||
| 465 | - or [Native Abstractions for Node.js][] APIs, the functions available | ||
| 466 | - in the Node-API are used. | ||
| 467 | - | ||
| 468 | - Creating and maintaining an addon that benefits from the ABI stability | ||
| 469 | - provided by Node-API carries with it certain | ||
| 470 | - [implementation considerations][]. | ||
| 471 | - | ||
| 472 | - To use Node-API in the above "Hello world" example, replace the content of | ||
| 473 | - `hello.cc` with the following. All other instructions remain the same. | ||
| 474 | - | ||
| 475 | - ```cpp | ||
| 476 | - // hello.cc using Node-API | ||
| 477 | - #include <node_api.h> | ||
| 478 | - | ||
| 479 | - namespace demo { | ||
| 480 | - | ||
| 481 | - napi_value Method(napi_env env, napi_callback_info args) { | ||
| 482 | - napi_value greeting; | ||
| 483 | - napi_status status; | ||
| 484 | - | ||
| 485 | - status = napi_create_string_utf8(env, "world", NAPI_AUTO_LENGTH, &greeting); | ||
| 486 | - if (status != napi_ok) return nullptr; | ||
| 487 | - return greeting; | ||
| 488 | - } | ||
| 489 | - | ||
| 490 | - napi_value init(napi_env env, napi_value exports) { | ||
| 491 | - napi_status status; | ||
| 492 | - napi_value fn; | ||
| 493 | - | ||
| 494 | - status = napi_create_function(env, nullptr, 0, Method, nullptr, &fn); | ||
| 495 | - if (status != napi_ok) return nullptr; | ||
| 496 | - | ||
| 497 | - status = napi_set_named_property(env, exports, "hello", fn); | ||
| 498 | - if (status != napi_ok) return nullptr; | ||
| 499 | - return exports; | ||
| 500 | - } | ||
| 501 | - | ||
| 502 | - NAPI_MODULE(NODE_GYP_MODULE_NAME, init) | ||
| 503 | - | ||
| 504 | - } // namespace demo | ||
| 505 | - ``` | ||
| 506 | - | ||
| 507 | - The functions available and how to use them are documented in | ||
| 508 | - [C/C++ addons with Node-API](n-api.md). | ||
| 482 | + See [C/C++ addons with Node-API][Node-API]. | ||
| 509 | 483 | ||
| 510 | 484 | ## Addon examples | |
| 511 | 485 | ||
@@ -1378,18 +1352,20 @@ console.log(result); | |||
| 1378 | 1352 | // Prints: 30 | |
| 1379 | 1353 | ``` | |
| 1380 | 1354 | ||
| 1355 | + [Building]: #building | ||
| 1381 | 1356 | [Electron]: https://electronjs.org/ | |
| 1382 | 1357 | [Embedder's Guide]: https://v8.dev/docs/embed | |
| 1383 | 1358 | [Linking to libraries included with Node.js]: #linking-to-libraries-included-with-nodejs | |
| 1384 | 1359 | [Native Abstractions for Node.js]: https://github.com/nodejs/nan | |
| 1360 | + [Node-API]: n-api.md | ||
| 1385 | 1361 | [V8]: https://v8.dev/ | |
| 1362 | + [`--experimental-addon-modules`]: cli.md#--experimental-addon-modules | ||
| 1386 | 1363 | [`Worker`]: worker_threads.md#class-worker | |
| 1364 | + [`libuv`]: https://github.com/libuv/libuv | ||
| 1365 | + [`node-gyp`]: https://github.com/nodejs/node-gyp | ||
| 1366 | + [`require()`]: modules.md#requireid | ||
| 1387 | 1367 | [bindings]: https://github.com/TooTallNate/node-bindings | |
| 1388 | 1368 | [download]: https://github.com/nodejs/node-addon-examples | |
| 1389 | 1369 | [examples]: https://github.com/nodejs/nan/tree/HEAD/examples/ | |
| 1390 | - [implementation considerations]: n-api.md#implications-of-abi-stability | ||
| 1391 | 1370 | [installation instructions]: https://github.com/nodejs/node-gyp#installation | |
| 1392 | - [libuv]: https://github.com/libuv/libuv | ||
| 1393 | - [node-gyp]: https://github.com/nodejs/node-gyp | ||
| 1394 | - [require]: modules.md#requireid | ||
| 1395 | 1371 | [v8-docs]: https://v8docs.nodesource.com/ | |
| Back | FazBrowse Home | New Git URL |
0 commit comments