Rollup 2 in a simple html+js use case
Tags: javascript, webdev
Published: 2021-06-03 18:52:08

In this series, I'll take a look on a simple usecase of js+html application, build with various js bundlers. In earlier articles, we've seen how far we can get without any bundler and how it goes wepback & esbuild. Here we will take look on [rollup.js](https://rollupjs.org/guide/en/).

# Rollup 2
Rollup seems to be in an interesting point - on many metrics just after webpack - *second* most popular, *second* oldest:

![npm trends for bundlers](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bxxhnej56kh9q53u9yit.png)

[source](https://www.npmtrends.com/esbuild-vs-parcel-vs-rollup-vs-snowpack-vs-vite-vs-webpack)

but in the same time, seems it's not generating as much excitement as some newer tools - like discussed previously esbuild. But it's still worth taking a look - just because of it's popularity, you can come across it in some project.


# The app

![application screenshot](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xacwuk055mv5c4cdot9z.png)

same as other articles of this series, simple app with 1 component with template & data in separate files.

# Code

The main html file is simple:
```html
<!-- index.html -->
<html>
  <head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8" />
    <title>Contact App</title>
    <link rel="shortcut icon" href="#" />

    <script type="module" src="./dist/index.js"></script>
    <link rel="stylesheet" href="style.css" />
  </head>
  <body></body>
</html>
```

the source file, `./dist/index.js` has to be match to what we set as output in `rollup.config.js`.

Main js file:

```js
// src/index.js
import "./contact-list/contact-list";
```

no surprise here, rollup behaves in the same way we are already accustomed by other bundlers.

# The component

The component is broken down into 3 files. By default, rollup supports JS files only. For JSON & HTML, we will need to install a plugin & set up configuration.

```js
// src/contact-list/contact-list.js
import template from "./contact-list.html";
import contacts from "./data.json";

const contactList = document.createElement("div");

contactList.className = "contact-list";

contacts.forEach((entry) => {
  const element = document.createElement("div");
  element.className = "contact";

  element.innerHTML = template;

  element.querySelector(".name").innerHTML = entry.name;
  element.querySelector(".phone").innerHTML = entry.phone;

  contactList.appendChild(element);
});

document.body.appendChild(contactList);
```

So far, each bundler works with the same application code - that's very good for migrations if we ever decide going from building with one tool to some other. 

Even for JSON files, rollup needs an aditional library - [a json plugin](https://github.com/rollup/plugins/tree/master/packages/json). The installation & configuration will be covered below.

`src/contact-list/data.json`, the data file:
```json 
[
  {
    "name": "Christopher L Sanders",
    "phone": "769-232-1807"
  },
  {
    "name": "Frances J Nolte",
    "phone": "901-287-0419"
  }
]
```

HTML files, where the source of the biggest confusion for me. Looks that I'm very used to webpack's `html-loader`, and it's way of thinking. The rollup's [core html plugin](https://github.com/rollup/plugins/tree/master/packages/html) seems to do slightly different thing. In the end I got my template as a string into js by a combination of `@rollup/plugin-babel` & babel's plugin `babel-plugin-transform-html-import-to-string`. This solution felt pretty over engineered to me - I'm either very off with how I approached the problem, or I was trying to force rollup to behave in a way it would rather not. I guess, in cases when you use reacts' `jsx` or `tsx`, it feels much smoother, because you don't move template out of js. 

```html
<!-- src/contact-list/contact-list.html -->
<h2 class="name">name</h2>

<p class="phone">phone</p>
```

# Build dependencies & configuration
For a successful build of the above code, we need quite a few things. To install them all:

```bash
$  npm install --save-dev @babel/preset-env @rollup/plugin-babel @rollup/plugin-json rollup babel-plugin-transform-html-import-to-string
```

The configuration:
```js
// rollup.config.js
import { babel } from "@rollup/plugin-babel";
import json from "@rollup/plugin-json";

const config = {
  input: "src/index.js",
  output: {
    format: "esm",
    file: "dist/index.js",
  },
  plugins: [
    babel({
      exclude: "node_modules/**",
      presets: ["@babel/preset-env"],
      plugins: ["babel-plugin-transform-html-import-to-string"],
      extensions: [".js", ".html"],
    }),
    json(),
  ],
};

export default config;
```

That is a bit disappoiting - especially if you heard horror storries about wepback confiuration, here even simple usecase requires a lot of configuration.

For easy access to build script, you can add following line to `package.json`:
``` js
  "scripts": {
    // other scripts
    "build": "rollup -c rollup.config.js"
  }
```

succesful build:
```bash
$ npm run build

> rollup@1.0.0 build
> rollup -c rollup.config.js


src/index.js → dist/index.js...
babelHelpers: 'bundled' option was used by default. It is recommended to configure this option explicitly, read more here: https://github.com/rollup/plugins/tree/master/packages/babel#babelhelpers
created dist/index.js in 301ms
```

# Complete code & application example

If you want to see the application in action in the browser you can see it here:
https://marcin-wosinek.github.io/js-html-comparison-rollup-2/

and for the working code example you can go here:
https://github.com/marcin-wosinek/js-html-comparison-rollup-2