Skip to content

Migrate native addon from nan to node-addon-api (N-API)#152

Open
sebi2k1 wants to merge 6 commits into
masterfrom
migrate/nan-to-node-addon-api
Open

Migrate native addon from nan to node-addon-api (N-API)#152
sebi2k1 wants to merge 6 commits into
masterfrom
migrate/nan-to-node-addon-api

Conversation

@sebi2k1
Copy link
Copy Markdown
Owner

@sebi2k1 sebi2k1 commented May 17, 2026

Summary

  • Replace nan with node-addon-api in both native source files (can.cc, signals.cc) and the build configuration (binding.gyp)
  • node-addon-api wraps the stable N-API layer, guaranteeing ABI compatibility across Node.js major versions — a binary compiled once for Node.js 20 continues to load on Node.js 22 and 24 without recompilation
  • Adds Dockerfile.build-test for reproducing the Linux build locally

Motivation

Users who upgrade Node.js (e.g. from v20 to v22) currently find the native addon silently broken until they manually rebuild. This is a common pain point for ioBroker adapters and other downstream consumers. N-API eliminates the need to recompile on major version upgrades and makes prebuilt binaries (e.g. via prebuildify) feasible in the future.

What changed

File Change
package.json nannode-addon-api ^8.0.0
binding.gyp Updated include path expression; added NAPI_DISABLE_CPP_EXCEPTIONS
native/can.cc Nan::ObjectWrapNapi::ObjectWrap<T>; static methods → instance methods; Nan::PersistentNapi::FunctionReference/ObjectReference; napi_env stored for uv_async callbacks
native/signals.cc NAN_METHODNapi::Value free functions; all Nan::* replaced with Napi::*
Dockerfile.build-test New — builds the package on node:22-bookworm-slim for verification

Test plan

  • Compiled cleanly on Node.js 22 / Linux arm64 (Debian bookworm) — zero warnings
  • Verify npm test passes on a machine with a real or virtual CAN interface (vcan0)
  • Smoke-test: load the binary built on Node.js 20 under Node.js 22 without rebuilding

🤖 Generated with Claude Code

sebi2k1 and others added 6 commits May 17, 2026 19:27
Replace nan with node-addon-api in both native source files and the
build configuration. N-API provides ABI stability across Node.js major
versions, so a binary compiled once continues to load on Node.js 20,
22, and 24 without recompilation.

Changes:
- binding.gyp: switch include path to node-addon-api, add
  NAPI_DISABLE_CPP_EXCEPTIONS define
- native/can.cc: rewrite RawChannel to use Napi::ObjectWrap<T>,
  instance methods, Napi::FunctionReference/ObjectReference for
  persistent listener handles, and napi_env stored for uv_async
  callbacks
- native/signals.cc: rewrite DecodeSignal/EncodeSignal as plain
  Napi::Value functions
- package.json: replace nan dependency with node-addon-api ^8.0.0
- Dockerfile.build-test: new file for verifying compilation on Linux

Verified: compiles cleanly on Node.js 22 / Linux arm64 (Debian
bookworm) with no warnings.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Run test-parsing and test-signal_conversion (the tests that exercise the
migrated can_signals native module) inside the container. The two vcan-
dependent test suites (test-raw_basic, test-signal_generation) require a
real Linux kernel with the vcan module and cannot run in Docker Desktop.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Regenerate lock file after replacing nan with node-addon-api.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
uv_async callbacks run on the Node.js main thread but are not entered
via NAPI, so no HandleScope is open when they fire. Accessing any
Napi::Reference value (FunctionReference::Value()) without one causes
a fatal: "Cannot create a handle without a HandleScope".

Add Napi::HandleScope scope(env) at the top of async_channel_stopped()
and async_receiver_ready(), matching the Nan::HandleScope that the
previous NAN implementation had in the same two functions.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Now that the native addon uses N-API, the same binary should load
unchanged across all LTS releases. Adding 24.x to the matrix verifies
this on every PR.

Dockerfile.build-test reverted to build-only — its purpose is to
quickly verify compilation on Linux without a CAN interface.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@MyHomeMyData
Copy link
Copy Markdown

Thank you for implementing this so quickly — that's much appreciated!

Tested on Node.js 22.22.3 / Linux x64. The native addon builds and loads correctly — ABI stability works as expected.

However, npm run build:ts fails because the declared devDependency typescript@4.8.4 is incompatible with the tsconfig base @tsconfig/node22, which requires TypeScript ≥ 5.4 for the es2024, ESNext.Collection and ESNext.Iterator lib targets.

Additionally, tsc inadvertently picks up .js files from the parent project directory (4 levels above the package). Adding an explicit include or rootDir to tsconfig.json would prevent this.

As a result, dist/socketcan.js is missing after install and the package cannot be loaded. Could you update the TypeScript devDependency and add a prepare script (or include dist/ in the repository) so the package is usable out of the box?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants