angularjstypescriptgulpgulp-typescript

Building Typescript for Angular using Gulp


I'm trying to setup an angularjs project according to Johnpapa's Angular Style Guide whilst using TypeScript and Gulp as a build tool. I believe Gulp is currently recommended over Grunt but I'm not very experienced with Gulp.

What I have:

My project currently looks like this:

src/
   +- ts/        # contains .ts source files
   +- typings/   # contains .d.ts typing definitions
   +- html/      # contains .html files
dist/
   +- bundle.js  # single .js file containing compiled typescript and sourcemaps

Following the angular style guide I have created a separate .ts file for each angular element.

my-app.module.ts
----------------

angular.module('myApp', []);

for initialization of the module and another for a simple implementation of a controller:

my-controller.controller.ts
----------------------------

export class MyController {
    testString = 'test';
}

angular
    .module('myApp')
    .controller('MyController', MyController);

typescript is configured using a simple tsconfig.json. (Note that filesGlob is not active yet - it will become available from TypeScript 2.0)

tsconfig.json
-------------

{
  "exclude" : [
    "node_modules"
  ],
  "filesGlob" : [
    "./src/typings/index.d.ts",
    "./src/ts/**/*.ts",
    "!./node_modules/**/*.ts"
  ],
  "compilerOptions": {
    "noImplicitAny": true,
    "target": "es5",
    "sourceMap" : true,
    "outFile" : "./dist/bundle.js",
    "removeComments": false
  }
}

What I want:

I would ideally like to

What I tried:

I've tried following the manual from the typescriptlang handbook but this uses browserify and won't work with angular.

Gulp-typescript also has a note on concatenating files but the out option doesn't work like this:

var gulp = require('gulp');
var ts = require('gulp-typescript');
var tsProject = ts.createProject('tsconfig.json');

gulp.task('default', function () {
    var tsResult = tsProject.src().pipe(ts(tsProject));

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

This configuration will output an empty file with only comments.

Another method mentioned in this question:

gulp.task('ts', function () {
    gulp.src('./src/ts/**/*.ts')
        .pipe(ts({
            noImplicitAny: true,
            out: 'output.js'
        }))
        .pipe(gulp.dest('./tmp/ts'));
});

gulp.task('default', ['ts'], function() {
    gulp.src(['./tmp/ts/output.js'])
        .pipe(sourcemaps.init())
        .pipe(uglify())
        .pipe(sourcemaps.write('/'))
        .pipe(gulp.dest('./dist/'));
});

But this gave two issues: 1. Even though I only pointed at the .ts files in ./src/ts the typescript compiler started spewing errors from .ts in ./node_modules. 2. It still didn't manage to concatenate everything.

I'm at quite a loss here. Can anyone help me set up this build script? I'm surprised I couldn't find a similar working demo anywhere.

Solution:

I've configured the gulp environment based on the solution in this answer and removed the 'export' statement for classes / objects that are not inside a typescript module.


Solution

  • If that helps, here is a Angular Typescript Gulp Tutorial that has a basic TypeScript, Angular, Gulp, etc. setup that concatenate the app and the vendor/nodes files. There is the demo code on github.

    /* File: gulpfile.js */
    
    // grab our gulp packages
    var gulp  = require('gulp');
    // Include plugins
    var plugins = require("gulp-load-plugins")({
        pattern: ['gulp-*', 'gulp.*', 'main-bower-files', 'del'],
        replaceString: /\bgulp[\-.]/
    });
    
    var browserSync = require('browser-sync').create();
    var reload      = browserSync.reload;
    
    
    // create a default task to build the app
    gulp.task('default', ['jade', 'typescript', 'bowerjs', 'bowercss', 'appcss'], function() {
      return plugins.util.log('App is built!')
    });
    

    In my example, we use Jade to HTML:

    // Jade to HTML
    gulp.task('jade', function() {
        return gulp.src('src/**/*.jade')
            .pipe(plugins.jade()) // pip to jade plugin
            .pipe(gulp.dest('dist')) // tell gulp our output folder
            .pipe(reload({stream: true}))
            ;
    });
    

    For TypeScript, we compiled into one single app.js file:

    // TYPESCRIPT to JavaScript
    gulp.task('typescript', function () {
        return gulp.src('src/**/*.ts')
            .pipe(plugins.typescript({
                noImplicitAny: true,
                out: 'app.js'
            }))
            .pipe(gulp.dest('dist/js/'))
        .pipe(reload({stream: true}))
        ;
    });
    

    For bower, we merge all the js files in vendor.js and CSS in vendor.css:

    // BOWER
    gulp.task('bowerjs', function() {
    
        gulp.src(plugins.mainBowerFiles())
            .pipe(plugins.filter('**/*.js'))
        .pipe(plugins.debug())
            .pipe(plugins.concat('vendor.js'))
            .pipe(plugins.uglify())
            .pipe(gulp.dest('dist/js'));
    
    });
    
    gulp.task('bowercss', function() {
        gulp.src(plugins.mainBowerFiles())
            .pipe(plugins.filter('**/*.css'))
        .pipe(plugins.debug())
            .pipe(plugins.concat('vendor.css'))
            .pipe(gulp.dest('dist/css'));
    
    });
    

    Custom CSS:

    // APP css
    gulp.task('appcss', function () {
        return gulp.src('src/css/**/*.css')
            .pipe(gulp.dest('dist/css/'))
            .pipe(reload({
                stream: true
            }));
    });
    
    // CLEAN
    gulp.task('clean', function(done) {
        var delconfig = [].concat(
            'dist',
            '.tmp/js'
        );
    
        // force: clean files outside current directory
        plugins.del(delconfig, {
            force: true
        }, done);
    });
    

    This is what reloads the browser when changes occur:

    // Watch scss AND html files, doing different things with each.
    gulp.task('serve', ['default'], function () {
    
        // Serve files from the root of this project
        browserSync.init({
            server: {
                baseDir: "./dist/"
            }
        });
    
        gulp.watch("src/**/*.jade", ['jade']).on("change", reload);
        gulp.watch("src/**/*.ts", ['typescript']).on("change", reload);
            gulp.watch("src/**/*.css", ['appcss']).on("change", reload);
    });
    

    My tsconfig.json looks like this... I put the JS files that are automatically compiled from the text editor (Atom) into .tmp/js/atom ... some people put the .js in the same directory as the .ts but I find it confusing... less files is better for me:

    {
      "compilerOptions": {
        "target": "ES5",
        "module": "commonjs",
        "outDir": ".tmp/js/atom"
      },
      "exclude": [
        "node_modules",
        "typings"
      ]
    }