Building Glimmer apps with Rollup
Tags: rollup, glimmer, glimmerjs, rollupjs
Published: 2020-12-07 06:18:38

## Intro
This is the second post in the series of posts describing building Glimmer.js apps with various bundlers like [Snowpack](https://snowpack.dev), Rollup, etc., If you want to check out the first post where we build Glimmer apps with Snowpack, it can be found here.
{% post rajasegar/building-glimmer-apps-with-snowpack-igc %}

In this post, we will be using [Rollup](https://rollupjs.org) to build our Glimmer apps.

Before diving into the topic, let's have a brief introduction about Glimmer and Rollup
## About Glimmer
[Glimmer](https://glimmerjs.com) is one of the fastest DOM rendering engines, delivering exceptional performance for initial renders as well as updates. Architected like a virtual machine (VM), Glimmer compiles your templates into low-level code so it can run as fast as possible—without sacrificing ease of use. Glimmer VM powers the components in [Ember.js](https://emberjs.com)

## About Rollup
[Rollup](https://rollupjs.org) is a module bundler for JavaScript which compiles small pieces of code into something larger and more complex, such as a library or application. It uses the new standardized ES Modules(ESM) format for code modules. ESM lets you freely and seamlessly combine the most useful individual functions from your favorite libraries.

## Setting up project
```
mkdir glimmer-rollup
cd glimmer-rollup
npm init -y
mkdir src public
touch src/index.js src/App.js src/App.css
touch public/index.html
```

### index.html
Let's create our index.html file with some markup. We will be including two main things here: the `dist/bundle.css` and `dist/bundle.js` for the final CSS and JS builds of our apps respectively.

```html
<!doctype html>
<html>
  <head>
    <title>glimmer-rollup</title>
    <link rel="icon" href="./public/favicon.png" />
    <link rel="stylesheet" href="dist/bundle.css">
  </head>
  <body>
    <div id="app"></div>
    <script src="dist/bundle.js"></script>
  </body>
</html>
```
### src/index.js
And in the `index.js` file which is the main entry point of our app, we will be importing our App component and mount it on the DOM with the `renderComponent` function which takes two parameters, a component and a DOM node.

```js
import { renderComponent } from '@glimmerx/core';
import App from './App';

const containerElement = document.getElementById('app');

renderComponent(App, containerElement);
```

### src/App.js
Our `App` component is a simple one, where we just display a logo and some text. We are importing the logo from an svg file using the standard import syntax. Similarly we import the style definitions for our component from a css file called `App.css`. This is a pretty common thing with other component based frameworks like React, and with bundlers like Webpack we will use the appropriate loader plugins to import non-JS stuff like images and css into our components. In case of Rollup, we will be using some Rollup plugins to do the job.

```js
import Component, { hbs } from '@glimmerx/component';

import logo from './logo.svg';
import './App.css';

export default class App extends Component {
  logo = logo;

  static template = hbs`
    <div id="intro">
      <img src={{this.logo}}/>
      <h1>Hello World, glimmerx!</h1>
      <h3>
        you can get started by editing <code>src/App.js</code>
      </h3>
    </div>
  `;
}
```

### src/App.css
This is how our style definitions will look like for our components. As you can see for the sake of brevity I have used id selectors to style the components, normally it is not recommended, but instead you can use re-usable class definitions or alike.

```css
body {
  margin: 0;
}

#app {
  background: #1E293B;
  min-height: 100vh;

  display: grid;
  align-items: center;
  justify-content: center;
}

#intro {
  width: 34em;
}

#intro h1, #intro h3 {
  font-family: Roboto, 'Helvetica Neue', Helvetica, Arial, sans-serif;
  font-style: italic;

  color: #FFFFFF;
  margin: 0.35em;
}

#intro img {
  float: left;
  width: 6.5em;
  margin: 0.5em 2em;
}

#intro a {
  color: #FFFFFF;
}
```

## Configuring Rollup
First, you need to install rollup and the necessary plugins and then create a config file for Rollup to direct the build process. For a bare minimum build setup, we need the following three official Rollup plugins to be installed in our project as dev dependencies.

### @rollup/plugin-node-resolve
This is a Rollup [plugin](https://github.com/rollup/plugins/tree/master/packages/node-resolve) which locates modules using the Node resolution algorithm, for using third party modules in `node_modules`

### @rollup/plugin-commonjs
This is a Rollup [plugin](https://github.com/rollup/plugins/tree/master/packages/commonjs) to convert CommonJS modules to ES6, so they can be included in a Rollup bundle.

### @rollup/plugin-babel
This is a Rollup [plugin](https://github.com/rollup/plugins/tree/master/packages/babel) for seamless integration between Rollup and [Babel](https://babeljs.io). We need Babel to compile our Glimmer components with advanced ES syntax using decorators and class properties.

```
yarn add -D rollup @rollup/plugin-node-resolve @rollup/plugin-commonjs @rollup/plugin-babel
```


So we also need to setup our babel plugins by creating a babel config `.babelrc.js` with the following setup. You can refer to the [previous post](https://dev.to/rajasegar/building-glimmer-apps-with-snowpack-igc) to see why we need the different babel plugins and what each one does.

```js
module.exports = function (api) {
  return {
    plugins: [
      ['@glimmer/babel-plugin-glimmer-env', { DEBUG: !api.env('production') }],
      '@glimmerx/babel-plugin-component-templates',
      ['@babel/plugin-proposal-decorators', { legacy: true }],
      '@babel/plugin-proposal-class-properties',
    ],
    presets: ['@babel/preset-env'],
  };
};
```

We should also install the necessary babel plugins for our project.
```
yarn add @babel/preset-env @glimmer/babel-plugin-glimmer-env @glimmerx/babel-plugin-component-templates @babel/plugin-proposal-decorators @babel/plugin-proposal-class-properties
```
Then we need to create a rollup config file called `rollup.config.js` in our root folder.
```js
/* globals process */
import nodeResolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import babel from '@rollup/plugin-babel';

const plugins = [
  nodeResolve(),
  commonjs({
    include: 'node_modules/**',
  }),
  babel(),
];

export default {
  input: './src/index.js',
  output: {
    file: 'public/dist/bundle.js',
  },
  plugins,
};
```

Now, we need a development server to watch the file changes, rebuild the assets and serve them in the browser.

### Setting up development server
If you want to setup a development server for our project, we need to install the [rollup-plugin-serve]() plugin into our project and add it to our `rollup.config.js`.
```
yarn add -D rollup-plugin-serve
```

```js
/* globals process */
import nodeResolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import babel from '@rollup/plugin-babel';
import serve from 'rollup-plugin-serve';

const IS_PRODUCTION = process.env.NODE_ENV === 'production';
const plugins = [
  ...
  !IS_PRODUCTION &&
    serve({
      open: true,
      port: 8080,
      contentBase: 'public',
    }),
];

...
```

Now you can add a npm script in your `package.json` 
```json
 "scripts": {
    ...
    "start": "rollup -c -w",
    ...
  },
```
Here we are invoking the rollup cli with `-c` option to use the `rollup.config.js` as the configuration and `-w` to watch for file changes. You can read more about configuring the rollup cli with various options and flags [here](https://rollupjs.org/guide/en/#command-line-flags) in the official documentation.

And you can start the build by issuing the following command:
```
yarn start
```
But you will get an error something like this in the console
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/s0rius0vcvn7icuvwrnj.png)

Your build won't run because it's missing the necessary plugins to load images and styling which we will take care now. 

### Importing images
Install the `@rollup/plugin-image` plugin for importing images in different formats into our components.
```
yarn add -D @rollup/plugin-image
```
### Importing css
For importing CSS files into our components, you can make use of the `rollup-plugin-css-only`
```
yarn add -D rollup-plugin-css-only
```

Then we add these plugins to our rollup config.

```js
/* globals process */
...
import image from '@rollup/plugin-image';
import css from 'rollup-plugin-css-only';
...

const plugins = [
  ...
  image(),
  css({ output: 'bundle.css' }),
  ...
];

...
```

Now we can start the server and watch for file changes and the re-build will be triggered automatically. Rollup will automatically open your browser and go to the page at `http://localhost:8080` to run our app, since we have configured the same in the rollup config file. And this is how our app should look like in the browser.
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/62ythu25fmoy3s08mrbg.png)

For live reloading changes, you can also install and use the [rollup-plugin-livereload](https://github.com/thgh/rollup-plugin-livereload).

### Minifying for production
So, the next thing we will looking at is how to minify our build output for production. For this we will make use of another Rollup plugin called [rollup-plugin-terser](https://github.com/TrySound/rollup-plugin-terser) and add it to the list of plugins in the rollup config.

```
yarn add -D rollup-plugin-terser
```
And then we add a build script to our package.json to use rollup to build for production
```json
 "scripts": {
    ...
    "build": "NODE_ENV=production rollup -c",
    "start": "rollup -c -w",
    ...
  },
```

Now you can run build the command for creating production optimised bundles:
```
yarn build
```

### Final rollup configuration
This is how our rollup config file will look like in the end.
```js
/* globals process */
import nodeResolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import image from '@rollup/plugin-image';
import css from 'rollup-plugin-css-only';
import babel from '@rollup/plugin-babel';
import serve from 'rollup-plugin-serve';
import { terser } from 'rollup-plugin-terser';

const IS_PRODUCTION = process.env.NODE_ENV === 'production';
const plugins = [
  nodeResolve(),
  commonjs({
    include: 'node_modules/**',
  }),
  image(),
  css({ output: 'bundle.css' }),
  babel(),
  !IS_PRODUCTION &&
    serve({
      open: true,
      port: 8080,
      contentBase: 'public',
    }),
  IS_PRODUCTION && terser(),
];

export default {
  input: './src/index.js',
  output: {
    file: 'public/dist/bundle.js',
  },
  plugins,
};
```

## Source code
The source code for this tutorial is available in [Github](https://github.com/rajasegar/glimmerx-rollup). You can fork and clone this project or you can use a tool something like [degit](https://github.com/Rich-Harris/degit) to use this template.

```
npx degit rajasegar/glimmerx-rollup my-glimmerx-app
```

## What's next?
In the next post in this series, we will take a look at bundling Glimmer apps with a more "modern" bundling tool. So stay tuned by following [me](https://twitter.com/rajasegar_c) for any updates, you can also let me know your feedback and suggestions in the comments below.