VENTO
This is a minimal template engine inspired by other great engines like Nunjucks, Liquid, Mustache or EJS.
Why another template engine?
Because I couldn't find the "perfect" template engine for me (probably this one neither is). The issues I found in existing template engines:
Nunjucks
(It's my favorite template engine so far).
- I like:
- I can invoke functions like
{{ user.getName() }}
. - Very flexible, with many built-in filters and features
- I can invoke functions like
- I don't like:
- It's not well maintained. The last version was released in Jun 2022. And the previous version in 2020.
- It's not async-friendly. For example, you have some tags to work with sync
values (like
for
andif
) and others for async values (likeasyncEach
andifAysnc
). Some features don't work in async contexts. - To me, it's very uncomfortable to have to type the delimiters
{%
and%}
all the time (especially the%
character). - By default, all variables are escaped, so you have to remember to use the
safe
filter everywhere. This is not very convenient for my use case (static site generators), where I can control all the content and the HTML generated. - Some filters are too specific.
Liquid
I like:
- The support for async evaluation is less hacky than Nunjucks.
- The variables are not escaped by default, there's an
escape
filter for that.
I don't like:
- It's not possible to invoke functions in a liquid template. For example
{{ user.getName() }}
fails. - It has the same problem as Nunjucks with the
%
character in the delimiters.
- It's not possible to invoke functions in a liquid template. For example
EJS/Eta
- I like:
- It allows running any javascript code in the template.
- I don't like:
- It has the same problem with the
%
character. And I don't like the opening and closing delimiters (<%
and%>
). - Because it runs javascript, it's very verbose to do a simple
forEach
orif
.
- It has the same problem with the
Mustache
- I like:
- Very simple, everything is inside
{{
and}}
. - The closing tag is
{{/tagname}}
, very nice!
- Very simple, everything is inside
- I don't like:
- Perhaps too simple and the syntax can be a bit confusing.
- Partials. It's not easy to include them dynamically.
- The data context is a bit confusing to me.
- Very uncomfortable to work with filters.
What this new template engine has to offer?
First, let's take a look at this syntax example:
{{ if printName }}
{{ await user.getName("full") |> toUpperCase }}
{{ /if }}
- Everything is between
{{
and}}
tags. Unlike Nunjucks or Liquid, there's no distinction between tags{% tag %}
and printing variables{{ var }}
. - The closed tag is done by prepending the
/
character (like Mustache). - Async friendly.
- Like EJS, you can use real JavaScript code everywhere.
await user.getName("full")
is real JS code that will be executed at runtime. - Filters are applied using the
pipeline operator
(
|>
). Note: this is not exactly like the last proposal for JavaScript, it's inspired by (the previous proposal that was rejected but it's way more simple and fits better for filters. - Filters can run prototype methods. In this example
users.getName("full")
returns a string, so thetoUpperCase
is a method of theString
object. It's the same asusers.getName("full").toUpperCase()
.
Getting started
This is a library for Deno. I'm planning to release an NPM version in the
future.
There's already an NPM version that you
can install with npm install ventojs
.
Import the library and create an instance:
import vento from "https://deno.land/x/vento/mod.ts";
const vto = vento({
// Resolve the non-relative includes paths
includes: "./path/to/includes",
});
Or in Node:
import vento from "ventojs";
const vto = vento({
// Resolve the non-relative includes paths
includes: "./path/to/includes",
});
There are different ways to load, compile and run a template. For example, you
can use load
to load and compile a template file and return it.
// Load and return a template
const template = vto.load("my-template.vto");
// Now you can use it passing the data
const result = template({ title: "Hello world" });
console.log(result.content);
Alternatively, you can load and run the template file in a single call:
const result = vto.run("my-template.vto", { title: "Hello world" });
console.log(result.content);
If the template code is not a file, you can run it directly:
const result = vto.runString("<h1>{{ title }}</h1>", { title: "Hello world" });
console.log(result.content);
Visual Studio Code Support
The Vento extension for VS Code enables syntax highlight and provides some useful snippets.