1 | # grunt
|
2 | Grunt is a command line build tool for JavaScript projects.
|
3 |
|
4 | As of now, grunt has the following predefined tasks:
|
5 |
|
6 | * **concat** - Concatenate files.
|
7 | * **init** - Generate project scaffolding based on user input.
|
8 | * **lint** - Validate files with [JSHint][jshint].
|
9 | * **min** - Minify files with [UglifyJS][uglify].
|
10 | * **test** - Run unit tests with [nodeunit][nodeunit].
|
11 | * **watch** - Run predefined tasks whenever watched files change.
|
12 |
|
13 | _(My TODO list includes more "project scaffolding" templates as well as a "QUnit headless unit-testing" task)_
|
14 |
|
15 | And in addition to the predefined tasks, you can define your own.
|
16 |
|
17 | [issues]: https://github.com/cowboy/grunt/issues
|
18 |
|
19 | [concat]: https://github.com/cowboy/grunt/blob/master/tasks/concat.js
|
20 | [init]: https://github.com/cowboy/grunt/blob/master/tasks/init.js
|
21 | [init-nodejs]: https://github.com/cowboy/grunt/blob/master/tasks/init/node.js
|
22 | [init-nodedir]: https://github.com/cowboy/grunt/blob/master/tasks/init/node
|
23 | [lint]: https://github.com/cowboy/grunt/blob/master/tasks/lint.js
|
24 | [min]: https://github.com/cowboy/grunt/blob/master/tasks/min.js
|
25 | [test]: https://github.com/cowboy/grunt/blob/master/tasks/test.js
|
26 | [misc]: https://github.com/cowboy/grunt/blob/master/tasks/misc.js
|
27 | [tasks]: https://github.com/cowboy/grunt/tree/master/tasks
|
28 | [gruntfile]: https://github.com/cowboy/grunt/blob/master/grunt.js
|
29 |
|
30 | [node]: http://nodejs.org/
|
31 | [npm]: http://npmjs.org/
|
32 | [jshint]: http://www.jshint.com/
|
33 | [uglify]: https://github.com/mishoo/UglifyJS/
|
34 | [nodeunit]: https://github.com/caolan/nodeunit
|
35 |
|
36 | ## Why does grunt exist?
|
37 | Doing all this stuff manually is a total pain, and building all this stuff into a gigantic Makefile / Jakefile / Cakefile / Rakefile / ?akefile that's maintained across all my projects was also becoming a total pain. Since I always found myself performing the same tasks over and over again, for every project, it made sense to build a task-based build tool.
|
38 |
|
39 | Being primarily a JavaScript developer, I decided to use [Node.js][node] and [npm][npm] because the dependencies I most care about ([JSHint][jshint] and [UglifyJS][uglify]) 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. Just install it and see.
|
40 |
|
41 | ## Installing grunt
|
42 |
|
43 | _Grunt is currently in beta. While I'm already using it on multiple projects, it might have a minor issue or two. And things might change before its final release, based on your feedback. Please try it out in a project, and [make suggestions][issues] or [report bugs][issues]!_
|
44 |
|
45 | Grunt is available as an [npm][npm] module. If you install grunt globally via `npm install -g grunt`, it will be available for use in all of your projects.
|
46 |
|
47 | Once grunt has been installed, you can type `grunt --help` at the command line for more information. And if you want to see grunt "grunt" itself, cd into grunt's directory and type `grunt` (in Windows, you might need to run it as `grunt.cmd`).
|
48 |
|
49 | ## The config file, aka "gruntfile"
|
50 | When run, grunt looks in the current directory for a file named `grunt.js`, and if not found, continues looking in parent directories until found. The gruntfile is typically placed in the root of your project repository, and is a valid JavaScript file comprised of two parts: [project configuration](#config), and [tasks](#tasks) / [helpers](#helpers).
|
51 |
|
52 | <div id="config"></div>
|
53 | ## Project configuration
|
54 | Each grunt task relies on configuration information defined in a single `config.init()` call in the gruntfile. Usually, this information is specified in task-named sub-properties of a main configuration object. It's not as complicated as it sounds.
|
55 |
|
56 | For example, this simple configuration would define a list of files to be linted when the task "lint:files" was run on the command line like this: `grunt lint:files`.
|
57 |
|
58 | ```javascript
|
59 | config.init({
|
60 | lint: {
|
61 | files: ['lib/*.js', 'test/*.js', 'grunt.js']
|
62 | }
|
63 | });
|
64 | ```
|
65 |
|
66 | Also note that because the "lint" task is a [basic task](#tasks_basic), you can also run _all_ lint sub-tasks with just `grunt lint`.
|
67 |
|
68 | You can store any arbitrary information inside of the configuration object, and as long as it doesn't conflict with a property one of your tasks is using, it will be ignored.
|
69 |
|
70 | ```javascript
|
71 | config.init({
|
72 | // Generic project information used by some helpers and tasks.
|
73 | meta: {},
|
74 | // Lists of files to be concatenated, used by the "concat" task.
|
75 | concat: {},
|
76 | // Lists of files to be minififed with UglifyJS, used by the "min" task.
|
77 | min: {},
|
78 | // Lists of files to be unit tested with Nodeunit, used by the "test" task.
|
79 | test: {},
|
80 | // Lists of files to be linted with JSHint, used by the "lint" task.
|
81 | lint: {},
|
82 | // Global configuration options for JSHint.
|
83 | jshint: {},
|
84 | // Global configuration options for UglifyJS.
|
85 | uglify: {}
|
86 | });
|
87 | ```
|
88 |
|
89 | Take a look at grunt's own [grunt.js gruntfile][gruntfile] or [javascript-hooker's gruntfile](https://github.com/cowboy/javascript-hooker/blob/master/grunt.js) or [glob-whatev's gruntfile](https://github.com/cowboy/node-glob-whatev/blob/master/grunt.js) for a few examples.
|
90 |
|
91 | _Note: you don't need to specify configuration settings for tasks that you don't use._
|
92 |
|
93 | <div id="tasks"></div>
|
94 | ## Tasks
|
95 | Tasks are the things you do most often, like [concat][concat], [lint][lint], [min][min] or [test][test] files. Every time grunt is run, one or more tasks must be specified, which tells grunt what you want it to do. Note that if you don't specify a task, but a task named "default" has been defined, that task will run (unsurprisingly) by default.
|
96 |
|
97 | _You should probably create a ["default" task][gruntfile] in your gruntfile._
|
98 |
|
99 | Tasks can be created in a few ways.
|
100 |
|
101 | <div id="tasks_alias"></div>
|
102 | ### Alias tasks
|
103 |
|
104 | ```javascript
|
105 | task.registerTask(taskName, [description, ] taskList);
|
106 | ```
|
107 |
|
108 | _Note that the description is optional. If omitted, a useful description will be added for you automatically._
|
109 |
|
110 | The following example defines a task named "theworks" that, when run, actually runs the "lint:files" "test:files" "concat" "min" tasks, in-order. so instead of typing `grunt lint:files test:files concat min` at the command line, you can just type `grunt theworks`. If this task were named "default" instead of "theworks" it would be run by default whenever `grunt` was executed without specifying tasks.
|
111 |
|
112 | ```javascript
|
113 | task.registerTask('theworks', 'lint:files test:files concat min');
|
114 | ```
|
115 |
|
116 | <div id="tasks_basic"></div>
|
117 | ### Basic tasks
|
118 | A basic task is a task that implicitly iterates over all of its configuration sub-properties if no sub-task is specified. For example, in the following, while `grunt lint:test` or `grunt lint:lib` will lint only those specific files, `grunt lint` will run the "test", "lib" and "grunt" sub-tasks for you, automatically. It's convenient.
|
119 |
|
120 | ```javascript
|
121 | config.init({
|
122 | lint: {
|
123 | test: ['test/*.js'],
|
124 | lib: ['lib/*.js'],
|
125 | grunt: ['grunt.js']
|
126 | }
|
127 | });
|
128 | ```
|
129 |
|
130 | While it's probably more useful for you to check out the JavaScript source of the [concat][concat], [lint][lint], [min][min] or [test][test] tasks, this is how you'd define a Basic task:
|
131 |
|
132 | ```javascript
|
133 | task.registerBasicTask('log', 'This task logs something.', function(data, name) {
|
134 | // data === the value of the config sub-prop
|
135 | // name === the name of the config sub-prop
|
136 |
|
137 | log.writeln(data);
|
138 |
|
139 | if (failureOfSomeKind) { return false; }
|
140 | log.writeln('Your success message.');
|
141 | });
|
142 | ```
|
143 |
|
144 | <div id="tasks_custom"></div>
|
145 | ### Custom tasks
|
146 |
|
147 | You can go crazy with tasks, though. They don't have to be basic. If your tasks don't follow the "basic task" structure, use a custom task.
|
148 |
|
149 | ```javascript
|
150 | task.registerTask('default', 'My "default" task description.', function() {
|
151 | log.writeln('Currently running the "default" task.');
|
152 | });
|
153 | ```
|
154 |
|
155 | Inside a task, you can run other tasks.
|
156 |
|
157 | ```javascript
|
158 | task.registerTask('foo', 'My "foo" task.', function() {
|
159 | // Enqueue "bar" and "baz" tasks, to run after 'foo' finishes, in-order.
|
160 | task.run('bar baz');
|
161 | // Or:
|
162 | task.run(['bar', 'baz']);
|
163 | });
|
164 | ```
|
165 |
|
166 | Tasks can be asynchronous.
|
167 |
|
168 | ```javascript
|
169 | task.registerTask('async', 'My "foo" task.', function() {
|
170 | // Force task into async mode and grab a handle to the "done" function.
|
171 | var done = this.async();
|
172 | // Run some sync stuff.
|
173 | log.writeln('Processing task...');
|
174 | // And some async stuff.
|
175 | setTimeout(function() {
|
176 | log.writeln('All done!');
|
177 | done();
|
178 | }, 1000);
|
179 | });
|
180 | ```
|
181 |
|
182 | Tasks can access their own name and arguments.
|
183 |
|
184 | ```javascript
|
185 | task.registerTask('foo', 'My "foo" task.', function(a, b) {
|
186 | log.writeln(this.name, a, b);
|
187 | });
|
188 |
|
189 | // Usage:
|
190 | // grunt foo foo:bar
|
191 | // logs: "foo", undefined, undefined
|
192 | // logs: "foo", "bar", undefined
|
193 | // grunt foo:bar:baz
|
194 | // logs: "foo", "bar", "baz"
|
195 | ```
|
196 |
|
197 | Tasks can fail if any errors were logged.
|
198 |
|
199 | ```javascript
|
200 | task.registerTask('foo', 'My "foo" task.', function() {
|
201 | if (someError) {
|
202 | log.error('This is an error message.');
|
203 | }
|
204 |
|
205 | // Fail task if errors were logged.
|
206 | if (task.hadErrors()) { return false; }
|
207 |
|
208 | log.writeln('This is the success message');
|
209 | });
|
210 | ```
|
211 |
|
212 | When tasks fail, all subsequent tasks will be aborted unless `--force` was specified.
|
213 |
|
214 | ```javascript
|
215 | task.registerTask('foo', 'My "foo" task.', function() {
|
216 | // Fail synchronously.
|
217 | return false;
|
218 | });
|
219 |
|
220 | task.registerTask('bar', 'My "bar" task.', function() {
|
221 | var done = this.async();
|
222 | setTimeout(function() {
|
223 | // Fail asynchronously.
|
224 | done(false);
|
225 | }, 1000);
|
226 | });
|
227 | ```
|
228 |
|
229 | Tasks can be dependent on the successful execution of other tasks. Note that `task.requires` won't actually RUN the other task. It'll just check to see that it has run and not failed.
|
230 |
|
231 | ```javascript
|
232 | task.registerTask('foo', 'My "foo" task.', function() {
|
233 | return false;
|
234 | });
|
235 |
|
236 | task.registerTask('bar', 'My "bar" task.', function() {
|
237 | // Fail task if "foo" task failed or never ran.
|
238 | task.requires('foo');
|
239 | // This code executes if the "foo" task ran successfully.
|
240 | log.writeln('Hello, world.');
|
241 | });
|
242 |
|
243 | // Usage:
|
244 | // grunt foo bar
|
245 | // doesn't log, because foo failed.
|
246 | // grunt bar
|
247 | // doesn't log, because foo never ran.
|
248 | ```
|
249 |
|
250 | Tasks can fail if required configuration properties don't exist.
|
251 |
|
252 | ```javascript
|
253 | task.registerTask('foo', 'My "foo" task.', function() {
|
254 | // Fail task if "meta.name" config prop is missing.
|
255 | config.requires('meta.name');
|
256 | // Also fails if "meta.name" config prop is missing.
|
257 | config.requires(['meta', 'name']);
|
258 | // Log... conditionally.
|
259 | log.writeln('This will only log if meta.name is defined in the config.');
|
260 | });
|
261 | ```
|
262 |
|
263 | Tasks can access configuration properties.
|
264 |
|
265 | ```javascript
|
266 | task.registerTask('foo', 'My "foo" task.', function() {
|
267 | // Log the property value. Returns null if the property is undefined.
|
268 | log.writeln('The meta.name property is: ' + config('meta.name'));
|
269 | // Also logs the property value. Returns null if the property is undefined.
|
270 | log.writeln('The meta.name property is: ' + config(['meta', 'name']));
|
271 | });
|
272 | ```
|
273 |
|
274 | Look at the [built-in tasks][tasks] for more examples.
|
275 |
|
276 | ## Helpers
|
277 | Helpers are just utility functions, exposed through the `task` global variable, so that they can be used by tasks in other files.
|
278 |
|
279 | It's not much more complex than this:
|
280 |
|
281 | ```javascript
|
282 | task.registerHelper('foo', function(a, b) {
|
283 | return a + b;
|
284 | });
|
285 |
|
286 | task.helper('foo', 2, 3) // 5
|
287 | ```
|
288 |
|
289 | For example, in the [min][min] task, the majority of the actual minification work is done in an [uglify][min] helper, so that other tasks can utilize that code if they need to.
|
290 |
|
291 | ## Directives
|
292 | Directives are essentially string placeholders for helper functions, specified as values in the [configuration object](#config). It's not as crazy as it sounds.
|
293 |
|
294 | A good example of directives would be the `<json:package.json>` and `<config:lint.files>` directives in grunt's own [grunt.js gruntfile][gruntfile]. Or the `<banner>` and `<file_strip_banner:lib/hooker.js>` directives in [javascript-hooker's gruntfile](https://github.com/cowboy/javascript-hooker/blob/master/grunt.js).
|
295 |
|
296 | In brief, when a directive like `<foo>` is encountered, the `foo` helper is executed, and its return value is used. If `<foo:bar:baz>` is encountered, the `foo` helper is executed, with arguments `"bar"` and `"baz"` passed in, and its return value is used.
|
297 |
|
298 | Some of the built-in directives:
|
299 |
|
300 | * `<config:prop.subprop>` - expand to the prop.subprop config property. Great for DRYing up file lists.
|
301 | * `<json:file.json>` - expand to the object parsed from file.json (a valid JSON file).
|
302 | * `<banner>` - the string in config property `meta.banner`, parsed via [handlebars][misc].
|
303 | * `<banner:prop.subprop>` - same as above, but using a custom config property.
|
304 | * `<file_strip_banner:file.js>` - expand to the given file, with any leading /*...*/ banner stripped.
|
305 |
|
306 | Can you guess what these directives do? They're from grunt's own [grunt.js gruntfile][gruntfile].
|
307 |
|
308 | ```javascript
|
309 | config.init({
|
310 | pkg: '<json:package.json>',
|
311 | lint: {
|
312 | files: ['grunt.js', 'lib/**/*.js', 'tasks/*.js', 'test/**/*.js']
|
313 | },
|
314 | watch: {
|
315 | files: '<config:lint.files>',
|
316 | tasks: 'default'
|
317 | }
|
318 | });
|
319 | ```
|
320 |
|
321 | ## Global Variables
|
322 | In an effort to make things easier, there are a lot of global variables.
|
323 |
|
324 | * `underscore` - [Underscore.js](http://underscorejs.org/)
|
325 | * `util` - miscellaneous utilities
|
326 | * `task` - the entire task interface
|
327 | * `file` - glob expansion, file reading, writing, directory traversing
|
328 | * `fail` - more serious than error logging, `fail.warn` and `fail.fatal` will halt everything
|
329 | * `config` - reading values from the grunt configuration
|
330 | * `option` - reading values from the command-line options
|
331 | * `log` - don't use `console.log`, use `log.writeln` instead! [More info on log](#logging).
|
332 | * `verbose` - just like `log`, but only logs if `--verbose` was specified. [More info on verbose](#logging).
|
333 |
|
334 | Unfortunately, I haven't documented everything yet. Fortunately, the source is open and browsable. Have fun!
|
335 |
|
336 | <div id="logging"></div>
|
337 | ## Logging
|
338 | I wanted grunt to look pretty. As such, there are a LOT of logging methods, and a few useful patterns:
|
339 |
|
340 | Note, all of the methods that actually log something are chainable.
|
341 |
|
342 | * `log.write(msg)` - log msg, with no trailing newline
|
343 | * `log.writeln(msg)` - log msg, with trailing newline
|
344 | * `log.error(msg)` - if msg is omitted, logs ERROR in red, otherwise logs: >> msg, with trailing newline
|
345 | * `log.ok(msg)` - if msg is omitted, logs OK in green, otherwise logs: >> msg, with trailing newline
|
346 | * `log.subhead(msg)` - logs msg in bold, with trailing newline
|
347 | * `log.writeflags(obj, prefix)` - logs a list of obj properties (good for debugging flags)
|
348 | * `log.wordlist(arr)` - returns a comma-separated list of array items
|
349 | * `log.verbose` - contains all methods of `log` but only logs if `--verbose` was specified.
|
350 | * `log.notverbose` - contains all methods of `log` but only logs if `--verbose` wasn't specified.
|
351 | * `log.verbose.or` - reference to `log.notverbose`
|
352 | * `log.notverbose.or` - reference to `log.verbose`
|
353 |
|
354 | There are a few other methods, but you shouldn't use them in your tasks or helpers, so they've been omitted.
|
355 |
|
356 | A common pattern is to only log when in `--verbose` mode OR if an error occurs, like so:
|
357 |
|
358 | ```javascript
|
359 | task.registerHelper('something', function(arg) {
|
360 | var result;
|
361 | var msg = 'Doing something...';
|
362 | verbose.write(msg);
|
363 | try {
|
364 | result = doSomethingThatThrowsAnExceptionOnError(arg);
|
365 | // Success!
|
366 | verbose.ok();
|
367 | return result;
|
368 | } catch(e) {
|
369 | // Something went wrong.
|
370 | verbose.or.write(msg).error().error(e.message);
|
371 | fail.warn('Something went wrong.', 50);
|
372 | }
|
373 | });
|
374 | ```
|
375 |
|
376 | An explanation of the above code:
|
377 |
|
378 | 1. `verbose.write(msg);` logs the message (no newline), but only in `--verbose` mode.
|
379 | 2. `verbose.ok();` logs OK in green, with a newline.
|
380 | 3. `verbose.or.write(msg).error().error(e.message);` does a few things:
|
381 | 1. `verbose.or.write(msg)` logs the message (no newline) if not in `--verbose` mode, and returns the `notverbose` object.
|
382 | 2. `.error()` logs ERROR in red, with a newline, and returns the `notverbose` object.
|
383 | 3. `.error(e.message);` logs the actual error message (and returns the `notverbose` object).
|
384 | 4. `fail.warn('Something went wrong.', 50);` logs a warning in bright yellow, existing grunt with exit code 50, unless `--force` was specified.
|
385 |
|
386 | You can write crazy logging chains, omg!
|
387 |
|
388 | <div id="exit_codes"></div>
|
389 | ## Exit Codes
|
390 |
|
391 | * `1` - Generic error.
|
392 | * `2` - Config file not found.
|
393 | * `3` - Generic task failed.
|
394 | * `10` - Uglify-JS error.
|
395 | * `11` - Banner generation error.
|
396 | * `20` - Init error.
|
397 | * `61-69` - Nodeunit errors.
|
398 |
|
399 | <div id="examples"></div>
|
400 | ## Examples
|
401 | In this example, you don't want to run `grunt lint concat` every time you need to process your code, because "dist/output.js" will be linted before it's created!
|
402 |
|
403 | You should really do `grunt lint:beforeconcat concat lint:afterconcat`.
|
404 |
|
405 | ```javascript
|
406 | config.init({
|
407 | // When the "concat:dist/output.js" task is run, the specified "foo.js" and
|
408 | // "bar.js" files will be concatenated in-order and saved to the "output.js"
|
409 | // output file. Because the "concat" task is a Basic task, when it is run
|
410 | // without an argument, all sub-tasks will automatically be run.
|
411 | concat: {
|
412 | 'dist/output.js': ['src/foo.js', 'src/bar.js']
|
413 | },
|
414 | lint: {
|
415 | // When the "lint:beforeconcat" task is run, the specified "foo.js" and
|
416 | // "bar.js" files will be linted with JSHint. The same follows for the
|
417 | // "lint:afterconcat" task. Because the "lint" task is a Basic task, when
|
418 | // it is run without an argument, all sub-tasks will automatically be run.
|
419 | beforeconcat: ['src/foo.js', 'src/bar.js'],
|
420 | afterconcat: ['dist/output.js']
|
421 | }
|
422 | });
|
423 | ```
|
424 |
|
425 | And to make your workflow easier, create an [Alias Task](#tasks_alias):
|
426 |
|
427 | ```javascript
|
428 | task.registerTask('default', 'lint:beforeconcat concat lint:afterconcat');
|
429 | ```
|
430 |
|
431 | _(more examples coming... soon)_
|
432 |
|
433 | ## Contributing
|
434 | Fork, tweak, and make pull requests.. but you'd better successfully `grunt` it first, or I'm not even looking.
|
435 |
|
436 | ## Release History
|
437 | _(Until v1.0.0, this will only be updated when major or breaking changes are made)_
|
438 |
|
439 | * 2012/01/29 - v0.2.5 - Added a "qunit" task as well as an init "jquery" template (as of now, there are also "node" and "commonjs" init templates).
|
440 | * 2012/01/22 - v0.2.1 - Removed handlebars, templates are universally handled by underscore now. Changed init task template tags from <% %> to {% %}. Banners beginning with /*! will no longer be stripped.
|
441 | * 2012/01/22 - v0.2.0 - Added "init" task with a sample template, reworked a lot of code. Hopefully it's backwards-compatible.
|
442 | * 2012/01/11 - v0.1.0 - Initial release.
|
443 |
|
444 | ## License
|
445 | Copyright (c) 2012 "Cowboy" Ben Alman
|
446 | Licensed under the MIT license.
|
447 | <http://benalman.com/about/license/>
|