DEV Community

Marcin Wosinek
Marcin Wosinek

Posted on • Originally published at how-to.dev

How to speed up Angular CLI app with esbuild-loader

A previews article in this series showed how to build Angular CLI app with esbuild. There are two downsides of this approach:

  • we drop every piece of configuration that is there in the Angular CLI build configuration
  • our build is faster, but it's way bigger - ~760kb instead of ~170kb

This article will try a different approach to harvest esbuild speed for your Angular CLI application. As I already shown in another post, using esbuild-loader provided minimization plugin can be pretty effective.

A word of warning

This is not an easy configuration. Getting it up & running made my head spin, and I started questioning my framework and tooling choices along the way. In the end, I got something working, but the results are not impressive. It probably deserves a heavier test, similar to the benchmark used in the post linked above.

Overriding webpack configuration in Angular CLI

Angular CLI used to have ng eject command to expose the webpack configuration. Unfortunately, since version 8, it's not available anymore. Instead, we can specify "projects.*.architect.build.builder" inside our angular.js to override the webpack config partially - without getting access to the whole thing.

This is the approach used in angular-builders project, which I used in this guide.

The approach

Here, I'll set up only ESBuildMinifyPlugin from esbuild-loader. The in the webpack article, it looked like better speed improvement than the loader itself. As angular is written in typescript, the loader can improve speed, but I'm afraid it will be an even bigger endeavor than this one.

Setting up upgraded configuration

For a starter, let's install the necessary dependencies:

$ npm install --save-dev @angular-builders/custom-webpack esbuild-loader
Enter fullscreen mode Exit fullscreen mode

The configuration change can be tricky. First, let's add our webpack.config.js with changes we want to apply:

const { ESBuildMinifyPlugin } = require("esbuild-loader");

module.exports = (config, options) => {
  // if you console log here, you can see what's inside the config

  // remove 2 first minimizers, hopeing they are the TerserPlugin
  config.optimization.minimizer.shift();
  config.optimization.minimizer.shift();

  config.optimization.minimizer.unshift(
    new ESBuildMinifyPlugin({
      target: "es2015", // Syntax to compile to (see options below for possible values)
    })
  );

  return config;
};
Enter fullscreen mode Exit fullscreen mode

To make it work, let's update angular.json:

  "projects": {
    "esbuild-loader-ng-cli": {
       ...
       "architect": {
         "build": {
-          "builder": "@angular-devkit/build-angular:browser",
+          "builder": "@angular-builders/custom-webpack:browser",
           ...
-            "baseHref": "./"
+            "baseHref": "./",
+            "customWebpackConfig": {
+              "path": "./webpack.config.js"
+            }
           },
...
Enter fullscreen mode Exit fullscreen mode

"builder": "@angular-builders/custom-webpack:browser" is necessary for the change to work - without it, the updated config will not even pass the syntax validation.

To check if everything is working as expected, let's visit <your-project-path>/dist/esbuild-loader-ng-cli/:

Screenshot 2021-07-18 at 11-34-41 EsbuildLoaderNgCli

The benchmark

So let's compare the default build with the esbuild enhanced one. Unfortunately, the results are inconclusive - you get better speed or better build size (and less configuration headache), so you need to pick what is a priority for you.

Baseline

As a baseline, let's use an example application as Angular CLI generated it. You can find the code in the main branch of the repo. My only change is that I tweaked the configuration to make the built code work from a subfolder - as describe here.

The build:

$ npm run build

> esbuild-loader-ng-cli@0.0.0 build
> ng build

✔ Browser application bundle generation complete.
✔ Copying assets complete.
✔ Index html generation complete.

Initial Chunk Files               | Names         |      Size
main.03b0a30b1c3bb39428d7.js      | main          | 133.79 kB
polyfills.9a3c7adfe54759783845.js | polyfills     |  35.99 kB
runtime.b557d7bc6f5a0a2b7c10.js   | runtime       |   1.02 kB
styles.31d6cfe0d16ae931b73c.css   | styles        |   0 bytes

                                  | Initial Total | 170.81 kB

Build at: 2021-07-18T09:10:42.459Z - Hash: 02058db989498bb0ecd9 - Time: 11882ms
Enter fullscreen mode Exit fullscreen mode

The key values:

  • build size - 170.81 KB
  • build time - almost 12s

esbuild-loader build

The building code is available in the esbuild-loader-minimizer branch. Let's see how it performs:

$ npm run build        

> esbuild-loader-ng-cli@0.0.0 build
> ng build

✔ Browser application bundle generation complete.
✔ Copying assets complete.
✔ Index html generation complete.

Initial Chunk Files               | Names         |      Size
main.276f4eb0574e0396c077.js      | main          | 437.14 kB
polyfills.4a7829fed9a06cc470d3.js | polyfills     |  39.22 kB
runtime.1d3895c9b4e2bbd6978f.js   | runtime       |   1.08 kB
styles.31d6cfe0d16ae931b73c.css   | styles        |   0 bytes

                                  | Initial Total | 477.43 kB

Build at: 2021-07-18T09:31:40.831Z - Hash: 0a22501608de71af5712 - Time: 7657ms
Enter fullscreen mode Exit fullscreen mode

The key values:

  • build size - 437.14 KB, about 2.5 bigger
  • build time - almost 8s, about 33% faster

Summary

The build is faster, but unfortunately, the output file is much bigger &, as such, will be less optimal for our final users & transfer usage of our website. We have the same tread-off as in building with esbuild, but with less extreme values.

Discussion (0)