vash
Version:
Razor syntax for JS templating
115 lines (92 loc) • 3.27 kB
JavaScript
var slice = Array.prototype.slice
, reHelperFuncHead = /vash\.helpers\.([^= ]+?)\s*=\s*function([^(]*?)\(([^)]*?)\)\s*{/
, reHelperFuncTail = /\}$/
, reBatchSeparator = /^\/\/\s*@\s*batch\s*=\s*(.*?)$/
// The logic for compiling a giant batch of templates or several
// helpers is nearly exactly the same. The only difference is the
// actual compilation method called, and the regular expression that
// determines how the giant string is split into named, uncompiled
// template strings.
module.exports = function compile(type, compile, str, options){
var separator = type === 'helper'
? reHelperFuncHead
: reBatchSeparator;
var tpls = splitByNamedTpl(separator, str, function(ma, name){
return name.replace(/^\s+|\s+$/, '');
}, type === 'helper' ? true : false);
if(tpls){
Object.keys(tpls).forEach(function(path){
tpls[path] = type === 'helper'
? compileSingleHelper(compile, tpls[path], options)
: compile('@{' + tpls[path] + '}', options);
});
tpls.toClientString = function(){
return Object.keys(tpls).reduce(function(prev, curr){
if(curr === 'toClientString'){
return prev;
}
return prev + tpls[curr].toClientString() + '\n';
}, '')
}
}
return tpls;
}
// Given a separator regex and a function to transform the regex result
// into a name, take a string, split it, and group the rejoined strings
// into an object.
// This is useful for taking a string, such as
//
// // tpl1
// what what
// and more
//
// // tpl2
// what what again
//
// and returning:
//
// {
// tpl1: 'what what\nand more\n',
// tpl2: 'what what again'
// }
var splitByNamedTpl = function(reSeparator, markup, resultHandler, keepSeparator){
var lines = markup.split(/[\n\r]/g)
,tpls = {}
,paths = []
,currentPath = ''
lines.forEach(function(line, i){
var pathResult = reSeparator.exec(line)
,handlerResult = pathResult ? resultHandler.apply(pathResult, pathResult) : null
if(handlerResult){
currentPath = handlerResult;
tpls[currentPath] = [];
}
if((!handlerResult || keepSeparator) && line){
tpls[currentPath].push(line);
}
});
Object.keys(tpls).forEach(function(key){
tpls[key] = tpls[key].join('\n');
})
return tpls;
}
var compileSingleHelper = function(compile, str, options){
options = options || {};
// replace leading/trailing spaces, and parse the function head
var def = str.replace(/^[\s\n\r]+|[\s\n\r]+$/, '').match(reHelperFuncHead)
// split the function arguments, kill all whitespace
,args = def[3].split(',').map(function(arg){ return arg.replace(' ', '') })
,name = def[1]
,body = str
.replace( reHelperFuncHead, '' )
.replace( reHelperFuncTail, '' )
// Wrap body in @{} to simulate it actually being inside a function
// definition, since we manually stripped it. Without this, statements
// such as `this.what = "what";` that are at the beginning of the body
// will be interpreted as markup.
body = '@{' + body + '}';
// `args` and `asHelper` inform `vash.compile/link` that this is a helper
options.args = args;
options.asHelper = name;
return compile(body, options);
}