UI Build and Deployment

using grunt to automate build and deployment

What is Grunt?

There are many references available online to describe this question but to keep it very simple, Grunt is a Javascript task runner which allows us to automate task such as compilation, minification, unit testing, linting etc.

Iteration 1 - A Sample Application:

To start with, we will be referring to a very simple UI application which shows an alert on page load. It can be downloaded from here. It contains three folders
  1. css
  2. html and
  3. javascript
On this application we will be adding below tasks for Grunt automation
  1. JS Lint,
  2. JS Minification,
  3. CSS Minification and
  4. HTML Compression

Iteration 2 - GruntFile.js:

To begin with any Grunt based setup we need to create a file "Gruntfile.js". Its a simple JavaScript file which define various tasks which can be executed during our build phase.
// a sample Grunt File
module.exports = function(grunt) {
  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    uglify: {
      dist: {
        files: {
          'dist/<%= pkg.name %>.min.js'
        }
      }
    }
  });

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

  grunt.registerTask('default', ['jshint']);
}
If we go through the code, we will notice that this file begins with initializing a function under module.exports. This function is used to initialize our configuration using grunt.initConfig which we will later use in build automation.
Also if we notice, we are reading a JSON file package.json, this file will contain configurable properties (as a JSON object) which we can use while defining any task (as can be seen with pkg.name in above example).
For our sample application we will be creating a gruntfile.js at root folder with code as below:
module.exports = function(grunt) {
  grunt.initConfig({

  });
}

Iteration 3 - Package.json:

We will be create a plain JSON file (on same location as gruntfile.js) with 5 properties
  1. name
  2. version
  3. jsFileName
  4. cssFileName
  5. htmlFileName
Final output will look as below
{
  name: "grunt-demo",
  version: "1.0.0",
  jsFileName: "main",
  cssFileName: "main",
  htmlFileName: "index"
}
We now need to modify our 'gruntfile.js' to read this JSON
module.exports = function(grunt) {
  grunt.initConfig({
    pkg: grunt.file.readJSON("package.json")
  });
}

Iteration 4 - NodeJs:

To automate a build using grunt we need NodeJs. Make sure that NodeJs is properly installed and configured on the machine. Once installed, open a command line and run the command as shown below
npm install -g grunt-cli
This will install grunt globally in the build machine and can be accessed from anywhere using a command line. For more reference click here

Iteration 5 - JS Lint:

The first task that we will be configuring is JSHint. It is a community-driven tool to detect errors and potential problems in JavaScript code. It is very flexible so we can easily adjust it to our particular coding guidelines and the environment we expect our code to execute in. For more reference click here
In order to add this task we need to modify gruntfile as below
module.exports = function(grunt) {
  grunt.initConfig({
    pkg: grunt.file.readJSON("package.json"),
    jshint: {
      files: ["gruntfile.js", "javascript/*.js"],
    }
  });

  grunt.loadNpmTasks("grunt-contrib-jshint");

  grunt.registerTask("default", ["jshint"]);
}
In the above declaration we are doing two tasks
  1. Under key 'files' we are telling JSHint to read all files matching the pattern in array
  2. We have asked grunt to load the required dependency and have registered a default task which will help us execute jshint when grunt command is triggered
We are now ready to test our build but before we do so we need to ensure that node dependency for grunt-contrib-jshint is downloaded and available. To ensure this we will run below command at root folder of our application in a command line
npm install grunt-contrib-jshint --save-dev
Once this command is successfully executed, we will notice few changes in our package.json file. It will now have a new entry under devDependencies tag. This entry will be useful later for us to install dependencies directly using npm install.
Go ahead and type grunt in a new command line window. This will execute default task for us (we can also type 'grunt jshint' to only execute jshint task) and we will see message as below
Running "jshint:files" (jshint) task
>> 2 files lint free.

Iteration 6 - More Tasks:

Now that we have a working setup ready, lets quickly add three more tasks. We need to run below commands in a command line
npm i grunt-contrib-uglify --save-dev
npm i grunt-contrib-cssmin --save-dev
npm i grunt-contrib-htmlmin --save-dev
Once above commands are executed we need to update gruntfile as shown below
module.exports = function(grunt) {
  grunt.initConfig({
    pkg: grunt.file.readJSON("package.json"),
    jshint: {
      files: ["gruntfile.js", "javascript/*.js"],
    },
    uglify: {
      dist: {
        files: {
          "target/javascript/<%= pkg.jsFileName %>.js": ["javascript/*.js"]
        }
      }
    },
    cssmin: {
      dist: {
        files: {
          "target/css/<%= pkg.cssFileName %>.css": ["css/*.js"]
        }
      }
    },
    htmlmin: {
      dist: {
        options: {
          removeComments: true,
          collapseWhitespace: true
        },
        files: {
          "target/html/<%= pkg.htmlFileName %>.html": "html/index.html"
        }
      }
    }
  });

  grunt.loadNpmTasks("grunt-contrib-jshint");
  grunt.loadNpmTasks("grunt-contrib-uglify");
  grunt.loadNpmTasks("grunt-contrib-cssmin");
  grunt.loadNpmTasks("grunt-contrib-htmlmin");

  grunt.registerTask("default", ["jshint", "uglify", "cssmin", "htmlmin"]);
}
If we run grunt now in command-line we will notice that a new target folder is created which has minified version of our JavaScript, CSS and HTML file. We can always add new task as per requirement. For details on that refer here.

Comments

Popular posts from this blog

Angular JS, Require JS and Karma

Google Chrome Extension - ReactJS

Anonymous Functions - Elixir