[@kbn/handlebars] Code cleanup (#147425)

This commit is contained in:
Thomas Watson 2022-12-19 18:24:26 +01:00 committed by GitHub
parent 794e721cc0
commit b00a2643cd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 439 additions and 433 deletions

View file

@ -429,15 +429,19 @@
> beforeEach(() => { > beforeEach(() => {
> global.kbnHandlebarsEnv = Handlebars.create(); > global.kbnHandlebarsEnv = Handlebars.create();
> }); > });
410c319,323 410c319,327
< handlebarsEnv.registerDecorator('foo', function() { < handlebarsEnv.registerDecorator('foo', function() {
--- ---
> afterEach(() => {
> global.kbnHandlebarsEnv = null;
> });
>
> it('unregisters', () => { > it('unregisters', () => {
> // @ts-expect-error: Cannot assign to 'decorators' because it is a read-only property. > // @ts-expect-error: Cannot assign to 'decorators' because it is a read-only property.
> kbnHandlebarsEnv!.decorators = {}; > kbnHandlebarsEnv!.decorators = {};
> >
> kbnHandlebarsEnv!.registerDecorator('foo', function () { > kbnHandlebarsEnv!.registerDecorator('foo', function () {
414,416c327,329 414,416c331,333
< equals(!!handlebarsEnv.decorators.foo, true); < equals(!!handlebarsEnv.decorators.foo, true);
< handlebarsEnv.unregisterDecorator('foo'); < handlebarsEnv.unregisterDecorator('foo');
< equals(handlebarsEnv.decorators.foo, undefined); < equals(handlebarsEnv.decorators.foo, undefined);
@ -445,14 +449,14 @@
> expect(!!kbnHandlebarsEnv!.decorators.foo).toEqual(true); > expect(!!kbnHandlebarsEnv!.decorators.foo).toEqual(true);
> kbnHandlebarsEnv!.unregisterDecorator('foo'); > kbnHandlebarsEnv!.unregisterDecorator('foo');
> expect(kbnHandlebarsEnv!.decorators.foo).toBeUndefined(); > expect(kbnHandlebarsEnv!.decorators.foo).toBeUndefined();
419,420c332,334 419,420c336,338
< it('allows multiple globals', function() { < it('allows multiple globals', function() {
< handlebarsEnv.decorators = {}; < handlebarsEnv.decorators = {};
--- ---
> it('allows multiple globals', () => { > it('allows multiple globals', () => {
> // @ts-expect-error: Cannot assign to 'decorators' because it is a read-only property. > // @ts-expect-error: Cannot assign to 'decorators' because it is a read-only property.
> kbnHandlebarsEnv!.decorators = {}; > kbnHandlebarsEnv!.decorators = {};
422,424c336,339 422,424c340,343
< handlebarsEnv.registerDecorator({ < handlebarsEnv.registerDecorator({
< foo: function() {}, < foo: function() {},
< bar: function() {} < bar: function() {}
@ -461,7 +465,7 @@
> kbnHandlebarsEnv!.registerDecorator({ > kbnHandlebarsEnv!.registerDecorator({
> foo() {}, > foo() {},
> bar() {}, > bar() {},
427,432c342,347 427,432c346,351
< equals(!!handlebarsEnv.decorators.foo, true); < equals(!!handlebarsEnv.decorators.foo, true);
< equals(!!handlebarsEnv.decorators.bar, true); < equals(!!handlebarsEnv.decorators.bar, true);
< handlebarsEnv.unregisterDecorator('foo'); < handlebarsEnv.unregisterDecorator('foo');
@ -475,7 +479,7 @@
> kbnHandlebarsEnv!.unregisterDecorator('bar'); > kbnHandlebarsEnv!.unregisterDecorator('bar');
> expect(kbnHandlebarsEnv!.decorators.foo).toBeUndefined(); > expect(kbnHandlebarsEnv!.decorators.foo).toBeUndefined();
> expect(kbnHandlebarsEnv!.decorators.bar).toBeUndefined(); > expect(kbnHandlebarsEnv!.decorators.bar).toBeUndefined();
435,445c350,356 435,445c354,360
< it('fails with multiple and args', function() { < it('fails with multiple and args', function() {
< shouldThrow( < shouldThrow(
< function() { < function() {
@ -495,7 +499,7 @@
> { > {
> world() { > world() {
> return 'world!'; > return 'world!';
447,452c358,364 447,452c362,368
< {} < {}
< ); < );
< }, < },

View file

@ -668,7 +668,10 @@
--- ---
> >
> afterEach(function () { > afterEach(function () {
578,580c481,484 575a479,480
>
> global.kbnHandlebarsEnv = null;
578,580c483,486
< it('should call logger at default level', function() { < it('should call logger at default level', function() {
< var levelArg, logArg; < var levelArg, logArg;
< handlebarsEnv.log = function(level, arg) { < handlebarsEnv.log = function(level, arg) {
@ -677,7 +680,7 @@
> let levelArg; > let levelArg;
> let logArg; > let logArg;
> kbnHandlebarsEnv!.log = function (level, arg) { > kbnHandlebarsEnv!.log = function (level, arg) {
585,590c489,491 585,590c491,493
< expectTemplate('{{log blah}}') < expectTemplate('{{log blah}}')
< .withInput({ blah: 'whee' }) < .withInput({ blah: 'whee' })
< .withMessage('log should not display') < .withMessage('log should not display')
@ -688,7 +691,7 @@
> expectTemplate('{{log blah}}').withInput({ blah: 'whee' }).toCompileTo(''); > expectTemplate('{{log blah}}').withInput({ blah: 'whee' }).toCompileTo('');
> expect(1).toEqual(levelArg); > expect(1).toEqual(levelArg);
> expect('whee').toEqual(logArg); > expect('whee').toEqual(logArg);
593,595c494,497 593,595c496,499
< it('should call logger at data level', function() { < it('should call logger at data level', function() {
< var levelArg, logArg; < var levelArg, logArg;
< handlebarsEnv.log = function(level, arg) { < handlebarsEnv.log = function(level, arg) {
@ -697,20 +700,20 @@
> let levelArg; > let levelArg;
> let logArg; > let logArg;
> kbnHandlebarsEnv!.log = function (level, arg) { > kbnHandlebarsEnv!.log = function (level, arg) {
605,606c507,508 605,606c509,510
< equals('03', levelArg); < equals('03', levelArg);
< equals('whee', logArg); < equals('whee', logArg);
--- ---
> expect('03').toEqual(levelArg); > expect('03').toEqual(levelArg);
> expect('whee').toEqual(logArg); > expect('whee').toEqual(logArg);
609,610c511,513 609,610c513,515
< it('should output to info', function() { < it('should output to info', function() {
< var called; < var called;
--- ---
> it('should output to info', function () { > it('should output to info', function () {
> let calls = 0; > let calls = 0;
> const callsExpected = process.env.AST || process.env.EVAL ? 1 : 2; > const callsExpected = process.env.AST || process.env.EVAL ? 1 : 2;
612,616c515,521 612,616c517,523
< console.info = function(info) { < console.info = function(info) {
< equals('whee', info); < equals('whee', info);
< called = true; < called = true;
@ -724,7 +727,7 @@
> console.info = $info; > console.info = $info;
> console.log = $log; > console.log = $log;
> } > }
618,622c523,529 618,622c525,531
< console.log = function(log) { < console.log = function(log) {
< equals('whee', log); < equals('whee', log);
< called = true; < called = true;
@ -738,7 +741,7 @@
> console.info = $info; > console.info = $info;
> console.log = $log; > console.log = $log;
> } > }
625,628c532,533 625,628c534,535
< expectTemplate('{{log blah}}') < expectTemplate('{{log blah}}')
< .withInput({ blah: 'whee' }) < .withInput({ blah: 'whee' })
< .toCompileTo(''); < .toCompileTo('');
@ -746,14 +749,14 @@
--- ---
> expectTemplate('{{log blah}}').withInput({ blah: 'whee' }).toCompileTo(''); > expectTemplate('{{log blah}}').withInput({ blah: 'whee' }).toCompileTo('');
> expect(calls).toEqual(callsExpected); > expect(calls).toEqual(callsExpected);
631,632c536,538 631,632c538,540
< it('should log at data level', function() { < it('should log at data level', function() {
< var called; < var called;
--- ---
> it('should log at data level', function () { > it('should log at data level', function () {
> let calls = 0; > let calls = 0;
> const callsExpected = process.env.AST || process.env.EVAL ? 1 : 2; > const callsExpected = process.env.AST || process.env.EVAL ? 1 : 2;
634,637c540,543 634,637c542,545
< console.error = function(log) { < console.error = function(log) {
< equals('whee', log); < equals('whee', log);
< called = true; < called = true;
@ -763,20 +766,20 @@
> expect('whee').toEqual(log); > expect('whee').toEqual(log);
> calls++; > calls++;
> if (calls === callsExpected) console.error = $error; > if (calls === callsExpected) console.error = $error;
645c551 645c553
< equals(true, called); < equals(true, called);
--- ---
> expect(calls).toEqual(callsExpected); > expect(calls).toEqual(callsExpected);
648,649c554,556 648,649c556,558
< it('should handle missing logger', function() { < it('should handle missing logger', function() {
< var called = false; < var called = false;
--- ---
> it('should handle missing logger', function () { > it('should handle missing logger', function () {
> let calls = 0; > let calls = 0;
> const callsExpected = process.env.AST || process.env.EVAL ? 1 : 2; > const callsExpected = process.env.AST || process.env.EVAL ? 1 : 2;
650a558 650a560
> // @ts-expect-error > // @ts-expect-error
652,655c560,563 652,655c562,565
< console.log = function(log) { < console.log = function(log) {
< equals('whee', log); < equals('whee', log);
< called = true; < called = true;
@ -786,18 +789,18 @@
> expect('whee').toEqual(log); > expect('whee').toEqual(log);
> calls++; > calls++;
> if (calls === callsExpected) console.log = $log; > if (calls === callsExpected) console.log = $log;
663c571 663c573
< equals(true, called); < equals(true, called);
--- ---
> expect(calls).toEqual(callsExpected); > expect(calls).toEqual(callsExpected);
666,667c574,576 666,667c576,578
< it('should handle string log levels', function() { < it('should handle string log levels', function() {
< var called; < var called;
--- ---
> it('should handle string log levels', function () { > it('should handle string log levels', function () {
> let calls = 0; > let calls = 0;
> const callsExpected = process.env.AST || process.env.EVAL ? 1 : 2; > const callsExpected = process.env.AST || process.env.EVAL ? 1 : 2;
669,671c578,580 669,671c580,582
< console.error = function(log) { < console.error = function(log) {
< equals('whee', log); < equals('whee', log);
< called = true; < called = true;
@ -805,26 +808,26 @@
> console.error = function (log) { > console.error = function (log) {
> expect('whee').toEqual(log); > expect('whee').toEqual(log);
> calls++; > calls++;
679c588 679c590
< equals(true, called); < equals(true, called);
--- ---
> expect(calls).toEqual(callsExpected); > expect(calls).toEqual(callsExpected);
681c590 681c592
< called = false; < called = false;
--- ---
> calls = 0; > calls = 0;
688c597 688c599
< equals(true, called); < equals(true, called);
--- ---
> expect(calls).toEqual(callsExpected); > expect(calls).toEqual(callsExpected);
691,692c600,602 691,692c602,604
< it('should handle hash log levels', function() { < it('should handle hash log levels', function() {
< var called; < var called;
--- ---
> it('should handle hash log levels [1]', function () { > it('should handle hash log levels [1]', function () {
> let calls = 0; > let calls = 0;
> const callsExpected = process.env.AST || process.env.EVAL ? 1 : 2; > const callsExpected = process.env.AST || process.env.EVAL ? 1 : 2;
694,696c604,606 694,696c606,608
< console.error = function(log) { < console.error = function(log) {
< equals('whee', log); < equals('whee', log);
< called = true; < called = true;
@ -832,7 +835,7 @@
> console.error = function (log) { > console.error = function (log) {
> expect('whee').toEqual(log); > expect('whee').toEqual(log);
> calls++; > calls++;
699,702c609,610 699,702c611,612
< expectTemplate('{{log blah level="error"}}') < expectTemplate('{{log blah level="error"}}')
< .withInput({ blah: 'whee' }) < .withInput({ blah: 'whee' })
< .toCompileTo(''); < .toCompileTo('');
@ -840,13 +843,13 @@
--- ---
> expectTemplate('{{log blah level="error"}}').withInput({ blah: 'whee' }).toCompileTo(''); > expectTemplate('{{log blah level="error"}}').withInput({ blah: 'whee' }).toCompileTo('');
> expect(calls).toEqual(callsExpected); > expect(calls).toEqual(callsExpected);
705,706c613,614 705,706c615,616
< it('should handle hash log levels', function() { < it('should handle hash log levels', function() {
< var called = false; < var called = false;
--- ---
> it('should handle hash log levels [2]', function () { > it('should handle hash log levels [2]', function () {
> let called = false; > let called = false;
708,711c616,623 708,711c618,625
< console.info = console.log = console.error = console.debug = function() { < console.info = console.log = console.error = console.debug = function() {
< called = true; < called = true;
< console.info = console.log = console.error = console.debug = $log; < console.info = console.log = console.error = console.debug = $log;
@ -860,7 +863,7 @@
> called = true; > called = true;
> console.info = console.log = console.error = console.debug = $log; > console.info = console.log = console.error = console.debug = $log;
> }; > };
713,716c625,626 713,716c627,628
< expectTemplate('{{log blah level="debug"}}') < expectTemplate('{{log blah level="debug"}}')
< .withInput({ blah: 'whee' }) < .withInput({ blah: 'whee' })
< .toCompileTo(''); < .toCompileTo('');
@ -868,14 +871,14 @@
--- ---
> expectTemplate('{{log blah level="debug"}}').withInput({ blah: 'whee' }).toCompileTo(''); > expectTemplate('{{log blah level="debug"}}').withInput({ blah: 'whee' }).toCompileTo('');
> expect(false).toEqual(called); > expect(false).toEqual(called);
719,720c629,631 719,720c631,633
< it('should pass multiple log arguments', function() { < it('should pass multiple log arguments', function() {
< var called; < var called;
--- ---
> it('should pass multiple log arguments', function () { > it('should pass multiple log arguments', function () {
> let calls = 0; > let calls = 0;
> const callsExpected = process.env.AST || process.env.EVAL ? 1 : 2; > const callsExpected = process.env.AST || process.env.EVAL ? 1 : 2;
722,727c633,638 722,727c635,640
< console.info = console.log = function(log1, log2, log3) { < console.info = console.log = function(log1, log2, log3) {
< equals('whee', log1); < equals('whee', log1);
< equals('foo', log2); < equals('foo', log2);
@ -889,7 +892,7 @@
> expect(1).toEqual(log3); > expect(1).toEqual(log3);
> calls++; > calls++;
> if (calls === callsExpected) console.log = $log; > if (calls === callsExpected) console.log = $log;
730,733c641,642 730,733c643,644
< expectTemplate('{{log blah "foo" 1}}') < expectTemplate('{{log blah "foo" 1}}')
< .withInput({ blah: 'whee' }) < .withInput({ blah: 'whee' })
< .toCompileTo(''); < .toCompileTo('');
@ -897,14 +900,14 @@
--- ---
> expectTemplate('{{log blah "foo" 1}}').withInput({ blah: 'whee' }).toCompileTo(''); > expectTemplate('{{log blah "foo" 1}}').withInput({ blah: 'whee' }).toCompileTo('');
> expect(calls).toEqual(callsExpected); > expect(calls).toEqual(callsExpected);
736,737c645,647 736,737c647,649
< it('should pass zero log arguments', function() { < it('should pass zero log arguments', function() {
< var called; < var called;
--- ---
> it('should pass zero log arguments', function () { > it('should pass zero log arguments', function () {
> let calls = 0; > let calls = 0;
> const callsExpected = process.env.AST || process.env.EVAL ? 1 : 2; > const callsExpected = process.env.AST || process.env.EVAL ? 1 : 2;
739,742c649,652 739,742c651,654
< console.info = console.log = function() { < console.info = console.log = function() {
< expect(arguments.length).to.equal(0); < expect(arguments.length).to.equal(0);
< called = true; < called = true;
@ -914,7 +917,7 @@
> expect(arguments.length).toEqual(0); > expect(arguments.length).toEqual(0);
> calls++; > calls++;
> if (calls === callsExpected) console.log = $log; > if (calls === callsExpected) console.log = $log;
745,748c655,656 745,748c657,658
< expectTemplate('{{log}}') < expectTemplate('{{log}}')
< .withInput({ blah: 'whee' }) < .withInput({ blah: 'whee' })
< .toCompileTo(''); < .toCompileTo('');
@ -922,13 +925,13 @@
--- ---
> expectTemplate('{{log}}').withInput({ blah: 'whee' }).toCompileTo(''); > expectTemplate('{{log}}').withInput({ blah: 'whee' }).toCompileTo('');
> expect(calls).toEqual(callsExpected); > expect(calls).toEqual(callsExpected);
753,754c661,662 753,754c663,664
< describe('#lookup', function() { < describe('#lookup', function() {
< it('should lookup arbitrary content', function() { < it('should lookup arbitrary content', function() {
--- ---
> describe('#lookup', () => { > describe('#lookup', () => {
> it('should lookup arbitrary content', () => { > it('should lookup arbitrary content', () => {
760c668 760c670
< it('should not fail on undefined value', function() { < it('should not fail on undefined value', function() {
--- ---
> it('should not fail on undefined value', () => { > it('should not fail on undefined value', () => {

View file

@ -1,8 +1,14 @@
1,4c1,6 1,10c1,6
< describe('compiler', function() { < describe('compiler', function() {
< if (!Handlebars.compile) { < if (!Handlebars.compile) {
< return; < return;
< } < }
<
< describe('#equals', function() {
< function compile(string) {
< var ast = Handlebars.parse(string);
< return new Handlebars.Compiler().compile(ast, {});
< }
--- ---
> /* > /*
> * This file is forked from the handlebars project (https://github.com/handlebars-lang/handlebars.js), > * This file is forked from the handlebars project (https://github.com/handlebars-lang/handlebars.js),
@ -10,15 +16,7 @@
> * Elasticsearch B.V. licenses this file to you under the MIT License. > * Elasticsearch B.V. licenses this file to you under the MIT License.
> * See `packages/kbn-handlebars/LICENSE` for more information. > * See `packages/kbn-handlebars/LICENSE` for more information.
> */ > */
6,10c8 12,60c8,9
< describe('#equals', function() {
< function compile(string) {
< var ast = Handlebars.parse(string);
< return new Handlebars.Compiler().compile(ast, {});
< }
---
> import Handlebars from '../..';
12,60c10,13
< it('should treat as equal', function() { < it('should treat as equal', function() {
< equal(compile('foo').equals(compile('foo')), true); < equal(compile('foo').equals(compile('foo')), true);
< equal(compile('{{foo}}').equals(compile('{{foo}}')), true); < equal(compile('{{foo}}').equals(compile('{{foo}}')), true);
@ -69,11 +67,9 @@
< }); < });
< }); < });
--- ---
> describe('compiler', () => { > import Handlebars from '../..';
> const compileFns = ['compile', 'compileAST']; > import { forEachCompileFunctionName } from '../__jest__/test_bench';
> if (process.env.AST) compileFns.splice(0, 1); 62,78c11,13
> else if (process.env.EVAL) compileFns.splice(1, 1);
62,78c15,17
< describe('#compile', function() { < describe('#compile', function() {
< it('should fail with invalid input', function() { < it('should fail with invalid input', function() {
< shouldThrow( < shouldThrow(
@ -92,10 +88,10 @@
< ); < );
< }); < });
--- ---
> compileFns.forEach((compileName) => { > describe('compiler', () => {
> // @ts-expect-error > forEachCompileFunctionName((compileName) => {
> const compile = Handlebars[compileName]; > const compile = Handlebars[compileName].bind(Handlebars);
80,92c19,24 80,92c15,20
< it('should include the location in the error (row and column)', function() { < it('should include the location in the error (row and column)', function() {
< try { < try {
< Handlebars.compile(' \n {{#if}}\n{{/def}}')(); < Handlebars.compile(' \n {{#if}}\n{{/def}}')();
@ -116,7 +112,7 @@
> compile(null); > compile(null);
> }).toThrow( > }).toThrow(
> `You must pass a string or Handlebars AST to Handlebars.${compileName}. You passed null` > `You must pass a string or Handlebars AST to Handlebars.${compileName}. You passed null`
94,102d25 94,102d21
< if (Object.getOwnPropertyDescriptor(err, 'column').writable) { < if (Object.getOwnPropertyDescriptor(err, 'column').writable) {
< // In Safari 8, the column-property is read-only. This means that even if it is set with defineProperty, < // In Safari 8, the column-property is read-only. This means that even if it is set with defineProperty,
< // its value won't change (https://github.com/jquery/esprima/issues/1290#issuecomment-132455482) < // its value won't change (https://github.com/jquery/esprima/issues/1290#issuecomment-132455482)
@ -126,7 +122,7 @@
< equal(err.lineNumber, 2, 'Checking error row'); < equal(err.lineNumber, 2, 'Checking error row');
< } < }
< }); < });
104,116c27,30 104,116c23,26
< it('should include the location as enumerable property', function() { < it('should include the location as enumerable property', function() {
< try { < try {
< Handlebars.compile(' \n {{#if}}\n{{/def}}')(); < Handlebars.compile(' \n {{#if}}\n{{/def}}')();
@ -145,7 +141,7 @@
> compile({}); > compile({});
> }).toThrow( > }).toThrow(
> `You must pass a string or Handlebars AST to Handlebars.${compileName}. You passed [object Object]` > `You must pass a string or Handlebars AST to Handlebars.${compileName}. You passed [object Object]`
118,129c32 118,129c28
< } < }
< }); < });
< <
@ -160,7 +156,7 @@
< }); < });
--- ---
> }); > });
131,133c34,48 131,133c30,44
< it('can pass through an empty string', function() { < it('can pass through an empty string', function() {
< equal(Handlebars.compile('')(), ''); < equal(Handlebars.compile('')(), '');
< }); < });
@ -180,7 +176,7 @@
> expect(err.lineNumber).toEqual(2); > expect(err.lineNumber).toEqual(2);
> } > }
> }); > });
135,142c50,57 135,142c46,53
< it('should not modify the options.data property(GH-1327)', function() { < it('should not modify the options.data property(GH-1327)', function() {
< var options = { data: [{ a: 'foo' }, { a: 'bar' }] }; < var options = { data: [{ a: 'foo' }, { a: 'bar' }] };
< Handlebars.compile('{{#each data}}{{@index}}:{{a}} {{/each}}', options)(); < Handlebars.compile('{{#each data}}{{@index}}:{{a}} {{/each}}', options)();
@ -198,7 +194,7 @@
> expect(Object.prototype.propertyIsEnumerable.call(err, 'column')).toEqual(true); > expect(Object.prototype.propertyIsEnumerable.call(err, 'column')).toEqual(true);
> } > }
> }); > });
144,152c59,66 144,152c55,62
< it('should not modify the options.knownHelpers property(GH-1327)', function() { < it('should not modify the options.knownHelpers property(GH-1327)', function() {
< var options = { knownHelpers: {} }; < var options = { knownHelpers: {} };
< Handlebars.compile('{{#each data}}{{@index}}:{{a}} {{/each}}', options)(); < Handlebars.compile('{{#each data}}{{@index}}:{{a}} {{/each}}', options)();
@ -217,7 +213,7 @@
> })({}) > })({})
> ).toEqual('Hello'); > ).toEqual('Hello');
> }); > });
154,170c68,70 154,170c64,66
< describe('#precompile', function() { < describe('#precompile', function() {
< it('should fail with invalid input', function() { < it('should fail with invalid input', function() {
< shouldThrow( < shouldThrow(
@ -239,7 +235,7 @@
> it('can pass through an empty string', () => { > it('can pass through an empty string', () => {
> expect(compile('')({})).toEqual(''); > expect(compile('')({})).toEqual('');
> }); > });
172,182c72,78 172,182c68,75
< it('can utilize AST instance', function() { < it('can utilize AST instance', function() {
< equal( < equal(
< /return "Hello"/.test( < /return "Hello"/.test(
@ -253,13 +249,14 @@
< }); < });
--- ---
> it('should not modify the options.data property(GH-1327)', () => { > it('should not modify the options.data property(GH-1327)', () => {
> const options = { data: [{ a: 'foo' }, { a: 'bar' }] }; > // The `data` property is supposed to be a boolean, but in this test we want to ignore that
> const options = { data: [{ a: 'foo' }, { a: 'bar' }] as unknown as boolean };
> compile('{{#each data}}{{@index}}:{{a}} {{/each}}', options)({}); > compile('{{#each data}}{{@index}}:{{a}} {{/each}}', options)({});
> expect(JSON.stringify(options, null, 2)).toEqual( > expect(JSON.stringify(options, null, 2)).toEqual(
> JSON.stringify({ data: [{ a: 'foo' }, { a: 'bar' }] }, null, 2) > JSON.stringify({ data: [{ a: 'foo' }, { a: 'bar' }] }, null, 2)
> ); > );
> }); > });
184,185c80,86 184,185c77,83
< it('can pass through an empty string', function() { < it('can pass through an empty string', function() {
< equal(/return ""/.test(Handlebars.precompile('')), true); < equal(/return ""/.test(Handlebars.precompile('')), true);
--- ---

View file

@ -45,47 +45,50 @@
> for (const prop in options.hash) { > for (const prop in options.hash) {
40d48 40d48
< .withMessage('Automatic data was triggered') < .withMessage('Automatic data was triggered')
44c52 41a50,51
>
> global.kbnHandlebarsEnv = null;
44c54
< it('parameter data can be looked up via @foo', function() { < it('parameter data can be looked up via @foo', function() {
--- ---
> it('parameter data can be looked up via @foo', () => { > it('parameter data can be looked up via @foo', () => {
47c55 47c57
< .withHelper('hello', function(noun) { < .withHelper('hello', function(noun) {
--- ---
> .withHelper('hello', function (noun) { > .withHelper('hello', function (noun) {
50d57 50d59
< .withMessage('@foo as a parameter retrieves template data') < .withMessage('@foo as a parameter retrieves template data')
54c61 54c63
< it('hash values can be looked up via @foo', function() { < it('hash values can be looked up via @foo', function() {
--- ---
> it('hash values can be looked up via @foo', () => { > it('hash values can be looked up via @foo', () => {
57c64 57c66
< .withHelper('hello', function(options) { < .withHelper('hello', function(options) {
--- ---
> .withHelper('hello', function (options) { > .withHelper('hello', function (options) {
60d66 60d68
< .withMessage('@foo as a parameter retrieves template data') < .withMessage('@foo as a parameter retrieves template data')
64c70 64c72
< it('nested parameter data can be looked up via @foo.bar', function() { < it('nested parameter data can be looked up via @foo.bar', function() {
--- ---
> it('nested parameter data can be looked up via @foo.bar', () => { > it('nested parameter data can be looked up via @foo.bar', () => {
67c73 67c75
< .withHelper('hello', function(noun) { < .withHelper('hello', function(noun) {
--- ---
> .withHelper('hello', function (noun) { > .withHelper('hello', function (noun) {
70d75 70d77
< .withMessage('@foo as a parameter retrieves template data') < .withMessage('@foo as a parameter retrieves template data')
74c79 74c81
< it('nested parameter data does not fail with @world.bar', function() { < it('nested parameter data does not fail with @world.bar', function() {
--- ---
> it('nested parameter data does not fail with @world.bar', () => { > it('nested parameter data does not fail with @world.bar', () => {
77c82 77c84
< .withHelper('hello', function(noun) { < .withHelper('hello', function(noun) {
--- ---
> .withHelper('hello', function (noun) { > .withHelper('hello', function (noun) {
80d84 80d86
< .withMessage('@foo as a parameter retrieves template data') < .withMessage('@foo as a parameter retrieves template data')
84,87c88,89 84,87c90,91
< it('parameter data throws when using complex scope references', function() { < it('parameter data throws when using complex scope references', function() {
< expectTemplate( < expectTemplate(
< '{{#goodbyes}}{{text}} cruel {{@foo/../name}}! {{/goodbyes}}' < '{{#goodbyes}}{{text}} cruel {{@foo/../name}}! {{/goodbyes}}'
@ -93,39 +96,39 @@
--- ---
> it('parameter data throws when using complex scope references', () => { > it('parameter data throws when using complex scope references', () => {
> expectTemplate('{{#goodbyes}}{{text}} cruel {{@foo/../name}}! {{/goodbyes}}').toThrow(Error); > expectTemplate('{{#goodbyes}}{{text}} cruel {{@foo/../name}}! {{/goodbyes}}').toThrow(Error);
90c92 90c94
< it('data can be functions', function() { < it('data can be functions', function() {
--- ---
> it('data can be functions', () => { > it('data can be functions', () => {
94c96 94c98
< hello: function() { < hello: function() {
--- ---
> hello() { > hello() {
96,97c98,99 96,97c100,101
< } < }
< } < }
--- ---
> }, > },
> }, > },
102c104 102c106
< it('data can be functions with params', function() { < it('data can be functions with params', function() {
--- ---
> it('data can be functions with params', () => { > it('data can be functions with params', () => {
106c108 106c110
< hello: function(arg) { < hello: function(arg) {
--- ---
> hello(arg: any) { > hello(arg: any) {
108,109c110,111 108,109c112,113
< } < }
< } < }
--- ---
> }, > },
> }, > },
114c116 114c118
< it('data is inherited downstream', function() { < it('data is inherited downstream', function() {
--- ---
> it('data is inherited downstream', () => { > it('data is inherited downstream', () => {
120,122c122,124 120,122c124,126
< .withHelper('let', function(options) { < .withHelper('let', function(options) {
< var frame = Handlebars.createFrame(options.data); < var frame = Handlebars.createFrame(options.data);
< for (var prop in options.hash) { < for (var prop in options.hash) {
@ -133,9 +136,9 @@
> .withHelper('let', function (this: any, options) { > .withHelper('let', function (this: any, options) {
> const frame = Handlebars.createFrame(options.data); > const frame = Handlebars.createFrame(options.data);
> for (const prop in options.hash) { > for (const prop in options.hash) {
130d131 130d133
< .withMessage('data variables are inherited downstream') < .withMessage('data variables are inherited downstream')
134,147c135 134,147c137
< it('passing in data to a compiled function that expects data - works with helpers in partials', function() { < it('passing in data to a compiled function that expects data - works with helpers in partials', function() {
< expectTemplate('{{>myPartial}}') < expectTemplate('{{>myPartial}}')
< .withCompileOptions({ data: true }) < .withCompileOptions({ data: true })
@ -152,63 +155,63 @@
< it('passing in data to a compiled function that expects data - works with helpers and parameters', function() { < it('passing in data to a compiled function that expects data - works with helpers and parameters', function() {
--- ---
> it('passing in data to a compiled function that expects data - works with helpers and parameters', () => { > it('passing in data to a compiled function that expects data - works with helpers and parameters', () => {
150c138 150c140
< .withHelper('hello', function(noun, options) { < .withHelper('hello', function(noun, options) {
--- ---
> .withHelper('hello', function (this: any, noun, options) { > .withHelper('hello', function (this: any, noun, options) {
155d142 155d144
< .withMessage('Data output by helper') < .withMessage('Data output by helper')
159c146 159c148
< it('passing in data to a compiled function that expects data - works with block helpers', function() { < it('passing in data to a compiled function that expects data - works with block helpers', function() {
--- ---
> it('passing in data to a compiled function that expects data - works with block helpers', () => { > it('passing in data to a compiled function that expects data - works with block helpers', () => {
162c149 162c151
< data: true < data: true
--- ---
> data: true, > data: true,
164c151 164c153
< .withHelper('hello', function(options) { < .withHelper('hello', function(options) {
--- ---
> .withHelper('hello', function (this: any, options) { > .withHelper('hello', function (this: any, options) {
167c154 167c156
< .withHelper('world', function(options) { < .withHelper('world', function(options) {
--- ---
> .withHelper('world', function (this: any, options) { > .withHelper('world', function (this: any, options) {
172d158 172d160
< .withMessage('Data output by helper') < .withMessage('Data output by helper')
176c162 176c164
< it('passing in data to a compiled function that expects data - works with block helpers that use ..', function() { < it('passing in data to a compiled function that expects data - works with block helpers that use ..', function() {
--- ---
> it('passing in data to a compiled function that expects data - works with block helpers that use ..', () => { > it('passing in data to a compiled function that expects data - works with block helpers that use ..', () => {
179c165 179c167
< .withHelper('hello', function(options) { < .withHelper('hello', function(options) {
--- ---
> .withHelper('hello', function (options) { > .withHelper('hello', function (options) {
182c168 182c170
< .withHelper('world', function(thing, options) { < .withHelper('world', function(thing, options) {
--- ---
> .withHelper('world', function (this: any, thing, options) { > .withHelper('world', function (this: any, thing, options) {
187d172 187d174
< .withMessage('Data output by helper') < .withMessage('Data output by helper')
191c176 191c178
< it('passing in data to a compiled function that expects data - data is passed to with block helpers where children use ..', function() { < it('passing in data to a compiled function that expects data - data is passed to with block helpers where children use ..', function() {
--- ---
> it('passing in data to a compiled function that expects data - data is passed to with block helpers where children use ..', () => { > it('passing in data to a compiled function that expects data - data is passed to with block helpers where children use ..', () => {
194c179 194c181
< .withHelper('hello', function(options) { < .withHelper('hello', function(options) {
--- ---
> .withHelper('hello', function (options) { > .withHelper('hello', function (options) {
197c182 197c184
< .withHelper('world', function(thing, options) { < .withHelper('world', function(thing, options) {
--- ---
> .withHelper('world', function (this: any, thing, options) { > .withHelper('world', function (this: any, thing, options) {
202d186 202d188
< .withMessage('Data output by helper') < .withMessage('Data output by helper')
206c190 206c192
< it('you can override inherited data when invoking a helper', function() { < it('you can override inherited data when invoking a helper', function() {
--- ---
> it('you can override inherited data when invoking a helper', () => { > it('you can override inherited data when invoking a helper', () => {
209,213c193,194 209,213c195,196
< .withHelper('hello', function(options) { < .withHelper('hello', function(options) {
< return options.fn( < return options.fn(
< { exclaim: '?', zomg: 'world' }, < { exclaim: '?', zomg: 'world' },
@ -217,55 +220,55 @@
--- ---
> .withHelper('hello', function (options) { > .withHelper('hello', function (options) {
> return options.fn({ exclaim: '?', zomg: 'world' }, { data: { adjective: 'sad' } }); > return options.fn({ exclaim: '?', zomg: 'world' }, { data: { adjective: 'sad' } });
215c196 215c198
< .withHelper('world', function(thing, options) { < .withHelper('world', function(thing, options) {
--- ---
> .withHelper('world', function (this: any, thing, options) { > .withHelper('world', function (this: any, thing, options) {
220d200 220d202
< .withMessage('Overriden data output by helper') < .withMessage('Overriden data output by helper')
224c204 224c206
< it('you can override inherited data when invoking a helper with depth', function() { < it('you can override inherited data when invoking a helper with depth', function() {
--- ---
> it('you can override inherited data when invoking a helper with depth', () => { > it('you can override inherited data when invoking a helper with depth', () => {
227c207 227c209
< .withHelper('hello', function(options) { < .withHelper('hello', function(options) {
--- ---
> .withHelper('hello', function (options) { > .withHelper('hello', function (options) {
230c210 230c212
< .withHelper('world', function(thing, options) { < .withHelper('world', function(thing, options) {
--- ---
> .withHelper('world', function (this: any, thing, options) { > .withHelper('world', function (this: any, thing, options) {
235d214 235d216
< .withMessage('Overriden data output by helper') < .withMessage('Overriden data output by helper')
239,240c218,219 239,240c220,221
< describe('@root', function() { < describe('@root', function() {
< it('the root context can be looked up via @root', function() { < it('the root context can be looked up via @root', function() {
--- ---
> describe('@root', () => { > describe('@root', () => {
> it('the root context can be looked up via @root', () => { > it('the root context can be looked up via @root', () => {
246,248c225 246,248c227
< expectTemplate('{{@root.foo}}') < expectTemplate('{{@root.foo}}')
< .withInput({ foo: 'hello' }) < .withInput({ foo: 'hello' })
< .toCompileTo('hello'); < .toCompileTo('hello');
--- ---
> expectTemplate('{{@root.foo}}').withInput({ foo: 'hello' }).toCompileTo('hello'); > expectTemplate('{{@root.foo}}').withInput({ foo: 'hello' }).toCompileTo('hello');
251c228 251c230
< it('passed root values take priority', function() { < it('passed root values take priority', function() {
--- ---
> it('passed root values take priority', () => { > it('passed root values take priority', () => {
259,260c236,237 259,260c238,239
< describe('nesting', function() { < describe('nesting', function() {
< it('the root context can be looked up via @root', function() { < it('the root context can be looked up via @root', function() {
--- ---
> describe('nesting', () => { > describe('nesting', () => {
> it('the root context can be looked up via @root', () => { > it('the root context can be looked up via @root', () => {
265,266c242,243 265,266c244,245
< .withHelper('helper', function(options) { < .withHelper('helper', function(options) {
< var frame = Handlebars.createFrame(options.data); < var frame = Handlebars.createFrame(options.data);
--- ---
> .withHelper('helper', function (this: any, options) { > .withHelper('helper', function (this: any, options) {
> const frame = Handlebars.createFrame(options.data); > const frame = Handlebars.createFrame(options.data);
272,273c249,250 272,273c251,252
< depth: 0 < depth: 0
< } < }
--- ---

File diff suppressed because it is too large Load diff

View file

@ -5,17 +5,18 @@ A custom version of the handlebars package which, to improve security, does not
## Limitations ## Limitations
- Only the following compile options are supported: - Only the following compile options are supported:
- `data`
- `knownHelpers` - `knownHelpers`
- `knownHelpersOnly` - `knownHelpersOnly`
- `noEscape`
- `strict` - `strict`
- `assumeObjects` - `assumeObjects`
- `noEscape`
- `data`
- Only the following runtime options are supported: - Only the following runtime options are supported:
- `helpers`
- `blockParams`
- `data` - `data`
- `helpers`
- `decorators` (not documented in the official Handlebars [runtime options documentation](https://handlebarsjs.com/api-reference/runtime-options.html))
- `blockParams` (not documented in the official Handlebars [runtime options documentation](https://handlebarsjs.com/api-reference/runtime-options.html))
The [Inline partials](https://handlebarsjs.com/guide/partials.html#inline-partials) handlebars template feature is currently not supported by `@kbn/handlebars`. The [Inline partials](https://handlebarsjs.com/guide/partials.html#inline-partials) handlebars template feature is currently not supported by `@kbn/handlebars`.

View file

@ -1,19 +1,5 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP // Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Handlebars.compileAST invalid template 1`] = `
"Parse error on line 1:
{{value
--^
Expecting 'ID', 'STRING', 'NUMBER', 'BOOLEAN', 'UNDEFINED', 'NULL', 'DATA', got 'INVALID'"
`;
exports[`Handlebars.compileAST invalid template 2`] = `
"Parse error on line 1:
{{value
--^
Expecting 'ID', 'STRING', 'NUMBER', 'BOOLEAN', 'UNDEFINED', 'NULL', 'DATA', got 'INVALID'"
`;
exports[`Handlebars.create 1`] = ` exports[`Handlebars.create 1`] = `
HandlebarsEnvironment { HandlebarsEnvironment {
"AST": Object { "AST": Object {
@ -103,7 +89,3 @@ HandlebarsEnvironment {
"template": [Function], "template": [Function],
} }
`; `;
exports[`blocks decorators registration should not be able to call decorators unregistered using the \`unregisterDecorator\` function 1`] = `"lookupProperty(...) is not a function"`;
exports[`blocks decorators registration should not be able to call decorators unregistered using the \`unregisterDecorator\` function 2`] = `"decoratorFn is not a function"`;

View file

@ -11,7 +11,7 @@
*/ */
import Handlebars from '.'; import Handlebars from '.';
import { expectTemplate } from './src/__jest__/test_bench'; import { expectTemplate, forEachCompileFunctionName } from './src/__jest__/test_bench';
it('Handlebars.create', () => { it('Handlebars.create', () => {
expect(Handlebars.create()).toMatchSnapshot(); expect(Handlebars.create()).toMatchSnapshot();
@ -35,7 +35,10 @@ describe('Handlebars.compileAST', () => {
}); });
it('invalid template', () => { it('invalid template', () => {
expectTemplate('{{value').withInput({ value: 42 }).toThrowErrorMatchingSnapshot(); expectTemplate('{{value').withInput({ value: 42 }).toThrow(`Parse error on line 1:
{{value
--^
Expecting 'ID', 'STRING', 'NUMBER', 'BOOLEAN', 'UNDEFINED', 'NULL', 'DATA', got 'INVALID'`);
}); });
if (!process.env.EVAL) { if (!process.env.EVAL) {
@ -108,33 +111,27 @@ describe('blocks', () => {
expect(calls).toEqual(callsExpected); expect(calls).toEqual(callsExpected);
}); });
it('should call decorator again if render function is called again', () => { forEachCompileFunctionName((compileName) => {
const callsExpected = process.env.AST || process.env.EVAL ? 1 : 2; it(`should call decorator again if render function is called again for #${compileName}`, () => {
global.kbnHandlebarsEnv = Handlebars.create(); global.kbnHandlebarsEnv = Handlebars.create();
kbnHandlebarsEnv!.registerDecorator('decorator', () => { kbnHandlebarsEnv!.registerDecorator('decorator', () => {
calls++; calls++;
}); });
let renderAST; const compile = kbnHandlebarsEnv![compileName].bind(kbnHandlebarsEnv);
let renderEval; const render = compile('{{*decorator}}');
if (process.env.AST || !process.env.EVAL) {
renderAST = kbnHandlebarsEnv!.compileAST('{{*decorator}}');
}
if (process.env.EVAL || !process.env.AST) {
renderEval = kbnHandlebarsEnv!.compile('{{*decorator}}');
}
let calls = 0; let calls = 0;
if (renderAST) expect(renderAST({})).toEqual(''); expect(render({})).toEqual('');
if (renderEval) expect(renderEval({})).toEqual(''); expect(calls).toEqual(1);
expect(calls).toEqual(callsExpected);
calls = 0; calls = 0;
if (renderAST) expect(renderAST({})).toEqual(''); expect(render({})).toEqual('');
if (renderEval) expect(renderEval({})).toEqual(''); expect(calls).toEqual(1);
expect(calls).toEqual(callsExpected);
global.kbnHandlebarsEnv = null;
});
}); });
it('should pass expected options to nested decorator', () => { it('should pass expected options to nested decorator', () => {
@ -207,6 +204,10 @@ describe('blocks', () => {
global.kbnHandlebarsEnv = Handlebars.create(); global.kbnHandlebarsEnv = Handlebars.create();
}); });
afterEach(() => {
global.kbnHandlebarsEnv = null;
});
it('should be able to call decorators registered using the `registerDecorator` function', () => { it('should be able to call decorators registered using the `registerDecorator` function', () => {
let calls = 0; let calls = 0;
const callsExpected = process.env.AST || process.env.EVAL ? 1 : 2; const callsExpected = process.env.AST || process.env.EVAL ? 1 : 2;
@ -228,7 +229,7 @@ describe('blocks', () => {
kbnHandlebarsEnv!.unregisterDecorator('decorator'); kbnHandlebarsEnv!.unregisterDecorator('decorator');
expectTemplate('{{*decorator}}').toThrowErrorMatchingSnapshot(); expectTemplate('{{*decorator}}').toThrow('lookupProperty(...) is not a function');
expect(calls).toEqual(0); expect(calls).toEqual(0);
}); });
}); });

View file

@ -50,7 +50,7 @@ export const compileFnName: 'compile' | 'compileAST' = allowUnsafeEval() ? 'comp
*/ */
export type ExtendedCompileOptions = Pick< export type ExtendedCompileOptions = Pick<
CompileOptions, CompileOptions,
'knownHelpers' | 'knownHelpersOnly' | 'strict' | 'assumeObjects' | 'noEscape' | 'data' 'data' | 'knownHelpers' | 'knownHelpersOnly' | 'noEscape' | 'strict' | 'assumeObjects'
>; >;
/** /**
@ -61,7 +61,7 @@ export type ExtendedCompileOptions = Pick<
*/ */
export type ExtendedRuntimeOptions = Pick< export type ExtendedRuntimeOptions = Pick<
RuntimeOptions, RuntimeOptions,
'helpers' | 'blockParams' | 'data' | 'decorators' 'data' | 'helpers' | 'decorators' | 'blockParams'
>; >;
/** /**
@ -77,14 +77,14 @@ export type DecoratorFunction = (
options: any options: any
) => any; ) => any;
export interface DecoratorsHash {
[name: string]: DecoratorFunction;
}
export interface HelpersHash { export interface HelpersHash {
[name: string]: Handlebars.HelperDelegate; [name: string]: Handlebars.HelperDelegate;
} }
export interface DecoratorsHash {
[name: string]: DecoratorFunction;
}
/** /**
* Normally this namespace isn't used directly. It's required to be present by * Normally this namespace isn't used directly. It's required to be present by
* TypeScript when calling the `Handlebars.create()` function. * TypeScript when calling the `Handlebars.create()` function.
@ -298,6 +298,7 @@ class ElasticHandlebarsVisitor extends Handlebars.Visitor {
Program(program: hbs.AST.Program) { Program(program: hbs.AST.Program) {
this.blockParamNames.unshift(program.blockParams); this.blockParamNames.unshift(program.blockParams);
// Run any decorators that might exist on the root
this.processDecorators(program, this.generateProgramFunction(program)); this.processDecorators(program, this.generateProgramFunction(program));
this.processedRootDecorators = true; this.processedRootDecorators = true;
@ -314,13 +315,13 @@ class ElasticHandlebarsVisitor extends Handlebars.Visitor {
} }
// This space intentionally left blank: We want to override the Visitor class implementation // This space intentionally left blank: We want to override the Visitor class implementation
// of `DecoratorBlock`, but since we handle decorators separately before traversing the // of this method, but since we handle decorators separately before traversing the nodes, we
// nodes, we just want to make this a no-op. // just want to make this a no-op.
DecoratorBlock(decorator: hbs.AST.DecoratorBlock) {} DecoratorBlock(decorator: hbs.AST.DecoratorBlock) {}
// This space intentionally left blank: We want to override the Visitor class implementation // This space intentionally left blank: We want to override the Visitor class implementation
// of `DecoratorBlock`, but since we handle decorators separately before traversing the // of this method, but since we handle decorators separately before traversing the nodes, we
// nodes, we just want to make this a no-op. // just want to make this a no-op.
Decorator(decorator: hbs.AST.Decorator) {} Decorator(decorator: hbs.AST.Decorator) {}
SubExpression(sexpr: hbs.AST.SubExpression) { SubExpression(sexpr: hbs.AST.SubExpression) {
@ -393,20 +394,23 @@ class ElasticHandlebarsVisitor extends Handlebars.Visitor {
) { ) {
// TypeScript: The types indicate that `decorator.path` technically can be an `hbs.AST.Literal`. However, the upstream codebase always treats it as an `hbs.AST.PathExpression`, so we do too. // TypeScript: The types indicate that `decorator.path` technically can be an `hbs.AST.Literal`. However, the upstream codebase always treats it as an `hbs.AST.PathExpression`, so we do too.
const name = (decorator.path as hbs.AST.PathExpression).original; const name = (decorator.path as hbs.AST.PathExpression).original;
const decoratorFn = this.container.lookupProperty<DecoratorFunction>(
this.container.decorators,
name
);
const props = {}; const props = {};
// TypeScript: Because `decorator` can be of type `hbs.AST.Decorator`, TS indicates that `decorator.path` technically can be an `hbs.AST.Literal`. However, the upstream codebase always treats it as an `hbs.AST.PathExpression`, so we do too. // TypeScript: Because `decorator` can be of type `hbs.AST.Decorator`, TS indicates that `decorator.path` technically can be an `hbs.AST.Literal`. However, the upstream codebase always treats it as an `hbs.AST.PathExpression`, so we do too.
const options = this.setupParams(decorator as hbs.AST.DecoratorBlock, name); const options = this.setupParams(decorator as hbs.AST.DecoratorBlock, name);
// @ts-expect-error: Property 'lookupProperty' does not exist on type 'HelperOptions' // @ts-expect-error: Property 'lookupProperty' does not exist on type 'HelperOptions'
delete options.lookupProperty; // There's really no tests/documentation on this, but to match the upstream codebase we'll remove `lookupProperty` from the decorator context delete options.lookupProperty; // There's really no tests/documentation on this, but to match the upstream codebase we'll remove `lookupProperty` from the decorator context
Object.assign(decoratorFn(prog, props, this.container, options) || prog, props); const result = this.container.lookupProperty<DecoratorFunction>(
this.container.decorators,
name
)(prog, props, this.container, options);
Object.assign(result || prog, props);
} }
private processStatementOrExpression(node: ProcessableNode) { private processStatementOrExpression(node: ProcessableNode) {
// Calling `transformLiteralToPath` has side-effects!
// It converts a node from type `ProcessableNode` to `ProcessableNodeWithPathParts`
transformLiteralToPath(node); transformLiteralToPath(node);
switch (this.classifyNode(node as ProcessableNodeWithPathParts)) { switch (this.classifyNode(node as ProcessableNodeWithPathParts)) {
@ -533,6 +537,7 @@ class ElasticHandlebarsVisitor extends Handlebars.Visitor {
private invokeKnownHelper(node: ProcessableNodeWithPathParts) { private invokeKnownHelper(node: ProcessableNodeWithPathParts) {
const name = node.path.parts[0]; const name = node.path.parts[0];
const helper = this.setupHelper(node, name); const helper = this.setupHelper(node, name);
// TypeScript: `helper.fn` might be `undefined` at this point, but to match the upstream behavior we call it without any guards
const result = helper.fn.apply(helper.context, helper.params); const result = helper.fn.apply(helper.context, helper.params);
this.output.push(result); this.output.push(result);
} }
@ -558,6 +563,7 @@ class ElasticHandlebarsVisitor extends Handlebars.Visitor {
} }
} }
// TypeScript: `helper.fn` might be `undefined` at this point, but to match the upstream behavior we call it without any guards
const result = helper.fn.apply(helper.context, helper.params); const result = helper.fn.apply(helper.context, helper.params);
this.output.push(result); this.output.push(result);
@ -573,8 +579,7 @@ class ElasticHandlebarsVisitor extends Handlebars.Visitor {
} }
} else { } else {
if ( if (
// @ts-expect-error: The `escaped` property is only on MustacheStatement nodes (node as hbs.AST.MustacheStatement).escaped === false ||
node.escaped === false ||
this.compileOptions.noEscape === true || this.compileOptions.noEscape === true ||
typeof invokeResult !== 'string' typeof invokeResult !== 'string'
) { ) {
@ -679,8 +684,9 @@ class ElasticHandlebarsVisitor extends Handlebars.Visitor {
nextContext: any, nextContext: any,
runtimeOptions: ExtendedRuntimeOptions = {} runtimeOptions: ExtendedRuntimeOptions = {}
) => { ) => {
// inherit data in blockParams from parent program
runtimeOptions = Object.assign({}, runtimeOptions); runtimeOptions = Object.assign({}, runtimeOptions);
// inherit data in blockParams from parent program
runtimeOptions.data = runtimeOptions.data || this.runtimeOptions!.data; runtimeOptions.data = runtimeOptions.data || this.runtimeOptions!.data;
if (runtimeOptions.blockParams) { if (runtimeOptions.blockParams) {
runtimeOptions.blockParams = runtimeOptions.blockParams.concat( runtimeOptions.blockParams = runtimeOptions.blockParams.concat(

View file

@ -10,6 +10,11 @@ import Handlebars, {
type ExtendedRuntimeOptions, type ExtendedRuntimeOptions,
} from '../..'; } from '../..';
type CompileFns = 'compile' | 'compileAST';
const compileFns: CompileFns[] = ['compile', 'compileAST'];
if (process.env.AST) compileFns.splice(0, 1);
else if (process.env.EVAL) compileFns.splice(1, 1);
declare global { declare global {
var kbnHandlebarsEnv: typeof Handlebars | null; // eslint-disable-line no-var var kbnHandlebarsEnv: typeof Handlebars | null; // eslint-disable-line no-var
} }
@ -24,12 +29,18 @@ export function expectTemplate(template: string, options?: TestOptions) {
return new HandlebarsTestBench(template, options); return new HandlebarsTestBench(template, options);
} }
export function forEachCompileFunctionName(
cb: (compileName: CompileFns, index: number, array: CompileFns[]) => void
) {
compileFns.forEach(cb);
}
class HandlebarsTestBench { class HandlebarsTestBench {
private template: string; private template: string;
private options: TestOptions; private options: TestOptions;
private compileOptions?: ExtendedCompileOptions; private compileOptions?: ExtendedCompileOptions;
private runtimeOptions?: ExtendedRuntimeOptions; private runtimeOptions?: ExtendedRuntimeOptions;
private helpers: { [key: string]: Handlebars.HelperDelegate | undefined } = {}; private helpers: { [name: string]: Handlebars.HelperDelegate | undefined } = {};
private decorators: DecoratorsHash = {}; private decorators: DecoratorsHash = {};
private input: any = {}; private input: any = {};
@ -58,7 +69,7 @@ class HandlebarsTestBench {
return this; return this;
} }
withHelpers(helperFunctions: { [key: string]: Handlebars.HelperDelegate }) { withHelpers(helperFunctions: { [name: string]: Handlebars.HelperDelegate }) {
for (const [name, helper] of Object.entries(helperFunctions)) { for (const [name, helper] of Object.entries(helperFunctions)) {
this.withHelper(name, helper); this.withHelper(name, helper);
} }
@ -108,12 +119,6 @@ class HandlebarsTestBench {
} }
} }
toThrowErrorMatchingSnapshot() {
const { renderEval, renderAST } = this.compile();
expect(() => renderEval(this.input)).toThrowErrorMatchingSnapshot();
expect(() => renderAST(this.input)).toThrowErrorMatchingSnapshot();
}
private compileAndExecute() { private compileAndExecute() {
if (process.env.EVAL) { if (process.env.EVAL) {
return { return {
@ -159,15 +164,6 @@ class HandlebarsTestBench {
return renderAST(this.input, runtimeOptions); return renderAST(this.input, runtimeOptions);
} }
private compile() {
const handlebarsEnv = getHandlebarsEnv();
return {
renderEval: this.compileEval(handlebarsEnv),
renderAST: this.compileAST(handlebarsEnv),
};
}
private compileEval(handlebarsEnv = getHandlebarsEnv()) { private compileEval(handlebarsEnv = getHandlebarsEnv()) {
this.execBeforeEach(); this.execBeforeEach();
return handlebarsEnv.compile(this.template, this.compileOptions); return handlebarsEnv.compile(this.template, this.compileOptions);

View file

@ -316,6 +316,10 @@ describe('blocks', () => {
global.kbnHandlebarsEnv = Handlebars.create(); global.kbnHandlebarsEnv = Handlebars.create();
}); });
afterEach(() => {
global.kbnHandlebarsEnv = null;
});
it('unregisters', () => { it('unregisters', () => {
// @ts-expect-error: Cannot assign to 'decorators' because it is a read-only property. // @ts-expect-error: Cannot assign to 'decorators' because it is a read-only property.
kbnHandlebarsEnv!.decorators = {}; kbnHandlebarsEnv!.decorators = {};

View file

@ -476,6 +476,8 @@ describe('builtin helpers', () => {
console.log = $log; console.log = $log;
console.info = $info; console.info = $info;
console.error = $error; console.error = $error;
global.kbnHandlebarsEnv = null;
}); });
it('should call logger at default level', function () { it('should call logger at default level', function () {

View file

@ -6,15 +6,11 @@
*/ */
import Handlebars from '../..'; import Handlebars from '../..';
import { forEachCompileFunctionName } from '../__jest__/test_bench';
describe('compiler', () => { describe('compiler', () => {
const compileFns = ['compile', 'compileAST']; forEachCompileFunctionName((compileName) => {
if (process.env.AST) compileFns.splice(0, 1); const compile = Handlebars[compileName].bind(Handlebars);
else if (process.env.EVAL) compileFns.splice(1, 1);
compileFns.forEach((compileName) => {
// @ts-expect-error
const compile = Handlebars[compileName];
describe(`#${compileName}`, () => { describe(`#${compileName}`, () => {
it('should fail with invalid input', () => { it('should fail with invalid input', () => {
@ -70,7 +66,8 @@ describe('compiler', () => {
}); });
it('should not modify the options.data property(GH-1327)', () => { it('should not modify the options.data property(GH-1327)', () => {
const options = { data: [{ a: 'foo' }, { a: 'bar' }] }; // The `data` property is supposed to be a boolean, but in this test we want to ignore that
const options = { data: [{ a: 'foo' }, { a: 'bar' }] as unknown as boolean };
compile('{{#each data}}{{@index}}:{{a}} {{/each}}', options)({}); compile('{{#each data}}{{@index}}:{{a}} {{/each}}', options)({});
expect(JSON.stringify(options, null, 2)).toEqual( expect(JSON.stringify(options, null, 2)).toEqual(
JSON.stringify({ data: [{ a: 'foo' }, { a: 'bar' }] }, null, 2) JSON.stringify({ data: [{ a: 'foo' }, { a: 'bar' }] }, null, 2)

View file

@ -47,6 +47,8 @@ describe('data', () => {
.withInput({ foo: true }) .withInput({ foo: true })
.withHelpers(helpers) .withHelpers(helpers)
.toCompileTo('Hello world'); .toCompileTo('Hello world');
global.kbnHandlebarsEnv = null;
}); });
it('parameter data can be looked up via @foo', () => { it('parameter data can be looked up via @foo', () => {

View file

@ -12,6 +12,10 @@ beforeEach(() => {
global.kbnHandlebarsEnv = Handlebars.create(); global.kbnHandlebarsEnv = Handlebars.create();
}); });
afterEach(() => {
global.kbnHandlebarsEnv = null;
});
describe('helpers', () => { describe('helpers', () => {
it('helper with complex lookup$', () => { it('helper with complex lookup$', () => {
expectTemplate('{{#goodbyes}}{{{link ../prefix}}}{{/goodbyes}}') expectTemplate('{{#goodbyes}}{{{link ../prefix}}}{{/goodbyes}}')