Using PUG (or Jade) templates with the Angular-CLI

Mark Pieszak
4 min readOct 1, 2017

--

Note: For Angular 6+ you can simply run ng add ng-cli-pug-loader to automatically run & add the scripts below to your project.

Note: The original article was for Angular 5 (and lower / older versions of the Angular CLI) It was more of a temporary/hacky solution until there is more formal support within the Angular-CLI or with Schematics.

[Update 5/29] Angular CLI 6+ PUG Schematics

This is only for the latest CLI 6.0 or higher, but you can run the script ng add ng-cli-pug-loader and it will automatically run and create all the scripts & code that are found below in this article.

ng add ng-cli-pug-loader

Angular <5 (and older Angular-CLI versions <6)

Remember if you’re using the latest CLI you can use the ng add script above, which utilizes the code & scripts below and runs them automatically for you.

If you’ve ever tried to get pug or jade templates working with the Angular CLI, you might have ran into a few random solutions out there, each with their own pros and cons, but nothing super simple.

@Component({
selector: 'my-thing',
templateUrl: './my-thing.pug' // <-- how can we get this to work?
})

Some things -not- to do

  • Don’t try to use require, since it won’t be statically analyzable by the Ahead-of-time compiler (AoT).
    template: require('pug-loader!./app.component.pug')()
    I’ve seen a lot of projects do this, and avoiding AoT withng build --aot=false but by doing that you’re including the compiler in your prod builds, and increasing package size.
  • You could use gulp to turn all of your .pug files into .html files (referencing the html file in your components templateUrl), and set up a gulp watch to check pug files into html, but it’s 2017 — it’s… illegal to use Gulp now isn’t it? (kidding of course). All of your templates now have to point to the compiled html file as well.
  • There are also some packages around that transform your pug files into strings within a .pug.ts TypeScript file string, and you import that result. That works great with AoT, but you’ll find ng serve doesn’t work anymore. So that’s a no-go.

What about ng eject?

So another option is that you could ng eject your Webpack configuration from the CLI, and simply add the pug rule yourself. But for a lot of users, people want to stay within the comforts of the CLI.

After ejecting, just look for rules: [ within the ejected webpack.config file, and add this in the rules array.

{ test: /.(pug|jade)$/, loader: "apply-loader!pug-loader?self" },// make sure you install the loaders for your project!
npm i -D apply-loader pug-loader

This would be the most elegant solution if you’re comfortable handling Webpack yourself, and leaving the bounds of the CLI itself.

But if you want keep the CLI, what else can you do?

If you want to stay within the comforts of the Angular CLI, the easiest solution I’ve come up with, and this is one that works with normal JIT compilation as well as AoT, is editing the underlying webpack config from inside the CLI itself (that’s inside your node_modules).

Like I said, this is a bit hacky :)

It’s as easy as adding a new rule within rules: [] (just like above but deep within node_modules ), but who wants to do all that manually!

Script time

Before we get started with that, just install two quick loaders that we’re going to need:

npm i -D apply-loader pug-loader//  ^ shortcut for "npm install --save-dev" here

Now, let’s make a little javascript file, calling it pug-rule-insert.js, and make sure you put it at the Root level of your entire project.

Here we’re just going to be looking for that common.js webpack file within node_modules/@angular/cli, and inserting a little pug test.

Now we can fire this up just by doing a quick node pug-rule-insert.js

Taking it one step further, let’s add a little postinstall hook to our package.json scripts… now we’re real fancy.

Now we get to use our .pug files directly for our templateUrl, and all is right with the world!

@Component({
selector: 'my-component',
templateUrl: './my-component.pug' // <-- ta-da!
})

Hack-tastic.

One thing to note, currently include ./my-other-pug isn’t fully working yet, but there’s a super easy work-around for that!

!=require('./my-other-pug.pug')

Piece of cake.

The best part?

ng serve && ng build --prod all work, and you even have live-reload / HMR when your doing your local development and updating your pug files.

It works with Universal+CLI projects as well https://github.com/angular/universal-starter/tree/master/cli

Enjoy!

Find me Online !

Twitter: @MarkPieszak
Github: @MarkPieszak

Trilon — Next-level Application Consulting

Trilon.io is a software consulting firm aimed to help get teams & applications up to speed by working with some of the leading industry experts in JavaScript, NodeJS, NestJS Framework & ASP.NET.

Contact us:
hello@trilon.io

Website:
Trilon.io

Trilon Consulting — Next-level Application Consulting https://trilon.io

--

--

Mark Pieszak

Trilon Co-Founder (Trilon.io) — Next level Consulting from Open-source fanatics and key contributors. >> Angular Universal core team >> NestJS Core Team