Common problem: you are writing a library that you want to reuse in Node and maybe in a browser. You want the client apps to be able to include only the used parts, and not your entire code. How do you do this?
A great tool for this is Rollup. If your library uses ES2015 modules
export keywords), then Rollup can perform static analysis and determine
which pieces are reachable and can drop everything else, making the tiny output bundle.
Write the library
First let us write a sample library, for example named
try-rollup with two functions
mul. Our single
index.js will export both
// try-rollup index.js
We can bundle this module so it is usable as a plain Node module for distribution. This is
simple enough to use "rollup" from the command line. In addition, I will set
the main file in my
package.json to point at the created bundle.
npm run build creates an output file that looks almost like the input file
but is compatible with Node CommonJS
The most important field in
module that points back at our original ES6 source
index.js. When we distribute our library we include the CommonJS bundle and the entry file
index.js (by listing them both in
The users of our library
try-rollup can use Rollup
or Webpack v2 to bundle their code and the tools can go back to the original ES6 code and do the
application tree-shaking again, see
Rollup module doc.
Use the library
Let us write a small Node client application called
try-rollup-user that uses
For simplicity I will install
try-rollup from the file system folder, without publishing to the
public NPM registry. I also need Rollup again. Because the module
try-rollup goes into
node_modules folder, I will need a Rollup plugin to actually find it (since Rollup supports
many different module definitions).
In our case it is node-resolve plugin
npm i -D rollup rollup-plugin-node-resolve ../try-rollup
package.json shows the build command - now I need to use the
[Rollup config file][https://rollupjs.org/#using-config-files]
to configure the build step
To execute the result we point Node at the output file
The Rollup config file just reads the entry file and uses the Node resolve plugin for any
module installed using
npm install command.
Bundle and run
Let us say our app is very simple and only uses
mul function from
try-rollup and nothing else.
The build step generates tree-shaken file in CommonJS format
// "npm run build" which runs "rollup -c"
That is it - Rollup finds
package.json, finds the
property and looks at the original ES6 source files, which can be analyzed and tree-shaken.
This is how we get
try-rollup and nothing else in the output file.
Bonus - Bundle ES6
You can roll and distribute 2 bundles: one with CommonJS code and another one with ES6 modules
without distributing your entire source. This is especially helpful if your library depends
on other ES6 libraries and can benefit from tree-shaking itself. Make sure to save your
devDependencies because we will include them in the generated bundle.
Because Rollup does not support multiple bundles right away (but you can write separate Rollup
commands of course), I will use my tool rollem.
Here is the
rollem.config.js file that defines two output bundles: one in CommonJS format,
another in ES6 format.
We are only going to distribute these bundles, and not the original files (see
$ npm run build
We can install and use the new
try-rollup just like before - nothing changes in the client code
except the install gets just three files:
$ ls -lR node_modules/try-rollup
Tree-shaking all the way!
Double bonus - Multiple targets using Rollup
Rich Harris, the author of Rollup, pointed that you can output
multiple formats from same entry point using
rollup.config.js, even reusing the filenames
defined in the package file.
This is my favorite syntax :)