Date post: | 11-Jul-2015 |
Category: |
Software |
Upload: | predhin-sapru |
View: | 1,129 times |
Download: | 2 times |
By,
Predhin Tom Sapru
The JavaScript Task Runner
Contsents Introduction .................................................................................................................................................. 3
What is GruntJS? ........................................................................................................................................... 4
Why GruntJS? ................................................................................................................................................ 4
How to setup GruntJS? ................................................................................................................................. 6
Getting Started with GruntJS ........................................................................................................................ 7
How the CLI works ........................................................................................................................................ 8
Hear a Hello from Grunt ............................................................................................................................... 8
Grunt tasks, config and warnings ................................................................................................................ 10
Grunt Errors and Warnings ......................................................................................................................... 14
Grunt Files and logs ..................................................................................................................................... 14
Inside Grunt tasks ....................................................................................................................................... 17
Inside MultiTasks ........................................................................................................................................ 18
Using Grunt.js plugins ................................................................................................................................. 20
Splitting Grunt configuration into different files ........................................................................................ 21
Walk through over a Project Skeleton ........................................................................................................ 23
10 most popular grunt plugins .................................................................................................................... 27
Project Scaffolding ...................................................................................................................................... 32
Summarizing Grunt ..................................................................................................................................... 42
Introduction
About this module
This module provides students with the knowledge and skills that are needed to
develop applications by using the GruntJS.
Target Audience
This module is designed for beginners of GruntJS who are expected to know the basic
concepts of Javascript.
Module Objectives
After completing this module handout, you will be able to:
Describe the features and characteristics of GruntJS
o Why GruntJS?
o What is GruntJS?
o How to integrate GruntJS?
o What advantage does GruntJS brings in?
Define GruntJS
Create a Grunt file
Run a Grunt Task
Create a complete Javascript task runner using GruntJS for applications
Pre-requisite
The prerequisite of GruntJs is knowledge in concepts of Javascript.
What is GruntJS? Grunt is a task-based command line build tool for JavaScript projects. It is a
JavaScript task runner. 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.
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?
Why GruntJS? As web developers, we do certain tasks repetitively. Some of these are simple
development tasks: things like linting your code or running unit tests. Others are
important to do before deploying your site, or when assembling a final package for
deployment: minifying your JavaScript and concatenating your CSS. Still others are
important in both contexts, like browserifying your code so you can transform your
source module files into something the browser can consume. 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.
The approach we all start with is simply running these tasks manually, using the
associated command-line tools at the appropriate times. This can be painful, particularly
during development; the code/compile/lint/test/refresh cycle is not a fun one.
Fortunately, tools have arisen to help automate this process, called task runners. A task
runner allows you to control all of these tasks in a uniform way, and allows for the easy
layering of functionality like watch mode or live reload on top of them.
The best task runner for the web development ecosystem is called Grunt
One of the best things about Grunt is the consistency it brings to teams. If you work
collaboratively, you‟ll know how frustrating inconsistency in the code can be. Grunt
enables teams to work with a unified set of commands, thus ensuring that everyone on
the team is writing code to the same standard. After all, nothing is more frustrating than
a build that fails because of little inconsistencies in how a team of developers writes
code.
Grunt also has an incredibly active community of developers, with new plugins being
released regularly. The barrier to entry is relatively low because a vast range of tools and
automated tasks are already available to use.
LET’S NIP SOME MISCONCEPTIONS IN THE BUD RIGHT AWAY
I don’t need the things Grunt does
You probably do, actually. Check out that list up top. Those things aren‟t nice-to-haves.
They are pretty vital parts of website development these days. If you already do all of
them, that‟s awesome. Perhaps you use a variety of different tools to accomplish them.
Grunt can help bring them under one roof, so to speak. If you don‟t already do all of
them, you probably should and Grunt can help. Then, once you are doing those, you can
keep using Grunt to do more for you, which will basically make you better at doing your
job.
Grunt runs on Node.js — I don’t know Node
You don‟t have to know Node. Just like you don‟t have to know Ruby to use Sass. Or
PHP to use WordPress. Or C++ to use Microsoft Word.
I have other ways to do the things Grunt could do for me
Are they all organized in one place, configured to run automatically when needed, and
shared among every single person working on that project? Unlikely, I‟d venture.
How to setup GruntJS? The first thing to do in order to use Grunt is to set up Node.js. (If you know nothing
about Node.js, don‟t worry — it merely needs to be installed in order for Grunt to be
able to run.). Grunt 0.4.x requires stable Node.js versions >= 0.8.0. Odd version numbers
of Node.js are considered unstable development versions.
Prerequisites for GruntJS Installation: Installing Node and NPM
Following are the steps to install node and npm:
Mac
Go to nodejs.org.
Click install, (to download the pkg).
Open the pkg and run through the install process.
Ubuntu
On the latest version of Ubuntu, you can simply:
sudo apt-get install nodejs nodejs-dev npm
On earlier versions, you might need to update your repository:
sudo apt-get install python-software-properties
sudo add-apt-repository ppa:chris-lea/node.js
sudo apt-get update
sudo apt-get install nodejs nodejs-dev npm
Windows
Since Windows package managers are less common, we recommend just
downloading the Windows binary.
Make sure to add node to the PATH environment variable.
Validating the node and npm installation
Node: node –v
NPM: npm -v
Once Node.js is installed, run this command:
npm install -g grunt-cli
To make sure Grunt has been properly installed, you can run the following command:
grunt --version
This will put the grunt command in your system path, allowing it to be run from any
directory.
Getting Started with GruntJS Before we get started with GruntJS, we have to understand three main components of
Grunt:
GruntJS CLI: This provides GruntJS command line tooling. To install the GruntJS
CLI, type the below mentioned command. If you get some error messages while
installing, this probably means that you need sudo/admin privileges to run the
command.
npm install -g grunt-cli
The command shown above will install the grunt-cli package globally,
allowing grunt command to be available from any directory. GruntJS CLI does not
include the grunt task runner. To use grunt task runner, we have to install it as an
application dev. dependency. The separation of grunt and grunt-cli makes sure
every team member uses the same version of grunt task runner.
GruntJS Task Runner: The grunt command invokes the Grunt task runner. We
have to install it as application dev. dependency.
How the CLI works Each time grunt is run, it looks for a locally installed Grunt using node's require() system.
Because of this, you can run grunt from any subfolder in your project.
If a locally installed Grunt is found, the CLI loads the local installation of the Grunt
library, applies the configuration from your Gruntfile, and executes any tasks you've
requested for it to run.
Hear a Hello from Grunt Create a directory grunt101 and change current directory to that one.
mkdir grunt101 && cd grunt101
Next thing is to create a project. For that we need a package.json file. Creation of
package.json is too cool with npm.
npm init
This will create package.json inside our grunt101 directory.
Now add grunt as a dependency to our project.
npm install grunt --save-dev
If you run the grunt command you will get a message like this:
grunt
# A valid Gruntfile could not be found. Please see the getting started guide
for more information on how to configure grunt: http://gruntjs.com/getting-st
arted
# Fatal error: Unable to find Gruntfile.
So, let‟s create the Gruntfile.js file:
var grunt = require('grunt');
grunt.registerTask('default', 'default task description', function(){
console.log('hello world');
});
If you run grunt again, you will see a message. The default task is run when nothing else
it is specified. We are going to create a 2nd task called „hello‟ and it is going to accept a
parameter that we can pass along with the task name separated with a colon. As follows:
grunt hello:adrian. We can handle errors using grunt.warn. Every time a grunt.warn is
found the task will stop executing, and it will give its warning message.. You can
override using --force. Try all this commands and noticed the different effects: grunt,
grunt hello, grunt hello --force, grunt hello:adrian.
var grunt = require('grunt');
grunt.registerTask('default', 'default task description', function(){
console.log('hello world');
});
grunt.registerTask('hello', 'say hello', function(name){
if(!name || !name.length)
grunt.warn('you need to provide a name.');
console.log('hello ' + name);
});
We can chain multiple grunt tasks by using and array. Change the Gruntfile.js for the
following and see what will happen when you type grunt.
var grunt = require('grunt');
grunt.registerTask('world', 'world task description', function(){
console.log('hello world');
});
grunt.registerTask('hello', 'say hello', function(name){
if(!name || !name.length)
grunt.warn('you need to provide a name.');
console.log('hello ' + name);
});
grunt.registerTask('default', ['world', 'hello:adrian']);
Grunt tasks, config and warnings Here are some of the methods that we have used so far and some more that we will use
in the next examples:
Grunt config
grunt.initConfig(configObject): Initialize a configuration object. It can be accessed
by grunt.config.get.
grunt.config.get([prop]): get the prop value from the grunt.initConfig. The
property could be deeply nested (e.g. concat.options.dest) and the values inside
<% %> are expanded.
Grunt tasks
grunt.registerTask(taskName[, description], taskFunction): register a task.
taskName: required to register the task and it allows the task to be e executed
with grunt taskName or called by other grunt task.
description: (optional) string describing task.
taskFunction: function which can accept parameters separated by colons (:). E.g.
grunt taskName:arg1:arg2
grunt.task.registerTask(taskName, taskList): register task.
taskName: required to register the task and it allows the task to be e executed
with grunt taskName or called by other grunt task.
taskList: array of taskNames to be executed, in the order specified, when the
taskName is called. E.g.: grunt.registerTask('concatAll', ['concat:templates',
'concat:javascripts', 'concat:stylesheets']);
grunt.registerMultiTask(taskName[, description], taskFunction): multi-tasks
accepts the same parameters as grunt.registerTask. However, it reads
grunt.initConfig parameters differently:
1. Grunt looks for a config that matches the taskName.
2. MultiTask can have multiple configurations referred as this.target and the value
as this.data.
3. All the “targets” are run if it is not specified otherwise.
grunt.initConfig({
print: {
target1: ['index.html', 'src/styles.css', 2],
target2: 'data',
hello: 'world'
}
});
grunt.registerMultiTask('print', 'print targets', function() {
grunt.log.writeln(this.target + ': ' + this.data);
});
You can specify one target grunt print:hello or run all them grunt print which will
produce this output:
Running "print:target1" (print) task
target1: index.html,src/styles.css,2
Running "print:target2" (print) task
target2: data
Running "print:hello" (print) task
hello: world
Example: Forex and grunt multiple async calls handling
The idea is get conversion rates from a base currency (e.g. USD) to a target
currency (e.g. EUR). We are using a registerMultiTask, so the taskName „currency‟
matches its property in the config.init. Notice that we can has additional arbitrary
data such as endpoint URL.
Async calls can be a little tricky in Javascript. We are going to do multiple
HTTP request. Since http.get is async Grunt will finish the task before even
receiving any response. this.async() solves the issue, we just need to call it when
we are done.
module.exports = function(grunt){
grunt.config.init({
currency: {
USD: ['EUR', 'GBP', 'DOP'],
DOP: ['USD']
},
endpoint: {
host: 'http://www.freecurrencyconverter3api.com',
path: '/api/v2/convert?compact=y&q='
}
});
grunt.registerMultiTask('currency', 'Fetches currency exchange rates',
function() {
var http = require('http'),
done = this.async(),
responses = 0;
var baseCurrency = this.target;
var targetCurrencies = this.data;
grunt.config.requires('endpoint');
targetCurrencies.forEach(function(targetCurrency, i, arr){
var convertTo = baseCurrency + '_' + targetCurrency,
body = [];
url = grunt.config.get('endpoint.host');
url += grunt.config.get('endpoint.path') + convertTo;
http.get(url, function(res) {
res.on('data', function(data){
body.push(data);
});
res.on('end', function () {
var conversion = JSON.parse(body.join());
grunt.log.ok(baseCurrency + '/' + targetCurrency + ' => ' + c
onversion[convertTo].val);
// if got all responses: done!
if(responses++ == arr.length - 1)
done();
});
}).on('error', function (err) {
grunt.warn('Please verify endpoint host and path: <'+ url +'>.
It might be incorrect or down.');
done(err);
});
});
});
}
Grunt Errors and Warnings grunt.fail.warn(error [, errorcode]): prints to STDOUT a message and abort grunt
executions. It can be overridden using --force and it can show the stack trace if --
stack is given. E.g. grunt taskName --force --stack.
grunt.fail.fatal(error [, errorcode]): similar to warn, displays message to STDOUT
and terminate Grunt. Cannot be --forced and it emits a beep unless --no-color
parameter is passed. It also accepts --stack. E.g. grunt taskName --no-color --
stack.
Grunt Files and logs Grunt logs
All of them starts with the prefix grunt.log and accepts msg which is displayed to
STDOUT (usually the screen). Here are the differences between them:
writeln([msg]), write(msg) and subhead(msg): writes message to STDOUT.
grunt.log.writeln will do the same as grunt.log.write but without trailing newline.
subhead(msg) will print the message in bold and proceeded by a newline and a
trailing newline as well.
The following methods add a “>>” before the message in the screen which could be of
different colors depending on the method:
grunt.log.error([msg]): print message prefixed with a RED “>>”.
grunt.log.ok([msg]): print message prefixed with a GREEN “>>”.
Grunt files
All has optional attributes options that could be encoding among others.
grunt.file.write(filepath, contents [, options]): writes contents to file, creates path if
necessary.
grunt.file.read(filepath [, options]): returns file content.
grunt.file.readJSON(filepath [, options]): reads file content and parse it to JSON.
grunt.file.delete(filepath [, options]): deletes files recursively.
grunt.file.copy(srcpath, destpath [, options]): copy file from srcpath to destpath.
Directories
grunt.file.mkdir(dirpath [, mode]): creates directory and any intermediary. Like
mkdir -p.
grunt.file.expand([options, ] patterns): returns an array with all the files matching
a pattern. It can also accept and array of patterns. Preceding a patter with ! will
negate them. E.g. ['**/*.js', !**/*spec.js] => get all javascript (including
subdirectories) but NOT the ones that ends with spec.js.
grunt.file.recurse(rootdir, callback): expand path and return a callback function
with the following signature callback(abspath, rootdir, subdir, filename).
Example: Gruntfile for files manipulation
GruntJS comes with built-in functions for basic file system handling. To see the
function in action: Create three directories: stylesheets, javascripts, templates and put
files on first three. The idea is to concatenate all the files into one index.html and
placed it a newly created public folder.
Here‟s the grunt file that will copy and concatenate all the files for us:
module.exports = function(grunt){
grunt.config.init({
concat: {
options: {
dest: 'tmp',
templates: ['templates/header.html', 'templates/footer.html'],
javascripts: ['javascripts/*.js'],
stylesheets: ['stylesheets']
}
}
});
var recursiveConcat = function(source, result){
grunt.file.expand(source).forEach(function(file){
if(grunt.file.isDir(file)){
grunt.file.recurse(file, function(f){
result = recursiveConcat(f, result);
});
} else {
grunt.log.writeln('Concatenating ' + file + ' to other ' + resu
lt.length + ' characters.');
result += grunt.file.read(file);
}
});
return result;
};
grunt.registerTask('concat', 'concatenates files', function(type){
grunt.config.requires('concat.options.' + type); // fail the task i
f this propary is missing.
grunt.config.requires('concat.options.dest');
var files = grunt.config.get('concat.options.' + type),
dest = grunt.config.get('concat.options.dest'),
concatenated = recursiveConcat(files, '');
grunt.log.writeln('Writing ' + concatenated.length + ' chars to ' +
'tmp/' + type);
grunt.file.write(dest + '/' + type, concatenated);
});
grunt.registerTask('concatAll', ['concat:templates', 'concat:javascri
pts', 'concat:stylesheets']);
grunt.registerTask('default', ['concatAll']);
}
Inside Grunt tasks Inside all Grunt task there are number of functions available through this:
this.async: designed for async tasks. Grunt will normally end the task without
waiting for the callback to be executed. If you need Grunt to wait use done().
var done = this.async();
http.get('http://adrianmejia.com', function(res){
res.on('data', function(data){
// ... process data ...
done(); // forces Grunt to wait until data is received.
})
}).on(function(err){
done(err); // or an error is received.
});
this.requires: list of taskNames that should executed successfully first. E.g.
this.requires(['concat', 'jshint']).
this.name: this is the name of the task. E.g. grunt hello, then this.name ===
'name'.
this.args: returns an array with the parameters. E.g. grunt hello:crazy:world, then
this.args will return ['crazy', 'world'].
this.options([defaultsObj]): it gets options values from the config.init, optionally
you can also pass an object containing the default values. Notice in the example
bellow that even though console.log has a this.options({gzip: true}) it gets
override by the options parameters. If not one it is specified in the config.init then
it will use the default gzip: true.
Inside MultiTasks Consider this grunt.config.init example:
module.exports = function(grunt){
grunt.config.init({
multiTaskName: {
options: {
gzip: false
},
target1: {
src: 'stylesheets/*.css',
dest: 'public',
ext: '.min.css'
},
target2: {
src: '*.js',
dest: 'public',
ext: '.min.js'
}
}
});
grunt.registerMultiTask('multiTaskName', 'example', function(){
console.log('this.options', this.options({gzip: true}));
console.log('this.data', this.data);
console.log('this.files', this.files);
console.log('this.filesSrc', this.filesSrc);
});
}
The output will be as follows:
grunt multiTaskName
# Running "multiTaskName:target1" (multiTaskName) task
# this.options { gzip: false }
# this.data { src: 'stylesheets/*.css', dest: 'public', ext: '.min.css'
}
# this.files [ { src: [Getter],
# dest: 'public',
# ext: '.min.css',
# orig: { src: [Object], dest: 'public', ext: '.min.css' } } ]
# this.filesSrc [ 'stylesheets/h1.css', 'stylesheets/h2.css' ]
#
# Running "multiTaskName:target2" (multiTaskName) task
# this.options { gzip: false }
# this.data { src: '*.js', dest: 'public', ext: '.min.js' }
# this.files [ { src: [Getter],
# dest: 'public',
# ext: '.min.js',
# orig: { src: [Object], dest: 'public', ext: '.min.js' } } ]
# this.filesSrc [ 'Gruntfile.js' ]
this.target: name of the target current target. If you call it grunt multiTaskName, it
will run like multiple tasks calling each target one at a time. this.target will be
equal to target1 and then target2.
this.files: return a (single) array that has all the properties for the current target.
Take a look the the output above.
this.filesSrc: it expands files and paths against src and return an array with them.
this.data: contains the raw data of the target parameters.
Using Grunt.js plugins Chances are that there is a plugin for most of your needs.
Installing a grunt plugin
Let‟s say we want to install jshint.
Get the plugin module
Download it from npm:
npm install grunt-contrib-jshint --save-dev
or from github:
npm install https://github.com/YOUR_USERNAME/grunt-contrib-YOUR-PLUGIN
--save-dev
Load it in your Gruntfile
grunt.loadNpmTasks('grunt-contrib-jshint');
or
grunt.loadNpmTasks('grunt-contrib-YOUR-PLUGIN');
Splitting Grunt configuration into
different files Let‟s break up your Gruntfile config by task. Moreover, it encapsulates load-grunt-tasks
and its functionality!
Our directory structure should look like this:
- myproject/
-- Gruntfile.js
-- grunt/
--- concat.js
--- uglify.js
--- imagemin.js
With load-grunt-config, your Gruntfile.js will look like this:
module.exports = function(grunt) {
require('load-grunt-config')(grunt);
};
Let‟s now put the task configuration of each of our tasks directly into the respective files
grunt/concat.js
module.exports = {
dist: {
src: ['src/js/jquery.js', 'src/js/intro.js', 'src/js/main.js', 'src
/js/outro.js'],
dest: 'dist/build.js',
}
};
grunt/uglify.js
module.exports = {
dist: {
files: {
'dist/build.min.js': ['dist/build.js']
}
}
};
grunt/imagemin.js
module.exports = {
options: {
cache: false
},
dist: {
files: [{
expand: true,
cwd: 'src/',
src: ['**/*.{png,jpg,gif}'],
dest: 'dist/'
}]
}
};
And that‟s it! Execute the following command in your terminal:
$ grunt
Walk through over a Project
Skeleton To demonstrate the power of Grunt, we‟re going to first need a project we can use it on.
For now, we‟ll create a very simple one: an index.html, and a lib/script.js file that logs
things to the console. Our first task will be to get JSHint up and running with Grunt on
this skeleton, before we branch out into getting Grunt to run our tests and the like.
Create a new directory, and add an index.html with this:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Grunt Test!</title>
</head>
<body>
<p>Check your console.</p>
<script src="lib/script.js"></script>
</body>
Then create a directory inside that, called lib, and add a script.js there with this:
'use strict';
console.log('Hello, world!')
Adding Grunt to our Project
Grunt, like most JavaScript these days, works through npm. In turn, npm
interfaces with your project via a special package.json file. So we need to create
one! There are a few ways to do this, but since we just want to get started, let‟s
skip to the middle: copy this package.json into your project directory from before.
{
"name": "grunt-demo",
"description": "A demo web development project using Grunt",
"version": "1.0.0",
"devDependencies": {
"grunt": "~0.4.2",
"grunt-contrib-jshint": "~0.7.2"
}
}
The important part here is our devDependencies (“development dependencies”). To
start off, we‟ll be using Grunt itself, and the grunt-contrib-jshint plugin. (Later, we‟ll add
more Grunt plugins, but for now, let‟s just try JSHint.) All of these things are distributed
by npm, which understands the package.json file. Speaking of which, let‟s tell npm to
actually install these things now. Open your command prompt in the same directory as
the package.json and type:
npm install
This will generate lots of log messages, but in the end you should get a nice tree telling
you what was installed. All those files go into the newly-created node_modules directory
inside your project.
Creating a Grunt File
Each project that uses Grunt has a Gruntfile specifying the plugins you‟ll use, their
configuration, and how they compose to create tasks. To get started, we‟ll create a
pretty basic Gruntfile. Copy these lines into a Gruntfile.js inside your project directory:
'use strict';
module.exports = function (grunt) {
grunt.loadNpmTasks('grunt-contrib-jshint'); // (1)
grunt.initConfig({
jshint: { // (2)
files: ['lib/**/*.js'],
options: {
devel: true,
globalstrict: true
}
}
});
grunt.registerTask('validate', ['jshint']); // (3)
};
A few things are going on here:
At (1), we load the grunt-contrib-jshint plugin into Grunt.
At (2), we configure the options for the jshint task, which was supplied by the grunt-
contrib-jshint plugin. The majority of Grunt tasks have a files option of some sort, and
grunt-contrib-jshint is no exception. It has other options too, which you can see in its
documentation grunt-contrib-jshint options. I usually use the jshintrc option so I can
keep my JSHint settings in the usual .jshintrc file, but for now we‟ll just set the options
inline.
At (3), we create a new task, called validate, which currently just runs the jshint task. In
the future, we‟ll put our test-running task here too.
Running Your First Grunt Task
OK, let‟s first make sure you‟ve got everything set up right. You should have a setup like
this:
┬ lib
│ └── script.js
├─┬ node_modules
│ ├── grunt
│ └── grunt-contrib-jshint
├── Gruntfile.js
├── index.html
└── package.json
If that looks right, then it‟s time to give Grunt a try! Run the command grunt validate to
run the validate task we set up earlier. You should get something like:
$ grunt validate
Running "jshint:files" (jshint) task
>> 1 file lint free.
Done, without errors.
You can test that it is indeed linting your files by, for example, removing the semicolon
from the end of the console.log line in lib/script.js. Then you‟ll get:
$ grunt validate
Running "jshint:files" (jshint) task
Linting lib/script.js ...ERROR
[L3:C29] W033: Missing semicolon.
console.log('Hello, world!')
Warning: Task "jshint:files" failed. Use --force to continue.
Aborted due to warnings.
That‟s it we have done our first exercise on integration of GruntJS.
The power of Grunt comes from leveraging multiple plugins in your project, and
combining them into different tasks like validate versus deploy, or by composing
plugins like grunt-contrib-watch to run tasks automatically in the background, or grunt-
concurrent to run multiple tasks at the same time and speed up the process.
10 most popular grunt plugins 1- jshint: Validate files with JSHint. Uses .jshintrc to settings.
{
"curly": true,
"eqnull": true,
"eqeqeq": true,
"undef": true,
"globals": {
"jQuery": true
}
}
2- watch: Run predefined tasks whenever watched file patterns are added, changed
or deleted. Spawn runs task in a child process but having set to spawn: false is
faster.
watch: {
scripts: {
files: ['**/*.js'],
tasks: ['jshint'],
options: {
spawn: false,
},
},
}
3- uglify: minifies javascript files.
uglify: {
my_target: {
files: {
'dest/output.min.js': ['src/input1.js', 'src/input2.js']
}
}
}
4- clean: Clean files and folders.
clean: {
// Deletes all .js files, but skips min.js files
js: ["path/to/dir/*.js", "!path/to/dir/*.min.js"]
// delete all files and directories here
build: ["path/to/dir/one", "path/to/dir/two"],
}
5- concat: Concatenate files.
concat: {
options: {
separator: ';',
},
dist: {
src: ['src/intro.js', 'src/project.js', 'src/outro.js'],
dest: 'dist/built.js',
},
}
o adding banners and multiple targets
concat: {
options: {
stripBanners: true,
banner: '/*! <%= pkg.name %> - v<%= pkg.version %> - ' +
'<%= grunt.template.today("yyyy-mm-dd") %> */',
},
dist: {
'dist/with_extras.js': ['src/main.js', 'src/extras.js'],
},
},
6- cssmin: Compress CSS files.
cssmin: {
combine: {
files: {
'path/to/output.css': ['path/to/input_one.css', 'path/to/input_tw
o.css']
}
}
}
o with banner and adding .min.css extension
cssmin: {
add_banner: {
options: {
banner: '/* My minified css file */'
},
files: [{
expand: true,
cwd: 'release/css/',
src: ['*.css', '!*.min.css'],
dest: 'release/css/',
ext: '.min.css'
}]
}
}
7- connect: runs server as long as Grunt is running. It can be persistent passing
keepalive like this grunt connect:keepalive.
connect: {
server: {
options: {
port: 9001,
base: 'www-root'
}
}
}
8- karma: runs karma testing tool.
karma: {
unit: {
options: {
files: ['test/**/*.js']
}
}
}
o overriding parameters
karma: {
unit: {
configFile: 'karma.conf.js',
runnerPort: 9999,
singleRun: true,
browsers: ['PhantomJS'],
logLevel: 'ERROR'
}
}
9- less: Compile LESS files to CSS.
less: {
development: {
options: {
paths: ["assets/css"]
},
files: {
"path/to/result.css": "path/to/source.less"
}
},
production: {
options: {
paths: ["assets/css"],
cleancss: true,
modifyVars: {
imgPath: '"http://mycdn.com/path/to/images"',
bgColor: 'red'
}
},
files: {
"path/to/result.css": "path/to/source.less"
}
}
}
10- concurrent: Run grunt tasks concurrently.
concurrent: {
target1: ['coffee', 'sass'],
target2: ['jshint', 'mocha'],
target3: {
tasks: ['nodemon', 'watch'],
options: {
logConcurrentOutput: true
}
}
}
Project Scaffolding grunt-init
Grunt-init is a scaffolding tool used to automate project creation. It will build an entire
directory structure based on current environment and answers to some questions. The
exact files and contents created depend on the template chosen along with the answers
to the questions asked.
In order to use grunt-init, you'll want to install it globally.
npm install -g grunt-init
This will put the grunt-init command in your system path, allowing it to be run from
anywhere.
Installing templates
Once templates are installed into your ~/.grunt-init/ directory (%USERPROFILE%\.grunt-
init\ on Windows) they will be available for use via grunt-init. It's recommended that you
use git to clone a template into that directory. For example, the grunt-init-jquery
template can be installed like so:
git clone https://github.com/gruntjs/grunt-init-jquery.git ~/.grunt-init/jque
ry
A few grunt-init templates are maintained officially:
grunt-init-commonjs - Create a commonjs module, including Nodeunit unit tests.
(sample "generated" repo | creation transcript)
grunt-init-gruntfile - Create a basic Gruntfile. (sample "generated" repo | creation
transcript)
grunt-init-gruntplugin - Create a Grunt plugin, including Nodeunit unit tests.
(sample "generated" repo | creation transcript)
grunt-init-jquery - Create a jQuery plugin, including QUnit unit tests. (sample
"generated" repo | creation transcript)
grunt-init-node - Create a Node.js module, including Nodeunit unit tests. (sample
"generated" repo | creation transcript)
Custom templates
You can create and use custom templates. Your template must follow the same
structure as the aforementioned templates.
A sample template named my-template would follow this general file structure:
my-template/template.js - the main template file.
my-template/rename.json - template-specific rename rules, processed as
templates.
my-template/root/ - files to be copied into the target location.
Assuming these files exist at /path/to/my-template, the command grunt-init
/path/to/my-template would be used to process the template. Multiple uniquely-named
templates may exist in the same directory.
Additionally, if you place this custom template in your ~/.grunt-init/ directory
(%USERPROFILE%\.grunt-init\ on Windows) it will be automatically available to be used
with just grunt-init my-template.
Copying files
As long as a template uses the init.filesToCopy and init.copyAndProcess methods, any
files in the root/ subdirectory will be copied to the current directory when the init
template is run.
Note that all copied files will be processed as templates, with any {% %} template being
processed against the collected props data object, unless the noProcess option is set.
See the jquery template for an example.
Renaming or excluding template files
The rename.json describes sourcepath to destpath rename mappings. The sourcepath
must be the path of the file-to-be-copied relative to the root/ folder, but the destpath
value can contain {% %} templates, describing what the destination path will be.
If false is specified as a destpath the file will not be copied. Also, glob patterns are
supported for srcpath.
Specifying default prompt answers
Each init prompt either has a default value hard-coded or it looks at the current
environment to attempt to determine that default value. If you want to override a
particular prompt's default value, you can do so in the optional OS X or Linux ~/.grunt-
init/defaults.json or Windows %USERPROFILE%\.grunt-init\defaults.json file.
For example, my defaults.json file looks like this, because I want to use a slightly
different name than the default name, I want to exclude my email address, and I want to
specify an author url automatically.
{
"author_name": "\"Cowboy\" Ben Alman",
"author_email": "none",
"author_url": "http://benalman.com/"
}
Defining an init template
exports.description
This brief template description will be displayed along with the template name when the
user runsgrunt init or grunt-init to display a list of all available init templates.
exports.description = descriptionString;
exports.notes
If specified, this optional extended description will be displayed before any prompts are
displayed. This is a good place to give the user a little help explaining naming
conventions, which prompts may be required or optional, etc.
exports.notes = notesString;
exports.warnOn
If this optional (but recommended) wildcard pattern or array of wildcard patterns is
matched, Grunt will abort with a warning that the user can override with --force. This is
very useful in cases where the init template could potentially override existing files.
exports.warnOn = wildcardPattern;
While the most common value will be '*', matching any file or directory,
the minimatch wildcard pattern syntax used allows for a lot of flexibility. For example:
exports.warnOn = 'Gruntfile.js'; // Warn on a Gruntfile.js file.
exports.warnOn = '*.js'; // Warn on any .js file.
exports.warnOn = '*'; // Warn on any non-dotfile or non-dotdir.
exports.warnOn = '.*'; // Warn on any dotfile or dotdir.
exports.warnOn = '{.*,*}'; // Warn on any file or dir (dot or non-do
t).
exports.warnOn = '!*/**'; // Warn on any file (ignoring dirs).
exports.warnOn = '*.{png,gif,jpg}'; // Warn on any image file.
// This is another way of writing the last example.
exports.warnOn = ['*.png', '*.gif', '*.jpg'];
exports.template
While the exports properties are defined outside this function, all the actual init code is
specified inside. Three arguments are passed into this function. The grunt argument is a
reference to grunt, containing all the grunt methods and libs. The init argument is an object
containing methods and properties specific to this init template. The done argument is a
function that must be called when the init template is done executing.
exports.template = function(grunt, init, done) {
// See the "Inside an init template" section.
};
Inside an init template
init.addLicenseFiles
Add properly-named license files to the files object.
var files = {};
var licenses = ['MIT'];
init.addLicenseFiles(files, licenses);
// files === {'LICENSE-MIT': 'licenses/LICENSE-MIT'}
init.availableLicenses
Return an array of available licenses.
var licenses = init.availableLicenses();
// licenses === [ 'Apache-2.0', 'GPL-2.0', 'MIT', 'MPL-2.0' ]
init.copy
Given an absolute or relative source path, and an optional relative destination path,
copy a file, optionally processing it through the passed callback.
init.copy(srcpath[, destpath], options)
init.copyAndProcess
Iterate over all files in the passed object, copying the source file to the destination,
processing the contents.
init.copyAndProcess(files, props[, options])
init.defaults
User-specified default init values from defaults.json.
init.defaults
init.destpath
Absolute destination file path.
init.destpath()
init.expand
Same as grunt.file.expand.
Return a unique array of all file or directory paths that match the given wildcard
pattern(s). This method accepts either comma separated wildcard patterns or an array of
wildcard patterns. Paths matching patterns that begin with ! will be excluded from the
returned array. Patterns are processed in order, so inclusion and exclusion order is
significant.
init.expand([options, ] patterns)
init.filesToCopy
Return an object containing files to copy with their absolute source path and relative
destination path, renamed (or omitted) according to rules in rename.json (if it exists).
var files = init.filesToCopy(props);
/* files === { '.gitignore': 'template/root/.gitignore',
'.jshintrc': 'template/root/.jshintrc',
'Gruntfile.js': 'template/root/Gruntfile.js',
'README.md': 'template/root/README.md',
'test/test_test.js': 'template/root/test/name_test.js' } */
init.getFile
Get a single task file path.
init.getFile(filepath[, ...])
init.getTemplates
Returns an object of all the available templates.
init.getTemplates()
init.initSearchDirs
Initialize the directories to search for init templates. template is the location of a
template. Will also include ~/.grunt-init/ and the core init tasks within grunt-init.
init.initSearchDirs([filename])
init.process
Start up the process to begin prompting for input.
init.process(options, prompts, done)
init.process({}, [
// Prompt for these values
init.prompt('name'),
init.prompt('description'),
init.prompt('version')
], function(err, props) {
// All finished, do something with the properties
});
init.prompt
Prompt a user for a value.
init.prompt(name[, default])
init.prompts
An object of all the prompts.
var prompts = init.prompts;
init.readDefaults
Read JSON defaults from task files (if they exist), merging them into one data object.
init.readDefaults(filepath[, ...])
init.renames
The rename rules for the template.
var renames = init.renames;
// renames === { 'test/name_test.js': 'test/{%= name %}_test.js' }
init.searchDirs
An array of directories to search for templates in.
var dirs = init.searchDirs;
/* dirs === [ '/Users/shama/.grunt-init',
'/usr/local/lib/node_modules/grunt-init/templates' ] */
init.srcpath
Search init template paths for filename and return an absolute path.
init.srcpath(filepath[, ...])
init.userDir
Returns the absolute path to the user's template directory.
var dir = init.userDir();
// dir === '/Users/shama/.grunt-init'
init.writePackageJSON
Save a package.json file in the destination directory. The callback can be used to post-
process properties to add/remove/whatever.
init.writePackageJSON(filename, props[, callback])
Built-in prompts
author_email
Author's email address to use in the package.json. Will attempt to find a default value
from the user's git config.
author_name
Author's full name to use in the package.json and copyright notices. Will attempt to find
a default value from the user's git config.
author_url
A public URL to the author's website to use in the package.json.
bin
A relative path from the project root for a cli script.
bugs
A public URL to the project's issues tracker. Will default to the github issue tracker if the
project has a github repository.
description
A description of the project. Used in the package.json and README files.
grunt_version
A valid semantic version range descriptor of Grunt the project requires.
homepage
A public URL to the project's home page. Will default to the github url if a github
repository.
jquery_version
If a jQuery project, the version of jQuery the project requires. Must be a valid semantic
version range descriptor.
licenses
The license(s) for the project. Multiple licenses are separated by spaces. The licenses
built-in are: MIT,MPL-2.0, GPL-2.0, and Apache-2.0. Defaults to MIT. Add custom licenses
with init.addLicenseFiles.
main
The primary entry point of the project. Defaults to the project name within the lib folder.
name
The name of the project. Will be used heavily throughout the project template. Defaults
to the current working directory.
node_version
The version of Node.js the project requires. Must be a valid semantic version range
descriptor.
npm_test
The command to run tests on your project. Defaults to grunt.
repository
Project's git repository. Defaults to a guess of a github url.
title
A human readable project name. Defaults to the actual project name altered to be more
human readable.
version
The version of the project. Defaults to the first valid semantic version, 0.1.0.
Summarizing Grunt Front-end developers are often told to do certain things:
Work in as small chunks of CSS and JavaScript as makes sense to you, then
concatenate them together for the production website.
Compress your CSS and minify your JavaScript to make their file sizes as small as
possible for your production website.
Optimize your images to reduce their file size without affecting quality.
Use Sass for CSS authoring because of all the useful abstraction it allows.
That‟s not a comprehensive list of course, but those are the kind of things we need to
do. You might call them tasks.
Grunt can do all of those things for you. Once you‟ve got it set up, which isn‟t
particularly difficult, those things can happen automatically without you having to think
about them again.