Blog

Max Gfeller's Blog

Using Browserify in Electron Applications

Electron is a framework that enables you developing cross-platform desktop applications using web technologies. In this blog post i’ll assume that you already know how Electron works and that you’ve also tried it out yourself.

If not so, here are a few links to start:

If you’re looking for a good introduction into the topic i can recommend Developing an Electron Edge, a book which i’ve co-authored.

Browserify for renderer processes

Renderer processes are basically the windows you open from your main process. They are browser windows with an optional nodeIntegration. I’ve happened to stop using the nodeIntegration feature most of the time firstly because of possible security risks and secondly because i often work with Babel which has to be bundled and transpiled anyway. Browserify therefore is my tool of choice and i prefer its simplicity over the more bloated Webpack. That being said, both of them are great tools and they both do the job.

Browserify for the main process

This is where it gets interesting. You may ask yourself “why would i need to use Browserify for the main process? Can’t i just use Electron’s require() function?”. This is right, and it is enough in most of the use cases. However, when your app starts getting bigger you may notice that your build artifacts also become a lot bigger. That often is the case when you use a lot of npm modules.

Although Browserify was created to use Node modules in the browser, it can also be used to bundle your Node modules for Node itself.

When you’re developing an Electron application you have a single main process. This process handles the whole lifecycle of the application and one or multiple renderer processes can be started from it.

It took me quite a while to get Browserify to bundle my main process file but i’ve eventually succeeded in doing so. Here’s how it’s done:

Install Browserify as a dev dependency

This is the way to go. Browserify can also be installed globally but having this on a project basis makes more sense.

1
npm install browserify --save-dev

Set up a npm script

Inside your package.json file create a new script called bundle:

1
2
3
4
5
{
  "scripts": {
    "bundle": "browserify --ignore-missing --no-builtins --no-commondir --insert-global-vars=\"global\" --no-browser-field main.js > main.bundle.js"
  }
}

Those parameters are all needed if you want to bundle up your main process. main.js is the script where you’ve implemented your main process. The generated bundle main.bundle.js needs to be set as the main script inside your package.json file.

Minify the bundle

This step is optional. If you want to save a few bytes in your build artifacts or want to easily make your code unreadable for your users you can uglify it.

Just do an npm install uglify-js --save-dev and enhance your bundle script:

1
2
3
4
5
{
  "scripts": {
    "bundle": "browserify --ignore-missing --no-builtins --no-commondir --insert-global-vars=\"global\" --no-browser-field main.js | uglifyjs -c > main.bundle.js"
  }
}

Package your application

Most of the time i use electron-packager for this. Easily npm install --save-dev electron-packager and define another script for the packaging:

1
2
3
4
5
6
{
  "scripts": {
    "bundle": "browserify --ignore-missing --no-builtins --no-commondir --insert-global-vars=\"global\" --no-browser-field main.js > main.bundle.js",
    "package-macos": "electron-packager ./ --ignore=\"(^(/main.js)$||node_modules/)\" --platform=darwin --arch=x64 --overwrite --out build/"
  }
}

Of course you can add package scripts for other platforms as well.

Notice the smaller size

Check out the size of the generated build artifacts and notice how they’ve become smaller. The bigger your application gets and the more npm modules you use, the more you can save through this additional step.

Composer Test

Composer is a package manager for PHP which already serves ~34’000 packages (as of July, 22nd) via Packagist and grows with 70 new packages a day.

It is already used by the majority of open source PHP libraries, such as Symfony and ZendFramework.

However, when it comes to unit testing, most libraries demand you to have either PHPUnit or Behat installed globally.

A change to composer that was merged yesterday makes testing now as easy as running composer test.

The scripts field

The scripts field in the composer.json was reserved for certain events until now, such as pre-package-install or post-update-cmd which made it possible to hook into these events and run additional scripts.

Additionally to this you can now define your own custom scripts and run them with composer run-script or simply with composer run. Composer will also map the vendor/.bin to the $PATH meaning that you can define phpunit or your unit test framework of choice as a development dependency:

1
2
3
4
5
6
7
8
9
{
    "name": "example-app",
    "require-dev": {
        "phpunit/phpunit": "4.*"
    },
    "scripts": {
        "test": "phpunit -c app"
    }
}

Now you can simply run composer test.

Additional tasks

Instead of make or phing you can define additional tasks directly in your composer.json file now. So for example, if you need to provide a phar for your app you could have a composer.json that looks like this:

1
2
3
4
5
6
7
8
9
{
    "name": "example-app",
    "require-dev": {
        "kherge/box": "2.*"
    },
    "scripts": {
        "build": "box"
    }
}

Then you could package you app by running composer run-script build. This would work because the box dependency provides an executable in the vendor/.bin folder which is being mapped directly to the $PATH.

Your Own Private Npm Repository With Sinopia

Having a private repository for your modules is a fair use case. While working on your own projects that should not (yet) be open source and available for everyone you still should not have to miss the comfort of having a big amount of small packages to organize your large codebase.

npm itself offers the possibility to define different repositories to work with via the --registry flag. This however requires you to have your own CouchDB instance correctly set up, which is possible but too complicated in my opinion.

sinopia

sinopia is a small package which solves exactly this use case. It does not require CouchDB and saves the packages directly on the file system. And the best part is that it is compatible with the standard npm client.

Setting up sinopia

Setting up sinopia is fairly easy. You need node and npm installed on your host and then you can easily run npm install -g sinopia

And then in the directory where you want the packages to be stored simply run sinopia and it automatically starts a server.

It then creates an admin user and a random password for you which you need to configure in your npm client:

1
npm --registry=http://yourhost:4873 adduser

If you want to reuse your existing user simply add your username to the config.yaml file in the directory you run sinopia. The password hash can be generated with the following node function: crypto.createHash('sha1').update('yourpass').digest('hex')

To enable the server to listen for connections from outside simply add the following line to your config: listen: 0.0.0.0:4873

alias

To have an additional command for accessing your private repository (rather than publish all new modules to it) simply create an alias:

1
alias pnpm="npm --registry=http://yourhost:4873 --always-auth=true --ca=null"

Now you can publish a module to your own server using pnpm publish and to registry.npmjs.org using npm publish.

One Week With the Geeksphone Peak

I ordered the Geeksphone Peak on the 5th May when there where devices available again and received it just two days later.

Unfortunately i couldn’t use all features of it back then because i had no SIM card adapter and only a nano SIM card available.

In the mean time i ordered one and i could use the phone in its whole functionality. To really find out how it behaves in everyday use, i decided to use it as my primary phone for a whole week instead of the iPhone.

This is my report after one week.

Calling, sms etc.

Having the device in your hand and doing a phone call for the first time feels very special when you realize that all of it is based on web technologies.

Importing the contacts was very easy as i had all of them synced in my Google account and the phone comes with an importer preinstalled.

The look of the dialer app doesn’t differ a lot from the ones you may already know from iOS or Android and works already very well and stable.

The SMS app also works as it should and the texts are organized in conversations with people.

Browser

The phone comes with the firefox (surpise!) mobile browser which basically looks like the one on Android. There are only few settings yet and at first it used Bing as search engine. Fortunately it now uses Google but you still can’t choose this on your own.

As far as i have seen, Firefox Sync also isn’t available yet.

Marketplace

The marketplace is actually very cool although it has not that many apps yet and you find a lot of mobile optimized websites that don’t offer any “native” functionality. Few people actually know that apps created for Firefox OS also run on Firefox for Android and desktop operating systems. That’s right: they run on nearly all platforms. Developers have to keep that in mind when developing for Firefox OS and make their app as responsible as possible.

Performance

If you use the Peak you will realize that it is not yet production ready. The operating system still has many performance issues and apps crash from time to time. But with the updates provided by Geeksphone it always gets better and i’m sure until the first devices are being released this summer the system will be stable and fast.

Updates

Geeksphone offers on-the-air updates for the operating system. If you want to use the unstable builds they offer current images here:

downloads.geeksphone.com

There is a small guide available on how to flash these images onto your phone.

Conclusion

Despite the Peak being my primary phone for this week i still couldn’t let my iPhone at home all the time. There are several apps (like Whatsapp which i use a lot) that are not yet available for Firefox OS. (I’m developing an instant messaging app at the moment but it is not yet finished)

But if you hold such a device in your hands, you will realize that it is a huge opportunity for an open operating system that is based on established technologies and for cheaper phones.

It especially is a huge chance for 3rd world countries where smart phones still are a luxury unavailable to most of the people living there.

I’m really looking forward to it being officially released and seeing all the devices which will be available with Firefox OS.

Understanding (and Manipulating) `this` in JavaScript

Knowing about this in JavaScript is not only very basic but also very important.

A lot of good articles have been written which describe how this in JavaScript works, for example:
- Mozilla MDN
- Yehuda Katz
- impressivewebs.com

Knowing how you can manipulate this gives you great power and you no more have to write var self = this.

See for example this code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var myClass = function() {
    this.files = [];
}

myClass.prototype.addFile = function(file) {
    this.files.push(file);
}

myClass.prototype.loadFilesFromFolder = function() {
    fs.readdir('/tmp', function(err, files) {
        if(err) throw new Error(err);
        files.forEach(this.addFile);
    });
}

var myInstance = new myClass();
myInstance.loadFilesFromFolder();

Now if you try to run that code in your node.js environment, it will give you the following error:

1
2
3
4
files.forEach(this.addFile);
      ^
TypeError: undefined is not a function
    at Array.forEach (native)

This comes because the addFile method was not found on the this keyword. So what is then exactly the this keyword now? Lets try it and log it to the console:

1
2
3
4
5
6
7
8
9
10
11
{ ArrayBuffer: [Function: ArrayBuffer],
  Int8Array: { [Function: Int8Array] BYTES_PER_ELEMENT: 1 },
  Uint8Array: { [Function: Uint8Array] BYTES_PER_ELEMENT: 1 },
  Uint8ClampedArray: { [Function: Uint8ClampedArray] BYTES_PER_ELEMENT: 1 },
  Int16Array: { [Function: Int16Array] BYTES_PER_ELEMENT: 2 },
  Uint16Array: { [Function: Uint16Array] BYTES_PER_ELEMENT: 2 },
  Int32Array: { [Function: Int32Array] BYTES_PER_ELEMENT: 4 },
  Uint32Array: { [Function: Uint32Array] BYTES_PER_ELEMENT: 4 },
  Float32Array: { [Function: Float32Array] BYTES_PER_ELEMENT: 4 },
  Float64Array: { [Function: Float64Array] BYTES_PER_ELEMENT: 8 },
[...]

The readdir method obviously uses an ArrayBuffer which then executes the callback function we defined - clearly manipulating the context we had expected.

So how can we manipulate this to use the this we expected?

The bind() method

The bind method(https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function/bind) takes an infinite number of arguments, where the first one is the this value and all the others can be used to pre-specify arguments to pass.

The bind method then creates a copy of the function.

For example:

1
2
3
var helloConsole = console.log.bind(null, 'Hello!');

helloConsole() // outputs "Hello!"

In this example i set the this keyword to null as we don’t really need it and instead i passed a default argument to the console.log method.

To come back to our example we can also simply use it to set the this keyword to a value we want:

1
2
3
4
5
myClass.prototype.loadFilesFromFolder = function() {
    fs.readdir('/tmp', function(err, files) {
        files.forEach(this.addFile);
    }.bind(this));
}

Well if we run the code again the error no more occurs but instead there is a new error:

1
2
3
  this.files.push(file);
             ^
TypeError: Cannot call method 'push' of undefined

The problem again is again that the files array does not exist on the this instance. Advanced methods like Array.forEach, Array.map or Array.filter all accept a second argument which should be used as the this value in the passed callback function.

1
2
3
4
5
myClass.prototype.loadFilesFromFolder = function() {
    fs.readdir('/tmp', function(err, files) {
        files.forEach(this.addFile, this);
    }.bind(this));
}

Now our code actually does what it was supposed to do.

Hello World

My name is Max and i´m a software developer living in Switzerland. I love open source, javascript and generally building things.

Why am i doing this blog?

As i love to work on side projects and hack on nifty little pieces of software i happen to learn a lot on different topics and stumble across many problem.

This blog has the purpose to show off cool hacks, and solutions to problems i had.

The plan is to write a blog post each week but i’m not sure if i can fulfill that promise.

The setup

The blog is built om the awesome Octopress which is a blogging framework built on the Jekyll static site generator.

Octopress offers the availability to write the texts in Markdown (which i a great markup language that is being compiled to html code), has a great code embedding view and happens to have a lot of plugins available. It is very leightweight as it only consists of static files and can easily be hosted anywhere.

The source code for my blog lays in an own Github repository, so that i can access it from everywhere and write new blog posts very quickly.

The blog itself is hosted at Cyon - where i work - and available under maxgfeller.com.

Octopress/Jekyll offers various ways to easily deploy the site to a server. Because i have ssh enabled at my webhosting i chose rsync: deploying is now as easy like typing rake deploy

Conclusion

If you like what i’m writing you should follow me on Twitter and on Github.