ivogabe / gulp-typescript

A TypeScript compiler for gulp with incremental compilation support.
MIT License
839 stars 129 forks source link

gulp-typescript

A gulp plugin for handling TypeScript compilation workflow. The plugin exposes TypeScript's compiler options to gulp using TypeScript API.

This plugin works best with gulp 4. If you cannot update to this version, please see the section "Gulp 3" below.

Updating from version 2? See the breaking changes in version 3.

Build Status

How to install

1. Install gulp CLI
npm install --global gulp-cli
2. Install gulp in the project dependency
npm install gulp@4
3. Install gulp-typescript & TypeScript
npm install gulp-typescript typescript

Options

Almost all options from TypeScript are supported.

See the TypeScript wiki for a complete list. These options are not supported:

API overview

gulp-typescript can be imported using const ts = require('gulp-typescript');. It provides the following functions:

Both ts(..) and tsProject() provide sub-streams that only contain the JavaScript or declaration files. An example is shown later in the readme.

Basic Usage

Below is a minimal gulpfile.js which will compile all TypeScript file in folder src and emit a single output file called output.js in built/local. To invoke, simple run gulp.

var gulp = require('gulp');
var ts = require('gulp-typescript');

gulp.task('default', function () {
    return gulp.src('src/**/*.ts')
        .pipe(ts({
            noImplicitAny: true,
            outFile: 'output.js'
        }))
        .pipe(gulp.dest('built/local'));
});

Another example of gulpfile.js. Instead of creating the default task, the file specifies custom named task. To invoke, run gulp scripts instead of gulp. As a result, the task will generate both JavaScript files and TypeScript definition files (.d.ts).

var gulp = require('gulp');
var ts = require('gulp-typescript');
var merge = require('merge2');  // Requires separate installation

gulp.task('scripts', function() {
    var tsResult = gulp.src('lib/**/*.ts')
        .pipe(ts({
            declaration: true
        }));

    return merge([
        tsResult.dts.pipe(gulp.dest('release/definitions')),
        tsResult.js.pipe(gulp.dest('release/js'))
    ]);
});

tsResult is a stream containing the generated JavaScript and definition files. In many situations, some plugins need to be executed on the JavaScript files. For these situations, the stream has sub-streams, namely a JavaScript stream (tsResult.js) and a definition file stream (tsResult.dts). You need to set the declaration option to generate definition files. If you don't need the definition files, you can use a configuration as seen in the first example, and you don't need to store the result into a variable as tsResult.

Incremental compilation

Instead of calling ts(options), you can create a project first outside of the task. Inside the task, you should then use tsProject(). An example:

var gulp = require('gulp');
var ts = require('gulp-typescript');
var merge = require('merge2');

var tsProject = ts.createProject({
    declaration: true
});

gulp.task('scripts', function() {
    return gulp.src('lib/*.ts')
        .pipe(tsProject())
        .pipe(gulp.dest('dist'));
});

gulp.task('watch', ['scripts'], function() {
    gulp.watch('lib/*.ts', ['scripts']);
});

When you run gulp watch, the source will be compiled as usual. Then, when you make a change and save the file, your TypeScript files will be compiled in about half the time.

You must create the project outside of the task. You can't use the same project in multiple tasks. Instead, create multiple projects or use a single task to compile your sources. Usually it is not worth to create different tasks for the client side, backend or tests.

Using tsconfig.json

To use tsconfig.json, you have to use ts.createProject:

var tsProject = ts.createProject('tsconfig.json');

If you want to add or overwrite certain settings in the tsconfig.json file, you can use:

var tsProject = ts.createProject('tsconfig.json', { noImplicitAny: true });

The task will look like:

gulp.task('scripts', function() {
    var tsResult = gulp.src("lib/**/*.ts") // or tsProject.src()
        .pipe(tsProject());

    return tsResult.js.pipe(gulp.dest('release'));
});

You can replace gulp.src(...) with tsProject.src() to load files based on the tsconfig file (based on files, excludes and includes).

TypeScript version

gulp-typescript isn't restricted to a single TypeScript version. You can install the latest stable version using npm install typescript --save-dev or a nightly npm install typescript@next --save-dev.

You can also use a fork of TypeScript, if it is based on TypeScript 2.x. You can configure this in your gulpfile:

[...].pipe(ts({
    typescript: require('my-fork-of-typescript')
}));

Or in combination with a tsconfig file:

var tsProject = ts.createProject('tsconfig.json', {
    typescript: require('my-form-of-typescript')
});

Source maps

gulp-typescript supports source maps by the usage of the gulp-sourcemaps plugin. It works for both JavaScript and definition (.d.ts) files. You don't have to set sourceMap or declarationMap in your configuration. When you use gulp-sourcemaps, they will be generated automatically.

Configuring the paths of source maps can be hard. The easiest way to get working source maps is to inline the sources of your TypeScript files in the source maps. This will of course increase the size of the source maps. The following example demonstrates this approach:

var gulp = require('gulp')
var ts = require('gulp-typescript');
var sourcemaps = require('gulp-sourcemaps');

gulp.task('scripts', function() {
    return gulp.src('lib/*.ts')
        .pipe(sourcemaps.init()) // This means sourcemaps will be generated
        .pipe(ts({
            // ...
        }))
        .pipe( ... ) // You can use other plugins that also support gulp-sourcemaps
        .pipe(sourcemaps.write()) // Now the sourcemaps are added to the .js file
        .pipe(gulp.dest('dist'));
});

When you are not inlining the source content, you should specify the sourceRoot property. It can be configured with the following rule:

Furthermore you should set includeContent: false. Here's an example where outDir isn't set:

gulp.task('scripts', function() {
    return gulp.src('lib/*.ts')
        .pipe(sourcemaps.init())
        .pipe(ts({
            // ...
        }))
        .pipe(sourcemaps.write('.', { includeContent: false, sourceRoot: '../lib' }))
        .pipe(gulp.dest('dist'));
});

Some examples can be found in ivogabe/gulp-typescript-sourcemaps-demo.

For more information, see gulp-sourcemaps.

Custom transforms

You can pass aditional transforms to the compiler pipeline. We aligned with the interface of awesome-typescript-loader. You can specify transforms by setting the getCustomTransformers option.

The option expects a string, pointing at a module that exposes the transforms, or a function that returns the transforms. Its type is getCustomTransformers: (string | ((program: ts.Program) => ts.CustomTransformers | undefined)).

const styledComponentsTransformer = require('typescript-plugin-styled-components').default;

const project = ts.createProject('test/customTransformers/tsconfig.json', {
    getCustomTransformers: () => ({
        before: [
            styledComponentsTransformer(),
        ]
    });
});

Reporters

By default, errors are logged to the console and the build crashes on compiler errors. In watch mode, the build does not throw, meaning that consequent builds are still ran. Note that gulp 4 is required for this behaviour. If you are still using gulp 3, see the section "Gulp 3" below.

If you want to change the way that messages are logged to the console (or some other output), you can provide a reporter. You can specify a custom reporter as the second argument of the main function, or as the only argument when using a tsProject:

ts(options, reporter);
tsProject(reporter);

Available reporters are:

If you want to build a custom reporter, you take a look at lib/reporter.ts, that file declares an interface which a reporter should implement.

Gulp 3

This plugin works best with gulp 4. If you cannot update to this version, you may experience problems when using incremental compilations with a watcher. A compilation error will namely crash the process, which is desired in a CI environment. Gulp 4 prevents that the process crashes in watch mode. This does not happen in gulp 3, so you will need to handle that manually.

You should attach an error handler to catch those compilation errors.

gulp.src(..)
  .pipe(ts(..))
  .on('error', () => { /* Ignore compiler errors */})
  .pipe(gulp.dest(..))

Build gulp-typescript

  1. Clone this repo
  2. Execute npm install
  3. Execute git submodule update --init to pull down the TypeScript compiler/services versions used in the test suite.
  4. Ensure the gulp CLI is globally installed (npm install -g gulp-cli).
  5. Execute the tests: gulp.

The plugin uses itself to compile. There are 2 build directories, release and release-2. release must always contain a working build. release-2 contains the last build. When you run gulp compile, the build will be saved in the release-2 directory. gulp test will compile the source to release-2, and then it will run some tests. If these tests give no errors, you can run gulp release. The contents from release-2 will be copied to release.

License

gulp-typescript is licensed under the MIT license.