Unleash the Power of Webpack in Angular

angular-webpack-under-the-hood

As all Angular developers should already know, Angular CLI is using Webpack under the hood. Fortunately, it’s not too deep and we can reach it without opening the hood (i.e. : ng eject or forking Angular CLI or using a boilerplate).

TL;DR: Use Webpack’s require() instead of assets.

Why would we do that?

Loading assets & cache busting

The common way of adding assets to your Angular app is to simply put the files in the assets directory.

Using the assets is quite straightforward but it has some disadvantages:

  • The assets directory gets cluttered
  • … and it’s never easy to remove files as you might end up with some 404s so it keeps getting cluttered.
  • There’s no out-of-the-box cache busting on these files.

We need more explicit dependencies between the components and their assets, just like we already do for components’ css files.

Loading test fixtures

Tests need fixtures (set of testing data).

These fixtures can be in any format (i.e.: yaml, toml, csv, txt, …) and you want to import them directly in your test.

Loading some random file format

Tests need fixtures but apps might also need to open any kind of files too (or some file formats from the future).

A/B Testing

You might also want to import different files depending on some contextual information like A/B testing.

How to load any file using Webpack

In order to load a file anywhere in your TypeScript code, we just have to call Webpack’s require function.

1. Declare require

First, we have to declare the require function; otherwise we would get the following error : error TS2304: Cannot find name ‘require’.

  1. Create a src/typings.d.ts file if not already present.
  2. Add the following line to the file: declare const require;.
  3. Make sure you have the following section in your tsconfig.app.json and tsconfig.spec.json.
{
  ...
  "include": [
    ...,
    "**/*.d.ts"
  ]
}

2. Use require

Now, you can call the require function anywhere but this will use the default webpack loader depending on the extension.

const data = require('./data.json');
console.log(data.company); // Wishtack

3. Choose the right loader

In the example above, we got lucky because the JSON loader is already set up by Angular CLI.

If you want to load some random format, you can choose the loader explicitly. Cf. https://webpack.js.org/concepts/loaders/#inline
Let’s use the raw loader.

const data = require('raw-loader!./data.txt');
console.log(data); // {"company": "Wishtack"}

You can create your own loader if you have some crazy custom format or simply add an existing loader to your app:
https://github.com/KyleAMathews/toml-loader
https://github.com/okonet/yaml-loader
https://github.com/theplatapi/csv-loader
https://github.com/tcoopman/image-webpack-loader

Remember that loaders process files at build time.

Cookbook

Loading a picture

In the example below, we are using url-loader to load the picture.


@Component({
selector: 'wt-logo',
template: `<img [src]="logoUrl">`
})
export LogoComponent {
logoUrl = require('!!url-loader!./logo.svg');
}

file-loader and url-loader are two similar loaders that will copy the file in your dist directory when building the application, apply cache busting and return the URL to the file (E.g.:  /019f547f4000b0e2d016ff1e87c9c4ea.svg).

Under a predefined size limit, url-loader will produce a Data URI:
data:image/svg+xml;base64,bW9kdWxlLmV4cG9ydHMgPSBfX3dlYnB...

The cool part now is that the picture file is in the same directory as the component it is related to and if the file is removed, the build will fail.

Dynamic path construction

One of the craziest features of webpack is that it even works with paths with dynamic parts as you can seen in the example below.

Yes! It’s a template string!


@Component({
selector: 'wt-logo',
template: `
<img [src]="getLogoUrl()">
<button (click)="company = 'wishtack'">WISHTACK</button>
<button (click)="company = 'google'">GOOGLE</button>
`
})
export class LogoComponent {
company: string;
getLogoUrl() {
return require(`!!url-loader!./${this.company}-logo.svg`);
}
}

angular-logo-selector.gif

This can be very useful for A/B testing or white labelling.

Actually, here’s what webpack does:

  1. it parses string concatenations,
  2. creates a regular expression that is used to select all matching files in the directory,
  3. load them (the behavior will depend on the loaders you choose),
  4. create a map between the possible variable values and the corresponding files.

Security Concerns

Do not load files containing some secret information as everything will end up in the app’s bundles which are loaded by the users’ browsers!

For example, never load package.json as it might disclose some critical information like your dependencies… and the associated known vulnerabilities.

 

Please let us know about your use cases.


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s