SerialPilot

/03 — Recipes

Electron

Native modules and Electron get along just fine — provided you put them in the right process and rebuild against Electron's V8. Two rules and three gotchas.

Install & rebuild

$ npm install serialpilot @serialpilot/bindings-cpp
$ npm install --save-dev electron-rebuild
$ npx electron-rebuild

electron-rebuild compiles the native binding against the V8 ABI shipped with your installed Electron version. Run it after every Electron upgrade.

Rule #1 — main process only

Create SerialPilot instances in the main process. Don't reach for them from a renderer; you'll fight context isolation and lose.

// main.js
const { SerialPilot } = require('serialpilot')
const { ipcMain } = require('electron')

ipcMain.handle(‘list-ports’, () => SerialPilot.list())

Rule #2 — bridge through contextBridge

Expose a small, audited surface to the renderer with contextBridge. Never set nodeIntegration: true:

// preload.js
const { contextBridge, ipcRenderer } = require('electron')

contextBridge.exposeInMainWorld(‘serialpilot’, { listPorts: () => ipcRenderer.invoke(‘list-ports’), open: (opts) => ipcRenderer.invoke(‘open-port’, opts), write: (id, data) => ipcRenderer.invoke(‘port-write’, id, data), })

Packaging gotchas

ToolWhat to do
electron-builderSet "npmRebuild": true in build; it rebuilds natives for the target Electron during the packaging step.
electron-forgeAdd @electron/rebuild as a maker plugin so each build picks up native changes.
  • ASAR: exclude @serialpilot/bindings-cpp from the asar bundle (Node can't require() a .node file from inside an asar archive).
  • Signing on macOS: the .node binary needs the com.apple.security.cs.allow-jit entitlement — most Electron projects already include this.
  • Linux distribution: remind users to add themselves to dialout at first run, or your app gets PermissionDeniedError the moment it tries to open a port.

Troubleshooting

SymptomLikely causeFix
"NODE_MODULE_VERSION mismatch"Built for Node, not Electron's V8npx electron-rebuild
App boots, but list() returns nothingSandbox blocks the bindingDisable sandbox for the main process or whitelist USB devices
Native module missing in packaged appasar swallowed itAdd to asarUnpack in builder config

Edit this page on GitHub