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 how to setup pug once you’ve installed a new webpack4-based Aurelia project.

THIS POST IS A WORK IN PROGRESS

I have two outstanding issues to get this to work:

  • 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.

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

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
 plugins: [
new HtmlWebpackPlugin({
template: 'index.pug',
inject: true,
metadata: {
// available in index.pug under locals
title, server, baseUrl // TODO: This doesn't work, and we need to figure out how to pass these via apply-loader
}
})
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.

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

The resultant index.pug is:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- 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)

// imported CSS are concatenated and added automatically

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

NOTE: The above is incorrect, and locals are not yet passing into index.pug

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.