Automating TypeScript with
Node And Grunt

Over the past week I have been playing around with NodeJS trying to figure out how to help make my own workflow more productive. Coming from a Flash and Java background I have been using Ant for years to automate builds of my projects and after switching over to HTML5 dev I find Ant not only overkill but a pain to set up, especially on Windows. So last week while I started doing some code experiments in TypeScript I decided to also dig into NodeJS and finally set up Grunt. Here is little background on how I got everything setup.

Most people consider NodeJS only a server side technology but it actually is incredibly useful when running locally. It’s easy to setup, simply go to http://nodejs.org/ and download the installer for your OS.

Once you install NodeJS you will be able start setting up a project workflow. There are a few automation tools out there but I ended up settling on one called Grunt. Grunt is basically the NodeJS equivalent of Ant and for the most part actually better in many ways. In order to install Grunt or any other NodeJS modules you need to do a few things on the command line. So open that up.

To install NodeJS modules we will be using a utility that comes with the NodeJS install called NPM. This command line based app will go out and fetch modules for you and install them globally or in your project. To get started simply type the flowing to make sure NPM is correctly installed:

> npm

This will run you through all the commands you can use with npm. For this post it will help if you already have a project created or make one from scratch. I’m just going to go over a few of the basic setup commands I used for my last project.

On a side note, if you are on Windows and have GitBash installed you can use that instead of the command prompt. This is incredibly helpful for people like me who come from a Unix background so you can still continue to use all the commands you are used to. This was something I was never able to get running with Ant since GitBash didn’t see the install paths correctly and I gave up on using CygWin a long time ago.

So once you are in a project you are going to want to set up a log to keep track of all the modules your project needs. To do this just cd into your project’s directory and type the following:

> npm init

This will start a utility to create a configuration file to store all of your modules. Simply walk through the setup questions:

When you are done you will be asked to save everything like this

Now you should have a file named package.json. Next we are going to want to add some modules to your project. Go back to the command line and type in the following:

> npm install grunt –save-dev

This command will install grunt into your project folder and also update our package.json with information on the module we just installed. This is critical for sharing your build with others so they can clearly see the dependencies your project may have. If you open up your package.json file you will now see a new object at the end called devDependancies:

 "devDependencies": {
    "grunt": "~0.4.1"
 }

Now you will have everything you need to run Grunt but without any context this is not going to be very useful. So for the rest of the post I am going to talk about the setup I am using in my own project and how I configured it to work together. The project I am working with is based on TypeScript. I have talked about TypeScript before but one of the things I really love about it is how easy it is to generate clean looking JavaScript from a strongly typed language syntax. One of the downsides of course is that you need a compiler to do this. While it is very easy to use TypeScript in Visual Studio, it is still a bit limiting. If you are not on Windows than you will most likely need to use some other editor so the only way to get TypeScript running will be with NodeJS. So to cut my losses I designed a workflow that allows me to rely on NodeJS the entire time and still use Visual Studio to write all of my code.

The last thing I want to cover before I get into the setup is what my goal of the automation is. Here is a high level overview of that:

  1. Continuously compile TypeScript into JavaScrpit
  2. Place all generated code into an output folder
  3. Run a server so I can locally test the output

There are a lot more things we can do with Grunt tasks but for the sake of trying to keep this short we will focus on just the first 3 things.

So in order to have NodeJS compile Typescript we are going to need the TypeScript module and Grunt Typescript task:

> npm install typescript –save-dev
>npm install grunt-typescrpit –save-dev

This will set us up with all the TypeScript dependencies. Now we need a way to automatically watch our code to see when changes happen and call the compiler. To do this we are going to take advantage of a Grunt task called watch.

>npm install grunt-contrib-watch –save-dev

You can read more about how watch works here. The final thing we are going to need to do is install something to server our project with. We’ll use Connect. This will give us a sever among other things.

>npm install grunt-contrib-connect –save-dev

The last thing we will need a way to open up a url:

>npm install grunt-open –save-dev

By now your devDependencies should look something like this:

"devDependencies": {
    "grunt": "~0.4.1",
    "typescript": "~0.8.3",
    "grunt-typescript": "~0.1.3",
    "grunt-contrib-watch": "~0.3.1",
    "grunt-contrib-connect": "~0.2.0",
    "grunt-open": "~0.2.0"
}

From here we will need to setup a GruntFile. Think of this as our Ant build.xml. Create a new GruntFile.js in your project and copy the following into it:

module.exports = function (grunt) {
    grunt.loadNpmTasks('grunt-typescript');
    grunt.loadNpmTasks('grunt-contrib-watch');
    grunt.loadNpmTasks('grunt-contrib-connect');
    grunt.loadNpmTasks('grunt-open');
 
    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),
        connect: {
            server: {
                options: {
                    port: 8080,
                    base: './'
                }
            }
        },
        typescript: {
            base: {
                src: ['lib/**/*.ts'],
                dest: 'js/PixelVisionJSDemos.js',
                options: {
                    module: 'amd',
                    target: 'es5'
                }
            }
        },
        watch: {
            files: '**/*.ts',
            tasks: ['typescript']
        },
        open: {
            dev: {
                path: 'http://localhost:8080/index.html'
            }
        }
    });
 
    grunt.registerTask('default', ['connect', 'open', 'watch']);
 
}

So with this file you should be able to CD into your directory with the GruntFile.js and run it. Let’s go through how the file works before we run it.

To start off we need in import the modules we need so you will see the following:

grunt.loadNpmTasks('grunt-typescript');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-connect');
grunt.loadNpmTasks('grunt-open');

Here we are simply importing the grunt tasks for TypeScript, watch, connect and open. After this we need to configure each command. Our first one sets up the server:

connect: {
    server: {
        options: {
            port: 8080,
            base: './'
        }
    }
}

The name of the object, “connect”, is how we will reference this task later on when we run it. Each task takes a set of options, in this case we are configuring a server with port 8080 and a base directory to server from as the root of the project. Next we need to configure TypeScript where to compile:

typescript: {
    base: {
        src: ['lib/**/*.ts'],
        dest: 'js/PixelVisionJSDemos.js',
        options: {
            module: 'amd',
            target: 'es5'
        }
    }
}

Here you will see we set our source directory to a folder called lib and have it recursively search every folder for any file ending in .ts. You can put your TypeScript files anywhere but I tend to keep them in their own folder called lib or src. Now we need to let Grunt know it needs to watch this project for changed:

watch: {
    files: '**/*.ts',
    tasks: ['typescript']
}

Here you can see we are setting up the watch task to look for any changes to .ts files and when that happens simply call our typescript task which will call the TypeScript compiler. Now all we need is to set up a simple open task:

open: {
    dev: {
        path: 'http://localhost:8080/index.html'
    }
}

This will allow us to open up our computer’s default browser and point it to our localhost.

The last thing we need to do is create some tasks to run this. In order to do this we simply register a task with Grunt:

grunt.registerTask('default', ['connect', 'open', 'watch']);

The default task will be called whenever we run grunt in our directory. The order of the tasks being called in very important. Connect will setup our server, open will point the browser to the server and watch will continue to run as it looks for changes to our .ts files. Watch is considered a “blocking task” so if we tried to run it first we wouldn’t be able to run anything else. As you will see in your terminal, it will not allow you to enter anything else in while watch is running.

Now that we have gone over how the task works you simply cd into the directory with the GruntFile.js and call grunt:

> grunt

With everything property configured to your own project structure you should see your browser open up when you run the grunt build. While this was just a brief introduction on how all of this works I’ll continue to post more details as I fine tune my automation tasks and use it in my future projects.

Subscribe To My Mailing List

Want to learn how to make a game? Not sure where to start? Even if you are a seasoned game maker there is still a lot you can learn from my free 15 page guide on how to build, release and market a game.

Simply sign up for my mailing list and also get access to great tips and advice on how to make better games. I promise to not spam your inbox!

Join Now