Hibri Marzook Musings on technology and systems thinking

My NodeJS development workflow

I’m building a NodeJS + Angular application and there are a few things I want a smooth dev workflow and improve developer happiness. Yay !!

  1. Run the node server process and restart automatically when I make changes
  2. Concatenate my client side JS code, and merge libraries such as angular, jquery and bootstrap.
  3. I don’t want to check in concatenated files.
  4. I don’t want to check in modules or libraries, even for client side scripts
  5. I want to catch typos and style violations.
  6. I want tests to run in the background.

Grunt is the task runner I’m using to automate the tasks. Let’s look at each of these tasks.

Automate the NodeJS server process with nodemon.

npm install --save-dev nodemon

Now, run

nodemon

Eventually, you’ll want to trigger nodemon through grunt. Get the grunt-nodemon plugin. The plugin registration and the Grunt config is below.

grunt.loadNpmTasks('grunt-nodemon');

nodemon: {
    dev: {
        script: 'bin/www',
        ignore:  ['node_modules/**','bower_components/**','public/**']
    }
}

I’ve configured nodemon to ignore javascript that is not my code, and ignore changes to client side javascript. The path to the node server script is set in the script property. Since I’ve used express to create an app, this is ‘bin/www’.

Great, now executing grunt nodemon  will watch for changes and restart the server.

Next, I want to avoid all those pesky typos, misplaced semi-colons and get into the habit of writing good Javascript. I’ll use JSHint.

npm install --save-dev grunt-contrib-jshint

Configure JSHint as below,

grunt.loadNpmTasks('grunt-contrib-jshint');

jshint: {
    all: ['Gruntfile.js', 'lib/**/*.js', 'test/**/*.js','public/javascripts/app/**/*.js']
}

This configures JSHint to run on my tests, client side javascript and server code.

As with nodemon, I want JSHint running in the background and checking my code as I type and save files. This gives me quick feedback. Because JSHint doesn’t do this, I’ll introduce another plugin to the Gruntfile.

This is the grunt watch plugin, which will watch for changes in my source directory and run other Grunt tasks.

npm install --save-dev grunt-contrib-watch

Configure grunt-watch as below,

grunt.loadNpmTasks('grunt-contrib-watch');
watch: {
 scripts: {
 files: ['**/*.js','!node_modules/**','!bower_components/**','!public/javascripts/dist/**'],
 tasks: ['jshint']
 }
}

This sets up grunt-watch to watch over all of my code. Ignore directories by placing an exclamation mark before the path spec.

Great, now if I execute grunt watch on the command line, it will start watching for changes and run jshint when a change is detected. Sweet.

There is a catch though. I want to run nodemon and watch at the same time. I could run both in separate shells, but that would mean switching back and forth.

Let’s introduce grunt-concurrent. This executes grunt tasks concurrently. Now I can run nodemon and watch at the same time.

npm install --save-dev grunt-concurrent

Configure as below;

grunt.loadNpmTasks('grunt-concurrent');


concurrent: {
    dev: {
        tasks: ['nodemon', 'watch'],
        options: {
            logConcurrentOutput: true
        }
    }
}

The config above, tells grunt-concurrent to run nodemon and watch. Both tasks are grouped logically under a “dev” subtask.

If I execute grunt concurrent on the command line, I’ll have my server restarting automatically and JSHint watching my code. To reduce what I need to type even more, I’ll set up grunt to run this task as default

grunt.registerTask('default', ['concurrent']);

All I need to do is type “grunt” and have it do all the work done for me.

This gives me a  basic framework to add other tasks. Grunt allows me to group tasks. For example, I want to group all of my build time tasks as one. My build time tasks are to run jshint, run tests, merge libraries like angular and concatenate client side javascript.

grunt.registerTask('build', ['jshint','karma','concat','bower_concat']);

Instead of watch running JSHint only, let’s change it to run the build task.

watch: {
 scripts: {
 files: ['**/*.js','!node_modules/**','!bower_components/**','!public/javascripts/dist/**'],
 tasks: ['build']
 }
}

Here is the complete Gruntfile https://gist.github.com/hibri/6ccd0aef805353dc8260

In addition to the tasks described above, I’ve also used the plugins listed below

  • grunt-bower-concat : merges all the dependencies installed via bower
  • grunt-contrib-concat : merges all the client side javascript I’ve written. This includes all my angularjs code.
  • grunt-karma : To run tests
  • grunt-bower-task : To run bower install through grunt.

You’ll also notice I’ve got the following task configured.

grunt.registerTask('heroku:production', ['bower:install','concat', 'bower_concat']);

The deployment to Heroku uses the task. I want the concatenation to run when I push to production, so I don’t have to check in any build artefacts to source control.

Heroku doesn’t run Grunt natively during a deploy, but they do support build packs. I’ve configured Heroku to use a nodejs buildpack with grunt support.

See https://github.com/mbuchetics/heroku-buildpack-nodejs-grunt for more.

This is the build framework I’m starting with, and most certainly will grow with the project. A way of sharing ignored files between tasks would be nice. There is some duplication already.

What’s your workflow ?

By Hibri Marzook

Discuss this post with me on @hibri