Building Javascript (and other things) with Rollup
Hey, another blog post!
Recently I've been somewhat distracted by another project which has been most interesting. I've learnt a bunch of things (including getting started with LeafletJS and Chart.JS), but most notably I've been experimenting with another Javascript build system.
I've used quite a few build systems over the years. For the uninitiated, their primary purpose is to turn lots of separate source files into as few output files as possible. This is important with things that run in the browser, because the browser has to download everything before it can execute it. Fewer files and less data means a speedier application - so everyone wins.
I've been rather unhappy with the build systems I've used so far:
- Gulp.js was really heavy and required a lot of configuration in order to get things moving.
- Browserify was great, but I found the feature set lacking and ended up moving on because it couldn't do what I wanted it to.
- Webpack was ok, but it also has a huge footprint and was super slow too.
One thing led to another, and after trying a few other systems (I can't remember their names, but one begin with P and has a taped-up cardboard box as a logo) I found Rollup (I think Webpack requires Rollup as a dependency under the hood, if I'm not mistaken).
Straight away, I found that Rollup was much faster than Webpack. It also requires minimal configuration (which doesn't need changing every time you create a new source file) - which allows me to focus on the code I'm writing instead of battling with the build system to get it to do what I want.
It also supports plugins - and with a bunch available on npm, there's no need to write your own plugin to perform most common tasks. For my recent project I linked to above, I was able to put a bunch of plugins together with a configuration file - and, with a bit of fiddling around, I've got it to build both my Javascript and my CSS (via a postcss integration plugin) and putt he result in a build output folder that I've added to my .gitignore file.
I ended up using the following plugins:
- rollup-plugin-node-resolve - essential. Allows
importstatements to be automatically resolved to their respective files. - rollup-plugin-commonjs - An addition to the above, if I recall correctly
- rollup-plugin-postcss - Provides integration with Postcss. I've found Postcss with be an effective solution to the awkward issue of CSS
- rollup-plugin-terser - Uses Terser to minify modern Javascript. I didn't know this existed until I found the Rollup plugin - it's a fork of and the successor to UglifyJS2, which only supports ES5 syntax.
....and the following Postcss plugins:
- postcss-import - Inlines
@importstatements into a single CSS source file, allowing you to split your CSS up into multiple files (finally! :P) - postcss-copy - after trying 5 different alternatives, I found this to be the most effective at automatically copying images etc. to the build output folder - potentially renaming them - and altering the corresponding references in the CSS source code
The definition of plugins is done in a Javascript configuration file in a array - making it clear that your code goes through a pipeline of plugins that transform step-by-step to form the final output - creating and maintaining a source map along the way (very handy for debugging code in the browser. It's basically a file that lets the browser reverse-engineer the final output to present you with the original source code again whilst debugging) - which works flawlessly in my browser (the same can't be said for Webpack - I had lots of issues with the generated source maps there).
I ended up with a config file like this:
(Can't see the file above? Try viewing it directly.)
In short, the pipeline goes something like this:
(Source file; Created in Draw.io)
Actually calling Rollup is done in a Bash-based general build task file. Rollup is installed locally to the project (npm install --save rollup), so I call it like this:
node_modules/.bin/rollup --sourcemap --config rollup.config.js;
I should probably make a blog post about that build task system I wrote and why/how I made it, but that's a topic for another time :P
I'll probably end up tweaking the setup I've described here a bit in the future, but for now it does everything I'd like it to :-)
Found this interesting? Got a different way of doing it? Still confused? Comment below!