Review My Code Please
Tags: webdev, help, codequality, javascript
Published: 2021-01-07 23:25:57

I am working on a little project using JavaScript (it's a work in progress and part of a bigger project). I would appreciate it if anyone could review any of the following code and criticize and tell me what I may be doing wrong. I am trying to code in a way where the code explains what is going on so if you don't understand, what is confusing?

~~~js
let TemplateBlocks = {
  eval: a => '`;result+='+a.content+';result+=`',
  repeat: a => {
    let i = a.args[1] || 'i';
    return '`;for(var '+i+'=1; '+i+'<'+(Number(a.args[0])+1)+'; '+i+'++){result+=`'+a.content+'`;}result += `';
  },
  var: a => '`;var ['+a.perams.keys()+'] = ['+a.perams.values().reduce((b,c) => b+'"'+c+'",','')+'];result += `',
  if: a => '`;if('+a.perams.condition+'){result+=`'+a.content+'`;}result += `',

  defaultBlock: a => {
    console.warn('Template block "'+a.name+'" is not defined');
    return '';
  },
}

////////////////////////////////////////////////////////////////////////////////

function Templater(template){
  let result = FormatTemplateVariables(template);
  result = ReplaceTemplateBlocks(result);
  return EvalTemplate('let result = `'+result+'`; return result;');
}

function FormatTemplateVariables(template){
  // {js} -> {eval}js{/eval}
  let regex = /{(([^{/ ]*?)( [^{]*?)?)}(?!.*{\/\2}.*)/gs;
  return template.replace(regex, '{eval}$1{/eval}');
}

function ReplaceTemplateBlocks(template){
  // {block arguments}content{/block}
  let regex = /{(\w+)( [^}]+)?}((?!.*{(\w+)( .*?)?}.*{\/\4}.*).*){\/\1}/s;
  return RegexReplace(template, regex, ReplaceBlock);
}

function EvalTemplate(text){
  try {
    return Function('', text)()
  }
  catch (error){
    console.error("Error Evaluating template: "+error.message+'\n\n'+
      text.split('\n')[error.lineNumber-1]+'\n'+" ".repeat(error.columnNumber-1)+'^\n');
    return '';
  }
}

function RegexReplace(text, regex, getReplacement){
  let safe = 10000;
  let result = text;
  while(result.match(regex) && --safe > 0){
    let match = result.match(regex);
    result = result.replace(match[0], getReplacement(match))
  }
  safe <= 0 && console.error('RegexReplace: loop has no end');
  return result;
}

function ReplaceBlock(match){
  let arg = (match[2]||' ').substring(1);
  let [content, name, perams, args] = [match[3], match[1], GetPerams(arg), arg.split(' ')];
  let func = TemplateBlocks[name] || TemplateBlocks.defaultBlock;
  let exportData = { arg,content,name,perams,args,func };
  return func(exportData);
}

function GetPerams(text){
  if(!text)return {};
  let regex = /(\w+)(="(.*?)")?/ // name="value" or name
  let result = {}
  RegexReplace(text, regex, match => {
    result[match[1]] = match[3] === undefined ? true : match[3];
    return '';
  });
  return result;
}

// For testing
$$('template').forEach(el => {
  el.outerHTML = '<div>'+Templater(el.innerHTML)+'</div>';
})

~~~

## Example

~~~html
      <template>
        <p>Welcome, this is a templating test. Now we will set some variables...</p>
        {var name="joe" age="24" born="1986"}{/var}
        <b>Hello {name}, you were born in 1986</b>
        <p>We know that you {age ? 'are a child beacuse your under 18' : 'are an adult because you are older than 18'}</p>
        <p>Now we will loop through something</p>
        {repeat 10 a}
          <br>
          <hr>
            <h1>Hello, this is item {a}</h1>
            <p>What we know is that you are on item number {a} and that {a} squared is {a*a}
            Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
            We made a special color button for you,</p>
            <button style="background: {'#'+(a*2)+a+a}; color: white" onclick="alert('you clicked button {a}')">Button {a}</button>
          <br>
        {/repeat}
        <p>lots of work to be done...</p>
        {repeat 10}
        Hello {i}
        {/repeat}
        {test}hello{/test}
      </template>
~~~
## Result

~~~html
<div>
        <p>Welcome, this is a templating test. Now we will set some variables...</p>
        
        <b>Hello joe, you were born in 1986</b>
        <p>We know that you are a child beacuse your under 18</p>
        <p>Now we will loop through something</p>
          ...
          <hr>
            <h1>Hello, this is item 10</h1>
            <p>What we know is that you are on item number 10 and that 10 squared is 100
            Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
            We made a special color button for you,</p>
            <button style="background: #201010; color: white" onclick="alert('you clicked button 10')">Button 10</button>
          <br>
        
        <p>lots of work to be done...</p>
        
        Hello 1
        
        ...
        
        Hello 10
        
        undefined
      </div>
~~~