Build Rollup UMD bundle for CommonJS


Webpack to is great for building apps and Rollup is great for building libraries.

Rollup supports ES modules out of the box. However, to support CommonJS, the following plugins are required:

Prerequisites

Install rollup locally (or globally):

$ npm install rollup # --global

Create main module index.js (entry file):

$ echo "export default 'Hello, world!';" > index.js

Initialize package.json:

$ npm init --yes

Add build script to package.json:

{
  "scripts": {
    "build": "rollup index.js --file dist/bundle.js"
  }
}

The script will compile index.js to dist/bundle.js.

However, when you run the script:

$ npm run build

You’ll get the error:

Error: You must specify "output.format", which can be one of "amd", "cjs", "system", "esm", "iife" or "umd".

Output Format

When bundling with Rollup, it expects an output format. You can follow the general guidelines:

See table below for summary:

Environment Output Format
Browser iife
Node.js cjs
Browser + Node.js umd

Thus, to generate a UMD bundle with the name of the export as MyModuleName:

rollup index.js --file dist/bundle.js --format umd --name 'MyModuleName'

Load with script

The module can be loaded in the browser with a <script> tag:

<!-- script.html -->
<script src="dist/bundle.js"></script>
<script>
  console.log(window.MyModuleName);
</script>

Load with AMD

Or it can be loaded in the browser using AMD (Asynchronous Module Definition):

<!-- amd.html -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.6/require.min.js"></script>
<script>
  window.requirejs(['dist/bundle'], function(MyModuleName) {
    console.log(MyModuleName);
  });
</script>

Load with CommonJS

Or it can be loaded in Node.js using CommonJS:

$ node
> const MyModuleName = require('./dist/bundle');
> console.log(MyModuleName);
Hello, world!

Config File

Instead of passing the options via the CLI (command-line interface), we can specify them in the configuration file rollup.config.js:

const config = {
  input: 'index.js',
  output: {
    file: 'dist/bundle.js',
    format: 'umd',
    name: 'MyModuleName',
  },
};

export default config;

Update the build script in package.json so it picks up the config:

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

To give the config file a name other than the default, you’ll need to specify the custom file location:

rollup --config my.config.js

CommonJS

To use CommonJS syntax, install rollup-plugin-commonjs:

$ npm install rollup-plugin-commonjs

Add the plugin to the rollup config:

+import commonjs from 'rollup-plugin-commonjs';

 const config = {
   input: 'index.js',
   output: {
     file: 'dist/bundle.js',
     format: 'umd',
     name: 'MyModuleName',
   },
+  plugins: [commonjs()],
 };

 export default config;

Now you can refactor index.js:

// index.js
module.exports = 'Hello, world!';

After running the build, loading the bundle via script, AMD, or CommonJS should still work:

$ npm run build
$ open script.html

Node Resolve

Let’s say we want to refactor index.js and use lodash:

$ npm install lodash
// index.js
var template = require('lodash/template');
var compiled = template('Hello, <%= name %>!');
module.exports = compiled({ name: 'world' });

In order for rollup to locate 3rd party modules in node_modules using Node’s resolution algorithm, we need to install rollup-plugin-node-resolve:

$ npm install rollup-plugin-node-resolve

Add the plugin to the rollup config:

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

 const config = {
   input: 'index.js',
   output: {
     file: 'dist/bundle.js',
     format: 'umd',
     name: 'MyModuleName',
   },
-  plugins: [commonjs()],
+  plugins: [commonjs(), resolve()],
 };

 export default config;

Building the bundle should still work as expected:

$ npm run build
$ node
> require('./dist/bundle');
'Hello, world!'

Note: If you want the module resolution to respect the “browser” field in package.json, you can set the option resolve({ browser: true }).

Uglify

Lastly, we may want to minify our bundle using UglifyJS. Luckily, there’s a plugin that does just that.

Install rollup-plugin-uglify:

$ npm install rollup-plugin-uglify

Add the plugin to the rollup config:

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

 const config = {
   input: 'index.js',
   output: {
     format: 'umd',
     name: 'MyModuleName',
   },
-  plugins: [commonjs(), resolve()],
+  plugins: [commonjs(), resolve(), uglify()],
 };

 export default config;

If you want to differentiate the build depending on the environment, you can do the following:

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

 const config = {
   input: 'index.js',
   output: {
     format: 'umd',
     name: 'MyModuleName',
   },
-  plugins: [commonjs(), resolve(), uglify()],
+  plugins: [commonjs(), resolve()],
 };

+if (process.env.NODE_ENV === 'production') {
+  config.plugins.push(uglify());
+}
+
 export default config;

You can either pass the environment variable to the build script:

NODE_ENV=production npm run build

Or set it in the build script itself:

{
  "scripts": {
    "build": "npm run build:min && npm run build:unmin",
    "build:min": "NODE_ENV=production rollup --config",
    "build:unmin": "NODE_ENV=production rollup --config --file dist/bundle.min.js"
  }
}


If you enjoyed this post, please consider supporting this site!