Hi everybody,
Over the past year I've been working on building a new templating engine from the ground up. I did this for several main reasons:
- templates.js was buggy and hard to maintain
- templates.js was orders of magnitude slower compared to other templating engines
- templates.js uses orders of magnitude more CPU time than other templating engines
- Curiosity surrounding parsers and compilers, especially dealing with how Babel and JS AST formats work
Why was templates.js slow?
It works opposite of most templating engines, iterating over the entire data object, instead of fetching values from it based on template tokens. This meant iterating through the template string possibly hundreds of times per request.
templates.js was begging for a replacement. My solution:
Benchpress is a brand new templating engine built from the ground up to be feature-rich and very fast. It is mostly backwards compatible with templates.js, and in some areas providing more flexible syntax.
How is it so fast?
Benchpress compiles template files into Javascript functions composed of string concatenation. For example, the following template:
I love {{forum}} because it uses {{templater}}!
Is compiled into this Javascript module:
(function (factory) {
if (typeof module === 'object' && module.exports) {
module.exports = factory();
} else if (typeof define === 'function' && define.amd) {
define(factory);
}
})(function () {
function compiled(helpers, context, get, iter, helper) {
return 'I love ' + get(context && context['forum']) + ' because it uses ' + get(context && context['templater']) + '!';
}
return compiled;
});
This puts Benchpress among the fastest templating libraries available. Faster than Handlebars, EJS, underscore, and Pug (Jade). It's almost 40x faster than templates.js was.
Breaking changes between templates.js and Benchpress
I tried to make Benchpress as backwards compatible as possible. Most existing templates should work fine. However, there are a few instances where Benchpress will behave differently.
- If you are iterating over an object or array, you can't access it's properties directly, since you can use
{obj.prop}
to mean the prop
property of each element. templates.js supported both of these, but due to the ambiguity of the syntax, Benchpress requires you to use {../../obj.prop}
to access that specific property.
- Somehow templates.js supports iterating over deep values. For example, for this data:
{ obj: { arr: [4, 7, 2] } }
and this template: <!-- BEGIN arr -->{@value}<!-- END arr -->
it would output 472
, even though arr
isn't at the top level of the input data. Obviously, this is not supported in Benchpress.
- Another crazy thing that templates.js supported was reversing the iterators in the template, so
<!-- BEGIN arr1 --><!-- BEGIN arr2 -->
behaved the same as <!-- BEGIN arr2 --><!-- BEGIN arr1 -->
. This is unsupported in Benchpress because it makes no sense.
By far, the largest breaking change in Benchpress is that client-side template compilation is unsupported. In fact, using Benchpress.parse(template, data)
is entirely unsupported. There is a new function only available on the server side, Benchpress.compileParse(template, data, callback)
which will compile a given template and then execute it with the given data. You must otherwise precompile templates before using them client-side. Any templates in NodeBB, themes, and plugins that are provided normally will continue to be compiled automatically.
I'm very excited to introduce you all to Benchpress, and for the future it has with NodeBB! Benchpress is planned on being shipped with NodeBB in an upcoming major release.
Edit: just to clarify, client-side parsing, using Benchpress.parse(templateName, data, callback)
is still available client side. Client side compilation is unsupported, but client side parsing is still supported.
Edit2: I forgot to mention that this forum is currently powered by Benchpress.