Webpack is the build system for your CH5 project. Think of it like a compiler for web apps. It’s a really flexible tool, so there’s a lot of complexity to it. I was having some difficulty separating out only the pieces I needed to make a very bare-bones configuration. I’m sure I’ll have to revisit this topic later once I’ve gathered a bit more experience playing with it.
In this post, I’m going to follow the Webpack Getting Started guide.
Clone the GitHub Repo
I advise cloning my GitHub repo so that you’re able to switch between branches for the different parts. If you’re using git
on the command line, you can simply run:
$ git checkout part-1
Switched to a new branch 'part-1'
Branch 'part-1' set up to track remote branch 'part-1' from 'origin'.
If you find bugs and want to submit pull requests, please do! Or if you’d like to keep your changes separate, you can create a new branch with:
$ git checkout -b my-part-1
Test that Webpack Works
Switch to the part-1 branch and open the project in VS Code. Install the project dependencies by running npm in a new terminal:
$ npm install
Once this finishes, webpack should be installed and ready to test out. Let’s make sure we get a workable output. In the terminal, run:
$ npx webpack
asset main.js 69.5 KiB [emitted] [minimized] (name: main) 1 related asset
runtime modules 1010 bytes 5 modules
cacheable modules 532 KiB
./src/index.js 230 bytes [built] [code generated]
./node_modules/lodash/lodash.js 531 KiB [built] [code generated]
Use a web browser to open the /dist/index.html file. If everything worked correctly, you’ll see “Hello World” rendered on the page. If you look at /dist/index.html in VS Code, you’ll see it calls to one script:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My CH5 Project</title>
</head>
<body>
<script src="main.js"></script>
</body>
</html>
Where did main.js come from? If you look back at the webpack output, you’ll see it built main.js from /src/index.js and /node_modules/lodash/lodash.js. Webpack scanned our source files and figured out how to package all the dependencies together into a single JavaScript file.
Using a Config File
We just used webpack without any config file. Now switch to the part-2 branch and you’ll see a webpack.config.js appear in the root of our project directory. Don’t forget to run npm install to pull in any missing dependencies! Here is our config file:
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.css$/i,
use: ['style-loader', 'css-loader']
},
{
test: /\.(png|svg|jpg|jpeg|gif)$/i,
type: 'asset/resource'
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/i,
type: 'asset/resource'
}
]
}
}
We’ve defined loaders for CSS, images, and fonts. If you look in /src/index.js, you’ll see how we use Webpack to load these resources:
import _ from 'lodash';
import './style.css';
import CrestronLogo from './crestron.png';
function component() {
const element = document.createElement('div');
element.innerHTML = _.join(['Hello', 'World'], ' ');
element.classList.add('hello');
const logo = new Image();
logo.src = CrestronLogo;
element.appendChild(logo);
return element;
}
document.body.appendChild(component());
I’ve also add a build NPM script to make it easier to call Webpack with our config file. If you build the web app and open /dist/index.html you should get:

Output Management
Now switch to the part-3 branch and run npm install. In a terminal, run:
$ npm run build
Webpack will now generate the /dist/index.html file for us. You can open it in VS Code to see how different it looks:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>My CH5 Project</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<script defer src="index.bundle.js"></script>
<script defer src="print.bundle.js"></script>
</head>
<body>
</body>
</html>
When you view it in a web browser, you should see the familiar Hello World generated. If you look at webpack.config.js, you’ll see we switched to using HtmlWebpackPlugin instead of style-loader and css-loader:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
mode: 'development',
entry: {
index: './src/index.js',
print: './src/print.js'
},
devServer: {
contentBase: './dist'
},
plugins: [
new HtmlWebpackPlugin({
title: 'My CH5 Project'
})
],
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
clean: true
}
}
How the Shell Template Uses Webpack
Time to switch to the part-4 branch. Run npm install again to make sure we have everything installed. I think what makes the Shell Template confusing is that the configuration is spread across several files. We’ve got:
- webpack.dev.js
- webpack.prod.js
- webpack.common.js
- app.config.js
- project-config.json
Splitting the configuration between development and production is a good way to keep things easy to debug while you’re testing and then streamlined once you’re ready to ship. It was probably setup this way so we really only need to touch project-config.json to change things. If we take a peek at our new webpack.config.js, we can see some of the typical plugins CH5 uses:
const path = require('path');
const glob = require('glob');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const ConcatPlugin = require('webpack-concat-files-plugin');
const CrComLibLibrary = glob.sync('./node_modules/@crestron/ch5-crcomlib/build_bundles/umd/cr-com-lib.js');
module.exports = {
mode: 'development',
entry: './src/index.js',
devServer: {
contentBase: './dist'
},
module: {
rules: [
{
test: /.css$/,
use: [MiniCssExtractPlugin.loader, "css-loader"]
}
]
},
plugins: [
new HtmlWebpackPlugin({
title: 'My CH5 Project',
template: './src/index.html'
}),
new MiniCssExtractPlugin({
filename: 'styles.css'
}),
new ConcatPlugin({
bundles: [
{
src: CrComLibLibrary,
dest: './dist/cr-com-lib.js'
}
],
allowOptimization: true
})
],
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
clean: true
}
}
I’ve also updated /src/index.html to show how CH5 typically includes the cr-com-lib library:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<form id="editor">
<textarea id="source" rows="10" cols="80"></textarea>
<br/>
<input type="submit" value="Preview"/>
</form>
<hr/>
<section id="preview"></section>
<script src="./cr-com-lib.js"></script>
</body>
</html>
The Shell Template is based on components that include a template markup file (using Handlebars), a module written in JavaScript, and optional styling (using Sass). Webpack grabs every *.js file underneath /app/template and /app/project and builds them into component.js. I’d like to walk through how this is done, but I think that will be saved for a future post after I’ve explored Webpack some more.
Until Next Time
I think the Webpack Getting Started guide was helpful in going from zero to a minimal configuration and learning what most of the pieces are doing. I highly recommend following that if you’re starting from nothing like I am. Also helpful was Webpack from Nothing which did a lot to uncover some of the Why webpack? questions I had.
I think next time I’m going to try building up a similar component model that’s used in the Shell Template to get a better idea how that’s working behind the scenes. We’ll continue using the same GitHub repo to expand on those ideas.
Thanks for reading!
2 thoughts on “A Quick Guide to Webpack”