Building Glimmer apps with Web Dev Server

rajasegar

Rajasegar Chandran

Posted on December 14, 2020

Building Glimmer apps with Web Dev Server

Intro

This is the third post in the series of blog posts describing about building Glimmer.js apps with general purpose bundlers. The previous posts walked you through building Glimmer apps with Snowpack and Rollup. You can check them out here.

With Snowpack

With Rollup

In this post, we are going to build our Glimmer.js apps with a build tool called Web Dev Server

What is Web Dev Server?

Web Dev Server helps developing for the web, using native browser features like ES modules. It is powered by esbuild and Rollup. esbuild is an extremely fast JavaScript bundler and minifier written in Go.

I came across this tool from Modern Web Dev which is an awesome project from the creators or Open Web Components, with a goal to provide developers with the guides and tools they need to build for the modern web. They enable developers to work closely with the browser and avoid complex abstractions.

Web Dev Server is ideal for buildless workflows, and has a plugin architecture for light code transformations. It has got many features like efficient browser caching for fast reloads, compatibility with older browsers, auto-reload on file changes, History API fallback for Single Page Apps(SPA) routing, plugins and middleware API for non-JS files.

Setting up the project

mkdir glimmer-web-dev-server
cd glimmer-web-dev-server
npm init -y
Enter fullscreen mode Exit fullscreen mode

Adding src directory for source files:

mkdir src
cd src
touch App.js App.css
Enter fullscreen mode Exit fullscreen mode

App.js

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

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

export default class App extends Component {

  constructor() {
    super(...arguments);
    this.styles = styles;
    this.logo = logo;
  }

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

Please note that we are going to use CSS Modules for our component with PostCSS. We will discuss about this later.

App.css

.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

Adding demo directory for index.html and main JS file:

mkdir demo
cd demo
touch index.html index.js
Enter fullscreen mode Exit fullscreen mode

Next we create our index.html file with the following markup, we need a mount-point for our main component to load in the DOM and we need to include the index.js file as a module with <script type="module">.

index.html

<!DOCTYPE html>
<html>
  <head>
    <title>GlimmerX with web-dev-server</title>
  </head>
  <body>
    <div id="app"></div>
    <script type="module" src="./index.js"></script>
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

Now the main index.js file will render our App component in the DOM node #app.

index.js

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

renderComponent(App, document.getElementById('app'));
Enter fullscreen mode Exit fullscreen mode

Adding dependencies

We need to install the @glimmerx libraries for creating and rendering our Glimmer components.

yarn add @glimmerx/component @glimmerx/core
Enter fullscreen mode Exit fullscreen mode

Adding devDependencies

Adding @web/dev-server dependencies

yarn add -D @web/dev-server @web/dev-server-esbuild
Enter fullscreen mode Exit fullscreen mode

Configuring @web/dev-server

Now, it's time to create the configuration file. Web Dev Server looks for a configuration file in the current working directory called web-dev-server.config.*

The file extension can be .js, .cjs or .mjs. A .js file will be loaded as an ES module or Common JS module based on your version of node, and the package type of your project.

The Web Dev Server core team recommends writing the configuration using Node.js ES module syntax and using the .mjs file extension to make sure your config is always loaded correctly.

touch web-dev-server.config.mjs
Enter fullscreen mode Exit fullscreen mode

web-dev-server.config.mjs

import { esbuildPlugin } from "@web/dev-server-esbuild";

export default {
  nodeResolve: true,
  plugins: [],
};
Enter fullscreen mode Exit fullscreen mode

Let's start building our configuration step-by-step. First we need to compile our JS from the component source files. We are going to use Rollup and Babel for the same. Let's install the required packages for rollup and babel plugins.

For babel

yarn add -D @babel/plugin-proposal-class-properties @babel/plugin-proposal-decorators @babel/preset-env
Enter fullscreen mode Exit fullscreen mode

For compiling our Glimmer components with the inline handlebars syntax we need to install the @glimmerx/babel-plugin-component-templates, about which you can know more in here.

yarn add -D @glimmerx/babel-plugin-component-templates
Enter fullscreen mode Exit fullscreen mode

We need an Adapter for using rollup plugins in Web Dev Server. Web Dev Server plugins and rollup plugins share a very similar API, making it possible to reuse rollup plugins inside Web Dev Server with an adapter.

For Rollup we need

yarn add -D @web/dev-server-rollup @rollup/plugin-babel
Enter fullscreen mode Exit fullscreen mode

Let's add our babel plugins to the config file.

import { esbuildPlugin } from "@web/dev-server-esbuild";
import { fromRollup } from '@web/dev-server-rollup';
import * as babelModule from '@rollup/plugin-babel';

const { babel } = babelModule;
const babelPlugin = fromRollup(babel);

export default {
  nodeResolve: true,
  plugins: [
    babelPlugin({
      babelHelpers: 'bundled',
      plugins: [
        '@glimmerx/babel-plugin-component-templates',
        ['@babel/plugin-proposal-decorators', { legacy: true }],
        '@babel/plugin-proposal-class-properties',
      ],
    }),
  ],
};
Enter fullscreen mode Exit fullscreen mode

We are importing the babel rollup plugin and the fromRollup function in our configuration file. Then, we are wrapping the rollup plugin with the adapter function to make it work.

Now it's time to add our scripts in the package.json to start our dev server and build our source files.

"start": "web-dev-server --open demo/ --node-resolve --watch"
Enter fullscreen mode Exit fullscreen mode

What we are doing here is to tell the web-dev-server to open and watch the demo folder for file changes and use the --node-resolve flag to resolve bare module imports for use in the browser.

So now you can start the web-dev-server to load our app in the browser by issuing the following command:

yarn start
Enter fullscreen mode Exit fullscreen mode

And if you go to the url http://localhost:8000/demo in your browser, you probably get a blank page and some errors in the console. The reason being that we have not yet configured our web-dev-server to process the CSS and images since the build process assumes that any imported files are meant to be compiled to JS, Web Dev Server serves many different kinds of files to the browser. If you are transforming a non-standard filetype to JS, for example .css or .svg files, you need to instruct the server to handle it as a JS file.

Let's handle them now using our Rollup plugins rollup-plugin-postcss and @rollup/plugin-image.

yarn add -D @rollup/plugin-image rollup-plugin-postcss
Enter fullscreen mode Exit fullscreen mode

This allows us to use the PostCSS CSS Modules feature which we discussed earlier. With that we don't have to worry about style clashes and naming our css classes and selectors since all of them will be properly scoped and encapsulated. We can import our style definitions in our components and use them like:

import styles from './App.css';
...
<div class={{styles.intro}}/>
Enter fullscreen mode Exit fullscreen mode

And now, add these plugins for loading css and images to our config.

...
import postcss from 'rollup-plugin-postcss';
import image from '@rollup/plugin-image';
...
const postcssPlugin = fromRollup(postcss);
const imagePlugin = fromRollup(image);

export default {
   ...
  plugins: [
    ...
    postcssPlugin({
      include: ['src/**/*.css'], 
      modules: true
    }),
    imagePlugin()
  ],
  mimeTypes: {
    '**/*.css': 'js',
    '**/*.svg': 'js'
  }
};
Enter fullscreen mode Exit fullscreen mode

In the above config, we are telling the rollup-plugin-postcss to include all the css files inside the src folder and use CSS modules with the modules option set to true.

And we also need to tell web-dev-server to load all the css and svg files as JS with the mimeTypes option.

Now this is how the full and final config for our web-dev-server looks like.

import { esbuildPlugin } from "@web/dev-server-esbuild";
import { fromRollup } from '@web/dev-server-rollup';
import * as babelModule from '@rollup/plugin-babel';
import postcss from 'rollup-plugin-postcss';
import image from '@rollup/plugin-image';

const { babel } = babelModule;
const babelPlugin = fromRollup(babel);
const postcssPlugin = fromRollup(postcss);
const imagePlugin = fromRollup(image);

export default {
  nodeResolve: true,
  plugins: [
    babelPlugin({
      babelHelpers: 'bundled',
      plugins: [
        '@glimmerx/babel-plugin-component-templates',
        ['@babel/plugin-proposal-decorators', { legacy: true }],
        '@babel/plugin-proposal-class-properties',
      ],
    }),
    postcssPlugin({
      include: ['src/**/*.css'], 
      modules: true
    }),
    imagePlugin()
  ],
  mimeTypes: {
    '**/*.css': 'js',
    '**/*.svg': 'js'
  }
};
Enter fullscreen mode Exit fullscreen mode

Now restart our server and visit http://localhost:8000/demo url in the browser. Your app should look like the this.

Alt Text

Source code

The source code for this post can be found in this Github repository. The repository also contains some bonus stuff of writing tests for your Glimmer components and running tests with Web Test Runner another awesome tool from the Modern Web project.

This concludes our Building Glimmer apps series with different bundlers. Please let me know in the comments if you have any queries or feedback, I will be glad to answer the same.

💖 💪 🙅 🚩
rajasegar
Rajasegar Chandran

Posted on December 14, 2020

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

Sign up to receive the latest update from our blog.

Related