UNPKG

18.7 kBMarkdownView Raw
1# grunt
2Grunt is a command line build tool for JavaScript projects.
3
4As 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
15And 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?
37Doing 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
39Being 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
45Grunt 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
47Once 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"
50When 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
54Each 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
56For 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
59config.init({
60 lint: {
61 files: ['lib/*.js', 'test/*.js', 'grunt.js']
62 }
63});
64```
65
66Also 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
68You 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
71config.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
89Take 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
95Tasks 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
99Tasks can be created in a few ways.
100
101<div id="tasks_alias"></div>
102### Alias tasks
103
104```javascript
105task.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
110The 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
113task.registerTask('theworks', 'lint:files test:files concat min');
114```
115
116<div id="tasks_basic"></div>
117### Basic tasks
118A 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
121config.init({
122 lint: {
123 test: ['test/*.js'],
124 lib: ['lib/*.js'],
125 grunt: ['grunt.js']
126 }
127});
128```
129
130While 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
133task.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
147You 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
150task.registerTask('default', 'My "default" task description.', function() {
151 log.writeln('Currently running the "default" task.');
152});
153```
154
155Inside a task, you can run other tasks.
156
157```javascript
158task.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
166Tasks can be asynchronous.
167
168```javascript
169task.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
182Tasks can access their own name and arguments.
183
184```javascript
185task.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
197Tasks can fail if any errors were logged.
198
199```javascript
200task.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
212When tasks fail, all subsequent tasks will be aborted unless `--force` was specified.
213
214```javascript
215task.registerTask('foo', 'My "foo" task.', function() {
216 // Fail synchronously.
217 return false;
218});
219
220task.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
229Tasks 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
232task.registerTask('foo', 'My "foo" task.', function() {
233 return false;
234});
235
236task.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
250Tasks can fail if required configuration properties don't exist.
251
252```javascript
253task.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
263Tasks can access configuration properties.
264
265```javascript
266task.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
274Look at the [built-in tasks][tasks] for more examples.
275
276## Helpers
277Helpers are just utility functions, exposed through the `task` global variable, so that they can be used by tasks in other files.
278
279It's not much more complex than this:
280
281```javascript
282task.registerHelper('foo', function(a, b) {
283 return a + b;
284});
285
286task.helper('foo', 2, 3) // 5
287```
288
289For 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
292Directives are essentially string placeholders for helper functions, specified as values in the [configuration object](#config). It's not as crazy as it sounds.
293
294A 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
296In 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
298Some 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
306Can you guess what these directives do? They're from grunt's own [grunt.js gruntfile][gruntfile].
307
308```javascript
309config.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
322In 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
334Unfortunately, I haven't documented everything yet. Fortunately, the source is open and browsable. Have fun!
335
336<div id="logging"></div>
337## Logging
338I wanted grunt to look pretty. As such, there are a LOT of logging methods, and a few useful patterns:
339
340Note, 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
354There are a few other methods, but you shouldn't use them in your tasks or helpers, so they've been omitted.
355
356A common pattern is to only log when in `--verbose` mode OR if an error occurs, like so:
357
358```javascript
359task.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
376An explanation of the above code:
377
3781. `verbose.write(msg);` logs the message (no newline), but only in `--verbose` mode.
3792. `verbose.ok();` logs OK in green, with a newline.
3803. `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).
3844. `fail.warn('Something went wrong.', 50);` logs a warning in bright yellow, existing grunt with exit code 50, unless `--force` was specified.
385
386You 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
401In 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
403You should really do `grunt lint:beforeconcat concat lint:afterconcat`.
404
405```javascript
406config.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
425And to make your workflow easier, create an [Alias Task](#tasks_alias):
426
427```javascript
428task.registerTask('default', 'lint:beforeconcat concat lint:afterconcat');
429```
430
431_(more examples coming... soon)_
432
433## Contributing
434Fork, 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
445Copyright (c) 2012 "Cowboy" Ben Alman
446Licensed under the MIT license.
447<http://benalman.com/about/license/>