Extending the Angular CLI’s build process

without ejecting

TLDR; ngx-build-plus allows us to ...

  • ?️ modify the ? CLI's build process by providing a partial webpack config
  • ? modify the ? CLI's bahavior by providing ngx-build-plus plugins (node scripts)
  • ➕ can be easily install by using ng add ngx-build-plus which automatically registers the solution in your angular.json.

The CLI does a myriad of things for us. Especially the provided build process is quite sophisticated and provides a lot of optimizations out of the box.

However, sometimes we want to extend its default behavior to address respective requirements in our projects. For this, we could eject from the CLI using ng eject in the past. However, after ejecting, one was not able to use the CLI for the build process anymore, which is one of the reasons it's not supported since CLI 6.

In this article I provide a solution for this, which even seems to have some benefits over ejecting. The example used for this can be found in my GitHub repo.

How ngx-build-plus can help

To provide an easy way for extending the CLI's build steps, I've written ngx-build-plus. It inherits from the default webpack-based builder and hence mirrors the CLI's default behavior. In addition it allows you to do two things:

  • Providing a partial webpack config which is merged with the CLI's one.
  • Providing a simple javascript file (aka a plugin) which modifies the existing webpack configuration dynamically.

In addition, it also gives you an --single-bundle switch for ng build, which -- well -- puts the applications code into one single bundle. This is especially useful for Web Components/ custom elements build with Angular Elements.

Getting started with ngx-build-plus

If you're using Angular and CLI 7, just install ngx-build-plus using ng add:

ng add ngx-build-plus

If you have several projects in your CLI workspace, use the --project switch to point to the project in question:

ng add ngx-build-plus --project getting-started

After this, you can provide a custom partial webpack config. I've put it in my project's root and called it webpack.extra.js:

const webpack = require('webpack'); module.exports = { plugins: [ new webpack.BannerPlugin('----- Manfred was here -----') ] }

To keep things simple, this example just uses the BannerPlugin which adds a comment at the top of each bundle.

To use the partial webpack config for your build process, call ng build with the --extra-webpack-config switch provided by ngx-build-plus:

ng build --extra-webpack-config webpack.extra.js

Once again, if your CLI workspace contains several projects, also mention the project to build:

ng build --extra-webpack-config webpack.extra.js --project getting-started

After this, you'll see the added comment in the first lines of the generated bundles:

The Banner plugin added a comment to the top of the bundles

In order to automate this command, it seems to be a good idea to define a npm script.

Writing a plugin

Sometimes, just providing a custom webpack config is not enough. In those situations you have to modify some parts of the default configuration. For this, ng-build-plus allows you to define a plugin.

Basically, such a plugin is just a javascript file exporting an object with three methods:

exports.default = { pre: function() { }, config: function(cfg) { var time = new Date().getTime(); var pattern = 'getting-started.[name].' + time + '.js'; cfg.output.filename = pattern; return cfg; }, post: function() { } }

In my example I put this file into the project's root and I called it plugin.js.

As you might imagine, the pre method is executed at the beginning of the build chain and the post method is called at the end. The config method is called after pre and gets the CLI's default webpack config. Here, you have the chance to modify it.

In this simple example, config just modifies the name of the bundles.

When calling ng build, you can point to your plugin:

ng build --plugin ~plugin.js

The ~ means, that you want to use a local file. Without this character, ngx-build-plus assumes that the provided name is the name of an installed npm package.

After this, you should see the new bundle names:

The plugin modified the bundle names

Once again, automating this command with a npm script seems to be a good idea.

A more sophisticated example for a plugin is my side project ngx-build-modern. It creates two sets of bundles: An ES5-based one for legacy browsers (IE) and an ES2015-based one for modern browsers. The latter one is more optimized and does not provide all the polyfills. Also, it provides an index.html which loads the right set of bundles for your current browser.