# Grunt ### http://anders.janmyr.com ### @andersjanmyr ### anders.janmyr@jayway.com

Gruntfile.coffee

module.exports = (grunt) ->
    grunt.initConfig(
        pkg: grunt.file.readJSON('package.json'),
        uglify:
        options:
            banner: '/*! <%= pkg.name %>; */\n'
        build:
            src: 'src/<%= pkg.name %>.js',
            dest: 'build/<%= pkg.name %>.min.js'

    grunt.loadNpmTasks('grunt-contrib-uglify')
    grunt.registerTask('default', ['uglify'])

Project and Task Configuration


// Project configuration.
grunt.initConfig({
  pkg: grunt.file.readJSON('package.json'),
  uglify: {
  options: {
    banner: '//<%= grunt.template.today("yyyy-mm-dd") %>'
  },
  build: {
    src: 'src/<%= pkg.name %>.js',
    dest: 'build/<%= pkg.name %>.min.js'
  } } });
                

Loading Plugins and Tasks


// Load the plugin that provides the "uglify" task.
grunt.loadNpmTasks('grunt-contrib-uglify');

// Load all tasks defined in local tasks directory
grunt.loadTasks('tasks');
                

Loading Plugins
a better way, (matchdep)

# Load a bunch of plugins
grunt.loadNpmTasks('grunt-contrib-watch')
grunt.loadNpmTasks('grunt-contrib-csslint')
grunt.loadNpmTasks('grunt-contrib-cssmin')
grunt.loadNpmTasks('grunt-contrib-coffee')
...
grunt.loadNpmTasks('grunt-mocha')

# Load tasks from package.json matching "grunt-*"
require('matchdep')
  .filterDev('grunt-*').forEach(grunt.loadNpmTasks)
                

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

watch and livereload

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