Build Angular2 HTML and TypeScript to a single file

If you’re using the default Angular2 tsconfig.json with SystemJS loader:

"module": "system",
"moduleResolution": "node",
...

You can leave all the heavy work on SystemJS Build Tool. This is for example how I do it in my projects using gulp:

  1. Compile *.ts and inline *.html templates

    I’m using gulp-angular-embed-templates right away to inline all templateUrl into template strings (this plugin works with both Angular 1.* and Angular 2).

    var tsc = require('gulp-typescript');
    var tsProject = tsc.createProject('tsconfig.json');
    var embedTemplates = require('gulp-angular-embed-templates');
    
    gulp.task('app.build', function () {
        var tsResult = gulp.src('app/src/**/*.ts', {base: './app/src'})
            .pipe(embedTemplates()) // inline templates
            .pipe(tsc(tsProject));
    
        return tsResult.js
            .pipe(gulp.dest('build/js'));
    });
    

    This will generate many anonymous System.register modules in build/js directory. All of them will have their templateUrl inlined already.

  2. Bundle everything into a single .js file

    I use SystemJS Build Tool for this because I think it’s way easier than using for example webpack. So, I have another gulp task for this to automate the process:

    var SystemBuilder = require('systemjs-builder');
    
    gulp.task('app.systemjs.bundle', function () {
        var builder = new SystemBuilder('build/js, {
            paths: {
                '*': '*.js'
            },
            meta: {
                'angular2/*': {
                    build: false
                },
                'rxjs/*': {
                    build: false
                }
            }
        });
    
        return builder.bundle('main', 'build/js/app.bundle.js');
    });
    

    The builder takes the same options as SystemJS so check their Config API.

    Calling builder.bundle('main', ...) searches for main.js (which is my initial script with Angular’s bootstrap call. It’s the same file you can see in the 5 min quickstart) because I append .js to all paths searched by the builder. This is because when you import a module in TS you usually call:

    import {ModalResultComponent} from './modal-result.component';
    

    which is compiled as ./modal-result.component dependency and it doesn’t care about the file extension. That’s why I had to add *.js to all paths to help builder find all compiled JavaScript files.

    The meta options just tell the builder to ignore dependencies that I don’t want to bundle. That’s Angular2 itself and rxjs library because I include import 'rxjs/add/operator/map' in main.ts.

    This generates a single app.bundle.js file with all modules registered as named modules using System.register.

  3. Using our app.bundle.js

    The last part is a piece of cake. We just import the default Angular2 stuff and then use bundle option to tell SystemJS where’re all our dependencies.

    <script>
    System.config({
        packages: {
            '/web': {
                format: 'register',
                defaultExtension: 'js'
            }
        },
        bundles: {
            '/web/app.bundle': ['main']
        }
    });
    System.import('main').then(null, console.error.bind(console));
    </script>
    

    When we call System.import('main') it in fact first downloads /web/app.bundle.js and registers all modules in the package and after that it imports modules main with our Angular2 bootstrap.

    And that’s it!

You actually don’t need to use gulp at all and do everything with pure node script.

Bundling CSS files is easy with things like cssnano and I’m sure you can find tutorials on how to use it everywhere.

I’m sure there’re other ways to the the same thing. Angular2 is designed not to restrict you to use just one technology. However, using SystemJS seems to me to be the easiest way because it uses the same module system as Angular2 by default.

I’m sure you could use also for example commonjs format but that would require you to use also some polyfill for require() loader, but I haven’t tried it yet. I belive UMD might be also worth trying.

Leave a Comment