layout: post title: "Integrate native Node.js modules into an Electron app (2/2)" date: 2018-02-28 21:48 comments: true categories: [javascript] cover: /images/cover/avatar.png keywords:
node_modules
directories—tips & tricksSo we've got super simple app which uses node.js features thanks to Electron. Let's say it evolves into 100k LOC large app with dozens of dependencies (both browser-friendly and native node.js). How to produce a space-efficient bundle (in the context of Electron)?
We'll be using electron-packager to create a OS-specific distributable bundle (Electron bundle). After we build javascript (javascript bundle) we keep an eye on native modules location and node_modules
content inside Electron bundle.
Build production quality javascript bundle is webpack-specific (and probably also babel-specific). I won't cover this part as it has nothing to do with to Electron. If you use newest Webpack 4.0 you can use nice new features related to development/production mode.
Electron-packager copies node_modules
into the final Electron bundle (which is slow and isn't space-efficient at all). The good news is it ignores all packages in devDependencies
group in package.json
. We'll use that.
We need bindings
dependency to keep in Electron bundle node_modules
. The dependency is responsible for lazy loading of native node.js modules and cannot be part of javascript bundle. As it is a dependency of your project dependencies, it is not listed in package.json
. Simply do npm i --save bindings
. This can be tricky and can break things but yolo.
Notice deps groups:
{% codeblock package.json lang:json %} { "name": "electron-tutorial", "main": "index.electron.js", "scripts": { "build": "webpack", "electron": "electron .", "test": "jest" }, "dependencies": { "bindings": "^1.3.0" }, "devDependencies": { "electron": "^1.8.2", "electron-packager": "^11.0.1", "electron-rebuild": "^1.7.3", "jest": "^22.4.0", "serialport": "^6.0.5", "webpack": "^3.11.0" } } {% endcodeblock %}
In projects I develop there's usually a few non-Electron dependencies in the main Electron file (as seen in example below). Keep all non-Electron dependencies inside dependencies
group (unless you plan to bundle the main file with Webpack's target: 'electron-main'
option).
{% codeblock index.electron.js lang:javascript %} const { app, BrowserWindow } = require('electron'); const Raven = require('raven'); const os = require('os'); const isDev = require('electron-is-dev');
const isBundled = !isDev;
if (process.env.NODEENV === 'production') { Raven.config('XXX', { captureUnhandledRejections: true, tags: { process: process.type, electron: process.versions.electron, chrome: process.versions.chrome, platform: os.platform(), platformrelease: os.release() } }).install(); }
// ...rest of electron main file {% endcodeblock %}
I would keep raven
and electron-is-dev
in dependencies
group.
Simply copy all native modules (*.node
) to build
directory (they should be built in production quality by default). I wrote a few words about them in the previous article.
There's a tiny change in relectron-rebuild
command. By default it won't rebuild modules in devDependencies
group. Run the command with t
option: ./node_modules/.bin/electron-rebuild -e node_modules/electron -t prod,dev
.
Note 1: I've run into this error while running Electron app: Uncaught Error: Could not find module root given file: "file:///Users/cinan/Coding/js/electron-tutorial/electron-tutorial-darwin-x64/electron-tutorial.app/Contents/Resources/app/build/app.js". Do you have a package.json file?
This is a known bug. There is a pull request (not yet merged), you can install fixed version with npm i --save "bindings@https://github.com/ArnsboMedia/node-bindings.git#fix-getFileName-method-for-electron-use"
First run PLATFORM=electron npm run build
to create a javacript bundle. Build native modules with ./node_modules/.bin/electron-rebuild -e node_modules/electron -t prod,dev
and copy them into build
directory: cp node_modules/serialport/build/Release/serialport.node build
.
Now run ./node_modules/.bin/electron-packager . --overwrite
and wait a minute. New Electron bundle will be created inside directory electron-tutorial-darwin-x64
(differs on Linux and Windows).
Check out node_modules
in Electron bundle (in macOS it is electron-tutorial-darwin-x64/electron-tutorial.app/Contents/Resources/app/node_modules
). There should be a single bindings
directory. On macOS you can run the product with open electron-tutorial-darwin-x64/electron-tutorial.app
.
Note 2: if you find out your node_modules
directory is empty (although there are dependencies
defined in package.json
) then upgrade to npm@next npm i -g npm@next
(related bug).