How I Use Angular 2 with Webpack: Webpack Configuration

Reading time: 6 minutes

A disclaimer: I’m not claiming to be an expert here and I’m open to any corrections or input from everyone; this is just what’s worked for me so far. Please enjoy!

As I mentioned in the first post of this series, this series looks out how I have been using Angular 2 with Webpack. The topics in the series are:

  1. Directory Structure
  2. Node Modules & NPM Configuration
  3. Typings
  4. Code Linting
  5. TypeScript Configuration
  6. Karma Configuration
  7. Webpack Configuration
  8. Sample Application

In this article we’ll look at the trio of Webpack configurations, in the tsconfig.json file.

Webpack Configurations (webpack.*.js)

The Webpack files are really the most important part (it’s all important, really) of this whole setup. As of today, I have three configurations, depending on what I’m trying to:

  • webpack.dev.js: this is used as I develop, namely with the webpack-dev-server.
  • webpack.test.js: this is used for running tests (as was seen in the Karma article).
  • webpack.prod.js: this is for doing a final build. However, it isn’t actually production ready, so just use it as a jumping point for your own use.

 Development (webpack.dev.js)

None of these Webpack configuration files are big (at least at the point I’m at right now), but explaining them isn’t necessarily as simple.

Entry Points

The output files (concatenated modules) I use are broken into two parts: vendor code and my code. Since each file we write will import its dependencies, we only really need to specify one file per entry point.

All this says is that for the vendor code, which loads dependencies (like Angular), Webpack will start looking in ./src/app/scripts/vendor. For my code, I use the bootstrapping file as the entry point. In that file the application is set up for use with Angular.

Loaders

There are really three types of files I’m bundling up: SASS/CSS, HTML, and TypeScript. With Webpack, if you’re unfamiliar, this is achieved by using loaders. To no surprise these loaders are: the SASS loader (sass-loader) and the TypeScript loader. There also is a raw-loader which used to load files as-is (this handles HTML files); all of these files were mentioned in the NPM post.

So what does all this say? First, each entry has a loader and a test section; the loader section says which loader(s) to use and the test section provides a regular expression which is used to find files to run the loader(s) on. You may notice two things: the first loader has a “!” in it and the last loader has an exclude attribute to it.

The loader attributes can run multiple loaders just by separating the names of the loaders by that “!”. So that first loader says, first load the file raw and the run it through sass run the file through sass and pass it through raw (pointed out to me by David in the comments), while the others just say run it through this one loader–the second loaders entry runs HTML files through the raw loader and the last one runs it through the ts (TypeScript) loader.

In the last loader, the exclude attribute tells Webpack to exclude files whose filename matches the test regular expression and also match the exclude regular expression. So that loader works on all TypeScript files that aren’t in the node_modules directory.

Plugins

Plugins are also used to transform the files. For development, I use two: the chunk plugin and the HTML plugin. The chunk plugin is provided by Webpack, while the HTML plugin is installed (as shown in the NPM post).

The chunk plugin (ChunkWebpack, a variable pointing to require('html-webpack-plugin')) is used, as far as I understand, so that if a file gets referenced a bunch of times, it’ll only be pulled in once. This is important for the vendor scripts since the same Angular files can be referenced many times throughout the bundling process.

The HTML plugin (HtmlWebpack, a variable pointing to webpack.optimize.CommonChunksPlugin) is one of my favorites. Instead of needing to statically include scripts and styles into my index.html, the HTML plugin allows me to inject the script tags for each of the bundles produced right into the <body></body> section of index.html.

Resolve and Output

The resolve section just tells Webpack what types of files to look for. In the above, I just tell it to pull in files with .js and .ts extensions. The empty string is needed to pull in modules which require their extension (i.e., require('./file.ext.req')).

The output section just configures options for generating the output files. We make the names of the output files their entry name plus “.bundle.js” (so vendor becomes vendor.bundle.js). I also tell it to place those files in the dist directory under the root of the project.

Dev Server Setup

The last couple of sections are there to setup usage of the Webpack-provided development server.

I use source maps so I can debug better and set the directory to serve from to dist. Pretty simple stuff here.

The Final Configuration File

My final file, in my preferred ordering, including pulled-in modules:

The Test Configuration (webpack.test.js)

The test Webpack configuration is even more concise than the development one. Gone are all the plugins and development server setup. Also gone is the need to process .scss files through the SASS loader. There is no entry or output sections either, as our Karma configuration takes care of file handling. We still need to do some processing however.

Resolving Files

We’re still resolving the same extensions but we also specify the root directory and where modules are served from (the node_modules directory).

Modules Section

We’re using similar loaders here but we don’t bother processing the SASS files since they’re irrelevant to unit testing; we do run them through the raw loader so the files can be resolved. There are, however, two new sections: preLoaders and noParse. In the preLoaders section we use the tslint-loader (which was installed in the NPM post) to make sure our code doesn’t have any noticeable style or pattern errors.

The other section, noParse, just tells Webpack not to use loaders on the specified files. In this case, we want Webpack to ignore anything in node_modules/angular2/bundles since they’ve already been processed by the Angular team.

TSLint Configuration and DevTool Specification

Here we tell the TSLint to emit errors and specify inline source maps as our debug tool. An inline source map just adds the source map as a data URL.

The Final Configuration File

The Production/Distribution Configuration (webpack.dist.js)

The production configuration is very similar to the development one, but with some minimal changes. More disclosure, I lifted the additions from the Angular 2 Webpack starter–which is quite a wonderful repository and one of the places I learned a lot of my Webpack/Angular 2 knowledge. I’ll go into detail on those changes, while avoiding rehashing of what you’ve already seen.

First, we’re taking the loaders out of debug mode.

The next part is more significant–we’re customizing how HTML is loaded.  Since there are Angular-specific attributes, like inline variable declarations (#myVar)  or the “banana in a box” syntax ([(myVar)]) the loader has to be aware not to clobber those characters out of the template. To handle this we set up the customAttributeSurround and customAttributeAssign replacements.

The other three attributes just tell the loader to keep the values case-sensitive (to make sure ngIf doesn’t become ngif), minimize the HTML, and keep attribute quotes.

Additionally, there is a third plugin used, the UglifyJS plugin, which, like the CommonsChunk, is provided by Webpack. If you’re unfamiliar, this just takes your JavaScript and compresses it down into hard-to-read, minimal code–this allows for faster load times for your users.

Here we make sure it doesn’t try to make it look nice (the opposite of what’s trying to be achieved). Additionally, I don’t need to keep comments or care about IE8 (which has the best attribute name of screw_ie8) during the mangling (obfuscating the code) or compression (removing unnecessary characters). When mangling, I want to keep function names since (I think) Angular relies on Function.prototype.name.

And the final file looks like the following; notice all of the development-specific setup has been removed:

And That’s It!

So all the set up is done! You’ve got everything you need to develop. I’ll do one more follow-on post that shows the most basic setup of an Angular project and you should be ready to hit the ground running!

2 Comments

  1. Great articles. Helped me a lot. Just thought I should point out that chained loaders run from right to left i.e. raw!sass, sass first then raw, where as you have stated raw first then sass.
    Cheers!

    1. Ah, thanks for that information, David! I’m still a novice when it comes to Webpack, so I could’ve spoken out of turn a lot here. I’ll update the article to reflect your help.

Leave a Comment

Your email address will not be published. Required fields are marked *