Favicons for Each Environment

Ben Racicot
3 min readMay 27, 2021

--

Have you ever been editing a local, dev or staged application and were confused why the production version wasn’t updating?

This article is part of a series on customizing Angular builds.

  1. Configure Angular’s Build System For Local, Dev, Stage and Prod
  2. Favicons for Each Environment
  3. Your App’s Git Branch In the Title (per env.)

This article focuses on Angular CLI’s use of Webpack bundling but likely applies to general Webpack build configuration.

We’ll be setting up two nice-to-haves:

  • Favicon for each environment configuration target
  • Git branch and app versioning in the webpage title

Prepare to enter… angular.json configuration.

What Didn’t Work

Commonly mentioned solutions include two configuration arguments that I could not get to work.

  • Configuring assets caused a mess IMO.
  • fileReplacements only works with some file types

Angular Asset Configuration

TL:DR; I didn’t want new folders filled with environment specific files. Especially for the simplicity of 3 new favicons. And how would we rename them or update the index.html’s favicon src?

{
...
"projects": {
"angular-extreme-build-configuration": {
...
"architect": {
...
"configurations": {
"staging": {
...
"assets": [
"src/favicon.ico",
{
"glob": "**/*",
"input": "src/staging_assets/",
"output": "/assets/" <-- just nope
}
],
...
},
...
}
},
...
}
}
},
...
}

If you got assets configuration to replace favicon.ico with favicon-prod.ico (and renamed to favicon.ico) please let me know how.

Angular.json fileReplacements

Another was fileReplacements which in my exp. only supports .ts files.

"dev": {
"fileReplacements": [
{
"replace": "src/favicon.ico", <-- nope
"with": "src/favicon-dev.ico" <-- nope
}
]

The Best Solution Includes Extras

I’ll assume your Angular project is setup for customizing each environment.

If not, checkout Angular Workspace Configuration in the article Customize Angular’s Build System For Local, Dev, Stage and Prod.

Let’s start with angular-builders/custom-webpack!

npm i -D @angular-builders/custom-webpack

You’ve seen Angular’s default builder used in the configuration, I’m sure of it!

{
...
"projects": {
"app-name": {
...
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser"

Yup we’re gonna replace that with Jeb’s awesome custom-webpack builder.

{
...
"projects": {
"app-name": {
...
"architect": {
"build": {
"builder": "@angular-builders/custom-webpack:browser"
"options": {
"customWebpackConfig": {
"path": "./webpack.config.js",
"replaceDuplicatePlugins": true
},
"indexTransform": "./index-html-transform.ts",

...

Now we have to create those two new configuration files.

src/index-html-transform.ts ← New JS file for manipulating index.html
src/webpack.config.js ← Customize Angular’s Webpack config!

Now you can customize your index file and Angular’s Webpack compilation!

angular-builders/custom-webpack index-html-transform

Let’s setup our favicon conditions.

Within your new index-html-transform.ts file use this code as a base to toggle favicon file names.

import { TargetOptions } from '@angular-builders/custom-webpack';
import * as cheerio from 'cheerio';
export default (targetOptions: TargetOptions, indexHtml:string) => {
const $ = cheerio.load(indexHtml);
const favicon = targetOptions.configuration
? `favicon-${targetOptions.configuration}`
: 'favicon';
const favLink = `<link rel="icon" type="image/x-icon" href="assets/${favicon}.ico"></link>`; $('head').find('link#favicon').remove(); // remove the OG favicon
$('head').append(favLink); // add the new favicon

return $.html();
};

A friend helped me understand that targetOptions.configuration contains the --configuration value from our build command (below)

"start:dev": "ng serve --configuration dev"

So convenient!

But we can push this setup even further by adding the Git Branch in the Title of each build target.

--

--

Ben Racicot

Hi! I’m passionate about web technology, specifically Angular and all things JavaScript.