Native addon test execution requires Electron, not Node.js
Cannot call npm build script from Node.js directly since Node.js doesn't set up an "app" in the eyes of macOS. Electron does set up an app, so native addon code (especially UI) must be tested by requiring and calling it from an Electron main or renderer process, not from bare Node.js.
Xcode requirements for macOS native development
To build native macOS addons, install Xcode from Mac App Store and Xcode Command Line Tools via xcode-select --install in Terminal.
macOS requirements for native addons
To build native Node.js addons on macOS, you need the Xcode Command Line Tools, which provide the necessary compilers and build tools (clang, clang++, and make). These can be installed via the command: xcode-select --install
Native Node.js addons in Electron
Electron supports the use of Native Node.js Addons, which are dynamically-linked shared objects on Unix-like systems or DLL files on Windows. These can be loaded into Node.js or Electron using require() or import functions, and behave like regular JavaScript modules while providing an interface to code written in C++, Rust, or other languages that compile to native code.
Native code use cases in Electron
Native code in Electron can be used to access native platform APIs not available in JavaScript (macOS, Windows, or Linux APIs), create UI components that interact with native desktop frameworks, integrate with existing native libraries, and implement performance-critical code that runs faster than JavaScript.
node-addon-api purpose
node-addon-api is a C++ wrapper for the low-level Node.js API that makes it easier to build addons. It provides a C++ object-oriented API that is more convenient and safer to use than the raw C-style API.
bindings module purpose
The bindings module is a helper module that simplifies the process of loading compiled native addons. It handles finding the compiled .node file automatically.
node-gyp build system
node-gyp is a cross-platform command-line tool written in Node.js that compiles native addon modules for Node.js using platform-specific build tools: Visual Studio on Windows, Xcode or command-line tools on macOS, and GCC or similar compilers on Linux.
binding.gyp configuration file
The binding.gyp file is a JSON-like configuration file that tells node-gyp how to build a native addon. It specifies the target name, sources, include directories, dependencies, compiler defines, and platform-specific settings for building the native module.
Windows requirements for native addons
On Windows, the official Node.js installer offers optional installation of 'Tools for Native Modules' which installs Python 3 and the 'Visual Studio Desktop development with C++' workload. Alternatively, you can use chocolatey, winget, or the Windows Store.
Linux requirements for native addons
To build native addons on Linux, you need a supported version of Python, make, and a proper C/C++ compiler toolchain like GCC.
Using native addons in Electron
To use a native addon in an Electron application: include it as a dependency in the Electron project, build it targeting the specific Electron version (electron-forge handles this automatically), and import and use it like any other module in a process that has Node.js enabled.
Native addon development languages
Native addons can be written in C++, Rust (using crates like napi-rs, neon, or node-bindgen), and Objective-C/Swift (through Objective-C++ on macOS). Platform-specific implementations differ significantly, especially when accessing platform-specific APIs or UI frameworks like Windows' Win32 API, COM, UWP/WinRT, macOS's Cocoa, AppKit, or ObjectiveC runtime.
Hello World native addon example
Example showing a complete native addon with a C++ hello_world function that concatenates strings, wrapped with node-addon-api (NAPI) to expose it to JavaScript. The addon is initialized with NODE_API_MODULE(my_addon, Init) macro. JavaScript code loads it using the bindings module: const native = bindings('my_addon'); const addon = new native.MyAddon(); const result = addon.helloWorld('input');