Fighting (and winning) Typescript for Webpack


Update January 2015: Web is moving too fast to and anything you read below may be obsolete or common knowledge.

There is a relatively new module/bundling system for the Web called webpack http://webpack.github.io/.

Update: It turns out that using webpack-dev-server (sort of livereload solution) with an external build system is hard. I could not hook multiple .ts file compilation to a webpack live reload event.
http://webpack.github.io/

The most useful feature is how easy it is to not only refer modules of your JavaScript code, every module system can do this. But how simple it is to link your JavaScript code with styles, external scripts, images and HTML templates.

If you run webpack CLI tool with the JS file above with no configuration whatsoever

> webpack src/mandrill-public-root.js build/build.js

it will generate a build.js bundle that will include all JS and HTML content of your app.

All you need to do is add

<script src="build/build.js"></script>

to your index.html file.

There are plenty of tutorials, introductions and overviews on the main site and the Internet, if you are not familiar with the tool, check out a short guide by Pete Hunt (a React guy) https://github.com/petehunt/webpack-howto

Wouldn’t it be suboptimal to bundle JS, HTML and CSS into one humongous file?

Webpack does not force you to bundle it all in one, it is very modular and provides a lot of plugins and loaders (I am still confused about the naming of those) to achieve the result you want. I just showed you what it can do with the least effort.

I guess for a typical web page you have to be considering the significance of every resource that you load, deferring less important bits to be loaded after the main content.

And this is not easy, check out an article by the great Ilya Grigorik https://www.igvita.com/2014/05/20/script-injected-async-scripts-considered-harmful, just to realise how many things need to be considered.

With Single Page Applications the choice is much simpler. In a typical SPA you can’t have the HTML content before the JavaScript is loaded and executed and CSS is useless until HTML is in the DOM. Loading any CSS async or eagerly probably does not do much difference, it should be there just in time when HTML is rendered.

If time-to-not-blank-page is critical to you (is it? https://medium.com/@bestander_nz/should-we-bundle-all-the-javascripts-for-a-single-page-app-6de4ec405788), then maybe you should use a server to generate HTML and order loading of all resources manually?

How do I use it with compiled languages like Typescript and Stylus?

Stylus/SASS/LESS is very easy.

You just install a few loaders:

npm install style-loader raw-loader stylus-loader

And chain them in your require statement

require("style!raw!stylus!./main-controller.styl");

That is it.

Your compiled module will contain the code that inserts a <style> block with the compiled content right into DOM.

But Typescript has a minor trouble.

Update January 2015: Since the time it was written a very good gulp plugin has emerged https://www.npmjs.com/package/gulp-typescript, also Typescript 1.4 was released and people started working on “native” webpack TypeScript plugin https://github.com/andreypopp/typescript-webpack-plugin. Use the plugin to build your code.

Our team tried all of them and decided to stay with https://github.com/k-maru/grunt-typescript, if TS compiler had no troubles then would this package grow into several 1000s of lines of code?

Anyway, you have to be aware that TS require statement is a special part of the compiler that triggers type checking and AMD/CommonJS module generation.

In TypeScript you can do only this:

import ctrl1 = require('./main/main-controller');

But using require statements for full webpack asset graph bundling will produce a compile time error:

template: require('raw!./main/main-controller.html')
error TS2095: Could not find symbol 'require'.

Luckily it is very easy to solve this by providing a custom definition for require function:

declare function require(string): string;

This will still allow Typescript to compile JS modules but webpack specific CommonJS style require statements won’t break the builds.

I’ve set up a simple Typescript + Webpack + Stylus + Angular project on github https://github.com/bestander/webpack-typescript-angular that shows a completed example with Angular components being compiled in one script with almost no configuration.

Why use webpack if your build system is working fine?

Webpack gives you a hassle-free way to package multi-root SPAs that would be a troublesome Grunt/gulp/Require.js configuration file.

The nicest thing about it is that you define your components (logic, view and styles) right in the JS code and write less configuration files.