summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/handlebars/base.js3
-rw-r--r--lib/handlebars/compiler/javascript-compiler.js6
-rw-r--r--lib/handlebars/runtime.js1
-rw-r--r--lib/handlebars/utils.js4
-rw-r--r--lib/index.js1
-rw-r--r--spec/blocks.js13
-rw-r--r--spec/compiler.js70
-rw-r--r--spec/env/common.js2
-rw-r--r--spec/helpers.js43
-rw-r--r--spec/parser.js2
-rw-r--r--spec/partials.js3
-rw-r--r--spec/precompiler.js45
-rw-r--r--spec/regressions.js16
-rw-r--r--spec/utils.js16
-rw-r--r--src/handlebars.l5
15 files changed, 186 insertions, 44 deletions
diff --git a/lib/handlebars/base.js b/lib/handlebars/base.js
index 022417b..4eac7e9 100644
--- a/lib/handlebars/base.js
+++ b/lib/handlebars/base.js
@@ -67,7 +67,8 @@ function registerDefaultHelpers(instance) {
});
instance.registerHelper('blockHelperMissing', function(context, options) {
- var inverse = options.inverse || function() {}, fn = options.fn;
+ var inverse = options.inverse,
+ fn = options.fn;
if (isFunction(context)) { context = context.call(this); }
diff --git a/lib/handlebars/compiler/javascript-compiler.js b/lib/handlebars/compiler/javascript-compiler.js
index ef02cc7..d52f2a0 100644
--- a/lib/handlebars/compiler/javascript-compiler.js
+++ b/lib/handlebars/compiler/javascript-compiler.js
@@ -99,6 +99,7 @@ JavaScriptCompiler.prototype = {
// Flush any trailing content that might be pending.
this.pushSource('');
+ /* istanbul ignore next */
if (this.stackSlot || this.inlineStack.length || this.compileStack.length) {
throw new Exception('Compile completed with content left on stack');
}
@@ -551,10 +552,6 @@ JavaScriptCompiler.prototype = {
var helper = this.setupHelper(paramSize, name);
var lookup = (isSimple ? helper.name + ' || ' : '') + nonHelper + ' || helperMissing';
- if (helper.paramsInit) {
- lookup += ',' + helper.paramsInit;
- }
-
this.push('((' + lookup + ').call(' + helper.callParams + '))');
// Always flush subexpressions. This is both to prevent the compounding size issue that
@@ -849,6 +846,7 @@ JavaScriptCompiler.prototype = {
return item.value;
} else {
if (!inline) {
+ /* istanbul ignore next */
if (!this.stackSlot) {
throw new Exception('Invalid stack pop');
}
diff --git a/lib/handlebars/runtime.js b/lib/handlebars/runtime.js
index bfdb3b4..d351918 100644
--- a/lib/handlebars/runtime.js
+++ b/lib/handlebars/runtime.js
@@ -23,6 +23,7 @@ export function checkRevision(compilerInfo) {
// TODO: Remove this line and break up compilePartial
export function template(templateSpec, env) {
+ /* istanbul ignore next */
if (!env) {
throw new Exception("No environment passed to template");
}
diff --git a/lib/handlebars/utils.js b/lib/handlebars/utils.js
index f2f1a54..087183e 100644
--- a/lib/handlebars/utils.js
+++ b/lib/handlebars/utils.js
@@ -14,7 +14,7 @@ var badChars = /[&<>"'`]/g;
var possible = /[&<>"'`]/;
function escapeChar(chr) {
- return escape[chr] || "&amp;";
+ return escape[chr];
}
export function extend(obj /* , ...source */) {
@@ -37,6 +37,7 @@ var isFunction = function(value) {
return typeof value === 'function';
};
// fallback for older versions of Chrome and Safari
+/* istanbul ignore next */
if (isFunction(/x/)) {
isFunction = function(value) {
return typeof value === 'function' && toString.call(value) === '[object Function]';
@@ -44,6 +45,7 @@ if (isFunction(/x/)) {
}
export var isFunction;
+/* istanbul ignore next */
export var isArray = Array.isArray || function(value) {
return (value && typeof value === 'object') ? toString.call(value) === '[object Array]' : false;
};
diff --git a/lib/index.js b/lib/index.js
index e150524..790aab7 100644
--- a/lib/index.js
+++ b/lib/index.js
@@ -14,6 +14,7 @@ handlebars.print = printer.print;
module.exports = handlebars;
// Publish a Node.js require() handler for .handlebars and .hbs files
+/* istanbul ignore else */
if (typeof require !== 'undefined' && require.extensions) {
var extension = function(module, filename) {
var fs = require("fs");
diff --git a/spec/blocks.js b/spec/blocks.js
index a13cce2..061947a 100644
--- a/spec/blocks.js
+++ b/spec/blocks.js
@@ -39,6 +39,13 @@ describe('blocks', function() {
"Templates can access variables in contexts up the stack with relative path syntax");
});
+ it('multiple blocks with complex lookup', function() {
+ var string = '{{#goodbyes}}{{../name}}{{../name}}{{/goodbyes}}';
+ var hash = {name: 'Alan', goodbyes: [{text: 'goodbye'}, {text: 'Goodbye'}, {text: 'GOODBYE'}]};
+
+ shouldCompileTo(string, hash, 'AlanAlanAlanAlanAlanAlan');
+ });
+
it("block with complex lookup using nested context", function() {
var string = "{{#goodbyes}}{{text}} cruel {{foo/../name}}! {{/goodbyes}}";
@@ -48,10 +55,10 @@ describe('blocks', function() {
});
it("block with deep nested complex lookup", function() {
- var string = "{{#outer}}Goodbye {{#inner}}cruel {{../../omg}}{{/inner}}{{/outer}}";
- var hash = {omg: "OMG!", outer: [{ inner: [{ text: "goodbye" }] }] };
+ var string = "{{#outer}}Goodbye {{#inner}}cruel {{../sibling}} {{../../omg}}{{/inner}}{{/outer}}";
+ var hash = {omg: "OMG!", outer: [{ sibling: 'sad', inner: [{ text: "goodbye" }] }] };
- shouldCompileTo(string, hash, "Goodbye cruel OMG!");
+ shouldCompileTo(string, hash, "Goodbye cruel sad OMG!");
});
describe('inverted sections', function() {
diff --git a/spec/compiler.js b/spec/compiler.js
new file mode 100644
index 0000000..fa7635e
--- /dev/null
+++ b/spec/compiler.js
@@ -0,0 +1,70 @@
+/*global Handlebars, shouldThrow */
+
+describe('compiler', function() {
+ if (!Handlebars.compile) {
+ return;
+ }
+
+ describe('#equals', function() {
+ function compile(string) {
+ var ast = Handlebars.parse(string);
+ return new Handlebars.Compiler().compile(ast, {});
+ }
+
+ it('should treat as equal', function() {
+ equal(compile('foo').equals(compile('foo')), true);
+ equal(compile('{{foo}}').equals(compile('{{foo}}')), true);
+ equal(compile('{{foo.bar}}').equals(compile('{{foo.bar}}')), true);
+ equal(compile('{{foo.bar baz "foo" true false bat=1}}').equals(compile('{{foo.bar baz "foo" true false bat=1}}')), true);
+ equal(compile('{{foo.bar (baz bat=1)}}').equals(compile('{{foo.bar (baz bat=1)}}')), true);
+ equal(compile('{{#foo}} {{/foo}}').equals(compile('{{#foo}} {{/foo}}')), true);
+ });
+ it('should treat as not equal', function() {
+ equal(compile('foo').equals(compile('bar')), false);
+ equal(compile('{{foo}}').equals(compile('{{bar}}')), false);
+ equal(compile('{{foo.bar}}').equals(compile('{{bar.bar}}')), false);
+ equal(compile('{{foo.bar baz bat=1}}').equals(compile('{{foo.bar bar bat=1}}')), false);
+ equal(compile('{{foo.bar (baz bat=1)}}').equals(compile('{{foo.bar (bar bat=1)}}')), false);
+ equal(compile('{{#foo}} {{/foo}}').equals(compile('{{#bar}} {{/bar}}')), false);
+ equal(compile('{{#foo}} {{/foo}}').equals(compile('{{#foo}} {{foo}}{{/foo}}')), false);
+ });
+ });
+
+ describe('#compile', function() {
+ it('should fail with invalid input', function() {
+ shouldThrow(function() {
+ Handlebars.compile(null);
+ }, Error, 'You must pass a string or Handlebars AST to Handlebars.compile. You passed null');
+ shouldThrow(function() {
+ Handlebars.compile({});
+ }, Error, 'You must pass a string or Handlebars AST to Handlebars.compile. You passed [object Object]');
+ });
+
+ it('can utilize AST instance', function() {
+ equal(Handlebars.compile(new Handlebars.AST.ProgramNode(true, [ new Handlebars.AST.ContentNode("Hello")]))(), 'Hello');
+ });
+
+ it("can pass through an empty string", function() {
+ equal(Handlebars.compile('')(), '');
+ });
+ });
+
+ describe('#precompile', function() {
+ it('should fail with invalid input', function() {
+ shouldThrow(function() {
+ Handlebars.precompile(null);
+ }, Error, 'You must pass a string or Handlebars AST to Handlebars.precompile. You passed null');
+ shouldThrow(function() {
+ Handlebars.precompile({});
+ }, Error, 'You must pass a string or Handlebars AST to Handlebars.precompile. You passed [object Object]');
+ });
+
+ it('can utilize AST instance', function() {
+ equal(/return "Hello"/.test(Handlebars.precompile(new Handlebars.AST.ProgramNode(true, [ new Handlebars.AST.ContentNode("Hello")]))), true);
+ });
+
+ it("can pass through an empty string", function() {
+ equal(/return ""/.test(Handlebars.precompile('')), true);
+ });
+ });
+});
diff --git a/spec/env/common.js b/spec/env/common.js
index a603fc0..e551837 100644
--- a/spec/env/common.js
+++ b/spec/env/common.js
@@ -44,7 +44,7 @@ global.shouldThrow = function(callback, type, msg) {
throw new Error('Type failure');
}
if (msg && !(msg.test ? msg.test(err.message) : msg === err.message)) {
- throw new Error('Message failure');
+ equal(msg, err.message);
}
}
if (failed) {
diff --git a/spec/helpers.js b/spec/helpers.js
index d686b64..e3b5863 100644
--- a/spec/helpers.js
+++ b/spec/helpers.js
@@ -208,20 +208,41 @@ describe('helpers', function() {
});
});
- it("Multiple global helper registration", function() {
- var helpers = handlebarsEnv.helpers;
- handlebarsEnv.helpers = {};
+ describe('registration', function() {
+ it('unregisters', function() {
+ var helpers = handlebarsEnv.helpers;
+ handlebarsEnv.helpers = {};
- handlebarsEnv.registerHelper({
- 'if': helpers['if'],
- world: function() { return "world!"; },
- test_helper: function() { return 'found it!'; }
+ handlebarsEnv.registerHelper('foo', function() {
+ return 'fail';
+ });
+ handlebarsEnv.unregisterHelper('foo');
+ equals(handlebarsEnv.helpers.foo, undefined);
});
- shouldCompileTo(
- "{{test_helper}} {{#if cruel}}Goodbye {{cruel}} {{world}}!{{/if}}",
- [{cruel: "cruel"}],
- "found it! Goodbye cruel world!!");
+ it('allows multiple globals', function() {
+ var helpers = handlebarsEnv.helpers;
+ handlebarsEnv.helpers = {};
+
+ handlebarsEnv.registerHelper({
+ 'if': helpers['if'],
+ world: function() { return "world!"; },
+ test_helper: function() { return 'found it!'; }
+ });
+
+ shouldCompileTo(
+ "{{test_helper}} {{#if cruel}}Goodbye {{cruel}} {{world}}!{{/if}}",
+ [{cruel: "cruel"}],
+ "found it! Goodbye cruel world!!");
+ });
+ it('fails with multiple and args', function() {
+ shouldThrow(function() {
+ handlebarsEnv.registerHelper({
+ world: function() { return "world!"; },
+ test_helper: function() { return 'found it!'; }
+ }, {});
+ }, Error, 'Arg not supported with multiple helpers');
+ });
});
it("decimal number literals work", function() {
diff --git a/spec/parser.js b/spec/parser.js
index ff12cc4..076ce36 100644
--- a/spec/parser.js
+++ b/spec/parser.js
@@ -1,4 +1,4 @@
-/*global Handlebars */
+/*global Handlebars, shouldThrow */
describe('parser', function() {
if (!Handlebars.print) {
return;
diff --git a/spec/partials.js b/spec/partials.js
index 208d038..cc464b9 100644
--- a/spec/partials.js
+++ b/spec/partials.js
@@ -91,6 +91,9 @@ describe('partials', function() {
var dude = "{{name}}";
var hash = {name:"Jeepers", another_dude:"Creepers"};
shouldCompileToWithPartials(string, [hash, {}, {'shared/dude':dude}], true, "Dudes: Jeepers Creepers", "Partials can use globals or passed");
+
+ handlebarsEnv.unregisterPartial('global_test');
+ equals(handlebarsEnv.partials.global_test, undefined);
});
it("Multiple partial registration", function() {
diff --git a/spec/precompiler.js b/spec/precompiler.js
index 7cd1ffd..57fc280 100644
--- a/spec/precompiler.js
+++ b/spec/precompiler.js
@@ -10,9 +10,12 @@ describe('precompiler', function() {
Precompiler = require('../lib/precompiler');
var log,
- logFunction;
+ logFunction,
+
+ precompile;
beforeEach(function() {
+ precompile = Handlebars.precompile;
logFunction = console.log;
log = '';
console.log = function() {
@@ -20,6 +23,7 @@ describe('precompiler', function() {
};
});
afterEach(function() {
+ Handlebars.precompile = precompile;
console.log = logFunction;
});
@@ -37,4 +41,43 @@ describe('precompiler', function() {
Precompiler.cli({templates: ['foo']});
}, Handlebars.Exception, 'Unable to open template file "foo"');
});
+ it('should throw when combining simple and minimized', function() {
+ shouldThrow(function() {
+ Precompiler.cli({templates: [__dirname], simple: true, min: true});
+ }, Handlebars.Exception, 'Unable to minimze simple output');
+ });
+ it('should throw when combining simple and multiple templates', function() {
+ shouldThrow(function() {
+ Precompiler.cli({templates: [__dirname + '/artifacts/empty.handlebars', __dirname + '/artifacts/empty.handlebars'], simple: true});
+ }, Handlebars.Exception, 'Unable to output multiple templates in simple mode');
+ });
+ it('should throw when combining simple and directories', function() {
+ shouldThrow(function() {
+ Precompiler.cli({templates: [__dirname], simple: true});
+ }, Handlebars.Exception, 'Unable to output multiple templates in simple mode');
+ });
+ it('should enumerate directories by extension', function() {
+ Precompiler.cli({templates: [__dirname + '/artifacts'], extension: 'hbs'});
+ equal(/'example_2'/.test(log), true);
+ log = '';
+
+ Precompiler.cli({templates: [__dirname + '/artifacts'], extension: 'handlebars'});
+ equal(/'empty'/.test(log), true);
+ equal(/'example_1'/.test(log), true);
+ });
+ it('should output simple templates', function() {
+ Handlebars.precompile = function() { return 'simple'; };
+ Precompiler.cli({templates: [__dirname + '/artifacts/empty.handlebars'], simple: true, extension: 'handlebars'});
+ equal(log, 'simple\n');
+ });
+ it('should output amd templates', function() {
+ Handlebars.precompile = function() { return 'amd'; };
+ Precompiler.cli({templates: [__dirname + '/artifacts/empty.handlebars'], amd: true, extension: 'handlebars'});
+ equal(/template\(amd\)/.test(log), true);
+ });
+ it('should output commonjs templates', function() {
+ Handlebars.precompile = function() { return 'commonjs'; };
+ Precompiler.cli({templates: [__dirname + '/artifacts/empty.handlebars'], commonjs: true, extension: 'handlebars'});
+ equal(/template\(commonjs\)/.test(log), true);
+ });
});
diff --git a/spec/regressions.js b/spec/regressions.js
index dd4eedd..d4598cc 100644
--- a/spec/regressions.js
+++ b/spec/regressions.js
@@ -130,12 +130,6 @@ describe('Regressions', function() {
shouldCompileTo(string, data, "Hello Chris. You have just won $10000! Well, $6000, after taxes.", "the hello world mustache example works");
});
- it("Passing falsy values to Handlebars.compile throws an error", function() {
- shouldThrow(function() {
- CompilerContext.compile(null);
- }, Error, 'You must pass a string or Handlebars AST to Handlebars.precompile. You passed null');
- });
-
it('GH-731: zero context rendering', function() {
shouldCompileTo('{{#foo}} This is {{bar}} ~ {{/foo}}', {foo: 0, bar: 'OK'}, ' This is ~ ');
});
@@ -143,14 +137,4 @@ describe('Regressions', function() {
it('GH-820: zero pathed rendering', function() {
shouldCompileTo('{{foo.bar}}', {foo: 0}, '');
});
-
- if (Handlebars.AST) {
- it("can pass through an already-compiled AST via compile/precompile", function() {
- equal(Handlebars.compile(new Handlebars.AST.ProgramNode(true, [ new Handlebars.AST.ContentNode("Hello")]))(), 'Hello');
- });
-
- it("can pass through an empty string", function() {
- equal(Handlebars.compile('')(), '');
- });
- }
});
diff --git a/spec/utils.js b/spec/utils.js
index 390ad05..ea7d782 100644
--- a/spec/utils.js
+++ b/spec/utils.js
@@ -56,4 +56,20 @@ describe('utils', function() {
equals(Handlebars.Utils.isEmpty({bar: 1}), false);
});
});
+
+ describe('#extend', function() {
+ it('should ignore prototype values', function() {
+ function A() {
+ this.a = 1;
+ }
+ A.prototype.b = 4;
+
+ var b = {b: 2};
+
+ Handlebars.Utils.extend(b, new A());
+
+ equals(b.a, 1);
+ equals(b.b, 2);
+ });
+ });
});
diff --git a/src/handlebars.l b/src/handlebars.l
index 0a531d5..57ba12a 100644
--- a/src/handlebars.l
+++ b/src/handlebars.l
@@ -67,11 +67,6 @@ ID [^\s!"#%-,\.\/;->@\[-\^`\{-~]+/{LOOKAHEAD}
this.begin('raw');
return 'CLOSE_RAW_BLOCK';
}
-<mu>"{{{{"[^\x00]*"}}}}" {
- yytext = yytext.substr(4, yyleng-8);
- this.popState();
- return 'RAW_BLOCK';
- }
<mu>"{{"{LEFT_STRIP}?">" return 'OPEN_PARTIAL';
<mu>"{{"{LEFT_STRIP}?"#" return 'OPEN_BLOCK';
<mu>"{{"{LEFT_STRIP}?"/" return 'OPEN_ENDBLOCK';