GitHub - ASPNET-ASPNETCore-AngularJS-Angular

The Router

If you want to enable lazy loading in your application with AoT, you first have to configure your router to load the module lazy - when it gets requested. You can do that with the loadChildren-attribute.

export const AppRoutes: Routes = [
  // normal routes
  { path: 'food', loadChildren: './path/to/module.file#ModuleName' },
    path: '**',
    redirectTo: 'home'

The syntax is { path: 'myPath', loadChildren: './path/to/module.file#ModuleName' },

The Module

If you did this you have to remove the module import from the module import array where you explicitly imported it in the first place!

// imports

    imports: [
        // other imports but NOT your lazy loaded module anymore

    declarations: [

    providers: [
        // ...

    bootstrap: [AppComponent]

export class AppModule { }

The ngc Compiler

If done so, you can install the necessary packages to get started with the ngc compiler:

npm install @angular/compiler-cli @angular/platform-server --save

After this, you have to configure a separate tsconfig-aot.json for Ahead of Time Compilation:

  "compilerOptions": {
    "target": "es5",
    "module": "es2015",
    "moduleResolution": "node",
    "sourceMap": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "lib": [
    "noImplicitAny": true,
    "suppressImplicitAnyIndexErrors": true
  "files": [
  "angularCompilerOptions": {
    "genDir": "aot",
    "skipMetadataEmit": true

Please pay special attention to the “files” array. Because the module is not imported in your application via the import statement anymore the compiler will not include it in the aot-folder (which is fixed in the “genDir”-attribute). So here you have to add it by yourself but only the path to the module.

If you now run:

node_modules/.bin/ngc -p tsconfig-aot.json

... an "aot" folder is created which should contain all of your compilated files.

├── app.component.ts
├── app.component.html
├── ...
├── app.module.ts
└── main.ts
└── app
    ├── module1
    ├── module2
    ├── module...
    └── ....ngfactory.ts

The (New) Entrypoint

Also, as an entry point, we specify the file(s) we have, but the main file is different. Because we created an output in the AoT folder, we need to point our entrypoint to that compilated files!

We can do that by adding a new main.ts file called "main-aot.ts" and add the following into it:

import { platformBrowser } from '@angular/platform-browser';
import { enableProdMode } from '@angular/core';
import { AppModuleNgFactory } from '../aot/app/app.module.ngfactory';



Here we are consuming the generated files in the AoT folder.

If this file causes problems because of not finding any files it may be that the aot folder does not exist yet. This is normal. What does not exist cant be imported. Just create the folder and the errors should go away.

Webpack: Consuming the AoT Output

After that output is created, you can go along and consume these files via webpack.

Here, it is very important to use the angular-router-loader which enables loading modules through the string we mentioned in the routes.

So, if a route ts file comes along we want to send it through the angular2-template-loader, angular-router-loader and the awesome-typescript-loader to proceed with our files.

The rule for this looks like the following:

    test: /\.ts$/,
    use: [

Pay attention to the parameters we give to the angular-router-loader. the genDir has to match our directory containing our aot compiled output.

... and as an entry point we are pointing to the new file we created:

entry: {
    'app': './app/main-aot.ts'

With this webpack uses our aot-file as an entry point and follows all imports in this file.

We also have to tell webpack how to name the files which are recognized as lazy loaded. We can do this by adding a "chunkFilename" in the output-settings:

output: {
    path: './.dist/web/aot/',
    filename: 'js/[name]-[hash:8].bundle.js',
    chunkFilename: 'js/[id].-[hash:8].chunk.js',

Webpack: Adding Tree Shaking

To add tree shaking we have to use the UglifyJsPlugin from webpack which we can configure like this:

var CompressionPlugin = require("compression-webpack-plugin");
// ...
plugins: [
    // ...
    new webpack.optimize.UglifyJsPlugin({
        compress: {
            warnings: false
        output: {
            comments: false
        sourceMap: false
    new CompressionPlugin({
        asset: "[path].gz[query]",
        algorithm: "gzip",
        test: /\.js$|\.html$/,
        threshold: 10240,
        minRatio: 0.8
    // ...

Here we also added the CompressionPlugin to get the compressed output too.

Wrapping It Up & Beautifying It

Ahead of Time compilation and treeshaking are two different things often used together. AoT is done via a different compiler. Treeshaking can be done via webpack but also rollup.js is a way to go.

So we need to have two steps:

  1. compile our files via the ngc compiler
  2. use the output of that and consume it via webpack

Beautify It

You can now "hide" those two commands in the package.json "scripts" section like this:

"webpack-prod": "npm run ngc && webpack",

which you can run via

npm run webpack-prod

Be sure to have the ngc command globally installed then…

I hope I was able to clarify a bit about how AoT and treeshaking work with webpack 2 and Angular.

Ahead of time compilation -