Permalink
Fetching contributors…
Cannot retrieve contributors at this time
429 lines (295 sloc) 10.2 KB

Grunt

The JavaScript Task Runner

Dr. Gleb Bahmutov, PhD

@bahmutov

Front end build steps

  • code needs to be tested
  • js/css files need to be
    • bundled up
    • minified
  • templates need to be compiled
  • images need adjustment
  • versions need to be updated

Tasks

Think of each build step as a task

Shell scripts: STOP FOR THE LOVE OF GOD

  • portability
  • power

Make

Oldest (?) technology still in use

all: dist/app.js dist/index.html

dist/app.js: tmp/index.js libs/jquery.js
  concat #$% &$& %$& %$^@!^
  cp -f -y ???

*.tmp.js: ($@.*.js)
  uglify @$

example.mk

Very non friendly syntax

include $(BUILD)/makefiles/bootstrap.mk
include $(INCLUDE_DIR)/module_interface.mk
$(foreach R,$(call REQUIREMENTS,.),\
    $(eval $(call BUILD_REQUIREMENT,$(R))))
...
# typical command
$(JAVAEXE) -Xms512m -Xmx512m -cp "$(DOJO_PATH)/util/shrinksafe/js.jar$(PATH_SEPARATOR)$(DOJO_PATH)/util/closureCompiler/compiler.jar$(PATH_SEPARATOR)$(DOJO_PATH)/util/shrinksafe/shrinksafe.jar" \
org.mozilla.javascript.tools.shell.Main $(DOJO_PATH)/dojo/dojo.js baseUrl=$(DOJO_PATH)/dojo load=build --profile $(1) $(BUILDMODE)
@echo "Dojo returned"
...

Ant and Maven

Java world's contribution to task runner

Fixed build lifecycle:

validate -> compile -> test -> ...

Manages dependencies locally and in central repo.

Maven positives

  • every developer knows what to expect
  • test step
  • managed dependencies

Maven is ...

  • inflexible
  • super verbose XML configuration
    • all commands through plugins (pom.xml > 500 loc commong for just jslint, minification and testing)
  • slow
  • Java-based

Enter

Grunt Logo

Ben "Cowboy" Alman

Works at Bocoup, Boston

Ben Alman

Grunt 0.1.0 - January 2012

"Doing all this stuff manually is a total pain, and building all this stuff into a gigantic Makefile / Jakefile / Cakefile / Rakefile / ?akefile that's maintained across all my projects was also becoming a total pain." - Ben Alman

Latest version 0.4.1 - major rewrite after 0.3.0

Setup

Install nodejs, server-side JavaScript engine.

sudo npm install -g grunt-cli

Grunt, plugins and project settings are CommonJs modules.

Example (included)

Create sample project npm init

Add grunt and a few plugins

npm i grunt --save
npm i grunt-contrib-jshint --save
npm i matchdep --save

Try running grunt

Gruntfile.js

module.exports = function(grunt) {
  grunt.initConfig({
    jshint: {
      src: '*.js'
    }
  });

  var plugins = require('matchdep').filter('grunt-*');
  plugins.forEach(grunt.loadNpmTasks);

  // tasks composed from other tasks
  grunt.registerTask('default', ['jshint']);
  grunt.registerTask('pre-commit',
    ['clean', 'jshint', 'all-tests']);
};

Flexibility

// runs jshint task
grunt jshint

// runs another task
grunt pre-commit

// run 'default', ignore errors
grunt --force

// see what's going on
grunt --verbose

task / goals / options

grunt.initConfig({
    jshint: { // grunt jshint
        options: {
            // applies to all subtasks
        },
        sourceFiles: { // grunt jshint:sourceFiles
            options: {
                // add options for some files
            },
            src: 'src/**/*.js'
        },
        testFiles: { // grunt jshint:testFiles
            src: 'test/**/*.js'
        }
    }
});

Flexibility with tasks

From concat plugin

grunt.registerTask('test',
  ['clean', 'concat', 'nodeunit']);

grunt.registerTask('default',
  ['jshint', 'test', 'build-contrib']);

Flexibility with Templates

uglify: {
  dist: {
    files: {
      'dist/<%= my.name %>.min.js':
        ['<%= concat.dist.dest %>']
    }
  }
},
my: { name: 'example' }

Flexibility with JSON

module.exports = function( grunt ) {
  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    banner: {
      compact: '/*! <%= pkg.name %> <%= pkg.version %> */'
    }
  });
};

Flexibility with JavaScript

Gruntfile.js is just a JavaScript file, so the tasks can be generated programmatically.

var jshintOptions = any crazy js code
var myTask = super task

module.exports = function(grunt) {
  jshint: jshintOptions,
  myTask: myTask
};

Debugging

var jshintOptions = any crazy js code
console.log(jshintOptions);

module.exports = function(grunt) {
  jshint: jshintOptions
};

Or pass filter function to see all filenames for a task, see Files

timing tasks

Use time-grunt

module.exports = function(grunt) {
    require('time-grunt')(grunt);
    grunt.initConfig({
        ...

time-grunt screenshot

Run tasks in parallel

grunt-parallel

grunt.initConfig({
  parallel: {
    all: {
      options: {
        grunt: true
      },
      tasks: ['jshint', 'unit-test', 'requirejs']
    }
  }
});

grunt plugins

28 official plugins have names like grunt-contrib-*. Install them all using single grunt-contrib module.

2k (!) user plugins have usually names like grunt-*

Full list - generates automatically from npm registry, modules tagged with gruntplugin

Most plugins upgraded to work with grunt 0.4

Creating plugins

Writing or updating a plugin is very easy

Use starter project and look at creating tasks. See api docs.

grunt.registerTask('foo', 'A sample',
  function(arg1, arg2) {
    grunt.log.writeln(this.name + ", " + arg1 + ", " + arg2);
});

grunt foo:first:second
// foo, first, second

grunt-contrib-clean

Typical plugin to delete files / folders.

README.md

Source code

Notable plugins

contrib-clean, contrib-copy, contrib-jshint, grunt-jslint, contrib-csslint, json-lint, contrib-qunit, contrib-sass, contrib-concat, contrib-requirjs, grunt-dojo, contrib-uglify, contrib-watch, grunt-notify

My grunt plugins

More resources

Examples

AngularJs Gruntfile.js, Modernizr Gruntfile.js, jQuery Gruntfile.js, QUnit Gruntfile.js,

Tutorials

Bulding library with Grunt

Getting started tutorial

Grunt boilerplate tutorial

The problems

Just like Makefile, grunt uses temp files to pass source between tasks, example

  • Gruntfile complexity
  • disk I/O is slow

gulp logo

Gulpjs

New kid on the block gulpjs

Based on Nodejs event streams:

var fs = require("fs");
var zlib = require("zlib");
fs.createReadStream("input/people.csv.gz")
    .pipe(zlib.createGunzip())
    .pipe(fs.createWriteStream("output/people.csv"));

Pipeline data

sudo npm install -g gulp
npm install --save-dev gulp gulp-util
// gulpfile.js
var gulp = require('gulp');
gulp.task('default', function(){
    gulp.src('./client/templates/*.jade')
        .pipe(jade())
        .pipe(minify())
        .pipe(gulp.dest('./build/minified_templates'));
});

Grunt vs Gulp

Good comparison post. Due to lower I/O Gulp is much faster.

Grunt 0.5 will introduce streams.

My gulp plugin gulp-dotdot.


grunt flow

grunt flow


gulp flow

gulp flow

The End

Grunt

  • simple, quick, easy
  • all the tools (plugins) for front end development
  • great API for writing plugins
  • does NOT manage dependencies

get your grunt on