A TypeScript Angular2 starter project walkthrough – the File Watcher pipeline

by
Tags: , , ,

Note – This was last updated on 11/9/2015. The Angular team has since developed Angular CLI as a project starter platform, which has moved on to using WebPack 2.0 and provides lots of features such as testing and deployment features out of the box.

This blog entry is kept published for historical reasons.

Installing Gulp – our build tool

To do this work you'll need to install the gulp global
library. For the OSX and Linux users who installed Node as
Root, you'll need to prefix this with sudo. (Check out the
awesome nvm project to run Node in userland,
and to install different versions of node, ala the Ruby rvm
version manager).

npm install -g gulp

The -g flag forces npm to install the gulp package
globally, which provides the command in your path for every project.

Next, you'll set up your Node project. Do something like this:

mkdir my-project
cd my-project
npm init

The npm init command will take you through a series of
questions. Don't install a module system when prompted, take
the defaults on all items, including the entry point, and
allow it to save your new file, package,.json.
Here are the contents of mine:

{
  "name": "my-project",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}

Install “local gulp” and the plugins we'll use

Next, we tell npm to locally install the Gulp API.

Note:It is very important that we use the exact syntax below for
each local package, which both installs the package in node_modules
and marks the installation in the <devDependencies>section of package.json
:

npm install --save-dev gulp

This test should output that both the CLI and local versions of Gulp
are installed and the versions match:

gulp -v
[10:24:20] CLI version 3.9.0
[10:24:20] Local version 3.9.0

Set up the Gulp build script

In our project, we'll develop live code out of the web
directory, compiling TypeScript on-the-fly to web/js
and serving Angular and other features out of the web/lib
directory. These two tasks will be performed by gulp, a
node-based development tool.

Let's create gulpfile.js in the root of our project.
This is the file that gulp will use to store its tasks:

var gulp = require('gulp');
gulp.task('setup', function(done) {
  gulp.src([
    'node_modules/angular2/bundles/js',
    'node_modules/angular2/bundles/angular2.*.js*',
    'node_modules/angular2/bundles/http.*.js*',
    'node_modules/angular2/bundles/router.*.js*',
    'node_modules/es6-shim/es6-shim.js*',
    'node_modules/systemjs/dist/*.*',
    'node_modules/jquery/dist/jquery.*js',
    'node_modules/bootstrap/dist/js/bootstrap*.js',
    'node_modules/@reactivex/rxjs/dist/global/Rx.js'
  ]).pipe(gulp.dest('web/lib'));
  gulp.src([
    'node_modules/bootstrap/dist/css/bootstrap.css'
  ]).pipe(gulp.dest('web/css'));
});

This script defines a task, setup, that can be invoked
from the shell in this way:

gulp setup

Although the files aren’t installed just yet, this task will copy the
right libraries for our ES5 + System.JS config
into our library deployment directory, web\lib.
There are better ways to do this work, including using a smarter
loading process, but I find teasing the files apart by hand helps
illustrate the dependencies an Angular2 project will actually need.

Adding our file management tasks

We’ll start out by defining a task for copying any static assets
from the src directory to the web/ directory,
and we’ll call it assets:

// called when we change our non-TypeScript assets
gulp.task('assets', function() {
  gulp.src(['./src/**/*.json',
            './src/**/*.html',
            './src/**/*.css'])
    .pipe(gulp.dest('./web'));
});

Note that the script above can be extended to support other file types, and
can easily copy to more than one location. Also note the general Gulp file
processing mechanism is to stream a series of files from one place to another.

Gulp tasks generally take source files and pass them through a chain of
plugins. So you can imagine it as a series of pipes:

  // example
  gulp.src(['**/*.js', ...]).pipe(pluginOne({ ... }).pipe(pluginTwo({ ..})...
  gulp.dest('some.folder/');

Implementing a watchdog start task

We’ll also create a task that watches any files in the web/
directory, so that any changes there (for testing purposes) also

and we add our watcher top-level task, which delegates calls to the
tasks listed in the array at the end of the function:

Gulp has some pretty nifty ways to allow tasks to depend on each other.
For example, a task depends on another task if the second parameter of that task
is one task name, or an array of task names.

gulp.task('watch',
          ['watch.assets',
           'watch.ts',
           'watch.web']);

The task above, watch, depends on firing off three other
tasks – watch.assets, watch.ts, and watch.web.
Those files actually use the Gulp watch function to observe files
and execute commands once the files themselves change.

A bare-bones asset pipeline

The first of these tasks, watch.assets, copies any changed files
in ./src when they change, and calls our assets script
above to march them over to the ./web directory.

// copies non-typescript assets by calling
// the assets task when any source files change
gulp.task('watch.assets', ['assets'], function() {
  return gulp.watch(['./src/**/*.json',
                     './src/**/*.html',
                     './src/**/*.css'],
                    ['assets']);
});

Stubbing out our TypeScript compiler

Up next, our TypeScript compiler. But we’re not ready to discuss that yet,
we want to keep this article focused on Gulp. So we’ll stub out the implementation
but provide the watcher now.

// calls the typescript compiler whenever
// any typescript files change
gulp.task('watch.ts', ['ts'], function() {
  return gulp.watch('src/**/*.ts', ['ts']);
});
gulp.task('ts', function() {
  console.log('Typescript compilation here.');
});

This watcher keeps an eye on any TypeScript file (we’ll see in the next
post that TypeScript files have an extension of .ts), and passes
it along to the TypeScript compiler task, ts which we currently
just use to log a console message.

Finally, the web directory change detector

This one is overkill, but we’ll also watch web/ for any
changes, in case we’re just testing the effects of something in that directory.

// stubbed out function (to implement later)
function notifyLiveReload() { }
// if we change any files in the web directory
// reload the page - this is for when we
// update any of our assets with the
// setup command or drop a file in the content
// area of web like a css file...
gulp.task('watch.web', function() {
  gulp.watch('web/**', notifyLiveReload);
});

The default task – start ‘er up!

Finally, we give the user a default task, which launches
our three main backgrounders – express, livereload,
and watch. You probably should run setup the first time,
so that you get the assets (and you may make setup smarter).

gulp.task('default',
          ['express',
           'livereload',
           'watch']);

Now we’ll install our Angular libraries, System.JS and other dependencies and shims, so we can have something to
deploy with our setup task

Angular libraries and dependencies install

Our Angular2 Runtime environment needs several key files:

  • angular2.dev.js – the Angular2 ES5 library, running with the System.JS module loader
  • http.dev.js – the Angular2 network library
  • router.dev.js – the new Angular2 router for routing across views
  • system.js – the System.JS ES5 Module runtime library
  • es6-shim.js – a shim file to support ES6 API features in ES5 browsers

Install these libraries into your NodeJS directory with the following commands (note how they don’t use --save-dev,
only --save – as there are no tools to add to your project’s path.

npm install --save angular2 system.js es6-shim

These libraries are optional and will be used in later posts:

npm install --save bootstrap jquery

Now you’re ready to move on to setting up the TypeScript compiler.

Series Table of Contents