Building Glimmer apps with Rollup

rajasegar

Rajasegar Chandran

Posted on December 7, 2020

Building Glimmer apps with Rollup

Intro

This is the second post in the series of posts describing building Glimmer.js apps with various bundlers like Snowpack, Rollup, etc., If you want to check out the first post where we build Glimmer apps with Snowpack, it can be found here.

In this post, we will be using Rollup to build our Glimmer apps.

Before diving into the topic, let's have a brief introduction about Glimmer and Rollup

About Glimmer

Glimmer 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

About Rollup

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. 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
Enter fullscreen mode Exit fullscreen mode

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.

<!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>
Enter fullscreen mode Exit fullscreen mode

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.

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

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

renderComponent(App, containerElement);
Enter fullscreen mode Exit fullscreen mode

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.

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>
  `;
}
Enter fullscreen mode Exit fullscreen mode

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.

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;
}
Enter fullscreen mode Exit fullscreen mode

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 which locates modules using the Node resolution algorithm, for using third party modules in node_modules

@rollup/plugin-commonjs

This is a Rollup plugin to convert CommonJS modules to ES6, so they can be included in a Rollup bundle.

@rollup/plugin-babel

This is a Rollup plugin for seamless integration between Rollup and Babel. 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
Enter fullscreen mode Exit fullscreen mode

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 to see why we need the different babel plugins and what each one does.

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'],
  };
};
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Then we need to create a rollup config file called rollup.config.js in our root folder.

/* 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,
};
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode
/* 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',
    }),
];

...
Enter fullscreen mode Exit fullscreen mode

Now you can add a npm script in your package.json

 "scripts": {
    ...
    "start": "rollup -c -w",
    ...
  },
Enter fullscreen mode Exit fullscreen mode

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 in the official documentation.

And you can start the build by issuing the following command:

yarn start
Enter fullscreen mode Exit fullscreen mode

But you will get an error something like this in the console
Alt Text

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Then we add these plugins to our rollup config.

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

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

...
Enter fullscreen mode Exit fullscreen mode

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

For live reloading changes, you can also install and use the 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 and add it to the list of plugins in the rollup config.

yarn add -D rollup-plugin-terser
Enter fullscreen mode Exit fullscreen mode

And then we add a build script to our package.json to use rollup to build for production

 "scripts": {
    ...
    "build": "NODE_ENV=production rollup -c",
    "start": "rollup -c -w",
    ...
  },
Enter fullscreen mode Exit fullscreen mode

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

yarn build
Enter fullscreen mode Exit fullscreen mode

Final rollup configuration

This is how our rollup config file will look like in the end.

/* 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,
};
Enter fullscreen mode Exit fullscreen mode

Source code

The source code for this tutorial is available in Github. You can fork and clone this project or you can use a tool something like degit to use this template.

npx degit rajasegar/glimmerx-rollup my-glimmerx-app
Enter fullscreen mode Exit fullscreen mode

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 for any updates, you can also let me know your feedback and suggestions in the comments below.

💖 💪 🙅 🚩
rajasegar
Rajasegar Chandran

Posted on December 7, 2020

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

Building Glimmer apps with Rollup
rollup Building Glimmer apps with Rollup

December 7, 2020