Razor is a templating idea from the ASP.NET team at Microsoft that shipped with their MVC 3 release. Razor differs from other templating languages in that there is no special template syntax to learn, and no keywords. It's also pretty clean, for a template. Here is an example of a template (C#) that loops through an array:

<ul id="theBigList">
@foreach(var item in myItems){
    <li class="@( i % 2 == 0 ? 'even' : 'odd' )">@item.name</li>
}
</ul>

There is a lot more you can do, but that gives you the basics. Why would you care? Because I've been working on Vash, which is an implementation (not a port) of Razor syntax to JavaScript. With Vash, the above becomes:

<ul id="theBigList">
@myItems.forEach(function(item){
    <li class="@( i % 2 === 0 ? 'even' : 'odd' )">@item.name</li>
})
</ul>

Notice how it's almost exactly the same, and is only JavaScript. There is no special syntax, aside from @ and @(). From here on out, Razor refers to the general syntax, while Vash refers to the parser implementation. Even though Vash is in JavaScript, there are few if any differences in the actual syntax.

Vash works on node.js as well as in the browser!

Quick Razor Primer

Razor has a single key character, @. This character has three primary usages:

  • Denote an expression, meaning some JavaScript that should be evaluated inline. For example: @someVar.aFunction()[3]
  • Denote an implicit expression, implying that all of the text is valid JavaScript. For example: @( someVar == true ? "yeah <strong>buddy</strong>" : 'nope.' )
  • Denote an anonymous block, which, while having no bearing on scope, can allow for multiline JavaScript blocks. For example:
@{
    var  a = []
        ,all = '';
    
    a.push('love', 'and', 'peace');
    all = a.join(' ');
    <span>@all</span>
}

In addition, there are two other usages that aren't very common:

  • Denote a "server-side" comment: @* this text is not included in the compiled template *@
  • Escape a literal character in content: @} would allow the } to be included literally, and not assumed to be the closing brace of a code block. This is how Vash treats this case, but this may not be compliant with Razor.

Occasionally, Razor might get confused between what's markup and what's code. The following example will fail to compile:

@model.forEach(function(item){
    I am text!
    I span many lines!
})

Razor has no way of knowing that I isn't some JS. So you need to give Razor a hint, using the <text> tag:

@model.forEach(function(item){
    <text>
        I am text!
        I span many lines!
    </text>
})

This will render as:

I am text!
I span many lines!

Occasionally you want to tell Razor that only one line is markup and not code:

@model.forEach(function(item){
    @:I am text!
})

Choices

For Vash, a few changes had to be made. JavaScript is not C#!

Line breaks are of no consequence. To Vash, the following statements are equivalent (and yes, Vash is smart enough to know what's code and what's markup!):

@* A, all on one line *@
<div class="how"> @for(var i = 0; i < 1; i++){ <div class="item-@i">I be an item!</div> } </div>

@* B, with line breaks *@
<div class="how"> 
@for(var i = 0; i < 1; i++){ 
    <div class="item-@i">I be an item!</div> 
} 
</div>

Things related to Razor as a View Engine are not implemented. This includes: LayoutPage, RenderSection, RenderBody, @helper, @section, @using, etc.

Inline templates as parameters are not supported. This requires more of a View Engine or framework.

However, because JS is awesome, you can do some interesting things...

function myHelper(item){
    <td>@item.firstName</td>
    <td>@item.LastName</td>
}

@model.items.map(function(item){
    myHelper()
    return item.age
})
.forEach(function(age){
    <td>@age</td>
});

Now why would you want to do this? I don't know, but you can!

Usage

Include vash.min.js on your page somewhere. Compile templates:

var compiled = vash.compile("<li>I'm a template, @model.name!</li>");

Return a template string:

compiled({ name: 'Vash the Stampede' });

For inline script tag usage, I recommend type="text/vash". For template files on the server or otherwise, I've been using a .vash extension.

More information on usage can be found at Vash's Github repo.

It's also available using npm:

npm install vash

To Conclude

Let me know what you think! This is definitely my first non-trivial parser experience, and I have a ton to learn.