Zach Schnackel

Grunt all the things

Apr 21st, 2014

As I mentioned in my previous article about Basey; a WordPress boilerplate theme I created, I use several modern dev tools to tackle handling some of the magic and more monotonous tasks that go into building a website. One such tool, is called GruntJS.

Grunt is a JavaScript task-runner, which can do everything from finding/replacing strings and placing copyright banners on compiled files, to running deployment tasks and r-syncing into a Vagrant instance; pretty powerful stuff. What I thought I'd do is run through some of the Grunt plugins I use on a daily basis and why you should think about doing the same.

grunt-contrib-copy - Copies folders/files. Seems pretty simple, but when you're updating Bower components or creating separate /src and /dist directories for your project, it's a little tidbit that just makes sense. And since you can use wildcard attributes for src items, it makes selecting exactly what you want that much easier.

copy: {
  main: {
    files: [
      {
        expand: true,
        flatten: true,
        src: ['bower_components/modernizr/modernizr.js'],
        dest: 'assets/js/vendor/',
        filter: 'isFile'
      }
    ]
  }
}

grunt-contrib-concat - You guessed it, combines files. Most of us might be used to creating a large plugins.js file and pasting numerous libraries into there, but why not keep those src files separate and combine them on during a build? It makes updating, locating and re-positioning files that much easier.

concat: {
    ie: {
        options: {
            separator: "\n\n"
        },
        src: [
            "bower_components/selectivizr/selectivizr.js",
            "bower_components/respond/dest/respond.min.js"
        ],
        dest: "assets/js/build/ie.js"
    }
}

grunt-contrib-jshint - Validates your Javascript with the ever-so-popular JSHint. Instead of mulling over your JS to determine where you missed a semi-colon, why not have JSHint do that for you?

Hint, use jshint-stylish for better looking error reports

jshint: {
  src: {
    options: {
      jshintrc: ".jshintrc",
        ignores: [
          "assets/js/build/*.js",
          "assets/js/vendor/*.js"
        ],
      reporter: require('jshint-stylish')
    },
    src: ["assets/js/src/*.js"]
  }
}

grunt-contrib-uglify - Remember using an online tool like Closure Compiler to minify all of your JS? Yeah... let's stop that and let Grunt do that for us; with help from UglifyJS.

uglify: {
  min: {
    files: {
      "assets/js/build/ie.min.js": ["assets/js/build/ie.js"]
    }
  }
}

grunt-autoprefixer - Know what's the greatest part about writing CSS? Figuring out which vendor prefixes you need for every single property out there! Or, you could let this plugin do it for you. Autoprefixer solves this for us by polling Can I Use for accurate browser support prefix conditionals. The best part about though, is that you can very easily configure which browsers you want to support. Done. Magic. You're welcome.

autoprefixer: {
  options: {
    browsers: ['last 2 version', 'ie 9']
  },
  dist: {
    src: 'assets/css/build/app.css',
    dest: 'assets/css/build/app.css'
  }
}

grunt-contrib-sass - What's the fun of writing Sass if you don't have a way to compile it quickly? This plugin sits on-top of Ruby (and the Sass gem) on your system to handle the dirty work. Yes, I'm fully aware that libsass is out there, but I'm not going to give up @extend... so there.

sass: {
  dist: {
    options: {
      style: "compressed",
      loadPath: "bower_components/foundation/scss"
    },
    files: {
      'assets/css/build/app.css': 'assets/css/src/app.scss'
    }
  }
}

grunt-pixrem - I'll go ahead and say it, I think the whole px vs em vs rem argument is moot. Certainly there's use-cases for usability on specific setups, but with all of the different facets that make up an interactive user experience, it's something that has caused more headaches than problem solving; px is straight-forward and always works, everywhere. That being said, I've developed with both and had a recent project that needed to support IE8 (sucks, amiright?) that was using REMs throughout (with Foundation 5). This plugin fit the bill nicely. Config takes a root px value and can either replace all of your current stylesheet, or create an entirely new one that is px based. For many projects, I simply convert all rem references to px.

pixrem: {
  options: {
    rootvalue: '16px',
    replace: true
  },
  dist: {
    src: 'assets/css/build/app.css',
    dest: 'assets/css/build/app.css'
  }
}

grunt-csso - CSSO (CSS Optimizer) that in addition to the usual minification techniques, it can perform structural optimization of CSS files, resulting in smaller file sizes. If this library could talk, it would say, "Your bloated, non-optimized code is about to get the whack slapped out of it".

csso: {
  compress: {
    files: {
      'assets/css/build/app.css': ['assets/css/build/app.css']
    }
  }
}

grunt-contrib-watch - Runs predefined tasks whenever watched file patterns are added, changed or deleted. Why run every single task whenever "any" file is changed? Run smaller, subsets of tasks and speed up your build-process. The great thing is that this integrates with LiveReload, which automatically injects style updates (or reloads the browser) when tasks are complete.

watch: {
  options: {
    livereload: true
  },
  grunt: {
    options: {
      reload: true,
    },
    files: ['Gruntfile.js'],
  },
  markup: {
    files: ["*.php"],
  },
  scss: {
    options: {
      livereload: false,
      spawn: false
    },
    files: ["assets/css/src/*.scss"],
    tasks: ["sass", "autoprefixer", "pixrem", "csso"]
  },
  css : {
    files: ["assets/css/build/*.css"],
    tasks: []
  },
  js: {
    options: {
      spawn: false
    },
    files: ["assets/js/src/*.js"],
    tasks: ["jshint", "concat", "uglify"]
  }
}

If this is a lot to take in, I highly suggest you check out Chris Coyier's article, Grunt for People Who Think Things Like Grunt are Weird and Hard. It'll get you up and runnning with Grunt in no time. The snippets I pasted above aren't just to pad the character count; use 'em. They're real-world examples to get past some of the initial frustrations about setting everything up.

Grunt has a large ecosystem of plugins available, so make sure you go to gruntjs.com/plugins to check 'em out!

Original Article