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
Building Glimmer apps with Rollup
Rajasegar Chandran ・ Dec 7 ・ 7 min read
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
Adding src
directory for source files:
mkdir src
cd src
touch App.js App.css
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>
`;
}
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;
}
Adding demo
directory for index.html and main JS file:
mkdir demo
cd demo
touch index.html index.js
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>
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'));
Adding dependencies
We need to install the @glimmerx
libraries for creating and rendering our Glimmer components.
yarn add @glimmerx/component @glimmerx/core
Adding devDependencies
Adding @web/dev-server dependencies
yarn add -D @web/dev-server @web/dev-server-esbuild
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
web-dev-server.config.mjs
import { esbuildPlugin } from "@web/dev-server-esbuild";
export default {
nodeResolve: true,
plugins: [],
};
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
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
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
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',
],
}),
],
};
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"
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
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
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}}/>
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'
}
};
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'
}
};
Now restart our server and visit http://localhost:8000/demo
url in the browser. Your app should look like the this.
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.
Top comments (2)
This is really interesting! I'd love to hear how you'd deploy with one of these -- I'm assuming you'd use Rollup?
Yes