Minko Gechev bio photo

Minko Gechev

Web hacker

Twitter GitHub StackOverflow
Edit

In the post “Building an Angular 2 Application for Production” we explored how we can decrease the bundle size of a “Hello world!” application to from about 1.6M (non-minified & uncompressed) to 49K! We did this by using the following techniques:

  • Angular offline template compiler for generating tree-shakable code.
  • Tree-shaking of ES2015 modules with rollup.
  • Bundling of the application.
  • Minification with uglifyjs.
  • Compression with gzip.

Although we achieved impressive results we can do even better! In this post I’ll show some of the work done by Alex Eagle and Jeff Cross from the Angular core team.

Disclaimer: This article explains a research which uses experimental tools tools which WILL change in near future. Do not use anything described here in production.

Closure Compiler

For our purpose we’ll use the Google Closure Compiler:

The Closure Compiler is a tool for making JavaScript download and run faster. Instead of compiling from a source language to machine code, it compiles from JavaScript to better JavaScript. It parses your JavaScript, analyzes it, removes dead code and rewrites and minimizes what’s left. It also checks syntax, variable references, and types, and warns about common JavaScript pitfalls.

The steps that we are going to go through this time will be simplified since Closure Compiler can do most of the work for us, such as tree-shaking, minification and bundling! This will help us to reduce the tools from our tool-chain to only ngc and Closure Compiler!

Everything looks like a piece of cake at first, however, there are some complications:

Obstacles - Incomplete ES2015 Module Support

Fortunately, Google Closure compiler officially supports ES2015, but unfortunately it supports only a subset of the module syntax. For instance:

bar.js

export const bar = 42;

index.js

export * from './bar';

foo.js

import {bar} from './index';
console.log(bar);

…is not supported by Closure Compiler because of export * from ./bar.

Solution - goog.module

An alternative module syntax that works well with the compiler is goog.module. Unfortunately the TypeScript compiler doesn’t support transpilation to goog.modules. For this purpose we have to use tsc combined with the tool tsickle which allows us to hook in the ngc compilation process and emit Closure-friendly ES2015 instead.

In short, we need a custom build of Angular and all of its dependencies to goog.module modules. At this point this is achievable by going through a list of hacky solutions, however, soon the CLI and the Angular starters will automate this process completely and will eliminate all the boilerplates.

Results

Alex Eagle, posted his experiment on GitHub and shared all the details about the build process in this document.

Lets run the build script and see what the bundle size of our Angular application will be!

Note that the build script below requires brotli. Brotli is a tool which implements the brotli general-purpose lossless compression algorithm. For further details visit Brotli’s GitHub repository.

$ git clone https://github.com/mgechev/closure-compiler-angular-bundling
$ cd closure-compiler-angular-bundling
$ npm install
$ npm run build

Now if we go to dist we’ll find:

$ cd dist
$ ls -lah
total 344
drwxr-xr-x   7 mgechev  staff   238B Jul 21 12:18 .
drwxr-xr-x  15 mgechev  staff   510B Jul 21 12:18 ..
-rw-r--r--   1 mgechev  staff    80K Jul 21 12:18 bundle.js
-rw-r--r--   1 mgechev  staff    20K Jul 21 12:18 bundle.js.brotli
-rw-r--r--   1 mgechev  staff    23K Jul 21 12:18 bundle.js.gz
-rw-r--r--   1 mgechev  staff   3.7K Jul 21 12:18 property_renaming_report
-rw-r--r--   1 mgechev  staff    32K Jul 21 12:18 variable_renaming_report

20K bundle! We have almost 150% improvement compared to our previous experiments!

In order to make sure that the application works run:

$ npm run serve

The code from above is hosted in my GitHub account.

Comparison

On the diagram below you can find how the Google Closure Compiler + ngc bundle stays next to the other ones produced by the different bundling strategies explored in the previous post:

ngc + Google Closure Compiler bundle comparison

Since the size amplitude above is huge, the diagram below illustrates how the bundle produced in this post compares to the other compressed bundles from the previous post:

ngc + Google Closure Compiler bundle comparison

Conclusion

With the advanced compression of Google Closure Compiler we are able to drop the bundle size of a “Hello world!” application to 20K! However, this came with the price of a few hacks - custom build of Angular and its dependencies to goog.modules. However, in near future the entire process will be completely abstracted and automated. This will allow us to apply advanced build techniques such as offline compilation, tree-shaking, transpilation, compression and minification with only a single command!

References

For further reference take a look at the following resources: