Setting up Grunt

Grunt is a great tool for setting up JavaScript builds and automating repetitive tasks.

We're going to need a folder and a file for this setup.

In our base project folder, create a new gruntfile.js:

touch gruntfile.js

and then also create a folder called grunt:

mkdir grunt

Next, let's add some base config to our gruntfile.

gruntfile.js


module.exports = function (grunt) {
  'use strict';

  config();
  tasks();

  function config() {
    var config = require('load-grunt-config')(grunt);
    grunt.initConfig(config);
  }

  function tasks() {
    grunt.registerTask('build', []);
  }
}

What we're doing here is we're using the load-grunt-config package we installed earlier to load our grunt configuration. Along with the load-grunt-tasks package, grunt will look in package.json and the grunt/ folder for the grunt tasks. We've also registered a single task for this project, build, which will be our main task that we will use to run our app.

Cool, let's add some additional sub-tasks to our main build task.

grunt.registerTask('build', ['tslint', 'browserify', 'connect:server', 'watch']);

We've added a handful of things here. So let's take a moment to understand what each of these things will do. Then, we'll add each of these tasks to the grunt/ folder.

tslint is a grunt task which will be responsible for linting our project. Linters are good for establishing your own "set of rules" for how code should be structured in your application. They help keep developers on the same page regarding expectations of code quality and structure.

browserify will be the tool used to bundle all the application code together. This will tie into being able to use ES6 modules and keep our code modularized.

grunt-contrib-connect is a grunt task for running a local web server for which we can run our application on. This will be nice for doing local development.

grunt-contrib-watch is a grunt task for watching all of our files we tell it about, adn then taking some action. The action we'll tell it to do may be to re-bundle our application so that we get latest JS changes. What's nice about this is we can tie is right into the web server so it will automatically "push the refresh button" for us. No more manual re-loading the web browser!!

Let's commit this change to our gruntfile

$ git add gruntfile.js
$ git commit -m 'added gruntfile'

Adding grunt task config files

Now that we have our main gruntfile established, let's add the actual tasks themselves. The nice thing about grunt is that it's all about configuration. So, you're essentially going to be creating objects as your tasks that give grunt the information it needs to run each task.

Let's start with out tslint task.

In your terminal, go into the grunt/ folder which we setup earlier. Now, we're going to be creating a series of files which will be our grunt tasks.

$ touch tslint.js

Now, let's add the code to this new file:

tslint.js

module.exports = function(grunt) {
  return {
    options: {
      configuration: grunt.file.readJSON("tslint.json")
    },
    files: {
      src: ['app.ts', 'components/**/*.ts']
    }
  }
}

There's a couple things going on here. First, we're telling it which files we're going to want it to look at for linting. We haven't created these files yet, but we will shortly. Second, we're telling this task to go look for a file named tslint.json. We haven't added this file yet, so let's go add it. Go back out to the main folder where the package.json and gruntfile.js```` files are. Here, let's add this newtslint.json``` file.

$ touch tslint.json

Great, now let's add some basic configuration for our project.

tslint.json

{
  "rules": {
    "no-unused-variable": true,
    "no-use-before-declare": true,
    "no-var-keyword": true,
    "quotemark": [true, "single"],
    "semicolon": true,
    "triple-equals": true,
    "use-strict": {
      "check-module": true
    },
    "variable-name": true
  }
}

If you're interesting in what these rules are, I encourage you to do check out the official tslint project where they explain each rule in detail.

Ok, we've successfully added the tslint task and it's accompanying json config file. Let's add this change and commit.

$ git add tslint.json
$ git add grunt/tslint.js
$ git commit -m 'added tslint task'

Next, let's add the browserify task.

Go back into the grunt/ folder. Create a new file called browserify.js

$ touch browserify.js

browserify.js

module.exports = {
  proto: {
    files: {
      'dist/bundle.js': ['app.ts', 'typings/tsd.d.ts']
    },
    options: {
      plugin: ['tsify']
    }
  }
};

Here we are telling browserify to go look for our app.ts file which will be the entry point to our application. What browserify is going to do is it will look at the first file we told it about, and then it will trace the dependency graph of the application. We'll see more how this will work once we start adding some application code. Also, we're telling it to use our tsd.d.ts file which we will be adding later. This will be important for typescript compilation. Finally, and most importantly, we're using the tsify browserify plugin which will do the work to transpile our TypeScript code. So, basically what's happening is it will transpile the code to regular JavaScript first, and then browserify will use that transpiled code to bundle the app. If you're interested to understand exactly how Browserify works, I encourage you to checkout their extremely awesome handbook!

Let's add and commit this new addition.

$ git add grunt/browserify.js
$ git commit -m 'added browserify task'

Great, now let's add the connect and watch tasks.

$ touch grunt/connect.js
$ touch grunt/watch.js

connect.js

module.exports = {
  server: {
    options: {
      port: 9999,
      hostname: '0.0.0.0',
      livereload: 35729
    }
  }
};

watch.js

module.exports = {
  scripts: {
    files: ['app.ts', 'components/**/*.ts'],
    tasks: ['browserify'],
    options: {
      spawn: false,
      livereload: true
    }
  },
  html: {
    files: ['index.html', 'components/**/*.html'],
    options: {
      spawn: false,
      livereload: true
    }
  },
  css: {
    files: ['assets/css/app.css'],
    options: {
      spawn: false,
      livereload: true
    }
  }
};

We setup the configuration for our local web server and we told the watch task which files we will interested in for it to watch. Remember, the watch and connect task are closely related so the watch task will be able to update the web server that the files have changed via the livereload: true field. This will be very useful going forward!

Let's add and commit these new file additions.

$ git add grunt/connect.js grunt/watch.js
$ git commit -m 'added connect and watch tasks'

Sweet! We are all ready to rock 'n roll as our grunt setup is now complete! Just remember, this is only the tip of the iceberg. There is still a lot more you can do with grunt that we didn't talk about here. We could do code minification, run unit tests, copy and move code around, package it for a package manager. The list goes on and on. Hopefully what you gained from this section is to understand how we can setup grunt and create tasks to do the work of repetitive tasks for us. Let's move onto the next section where we'll learn how to install our TypeScript type definitions which we will need for our project.