Compiling Angular Apps Ahead of Time with Webpack

This post relies on the AotPlugin and @ngtools/webpack for Webpack. Both are developed as part of the Angular CLI. Since the CLI often trails the latest Angular releases, the procedures outlined here might not work with the newest version of Angular. The general approach, which uses the Angular compiler ngc directly and is described here, should work in any case. It can also be combined with the angular2-router-loader mentioned below.

Suguru Inatomi recently published a blog post on Angular 2 AoT Compilation with webpack, detailing how to enable ahead-of-time compilation within a webpack-based Angular 2 project. His solution leverages the @ngtools/webpack package, which is maintained under the Angular CLI umbrella. With this package, switching from JIT to AOT only requires adjusting the webpack configuration — no changes to the application source code are necessary.

I applied this idea to one of my demo projects and extended it to support lazy loading. In addition to @ngtools/webpack, I integrated the angular2-router-loader into the setup. This extra loader may become unnecessary once the CLI supports lazy loading together with AOT out of the box.

The complete source code for this project is available on GitHub.

For the build, I installed the following packages as devDependencies:

"devDependencies": {
    "webpack": "^2.1.0-beta.25",
    "@angular/compiler-cli": "^2.1.1",
    "@ngtools/webpack": "^1.1.2",
    "angular2-router-loader": "^0.3.3",
    "angular2-template-loader": "^0.5.0",
    [...]
}

In addition, I configured the loaders @ngtools/webpack and angular2-router-loader for .ts files inside webpack.config.js:

module: {
    rules: [
        [...]
        { test: /\.ts$/, loaders: ['@ngtools/webpack', 'angular2-router-loader?aot=true&genDir=aot/app']}
    ],
},

It is crucial that the angular2-router-loader receives the parameters aot and genDir. The former prepares it for AOT compilation, while the latter specifies the directory where the AOT compiler writes its generated files.

Furthermore, the AOT plugin itself needs to be configured:

var AotPlugin = require('@ngtools/webpack').AotPlugin;

[...]

plugins: [
    new AotPlugin({
        tsConfigPath: './tsconfig.json',
        entryModule: 'app/app.module#AppModule'
    }),
    new webpack.optimize.UglifyJsPlugin({
        compress: {
            warnings: false
        },
        output: {
            comments: false
        },
        sourceMap: false
    }),
    [...]
}

The most appealing aspect of this approach is that the rest of the application remains unchanged for AOT. This means you can maintain two separate webpack configurations: one with AOT enabled for production builds and another using JIT for development. This split is particularly useful because AOT builds currently take considerably longer — although the Angular team is actively working on incremental AOT compilation, which should make such builds much faster in the future.

To enable lazy loading, you simply point to the module you want to load on demand using loadChildren in your route configuration. The angular2-router-loader rewrites this code during the build, so that the factory generated by the AOT compiler is loaded whenever the corresponding route is requested.

export const ROUTE_CONFIG: Routes = [
    [...]
    {
        path: 'flug-buchen',
        loadChildren: './flug/flug.module#FlugModule'
    },
    [...]
];

The full example is available here.