diff options
author | kpdecker <kpdecker@gmail.com> | 2015-08-03 15:15:09 -0500 |
---|---|---|
committer | kpdecker <kpdecker@gmail.com> | 2015-08-03 15:15:09 -0500 |
commit | 279e038ba7ff7e5966a60e36d326e2d4c310f0c6 (patch) | |
tree | fe96ba921f7f8dde4492df5ecbb761d8290f837f | |
parent | 1c08771215af7377fb8f33a26f64b3af0e06c168 (diff) | |
download | handlebars.js-279e038ba7ff7e5966a60e36d326e2d4c310f0c6.zip handlebars.js-279e038ba7ff7e5966a60e36d326e2d4c310f0c6.tar.gz handlebars.js-279e038ba7ff7e5966a60e36d326e2d4c310f0c6.tar.bz2 |
Avoid depth creation when context remains the same
Creating a new depth value seems to confuse users as they don’t expect things like `if` to require multiple `..` to break out of. With the change, we avoid pushing a context to the depth list if it’s already on the top of the stack, effectively removing cases where `.` and `..` are the same object and multiple `..` references are required.
This is a breaking change and all templates that utilize `..` will have to check their usage and confirm that this does not break desired behavior. Helper authors now need to take care to return the same context value whenever it is conceptually the same and to avoid behaviors that may execute children under the current context in some situations and under different contexts under other situations.
Fixes #1028
-rw-r--r-- | lib/handlebars/runtime.js | 13 | ||||
-rw-r--r-- | spec/builtins.js | 5 | ||||
-rw-r--r-- | spec/helpers.js | 15 |
3 files changed, 30 insertions, 3 deletions
diff --git a/lib/handlebars/runtime.js b/lib/handlebars/runtime.js index d41b42a..bc12fc9 100644 --- a/lib/handlebars/runtime.js +++ b/lib/handlebars/runtime.js @@ -135,7 +135,11 @@ export function template(templateSpec, env) { let depths, blockParams = templateSpec.useBlockParams ? [] : undefined; if (templateSpec.useDepths) { - depths = options.depths ? [context].concat(options.depths) : [context]; + if (options.depths) { + depths = context !== options.depths[0] ? [context].concat(depths) : options.depths; + } else { + depths = [context]; + } } return templateSpec.main.call(container, context, container.helpers, container.partials, data, blockParams, depths); @@ -170,12 +174,17 @@ export function template(templateSpec, env) { export function wrapProgram(container, i, fn, data, declaredBlockParams, blockParams, depths) { function prog(context, options = {}) { + let currentDepths = depths; + if (depths && context !== depths[0]) { + currentDepths = [context].concat(depths); + } + return fn.call(container, context, container.helpers, container.partials, options.data || data, blockParams && [options.blockParams].concat(blockParams), - depths && [context].concat(depths)); + currentDepths); } prog.program = i; prog.depth = depths ? depths.length : 0; diff --git a/spec/builtins.js b/spec/builtins.js index bcacf59..a7f6204 100644 --- a/spec/builtins.js +++ b/spec/builtins.js @@ -32,6 +32,11 @@ describe('builtin helpers', function() { shouldCompileTo(string, {goodbye: function() {return this.foo; }, world: 'world'}, 'cruel world!', 'if with function does not show the contents when returns undefined'); }); + + it('should not change the depth list', function() { + var string = '{{#with foo}}{{#if goodbye}}GOODBYE cruel {{../world}}!{{/if}}{{/with}}'; + shouldCompileTo(string, {foo: {goodbye: true}, world: 'world'}, 'GOODBYE cruel world!'); + }); }); describe('#with', function() { diff --git a/spec/helpers.js b/spec/helpers.js index 00bcb79..94e503f 100644 --- a/spec/helpers.js +++ b/spec/helpers.js @@ -38,6 +38,19 @@ describe('helpers', function() { shouldCompileTo(string, [{}, helpers], ' {{{{b}}}} {{{{/b}}}} ', 'raw block helper should get nested raw block as raw content'); }); + it('helper block with identical context', function() { + var string = '{{#goodbyes}}{{name}}{{/goodbyes}}'; + var hash = {name: 'Alan'}; + var helpers = {goodbyes: function(options) { + var out = ''; + var byes = ['Goodbye', 'goodbye', 'GOODBYE']; + for (var i = 0, j = byes.length; i < j; i++) { + out += byes[i] + ' ' + options.fn(this) + '! '; + } + return out; + }}; + shouldCompileTo(string, [hash, helpers], 'Goodbye Alan! goodbye Alan! GOODBYE Alan! '); + }); it('helper block with complex lookup expression', function() { var string = '{{#goodbyes}}{{../name}}{{/goodbyes}}'; var hash = {name: 'Alan'}; @@ -45,7 +58,7 @@ describe('helpers', function() { var out = ''; var byes = ['Goodbye', 'goodbye', 'GOODBYE']; for (var i = 0, j = byes.length; i < j; i++) { - out += byes[i] + ' ' + options.fn(this) + '! '; + out += byes[i] + ' ' + options.fn({}) + '! '; } return out; }}; |