A while back I had built a bit of a blogging platform using the ubiquitous node.js stack of Express and Jade templates. It was pretty straight-forward — blog posts were just written in Jade and served with Express — but syntax highlighting ended up being a problem.
I had opted to use google-code-prettify, which takes an escaped <code> block of HTML like this and highlights it:
<pre class="prettyprint">
<code>
<html>
<head></head>
<body></body>
</html>
</code>
</pre>
I really didn't want to pre-escape my code, of course. That's horrible. And as far as I can tell, Jade has no way to automatically escape HTML like this, unless it's already stored in a variable.
Solution: Custom Filters
Jade's documentation briefly mentions filters. The syntax is just a word prefixed with a colon. There are a few canned ones like :markdown. I jumped in and wrote my own custom filter, :code, to strip out significant HTML characters and replace them with < > and the like.
require('jade').filters.code = function( block ) {
return block
.replace( /&/g, '&' )
.replace( /</g, '<' )
.replace( />/g, '>' )
.replace( /"/g, '"' );
}
Cryptic errors ensued.
SyntaxError: Unexpected token ILLEGAL
at Object.Function (unknown source)
at Object.compile (/Users/paul/Code/paulzumbrun.com/node_modules/jade/lib/jade.js:176:8)
at Function.compile (/Users/paul/Code/paulzumbrun.com/node_modules/express/lib/view.js:68:33)
Turns out the result still gets parsed by Jade; it's not just a string that gets dropped in like I thought. This is actually a good thing because it means you can chain filters, but we have to work around it. To make the parser happy with me again I had to strip out a few extra characters. Newlines needed to become \n. Other escape sequences needed double-escaped. Hashes need stripped too, since they're used to execute JavaScript inline #{thusly}.
This is the final working filter.
require('jade').filters.code = function( block ) {
return block
.replace( /&/g, '&' )
.replace( /</g, '<' )
.replace( />/g, '>' )
.replace( /"/g, '"' )
.replace( /#/g, '#' )
.replace( /\\/g, '\\\\' )
.replace( /\n/g, '\\n' );
}
Now when you go to use this in a template, all you have to do is this. Pretty close to my original goal.
pre.prettyprint
code.lang-html
:code
<html>
<head></head>
<body></body>
</html>
There may be a cleaner way to do this, but I sure couldn't find it with the search terms I was using.