INTRODUCTION TO GRUNT

Grunt, simply defined, is a task runner built over Node.js that can be used to automate certain tasks in almost any project, in any language. Grunt and Grunt plugins are installed and managed via npm.

If you’re unaware, npm is a package manager that provides a central repository for custom open source modules for Node.js and JavaScript. npm makes it simple to manage modules versions and distribution. For this tutorial project, we used the npm install command to install the required module.

Why use Grunt?

In one word? Automation.

Grunt helps you in performing repetitive tasks like:

Installing Grunt CLI

In order to get more productive with Grunt, you will need to install Grunt’s command line interface (CLI) globally. Run the following command:

npm install -g grunt-cli

This will put the grunt command in your system path, allowing it to be run from any directory. Note that installing grunt-cli does not install the Grunt task runner! The job of the Grunt CLI is to run the version of Grunt which has been installed next to a Gruntfile. This allows multiple versions of Grunt to be installed on the same machine simultaneously.

How to Use Grunt in Your Project

For a how-to example. let’s build one empty project from scratch and configure Gruntin it.

Create a Project Root Directory

First, create an empty project root directory and create the following files in it:

  • package.json: This file is used by npm to store metadata for projects published as npm modules. We will list Grunt and the Grunt plugins that we need in our project as devDependencies in this file. This file needs to be in the root directory of the project. You can also use npm init command to generate this file.
  • Gruntfile.js: This file is used to configure or define tasks as well as load Grunt plugins. The Gruntfile.js file also needs to be in the root directory of the project. You can also name the file as Gruntfile.coffee if you need to configure tasks in coffee script. We will discuss the contents of this file in detail in an upcoming section of this article.

Add the Grunt module to your project

Next we need to add the Grunt module to our project via npm. Let’s run the following command to install the module:

> npm install grunt --save-dev

This will install Grunt and also make an entry in the package.json mentioning this module as a devDependency. Modules marked as devDependencies are ones that are only installed in the development environment; in production environments they are ignored.

Understanding the Gruntfile

The Gruntfile is a valid JavaScript or CoffeeScript file that belongs in the root directory of your project next to the package.json file, and should be committed with your project source. A Gruntfile is comprised of the following parts:

  • The “wrapper” function
  • Project and task configuration
  • Loading Grunt plugins and tasks
  • Custom tasks

Let us discuss each of these in detail.

About the “Wrapper” Function in a Gruntfile

The wrapper function is nothing but a function assigned to your module exports that encapsulates all the Grunt code. This function is called by the Grunt engine and serves as the entry point for Grunt configurations. Hence, our Gruntfile at this point looks like the text below:

module.exports = function(grunt) {
  // Do grunt-related things in here
};

Every Gruntfile and Grunt plugin uses this basic format, and all of your Grunt code must be specified inside this function.

Project and Task Configuration in a Gruntfile

Most Grunt tasks rely on configuration data defined in an object passed to the grunt.initConfig method. That’s why we need to specify configurations as desired by the plugins we use. We can also specify our own configurations which we intend to use. Lets define some random configurations:

module.exports = function(grunt) {
  // Project configuration.
  grunt.initConfig({
    //Lets read the project package.json
    pkg: grunt.file.readJSON('package.json'),
    //Some other configs
    someKey: 'Some Value',
    author: 'Modulus.io'
  });

};

Grunt also have several helper methods; one of them is reading files. Hence we used grunt.file.readJSON to read our package.json file and store its parsed object in the pkg key. You can also refer the config values in your strings as variables, making your config strings dynamic. Here’s an example below:

module.exports = function (grunt) {
 // Project configuration.
  grunt.initConfig({
    //Lets read the project package.json
    pkg: grunt.file.readJSON('package.json'),
    //Some other configs
    someKey: 'Some Value',
    author: 'Modulus.io',
    filePostfix: '-<%= pkg.name %>-<%= pkg.version %>',
    greetingMessage: 'Running Grunt for project: <%= pkg.name %>, Version: <%= pkg.version %>, Author: <%= author %>'
  });

};

Here we used the <%= varName %> notation to specify dynamic text in strings. We can refer the config values directly here as demonstrated in the example above.

Loading Grunt Plugins and Tasks

Many commonly used tasks like concatenation, minification and linting are available as Grunt plugins. As long as a plugin is specified in package.json as a dependency and has been installed via npm install, it may be enabled inside your Gruntfile.

Let’s install a simple plugin, grunt-clean, into our project and try to configure that in our Gruntfile. Install the module via npm by running the command:

> npm install grunt-contrib-clean --save-dev

Now we’ll load the task in our Gruntfile via the grunt.loadNpmTask(‘pluginName’)method and do some configurations as required. Now our Gruntfile reads as shown below:

module.exports = function (grunt) {
 // Project configuration.
  grunt.initConfig({
    //Lets read the project package.json
    pkg: grunt.file.readJSON('package.json'),
    //Some other configs
    someKey: 'Some Value',
    author: 'Modulus.io',
    filePostfix: '-<%= pkg.name %>-<%= pkg.version %>',
    greetingMessage: 'Running Grunt for project: <%= pkg.name %>, Version: <%= pkg.version %>, Author: <%= author %>',

    //Configure Clean Module
    clean: ['.tmp', 'dist', 'npm-debug.log']
  });

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

};

In the above example, we configured clean to delete the .tmp and dist directories as well as the npm-debug.log file in the project root. You can specify any number of files/dirs there. Each plugin has documentation specifying its necessary configurations and format.

Tip: The grunt –help command will list all available tasks.

Now, try to run the following command in your project root:

> grunt clean

This command will delete the specified files and directories you configured.

Specifying Subtasks in Gruntfile

You can also configure sub tasks as they are supported by most Plugins. To understand this concept, let’s review the following example:

module.exports = function (grunt) {
 ***javascript***
 // Project configuration.
  grunt.initConfig({
    //Lets read the project package.json
    pkg: grunt.file.readJSON('package.json'),
    //Some other configs
    someKey: 'Some Value',
    author: 'Modulus.io',
    filePostfix: '-<%= pkg.name %>-<%= pkg.version %>',
    greetingMessage: 'Running Grunt for project: <%= pkg.name %>, Version: <%= pkg.version %>, Author: <%= author %>',

    //Configure Clean Module
    clean: {
      npm: 'npm-debug.log',
      temp: ['temp', '.tmp'],
      dist: ['dist', 'out/dist']
    }
  });

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

};

Here during configurations we specified an object with different keys including npmtemp and dist. These are random names we came up with in order to divide the clean task into subtasks. Now we can run subtasks to delete all mentioned files/dirs or delete a subgroup. This is demonstrated below:

#This will delete all files, npm-debug.log, temp, .tmp, dist, out/dist > grunt clean

#This will delete only npm-debug.log > grunt clean:npm

#This will delete only temp, .tmp > grunt clean:temp

#This will delete only dist, out/dist > grunt clean:dist

In the previous example, we specified the key we provided in the config to run the subtask. The syntax to use for writing the key is:

> grunt TaskName:SubTaskKeySpecifiedInConfig

Custom Tasks

You can define tasks with custom names, which can be a combination of existing tasks or purely your own implementation. If you are implementing custom tasks, you must implement them in JavaScript.

Let’s try to define a task which is combination of existing tasks and a pure implementation by us.

module.exports = function (grunt) {
 // Project configuration.
  grunt.initConfig({
    //Lets read the project package.json
    pkg: grunt.file.readJSON('package.json'),
    //Some other configs
    someKey: 'Some Value',
    author: 'Modulus.io',
    filePostfix: '-<%= pkg.name %>-<%= pkg.version %>',
    greetingMessage: 'Running Grunt for project: <%= pkg.name %>, Version: <%= pkg.version %>, Author: <%= author %>. ',

    //Configure Clean Module
    clean: {
      npm: 'npm-debug.log',
      temp: ['temp', '.tmp'],
      dist: ['dist', 'out/dist']
    }
  });

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

  //Lets register a basic task.
  grunt.registerTask('print-info', 'Lets print some info about the project.', function() {
    grunt.log.write(grunt.config('greetingMessage')).ok();
  });

  //Specify a custom task which is combination of tasks.
  grunt.registerTask('my-clean', ['print-info', 'clean:dist', 'clean:npm']);

};

In this latest example above, we created a custom task print-info, and executed some JavaScript in it. We used the grunt.registerTask() method to do so. We also defined a custom task my-clean which is combination of other tasks. Because you can run any JavaScript in your tasks, the possibilities are endless.

Run the following command to see what tasks are available:

> grunt --help

You should see the two custom tasks specified. Now you can run your my-clean task:

> grunt my-clean

Default Task

When you just run grunt in your project root without any tasks specified, then it looks for the default task and runs it. You can specify the default task by simply issuing the command:

> grunt

To specify a default task we simply register a task named default. Let’s do that; now our Gruntfile looks like this:

module.exports = function (grunt) {
 ***javascript***
 // Project configuration.
  grunt.initConfig({
    //Lets read the project package.json
    pkg: grunt.file.readJSON('package.json'),
    //Some other configs
    someKey: 'Some Value',
    author: 'Modulus.io',
    filePostfix: '-<%= pkg.name %>-<%= pkg.version %>',
    greetingMessage: 'Running Grunt for project: <%= pkg.name %>, Version: <%= pkg.version %>, Author: <%= author %>. ',

    //Configure Clean Module
    clean: {
      npm: 'npm-debug.log',
      temp: ['temp', '.tmp'],
      dist: ['dist', 'out/dist']
    }
  });

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

  //Lets register a basic task.
  grunt.registerTask('print-info', 'Lets print some info about the project.', function() {
    grunt.log.write(grunt.config('greetingMessage')).ok();
  });

  //Specify a custom task which is combination of tasks.
  grunt.registerTask('my-clean', ['print-info', 'clean:dist', 'clean:npm']);

  //Specify a default task
  grunt.registerTask('default', ['my-clean', 'clean:temp']);

};

Gruntfile Tutorial Summary

In this article we talked about the basics of Grunt and how to use and configure the Gruntfile. We saw how we can define Gruntfile configurations and use them within strings and custom tasks. We also saw the use of multi-tasks (subtasks) and how to configure them.


Posted

in

by

Tags:

Recent Post

  • Transforming HR with AI Assistants: The Comprehensive Guide

    The role of Human Resources (HR) is critical for the smooth functioning of any organization, from handling administrative tasks to shaping workplace culture and driving strategic decisions. However, traditional methods often fall short of meeting the demands of a modern, dynamic workforce. This is where our Human Resource AI assistants enter —a game-changing tool that […]

  • How Conversational AI Chatbots Improve Conversion Rates in E-Commerce?

    The digital shopping experience has evolved, with Conversational AI Chatbots revolutionizing customer interactions in e-commerce. These AI-powered systems offer personalized, real-time communication with customers, streamlining the buying process and increasing conversion rates. But how do Conversational AI Chatbots improve e-commerce conversion rates, and what are the real benefits for customers? In this blog, we’ll break […]

  • 12 Essential SaaS Metrics to Track Business Growth

    In the dynamic landscape of Software as a Service (SaaS), the ability to leverage data effectively is paramount for long-term success. As SaaS businesses grow, tracking the right SaaS metrics becomes essential for understanding performance, optimizing strategies, and fostering sustainable growth. This comprehensive guide explores 12 essential SaaS metrics that every SaaS business should track […]

  • Bagging vs Boosting: Understanding the Key Differences in Ensemble Learning

    In modern machine learning, achieving accurate predictions is critical for various applications. Two powerful ensemble learning techniques that help enhance model performance are Bagging and Boosting. These methods aim to combine multiple weak learners to build a stronger, more accurate model. However, they differ significantly in their approaches. In this comprehensive guide, we will dive […]

  • What Is Synthetic Data? Benefits, Techniques & Applications in AI & ML

    In today’s data-driven era, information is the cornerstone of technological advancement and business innovation. However, real-world data often presents challenges—such as scarcity, sensitivity, and high costs—especially when it comes to specific or restricted datasets. Synthetic data offers a transformative solution, providing businesses and researchers with a way to generate realistic and usable data without the […]

  • Federated vs Centralized Learning: The Battle for Privacy, Efficiency, and Scalability in AI

    The ever-expanding field of Artificial Intelligence (AI) and Machine Learning (ML) relies heavily on data to train models. Traditionally, this data is centralized, aggregated, and processed in one location. However, with the emergence of privacy concerns, the need for decentralized systems has grown significantly. This is where Federated Learning (FL) steps in as a compelling […]

Click to Copy