100 days of Angular 2 - Day 3: Migrating from SystemJS to Webpack

SytemJS or Webpack?

Before my project becomes too complicated I want to switch from using SystemJS to Webpack.

Besides that Webpack seems like it's used more, it's also the only module loader that's supported with Angular Universal which I'd like to use at some point down the road.

But before I choose between rival technologies, I always consult three things first - Google Trends, Github stars, and Stackoverflow discussions.

Here's the Google Trends chart:

And the Stackoverflow answers related to Webpack or SystemJS (by the way I made the site that charts Stackoverflow discussion, please let me know what you think!)

And Github stars as of today:

So even if this turns out to be an awful decision, at least I'll have company along the way.

Migrating from SystemJS to Webpack

This wasn't super straightforward. The official Angular 2 docs include a lengthy page about Webpack but it's not laid out in a very clear manner.

From consulting the Angular 2 docs as well as the following the two Angular 2 + Webpack "Starter" projects I was able to figure it out, sort of.

Sometimes it's really silly things that can mess you up. For me, I accidentally had my config file named webpack.js instead of webpack.config.js which is what Webpack looks for by default when you run the command webpack or webpack-dev-server.

I should have known better when Webpack was completing everything in 7ms.

$ webpack-dev-server --port 8080
Hash: 396f0bfb9d565b6f60f0
Version: webpack 1.13.2
Time: 7ms
webpack: bundle is now VALID.
 http://localhost:8080/webpack-dev-server/
webpack result is served from /
content is served from C:\Users\stephen\git\100-days-of-angular

Anyways, I was able to hack something together and get it partially working, or at least an error that I could troubleshoot.

You'll notice I strip away any references to JavaScript files in the index.html file:

Supposedly the The HtmlWebpackPlugin will generate appropriate <script> and <link> tags into the index.html.

Oh look! It's kind of working! I'm not sure why they end up in the body, but at least that's progress. Note that the Angular 2 documentation on using Webpack suggested breaking out all of the JavaScript dependencies into three files - polyfills, vendor, and the app, so I heeded their advice and did that.

Now, to fix this error. From following the one of links supplied in the error message, it looks like I don't have to include this module.id thing that I didn't quite understand. Good!

Webpack will do a require behind the scenes to load the templates and styles if I include a ./ at the beginning of the template and styles. That is, if you have the angular2-template-loader Webpack loader included. From those docs it says:

The angular2-template-loader searches for templateUrl and styleUrls declarations inside of the Angular 2 Component metadata and replaces the paths with the corresponding require statement.

The generated require statements will be handled by the given loader for .html and .js files.

Then I notice the requirements for angular2-template-loader state:

To be able to use the template loader you must have a loader registered, which can handle .html and .css files.

The most recommended loader is raw-loader

So I had to go and include 'raw-loader' in my package.json and install it. Ugh, this is getting complicated.

Okay, so now I save everything and have Webpack bundle everything up. Reloading the web page results in.... the same error. What??

Inspecting the source code I see that it still has the module.id

What's going on? I make a change to the source dashboard.component.ts file and see that Webpack detects it and rebuilds the bundle. I do a hard reload in Chrome. I stop webpack-dev-server and start it up again. I tried adding hashes to the assets. Nothing. It's still looking at the old code.

After lots of googling I found this Stackoverflow answer and tried changing my output settings. Surprisingly, that actually made things worse.

Now I'm really confused. Why are they being loaded twice?

A little while later...

After a couple hours of googling, head scratching, and experimentation, I noticed some more strange things.

Since I was running Webpack with the dev server I saw that it would recompile the bundle and serve it up, but there weren't any changes. The dev console said this as well:

I would edit a .ts file, save it, see that Webpack would detect the file changes and rebundle, but it wouldn't affect the resulting source.

Then I thought, what if I edited a derived .js file? Turns out, that actually had an effect!

So somehow Webpack was not transpiling the .ts files into the .js files and serving those, it was instead just bundling and serving the days old .js files from back when I was running the tsc compiler with the lite-server!

Aha!

Since they were old, derived files, I just deleted them. Then I re-ran the webpack-dev-server and finally made progress! A different error!

Woo! After some googling I realized I just needed to include the following loaders in my webpack.config

  {
    test: /\.css$/,
    exclude: root('app'),
    loader: ExtractTextPlugin.extract('style', 'css?sourceMap')
  },
  {
    test: /\.css$/,
    include: root('app'),
    loader: 'raw'
  }

And my super slick Angular 2 app is back! Now built and served locally with Webpack! Finally!!!

Since this took so long I'm calling it quits for the day.

Day 3 summary

It was not a super smooth transition of the "Tour of Heros/Vegetables" from SystemJS to Webpack, but I was eventually able to do it. This is probably a result of trying to use too many unfamiliar tools all at once.

Delete your derived .js files if you used to run the tsc compiler and are now using Webpack. It will confuse the hell out of Webpack and then you!

I think I have a rough idea what all the dev dependencies are that were added to the project:

awesome-typescript-loader turns your .ts files into the .js bundles that get served to visitors

angular2-template-loader handles all the templateURL and styleUrls stuff. But be sure to read the component relative paths and Webpack section, especially with the whole module.id thing

ExtractTextPlugin moves every require("style.css") in entry chunks into a separate css output file so your styles are no longer inlined into the JavaScript.

HtmlWebpackPlugin automagically inserts the <script src=...> tags into a HTML page. There are options to insert them into the <head> or <body>

I'm still not entirely sure what the devtool or devServer options are in the webpack config but I'll save that for future reading.

Tomorrow I'll want to make a webpack config file for production, since all I did today was get it running in a local dev environment.

As a reminder, the full code is available on GitHub.

See day 4 here.