Comparing version 0.7.12-1 to 0.8.0-beta
@@ -1,474 +0,525 @@ | ||
/** | ||
* Vash - JavaScript Template Parser, v0.7.12-1 | ||
* | ||
* https://github.com/kirbysayshi/vash | ||
* | ||
* Copyright (c) 2013 Andrew Petersen | ||
* MIT License (LICENSE) | ||
*/ | ||
void(0); // hack for https://github.com/mishoo/UglifyJS/issues/465 | ||
/*jshint strict:false, asi: false, laxcomma:true, laxbreak:true, boss:true, curly:true, node:true, browser:true, devel:true */ | ||
;(function(){ | ||
!function(e){if("object"==typeof exports)module.exports=e();else if("function"==typeof define&&define.amd)define(e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.vash=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){ | ||
vash = typeof vash === 'undefined' ? {} : vash; | ||
exports.context = function(input, lineno, columnno, linebreak) { | ||
linebreak = linebreak || '!LB!'; | ||
// only fully define if this is standalone | ||
if(!vash.compile){ | ||
if(typeof define === 'function' && define['amd']){ | ||
define(function(){ return vash }); // AMD | ||
} else if(typeof module === 'object' && module['exports']){ | ||
module['exports'] = vash; // NODEJS | ||
} else { | ||
window['vash'] = vash; // BROWSER | ||
} | ||
} | ||
var lines = input.split(linebreak) | ||
, contextSize = lineno === 0 && columnno === 0 ? lines.length - 1 : 3 | ||
, start = Math.max(0, lineno - contextSize) | ||
, end = Math.min(lines.length, lineno + contextSize); | ||
var helpers = vash['helpers']; | ||
return lines | ||
.slice(start, end) | ||
.map(function(line, i, all){ | ||
var curr = i + start + 1; | ||
var Helpers = function ( model ) { | ||
this.buffer = new Buffer(); | ||
this.model = model; | ||
this.options = null; // added at render time | ||
return (curr === lineno ? ' > ' : ' ') | ||
+ (curr < 10 ? ' ' : '') | ||
+ curr | ||
+ ' | ' | ||
+ line; | ||
}).join('\n'); | ||
} | ||
},{}],2:[function(_dereq_,module,exports){ | ||
module.exports={ | ||
"name": "vash", | ||
"description": "Razor syntax for JS templating", | ||
"version": "0.8.0-beta", | ||
"author": "Andrew Petersen <senofpeter@gmail.com>", | ||
"homepage": "https://github.com/kirbysayshi/vash", | ||
"bin": { | ||
"vash": "./bin/vash" | ||
}, | ||
"keywords": [ | ||
"razor", | ||
"parser", | ||
"template", | ||
"express" | ||
], | ||
"repository": { | ||
"type": "git", | ||
"url": "git://github.com/kirbysayshi/vash" | ||
}, | ||
"main": "index.js", | ||
"engines": { | ||
"node": ">= 0.8" | ||
}, | ||
"scripts": { | ||
"coverage": "VASHPATH=../../index.js VASHRUNTIMEPATH=../../runtime.js browserify -t envify -t coverify test/vows/vash.test.js | node | coverify", | ||
"build": "browserify index.js --standalone vash > build/vash.js && browserify --standalone vash runtime.js > build/vash-runtime.js && browserify --standalone vash --external fs --external path lib/helpers/index.js > build/vash-runtime-all.js", | ||
"test": "VASHPATH=../../index.js VASHRUNTIMEPATH=../../runtime.js vows test/vows/vash.*.js --spec", | ||
"docs": "scripts/docs.sh", | ||
"docs-dev": "scripts/docs-dev.sh" | ||
}, | ||
"dependencies": { | ||
"commander": "~1.1.1", | ||
"uglify-js": "1.0.6", | ||
"debug": "^0.7.4" | ||
}, | ||
"devDependencies": { | ||
"browserify": "^3.33.0", | ||
"coverify": "~1.0.6", | ||
"envify": "^1.2.1", | ||
"jshint": "0.8.0", | ||
"marked": "~0.2.8", | ||
"semver": "~1", | ||
"uglify-js": "^2.4.13", | ||
"vows": "^0.8.1" | ||
} | ||
} | ||
this.vl = 0; | ||
this.vc = 0; | ||
}; | ||
},{}],3:[function(_dereq_,module,exports){ | ||
vash['helpers'] | ||
= helpers | ||
= Helpers.prototype | ||
= { constructor: Helpers, config: {}, tplcache: {} }; | ||
var error = _dereq_('./lib/error'); | ||
var runtime = { | ||
version: _dereq_('./package.json').version | ||
}; | ||
// this allows a template to return the context, and coercion | ||
// will handle it | ||
helpers.toString = helpers.toHtmlString = function(){ | ||
// not calling buffer.toString() results in 2x speedup | ||
return this.buffer._vo.join('');//.toString(); | ||
} | ||
var helpers = runtime['helpers']; | ||
/////////////////////////////////////////////////////////////////////////// | ||
// HTML ESCAPING | ||
module.exports = runtime; | ||
var HTML_REGEX = /[&<>"'`]/g | ||
,HTML_REPLACER = function(match) { return HTML_CHARS[match]; } | ||
,HTML_CHARS = { | ||
"&": "&" | ||
,"<": "<" | ||
,">": ">" | ||
,'"': """ | ||
,"'": "'" | ||
,"`": "`" | ||
}; | ||
function Helpers( model ) { | ||
this.buffer = new Buffer(); | ||
this.model = model; | ||
this.options = null; // added at render time | ||
helpers['raw'] = function( val ) { | ||
var func = function() { return val; }; | ||
this.vl = 0; | ||
this.vc = 0; | ||
}; | ||
val = val != null ? val : ""; | ||
runtime['helpers'] | ||
= helpers | ||
= Helpers.prototype | ||
= { constructor: Helpers, config: {}, tplcache: {} }; | ||
return { | ||
toHtmlString: func | ||
,toString: func | ||
}; | ||
}; | ||
// this allows a template to return the context, and coercion | ||
// will handle it | ||
helpers.toString = helpers.toHtmlString = function(){ | ||
// not calling buffer.toString() results in 2x speedup | ||
return this.buffer._vo.join('');//.toString(); | ||
} | ||
helpers['escape'] = function( val ) { | ||
var func = function() { return val; }; | ||
/////////////////////////////////////////////////////////////////////////// | ||
// HTML ESCAPING | ||
val = val != null ? val : ""; | ||
var HTML_REGEX = /[&<>"'`]/g | ||
,HTML_REPLACER = function(match) { return HTML_CHARS[match]; } | ||
,HTML_CHARS = { | ||
"&": "&" | ||
,"<": "<" | ||
,">": ">" | ||
,'"': """ | ||
,"'": "'" | ||
,"`": "`" | ||
}; | ||
if ( typeof val.toHtmlString !== "function" ) { | ||
helpers['raw'] = function( val ) { | ||
var func = function() { return val; }; | ||
val = val.toString().replace( HTML_REGEX, HTML_REPLACER ); | ||
val = val != null ? val : ""; | ||
return { | ||
toHtmlString: func | ||
,toString: func | ||
}; | ||
} | ||
return { | ||
toHtmlString: func | ||
,toString: func | ||
}; | ||
}; | ||
return val; | ||
}; | ||
helpers['escape'] = function( val ) { | ||
var func = function() { return val; }; | ||
// HTML ESCAPING | ||
/////////////////////////////////////////////////////////////////////////// | ||
val = val != null ? val : ""; | ||
if ( typeof val.toHtmlString !== "function" ) { | ||
/////////////////////////////////////////////////////////////////////////// | ||
// BUFFER MANIPULATION | ||
// | ||
// These are to be used from within helpers, to allow for manipulation of | ||
// output in a sane manner. | ||
val = val.toString().replace( HTML_REGEX, HTML_REPLACER ); | ||
var Buffer = function() { | ||
this._vo = []; | ||
} | ||
return { | ||
toHtmlString: func | ||
,toString: func | ||
}; | ||
} | ||
Buffer.prototype.mark = function( debugName ) { | ||
var mark = new Mark( this, debugName ); | ||
mark.markedIndex = this._vo.length; | ||
this._vo.push( mark.uid ); | ||
return mark; | ||
}; | ||
return val; | ||
}; | ||
Buffer.prototype.fromMark = function( mark ) { | ||
var found = mark.findInBuffer(); | ||
// HTML ESCAPING | ||
/////////////////////////////////////////////////////////////////////////// | ||
if( found > -1 ){ | ||
// automatically destroy the mark from the buffer | ||
mark.destroy(); | ||
// `found` will still be valid for a manual splice | ||
return this._vo.splice( found, this._vo.length ); | ||
} | ||
return []; | ||
}; | ||
/////////////////////////////////////////////////////////////////////////// | ||
// BUFFER MANIPULATION | ||
// | ||
// These are to be used from within helpers, to allow for manipulation of | ||
// output in a sane manner. | ||
Buffer.prototype.spliceMark = function( mark, numToRemove, add ){ | ||
var found = mark.findInBuffer(); | ||
var Buffer = function() { | ||
this._vo = []; | ||
} | ||
if( found > -1 ){ | ||
mark.destroy(); | ||
arguments[0] = found; | ||
return this._vo.splice.apply( this._vo, arguments ); | ||
} | ||
Buffer.prototype.mark = function( debugName ) { | ||
var mark = new Mark( this, debugName ); | ||
mark.markedIndex = this._vo.length; | ||
this._vo.push( mark.uid ); | ||
return mark; | ||
}; | ||
return []; | ||
}; | ||
Buffer.prototype.fromMark = function( mark ) { | ||
var found = mark.findInBuffer(); | ||
Buffer.prototype.empty = function() { | ||
return this._vo.splice( 0, this._vo.length ); | ||
}; | ||
if( found > -1 ){ | ||
// automatically destroy the mark from the buffer | ||
mark.destroy(); | ||
// `found` will still be valid for a manual splice | ||
return this._vo.splice( found, this._vo.length ); | ||
} | ||
Buffer.prototype.push = function( buffer ) { | ||
return this._vo.push( buffer ); | ||
}; | ||
return []; | ||
}; | ||
Buffer.prototype.pushConcat = function( buffer ){ | ||
var buffers; | ||
if (Array.isArray(buffer)) { | ||
buffers = buffer; | ||
} else if ( arguments.length > 1 ) { | ||
buffers = Array.prototype.slice.call( arguments ); | ||
} else { | ||
buffers = [buffer]; | ||
} | ||
Buffer.prototype.spliceMark = function( mark, numToRemove, add ){ | ||
var found = mark.findInBuffer(); | ||
for (var i = 0; i < buffers.length; i++) { | ||
this._vo.push( buffers[i] ); | ||
} | ||
if( found > -1 ){ | ||
mark.destroy(); | ||
arguments[0] = found; | ||
return this._vo.splice.apply( this._vo, arguments ); | ||
} | ||
return this.__vo; | ||
} | ||
return []; | ||
}; | ||
Buffer.prototype.indexOf = function( str ){ | ||
Buffer.prototype.empty = function() { | ||
return this._vo.splice( 0, this._vo.length ); | ||
}; | ||
for( var i = 0; i < this._vo.length; i++ ){ | ||
if( | ||
( str.test && this._vo[i] && this._vo[i].search(str) > -1 ) | ||
|| this._vo[i] == str | ||
){ | ||
return i; | ||
} | ||
} | ||
Buffer.prototype.push = function( buffer ) { | ||
return this._vo.push( buffer ); | ||
}; | ||
return -1; | ||
} | ||
Buffer.prototype.pushConcat = function( buffer ){ | ||
var buffers; | ||
if (Array.isArray(buffer)) { | ||
buffers = buffer; | ||
} else if ( arguments.length > 1 ) { | ||
buffers = Array.prototype.slice.call( arguments ); | ||
} else { | ||
buffers = [buffer]; | ||
} | ||
Buffer.prototype.lastIndexOf = function( str ){ | ||
var i = this._vo.length; | ||
for (var i = 0; i < buffers.length; i++) { | ||
this._vo.push( buffers[i] ); | ||
} | ||
while( --i >= 0 ){ | ||
if( | ||
( str.test && this._vo[i] && this._vo[i].search(str) > -1 ) | ||
|| this._vo[i] == str | ||
){ | ||
return i; | ||
} | ||
} | ||
return this.__vo; | ||
} | ||
return -1; | ||
} | ||
Buffer.prototype.indexOf = function( str ){ | ||
Buffer.prototype.splice = function(){ | ||
return this._vo.splice.apply( this._vo, arguments ); | ||
} | ||
for( var i = 0; i < this._vo.length; i++ ){ | ||
if( | ||
( str.test && this._vo[i] && this._vo[i].search(str) > -1 ) | ||
|| this._vo[i] == str | ||
){ | ||
return i; | ||
} | ||
} | ||
Buffer.prototype.index = function( idx ){ | ||
return this._vo[ idx ]; | ||
} | ||
return -1; | ||
} | ||
Buffer.prototype.flush = function() { | ||
return this.empty().join( "" ); | ||
}; | ||
Buffer.prototype.lastIndexOf = function( str ){ | ||
var i = this._vo.length; | ||
Buffer.prototype.toString = Buffer.prototype.toHtmlString = function(){ | ||
// not using flush because then console.log( tpl() ) would artificially | ||
// affect the output | ||
return this._vo.join( "" ); | ||
} | ||
while( --i >= 0 ){ | ||
if( | ||
( str.test && this._vo[i] && this._vo[i].search(str) > -1 ) | ||
|| this._vo[i] == str | ||
){ | ||
return i; | ||
} | ||
} | ||
// BUFFER MANIPULATION | ||
/////////////////////////////////////////////////////////////////////////// | ||
return -1; | ||
} | ||
/////////////////////////////////////////////////////////////////////////// | ||
// MARKS | ||
// These can be used to manipulate the existing entries in the rendering | ||
// context. For an example, see the highlight helper. | ||
Buffer.prototype.splice = function(){ | ||
return this._vo.splice.apply( this._vo, arguments ); | ||
} | ||
var Mark = vash['Mark'] = function( buffer, debugName ){ | ||
this.uid = '[VASHMARK-' | ||
+ ~~( Math.random() * 10000000 ) | ||
+ (debugName ? ':' + debugName : '') | ||
+ ']'; | ||
this.markedIndex = 0; | ||
this.buffer = buffer; | ||
this.destroyed = false; | ||
} | ||
Buffer.prototype.index = function( idx ){ | ||
return this._vo[ idx ]; | ||
} | ||
var reMark = Mark.re = /\[VASHMARK\-\d{1,8}(?::[\s\S]+?)?]/g | ||
Buffer.prototype.flush = function() { | ||
return this.empty().join( "" ); | ||
}; | ||
// tests if a string has a mark-like uid within it | ||
Mark.uidLike = function( str ){ | ||
return (str || '').search( reMark ) > -1; | ||
} | ||
Buffer.prototype.toString = Buffer.prototype.toHtmlString = function(){ | ||
// not using flush because then console.log( tpl() ) would artificially | ||
// affect the output | ||
return this._vo.join( "" ); | ||
} | ||
Mark.prototype.destroy = function(){ | ||
// BUFFER MANIPULATION | ||
/////////////////////////////////////////////////////////////////////////// | ||
var found = this.findInBuffer(); | ||
/////////////////////////////////////////////////////////////////////////// | ||
// MARKS | ||
// These can be used to manipulate the existing entries in the rendering | ||
// context. For an example, see the highlight helper. | ||
if( found > -1 ){ | ||
this.buffer.splice( found, 1 ); | ||
this.markedIndex = -1; | ||
} | ||
var Mark = runtime['Mark'] = function( buffer, debugName ){ | ||
this.uid = '[VASHMARK-' | ||
+ ~~( Math.random() * 10000000 ) | ||
+ (debugName ? ':' + debugName : '') | ||
+ ']'; | ||
this.markedIndex = 0; | ||
this.buffer = buffer; | ||
this.destroyed = false; | ||
} | ||
this.destroyed = true; | ||
} | ||
var reMark = Mark.re = /\[VASHMARK\-\d{1,8}(?::[\s\S]+?)?]/g | ||
Mark.prototype.findInBuffer = function(){ | ||
// tests if a string has a mark-like uid within it | ||
Mark.uidLike = function( str ){ | ||
return (str || '').search( reMark ) > -1; | ||
} | ||
if( this.destroyed ){ | ||
return -1; | ||
} | ||
Mark.prototype.destroy = function(){ | ||
if( this.markedIndex && this.buffer.index( this.markedIndex ) === this.uid ){ | ||
return this.markedIndex; | ||
} | ||
var found = this.findInBuffer(); | ||
// The mark may be within a string due to string shenanigans. If this is | ||
// true this is bad, because all the Mark manipulation commands assume | ||
// that the Mark is the only content at that index in the buffer, which | ||
// means that splice commands will result in lost content. | ||
var escaped = this.uid.replace(/(\[|\])/g, '\\$1'); | ||
var re = new RegExp(escaped); | ||
return this.markedIndex = this.buffer.indexOf( re ); | ||
} | ||
if( found > -1 ){ | ||
this.buffer.splice( found, 1 ); | ||
this.markedIndex = -1; | ||
} | ||
// MARKS | ||
/////////////////////////////////////////////////////////////////////////// | ||
this.destroyed = true; | ||
} | ||
/////////////////////////////////////////////////////////////////////////// | ||
// ERROR REPORTING | ||
Mark.prototype.findInBuffer = function(){ | ||
// Liberally modified from https://github.com/visionmedia/jade/blob/master/jade.js | ||
helpers.constructor.reportError = function(e, lineno, chr, orig, lb){ | ||
if( this.destroyed ){ | ||
return -1; | ||
} | ||
lb = lb || '!LB!'; | ||
if( this.markedIndex && this.buffer.index( this.markedIndex ) === this.uid ){ | ||
return this.markedIndex; | ||
} | ||
var lines = orig.split(lb) | ||
,contextSize = lineno === 0 && chr === 0 ? lines.length - 1 : 3 | ||
,start = Math.max(0, lineno - contextSize) | ||
,end = Math.min(lines.length, lineno + contextSize); | ||
// The mark may be within a string due to block manipulation shenanigans. | ||
var escaped = this.uid.replace(/(\[|\])/g, '\\$1'); | ||
var re = new RegExp(escaped); | ||
return this.markedIndex = this.buffer.indexOf( re ); | ||
} | ||
var contextStr = lines.slice(start, end).map(function(line, i, all){ | ||
var curr = i + start + 1; | ||
// MARKS | ||
/////////////////////////////////////////////////////////////////////////// | ||
return (curr === lineno ? ' > ' : ' ') | ||
+ (curr < 10 ? ' ' : '') | ||
+ curr | ||
+ ' | ' | ||
+ line; | ||
}).join('\n'); | ||
/////////////////////////////////////////////////////////////////////////// | ||
// ERROR REPORTING | ||
e.vashlineno = lineno; | ||
e.vashcharno = chr; | ||
e.message = 'Problem while rendering template at line ' | ||
+ lineno + ', character ' + chr | ||
+ '.\nOriginal message: ' + e.message + '.' | ||
+ '\nContext: \n\n' + contextStr + '\n\n'; | ||
// Liberally modified from https://github.com/visionmedia/jade/blob/master/jade.js | ||
helpers.constructor.reportError = function(e, lineno, chr, orig, lb, atRenderTime){ | ||
throw e; | ||
}; | ||
lb = lb || '!LB!'; | ||
helpers['reportError'] = function() { | ||
this.constructor.reportError.apply( this, arguments ); | ||
}; | ||
var contextStr = error.context(orig, lineno, chr, lb); | ||
// ERROR REPORTING | ||
/////////////////////////////////////////////////////////////////////////// | ||
e.vashlineno = lineno; | ||
e.vashcharno = chr; | ||
e.message = 'Problem while ' | ||
+ (atRenderTime ? 'rendering' : 'compiling') | ||
+ ' template at line ' | ||
+ lineno + ', character ' + chr | ||
+ '.\nOriginal message: ' + e.message + '.' | ||
+ '\nContext: \n\n' + contextStr + '\n\n'; | ||
/////////////////////////////////////////////////////////////////////////// | ||
// VASH.LINK | ||
// Take a compiled string or function and "link" it to the current vash | ||
// runtime. This is necessary to allow instantiation of `Helpers` and | ||
// proper decompilation via `toClientString`. | ||
// | ||
// If `options.asHelper` and `options.args` are defined, the `cmpFunc` is | ||
// interpreted as a compiled helper, and is attached to `vash.helpers` at | ||
// a property name equal to `options.asHelper`. | ||
throw e; | ||
}; | ||
vash['link'] = function( cmpFunc, options ){ | ||
helpers['reportError'] = function() { | ||
this.constructor.reportError.apply( this, arguments ); | ||
}; | ||
// TODO: allow options.filename to be used as sourceUrl? | ||
// ERROR REPORTING | ||
/////////////////////////////////////////////////////////////////////////// | ||
var originalFunc | ||
,cmpOpts; | ||
/////////////////////////////////////////////////////////////////////////// | ||
// VASH.LINK | ||
// Take a compiled string or function and "link" it to the current vash | ||
// runtime. This is necessary to allow instantiation of `Helpers` and | ||
// proper decompilation via `toClientString`. | ||
// | ||
// If `options.asHelper` and `options.args` are defined, the `cmpFunc` is | ||
// interpreted as a compiled helper, and is attached to `runtime.helpers` at | ||
// a property name equal to `options.asHelper`. | ||
if( !options.args ){ | ||
// every template has these arguments | ||
options.args = [options.modelName, options.helpersName, '__vopts', 'vash']; | ||
} | ||
runtime['link'] = function( cmpFunc, options ){ | ||
if( typeof cmpFunc === 'string' ){ | ||
originalFunc = cmpFunc; | ||
// TODO: allow options.filename to be used as sourceUrl? | ||
try { | ||
// do not pollute the args array for later attachment to the compiled | ||
// function for later decompilation/linking | ||
cmpOpts = options.args.slice(); | ||
cmpOpts.push(cmpFunc); | ||
cmpFunc = Function.apply(null, cmpOpts); | ||
} catch(e) { | ||
// TODO: add flag to reportError to know if it's at compile time or runtime | ||
helpers.reportError(e, 0, 0, originalFunc, /\n/); | ||
} | ||
} | ||
var originalFunc | ||
,cmpOpts; | ||
// need this to enable decompilation / relinking | ||
cmpFunc.options = { | ||
simple: options.simple | ||
,modelName: options.modelName | ||
,helpersName: options.helpersName | ||
} | ||
if( !options.args ){ | ||
// every template has these arguments | ||
options.args = [options.modelName, options.helpersName, '__vopts', 'runtime']; | ||
} | ||
var linked; | ||
if( typeof cmpFunc === 'string' ){ | ||
originalFunc = cmpFunc; | ||
if( options.asHelper ){ | ||
try { | ||
// do not pollute the args array for later attachment to the compiled | ||
// function for later decompilation/linking | ||
cmpOpts = options.args.slice(); | ||
cmpOpts.push(cmpFunc); | ||
cmpFunc = Function.apply(null, cmpOpts); | ||
} catch(e) { | ||
// TODO: add flag to reportError to know if it's at compile time or runtime | ||
helpers.reportError(e, 0, 0, originalFunc, /\n/, false); | ||
} | ||
} | ||
cmpFunc.options.args = options.args; | ||
cmpFunc.options.asHelper = options.asHelper; | ||
// need this to enable decompilation / relinking | ||
cmpFunc.options = { | ||
simple: options.simple | ||
,modelName: options.modelName | ||
,helpersName: options.helpersName | ||
} | ||
linked = function(){ | ||
return cmpFunc.apply(this, slice.call(arguments)); | ||
} | ||
var linked; | ||
helpers[options.asHelper] = linked; | ||
if( options.asHelper ){ | ||
} else { | ||
cmpFunc.options.args = options.args; | ||
cmpFunc.options.asHelper = options.asHelper; | ||
linked = function( model, opts ){ | ||
if( options.simple ){ | ||
var ctx = { | ||
buffer: [] | ||
,escape: Helpers.prototype.escape | ||
,raw: Helpers.prototype.raw | ||
} | ||
return cmpFunc( model, ctx, opts, vash ); | ||
} | ||
linked = function(){ | ||
return cmpFunc.apply(this, slice.call(arguments)); | ||
} | ||
opts = divineRuntimeTplOptions( model, opts ); | ||
return cmpFunc( model, (opts && opts.context) || new Helpers( model ), opts, vash ); | ||
} | ||
} | ||
helpers[options.asHelper] = linked; | ||
// show the template-specific code, instead of the generic linked function | ||
linked['toString'] = function(){ return cmpFunc.toString(); } | ||
} else { | ||
// shortcut to show the actual linked function | ||
linked['_toString'] = function(){ return Function.prototype.toString.call(linked) } | ||
linked = function( model, opts ){ | ||
if( options.simple ){ | ||
var ctx = { | ||
buffer: [] | ||
,escape: Helpers.prototype.escape | ||
,raw: Helpers.prototype.raw | ||
} | ||
return cmpFunc( model, ctx, opts, runtime ); | ||
} | ||
linked['toClientString'] = function(){ | ||
return 'vash.link( ' | ||
+ cmpFunc.toString() + ', ' | ||
+ JSON.stringify( cmpFunc.options ) + ' )'; | ||
} | ||
opts = divineRuntimeTplOptions( model, opts ); | ||
return cmpFunc( model, (opts && opts.context) || new Helpers( model ), opts, runtime ); | ||
} | ||
} | ||
return linked; | ||
} | ||
// show the template-specific code, instead of the generic linked function | ||
linked['toString'] = function(){ return cmpFunc.toString(); } | ||
// given a model and options, allow for various tpl signatures and options: | ||
// ( model, {} ) | ||
// ( model, function onRenderEnd(){} ) | ||
// ( model ) | ||
// and model.onRenderEnd | ||
function divineRuntimeTplOptions( model, opts ){ | ||
// shortcut to show the actual linked function | ||
linked['_toString'] = function(){ return Function.prototype.toString.call(linked) } | ||
// allow for signature: model, callback | ||
if( typeof opts === 'function' ) { | ||
opts = { onRenderEnd: opts }; | ||
} | ||
// This assumes a vash global, and should be deprecated. | ||
// TODO: @deprecate | ||
linked['toClientString'] = function(){ | ||
return 'vash.link( ' | ||
+ cmpFunc.toString() + ', ' | ||
+ JSON.stringify( cmpFunc.options ) + ' )'; | ||
} | ||
// allow for passing in onRenderEnd via model | ||
if( model && model.onRenderEnd ){ | ||
opts = opts || {}; | ||
return linked; | ||
} | ||
if( !opts.onRenderEnd ){ | ||
opts.onRenderEnd = model.onRenderEnd; | ||
} | ||
// given a model and options, allow for various tpl signatures and options: | ||
// ( model, {} ) | ||
// ( model, function onRenderEnd(){} ) | ||
// ( model ) | ||
// and model.onRenderEnd | ||
function divineRuntimeTplOptions( model, opts ){ | ||
delete model.onRenderEnd; | ||
} | ||
// allow for signature: model, callback | ||
if( typeof opts === 'function' ) { | ||
opts = { onRenderEnd: opts }; | ||
} | ||
// ensure options can be referenced | ||
if( !opts ){ | ||
opts = {}; | ||
} | ||
// allow for passing in onRenderEnd via model | ||
if( model && model.onRenderEnd ){ | ||
opts = opts || {}; | ||
return opts; | ||
} | ||
if( !opts.onRenderEnd ){ | ||
opts.onRenderEnd = model.onRenderEnd; | ||
} | ||
// shortcut for compiled helpers | ||
var slice = Array.prototype.slice; | ||
delete model.onRenderEnd; | ||
} | ||
// VASH.LINK | ||
/////////////////////////////////////////////////////////////////////////// | ||
// ensure options can be referenced | ||
if( !opts ){ | ||
opts = {}; | ||
} | ||
/////////////////////////////////////////////////////////////////////////// | ||
// TPL CACHE | ||
return opts; | ||
} | ||
vash['lookup'] = function( path, model ){ | ||
var tpl = vash.helpers.tplcache[path]; | ||
if( !tpl ){ throw new Error('Could not find template: ' + path); } | ||
if( model ){ return tpl(model); } | ||
else return tpl; | ||
}; | ||
// shortcut for compiled helpers | ||
var slice = Array.prototype.slice; | ||
vash['install'] = function( path, tpl ){ | ||
var cache = vash.helpers.tplcache; | ||
if( typeof tpl === 'string' ){ | ||
if( !vash.compile ){ throw new Error('vash.install(path, [string]) is not available in the standalone runtime.') } | ||
tpl = vash.compile(tpl); | ||
} else if( typeof path === 'object' ){ | ||
tpl = path; | ||
Object.keys(tpl).forEach(function(path){ | ||
cache[path] = tpl[path]; | ||
}); | ||
return cache; | ||
} | ||
return cache[path] = tpl; | ||
}; | ||
// VASH.LINK | ||
/////////////////////////////////////////////////////////////////////////// | ||
vash['uninstall'] = function( path ){ | ||
var cache = vash.helpers.tplcache | ||
,deleted = false; | ||
/////////////////////////////////////////////////////////////////////////// | ||
// TPL CACHE | ||
if( typeof path === 'string' ){ | ||
return delete cache[path]; | ||
} else { | ||
Object.keys(cache).forEach(function(key){ | ||
if( cache[key] === path ){ deleted = delete cache[key]; } | ||
}) | ||
return deleted; | ||
} | ||
}; | ||
runtime['lookup'] = function( path, model ){ | ||
var tpl = runtime.helpers.tplcache[path]; | ||
if( !tpl ){ throw new Error('Could not find template: ' + path); } | ||
if( model ){ return tpl(model); } | ||
else return tpl; | ||
}; | ||
}()); | ||
runtime['install'] = function( path, tpl ){ | ||
var cache = runtime.helpers.tplcache; | ||
if( typeof tpl === 'string' ){ | ||
// Super hacky: if the calling context has a `compile` function, | ||
// then `this` is likely full vash. This is simply for backwards | ||
// compatibility. | ||
// TODO: @deprecate | ||
if ( typeof this.compile === 'function') { | ||
tpl = this.compile(tpl); | ||
} else { | ||
throw new Error('.install(path, [string]) is not available in the standalone runtime.'); | ||
} | ||
} else if( typeof path === 'object' ){ | ||
tpl = path; | ||
Object.keys(tpl).forEach(function(path){ | ||
cache[path] = tpl[path]; | ||
}); | ||
return cache; | ||
} | ||
return cache[path] = tpl; | ||
}; | ||
runtime['uninstall'] = function( path ){ | ||
var cache = runtime.helpers.tplcache | ||
,deleted = false; | ||
if( typeof path === 'string' ){ | ||
return delete cache[path]; | ||
} else { | ||
Object.keys(cache).forEach(function(key){ | ||
if( cache[key] === path ){ deleted = delete cache[key]; } | ||
}) | ||
return deleted; | ||
} | ||
}; | ||
},{"./lib/error":1,"./package.json":2}]},{},[3]) | ||
(3) | ||
}); |
@@ -7,21 +7,13 @@ How to Contribute | ||
* Before rewriting major portions of code, open an Issue to discuss it. Someone might be working on what you want already! | ||
* Maintain code style whenever possible: make your code look like the code around it. General syntax rules: | ||
* Comma-first | ||
* Keep line length within reason (< 80 chars) | ||
* Use tabs, not spaces (for now...) | ||
* Maintain code style whenever possible: make your code look like the code around it. | ||
Building Vash | ||
------------- | ||
Building Vash for distribution | ||
------------------------------ | ||
Simple as: | ||
$ support/task build | ||
$ npm run build | ||
And tests (includes `build` task): | ||
$ support/task test | ||
If you need to test the minified version of `vash.js`: | ||
$ support/task test --min | ||
$ npm test |
{ | ||
"name": "vash", | ||
"description": "Razor syntax for JS templating", | ||
"version": "0.7.12-1", | ||
"version": "0.8.0-beta", | ||
"author": "Andrew Petersen <senofpeter@gmail.com>", | ||
@@ -20,3 +20,3 @@ "homepage": "https://github.com/kirbysayshi/vash", | ||
}, | ||
"main": "build/vash", | ||
"main": "index.js", | ||
"engines": { | ||
@@ -26,16 +26,23 @@ "node": ">= 0.8" | ||
"scripts": { | ||
"build": "make build", | ||
"test": "make test && make test-min", | ||
"docs": "make docs" | ||
"coverage": "VASHPATH=../../index.js VASHRUNTIMEPATH=../../runtime.js browserify -t envify -t coverify test/vows/vash.test.js | node | coverify", | ||
"build": "browserify index.js --standalone vash > build/vash.js && browserify --standalone vash runtime.js > build/vash-runtime.js && browserify --standalone vash --external fs --external path lib/helpers/index.js > build/vash-runtime-all.js", | ||
"test": "VASHPATH=../../index.js VASHRUNTIMEPATH=../../runtime.js vows test/vows/vash.*.js --spec", | ||
"docs": "scripts/docs.sh", | ||
"docs-dev": "scripts/docs-dev.sh" | ||
}, | ||
"dependencies": { | ||
"commander": "~1.1.1", | ||
"uglify-js": "1.0.6" | ||
"uglify-js": "1.0.6", | ||
"debug": "^0.7.4" | ||
}, | ||
"devDependencies": { | ||
"vows": "~0.7.0", | ||
"browserify": "^3.33.0", | ||
"coverify": "~1.0.6", | ||
"envify": "^1.2.1", | ||
"jshint": "0.8.0", | ||
"marked": "~0.2.8", | ||
"semver": "~1", | ||
"jshint": "0.8.0", | ||
"marked": "~0.2.8" | ||
"uglify-js": "^2.4.13", | ||
"vows": "^0.8.1" | ||
} | ||
} | ||
} |
@@ -30,2 +30,3 @@ <!-- This document was generated from README.vash --> | ||
- [Browser - Vanilla](#browser---vanilla) | ||
- [Browser - Browserify et al](#browser---browserify-et-al) | ||
- [Browser - RequireJS](#browser---requirejs) | ||
@@ -146,12 +147,12 @@ - [Playground](#playground) | ||
<a name="browser---browserify-et-al"></a>Browser - Browserify et al | ||
---------------------------------- | ||
Just `require` Vash, and compile. If you want something fancier, try [vashify](https://www.npmjs.com/package/vashify)! | ||
<a name="browser---requirejs"></a>Browser - RequireJS | ||
--------------------------- | ||
Be sure to configure paths appropriately, but vash is AMD ready. | ||
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. | ||
require(['vash'], function(vash){ | ||
var tpl = vash.compile( '<p>I am a @model.t!</p>' ); | ||
document.querySelector('#content').innerHTML = tpl({ t: 'template' }); | ||
}) | ||
<a name="playground"></a>Playground | ||
@@ -982,2 +983,4 @@ ================== | ||
Note: this assumes that `vash` is available globally. A future version of Vash will hopefully remove this assumption. | ||
<a name="vash-runtime-browser"></a>Vash Runtime (Browser) | ||
@@ -993,2 +996,10 @@ ==================== | ||
If you're in a Browserify-like environemnt, you should be able to: | ||
```js | ||
var vashruntime = require('vash/runtime'); | ||
``` | ||
..and have access to the [Runtime API][]. | ||
[vash-runtime.min.js]: https://github.com/kirbysayshi/vash/blob/master/build/vash-runtime.min.js | ||
@@ -1119,3 +1130,3 @@ [vash-runtime-all.min.js]: https://github.com/kirbysayshi/vash/blob/master/build/vash-runtime-all.min.js | ||
-a, --no-autolink Wrap each template in `vash.link`. | ||
-r, --render [json] Render the template using <json> as the model | ||
-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] | ||
@@ -1224,2 +1235,3 @@ --helper Assume the input is a to-be-compiled helper | ||
[Browser - Vanilla]: #browser---vanilla | ||
[Browser - Browserify et al]: #browser---browserify-et-al | ||
[Browser - RequireJS]: #browser---requirejs | ||
@@ -1289,6 +1301,10 @@ [Playground]: #playground | ||
<a rev="footnote" href="#fnref:razor-ms">↩</a> | ||
</li><li id="fn:this-doc"> | ||
</li> | ||
<li id="fn:this-doc"> | ||
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. | ||
<a rev="footnote" href="#fnref:this-doc">↩</a> | ||
</li> | ||
</ol> | ||
@@ -1295,0 +1311,0 @@ |
@@ -10,3 +10,3 @@ var vows = require('vows') | ||
vash.config.useWith = false; | ||
vash.config.debug = false; | ||
vash.config.debug = true; | ||
@@ -29,2 +29,13 @@ var tryCompile = function(str){ | ||
// SUPER HACK: | ||
var aeToStringVows = assert.AssertionError.prototype.toString; | ||
assert.AssertionError.prototype.toString = function wrapper() { | ||
// ...Put the default back to get around vows#278 caused by recursively | ||
// calling itself. | ||
assert.AssertionError.prototype.toString = Error.prototype.toString; | ||
var out = aeToStringVows.call(this); | ||
assert.AssertionError.prototype.toString = wrapper; | ||
return out; | ||
} | ||
vows.describe('vash templating library').addBatch({ | ||
@@ -351,2 +362,60 @@ | ||
} | ||
,'explicit expressions containing quoted characters': { | ||
topic: function() { | ||
// For now just include ASCII. | ||
var UNICODE_MAX = 127; // 1114111; | ||
// 0-31 are control characters. | ||
for(var i = 32, chars = []; i <= UNICODE_MAX; i++) { | ||
chars.push(String.fromCharCode(i)); | ||
} | ||
return chars; | ||
} | ||
,'do not need to be escaped': function(chars){ | ||
var str = chars.map(wrapCharWithExpression).join(''); | ||
var tpl = vash.compile(str, { htmlEscape: false }); | ||
var expected = chars.map(wrapCharWithP); | ||
assert.equal( tpl(), expected.join('') ); | ||
function wrapCharWithExpression(chr) { | ||
return '<p>@("' + escapeIfNeeded(chr) + '")</p>\n'; | ||
} | ||
function wrapCharWithP(chr) { | ||
return '<p>' + chr + '</p>\n'; | ||
} | ||
function escapeIfNeeded(chr) { | ||
if (chr === '"' || chr === '\n' || chr === '\\') return '\\' + chr; | ||
else return chr; | ||
} | ||
} | ||
} | ||
,'a parent expression': { | ||
topic: '@(function() { <p>)</p> }())' | ||
,'should not consume content PAREN_CLOSE': function(topic) { | ||
var tpl = vash.compile(topic); | ||
assert.equal( tpl(), '<p>)</p>' ); | ||
} | ||
} | ||
,'a parent block': { | ||
topic: '@{ function what() { <p>}</p> } }' | ||
,'should not consume content BRACE_CLOSE': function(topic) { | ||
var tpl = vash.compile(topic); | ||
assert.equal( tpl(), '' ); | ||
} | ||
} | ||
,'anonymous blocks': { | ||
@@ -445,10 +514,11 @@ | ||
} | ||
//,'array literal': { | ||
// topic: function(){ | ||
// return vash.compile('<a>@["a", "b", "c"]</a>'); | ||
// } | ||
// ,'toStrings': function(topic){ | ||
// assert.equal( topic(), '<a>a,b,c</a>' ); | ||
// } | ||
//} | ||
,'array literal within markup': { | ||
topic: '<a>@["a", "b", "c"].join("")</a>' | ||
,'outputs': function(topic){ | ||
var tpl = vash.compile(topic); | ||
assert.equal( tpl(), '<a>abc</a>' ); | ||
} | ||
} | ||
,'function invocation within expression buffers': { | ||
@@ -603,47 +673,2 @@ topic: function(){ | ||
} | ||
,'markup within a code block followed by else with an expression': { | ||
topic: function(){ | ||
var str = '@if(false){ \n' | ||
+ '<span>this is text \n' | ||
+ 'that spans multiple lines</span> \n' | ||
+ ' } else { \n' | ||
+ ' @name.how \n' | ||
+ ' } '; | ||
return str; | ||
} | ||
,'disregards newline re-entry into BLK mode': function(topic){ | ||
vash.config.useWith = true; | ||
var tpl = tryCompile(topic); | ||
vash.config.useWith = false; | ||
assert.equal(tpl( { name: {how: 'you' } } ), 'you'); | ||
} | ||
} | ||
,'markup within a complex if code block followed by else with an expression': { | ||
topic: function(){ | ||
var str = '@if( heyo.ya !== true ){ \n' | ||
+ '<input name="item-quantity-@item.id" type="text" value="@item.quantity" maxlength="5" size="6" />' | ||
+ ' } else { \n' | ||
+ ' @name.how \n' | ||
+ ' } '; | ||
return str; | ||
} | ||
,'disregards newline re-entry into BLK mode': function(topic){ | ||
var tpl = tryCompile(topic); | ||
assert.equal(tpl( { heyo: { ya: true }, name: {how: 'you' }, item: { id: 0, quantity: 23 } } ), 'you'); | ||
} | ||
} | ||
,'markup within a complex if code block followed by else with an expression, all within markup': { | ||
topic: function(){ | ||
var str = '<td>@if( false ){ } else { @name.how }</td>'; | ||
return str; | ||
} | ||
,'"else" is a keyword': function(topic){ | ||
var tpl = tryCompile(topic); | ||
assert.equal(tpl( { | ||
name: { | ||
how: 'you' | ||
} | ||
} ), '<td>you</td>'); | ||
} | ||
} | ||
,'markup within a code block with an expression in the tag name': { | ||
@@ -660,3 +685,3 @@ topic: function(){ | ||
var tpl = tryCompile(topic); | ||
assert.equal(tpl({ name: 'what' }), '<span-what>this is text \nthat spans multiple lines</span-what> \n'); | ||
assert.equal(tpl({ name: 'what' }), '<span-what>this is text \nthat spans multiple lines</span-what>'); | ||
} | ||
@@ -676,3 +701,3 @@ } | ||
var tpl = tryCompile(topic); | ||
assert.equal(tpl({ name: 'what' }), '<span-what>this is text \nthat spans multiple lines</span-what> \n<span class="what">this is text \nthat spans multiple lines</span> \n'); | ||
assert.equal(tpl({ name: 'what' }), '<span-what>this is text \nthat spans multiple lines</span-what><span class="what">this is text \nthat spans multiple lines</span>'); | ||
} | ||
@@ -747,4 +772,5 @@ } | ||
,'exits MKP with expression': function(topic){ | ||
var tpl = vash.compile(topic.replace('/>', '@(true)/>')) | ||
,expected = '<br true/>'; | ||
var str = topic.replace('/>', '@(true)/>') | ||
var tpl = vash.compile(str) | ||
,expected = '<br true />'; | ||
@@ -791,3 +817,3 @@ assert.equal(tpl(), expected); | ||
var tpl = vash.compile(topic) | ||
,expected = '<button data-bind="enable:0>0"/>'; | ||
,expected = '<button data-bind="enable:0>0" />'; | ||
@@ -804,3 +830,3 @@ assert.equal(tpl([]), expected); | ||
var tpl = vash.compile(topic) | ||
, expected = '<b ></b><img />' | ||
, expected = '<b></b><img />' | ||
@@ -840,3 +866,3 @@ assert.equal(tpl(), expected); | ||
,'is allowed': function(topic){ | ||
assert.equal( vash.compile(topic, { useWith: true })({name: 'what'}), '<what>This is content</what> ' ); | ||
assert.equal( vash.compile(topic, { useWith: true })({name: 'what'}), '<what>This is content</what>' ); | ||
} | ||
@@ -917,6 +943,6 @@ } | ||
'simple expression': { | ||
topic: '<span>@a.replace(/a/gi, "o")</span>' | ||
topic: '<span>@a.replace(/\\)a"\'/gi, "o")</span>' | ||
,'replaces': function( topic ){ | ||
var tpl = vash.compile( topic, { useWith: true } ); | ||
assert.equal( tpl({ a: 'a' }), '<span>o</span>'); | ||
assert.equal( tpl({ a: ')a"\'' }), '<span>o</span>'); | ||
} | ||
@@ -926,6 +952,6 @@ } | ||
,'period meta character': { | ||
topic: '<span>@a.replace(/./gi, "o")</span>' | ||
topic: '<span>@a.replace(/\\)."\'/gi, "o")</span>' | ||
,'replaces': function( topic ){ | ||
var tpl = vash.compile( topic, { useWith: true } ); | ||
assert.equal( tpl({ a: 'a' }), '<span>o</span>') | ||
assert.equal( tpl({ a: ')a"\'' }), '<span>o</span>') | ||
} | ||
@@ -935,3 +961,3 @@ } | ||
,'within BLK': { | ||
topic: '@{ var re = /[@\'"]/gi; }<span>@a.replace(re, "o")</span>' | ||
topic: '@{ var re = /[@}\'"]/gi; }<span>@a.replace(re, "o")</span>' | ||
,'replaces': function( topic ){ | ||
@@ -942,2 +968,57 @@ var tpl = vash.compile( topic, { useWith: true } ); | ||
} | ||
,'within an expression': { | ||
topic: '@(/a/.exec(\'abc\')[0])' | ||
,outputs: function( topic ){ | ||
var tpl = vash.compile(topic); | ||
assert.equal( tpl(), 'a' ); | ||
} | ||
} | ||
,'literal': { | ||
'within markup': { | ||
topic: '<span>@/a/.test(\'abc\')</span>' | ||
,outputs: function ( topic ) { | ||
var tpl = vash.compile(topic); | ||
assert.equal( tpl(), '<span>true</span>' ); | ||
} | ||
} | ||
,'within markup attribute': { | ||
topic: '<span b="@/a/.exec(\'abc\')[0]"></span>' | ||
,outputs: function ( topic ) { | ||
var tpl = vash.compile(topic); | ||
assert.equal( tpl(), '<span b="a"></span>' ); | ||
} | ||
} | ||
} | ||
,'following conditional': { | ||
topic: '@if (true) /a/.test(\'abc\')' | ||
,outputs: function ( topic ) { | ||
var tpl = vash.compile(topic); | ||
assert.equal( tpl(), '' ); | ||
} | ||
} | ||
,'are not mistaken for': { | ||
'division expression': { | ||
topic: '@{ var test = 100/2; }<span>@test</span>' | ||
,'is not mistaken for regex': function (topic) { | ||
var tpl = vash.compile(topic); | ||
assert.equal( tpl({}), '<span>50</span>' ); | ||
} | ||
} | ||
,'division within condition': { | ||
topic: '@{ if(100/2) <span></span> }' | ||
,'is not mistaken for regex': function (topic) { | ||
var tpl = vash.compile(topic); | ||
assert.equal( tpl({}), '<span></span>' ); | ||
} | ||
} | ||
} | ||
} | ||
@@ -965,4 +1046,74 @@ | ||
} | ||
,'within ProgramNode (root) in front of keywords': { | ||
topic: '' | ||
+ '@@if(model.type){\n' | ||
+ ' <p>I\'m a @@model.type!</p>\n' | ||
+ '} else if(model.name){\n' | ||
+ ' <p>My name is @@model.name.</p>\n' | ||
+ '} else {\n' | ||
+ ' <p>I DON\'T KNOW WHO OR WHAT I AM...</p>\n' | ||
+ '}\n' | ||
,'outputs': function(topic) { | ||
var tpl = vash.compile( topic ); | ||
assert.equal( tpl(), '' | ||
+ '@if(model.type){\n' | ||
+ ' <p>I\'m a @model.type!</p>\n' | ||
+ '} else if(model.name){\n' | ||
+ ' <p>My name is @model.name.</p>\n' | ||
+ '} else {\n' | ||
+ ' <p>I DON\'T KNOW WHO OR WHAT I AM...</p>\n' | ||
+ '}\n'); | ||
} | ||
} | ||
,'within markup attribute': { | ||
topic: '<figure id="fig-@@(figcount++)"></figure>' | ||
,outputs: function(topic) { | ||
var tpl = vash.compile(topic); | ||
assert.equal( tpl(), '<figure id="fig-@(figcount++)"></figure>' ); | ||
} | ||
} | ||
,'within markup node': { | ||
topic: '<f@@e></f@@e>' | ||
,outputs: function(topic) { | ||
var tpl = vash.compile(topic); | ||
assert.equal( tpl(), '<f@e></f@e>' ); | ||
} | ||
} | ||
,'<ul class="@@(model.active ? \'highlight\' : \'\')"></ul>': { | ||
topic: '<ul class="@@(model.active ? \'highlight\' : \'\')"></ul>' | ||
,outputs: function(topic) { | ||
var tpl = vash.compile(topic); | ||
assert.equal( tpl(), '<ul class="@(model.active ? \'highlight\' : \'\')"></ul>' ); | ||
} | ||
} | ||
,'@@@@' : { | ||
topic: '`@@@@`' | ||
,outputs: function(topic) { | ||
var tpl = vash.compile(topic); | ||
assert.equal( tpl(), '`@@`' ); | ||
} | ||
} | ||
,'@@{ }': { | ||
topic: '@@{ }' | ||
,outputs: function(topic) { | ||
var tpl = vash.compile(topic); | ||
assert.equal( tpl(), '@{ }' ); | ||
} | ||
} | ||
} | ||
,'PHP-like tags are not confused for attributes': { | ||
topic: '<? $what = \'what\' ?>' | ||
,outputs: function(topic) { | ||
var tpl = vash.compile(topic); | ||
assert.equal( tpl(), '<? $what = \'what\' ?>' ); | ||
} | ||
} | ||
,'"server-side" comments': { | ||
@@ -1019,2 +1170,9 @@ | ||
} | ||
,'can be escaped': { | ||
topic: 'with `@@*` and `*@@`' | ||
,'successfully': function(topic) { | ||
var tpl = vash.compile(topic); | ||
assert.equal( tpl(), 'with `@*` and `*@`' ); | ||
} | ||
} | ||
} | ||
@@ -1114,9 +1272,17 @@ ,'mixing expressions and text': { | ||
} | ||
,'content following parens preserves whitespace': { | ||
topic: '<(bin/vash <docs/helpers/* --helper) > README2.md' | ||
,outputs: function(topic) { | ||
var tpl = vash.compile(topic); | ||
assert.equal( tpl(), '<(bin/vash <docs/helpers/* --helper) > README2.md' ); | ||
} | ||
} | ||
,'content } in closed markup': { | ||
topic: function(){ | ||
var str = '@if(true) { <li> @} </li> }'; | ||
var str = '@if(true) { <li> } </li> }'; | ||
return str; | ||
} | ||
,'does not need to be escaped': function(topic){ | ||
//assert.doesNotThrow( function(){ vash.compile(topic) }, Error); | ||
assert.doesNotThrow( function(){ vash.compile(topic) }, Error ); | ||
@@ -1126,12 +1292,2 @@ assert.equal( vash.compile(topic)(), '<li> } </li>'); | ||
} | ||
,'content } in open markup': { | ||
topic: function(){ | ||
var str = '@if(true) { <img src="" /> @} }'; | ||
return str; | ||
} | ||
,'can be escaped with @': function(topic){ | ||
assert.doesNotThrow( function(){ vash.compile(topic) }, Error ); | ||
assert.equal( vash.compile(topic)(), '<img src="" />} '); | ||
} | ||
} | ||
,'content } in expression': { | ||
@@ -1168,3 +1324,3 @@ topic: function(){ | ||
,'HTML5:': { | ||
'unclosed tags': { | ||
/*'unclosed tags': { | ||
topic: function(){ | ||
@@ -1178,3 +1334,3 @@ var str = '<div class="how what">This is content @for(var i = 0; i < 1; i++){ <p>@i }'; | ||
} | ||
,'unclosed tag followed by previous closing tag does not bork': { | ||
,*/'unclosed tag followed by previous closing tag does not bork': { | ||
topic: function(){ | ||
@@ -1190,3 +1346,3 @@ var str = '<div class="how what">This is content @for(var i = 0; i < 1; i++){ <p>@i </div> }'; | ||
topic: function(){ | ||
var str = '<div class="how what">This is content @for(var i = 0; i < 1; i++){ <br>@i </div> }' | ||
var str = '<div class="how what">This is content @for(var i = 0; i < 1; i++){ <text><br>@i</text> } </div>' | ||
return str; | ||
@@ -1198,3 +1354,10 @@ } | ||
} | ||
,'closing tag within block': { | ||
,'explicitly unclosed tags': { | ||
topic: '<a><b><c>' | ||
,'do not become closed': function(topic) { | ||
var tpl = vash.compile(topic); | ||
assert.equal(tpl(), topic); | ||
} | ||
} | ||
/*,'closing tag within block': { | ||
topic: function(){ | ||
@@ -1208,3 +1371,3 @@ var str = '<div>This is content @if(true){ </div> } else { </div> }'; | ||
} | ||
} | ||
}*/ | ||
@@ -1227,6 +1390,6 @@ ,'operators': { | ||
,'does not prevent HTML matching': function(topic) { | ||
var tpl = vash.compile(topic) | ||
var tpl = vash.compile(topic, {useWith: false}) | ||
, actual = tpl('1'); | ||
assert.equal( '<span>1<span\n></span></span>', actual ) | ||
assert.equal( '<span>1<span></span></span>', actual ) | ||
} | ||
@@ -1236,10 +1399,32 @@ } | ||
} | ||
,'unbalanced characters are ok': { | ||
// https://github.com/kirbysayshi/vash/issues/26 | ||
'open paren': { | ||
topic: '@("(")' | ||
,'outputs': function( topic ) { | ||
var tpl = vash.compile(topic); | ||
assert.equal( tpl(), '(' ); | ||
} | ||
} | ||
,'open paren within markup within block': { | ||
topic: '@(function(model) { <p>(</p> }())' | ||
,'outputs': function( topic ) { | ||
var tpl = vash.compile(topic); | ||
assert.equal( tpl(), '<p>(</p>' ); | ||
} | ||
} | ||
} | ||
,'simple expression followed by @()': { | ||
topic: function(){ | ||
return '<li data-score="@model.Score" class="user-panel-track @(model.i % 2 === 0 ? \'even\' : \'odd\')">'; | ||
return '<li data-score="@model.Score" class="user-panel-track @(model.i % 2 === 0 ? \'even\' : \'odd\')"></li>'; | ||
} | ||
,'renders': function(topic){ | ||
assert.equal( vash.compile(topic)({ Score: '1', i: 0 }) | ||
, '<li data-score="1" class="user-panel-track even">'); | ||
, '<li data-score="1" class="user-panel-track even"></li>'); | ||
} | ||
@@ -1372,3 +1557,3 @@ } | ||
topic: function(){ | ||
return vash.compile( '@function f(i){ <b>@i</b> function d(i){ <b>@i</b> } @d(model.it) }<span>@f(model.it)</span>@f(model.it)' ); | ||
return vash.compile( '@function f(i){ <b>@i</b> function d(i){ <b>@i</b> } d(model.it) }<span>@f(model.it)</span>@f(model.it)' ); | ||
} | ||
@@ -1526,3 +1711,3 @@ ,'are escaped': function(topic){ | ||
topic: function(){ | ||
return '<style type="text/css">#parallax_field #parallax_bg { position: absolute; top: -20px; left: -20px; width: 110%; height: 425px; z-index: 1; }' | ||
return '<style type="text/css">#parallax_field #parallax_bg { position: absolute; top: -20px; left: -20px; width: 110%; height: 425px; z-index: 1; }</style>' | ||
} | ||
@@ -1602,9 +1787,20 @@ ,"is unchanged": function(topic){ | ||
topic: '@switch(model){ case 1: <p></p>break; case 2: <b></b>break; }' | ||
'without braced cases': { | ||
topic: '@switch(model){ case 1: <p></p>break; case 2: <b></b>break; }' | ||
,'work': function( topic ){ | ||
var tpl = vash.compile( topic ); | ||
assert.equal( tpl(1), '<p></p>' ); | ||
assert.equal( tpl(2), '<b></b>' ); | ||
,'work': function( topic ){ | ||
var tpl = vash.compile( topic ); | ||
assert.equal( tpl(1), '<p></p>' ); | ||
assert.equal( tpl(2), '<b></b>' ); | ||
} | ||
} | ||
,'with braced cases': { | ||
topic: '@switch(model){ case 1: { <p></p>break; } case 2: { <b></b>break; } }' | ||
,work: function( topic ) { | ||
var tpl = vash.compile( topic ); | ||
assert.equal( tpl(1), '<p></p>' ); | ||
assert.equal( tpl(2), '<b></b>' ); | ||
} | ||
} | ||
} | ||
@@ -1624,2 +1820,10 @@ | ||
} | ||
,'within content regex': { | ||
topic: '/^([a-zA-Z0-9.%]+@@[a-zA-Z0-9.\\-]+\\.(?:ca|co\\.uk|com|edu|net|org))\\b/' | ||
,outputs: function(topic) { | ||
var tpl = vash.compile(topic); | ||
assert.equal( tpl(), '/^([a-zA-Z0-9.%]+@[a-zA-Z0-9.\\-]+\\.(?:ca|co\\.uk|com|edu|net|org))\\b/' ); | ||
} | ||
} | ||
} | ||
@@ -1941,21 +2145,26 @@ | ||
topic: '@if(true){ <a><br /></a> }' | ||
topic: '@if(true){<a><br /></a>}' | ||
,'does not close parent tag': function(topic){ | ||
var p = this.parse(this.lex(topic)); | ||
var flattened = p.ast.flatten() | ||
// this is like "indexOf" for a complex object | ||
,openIdx = flattened.reduce(function(prev, curr, i){ | ||
return prev !== undefined | ||
? prev | ||
: curr.type === 'HTML_TAG_OPEN' && curr.val === '<a>' | ||
? i | ||
: undefined; | ||
}, undefined); | ||
var Lexer = require('../../lib/lexer'); | ||
var Parser = require('../../lib/parser'); | ||
var l = new Lexer(); | ||
assert.equal( flattened[openIdx].type, 'HTML_TAG_OPEN' ); | ||
assert.equal( flattened[openIdx+2].type, 'HTML_TAG_OPEN' ); | ||
assert.equal( flattened[openIdx+3].type, 'HTML_TAG_VOID_CLOSE' ); | ||
assert.equal( flattened[openIdx+5].type, 'HTML_TAG_CLOSE' ); | ||
l.write(topic); | ||
var tokens = l.read(); | ||
var p = new Parser(); | ||
p.write(tokens); | ||
var more = true; | ||
while(more !== null) more = p.read(); | ||
var root = p.stack[0]; | ||
assert.equal( root.body[0].type, 'VashMarkup' ); | ||
assert.equal( root.body[0].values[0].type, 'VashMarkupContent' ); | ||
assert.equal( root.body[0].values[0].values[1].type, 'VashBlock' ); | ||
assert.equal( root.body[0].values[0].values[1].values[0].type, 'VashMarkup' ); | ||
assert.equal( root.body[0].values[0].values[1].values[0].values[0].type, 'VashMarkupContent' ); | ||
assert.equal( root.body[0].values[0].values[1].values[0].values[0].values[0].type, 'VashText' ); | ||
assert.equal( root.body[0].values[0].values[1].values[0].values[0].values[1].isVoid, true ); | ||
} | ||
@@ -1962,0 +2171,0 @@ } |
@@ -10,2 +10,5 @@ var vows = require('vows') | ||
var copyrtl = require(path.join(path.dirname(process.env.VASHPATH), 'lib', 'util', 'copyrtl')); | ||
require(path.join(path.dirname(process.env.VASHRUNTIMEPATH), 'lib', 'helpers', 'layout')); | ||
vash.config.useWith = false; | ||
@@ -21,3 +24,3 @@ vash.config.debug = false; | ||
this.opts = function(model){ | ||
return vash.vQuery.extend( model || {}, { | ||
return copyrtl( model || {}, { | ||
// mock up express settings | ||
@@ -190,4 +193,7 @@ settings: { | ||
,actual = maker( footer + block )( this.opts() ) | ||
,actual = maker( footer + block )( this.opts() ); | ||
//console.log('UNCOMPILED', footer + block + 'END'); | ||
//console.log('FN', maker( footer + block )) | ||
//console.log('actual', 'START', actual, 'END'); | ||
assert.equal( actual, '<footer></footer><footer2></footer2>' ) | ||
@@ -347,3 +353,2 @@ } | ||
var actual = tpl( this.opts({ cache: true }) ) | ||
assert.equal( actual, '<p></p>' ); | ||
@@ -406,2 +411,16 @@ | ||
,'empty include throws': function() { | ||
var str = '@html.include("empty.include.vash")'; | ||
var opts = this.opts(); | ||
var tpl = vash.compile(str); | ||
assert.throws(function() { tpl(opts) }, /Empty or non\-string/g); | ||
} | ||
,'empty layout throws': function() { | ||
var str = '@html.extend("empty.layout.vash")'; | ||
var opts = this.opts(); | ||
var tpl = vash.compile(str); | ||
assert.throws(function() { tpl(opts) }, /Empty or non\-string/g); | ||
} | ||
} | ||
@@ -408,0 +427,0 @@ |
@@ -13,6 +13,21 @@ var vows = require('vows') | ||
require(path.join(path.dirname(process.env.VASHRUNTIMEPATH), 'lib', 'helpers')); | ||
vows.describe('vash templating library runtime').addBatch({ | ||
'default runtime helpers': { | ||
// TODO: | ||
/*'requiring': { | ||
'does not expose global': function() { | ||
var ctx = {}; | ||
vm.runInNewContext( 'var vash = require("vash/runtime")', ctx ); | ||
assert.equal(ctx.vash, undefined); | ||
} | ||
,'does not have full vash': function() { | ||
assert.equal(vruntime.compile, undefined); | ||
} | ||
} | ||
,*/'default runtime helpers': { | ||
'highlight': { | ||
@@ -81,5 +96,15 @@ topic: "@html.highlight('javascript', function(){<text>I am code</text>})" | ||
,'installing with string auto-compiles': function(){ | ||
// Trick the runtime into thinking it's the full thing. | ||
vruntime.compile = vash.compile; | ||
vruntime.compileBatch = vash.compileBatch; | ||
vruntime.compileHelper = vash.compileHelper; | ||
var str = '<p></p>' | ||
,tpl = vash.install('testpath', str); | ||
delete vruntime.compile; | ||
delete vruntime.compileBatch; | ||
delete vruntime.compileHelper; | ||
assert.equal( tpl(), str ); | ||
@@ -169,2 +194,9 @@ } | ||
topic: function(){ | ||
// Trick the runtime into thinking it's the full thing. | ||
vruntime.compile = vash.compile; | ||
vruntime.compileBatch = vash.compileBatch; | ||
vruntime.compileHelper = vash.compileHelper; | ||
vruntime.config = vash.config; | ||
return { | ||
@@ -174,2 +206,6 @@ | ||
var tpl = vash.compile(str, { debug: true }); | ||
model = model || {}; | ||
model.settings = model.settings || {}; | ||
model.settings.views = __dirname + '/../fixtures/views/'; | ||
model.settings['view engine'] = 'vash'; | ||
try { | ||
@@ -204,3 +240,3 @@ tpl(model); | ||
// when debugging, dumping this is useful to clearly see the mismatch | ||
//console.log(e.message); | ||
// console.log(e.message); | ||
@@ -207,0 +243,0 @@ assert.equal( e.vashlineno, actualLine, 'expected error line ' + actualLine + ', got ' + e.vashlineno ); |
12
TODO.md
Ensure vash#46, vash#27?, vash#26, and vash#22 are handled in tests | ||
Add license comment headers to builds (ignore version number inclusion) | ||
Update changelog | ||
Add documentation for 0.7 -> 0.8 migration | ||
Plan future deprecations, like helper system, layout `fs` usage? | ||
Reformat helpers code style (esp layout) | ||
Reformat runtime code style | ||
Add debug statements to layout helpers | ||
Most Important Things: | ||
@@ -3,0 +15,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Uses eval
Supply chain riskPackage uses dynamic code execution (e.g., eval()), which is a dangerous practice. This can prevent the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
Found 1 instance in 1 package
453039
56
10843
1
1307
3
8
27
+ Addeddebug@^0.7.4
+ Addeddebug@0.7.4(transitive)