51
Brian P. Hogan twitter: @bphogan www.bphogan.com grunt from the ground up

Grunt From The Ground Up

Embed Size (px)

DESCRIPTION

Modern web development requires managing CSS, JavaScript, HTML, and other assets, and things can get out of hand quickly. Grunt has become the standard for managing all of the tasks related to modern development, from concatenating files to minifying files for production. Unfortunately, most documentation on the Web focuses on how to cut and paste configuration files together. That’s not very helpful. In this talk you'll learn how to use Grunt for a variety of purposes as we explore how it all works. We’ll cover how to develop Grunt tasks, how to work with files, how Multitasks work, and how to use Grunt and its plugin system to manage the development of a single page app that uses CoffeeScript, Sass, and Angular. When we’re done you’ll know exactly how Grunt works so you can use it on your own projects right away.

Citation preview

Page 1: Grunt From The Ground Up

Brian P. Hogan twitter: @bphoganwww.bphogan.com

grunt from the ground up

Page 2: Grunt From The Ground Up

Brian P. Hogan twitter: @bphoganwww.bphogan.com

About me

• I build things

• I write books

• I teach people

• I love code!

Page 3: Grunt From The Ground Up

Brian P. Hogan twitter: @bphoganwww.bphogan.com

Why are you here?

Page 4: Grunt From The Ground Up

Brian P. Hogan twitter: @bphoganwww.bphogan.com

What is Grunt?

Grunt is a task runner written in JavaScript. It has advantages and disadvantages to other build tools.

Page 5: Grunt From The Ground Up

Brian P. Hogan twitter: @bphoganwww.bphogan.com

However, one of the worst ways to use Grunt is to just copy and paste people’s examples that you find online. There are lots of tutorials out there to follow, but before you start copying configurations around, you should learn how Grunt handles things under the hood.

Page 6: Grunt From The Ground Up

Brian P. Hogan twitter: @bphoganwww.bphogan.com

Prerequisites

SO to do that let’s work with Grunt from the ground up. In order to do this you’ll need two things: You’ll need NodeJS installed and you’ll need to be comfortable with the shell. The stuff we’ll do here today is code that works on my Mac. But it’ll work everywhere. !

Page 7: Grunt From The Ground Up

Brian P. Hogan twitter: @bphoganwww.bphogan.com

Initial Setup

$  npm  install  grunt-­‐cli

$  cd  yourwebsite

$  npm  init

Page 8: Grunt From The Ground Up

Brian P. Hogan twitter: @bphoganwww.bphogan.com

The nom init command brings you through a wizard that asks a few questions about your project. The answers to these questions can be used by Grunt or other apps later. For example, Grunt can make your application name available as a variable when it writes files. You could also use this file as the source of your app’s version number.

Page 9: Grunt From The Ground Up

Brian P. Hogan twitter: @bphoganwww.bphogan.com

package.json{ "name": "awesomeco", "version": "0.0.1", "description": "This is my basic site", "main": "index.html", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "Brian Hogan", "license": "MIT" }

Here’s how it works. That command creates the file package.json which contains all the things we specified. And what’s really important about this is that this file can also list the dependencies of our app.

Page 10: Grunt From The Ground Up

Brian P. Hogan twitter: @bphoganwww.bphogan.com

So if we try to run the `grunt` command we get told that it can’t find “local grunt.”

Page 11: Grunt From The Ground Up

Brian P. Hogan twitter: @bphoganwww.bphogan.com

Installing Grunt to a project

$  npm  install  grunt  -­‐-­‐save-­‐dev

The grunt-cli command we installed earlier lets us use the grunt command. But that doesn’t actually install Grunt. It just installs a command line tool that is designed to talk to the version of Grunt that is installed as an app dependency.

Page 12: Grunt From The Ground Up

Brian P. Hogan twitter: @bphoganwww.bphogan.com

And so we install Grunt to our project and then when we try to run Grunt again it says we need a Gruntfile.

Page 13: Grunt From The Ground Up

Brian P. Hogan twitter: @bphoganwww.bphogan.com

Gruntfile.js'use strict' module.exports = function(grunt){ };

The Gruntfile is how we configure our tasks. Our Gruntfile looks like this. !The expression ‘use strict’ tells the JavaScript interpreter to be more strict on how it processes rules, raises errors, etc.

Page 14: Grunt From The Ground Up

Brian P. Hogan twitter: @bphoganwww.bphogan.com

A task'use strict'; module.exports = function(grunt){ grunt.registerTask("hello", function(){ // your code here }); };

This is a task declaration. We “register the task” by giving it a name and a callback function that gets run. It’s like an event listener.

Page 15: Grunt From The Ground Up

Brian P. Hogan twitter: @bphoganwww.bphogan.com

“Hello world”?

grunt.registerTask('hello', function(){ console.log("Hello World"); });

There’s a simple task. Works great!!

Page 16: Grunt From The Ground Up

Brian P. Hogan twitter: @bphoganwww.bphogan.com

Page 17: Grunt From The Ground Up

Brian P. Hogan twitter: @bphoganwww.bphogan.com

Demo

Live demo

Page 18: Grunt From The Ground Up

Brian P. Hogan twitter: @bphoganwww.bphogan.com

Arguments

grunt.registerTask('greet', function(name){ grunt.log.writeln("Hello " + name); });

Code originally shown live during talk.

Page 19: Grunt From The Ground Up

Brian P. Hogan twitter: @bphoganwww.bphogan.com

Errors? grunt.registerTask('add', function(firstNumber, secondNumber){ firstNumber = Number(firstNumber); secondNumber = Number(secondNumber); if(isNaN(firstNumber)) grunt.warn("Nope! Why do you fail?????"); if(isNaN(secondNumber)) grunt.warn("Nope! Why do you fail?????"); var answer = firstNumber + secondNumber; grunt.log.writeln(answer); });

Code originally shown live during talk.

Page 20: Grunt From The Ground Up

Brian P. Hogan twitter: @bphoganwww.bphogan.com

External'use strict'; module.exports = function(grunt){ grunt.registerTask('praise', function(){ var praise = [ "You're awesome!", "You are the best developer ever!", "You deserve a raise!", "You are good looking!", "Everyone likes you!" ]; grunt.log.writeln(praise[Math.floor(Math.random() * praise.length)]); }); };

Code originally shown live during talk.

Page 21: Grunt From The Ground Up

Brian P. Hogan twitter: @bphoganwww.bphogan.com

Loading External Tasks

grunt.loadTasks('tasks');

Code originally shown live during talk.

Page 22: Grunt From The Ground Up

Brian P. Hogan twitter: @bphoganwww.bphogan.com

Chaining

grunt.registerTask('default', ['praise', 'hello', 'greet:Brian']); grunt.registerTask('build', ['praise', 'hello']);

Code originally shown live during talk.

Page 23: Grunt From The Ground Up

Brian P. Hogan twitter: @bphoganwww.bphogan.com

Simple tasks

• grunt.registerTask

• arguments

• external tasks (grunt.loadTasks)

• Task chaining

• default tasks

So, we have the grunt.registerTask command to define a tasks. We can also define tasks that take arguments. We can have tasks defined outside of the Gruntfile and load those in, and we can define a task that calls other tasks. We can even have a default task that gets called when we type `grunt`.

Page 24: Grunt From The Ground Up

Brian P. Hogan twitter: @bphoganwww.bphogan.com

Configuration

grunt.initConfig({ mainFile: 'index.html' });

Grunt tasks are better when they’re configured. We can set up a configuration object and add some stuff to it. !

Page 25: Grunt From The Ground Up

Brian P. Hogan twitter: @bphoganwww.bphogan.com

grunt.config.get

grunt.registerTask('printFile', function(){ var file = grunt.config.get('mainFile'); console.log(file); });

Then we use grunt.config.get to grab the value out!

Page 26: Grunt From The Ground Up

Brian P. Hogan twitter: @bphoganwww.bphogan.com

Page 27: Grunt From The Ground Up

Brian P. Hogan twitter: @bphoganwww.bphogan.com

Config Demo

grunt open:index.html

Chrome Firefox Safari

Let’s use configuration to create a task that opens a page in all the browsers We’ll create a configuration for this and then write a task that launches the browsers that we configure.

Page 28: Grunt From The Ground Up

Brian P. Hogan twitter: @bphoganwww.bphogan.com

Config

grunt.initConfig({ browsers: [ 'open -a Firefox', 'open -a Safari', 'open -a "Google Chrome"' ] });

Code originally shown live during talk.

Page 29: Grunt From The Ground Up

Brian P. Hogan twitter: @bphoganwww.bphogan.com

grunt.registerTask('open', function(file){ var browsers = grunt.config.get('browsers'); var exec = require('child_process').exec; for (var index = 0; index < browsers.length; index++) { // tell Grunt to wait var done = this.async(); var command = browsers[index] + ' ' + file; grunt.log.writeln("Running " + command); var process = exec(command, function (error, stdout, stderr) { if (error) { if(error.code !== 0){ grunt.warn(stderr); grunt.log.writeln(error.stack); } } // tell Grunt to get going again. done(); }); } });

Code originally shown live during talk.

Page 30: Grunt From The Ground Up

Brian P. Hogan twitter: @bphoganwww.bphogan.com

Multitasks

grunt build

basic version full version

Grunt’s multitasks let us build a single task that has many targets. Let’s say we’re doing our own JS framework. We want to build a basic version and a version that includes jQuery since our library depends on that. We’ll create two versions with one task.

Page 31: Grunt From The Ground Up

Brian P. Hogan twitter: @bphoganwww.bphogan.com

Our libraryaquery

lib

a.js

b.js

vendor

jquery.js

So we’ll organize our code like this: our javascript files get split up into their own little files. We’ll put them in the lib/ folder. Then we’ll put jQuery in the vendor folder.

Page 32: Grunt From The Ground Up

Brian P. Hogan twitter: @bphoganwww.bphogan.com

Config grunt.config.init({ build: { aquery: { src: ['lib/a.js', 'lib/b.js'], dest: 'dist/aquery.js' }, aqueryWithJquery: { src: ['lib/a.js', 'lib/b.js', 'vendor/jquery.js'], dest: 'dist/.aqueryFull.js' } } });

Multitasks can look at a task’s configuration for source and destination files. Here we’re specifying both versions.

Page 33: Grunt From The Ground Up

Brian P. Hogan twitter: @bphoganwww.bphogan.com

The Task grunt.registerMultiTask('build', 'Concatenate files.', function() { var output = ''; this.files.forEach(function(filegroup) { var sources = filegroup.src.map(function(file){ return(grunt.file.read(file)); }); output = sources.join(';'); grunt.file.write(filegroup.dest, output); }); });

Then we use grunt’s multitask declaration, give it the same name as the configuration. and then we can iterate over the files. Grunt has utilities we can use to open the files, read the contents, and write new files. It’s pretty cool!

Page 34: Grunt From The Ground Up

Brian P. Hogan twitter: @bphoganwww.bphogan.com

Page 35: Grunt From The Ground Up

Brian P. Hogan twitter: @bphoganwww.bphogan.com

Workflows with plugins!

One of the things that makes Grunt so attractive is its huge library of plugins.

Page 36: Grunt From The Ground Up

Brian P. Hogan twitter: @bphoganwww.bphogan.com

Our plan

sass sass/style.sass stylesheets/style.sass

cssmin stylesheets/style.sass stylesheets/style.sass

Page 37: Grunt From The Ground Up

Brian P. Hogan twitter: @bphoganwww.bphogan.com

Using A Grunt Plugin

• $ npm install grunt-contrib-whatever

• grunt.loadNpmTasks(‘whatever');

• Add configuration.

Page 38: Grunt From The Ground Up

Brian P. Hogan twitter: @bphoganwww.bphogan.com

Plugin Configgrunt.initConfig({ // other plugin configuration concat: { options: { separator: ';', }, dist: { src: ['src/intro.js', 'src/project.js', 'src/outro.js'], dest: 'dist/built.js', }, }, // other plugin configuration. });

Here’s an example of a plugin config. But see… the examples in all the docs just tell you to paste a bunch of config options right in the main configuration.

Page 39: Grunt From The Ground Up

Brian P. Hogan twitter: @bphoganwww.bphogan.com

grunt.config()grunt.config('concat', { options: { separator: ';', }, dist: { src: ['src/intro.js', 'src/project.js', 'src/outro.js'], dest: 'dist/built.js', }, });

But instead we can use grunt.config to put the code for each plugin in its own configuration block. This makes things much easier to maintain.

Page 40: Grunt From The Ground Up

Brian P. Hogan twitter: @bphoganwww.bphogan.com

Plugins we need

• grunt-contrib-sass

• grunt-contrib-cssmin

• grunt-contrib-watch

So here are the plugins we’ll configure.

Page 41: Grunt From The Ground Up

Brian P. Hogan twitter: @bphoganwww.bphogan.com

Demo!

Page 42: Grunt From The Ground Up

Brian P. Hogan twitter: @bphoganwww.bphogan.com

Sass grunt.loadNpmTasks('grunt-contrib-sass'); grunt.config('sass', { app: { files: { 'stylesheets/style.css' : ['sass/style.scss'] } } });

Code originally shown live during talk.

Page 43: Grunt From The Ground Up

Brian P. Hogan twitter: @bphoganwww.bphogan.com

Watch for changes!

sass stylesheets/style.sass

Changes to style.scss

We also want to watch for changes.

Page 44: Grunt From The Ground Up

Brian P. Hogan twitter: @bphoganwww.bphogan.com

Watch grunt.loadNpmTasks('grunt-contrib-watch'); grunt.config('watch', { styles: { files: ['sass/**/*.scss'], tasks: ['sass'] } });

Page 45: Grunt From The Ground Up

Brian P. Hogan twitter: @bphoganwww.bphogan.com

LiveReload grunt.loadNpmTasks('grunt-contrib-watch'); grunt.config('watch', { options: { livereload: true }, styles: { files: ['sass/**/*.scss'], tasks: ['sass'] } });

Page 46: Grunt From The Ground Up

Brian P. Hogan twitter: @bphoganwww.bphogan.com

Build

cssmin stylesheets/style.sass

build

sass

So then all we need to do is make a task that calls these parts. And you can do that with a task chain.

Page 47: Grunt From The Ground Up

Brian P. Hogan twitter: @bphoganwww.bphogan.com

Other Plugins?

• grunt-contrib-jshint

• grunt-contrib-less

• grunt-contrib-concat

• grunt-contrib-coffee

• grunt-contrib-uglify

There are tons of Grunt plugins out there for you to work with. The JSHint plugin will scan your code for syntax and formatting issues. The Less plugin will let you use Less, another CSS preprocesssor. Into CoffeeScript? use that! You can even make your JavaScript smaller using Uglify. Or you can write your own Grunt plugin.

Page 48: Grunt From The Ground Up

Brian P. Hogan twitter: @bphoganwww.bphogan.com

Grunt

• Simple tasks

• Multitasks

• Plugins

Grunt is the standard for JavaScript projects right now. Simple tasks are easy to define. Multitasks make it very easy to build a single task that builds multiple outputs, and plugins make Grunt incredibly flexible.

Page 49: Grunt From The Ground Up

Brian P. Hogan twitter: @bphoganwww.bphogan.com

Grunt is there for you, to be used to automate everything that you need. If you have to do some repetitive task, you owe it to yourself to automate everything.

Page 50: Grunt From The Ground Up

Brian P. Hogan twitter: @bphoganwww.bphogan.com

http://pragprog.com/book/bhgrunt/

More? Here’s a shameless plug. In a couple of weeks, this book will be available for you to own. And the two best questions will get a free ebook copy when it’s released.

Page 51: Grunt From The Ground Up

Brian P. Hogan twitter: @bphoganwww.bphogan.com

Questions?Twitter: @bphogan