diff --git a/components/bower.json b/components/bower.json index 840c77235..c67efbb5b 100644 --- a/components/bower.json +++ b/components/bower.json @@ -1,6 +1,6 @@ { "name": "handlebars", - "version": "4.0.5", + "version": "4.0.6", "main": "handlebars.js", "license": "MIT", "dependencies": {} diff --git a/components/handlebars.js.nuspec b/components/handlebars.js.nuspec index 9ede3d607..1145f057c 100644 --- a/components/handlebars.js.nuspec +++ b/components/handlebars.js.nuspec @@ -2,7 +2,7 @@ handlebars.js - 4.0.5 + 4.0.6 handlebars.js Authors https://github.com/wycats/handlebars.js/blob/master/LICENSE https://github.com/wycats/handlebars.js/ diff --git a/lib/handlebars/base.js b/lib/handlebars/base.js index 836422d1e..155e7ca04 100644 --- a/lib/handlebars/base.js +++ b/lib/handlebars/base.js @@ -4,7 +4,7 @@ import {registerDefaultHelpers} from './helpers'; import {registerDefaultDecorators} from './decorators'; import logger from './logger'; -export const VERSION = '4.0.5'; +export const VERSION = '4.0.6'; export const COMPILER_REVISION = 7; export const REVISION_CHANGES = { diff --git a/lib/handlebars/exception.js b/lib/handlebars/exception.js index 08ae531aa..b9a5557b6 100644 --- a/lib/handlebars/exception.js +++ b/lib/handlebars/exception.js @@ -31,7 +31,10 @@ function Exception(message, node) { // Work around issue under safari where we can't directly set the column value /* istanbul ignore next */ if (Object.defineProperty) { - Object.defineProperty(this, 'column', {value: column}); + Object.defineProperty(this, 'column', { + value: column, + enumerable: true + }); } else { this.column = column; } diff --git a/lib/handlebars/runtime.js b/lib/handlebars/runtime.js index 7426f1f5e..310b108e0 100644 --- a/lib/handlebars/runtime.js +++ b/lib/handlebars/runtime.js @@ -197,12 +197,7 @@ export function wrapProgram(container, i, fn, data, declaredBlockParams, blockPa export function resolvePartial(partial, context, options) { if (!partial) { if (options.name === '@partial-block') { - let data = options.data; - while (data['partial-block'] === noop) { - data = data._parent; - } - partial = data['partial-block']; - data['partial-block'] = noop; + partial = options.data['partial-block']; } else { partial = options.partials[options.name]; } @@ -215,15 +210,24 @@ export function resolvePartial(partial, context, options) { } export function invokePartial(partial, context, options) { + // Use the current closure context to save the partial-block if this partial + const currentPartialBlock = options.data && options.data['partial-block']; options.partial = true; let partialBlock; if (options.fn && options.fn !== noop) { options.data = createFrame(options.data); - partialBlock = options.data['partial-block'] = options.fn; - - if (partialBlock.partials) { - options.partials = Utils.extend({}, options.partials, partialBlock.partials); + // Wrapper function to get access to currentPartialBlock from the closure + let fn = options.fn; + partialBlock = options.data['partial-block'] = function partialBlockWrapper(context, options) { + // Restore the partial-block from the closure for the execution of the block + // i.e. the part inside the block of the partial call. + options.data = createFrame(options.data); + options.data['partial-block'] = currentPartialBlock; + return fn(context, options); + }; + if (fn.partials) { + options.partials = Utils.extend({}, options.partials, fn.partials); } } diff --git a/package.json b/package.json index acf847def..e8951e9e5 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "handlebars", "barename": "handlebars", - "version": "4.0.5", + "version": "4.0.6", "description": "Handlebars provides the power necessary to let you build semantic templates effectively with no frustration", "homepage": "http://www.handlebarsjs.com/", "keywords": [ diff --git a/release-notes.md b/release-notes.md index 497f5e898..8b073d520 100644 --- a/release-notes.md +++ b/release-notes.md @@ -2,7 +2,28 @@ ## Development -[Commits](https://github.com/wycats/handlebars.js/compare/v4.0.5...master) +[Commits](https://github.com/lawnsea/handlebars.js/compare/v4.0.6...master) + +## v4.0.6 - November 12th, 2016 +- [#1243](https://github.com/wycats/handlebars.js/pull/1243) - Walk up data frames for nested @partial-block ([@lawnsea](https://github.com/lawnsea)) +- [#1210](https://github.com/wycats/handlebars.js/pull/1210) - Add a new lightweight package based on handlebars in the README ([@kabirbaidhya](https://github.com/kabirbaidhya)) +- [#1187](https://github.com/wycats/handlebars.js/pull/1187) - Ensure that existing blockParams and depths are respected on dupe programs ([@charleso](https://github.com/charleso)) +- [#1191](https://github.com/wycats/handlebars.js/pull/1191) - Added cory ([@leo](https://github.com/leo)) +- [#1177](https://github.com/wycats/handlebars.js/pull/1177) - Preserve License info in Closure Compiler ([@gennadiylitvinyuk](https://github.com/gennadiylitvinyuk)) +- [#1171](https://github.com/wycats/handlebars.js/pull/1171) - Contributing doc fix: failing thats -> failing tests ([@paulfalgout](https://github.com/paulfalgout)) +- [#1166](https://github.com/wycats/handlebars.js/pull/1166) - Update license date ([@timwangdev](https://github.com/timwangdev)) +- Update jsfiddle to point to latest - 959ee55 (originally dfc7554 by [@kpdecker](https://github.com/kpdecker)) +- [#1163](https://github.com/wycats/handlebars.js/pull/1163) - Fix typos on decorators-api.md. ([@adjohnson916](https://github.com/adjohnson916)) +- Drop extra Error params - 8c19874 (originally 63fdb92 by [@kpdecker](https://github.com/kpdecker)) +- [#1153](https://github.com/wycats/handlebars.js/pull/1153) - Add documentation for running tests to contributing.md ([@ryanmurakami](https://github.com/ryanmurakami)) +- Avoid error in older browsers in test - 400916c (originally a6121ca by [@kpdecker](https://github.com/kpdecker)) +- Update target browser test versions - fee2334 (originally 871c32a by [@kpdecker](https://github.com/kpdecker)) +- Exclude coverage check in exception conditional - 32d6363 (originally 326734b by [@kpdecker](https://github.com/kpdecker)) +- Fix throw when creating exception object in Safari - 20c965c (originally 2ea6119 by [@kpdecker](https://github.com/kpdecker)) +- Update build for modern node versions - 6c9f98c (originally 8289c0b by [@kpdecker](https://github.com/kpdecker)) +- [#1135](https://github.com/wycats/handlebars.js/issues/1135) - Relax depth check for context push - c393c81 (originally 25458fd by [@kpdecker](https://github.com/kpdecker)) + +[Commits](https://github.com/wycats/handlebars.js/compare/v4.0.5...v4.0.6) ## v4.0.5 - November 19th, 2015 - [#1132](https://github.com/wycats/handlebars.js/pull/1132) - Update uglify-js to avoid vulnerability ([@plynchnlm](https://api.github.com/users/plynchnlm)) diff --git a/spec/compiler.js b/spec/compiler.js index 9ae7099d6..19b926f0e 100644 --- a/spec/compiler.js +++ b/spec/compiler.js @@ -38,6 +38,31 @@ describe('compiler', function() { }, Error, 'You must pass a string or Handlebars AST to Handlebars.compile. You passed [object Object]'); }); + it('should include the location in the error (row and column)', function() { + try { + Handlebars.compile(' \n {{#if}}\n{{/def}}')(); + equal(true, false, 'Statement must throw exception. This line should not be executed.'); + } catch (err) { + equal(err.message, 'if doesn\'t match def - 2:5', 'Checking error message'); + if (Object.getOwnPropertyDescriptor(err, 'column').writable) { + // In Safari 8, the column-property is read-only. This means that even if it is set with defineProperty, + // its value won't change (https://github.com/jquery/esprima/issues/1290#issuecomment-132455482) + // Since this was neither working in Handlebars 3 nor in 4.0.5, we only check the column for other browsers. + equal(err.column, 5, 'Checking error column'); + } + equal(err.lineNumber, 2, 'Checking error row'); + } + }); + + it('should include the location as enumerable property', function() { + try { + Handlebars.compile(' \n {{#if}}\n{{/def}}')(); + equal(true, false, 'Statement must throw exception. This line should not be executed.'); + } catch (err) { + equal(err.propertyIsEnumerable('column'), true, 'Checking error column'); + } + }); + it('can utilize AST instance', function() { equal(Handlebars.compile({ type: 'Program', diff --git a/spec/partials.js b/spec/partials.js index d6baba504..266837db8 100644 --- a/spec/partials.js +++ b/spec/partials.js @@ -249,6 +249,13 @@ describe('partials', function() { true, 'success'); }); + it('should be able to render the partial-block twice', function() { + shouldCompileToWithPartials( + '{{#> dude}}success{{/dude}}', + [{}, {}, {dude: '{{> @partial-block }} {{> @partial-block }}'}], + true, + 'success success'); + }); it('should render block from partial with context', function() { shouldCompileToWithPartials( '{{#> dude}}{{value}}{{/dude}}', @@ -256,6 +263,32 @@ describe('partials', function() { true, 'success'); }); + it('should allow the #each-helper to be used along with partial-blocks', function() { + shouldCompileToWithPartials( + '', + [ + {value: ['a', 'b', 'c']}, + {}, + { + list: '{{#each .}}{{> @partial-block}}{{/each}}' + } + ], + true, + ''); + }); + it('should render block from partial with context (twice)', function() { + shouldCompileToWithPartials( + '{{#> dude}}{{value}}{{/dude}}', + [ + {context: {value: 'success'}}, + {}, + { + dude: '{{#with context}}{{> @partial-block }} {{> @partial-block }}{{/with}}' + } + ], + true, + 'success success'); + }); it('should render block from partial with context', function() { shouldCompileToWithPartials( '{{#> dude}}{{../context/value}}{{/dude}}', @@ -284,6 +317,50 @@ describe('partials', function() { true, ''); }); + it('should render nested partial blocks at different nesting levels', function() { + shouldCompileToWithPartials( + '', + [ + {value: 'success'}, + {}, + { + outer: '{{#> nested}}{{> @partial-block}}{{/nested}}{{> @partial-block}}', + nested: '{{> @partial-block}}' + } + ], + true, + ''); + }); + it('should render nested partial blocks at different nesting levels (twice)', function() { + shouldCompileToWithPartials( + '', + [ + {value: 'success'}, + {}, + { + outer: '{{#> nested}}{{> @partial-block}} {{> @partial-block}}{{/nested}}{{> @partial-block}}+{{> @partial-block}}', + nested: '{{> @partial-block}}' + } + ], + true, + ''); + }); + it('should render nested partial blocks (twice at each level)', function() { + shouldCompileToWithPartials( + '', + [ + {value: 'success'}, + {}, + { + outer: '{{#> nested}}{{> @partial-block}} {{> @partial-block}}{{/nested}}', + nested: '{{> @partial-block}}{{> @partial-block}}' + } + ], + true, + ''); + }); }); describe('inline partials', function() { @@ -332,6 +409,24 @@ describe('partials', function() { true, 'success'); }); + it('should render nested inline partials with partial-blocks on different nesting levels', function() { + shouldCompileToWithPartials( + '{{#*inline "outer"}}{{#>inner}}{{>@partial-block}}{{/inner}}{{>@partial-block}}{{/inline}}' + + '{{#*inline "inner"}}{{>@partial-block}}{{/inline}}' + + '{{#>outer}}{{value}}{{/outer}}', + [{value: 'success'}, {}, {}], + true, + 'successsuccess'); + }); + it('should render nested inline partials (twice at each level)', function() { + shouldCompileToWithPartials( + '{{#*inline "outer"}}{{#>inner}}{{>@partial-block}} {{>@partial-block}}{{/inner}}{{/inline}}' + + '{{#*inline "inner"}}{{>@partial-block}}{{>@partial-block}}{{/inline}}' + + '{{#>outer}}{{value}}{{/outer}}', + [{value: 'success'}, {}, {}], + true, + 'success successsuccess success'); + }); }); it('should pass compiler flags', function() {