David N Brooks Designer, Developer, Musician, Artist, and Writer

Hello! I’m David, an interactive designer, musician, artist, and writer living in Georgia.

Better Team Projects with Grunt JS

10 July 2014

For years I pre-processed my SASS/LESS files and minified my JavaScript with apps like Codekit. And while those are great, they don’t always work nicely with whatever apps your coworkers are using. That’s where Grunt JS shines.

What I Was Doing

Because I had mostly been working by myself on things, I was running CodeKit or even SASS/Compass from the command line. It works, obviously, but when your teammates want to run something else, like Prepros, for example, not all apps build things the same way.

Sometimes it was a minor difference. The first thing we noticed, for example, was that one of our apps was adding in line-numbers while the other one was not. It’s fine, until you work in a shared Git repo and suddenly those line numbers are all changes that need to be merged.

Or in some cases, because I was using CodeKit to also minify my JavaScript and lint it for errors, we would run into differences in which files were being included at what points. So while it worked fine for me, my coworkers were throwing errors because one library was being included before jQuery, or it just wasn’t being pulled in at all.

It was a mess, an actual mess.

Enter GruntJS

I had heard things about Grunt, but I wasn’t buying into the hype. Until I got really frustrated one night and decided that it was either give it a shot or hate working with pre-processors and minify everything by hand.

Within an hour, I was up and running on Grunt. I now use it for every last project because, well, I have too much to do to fight inconsistencies with teammates. (Totally not their fault.)

Configuring Grunt

I won’t get into the install steps, because the Grunt site does that rather well: Getting Started.

But what might be more useful is to show how my Grunt file is helping me.

module.exports = function(grunt) {
	'use strict';
	grunt.initConfig({
		pkg: grunt.file.readJSON('package.json'),
		concat: {
			options: {
				separator: ';'
			},
			dist: {
				src: ['/_js/jquery.js', '/_js/handlebars.js', '/_js/raphael.js', '/v10/_js/g.raphael.js', '/_js/g.line.js', '/_js/interaction.js'],
				dest: /_js/interaction.min.js'
			}
		},
		jslint: { // configure the task
			// lint your project's client code
			client: {
				src: [
				'/_js/interaction.js'
				],
				directives: {
					browser: true,
					predef: [
					'jQuery'
					]
				},
				options: {
					junit: 'out/client-junit.xml'
				}
			}
		},
		sass: {
			dist: {
				files: {
					'/_css/screen.css': '/_css/screen.scss'
				}
			}
		},
		uglify: {
			options: {
				banner: '/*! <%= pkg.name %> <%= grunt.template.today("dd-mm-yyyy") %> */\n'
			},
			dist: {
				files: {
					'/_js/interaction.min.js': ['/_js/interaction.min.js']
				}
			}
		},
		watch: {
			files: ['/_js/interaction.js', '/_css/*.scss'],
			tasks: ['concat', 'uglify', 'sass']
		}
	});
	grunt.loadNpmTasks('grunt-contrib-concat');
	grunt.loadNpmTasks('grunt-contrib-uglify');
	grunt.loadNpmTasks('grunt-contrib-watch');
	grunt.loadNpmTasks('grunt-contrib-sass');
	grunt.loadNpmTasks('grunt-jslint');
	grunt.registerTask('default', ['concat', 'uglify', 'sass']);
	grunt.registerTask('tests', ['jslint']);
};

Grunt and Concatenation

To break that down, there’s a bit of configuration before we get to this important bit here:

concat: {
	options: {
		separator: ';'
	},
	dist: {
		src: [‘/_js/jquery.js', '/_js/handlebars.js', '/_js/raphael.js', ‘/_js/g.raphael.js', '/_js/g.line.js', '/_js/interaction.js'],
		dest: '/_js/interaction.min.js'
		}
	},

As you might have guessed, that’s the concatenation function. It grabs everything listed in the “src” array and uses it to create interaction.min.js. If you were to run just that, it would be as if you manually copied and pasted all those files into one, large JavaScript file.

Next up, we have the JSLint plugin which opens up just my custom JavaScript file, and looks for errors or syntax issues. A point of consideration here is that I’m working only with the “client” code block. If you’re a NodeJS developer, (you probably already use Grunt), but you can also separate your server code to be linted.

Grunt and SASS

The SASS section caught me a bit off guard at first, because it reads backwards to me, but it looks like this:

sass: {
	dist: {
		files: {
			'/_css/screen.css': '/_css/screen.scss'
		}
	}
},

All it’s doing is taking that second file (/_css/screen.scss’) and using compiling it to the first (/_css/screen.css). Now for me, I have a bunch of other SCSS and CSS files that I include in screen.scss, but they don’t need to be pre-compiled. So this is the extent of it.

Grunt and JavaScript Minification

The next function is Uglify, which is this block:

uglify: {
	options: {
		banner: '/*! <%= pkg.name %> <%= grunt.template.today("dd-mm-yyyy") %> */\n'
	},
	dist: {
		files: {
			'/_js/interaction.min.js': ['/_js/interaction.min.js']
		}
	}
},

The options section just creates a stamp at the top of your file, in this case I have the package name and the timestamp for today. But the “dist” section is the important part. In the concat function, we’re building interaction.js into interaction.min.js, which until this function runs, isn’t actually minified. If I were to point this function at interaction.js, it would minify my source, and that would be awful to maintain. So all we’re doing is taking that temporary file from above and minifying it instead.

Watching for Changes

And lastly, we have the watch section:

watch: {
	files: ['/_js/interaction.js', '/_css/*.scss'],
	tasks: [‘concat', 'uglify', 'sass']
}

All this does, is keep an eye on anything listed in the “files” array for changes. If any of them change, it runs the other functions in order. (Concat, uglify, SASS). And this is key here. If you were to switch concat and uglify, you’d always see unminified code. And, if you’re only working on the CSS side of your project at any given time, you can move “sass” to the front of the list and have your output faster.

After the functions are all defined, we get to some Grunt commands that load the actual modules to do the work, but the last bit of importance for us is this:

grunt.registerTask('default', ['concat', 'uglify', 'sass']);
	grunt.registerTask('tests', ['jslint']);

The first line instructs what should happen if I were to run Grunt without any other options. It would roll through concat, uglify, and sass, just like in the watch function. The second line, only runs the JSLint function. Personally, I don’t like to have JSLint yelling at me every time I need to compile the scripts, or worse, CSS. Sublime text can already do basic linting as I write.

Running Grunt

Now, once this file is in place, I can open terminal and find the root of my project. Then, it’s just as easy as typing “grunt.” Doing that would load the default function. But, I don’t do that very often since I’d have to do it over and over again. Instead, I usually run:

grunt watch

That invokes the watch function and keeps an eye on the JS and SCSS files I work with.

And while there’s a lot more you can do with Grunt, I personally don’t need anything more than this. I’m sure as I get deeper into Angular JS and Node JS that will change, but for now, I’m content with just having a way to get my whole team onto the same page without conflict over the files we work with most often.

Care to Leave a Comment?

Note: Off-topic or offensive comments will be removed.

About Me

I'm David Brooks, an interactive designer, generative artist, and electronic musician from Michigan. During the day I build things for Artletic, a great UX studio in Colorado. At night I create art and write music as Light The Deep and craft applications like Thousand Wires, JavaScript libraries like ArtisanJS, and build interesting things for the amazing clients of Northward Compass.

Read My Full Bio

You Also Might Like...

Technically Speaking

This site runs on the Textpattern content management system. CSS is precompiled by SASS, of course. Only a handfull of JavaScript libraries are in use, most notably Grunt. The visuals are done (by me) in Processing and Modo. The fonts are delivered with love by Typekit.

Whew!

365 Days of Paleo/Progress

On July 1, 2014 I started into a year of Paleo and better health.
Here's my progress at a glance.

-18.2 lb. Weight

72 oz. Water Per Day

1787 Ave. Calories

See the progress

© Copyright 2005 - 2017 David Brooks. All rights reserved.