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
(think import and 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 add
and mul. Our single index.js will export both
1 | // 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.
1 | { |
Command npm run build creates an output file that looks almost like the input file index.js,
but is compatible with Node CommonJS require call
1 | // dist/index.js |
The most important field in package.json is module that points back at our original ES6 source
file index.js. When we distribute our library we include the CommonJS bundle and the entry file
index.js (by listing them both in files list).
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 try-rollup.
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
1 | npm i -D rollup rollup-plugin-node-resolve ../try-rollup |
The 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
1 | { |
To execute the result we point Node at the output file build/index.js.
The Rollup config file just reads the entry file and uses the Node resolve plugin for any
module installed using npm install command.
1 | // rollup.config.js |
Bundle and run
Let us say our app is very simple and only uses mul function from try-rollup and nothing else.
1 | // index.js |
The build step generates tree-shaken file in CommonJS format
1 | // "npm run build" which runs "rollup -c" |
That is it - Rollup finds node_modules/try-rollup, loads package.json, finds the module
property and looks at the original ES6 source files, which can be analyzed and tree-shaken.
This is how we get mul from 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
dependencies as 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.
1 | // rollem.config.js |
We are only going to distribute these bundles, and not the original files (see files list)
1 | { |
1 | $ 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:
1 | $ 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.
1 | // rollup.config.js |
This is my favorite syntax :)