aurelia-nw Part 2

aurelia, nodewebkit, nw.js, ES6, gulp comments edit

This is the second post regarding creating a nw.js (node-webkit) app with Aurelia. The first post can be found here In this followup post, we will make our aurelia-nw app and its build process production ready. The completed source code is available on GitHub.

I have taken the project where we left off in the previous post (view source here), and I added the content from the Aurelia demo project. I made some minor changes such as using LESS for my styles, moving the styles folder inside the src folder, and updating the gulp tasks respectively. The updated project can now be found here, and it is the starting point for this post.

Creating a Production Release

Goals:

  1. Minify and uglify all Javascript
  2. Minify all HTML/CSS
  3. Use semver to manage versioning
  4. Ensure various production values are set (i.e toolbar: false in our nw manifest properties)
  5. Isolate all release builds from the rest of the codebase, then use node-webkit-builder to compile

Release Arguments

In order to differentiate between a normal (development) build and a release build, I’ll use yargs so that we can do something along the lines of gulp build --release. I’ll also use gulp-bump to increase the app’s version. After installing those packages (npm install yargs gulp-bump --save-dev), add a args.js file in the gulp folder.

gulp\args.js:

var yargs = require('yargs');

var argv           = yargs.argv,
    validBumpTypes = "major|minor|patch|prerelease".split("|"),
    bump           = (argv.bump || 'patch').toLowerCase(),
    isRelease      = argv.release ? true : false;

if (validBumpTypes.indexOf(bump) === -1) {
  throw new Error('Unrecognized bump "' + bump + '".');
}

module.exports = {
  bump: bump,
  isRelease: isRelease
};

Minification

In each of the (relevant)gulp tasks, I will require gulp\args to determine if the build is a release build. Using gulp-if, I will either run the task for release or development. If it is a release build, then all output will be directed to a dist folder. Once it is there, I’ll use node-webkit-builder to compile it.

I won’t go into each library that is used for minification and uglification - they are pretty standard.

Here is the project after these modifications have been made. Running gulp build --release will produce a dist folder with the same structure as the build folder, but everything will be minified/uglified.

Versioning

When I build a production release, I would like the version of the app to be increased using semver. Let’s add a gulp task for that.

gulp\tasks\prepare-release.js:

var gulp        = require('gulp'),
    runSequence = require('run-sequence'),
    args        = require('../args'),
    paths       = require('../paths'),
    bump        = require('gulp-bump');

gulp.task('prepare-release', function(done){
  return runSequence(
    'build',
    'bump-version',
    done
  );
});

gulp.task('bump-version', function () {
  return gulp.src('./package.json')
    .pipe(bump({type: args.bump})) //major|minor|patch|prerelease
    .pipe(gulp.dest('./'))
    .pipe(gulp.dest(paths.dist));
});

This can be called like so: gulp prepare-release --release --bump minor

Prepping the Dist Folder

I want to call node-webkit-builder’s build() on the dist folder, and I want it to look something like this:

|-- app
    |-- app.js
    |-- app.html
    |-- etc..
|-- content
    |--styles
|-- jspm_packages
|-- node_modules
|-- index.html
|-- config.js
|-- package.json

A gulp task can be added to simply copy index.html, config.js, and package.json; but I don’t want to copy all of the node modules and jspm packages. I only want to include the packages that are being used - after all, most of the node packages are used for development only. Let’s add a gulp task for that.

gulp.task('npm-dependencies', function(){
  var buffer, packages, keys;

  buffer = fs.readFileSync('./package.json');
  packages = JSON.parse(buffer.toString());
  keys = [];

  for (var key in packages.dependencies) {
    keys.push('./node_modules/' + key + '/**/*');
  }

  return gulp.src(keys, {base: './'})
    .pipe(gulp.dest(paths.dist));
});

Miscellaneous Production Values

When I release the app, I want some of the NW.js manifest properties to be changed (mainly the display of the toolbar and the frame). I’ll add a gulp task and use gulp-json-editor to do this.

gulp.task('prep-nw-properties', function () {
  return gulp.src(paths.dist + 'package.json')
    .pipe(jeditor(function (json) {

      json.window.toolbar = false;
      json.window.frame = false;

      return json;
    }))
    .pipe(gulp.dest(paths.dist));
});

Building

Once the dist folder is prepped, I’ll build it with node-webkit-builder.

gulp.task('build-release', ['prepare-release'], function(cb){
  var nw = new NwBuilder(config.nwBuilderConfig);

  nw.on('log', gutil.log);
  nw.build(cb);

  return nw;
});

That’s it! At this point, we have a ready-to-release production build alongside a development build. Running gulp build-release --release will produce binaries for each platform that was supplied in a release folder in the project’s root. It’s not perfect right now, but it is effective. I’ll dive into some of the those imperfections in a future post. Here is the source for the project at this point.

Comments