How to Bundle JavaScript With Rollup
Introduction
Rollup.js focuses largely on JavaScript (although there are plugins for HTML templates and CSS). It offers a bewildering number of options, but it’s simple to get started and bundling is quick.
Other bundler choices, such as webpack, Snowpack, and Parcel, claim to magically manage everything: HTML templating, image optimization, CSS processing, JavaScript bundling, and more. This works well when you’re satisfied with the default settings, but changing customizations can be tricky
Prerequisites
- Basic knowledge of JavaScript.
- Being Familiar with ES2015 modules doesn’t hurt, either.
- Installing npm on your machine.
What Is Rollup?
Rollup is a next-generation JavaScript module bundler. Create your app or library with ES2015 modules, then effectively bundle them into a single file for usage in browsers and Node.js. It’s identical to Browserify and webpack.
Using Rollup to Process and Bundle JavaScript Files
Step 1: Create a compiled JavaScript and CSS project.
You need to have some code to work in order to get started. You can get an app available on GITHUB to work on.
The structure of the folder looks like this:
learn-rollup/
├── build/
│ └── index.html
├── src/
│ ├── scripts/
│ │ ├── modules/
│ │ │ ├── mod1.js
│ │ │ └── mod2.js
│ │ └── main.js
│ └── styles/
│ └── main.css
└── package.json
By running the following command you can install the app we’ll be working with into your terminal.
# Move to the folder where you keep your dev projects.
cd /path/to/your/projects
# Clone the starter branch of the app from GitHub.
git clone -b step-0 –single-branch https://github.com/jlengstorf/learn-rollup.git
# The files are downloaded to /path/to/your/projects/learn-rollup/
Create a configuration file after Installing Rollup
With following command, Get started with installing Rollup:
npm install –save-dev rollup
You need to create a new file called rollup.config.js in the learn-rollup folder and add the following.
export default {
entry: ‘src/scripts/main.js’,
dest: ‘build/js/main.min.js’,
format: ‘iife’,
sourceMap: ‘inline’,
};
Test the Rollup configuration
You can test Once we’ve created the config file, we can test that everything is working by running the following command in our terminal:
./node_modules/.bin/rollup -c
A folder called as build in your projects will be created with a js subfolder which contains generated main.min.js file.
You can see the bundle that was created properly by opening build/index.html in the browser:
The example shows how the project will look like.
Look at the Bundled Output
(function () {
‘use strict’;
/**
* Says hello.
* @param {String} name a name
* @return {String} a greeting for `name`
*/
function sayHelloTo(name) {
const toSay = `Hello, ${name}!`;
return toSay;
}
/**
* Adds all the values in an array.
* @param {Array} arr an array of numbers
* @return {Number} the sum of all the array values
*/
const addArray = (arr) => {
const result = arr.reduce((a, b) => a + b, 0);
return result;
};
// Import a couple modules for testing.
// Run some functions from our imported modules.
const result1 = sayHelloTo(‘Jason’);
const result2 = addArray([1, 2, 3, 4]);
// Print the results on the page.
const printTarget = document.getElementsByClassName(‘debug__output’)[0];
printTarget.innerText = `sayHelloTo(‘Jason’) => ${result1}\n\n`;
printTarget.innerText += `addArray([1, 2, 3, 4]) => ${result2}`;
})();
//# sourceMappingURL=data:application/json;charset=utf-8;base64,…
Step 2: To use a new Javascript features set up the Babel
We now have a code bundle that will work in modern browsers, but it will break if the browser is more than a couple versions old in some cases — not ideal.
Install the required modules
First, we must install the Babel Rollup plugin and the corresponding Babel preset.
Installing Rollup’s Babel plugin
npm install –save-dev rollup-plugin-babel
Installing the Babel preset for transpiling ES2015
npm install –save-dev babel-preset-es2015
Installing Babel’s external helpers for module support
npm install –save-dev babel-plugin-external-helpersCreate a .babelrc.
Next, in the root directory of your project (learn-rollup/), create a new file called.babelrc. Add the following JSON inside:
{
“presets”: [
[
“es2015”,
{
“modules”: false
}
]
],
“plugins”: [“external-helpers”]
}This tells Babel which preset it should use during transpiling.
Update rollup.config.js
To make this work, we need to alter rollup.config.js.
We import the Babel plugin inside, then add it to a new configuration variable called plugins, which will store an array of plugins.
// Rollup plugins
import babel from ‘rollup-plugin-babel’;
export default {
entry: ‘src/scripts/main.js’,
dest: ‘build/js/main.min.js’,
format: ‘iife’,
sourceMap: ‘inline’,
plugins: [
babel({
exclude: ‘node_modules/**’,
}),
],
};
We set an exclude config property to ignore the node modules directory to avoid transpiling third-party scripts.
Check the bundle output
We can recreate the bundle now that everything is installed and configured:
./node_modules/.bin/rollup -c
When we look at the output, we see that it is basically the same. However, there are a few significant differences: Take a look at the addArray() method, for example:
var addArray = function addArray(arr) {
var result = arr.reduce(function (a, b) {
return a + b;
}, 0);
return result;
};
Step 3: Adding ESLint to check for common JavaScript errors
Using a linter for your code is usually a good idea since it promotes consistent coding practices and helps discover tough errors like missing brackets or parentheses.
Modules Installation
To Install the ESLint Rollup plugin in order to use ESLint:
npm install –save-dev rollup-plugin-eslint
Generate a .eslintrc.json
To ensure that we only obtain the errors we desire, we must first setup ESLint. Fortunately, we can construct the most of this setup automatically by executing the following command:
$ ./node_modules/.bin/eslint –init
? How would you like to configure ESLint? Answer questions about your style
? Are you using ECMAScript 6 features? Yes
? Are you using ES6 modules? Yes
? Where will your code run? Browser
? Do you use CommonJS? No
? Do you use JSX? No
? What style of indentation do you use? Spaces
? What quotes do you use for strings? Single
? What line endings do you use? Unix
? Do you require semicolons? Yes
? What format do you want your config file to be in? JSON
Created .eslintrc.json file in /Users/jlengstorf/dev/code.lengstorf.com/projects/learn-rollup Successfully
If you answer the questions as indicated above, the result in.eslintrc.json will be as follows:
{
“env”: {
“browser”: true,
“es6”: true
},
“extends”: “eslint:recommended”,
“parserOptions”: {
“sourceType”: “module”
},
“rules”: {
“indent”: [“error”, 4],
“linebreak-style”: [“error”, “unix”],
“quotes”: [“error”, “single”],
“semi”: [“error”, “always”]
}
}
Tweak .eslintrc.json
However, in order to avoid errors in our project, we must make the following changes:
- Use 2 spaces instead of 4.
- Use a global variable called ENV later.
Make the necessary changes to your.eslintrc.json — the globals property and the indent property — as follows:
{
“env”: {
“browser”: true,
“es6”: true
},
“globals”: {
“ENV”: true
},
“extends”: “eslint:recommended”,
“parserOptions”: {
“sourceType”: “module”
},
“rules”: {
“indent”: [“error”, 2],
“linebreak-style”: [“error”, “unix”],
“quotes”: [“error”, “single”],
“semi”: [“error”, “always”]
}
}
Update rollup.config.js
Next, add the ESLint plugin to the Rollup configuration by importing it:
// Rollup plugins
import babel from ‘rollup-plugin-babel’;
import eslint from ‘rollup-plugin-eslint’;
export default {
entry: ‘src/scripts/main.js’,
dest: ‘build/js/main.min.js’,
format: ‘iife’,
sourceMap: ‘inline’,
plugins: [
eslint({
exclude: [‘src/styles/**’],
}),
babel({
exclude: ‘node_modules/**’,
}),
],
};
Check the console output
At first, when we run ./node_modules/.bin/rollup -c, nothing seems to be happening. That’s because as it stands, the app’s code passes the linter without issues.
$ ./node_modules/.bin/rollup -c
/Users/jlengstorf/dev/code.lengstorf.com/projects/learn-rollup/src/scripts/main.js
12:64 error Missing semicolon semi
✖ 1 problem (1 error, 0 warnings)
Step 4: Adding Plugins to handle non-ES modules
If any of your dependencies use Node-style modules, this is critical. You’ll receive require errors if you don’t have it.
Add a Node module as a dependency.
It would be simple to get through this sample project without mentioning a third-party module, but it will not suffice in real-world projects. So, in order to make our Rollup configuration genuinely functional.
For simplicity, add a simple logger to the code using the debug package. Start by installing it:
npm install –save debug
Then, inside src/scripts/main.js, add some simple logging
// Import a couple modules for testing.
import { sayHelloTo } from ‘./modules/mod1’;
import addArray from ‘./modules/mod2’;
// Import a logger for easier debugging.
import debug from ‘debug’;
const log = debug(‘app:log’);
// Enable the logger.
debug.enable(‘*’);
log(‘Logging is enabled!’);
// Run some functions from our imported modules.
const result1 = sayHelloTo(‘Jason’);
const result2 = addArray([1, 2, 3, 4]);
// Print the results on the page.
const printTarget = document.getElementsByClassName(‘debug__output’)[0];
printTarget.innerText = `sayHelloTo(‘Jason’) => ${result1}\n\n`;
printTarget.innerText += `addArray([1, 2, 3, 4]) => ${result2}`;
You will get a warning when you run rollup.
$ ./node_modules/.bin/rollup -c
Treating ‘debug’ as external dependency
No name was provided for external module ‘debug’ in options.globals – guessing ‘debug’
And if we check our index.html again, we can see that a ReferenceError was thrown for debug.
The third party node modules won’t load properly with Rollup by default.
Installing the modules
You need to add two plugins to Rollup to work around this problem:
- rollup-plugin-node-resolve,
- rollup-plugin-commonjs
With the following command Install both plugins
npm install –save-dev rollup-plugin-node-resolve rollup-plugin-commonjs
Update rollup.config.js
Next step is to add and import the plugins to the Rollup config:
// Rollup plugins
import babel from ‘rollup-plugin-babel’;
import eslint from ‘rollup-plugin-eslint’;
import resolve from ‘rollup-plugin-node-resolve’;
import commonjs from ‘rollup-plugin-commonjs’;
export default {
entry: ‘src/scripts/main.js’,
dest: ‘build/js/main.min.js’,
format: ‘iife’,
sourceMap: ‘inline’,
plugins: [
resolve({
jsnext: true,
main: true,
browser: true,
}),
commonjs(),
eslint({
exclude: [‘src/styles/**’],
}),
babel({
exclude: ‘node_modules/**’,
}),
],
};
Check the console output
check the browser again to see the output after rebuilding the bundle with ./node_modules/.bin/rollup -c, then:
Step 5: Replace environment variables with a plugin
Add an ENV-based conditional in main.js
Let’s utilize an environment variable to disable our logging script while we’re not in production mode. Let’s update the way log() is started in src/scripts/main.js:
// Import a logger for easier debugging.
import debug from ‘debug’;
const log = debug(‘app:log’);
// The logger should only be disabled if we’re not in production.
if (ENV !== ‘production’) {
// Enable the logger.
debug.enable(‘*’);
log(‘Logging is enabled!’);
} else {
debug.disable();
}
However, after rebuilding our bundle (./node modules/.bin/rollup -c) and inspecting the browser, we observe that this causes a ReferenceError for ENV.
That should come as no surprise, given that we haven’t defined it anywhere. However, even if we attempt ENV=production./node modules/.bin/rollup -c, it still does not function. This is due to the fact that changing an environment variable in this manner only makes it available to Rollup, not the bundle formed by Rollup.
To feed our environment variables into the bundle, we’ll need to utilize a plugin.
Install the modules
Install rollup-plugin-replace first, which is effectively a find-and-replace application. It can perform a lot of things, but for our needs, we’ll just have it detect an instance of an environment variable and replace it with the actual value.
npm install –save-dev rollup-plugin-replace
Update rollup.config.js
let’s import the plugin and add it to our list of plugins inside rollup.config.js,.
// Rollup plugins
import babel from ‘rollup-plugin-babel’;
import eslint from ‘rollup-plugin-eslint’;
import resolve from ‘rollup-plugin-node-resolve’;
import commonjs from ‘rollup-plugin-commonjs’;
import replace from ‘rollup-plugin-replace’;
export default {
entry: ‘src/scripts/main.js’,
dest: ‘build/js/main.min.js’,
format: ‘iife’,
sourceMap: ‘inline’,
plugins: [
resolve({
jsnext: true,
main: true,
browser: true,
}),
commonjs(),
eslint({
exclude: [‘src/styles/**’],
}),
babel({
exclude: ‘node_modules/**’,
}),
replace({
exclude: ‘node_modules/**’,
ENV: JSON.stringify(process.env.NODE_ENV || ‘development’),
}),
],
};
In our setup, we’ll look for every instance of ENV and replace it with the value of process.env. NODE ENV – the standard means of configuring the environment in Node apps — or “development.” Because ENV is not encased in double quotes, we use JSON.stringify() to ensure that the value is.
Set the exclude attribute to disregard our node modules directory and all the packages it contains to avoid conflicts with third-party code.
Check the results
Begin by rebuilding the bundle and testing the browser. The console log should appear, as previously. That’s excellent since it suggests our default value was used.
Let’s execute the command in production mode to observe where the power comes in:
NODE_ENV=production ./node_modules/.bin/rollup -c
When we restart the browser, nothing is reported to the terminal:
With no code modifications, using an environment variable disables logging.
Step 6: To compress and minify our generated script Add UglifyJS
The final JavaScript step in this tutorial will be to add UglifyJS to minify and compress the bundle. This may significantly reduce the size of a bundle by eliminating comments, reducing variable names, and generally mangling the code – rendering it incomprehensible for humans but much more efficient to distribute over a network.
Install the plugin
UglifyJS will be used to compress the bundle using rollup-plugin-uglify.
Install it with the following:
npm install –save-dev rollup-plugin-uglify
Update rollup.config.js
Next, let’s include Uglify in our Rollup configuration. However, for development legibility, let’s make uglification a production-only feature:
// Rollup plugins
import babel from ‘rollup-plugin-babel’;
import eslint from ‘rollup-plugin-eslint’;
import resolve from ‘rollup-plugin-node-resolve’;
import commonjs from ‘rollup-plugin-commonjs’;
import replace from ‘rollup-plugin-replace’;
import uglify from ‘rollup-plugin-uglify’;
export default {
entry: ‘src/scripts/main.js’,
dest: ‘build/js/main.min.js’,
format: ‘iife’,
sourceMap: ‘inline’,
plugins: [
resolve({
jsnext: true,
main: true,
browser: true,
}),
commonjs(),
eslint({
exclude: [‘src/styles/**’],
}),
babel({
exclude: ‘node_modules/**’,
}),
replace({
ENV: JSON.stringify(process.env.NODE_ENV || ‘development’),
}),
process.env.NODE_ENV === ‘production’ && uglify(),
],
};
Check the minified bundle
After saving the settings, run Rollup with NODE ENV in production:
NODE_ENV=production ./node_modules/.bin/rollup -c
The result isn’t attractive, but it’s a lot smaller. Here’s a snapshot of build/js/main.min.js right now:
Conclusion
Rollup Revolution
Rollup.js takes some time to configure, but the result will be adequate for many of your applications. It’s great if you want a more customizable and speedier JavaScript bundler.
Add comment