diff --git a/doT.js b/doT.js index c61400a..eef3ef1 100644 --- a/doT.js +++ b/doT.js @@ -129,7 +129,23 @@ + doT.encodeHTMLSource.toString() + "(" + (c.doNotSkipEncoded || '') + "));" + str; } + try { + if (c.varname.length === 0) { + var escapedFunctionBody = str + .replace(/\\'/g, "\\\\'") + .replace(/\\"/g, '\\\\"') + .replace(/(?!\\)'/g, "\\'") + .replace(/(?!\\)"/g, '\\"') + .replace(/\r/g, "\\r") + .replace(/\n/g, "\\n") + .replace(/\t/g, "\\t"); + + return new Function('it', (function (str) { + return 'return (new Function("{" + ' + "Object.keys(it).join(', ')" + ' + "}", "' + str + '"))(it)'; + })(escapedFunctionBody)); + } + return new Function(c.varname, str); } catch (e) { /* istanbul ignore else */ diff --git a/test/noContext.test.js b/test/noContext.test.js new file mode 100644 index 0000000..499e189 --- /dev/null +++ b/test/noContext.test.js @@ -0,0 +1,157 @@ +'use strict'; + +var test = require('./util').testWithoutContext; +var assert = require("assert") +var doT = require(".."); + +var settings = { + evaluate: /\{\{([\s\S]+?(\}?)+)\}\}/g, + interpolate: /\{\{=([\s\S]+?)\}\}/g, + encode: /\{\{!([\s\S]+?)\}\}/g, + use: /\{\{#([\s\S]+?)\}\}/g, + useParams: /(^|[^\w$])def(?:\.|\[[\'\"])([\w$\.]+)(?:[\'\"]\])?\s*\:\s*([\w$\.]+|\"[^\"]+\"|\'[^\']+\'|\{[^\}]+\})/g, + define: /\{\{##\s*([\w\.$]+)\s*(\:|=)([\s\S]+?)#\}\}/g, + defineParams:/^\s*([\w$]+):([\s\S]+)/, + conditional: /\{\{\?(\?)?\s*([\s\S]*?)\s*\}\}/g, + iterate: /\{\{~\s*(?:\}\}|([\s\S]+?)\s*\:\s*([\w$]+)\s*(?:\:\s*([\w$]+))?\s*\}\})/g, + varname: "", + strip: true, + append: true, + selfcontained: false, + doNotSkipEncoded: false +}; + +describe('noContext', function(){ + describe('evaluate JavaScript', function() { + it('should print numbers next to each other', function() { + test([ + '{{ one = 1; two = 2; }}{{= one }}{{= two }}', + ], {}, '12'); + }); + }); + + describe('interpolate 2 numbers', function() { + it('should print numbers next to each other', function() { + test([ + '{{=one}}{{=two}}', + '{{= one}}{{= two}}', + '{{= one }}{{= two }}' + ], {one:1, two: 2}, '12'); + }); + }); + + describe('encoding with doNotSkipEncoded=false', function() { + it('should not replace &', function() { + global._encodeHTML = undefined; + doT.templateSettings.doNotSkipEncoded = false; + var fn = doT.template('
{{!foo}}
', settings); + assert.equal(fn({foo:"&"}), "
&
"); + }); + }); + + describe('encoding with doNotSkipEncoded=true', function() { + it('should replace &', function() { + global._encodeHTML = undefined; + settings.doNotSkipEncoded = true; + assert.equal(doT.template('
{{!foo}}
', settings)({foo:"&"}), "
&
"); + assert.equal(doT.template('{{!a}}', settings)({a:"& < > / ' \""}), "& < > / ' ""); + assert.equal(doT.template('{{!"& < > / \' \\""}}')(), "& < > / ' ""); + }); + }); + + describe('invalid JS in templates', function() { + it('should throw exception', function() { + assert.throws(function() { + var fn = doT.template('
{{= foo + }}
', settings)(); + }); + }); + }); + + describe('conditionals', function() { + describe('without else', function() { + var templates = [ + '{{?one < 2}}{{=one}}{{?}}{{=two}}', + '{{? one < 2 }}{{= one }}{{?}}{{= two }}' + ]; + + it('should evaluate condition and include template if valid', function() { + test(templates, {one: 1, two: 2}, '12') + }); + + it('should evaluate condition and do NOT include template if invalid', function() { + test(templates, {one: 3, two: 2}, '2') + }); + }); + + + describe('with else', function() { + var templates = [ + '{{?one < 2}}{{=one}}{{??}}{{=two}}{{?}}', + '{{? one < 2 }}{{= one }}{{??}}{{= two }}{{?}}' + ]; + + it('should evaluate condition and include "if" template if valid', function() { + test(templates, {one: 1, two: 2}, '1') + }); + + it('should evaluate condition and include "else" template if invalid', function() { + test(templates, {one: 3, two: 2}, '2') + }); + }); + + describe('with else if', function() { + var templates = [ + '{{?one < 2}}{{=one}}{{??two < 3}}{{=two}}{{??}}{{=three}}{{?}}', + '{{? one < 2 }}{{= one }}{{?? two < 3 }}{{= two }}{{??}}{{= three }}{{?}}' + ]; + + it('should evaluate condition and include "if" template if valid', function() { + test(templates, {one: 1, two: 2, three: 3}, '1') + }); + + it('should evaluate condition and include "else if" template if second condition valid', function() { + test(templates, {one: 10, two: 2, three: 3}, '2') + }); + + it('should evaluate condition and include "else" template if invalid', function() { + test(templates, {one: 10, two: 20, three: 3}, '3') + }); + }); + }); + + describe('iteration', function() { + describe('without index', function() { + it('should repeat string N times', function() { + test([ + '{{~arr:x}}*{{~}}', + '{{~ arr:x }}*{{~}}', + '{{~ arr: x }}*{{~}}', + '{{~ arr :x }}*{{~}}' + ], {arr: Array(3)}, '***'); + }); + + it('should concatenate items', function() { + test(['{{~arr:x}}{{=x}}{{~}}'], {arr: [1,2,3]}, '123'); + }); + }); + + describe('with index', function() { + it('should repeat string N times', function() { + test([ + '{{~arr:x:i}}*{{~}}', + '{{~ arr : x : i }}*{{~}}' + ], {arr: Array(3)}, '***'); + }); + + it('should concatenate indices', function() { + test(['{{~arr:x:i}}{{=i}}{{~}}'], {arr: Array(3)}, '012'); + }); + + it('should concatenate indices and items', function() { + test([ + '{{~arr:x:i}}{{?i}}, {{?}}{{=i}}:{{=x}}{{~}}' + ], {arr: [10,20,30]}, '0:10, 1:20, 2:30'); + }); + }); + }); +}) diff --git a/test/util.js b/test/util.js index b676b28..7775d14 100644 --- a/test/util.js +++ b/test/util.js @@ -9,3 +9,27 @@ exports.test = function (templates, data, result) { assert.strictEqual(fn(data), result); }); }; + +exports.testWithoutContext = function (templates, data, result) { + var settings = { + evaluate: /\{\{([\s\S]+?(\}?)+)\}\}/g, + interpolate: /\{\{=([\s\S]+?)\}\}/g, + encode: /\{\{!([\s\S]+?)\}\}/g, + use: /\{\{#([\s\S]+?)\}\}/g, + useParams: /(^|[^\w$])def(?:\.|\[[\'\"])([\w$\.]+)(?:[\'\"]\])?\s*\:\s*([\w$\.]+|\"[^\"]+\"|\'[^\']+\'|\{[^\}]+\})/g, + define: /\{\{##\s*([\w\.$]+)\s*(\:|=)([\s\S]+?)#\}\}/g, + defineParams:/^\s*([\w$]+):([\s\S]+)/, + conditional: /\{\{\?(\?)?\s*([\s\S]*?)\s*\}\}/g, + iterate: /\{\{~\s*(?:\}\}|([\s\S]+?)\s*\:\s*([\w$]+)\s*(?:\:\s*([\w$]+))?\s*\}\})/g, + varname: "", + strip: true, + append: true, + selfcontained: false, + doNotSkipEncoded: false + }; + + templates.forEach(function (tmpl) { + var fn = doT.template(tmpl, settings); + assert.strictEqual(fn(data), result); + }); +};