Angular CLI and Moment.js: A recipe for disaster … and how to fix it.
When I wrote about “To use Angular CLI or not?”, I claimed that there was not much risk involved in starting a project with Angular CLI, since ng eject
allowed you to change your mind later and jump to a vanilla Webpack setup.
This has changed since AngularCLI 6: the eject
command is now disabled.
Relying on Angular CLI has just become a much bigger risk for your project.
When I wrote about “Angular vs. React: The CLI” I claimed that I have not yet had a mandatory reason to eject
. In the meantime I have come across several cases where the is a need to modify the webpack config of an Angular CLI project, which could be achieved witheject
… but not any more.
One reasons is an integration with three.js. Many three.js extension rely on the global variable THREE
. The typical solution in a webpack build is to expose THREE
globally via webpack config… bad luck, with Angular CLI v6 you don’t have that option any more.
Other solutions are much more hacky …
Another nasty example where you typically need access to the webpack configuration is when you want to use the popular library Moment.js.
Let me illustrate the problem:
npx @angular/cli@6.1 new my-project
cd my-project
npm i moment @types/moment
Now use Moment.js in your project, i.e. in main.ts
:
import * as moment from 'moment'
console.log(moment());
Now run the build and have look at the bundle:
npm run build -- --prod --stats-json
npx webpack-bundle-analyzer dist/my-project/stats.json
You get the following scary picture:
The main bundle of your application has a size of 498 KB of which Moment.js is 329 KB! The biggest part of Moment.js consists of a bunch of locales, which you most probably do not need!
Of course this is a shortcoming of Moment.js in combination with webpack. This is not inherently a problem of the Angular CLI. However Moment.js is a very popular library, many Angular projects want/need to use it. Maybe only because some other library depends on Moment.js (many do!).
There is an easy fix for the problem: how-to-optimize-momentjs-with-webpack.
However you need to modify the webpack config… which is not possible in a plain Angular CLI project! Bummer!
The answer of the Angular CLI is: that this is unfortunate, but they can’t deal with such special cases… bummer again!
Note: create-react-app accepts that Moment.js, as a very common library, deserves special handling and solves the problem out of the box.
At this point your project has a problem…
Of course the JavaScript ecosystem has a solution for everything:
… but I don’t like that a “state-of-the-art” framework forces me into hacks like this.
A solution (sort of …)
With ngx-build-plus you can change the webpack configuration in an Angular CLI project without ejecting.
(Note: The section below was updated on 2018–12–29 for Angular CLI 7.1.4)
In the example from above you can do the following:
ng add ngx-build-plus
Add a file webpack.extra.js
in the root of your project:
const webpack = require('webpack');module.exports = {
plugins: [
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
]
}
And run:
npm run build -- --prod --stats-json --extra-webpack-config webpack.extra.jsnpx webpack-bundle-analyzer dist/my-project/stats.json
Yay! All the locales have disappeared from the bundle:
You have now solved the problem! … But did you consider the price of the solution?
At best I can congratulate someone for quickly and simply solving a problem on top of the shit that they are given. The only software that I like is one that I can easily understand and solves my problems.
- Ryan Dahl on Software
Yup, ngx-build-plus is yet another “arcane” library you now depend on. Are you prepared to maintain that library yourself?
For Angular CLI v7.1.4 using ngx-build-plus is a working solution.
… but ngx-build-plus is was broken for Angular CLI 6.2:
… bummer!
Note: There are other projects similar to ngx-build-plus, maybe they make you happy:
https://github.com/meltedspark/angular-cli-builders
https://github.com/Angular-RU/angular-cli-webpack
The source code for the above example is available here: https://github.com/jbandi/ng-moment-problem