Hooking up HTML Preprocessors like EJS, Pug, & Handlebars
In this section we've been hooking up our favorite front end solutions and seeing how they interact with Webpack.
Next up are HTML templating languages like EJS, Pug and Handlebars.
They all pretty much hook up the same way.
To begin we're going to use the hookup branch as a starter for everything in this section. Since we won't be carrying over some of the changes into the Optimizing for Production section.
git clone https://github.com/lawwantsin/webpack-course.git
cd webpack-course
git checkout hookup
npm install
EJS
Embedded Javascript is the most ruby like syntax. It's also the default for webpack HTMLPlugin
touch src/index.ejs
In your webpack.dev.js
change the HTMLWebpackPlugin template to ejs.
new HTMLWebpackPlugin({
template: "./src/index.ejs"
})
Copy and paste the entire index.html
into index.ejs
.
Okay, now that we're setup. let's add a variable to this template.
<html>
<head>
<title><%= htmlWebpackPlugin.options.title =%></title>
</head>
<body>
<div class="profile">
<img src="./images/link.jpg">
<h1>Link's Journal</h1>
</div>
</body>
</html>
We have access to this template object htmlWebpackPlugin
which literally is
the plugin code, the options is an object of the same stuff that's in the
webpack.dev.js
. Let's add a title.
new HTMLWebpackPlugin({
template: "./src/index.ejs",
title: "Link's Journal"
})
We see the title
. Which means, yes, there is a template variable, and you can
really add whatever you want to this object and pass it through.
In addition to the options
object, this global also holds a files
object.
"htmlWebpackPlugin": {
"files": {
"css": [ "main.css" ],
"js": [ "assets/main_bundle.js"],
"chunks": {
"main": {
"entry": "assets/main_bundle.js",
"css": []
},
}
}
}
So yes, we can sort of hand inject certain scripts into this index.html
at
different points. Maybe that's useful to someone. I believe there are better
ways to skin this particular cat, so I'm not going to teach this as a best
practice.
Setting:
inject: false
in the plugin options, removes the automatic script and link tags and lets you choose how to add assets.
Assets in EJS
It seems that EJS loader doesn't parse images or other asset paths in the same way as the plain html loader. If we change the image loader option to include a hash.
test: /\.jpg$/,
use: [
{
loader: "file-loader",
options: {
name: "images/[name]-[hash:4].[ext]"
}
}
]
We see the image is broken in the browser.
There [is a workaround] library buit with lodash (https://github.com/emaphp/underscore-template-loader) to this for EJS style asset interpolation.
Put there's actually a slightly simpler way. In index.ejs
, if you change the
image line to require
like so:
<img src="<%= require("./images/link.jpg") %>">
We will see the image comes thru with the proper handling. That's really the
power of loaders. They introduce this require
function into any file webpack
handles. You can inline any asset, you can refer to any asset and webpack works
it out.
Pug (Formerly Jade
Now let's do the same with Pug. Formerly Jade.
In terminal:
touch src/index.pug
npm install pug pug-loader
In src/index.pug
doctype html
html
head
title
= htmlWebpackPlugin.options.title
body
.profile
img(src=require("./images/link.jpg"))
h1
Hello Hyrule
In webpack.dev.js
add the loader to handle pug
files:
{
test: /\.pug$/,
use: [
{
loader: "pug-loader"
}
]
}
In the new HTMLWebpackPlugin
plugin options change the template to the pug
file.
...
template: "./src/index.pug",
...
Handlebars
Same goes for handlebars (.hbs
files). I'll include the code in the final
branch, but suffice it to say, webpack handles all of them very similarly.
In webpack.dev.js
add a new loader:
{
test: /\.hbs$/,
use: [
{
loader: "handlebars-loader",
query: { inlineRequires: '\/images\/' } }
}
]
}
Handlebars requires specifics about what to include in inlineRequires
. This
query option tells it to include anything from the images folder. Pretty cool.
Update plugin options:
template: "./src/index.hbs",
Finally, add a new template file next to the rest.
touch src/index.hbs
npm install handlebars handlebars-loader
<html>
<head>
<title>{{ htmlWebpackPlugin.options.title }}</title>
</head>
<body>
<div class="profile">
<img src="./images/link.jpg"/>
<h1>Link's Journal</h1>
</div>
</body>
</html>
In Sum
HTML Preprocessors are easily doable in webpack. The HTMLWebpackPlugin
accommodates them all and even interpolates the images, same as HTML for all but
pug. Hot-reloading the finished html on change once again becomes an issue with
anything other than EJS, though I'm sure some Plugin author out there has solved
that particular problem.
git checkout templates-final