Compare commits
59 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8de121d21c | |||
| 6914090086 | |||
| d54137810a | |||
| c2ac79c970 | |||
| 7ef86173ab | |||
| b75e3e1f40 | |||
| 5e9d17f8fa | |||
| b24797da01 | |||
| a243067883 | |||
| 088e61812a | |||
| 7052e88068 | |||
| b8913fcc65 | |||
| 62ed3c25c7 | |||
| 7fcf9d24f8 | |||
| c76ded8f0f | |||
| a15d01d383 | |||
| 8e1cce7918 | |||
| def79fd5d6 | |||
| fd2e5c983d | |||
| 8d5530ee2c | |||
| 2ab261eab7 | |||
| 143ac806da | |||
| f1752fe66f | |||
| aaab6099f2 | |||
| feb60f85c9 | |||
| 0b593bfe5d | |||
| 2e53fba68f | |||
| ba570c42cf | |||
| 0440af2147 | |||
| b793350fec | |||
| 26d0f7a80d | |||
| b7eada0149 | |||
| b8e769fcb6 | |||
| 9cb31653a4 | |||
| c53f340374 | |||
| 059b330579 | |||
| 8f6047cdfd | |||
| cf7545ef5a | |||
| c958cc8955 | |||
| b250b2d53a | |||
| ff4d827c09 | |||
| e4738491b3 | |||
| 54f7e11b28 | |||
| 8742bde701 | |||
| 2357140c68 | |||
| c5cbeac039 | |||
| 213c0bbe3c | |||
| 050cca0866 | |||
| 2d5579078b | |||
| e3639e2407 | |||
| c2a17c7b30 | |||
| 1266838829 | |||
| 64ecb9ea84 | |||
| 93444c58db | |||
| d148d7cb1f | |||
| a89081d440 | |||
| bba65605b1 | |||
| 06b7224ed9 | |||
| 2078c727c6 |
+7
-19
@@ -1,25 +1,11 @@
|
||||
module.exports = {
|
||||
"extends": "eslint:recommended",
|
||||
"extends": ["eslint:recommended","plugin:compat/recommended"],
|
||||
"globals": {
|
||||
"self": false
|
||||
},
|
||||
"env": {
|
||||
"node": true
|
||||
},
|
||||
"ecmaFeatures": {
|
||||
// Enabling features that can be implemented without polyfills. Want to avoid polyfills at this time.
|
||||
"arrowFunctions": true,
|
||||
"blockBindings": true,
|
||||
"defaultParams": true,
|
||||
"destructuring": true,
|
||||
"modules": true,
|
||||
"objectLiteralComputedProperties": true,
|
||||
"objectLiteralDuplicateProperties": true,
|
||||
"objectLiteralShorthandMethods": true,
|
||||
"objectLiteralShorthandProperties": true,
|
||||
"restParams": true,
|
||||
"spread": true,
|
||||
"templateStrings": true
|
||||
"node": true,
|
||||
"es6": true
|
||||
},
|
||||
"rules": {
|
||||
// overrides eslint:recommended defaults
|
||||
@@ -124,6 +110,8 @@ module.exports = {
|
||||
"no-var": "warn"
|
||||
},
|
||||
"parserOptions": {
|
||||
"sourceType": "module"
|
||||
"sourceType": "module",
|
||||
"ecmaVersion": 6,
|
||||
"ecmaFeatures": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -90,6 +90,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/
|
||||
@@ -104,7 +105,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
@@ -239,7 +239,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');
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "handlebars",
|
||||
"version": "4.2.1",
|
||||
"version": "4.5.2",
|
||||
"main": "handlebars.js",
|
||||
"license": "MIT",
|
||||
"dependencies": {}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<package>
|
||||
<metadata>
|
||||
<id>handlebars.js</id>
|
||||
<version>4.2.1</version>
|
||||
<version>4.5.2</version>
|
||||
<authors>handlebars.js Authors</authors>
|
||||
<licenseUrl>https://github.com/wycats/handlebars.js/blob/master/LICENSE</licenseUrl>
|
||||
<projectUrl>https://github.com/wycats/handlebars.js/</projectUrl>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "handlebars",
|
||||
"version": "4.2.1",
|
||||
"version": "4.5.2",
|
||||
"license": "MIT",
|
||||
"jspm": {
|
||||
"main": "handlebars",
|
||||
|
||||
@@ -16,6 +16,34 @@ var ast = Handlebars.parse(myTemplate);
|
||||
Handlebars.precompile(ast);
|
||||
```
|
||||
|
||||
### Parsing
|
||||
|
||||
There are two primary APIs that are used to parse an existing template into the AST:
|
||||
|
||||
#### parseWithoutProcessing
|
||||
|
||||
`Handlebars.parseWithoutProcessing` is the primary mechanism to turn a raw template string into the Handlebars AST described in this document. No processing is done on the resulting AST which makes this ideal for codemod (for source to source transformation) tooling.
|
||||
|
||||
Example:
|
||||
|
||||
```js
|
||||
let ast = Handlebars.parseWithoutProcessing(myTemplate);
|
||||
```
|
||||
|
||||
#### parse
|
||||
|
||||
`Handlebars.parse` will parse the template with `parseWithoutProcessing` (see above) then it will update the AST to strip extraneous whitespace. The whitespace stripping functionality handles two distinct situations:
|
||||
|
||||
* Removes whitespace around dynamic statements that are on a line by themselves (aka "stand alone")
|
||||
* Applies "whitespace control" characters (i.e. `~`) by truncating the `ContentStatement` `value` property appropriately (e.g. `\n\n{{~foo}}` would have a `ContentStatement` with a `value` of `''`)
|
||||
|
||||
`Handlebars.parse` is used internally by `Handlebars.precompile` and `Handlebars.compile`.
|
||||
|
||||
Example:
|
||||
|
||||
```js
|
||||
let ast = Handlebars.parse(myTemplate);
|
||||
```
|
||||
|
||||
### Basic
|
||||
|
||||
|
||||
+2
-1
@@ -2,7 +2,7 @@ import runtime from './handlebars.runtime';
|
||||
|
||||
// Compiler imports
|
||||
import AST from './handlebars/compiler/ast';
|
||||
import { parser as Parser, parse } from './handlebars/compiler/base';
|
||||
import { parser as Parser, parse, parseWithoutProcessing } from './handlebars/compiler/base';
|
||||
import { Compiler, compile, precompile } from './handlebars/compiler/compiler';
|
||||
import JavaScriptCompiler from './handlebars/compiler/javascript-compiler';
|
||||
import Visitor from './handlebars/compiler/visitor';
|
||||
@@ -25,6 +25,7 @@ function create() {
|
||||
hb.JavaScriptCompiler = JavaScriptCompiler;
|
||||
hb.Parser = Parser;
|
||||
hb.parse = parse;
|
||||
hb.parseWithoutProcessing = parseWithoutProcessing;
|
||||
|
||||
return hb;
|
||||
}
|
||||
|
||||
@@ -4,8 +4,9 @@ import {registerDefaultHelpers} from './helpers';
|
||||
import {registerDefaultDecorators} from './decorators';
|
||||
import logger from './logger';
|
||||
|
||||
export const VERSION = '4.2.1';
|
||||
export const COMPILER_REVISION = 7;
|
||||
export const VERSION = '4.5.2';
|
||||
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
|
||||
@@ -14,7 +15,8 @@ export const REVISION_CHANGES = {
|
||||
4: '== 1.x.x',
|
||||
5: '== 2.0.0-alpha.x',
|
||||
6: '>= 2.0.0-beta.1',
|
||||
7: '>= 4.0.0'
|
||||
7: '>= 4.0.0 <4.3.0',
|
||||
8: '>= 4.3.0'
|
||||
};
|
||||
|
||||
const objectType = '[object Object]';
|
||||
|
||||
@@ -8,7 +8,7 @@ export { parser };
|
||||
let yy = {};
|
||||
extend(yy, Helpers);
|
||||
|
||||
export function parse(input, options) {
|
||||
export function parseWithoutProcessing(input, options) {
|
||||
// Just return if an already-compiled AST was passed in.
|
||||
if (input.type === 'Program') { return input; }
|
||||
|
||||
@@ -19,6 +19,14 @@ export function parse(input, options) {
|
||||
return new yy.SourceLocation(options && options.srcName, locInfo);
|
||||
};
|
||||
|
||||
let strip = new WhitespaceControl(options);
|
||||
return strip.accept(parser.parse(input));
|
||||
let ast = parser.parse(input);
|
||||
|
||||
return ast;
|
||||
}
|
||||
|
||||
export function parse(input, options) {
|
||||
let ast = parseWithoutProcessing(input, options);
|
||||
let strip = new WhitespaceControl(options);
|
||||
|
||||
return strip.accept(ast);
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
@@ -214,7 +220,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;
|
||||
@@ -311,7 +316,7 @@ JavaScriptCompiler.prototype = {
|
||||
// replace it on the stack with the result of properly
|
||||
// invoking blockHelperMissing.
|
||||
blockValue: function(name) {
|
||||
let blockHelperMissing = this.aliasable('helpers.blockHelperMissing'),
|
||||
let blockHelperMissing = this.aliasable('container.hooks.blockHelperMissing'),
|
||||
params = [this.contextName(0)];
|
||||
this.setupHelperArgs(name, 0, params);
|
||||
|
||||
@@ -329,7 +334,7 @@ JavaScriptCompiler.prototype = {
|
||||
// On stack, after, if lastHelper: value
|
||||
ambiguousBlockValue: function() {
|
||||
// We're being a bit cheeky and reusing the options value from the prior exec
|
||||
let blockHelperMissing = this.aliasable('helpers.blockHelperMissing'),
|
||||
let blockHelperMissing = this.aliasable('container.hooks.blockHelperMissing'),
|
||||
params = [this.contextName(0)];
|
||||
this.setupHelperArgs('', 0, params, true);
|
||||
|
||||
@@ -339,9 +344,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]
|
||||
@@ -538,7 +543,7 @@ JavaScriptCompiler.prototype = {
|
||||
if (this.hash) {
|
||||
this.hashes.push(this.hash);
|
||||
}
|
||||
this.hash = {values: [], types: [], contexts: [], ids: []};
|
||||
this.hash = {values: {}, types: [], contexts: [], ids: []};
|
||||
},
|
||||
popHash: function() {
|
||||
let hash = this.hash;
|
||||
@@ -622,18 +627,32 @@ JavaScriptCompiler.prototype = {
|
||||
// If the helper is not found, `helperMissing` is called.
|
||||
invokeHelper: function(paramSize, name, isSimple) {
|
||||
let nonHelper = this.popStack(),
|
||||
helper = this.setupHelper(paramSize, name),
|
||||
simple = isSimple ? [helper.name, ' || '] : '';
|
||||
helper = this.setupHelper(paramSize, name);
|
||||
|
||||
let lookup = ['('].concat(simple, nonHelper);
|
||||
if (!this.options.strict) {
|
||||
lookup.push(' || ', this.aliasable('helpers.helperMissing'));
|
||||
let possibleFunctionCalls = [];
|
||||
|
||||
if (isSimple) { // direct call to helper
|
||||
possibleFunctionCalls.push(helper.name);
|
||||
}
|
||||
// call a function from the input object
|
||||
possibleFunctionCalls.push(nonHelper);
|
||||
if (!this.options.strict) {
|
||||
possibleFunctionCalls.push(this.aliasable('container.hooks.helperMissing'));
|
||||
}
|
||||
lookup.push(')');
|
||||
|
||||
this.push(this.source.functionCall(lookup, 'call', helper.callParams));
|
||||
let functionLookupCode = ['(', this.itemsSeparatedBy(possibleFunctionCalls, '||'), ')'];
|
||||
let functionCall = this.source.functionCall(functionLookupCode, 'call', helper.callParams);
|
||||
this.push(functionCall);
|
||||
},
|
||||
|
||||
itemsSeparatedBy: function(items, separator) {
|
||||
let result = [];
|
||||
result.push(items[0]);
|
||||
for (let i = 1; i < items.length; i++) {
|
||||
result.push(separator, items[i]);
|
||||
}
|
||||
return result;
|
||||
},
|
||||
// [invokeKnownHelper]
|
||||
//
|
||||
// On stack, before: hash, inverse, program, params..., ...
|
||||
@@ -672,16 +691,16 @@ JavaScriptCompiler.prototype = {
|
||||
if (!this.options.strict) {
|
||||
lookup[0] = '(helper = ';
|
||||
lookup.push(
|
||||
' != null ? helper : ',
|
||||
this.aliasable('helpers.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))'
|
||||
]);
|
||||
},
|
||||
|
||||
@@ -1072,6 +1091,7 @@ JavaScriptCompiler.prototype = {
|
||||
|
||||
setupHelperArgs: function(helper, paramSize, params, useRegister) {
|
||||
let options = this.setupParams(helper, paramSize, params);
|
||||
options.loc = JSON.stringify(this.source.currentLocation);
|
||||
options = this.objectLiteral(options);
|
||||
if (useRegister) {
|
||||
this.useRegister('options');
|
||||
@@ -1131,7 +1151,7 @@ function strictLookup(requireTerminal, compiler, parts, type) {
|
||||
}
|
||||
|
||||
if (requireTerminal) {
|
||||
return [compiler.aliasable('container.strict'), '(', stack, ', ', compiler.quotedString(parts[i]), ')'];
|
||||
return [compiler.aliasable('container.strict'), '(', stack, ', ', compiler.quotedString(parts[i]), ', ', JSON.stringify(compiler.source.currentLocation), ' )'];
|
||||
} else {
|
||||
return stack;
|
||||
}
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
|
||||
const errorProps = ['description', 'fileName', 'lineNumber', 'message', 'name', 'number', 'stack'];
|
||||
const errorProps = ['description', 'fileName', 'lineNumber', 'endLineNumber', 'message', 'name', 'number', 'stack'];
|
||||
|
||||
function Exception(message, node) {
|
||||
let loc = node && node.loc,
|
||||
line,
|
||||
column;
|
||||
endLineNumber,
|
||||
column,
|
||||
endColumn;
|
||||
|
||||
if (loc) {
|
||||
line = loc.start.line;
|
||||
endLineNumber = loc.end.line;
|
||||
column = loc.start.column;
|
||||
endColumn = loc.end.column;
|
||||
|
||||
message += ' - ' + line + ':' + column;
|
||||
}
|
||||
@@ -27,6 +32,7 @@ function Exception(message, node) {
|
||||
try {
|
||||
if (loc) {
|
||||
this.lineNumber = line;
|
||||
this.endLineNumber = endLineNumber;
|
||||
|
||||
// Work around issue under safari where we can't directly set the column value
|
||||
/* istanbul ignore next */
|
||||
@@ -35,8 +41,13 @@ function Exception(message, node) {
|
||||
value: column,
|
||||
enumerable: true
|
||||
});
|
||||
Object.defineProperty(this, 'endColumn', {
|
||||
value: endColumn,
|
||||
enumerable: true
|
||||
});
|
||||
} else {
|
||||
this.column = column;
|
||||
this.endColumn = endColumn;
|
||||
}
|
||||
}
|
||||
} catch (nop) {
|
||||
|
||||
@@ -15,3 +15,12 @@ export function registerDefaultHelpers(instance) {
|
||||
registerLookup(instance);
|
||||
registerWith(instance);
|
||||
}
|
||||
|
||||
export function moveHelperToHooks(instance, helperName, keepHelper) {
|
||||
if (instance.helpers[helperName]) {
|
||||
instance.hooks[helperName] = instance.helpers[helperName];
|
||||
if (!keepHelper) {
|
||||
delete instance.helpers[helperName];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,6 +49,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;
|
||||
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import {isEmpty, isFunction} from '../utils';
|
||||
import { isEmpty, isFunction } from '../utils';
|
||||
import Exception from '../exception';
|
||||
|
||||
export default function(instance) {
|
||||
instance.registerHelper('if', function(conditional, options) {
|
||||
if (arguments.length != 2) { throw new Exception('#if requires exactly one argument');}
|
||||
if (isFunction(conditional)) { conditional = conditional.call(this); }
|
||||
|
||||
// Default behavior is to render the positive path if the value is truthy and not empty.
|
||||
@@ -15,6 +17,7 @@ export default function(instance) {
|
||||
});
|
||||
|
||||
instance.registerHelper('unless', function(conditional, options) {
|
||||
if (arguments.length != 2) { throw new Exception('#unless requires exactly one argument');}
|
||||
return instance.helpers['if'].call(this, conditional, {fn: options.inverse, inverse: options.fn, hash: options.hash});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ export default function(instance) {
|
||||
if (!obj) {
|
||||
return obj;
|
||||
}
|
||||
if (field === 'constructor' && !obj.propertyIsEnumerable(field)) {
|
||||
if (String(field) === 'constructor' && !obj.propertyIsEnumerable(field)) {
|
||||
return undefined;
|
||||
}
|
||||
return obj[field];
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import {appendContextPath, blockParams, createFrame, isEmpty, isFunction} from '../utils';
|
||||
import { appendContextPath, blockParams, createFrame, isEmpty, isFunction } from '../utils';
|
||||
import Exception from '../exception';
|
||||
|
||||
export default function(instance) {
|
||||
instance.registerHelper('with', function(context, options) {
|
||||
if (arguments.length != 2) { throw new Exception('#with requires exactly one argument');}
|
||||
if (isFunction(context)) { context = context.call(this); }
|
||||
|
||||
let fn = options.fn;
|
||||
|
||||
+39
-30
@@ -1,26 +1,30 @@
|
||||
import * as Utils from './utils';
|
||||
import Exception from './exception';
|
||||
import { COMPILER_REVISION, REVISION_CHANGES, createFrame } 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] + ').');
|
||||
}
|
||||
}
|
||||
|
||||
export function template(templateSpec, env) {
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (!env) {
|
||||
throw new Exception('No environment passed to template');
|
||||
@@ -35,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);
|
||||
@@ -42,13 +49,15 @@ export function template(templateSpec, env) {
|
||||
options.ids[0] = true;
|
||||
}
|
||||
}
|
||||
|
||||
partial = env.VM.resolvePartial.call(this, partial, context, options);
|
||||
let result = env.VM.invokePartial.call(this, partial, context, options);
|
||||
|
||||
let optionsWithHooks = Utils.extend({}, options, {hooks: this.hooks});
|
||||
|
||||
let result = env.VM.invokePartial.call(this, partial, context, optionsWithHooks);
|
||||
|
||||
if (result == null && env.compile) {
|
||||
options.partials[options.name] = env.compile(partial, templateSpec.compilerOptions, env);
|
||||
result = options.partials[options.name](context, options);
|
||||
result = options.partials[options.name](context, optionsWithHooks);
|
||||
}
|
||||
if (result != null) {
|
||||
if (options.indent) {
|
||||
@@ -70,9 +79,9 @@ export function template(templateSpec, env) {
|
||||
|
||||
// Just add water
|
||||
let container = {
|
||||
strict: function(obj, name) {
|
||||
if (!(name in obj)) {
|
||||
throw new Exception('"' + name + '" not defined in ' + obj);
|
||||
strict: function(obj, name, loc) {
|
||||
if (!obj || !(name in obj)) {
|
||||
throw new Exception('"' + name + '" not defined in ' + obj, { loc: loc });
|
||||
}
|
||||
return obj[name];
|
||||
},
|
||||
@@ -115,15 +124,6 @@ export function template(templateSpec, env) {
|
||||
}
|
||||
return value;
|
||||
},
|
||||
merge: function(param, common) {
|
||||
let obj = param || common;
|
||||
|
||||
if (param && common && (param !== common)) {
|
||||
obj = Utils.extend({}, common, param);
|
||||
}
|
||||
|
||||
return obj;
|
||||
},
|
||||
// An empty object to use as replacement for null-contexts
|
||||
nullContext: Object.seal({}),
|
||||
|
||||
@@ -158,19 +158,28 @@ export function template(templateSpec, env) {
|
||||
|
||||
ret._setup = function(options) {
|
||||
if (!options.partial) {
|
||||
container.helpers = container.merge(options.helpers, env.helpers);
|
||||
container.helpers = Utils.extend({}, env.helpers, options.helpers);
|
||||
|
||||
if (templateSpec.usePartial) {
|
||||
container.partials = container.merge(options.partials, env.partials);
|
||||
container.partials = Utils.extend({}, env.partials, options.partials);
|
||||
}
|
||||
if (templateSpec.usePartial || templateSpec.useDecorators) {
|
||||
container.decorators = container.merge(options.decorators, env.decorators);
|
||||
container.decorators = Utils.extend({}, env.decorators, options.decorators);
|
||||
}
|
||||
|
||||
container.hooks = {};
|
||||
|
||||
let keepHelperInHelpers = options.allowCallsToHelperMissing || templateWasPrecompiledWithCompilerV7;
|
||||
moveHelperToHooks(container, 'helperMissing', keepHelperInHelpers);
|
||||
moveHelperToHooks(container, 'blockHelperMissing', keepHelperInHelpers);
|
||||
|
||||
} else {
|
||||
container.helpers = options.helpers;
|
||||
container.partials = options.partials;
|
||||
container.decorators = options.decorators;
|
||||
container.hooks = options.hooks;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
ret._child = function(i, data, blockParams, depths) {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
|
||||
const escape = {
|
||||
'&': '&',
|
||||
'<': '<',
|
||||
|
||||
Generated
+182
-69
@@ -1,9 +1,88 @@
|
||||
{
|
||||
"name": "handlebars",
|
||||
"version": "4.1.2-0",
|
||||
"version": "4.5.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
"@babel/runtime": {
|
||||
"version": "7.6.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.6.3.tgz",
|
||||
"integrity": "sha512-kq6anf9JGjW8Nt5rYfEuGRaEAaH1mkv3Bbu6rYvLOpPh/RusSJXuKPEAoZ7L7gybZkchE8+NV5g9vKF4AGAtsA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"regenerator-runtime": "^0.13.2"
|
||||
}
|
||||
},
|
||||
"@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",
|
||||
@@ -251,6 +330,12 @@
|
||||
"integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=",
|
||||
"dev": true
|
||||
},
|
||||
"ast-metadata-inferer": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ast-metadata-inferer/-/ast-metadata-inferer-0.1.1.tgz",
|
||||
"integrity": "sha512-hc9w8Qrgg9Lf9iFcZVhNjUnhrd2BBpTlyCnegPVvCe6O0yMrF57a6Cmh7k+xUsfUOMh9wajOL5AsGOBNEyTCcw==",
|
||||
"dev": true
|
||||
},
|
||||
"ast-traverse": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ast-traverse/-/ast-traverse-0.1.1.tgz",
|
||||
@@ -762,6 +847,17 @@
|
||||
"pako": "~0.2.0"
|
||||
}
|
||||
},
|
||||
"browserslist": {
|
||||
"version": "4.7.2",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.7.2.tgz",
|
||||
"integrity": "sha512-uZavT/gZXJd2UTi9Ov7/Z340WOSQ3+m1iBVRUknf+okKxonL9P83S3ctiBDtuRmRu8PiCHjqyueqQ9HYlJhxiw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"caniuse-lite": "^1.0.30001004",
|
||||
"electron-to-chromium": "^1.3.295",
|
||||
"node-releases": "^1.1.38"
|
||||
}
|
||||
},
|
||||
"buffer": {
|
||||
"version": "4.9.1",
|
||||
"resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz",
|
||||
@@ -861,6 +957,18 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"caniuse-db": {
|
||||
"version": "1.0.30001005",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30001005.tgz",
|
||||
"integrity": "sha512-MSRfm2N6FRDSpAJ00ipCuFe0CNink5JJOFzl4S7fLSBJdowhGq3uMxzkWGTjvvReo1PuWfK5YYJydJJ+9mJebw==",
|
||||
"dev": true
|
||||
},
|
||||
"caniuse-lite": {
|
||||
"version": "1.0.30001005",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001005.tgz",
|
||||
"integrity": "sha512-g78miZm1Z5njjYR216a5812oPiLgV1ssndgGxITHWUopmjUrCswMisA0a2kSB7a0vZRox6JOKhM51+efmYN8Mg==",
|
||||
"dev": true
|
||||
},
|
||||
"caseless": {
|
||||
"version": "0.12.0",
|
||||
"resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
|
||||
@@ -1677,6 +1785,12 @@
|
||||
"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=",
|
||||
"dev": true
|
||||
},
|
||||
"electron-to-chromium": {
|
||||
"version": "1.3.296",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.296.tgz",
|
||||
"integrity": "sha512-s5hv+TSJSVRsxH190De66YHb50pBGTweT9XGWYu/LMR20KX6TsjFzObo36CjVAzM+PUeeKSBRtm/mISlCzeojQ==",
|
||||
"dev": true
|
||||
},
|
||||
"emojis-list": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz",
|
||||
@@ -1997,6 +2111,35 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"eslint-plugin-compat": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-compat/-/eslint-plugin-compat-3.3.0.tgz",
|
||||
"integrity": "sha512-QCgYy3pZ+zH10dkBJus1xER0359h1UhJjufhQRqp9Owm6BEoLZeSqxf2zINwL1OGao9Yc96xPYIW3nQj5HUryg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.4.5",
|
||||
"ast-metadata-inferer": "^0.1.1",
|
||||
"browserslist": "^4.6.3",
|
||||
"caniuse-db": "^1.0.30000977",
|
||||
"lodash.memoize": "4.1.2",
|
||||
"mdn-browser-compat-data": "^0.0.84",
|
||||
"semver": "^6.1.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"semver": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
||||
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"eslint-plugin-es5": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-es5/-/eslint-plugin-es5-1.4.1.tgz",
|
||||
"integrity": "sha512-kktkmkF2O7pnSZYgrMiYMbt3wCKRIiXePwILv8USDG95YgP0PzhIxSIROLLKmiQQ/Z6LuhDGWTHK04gnbXBvkg==",
|
||||
"dev": true
|
||||
},
|
||||
"eslint-scope": {
|
||||
"version": "3.7.3",
|
||||
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.3.tgz",
|
||||
@@ -3703,33 +3846,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",
|
||||
@@ -5086,6 +5202,12 @@
|
||||
"integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=",
|
||||
"dev": true
|
||||
},
|
||||
"lodash.memoize": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
|
||||
"integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=",
|
||||
"dev": true
|
||||
},
|
||||
"longest": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz",
|
||||
@@ -5177,6 +5299,15 @@
|
||||
"pretty-bytes": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"mdn-browser-compat-data": {
|
||||
"version": "0.0.84",
|
||||
"resolved": "https://registry.npmjs.org/mdn-browser-compat-data/-/mdn-browser-compat-data-0.0.84.tgz",
|
||||
"integrity": "sha512-fAznuGNaQMQiWLVf+gyp33FaABTglYWqMT7JqvH+4RZn2UQPD12gbMqxwP9m0lj8AAbNpu5/kD6n4Ox1SOffpw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"extend": "3.0.2"
|
||||
}
|
||||
},
|
||||
"media-typer": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
|
||||
@@ -5579,6 +5710,23 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node-releases": {
|
||||
"version": "1.1.39",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.39.tgz",
|
||||
"integrity": "sha512-8MRC/ErwNCHOlAFycy9OPca46fQYUjbJRDcZTHVWIGXIjYLM73k70vv3WkYutVnM4cCo4hE0MqBVVZjP6vjISA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"semver": "^6.3.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"semver": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
||||
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"nomnom": {
|
||||
"version": "0.4.3",
|
||||
"resolved": "https://registry.npmjs.org/nomnom/-/nomnom-0.4.3.tgz",
|
||||
@@ -6703,6 +6851,12 @@
|
||||
"through": "~2.3.8"
|
||||
}
|
||||
},
|
||||
"regenerator-runtime": {
|
||||
"version": "0.13.3",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz",
|
||||
"integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw==",
|
||||
"dev": true
|
||||
},
|
||||
"regex-cache": {
|
||||
"version": "0.4.4",
|
||||
"resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz",
|
||||
@@ -7020,47 +7174,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",
|
||||
|
||||
+4
-2
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "handlebars",
|
||||
"barename": "handlebars",
|
||||
"version": "4.2.1",
|
||||
"version": "4.5.2",
|
||||
"description": "Handlebars provides the power necessary to let you build semantic templates effectively with no frustration",
|
||||
"homepage": "http://www.handlebarsjs.com/",
|
||||
"keywords": [
|
||||
@@ -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",
|
||||
@@ -36,6 +37,8 @@
|
||||
"dtslint": "^0.5.5",
|
||||
"dustjs-linkedin": "^2.0.2",
|
||||
"eco": "~1.1.0-rc-3",
|
||||
"eslint-plugin-compat": "^3.3.0",
|
||||
"eslint-plugin-es5": "^1.4.1",
|
||||
"grunt": "^1.0.3",
|
||||
"grunt-babel": "^5.0.0",
|
||||
"grunt-bg-shell": "^2.3.3",
|
||||
@@ -48,7 +51,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": "^0.3.0",
|
||||
"jison": "~0.3.0",
|
||||
|
||||
+159
-1
@@ -2,7 +2,165 @@
|
||||
|
||||
## Development
|
||||
|
||||
[Commits](https://github.com/wycats/handlebars.js/compare/v4.2.1...master)
|
||||
[Commits](https://github.com/wycats/handlebars.js/compare/v4.5.2...master)
|
||||
|
||||
## v4.5.2 - November 13th, 2019
|
||||
# Bugfixes
|
||||
|
||||
- fix: use String(field) in lookup when checking for "constructor" - d541378
|
||||
- test: add fluent API for testing Handlebars - c2ac79c
|
||||
|
||||
Compatibility notes:
|
||||
- no incompatibility are to be expected
|
||||
|
||||
[Commits](https://github.com/wycats/handlebars.js/compare/v4.5.1...v4.5.2)
|
||||
|
||||
## v4.5.1 - October 29th, 2019
|
||||
Bugfixs
|
||||
|
||||
- fix: move "eslint-plugin-compat" to devDependencies - 5e9d17f (#1589)
|
||||
|
||||
Compatibility notes:
|
||||
- No compatibility issues are to be expected
|
||||
|
||||
|
||||
[Commits](https://github.com/wycats/handlebars.js/compare/v4.5.0...v4.5.1)
|
||||
|
||||
## v4.5.0 - October 28th, 2019
|
||||
Features / Improvements
|
||||
- Add method Handlebars.parseWithoutProcessing (#1584) - 62ed3c2
|
||||
- add guard to if & unless helpers (#1549)
|
||||
- show source location for the strict lookup exceptions - feb60f8
|
||||
|
||||
Bugfixes:
|
||||
- Use objects for hash value tracking - 7fcf9d2
|
||||
|
||||
Chore:
|
||||
- Resolve deprecation warning message from eslint while running eslint (#1586) - 7052e88
|
||||
- chore: add eslint-plugin-compat and eslint-plugin-es5 - 088e618
|
||||
|
||||
Compatibility notes:
|
||||
- No compatibility issues are to be expected
|
||||
|
||||
[Commits](https://github.com/wycats/handlebars.js/compare/v4.4.5...v4.5.0)
|
||||
|
||||
## v4.4.5 - October 20th, 2019
|
||||
Bugfixes:
|
||||
|
||||
- Contents of raw-blocks must be matched with non-eager regex-matching - 8d5530e, #1579
|
||||
|
||||
|
||||
|
||||
[Commits](https://github.com/wycats/handlebars.js/compare/v4.4.4...v4.4.5)
|
||||
|
||||
## v4.4.4 - October 20th, 2019
|
||||
Bugfixes:
|
||||
- fix: prevent zero length tokens in raw-blocks (#1577, #1578) - f1752fe
|
||||
|
||||
Chore:
|
||||
- chore: link to s3 bucket with https, add "npm ci" to build instructions - 0b593bf
|
||||
|
||||
Compatibility notes:
|
||||
- no compatibility issues are expected
|
||||
|
||||
[Commits](https://github.com/wycats/handlebars.js/compare/v4.4.3...v4.4.4)
|
||||
|
||||
## 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:
|
||||
|
||||
- Security: Disallow calling "helperMissing" and "blockHelperMissing" directly - 2078c72
|
||||
- Disallow calling "helperMissing" and "blockHelperMissing" directly - 2078c72
|
||||
|
||||
Features:
|
||||
|
||||
- Add new runtime option `allowCallsToHelperMissing` to allow calling `blockHelperMissing` and `helperMissing`.
|
||||
|
||||
Breaking changes:
|
||||
|
||||
Compatibility notes:
|
||||
- Compiler revision increased - 06b7224
|
||||
- This means that template compiled with versions prior to 4.3.0 will not work with runtimes >= 4.3.0
|
||||
The increase was done because the "helperMissing" and "blockHelperMissing" are now moved from the helpers
|
||||
to the internal "container.hooks" object, so old templates will not be able to call them anymore. We suggest
|
||||
that you always recompile your templates with the latest compiler in your build pipelines.
|
||||
|
||||
- Disallow calling "helperMissing" and "blockHelperMissing" directly - 2078c72
|
||||
- Calling "helperMissing" and "blockHelperMissing" directly from a template (like in `{{blockHelperMissing}}` was
|
||||
never intended and was part of the exploits that have been revealed early in 2019
|
||||
(see https://github.com/wycats/handlebars.js/issues/1495). *It is also part of a new exploit that
|
||||
is not captured by the earlier fix.* In order to harden Handlebars against such exploits, calling thos helpers
|
||||
is now not possible anymore. *Overriding* those helpers is still possible.
|
||||
- If you really need this behavior, you can set the runtime option `allowCallsToHelperMissing` to `true` and the
|
||||
calls will again be possible
|
||||
|
||||
Both bullet points imly that Handlebars is not 100% percent compatible to 4.2.0, despite the minor version bump.
|
||||
|
||||
We consider it more important to resolve a major security issue than to maintain 100% compatibility.
|
||||
|
||||
|
||||
|
||||
[Commits](https://github.com/wycats/handlebars.js/compare/v4.2.1...v4.3.0)
|
||||
|
||||
## v4.2.1 - September 20th, 2019
|
||||
Bugfixes:
|
||||
|
||||
+8
-2
@@ -1,14 +1,20 @@
|
||||
{
|
||||
"extends": [
|
||||
"../.eslintrc.js",
|
||||
"plugin:es5/no-es2015"
|
||||
],
|
||||
"plugins": [
|
||||
"es5"
|
||||
],
|
||||
"globals": {
|
||||
"CompilerContext": true,
|
||||
"Handlebars": true,
|
||||
"handlebarsEnv": true,
|
||||
|
||||
"shouldCompileTo": true,
|
||||
"shouldCompileToWithPartials": true,
|
||||
"shouldThrow": true,
|
||||
"expectTemplate": true,
|
||||
"compileWithPartials": true,
|
||||
|
||||
"console": true,
|
||||
"require": true,
|
||||
"suite": true,
|
||||
|
||||
+105
@@ -123,6 +123,40 @@ describe('ast', function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe('whitespace control', function() {
|
||||
describe('parse', function() {
|
||||
it('mustache', function() {
|
||||
var ast = Handlebars.parse(' {{~comment~}} ');
|
||||
|
||||
equals(ast.body[0].value, '');
|
||||
equals(ast.body[2].value, '');
|
||||
});
|
||||
|
||||
it('block statements', function() {
|
||||
var ast = Handlebars.parse(' {{# comment~}} \nfoo\n {{~/comment}}');
|
||||
|
||||
equals(ast.body[0].value, '');
|
||||
equals(ast.body[1].program.body[0].value, 'foo');
|
||||
});
|
||||
});
|
||||
|
||||
describe('parseWithoutProcessing', function() {
|
||||
it('mustache', function() {
|
||||
var ast = Handlebars.parseWithoutProcessing(' {{~comment~}} ');
|
||||
|
||||
equals(ast.body[0].value, ' ');
|
||||
equals(ast.body[2].value, ' ');
|
||||
});
|
||||
|
||||
it('block statements', function() {
|
||||
var ast = Handlebars.parseWithoutProcessing(' {{# comment~}} \nfoo\n {{~/comment}}');
|
||||
|
||||
equals(ast.body[0].value, ' ');
|
||||
equals(ast.body[1].program.body[0].value, ' \nfoo\n ');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('standalone flags', function() {
|
||||
describe('mustache', function() {
|
||||
it('does not mark mustaches as standalone', function() {
|
||||
@@ -131,6 +165,54 @@ describe('ast', function() {
|
||||
equals(!!ast.body[2].value, true);
|
||||
});
|
||||
});
|
||||
describe('blocks - parseWithoutProcessing', function() {
|
||||
it('block mustaches', function() {
|
||||
var ast = Handlebars.parseWithoutProcessing(' {{# comment}} \nfoo\n {{else}} \n bar \n {{/comment}} '),
|
||||
block = ast.body[1];
|
||||
|
||||
equals(ast.body[0].value, ' ');
|
||||
|
||||
equals(block.program.body[0].value, ' \nfoo\n ');
|
||||
equals(block.inverse.body[0].value, ' \n bar \n ');
|
||||
|
||||
equals(ast.body[2].value, ' ');
|
||||
});
|
||||
it('initial block mustaches', function() {
|
||||
var ast = Handlebars.parseWithoutProcessing('{{# comment}} \nfoo\n {{/comment}}'),
|
||||
block = ast.body[0];
|
||||
|
||||
equals(block.program.body[0].value, ' \nfoo\n ');
|
||||
});
|
||||
it('mustaches with children', function() {
|
||||
var ast = Handlebars.parseWithoutProcessing('{{# comment}} \n{{foo}}\n {{/comment}}'),
|
||||
block = ast.body[0];
|
||||
|
||||
equals(block.program.body[0].value, ' \n');
|
||||
equals(block.program.body[1].path.original, 'foo');
|
||||
equals(block.program.body[2].value, '\n ');
|
||||
});
|
||||
it('nested block mustaches', function() {
|
||||
var ast = Handlebars.parseWithoutProcessing('{{#foo}} \n{{# comment}} \nfoo\n {{else}} \n bar \n {{/comment}} \n{{/foo}}'),
|
||||
body = ast.body[0].program.body,
|
||||
block = body[1];
|
||||
|
||||
equals(body[0].value, ' \n');
|
||||
|
||||
equals(block.program.body[0].value, ' \nfoo\n ');
|
||||
equals(block.inverse.body[0].value, ' \n bar \n ');
|
||||
});
|
||||
it('column 0 block mustaches', function() {
|
||||
var ast = Handlebars.parseWithoutProcessing('test\n{{# comment}} \nfoo\n {{else}} \n bar \n {{/comment}} '),
|
||||
block = ast.body[1];
|
||||
|
||||
equals(ast.body[0].omit, undefined);
|
||||
|
||||
equals(block.program.body[0].value, ' \nfoo\n ');
|
||||
equals(block.inverse.body[0].value, ' \n bar \n ');
|
||||
|
||||
equals(ast.body[2].value, ' ');
|
||||
});
|
||||
});
|
||||
describe('blocks', function() {
|
||||
it('marks block mustaches as standalone', function() {
|
||||
var ast = Handlebars.parse(' {{# comment}} \nfoo\n {{else}} \n bar \n {{/comment}} '),
|
||||
@@ -204,6 +286,18 @@ describe('ast', function() {
|
||||
equals(ast.body[2].value, '');
|
||||
});
|
||||
});
|
||||
describe('partials - parseWithoutProcessing', function() {
|
||||
it('simple partial', function() {
|
||||
var ast = Handlebars.parseWithoutProcessing('{{> partial }} ');
|
||||
equals(ast.body[1].value, ' ');
|
||||
});
|
||||
it('indented partial', function() {
|
||||
var ast = Handlebars.parseWithoutProcessing(' {{> partial }} ');
|
||||
equals(ast.body[0].value, ' ');
|
||||
equals(ast.body[1].indent, '');
|
||||
equals(ast.body[2].value, ' ');
|
||||
});
|
||||
});
|
||||
describe('partials', function() {
|
||||
it('marks partial as standalone', function() {
|
||||
var ast = Handlebars.parse('{{> partial }} ');
|
||||
@@ -223,6 +317,17 @@ describe('ast', function() {
|
||||
equals(ast.body[1].omit, undefined);
|
||||
});
|
||||
});
|
||||
describe('comments - parseWithoutProcessing', function() {
|
||||
it('simple comment', function() {
|
||||
var ast = Handlebars.parseWithoutProcessing('{{! comment }} ');
|
||||
equals(ast.body[1].value, ' ');
|
||||
});
|
||||
it('indented comment', function() {
|
||||
var ast = Handlebars.parseWithoutProcessing(' {{! comment }} ');
|
||||
equals(ast.body[0].value, ' ');
|
||||
equals(ast.body[2].value, ' ');
|
||||
});
|
||||
});
|
||||
describe('comments', function() {
|
||||
it('marks comment as standalone', function() {
|
||||
var ast = Handlebars.parse('{{! comment }} ');
|
||||
|
||||
@@ -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() {
|
||||
|
||||
Vendored
+1
-1
@@ -9,7 +9,7 @@ var filename = 'dist/handlebars.js';
|
||||
if (global.minimizedTest) {
|
||||
filename = 'dist/handlebars.min.js';
|
||||
}
|
||||
var distHandlebars = fs.readFileSync(require.resolve(`../../${filename}`), 'utf-8');
|
||||
var distHandlebars = fs.readFileSync(require.resolve('../../' + filename), 'utf-8');
|
||||
vm.runInThisContext(distHandlebars, filename);
|
||||
|
||||
global.CompilerContext = {
|
||||
|
||||
Vendored
+83
-3
@@ -1,4 +1,6 @@
|
||||
var global = (function() { return this; }());
|
||||
var global = (function() {
|
||||
return this;
|
||||
}());
|
||||
|
||||
var AssertError;
|
||||
if (Error.captureStackTrace) {
|
||||
@@ -16,10 +18,16 @@ if (Error.captureStackTrace) {
|
||||
AssertError = Error;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use "expectTemplate(template)...toCompileTo(output)" instead
|
||||
*/
|
||||
global.shouldCompileTo = function(string, hashOrArray, expected, message) {
|
||||
shouldCompileToWithPartials(string, hashOrArray, false, expected, message);
|
||||
};
|
||||
|
||||
/**
|
||||
* @deprecated Use "expectTemplate(template)...toCompileTo(output)" instead
|
||||
*/
|
||||
global.shouldCompileToWithPartials = function shouldCompileToWithPartials(string, hashOrArray, partials, expected, message) {
|
||||
var result = compileWithPartials(string, hashOrArray, partials);
|
||||
if (result !== expected) {
|
||||
@@ -27,6 +35,9 @@ global.shouldCompileToWithPartials = function shouldCompileToWithPartials(string
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @deprecated Use "expectTemplate(template)...toCompileTo(output)" instead
|
||||
*/
|
||||
global.compileWithPartials = function(string, hashOrArray, partials) {
|
||||
var template,
|
||||
ary,
|
||||
@@ -36,8 +47,8 @@ global.compileWithPartials = function(string, hashOrArray, partials) {
|
||||
delete hashOrArray.hash;
|
||||
} else if (Object.prototype.toString.call(hashOrArray) === '[object Array]') {
|
||||
ary = [];
|
||||
ary.push(hashOrArray[0]);
|
||||
ary.push({ helpers: hashOrArray[1], partials: hashOrArray[2] });
|
||||
ary.push(hashOrArray[0]); // input
|
||||
ary.push({helpers: hashOrArray[1], partials: hashOrArray[2]});
|
||||
options = typeof hashOrArray[3] === 'object' ? hashOrArray[3] : {compat: hashOrArray[3]};
|
||||
if (hashOrArray[4] != null) {
|
||||
options.data = !!hashOrArray[4];
|
||||
@@ -75,3 +86,72 @@ global.shouldThrow = function(callback, type, msg) {
|
||||
throw new AssertError('It failed to throw', shouldThrow);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
global.expectTemplate = function(templateAsString) {
|
||||
return new HandlebarsTestBench(templateAsString);
|
||||
};
|
||||
|
||||
|
||||
function HandlebarsTestBench(templateAsString) {
|
||||
this.templateAsString = templateAsString;
|
||||
this.helpers = {};
|
||||
this.partials = {};
|
||||
this.input = {};
|
||||
this.message = 'Template' + templateAsString + ' does not evaluate to expected output';
|
||||
this.compileOptions = {};
|
||||
this.runtimeOptions = {};
|
||||
}
|
||||
|
||||
HandlebarsTestBench.prototype.withInput = function(input) {
|
||||
this.input = input;
|
||||
return this;
|
||||
};
|
||||
|
||||
HandlebarsTestBench.prototype.withHelper = function(name, helperFunction) {
|
||||
this.helpers[name] = helperFunction;
|
||||
return this;
|
||||
};
|
||||
|
||||
HandlebarsTestBench.prototype.withPartial = function(name, partialAsString) {
|
||||
this.partials[name] = partialAsString;
|
||||
return this;
|
||||
};
|
||||
|
||||
HandlebarsTestBench.prototype.withCompileOptions = function(compileOptions) {
|
||||
this.compileOptions = compileOptions;
|
||||
return this;
|
||||
};
|
||||
|
||||
HandlebarsTestBench.prototype.withRuntimeOptions = function(runtimeOptions) {
|
||||
this.runtimeOptions = runtimeOptions;
|
||||
return this;
|
||||
};
|
||||
|
||||
HandlebarsTestBench.prototype.withMessage = function(message) {
|
||||
this.message = message;
|
||||
return this;
|
||||
};
|
||||
|
||||
HandlebarsTestBench.prototype.toCompileTo = function(expectedOutputAsString) {
|
||||
var self = this;
|
||||
var compile = Object.keys(this.partials).length > 0
|
||||
? CompilerContext.compileWithPartial
|
||||
: CompilerContext.compile;
|
||||
|
||||
var combinedRuntimeOptions = {};
|
||||
Object.keys(this.runtimeOptions).forEach(function(key) {
|
||||
combinedRuntimeOptions[key] = self.runtimeOptions[key];
|
||||
});
|
||||
combinedRuntimeOptions.helpers = this.helpers;
|
||||
combinedRuntimeOptions.partials = this.partials;
|
||||
|
||||
var template = compile(this.templateAsString, this.compileOptions);
|
||||
var output = template(this.input, combinedRuntimeOptions);
|
||||
|
||||
if (output !== expectedOutputAsString) {
|
||||
// Error message formatted so that IntelliJ-Idea shows "diff"-button
|
||||
// https://stackoverflow.com/a/10945655/4251384
|
||||
throw new AssertError(this.message + '\nexpected:' + expectedOutputAsString + 'but was:' + output);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
define(['handlebars.runtime'], function(Handlebars) {
|
||||
Handlebars = Handlebars["default"]; var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
|
||||
return templates['empty'] = template({"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data) {
|
||||
return templates['empty'] = template({"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
|
||||
return "";
|
||||
},"useData":true});
|
||||
});
|
||||
|
||||
+102
-7
@@ -28,14 +28,43 @@ describe('helpers', function() {
|
||||
'raw block helper gets raw content');
|
||||
});
|
||||
|
||||
it('helper for nested raw block gets raw content', function() {
|
||||
var string = '{{{{a}}}} {{{{b}}}} {{{{/b}}}} {{{{/a}}}}';
|
||||
var helpers = {
|
||||
a: function(options) {
|
||||
describe('raw block parsing (with identity helper-function)', function() {
|
||||
|
||||
function runWithIdentityHelper(template, expected) {
|
||||
var helpers = {
|
||||
identity: function(options) {
|
||||
return options.fn();
|
||||
}
|
||||
};
|
||||
shouldCompileTo(string, [{}, helpers], ' {{{{b}}}} {{{{/b}}}} ', 'raw block helper should get nested raw block as raw content');
|
||||
}
|
||||
};
|
||||
shouldCompileTo(template, [{}, helpers], expected);
|
||||
}
|
||||
|
||||
it('helper for nested raw block gets raw content', function() {
|
||||
runWithIdentityHelper('{{{{identity}}}} {{{{b}}}} {{{{/b}}}} {{{{/identity}}}}', ' {{{{b}}}} {{{{/b}}}} ');
|
||||
});
|
||||
|
||||
it('helper for nested raw block works with empty content', function() {
|
||||
runWithIdentityHelper('{{{{identity}}}}{{{{/identity}}}}', '');
|
||||
});
|
||||
|
||||
xit('helper for nested raw block works if nested raw blocks are broken', function() {
|
||||
// This test was introduced in 4.4.4, but it was not the actual problem that lead to the patch release
|
||||
// The test is deactivated, because in 3.x this template cases an exception and it also does not work in 4.4.3
|
||||
// If anyone can make this template work without breaking everything else, then go for it,
|
||||
// but for now, this is just a known bug, that will be documented.
|
||||
runWithIdentityHelper('{{{{identity}}}} {{{{a}}}} {{{{ {{{{/ }}}} }}}} {{{{/identity}}}}', ' {{{{a}}}} {{{{ {{{{/ }}}} }}}} ');
|
||||
});
|
||||
|
||||
it('helper for nested raw block closes after first matching close', function() {
|
||||
runWithIdentityHelper('{{{{identity}}}}abc{{{{/identity}}}} {{{{identity}}}}abc{{{{/identity}}}}', 'abc abc');
|
||||
});
|
||||
|
||||
it('helper for nested raw block throw exception when with missing closing braces', function() {
|
||||
var string = '{{{{a}}}} {{{{/a';
|
||||
shouldThrow(function() {
|
||||
Handlebars.compile(string)();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('helper block with identical context', function() {
|
||||
@@ -737,4 +766,70 @@ describe('helpers', function() {
|
||||
shouldCompileTo('{{#if bar}}{{else goodbyes as |value|}}{{value}}{{/if}}{{value}}', [hash, helpers], '1foo');
|
||||
});
|
||||
});
|
||||
|
||||
describe('built-in helpers malformed arguments ', function() {
|
||||
it('if helper - too few arguments', function() {
|
||||
var template = CompilerContext.compile('{{#if}}{{/if}}');
|
||||
shouldThrow(function() {
|
||||
|
||||
template({});
|
||||
}, undefined, /#if requires exactly one argument/);
|
||||
});
|
||||
|
||||
it('if helper - too many arguments, string', function() {
|
||||
var template = CompilerContext.compile('{{#if test "string"}}{{/if}}');
|
||||
shouldThrow(function() {
|
||||
|
||||
template({});
|
||||
}, undefined, /#if requires exactly one argument/);
|
||||
});
|
||||
|
||||
it('if helper - too many arguments, undefined', function() {
|
||||
var template = CompilerContext.compile('{{#if test undefined}}{{/if}}');
|
||||
shouldThrow(function() {
|
||||
|
||||
template({});
|
||||
}, undefined, /#if requires exactly one argument/);
|
||||
});
|
||||
|
||||
it('if helper - too many arguments, null', function() {
|
||||
var template = CompilerContext.compile('{{#if test null}}{{/if}}');
|
||||
shouldThrow(function() {
|
||||
|
||||
template({});
|
||||
}, undefined, /#if requires exactly one argument/);
|
||||
});
|
||||
|
||||
it('unless helper - too few arguments', function() {
|
||||
var template = CompilerContext.compile('{{#unless}}{{/unless}}');
|
||||
shouldThrow(function() {
|
||||
|
||||
template({});
|
||||
}, undefined, /#unless requires exactly one argument/);
|
||||
});
|
||||
|
||||
it('unless helper - too many arguments', function() {
|
||||
var template = CompilerContext.compile('{{#unless test null}}{{/unless}}');
|
||||
shouldThrow(function() {
|
||||
|
||||
template({});
|
||||
}, undefined, /#unless requires exactly one argument/);
|
||||
});
|
||||
|
||||
it('with helper - too few arguments', function() {
|
||||
var template = CompilerContext.compile('{{#with}}{{/with}}');
|
||||
shouldThrow(function() {
|
||||
|
||||
template({});
|
||||
}, undefined, /#with requires exactly one argument/);
|
||||
});
|
||||
|
||||
it('with helper - too many arguments', function() {
|
||||
var template = CompilerContext.compile('{{#with test "string"}}{{/with}}');
|
||||
shouldThrow(function() {
|
||||
|
||||
template({});
|
||||
}, undefined, /#with requires exactly one argument/);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -291,4 +291,47 @@ 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});
|
||||
}
|
||||
});
|
||||
|
||||
it('should allow hash with protected array names', function() {
|
||||
var obj = {array: [1], name: 'John'};
|
||||
var helpers = {
|
||||
helpa: function(options) {
|
||||
return options.hash.length;
|
||||
}
|
||||
};
|
||||
|
||||
shouldCompileTo('{{helpa length="foo"}}', [obj, helpers], 'foo');
|
||||
});
|
||||
});
|
||||
|
||||
+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() {
|
||||
|
||||
+94
-3
@@ -1,11 +1,26 @@
|
||||
describe('security issues', function() {
|
||||
describe('GH-1495: Prevent Remote Code Execution via constructor', function() {
|
||||
it('should not allow constructors to be accessed', function() {
|
||||
shouldCompileTo('{{constructor.name}}', {}, '');
|
||||
shouldCompileTo('{{lookup (lookup this "constructor") "name"}}', {}, '');
|
||||
expectTemplate('{{lookup (lookup this "constructor") "name"}}')
|
||||
.withInput({})
|
||||
.toCompileTo('');
|
||||
|
||||
expectTemplate('{{constructor.name}}')
|
||||
.withInput({})
|
||||
.toCompileTo('');
|
||||
});
|
||||
|
||||
it('should allow the "constructor" property to be accessed if it is enumerable', function() {
|
||||
it('GH-1603: should not allow constructors to be accessed (lookup via toString)', function() {
|
||||
expectTemplate('{{lookup (lookup this (list "constructor")) "name"}}')
|
||||
.withInput({})
|
||||
.withHelper('list', function(element) {
|
||||
return [element];
|
||||
})
|
||||
.toCompileTo('');
|
||||
});
|
||||
|
||||
|
||||
it('should allow the "constructor" property to be accessed if it is enumerable', function() {
|
||||
shouldCompileTo('{{constructor.name}}', {'constructor': {
|
||||
'name': 'here we go'
|
||||
}}, 'here we go');
|
||||
@@ -14,6 +29,13 @@ describe('security issues', function() {
|
||||
}}, 'here we go');
|
||||
});
|
||||
|
||||
it('should allow the "constructor" property to be accessed if it is enumerable', function() {
|
||||
shouldCompileTo('{{lookup (lookup this "constructor") "name"}}', {'constructor': {
|
||||
'name': 'here we go'
|
||||
}}, 'here we go');
|
||||
});
|
||||
|
||||
|
||||
it('should allow prototype properties that are not constructors', function() {
|
||||
function TestClass() {
|
||||
}
|
||||
@@ -32,4 +54,73 @@ describe('security issues', function() {
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
describe('GH-1558: Prevent explicit call of helperMissing-helpers', function() {
|
||||
if (!Handlebars.compile) {
|
||||
return;
|
||||
}
|
||||
|
||||
describe('without the option "allowExplicitCallOfHelperMissing"', function() {
|
||||
it('should throw an exception when calling "{{helperMissing}}" ', function() {
|
||||
shouldThrow(function() {
|
||||
var template = Handlebars.compile('{{helperMissing}}');
|
||||
template({});
|
||||
}, Error);
|
||||
});
|
||||
it('should throw an exception when calling "{{#helperMissing}}{{/helperMissing}}" ', function() {
|
||||
shouldThrow(function() {
|
||||
var template = Handlebars.compile('{{#helperMissing}}{{/helperMissing}}');
|
||||
template({});
|
||||
}, Error);
|
||||
});
|
||||
it('should throw an exception when calling "{{blockHelperMissing "abc" .}}" ', function() {
|
||||
var functionCalls = [];
|
||||
shouldThrow(function() {
|
||||
var template = Handlebars.compile('{{blockHelperMissing "abc" .}}');
|
||||
template({ fn: function() { functionCalls.push('called'); }});
|
||||
}, Error);
|
||||
equals(functionCalls.length, 0);
|
||||
});
|
||||
it('should throw an exception when calling "{{#blockHelperMissing .}}{{/blockHelperMissing}}"', function() {
|
||||
shouldThrow(function() {
|
||||
var template = Handlebars.compile('{{#blockHelperMissing .}}{{/blockHelperMissing}}');
|
||||
template({ fn: function() { return 'functionInData';}});
|
||||
}, Error);
|
||||
});
|
||||
});
|
||||
|
||||
describe('with the option "allowCallsToHelperMissing" set to true', function() {
|
||||
it('should not throw an exception when calling "{{helperMissing}}" ', function() {
|
||||
var template = Handlebars.compile('{{helperMissing}}');
|
||||
template({}, {allowCallsToHelperMissing: true});
|
||||
});
|
||||
it('should not throw an exception when calling "{{#helperMissing}}{{/helperMissing}}" ', function() {
|
||||
var template = Handlebars.compile('{{#helperMissing}}{{/helperMissing}}');
|
||||
template({}, {allowCallsToHelperMissing: true});
|
||||
});
|
||||
it('should not throw an exception when calling "{{blockHelperMissing "abc" .}}" ', function() {
|
||||
var functionCalls = [];
|
||||
var template = Handlebars.compile('{{blockHelperMissing "abc" .}}');
|
||||
template({ fn: function() { functionCalls.push('called'); }}, {allowCallsToHelperMissing: true});
|
||||
equals(functionCalls.length, 1);
|
||||
});
|
||||
it('should not throw an exception when calling "{{#blockHelperMissing .}}{{/blockHelperMissing}}"', function() {
|
||||
var template = Handlebars.compile('{{#blockHelperMissing true}}sdads{{/blockHelperMissing}}');
|
||||
template({}, {allowCallsToHelperMissing: true});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
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}}', {}, '');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -95,6 +95,25 @@ describe('strict', function() {
|
||||
};
|
||||
equals(template({}, {helpers: helpers}), 'success');
|
||||
});
|
||||
|
||||
it('should show error location on missing property lookup', function() {
|
||||
shouldThrow(function() {
|
||||
var template = CompilerContext.compile('\n\n\n {{hello}}', {strict: true});
|
||||
template({});
|
||||
}, Exception, '"hello" not defined in [object Object] - 4:5');
|
||||
});
|
||||
|
||||
it('should error contains correct location properties on missing property lookup', function() {
|
||||
try {
|
||||
var template = CompilerContext.compile('\n\n\n {{hello}}', {strict: true});
|
||||
template({});
|
||||
} catch (error) {
|
||||
equals(error.lineNumber, 4);
|
||||
equals(error.endLineNumber, 4);
|
||||
equals(error.column, 5);
|
||||
equals(error.endColumn, 10);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('assume objects', function() {
|
||||
|
||||
@@ -441,4 +441,9 @@ describe('Tokenizer', function() {
|
||||
result = tokenize('{{else foo as |bar baz|}}');
|
||||
shouldMatchTokens(result, ['OPEN_INVERSE_CHAIN', 'ID', 'OPEN_BLOCK_PARAMS', 'ID', 'ID', 'CLOSE_BLOCK_PARAMS', 'CLOSE']);
|
||||
});
|
||||
|
||||
it('tokenizes raw blocks', function() {
|
||||
var result = tokenize('{{{{a}}}} abc {{{{/a}}}} aaa {{{{a}}}} abc {{{{/a}}}}');
|
||||
shouldMatchTokens(result, ['OPEN_RAW_BLOCK', 'ID', 'CLOSE_RAW_BLOCK', 'CONTENT', 'END_RAW_BLOCK', 'CONTENT', 'OPEN_RAW_BLOCK', 'ID', 'CLOSE_RAW_BLOCK', 'CONTENT', 'END_RAW_BLOCK']);
|
||||
});
|
||||
});
|
||||
|
||||
+1
-1
@@ -63,7 +63,7 @@ ID [^\s!"#%-,\.\/;->@\[-\^`\{-~]+/{LOOKAHEAD}
|
||||
return 'END_RAW_BLOCK';
|
||||
}
|
||||
}
|
||||
<raw>[^\x00]*?/("{{{{") { return 'CONTENT'; }
|
||||
<raw>[^\x00]+?/("{{{{") { return 'CONTENT'; }
|
||||
|
||||
<com>[\s\S]*?"--"{RIGHT_STRIP}?"}}" {
|
||||
this.popState();
|
||||
|
||||
+1
-1
@@ -39,7 +39,7 @@ content
|
||||
};
|
||||
|
||||
rawBlock
|
||||
: openRawBlock content+ END_RAW_BLOCK -> yy.prepareRawBlock($1, $2, $3, @$)
|
||||
: openRawBlock content* END_RAW_BLOCK -> yy.prepareRawBlock($1, $2, $3, @$)
|
||||
;
|
||||
|
||||
openRawBlock
|
||||
|
||||
Vendored
+37
-5
@@ -29,6 +29,7 @@ declare namespace Handlebars {
|
||||
decorators?: { [name: string]: Function };
|
||||
data?: any;
|
||||
blockParams?: any[];
|
||||
allowCallsToHelperMissing?: boolean;
|
||||
}
|
||||
|
||||
export interface HelperOptions {
|
||||
@@ -46,8 +47,8 @@ declare namespace Handlebars {
|
||||
}
|
||||
|
||||
export interface ParseOptions {
|
||||
srcName?: string,
|
||||
ignoreStandalone?: boolean
|
||||
srcName?: string;
|
||||
ignoreStandalone?: boolean;
|
||||
}
|
||||
|
||||
export function registerHelper(name: string, fn: HelperDelegate): void;
|
||||
@@ -65,9 +66,9 @@ declare namespace Handlebars {
|
||||
export function K(): void;
|
||||
export function createFrame(object: any): any;
|
||||
export function blockParams(obj: any[], ids: any[]): any[];
|
||||
export function Exception(message: string): void;
|
||||
export function log(level: number, obj: any): void;
|
||||
export function parse(input: string, options?: ParseOptions): hbs.AST.Program;
|
||||
export function parseWithoutProcessing(input: string, options?: ParseOptions): hbs.AST.Program;
|
||||
export function compile<T = any>(input: any, options?: CompileOptions): HandlebarsTemplateDelegate<T>;
|
||||
export function precompile(input: any, options?: PrecompileOptions): TemplateSpecification;
|
||||
export function template<T = any>(precompilation: TemplateSpecification): HandlebarsTemplateDelegate<T>;
|
||||
@@ -85,6 +86,20 @@ declare namespace Handlebars {
|
||||
|
||||
export function noConflict(): typeof Handlebars;
|
||||
|
||||
export class Exception {
|
||||
constructor(message: string, node?: hbs.AST.Node);
|
||||
description: string;
|
||||
fileName: string;
|
||||
lineNumber?: any;
|
||||
endLineNumber?: any;
|
||||
message: string;
|
||||
name: string;
|
||||
number: number;
|
||||
stack?: string;
|
||||
column?: any;
|
||||
endColumn?: any;
|
||||
}
|
||||
|
||||
export class SafeString {
|
||||
constructor(str: string);
|
||||
toString(): string;
|
||||
@@ -269,6 +284,7 @@ declare namespace hbs {
|
||||
interface Statement extends Node {}
|
||||
|
||||
interface MustacheStatement extends Statement {
|
||||
type: 'MustacheStatement';
|
||||
path: PathExpression | Literal;
|
||||
params: Expression[];
|
||||
hash: Hash;
|
||||
@@ -279,6 +295,7 @@ declare namespace hbs {
|
||||
interface Decorator extends MustacheStatement { }
|
||||
|
||||
interface BlockStatement extends Statement {
|
||||
type: 'BlockStatement';
|
||||
path: PathExpression;
|
||||
params: Expression[];
|
||||
hash: Hash;
|
||||
@@ -292,6 +309,7 @@ declare namespace hbs {
|
||||
interface DecoratorBlock extends BlockStatement { }
|
||||
|
||||
interface PartialStatement extends Statement {
|
||||
type: 'PartialStatement';
|
||||
name: PathExpression | SubExpression;
|
||||
params: Expression[];
|
||||
hash: Hash;
|
||||
@@ -300,6 +318,7 @@ declare namespace hbs {
|
||||
}
|
||||
|
||||
interface PartialBlockStatement extends Statement {
|
||||
type: 'PartialBlockStatement';
|
||||
name: PathExpression | SubExpression;
|
||||
params: Expression[];
|
||||
hash: Hash;
|
||||
@@ -309,11 +328,13 @@ declare namespace hbs {
|
||||
}
|
||||
|
||||
interface ContentStatement extends Statement {
|
||||
type: 'ContentStatement';
|
||||
value: string;
|
||||
original: StripFlags;
|
||||
}
|
||||
|
||||
interface CommentStatement extends Statement {
|
||||
type: 'CommentStatement';
|
||||
value: string;
|
||||
strip: StripFlags;
|
||||
}
|
||||
@@ -321,12 +342,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[];
|
||||
@@ -335,29 +358,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;
|
||||
}
|
||||
|
||||
+134
-1
@@ -97,7 +97,7 @@ Handlebars.VM.resolvePartial = <T>(partial: HandlebarsTemplateDelegate<T> | unde
|
||||
// transform name.
|
||||
options.name = name;
|
||||
return originalResolvePartial(partial, context, options);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// #1544, allow custom helpers in knownHelpers
|
||||
@@ -107,3 +107,136 @@ Handlebars.compile('test', {
|
||||
customHelper: true
|
||||
}
|
||||
});
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
function testParseWithoutProcessing() {
|
||||
const parsedTemplate: hbs.AST.Program = Handlebars.parseWithoutProcessing('<p>Hello, my name is {{name}}.</p>', {
|
||||
srcName: "/foo/bar/baz.hbs",
|
||||
});
|
||||
|
||||
const parsedTemplateWithoutOptions: hbs.AST.Program = Handlebars.parseWithoutProcessing('<p>Hello, my name is {{name}}.</p>');
|
||||
}
|
||||
|
||||
function testExceptionTypings() {
|
||||
// Test exception constructor with a single argument - message.
|
||||
let exception: Handlebars.Exception = new Handlebars.Exception('message');
|
||||
|
||||
// Fields
|
||||
let message: string = exception.message;
|
||||
let lineNumber: number = exception.lineNumber;
|
||||
let column: number = exception.column;
|
||||
let endLineNumber: number = exception.endLineNumber;
|
||||
let endColumn: number = exception.endColumn;
|
||||
let description = exception.description;
|
||||
let name: string = exception.name;
|
||||
let fileName: string = exception.fileName;
|
||||
let stack: string | undefined = exception.stack;
|
||||
}
|
||||
|
||||
function testExceptionWithNodeTypings() {
|
||||
// Test exception constructor with both arguments.
|
||||
const exception: Handlebars.Exception = new Handlebars.Exception('message', {
|
||||
type: 'MustacheStatement',
|
||||
loc: {
|
||||
source: 'source',
|
||||
start: { line: 1, column: 5 },
|
||||
end: { line: 10, column: 2 }
|
||||
}
|
||||
});
|
||||
|
||||
// Fields
|
||||
let message: string = exception.message;
|
||||
let lineNumber: number = exception.lineNumber;
|
||||
let column: number = exception.column;
|
||||
let endLineNumber: number = exception.endLineNumber;
|
||||
let endColumn: number = exception.endColumn;
|
||||
let description = exception.description;
|
||||
let name: string = exception.name;
|
||||
let fileName: string = exception.fileName;
|
||||
let stack: string | undefined = exception.stack;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user