diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3e4d5530..4559c726 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -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. diff --git a/Gruntfile.js b/Gruntfile.js index 806ca184..420afd62 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -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'); diff --git a/lib/handlebars/base.js b/lib/handlebars/base.js index 2703dafd..7d649de0 100644 --- a/lib/handlebars/base.js +++ b/lib/handlebars/base.js @@ -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 diff --git a/lib/handlebars/compiler/javascript-compiler.js b/lib/handlebars/compiler/javascript-compiler.js index 15fa7aae..603d2567 100644 --- a/lib/handlebars/compiler/javascript-compiler.js +++ b/lib/handlebars/compiler/javascript-compiler.js @@ -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))' ]); }, diff --git a/lib/handlebars/helpers/each.js b/lib/handlebars/helpers/each.js index 914928d6..5bb4c903 100644 --- a/lib/handlebars/helpers/each.js +++ b/lib/handlebars/helpers/each.js @@ -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; diff --git a/lib/handlebars/runtime.js b/lib/handlebars/runtime.js index 7402313d..16c272ad 100644 --- a/lib/handlebars/runtime.js +++ b/lib/handlebars/runtime.js @@ -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; diff --git a/package-lock.json b/package-lock.json index f45462c7..6cec0306 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", diff --git a/package.json b/package.json index b6bbc7c6..53e5d06d 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/release-notes.md b/release-notes.md index 8d11f07b..c20a86cb 100644 --- a/release-notes.md +++ b/release-notes.md @@ -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: diff --git a/spec/builtins.js b/spec/builtins.js index ce6d8f4e..b29927f0 100644 --- a/spec/builtins.js +++ b/spec/builtins.js @@ -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() { diff --git a/spec/regressions.js b/spec/regressions.js index d51765c1..8209f5cc 100644 --- a/spec/regressions.js +++ b/spec/regressions.js @@ -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}); + } + }); }); diff --git a/spec/runtime.js b/spec/runtime.js index 2a858993..8bea4201 100644 --- a/spec/runtime.js +++ b/spec/runtime.js @@ -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() { diff --git a/spec/security.js b/spec/security.js index 4c092b1c..c50bda6d 100644 --- a/spec/security.js +++ b/spec/security.js @@ -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}}', {}, ''); + }); + }); }); diff --git a/types/index.d.ts b/types/index.d.ts index bf8d1756..6fdaf0f9 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -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; } diff --git a/types/test.ts b/types/test.ts index 9ecc2d16..ff435916 100644 --- a/types/test.ts +++ b/types/test.ts @@ -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; +} \ No newline at end of file