Bundling ES2015 Modules with TypeScript and Rollup
The TypeScript compiler allows you to specify the JavaScript version to transpile your code to. As of June 2016, you can target the following language levels:
ES3
ES5
ES6
/ES2015
Similarly, the compiler can emit modules in various formats:
AMD
CommonJS
ES2015
System
UMD
Depending on the JavaScript environment you're targeting, you'd choose a specific combination of language target and module format. For instance, you could pick ES6
and CommonJS
when targeting Node v6.2.2, which supports pretty much all ECMAScript 2015 features except native modules.
If you're writing a web application, you'd transpile your TypeScript down to ES5
to have your JavaScript run in all current browsers. As for the module system, a popular choice is to target the CommonJS
format and then use Browserify or Webpack to bundle all modules together into a single file.
#Bundling ES2015 Modules with Rollup
In addition to Browserify and Webpack, there's a new kid on the block: Rollup, the next-generation JavaScript module bundler. Its main value proposition is tree-shaking, a process that automatically excludes unused module exports from bundles. The idea is that there's no need to include all functions of a library in the generated bundle if your application only imports a few of them.
Rollup needs to understand the entire dependency graph of your modules in order to determine which exports are being used by your application. The fully static structure of the ECMAScript 2015 module system makes it possible to analyze all imports and exports at compile-time. Check out Bundling and Tree-Shaking with Rollup and ECMAScript 2015 Modules for more details.
#Emitting ES2015 Modules and ES5 Code with tsc
To create a web application that runs in all browsers, the TypeScript compiler must target ES3
or ES5
. At the same time, it needs to emit ES2015
modules so that Rollup can do its work. Up until recently, these were conflicting requirements that made the compiler complain. You could only emit ES2015
modules when targeting ES6
:
This restriction has been removed with pull request #9042, which has been merged into the master
branch. The feature will be part of the upcoming TypeScript 2.0 release and is available in the nightly builds today.
#Creating a Bundle with TypeScript and Rollup
Let's take look at an example. Here's a simple math module that exports two functions using the exponentiation operator standardized in ECMAScript 2016:
// math.ts
export function square(x: number) {
return x ** 2;
}
export function cube(x: number) {
return x ** 3;
}
The square
function is then imported in the main module:
// main.ts
import { square } from "./math";
console.log(square(3));
I'm using a nightly build of the TypeScript compiler with the following options in the tsconfig.json:
{
"compilerOptions": {
"target": "es5",
"module": "es2015"
}
}
Here's the math.js file with all traces of TypeScript removed. Except for the export
keyword, it's valid ES5 code:
// math.js
export function square(x) {
return Math.pow(x, 2);
}
export function cube(x) {
return Math.pow(x, 3);
}
Except for a missing blank line, the main.js is no different from the original TypeScript file:
// main.js
import { square } from "./math";
console.log(square(3));
If we now run the rollup main.js
command, we get the following bundle:
function square(x) {
return Math.pow(x, 2);
}
console.log(square(3));
Notice what just happened: Rollup determined that the exported cube
function is never used, so it's not part of the bundle. Also, all import
and export
keywords are gone because all dependencies have been inlined in the correct order.
And there it is, the entire application in a single file containing only ES5 code. No TypeScript, no ECMAScript 2015 modules!