Upload
brian-hogan
View
103
Download
1
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
Brian P. Hogan twitter: @bphoganwww.bphogan.com
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!
Brian P. Hogan twitter: @bphoganwww.bphogan.com
Why are you here?
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.
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.
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. !
Brian P. Hogan twitter: @bphoganwww.bphogan.com
Initial Setup
$ npm install grunt-‐cli
$ cd yourwebsite
$ npm init
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.
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.
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.”
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.
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.
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.
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.
Brian P. Hogan twitter: @bphoganwww.bphogan.com
“Hello world”?
grunt.registerTask('hello', function(){ console.log("Hello World"); });
There’s a simple task. Works great!!
Brian P. Hogan twitter: @bphoganwww.bphogan.com
Brian P. Hogan twitter: @bphoganwww.bphogan.com
Arguments
grunt.registerTask('greet', function(name){ grunt.log.writeln("Hello " + name); });
Code originally shown live during talk.
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.
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.
Brian P. Hogan twitter: @bphoganwww.bphogan.com
Loading External Tasks
grunt.loadTasks('tasks');
Code originally shown live during talk.
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.
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`.
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. !
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!
Brian P. Hogan twitter: @bphoganwww.bphogan.com
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.
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.
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.
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.
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.
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.
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!
Brian P. Hogan twitter: @bphoganwww.bphogan.com
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.
Brian P. Hogan twitter: @bphoganwww.bphogan.com
Our plan
sass sass/style.sass stylesheets/style.sass
cssmin stylesheets/style.sass stylesheets/style.sass
Brian P. Hogan twitter: @bphoganwww.bphogan.com
Using A Grunt Plugin
• $ npm install grunt-contrib-whatever
• grunt.loadNpmTasks(‘whatever');
• Add configuration.
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.
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.
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.
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.
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.
Brian P. Hogan twitter: @bphoganwww.bphogan.com
Watch grunt.loadNpmTasks('grunt-contrib-watch'); grunt.config('watch', { styles: { files: ['sass/**/*.scss'], tasks: ['sass'] } });
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'] } });
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.
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.
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.
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.
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.
Brian P. Hogan twitter: @bphoganwww.bphogan.com
Questions?Twitter: @bphogan