Grunt and Bower with WordPress Part 2

In the first part of this series, I walked you through setting up Grunt and Bower on your machine as well as getting a decent terminal setup using iTerm 2.

Now I’m going to show you how Grunt tasks work. The tasks you use Grunt for depend on your needs. There will be some tasks you’ll want to use for every project. Others may be very project specific. I’ll focus on the tasks you’re most likely to need.

Create Gruntfile.js

Here’s a barebones Gruntfile.js file:

'use strict';
module.exports = function (grunt) {

  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),

    // package options will go here

  });

  // register tasks here

  grunt.registerTask('default', [

    // default actions go here

  ]);

};

With this file created, Grunt is up and running. You’ll execute it by going to the project directory in the terminal and typing ‘grunt’. But, in its current state, it won’t actually do anything. This sample configuration doesn’t have any packages loaded. Before we load any packages, we need to know what we want Grunt to do for us.

Tools You Always Need

First think about what you will always need for your project. What frameworks do you use? Bootstrap? Foundation? Font Awesome? jQuery? Zepto? Round these up with Bower (don’t forget about the package directory here). Do you use LESS or SASS? Get those files set up. Then depending on what you need out of these frameworks, you’ll configure Grunt to process your CSS, lint and concatenate your JavaScript, and minify both.

  • CSS Preprocessing: You’ll want the LESS, SASS, or some other package if your flavor is different.

  • JavaScript Linting: Get JSLint.

  • JavaScript File Concatenation: You’ll want this.

  • JavaScript Minification: Uglify.

  • Error Notifications: Try Grunt Notify.

  • Image Optimization: I like Imagemin.

  • Live Updating: You want to use Watch. And here’s a handy little Chrome extension called LiveReload for updating CSS and JavaScript in the browser without refreshing the page.

Go ahead and follow the instructions for setting up each of these that you want. Don’t worry about configuring them yet, this can be confusing at first and I have a sample I’ll show at the end to get you started.

Registering Packages

With the packages you want registered, the ‘Load Tasks’ section of your file should look something like this:

// Load tasks
grunt.loadNpmTasks('grunt-contrib-clean');
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-notify');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-imagemin');
grunt.loadNpmTasks('grunt-contrib-compass');
grunt.loadNpmTasks('grunt-express');

Configuring Options

Next you’ll want to set the options (under the comment ‘package options’ in the sample file) for each task that will be run. Here’s an example based off of what I commonly use:

// package options
express: {
  server: {
    options: {
      port: 3000,
      hostname: 'localhost',
      bases: 'public' // the 'public' folder for your project
    }
  }
},
jshint: {
  options: {
    jshintrc: '.jshintrc' // jshint config file
  },
  all: [
    'Gruntfile.js',
    'assets/js/*.js'
  ]
},
concat: {
  basic: {
    src: [
      'bower_components/jquery/dist/jquery.js',
      'bower_components/foundation/js/foundation/foundation.js',
      'assets/js/main.js'
    ],
    dest: 'tmp/app.js'
  },
  extras: {
    src: [
      'bower_components/modernizr/modernizr.js'
    ],
    dest: 'tmp/modernizr.js'
  }
},
compass: {
  dist: {
    options: {
      config: 'config.rb'
    }
  }
},
imagemin: {
  dynamic: {
    files: [{
      expand: true,
      cwd: 'assets/img/',
      src: ['**/*.{png,jpg,gif}'],
      dest: 'public/build/img/'
    }]
  }
},
uglify: {
  build: {
    files: {
      'public/build/js/modernizr.min.js' : 'tmp/modernizr.js',
      'public/build/js/app.min.js' : 'tmp/app.js'
    }
  }
},
clean: {
  dist: [
    'tmp/**',
    'public/build/img/**'
  ]
}

A few things to note here - I’m using a tmp folder to concatenate the JavaScript to, before it’s minified. I also have my ‘production’ files in an assets folder outside of the public folder, and everything is processed into a build folder inside my public folder. You don’t have to do this, but I prefer to keep my source files and processed files separate.

Also, watch your commas. As you nest options, it’s easy to miss one.

Watch

‘Watch’ is a very powerful Grunt package that continues to run until you tell it to stop. This is super useful for development tasks like rebuilding your files as you make changes and refreshing the browser. Here’s a sample configuration:

watch: {
  compass: {
    files: ['assets/sass/**/*.{scss,sass}'],
    tasks: ['compass']
  },
  css: {
    files: ['public/build/css/*'],
    options: {
      livereload: true
    }
  },
  js: {
    files: [
      'assets/js/*.js'
    ],
    tasks: ['concat', 'uglify'],
    options: {
      livereload: true,
      atBegin: true
    }
  },
  imagemin: {
    files: [
      'assets/img/**'
    ],
    tasks: ['imagemin'],
    options: {
      livereload: true,
      atBegin: true
    }
  }
}

What’s happening here is for as long as Grunt Watch is running, it’s watching all of our production files and re-running the tasks we tell it to whenever one of those files changes. This can be run with the command grunt watch, but there’s a simpler way we can incorporate this by including it in our default tasks.

Register Default Tasks

The last thing we need to do before Grunt will do this work for us is to register the tasks it will run by default. Grunt can be set up with and many different task configurations as you want, so you could have one set that runs with grunt dev or grunt dist, but for now I’m just going to set up the default grunt:

// Register default tasks
grunt.registerTask('default', [
  'express:server',
  'watch'
]);

Any tasks inside ‘default’ will be run whenever you run the command grunt. This configuration first starts an Express server, and then runs Grunt’s ‘watch’ function. With this, all I need to get everything up and running is the simple command grunt. Nice and simple.

Putting it All Together

With all of these modules registered and configured, your Gruntfile.js should look something like this:

'use strict';
module.exports = function (grunt) {

  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
        // package options
    express: {
      server: {
        options: {
          port: 3000,
          hostname: 'localhost',
          bases: 'public'
        }
      }
    },
    jshint: {
      options: {
        jshintrc: '.jshintrc'
      },
      all: [
        'Gruntfile.js',
        'assets/js/*.js'
      ]
    },
    concat: {
      basic: {
        src: [
          'bower_components/jquery/dist/jquery.js',
          'bower_components/foundation/js/foundation/foundation.js',
          'assets/js/main.js'
        ],
        dest: 'tmp/app.js'
      },
      extras: {
        src: [
          'bower_components/modernizr/modernizr.js'
        ],
        dest: 'tmp/modernizr.js'
      }
    },
    compass: {
      dist: {
        options: {
          config: 'config.rb'
        }
      }
    },
    imagemin: {
      dynamic: {
        files: [{
          expand: true,
          cwd: 'assets/img/',
          src: ['**/*.{png,jpg,gif}'],
          dest: 'public/build/img/'
        }]
      }
    },
    uglify: {
      build: {
        files: {
          'public/build/js/modernizr.min.js' : 'tmp/modernizr.js',
          'public/build/js/app.min.js' : 'tmp/app.js'
        }
      }
    },
    clean: {
      dist: [
        'tmp/**',
        'public/build/img/**'
      ]
    },
    watch: {
      compass: {
        files: ['assets/sass/**/*.{scss,sass}'],
        tasks: ['compass']
      },
      css: {
        files: ['public/build/css/*'],
        options: {
          livereload: true
        }
      },
      js: {
        files: [
          'assets/js/*.js'
        ],
        tasks: ['concat', 'uglify'],
        options: {
          livereload: true,
          atBegin: true
        }
      },
      imagemin: {
        files: [
          'assets/img/**'
        ],
        tasks: ['imagemin'],
        options: {
          livereload: true,
          atBegin: true
        }
      }
    }
  });

  // Load tasks
  grunt.loadNpmTasks('grunt-contrib-clean');
  grunt.loadNpmTasks('grunt-contrib-jshint');
  grunt.loadNpmTasks('grunt-contrib-concat');
  grunt.loadNpmTasks('grunt-contrib-uglify');
  grunt.loadNpmTasks('grunt-notify');
  grunt.loadNpmTasks('grunt-contrib-watch');
  grunt.loadNpmTasks('grunt-contrib-imagemin');
  grunt.loadNpmTasks('grunt-contrib-compass');
  grunt.loadNpmTasks('grunt-express');

  // Register default tasks
  grunt.registerTask('default', [
    'express:server',
    'watch'
  ]);

};

Go Play

Hopefully this is enough to get you started using Grunt to handle your common tasks and Bower to handle your frameworks and plugins. Grunt is as complex as you want to make it - there is no end to how much you can make it do for you and I’ve only scratched the surface. Have fun.