Automating Front-End Development with Grunt.js

Image Title

posted by Darren Barklie
on Dec 02, 2014

Having recently worked on numerous front-end projects and products simultaneously over the past few months, I recently decided to explore ways of optimizing my front-end development workflow. I had previously presented on how frameworks like Bootstrap and CSS pre-processors like SASS can greatly speed up creating good-looking, responsive front-end templates, whilst simultaneously extending functionality over what is traditionally offered by “standard” syntaxes.

READ MORE: BOOTSTRAP 101

 

Whilst this had worked great on projects where I was the sole author, problems would occur when working within a team environment, where common setups and understanding of these improved workflows were lacking. Conflicts would arise and actually cost me more time as I tried to integrate these differences, which is obviously the opposite of optimization.

 

Since it seemed impractical to ask all team members to adopt new practices as they already busied themselves setting up for new projects, I decided to continue working as I always had been, despite the knowledge that there were more efficient solutions available, if not yet adoptable.

 

The Solution

Enter Grunt.js. I recently watched Chris Coyier’s highly-recommended screencast on CSS Tricks where he gives a brief tour of his personal front-end workflow that leverages Jekyll, Grunt, SASS and SVG. Suddenly I realized that Grunt was the answer to my problem! While I had improved my understanding and adoption, I wasn’t aware of a single solution to assist and automate all these new practices. I quickly dived in to Chris’ introductory screencast First Moments with Grunt, which taught me many of the specifics detailed below.

 

What is Grunt?

Grunt (0.4.5 at the time of writing) is a Javascript task runner that automates a lot of repetitive tasks associated to web development. Specifically for front-end development it can handle compressing images, compiling SASS, minifying code, generating SVG files, compiling sprite sheets, managing local server generation and numerous other tasks. Grunt is already compatible with a lot of great software including; SASS, LESS, Coffeescript, jade, require.js and many more.

 

Setup

While setup does take a little time and understanding, know that this is definitely a worthwhile investment.

 

The first requirement is that Node.js [nodejs.org] is installed on your machine. Once installed, it is required to jump in to the command line. With a successful node.js install, we have use of the npm (Node Package Manager) command. Ensure npm is up to date by running:

 

    npm update –g npm

 

Next we want to install Grunt, so run:
 

    npm install –g grunt-cli

 

These will be installed globally to the machine and so can run from any directory location. It’s interesting to note that the grunt-cli is not an actual installation of the Grunt task runner. It is instead a device to run multiple versions and instances of Grunt. In practice, this means you install Grunt locally at a project’s root, alongside the Gruntfile, and the grunt-cli only helps run an instance of Grunt within each environment.

 

A New Grunt Project

A typical Grunt setup involves two important files that need to be created and placed in the project root:

 

package.json : this file is used by npm to store metadata for the project’s dependencies. This is what unlocks team-working with only a basic common understanding: I can set up a project that requires Grunt, Sass and Ruby for example. With a single command, npm will address this file, determine what files are required, and automatically download and install everything, as per the configuration. No more discrepancies between team-member setups; everyone has exactly what they need for each project.

 

Gruntfile.js : the backbone of Grunt, the Gruntfile loads and configures Grunt plugins. This file contains all the behaviors that you wish to automate leveraging Grunt plugins; for example: reading a .scss file from one directory, compiling to a .css file and placing in an alternate directory.

 

For Grunt to successfully initialize, it is vital that you create these two empty files and place them in the project root directory: Gruntfile.js (yes, capitalized) and package.json

 

Installing Grunt and gruntplugins

With the above files in place, use the command line to navigate to your project’s root directory and then enter:

    npm install grunt -–save-dev

 

This will install an instance of the latest version on grunt to your project. Here you will find your Gruntfile and package.json files are updated with some sensible defaults, and a ‘node_modules’ folder has been added to your directory.

The same command is also utilized to install gruntplugins:

 

    npm install -–save-dev

 

Be aware that some certain plugins will have their own dependencies – for example SASS requires a Ruby installation to function. If Grunt is functioning, but a specific plugin is failing, be sure to consult the plugin’s original documentation to ensure all specific dependencies are satisfied. A properly configured setup should include all dependencies, but I certainly tripped up on this a few times at the beginning.

Example package.json file

The package.json file lists the devDependencies required for a specific project build. They all follow the same format and ensure any machine working on the project has, or will retrieve, all necessary files of the same version number to correctly function. Below is a sample of this fairly simple file, which is automatically populated during plugin installation and requires minimal maintenance.

    {

      “name”: “project-name”,

      “version”: “0.1.0”,

      “devDependencies”: {

        “grunt”: “~0.4.5”,

        “grunt-contrib-jshint”: “~0.10.0”,

        “grunt-contrib-nodeunit”: “~0.4.1”,

        “grunt-contrib-sass”: “^0.8.1”,

        “grunt-contrib-uglify”: “~0.5.0”,

        “grunt-contrib-watch”: “^0.6.1”

      }

    }

 

The standard naming format for a gruntplug is grunt-contrib-plugin. Searching Google for this format should narrow down your search to Grunt-specific documentation.

Example Gruntfile

Next we take a look at an example Gruntfile below to sample a typical setup. Here we use the default plugin Uglify to collect multiple JS files and compress them in to a single file. Take note of the commenting to help understand the new syntax:

// Gruntfile wrapper

module.exports = function(grunt) {

 

// Project configuration

grunt.initConfig({

   // Read dependancies from package.json

   pkg: grunt.file.readJSON(‘package.json’),

    // Initialize uglify plugin

    uglify: {

     // Files to source in uglify action

     src: [‘js/libs/jquery-1.1.1.js’, ‘js/libs/*.js’],

     // Destination to output uglify actioned files

     dest: ‘js/build/global.min.js’

     }

    }

   });

 

   // Load the plugin that provides the ‘uglify’ task

   grunt.loadNpmTasks(‘grunt-contrib-uglify’);

 

   // Default tasks to run with ‘grunt’ command

   grunt.registerTask(‘default’, [‘uglify’]);

 

};

 

Note that JS syntax is applicable when setting up these files. In the example above, where the JS files are sourced, an array is used to call the files. Ensure that core libraries like jQuery are listed first, before JS plugins requiring jQuery are listed; Grunt will respect this list order during compilation. Also note use of the wildcard * operator to target all other .js files after jQuery has been compiled. This can be a great timesaver whilst still keeping your coding modular.

 

Smart file and directory structuring will keep these extra files organized. I use a ‘libs’ directory to host original files and then a ‘build’ directory for Grunt to place all compiled outputs. It is the build directories that we will reference in our HTML/CSS.

 

Working Practice

With our setup established, it’s time to put Grunt to the test! And really it couldn’t be simpler (–actually it can, as we’ll explore later!).

 

In the command line, located in your root directory (hosting your Gruntfile and package.json file), simply run:

 

    grunt

 

This ‘grunt’ command is a ‘catch all’ that will run all the tasks listed in the last line of the Gruntfile. Using our current setup, this only denotes uglify to compress all of our Javascript files denoted in the uglify task. Alternatively, if we wanted to run an isolated task only, we could use:

 

    grunt uglify

 

Grunt will drive uglify to do its thing in accordance with the parameters set in the Gruntfile. You should get a ‘Done, without errors’ message to confirm your success. You can now check your js/build directory where there should be a ‘global.min.js’ file. Inspecting this file will show that uglify has compressed all of our JS files (respecting the array order) into a single file; minifying all variable names and expressions and removing all surplus commenting and whitespace.

 

With only a little setup, we’re now automating several best practices, which should have a positive impact on page load times, since all of our Javascript code has been highly compressed and only requires one file request from the host server. As developers, we still have all of our code modularized for easy organization and for easier isolation of problems when debugging.

 

Stacking Multiple Tasks

Now that we have successfully configured Grunt to automate one task for us, what else can we make it do? Automatic SASS compilation is a great way to speed up our front-end development and bug hunting. If CSS/SASS isn’t your thing, then skim this step to get a feel for initializing a second task, but note that this pattern is standardized for any additional gruntplugin installation.

 

Since installation follows the pattern above, so let’s breeze through it:

 

Install SASS. Remember that SASS has its own dependencies on the Ruby platform and should be installed via a gem command. Check for successful installation with the command:

    sass -v

which should return the version number. Visit the Grunt-specific documentation:

Run the following command in the project root:

 

    npm install grunt-contrib-sass –save-dev

 

Add SASS to your Gruntfile task stack:

 

    grunt.loadNpmTasks(‘grunt-contrib-sass’);

 

Run the task with:

 

    grunt sass

 

At this point, you will probably see an SASS specific error, since we are yet to specify any behavior for the plugin. If you see an generic error from Grunt claiming that the command sass is not recognized, you should ensure your installation was successful.

 

Now let’s further configure our Gruntfile:

 

   module.exports = function(grunt) {

    grunt.initConfig({

     pkg: grunt.file.readJSON(‘package.json’),

     uglify: {

      build: {

       src: [‘js/libs/jquery-1.1.1.js’, ‘js/global.js’],

       dest: ‘js/build/global.min.js’

      }

     },

 

     sass: {

      dist: {

       options: {

        style: ‘compressed’

       },

       files: {

        ‘css/build/global.css’: ‘css/libs/global.scss’

       }

      }

     }

    });

 

    // Load plugins

    grunt.loadNpmTasks(‘grunt-contrib-uglify’);

    grunt.loadNpmTasks(‘grunt-contrib-sass’);

 

    // Default tasks

    grunt.registerTask(‘default’, [‘uglify’, ‘sass’]);

 

   };

 

As you can see the plugin formatting follows a similar pattern as uglify, with a few specific configurations:

 

‘options’ determines the output style. Here ‘compressed’ is set, minifying the CSS output. This would be most applicable for a final deployment, where the ‘expanded’ would be most applicable to aid debugging during a build. Changing this single Gruntfile parameter is easy.

‘files’ determine which CSS files to include in the compilation action. This behave in the same manner as uglify, though uses a plugin-specific syntax. Again, we reference a ‘css/libs’ folder and a ‘css/build’ folder to keep things organized.

 

Adding ‘sass’ to the final registerTask array means that first the uglify and then the sass commands are run with the default ‘grunt’ command in the CLI. Alternatively, trigger only the sass command with ‘grunt sass’.

 

Further Steps

Hopefully this tutorial has served as a solid introduction to the powers of automation that Grunt can offer a front-end developer.

 

There are of course many more gruntplugins and configurations to explore. I have had great success with the grunt-config-watch plugin, which is configurable to watch specified files and/or directories for any changes and then run the grunt command automatically (negating the need to run grunt commands in the CLI – as alluded to above). Consult the official documentation and apply the standard installation/configuration practices established above.

 

Other gruntplugins that might be of interest include:

  • grunt-config-connect – starts a local static web server (great for working with CMS’ like WordPress)
  • grunt-config-jshint – validates files with JSHint (flagging suspicious uses of Javascript in a development build)
  • grunt-config-clean – cleans up files and folder directories (another automated best practice)
  • An exhaustive directory can be viewed here.

     

    In Conclusion

    Taking the time to set up and understand Grunt.js has already proved a worthwhile experience for me personally. While it involves a little work with the command line, any obstacle I encountered was easy enough to overcome with a little searching and/or use of logic. The beauty of this approach is that it’s ‘fire and forget’ nature. Once you get it working once, the setup should be reusable across all your future projects and is easily configurable on a case-by-case basis.

     

    I will be continuing to expand my knowledge Grunt over the coming weeks and will be available to address any comments or questions. If there is enough interest in a ‘hands on’ LIFTIT session, please let me know, since I would be keen to test how Grunt can improve our collective efficiency in a team environment on top of a version-controlled Git/TFS environment.