summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/handlebars/compiler/compiler.js56
-rw-r--r--spec/qunit_spec.js40
2 files changed, 85 insertions, 11 deletions
diff --git a/lib/handlebars/compiler/compiler.js b/lib/handlebars/compiler/compiler.js
index 26df606..6d8c603 100644
--- a/lib/handlebars/compiler/compiler.js
+++ b/lib/handlebars/compiler/compiler.js
@@ -94,7 +94,24 @@ Handlebars.JavaScriptCompiler = function() {};
compile: function(program, options) {
this.children = [];
this.depths = {list: []};
- this.options = options || {};
+ this.options = options;
+
+ // These changes will propagate to the other compiler components
+ var knownHelpers = this.options.knownHelpers;
+ this.options.knownHelpers = {
+ 'helperMissing': true,
+ 'blockHelperMissing': true,
+ 'each': true,
+ 'if': true,
+ 'unless': true,
+ 'with': true
+ };
+ if (knownHelpers) {
+ for (var name in knownHelpers) {
+ this.options.knownHelpers[name] = knownHelpers[name];
+ }
+ }
+
return this.program(program);
},
@@ -475,9 +492,13 @@ Handlebars.JavaScriptCompiler = function() {};
if(name) {
var topStack = this.nextStack();
- var toPush;
+ this.usingKnownHelper = false;
- if (isScoped) {
+ var toPush;
+ if (!isScoped && this.options.knownHelpers[name]) {
+ toPush = topStack + " = " + this.nameLookup('helpers', name, 'helper');
+ this.usingKnownHelper = true;
+ } else if (isScoped || this.options.knownHelpersOnly) {
toPush = topStack + " = " + this.nameLookup('depth' + this.lastContext, name, 'context');
} else {
toPush = topStack + " = "
@@ -512,10 +533,12 @@ Handlebars.JavaScriptCompiler = function() {};
invokeMustache: function(paramSize, original) {
this.populateParams(paramSize, this.quotedString(original), "{}", null, function(nextStack, helperMissingString, id) {
- this.context.aliases.helperMissing = 'helpers.helperMissing';
- this.context.aliases.undef = 'void 0';
- this.source.push("else if(" + id + "=== undef) { " + nextStack + " = helperMissing.call(" + helperMissingString + "); }");
- this.source.push("else { " + nextStack + " = " + id + "; }");
+ if (!this.usingKnownHelper) {
+ this.context.aliases.helperMissing = 'helpers.helperMissing';
+ this.context.aliases.undef = 'void 0';
+ this.source.push("else if(" + id + "=== undef) { " + nextStack + " = helperMissing.call(" + helperMissingString + "); }");
+ this.source.push("else { " + nextStack + " = " + id + "; }");
+ }
});
},
@@ -524,8 +547,10 @@ Handlebars.JavaScriptCompiler = function() {};
var mainProgram = this.programExpression(guid);
this.populateParams(paramSize, null, mainProgram, inverse, function(nextStack, helperMissingString, id) {
- this.context.aliases.blockHelperMissing = 'helpers.blockHelperMissing';
- this.source.push("else { " + nextStack + " = blockHelperMissing.call(" + helperMissingString + "); }");
+ if (!this.usingKnownHelper) {
+ this.context.aliases.blockHelperMissing = 'helpers.blockHelperMissing';
+ this.source.push("else { " + nextStack + " = blockHelperMissing.call(" + helperMissingString + "); }");
+ }
});
},
@@ -571,9 +596,14 @@ Handlebars.JavaScriptCompiler = function() {};
var nextStack = this.nextStack();
- this.context.aliases.functionType = '"function"';
- this.source.push("if(typeof " + id + " === functionType) { " + nextStack + " = " + id + ".call(" + paramString + "); }");
+ if (this.usingKnownHelper) {
+ this.source.push(nextStack + " = " + id + ".call(" + paramString + ");");
+ } else {
+ this.context.aliases.functionType = '"function"';
+ this.source.push("if(typeof " + id + " === functionType) { " + nextStack + " = " + id + ".call(" + paramString + "); }");
+ }
fn.call(this, nextStack, helperMissingString, id);
+ this.usingKnownHelper = false;
},
invokePartial: function(context) {
@@ -681,12 +711,16 @@ Handlebars.JavaScriptCompiler = function() {};
})(Handlebars.Compiler, Handlebars.JavaScriptCompiler);
Handlebars.precompile = function(string, options) {
+ options = options || {};
+
var ast = Handlebars.parse(string);
var environment = new Handlebars.Compiler().compile(ast, options);
return new Handlebars.JavaScriptCompiler().compile(environment, options);
};
Handlebars.compile = function(string, options) {
+ options = options || {};
+
var ast = Handlebars.parse(string);
var environment = new Handlebars.Compiler().compile(ast, options);
var templateSpec = new Handlebars.JavaScriptCompiler().compile(environment, options, undefined, true);
diff --git a/spec/qunit_spec.js b/spec/qunit_spec.js
index ba8173d..1774bbf 100644
--- a/spec/qunit_spec.js
+++ b/spec/qunit_spec.js
@@ -519,6 +519,46 @@ test("if a context is not found, helperMissing is used", function() {
shouldCompileTo(string, context, "Hello <a>world</a>")
});
+module("knownHelpers");
+
+test("Known helper should render helper", function() {
+ var template = CompilerContext.compile("{{hello}}", {knownHelpers: {"hello" : true}})
+
+ var result = template({}, {helpers: {hello: function() { return "foo"; }}});
+ equal(result, "foo", "'foo' should === '" + result);
+});
+
+test("Unknown helper in knownHelpers only mode should be passed as undefined", function() {
+ var template = CompilerContext.compile("{{typeof hello}}", {knownHelpers: {'typeof': true}, knownHelpersOnly: true})
+
+ var result = template({}, {helpers: {'typeof': function(arg) { return typeof arg; }, hello: function() { return "foo"; }}});
+ equal(result, "undefined", "'undefined' should === '" + result);
+});
+test("Builtin helpers available in knownHelpers only mode", function() {
+ var template = CompilerContext.compile("{{#unless foo}}bar{{/unless}}", {knownHelpersOnly: true})
+
+ var result = template({});
+ equal(result, "bar", "'bar' should === '" + result);
+});
+test("Field lookup works in knownHelpers only mode", function() {
+ var template = CompilerContext.compile("{{foo}}", {knownHelpersOnly: true})
+
+ var result = template({foo: 'bar'});
+ equal(result, "bar", "'bar' should === '" + result);
+});
+test("Conditional blocks work in knownHelpers only mode", function() {
+ var template = CompilerContext.compile("{{#foo}}bar{{/foo}}", {knownHelpersOnly: true})
+
+ var result = template({foo: 'baz'});
+ equal(result, "bar", "'bar' should === '" + result);
+});
+test("Invert blocks work in knownHelpers only mode", function() {
+ var template = CompilerContext.compile("{{^foo}}bar{{/foo}}", {knownHelpersOnly: true})
+
+ var result = template({foo: false});
+ equal(result, "bar", "'bar' should === '" + result);
+});
+
module("built-in helpers");
test("with", function() {