Speeding up Webpack

Using TypeScript with Webpack can be frustrating. Luckily, we can use some tricks to improve the experience.

Webpack is a bundler that is very useful to write modular web apps that are minified and transformed into a single or multiple deliverables. It is a must for developing medium to large web application projects. TypeScript is also a must for these kinds of projects. Thus, I believe that the two need to play well together. Unfortunately, in reality this is not the case.

I am currently in the process of refactoring a large web application that looks as if it was bundled together by using examples of common development mistakes from standard literature. Obviously, the previous team did not know any best practice nor any practice to guarantee correctness in their code.

In my opinion part of the low quality can be assigned to JavaScript. It is both, the best and worst language available. It let's us do amazing things really fast. It let's use distribute these things everywhere. But it also let's everyone do that. And it does not show tree flags everywhere before the delivery. Despite some things that cannot possibly work. How can this be mitigated and JavaScript be tamed? The answer is obviously TypeScript and better tooling. First rule before implementing something new: Think about how it can be tested / validated automatically. Second rule: Write the test(s).

The problem with Webpack and TypeScript is that all existing loaders follow the Webpack principle. That simple rule states: Only look at a single file and let Webpack determine it's dependencies. However, in order to perform proper type checking TypeScript needs to look at multiple files: The current one and the referenced, i.e., imported ones. This is necessary to have the necessary type information. As a result most files (especially in a dense network of dependencies) are consumed multiple times.

One of the problems with the current application is the dense web of dependencies. It's a nightmare. Everything is in there. Even cyclic references - sometimes even A to B and B to A. As a result the switch from Babel to TypeScript as a loader increased the build time from 90s to 15min. Ouch! This was independent of the exact loader and options (we ended up with the awesome-typescript-loader). So, how can we improve this?

As we already introduced Gulp for taking care of our different build tasks I knew that a proper combination of TypeScript transformed via Gulp and Webpack may lead to a quick win here. Indeed, we can use the gulp-typescript package to pre-compile all source files and send the results (i.e., virtual files) to a custom target, which takes these files and uses them as input for a new loader. The new loader will eventually take the cached (i.e., pre-compiled) files for Webpack - without the need to actually transpile them on the fly or look at all the references.

The result is simple: The TypeScript process takes 10 to 15 seconds. After that time build errors are already visible to the developer. Webpack still takes 80 more seconds, but that is alright for our purposes. So in the end we are a little bit over the previous 90s, but with the advantage of having full type safety.

Created .

References

Sharing is caring!