I’ve been working on a new open-source project lately. To be honest, I’ve been so busy coding that I haven’t had time to write about it, but since everyone at Bocoup got together the other day and told me I had to write a blog post—apparently they’re pretty excited—I’d like to introduce grunt.
What is grunt?
Grunt is a task-based command line build tool for JavaScript projects.
Why did I create grunt?
It’s far too easy to create a gist or one-file repo and promise yourself that you’ll add linting and unit tests later. With grunt, all the excuses you’ve ever made just disappear. Instead of being a chore, creating a new project and performing repetitive but necessary tasks such as linting, unit testing, concatenating and minifying files become trivially easy.
After a lot of experimentation and failed attempts, I learned that writing and maintaining a suite of “javascript build process” tasks in a gigantic, monolithic Makefile / Jakefile / Cakefile / Rakefile / ?akefile that was shared across all my projects was overwhelming, especially considering how many projects I have. That approach just wasn’t going to scale to meet my needs.
In the end, I realized that a task-based build tool with built-in, commonly used tasks was the approach that would work best for me. Unfortunately, I couldn’t find a build tool that worked the way that I wanted. So I built one.
How did I create grunt?
Being primarily a JavaScript developer, I decided to use Node.js and npm because the dependencies I most care about (JSHint and UglifyJS) were already npm modules. That being said, while Node.js was designed to support highly-concurrent asynchronous-IO-driven web servers, it was clearly NOT designed to make command-line build tools. But none of that matters, because grunt works on OS X, Linux and Windows.
Just install it with npm install -g grunt
and see for yourself.
Built-in tasks
As of now, grunt has the following predefined tasks that you can use in your project:
- concat – Concatenate files.
- init – Generate project scaffolding from a predefined template.
- lint – Validate files with JSHint.
- min – Minify files with UglifyJS.
- qunit – Run QUnit unit tests in a headless PhantomJS instance.
- server – Start a static web server.
- test – Run unit tests with nodeunit.
- watch – Run predefined tasks whenever watched files change.
Custom tasks
In addition to the built-in tasks, you can create your own tasks. Don’t like a built-in task’s default behavior? Override it. Check out the grunt API documentation and the built-in tasks source for everything you need to know about creating custom tasks.
Also, creating a grunt plugin allows you to package related tasks into an easily-installed npm module. Backbone Boilerplate is a good example of a grunt plugin. If you’ve developed a grunt plugin, let me know by telling me in the issues tracker.
Initializing a new project
This might be my favorite grunt feature. The init task initializes a new project, based on the current environment and the answers to a few questions. Once complete, a grunt.js configuration file will be generated along with a complete directory structure, including a basic readme, license, package.json, sample source files and unit tests (etc). The exact files and contents created depend on the template chosen along with the answers to the questions asked.
Now, instead of sketching out an idea in a quick one-off gist, I can create the scaffolding for an entire project in about 10 seconds, “fill in the blanks,” and then run grunt to lint, test, concat and minify my code. If everything passes, I can then commit and push my code. And anyone who wants to contribute to the project can install grunt and do the same.
There are currently a handful of built-in init templates, including a “jquery” template that has been designed to create a basic jQuery plugin compatible with the upcoming jQuery plugins site, and a generic “gruntfile” template that can be used to very quickly add a grunt.js gruntfile to an existing project. You can override init template files if you want to customize a built-in init template or create your own. And your templates don’t need to be grunt-related. You could create an init template that builds out models or views or package.json files—whatever you need.
If you want to try the init task, cd to an empty directory, run the command grunt init:jquery
(assuming grunt is already installed) and follow the on-screen prompts. When grunt is done creating files, poke around. Run grunt
. See what happens.
Actually using grunt
Each time grunt is run, it looks in the current directory for a file named grunt.js, aka the “gruntfile.” If this file is not found, grunt continues looking in parent directories until it is found. This file is typically placed in the root of your project repository, and is a valid JavaScript file comprising project configuration as well as any custom project-specific tasks and helpers you’ve defined.
If you like “action shots,” here’s an example of grunt, grunting grunt:
BTW, if you like my bash prompt, it’s available in my dotfiles repo.
Grunt in action
We’ve already been using grunt at Bocoup. Rick Waldron used grunt to build the Ringmark mobile test suite. He, Scott González and Jörn Zaefferer have been working on replacing all the jQuery project (jQuery, jQuery UI and QUnit) build tasks with grunt. Tim Branyen has created the Backbone Boilerplate grunt plugin. Mickael Daniel is working hard to create a grunt plugin for HTML5 Boilerplate. And I’ve been using grunt to initialize and build every new project I work on.
Grunt is currently in beta. While we’re already using it on multiple projects, it might have a minor issue or two. The API has gone through multiple iterations, and is considered stable, but there is a possibility that it will change in the future, based on your feedback.
I encourage you to try grunt out in your projects. If you’re interested in creating grunt tasks or a grunt plugin, or just want to discuss grunt, join the irc.freenode.net #grunt IRC channel. And by all means make suggestions or report bugs!
The future
I have a lot of excellent features planned for grunt. I want to further evolve the API to help make grunt as flexible and powerful as possible. I want to find a way to make the creation and discovery of grunt tasks and plugins easier. And I want to create more built-in tasks and init templates.
This blog post is only the tip of the iceberg. There’s a lot of depth to grunt, so I encourage you to read the documentation and the getting started guide, and look at the built-in tasks.
Good luck, and happy grunting!