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

  • How to Implement In-Order, Pre-Order, and Post-Order Tree Traversal in Python?

    Tree traversal is an essential operation in many tree-based data structures. In binary trees, the most common traversal methods are in-order traversal, pre-order traversal, and post-order traversal. Understanding these tree traversal techniques is crucial for tasks such as tree searching, tree printing, and more complex operations like tree serialization. In this detailed guide, we will […]

  • Mastering Merge Sort: A Comprehensive Guide to Efficient Sorting

    Are you eager to enhance your coding skills by mastering one of the most efficient sorting algorithms? If so, delve into the world of merge sort in Python. Known for its powerful divide-and-conquer strategy, merge sort is indispensable for efficiently handling large datasets with precision. In this detailed guide, we’ll walk you through the complete […]

  • Optimizing Chatbot Performance: KPIs to Track Chatbot Accuracy

    In today’s digital age, chatbots have become integral to customer service, sales, and user engagement strategies. They offer quick responses, round-the-clock availability, and the ability to handle multiple users simultaneously. However, the effectiveness of a chatbot hinges on its accuracy and conversational abilities. Therefore, it is necessary to ensure your chatbot performs optimally, tracking and […]

  • Reinforcement Learning: From Q-Learning to Deep Q-Networks

    In the ever-evolving field of artificial intelligence (AI), Reinforcement Learning (RL) stands as a pioneering technique enabling agents (entities or software algorithms) to learn from interactions with an environment. Unlike traditional machine learning methods reliant on labeled datasets, RL focuses on an agent’s ability to make decisions through trial and error, aiming to optimize its […]

  • Understanding AI Predictions with LIME and SHAP- Explainable AI Techniques

    As artificial intelligence (AI) systems become increasingly complex and pervasive in decision-making processes, the need for explainability and interpretability in AI models has grown significantly. This blog provides a comprehensive review of two prominent techniques for explainable AI: Local Interpretable Model-agnostic Explanations (LIME) and Shapley Additive Explanations (SHAP). These techniques enhance transparency and accountability by […]

  • Building and Deploying a Custom Machine Learning Model: A Comprehensive Guide

    Machine Learning models are algorithms or computational models that act as powerful tools. Simply put, a Machine Learning model is used to automate repetitive tasks, identify patterns, and derive actionable insights from large datasets. Due to these hyper-advanced capabilities of Machine Learning models, it has been widely adopted by industries such as finance and healthcare.  […]

Click to Copy