• Stars
    star
    523
  • Rank 84,684 (Top 2 %)
  • Language
    JavaScript
  • License
    Other
  • Created over 13 years ago
  • Updated over 2 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Vash, the 60 billion double-dollar template-maker. Razor syntax, for JavaScript templates

Vash

"... the 60 billion double-dollar template-maker!" ~ The previous README, and no one else, ever.

Vash is a template engine that offers a swift flow between code and content using Razor Syntax 1 . This document 2 is intended for users of Vash, and also serves as a reference for Vash's implementation of Razor Syntax.

Build Status

NPM NPM

Bitdeli Badge

Features

  • Mix code and content without ugly delineators, like <?, <%, or {{.
  • No new language to learn: Vash is just HTML-aware JavaScript.
  • Great with markup, but can be used with nearly any other language as well (even Markdown!).
  • Helpers API allows for extensibility and meta programming.
  • Works in the browser or in node.
  • Comes with a Jade-inspired layout engine (block, include, extend, append/prepend), which even works in the browser.

Syntax Example

<p>How are you @model.name? Today is a sunny day on the planet Gunsmoke.</p>

<ul class="@(model.active ? 'highlight' : '')">
	@model.forEach(function(m){
		<li>@m.name</li>
	})
</ul>

Quick Start

nodejs

var vash = require('vash');
var tpl = vash.compile('<p>I am a @model.t!</p>');

var out = tpl({ t: 'template' });
// <p>I am a template!</p>

express

Check out vash-express-example for a full example of hooking up vash as a view engine for express 3. But it's basically as simple as:

var express = require('express');

var app = express();
app.set('view engine', 'vash');

More information is also available in the Layout Helpers sections.

Browser - Vanilla

<script type="text/javascript" src="build/vash.js"></script>

var tpl = vash.compile( '<p>I am a @model.t!</p>' );
document.querySelector('#content').innerHTML = tpl({ t: 'template' });

But you should probably be precompiling your templates. See Precompiling Templates for more info. Then you can just include the Vash runtime instead of the entire compiler.

Browser - Browserify et al

Just require Vash, and compile. If you want something fancier, try vashify! Then you can directly require any .vash file and it will be resolved as compiled template:

var tpl = require('my-awesome-template.vash');
document.querySelector('#content').innerHTML = tpl({ t: 'template' });

Browser - RequireJS

RequireJS support has been recently dropped. However Vash does support CJS environments, so as long as you configure RequireJS to consume Vash as a CJS project (including node_modules resolution), everything should work.

Playground

Vash now has a playground of sorts at CodePen.io. It uses the current version of vash.js from the build folder. Fork it to test your own template ideas!

Syntax

For the following examples, assume a model is passed into the compiled function. If a model is explicitly defined, it will appear as:

// model = { what: 'hello!' }

The Transition Character: @

Vash uses the @ symbol to transition between code and markup. To escape and print a literal @, use a double @, like this: @@.

Expressions

The most basic usage of Vash is an implicit expression. Vash is smart enough to know what's valid JS and what's not, and can usually do what you want it to do. An expression is an @ followed by a valid JS identifier. This is then interpolated automatically.

input:

// model = { what: 'hello!' }
<p>@what</p>

output:

<p>hello!</p>

The model comment is just to show that the object passed into the compiled template contains a key that matches the expression.

To allow for the fastest render time possible, Vash by default requires the model to be addressed explicitly. This is to avoid using a with statment in the compiled template, which is approximately 25 times slower. The above example then becomes:

input:

<p>@model.what</p>

output:

<p>hello!</p>

As you can see, the output is exactly the same. The name used to reference the model is configurable via vash.config.modelName. Typical values are model and it.

Advanced Expressions

Vash typically knows when an expression ends, even when the expression is complex. For example:

input:

<p>@model.what().who[2]('are you sure')('yes, it\'s ok')( model.complex ? 'FULL POWER' : '' )</p>

This will work just fine, assuming you have a model that actually contains that complexity! I hope you don't, and if so, I feel bad.

Callbacks work as well:

input:

// model = ['a', 'b']
@model.forEach(function(item){
	<li>@item</li>
})

outputs:

<li>a</li><li>b</li>

Vash also knows the difference between JS dot notation and a period.

input:

// model = { description: 'living' }
<p>Plants are @model.description.</p>

output:

<p>Plants are living.</p>

And empty brackets, because they're not valid JS:

input:

// model = { formName: 'addresses' }
<input type="text" name="@model.formName[]" />

output:

<input type="text" name="addresses[]" />

Email addresses, to an extent, are fine as well. Vash makes a trade-off. It uses the following regex to validate an email address:

/^([a-zA-Z0-9.%]+@[a-zA-Z0-9.\-]+\.(?:ca|co\.uk|com|edu|net|org))\b/

Email addresses can actually contain many more valid characters, and are really hard to validate. Vash can handle a typical email address with ease:

input:

<a href="mailto:[email protected]">Email Me</a>

output:

<a href="mailto:[email protected]">Email Me</a>

If you have a complex email address that confuses Vash, then you should use an explicit expression instead.

Explicit Expressions

An explicit expression is simply an expression that, instead of being composed of @ and a valid JS identifier, is surrounded by parenthesis.

input:

<p>@(model.what)</p>

output:

<p>hello!</p>

Why would you ever need this? Perhaps you want to do something like:

input:

// model = { hasIceCream: true }
<p class="@( model.hasIceCream ? 'ice-cream' : '')">Ice Cream</p>

output:

<p class="ice-cream">Ice Cream</p>

You could even create an anonymous function.

input:

@(function(type){ return type + ' cream'; }('banana'))

output:

banana cream

As you can see, Vash does not require a model to be referenced, or even passed in.

Code Blocks

Sometimes, AGAINST ALL ODDS, a template may need some quick computation of values to avoid repeating yourself. Unlike expressions and explicit expressions, a code block does not directly output. To compare to PHP, expressions are like <?= $what ?>, while a code block is like <? $what = 'what' ?>.

A code block is simply @{ }.

input:

@{ var rideOn = 'shooting star'; }

output:

That's right, nothing! Here's a better example:

input:

@{
	var total = model.price + model.tax;
}

<p>Your total is: $@total</p>

output:

<p>Your total is: $2.70</p>

Anything is valid within a code block, such as function declarations or even something as complex as defining a prototype. You can also use markup within a code block, and it will behave as expected:

input:

@{ <p>This works!</p> }

output:

<p>This works!</p>

A code block just tells Vash, "expect the next stuff to be code until otherwise".

Keyword Blocks

Vash is aware of keywords, and will open a code block automatically for you.

input:

// model = { type: 'banana' }
@if(model.type){
	<p>I'm a @model.type!</p>
} else if(model.name){
	<p>My name is @model.name.</p>
} else {
	<p>I DON'T KNOW WHO OR WHAT I AM...</p>
}

output:

<p>I'm a banana!</p>

This also works for while, for, do, try/catch, with, switch, function, and other keywords.

You don't even need to worry about whitespace or newlines:

input:

// model = 1
@switch(model){case 1:<p></p>break;case 2:<b></b>break;}

output:

<p></p>

Comments

Vash also supports comments that are not compiled into the template. These are delineated with @* and *@

input:

@* I am a comment that extends
over multiple lines *@
<p>BANANA!</p>

output:

<p>BANANA!</p>

HTML Escaping

By default, Vash escapes any HTML-like values before outputting them.

input:

// model = { what: '<img />' }
<p>@model.what</p>

output:

<p>&lt;img /&gt;</p>

If you are sure that you trust the content and/or need to display HTML-like values, you can escape the HTML escaping via a call to Vash's helper system: html.raw.

input:

// model = { what: '<img />' }
<p>@html.raw(model.what)</p>

output:

<p><img /></p>

This behavior can be disabled using vash.config.htmlEscape.

Explicit Markup

Sometimes you may wish to tell Vash that what you're typing is markup or content, as opposed to code. Take the following example:

input:

// model = ['a']
@model.forEach(function(item){
	this should be content @item
})

output:

(Error when compiling)

In this situation, you have two options. The first is the @: (at colon) escape. It tells Vash that until it sees a newline, treat the input as content, not code.

input:

// model = ['a']
@model.forEach(function(item){
	@: this should be content @item
})

output:

this should be content a

The other option, in the event that more than one line is needed, is by using an imaginary HTML tag named <text>. When Vash sees this tag, it switches to content mode, and discards the tag. This means that the tag will never be output.

input:

// model = ['Indeed!']
@model.forEach(function(item){
	<text>
		This is some longer content that you
		apparently wanted on multiple lines,
		multiple times! @item
	</text>
})

output:

This is some longer content that you
apparently wanted on multiple lines,
multiple times! Indeed!

Configuration

Vash has a few compilation options that are configurable either by setting the relevant value in vash.config or by passing in an object with that key/value to vash.compile, vash.compileBatch, or vash.compileHelper.

For example:

vash.config.debug = true;

Is the global version of:

vash.compile('<p>My tpl</p>', { debug: true });

vash.config.useWith

Default: false

If useWith is set to true, then Vash will wrap a with block around the contents of the compiled function.

// vash.config.useWith == true
<li>@description</li>

vs

// vash.config.useWith == false
<li>@model.description</li>

Rendering is the same regardless:

tpl( { description: 'I am a banana!' } );
// outputs:
// <li>I'm a banana!</li>

Tech note: using a with block comes at a severe performance penalty (at least 25x slower!).

vash.config.modelName

Default: 'model'

If vash.config.useWith is false (default), then this property is used to determine what the name of the default free variable will be. Example:

// vash.config.useWith == false
<li>@model.description</li>

vs

// vash.config.useWith == false
// vash.config.modelName == 'whatwhat'
<li>@whatwhat.description</li>

Again, rendering is the same regardless:

tpl( { description: 'I am a banana!' } );
// outputs:
// <li>I'm a banana!</li>

A common alternative to model is it.

vash.config.helpersName

Default: 'html'

Determines the name of the free variable through which registered helper methods can be reached. Example:

<li>@html.raw(model.description)</li>

vs

// vash.config.helpersName == "help";
<li>@help.raw(model.description)</li>

Again, rendering is the same regardless:

tpl( { description : '<strong>Raw</strong> content!' } );
// outputs:
// <li><strong>Raw</strong> content!</li>

vash.config.htmlEscape

Default: true

As of version 0.4x, Vash automatically HTML encodes values generated by an explicit or implicit expression. To disable this behavior, set htmlEscape to false. For an more in depth example, see HTML Escaping.

If a value should not be escaped, simply wrap it in a call to vash.helpers.raw.

vash.config.debug

Default: true

By default, templates are compiled with extensive debugging information, so if an error is thrown while rendering a template (not compiling), exact location (line, character) information can be given.

Using the following template:

<p></p>

A template with debug set to true (default):

function anonymous(model,html,__vopts,vash) {
	try {
		var __vbuffer = html.buffer;
		html.options = __vopts;
		model = model || {};
		html.vl = 1, html.vc = 0;
		__vbuffer.push('<p>');
		html.vl = 1, html.vc = 3;
		__vbuffer.push('</p>');
		html.vl = 1, html.vc = 7;
		__vbuffer.push('\n');
		(__vopts && __vopts.onRenderEnd && __vopts.onRenderEnd(null, html));
		return (__vopts && __vopts.asContext)
			? html
			: html.toString();
	} catch( e ){
		html.reportError( e, html.vl, html.vc, "<p></p>!LB!" );
	}
}

And that same template with debug set to false:

function anonymous(model,html,__vopts,vash) {
	var __vbuffer = html.buffer;
	html.options = __vopts;
	model = model || {};
	__vbuffer.push('<p></p>\n');
	(__vopts && __vopts.onRenderEnd && __vopts.onRenderEnd(null, html));
	return (__vopts && __vopts.asContext)
		? html
		: html.toString();
}

As you can see, the difference, especially in code size and instruction size is significant. For production apps, templates should be precompiled with debug as false.

vash.config.debugParser

Default: false

Vash's parser will output useful debugging infomation if debugParser is true:

  • Tokens and what mode they were processed as
  • A textual representation of the fully parsed AST

vash.config.debugCompiler

Default: false

Vash's compiler will output useful debugging information if debugCompiler is true:

vash.config.simple

Default: false

If true, the template is compiled in "fast path" mode. This disables several advanced features for the sake of speed:

While standard Vash templates are definitely not slow, using true for this option decreases render time by 15% - 25% depending on the size of the template.

vash-benchgraph can be used to show the speed increase:

node benches.js --tinclude 004.vash,007.vash --vinclude '0.6.2-2482' --chart vashv,ops

vash.config.favorText

Default: false

When Vash encounters text that directly follows an opening brace of a block, it assumes that unless it encounters an HTML tag, the text is JS code. For example:

@it.forEach(function(a){
	var b = a; // vash assumes this line is code
})

When favorText is set to true, Vash will instead assume that most things are content (not code) unless it's very explicit.

@it.forEach(function(a){
	var b = a; // vash.config.favorText assumes this line is content
})

This option is EXPERIMENTAL, and should be treated as such. It allows Vash to be used in a context like Markdown, where HTML tags, which typically help Vash tell the difference between code and content, are rare.

Template Options

These options concern rendering a template, after it has already been compiled. For options related to compiling templates, see Configuration.

The compiled templates themselves have three signatures.

tpl(model) -> string

The most basic form accepts a single argument, model, that can be any value: Number, Boolean, Object, Array, undefined, null, etc. It returns the rendered template as a string.

tpl(model, function(){}) -> string

The second form accepts a function callback as its second parameter, which is called onRenderEnd (see below).

tpl(model, options) -> string

The third form allows for options in addition to onRenderEnd. There are two options that can affect a template while rendering:

asContext

tpl(model, { asContext: true }) -> vash.helpers.constructor

This option tells the template that instead of returning a string, it should return the "render context", otherwise known as an instance of vash.helpers.constructor (Helper System).

onRenderEnd

tpl(model, { onRenderEnd: function(){} }) -> string

This option is effectively a callback for once primary execution of the rendering template has finished. The arguments passed to the callback are: ( err, html ), where err is always null (for now), and html is the render context (instance of vash.helpers.constructor). This callback is not required, and is only called if defined (and has no default definition). The Layout Helpers use this to know when all includes, prepends, appends, blocks, and extend calls have finished.

onRenderEnd can also be defined as a property of the model:

var model = { hey: 'what', onRenderEnd: function(err, ctx){ ... } }

Helper System

Vash's primary point of expandability lies in its Helper API. When a template is rendering, there is a free variable avaiable. This variable is, by default, named html. This name can be changed with the vash.config.helpersName option. html is an instance of the prototype that is attached to vash.helpers. It's a bit confusing, but this is how it kind of works:

var Helpers = function(){}
vash.helpers = Helpers.prototype;
vash.helpers.constructor = Helpers;

What this means is that any function that is attached to vash.helpers is available within a rendering template via html. For example:

// defined in a JS file or script tag somewhere
vash.helpers.echo = function(arg){ return arg; }

input:

<p>@html.echo('hello!')</p>

output:

<p>hello!</p>

Here is a simple helper that converts text like "This is a holdup!" to "this-is-a-holdup":

vash.helpers.mdHref = function(text){
	return text
		.toLowerCase()
		.replace(/[^a-zA-Z0-9-_]+/g, '-')
		.replace(/^-+|\n|-+$/g, '');
}

Notice how it's just JavaScript. Within a template, it could be accessed via html.mdHref("This is a holdup!").

Built-in Helpers

vash.helpers.raw

Available as html.raw within an executing template. By default, all content that passes from a model to a template is HTML encoded. In the event that the text is trusted (or is already encoded), wrap the text in this function. For an example, see HTML Escaping;

vash.helpers.escape

Available as html.escape within an executing template, this is the method Vash uses to HTML encode model values. It can also be used manually.

vash.helpers.tplcache

The tplcache is just that, a place to put a global index of templates. This is used primarily for the more "view engine" aspects that Vash provides, as well as a default location for precompiled templates using vash(1).

Layout Helpers

Vash provides a relatively simple but powerful view engine whose API is borrowed directly from Jade. Below is the API, but an example can be found at vash-express-example.

Callbacks are used to maintain compatibility with typical JS syntax.

When running in nodejs and using express, Vash will automatically resolve and load templates using the same conventions as express itself, specifically app.engine. When in the browser, Vash uses the same rules, but looks in vash.helpers.tplcache instead.

vash.helpers.extend

vash.helpers.extend(parent_path, cb)

This is Vash's main form of inheritance for view templates. parent_path is the location or name of the template to be extended.

A template can define various locations in itself that can be overridden or added to. In addition, a template that calls extend can even be extended itself!

In the following example, this template extends another named layout.vash. Layout.vash defines an empty block named 'content', which is overrided in this example.

@html.extend('layout', function(model){
	@html.block('content', function(model){
		<h1 class="name">Welcome to </h1>
	})
})

Tech note: due to the way JS scoping works, the model parameter of the cb function must be explicitely defined as above if it is referenced in the content. This may change in a future version of Vash.

vash.helpers.block

vash.helpers.block(name)

A block is essentially a placeholder within a template that can be overridden via another call to vash.helpers.block, or modified using vash.helpers.append and vash.helpers.prepend.

vash.helpers.block(name, cb)

If cb is defined, then it becomes default content for the block. The eventual contents of the block can still be overridden by a subsequent call to vash.helpers.block using the same name value, either within the current template (silly) or in a template that extends this one using vash.helpers.extend. If vash.helpers.append or vash.helpers.prepend are later called, their content is added to the content defined in cb.

@html.block('main', function(model){
	<p>Hello, I'm default content. It's nice to meet you.</p>
})

Tech note: due to the way JS scoping works, the model parameter of the cb function must be explicitely defined as above if it is referenced in the content. This may change in a future version of Vash.

vash.helpers.append

vash.helpers.append(name, cb)

vash.helpers.append is a way to control the content of a block from within an extending template. In this way, it allows templates to invert control over content "above" them.

An example is a navigation area. Perhaps there is a default navigation list that templates can add to:

// layout.vash
<ul>
@html.block('main-nav', function(model){
	<li><a href="/">Home</a></li>
})
</ul>

// another.vash
@html.extend('layout', function(model){
	@html.append('main-nav', function(){
		<li><a href="/another">Another Link</a></li>
	})
})

This would output when fully rendered:

<li><a href="/">Home</a></li>
<li><a href="/another">Another Link</a></li>

Tech note: due to the way JS scoping works, the model parameter of the cb function must be explicitely defined as above if it is referenced in the content. This may change in a future version of Vash.

vash.helpers.prepend

vash.helpers.prepend(name, cb)

vash.helpers.prepend behaves nearly the same as vash.helpers.append except that it places content at the beginning of a block instead of at the end. The previous example, if prepend were substituted for append, would render as:

<li><a href="/another">Another Link</a></li>
<li><a href="/">Home</a></li>

Tech note: due to the way JS scoping works, the model parameter of the cb function must be explicitely defined as above if it is referenced in the content. This may change in a future version of Vash.

vash.helpers.include

vash.helpers.include(name, model)

This grabs the template name and executes it using model as the... model. vash.helpers.include is used to literally include the contents of another template. It is analogous to a "partial" in other view engines. Except that there is a hidden power here... as included templates share the same "view engine scope" as other templates, and can thus call all of the layout helper functions, and it will just work. Thus, a block within an included template can append to a block defined in a parent. It can even use vash.helpers.extend!

Compiled Helpers

A relatively new feature in Vash (added in 0.6), compiled helpers are a bit meta. They allow a developer to write a helper using Vash syntax instead of the manual buffer API. The below buffer API example imgfigure could be rewritten:

vash.helpers.imgfigure = function(path, caption){
	vash.helpers.imgfigure.figcount = vash.helpers.imgfigure.figcount || 0;
	var figcount = vash.helpers.imgfigure.figcount;
	<figure id="fig-@(figcount++)">
		<img src="@path" alt="@caption" />
		<figcaption>Fig. @figcount: @caption</figcaption>
	</figure>
}

There are two ways to compile a helper. The first is using vash.compileHelper, the second is using vash(1)'s --helper option.

Buffer API

Within a helper (not a template), this refers to the current Helpers instance. Every instance has a Buffer that has methods to help easily add, subtract, or mark content put there by the rendering template.

Adding to the buffer:

vash.helpers.imgfigure = function(path, caption){
	vash.helpers.imgfigure.figcount = vash.helpers.imgfigure.figcount || 0;
	var figcount = vash.helpers.imgfigure.figcount++;
	this.buffer.push('<figure id="fig-' + figcount + '">';
	this.buffer.push('<img src="' + path + '" alt="' + caption + '" />';
	this.buffer.push('<figcaption>Fig.' + figcount + ':' + caption + '</figcaption>';
	this.buffer.push('</figure>');
}

Here is a more advanced example, which is contained within Vash:

vash.helpers.highlight = function(lang, cb){

	// context (this) is an instance of Helpers, aka a rendering context

	// mark() returns an internal `Mark` object
	// Use it to easily capture output...
	var startMark = this.buffer.mark();

	// cb() is simply a user-defined function. It could (and should) contain
	// buffer additions, so we call it...
	cb();

	// ... and then use fromMark() to grab the output added by cb().
	var cbOutLines = this.buffer.fromMark(startMark);

	// The internal buffer should now be back to where it was before this
	// helper started, and the output is completely contained within cbOutLines.

	this.buffer.push( '<pre><code>' );

	if( helpers.config.highlighter ){
		this.buffer.push( helpers.config.highlighter(lang, cbOutLines.join('')).value );
	} else {
		this.buffer.push( cbOutLines );
	}

	this.buffer.push( '</code></pre>' );

	// returning is allowed, but could cause surprising effects. A return
	// value will be directly added to the output directly following the above.
}

A Mark is effectively a placeholder that can be used to literally mark the rendered content, and later do something with that mark. Possibilities include inserting content at the mark, deleting content that follows a mark, and more. It is an internal constructor that is only ever created through the Buffer#mark method within a helper. Examples of Mark usage can be found in the layout helpers code.

TODO: Explain the Buffer methods:

  • mark
  • fromMark
  • spliceMark
  • empty
  • push
  • pushConcat
  • indexOf
  • lastIndexOf
  • splice
  • index
  • flush
  • toString
  • toHtmlString

Precompiling Templates

To save both processing time (compiling templates is not trivial) as well as bandwidth (no need to send the whole compiler to the client), Vash supports precompilation of templates. Any template that Vash compiles is given a method called toClientString. This method returns a string that can either be evaled or sent to a remote client. For example:

<p>Hello</p>

Compiles to a function:

function anonymous(model,html,__vopts,vash) {
	var __vbuffer = html.buffer;
	html.options = __vopts;
	model = model || {};
	__vbuffer.push('<p></p>\n');
	(__vopts && __vopts.onRenderEnd && __vopts.onRenderEnd(null, html));
	return (__vopts && __vopts.asContext)
		? html
		: html.toString();
}

If toClientString is called on that function, the following is returned:

vash.link( function anonymous(model,html,__vopts,vash) {
	var __vbuffer = html.buffer;
	html.options = __vopts;
	model = model || {};
	__vbuffer.push('<p></p>\n');
	(__vopts && __vopts.onRenderEnd && __vopts.onRenderEnd(null, html));
	return (__vopts && __vopts.asContext)
		? html
		: html.toString();
}, {"simple":false,"modelName":"model","helpersName":"html"} )

This string could then be sent to the client (probably prefixed with something like TPLCACHE["name-of-template"] = ). vash(1) helps to automate this easily.

Note: this assumes that vash is available globally. A future version of Vash will hopefully remove this assumption.

Vash Runtime (Browser)

The Vash runtime is a set of functions that every executing template expects to be available. The runtime is automatically packaged with full Vash builds. However, if only precompiled templates are sent to the browser, then only the runtime must be sent. The runtime includes all helpers and a few standard functions, such as HTML Escaping.

There are two runtime builds:

  • vash-runtime.js: This is the basic runtime. It contains everything a standard Vash template needs to execute.
  • vash-runtime-all.js: This also includes the Layout Helpers. It is roughly twice as large as vash-runtime.js. Unless you're using the Vash view system in the browser, this is probably not necessary.

If you're in a Browserify-like environemnt, you should be able to:

var vashruntime = require('vash/runtime');

..and have access to the Runtime API.

Compile-time API

vash.compile

vash.compile(str_template, opt_options) -> Function

At its core, Vash has a compile function that accepts a string and options, and returns a function, otherwise known as a compiled template. That function, when called with a parameter (otherwise known as a model), will use that parameter to fill in the template. A model can be any value, including undefined, objects, arrays, strings, and booleans.

vash.compileHelper

vash.compileHelper(str_template, opt_options) -> Object

See Compiled Helpers for more detail.

vash.compileBatch

vash.compileBatch(str_template, opt_options) -> Object

This function can take a single string containing many named templates, and output an object containing the compiled versions of those templates. A "named template" is of the form (similar to a sourceURL):

//@batch = div
<div>@model</div>\n'

//@batch = a
<a>@model</a>'

This example contains two named templates, "div" and "a". If this example were passed as a single string to compileBatch:

var tpls = vash.compileBatch(theTplString);

One could be called:

tpls.div('yes!');
// returns: <div>yes!</div>

This is meant as a convenience function for developers. Putting each template in a separate file can get old, especially if a template is small. Instead, templates can be grouped together. The object returned also has a custom toClientString function, which serializes each template in the object automatically.

Aside from the newline following the "name" of the template, whitespace is ignored:

//@              batch = div
//@batch=div
//         @batch =div

Each is treated the same.

Runtime API

vash.link

vash.link(str_tpl, options) -> Function
vash.link(func_tpl, options) -> Function

This is primarily an internal function, and has relatively complex behavioral differences depending on what options are passed in. It takes either a decompiled string function or function instance and "links" it by wrapping it in a closure that provides access to Vash's runtime functions. It also sets up things like toClientString and toString. It makes precompiled functions possible. As a developer working on Vash, it's best to take a look at the source itself.

vash.lookup

vash.lookup(str_path) -> Function

Attempts to grab a template from vash.helpers.tplcache[str_path], and throws an exception if it is not found.

vash.lookup(str_path, model) -> Function

If model is passed and the template is found, the template is automatically executed and returned using model as the model.

vash.install

vash.install accepts a few signatures:

vash.install(str_path, func_tpl) -> func_tpl

"Saves" the template at vash.helpers.tplcache[str_path].

vash.install(str_path, str_tpl) -> func_tpl

If vash.compile is available (meaning the entire compiler is available, not just the runtime), then the string is automatically compiled. and saved at vash.helpers.tplcache[str_path].

vash.install(obj) -> obj

If an object containing string keys pointing at template functions is passed, then the object's keys are used as the keys for vash.helpers.tplcache. This is especially useful when using vash.compileBatch, as the result can be directly passed.

vash.uninstall

vash.uninstall(str_path) -> bool

Deletes the key named str_path from vash.helpers.tplcache.

vash.uninstall(func_tpl) -> bool

Loops through all templates in vash.helpers.tplcache, and if a strict equality is successful, deletes that reference.

vash(1)

Vash also includes a commandline tool that enables easy integration of templates into a unix toolchain. For example, to compile this documentation, the following command is used:

bin/vash <README2.vash --render --helpers <(bin/vash <docs/helpers/* --helper) > README2.md

This first grabs all files in docs/helpers/, and compiles them as Vash helpers using the --helper option. These compiled helpers are then fed via a temporary named pipe into the --helpers option, which accepts a file. This option user the file (temporary, in this case) as helpers, and they are added to the rendering context's prototype (see Helper System. Next, README2.vash is fed into vash(1), which is told to both compile the input as a template, and render it immediately, using the --render option. Granted, this is not how bash actually handles it, but this explanation will suffice.

In short, this loads and compiles helpers necessary for this document, grabs the file, and renders the whole thing as plain markdown.

vash(1) has many options:

-h, --help                          output usage information
-t, --target-namespace <namespace>  Assign template to a <namespace>. Recommended is `vash.helpers.tplcache` for view engine compatibility
-p, --property-name [name]          Assign template to property named [name]. Defaults to filename, and requires --target-namespace.
-f, --file <file>                   Compile the template in <file>
-j, --json <json>                   Pass options to the Vash compiler. See docs for valid options.
-o, --out <path>                    Write template into <path> directory
-u, --uglify                        Uglify the template, safely
-a, --no-autolink                   Wrap each template in `vash.link`.
-r, --render [json]                 Render the template using [json] as the model. If [json] is not valid json, assume a filename and load those contents as json.
-s, --separator [separator]         Templates are auto-named by concatenating the file path with [separator]
--helper                            Assume the input is a to-be-compiled helper
--helpers <file>                    Execute these compiled helpers

Some of the options are explained in greater detail below.

Installation

vash(1) comes with Vash, so it will always be within node_modules/vash/bin/. However, a global install is also supported, which can be accomplished via:

npm install -g vash

--target-namespace

Assigns the compiled template to a specific "namespace". This value only supports simple namespaces, such as blah.who.what.something.

Using this option while piping into vash(1) REQUIRES --property-name to also be specified.

Example:

$ echo 'function(){}' | bin/vash \
--target-namespace "vash.helpers.tplcache" \
--property-name 'myTpl'

vash = vash || {};
vash.helpers = vash.helpers || {};
vash.helpers.tplcache = vash.helpers.tplcache || {};
vash.helpers.tplcache["myTpl"]=vash.link( ... )

--property-name

Specifies what name to use when assigning the compiled template. Defaults to the filename specified with --file. If content is piped into vash(1), then this option is MANDATORY.

--helper

This instructs vash(1) to call vash.compileHelper instead of vash.compile, and assumes the input is a template that is meant to be a compiled helper.

An empty helper:

$ echo 'vash.helpers.who = function(){}' | bin/vash --helper --json '{"debug":false}'

vash.link( function anonymous() {
var __vbuffer = this.buffer;
var model = this.model;
var html = this;
{}
}, {"simple":false,"modelName":"model","helpersName":"html","args":[""],"asHelper":"who"} )

And without --helper, vash(1) just outputs an empty template:

$ echo 'vash.helpers.who = function(){}' | bin/vash --json '{"debug":false}'

vash.link( function anonymous(model,html,__vopts,vash) {
var __vbuffer = html.buffer;
html.options = __vopts;
model = model || {};
__vbuffer.push('vash.helpers.who = function(){}\n');
(__vopts && __vopts.onRenderEnd && __vopts.onRenderEnd(null, html));
return (__vopts && __vopts.asContext)
  ? html
  : html.toString();
}, {"simple":false,"modelName":"model","helpersName":"html"} )

Contributing / Building

Please see CONTRIBUTING.md. In general, if you want something that Vash doesn't have, file a ticket. Pull Requests are also always welcome!

Getting Help

File a ticket! Or hit me up on Twitter: @KirbySaysHi

Special Thanks

Extreme thanks goes to TJ Holowaychuck and his template engine Jade. It was the original inspiration for Vash's lexer, Layout Helpers, and error reporting, and has been a constant source of inspiration and motivation.

Some of the techniques Vash's compiler uses were directly inspired from doT.

Dev doc styling (gfm.css) from https://gist.github.com/andyferra/2554919.

And of course to Vash's contributors.

License

MIT


  1. Razor syntax was developed at Microsoft, and typically refers to their Razor View Engine, which ships with ASP.NET MVC 3 and above. In this document, "Razor" will refer to the Razor View Engine, while "syntax" or "Vash syntax" refers to Vash's implementation. ↩
  2. This document starts off as a [Vash template][] that is then compiled and rendered via [vash(1)][] into markdown! It uses several custom helpers that are not shipped with Vash, but are of course available [for perusal][]. They include things like these footnotes and an autogenerated and linked table of contents. ↩
  3. </ol>
    

More Repositories

1

multithreaded-game-example

example of multithreading a game using JS
JavaScript
58
star
2

pocket-ces

Component entity system, probably for game development.
JavaScript
42
star
3

pocket-physics

Verlet physics extracted from pocket-ces demos
TypeScript
34
star
4

ghembedder

Embed any source file (or specific lines) from any github repo in your webpage with no server-side dependencies.
JavaScript
26
star
5

tap-browser-color

A simple reporter for tap/tape that makes the body tag yellow/red/green if pending/failing/passing.
JavaScript
18
star
6

CT

A JavaScript RPG engine, with Chrono Trigger qualities...
JavaScript
15
star
7

vash-express-example

An example express app built using Vash
JavaScript
14
star
8

image-juggler

Juggle pixel data between different web containers, like <img>, <canvas>, ImageData, File, ArrayBuffer, Blob, Array, etc.
JavaScript
12
star
9

broad-phase-bng

Materials for an article on Broad Phase Collision Detection for BuildNewGames
JavaScript
11
star
10

node-ddd-jquery

de-bowerified, de-amdified, de-gruntified... jQuery.
JavaScript
11
star
11

soundtouch-ts

A TypeScript conversion of SoundTouchJS
TypeScript
9
star
12

oli-demos

demos from http://members.multimania.co.uk/olivierrenault/, possibly eventually ported to JavaScript.
C++
9
star
13

HSHG

Hierarchical Spatial Hash Grid, used for fast spatial hashing in 2D and 3D of any sized object in infinite space
JavaScript
8
star
14

csp-ksh

Playing with CSP without generators
JavaScript
6
star
15

node-quickserve

A quick command line utility that instantly serves static content from a given directory
JavaScript
6
star
16

game-bucket

Common game utilities + rollup + typescript, suitable for a small game competition. Like JS13k! http://js13kgames.com/
TypeScript
5
star
17

revisit-ncolorpalette-service

Color cycle and palettize an image according to the revisit.link spec!
JavaScript
5
star
18

jekyll.tmbundle

A very basic Jekyll textmate bundle.
5
star
19

ncolorpalette

Drop a photo in, get a n-color-ized version of it
JavaScript
4
star
20

soldat.js

soldat, in javascript, using websockets
JavaScript
4
star
21

silly-redux-object-pooling-benchmarks

JS object pooling benchmarks! Might be irrelevant!
JavaScript
4
star
22

ZapThwap

An attempt at a position-based physics engine, along with a few helpers.
JavaScript
4
star
23

chipmunk-impactjs

ImpactJS plugin allowing for Chipmunk-JS entities
JavaScript
3
star
24

jsbook

JavaScript
3
star
25

ncolorpalette-clusterer

Group an array of pixels using k-means clustering as efficiently as possible.
JavaScript
3
star
26

thwap.js

A basic JavaScript physics engine
JavaScript
3
star
27

punch-bench

Benchmark web code with nuance!
TypeScript
3
star
28

yoda-stories-docker

Run Yoda Stories in a Docker container on Mac or Linux
Shell
3
star
29

filtration10k

a game, aiming to be under 10K
JavaScript
2
star
30

grow-plants

Fooling around with a clicky plants game
JavaScript
2
star
31

idier

Make your projects folder tidier
TypeScript
2
star
32

ID.js

An input detection utility for JavaScript games
JavaScript
2
star
33

imgswap-golf

playing around with image pixel swapping
JavaScript
2
star
34

nodecmd

Accept input, then output the result given a JS function
JavaScript
1
star
35

twitter-sequencer

Arrange tweets in any order, share it, and even view them as a paragraph
JavaScript
1
star
36

night-shift-barista

A 2016 JS13k entry
JavaScript
1
star
37

kirbysayshi.github.com

Stuff for people to see.
HTML
1
star
38

innerDOM

A JavaScript implementation of the HTML Fragment Serialization Algorithm.
JavaScript
1
star
39

Ops

trying to make vector math look a little better in JS
JavaScript
1
star
40

ncolorpalette-palettes

Palettes (pixel datas) that power the ncolorpalette and friends
JavaScript
1
star
41

ts-run

Run TypeScript in node directly, via Babel.
JavaScript
1
star
42

action-stations

nodejs script to run input data through checks, which fire alerts through transports (sms, email, etc).
JavaScript
1
star
43

instant-public-folder

Give a folder a public URL to enable no-frills https file downloading
JavaScript
1
star
44

cut-ordered

Like a `cut(1)` that allows reordering of fields.
JavaScript
1
star
45

allocated-dynamic-typedarray

A typed array whose length() can change.
JavaScript
1
star
46

HookShot

A LinkedList in JavaScript
JavaScript
1
star
47

flave.js

JavaScript port of the Flave AS3 physics engine
JavaScript
1
star
48

synaesthesia-experiments

JavaScript
1
star
49

SignalSlot

Signals and Slots, for PHP
PHP
1
star
50

transit-authority

Define state transitions as callbacks.
JavaScript
1
star
51

proustiary

Answer some questions about yourself
JavaScript
1
star
52

cutout

a jQuery plugin that highlights a single element on the page
1
star
53

MM.js

A quick marks (frames) per second counter
JavaScript
1
star
54

Util

General small scripts and other things too small to justify their own repo.
Shell
1
star
55

dotvim

my sad attempt at a cross-platform (g)vim config for myself
Vim Script
1
star
56

kirbysayshi

Stuff
1
star
57

bespin-presentation

Slides and files from a presentation I gave about Mozilla Labs' Bespin
JavaScript
1
star
58

tetris-prng

A Fibonacci linear feedback shift register used as a PRNG (Pseudo-random Number Generator), as in NES Tetris
JavaScript
1
star
59

best-effort-concurrent-cache

Make a best effort to offer a simple filesystem-based cache for concurrent access from multiple processes.
JavaScript
1
star
60

sickofit

HTML canvas boilerplate
JavaScript
1
star