🚀 Harness AI to execute your million-dollar ideas 10x faster, with 1/10th the effort.Get Started

Using Grunt For A Better Workflow

11/7/2013

Grunt is a NodeJS (JavaScript) task runner that allows you to automate all the things! I use it to speed up my development workflow and enhancing the performance of my projects.

2 Years: A Sour Memory

It's Thursday. I've just started a front-end project that needs to be done...tomorrow. I've started a new WordPress site for a client that we needed to churn out quickly, so I open up Coda, start to manually scaffold out a theme, manually create my php and html files, manually start to add CSS, then I flip over to my browser, hit the refresh button and watch the page reload.

Fast forward to the next day, and I'm cleaning up code, trying to make files smaller (and doing an awful job) and I finally wrap it up, and manually drag files into ftp in Coda, then manually sanity the site and call it a day.

And then I found Grunt.

As I'm writing this post, I've got Chrome open in the background and SublimeText 3 open taking half of the screen. I save my file and watch chrome magically update to my most recent markdown file's changes.

This is just one advantage of Grunt.

Grunt has become one of those things in life where you didn't realize just how badly you needed it until you start using it and thinking, "what have I been doing with my life?"

By automating all the things I used to have to manually do, I make more time to do more things, and ultimately make more time to focus on the important things while Grunt takes care of the trivially mundane.

Advantages to Using Grunt

  • It has a huge library of plugins to automate almost anything you can think of, the community on a whole is using Grunt for all of their projects (jQuery, twitter, Modernizr) which leads to contributing, which leads to more plugins, which, as you guessed, leads to greater adoption.
  • Easy to get up and running and lots of documentation.
  • Options are passed in as Javascript objects, which is nice for front-end developers
  • Robust enough that you can use it for any kind of tasks, but minimal enough that it's not bloated unless you need it to be.

Getting Started

I've added all of these code samples to a github gist, you can clone done the repo, and run the commands as you read rather than having to write out the examples yourself if it's more convenient.

Project gist: Grunt: Up & Running

Grunt has a few dependencies that you'll need to have installed:

From anywhere within your terminal run:

npm install -g grunt-cli

This is going to install the module for the command line tool globally (-g) so you can run grunt anywhere on your machine and it will know what you're referencing.

package.json

Next, in your project's root, you'll need to create a package.json (run npm init in your project) and a Gruntfile.js. The package.json file is a NodeJS file that tells Node about all the attributes of your project and the different Node modules your project will depend on.

Here's my sample package.json:

{
  "name": "chaseadamsio",
  "version": "1.0.24",
  "description": "Yeoman / Grunt workflow for realchaseadams",
  "dependencies": {},
  "devDependencies": {
    "grunt": "~0.4.1",
    "grunt-contrib-jshint": "~0.5.2",
    "grunt-contrib-uglify": "~0.2.0",
    "grunt-contrib-watch": "~0.5.3"
 },
  "engines": {
    "node": ">=0.8.0"
  }
}

After you've created your package.json, you'll need to run npm install, which is going to install grunt, jshint and uglify (any dependencies you specify in your package.json).

Gruntfile.js

The final piece of the Grunt puzzle is to create your Gruntfile.js.

In it we're going to have it run two tasks:

  • lint or javascript
  • uglify it if there aren't any errors

We'll create a default task and add the options to the initConfig:

'use strict';
var packagejson = require('./package.json');

module.exports = function (grunt) {

  // Configuration
  grunt.initConfig({
    pkg: packagejson,
    watch: {
      scripts: {
        files: ['<%= pkg.name %>.js'],
        tasks: ['default']
      }
    },
    jshint: {
      build: [
        '<%= pkg.name %>.js'
      ]
    },
    uglify: {
      options: {
        banner: '/*! <%= pkg.name %> - v<%= pkg.version %> - ' +
        '<%= grunt.template.today("yyyy-mm-dd") %> */'
      },
      build: {
        src: '<%= pkg.name %>.js',
        dest: 'build/<%= pkg.name %>.min.js'
      }
    }
  });

  grunt.registerTask('default', [
    'jshint',
    'uglify'
  ]);

  grunt.loadNpmTasks('grunt-contrib-jshint');
  grunt.loadNpmTasks('grunt-contrib-uglify');
  grunt.loadNpmTasks('grunt-contrib-watch');
};

In our Gruntfile, we've very basically told it for jshint, we want to lint the javascript file script.js, the path being relative to the Gruntfile's location.

After that we want to use uglify to compress and minify our javascript file for performance, and add a banner with the project's name, the version and the date (useful for debugging).

At the very least for running grunt to work, you have to:

  • registerTask of default and add the tasks you want to run
  • loadNpmTasks of all the tasks that are in your configuration object.

The only thing we need to do for this to work is to add a script.js to your project give it some javascript to execute. Here's a sample js file just for convenience:

(function() {
  var foo = "hello ",
      bar = "world!";

  console.log(foo + bar);
})();

Now that we have everything setup, , and you can switch back to your terminal and run grunt from anywhere in your project (I always run it from the project root just to be safe), and grunt will run jshint and uglify on your script with the package name!

Having to run grunt every time you make a change can be a real hassle, so for convenience to almost every project I use, I add the grunt-contrib-watch task, so let's go ahead and add it as a task:

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

When you do this as --save-dev it will add it to your developer dependencies in your package.json file, which will help you out in the long run, especially if you want other people to use your project. If you don't pass in the flag to save as a dependency, when someone else pulls down your project and tries to work with it, they'll have to manually install node modules and most likely give up on your project.

Updated package.json after saving dev dependency for watch:

{
  "name": "chaseadamsio",
  "version": "1.0.24",
  "description": "Yeoman / Grunt workflow for realchaseadams",
  "dependencies": {},
  "devDependencies": {
    "grunt": "~0.4.1",
    "grunt-contrib-jshint": "~0.5.2",
    "grunt-contrib-uglify": "~0.2.0",
    "grunt-contrib-watch": "~0.5.3"
 },
  "engines": {
    "node": ">=0.8.0"
  }
}

Updating Grunt

Now that we have the ability to watch files, we can add it to our Gruntfile.js:

'use strict';
var packagejson = require('./package.json');

module.exports = function (grunt) {

  // Configuration
  grunt.initConfig({
    pkg: packagejson,
    watch: {
      scripts: {
        files: ['<%= pkg.name %>.js'],
        tasks: ['default']
      }
    },
    jshint: {
      build: [
        '<%= pkg.name %>.js'
      ]
    },
    uglify: {
      options: {
        banner: '/*! <%= pkg.name %> - v<%= pkg.version %> - ' +
        '<%= grunt.template.today("yyyy-mm-dd") %> */'
      },
      build: {
        src: '<%= pkg.name %>.js',
        dest: 'build/<%= pkg.name %>.min.js'
      }
    }
  });

  grunt.registerTask('default', [
    'jshint',
    'uglify'
  ]);

  grunt.loadNpmTasks('grunt-contrib-jshint');
  grunt.loadNpmTasks('grunt-contrib-uglify');
  grunt.loadNpmTasks('grunt-contrib-watch');
};

With an updated file, we can simply run grunt watch from our terminal, update our javascript file, and watch as it magically runs our tasks everytime we save!

If you're using Grunt, let me know what you like/don't like about it, or some of the really useful plugins you've found! I'll be adding posts every month about the plugins that I find useful for my workflow.

Chase Adams