If you’re working on a large project, you’ll no doubt have a build script or a bunch of task scripts to help with some of the repetitive parts of the process. You might use Ant or Rake, depending on the language the project is written in.
But what do you use if the project is primarily JavaScript? That’s the problem Ben Alman set out to solve when he created Grunt.
What is Grunt, Anyway?
What exactly is Grunt? Well, the README on Github says
Grunt is a task-based command line build tool for JavaScript projects.
Here’s the idea: when working on a JavaScript project, there are a bunch of things you’ll want to do regularly. Like what, you ask? Well, like concatenating given files, running JSHint on your code, running tests, or minifying your scripts. If you’re pasting your JavaScript into JSHint online, you probably realize that there’s a better way to do it; even if you’re using cat
to concatenate files or a command line minifier, it would be nice to have a single, unified set of commands for all those extra tasks, that worked for every single JavaScript project, right?
That’s what Grunt aims to be. It has a bunch of built-in tasks that will get you pretty far, with the ability to build your own plugins and scripts that extend the basic functionality.
For more Grunt intro goodness, see Ben’s post on his personal blog and the Bocoup blog.
How Do I Install Grunt?
Grunt is built on Node.js, and it’s available as a package via the Node package manager (npm). You’ll want to install it globally, so use this command:
npm install -g grunt
You’ll notice it installs quite a few dependencies; there are other npm packages that Grunt uses. Once that’s done, you’re all set to go!
How Do I Use Grunt?
As you know, Grunt is a command line tool; therefore, I’ll assume you have a terminal window open for the rest of this tutorial.
Let’s start by creating a sample project directory; we’re not actually going to be building a project here, but we’ll be seeing how Grunt works in this directory. Once you’re within that directory, run the grunt
command (according to the documentation, if you’re on Windows, you might have to run grunt.cmd
). You’ll probably see something like this:
<FATAL> Unable to find 'grunt.js' config file. Do you need any --help? </FATAL>
Before you can really leverage Grunt to its fullest potential, you’re going to need a grunt.js
file in the project directory. Thankfully, Grunt can auto-generate a grunt.js
file—and some other project skeleton material—with the init
task, which can run without a grunt.js
file in place. But grunt init
still isn’t enough to get your project started, as you’ll see if you run it. You need to choose a type of project to generate. Running grunt init
will give you a list of project types to choose from:
-
jquery
: A jQuery plugin -
node
: A Node module -
commonjs
: A CommonJS module -
gruntplugin
: A Grunt plugin -
gruntfile
: A Gruntfile (grunt.js
)
If your project doesn’t really match any of the first four project types, you can use the final one: gruntfile
: it just creates a basic grunt.js
that you can fill in. So, let’s give this a try, with the jQuery plugin template. Run grunt init:jquery
in your terminal.
You’ll notice a lot of initial output. If you take the time to read the template notes, you’ll see that we’re going to have to fill in a few values, like project name and project title. In fact, after that note, you’ll see something like this:
Please answer the following: [?] Project name (jquery.demo)
Whenever you initialize a project, Grunt will ask you a series of questions, so it can fill in a few options. That value in the parentheses? That’s the default suggestion, based on the project type and the name of the project directory. If you want to change it, write your own project name at the end of the line and hit ‘enter’; otherwise, just hit ‘enter’ to use the default name.
Keep going and fill in the rest of the fields. For a jQuery plugin project, here’s what else you’ll need to give it:
- Project title
- Description
- Version
- Project git repository
- Project homepage
- Project issues tracker
- Licenses
- Author name
- Author email
- Author url
- Required jQuery version
A lot of these have default values; if you want to use the default value, just hit enter for that line; to leave the field blank, you can just type “none.” Once you’ve gone through all the options, you’ll see that Grunt is creating some basic project files. Like what? Like this:
LICENSE-GPL LICENSE-MIT README.md grunt.js libs |-- jquery | |-- jquery.js |-- qunit |-- qunit.css |-- qunit.js package.json src |-- jquery.demo.js test |-- jquery.demo.html |-- jquery.demo_test.js
As you can see, this gives us a good start: not only do we have our plugin file (src/jquery.demo.js
), we also have Qunit tests (test/jquery.demo_test.js
). And these aren’t empty files, either. They’ve got some initial content, with a so-super-basic jQuery plugin and unit tests. Go ahead and check out the contents of these files, you’ll see what I mean.
Grunt does more than set up the project for you.
Of course, Grunt does more than set up the project for you. Notably, our project now has grunt.js
: a project-specific configuration file; because of the options it sets, we’re now able to use Grunt’s other built-in tasks. Soon we’ll crack it open and make some adjustments, but for now let’s run some tasks.
If you run grunt
with no options now, we’ll run the default task, if one has been set. In the case of a jQuery plugin project, that’s equivalent to running these four commands:
-
grunt lint
: checks your JavaScript against JSHint -
grunt qunit
: runs your Qunit tests -
grunt concat
: concatenates your project files together and puts the new file in adist
folder -
grunt min
: minifies the fileconcat
put out.
I should note something about the Qunit tests here: Qunit tests are meant to run in the browser by default; just open tests/jquery.demo.html
(or your equivalent) in the browser. However, the grunt qunit
test wants to run them on the terminal, which means you need to have PhantomJS installed. It’s not difficult: just head over to phantomjs.org and download and install the latest version. If Grunt can find that in your path, it will be able to run the Qunit tests from the terminal.
So, running grunt
should give you output to similar to this:
As you can see, each of our four tasks have run. If any of them were to fail, the rest of the tasks would be cancelled (unless you call Grunt with the --force
flag).
How Do I Customize My Tasks?
Already, we’ve gotten a lot of great functionality out of Grunt, using it just as it comes. However, let’s crack open that grunt.js
file and do some configuring.
Inside grunt.js
, you’ll see that all configuring is done by passing an object literal to grunt.initConfig()
. Let’s look at a few of the properties of our config object.
pkg
This property points to the package.json
file that Grunt created in our project directory. Having a package.json
file is part of the CommonJS Packages spec; it’s a single place where most of the metadata about the project (name, version, homepage, repository link … many of the values you set when initializing the project) can be stored. However, this pkg
property does more than point to the package file: notice the syntax: '<json:package.json>'
. That’s one of Grunt’s built-in directives: it actually loads the JSON file, so Grunt (or you) can access all the properties in the package.json file from the pkg
property.
meta
The meta
property is an object with only a single property: a banner. This banner is the comment that goes at the top of concatenated or minified project files. As you can see, it’s a string with some template tags (<%= %>
); in most cases, the tags surround a call to a property on the pkg
property, such as pkg.title
. However, you can also execute functions from inside those tags: the use of grunt.template.today()
and _.pluck()
shows us that.
concat
/ min
/ qunit
/ lint
/ watch
I’ve grouped the next five properties together because they’re very similar. They all set options for specific tasks, the tasks they’re named after. When configuring these tasks, it’s important to note that Grunt distinguished between two types of tasks: regular tasks, and multitasks. Basically, the difference is that regular tasks have only a single set of configuration options, whereas multitasks can have multiple sets of instructions (called targets). Of the five tasks I listed in the header of this section, the only one that isn’t a multitask is watch
.
Notice that in our config object, the qunit
and lint
properties are both objects with the files
property. files
is a single target for this task. In both cases, they’re an array of files to be used when executing this task. Let’s say I want to be able to lint only the files in src
sub-directory. I could add another target so that the lint
property would look like this:
lint: { files: ['grunt.js', 'src/**/*.js', 'test/**/*.js'], src: ['src/**/*.js'] },
Now, to lint only the files in src
, I run grunt lint:src
: I pass the target name after a colon. If I run only grunt lint
, both targets will be run.
In the case of the concat
and min
tasks, the targets are more complicated: they are objects with source (src
) and destination (dest
) properties. Of course, this tells Grunt where to get the files and where to put them when it is done processing them, respectively. If you add other files to your project, you’ll want to add them in the right place to make sure they are concatenated and minified correctly. So, if I added a src/utils.js
file that my jQuery plugin depended on, I’d change concat.dist.src
to this:
src: ['<banner:meta.banner>', 'src/utils.js', '<file_strip_banner:src/<%= pkg.name %>.js>'],
Looking at some of these tasks more closely, you’ll notice a few other directives: the most important is probably the directive. This allows you to access the properties of other tasks for reuse. You’ll notice that the configuration for the
watch
task uses , so that it operates on the same list of files that we gave to the
lint
task. You can learn more about the other directives in the Grunt docs.
Speaking of the watch task, what exactly does it do? Very simple: it runs the tasks in the tasks
property when a file in that list of files is changed. By default, the lint
and qunit
tasks are run.
jshint
This property simply configures what “bad parts” JSHint looks for in your JavaScript. The complete list of options can be found on the JSHint website’s options pages.
At the very bottom of our grunt.js
file, you’ll see this line:
grunt.registerTask('default', 'lint qunit concat min');
This is what creates our default task; you know, the one that runs when we run just grunt
. It’s actually creating an alias task, and you can create as many alias tasks as you want:
grunt.registerTask('src', 'lint:src qunit:src concat:src min:src');
Assuming you created src
targets for each of those tasks, you can now call grunt src
and do exactly what you want.
How Do I Use Third-Party Tasks?
While the tasks that come with Grunt will get your pretty far, you can probably think of other things that you’d love to be able to automate. Not to worry: Grunt comes with an API that allows anyone to create Grunt tasks and plugins. While we won’t be creating any Grunt tasks in this tutorial, if you’re interested in doing so, you should start with the Grunt plugin template (run grunt init:gruntplugin
), and then read through the API docs. Once you’ve written your task, you can load it into a project by adding this line inside your project’s grunt.js
file:
grunt.loadTasks(PATH_TO_TASKS_FOLDER);
Note that the parameter isn’t the path to the task file itself, it’s the path to the folder the task file is in.
However, other Grunt plugins are starting to appear, and some are available on NPM. After you install them via npm install
, you’ll load them into your project with this line:
grunt.loadNpmTasks(PLUGIN_NAME);
Of course, you’ll want to check the plugin documentation to see what you should add to your configuration object.
What Grunt plugins are available? Well, since Grunt is so new (less than an month old as I write this), there aren’t too many yet. I’ve found two:
-
grunt-css
: for linting and minifying CSS -
grunt-jasmine-task
: for running Jasmine specs
If you’ve found others, I’d love to hear about them; post ‘em in the comments!
Conclusion
While Grunt is a very new project, it’s hardly incomplete; as we’ve seen, it comes with pretty much everything you’ll need to use it on a large project, and can be extended as much as you want.
I’m hoping Grunt will become a community standard, and that we’ll see lots of tasks, plugins, and init templates popping up in the near future. How do you feel about it?
Comments