Front-End development has become extremely interesting and fun to do with the beginning of the ECMAScript and NPM era. There is a lot of packages and tools out there that we can use in our projects that can make our life easier. One of these tools is rollup.js.

Let's start the article with a short introduction and find out what rollup actually is and what it does for us and our application.

The official statement:

Rollup is a module bundler for JavaScript which compiles small pieces of code into something larger and more complex, such as a library or application. It uses the new standardized format for code modules included in the ES6 revision of JavaScript, instead of previous idiosyncratic solutions such as CommonJS and AMD.

Let's break down the statement above.

Rollup is a module bundler for JavaScript which compiles small pieces of code into something larger

Developing an application is a lot easier if we split it into logically independent smaller pieces. That way we reduce the overall complexity of our code during development thus making it more approachable and maintainable. It is easier and faster for someone to join the team if he/she can focus on the smaller part instead of analyzing the entire application logic and trying to isolate a specific code block. This can dramatically increase the possibility of errors after injecting a new piece of code in the middle of it which is something we do not want.

Rollup helps us solve the use case described above. It takes our small pieces and bundles them all together into a single code base. To do this, we can use the command line or a specific configuration file called rollup.config.js.

In this article, I will cover a configuration file approach.

It uses the new standardized format for code modules included in the ES6

This is very neat. What this does is that enables us to write the import/export statements within our JavaScript files. We can import data, constants, functions, entire logic blocks...and all this we can write in the next generation of JavaScript and let rollup (and its plugins) worry about creating the browser readable output. It's possible to specify the output format, which we will see later in this post.

Just to summarize and answer the WHAT and WHY questions. Rollup is a JavaScript bundler (also can minify the output with a plugin) and we need it if we want to use the new syntactic sugar like import and export from the ECMAScript specification.

Note that the code below assumes that Node.js and NPM package manager had already been installed and that your application had been initialized with npm init command.

Installing Rollup

To install rollup and save it as development dependency we should run the following command:

npm install rollup --save-dev

The command above will install the rollup node package and update the package.json file located in our application root folder.

"devDependencies": {
    "rollup": "^1.10.0" // the version might be different in your case depending on the time reading this
}

Next, create a new file called rollup.config.js in the application root folder. Inside, add the following.

export default {
    input: './src/main.js',
    output: {
        file: './build/bundle.min.js',
        format: 'iife',
        name: 'bundle'
    }
}

Let’s see what each of these configuration options does for us:

  1. input - This is a required setup and it represents the file we want rollup to process. It should be the main entry point of the application where we import everything else required by it

  2. output - It's an object like configuration where we set up the result of our processing. Minimum of the configuration is to include the:

    1. file - This is the location where our bundle will be created. It represents the file to write to. Usually under the build or dist folder. The folder and the file will be generated automatically by rollup

    2. format - Rollup supports several output formats. In our example, we will use an immediately-invoked function expression (iife)

    3. name - Global variable name representing the created bundle

Other formats can be found here, section output.format

Test the configuration

Now when we have our setup, we can test if everything works.

First, create a source folder, src. This folder will contain our application source files. Inside of it, create the application entry point, a file called main.js and the index.html page.

Next, let's create a test module. In the src folder, create a subfolder modules and a file within called MyModule.js. Inside, add the following:

const sayHello = (message) => {
    alert(message);
}

export default sayHello;

In the main.js file add the import statement and use the imported function:

import sayHello from './modules/MyModule';

sayHello('Hello from Rollup');

Open the package.json file and add the following script under the script setting:

"scripts": {
    "build": "rollup -c"
}

and run the following command:

npm run build

This will create a new folder called build in our project that contains the generated bundle.min.js file. We can see that the bundle was created properly by adding it as a reference to our index.html page and opening it in the browser.

<!DOCTYPE html>
<html lang="en">
	<meta charset="utf-8">
	<head>
		<title>Rollup Example</title>
	</head>
	
	<body>
	</body>
	
	<script src="../build/bundle.min.js"></script>
</html>

If everything was done properly, an alert should popup immediately after opening the page.

At this point, only modern browsers will work without errors. To get this working with browsers that don’t support ECMAScript features, we need to include a couple of plugins.

Next Generation of JavaScript

Installing babel

In order to properly parse our module and make it compatible with older browsers, we should include babel to compile the output. If you are not familiar with it, babel is a JavaScript compiler and makes the next-gen JavaScript code cross-browser compatible by compiling it to the older version of it.

In order to continue with the example, we need to install the required packages:

npm install @babel/core @babel/preset-env rollup-plugin-babel --save-dev

The command above will update our dev dependencies like so:

// the versions might be different in your case depending on the time reading this
"devDependencies": {
    "@babel/core": "^7.4.3",
    "@babel/preset-env": "^7.4.3",
    "rollup": "^1.10.0",
    "rollup-plugin-babel": "^4.3.2"
 }

Next, we need to create a babel configuration file .babelrc in the application folder with the following content:

{
  "presets": [
      "@babel/env"
  ]
}

After these actions, babel is configured and ready for usage. Considering that this article is about rollup, visit the official babel site for more information.

Updating rollup.config.js

The above changes alone will do nothing because we did not tell rollup that it needs to use the newly installed packages. We do this by updating the rollup.config.js file as shown below:

import babel from 'rollup-plugin-babel';

export default {
    input: './src/main.js',
    output: {
        file: './build/bundle.min.js',
        format: 'iife',
        name: 'bundle'
    },
    plugins: [
        babel({
            exclude: 'node_modules/**'
        })
    ]
}

We left the input and output configurations like they were before, added an import statement to include the rollup-plugin-babel and introduced the plugins config option. Plugins are used to customize rollup behavior. In this case, we want it to compile our ECMAScript into its predecessor.

Also, we've excluded the node_modules folder to avoid third-party scripts and libraries being compiled. Now, we are ready to run our build command again:

npm run build

The expected result is that our bundle now should have different content which is cross-browser compatible.

The bundle.min.js without babel:

(function () {
	'use strict';

	const sayHello = (message) => {
		alert(message);
	};

	sayHello('Hello from Rollup');

}());

and with babel:

(function () {
	'use strict';

	var sayHello = function sayHello(message) {
	  alert(message);
	};

	sayHello('Hello from Rollup');

}());

Clearly, we can see the difference. Reserved word const is no longer present and it has been converted to var. Also, our arrow function has been converted to a cross-browser compatible version.

After opening index.html page in the browser, the result should be the same and a popup message should again be displayed.

Handling non-ES modules

So far our project was working without any node module dependency and the only module imported was the test one we've created. However, in the real world, this is rarely the case and our app would require a non-ES module.

Supporting the CommonJS modules is not provided out of the box by rollup, therefore we need a couple more plugins. In order to make our project to work with the node modules dependencies, we need to install the following packages:

npm install rollup-plugin-node-resolve rollup-plugin-commonjs --save-dev

The rollup-plugin-node-resolve plugin allows us to load the third-party modules and the rollup-plugin-commonjs plugin converts them into the ES6 version.

Our package.json file should look like this:

// the versions might be different in your case depending on the time reading this
"devDependencies": {
    "@babel/core": "^7.4.3",
    "@babel/preset-env": "^7.4.3",
    "rollup": "^1.10.0",
    "rollup-plugin-babel": "^4.3.2",
    "rollup-plugin-commonjs": "^9.3.4",
    "rollup-plugin-node-resolve": "^4.2.3"
}

Updating rollup.config.js - part 2

Again, rollup needs to know that it needs to use the new plugins. We configure them the same way we did for the rollup-plugin-babel plugin:

import babel from 'rollup-plugin-babel';
import resolve from 'rollup-plugin-node-resolve';
import commonjs from 'rollup-plugin-commonjs';

export default {
	input: './src/main.js',
    output: {
        file: './build/bundle.min.js',
        format: 'iife',
        name: 'bundle'
    },
	plugins: [
		babel({
			exclude: 'node_modules/**'
		}),
		resolve(),
		commonjs()
	]
}

Installing the Third-Party Library

Now we are ready to install and use our first third-party dependency. lodash for example. To install it, run the following command:

npm install lodash --save-dev

Our package.json file should look like this:

"devDependencies": {
    "@babel/core": "^7.4.3",
    "@babel/preset-env": "^7.4.3",
    "lodash": "^4.17.11",
    "rollup": "^1.10.0",
    "rollup-plugin-babel": "^4.3.2",
    "rollup-plugin-commonjs": "^9.3.4",
    "rollup-plugin-node-resolve": "^4.2.3"
}

Updating rollup.config.js - part 3

In order to use it, we again need to tweak the rollup.config.js file a little bit. We need to tell the rollup that we are using an external library with a global variable _. This is required since we are going to import it in our main.js file. Update the config like so:

import babel from 'rollup-plugin-babel';
import resolve from 'rollup-plugin-node-resolve';
import commonjs from 'rollup-plugin-commonjs';

export default {
    input: './src/main.js',
    output: {
        file: './build/bundle.min.js',
        format: 'iife',
        name: 'bundle',
        globals: {
            'lodash': '_',
        }
    },
    plugins: [
        babel({
            exclude: 'node_modules/**'
        }),
        resolve(),
        commonjs()
    ]
}

By adding the globals configuration we made sure that rollup knows what to do with the external import.

Next, we should test to see if everything is working fine by trying to use the lodash library. For example, let's use the _concat function.

Update the main.js file like so:

import sayHello from './modules/MyModule';
import _ from 'lodash';

const arr = _.concat([1, 2, 3], 4, [5]);
sayHello('Hello from Rollup and lodash: ' + arr);

and run the build command:

npm run build

The created bundle.min.js should contain both modules. The test module we've created and the externally imported lodash module.

If we run the index.html page at this point we should see an alert with a different message. It should print the Hello from Rollup and lodash: 1,2,3,4,5 without problems.

Compressing the Output

It is not uncommon that production environments require a minified version of the final bundle. This is needed for various reasons like reduced size, loading speed, network delivery...etc. In order to do minify it, we need to install another plugin called rollup-plugin-uglify:

npm install rollup-plugin-uglify --save-dev

Next, tell the rollup that it needs to use it by updating the rollup.config.js for the 4th time in this example:

import babel from 'rollup-plugin-babel';
import resolve from 'rollup-plugin-node-resolve';
import commonjs from 'rollup-plugin-commonjs';
import { uglify } from 'rollup-plugin-uglify';

export default {
    input: './src/main.js',
    output: {
        file: './build/bundle.min.js',
        format: 'iife',
        name: 'bundle',
        globals: {
            'lodash': '_',
        }
    },
    plugins: [
        babel({
            exclude: 'node_modules/**'
        }),
        resolve(),
        commonjs(),
        uglify()
    ]
}

and run the build command:

npm run build

Now, if we take a look at our bundle.min.js file the code should be a lot less readable :) If you compare the file before and after minification there should be an obvious size difference.

Comming Up

In the next rollup article, I will cover importing of the CSS and HTML files.

Thank you for reading and see you in the next post.