Automated front-end builds with grunt task runner

(Comments)

Grunt is a javascript task runner used to automate the building and packaging of all front-end components. Grunt has numerous plugins for all repetitive tasks like minification, linting, compilation etc.

Javascript being extremely popular language for sometime now, there are good alternatives to grunt such as gulp and webpack. We will limit this post to just grunt and how to perform some basic tasks with it.

Installing grunt

grunt is a node package. So we need node and npm installed in our system. If you don't have them installed already, follow our guide managing front-end javascript dependencies using Node Package Manager and bower.
Install grunt-cli globally with

npm install -g grunt-cli

Since we are installing it globally, we need superuser credentials (on linux) or administrator rights (on windows). Once the grunt-cli is installed, we can access it from any directory.


Initiate node project and then install grunt with

npm install --save-dev grunt

Now we can run grunt in our project directory with the command grunt. But that will only raise an error like this

A valid Gruntfile could not be found. Please see the getting started guide for
more information on how to configure grunt: http://gruntjs.com/getting-started
Fatal error: Unable to find Gruntfile.

This is because grunt looks for a file named gruntfile.js that describes the tasks and the configuration of individual tasks. The above error is caused when it fails to find gruntfile.js.

Creating grunt tasks

Let us create the file gruntfile.js. The grunt file is basically of the form

module.exports = function(grunt) {
    // List the grunt tasks here.
};

All the tasks and configurations are defined within this function.


To understand how grunt works, let us install a grunt plugin called grunt-contrib-uglify which is used to minify javascript files. Install it with

npm install --save-dev grunt-contrib-uglify

We need to load the plugin in the grunt function to import the tasks from it. Load it with

grunt.loadNpmTasks('grunt-contrib-uglify');

Though this loads all the tasks, uglify still needs some configuration. All the configuration to grunt is passed as an object to the methd initConfig as follows.

grunt.initConfig({
    // Configuration in this object
});

We provide configuration for uglify plugin with this object as the value to the key 'uglify'. The configuration looks something like this.

grunt.initConfig({
    uglify: {
        target: {
            files: {
                'dist/output.min.js': ['src/file1.js', 'src/file2.js']
            }
        }
    }
});

To be clear the complete file look like this.

module.exports = function(grunt) {
    grunt.initConfig({
        uglify: {
            target: {
                files: {
                    'dist/output.min.js': ['src/file1.js', 'src/file2.js']
                }
            }
        }
    });

    grunt.loadNpmTasks('grunt-contrib-uglify');
};

Before we try to understand the configuration completely, create a directory named src with two files file1.js and file2.js. Let us put the following contents in those files.

function greeting_one() {
    console.log('Hello world! This is the first file.');
}
greeting_one();

in the first file and

function greeting_two() {
    console.log('Hello world! This is the second file.');
}
greeting_two();

in the second file. Now let us look at the configuration. The object with key files is of interest here. The key in the object is 'dist/output.min.js' and its value is the list ['src/file1.js', 'src/file2.js']. Uglify takes the files specified in the list and minifies them and saves the result as the given key 'dist/output.min.js'. Now run uglify task with

grunt uglify

Grunt will run the uglify task, which will concatinate the 'src/file1.js' and 'src/file2.js' files and remove uncessary white spaces and save the result as 'dist/output.min.js'. The content of the dist/output.min.js file will be like this.

function greeting_one(){console.log("Hello world! This is the first file.")}function greeting_two(){console.log("Hello world! This is the second file.")}greeting_one(),greeting_two();

Yes, it is ugly. You can run this file with node with command

node dist/output.min.js

The output will be

ravi$ node dist/output.min.js 
Hello world! This is the first file.
Hello world! This is the second file.

It is difficult to specify each and every file that needs to be minified. Grunt allows wildcards to specify source files. To include all the .js files from the src directory, the configuration would be

files: {
   'dist/output.min.js': ['src/*.js']
}

We can specify to include all the .js files from all the sub-directories with

files: {
   'dist/output.min.js': ['src/**/*.js']
}

This will combine all the js files from all the sub-directories in src, minify and save as dist/output.min.js. Other options provided by uglify are listed and explained on uglify's gihub page.

Create a default task for grunt

uglify plugin will have defined the task named uglify which we ran with grunt uglify. How do we create our own custom task? We create a task with the registerTask() method

grunt.registerTask('<custom_taskname>', ['<task 1>', '<task 2>', ...]);

The task registered with above method is aggregation of the list of tasks passed and second argument. For example, we can define the 'default' task that executes 'uglify' with

grunt.registerTask('default', ['uglify']);

We can execute the default task with grunt default. Grunt by default looks for a task named 'default' and execute it when we do not specify any task.

grunt

is equivalent to

grunt default

We can add multiple tasks to single task and execute all of them as if it is a single task. The tasks are run in the specified order.


For more information on how tasks work and are created, visit grunt's official tutorial creating tasks and the grunt's API documentation.

Compile Sass to CSS in grunt

Let us also configure our gruntfile to compile Sass files into CSS files. This can be done with the grunt plugin grunt-sass. Install grunt-sass with

npm install --save-dev grunt-sass

Load the grunt sass tasks with

grunt.loadNpmTasks('grunt-sass');

Then configure sass with the

grunt.initConfig({
    sass: {
        dist: {
            files: {
                'dist/css/app.css': ['src/styles/*.scss']
            }
        }
    }
});

Also add the sass task to the 'default' task with the line

grunt.registerTask('default', ['sass']);

My gruntfile at this point (along with uglify tasks) looks like this.

module.exports = function(grunt) {
    grunt.initConfig({
        uglify: {
            target: {
                files: {
                    'dist/output.min.js': ['src/file1.js', 'src/file2.js']
                }
            }
        },
        sass: {
            dist: {
                files: {
                    'dist/css/app.css': ['src/styles/*.scss']
                }
            }
        }
    });

    grunt.loadNpmTasks('grunt-contrib-uglify');
    grunt.loadNpmTasks('grunt-sass');

    grunt.registerTask('default', ['uglify', 'sass']);
};

This configuration will compile all the .scss files in src/styles/ directory and save the output as the file dist/css/app.css. To test this create a file in src/styles/ named main.scss with the following contents.

.container {
    .right {
        text-align: right;
    }
}

Save the file and then run grunt with

grunt

The .scss sass file will be compiled and the resulting file will be placed in dist/css as app.css with the contents

.container .right {
  text-align: right; }

grunt-sass works with node-sass. The options will be same as that of node-sass which can be found at https://github.com/sass/node-sass#options.

Comments

Recent Posts

Archive

2022
2021
2020
2019
2018
2017
2016
2015
2014

Tags

Authors

Feeds

RSS / Atom