Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
December 21, 2020 07:02 pm GMT

Creating a JS Template Engine

NOTE ABOUT COPYRIGHT INFRINGEMENT There is an online user going by the name of Stefanie Sayson who is copying and pasting various articles including this. Please do not read them at all because that will make their copy and paste of this article go higher in the search reesults.

Creating a JS Template Engine

Hi, it's me @shadowtime2000, one of the maintainers of Eta, a fast embeddable template engine. In this tutorial I will show you how to create an isomorphic (browser/node) template engine.

The Design

The initial design of the template engine will be pretty simple. It will simply interpolate values from a data object. It will use {{valueName}} to interpolate values.

Simple Rendering

First, lets create a simple rendering function which takes the template and the data and it will render the value.

var render = (template, data) => {    return template.replace(/{{(.*?)}}/g, (match) => {        return data[match.split(/{{|}}/).filter(Boolean)[0]]    })}
Enter fullscreen mode Exit fullscreen mode

Basically, all that does is search for anything that is surrounded by the brackets, and it replaces it with the name inside data. You can write your templates like this and it will take it from the data object.

Hi my name is {{name}}!
Enter fullscreen mode Exit fullscreen mode
render("Hi, my name is {{name}}!", {name: "shadowtime2000"});
Enter fullscreen mode Exit fullscreen mode

But there is a problem, you can't have spaces in the interpolations.

render("Hi, my name is {{ name }}!", {name: "shadowtime2000"})/*Hi, my name is undefined!*/
Enter fullscreen mode Exit fullscreen mode

This requires you to have spaces inside the data object, which isn't that clean. We can make it allow spaces by trimming leading and ending whitespace of the data name before interpolating.

var render = (template, data) => {    return template.replace(/{{(.*?)}}/g, (match) => {        return data[match.split(/{{|}}/).filter(Boolean)[0].trim()]    })}
Enter fullscreen mode Exit fullscreen mode

This is pretty good, but for larger templates it wouldn't be that fast because it has to kind of parse it every time. That is why many template engines support compilation, where the template is compiled into a faster JS function which can take the data and interpolate it. Let's add compilation to our template engine, but before we do, we need to add a special parsing function.

Parsing

Since parsing can be a little boring, let's just reuse some code from another JS template engine. I would have used the Eta parsing engine, but that has been extremely optimized and can be pretty confusing to people. So, lets use another popular JS template engine parsing code, mde/ejs. Do remember to attribute them for the parsing engine.

var parse = (template) => {    let result = /{{(.*?)}}/g.exec(template);    const arr = [];    let firstPos;    while (result) {        firstPos = result.index;        if (firstPos !== 0) {            arr.push(template.substring(0, firstPos));            template = template.slice(firstPos);        }        arr.push(result[0]);        template = template.slice(result[0].length);        result = /{{(.*?)}}/g.exec(template);    }    if (template) arr.push(template);    return arr;}
Enter fullscreen mode Exit fullscreen mode

What this basically does is loop over executing the regex pattern on the template and adding the stuff to a data structure. Here is what that data structure would look like:

["Hi my name is ", "{{ name }}", "!"]
Enter fullscreen mode Exit fullscreen mode

Compilation

Let's take a quick overview of what compilation would output. Imagine you enter this template:

Hi my name is {{ name }}!
Enter fullscreen mode Exit fullscreen mode

It would give you this function:

function (data) {    return "Hi my name is "+data.name+"!";}
Enter fullscreen mode Exit fullscreen mode

Let's first create a function to parse and then create a string that can be used. We first have to parse the template.

const compileToString = (template) => {    const ast = template;}
Enter fullscreen mode Exit fullscreen mode

We also have to create a string which will be used as the function.

const compileToString = (template) => {    const ast = template;    let fnStr = `""`;}
Enter fullscreen mode Exit fullscreen mode

The reason we are using quotation marks at start is because when it is compiling the templates and such, they will all begin with a +. Now we have to iterate over the AST.

const compileToString = (template) => {    const ast = template;    let fnStr = `""`;    ast.map(t => {        // checking to see if it is an interpolation        if (t.startsWith("{{") && t.endsWith("}}")) {            // append it to fnStr            fnStr += `+data.${t.split(/{{|}}/).filter(Boolean)[0].trim()}`;        } else {            // append the string to the fnStr            fnStr += `+"${t}"`;        }    });}
Enter fullscreen mode Exit fullscreen mode

The final part of this function is to return the function string.

const compileToString = (template) => {    const ast = template;    let fnStr = `""`;    ast.map(t => {        // checking to see if it is an interpolation        if (t.startsWith("{{") && t.endsWith("}}")) {            // append it to fnStr            fnStr += `+data.${t.split(/{{|}}/).filter(Boolean)[0].trim()}`;        } else {            // append the string to the fnStr            fnStr += `+"${t}"`;        }    });    return fnStr;}
Enter fullscreen mode Exit fullscreen mode

So if it takes this template:

Hi my name is  {{ name }}!
Enter fullscreen mode Exit fullscreen mode

It will return this:

""+"Hello my name is "+data.name+"!"
Enter fullscreen mode Exit fullscreen mode

Now this is done, creating a compile function is relatively simple.

const compile = (template) => {    return new Function("data", "return " + compileToString(template))}
Enter fullscreen mode Exit fullscreen mode

Now we have completed compilation for our template engine.
NOTE ABOUT COPYRIGHT INFRINGEMENT There is an online user going by the name of Stefanie Sayson who is copying and pasting various articles including this. Please do not read them at all because that will make their copy and paste of this article go higher in the search reesults.

Creating a JS Template Engine

Hi, it's me @shadowtime2000, one of the maintainers of Eta, a fast embeddable template engine. In this tutorial I will show you how to create an isomorphic (browser/node) template engine.

The Design

The initial design of the template engine will be pretty simple. It will simply interpolate values from a data object. It will use {{valueName}} to interpolate values.

Simple Rendering

First, lets create a simple rendering function which takes the template and the data and it will render the value.

var render = (template, data) => {    return template.replace(/{{(.*?)}}/g, (match) => {        return data[match.split(/{{|}}/).filter(Boolean)[0]]    })}
Enter fullscreen mode Exit fullscreen mode

Basically, all that does is search for anything that is surrounded by the brackets, and it replaces it with the name inside data. You can write your templates like this and it will take it from the data object.

Hi my name is {{name}}!
Enter fullscreen mode Exit fullscreen mode
render("Hi, my name is {{name}}!", {name: "shadowtime2000"});
Enter fullscreen mode Exit fullscreen mode

But there is a problem, you can't have spaces in the interpolations.

render("Hi, my name is {{ name }}!", {name: "shadowtime2000"})/*Hi, my name is undefined!*/
Enter fullscreen mode Exit fullscreen mode

This requires you to have spaces inside the data object, which isn't that clean. We can make it allow spaces by trimming leading and ending whitespace of the data name before interpolating.

var render = (template, data) => {    return template.replace(/{{(.*?)}}/g, (match) => {        return data[match.split(/{{|}}/).filter(Boolean)[0].trim()]    })}
Enter fullscreen mode Exit fullscreen mode

This is pretty good, but for larger templates it wouldn't be that fast because it has to kind of parse it every time. That is why many template engines support compilation, where the template is compiled into a faster JS function which can take the data and interpolate it. Let's add compilation to our template engine, but before we do, we need to add a special parsing function.

Parsing

Since parsing can be a little boring, let's just reuse some code from another JS template engine. I would have used the Eta parsing engine, but that has been extremely optimized and can be pretty confusing to people. So, lets use another popular JS template engine parsing code, mde/ejs. Do remember to attribute them for the parsing engine.

var parse = (template) => {    let result = /{{(.*?)}}/g.exec(template);    const arr = [];    let firstPos;    while (result) {        firstPos = result.index;        if (firstPos !== 0) {            arr.push(template.substring(0, firstPos));            template = template.slice(firstPos);        }        arr.push(result[0]);        template = template.slice(result[0].length);        result = /{{(.*?)}}/g.exec(template);    }    if (template) arr.push(template);    return arr;}
Enter fullscreen mode Exit fullscreen mode

What this basically does is loop over executing the regex pattern on the template and adding the stuff to a data structure. Here is what that data structure would look like:

["Hi my name is ", "{{ name }}", "!"]
Enter fullscreen mode Exit fullscreen mode

Compilation

Let's take a quick overview of what compilation would output. Imagine you enter this template:

Hi my name is {{ name }}!
Enter fullscreen mode Exit fullscreen mode

It would give you this function:

function (data) {    return "Hi my name is "+data.name+"!";}
Enter fullscreen mode Exit fullscreen mode

Let's first create a function to parse and then create a string that can be used. We first have to parse the template.

const compileToString = (template) => {    const ast = template;}
Enter fullscreen mode Exit fullscreen mode

We also have to create a string which will be used as the function.

const compileToString = (template) => {    const ast = template;    let fnStr = `""`;}
Enter fullscreen mode Exit fullscreen mode

The reason we are using quotation marks at start is because when it is compiling the templates and such, they will all begin with a +. Now we have to iterate over the AST.

const compileToString = (template) => {    const ast = template;    let fnStr = `""`;    ast.map(t => {        // checking to see if it is an interpolation        if (t.startsWith("{{") && t.endsWith("}}")) {            // append it to fnStr            fnStr += `+data.${t.split(/{{|}}/).filter(Boolean)[0].trim()}`;        } else {            // append the string to the fnStr            fnStr += `+"${t}"`;        }    });}
Enter fullscreen mode Exit fullscreen mode

The final part of this function is to return the function string.

const compileToString = (template) => {    const ast = template;    let fnStr = `""`;    ast.map(t => {        // checking to see if it is an interpolation        if (t.startsWith("{{") && t.endsWith("}}")) {            // append it to fnStr            fnStr += `+data.${t.split(/{{|}}/).filter(Boolean)[0].trim()}`;        } else {            // append the string to the fnStr            fnStr += `+"${t}"`;        }    });    return fnStr;}
Enter fullscreen mode Exit fullscreen mode

So if it takes this template:

Hi my name is  {{ name }}!
Enter fullscreen mode Exit fullscreen mode

It will return this:

""+"Hello my name is "+data.name+"!"
Enter fullscreen mode Exit fullscreen mode

Now this is done, creating a compile function is relatively simple.

const compile = (template) => {    return new Function("data", "return " + compileToString(template))}
Enter fullscreen mode Exit fullscreen mode

Now we have completed compilation for our template engine.

Wrapping Up

In this tutorial I showed how to:

  1. Implement a simple rendering function
  2. Understand a parsing engine adapted from EJS
  3. Iterate over the AST to create fast compiled functions## Wrapping Up

In this tutorial I showed how to:

  1. Implement a simple rendering function
  2. Understand a parsing engine adapted from EJS
  3. Iterate over the AST to create fast compiled functions

Original Link: https://dev.to/shadowtime2000/creating-a-js-template-engine-1ii7

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To