I decided to shift from ejs
to pug
as a template engine in my webpack projects. And what I need is pre-compiled static output (either in dev or in prod mode). Plus, I need to pass data to templates through the compilation process, and I use custom options of htmlWebpackPlugin
for it. The project structure is as follows:
root
|--package.json
|--webpack.configs // .dev.js, build.js
|--data.json
|--layout.pug
|--app
|--|--index.js
|--|--views
|--|--|--index.pug
|--|--|--includes
|--|--|--|--nav.pug
|--|--|--|--footer.pug
|--assets
|--|--(styles, images...)
The first way I found and tried is to pass data to the entry point (compile locals at index.js
and insert them to the page via innerHTML =
). This way is absolutely not suitable for me as I need 100% pre-compiled static output without runtime HTML insertions. And then the fun begins...
For example, I need to create navigation. I have the schema of navigation as follows:
module.exports.nav = [
{
text: 'Home',
href: '/'
},
{
text: 'Offers',
href: '/offers.html'
},
{
text: 'Subnav Parent Test',
isSubnavTrigger: true
},
{
text: 'Subnav Item',
href: '/test.html',
isSubnavItem: true,
subnavParent: 'Subnav Parent Test'
}
]
and pug partial for this app/views/includes/nav.pug
:
nav.navbar(role="navigation", ariaLabel="navigation")
.container
.navbar-brand
a.navbar-item(href="/")
img(src="/assets/images/logo.png", alt=title)
.navbar-menu(id="navbar-menu")
.navbar-start
.navbar-end
for link, index in nav
//- TODO: isActive
if !link.isSubnavTrigger && !link.isSubnavItem
a(href=link.href, class="navbar-item")= link.text
else if link.isSubnavTrigger
.navbar-item.has-dropdown.is-hoverable
a.navbar-link= link.text
.navbar-dropdown
- var parent = link.text
- var dd = nav.filter(_item => _item.subnavParent ===
parent)
each dd_link in dd
a.navbar-item(href=dd_link.href)= dd_link.text
that partial is included in common layout layout.pug
:
- var { title, nav } = htmlWebpackPlugin.options;
<!DOCTYPE html>
html(lang="en")
head
title= title + 'Pug Webpack Test'
block metas
body
header
include ./app/views/includes/nav.pug
main
block content
footer
include ./app/views/includes/footer.pug
with the hope that app/views/includes/nav.pug
will interpolate nav
variable.
So I've discovered 2 cases: use module -> rules -> loader
and inline loader to HTMLWebpackPlugin.
The 1st. webpack.config.js
settings:
module.exports = {
module: {
rules: [
//...
{
test: /\.pug$/,
use: ['file-loader?name=[path][name].html', 'pug-html-loader?pretty&exports=false']
}
//...
],
},
plugins: [
//...
new HtmlWebpackPlugin({
template: './layout.pug',
filename: 'index.html',
title: 'siteTitle',
nav: nav // required somewhere at the top of the file
})
]
}
With this case I fail to pass options from htmlWebpackPlugin
into templates. As mentioned in layout.pug
, I try to destruct title
and nav
fields from options and get compilation error: Cannot read property 'options' of undefined
.
The 2nd case. webpack.config.js
:
module.exports = {
module: {
rules: [
//...
// Removed PUG loaders
//...
],
},
plugins: [
//...
new HtmlWebpackPlugin({
template: '!!pug-loader!./layout.pug', // <- inlined loader
filename: 'index.html',
title: 'siteTitle',
nav: nav // required somewhere at the top of the file
})
]
}
// by the way, in case of EJS I've been using exactly this approach
// and it worked out pretty well with partials
This approach perfectly compiles layout.pug
with options taken from htmlWebpackPlugin
, until it does not contain included partials. And I need to pass my nav
to corresponding partial and to render nav from there. And partial start to throw errors into compilation as if it doesn't understand pug
and needs loader:
ERROR in ./app/views/includes/nav.pug 13:12
Module parse failed: Unexpected token (13:12)
You may need an appropriate loader to handle this file type.
| .navbar-start
| .navbar-end
> for link, index in nav
| //- TODO: isActive
| if !link.isSubnavTrigger && !link.isSubnavItem
@ ./app/views/index.pug (c:/_npmg/node_modules/pug-loader!./app/views/index.pug) 4:514-543
I'm totally in mess. I do not want to draw back to ejs
, but if I will not find an answer, it seems that I will have to.
Thanks forward.