--- layout: post title: "Integrate native Node.js modules into an Electron app (1/2)" date: 2018-02-22 21:12 comments: true categories: [javascript] cover: /images/cover/avatar.png keywords: description: --- ### tl;dr - when you probably really need Electron - how to integrate Webpack with Electron - develop (and test) browser and Electron app - Electron vs. system-wide node.js ### OMG, another Electron freak! Yeah yeah, I hear you saying *Why don't you learn Swift/C#/C++/…*, *Electron is so much memory intensive* etc. I know you're there, [Electron](https://electronjs.org/) haters. Well, in my opinion it's super convenient to develop web application and have a possibility to run it inside Electron (with a great advantage—access to underlying node.js). Personally, it's been thrilling to communicate with node.js from Electron (I'm kind of passionate developer). Sure, you can feel no Electron app is native, but that's what trade-off is about. # When you may need Electron A simple rule: if your project has a node.js dependency (meaning, the dependency is working only in node.js environment, not in a browser), you need Electron. What Electron basically does is running your Javascript in Chromium with node.js environment. Imagine you can do your usual front-end stuff and also you can control for example serial port (or USB port) peripherals in the same fashion, in the same project, even in the same file. # Goal I'll show you how to communicate with serialport device from your Electron app. First, we'll create a project and install dependencies. Then [Webpack](https://github.com/webpack/webpack) needs to be configured. After this step we can finally run the app. The demo project will be fully functional in Electron and will gracefully degrade in a browser. # Prerequisites and project structure Install [node.js](https://nodejs.org) with npm and [Electron](https://electronjs.org/). MacOS and Linux is OK, Windows should be supported too. ``` ├── app.js ├── app.test.js ├── build │ └── ... ├── index.electron.js ├── index.html ├── node_modules │ └── ... ├── package.json ├── release-builds │ └── ... └── webpack.config.js ``` Actual app code is inside `app.js`; we will write some tests in `app.test.js`; built bundle inside `build` directory; Electron main file is `index.electron.js`; our webpage index file is `index.html`; `package.json` contains list of dependencies and scripts; into `release-builds` we will pack whole Electron ready for distribution (in the next article); and finally `webpack.config.js` serves as Webpack config file. Paste this into `package.json` and run `npm install`: {% codeblock package.json lang:json %} { "name": "electron-tutorial", "main": "index.electron.js", "scripts": { "build": "webpack", "electron": "electron .", "test": "jest" }, "dependencies": { "electron": "^1.8.2", "electron-rebuild": "^1.7.3", "jest": "^22.4.0", "serialport": "^6.0.5", "webpack": "^3.11.0" } } {% endcodeblock %} # Let's start with Webpack Paste this into `webpack.config.js`: {% codeblock webpack.config.js lang:javascript %} const webpack = require('webpack'); const path = require('path'); const platform = process.env.PLATFORM; module.exports = { entry: { app: path.join(__dirname, 'app.js') }, output: { path: path.join(__dirname, 'build'), filename: '[name].js', publicPath: 'build/', }, plugins: [ new webpack.DefinePlugin({ 'process.env.IS_ELECTRON': JSON.stringify(platform === 'electron'), }) ], target: platform === 'electron' ? 'electron-renderer' : 'web', externals: { bindings: 'require("bindings")' // fixes warnings during build } }; {% endcodeblock %} You probably already know Webpack. What's critical in configuration below is `target` option. You need to set `target` to `electron-renderer` if you're building code for Electron. Long story short—this setting will enable node.js and browser environment in your bundle. Also notice `process.env.IS_ELECTRON` definition. This way we can easily execute parts of javascript in Electron only. # Develop `app.js`, `index.html` and `index.electron.js` code looks like this: {% codeblock app.js lang:javascript %} if (process.env.IS_ELECTRON) { const serialport = require('serialport'); // will show connected serialport devices if any serialport.list().then(list => console.log(list)); alert('Electron detected'); } else { alert('Browser detected'); } {% endcodeblock %} Notice the `if` clause; the code inside the clause will be executed only if the bundle was built for Electron environment. {% codeblock index.html lang:html %}{% raw %}