TypeScript and Gulp

Introduction

As much as I enjoy using Visual Studio, I feel that TypeScript development can be a bit more streamlined. I have investigated various tools to help out, and gulp has caught my eye. Unlike grunt, which was a contender for task automation, gulp allows the creation of automation tasks via code. In this case, we can create such tasks in JavaScript. Although grunt is quite flexible, task automation is achieved via JSON configurations. It may come down to personal preference, and in my case, I chose to dive into gulp.

Today we are going to create a simple script for building and running a typical node.js application written in TypeScript. Let’s get to it.

Install Gulp

You can install gulp via npm:

$ npm install -g gulp

You can also add the following development dependencies in your project’s package.json:

"devDependencies": {
    "gulp": "^3.8.5",
    "gulp-tsc": "^0.9.1",
    "gulp-shell": "^0.2.5",
    "run-sequence":"^0.3.6"
}

This will install all the packages we will use in our gulpfile. Note that these dependencies will only be installed in a development environment:

  • gulp: The core gulp package.
  • gulp-tsc: TypeScript compiler for gulp.js
  • gulp-shell: command-line interface for gulp.js. Useful for when we want to run the application via gulp.
  • run-sequence: run a series of tasks in a certain order. For example, build and then run.

Create a gulpfile

For gulp to work properly, we need to define gulpfile.js. Here we can define all sorts of tasks to aid in building and running our application, among other things. Let’s create a basic gulpfile that can handle building and running our application that is written in TypeScript:

var gulp   = require('gulp');
var tsc    = require('gulp-tsc');
var shell  = require('gulp-shell');
var runseq = require('run-sequence');

var paths = {
    tscripts : { 
        src : [
            './server.ts',
            'app/**/*.ts', 
            'public/**/*.ts', 
            'Scripts/**/*.ts'
        ],
        dest : './' 
    }
};

gulp.task('default', ['buildrun']);

// Run

gulp.task('run', shell.task([
    'node ./server.js'
]));

gulp.task('buildrun', function (cb) {
    runseq('build', 'run', cb);
});

// Compile

gulp.task('build', ['compile:typescript']);
gulp.task('compile:typescript', function () {
    return gulp
        .src(paths.tscripts.src)
        .pipe(tsc({
            module: "CommonJS",
            sourcemap: true,
            emitError: false
        }))
        .pipe(gulp.dest(paths.tscripts.dest));
});

This file defines a few tasks:

  • build, which compiles the TypeScript code, generating the JavaScript and map files alongside their respective TypeScript files. We can define a different destination for the generated JavaScript, but we will keep it the source and destination directories the same for now.
  • run, which executes the application.
  • buildrun, which performs a build, followed by a run.

Running the command “gulp” will build the solution and run it: image

You can also run more specific commands like “gulp build”: image

and “gulp run”: image

All Done!

So we were able to get all some gulp packages together and are able to compile and run a node application that’s been written in TypeScript.

Wait!

Although this file handles a lot of scenarios quite nicely, it’s written in JavaScript. The aim is to get as much as possible written in TypeScript. So let’s keep digging… The extension typescript-require enables the use of require for modules written in TypeScript without needing them to be compiled to JavaScript beforehand. This includes gulpfiles! So we can move all the code from gulpfile.js to gulpfile.ts, and the gulpfile.js would have the following two lines:

require('typescript-require');
require('./gulpfile.ts');

Now we add a reference to the node definition file to our new gulpfile.ts:

/// <reference path='Scripts/typings/node/node.d.ts' />

So without further modifying the actual code, and taking advantage of the fact that TypeScript is a superset of JavaScript and it should work: image

Well, that’s not good. After doing a bit of digging, it appears that there’s some kind of problem with the versions of lib.d.ts used in the different packages. Running a local search and sure enough we see a problem: image

So let’s replace that with the proper version and… success! Let’s hope typescript-require gets updated soon so this step can be avoided.

After a few modifications, we can have gulpfile.ts be a bit more TypeScript-y:

/// <reference path='Scripts/typings/node/node.d.ts' />

var gulp   = require('gulp');
var tsc    = require('gulp-tsc');
var shell  = require('gulp-shell');
var runseq = require('run-sequence');

var paths = {
    tscripts : { 
        src : [
            './server.ts',
            'app/**/*.ts', 
            'public/**/*.ts', 
            'Scripts/**/*.ts'
        ],
        dest : './' 
    }
};

gulp.task('default', ['buildrun']);

// Run

gulp.task('run', shell.task([
    'node ./server.js'
]));

gulp.task('buildrun', (cb) => {
    runseq('build', 'run', cb);
});

// Compile

gulp.task('build', ['compile:typescript']);
gulp.task('compile:typescript', () => {
    return gulp
        .src(paths.tscripts.src)
        .pipe(tsc({
            module: "CommonJS",
            sourcemap: true,
            emitError: false
        })) 
        .pipe(gulp.dest(paths.tscripts.dest));
});

Conclusion

Today we were able to get everything together to compile and run a node application that’s been written in TypeScript, using gulp instead of the tools provided by Visual Studio. We also took the extra step to get the gulp code, traditionally written in JavaScript, ported over to TypeScript. We now have a solid basis to add more tasks according to the project’s needs.

Cheers!

Advertisements

4 thoughts on “TypeScript and Gulp

  1. Thanks for this great getting-started article for someone with TypeScript knowledge, but little node or gulp knowledge. I’d like to add a more robust / programatic solution to the lib.d.ts issue. Since these files are in node_modules, changes to them will not persist beyond npm installs, etc…

    The specific problem here is that typescript-require has it’s own copy of lib.d.ts for some reason (it’s shipped with the typescript module), and that version is out-of-date. It also has a copy of node.d.ts, but as per your instructions, we using our own (though a explanation of how to get that .d.ts would be useful in this article; the short version is to use tsd.).

    To programmatically fix this (though it is still a hack), install node-fs-extra and typescript as development dependencies of your project, and modify your gulp.js file to look like this:

    // Hack to use the typescript lib.d.ts instead of the one in the typescript-require module
    var fs = require(‘node-fs-extra’);
    fs.copySync(“./node_modules/typescript/bin/lib.d.ts”, “./node_modules/typescript-require/typings/lib.d.ts”);

    require(‘typescript-require’);
    require(‘./gulpfile.ts’);

  2. Thanks for this great getting-started article for someone with TypeScript knowledge, but little node or gulp knowledge. I’d like to add a more robust / programatic solution to the lib.d.ts issue. Since these files are in node_modules, changes to them will not persist beyond npm installs, etc…

    The specific problem here is that typescript-require has it’s own copy of lib.d.ts for some reason (it’s shipped with the typescript module), and that version is out-of-date. It also has a copy of node.d.ts, but as per your instructions, we using our own (though a explanation of how to get that .d.ts would be useful in this article; the short version is to use tsd.).

    To programmatically fix this (though it is still a hack), install node-fs-extra and typescript as development dependencies of your project, and modify your gulp.js file to look like this:

    // Hack to use the typescript lib.d.ts instead of the one in the typescript-require module
    var fs = require(‘node-fs-extra’);
    fs.copySync(“./node_modules/typescript/bin/lib.d.ts”, “./node_modules/typescript-require/typings/lib.d.ts”);

    require(‘typescript-require’);
    require(‘./gulpfile.ts’);

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s