Patrick Desjardins Blog
Patrick Desjardins picture from a conference

Visual Studio Code with NPM and TypeScript (Part 3 : Gulp)

Posted on: 2017-02-27

Previously, we setup TypeScript but had two problems. The first one was that we couldn't move the vendor library and the second that we couldn't test the generated TypeScript file since we do not have a web server running. We will tackle the first problem by using Gulp. Gulp is like MsBuild, it's only goal is to help you automate some tasks.

To be able to use Gulp, we need to download gulp. It's convenient since we can get it from npm by executing the install command.

npm install gulp --save-development //or npm install gulp -g

Create a Gulp's configuration file : gulpfile.js at the root, sibling to TypeScript's configuration file, and typings' configuration file. We will need some additional node modules to help us working inside Gulp. You can get the one we will use by typing:

 npm install gulp-typescript --save-development 
 npm install typescript --save-development 
 npm install del --save-development 
 npm install gulp-sourcemaps --save-development

This file is pure JavaScript that use Node's module to execute JavasScript for specific tasks. The task runner is Gulp. The goal is to be able in the console to write:

gulp clean //or gulp build

The Gulp file that we will build is simple for the case of learning how to use Gulp to do:

Clean up previous build file

Use TypeScript to compile

Move vendors (third-party) libraries

Before creating these 3 Gulp's tasks, we need to instanciate some node's modules that will help us doing the work.

var gulp = require('gulp');
var tsc = require('gulp-typescript');
var del = require('del');
var sourcemaps = require('gulp-sourcemaps');
var tsProject = tsc.createProject('tsconfig.json');

The main one is Gulp which is the runner. This will create entry point that we can invoke by the command line. Gulp-typescript will read our tsconfig.json and use TypeScript to build the code. Del is a library to delete files, which we will use to delete old files. Gulp-sourcemaps is used to create .map.js file, for debugging purpose. Finally the last line just load the TypeScript configuration by using gulp-typescript module that we just loaded.

The next step is to define some constants to help us having a cleaner Gulp code.

var paths = {
  webroot: "./",
  node_modules: "./node_modules/",
  typescript_in: "./src/",
  typescript_out: "output",
  typings: "./typings/",
  typescript_definitions: "./typings/main/**/*.ts",
};
paths.allTypeScript = paths.typescript_in + "**/*.ts";
paths.modulesDestination = paths.webroot + "vendors/";

Here start the fun. Let's create the first Gulp task to delete previous generated JavaScript file and mapping file from TypeScript. This is required because even if TypeScript will overwrite previous JavaScript generated, if you delete a TypeScript, the JavaScript will hang in the folder which can cause size problem once we bundle all JavaScript.

gulp.task("clean", function (callback) {
  var typeScriptGenFiles = [
    paths.typescript_out + "/**/*.js",
    paths.typescript_out + "/**/*.js.map",
  ];

  del(typeScriptGenFiles, callback);
});

The syntax is the name of the task, followed by an optional depencency array (not present in this example) and a callback function. The purpose of this callback is to notify Gulp that the task is done.

In our simple case, we pass an array of file patterns to a node's module to delete the file. Nothing complex. Let's move on to the vendor copy task.

The second task is to copy vendors library. This time, we use Gulp's functions that will get a list of library, get the code, pipe the result into a new destination.

gulp.task("copy", function () {
  var modulesToMove = { jquery: "jquery/dist/jquery*.{js,map}" };

  for (var destinationDir in modulesToMove) {
    gulp
      .src(paths.node_modules + modulesToMove[destinationDir])
      .pipe(gulp.dest(paths.modulesDestination + destinationDir));
  }
});

Finally, the biggest task : building. This need to create the TypeScript and if needed definition files. We use the tsProject variable that come from the tsconfig.json, get all the source, generate the source map, compile. After, we take all definition file and write everything at the destination path. Then, we do the same with JavaScript file where we write the source map and source in the destination folder. Here how it looks:

gulp.task("build", function () { 
  var sourceTsFiles = [paths.typescript_in, paths.typescript_definitions]; 
  var compilationResults = tsProject.src()
  .pipe(sourcemaps.init())
  .pipe(tsProject()); 
  compilationResults.dts.pipe(gulp.dest(paths.typescript_out)); 
  return compilationResults.js
    .pipe(sourcemaps.write('.'))
    .pipe(gulp.dest(paths.typescript_out)); 
});

So far, we can go in the console and use: gulp clean or gulp copy or gulp build. We saw that we can have a second parameter that is an array of task name, we could have a task that execute these three tasks we just created which will allow us to execute gulp buildall and have the clean, copy and build executed.

gulp.task("buildall", ["clean", "copy", "build"], function (callback){ callback(); });

Gulp's tasks could be even more complex, do JavaScript minimization, bundle, etc. The idea is that you need to find existing node modules (or create your own) and use it.