ventojs / vento

🌬 A template engine for Deno & Node
https://vento.js.org/
MIT License
153 stars 9 forks source link

Remove `with` usage in generated code, add code transform step #43

Closed wrapperup closed 4 months ago

wrapperup commented 4 months ago

Motivation

The with statement comes with a lot of caveats, the main one (for me) is the huge performance penalty. JavaScript runtimes will de-optimize value accesses inside of with statements. I like the convenience of accessing state properties with just {{ prop }}, but since it has a huge perf penalty, I usually opt to disable useWith, which forces me to write templates with it.

Additionally, the generated code can't be used in strict mode.

Solution

Remove the with statement, and rewrite the generated JavaScript to insert the template state access.

Currently, it happens in a (~200 LoC) transform step that analyzes the AST of the generated code, and rewrites any unbound and non-global values to access from the template state. Accessing globals like Deno, Promise, JSON and other globals should still work to try and keep the "write real JavaScript" promise, and it should also respect variable bindings and their scopes.

Benchmarks

Quick benchmark to guage the improvement, it varies a bit (mostly on accesses inside the with statement)

Rank Engine Time Times Slower
1 String Template 138.114ms 1.00x
2 Vento (new) 164.576ms 1.192x
3 Vento (useWith = false) 166.130ms 1.203x
4 Vento (old) 941.945ms 6.820x

With the code transform step, Vento now performs within error of disabling useWith. The real-world improvements can be pretty huge for SSR use-cases, especially in larger templates (though this varies on accesses!).

Todo

wrapperup commented 4 months ago

I guess the one question I have is do we want to still support CJS modules? estree-walker doesn't have a CJS version. There isn't a deno alternative for any of these unfortunately.

oscarotero commented 4 months ago

Thanks for this! Please, let me some time to test it.

I think we can remove CJS support.