In our industry, the name of the game is speed. Speed comes into play in nearly every aspect of our work; from meetings, to process, to development, to performance in the end product. Many of our tools brag of the ability to speed this or that, to make your life better by doing X faster. So much effort goes into the development of these tools, and making use of them can be a great boon to your overall productivity.
The latest tool I’ve fully embraced is Grunt. Grunt is labeled as The JavaScript Task Runner, and boy is it. It’s a piece of software powered by node.js that lets you build certain routines that run on the command line. Grunt is an amazing framework of sorts that you can essentially turn into anything you want it to be. If you’re at all like me though, a generalized definition of software purpose like that doesn’t really hit home, so I want to get a bit more clear about what Grunt does.
What Grunt is
Reiterating Grunt’s self-definition as The JavaScript Task Runner, the first step is clarifying what Grunt considers a Task Runner to be:
In one word: automation. The less work you have to do when performing repetitive tasks like minification, compilation, unit testing, linting, etc, the easier your job becomes. After you’ve configured it, a task runner can do most of that mundane work for you—and your team—with basically zero effort.
That clears it up a bit, but I understand why they need to still be a bit more open ended in their explanation. Grunt is heavily powered by it’s plugins, and take a look at the extensive list of what’s available to us.
Side note: I love CodeKit
If you’re a CodeKit user, you might ask yourself why you’d want to even bother with something like Grunt. After all, CodeKit is a stellar, GUI based application that essentially does everything you’d need insofar as building a WordPress theme efficiently.
CodeKit is a great piece of software, something I happily paid the nominal fee to use for the past few months after having used it completely for free during the beta period. CodeKit does fantastic job at accomplishing it’s goal of making it trivial for OS X Web developers to instantly get up and running with many of the command line tools utilized either by hand or via Grunt.
The (only) drawback to CodeKit for me is the OS X bit. While I’m the sole developer the majority of my projects, it’s a bit unsettling to me that certain aspects of the build process are now dependent on anyone else wanting to make certain edits being a CodeKit user. Again, this isn’t a knock against CodeKit, it doesn’t set itself up to be a ubiquitous tool for developers the world over, but it is where Grunt comes into play.
Getting Grunt up and running
If you’ve never used anything node
you’re likely pretty intimidated by it. It was really hard for me to wrap my head around JavaScript on the command line. Luckily for us, the entire installation process has been automated in a variety of very familiar ways.
The docs for Getting Started with Grunt are super helpful out of the box, but I’ll provide my own iteration here because I’m still super new to Node and some things went over my head the first time around.
-
Get Node installed
Installing Node has been made awesomely easy via a variety of methods. The most straightforward I’ve found is to simply use the installer provided on nodejs.org. If you’re running a Mac it’s a standard installer package you’re familiar with. I can’t say for sure what happens on Windows but I’ve got to think it’s about the same experience. All you Linux guys already have it running I bet, but if not I’d suggest compiling from source as it’s an aggressively developed piece of software and any package manager will likely be out of date.
-
Install Grunt from npm
Node ships with what’s called npm or Node Packaged Modules. These are fantastic and can be compared to the functionality of Linux package managers. Think of npm as a repository of Node-powered software you can install with one terminal command. This method of software distribution has taken off like crazy and it’s really helpful to us as developers because it provides a single place for us to go when wanting to find a piece of software. After installing Node, Grunt is installed via npm with a single command:
npm install -g grunt-cli
Also if you’re like me, you’re familiar with instructions about having to update your
PATH
with something-or-other at this point. Not the case with Grunt, it took care of that for you.
Without going into too much technical detail about why you’ve just installed grunt-cli versus grunt as you may have expected, the act of installing Grunt really means the act of installing a command line interface with which you can interact. Every time you use Grunt in a project, you need to have a corresponding Gruntfile that tells Grunt what to do when you invoke it.
Truth be told: that’s it. Grunt is installed on your system and ready to rock. It’s not doing anything yet of course, but that’s where things start to get interesting.
Using Grunt for WordPress theme development
With Grunt installed and ready to go, it’s a matter of figuring out how to get Grunt to do things for you. When it comes to WordPress theme development (for me) that comes down to accomplishing these few tasks:
- Hinting my JavaScript
- Concatenating my JavaScript
- Uglifying my JavaScript
- Performing all three each time I edit any JavaScript file
- Watching my Sass (Compass in particular)
Since WordPress themes aren’t typically as involved as other MV* JavaScript projects (think Gmail) I don’t mind my build process running top to bottom each time I make an edit. Without going too far too quick, Grunt does provide the ability to set up various environments that run tasks in different ways, but I tend to take the minimally invasive route when it comes to workflow, so this intro will simply outline how to get up and running in a really basic way. Hopefully it will provide enough context for those that want to take it a step further to do so.
Configuring Grunt for your WordPress Theme
I’ve been spending some time reorganizing the folder structure of my WordPress themes, which does come into play when setting up Grunt with your project. Folder structure and file organization is really personal when it comes to a WordPress theme developer so rest assured this can be customized to integrate with your workflow, it doesn’t have to replicate mine top to bottom. Continuing, the folder structure I’ve been working with lately for my custom WordPress themes is as follows:
- client-name
- assets
- fonts – Any fonts used in the theme
- images – Any images used in the theme
- javascripts
- build
- source – Any custom JavaScript used in the theme
- vendor – Any vendor-provided JavaScript used in the theme (e.g. jQuery plugins)
- styles
- build
- source – Any custom Sass used in the theme
- vendor – Any vendor-provided Sass used in the theme (e.g. third party mixins/functions)
- Gruntfile.js
- package.json
- assets
- lib – Custom classes or otherwise that take care of heavier lifting
- templates – All theme template files
- partials – Partial templates (e.g. header, footer)
- functions.php – Essentially repurposed as a bootloader for the content of
lib
- index.php
- style.css
My folder structure does have an effect on the way I use Grunt, as outlined by the location of my Gruntfile.js
. Essentially my goal with the above folder/file structure is to keep things organized into ‘pockets’ of like-minded files. I plan on outlining my WordPress template file structure in more detail in a separate article, but it’s 100% based upon Scribu’s Theme Wrapper class which I quite like.
I spent some time thinking about how to best utilize Grunt given the context of a WordPress theme and noted that I’m only using Grunt to handle the assets of my theme. Grunt is responsible only for the build process surrounding my JavaScript and Sass (Compass to be more specific). As a result, the Grunt specifics are housed completely within the assets
folder.
Configuring the Grunt environment for your project
All things Grunt are configured by a combination of two files within your project directory; package.json
and Gruntfile.js
. Each is required to have a fully functional Grunt implementation, and they come in supremely handy when a project that’s been preconfigured to use Grunt lands on your desk. Taking a snippet from the Grunt docs about package.json
:
The
package.json
file belongs in the root directory of your project, next to theGruntfile
, and should be committed with your project source. Runningnpm install
in the same folder as apackage.json
file will install the correct version of each dependency listed therein.
The second sentence in there is key. This package.json
file not only defines the metadata of your project, but also outlines what Grunt dependencies (Grunt version and Grunt plugins) your project needs. Creating this text file is as easy as copying and pasting the sample from the Grunt docs and customizing it, or opening up a terminal, cding to your project directory (as per my example above it would be ~/dev/client/wp-content/themes/client) and running npm init. I would suggest the former as it’s quicker and more straightforward. The important part to keep in mind is the devDependencies
property object in which all of the Grunt plugins you’re using will be defined. The devDependencies
property includes property names that match the Grunt plugin name, and values that correlate to the minimum version needed for your project. A typical client WordPress theme project’s package.json
looks something like this:
{
"name": "client",
"version": "1.0.0",
"description": "Custom WordPress theme for Client by Iron to Iron",
"main": "Gruntfile.js",
"dependencies": {
"grunt": "~0.4.0",
"grunt-contrib-compass": "~0.1.3",
"grunt-contrib-jshint": "~0.2.0",
"grunt-contrib-uglify": "~0.1.2",
"grunt-contrib-watch": "~0.3.1"
}
}
That package.json
sits within my ~/dev/client/wp-content/themes/client/assets directory waiting to be parsed by Grunt. The next piece of the puzzle is where all the magic happens, Gruntfile.js
.
Giving Grunt tasks to complete
Setting up Grunt revolves around the idea of giving it tasks to complete when it runs. A task is basically a call to one of the plugins that in turn provides a specific function. As mentioned above, I’m interested primarily in hinting my JavaScript, concatenating said JavaScript, and then uglifying it. Additionally I like using Compass on my projects so I’d like Grunt to take care of that for me too. It’s not surprising that Grunt plugins are available for everything in my list. If you prefer vanilla Sass, there’s a Grunt plugin for that too. Again, as per the Grunt docs, the Gruntfile
is:
The
Gruntfile.js
orGruntfile.coffee
file is a valid JavaScript or CoffeeScript file that belongs in the root directory of your project, next to thepackage.json
file, and should be committed with your project source.
Keep in mind that both of these files (package.json
and Gruntfile.js
) should be committed with your project source, as they’re now essential to your build process.
Your Gruntfile.js
is, as you likely have guessed, another JavaScript text file in the same directory as your package.json
(in my case the assets directory of my WordPress theme). The Grunt docs have an awesome starter template you can use for your Gruntfile.js
as well, and outlines all the pieces that make up a Gruntfile
. Mine focuses on a default implementation outlining a desired set of tasks that:
- Watching all
.scss
and.js
files in mystyles/source
andjavascripts/source
directories, respectively - When a new file is added or an existing file modified do one of the following
- If the file is Sass: recompile my Compass project
- If the file was JavaScript: hint, concatenate, uglify
Grunt is capable of this right out of the box, it’s just a matter of setting up your Tasks properly. Each Grunt task is represented as an object property that has it’s own properties which correlate loosely to function calls and parameters. A Gruntfile
can get very elaborate very quickly, but the focus of this implementation is the quick objective outlined above. If after reading this article you’d like to find out more, by all means check out the Grunt docs.
It’s important to note that out of the box the grunt command runs through it’s Tasks once and then exits. There’s a Grunt plugin called grunt-contrib-watch
though, that allows you an event listener of sorts that triggers any number of your Tasks when files are edited, added, or removed. This plugin is essential to my workflow as I’m able to invoke grunt when I sit down to work and essentially forget about it until I’m done, it will watch for my file changes and re-run all of my Tasks every time, automatically. It’s the same idea behind compass watch my_project_dir if you’re familiar with that.
Getting straight to the point, my Gruntfile.js
for WordPress themes as per the above folder structure is:
'use strict';
module.exports = function(grunt) {
grunt.initConfig({
// let us know if our JS is sound
jshint: {
options: {
"bitwise": true,
"browser": true,
"curly": true,
"eqeqeq": true,
"eqnull": true,
"es5": true,
"esnext": true,
"immed": true,
"jquery": true,
"latedef": true,
"newcap": true,
"noarg": true,
"node": true,
"strict": false,
"trailing": true,
"undef": true,
"globals": {
"jQuery": true,
"alert": true
}
},
all: [
'Gruntfile.js',
'javascripts/source/*.js'
]
},
// concatenation and minification all in one
uglify: {
dist: {
files: {
'javascripts/build/vendor.min.js': [
'javascripts/vendor/plugin1/jquery.plugin.js',
'javascripts/vendor/plugin2/js/plugin/plugin.js'
],
'javascripts/build/script.min.js': [
'javascripts/source/script.js'
]
}
}
},
// style (Sass) compilation via Compass
compass: {
dist: {
options: {
sassDir: 'styles/source',
cssDir: 'styles/build',
imagesDir: 'images',
images: 'images',
javascriptsDir: 'javascripts/build',
fontsDir: 'fonts',
environment: 'production',
outputStyle: 'expanded',
relativeAssets: true,
noLineComments: true,
force: true
}
}
},
// watch our project for changes
watch: {
compass: {
files: [
'styles/source/*',
'styles/source/**/*',
'styles/vendor/*',
'styles/vendor/**/*'
],
tasks: ['compass']
},
js: {
files: [
'<%= jshint.all %>'
],
tasks: ['jshint', 'uglify']
}
}
});
// load tasks
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-compass');
grunt.loadNpmTasks('grunt-contrib-watch');
// register task
grunt.registerTask('default', [
'jshint',
'compass',
'uglify',
'watch'
]);
};
Note: File paths are relative to the Gruntfile.js
itself.
I’ll walk through some of the configuration options, but a lot can be further researched in the Grunt docs. Everything is wrapped in what Grunt calls it’s ‘wrapper’ function. To use Grunt you don’t really need to know why it does it, but it does and you need to do it too. Things start to get customized with the grunt.initConfig
line. This is where all of my Tasks are being named and configured. Keep in mind that all of these configuration options are on the Grunt Plugin page for each particular plugin.
JSHint
The first block outlines my jshint
configuration options. You can check out the full range of options on the plugin detail page but essentially I’ve got it configured to let me easily work with jQuery, alerts(), and some best practices when it comes to writing JavaScript. When this Task runs, Grunt will let me know if my JavaScript has any errors in it before proceeding to the rest of the build process. The other property being defined outlines what files this Task should keep an eye on. Note that it’s only watching my source
directory.
Uglify
Once my JavaScript has passed hinting, it gets sent to the uglify
task. This one is great because it kills two birds with one stone by both concatenating and minifying all in one step, which is great. The dist
property is named as such because this task is intended for distribution. You can create multiple properties like this so as to define a different set of rules for your development environment versus your production environment, but that’s outside the scope of this intro. The first property within dist
outlines an array of files
I’d like to concatenate and uglify. I add each jQuery plugin I’m using here, for instance, so I’m able to package it into a single compressed file straight from the vendor. I also keep the script I’m writing by hand in a separate file. Each property name in files
outlines the output file you want to use, and it’s value includes either a string (if only one file) or an array of files that will be included in the process to generate that destination file.
Compass
Up next is my Compass task. The grunt-contrib-compass
plugin lets you include your config right within the Gruntfile
so as to remove the necessity of one more file in your setup, I like that. Again, dist
is used here in the same capacity as the uglify task. You’ll notice that the options
closely match those of your standard config.rb
with other Compass projects, but the property names are slightly different in certain cases so you’ll want to review the grunt-contrib-compass
docs.
Watch
Next is our watch task, which tells Grunt when to automatically rebuild my project. You can break out functionality into a number of what are essentially sub-tasks that each watch for different file patterns and trigger different tasks. My first is compass
that watches for any changed files within my source and vendor styles subdirectories. Note that with each files
property there is a paired tasks
property, that tells Grunt which tasks (defined earlier in the Gruntfile
) to fire when the file pattern triggers a match. You can take a look at my js
watch
task to see some other awesomeness that Grunt provides; my files
pattern doesn’t look like my compass
file
pattern because Grunt allows you to use declared files or file patterns from other tasks in places like this. That way, I only have to keep one canonical list of files in a Task that can be used in other tasks down the line.
Now we can use Grunt
The last two blocks of the Gruntfile
take care of some business Grunt needs taken care of in loading the Tasks that shipped with our plugins and registering our default task which you can consider “what gets fired when I execute grunt on the command line”. Grunt is now set up to take care of all the dirty work when it comes to running the build for my WordPress theme assets. Invoking everything that was just set up is a matter of cracking open a terminal session, cding to my project directory and executing the following command:
grunt
Grunt will basically ‘execute’ my Gruntfile.js
and watch for all of the changes I want based on the watch
Task that was set up. It’s quite a beautiful thing.
The ‘standardization’ part
A very attractive aspect of Grunt for me in comparison to something platform-specific like CodeKit is that a properly set up Grunt configuration is quite portable. When I hand off this custom theme to a client, they can take it in house and get up and running with that Grunt configuration in a matter of minutes. When faced with a new Grunt project, you simply cd to the project directory and execute the following in a terminal session:
npm install
Which will parse the package.json
file that was set up and grab each of the devDependencies
needed for the project. Once that command finishes, the client developer will be able to fire grunt and have the exact same build process I was working with. It’s fantastic.
Some best practices and closing thoughts
Integrating Grunt within your workflow for WordPress theme development is a smart move in my opinion. There are some things to keep in mind though. Primarily how you get things set up with version control. As per the documentation, it’s important to include both package.json
and Gruntfile.js
in your repository so as to make it available to teammates or the client down the line. Further, when Grunt utilizes Plugins, they’re downloaded and included in a node_modules
directory in your project root. This folder should be omitted from version control for a number of reasons. Primarily because they’re put in place by a package manager and will be implemented for other developers when they run npm install, and in doing so fetch the proper version in real time.
One other tip I’ve made for myself is one surrounding easy invokation of grunt for any particular project I may be working on. While it’s no big deal to open up Terminal.app, cd to my project directory and fire grunt, there’s a pattern to my local development environment that I can exploit to save a couple keystrokes. All of my client work takes the format ~/dev/%client%/wp-content/themes/%client$ where %client%
is the name of the client project (the same in both places). Realizing that, I set up a quick Alfred Extension to quickly open iTerm2 and get grunt up and running for any particular project. The Extension is quite trivial, but it feels really quick to get up and running for the day using 4-5 keypresses. I’d make the Extension available as a download, but the chances of your local development environment directory structure matching mine are too slim for that, so here’s what it looks like:
I’m so glad I finally took the time to look into Grunt. It’s a staggeringly useful piece of software that I think has already rooted itself in the community, but will continue to refine itself as indispensable amongst the majority of us.
Comments
I’m using Win7. I installed Node via the windows installer: http://nodejs.org/download/ for 64 bit. After installing node, I opened the command prompt (start -> cmd) and typed the command “npm install -g grunt-cli” and got the message: npm is not recognized as an internal or external command operable program or batch file . Turns out I had to change my environment variables path (system properties -> advanced -> environment variables -> edit path ) from C:\Users\username\AppData\Roaming\npm to C:\Users\username\AppData\Roaming\npm\ . Basically the ending slash was missing ( \ ). After I made that change I was able to successfully install grunt. Thought that might be helpful to some of the windows users.
Hi Jonathan,
as always, thank you for a very informative article. Quick question though: How would you use Grunt with parent / child themes?
Live well, Petr!
Hi Jonathan,
I am new to Grunt and Compass. This article really helped me to understand the concepts.
Just want to say you a big Thanks!
Great post. I finally went through the effort of getting this all setup yesterday. I’d tried and failed to get Gulp going about 5 times and I was about ready to write task runners off completely. But I decided yesterday to give Grunt one last try before calling it a day.
It took me less than a few hours of reading and playing around, but I got everything going with Grunt. I setup all you mentioned above, and added in BrowserSync. It’s really, really worth having a look at.
Excellent post.
For anyone with a same question, here is an advice from David Herrera: https://gist.github.com/dlh01/5726683
In a nutshell: use `importPath` directive for “default styles” applying for both parent and child theme!
Hi Jonathan,
Can you tell me how your style.css is compiled after you’ve run the Grunt tasks. As I understand it the CSS files will be put into the styles/build folder, so are you just using @import to import these into the main style.css file or is there something else going on I’m not seeing?
Thanks.
When you use a preprocessor like Sass, all of your broken up files are concatenated into a single CSS file in the build folder.