📜 ⬆️ ⬇️

Native JavaScript Templates (nJSt). Native JavaScript template engine

Greetings, dear reader. Chasing the idea of ​​making a template engine based on native JavaScript - I came to something. In Node.JS, for the implementation of this task, everything I could wish for was found, and the task was accomplished by the same native means. For example, the VM module served as the main tool for executing JavaScript-code that is isolated from the external environment. Template inserts are pure JS, but all sorts of dangerous tools like the scalpel, require, global, etc. do not get there.

Before proceeding to a detailed debriefing, let's take a look at an example of use, which will more clearly reflect the essence than anything else. I will not give the server creation code, I will simply describe the template itself and parsing under a certain context.

HTML template:
<html >
<head >
<title > #{PageTitle} </title >
</head >

<body >
<h1 > #{PageTitle} </h1 >

<ul >
<# for (var i=0; i
<List .length; i++) { # >
<li >
<#
if (typeof List[i] !== 'object') {
show(List[i]);
} else {
show(List[i].name +' - '+ List[i].note);
}
#>
</li >
<# } #>
</ul >
</body >
</html >
FractalizeR's HabraSyntax Source Code Highlighter .

')
Context:
var context = {
PageTitle
: ' jJSt test ' ,
List
: [ ' First ' , { name : ' Second ' , note : ' 2th ' } , ' Third ' ] ,
} ;
var result = njst . parse ( html , context , { debug : 1 } ) ;
FractalizeR's HabraSyntax Source Code Highlighter .


Idea


I think I should give a few words about how I came to this. Inspired by the idea of ​​1C-Bitrix (you heard right) about native templating, using a language that most people know (PHP) - I decided to do the same with Node.JS. But here, in my opinion, the option is even more advantageous, because it is pure JavaScript. The coder writes templates, HTML-CSS-JavaScript ... That's right, and here he writes template inserts again on JavaScript, which he should be familiar with from programming languages. And he does not need to delve into the subtleties of the syntax of any new template engine. Plus, in many template engines float the pitfalls that do not allow to implement quite trivial tasks. Of course, ideally, you need to correct the logic, within the framework of one project - this is normal, but when versatility is required, for any reason you will not get into logic. Of course, I do not offer a tool for the ability to showcase the skills of programming to typesetters, and the calculation goes on the minimum necessary inserts, and not on the transfer of logic into templates. This method of the whip will not thoroughly teach, there must be a place for self-discipline. Although if we talk about the whip method, thanks to the VM module supplied with Node.JS, the chances of implementing logic in patterns are significantly reduced.

In previous attempts to put this idea into practice, I applied the following construction of template inserts:

<script type = " nodejs " > ... </script >
FractalizeR's HabraSyntax Source Code Highlighter .


This was good because it guaranteed the highlighting of the JS code in almost any editor. Plus, it reminds that the insertion code is the same javascript. But I quickly left this when I made a sober list of shortcomings of this technique:

There were not many throwings, as I knew what was needed. First, provide also a short and compact version for outputting a specific field without extra code. Second, use characters that are not found in the JS syntax to avoid parsing conflicts. Initially, I wanted to use <% ... %> , a popular variant in template engines, as far as I know. My Notepad ++ highlighted it, I even noted that it could be good, that the template code was immediately visible. But for HTML inserts, I used the following construction initially:

<% if (true) { &> html <& } %>
FractalizeR's HabraSyntax Source Code Highlighter .


That is, for the output of pure HTML, not %> and <% , but &> and <& . And this highlight quickly became a problem. I chose the <# if (true) { &> html <& } #> grid option. And after a while I changed the principle of parsing and eliminated the problems with <&> . And in the end, everything became similar to similar PHP inserts (by the way, the template engine is close to its principle of code inserts). But to change the grid by a percent symbol (<% if (true) { %> html <% } %>) I somehow did not. In other matters, if you think that it is in vain, tell me about it.

Here are the inserts that are available in the application:

Specification


Now let's talk about the recommendations for use. Since there are no particular restrictions here, you need to set the tone, because the potential of govnokoding is quite large.

Firstly: in #{...} - it is desirable to write only the name of the key from the context in order to display a specific value, or the name of an earlier given variable, no unnecessary operations, even if some of them are possible. Well, for example, to assemble an array into a string: #{arr.join(', ')} - I think it will not be a great sin. String translations and ; are intentionally ignored here. For all kinds of code (cycles, conditions and other things) use <# ... #> .

If you need output inside the insert <# ... #> , then you will come to the aid with the show() function or the toShow variable, which can be filled (but not replaced, otherwise the previous output, including from show() will be overwritten ). Here is an example of output using these tools:
<#
while (toShow.length < 100) {
show('I like it! ');
toShow += 'I love it! ';
}
#>
FractalizeR's HabraSyntax Source Code Highlighter .


Secondly: a simple rule in theory, but in reality it is often forgotten about it: no excess coding! Only I need to withdraw, I think cycles and conditions will be enough for the eyes. Functions, although you can create, but always remember that this is a mortal sin, and if you do that, your fingers will fade and you will no longer be able to write functions in templates.

Thirdly: if you are unfamiliar with the JavaScript specification (albeit unofficial), then be sure to start with it, read here . If other people have the potential to work with your code, or you yourself after some time, hoping to understand yourself later, are going to work with it, then do not avoid this item. This is a very popular problem, I myself was sick, but after being in the role of a victim, I realized how important this moment is. Sometimes you meet such that the brain cannot interpret in any way, and the author himself can hardly cope without psychotropic drugs.

Area of ​​visibility


One of the questions that most likely the rest can flash in your head: “and if you set the variable var v = true; will it be visible in the next template insert box? ”- the answer is YES! And if you find it difficult to understand “how it works”, then it’s good, if you know PHP, imagine that inserts of the nJSt template engine are PHP inserts, the behavior is most approximate to this type. As well as the conclusion:
#{...} = <?=...?>
<# ... #> = <? ... ?>
FractalizeR's HabraSyntax Source Code Highlighter .


Security


As mentioned above, only the context gets inside the template. No global objects (require, global). Overwriting any external global variable also fails. In general, everything is within context. Of course, if there is a need to work with the exterior - we transfer the necessary function in the context that will do it on demand.

I don’t hide it, there are still gaps, but in the next versions I’ll cover them. For example, if you write <# while(true)++ #> - we get 100% of the load on the processor, and until you kill the node process, you can use the computer as a heater, and further actions in the node become impossible. In the next version I'm going to hang up a timer and kill the execution, if it is delayed in order to avoid such server hangs.

Features and Applications


Opportunities rather are limited only by your imagination, because we transfer to the context what we want. On GitHub , the examples have a demo of calling a function to download the current page. The download function is passed to the context and called in the template. You can transfer any function in the context that is a tool of your project, and which, perhaps, operates with the external environment. In general, if you know JS, then there should be no problems. And if you do not know, then here and two times .

In the next versions



Use on health


If you want more examples and more detail, then see the examples folder on GitHub, there also download the module itself and study the source codes.

nJSt on GitHub : https://github.com/unclechu/njst

Waiting for your feedback and suggestions for improvement.

Source: https://habr.com/ru/post/120486/


All Articles