Blog.

Automate your development tasks using Grunt

Marco Franssen

Marco Franssen /

6 min read1100 words

Cover Image for Automate your development tasks using Grunt

Grunt is an extremely useful Node.js package to automate lots of development and continuous integration tasks. The Grunt eco-system has lots of packages available on npm. This enables us to quickly setup our development/continuous integration environment.

Grunt tasks mostly have two required properties. An files array, which is used to configure on what files the tasks is executed, and an options property which configures some task specific settings. The files array supports the globbing and minimatch pattern to match files based on the provided expression.

So what tasks could you use for your projects, or for which project can you use Grunt? How do I configure Grunt tasks? How do I execute them? All these questions I try to answer for you in this article.

If you are working on Node.js related projects, Grunt perfectly suites the job to do some tasks on your javascript. For example listing the javascript using jshint, execute unittests, do some static code analysis etc.

Also for other web development kind of projects Grunt perfectly fits the job. Example minify JavaScript, compile less files to CSS, minify images and/or replace small image urls in your CSS by base64 encoded images.

Still we didn't reached the top of the iceberg. If you are working on .NET related projects grunt can also be used. There are tasks in the npm registry to compile your code using MSBuild, execute nunit and mspec tests and calculate code coverage using open cover etc. Grunt simplifies configuring the tasks and executing them.

So what if there is no Grunt tasks for the thing you would like to do. There is also a package which can execute commandline/shell jobs. So if you just want to execute some commandline/shell jobs, just use this package to execute these commandline tools more easily.

With a little bit of javascript knowledge you can easily write your own Grunt task. That's what I did when I wrote the grunt-dotnet-mspec and the grunt-dotnet-codecoverage tasks. Both are available on Github (not yet implemented the full feature set) and I accept pull requests enhancing the grunt tasks. In this blogpost I won't zoom in on writing your own Grunt tasks.

So enough chitchat for now. Lets provide you a small example gruntfile with an example configuration for tasks.

gruntfile.js
module.exports = function (grunt) {
  "use strict";
 
  grunt.initConfig({
    pkg: grunt.file.readJSON("package.json"),
 
    meta: {
      banner:
        '/*! <%= pkg.name %> - v<%= pkg.version %> <%= grunt.template.today("yyyy-mm-dd") %> */',
    },
 
    secret: grunt.file.readJSON("secret.json"),
 
    jshint: {
      options: {
        jshintrc: ".jshintrc",
      },
      gruntfile: ["Gruntfile.js"],
      serverside: ["server/src/**/*.js", "server/*.js"],
      clientside: ["js/cors/*.js", "js/*.js", "!js/thirdparty/**"],
      tests: ["test/**/*.js"],
    },
 
    concat: {
      options: {
        stripBanners: true,
        banner: "<%= meta.banner %>",
      },
      javascript: {
        files: {
          "build/js/myawesomeapplication.js": [
            "js/thirdparty/jquery.iframe-transports.js",
            "js/app.js",
            "js/validation.js",
            "js/controller.js",
            "js/router.js",
            "js/modules/**/*.js",
          ],
        },
        nonull: true,
      },
    },
 
    uglify: {
      options: {
        banner: "<%= meta.banner %>",
        compress: {
          drop_console: true,
          sequences: true, // join consecutive statemets with the “comma operator”
          properties: true, // optimize property access: a["foo"] → a.foo
          dead_code: true, // discard unreachable code
          drop_debugger: true, // discard “debugger” statements
          unsafe: false, // some unsafe optimizations (see below)
          conditionals: true, // optimize if-s and conditional expressions
          comparisons: true, // optimize comparisons
          evaluate: true, // evaluate constant expressions
          booleans: true, // optimize boolean expressions
          loops: true, // optimize loops
          unused: true, // drop unused variables/functions
          hoist_funs: true, // hoist function declarations
          hoist_vars: false, // hoist variable declarations
          if_return: true, // optimize if-s followed by return/continue
          join_vars: true, // join var declarations
          cascade: true, // try to cascade `right` into `left` in sequences
          side_effects: true, // drop side-effect-free statements
          warnings: true,
        },
        report: "gzip",
      },
      javascript: {
        files: {
          "build/js/myawesomeapplication.min.js": [
            "build/js/myawesomeapplication.js",
          ],
        },
      },
    },
  });
 
  grunt.loadNpmTasks("grunt-contrib-jshint");
  grunt.loadNpmTasks("grunt-contrib-concat");
  grunt.loadNpmTasks("grunt-contrib-uglify");
 
  grunt.registerTask("default", [""]);
  grunt.registerTask("minify", ["concat", "uglify"]);
};

In this blogpost I want go into detailed explanation of the specific Grunt tasks since they  are pretty well explained on npm and Github you could find detailed information for these task configurations over there.

How do we execute grunt tasks

A Grunt task can be executed using grunt-cli which you can install globally using npm.

npm install -g grunt-cli

Then you can execute all grunt tasks on your shell (NodeJS commandline on Windows) using following command.

grunt taskname

This executes the specified task based on the options and files provided in your gruntfile. A task can also have multiple targets so you can execute the task with different options or on different files. The specific target can be specified by appending it to the taskname using a colon.

grunt taskname:targetname

So as an example on the above provided gruntfile we could execute following tasks or a task its specific target.

grunt jshint

This executes all the jshint tasks configured.

That sounds cool right? So now we can execute preconfigured tasks manually by keying in some simple commands on the commandline. Can we take this one step further? We can also define new tasks in our gruntfile to execute multiple tasks at once. For example a ci task which executes all the tasks required for our continuous integration. This gives us the ability to execute multiple tasks at once by calling only one task.

Example:

grunt.registerTask("ci", ["jshint", "concat", "uglify", "less", "cssmin"]);

This task can be executed using grunt ci.

For example on your Jenkins server, automatically every time your Jenkins server gets new code pushed. Or locally every time you want to run all of these tasks. Ofcourse this is not ideal on you local machine since a lot of files change regularly and you don't want to execute the specific tasks everytime manually when you change something. Therefore we also want to automate this. We can use the grunt-contrib-watch task.

Grunt-contrib-watch enables us to watch our files for changes and execute the other grunt jobs automatically. For example: lint your javascript on changes to *.js files or compile your LESS files to CSS on changes to your *.less files.

Example:

gruntfile.js
module.exports = function (grunt) {
  "use strict";
  grunt.initConfig({
    //other tasks left for brevity
    watch: {
      scripts: {
        files: "js/**/*.js",
        tasks: ["jshint", "mocha:unittest", "phantom"],
      },
      less: {
        files: "less/**/*.less",
        tasks: ["less"],
      },
      styles: {
        files: "css/**/*.css",
        tasks: ["cssmin"],
      },
    },
  });
 
  grunt.loadNpmTasks("grunt-contrib-watch");
};

So now you know the what and how about Grunt I would like to challenge you to share your personal experiences and preferred Grunt packages using a comment on this article. This way you help the other readers by adding value to this article. Thanks for reading and have fun playing around with Grunt.

You have disabled cookies. To leave me a comment please allow cookies at functionality level.

More Stories

Cover Image for Packer.io machine building and provisioning part 1

Packer.io machine building and provisioning part 1

Marco Franssen

Marco Franssen /

Large development teams are often coping with the "It works on my machine" syndrome. One solution to solve these kind of issues is by give each single developer the same VM, which is most preferably the same as your production server. So imagine your company is building a web application. The web application is hosted on a Debian server using Apache, MySQL and PHP. So considering these preconditions I will give you a simple example to get your machines scripted and fully provisioned. In this fir…

Cover Image for Using Mocha Chai Sinon to test Node.js

Using Mocha Chai Sinon to test Node.js

Marco Franssen

Marco Franssen /

In this article I'm going to show you how to write tests for your NodeJS application using Mocha, Chai and Sinon. Mocha is a feature-rich JavaScript test framework running on node.js and the browser, making asynchronous testing simple and fun. Mocha tests run serially, allowing for flexible and accurate reporting, while mapping uncaught exceptions to the correct test cases. One of the cool things is you can choose your own assertion style when writing Mocha tests. In this article I will use Ch…

Cover Image for npm tips and tricks

npm tips and tricks

Marco Franssen

Marco Franssen /

In my previous post I showed you how easily you can create a simple webserver using Node.js. In this post I want to show you how to make more advanced usage of node package manager. npm init Using node package manager you can get an even quicker start of your project by using the npm init command. So let's get started by opening a command prompt (on windows open the Node.js command prompt). Then create a new folder and navigate into this newly created folder. In the folder execute following co…

Cover Image for Starting with a Node.js webserver

Starting with a Node.js webserver

Marco Franssen

Marco Franssen /

UPDATE: updated the console output to latest Node.js version and updated the express.js example to latest version. Before starting to explain how you start your first Node.js project for building a simple web server I will first explain you what Node.js is. To do so I just include a quote of the Node.js themself, because I don't like to reinvent the wheel. Node.js is a platform built on Chrome's JavaScript runtime for easily building fast, scalable network applications. Node.js uses an event-d…