Apache Cordova, ES2015 and Babel

I created a simple Apache Cordova app and got it working on my iOS and Android emulators in the last article. My hope was to convert the app to ECMAScript 2015 (the new fancy name for what we have been calling ES6 for the past year) and work on Browserify for the app packaging. However, the initial bits took too long. So let’s remedy that now. I’m starting from the basic app template that the cordova tool produced.

Let’s start by looking at the code that the basic app template includes (in ./www/js/index.js):

var app = {
    // Application Constructor
    initialize: function() {
    // Bind Event Listeners
    // Bind any events that are required on startup. Common events are:
    // 'load', 'deviceready', 'offline', and 'online'.
    bindEvents: function() {
        document.addEventListener('deviceready', this.onDeviceReady, false);
    // deviceready Event Handler
    // The scope of 'this' is the event. In order to call the 'receivedEvent'
    // function, we must explicitly call 'app.receivedEvent(...);'
    onDeviceReady: function() {
    // Update DOM on a Received Event
    receivedEvent: function(id) {
        var parentElement = document.getElementById(id);
        var listeningElement = parentElement.querySelector('.listening');
        var receivedElement = parentElement.querySelector('.received');

        listeningElement.setAttribute('style', 'display:none;');
        receivedElement.setAttribute('style', 'display:block;');

        console.log('Received Event: ' + id);


This is basically a class for handling events together with a method that handles the application code. I think I can abstract the event handling and use the EventEmitter class from the NodeJS package. I like the semantics of EventEmitter a little better. Let’s take a look at my new code (which I’ve placed in src/js/index.js):

import DeviceManager from './lib/device-manager';

var app = new DeviceManager();
app.on('deviceready', function () {
  var parentElement = document.getElementById('deviceready');
  var listeningElement = parentElement.querySelector('.listening');
  var receivedElement = parentElement.querySelector('.received');

  listeningElement.setAttribute('style', 'display:none;');
  receivedElement.setAttribute('style', 'display:block;');

I could have used an arrow-function for the callback in app.on(), but I like the callback semantics when I’m not in a class and have no parameters. I believe it is more readable. I now need a DeviceManager class. This is stored in the file src/js/lib/device-manager.js:

import {EventEmitter} from 'events';

 * A class for handling all the event handling for Apache Cordova
 * @extends EventEmitter
export default class DeviceManager extends EventEmitter {
   * Create  new DeviceManager instance
  constructor() {
    document.addEventListener('deviceready', this.onDeviceReady.bind(this), false);

   * Handle the deviceready event
   * @see http://cordova.apache.org/docs/en/5.4.0/cordova/events/events.deviceready.html
   * @emits {deviceready} a deviceready event
   * @param {Event} the deviceready event object
  onDeviceReady(e) {
    console.debug('[DeviceManager#onDeviceReady] event = ', e);

    this.emit('deviceready', e);

I’m preparing this for conversion into a library by include esdoc tags for documentation. There is more to do in this class. I want to trap all the Apache Cordova events so that I can re-emit them through the EventEmitter module, for example. However, this is enough to get us started.

Note that there is a little extra work needed if you want to use Visual Studio Code to edit ES2015 code. Add the following jsconfig.json file:

    "compilerOptions": {
        "target": "ES6"

Now that I have the code written, how does one build it? First step is to bring in npm, which I will use as the package manager for this project:

npm init --yes

I like to answer yes to everything and then edit the file directly. In this case, I’ve set the license to MIT, added a description and updated the author. However all of these are optional, so this single line lets me start working straight away.

A Diversion: Babel 6

My next step was to download the tool chain which includes gulp, browserify and babelify. I always browse the blog before adding a module to a project and it was lucky I did that in the case of Babel as there were major changes. Here is the short short version:

  1. Babel is just a transpiler framework now
  2. You must create a .babelrc file for it to work

Fortunately, getting ES2015 compilation working with the .babelrc file is simple. Here it is:

	"presets": [ "es2015" ]

If you are using Babel on the command line, I highly recommend you read the introductory blog post by the team.

Build Process: Gulp

As I pretty much always do nowadays, gulp is my go-to build process handler. To set it up:

npm install --save-dev gulp gulp-rename vinyl-source-stream browserify babelify babel-preset-es2015

The first five modules are from my prior experience with Browserify. The last one is a new one and brings in the ES2015 preset for Babel6. The Gulpfile.js is relatively simple right now:

var babelify = require('babelify'),
    browserify = require('browserify'),
    gulp = require('gulp'),
    rename = require('gulp-rename')
    source = require('vinyl-source-stream');

gulp.task('default': [ 'build' ]);
gulp.task('build': [ 'js:bundle' ]);

gulp.task('js:bundle', function () {
  var bundler = browserify({
    entry: './src/js/index.js',
    debug: true

  return bundler

This is the exact same recipe I have used before, so the semantics haven’t changed. However, you need to uninstall old-babel and install the new packages for this to work. Of course, this is a new project, so I didn’t have to uninstall old-babel. To build the package, I now need to do the following:

gulp build

To make it even easier, I’ve added the following section to the npm package.json:

  "scripts": {
    "build": "gulp build"

With this code, I can now run npm run build instead of gulp directly. It’s a small level of indirection, but a common one.

Wrapping up

The build process overwrites the original www/js/index.js. Once the build is done, you should be able to use cordova run browser to check it out. You should also be able to build the iOS and Android versions and run those. There isn’t any functional change, but the code is now ES2015, and that makes me happy.

In the meantime, check out the code!