There's a new templating language on the block, Vento, and it seems to be all the rage recently, but I have to admit I'm not entirely convinced by it.
First of all, I do agree that Vento looks better than Liquid or Nunjucks. The delimiters look more ergonomic, for instance {{ if … }}
is nicer to type than {% if … %}
. Also, expressions are JavaScript-based, which is huge; when doing any amount of remotely complex logic in Liquid, it always ended up as an unreadable puddle of {% assign %}
tags with a bunch of filters and some flow control. Being able to use JavaScript will simplify this immensely.
It seems that Vento's syntax is still in its infancy. It doesn't have a formal specification, or anywhere near that, and that creates some edge cases that I'm not super comfortable with. The fact that it allows arbitrary JavaScript expressions creates a huge amount of possibilities. I think it'd be nice to have these kinds of things documented, giving some reassurrance that what works now, will work in the future. Some examples include:
- If I write
{{--foo}}
, does this mean "negate foo
, print it, and trim the whitespace on the left" or does it mean "decrement foo
, and print it"?
- If I write
{{#-#}}
, which side is the whitespace trimmed on? Is it even valid?
- If I write
{{> // comment }}
, is the ending }}
part of the JS comment or does it close the {{>
tag?
The answers to these questions are easily testable, but that's beside the point; the fact that the behavior is undocumented makes me unsure that what I test now will have the same results in upcoming versions of Vento.
Vento does its own naive "parsing" of JavaScript to determine where the closing tag brackets }}
are. This is really hard to do well, and sure enough, Vento's got some bugs here:
- Writing
{{ `\${{` }}
throws an error, because Vento fails to see the ${
is escaped;
- Writing
{{ /[/]}}/.test(foo) }}
throws an error, because Vento fails to account for unescaped slashes within regular expressions;
- Writing
{{ !/}}/.test(foo) }}
throws an error, because Vento fails to recognize /}}/
as a regular expression altogether;
- Writing
{{ for [[n]] of [[[1]], [[2]]] }}
throws an error, because Vento is naively detecting variable destructuring and fails to do so correctly.
These types of issues are some of the primary reasons that, when I implemented interpolation in Yozo, I chose the "dumb" route and require that the interpolation content does not itself contain }}
. This makes parsing much simpler, and while not ideal, gives users confidence about the correctness of the code they're writing.
Lastly, I'm not entirely sure about how well the syntax will hold up over time. My main concerns;
- The pipeline operator is also being proposed to JavaScript (in fact, this was the inspiration for Vento's
|>
), and whatever it ends up being, it will be incompatible with how Vento treats them. foo |> toUpperCase()
and foo |> toUpperCase
are both supported in Vento, but would definitely not be doing the same thing if pipes were implemented in native JavaScript, and wouldn't read from foo
's prototype, either.
{{ tag }}
is ambiguous; if tag
is defined as a valid tag name, like echo
, then it does whatever it is defined to do, but otherwise it tries to print a variable tag
. This effectively means any new tags (that are valid JS variable names) are a breaking change in Vento, and even Vento users themselves should be careful defining their own tags because there is a risk of clashing with existing code.
- Its data "cascade" is not great; if I redefine a runtime-global variable, such as
{{ set performance = 'good' }}
, and then attempt to print {{ performance }}
, the result is something like [object Performance]
because the runtime's global variable takes presedence over the local one. On the other hand, this does work when defined using {{> const performance = 'good' }}
, because then we leverage the way JavaScript handles variables. I'm assuming most people will use the {{ set }}
syntax, both because it is shorter and because it is the analogous way to set variables coming from something like Liquid or Nunjucks. This ends up making their code more fragile, because now they are implicitly tied to the globals defined in the runtime, and those are not always the same. For example, I might define a variable getParent
on a Node-Vento project, and this'll unexpectedly break if I decide to migrate to Deno.
I know this post can be interpreted as having some discouraging undertones, but I think it's important not to blindly jump on the bandwagon and address the shortcomings of the shiny new thing. Don't get me wrong, I am excited about Vento; but it has a long way to go before I feel comfortable recommending it in a professional setting.