StephenWilson StephenWilson - 1 month ago 108
TypeScript Question

Build Angular2 HTML and TypeScript to a single file

I'm putting together an app (Large scale) using Angular2 and TypeScript. It will need to be divided into numerous projects and each project will have numerous components each with a

.html
view,
.css
stylesheet and
.ts
logic / class.

I would like each project to compile to a single
*.js
file and be copied to a host website which will run in IIS. This would be similar to how a WPF or Silverlight app is built with the
.xaml
files all getting compiled into the
.dll
.

I've had a look around the web and am able to get the
.ts
files to build to a single
.js
file using the
--outFile
option. But this doesn't help me with the
.html
files or the
css
.

Any help would be greatly appreciated even if it's to say that what I'm asking is impossible :-)

Stephen

Answer

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.