Configuring Pug with Webpack 4 and the Aurelia CLI

Aurelia is a front-end framework along the lines of Angular and vuejs. The Aurelia CLI (currently v0.33.1) does not support pug templates.

This post shows my effort so far to configure pug on top of a new webpack4-based Aurelia project. There are two outstanding issues that I have not yet resolved:

  • Pug file require statements are not resolving the css file (e.g. require(from='app.styl'))
  • Have not yet tackled the issue of passing locals to index.pug

Configure

pug is needed to process pug files, and pug-loader is needed to load pug files into webpack. apply-loader is needed because pug-loader uses a function (if anyone can add a better description, please do).

1
2
3
yarn add -D pug-loader
yarn add -D pug
yarn add -D apply-loader

webpack.config.js

Within the webpack.config.js module.rules array you’ll need to add the following. Most loaders return a string result, but pug-loader returns a function that returns the string result. apply-loader will execute the pug-loader function.

TODO: determine how to pass parameters/locals to pug-loader

1
2
3
4
5
6
7
8
9
10
11
rules: [
{
test: /\.pug$/,
loaders: [{
loader: 'apply-loader'
}, {
loader: 'pug-loader',
options: { pretty: true }
}]
},
]

And within the list of plugins you’ll need to

  • configure webpack to load your index.pug file rather than the Aurelia CLI default index.ejs file
  • tell Aurelia that your view files are .pug files
1
2
3
4
5
6
7
8
9
10
11
12
 plugins: [
new HtmlWebpackPlugin({
template: 'index.pug',
inject: true,
metadata: {
// available in index.pug under locals
// TODO: This doesn't work, and we need to figure out how to pass these via apply-loader
title, server, baseUrl
}
})
new AureliaPlugin( { viewsExtensions: '.pug' } ),
]

index.pug

Converting the default index.ejs to index.pug requires access to the metadata variables added above. In pug these are attached to locals. Pug evals anything within #{ and }`, so you can see what variables are available by using Object.keys(locals). It works out that our metadata is actually attached to locals.htmlWebpackPlugin.options.metadata.

NOTE: Attaching to locals is not yet working. I’ll update this when I figure out how to get this working.

1
2
doctype html
keys #{Object.keys(locals.htmlWebpackPlugin.options.metadata)}

The resultant index.pug is here. Note that I’ve added a few additional script includes for illustrative purposes.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
- var metadata = locals.htmlWebpackPlugin.options.metadata
doctype html
html
head
meta(charset='utf-8')
title #{locals.htmlWebpackPlugin.options.metadata.title}
meta(name='viewport' content='width=device-width, initial-scale=1')
base(href=metadata.baseUrl)
script
include javascripts/fontawesome-config.js
script
include javascripts/google-tag-manager.js
script(defer src='https://use.fontawesome.com/releases/v5.0.2/js/all.js')
script
include javascripts/intercom.js

// imported CSS are concatenated and added automatically

body(aurelia-app='main')
// Webpack Dev Server reload
if (metadata.server)
script(src='/webpack-dev-server.js')

main.js

A bit of additional config to associate views with pug.

1
2
3
var Promise = require('bluebird') // Promise polyfill for IE11;
import {bootstrap} from 'aurelia-bootstrapper-webpack';
import {ViewLocator} from 'aurelia-framework';
1
2
3
4
5
6
7
8
9
10
11
12
13
bootstrap(function(aurelia) {
aurelia.use
.standardConfiguration()
.developmentLogging();

ViewLocator.prototype.convertOriginToViewUrl = function (origin) {
let moduleId = origin.moduleId;
let id = (moduleId.endsWith('.js') || moduleId.endsWith('.ts')) ? moduleId.substring(0, moduleId.length - 3) : moduleId;
return id + '.pug';
}

aurelia.start().then(() => aurelia.setRoot('app', document.body));
})

One last bit

This isn’t everything you need to know. You’ll also need to declare global components using my-component.pug rather than my-component.html in cases where there is not an associated .js file. You can read more about this in my next blog post.