Automate all the Javascript things!

Automation can save you loads of time whether you apply it to your every day chores or to your development workflow. In this article I'll focus on the latter, describing some of the common technics focusing on development in JavaScript (both Browser and Node). Let me just stick an obvious, cheesy meme here:

Automate all the things!

There are many opportunities throughout your development workflow from setting up the environment through the build process all the way to deployment, let's see through some examples

Build process

An obvious one but still too often neglected especially when you're coding for the browser. Code that you write should be easy to maintain and read for humans, code you use in production should be minified and in most cases delivered as a single file together with dependencies. Easiest way to get there? Grunt!

What's Grunt? Grunt is a JavaScript-based task runner, it helps you automate repetitive tasks. There are Grunt plugins to do just about everything and since it's all written in JS it should work on all the platforms where you can have Node (however I expect tasks like grunt-rsync or grunt-shell to fail on Windows).

I use RequireJS to make the browser understand and load correctly my code which I want divided into logical chunks/modules. RequireJS is a most popular implementation of the AMD API which I prefer for its explicit syntax, asynchronous loading and shimming capabilities. If AMD doesn't float your boat there are alternatives such as browserify that you might want to consider. Either way for production you want your files compiled and minified and there are a Grunt plugins for it: Grunt RequireJS and grunt-browserify. (and grunt-contrib-uglify for minification)

Let's take a look at a slightly modified Gruntfile taken from my seed app

module.exports = function(grunt) {
	grunt.initConfig({
		pkg: grunt.file.readJSON('package.json'),
		requirejs: {
			compile: {
				options: {
					name: "../../node_modules/almond/almond",
					baseUrl: "src/js",
					mainConfigFile: "src/js/require-config.js",
					out: "build/js/myapp.js",
					optimize: 'uglify'
					include: ['bootstrap'],
					insertRequire: ['bootstrap'],
					stubModules: ['text'],
					preserveLicenseComments: false,
					wrap: true
				}
			}
		}
	});
	grunt.registerTask('build', ['requirejs']);
});

If you have your dependencies installed (see below) and Grunt command line tool installed (sudo npm install -g grunt-cli) you should be able to run grunt build to get your source code built using rules defined in require-config.js. It's not much but it should already save you some time and a nice bonus is it can be easily plugged into all your future projects!

Config above makes more sense when you look through the seed application I've created. It uses single config file for shims and paths for both development (before code is built) and production environments. Both environments use bootstrap.js as an entry point for the application but it's mentioned here explicitly as r.js optimizer (used internally by Grunt RequireJS plugin) will ignore inline require call in require-config.js. To make production code smaller this seed app uses almond as a lightweight AMD replacement in built code and will remove all comments including license comments (preserveLicenseComments:false).

So we've compiled/built the Javascript files, now we need to prepare a modified version of *.html files that will use packaged up javascript file instead of RequireJS? Can Grunt help? Sure thing! Just use one of many grunt plugins for text replacement, such as grunt-text-replace, relevant part for of the config below

replace: {
	index: {
		src: 'src/index.html',
		dest: 'build/index.html',
		replacements: [{
			from: '<script data-main="/js/require-config" src="/bower_components/requirejs/require.js"></script>',
			to: '<script src="/js/webapp.js?cb=<%= Date.now() %>"></script>'
		}]
	}
}

and extended build task:

grunt.registerTask('build', ['requirejs', 'replace:index']);

So what's ?cb=<%= date.now()="" %=""> above? It's a free, built-in cache buster. Every time you run the build task, it will regenerate index.html to include current time in ms as url query parameter forcing fresh copy of the js file to be downloaded. You can set very generous caching headers in your production web server and whenever you deploy a new version, users will be forced to download a fresh copy of your JS file.

In my web app seed project I've also included a task to automatically remove logging i.e. all console.log (and warn, info etc.) calls and because of that a separate uglify task instead of using one built in requireJS (removing calls from a code already minified might produce unexpected results). Furthermore I'm building less into a single, compact, production-ready css file (with cache buster as well).

Dependency management

npm is great for managing Node dependencies, however it's not as good for code that must work in a browser as many packages are missing and/or have been specifically adapted for Node. I've never liked including project dependencies such as jQuery, AngularJS or Twitter's Bootstrap into my code base. It bloats the repo size (and hence cloning time etc.), skews the statistics (yeah I like these!) and generally feels bad. On the other hand not including them makes "your future self", other members of your development team or (in case of open-source software) all the people who try to run your software to manually chase for dependencies. Neither options is good, luckily we now have Bower for the rescue. Instead of manually downloading and adding all these packages to the repo, we create a simple bower.json file and include package names and versions of all the dependancies. Similarly to Node's npm we can run bower install jquery --save to update bower.json file and install the desired package at the same time. What if a package we want doesn't exist? Doesn't matter! You can specify a combination of a username and repo name to obtain that repo from Github, e.g. if you would like to include my modified version of Virtual Joystick lib with AMD support, just type in bower install tnajdek/virtualjoystick.js --save. As a matter of fact you can fed bower with an url to a .js file online and it will fetch that file and make an appropriate entry in bower.json if desired.

Once your package.json and bower.json are included in the repo, everyone else (including "your future self") can just run npm install followed by bower install to get up to speed with all the repository dependencies. It's well worth it, just remember to add node_modules and bower_components to .gitignore!

Development automation

Developing in less? Use Grunt's watch ability to execute less:development task every time you modify one of your .less files:

watch: {
	less: {
		files: ['src/less/*.less', 'src/less/**/*.less'],
		tasks: ['less:development'],
		options: {
			spawn: false,
		}
	}
}

and:

less: {
	development: {
		options: {
			compress: false,
			yuicompress: false,
			dumpLineNumbers: 'all'
		},
		files: {
			"build/css/webapp.css": "src/less/webapp.less"
		}
	}
}

Alternatively you might prefer to run less straight in the browser and make sure that your build process includes relevant replacement i.e. replace source of stylesheet to css and make sure you don't include less.js in production. Prefer more sassy syntax? There is a plugin for it, just wire it to the watch task above, update relevant configs (remember to update your build task as well) and never think about it again.

Speaking of stylesheets, there are few more things Grunt can help you with. First let it take care of vendor prefixes. I use it in both development (chained in the watch task after less compilation) as well as in the build process for the production environment. Also for your build process you might want to do minification or some crazy post processing.

Need to start a development server to serve all that HTML and JS? Yepp, there is a plugin for it. Here is a typical default task that will watch your less files, regenerate css when needed and serve all the files on the local web server

grunt.registerTask('default', ['less:development', 'connect', 'watch:less']);

Testing, Linting & Deployment

So far I've covered automation of the dependency management, development and the build process. However we can go further - run automated test suite, lint the code and deploy it to a staging/production server. Follow me on Twitter or watch the repo for future updates!