Grunt

http://anders.janmyr.com

@andersjanmyr

anders.janmyr@jayway.com

Grunt Logo

Installation

# Install grunt-cli GLOBALLY
$ npm install -g grunt-cli

It uses the locally installed grunt via require

Command line completion

# Install command line completion
$ eval "$(grunt --completion=bash)"

Put this in ~/.bashrc or elsewhere

A Grunt Project

Two files:

  • package.json
  • Gruntfile.js or Gruntfile.coffee

package.json

{
    "name": "my-project-name",
    "version": "0.1.0",
    "devDependencies": {
        "grunt": "~0.4.1", /* required by grunt-cli */
        "grunt-contrib-jshint": "~0.1.1" /* a grunt plugin */
    }
}

npm tips

# Add to devDependencies section
$ npm install grunt-contrib-uglify --save-dev

# Add to dependencies section
$ npm install restify --save

# Create a new project (package.json)
$ npm init

Configuration

grunt.initConfig({
  // Arbitrary non-task-specific properties.
  my_property: 'whatever',
  my_src_files: ['foo/*.js', 'bar/*.js'],

  concat: {
    // concat task configuration goes here.
  },
  uglify: {
    // uglify task configuration goes here.
  },
});

Multi-tasks

grunt.initConfig(
  concat:
    options:
      # Task-level options, overriding defaults

    development:
      # development, $ grunt foo:development
      options:
        # "development" options, overriding task-level

    production: # production, $ grunt foo:production
      options:
        # "production" options, overriding task-level
                

Writing Basic Tasks


# Invoke with grunt foo cool:tapir
grunt.registerTask('foo', 'Foo description, (a, b) ->
  grunt.log.witeln(this.name, a, b)

# Prints foo cool tapir
                

Writing Multi-tasks


grunt.registerMultiTask('mytask', 'task description', function() {
  // Iterate over all specified file groups.
  this.files.forEach(function(f) {
    // Do something with file
  }
});
                

Async Tasks


grunt.registerTask('asyncfoo', 'My "asyncfoo" task.', function() {
  // Grab a handle to the "done" function.
  var done = this.async();
  // Run some sync stuff.
  grunt.log.writeln('Processing task...');
  // And some async stuff.
  setTimeout(function() {
    grunt.log.writeln('All done!');
    done();
  }, 1000);
});
                

Files

Multitasks supports many files formats

Single src: and dest:

grunt.initConfig({
  jshint: {
    foo: {
      src: ['src/aa.js', 'src/aaa.js']
    },
  },
  concat: {
    bar: {
      src: ['src/bb.js', 'src/bbb.js'],
      dest: 'dest/b.js',
    },
  },
});
                

Files (Object Format)

grunt.initConfig(
  concat:
    foo:
      files:
        'dest/a.js': ['src/aa.js', 'src/aaa.js']
        'dest/a1.js': ['src/aa1.js', 'src/aaa1.js']

    bar:
      files:
        'dest/b.js': ['src/bb.js', 'src/bbb.js']
        'dest/b1.js': ['src/bb1.js', 'src/bbb1.js']
                

Files (Array Format)

grunt.initConfig(
  concat:
    foo:
      files: [
        {src: ['src/client/*.js'], dest: 'dest/a.js'}
        {src: ['src/server/**/*.js'], dest: 'dest/a1.js'}
      ]
    bar:
      files: [
        {src: 'src/a.js', dest: 'dest/', filter: 'isFile'}
      ]

Files (globbing)

// You can specify single files:
{src: 'foo/this.js', dest: ...}

// Or arrays of files:
{src: ['foo/this.js', 'foo/that.js'], dest: ...}

// Or you can generalize with a glob pattern:
{src: 'foo/th*.js', dest: ...}

// All .js files, in foo/, in alpha order:
{src: ['foo/*.js'], dest: ...}

Let it glob, let it glob, let it glob

// Here, bar.js is first, remaining files, in alpha order:
{src: ['foo/bar.js', 'foo/*.js'], dest: ...}

// This single node-glob pattern:
{src: 'foo/{a,b}*.js', dest: ...}

// All files except for bar.js, in alpha order:
{src: ['foo/*.js', '!foo/bar.js'], dest: ...}

// All files in alpha order, but with bar.js at the end.
{src: ['foo/*.js', '!foo/bar.js', 'foo/bar.js'], dest: ...}
                

Files filter

grunt.initConfig({
  clean: {
    foo: {
      src: ['tmp/**/*'],
      filter: function(filepath) {
        return grunt.file.isDir(filepath) &&
          require('fs').readdirSync(filepath).length === 0;
      }
    }
  }
});
                

Files expand

grunt.initConfig({
  minify: {
    dynamic_mappings: {
      files: [
        {
          expand: true,     // Enable dynamic expansion.
          cwd: 'lib/',      // Src are relative to this
          src: ['**/*.js'], // Actual pattern(s) to match.
          dest: 'build/',   // Destination path prefix.
          ext: '.min.js',   // Dest files gets extension.
        }
      ]}}});
                

Templates

grunt.initConfig({
  concat: {
    sample: {
      options: {
        banner: '/* <%= bar %> */\n',
      },
      src: ['<%= foo %>'],
    },
  },
  // Properties are expanded recursively
  foo: 'c',
  bar: 'b<%= foo %>d', // 'bcd'
});            

External Data

grunt.initConfig({
  pkg: grunt.file.readJSON('package.json'),
  uglify: {
    dist: {
      src: 'src/<%= pkg.name %>.js',
      dest: 'dist/<%= pkg.name %>.min.js'
    }
  }
});
                

Plugins

watch and livereload

  • grunt-contrib-watch
  • grunt-contrib-livereload
grunt.initConfig({
  watch: {
    scripts: {
      files: ['**/*.js'],
      tasks: ['livereload'],
      options: {
        nospawn: true,
      }
    }
}
              

Javascript

  • grunt-contrib-requirejs
  • grunt-contrib-jshint
  • grunt-contrib-uglify
  • grunt-contrib-coffee

Test

  • grunt-contrib-mocha
  • grunt-buster
  • grunt-contrib-jasmine
  • grunt-contrib-nodeunit
  • grunt-contrib-qunit

CSS

  • grunt-contrib-cssmin
  • grunt-contrib-csslint
  • grunt-contrib-concat
  • grunt-contrib-sass
  • grunt-contrib-less
  • grunt-contrib-stylus
  • grunt-contrib-compass
  • grunt-recess

Templating

  • grunt-contrib-handlebars
  • grunt-contrib-jade
  • grunt-contrib-jst

Images

  • grunt-contrib-imagemin
  • grunt-imageoptim
  • grunt-image-resize
  • grunt-imagine

Utilities

  • grunt-contrib-compress
  • grunt-contrib-copy
  • grunt-contrib-clean
  • grunt-contrib-htmlmin
  • grunt-s3

questions

Questions

http://anders.janmyr.com

@andersjanmyr

anders.janmyr@jayway.com