+ All Categories
Home > Software > GruntJS

GruntJS

Date post: 11-Jul-2015
Category:
Upload: predhin-sapru
View: 1,129 times
Download: 2 times
Share this document with a friend
42
By, Predhin Tom Sapru The JavaScript Task Runner
Transcript
Page 1: GruntJS

By,

Predhin Tom Sapru

The JavaScript Task Runner

Page 2: GruntJS

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

Page 3: GruntJS

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.

Page 4: GruntJS

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.

Page 5: GruntJS

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.

Page 6: GruntJS

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

Page 7: GruntJS

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.

Page 8: GruntJS

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');

Page 9: GruntJS

});

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);

});

Page 10: GruntJS

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:

Page 11: GruntJS

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.

Page 12: GruntJS

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 = [];

Page 13: GruntJS

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);

});

});

});

}

Page 14: GruntJS

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.

Page 15: GruntJS

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']

Page 16: GruntJS

}

}

});

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);

Page 17: GruntJS

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'.

Page 18: GruntJS

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'

}

}

Page 19: GruntJS

});

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' } } ]

Page 20: GruntJS

# 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

Page 21: GruntJS

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',

Page 22: GruntJS

}

};

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:

Page 23: GruntJS

$ 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:

&lt;!DOCTYPE html&gt;

&lt;html&gt;

&lt;head&gt;

&lt;meta charset="utf-8" /&gt;

&lt;title&gt;Grunt Test!&lt;/title&gt;

&lt;/head&gt;

&lt;body&gt;

&lt;p&gt;Check your console.&lt;/p&gt;

&lt;script src="lib/script.js"&gt;&lt;/script&gt;

&lt;/body&gt;

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

Page 24: GruntJS

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) {

Page 25: GruntJS

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

Page 26: GruntJS

│ └── 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

&gt;&gt; 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.

Page 27: GruntJS

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: {

Page 28: GruntJS

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: {

Page 29: GruntJS

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'],

Page 30: GruntJS

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: {

Page 31: GruntJS

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: {

Page 32: GruntJS

"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.

Page 33: GruntJS

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.

Page 34: GruntJS

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",

Page 35: GruntJS

"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).

Page 36: GruntJS

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

Page 37: GruntJS

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);

Page 38: GruntJS

/* 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

Page 39: GruntJS

});

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

Page 40: GruntJS

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

Page 41: GruntJS

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

Page 42: GruntJS

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.


Recommended