Cleaning up your codebase is a fundamental step in writing reliable code. This process can be particularly complex when the same code must be compiled for different technological platforms. Modern web development is a great example of this.
Nowadays, even when you have one single server providing a simple API, you will likely still handle tens of different smaller functionalities. This can get confusing pretty quickly, but luckily there are processes and tools to make our lives easier.
In this article, we’ll discuss the use of Rollup. Rollup is a module bundler designed to assist in the process of collecting all the functionalities of your brand-new library into a single bundle.
The idea behind Rollup is to simplify development by letting you concentrate on the function while Rollup prepares the bundle and wraps it up in the most efficient way — whether that be a library for a backend application or a module for the browser.
According to Rollup’s official documentation, “Rollup is a module bundler for JavaScript which compiles small pieces of code into something larger and more complex, such as a library or application.”
This is an interesting stance in designing a new library and is a step away from the long sessions of designing that were common in the early 2000s. Back then, you’d design your functions and objects and then bundle them into a single module. This would homogenize the interfaces and let them easily integrate into the place or platform to run on.
What makes Rollup special is its ability to keep files small. It achieves this in two ways: first due to the fact that Rollup is based on ES modules (more efficient than CommonJS modules); and second because it removes unused code from the produced bundle.
To prove the efficiency of Rollup, we’ll use a demo library. The challenge (that we will win, of course) is to have the exact same codebase, and then compile and use it with both TypeScript and JavaScript.
The small demo library we’ll use in this demo is a juxtaposition of two sets of functions, one for strings and the other for numbers. I assembled a repository on GitHub to save you some setup, found here.
The general idea is to have the exact same basic functions (you will find them in the /src
directory) collected in the files called numbersManipulation.js
and stringsManipulation.js
.
The functions we intend to use are collected in another file, gameLibrary.js
. This very file is the one we intend to use in our application.
The idea is to mimic the way Rollup is used to bundle together functionalities coming from different libraries.
Before going on, let’s set up and run the project to check that everything is working fine. First, run npm i
(you can also use yarn
if you prefer) to download all the packages.
Now, you are ready to execute the test script with npm run test
, which will compile the bundle and run both versions of the applications: the JavaScript (appJS.js
) version and the TypeScript version (appTS.ts
).
The output of the execution, seen below, will simply execute the two scripts, appJS.js
and appTS.ts
. Both scripts use the same codebase, the library /src/gameLibrary.js
, that will contribute to the bundle.js
file produced by Rollup.
Let’s look at the repository the /src
directory contains. As aforementioned, the libraries we intend to use in our applications and the command rollup -c
, executed in the script build
contained in the package.json
, will read the rollup.configuration.js
and assemble the bundle in the /dist
directory.
Let’s consider the content of such a directory:
The first file is the d.ts
declaration file that describes the shape of an existing JavaScript codebase for TypeScript.
The second file, bundle.js
, is the most interesting one. This is the bundle we created using Rollup.
The third file is .map
and is used to keep track of the original source, just in case we want to minify the bundle.
Now, we’ll briefly discuss how the tree shaking process works.
Tree shaking is an important feature in Rollup. Tree shaking allows for the creation of a bundle that includes the bare minimum functionality, keeping the code lighter and faster.
In the picture above, you can see the function call tree that is deducted by the static analysis of the code. Functions are enumerated and their relevance to the code we’re going to use is tracked down.
After this analysis, the bundle is assembled by collecting and juxtaposing the relevant functions. You can see the result of the bundle.js
file that contains all functions except isOdd()
in the photo below. isOdd()
was discarded because it wasn’t linked to any code.
Metaphorically, the function isOdd()
fell down by shaking the tree of the dependencies because it was not attached to any branch.
Another interesting detail is displayed in the dark blue blocks. These are functions that are not explicitly exported in the bundle but are used by the convert()
function. As expected, you will find them in bundle.js
, and the light-blue blocks explicitly exported in their own files.
Generally speaking, tree shaking is effective when your project needs many third-party libraries and frameworks that each have dozens of functions and methods available.
The bundle is created by executing rollup.config.js
. This describes the operations to be performed in terms of input files and plugins to create the bundle.
import dts from 'rollup-plugin-dts' import esbuild from 'rollup-plugin-esbuild' export default [ { input: `src/gameLibrary.js`, plugins: [esbuild()], output: [ { file: `dist/bundle.js`, format: 'cjs', sourcemap: true, exports: 'default', }, ] }, { input: `src/gameLibrary.js`, plugins: [dts()], output: { file: `dist/bundle.d.ts`, format: 'es', }, } ]
In the code above, you can see the configuration file from my GitHub repository. Let’s discuss this to understand where the magic happens.
The file instructs Rollup to perform two operations: assemble the bundle using the esbuild plugin
on line 7 and generate the d.ts
file using the dts
plugin on line 19. Both steps will be performed on the same input file, /src/gameLibrary.js
, shown on lines 6 and 18.
The file /src/gameLibrary.js
is the root of the dependencies tree that will be “shaken” in the bundle creation process.
The esbuild
plugin will generate a CommonJS bundle, shown on line 11, and also a source map (the .map
file we saw in the previous section) as requested by the value on line 12. It is worth noting that the general structure of the Rollup configuration file exports an array of plugins, each with its own configurations.
You can check this repository for a (not so) complete list of the plugins available.
Right after the publication of my post about Rollup and how cool it appeared to my inexperienced eyes, I stumbled on this interesting article that goes a little deeper into the whole concept of tree shaking that makes Rollup so unique.
Tree shaking is a method to remove unused code in the process of delivering code to the execution environment. In the context of a web app, it means delivering the exact amount of code involved in real computation within the browser.
The effectiveness of the tree shaking algorithm in Rollups is quite effective with respect to other bundlers (e.g., Webpack), as described in this LogRocket Blog post, the secret is the approach it adopts in handling code. Another point in favor of Rollup is the plugin ecosystem that allows to expand basic functionalities.
My old passion for benchmarks was titillated at this point, so I went on looking for more comparisons and found this rigorous blog post that verifies how effective the process of preparing the bundled code is to be delivered to clients.
Rollup is compared against Parcel and Webpack 4 (which is largely compatible with Webpack 3 in terms of configuration): the results, in terms of building time, is that Rollup is the fastest in the build process but the real point where it wins is in the easiness of configuration and the support to debugging by means of the source map it produces.
Playing along with the benchmarks, there is also this article from a fellow colleague of LogRocket (but consider it is from 2020) that compares Rollup against the other competitors going even deeper in considering, this time, how efficient it is the process of production of the bundles. Various comparisons are discussed but, in my opinion, it is worth noting how effective Rollup is in producing a lighter bundle:
The general feeling about these comparisons is that Rollup seems more efficient (in terms of build time) and effective (in terms of the size of the bundle); it also has a flourishing ecosystem of plugins that seems well supported by the community.
In this post, we demonstrated how the Rollup framework can be used to homogenize code coming from different libraries of functions, as well as how to produce an efficient bundle for use in two different languages.
In my opinion, Rollup can be adopted for any project that may be used in different contexts, languages, or platforms. It offers a surface for the tweaks you may need, whether big or small, allowing you to keep an eye on the size of the code you are actually producing.
There’s no doubt that frontends are getting more complex. As you add new JavaScript libraries and other dependencies to your app, you’ll need more visibility to ensure your users don’t run into unknown issues.
LogRocket is a frontend application monitoring solution that lets you replay JavaScript errors as if they happened in your own browser so you can react to bugs more effectively.
LogRocket works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred. LogRocket also monitors your app’s performance, reporting metrics like client CPU load, client memory usage, and more.
Build confidently — start monitoring for free.
Hey there, want to help make our blog better?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowLearn how to implement one-way and two-way data binding in Vue.js, using v-model and advanced techniques like defineModel for better apps.
Compare Prisma and Drizzle ORMs to learn their differences, strengths, and weaknesses for data access and migrations.
It’s easy for devs to default to JavaScript to fix every problem. Let’s use the RoLP to find simpler alternatives with HTML and CSS.
Learn how to manage memory leaks in Rust, avoid unsafe behavior, and use tools like weak references to ensure efficient programs.