rollup.js

the next-generation JavaScript module bundler

What is Rollup?

Rollup is a JavaScript module bundler. It allows you to write your application or library as a set of modules – using modern ES2015 import/export syntax – and bundle them up into a single file that you can easily include on an HTML page as a <script> tag, or distribute via package managers like npm.

What are modules, and why should we use them?

A module is just a JavaScript file that is explicit about what it needs in order to run, and what code it makes available to the outside world. Breaking your program apart like this makes it much easier to write good, maintainable code, because you only need to focus on one thing at a time. Suppose you were writing a spellchecker for a web app:

import dictionary from './dictionary.js';
import { extractWords } from './string-utils.js';

export default function spellcheck ( text ) {
  var words = extractWords( text );
  var mistakes = [];

  words.forEach( function ( word ) {
    if ( !dictionary.contains( word.text ) ) {
      mistakes.push({
        start: word.start,
        end: word.end,
        suggestions: dictionary.getSuggestions( word.text )
      });
    }
  });

  /* ... */
}

We don't need to worry about the implementation of extractWords – how it deals with punctuation and so forth. And we don't need to worry about where dictionary gets its data from, or what algorithm it uses to generate suggestions. All we need to worry about is what to do when we find mistakes.

Now imagine writing all the relevant logic in a single file. It would quickly become very large, and very difficult to maintain.

In short, modules help us write code that is

  • reusable
  • maintainable
  • testable
  • easier to collaborate on, because Alice and Bob can work on the same app simultaneously (Alice can work on dictionary while Bob fixes extractWords) without stomping each other's changes
  • more bug-resistant, because we don't need to worry about things like naming conflicts between modules

For a while, JavaScripters have been using CommonJS modules and AMD modules as well as various ad hoc formats, but the modern import/export syntax has a number of crucial advantages.

Why do we need to bundle our modules?

For one thing, ES2015 modules don't yet work in browsers or Node.js, so we need to convert them into a usable format. Rollup lets you create AMD modules (for use with tools like Require.js), CommonJS modules (which run in Node.js), self-executing bundles (for inclusion on a page as a <script> tag), or Universal Module Definition modules, which work in all environments.

But there are also some major advantages to bundling your code:

  • A bundle is more portable and easier to consume than a collection of files
  • Compression works better with fewer bigger files than with lots of small ones
  • In the browser, a 100kb bundle loads much faster than 5 20kb files (that will change when HTTP/2 gains widespread adoption, but we're not there yet)
  • By bundling code, we can take advantage of tree-shaking, resulting in fewer wasted bytes

Creating your first bundle

Before we begin, you'll need to have Node.js installed so that you can use npm. You'll also need to know how to access the command line on your machine.

The easiest way to use Rollup is via the Command Line Interface (or CLI). For now, we'll install it globally (later on we'll learn how to install it locally to your project so that your build process is portable, but don't worry about that yet). Type this into the command line:

npm install rollup --global # or `npm i rollup -g` for short

You can now run the rollup command. Try it!

rollup

Because no arguments were passed, Rollup prints usage instructions. This is the same as running rollup --help, or rollup -h.

Let's create a simple project:

mkdir -p my-rollup-project/src
cd my-rollup-project

First, we need an entry point:

cat <<EOS > src/main.js
import foo from './foo.js';
export default function () {
  console.log(foo);
}
EOS

Then, let's create the foo.js module that our entry point imports:

echo "export default 42;" > src/foo.js

Now we're ready to create a bundle:

rollup src/main.js

This will print the bundle straight to stdout:

var foo = 42;

function main () {
  console.log(foo);
}

export default main;

You can save the bundle as a file like so:

rollup src/main.js --output bundle.js # or rollup main.js -o bundle.js

(You could also do rollup src/main.js > bundle.js, but as we'll see later, this is less flexible if you're generating sourcemaps.)

Of course, this code won't actually run, because it's still an ES2015 module with an export statement. So let's create a CommonJS module, which will run in Node.js:

rollup src/main.js --output bundle.js --format cjs
# or rollup src/main.js -o bundle.js -f cjs

This creates the following bundle – anything exported from the entry module (in this case, a function that logs the answer to life, the universe and everything) becomes part of the bundle's exports:

'use strict';

var foo = 42;

function main () {
  console.log(foo);
}

module.exports = main;

Try running the code:

node
> var myBundle = require('./bundle.js');
> myBundle();
42

Congratulations! You've created your first bundle with Rollup.

Using config files

So far, so good, but as we start adding more options it becomes a bit of a nuisance to type out the command.

To save repeating ourselves, we can create a config file containing all the options we need. A config file is written in JavaScript and is more flexible than the raw CLI.

Create a file in the project root called rollup.config.js, and add the following code:

export default {
  entry: 'src/main.js',
  format: 'cjs',
  dest: 'bundle.js' // equivalent to --output
};

To use the config file, we use the --config or -c flag:

rm bundle.js # so we can check the command works!
rollup -c

You can override any of the options in the config file with the equivalent command line options:

rollup -c -o bundle-2.js # --output is equivalent to dest

(Note that Rollup itself processes the config file, which is why we're able to use export default syntax – the code isn't being transpiled with Babel or anything similar, so you can only use ES2015 features that are supported in the version of Node.js that you're running.)

You can, if you like, specify a different config file from the default rollup.config.js:

rollup --config rollup.config.dev.js
rollup --config rollup.config.prod.js

Getting started with plugins

So far, we've created a simple bundle from an entry point and a module imported via a relative path. As you build more complex bundles, you'll often need more flexibility – importing modules installed with npm, compiling code with Babel, working with JSON files and so on.

For that, we use plugins, which change the behaviour of Rollup at key points in the bundling process. A list of available plugins is maintained on the Rollup wiki.

Using plugins

For this tutorial, we'll use rollup-plugin-json, which allows Rollup to import data from a JSON file.

We'll start by adding a package.json file to our project. A package.json file contains essential metadata about your project, such as the current version, and which other packages it depends on:

{
  "name": "my-rollup-project",
  "version": "1.0.0"
}

Now, install rollup-plugin-json as a development dependency:

npm install --save-dev rollup-plugin-json # or npm i -D rollup-plugin-json

(We're using --save-dev rather than --save because our code doesn't actually depend on the plugin when it runs – only when we're building the bundle.)

Take a look at your package.json. It should look something like this:

{
  "name": "my-rollup-project",
  "version": "1.0.0",
  "devDependencies": {
    "rollup-plugin-json": "^2.0.0"
  }
}

Delete src/foo.js, and replace the contents of src/main.js with the following:

import { version } from '../package.json';
export default function () {
  console.log('current version is ' + version);
}

Edit your rollup.config.js file to include the JSON plugin:

import json from 'rollup-plugin-json';

export default {
  entry: 'src/main.js',
  format: 'cjs',
  plugins: [ json() ],
  dest: 'bundle.js'
};

Run Rollup with rollup -c. The result should look like this:

'use strict';

var version = "1.0.0";

function main () {
  console.log('current version is ' + version);
}

module.exports = main;

(Notice that only the data we actually need gets imported – the name and devDependencies parts of the package.json are ignored. That's tree-shaking in action!)

Using Rollup with Babel

Many developers use Babel in their projects, so that they can use futuristic JavaScript features that aren't yet supported by browsers and Node.js.

The easiest way to use both Babel and Rollup is with rollup-plugin-babel. Install it:

npm i -D rollup-plugin-babel

Add it to rollup.config.js:

import json from 'rollup-plugin-json';
import babel from 'rollup-plugin-babel';

export default {
  entry: 'src/main.js',
  format: 'cjs',
  plugins: [ json(), babel() ],
  dest: 'bundle.js'
};

Before Babel will actually compile your code, it needs to be configured. Create a new file, src/.babelrc:

{
  "presets": ["es2015-rollup"]
}

There are two slightly unusual things about this setup. First, we're using es2015-rollup instead of es2015 – that's because otherwise Babel will convert our modules to CommonJS before Rollup gets a chance to do its thing, causing it to fail. es2015-rollup also includes the external-helpers plugin, which allows Rollup to include any 'helpers' just once at the top of the bundle, rather than including them in every module that uses them (which is the default behaviour).

Secondly, we're putting our .babelrc file in src, rather than the project root. This allows us to have a different .babelrc for things like tests, if we need that later – it's generally a good idea to have separate configuration for separate tasks.

Run Rollup with rollup -c:

Couldn't find preset "es2015-rollup" relative to directory "/path/to/my-rollup-project/src"

Whoops! We need to install the preset:

npm i -D babel-preset-es2015-rollup

Running Rollup now will create a bundle... except we're not actually using any ES2015 features. Let's change that. Edit src/main.js:

import { version } from '../package.json';

const message = `current version is ${version}`;
export default () => console.log( message );

Try now – Rollup should create a bundle like this:

'use strict';

var version = "1.0.0";

var message = 'current version is ' + version;
var main = (function () {
  return console.log(message);
})

module.exports = main;

Using Rollup with npm

First, we need an file package.json, thereunto run the command

npm init

which returns the file

  {
    "name": "name-your-project",
    "version": "1.0.0",
    "description": "",
    "main": "index.js",
    "scripts": {
      "test": "echo \"Error: no test specified\" && exit 1"
    },
    "author": "",
    "license": "ISC"
  }

Then just install the rollup

npm install --save rollup

Create your file using of module es2015

  //src/main.js

  import foo from './foo.js';

  export default function () {
    console.log(foo);
  }

and create your module

 //src/foo.js

 export default 42;

To run, create one script in package.json

"scripts": {
    "build": "rollup src/main.js --output bundle.js"
}

and run

npm run build

you now have a compiled module

  var foo = 42;

  function main () {
    console.log(foo);
  }

  export default main;

Sourcemaps

coming soon...

Frequently asked questions

What is 'tree-shaking'?

coming soon...

Why are ES2015 modules better than AMD and CommonJS?

coming soon...

Who made the Rollup logo? It's lovely.

I know! It was made by Julian Lloyd.

Comparison with other tools

coming soon...