Comparing version 0.0.4 to 0.0.5
@@ -107,6 +107,4 @@ var _ = require('underscore'); | ||
builtins.prototype.slice = function(x, start, stop) { | ||
if(_.isArray(x)) { | ||
if(_.isArray(x) || _.isString(x)) { | ||
return x.slice(start, stop); | ||
} else if(_.isString(x)) { | ||
return x.split('').slice(start, stop).join(''); | ||
} else { | ||
@@ -113,0 +111,0 @@ return ''; |
@@ -9,5 +9,9 @@ var builtins = require('./builtins'); | ||
var _func = new Function('__ctx', '__builtin', this.code); | ||
return _func(context, builtins); | ||
try { | ||
return _func(context, builtins); | ||
} catch(err) { | ||
throw err; | ||
} | ||
}; | ||
exports = module.exports = Precompiled; |
128
lib/wash.js
@@ -13,6 +13,12 @@ var _ = require('underscore'), | ||
var evalRegex = /\{\{.*?\}\}/; | ||
var tagRegex = /\{%.*?%\}/; | ||
var captureRegex = new RegExp('('+evalRegex.source+'|'+tagRegex.source+')', 'gm'); | ||
var evalOpenTag = '{{'; | ||
var evalCloseTag = '}}'; | ||
var actionOpenTag = '{%'; | ||
var actionCloseTag = '%}'; | ||
var evalTagRegex = new RegExp(escapeRegex(evalOpenTag) + '(.*?)' + escapeRegex(evalCloseTag)); | ||
var actionTagRegex = new RegExp(escapeRegex(actionOpenTag) + '(.*?)' + escapeRegex(actionCloseTag)); | ||
var tagCaptureRegex = new RegExp('('+escapeRegex(evalOpenTag) + '.*?' + escapeRegex(evalCloseTag) | ||
+'|'+escapeRegex(actionOpenTag) + '.*?' + escapeRegex(actionCloseTag)+')', 'gm'); | ||
var operators = ['+', '-', '*', '/', '==', '!=', '>=', '<=', '>', '<', '||', '&&', '!']; | ||
@@ -48,51 +54,31 @@ | ||
var temp = expr[0]; | ||
var validTest = '('; | ||
_.each(expr, function(e, i) { | ||
var p = expr.slice(0, i+1); | ||
validTest += '__ctx.' + p.join('.') + ' !== undefined'; | ||
if(i < expr.length - 1) { validTest += ' && '; } | ||
}); | ||
validTest += ')'; | ||
function _safe(ctx) { | ||
var c = ctx + temp; | ||
var m = expr; | ||
var build = '(typeof ' + c + ' !== "undefined"'; | ||
_.each(m, function (v, i) { | ||
if(i > 0) { | ||
build += ' && ' + c + '.' + v + ' !== undefined'; | ||
c += '.' + v; | ||
} | ||
}); | ||
return '((' + validTest + ')?(__ctx.' + expr.join('.') + '):"")'; | ||
} | ||
return build + ')'; | ||
function findSub(tokens, openIdx, openChar, closeChar) { | ||
if(tokens[openIdx] !== openChar) { | ||
throw new Error('opening character mismatch: ' + openChar); | ||
} | ||
return '((' + _safe('__ctx.') + ')?(__ctx.' + expr.join('.') + '):"")'; | ||
} | ||
function findSub(tokens, idx, openStr, closeStr, nested) { | ||
nested = (typeof nested === 'undefined') ? true : nested; | ||
var len = tokens.length; | ||
if(idx + 2 >= len) { | ||
return -1; | ||
} else { | ||
// next token must be 'openStr' anyway | ||
if(tokens[idx+1] !== openStr) { | ||
return -1; | ||
} | ||
var level = 1; | ||
for(var j=idx+2; j<len; ++j) { | ||
if(tokens[j] == openStr) { | ||
if(!nested) { | ||
return -1; | ||
} | ||
level += 1; | ||
} else if(tokens[j] == closeStr) { | ||
level -= 1; | ||
if(level == 0) { | ||
return j; | ||
} | ||
var level = 1; | ||
for(var j=openIdx+1, len=tokens.length; j<len; ++j) { | ||
if(tokens[j] == openChar) { | ||
level += 1; | ||
} else if(tokens[j] == closeChar) { | ||
level -= 1; | ||
if(level == 0) { | ||
return j; | ||
} | ||
} | ||
} | ||
return -1; | ||
} | ||
return -1; | ||
} | ||
@@ -113,24 +99,16 @@ | ||
if(_.contains(operators, token) || /^\".*\"$|^true$|^false$|^-?\d*\.?\d+$/.test(token)) { | ||
// operators, string literal, true, false, number literals, comman | ||
if(_.contains(operators, token) || /^\".*\"$|^true$|^false$|^-?\d*\.?\d+$|^\,$/.test(token)) { | ||
outs.push(token); | ||
} else if(builtins.__containsName(token)) { | ||
} else if(token === '(') { | ||
var closeIdx = findSub(tokens, i, '(', ')'); | ||
if(closeIdx <= i) { | ||
throw new Error('invalid form of built-in: ' + token); | ||
throw new Error('closing parenthesis not found: ' + tokens.join(' ')); | ||
} | ||
outs.push('__builtin.' + token + '(' + this._evalTokens(tokens.slice(i+2, closeIdx)) + ')'); | ||
outs.push('(' + this._evalTokens(tokens.slice(i+1, closeIdx)) + ')'); | ||
i = closeIdx; | ||
} else if(token === '(' || token === ')' || token === ',') { | ||
if(token === '(' && tokens[i+1] === ')') { | ||
// empty (); ignore it | ||
i = i + 1; | ||
} else { | ||
outs.push(token); | ||
} | ||
} else if(builtins.__containsName(token)) { | ||
outs.push('__builtin.' + token); | ||
} else { | ||
if(i+1 < len && tokens[i+1] == '(') { | ||
throw new Error('undefined function: ' + token); | ||
} | ||
var first; | ||
@@ -164,3 +142,3 @@ var dot = token.indexOf('.'); | ||
this._code.push('var __cnt_' + loopId + '=0;\n'); | ||
this._code.push('var __iterable_' + loopId + '=' + iterable + ';\n'); | ||
this._code.push('var __iterable_' + loopId + '=(' + iterable + ');\n'); | ||
this._code.push('var __obj_' + loopId + '=__builtin.isArray(__iterable_' + loopId + ')?__builtin.__toObject(__iterable_' + loopId + '):__iterable_' + loopId + ';\n'); | ||
@@ -218,5 +196,7 @@ this._code.push('for(var __key_' + loopId + ' in __obj_' + loopId + '){\n'); | ||
Wash.prototype.precompile = function() { | ||
var tokens = this.source.split(captureRegex); | ||
var tokens = this.source.split(tagCaptureRegex); | ||
this._code.push('__out="";\n'); | ||
this._code.push('"use strict";\n'); | ||
this._code.push('try {\n'); | ||
this._code.push('var __out="";\n'); | ||
@@ -228,21 +208,23 @@ for(var i=0,len=tokens.length; i<len; ++i) { | ||
if(evalRegex.test(token)) { | ||
var expr = token.slice(2, -2); | ||
if(expr) { | ||
var match; | ||
if((match = evalTagRegex.exec(token))) { | ||
var expr = match[1].trim(); | ||
if(expr.length) { | ||
var output = this._evalTokens(tokenize(expr)); | ||
if(output) { | ||
this._code.push('__out+=' + output + ';\n'); | ||
if(output.length) { | ||
this._code.push('__out+=(' + output + ');\n'); | ||
} | ||
} | ||
} else if(tagRegex.test(token)) { | ||
var expr = token.slice(2, -2); | ||
if(expr) { | ||
} else if((match = actionTagRegex.exec(token))) { | ||
var expr = match[1].trim(); | ||
if(expr.length) { | ||
this._parseTag(tokenize(expr)); | ||
} | ||
} else { | ||
this._code.push('__out+="'+escapeStr(token)+'";\n'); | ||
this._code.push('__out+="' + escapeStr(token) + '";\n'); | ||
} | ||
} | ||
this._code.push('return __out;'); | ||
this._code.push('return __out;\n'); | ||
this._code.push('} catch(__err) { return ""; }\n'); | ||
@@ -249,0 +231,0 @@ return new precompiled(this._code.join('')); |
{ | ||
"name": "wash", | ||
"description": "a safe template rendering engine", | ||
"version": "0.0.4", | ||
"version": "0.0.5", | ||
"main": "index", | ||
@@ -6,0 +6,0 @@ "author": { |
@@ -121,4 +121,4 @@ var t = require('../lib/index'), | ||
expect(t.render('{{}}', ctx)).to.equal(''); | ||
expect(t.render('{{ () }}', ctx)).to.equal(''); | ||
expect(t.render('{{ ( ) }}', ctx)).to.equal(''); | ||
expect(t.render('{{ }}', ctx)).to.equal(''); | ||
expect(t.render('{{\t}}', ctx)).to.equal(''); | ||
}); | ||
@@ -149,2 +149,6 @@ | ||
}); | ||
it('syntax-errors', function() { | ||
expect(function() { t.render('{{ ( ) }}', ctx) }).to.throwError(); | ||
}); | ||
}); | ||
@@ -226,2 +230,7 @@ | ||
it('cannot access global', function() { | ||
expect(t.render('{{ Math.E }}', ctx)).to.equal(''); | ||
expect(t.render('{{ JSON }}', ctx)).to.equal(''); | ||
}); | ||
it('dots', function() { | ||
@@ -232,5 +241,5 @@ expect(t.render('{{ comp.a }}', ctx)).to.equal('123'); | ||
expect(t.render('{{ comp.heck }}', ctx)).to.equal(''); | ||
expect(t.render('{{ comp.c.heck }}', ctx)).to.equal(''); | ||
expect(t.render('{{ comp.heck.heck }}', ctx)).to.equal(''); | ||
expect(t.render('{{ comp.notDefined }}', ctx)).to.equal(''); | ||
expect(t.render('{{ comp.c.notDefined }}', ctx)).to.equal(''); | ||
expect(t.render('{{ comp.notDefined.notDefined }}', ctx)).to.equal(''); | ||
}); | ||
@@ -317,6 +326,7 @@ }); | ||
it('function calls', function() { | ||
expect(function() { t.render('{{ fx1() }}')}).to.throwError(); | ||
expect(function() { t.render('{{ fx2(10) }}')}).to.throwError(); | ||
expect(function() { t.render('{{ comp.c.fx() }}')}).to.throwError(); | ||
expect(function() { t.render('{{ (fx1( )) }}')}).to.throwError(); | ||
expect(t.render('{{ fx1() }}')).to.equal(''); | ||
expect(t.render('{{ fx2(10) }}')).to.equal(''); | ||
expect(t.render('{{ comp.c.fx() }}')).to.equal(''); | ||
expect(t.render('{{ (fx1( )) }}')).to.equal(''); | ||
expect(t.render('{{ Math.abs(-123) }}')).to.equal(''); | ||
}); | ||
@@ -323,0 +333,0 @@ }); |
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
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
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
1
49970
957