Nice buildings

This is a small tutorial to get started with Electron as a regular Node.js / Npm dependency, so you can build stand-alone app that can be published to npmjs.org.

You should be aware that Electron is the new name of the formerly known Atom-Shell. If you can't figure out what it is, please start reading First encounter with Atom-Shell first. This previous article contains valuable informations and may help you deciding which node/chromium runtime technologies you should pick... the other major contender being NW.js (formerly known as Node-Webkit, name dropped because it is now using io.js and chromium).

Let's get started!

Firstly, create your basic package structure, the usual way. Mine usually looks like this:

.
├── app.js
├── bin/
│   └── my-app
├── front/
│   ├── css
│   ├── html
│   ├── js
│   └── main.html
├── LICENSE
├── log/
├── lib/
├── Makefile
├── node_modules/
├── package.json
├── README.md
└── test/

Here, app.js is the entry point of the Electron application. When your application is not made the stand-alone way, you run with electron followed by the directory of your application, e.g. electron path/to/my/app (assuming Electron is installed globally). Electron needs a package.json, and fortunately, its package is fully compatible with node/npm package. So app.js is the entry point provided as the "main" property of your package.json, and it could be anything you want.

Have in mind that the root directory of your stand-alone application is the root directory of your package.

Also, I like app.js to be as simple and as short as possible. I choose to put Javascript code running in the browser context in the lib/ directory, and I choose to put Javascript code running in the renderer/webpage context into the front/js/ directory (the difference between both contexts has been explained here).

Any front-end code is put into the front/ directory. It could be useful to keep things separated, if one day we want an online website version of our app, and we have to bring back to life the good ol' client/server paradigm.

Secondly, add the electron-prebuilt dependency:

npm install electron-prebuilt --save

If you plan to use third-party package with native code, you should install electron-rebuild as well:

npm install electron-rebuild --save

... I will explain later how to use it properly.

Let's build it!

Finally, you need to create your own executable file. For this step, I have simply copied, adapted and reformated the launcher that you can found at ./node_modules/electron-prebuilt/cli.js, and put it into the ./bin/ directory with the name my-app.

I do not append .js to the name, because it will be the real command your user will have to type in their console, if they installed your package globally (e.g. npm install -g my-app). In fact, you CAN provide a different name for the globally installed binaries (see below), but I don't like to confuse myself, I like to stay consistent...

Here is the ./bin/my-app file:

#!/usr/bin/env node

// It just returns a path
var electronPath = require( 'electron-prebuilt' ) ;

var childProcess = require( 'child_process' ) ;

// Adjust the command line arguments: remove the "node <code.js>" part
var args = process.argv.slice( 2 ) ;  
// ... and insert the root path of our application (it's the parent directory)
// as the first argument
args.unshift( __dirname + '/../' ) ;

// Run electron
childProcess.spawn( electronPath , args , { stdio: 'inherit' } ) ;  

Now you can run your application using the command ./bin/my-app, and by the way, if you installed your application globally (npm install -g my-app), you would run it simply with the command: my-app. You should just add that line into your package.json to let npm know about it:

"bin": {
  "my-app": "./bin/my-app"
},

But wait? We don't have an application right now, do we?

Okey, let's copy-paste the code from the quick start documentation of Electron!

Here is our ./app.js file, slightly modified:

var app = require('app');  // Module to control application life.  
var BrowserWindow = require('browser-window');  // Module to create native browser window.

// Report crashes to our server.
require('crash-reporter').start();

// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the javascript object is GCed.
var mainWindow = null;

// Quit when all windows are closed.
app.on('window-all-closed', function() {  
  if (process.platform != 'darwin')
    app.quit();
});

// This method will be called when Electron has done everything
// initialization and ready for creating browser windows.
app.on('ready', function() {  
  // Create the browser window.
  mainWindow = new BrowserWindow({width: 800, height: 600});

  // and load the index.html of the app.
  mainWindow.loadUrl('file://' + __dirname + '/front/main.html');

  // Open the devtools?
  //mainWindow.openDevTools();

  // Emitted when the window is closed.
  mainWindow.on('closed', function() {
    // Dereference the window object, usually you would store windows
    // in an array if your app supports multi windows, this is the time
    // when you should delete the corresponding element.
    mainWindow = null;
  });
});

And now our ./front/main.html file:

<!DOCTYPE html>  
<html>  
  <head>
    <title>Hello World!</title>
  </head>
  <body>
    <h1>Hello World!</h1>
    We are using io.js <script>document.write(process.version)</script>
    and Electron <script>document.write(process.versions['electron'])</script>.
  </body>
</html>  

Now you can run your wonderful app! Now just run ./bin/my-app and let it shine!

Waoh! We built something really shiny!

Using package featuring native code

Electron use a version of Chromium that is different from node.js. So native packages cannot work without being rebuilt.

Rebuilding can be painful, hopefully electron-rebuild is here to do the job for us.

You should have installed it using npm install electron-rebuild --save already.

Anytime you install some packages featuring native code, you should run electron-rebuild afterward. E.g. if we want to install the package whatever, we have to do it in two step:

npm install whatever --save  
./node_modules/.bin/electron-rebuild

I should warn you here:

“electron-rebuild is REALLY slow”

... and aside from that:

“electron-rebuild does NOT output ANYTHING

It's really misleading...

The first time I used it, I was thinking that it was not working at all, and I Ctrl-C it multiple times... Then, I finally give it a last chance to shine, while I was making a tea. On my two year old laptop, it takes few minutes.

Also people installing your supa-ground-breaking package are not aware of electron-rebuild, so you have to add a line into your package.json, so it will kick-in at installation time. Here is the line:

"scripts": {
    "install": "electron-rebuild"
},

You should also warn your user that the installation process can take a while...

That's all folks!