Merge branch '4.x'
# Conflicts: # components/bower.json # components/handlebars.js.nuspec # components/package.json # package-lock.json # package.json
This commit is contained in:
+2
-1
@@ -95,6 +95,7 @@ Handlebars utilizes the [release yeoman generator][generator-release] to perform
|
||||
A full release may be completed with the following:
|
||||
|
||||
```
|
||||
npm ci
|
||||
yo release
|
||||
npm publish
|
||||
yo release:publish components handlebars.js dist/components/
|
||||
@@ -109,7 +110,7 @@ in those places still point to the latest version
|
||||
|
||||
* [The npm-package](https://www.npmjs.com/package/handlebars) (check latest-tag)
|
||||
* [The bower package](https://github.com/components/handlebars.js) (check the package.json)
|
||||
* [The AWS S3 Bucket](http://builds.handlebarsjs.com.s3.amazonaws.com/) (check latest-tag)
|
||||
* [The AWS S3 Bucket](https://s3.amazonaws.com/builds.handlebarsjs.com) (check latest-tag)
|
||||
* [RubyGems](https://rubygems.org/gems/handlebars-source)
|
||||
|
||||
When everything is OK, the handlebars site needs to be updated to point to the new version numbers. The jsfiddle link should be updated to point to the most recent distribution for all instances in our documentation.
|
||||
|
||||
+1
-1
@@ -194,7 +194,7 @@ module.exports = function(grunt) {
|
||||
grunt.loadNpmTasks('grunt-babel');
|
||||
grunt.loadNpmTasks('grunt-bg-shell');
|
||||
grunt.loadNpmTasks('grunt-eslint');
|
||||
grunt.loadNpmTasks('grunt-saucelabs');
|
||||
grunt.loadNpmTasks('@knappi/grunt-saucelabs');
|
||||
grunt.loadNpmTasks('grunt-webpack');
|
||||
|
||||
grunt.task.loadTasks('tasks');
|
||||
|
||||
@@ -4,8 +4,9 @@ import {registerDefaultHelpers} from './helpers';
|
||||
import {registerDefaultDecorators} from './decorators';
|
||||
import logger from './logger';
|
||||
|
||||
export const VERSION = '4.3.0';
|
||||
export const VERSION = '4.4.3';
|
||||
export const COMPILER_REVISION = 8;
|
||||
export const LAST_COMPATIBLE_COMPILER_REVISION = 7;
|
||||
|
||||
export const REVISION_CHANGES = {
|
||||
1: '<= 1.0.rc.2', // 1.0.rc.2 is actually rev2 but doesn't report it
|
||||
|
||||
@@ -13,13 +13,19 @@ JavaScriptCompiler.prototype = {
|
||||
// PUBLIC API: You can override these methods in a subclass to provide
|
||||
// alternative compiled forms for name lookup and buffering semantics
|
||||
nameLookup: function(parent, name/* , type*/) {
|
||||
const isEnumerable = [ this.aliasable('container.propertyIsEnumerable'), '.call(', parent, ',"constructor")'];
|
||||
|
||||
if (name === 'constructor') {
|
||||
return ['(', parent, '.propertyIsEnumerable(\'constructor\') ? ', parent, '.constructor : undefined', ')'];
|
||||
return ['(', isEnumerable, '?', _actualLookup(), ' : undefined)'];
|
||||
}
|
||||
if (JavaScriptCompiler.isValidJavaScriptVariableName(name)) {
|
||||
return [parent, '.', name];
|
||||
} else {
|
||||
return [parent, '[', JSON.stringify(name), ']'];
|
||||
return _actualLookup();
|
||||
|
||||
function _actualLookup() {
|
||||
if (JavaScriptCompiler.isValidJavaScriptVariableName(name)) {
|
||||
return [parent, '.', name];
|
||||
} else {
|
||||
return [parent, '[', JSON.stringify(name), ']'];
|
||||
}
|
||||
}
|
||||
},
|
||||
depthedLookup: function(name) {
|
||||
@@ -212,7 +218,6 @@ JavaScriptCompiler.prototype = {
|
||||
let aliasCount = 0;
|
||||
for (let alias in this.aliases) { // eslint-disable-line guard-for-in
|
||||
let node = this.aliases[alias];
|
||||
|
||||
if (this.aliases.hasOwnProperty(alias) && node.children && node.referenceCount > 1) {
|
||||
varDeclarations += ', alias' + (++aliasCount) + '=' + alias;
|
||||
node.children[0] = 'alias' + aliasCount;
|
||||
@@ -337,9 +342,9 @@ JavaScriptCompiler.prototype = {
|
||||
params.splice(1, 0, current);
|
||||
|
||||
this.pushSource([
|
||||
'if (!', this.lastHelper, ') { ',
|
||||
current, ' = ', this.source.functionCall(blockHelperMissing, 'call', params),
|
||||
'}']);
|
||||
'if (!', this.lastHelper, ') { ',
|
||||
current, ' = ', this.source.functionCall(blockHelperMissing, 'call', params),
|
||||
'}']);
|
||||
},
|
||||
|
||||
// [appendContent]
|
||||
@@ -646,16 +651,16 @@ JavaScriptCompiler.prototype = {
|
||||
if (!this.options.strict) {
|
||||
lookup[0] = '(helper = ';
|
||||
lookup.push(
|
||||
' != null ? helper : ',
|
||||
this.aliasable('container.hooks.helperMissing')
|
||||
' != null ? helper : ',
|
||||
this.aliasable('container.hooks.helperMissing')
|
||||
);
|
||||
}
|
||||
|
||||
this.push([
|
||||
'(', lookup,
|
||||
(helper.paramsInit ? ['),(', helper.paramsInit] : []), '),',
|
||||
'(typeof helper === ', this.aliasable('"function"'), ' ? ',
|
||||
this.source.functionCall('helper', 'call', helper.callParams), ' : helper))'
|
||||
'(', lookup,
|
||||
(helper.paramsInit ? ['),(', helper.paramsInit] : []), '),',
|
||||
'(typeof helper === ', this.aliasable('"function"'), ' ? ',
|
||||
this.source.functionCall('helper', 'call', helper.callParams), ' : helper))'
|
||||
]);
|
||||
},
|
||||
|
||||
|
||||
@@ -40,6 +40,16 @@ export default function(instance) {
|
||||
execIteration(i, i, i === context.length - 1);
|
||||
}
|
||||
}
|
||||
} else if (global.Symbol && context[global.Symbol.iterator]) {
|
||||
const newContext = [];
|
||||
const iterator = context[global.Symbol.iterator]();
|
||||
for (let it = iterator.next(); !it.done; it = iterator.next()) {
|
||||
newContext.push(it.value);
|
||||
}
|
||||
context = newContext;
|
||||
for (let j = context.length; i < j; i++) {
|
||||
execIteration(i, i, i === context.length - 1);
|
||||
}
|
||||
} else {
|
||||
let priorKey;
|
||||
|
||||
|
||||
+21
-15
@@ -1,23 +1,25 @@
|
||||
import * as Utils from './utils';
|
||||
import Exception from './exception';
|
||||
import {COMPILER_REVISION, createFrame, REVISION_CHANGES} from './base';
|
||||
import {COMPILER_REVISION, createFrame, LAST_COMPATIBLE_COMPILER_REVISION, REVISION_CHANGES} from './base';
|
||||
import {moveHelperToHooks} from './helpers';
|
||||
|
||||
export function checkRevision(compilerInfo) {
|
||||
const compilerRevision = compilerInfo && compilerInfo[0] || 1,
|
||||
currentRevision = COMPILER_REVISION;
|
||||
|
||||
if (compilerRevision !== currentRevision) {
|
||||
if (compilerRevision < currentRevision) {
|
||||
const runtimeVersions = REVISION_CHANGES[currentRevision],
|
||||
compilerVersions = REVISION_CHANGES[compilerRevision];
|
||||
throw new Exception('Template was precompiled with an older version of Handlebars than the current runtime. ' +
|
||||
'Please update your precompiler to a newer version (' + runtimeVersions + ') or downgrade your runtime to an older version (' + compilerVersions + ').');
|
||||
} else {
|
||||
// Use the embedded version info since the runtime doesn't know about this revision yet
|
||||
throw new Exception('Template was precompiled with a newer version of Handlebars than the current runtime. ' +
|
||||
'Please update your runtime to a newer version (' + compilerInfo[1] + ').');
|
||||
}
|
||||
if (compilerRevision >= LAST_COMPATIBLE_COMPILER_REVISION && compilerRevision <= COMPILER_REVISION) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (compilerRevision < LAST_COMPATIBLE_COMPILER_REVISION) {
|
||||
const runtimeVersions = REVISION_CHANGES[currentRevision],
|
||||
compilerVersions = REVISION_CHANGES[compilerRevision];
|
||||
throw new Exception('Template was precompiled with an older version of Handlebars than the current runtime. ' +
|
||||
'Please update your precompiler to a newer version (' + runtimeVersions + ') or downgrade your runtime to an older version (' + compilerVersions + ').');
|
||||
} else {
|
||||
// Use the embedded version info since the runtime doesn't know about this revision yet
|
||||
throw new Exception('Template was precompiled with a newer version of Handlebars than the current runtime. ' +
|
||||
'Please update your runtime to a newer version (' + compilerInfo[1] + ').');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,6 +39,9 @@ export function template(templateSpec, env) {
|
||||
// for external users to override these as pseudo-supported APIs.
|
||||
env.VM.checkRevision(templateSpec.compiler);
|
||||
|
||||
// backwards compatibility for precompiled templates with compiler-version 7 (<4.3.0)
|
||||
const templateWasPrecompiledWithCompilerV7 = templateSpec.compiler && templateSpec.compiler[0] === 7;
|
||||
|
||||
function invokePartialWrapper(partial, context, options) {
|
||||
if (options.hash) {
|
||||
context = Utils.extend({}, context, options.hash);
|
||||
@@ -160,9 +165,10 @@ export function template(templateSpec, env) {
|
||||
}
|
||||
|
||||
container.hooks = {};
|
||||
let keepHelper = options.allowCallsToHelperMissing;
|
||||
moveHelperToHooks(container, 'helperMissing', keepHelper);
|
||||
moveHelperToHooks(container, 'blockHelperMissing', keepHelper);
|
||||
|
||||
let keepHelperInHelpers = options.allowCallsToHelperMissing || templateWasPrecompiledWithCompilerV7;
|
||||
moveHelperToHooks(container, 'helperMissing', keepHelperInHelpers);
|
||||
moveHelperToHooks(container, 'blockHelperMissing', keepHelperInHelpers);
|
||||
|
||||
} else {
|
||||
container.helpers = options.helpers;
|
||||
|
||||
Generated
+70
-68
@@ -4,6 +4,76 @@
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
"@knappi/grunt-saucelabs": {
|
||||
"version": "9.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@knappi/grunt-saucelabs/-/grunt-saucelabs-9.0.2.tgz",
|
||||
"integrity": "sha512-PHnusXA+begWFgZS084ZAC8kT7FNRvvJIWg5FOqusRjr4LSe++RIk2HRULAQ8Dx+6HiKJiQXzrAsDagKh3k1pw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@knappi/sauce-tunnel": "^2.5.0",
|
||||
"colors": "~1.1.2",
|
||||
"lodash": "^4.17.11",
|
||||
"q": "~1.4.1",
|
||||
"requestretry": "~1.9.0",
|
||||
"saucelabs": "^1.5.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"lodash": {
|
||||
"version": "4.17.15",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
|
||||
"integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==",
|
||||
"dev": true
|
||||
},
|
||||
"q": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/q/-/q-1.4.1.tgz",
|
||||
"integrity": "sha1-VXBbzZPF82c1MMLCy8DCs63cKG4=",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"@knappi/sauce-tunnel": {
|
||||
"version": "2.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@knappi/sauce-tunnel/-/sauce-tunnel-2.5.0.tgz",
|
||||
"integrity": "sha512-8f60HrHH4SRHOspcVLbN0gpDiBYLQjmZCXwmqibhzJDHGaD/fflPpdUD2kJJyeioydcf1T0xfpbMcJW0dLjpnw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"chalk": "^2.4.2",
|
||||
"request": "^2.88.0",
|
||||
"split": "^1.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"ansi-styles": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
|
||||
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-convert": "^1.9.0"
|
||||
}
|
||||
},
|
||||
"chalk": {
|
||||
"version": "2.4.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
|
||||
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-styles": "^3.2.1",
|
||||
"escape-string-regexp": "^1.0.5",
|
||||
"supports-color": "^5.3.0"
|
||||
}
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
|
||||
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"has-flag": "^3.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@types/parsimmon": {
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/parsimmon/-/parsimmon-1.10.0.tgz",
|
||||
@@ -3737,33 +3807,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"grunt-saucelabs": {
|
||||
"version": "github:nknapp/grunt-saucelabs#5b9b7150e66051dbf68e9212fbb89561e447e855",
|
||||
"from": "github:nknapp/grunt-saucelabs",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"colors": "~1.1.2",
|
||||
"lodash": "^4.17.11",
|
||||
"q": "~1.4.1",
|
||||
"requestretry": "~1.9.0",
|
||||
"sauce-tunnel": "github:nknapp/sauce-tunnel",
|
||||
"saucelabs": "^1.5.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"lodash": {
|
||||
"version": "4.17.15",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
|
||||
"integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==",
|
||||
"dev": true
|
||||
},
|
||||
"q": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/q/-/q-1.4.1.tgz",
|
||||
"integrity": "sha1-VXBbzZPF82c1MMLCy8DCs63cKG4=",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"grunt-webpack": {
|
||||
"version": "1.0.18",
|
||||
"resolved": "https://registry.npmjs.org/grunt-webpack/-/grunt-webpack-1.0.18.tgz",
|
||||
@@ -7148,47 +7191,6 @@
|
||||
"integrity": "sha1-RPoWGwGHuVSd2Eu5GAL5vYOFzWo=",
|
||||
"dev": true
|
||||
},
|
||||
"sauce-tunnel": {
|
||||
"version": "github:nknapp/sauce-tunnel#746744c71b9bec69ca612e751e7121f61a37d489",
|
||||
"from": "github:nknapp/sauce-tunnel",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"chalk": "^2.4.2",
|
||||
"request": "^2.88.0",
|
||||
"split": "^1.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"ansi-styles": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
|
||||
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-convert": "^1.9.0"
|
||||
}
|
||||
},
|
||||
"chalk": {
|
||||
"version": "2.4.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
|
||||
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-styles": "^3.2.1",
|
||||
"escape-string-regexp": "^1.0.5",
|
||||
"supports-color": "^5.3.0"
|
||||
}
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
|
||||
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"has-flag": "^3.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"saucelabs": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/saucelabs/-/saucelabs-1.5.0.tgz",
|
||||
|
||||
+1
-1
@@ -29,6 +29,7 @@
|
||||
"uglify-js": "^3.1.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@knappi/grunt-saucelabs": "^9.0.2",
|
||||
"aws-sdk": "^2.1.49",
|
||||
"babel-loader": "^5.0.0",
|
||||
"babel-runtime": "^5.1.10",
|
||||
@@ -48,7 +49,6 @@
|
||||
"grunt-contrib-uglify": "^1",
|
||||
"grunt-contrib-watch": "^1.1.0",
|
||||
"grunt-eslint": "^20.1.0",
|
||||
"grunt-saucelabs": "github:nknapp/grunt-saucelabs",
|
||||
"grunt-webpack": "^1.0.8",
|
||||
"istanbul": "kpdecker/istanbul",
|
||||
"jison": "0.4.16",
|
||||
|
||||
+62
-1
@@ -2,7 +2,68 @@
|
||||
|
||||
## Development
|
||||
|
||||
[Commits](https://github.com/wycats/handlebars.js/compare/v4.3.0...master)
|
||||
[Commits](https://github.com/wycats/handlebars.js/compare/v4.4.3...master)
|
||||
|
||||
## v4.4.3 - October 8th, 2019
|
||||
Bugfixes
|
||||
|
||||
Typings:
|
||||
- add missing type fields to AST typings and add tests for them - 0440af2
|
||||
|
||||
|
||||
|
||||
[Commits](https://github.com/wycats/handlebars.js/compare/v4.4.2...v4.4.3)
|
||||
|
||||
## v4.4.2 - October 2nd, 2019
|
||||
- chore: fix grunt-saucelabs dependency - b7eada0
|
||||
|
||||
|
||||
[Commits](https://github.com/wycats/handlebars.js/compare/v4.4.1...v4.4.2)
|
||||
|
||||
## v4.4.1 - October 2nd, 2019
|
||||
- [#1562](https://github.com/wycats/handlebars.js/issues/1562) - Error message for syntax error missing location in 4.2.1+
|
||||
|
||||
|
||||
|
||||
[Commits](https://github.com/wycats/handlebars.js/compare/v4.4.0...v4.4.1)
|
||||
|
||||
## v4.4.0 - September 29th, 2019
|
||||
- Added support for iterable objects in {{#each}} helper (#1557) - cf7545e
|
||||
|
||||
|
||||
[Commits](https://github.com/wycats/handlebars.js/compare/v4.3.4...v4.4.0)
|
||||
|
||||
## v4.3.4 - September 28th, 2019
|
||||
- fix: harden "propertyIsEnumerable"-check - ff4d827
|
||||
|
||||
Compatibility notes:
|
||||
- No incompatibilities are known.
|
||||
|
||||
[Commits](https://github.com/wycats/handlebars.js/compare/v4.3.3...v4.3.4)
|
||||
|
||||
## v4.3.3 - September 27th, 2019
|
||||
- fix test case for browsers that do not support __defineGetter__ - 8742bde
|
||||
|
||||
|
||||
[Commits](https://github.com/wycats/handlebars.js/compare/v4.3.2...v4.3.3)
|
||||
|
||||
## v4.3.2 - September 26th, 2019
|
||||
- Use Object.prototype.propertyIsEnumerable to check for constructors - 213c0bb, #1563
|
||||
|
||||
Compatibility notes:
|
||||
- There are no breaking changes
|
||||
|
||||
[Commits](https://github.com/wycats/handlebars.js/compare/v4.3.1...v4.3.2)
|
||||
|
||||
## v4.3.1 - September 25th, 2019
|
||||
Fixes:
|
||||
|
||||
- do not break on precompiled templates from Handlebars >=4.0.0 <4.3.0 - 1266838, #1561
|
||||
- Ensure allowCallsToHelperMissing runtime option is optional in typings - 93444c5, 64ecb9e, #1560
|
||||
|
||||
|
||||
|
||||
[Commits](https://github.com/wycats/handlebars.js/compare/v4.3.0...v4.3.1)
|
||||
|
||||
## v4.3.0 - September 24th, 2019
|
||||
Fixes:
|
||||
|
||||
@@ -254,6 +254,37 @@ describe('builtin helpers', function() {
|
||||
template({});
|
||||
}, handlebarsEnv.Exception, 'Must pass iterator to #each');
|
||||
});
|
||||
|
||||
if (global.Symbol && global.Symbol.iterator) {
|
||||
it('each on iterable', function() {
|
||||
function Iterator(arr) {
|
||||
this.arr = arr;
|
||||
this.index = 0;
|
||||
}
|
||||
Iterator.prototype.next = function() {
|
||||
var value = this.arr[this.index];
|
||||
var done = this.index === this.arr.length;
|
||||
if (!done) {
|
||||
this.index++;
|
||||
}
|
||||
return { value: value, done: done };
|
||||
};
|
||||
function Iterable(arr) {
|
||||
this.arr = arr;
|
||||
}
|
||||
Iterable.prototype[global.Symbol.iterator] = function() {
|
||||
return new Iterator(this.arr);
|
||||
};
|
||||
var string = '{{#each goodbyes}}{{text}}! {{/each}}cruel {{world}}!';
|
||||
var goodbyes = new Iterable([{text: 'goodbye'}, {text: 'Goodbye'}, {text: 'GOODBYE'}]);
|
||||
var goodbyesEmpty = new Iterable([]);
|
||||
var hash = {goodbyes: goodbyes, world: 'world'};
|
||||
shouldCompileTo(string, hash, 'goodbye! Goodbye! GOODBYE! cruel world!',
|
||||
'each with array argument iterates over the contents when not empty');
|
||||
shouldCompileTo(string, {goodbyes: goodbyesEmpty, world: 'world'}, 'cruel world!',
|
||||
'each with array argument ignores the contents when empty');
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
describe('#log', function() {
|
||||
|
||||
@@ -302,4 +302,36 @@ describe('Regressions', function() {
|
||||
};
|
||||
shouldCompileToWithPartials(string, [{}, {}, partials], true, 'template block partial block template');
|
||||
});
|
||||
|
||||
describe('GH-1561: 4.3.x should still work with precompiled templates from 4.0.0 <= x < 4.3.0', function() {
|
||||
|
||||
it('should compile and execute templates', function() {
|
||||
var newHandlebarsInstance = Handlebars.create();
|
||||
|
||||
registerTemplate(newHandlebarsInstance);
|
||||
newHandlebarsInstance.registerHelper('loud', function(value) {
|
||||
return value.toUpperCase();
|
||||
});
|
||||
var result = newHandlebarsInstance.templates['test.hbs']({name: 'yehuda'});
|
||||
equals(result.trim(), 'YEHUDA');
|
||||
});
|
||||
|
||||
it('should call "helperMissing" if a helper is missing', function() {
|
||||
var newHandlebarsInstance = Handlebars.create();
|
||||
|
||||
shouldThrow(function() {
|
||||
registerTemplate(newHandlebarsInstance);
|
||||
newHandlebarsInstance.templates['test.hbs']({});
|
||||
}, Handlebars.Exception, 'Missing helper: "loud"');
|
||||
});
|
||||
|
||||
// This is a only slightly modified precompiled templated from compiled with 4.2.1
|
||||
function registerTemplate(Handlebars) {
|
||||
var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
|
||||
templates['test.hbs'] = template({'compiler': [7, '>= 4.0.0'], 'main': function(container, depth0, helpers, partials, data) {
|
||||
return container.escapeExpression((helpers.loud || (depth0 && depth0.loud) || helpers.helperMissing).call(depth0 != null ? depth0 : (container.nullContext || {}), (depth0 != null ? depth0.name : depth0), {'name': 'loud', 'hash': {}, 'data': data}))
|
||||
+ '\n\n';
|
||||
}, 'useData': true});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
+1
-1
@@ -21,7 +21,7 @@ describe('runtime', function() {
|
||||
shouldThrow(function() {
|
||||
Handlebars.template({
|
||||
main: {},
|
||||
compiler: [Handlebars.COMPILER_REVISION - 1]
|
||||
compiler: [Handlebars.LAST_COMPATIBLE_COMPILER_REVISION - 1]
|
||||
});
|
||||
}, Error, /Template was precompiled with an older version of Handlebars than the current runtime/);
|
||||
shouldThrow(function() {
|
||||
|
||||
+14
-1
@@ -33,7 +33,7 @@ describe('security issues', function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe('GH-xxxx: Prevent explicit call of helperMissing-helpers', function() {
|
||||
describe('GH-1558: Prevent explicit call of helperMissing-helpers', function() {
|
||||
if (!Handlebars.compile) {
|
||||
return;
|
||||
}
|
||||
@@ -88,4 +88,17 @@ describe('security issues', function() {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('GH-1563', function() {
|
||||
it('should not allow to access constructor after overriding via __defineGetter__', function() {
|
||||
if (({}).__defineGetter__ == null || ({}).__lookupGetter__ == null) {
|
||||
return; // Browser does not support this exploit anyway
|
||||
}
|
||||
shouldCompileTo('{{__defineGetter__ "undefined" valueOf }}' +
|
||||
'{{#with __lookupGetter__ }}' +
|
||||
'{{__defineGetter__ "propertyIsEnumerable" (this.bind (this.bind 1)) }}' +
|
||||
'{{constructor.name}}' +
|
||||
'{{/with}}', {}, '');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Vendored
+20
-3
@@ -29,7 +29,7 @@ declare namespace Handlebars {
|
||||
decorators?: { [name: string]: Function };
|
||||
data?: any;
|
||||
blockParams?: any[];
|
||||
allowCallsToHelperMissing: boolean;
|
||||
allowCallsToHelperMissing?: boolean;
|
||||
}
|
||||
|
||||
export interface HelperOptions {
|
||||
@@ -270,6 +270,7 @@ declare namespace hbs {
|
||||
interface Statement extends Node {}
|
||||
|
||||
interface MustacheStatement extends Statement {
|
||||
type: 'MustacheStatement';
|
||||
path: PathExpression | Literal;
|
||||
params: Expression[];
|
||||
hash: Hash;
|
||||
@@ -280,6 +281,7 @@ declare namespace hbs {
|
||||
interface Decorator extends MustacheStatement { }
|
||||
|
||||
interface BlockStatement extends Statement {
|
||||
type: 'BlockStatement';
|
||||
path: PathExpression;
|
||||
params: Expression[];
|
||||
hash: Hash;
|
||||
@@ -293,6 +295,7 @@ declare namespace hbs {
|
||||
interface DecoratorBlock extends BlockStatement { }
|
||||
|
||||
interface PartialStatement extends Statement {
|
||||
type: 'PartialStatement';
|
||||
name: PathExpression | SubExpression;
|
||||
params: Expression[];
|
||||
hash: Hash;
|
||||
@@ -301,6 +304,7 @@ declare namespace hbs {
|
||||
}
|
||||
|
||||
interface PartialBlockStatement extends Statement {
|
||||
type: 'PartialBlockStatement';
|
||||
name: PathExpression | SubExpression;
|
||||
params: Expression[];
|
||||
hash: Hash;
|
||||
@@ -310,11 +314,13 @@ declare namespace hbs {
|
||||
}
|
||||
|
||||
interface ContentStatement extends Statement {
|
||||
type: 'ContentStatement';
|
||||
value: string;
|
||||
original: StripFlags;
|
||||
}
|
||||
|
||||
interface CommentStatement extends Statement {
|
||||
type: 'CommentStatement';
|
||||
value: string;
|
||||
strip: StripFlags;
|
||||
}
|
||||
@@ -322,12 +328,14 @@ declare namespace hbs {
|
||||
interface Expression extends Node {}
|
||||
|
||||
interface SubExpression extends Expression {
|
||||
type: 'SubExpression';
|
||||
path: PathExpression;
|
||||
params: Expression[];
|
||||
hash: Hash;
|
||||
}
|
||||
|
||||
interface PathExpression extends Expression {
|
||||
type: 'PathExpression';
|
||||
data: boolean;
|
||||
depth: number;
|
||||
parts: string[];
|
||||
@@ -336,29 +344,38 @@ declare namespace hbs {
|
||||
|
||||
interface Literal extends Expression {}
|
||||
interface StringLiteral extends Literal {
|
||||
type: 'StringLiteral';
|
||||
value: string;
|
||||
original: string;
|
||||
}
|
||||
|
||||
interface BooleanLiteral extends Literal {
|
||||
type: 'BooleanLiteral';
|
||||
value: boolean;
|
||||
original: boolean;
|
||||
}
|
||||
|
||||
interface NumberLiteral extends Literal {
|
||||
type: 'NumberLiteral';
|
||||
value: number;
|
||||
original: number;
|
||||
}
|
||||
|
||||
interface UndefinedLiteral extends Literal {}
|
||||
interface UndefinedLiteral extends Literal {
|
||||
type: 'UndefinedLiteral';
|
||||
}
|
||||
|
||||
interface NullLiteral extends Literal {}
|
||||
interface NullLiteral extends Literal {
|
||||
type: 'NullLiteral';
|
||||
}
|
||||
|
||||
interface Hash extends Node {
|
||||
type: 'Hash';
|
||||
pairs: HashPair[];
|
||||
}
|
||||
|
||||
interface HashPair extends Node {
|
||||
type: 'HashPair';
|
||||
key: string;
|
||||
value: Expression;
|
||||
}
|
||||
|
||||
@@ -109,3 +109,87 @@ Handlebars.compile('test', {
|
||||
});
|
||||
|
||||
Handlebars.compile('test')({},{allowCallsToHelperMissing: true});
|
||||
|
||||
Handlebars.compile('test')({},{});
|
||||
|
||||
|
||||
const allthings = {} as hbs.AST.MustacheStatement |
|
||||
hbs.AST.BlockStatement |
|
||||
hbs.AST.PartialStatement |
|
||||
hbs.AST.PartialBlockStatement |
|
||||
hbs.AST.ContentStatement |
|
||||
hbs.AST.CommentStatement |
|
||||
hbs.AST.SubExpression |
|
||||
hbs.AST.PathExpression |
|
||||
hbs.AST.StringLiteral |
|
||||
hbs.AST.BooleanLiteral |
|
||||
hbs.AST.NumberLiteral |
|
||||
hbs.AST.UndefinedLiteral |
|
||||
hbs.AST.NullLiteral |
|
||||
hbs.AST.Hash |
|
||||
hbs.AST.HashPair;
|
||||
|
||||
switch(allthings.type) {
|
||||
case "MustacheStatement":
|
||||
let mustacheStatement: hbs.AST.MustacheStatement;
|
||||
mustacheStatement = allthings;
|
||||
break;
|
||||
case "BlockStatement":
|
||||
let blockStatement: hbs.AST.BlockStatement;
|
||||
blockStatement = allthings;
|
||||
break;
|
||||
case "PartialStatement":
|
||||
let partialStatement: hbs.AST.PartialStatement;
|
||||
partialStatement = allthings;
|
||||
break;
|
||||
case "PartialBlockStatement":
|
||||
let partialBlockStatement: hbs.AST.PartialBlockStatement;
|
||||
partialBlockStatement = allthings;
|
||||
break;
|
||||
case "ContentStatement":
|
||||
let ContentStatement: hbs.AST.ContentStatement;
|
||||
ContentStatement = allthings;
|
||||
break;
|
||||
case "CommentStatement":
|
||||
let CommentStatement: hbs.AST.CommentStatement;
|
||||
CommentStatement = allthings;
|
||||
break;
|
||||
case "SubExpression":
|
||||
let SubExpression: hbs.AST.SubExpression;
|
||||
SubExpression = allthings;
|
||||
break;
|
||||
case "PathExpression":
|
||||
let PathExpression: hbs.AST.PathExpression;
|
||||
PathExpression = allthings;
|
||||
break;
|
||||
case "StringLiteral":
|
||||
let StringLiteral: hbs.AST.StringLiteral;
|
||||
StringLiteral = allthings;
|
||||
break;
|
||||
case "BooleanLiteral":
|
||||
let BooleanLiteral: hbs.AST.BooleanLiteral;
|
||||
BooleanLiteral = allthings;
|
||||
break;
|
||||
case "NumberLiteral":
|
||||
let NumberLiteral: hbs.AST.NumberLiteral;
|
||||
NumberLiteral = allthings;
|
||||
break;
|
||||
case "UndefinedLiteral":
|
||||
let UndefinedLiteral: hbs.AST.UndefinedLiteral;
|
||||
UndefinedLiteral = allthings;
|
||||
break;
|
||||
case "NullLiteral":
|
||||
let NullLiteral: hbs.AST.NullLiteral;
|
||||
NullLiteral = allthings;
|
||||
break;
|
||||
case "Hash":
|
||||
let Hash: hbs.AST.Hash;
|
||||
Hash = allthings;
|
||||
break;
|
||||
case "HashPair":
|
||||
let HashPair: hbs.AST.HashPair;
|
||||
HashPair = allthings;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
Reference in New Issue
Block a user