mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
[@kbn/handlebars] Improve types (#147800)
This commit is contained in:
parent
b697704639
commit
41fd68b719
11 changed files with 406 additions and 274 deletions
|
@ -304,123 +304,141 @@
|
|||
---
|
||||
> describe('decorators', () => {
|
||||
> it('should apply mustache decorators', () => {
|
||||
299c203
|
||||
299,300c203,204
|
||||
< .withHelper('helper', function(options) {
|
||||
< return options.fn.run;
|
||||
---
|
||||
> .withHelper('helper', function (options) {
|
||||
302c206,207
|
||||
> .withHelper('helper', function (options: Handlebars.HelperOptions) {
|
||||
> return (options.fn as any).run;
|
||||
302,303c206,207
|
||||
< .withDecorator('decorator', function(fn) {
|
||||
< fn.run = 'success';
|
||||
---
|
||||
> .withDecorator('decorator', function (fn) {
|
||||
> // @ts-expect-error: Property 'run' does not exist on type 'TemplateDelegate<any>'
|
||||
309c214
|
||||
> (fn as any).run = 'success';
|
||||
309c213
|
||||
< it('should apply allow undefined return', function() {
|
||||
---
|
||||
> it('should apply allow undefined return', () => {
|
||||
311c216
|
||||
311,312c215,216
|
||||
< .withHelper('helper', function(options) {
|
||||
< return options.fn() + options.fn.run;
|
||||
---
|
||||
> .withHelper('helper', function (options) {
|
||||
314c219,220
|
||||
> .withHelper('helper', function (options: Handlebars.HelperOptions) {
|
||||
> return options.fn() + (options.fn as any).run;
|
||||
314,315c218,219
|
||||
< .withDecorator('decorator', function(fn) {
|
||||
< fn.run = 'cess';
|
||||
---
|
||||
> .withDecorator('decorator', function (fn) {
|
||||
> // @ts-expect-error: Property 'run' does not exist on type 'TemplateDelegate<any>'
|
||||
320,324c226,228
|
||||
> (fn as any).run = 'cess';
|
||||
320,325c224,227
|
||||
< it('should apply block decorators', function() {
|
||||
< expectTemplate(
|
||||
< '{{#helper}}{{#*decorator}}success{{/decorator}}{{/helper}}'
|
||||
< )
|
||||
< .withHelper('helper', function(options) {
|
||||
< return options.fn.run;
|
||||
---
|
||||
> it('should apply block decorators', () => {
|
||||
> expectTemplate('{{#helper}}{{#*decorator}}success{{/decorator}}{{/helper}}')
|
||||
> .withHelper('helper', function (options) {
|
||||
327c231,232
|
||||
> .withHelper('helper', function (options: Handlebars.HelperOptions) {
|
||||
> return (options.fn as any).run;
|
||||
327,328c229,230
|
||||
< .withDecorator('decorator', function(fn, props, container, options) {
|
||||
< fn.run = options.fn();
|
||||
---
|
||||
> .withDecorator('decorator', function (fn, props, container, options) {
|
||||
> // @ts-expect-error: Property 'run' does not exist on type 'TemplateDelegate<any>'
|
||||
334c239
|
||||
> (fn as any).run = options.fn();
|
||||
334c236
|
||||
< it('should support nested decorators', function() {
|
||||
---
|
||||
> it('should support nested decorators', () => {
|
||||
338c243
|
||||
338,339c240,241
|
||||
< .withHelper('helper', function(options) {
|
||||
< return options.fn.run;
|
||||
---
|
||||
> .withHelper('helper', function (options) {
|
||||
342c247,248
|
||||
> .withHelper('helper', function (options: Handlebars.HelperOptions) {
|
||||
> return (options.fn as any).run;
|
||||
342,343c244,245
|
||||
< decorator: function(fn, props, container, options) {
|
||||
< fn.run = options.fn.nested + options.fn();
|
||||
---
|
||||
> decorator(fn, props, container, options) {
|
||||
> // @ts-expect-error: Property 'run' does not exist on type 'TemplateDelegate<any>'
|
||||
346c252
|
||||
> (fn as any).run = options.fn.nested + options.fn();
|
||||
346c248
|
||||
< nested: function(fn, props, container, options) {
|
||||
---
|
||||
> nested(fn, props, container, options) {
|
||||
348c254
|
||||
348c250
|
||||
< }
|
||||
---
|
||||
> },
|
||||
353c259
|
||||
353c255
|
||||
< it('should apply multiple decorators', function() {
|
||||
---
|
||||
> it('should apply multiple decorators', () => {
|
||||
357c263
|
||||
357,358c259,260
|
||||
< .withHelper('helper', function(options) {
|
||||
< return options.fn.run;
|
||||
---
|
||||
> .withHelper('helper', function (options) {
|
||||
360c266,267
|
||||
> .withHelper('helper', function (options: Handlebars.HelperOptions) {
|
||||
> return (options.fn as any).run;
|
||||
360,361c262,263
|
||||
< .withDecorator('decorator', function(fn, props, container, options) {
|
||||
< fn.run = (fn.run || '') + options.fn();
|
||||
---
|
||||
> .withDecorator('decorator', function (fn, props, container, options) {
|
||||
> // @ts-expect-error: Property 'run' does not exist on type 'TemplateDelegate<any>'
|
||||
367c274
|
||||
> (fn as any).run = ((fn as any).run || '') + options.fn();
|
||||
367c269
|
||||
< it('should access parent variables', function() {
|
||||
---
|
||||
> it('should access parent variables', () => {
|
||||
369c276
|
||||
369,370c271,272
|
||||
< .withHelper('helper', function(options) {
|
||||
< return options.fn.run;
|
||||
---
|
||||
> .withHelper('helper', function (options) {
|
||||
372c279,280
|
||||
> .withHelper('helper', function (options: Handlebars.HelperOptions) {
|
||||
> return (options.fn as any).run;
|
||||
372,373c274,275
|
||||
< .withDecorator('decorator', function(fn, props, container, options) {
|
||||
< fn.run = options.args;
|
||||
---
|
||||
> .withDecorator('decorator', function (fn, props, container, options) {
|
||||
> // @ts-expect-error: Property 'run' does not exist on type 'TemplateDelegate<any>'
|
||||
380,381c288,289
|
||||
> (fn as any).run = options.args;
|
||||
380,381c282,283
|
||||
< it('should work with root program', function() {
|
||||
< var run;
|
||||
---
|
||||
> it('should work with root program', () => {
|
||||
> let run;
|
||||
383,384c291,292
|
||||
383,384c285,286
|
||||
< .withDecorator('decorator', function(fn, props, container, options) {
|
||||
< equals(options.args[0], 'success');
|
||||
---
|
||||
> .withDecorator('decorator', function (fn, props, container, options) {
|
||||
> expect(options.args[0]).toEqual('success');
|
||||
390c298
|
||||
390c292
|
||||
< equals(run, true);
|
||||
---
|
||||
> expect(run).toEqual(true);
|
||||
393,394c301,302
|
||||
393,394c295,296
|
||||
< it('should fail when accessing variables from root', function() {
|
||||
< var run;
|
||||
---
|
||||
> it('should fail when accessing variables from root', () => {
|
||||
> let run;
|
||||
396,397c304,305
|
||||
396,397c298,299
|
||||
< .withDecorator('decorator', function(fn, props, container, options) {
|
||||
< equals(options.args[0], undefined);
|
||||
---
|
||||
> .withDecorator('decorator', function (fn, props, container, options) {
|
||||
> expect(options.args[0]).toBeUndefined();
|
||||
403c311
|
||||
403c305
|
||||
< equals(run, true);
|
||||
---
|
||||
> expect(run).toEqual(true);
|
||||
406,408c314,317
|
||||
406,408c308,311
|
||||
< describe('registration', function() {
|
||||
< it('unregisters', function() {
|
||||
< handlebarsEnv.decorators = {};
|
||||
|
@ -429,7 +447,7 @@
|
|||
> beforeEach(() => {
|
||||
> global.kbnHandlebarsEnv = Handlebars.create();
|
||||
> });
|
||||
410c319,327
|
||||
410c313,321
|
||||
< handlebarsEnv.registerDecorator('foo', function() {
|
||||
---
|
||||
> afterEach(() => {
|
||||
|
@ -441,7 +459,7 @@
|
|||
> kbnHandlebarsEnv!.decorators = {};
|
||||
>
|
||||
> kbnHandlebarsEnv!.registerDecorator('foo', function () {
|
||||
414,416c331,333
|
||||
414,416c325,327
|
||||
< equals(!!handlebarsEnv.decorators.foo, true);
|
||||
< handlebarsEnv.unregisterDecorator('foo');
|
||||
< equals(handlebarsEnv.decorators.foo, undefined);
|
||||
|
@ -449,14 +467,14 @@
|
|||
> expect(!!kbnHandlebarsEnv!.decorators.foo).toEqual(true);
|
||||
> kbnHandlebarsEnv!.unregisterDecorator('foo');
|
||||
> expect(kbnHandlebarsEnv!.decorators.foo).toBeUndefined();
|
||||
419,420c336,338
|
||||
419,420c330,332
|
||||
< it('allows multiple globals', function() {
|
||||
< handlebarsEnv.decorators = {};
|
||||
---
|
||||
> it('allows multiple globals', () => {
|
||||
> // @ts-expect-error: Cannot assign to 'decorators' because it is a read-only property.
|
||||
> kbnHandlebarsEnv!.decorators = {};
|
||||
422,424c340,343
|
||||
422,424c334,337
|
||||
< handlebarsEnv.registerDecorator({
|
||||
< foo: function() {},
|
||||
< bar: function() {}
|
||||
|
@ -465,7 +483,7 @@
|
|||
> kbnHandlebarsEnv!.registerDecorator({
|
||||
> foo() {},
|
||||
> bar() {},
|
||||
427,432c346,351
|
||||
427,432c340,345
|
||||
< equals(!!handlebarsEnv.decorators.foo, true);
|
||||
< equals(!!handlebarsEnv.decorators.bar, true);
|
||||
< handlebarsEnv.unregisterDecorator('foo');
|
||||
|
@ -479,7 +497,7 @@
|
|||
> kbnHandlebarsEnv!.unregisterDecorator('bar');
|
||||
> expect(kbnHandlebarsEnv!.decorators.foo).toBeUndefined();
|
||||
> expect(kbnHandlebarsEnv!.decorators.bar).toBeUndefined();
|
||||
435,445c354,360
|
||||
435,445c348,354
|
||||
< it('fails with multiple and args', function() {
|
||||
< shouldThrow(
|
||||
< function() {
|
||||
|
@ -499,7 +517,7 @@
|
|||
> {
|
||||
> world() {
|
||||
> return 'world!';
|
||||
447,452c362,368
|
||||
447,452c356,362
|
||||
< {}
|
||||
< );
|
||||
< },
|
||||
|
|
|
@ -163,7 +163,7 @@
|
|||
---
|
||||
> it('should include the location in the error (row and column)', () => {
|
||||
> try {
|
||||
> compile(' \n {{#if}}\n{{/def}}')({});
|
||||
> compile(' \n {{#if}}\n{{/def}}')();
|
||||
> expect(true).toEqual(false);
|
||||
> } catch (err) {
|
||||
> expect(err.message).toEqual("if doesn't match def - 2:5");
|
||||
|
@ -188,7 +188,7 @@
|
|||
---
|
||||
> it('should include the location as enumerable property', () => {
|
||||
> try {
|
||||
> compile(' \n {{#if}}\n{{/def}}')({});
|
||||
> compile(' \n {{#if}}\n{{/def}}')();
|
||||
> expect(true).toEqual(false);
|
||||
> } catch (err) {
|
||||
> expect(Object.prototype.propertyIsEnumerable.call(err, 'column')).toEqual(true);
|
||||
|
@ -210,7 +210,7 @@
|
|||
> compile({
|
||||
> type: 'Program',
|
||||
> body: [{ type: 'ContentStatement', value: 'Hello' }],
|
||||
> })({})
|
||||
> })()
|
||||
> ).toEqual('Hello');
|
||||
> });
|
||||
154,170c64,66
|
||||
|
@ -233,7 +233,7 @@
|
|||
< });
|
||||
---
|
||||
> it('can pass through an empty string', () => {
|
||||
> expect(compile('')({})).toEqual('');
|
||||
> expect(compile('')()).toEqual('');
|
||||
> });
|
||||
172,182c68,75
|
||||
< it('can utilize AST instance', function() {
|
||||
|
@ -251,7 +251,7 @@
|
|||
> it('should not modify the options.data property(GH-1327)', () => {
|
||||
> // 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(
|
||||
> JSON.stringify({ data: [{ a: 'foo' }, { a: 'bar' }] }, null, 2)
|
||||
> );
|
||||
|
@ -262,7 +262,7 @@
|
|||
---
|
||||
> it('should not modify the options.knownHelpers property(GH-1327)', () => {
|
||||
> const options = { knownHelpers: {} };
|
||||
> compile('{{#each data}}{{@index}}:{{a}} {{/each}}', options)({});
|
||||
> compile('{{#each data}}{{@index}}:{{a}} {{/each}}', options)();
|
||||
> expect(JSON.stringify(options, null, 2)).toEqual(
|
||||
> JSON.stringify({ knownHelpers: {} }, null, 2)
|
||||
> );
|
||||
|
|
|
@ -41,7 +41,7 @@
|
|||
19c35
|
||||
< .withHelper('raw', function(options) {
|
||||
---
|
||||
> .withHelper('raw', function (options) {
|
||||
> .withHelper('raw', function (options: Handlebars.HelperOptions) {
|
||||
22d37
|
||||
< .withMessage('raw block helper gets raw content')
|
||||
26c41
|
||||
|
@ -52,7 +52,7 @@
|
|||
< .withHelper('raw', function(a, b, c, options) {
|
||||
< return options.fn() + a + b + c;
|
||||
---
|
||||
> .withHelper('raw', function (a, b, c, options) {
|
||||
> .withHelper('raw', function (a, b, c, options: Handlebars.HelperOptions) {
|
||||
> const ret = options.fn() + a + b + c;
|
||||
> return ret;
|
||||
32d47
|
||||
|
@ -66,7 +66,7 @@
|
|||
39c54
|
||||
< .withHelper('identity', function(options) {
|
||||
---
|
||||
> .withHelper('identity', function (options) {
|
||||
> .withHelper('identity', function (options: Handlebars.HelperOptions) {
|
||||
45c60
|
||||
< it('helper for nested raw block gets raw content', function() {
|
||||
---
|
||||
|
@ -99,7 +99,7 @@
|
|||
< var byes = ['Goodbye', 'goodbye', 'GOODBYE'];
|
||||
< for (var i = 0, j = byes.length; i < j; i++) {
|
||||
---
|
||||
> .withHelper('goodbyes', function (this: any, options) {
|
||||
> .withHelper('goodbyes', function (this: any, options: Handlebars.HelperOptions) {
|
||||
> let out = '';
|
||||
> const byes = ['Goodbye', 'goodbye', 'GOODBYE'];
|
||||
> for (let i = 0, j = byes.length; i < j; i++) {
|
||||
|
@ -113,7 +113,7 @@
|
|||
< var byes = ['Goodbye', 'goodbye', 'GOODBYE'];
|
||||
< for (var i = 0, j = byes.length; i < j; i++) {
|
||||
---
|
||||
> .withHelper('goodbyes', function (options) {
|
||||
> .withHelper('goodbyes', function (options: Handlebars.HelperOptions) {
|
||||
> let out = '';
|
||||
> const byes = ['Goodbye', 'goodbye', 'GOODBYE'];
|
||||
> for (let i = 0, j = byes.length; i < j; i++) {
|
||||
|
@ -141,7 +141,7 @@
|
|||
< '</a>'
|
||||
< );
|
||||
---
|
||||
> .withHelper('link', function (this: any, prefix, options) {
|
||||
> .withHelper('link', function (this: any, prefix, options: Handlebars.HelperOptions) {
|
||||
> return '<a href="' + prefix + '/' + this.url + '">' + options.fn(this) + '</a>';
|
||||
130,133c135,136
|
||||
< it('helper with complex lookup and nested template in VM+Compiler', function() {
|
||||
|
@ -167,7 +167,7 @@
|
|||
< '</a>'
|
||||
< );
|
||||
---
|
||||
> .withHelper('link', function (this: any, prefix, options) {
|
||||
> .withHelper('link', function (this: any, prefix, options: Handlebars.HelperOptions) {
|
||||
> return '<a href="' + prefix + '/' + this.url + '">' + options.fn(this) + '</a>';
|
||||
152c147
|
||||
< it('helper returning undefined value', function() {
|
||||
|
@ -188,7 +188,7 @@
|
|||
169c164
|
||||
< .withHelper('goodbyes', function(options) {
|
||||
---
|
||||
> .withHelper('goodbyes', function (options) {
|
||||
> .withHelper('goodbyes', function (options: Handlebars.HelperOptions) {
|
||||
172d166
|
||||
< .withMessage('Block helper executed')
|
||||
176c170
|
||||
|
@ -198,7 +198,7 @@
|
|||
179c173
|
||||
< .withHelper('form', function(options) {
|
||||
---
|
||||
> .withHelper('form', function (this: any, options) {
|
||||
> .withHelper('form', function (this: any, options: Handlebars.HelperOptions) {
|
||||
182d175
|
||||
< .withMessage('Block helper executed with current context')
|
||||
186,187c179,180
|
||||
|
@ -230,7 +230,7 @@
|
|||
213c204
|
||||
< .withHelper('form', function(context, options) {
|
||||
---
|
||||
> .withHelper('form', function (context, options) {
|
||||
> .withHelper('form', function (context, options: Handlebars.HelperOptions) {
|
||||
216d206
|
||||
< .withMessage('Context variable resolved')
|
||||
220c210
|
||||
|
@ -240,7 +240,7 @@
|
|||
223c213
|
||||
< .withHelper('form', function(context, options) {
|
||||
---
|
||||
> .withHelper('form', function (context, options) {
|
||||
> .withHelper('form', function (context, options: Handlebars.HelperOptions) {
|
||||
226d215
|
||||
< .withMessage('Complex path variable resolved')
|
||||
230,233c219,220
|
||||
|
@ -258,11 +258,11 @@
|
|||
237c224
|
||||
< .withHelper('link', function(options) {
|
||||
---
|
||||
> .withHelper('link', function (this: any, options) {
|
||||
> .withHelper('link', function (this: any, options: Handlebars.HelperOptions) {
|
||||
240c227
|
||||
< .withHelper('form', function(context, options) {
|
||||
---
|
||||
> .withHelper('form', function (context, options) {
|
||||
> .withHelper('form', function (context, options: Handlebars.HelperOptions) {
|
||||
243d229
|
||||
< .withMessage('Both blocks executed')
|
||||
247,249c233,235
|
||||
|
@ -598,7 +598,7 @@
|
|||
539c480
|
||||
< .withHelper('goodbye', function(cruel, world, options) {
|
||||
---
|
||||
> .withHelper('goodbye', function (cruel, world, options) {
|
||||
> .withHelper('goodbye', function (cruel, world, options: Handlebars.HelperOptions) {
|
||||
542d482
|
||||
< .withMessage('block helpers with multiple params')
|
||||
547,548c487,488
|
||||
|
@ -610,7 +610,7 @@
|
|||
550c490
|
||||
< .withHelper('goodbye', function(options) {
|
||||
---
|
||||
> .withHelper('goodbye', function (options) {
|
||||
> .withHelper('goodbye', function (options: Handlebars.HelperOptions) {
|
||||
561d500
|
||||
< .withMessage('Helper output hash')
|
||||
565,566c504,505
|
||||
|
@ -630,7 +630,7 @@
|
|||
589c526
|
||||
< .withHelper('goodbye', function(options) {
|
||||
---
|
||||
> .withHelper('goodbye', function (this: any, options) {
|
||||
> .withHelper('goodbye', function (this: any, options: Handlebars.HelperOptions) {
|
||||
600d536
|
||||
< .withMessage('Hash parameters output')
|
||||
604c540
|
||||
|
@ -640,7 +640,7 @@
|
|||
606c542
|
||||
< .withHelper('goodbye', function(options) {
|
||||
---
|
||||
> .withHelper('goodbye', function (this: any, options) {
|
||||
> .withHelper('goodbye', function (this: any, options: Handlebars.HelperOptions) {
|
||||
617d552
|
||||
< .withMessage('Hash parameters output')
|
||||
621,622c556,557
|
||||
|
@ -670,7 +670,7 @@
|
|||
654c585
|
||||
< .withHelper('helperMissing', function(mesg, options) {
|
||||
---
|
||||
> .withHelper('helperMissing', function (mesg, options) {
|
||||
> .withHelper('helperMissing', function (mesg, options: Handlebars.HelperOptions) {
|
||||
662c593
|
||||
< it('if a value is not found, custom helperMissing is used', function() {
|
||||
---
|
||||
|
@ -678,7 +678,7 @@
|
|||
665c596
|
||||
< .withHelper('helperMissing', function(options) {
|
||||
---
|
||||
> .withHelper('helperMissing', function (options) {
|
||||
> .withHelper('helperMissing', function (options: Handlebars.HelperOptions) {
|
||||
674,675c605,606
|
||||
< describe('knownHelpers', function() {
|
||||
< it('Known helper should render helper', function() {
|
||||
|
@ -893,7 +893,7 @@
|
|||
869c791
|
||||
< .withHelper('goodbye', function(options) {
|
||||
---
|
||||
> .withHelper('goodbye', function (this: any, options) {
|
||||
> .withHelper('goodbye', function (this: any, options: Handlebars.HelperOptions) {
|
||||
872c794
|
||||
< .withHelper('cruel', function(world) {
|
||||
---
|
||||
|
@ -931,7 +931,7 @@
|
|||
---
|
||||
> it('Scoped names take precedence over block helpers', () => {
|
||||
> expectTemplate('{{#goodbye}} {{cruel world}}{{/goodbye}} {{this.goodbye}}')
|
||||
> .withHelper('goodbye', function (this: any, options) {
|
||||
> .withHelper('goodbye', function (this: any, options: Handlebars.HelperOptions) {
|
||||
906c824
|
||||
< .withHelper('cruel', function(world) {
|
||||
---
|
||||
|
@ -952,7 +952,7 @@
|
|||
< .withHelper('goodbyes', function(options) {
|
||||
< equals(options.fn.blockParams, 1);
|
||||
---
|
||||
> .withHelper('goodbyes', function (options) {
|
||||
> .withHelper('goodbyes', function (options: Handlebars.HelperOptions) {
|
||||
> expect(options.fn.blockParams).toEqual(1);
|
||||
929c846
|
||||
< it('should take presedence over helper values', function() {
|
||||
|
@ -966,7 +966,7 @@
|
|||
< .withHelper('goodbyes', function(options) {
|
||||
< equals(options.fn.blockParams, 1);
|
||||
---
|
||||
> .withHelper('goodbyes', function (options) {
|
||||
> .withHelper('goodbyes', function (options: Handlebars.HelperOptions) {
|
||||
> expect(options.fn.blockParams).toEqual(1);
|
||||
941,944c858,859
|
||||
< it('should not take presedence over pathed values', function() {
|
||||
|
@ -984,7 +984,7 @@
|
|||
< .withHelper('goodbyes', function(options) {
|
||||
< equals(options.fn.blockParams, 1);
|
||||
---
|
||||
> .withHelper('goodbyes', function (this: any, options) {
|
||||
> .withHelper('goodbyes', function (this: any, options: Handlebars.HelperOptions) {
|
||||
> expect(options.fn.blockParams).toEqual(1);
|
||||
956,957c871,872
|
||||
< it('should take presednece over parent block params', function() {
|
||||
|
@ -1004,7 +1004,7 @@
|
|||
962c882
|
||||
< .withHelper('goodbyes', function(options) {
|
||||
---
|
||||
> .withHelper('goodbyes', function (options) {
|
||||
> .withHelper('goodbyes', function (options: Handlebars.HelperOptions) {
|
||||
966,967c886
|
||||
< blockParams:
|
||||
< options.fn.blockParams === 1 ? [value++, value++] : undefined
|
||||
|
@ -1022,7 +1022,7 @@
|
|||
< .withHelper('goodbyes', function(options) {
|
||||
< equals(options.fn.blockParams, 1);
|
||||
---
|
||||
> .withHelper('goodbyes', function (options) {
|
||||
> .withHelper('goodbyes', function (options: Handlebars.HelperOptions) {
|
||||
> expect(options.fn.blockParams).toEqual(1);
|
||||
987,991c904,906
|
||||
< describe('built-in helpers malformed arguments ', function() {
|
||||
|
@ -1088,11 +1088,18 @@
|
|||
---
|
||||
> describe('the lookupProperty-option', () => {
|
||||
> it('should be passed to custom helpers', () => {
|
||||
1040c945
|
||||
1040,1042c945,950
|
||||
< .withHelper('testHelper', function testHelper(options) {
|
||||
< return options.lookupProperty(this, 'testProperty');
|
||||
< })
|
||||
---
|
||||
> .withHelper('testHelper', function testHelper(this: any, options) {
|
||||
1047a953,958
|
||||
> .withHelper(
|
||||
> 'testHelper',
|
||||
> function testHelper(this: any, options: Handlebars.HelperOptions) {
|
||||
> return options.lookupProperty(this, 'testProperty');
|
||||
> }
|
||||
> )
|
||||
1047a956,961
|
||||
>
|
||||
> function deleteAllKeys(obj: { [key: string]: any }) {
|
||||
> for (const key of Object.keys(obj)) {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
1,2c1,11
|
||||
1,2c1,12
|
||||
< describe('Regressions', function() {
|
||||
< it('GH-94: Cannot read property of undefined', function() {
|
||||
---
|
||||
|
@ -9,17 +9,18 @@
|
|||
> * See `packages/kbn-handlebars/LICENSE` for more information.
|
||||
> */
|
||||
>
|
||||
> import Handlebars from '../..';
|
||||
> import { expectTemplate } from '../__jest__/test_bench';
|
||||
>
|
||||
> describe('Regressions', () => {
|
||||
> it('GH-94: Cannot read property of undefined', () => {
|
||||
9,10c18,19
|
||||
9,10c19,20
|
||||
< name: 'Charles Darwin'
|
||||
< }
|
||||
---
|
||||
> name: 'Charles Darwin',
|
||||
> },
|
||||
13,15c22,24
|
||||
13,15c23,25
|
||||
< title: 'Lazarillo de Tormes'
|
||||
< }
|
||||
< ]
|
||||
|
@ -27,9 +28,9 @@
|
|||
> title: 'Lazarillo de Tormes',
|
||||
> },
|
||||
> ],
|
||||
17d25
|
||||
17d26
|
||||
< .withMessage('Renders without an undefined property error')
|
||||
21,43c29,34
|
||||
21,43c30,35
|
||||
< it("GH-150: Inverted sections print when they shouldn't", function() {
|
||||
< var string = '{{^set}}not set{{/set}} :: {{#set}}set{{/set}}';
|
||||
<
|
||||
|
@ -60,47 +61,47 @@
|
|||
> expectTemplate(string).withInput({ set: undefined }).toCompileTo('not set :: ');
|
||||
> expectTemplate(string).withInput({ set: false }).toCompileTo('not set :: ');
|
||||
> expectTemplate(string).withInput({ set: true }).toCompileTo(' :: set');
|
||||
46c37
|
||||
46c38
|
||||
< it('GH-158: Using array index twice, breaks the template', function() {
|
||||
---
|
||||
> it('GH-158: Using array index twice, breaks the template', () => {
|
||||
49d39
|
||||
49d40
|
||||
< .withMessage('it works as expected')
|
||||
53,54c43,44
|
||||
53,54c44,45
|
||||
< it("bug reported by @fat where lambdas weren't being properly resolved", function() {
|
||||
< var string =
|
||||
---
|
||||
> it("bug reported by @fat where lambdas weren't being properly resolved", () => {
|
||||
> const string =
|
||||
69,70c59,60
|
||||
69,70c60,61
|
||||
< var data = {
|
||||
< thing: function() {
|
||||
---
|
||||
> const data = {
|
||||
> thing() {
|
||||
76c66
|
||||
76c67
|
||||
< { className: 'three', word: '@sayrer' }
|
||||
---
|
||||
> { className: 'three', word: '@sayrer' },
|
||||
78c68
|
||||
78c69
|
||||
< hasThings: function() {
|
||||
---
|
||||
> hasThings() {
|
||||
80c70
|
||||
80c71
|
||||
< }
|
||||
---
|
||||
> },
|
||||
83c73
|
||||
83c74
|
||||
< var output =
|
||||
---
|
||||
> const output =
|
||||
92,94c82
|
||||
92,94c83
|
||||
< expectTemplate(string)
|
||||
< .withInput(data)
|
||||
< .toCompileTo(output);
|
||||
---
|
||||
> expectTemplate(string).withInput(data).toCompileTo(output);
|
||||
97,100c85,86
|
||||
97,100c86,87
|
||||
< it('GH-408: Multiple loops fail', function() {
|
||||
< expectTemplate(
|
||||
< '{{#.}}{{name}}{{/.}}{{#.}}{{name}}{{/.}}{{#.}}{{name}}{{/.}}'
|
||||
|
@ -108,37 +109,37 @@
|
|||
---
|
||||
> it('GH-408: Multiple loops fail', () => {
|
||||
> expectTemplate('{{#.}}{{name}}{{/.}}{{#.}}{{name}}{{/.}}{{#.}}{{name}}{{/.}}')
|
||||
103c89
|
||||
103c90
|
||||
< { name: 'Jane Doe', location: { city: 'New York' } }
|
||||
---
|
||||
> { name: 'Jane Doe', location: { city: 'New York' } },
|
||||
105d90
|
||||
105d91
|
||||
< .withMessage('It should output multiple times')
|
||||
109,110c94,95
|
||||
109,110c95,96
|
||||
< it('GS-428: Nested if else rendering', function() {
|
||||
< var succeedingTemplate =
|
||||
---
|
||||
> it('GS-428: Nested if else rendering', () => {
|
||||
> const succeedingTemplate =
|
||||
112c97
|
||||
112c98
|
||||
< var failingTemplate =
|
||||
---
|
||||
> const failingTemplate =
|
||||
115,116c100,101
|
||||
115,116c101,102
|
||||
< var helpers = {
|
||||
< blk: function(block) {
|
||||
---
|
||||
> const helpers = {
|
||||
> blk(block: Handlebars.HelperOptions) {
|
||||
119c104
|
||||
119c105
|
||||
< inverse: function(block) {
|
||||
---
|
||||
> inverse(block: Handlebars.HelperOptions) {
|
||||
121c106
|
||||
121c107
|
||||
< }
|
||||
---
|
||||
> },
|
||||
124,130c109,110
|
||||
124,130c110,111
|
||||
< expectTemplate(succeedingTemplate)
|
||||
< .withHelpers(helpers)
|
||||
< .toCompileTo(' Expected ');
|
||||
|
@ -149,7 +150,7 @@
|
|||
---
|
||||
> expectTemplate(succeedingTemplate).withHelpers(helpers).toCompileTo(' Expected ');
|
||||
> expectTemplate(failingTemplate).withHelpers(helpers).toCompileTo(' Expected ');
|
||||
133,136c113,114
|
||||
133,136c114,115
|
||||
< it('GH-458: Scoped this identifier', function() {
|
||||
< expectTemplate('{{./foo}}')
|
||||
< .withInput({ foo: 'bar' })
|
||||
|
@ -157,25 +158,25 @@
|
|||
---
|
||||
> it('GH-458: Scoped this identifier', () => {
|
||||
> expectTemplate('{{./foo}}').withInput({ foo: 'bar' }).toCompileTo('bar');
|
||||
139c117
|
||||
139c118
|
||||
< it('GH-375: Unicode line terminators', function() {
|
||||
---
|
||||
> it('GH-375: Unicode line terminators', () => {
|
||||
143c121
|
||||
143c122
|
||||
< it('GH-534: Object prototype aliases', function() {
|
||||
---
|
||||
> it('GH-534: Object prototype aliases', () => {
|
||||
144a123
|
||||
144a124
|
||||
> // @ts-expect-error
|
||||
147,149c126
|
||||
147,149c127
|
||||
< expectTemplate('{{foo}}')
|
||||
< .withInput({ foo: 'bar' })
|
||||
< .toCompileTo('bar');
|
||||
---
|
||||
> expectTemplate('{{foo}}').withInput({ foo: 'bar' }).toCompileTo('bar');
|
||||
150a128
|
||||
150a129
|
||||
> // @ts-expect-error
|
||||
155,157c133,135
|
||||
155,157c134,136
|
||||
< it('GH-437: Matching escaping', function() {
|
||||
< expectTemplate('{{{a}}').toThrow(Error, /Parse error on/);
|
||||
< expectTemplate('{{a}}}').toThrow(Error, /Parse error on/);
|
||||
|
@ -183,7 +184,7 @@
|
|||
> it('GH-437: Matching escaping', () => {
|
||||
> expectTemplate('{{{a}}').toThrow(/Parse error on/);
|
||||
> expectTemplate('{{a}}}').toThrow(/Parse error on/);
|
||||
160,166c138,140
|
||||
160,166c139,141
|
||||
< it('GH-676: Using array in escaping mustache fails', function() {
|
||||
< var data = { arr: [1, 2] };
|
||||
<
|
||||
|
@ -195,30 +196,30 @@
|
|||
> it('GH-676: Using array in escaping mustache fails', () => {
|
||||
> const data = { arr: [1, 2] };
|
||||
> expectTemplate('{{arr}}').withInput(data).toCompileTo(data.arr.toString());
|
||||
169c143
|
||||
169c144
|
||||
< it('Mustache man page', function() {
|
||||
---
|
||||
> it('Mustache man page', () => {
|
||||
177c151
|
||||
177c152
|
||||
< in_ca: true
|
||||
---
|
||||
> in_ca: true,
|
||||
179,182c153
|
||||
179,182c154
|
||||
< .withMessage('the hello world mustache example works')
|
||||
< .toCompileTo(
|
||||
< 'Hello Chris. You have just won $10000! Well, $6000, after taxes.'
|
||||
< );
|
||||
---
|
||||
> .toCompileTo('Hello Chris. You have just won $10000! Well, $6000, after taxes.');
|
||||
185c156
|
||||
185c157
|
||||
< it('GH-731: zero context rendering', function() {
|
||||
---
|
||||
> it('GH-731: zero context rendering', () => {
|
||||
189c160
|
||||
189c161
|
||||
< bar: 'OK'
|
||||
---
|
||||
> bar: 'OK',
|
||||
194,197c165,166
|
||||
194,197c166,167
|
||||
< it('GH-820: zero pathed rendering', function() {
|
||||
< expectTemplate('{{foo.bar}}')
|
||||
< .withInput({ foo: 0 })
|
||||
|
@ -226,37 +227,37 @@
|
|||
---
|
||||
> it('GH-820: zero pathed rendering', () => {
|
||||
> expectTemplate('{{foo.bar}}').withInput({ foo: 0 }).toCompileTo('');
|
||||
200c169
|
||||
200c170
|
||||
< it('GH-837: undefined values for helpers', function() {
|
||||
---
|
||||
> it('GH-837: undefined values for helpers', () => {
|
||||
203c172
|
||||
203c173
|
||||
< str: function(value) {
|
||||
---
|
||||
> str(value) {
|
||||
205c174
|
||||
205c175
|
||||
< }
|
||||
---
|
||||
> },
|
||||
210c179
|
||||
210c180
|
||||
< it('GH-926: Depths and de-dupe', function() {
|
||||
---
|
||||
> it('GH-926: Depths and de-dupe', () => {
|
||||
217c186
|
||||
217c187
|
||||
< notData: [1]
|
||||
---
|
||||
> notData: [1],
|
||||
222c191
|
||||
222c192
|
||||
< it('GH-1021: Each empty string key', function() {
|
||||
---
|
||||
> it('GH-1021: Each empty string key', () => {
|
||||
228,229c197,198
|
||||
228,229c198,199
|
||||
< value: 10000
|
||||
< }
|
||||
---
|
||||
> value: 10000,
|
||||
> },
|
||||
234,248c203,204
|
||||
234,248c204,205
|
||||
< it('GH-1054: Should handle simple safe string responses', function() {
|
||||
< expectTemplate('{{#wrap}}{{>partial}}{{/wrap}}')
|
||||
< .withHelpers({
|
||||
|
@ -275,27 +276,27 @@
|
|||
---
|
||||
> it('GH-1065: Sparse arrays', () => {
|
||||
> const array = [];
|
||||
252c208
|
||||
252c209
|
||||
< .withInput({ array: array })
|
||||
---
|
||||
> .withInput({ array })
|
||||
256c212
|
||||
256c213
|
||||
< it('GH-1093: Undefined helper context', function() {
|
||||
---
|
||||
> it('GH-1093: Undefined helper context', () => {
|
||||
260c216
|
||||
260c217
|
||||
< helper: function() {
|
||||
---
|
||||
> helper(this: any) {
|
||||
263c219
|
||||
263c220
|
||||
< for (var name in this) {
|
||||
---
|
||||
> for (const name in this) {
|
||||
270c226
|
||||
270c227
|
||||
< }
|
||||
---
|
||||
> },
|
||||
275,306c231
|
||||
275,306c232
|
||||
< it('should support multiple levels of inline partials', function() {
|
||||
< expectTemplate(
|
||||
< '{{#> layout}}{{#*inline "subcontent"}}subcontent{{/inline}}{{/layout}}'
|
||||
|
@ -330,15 +331,15 @@
|
|||
< it('GH-1135 : Context handling within each iteration', function() {
|
||||
---
|
||||
> it('GH-1135 : Context handling within each iteration', () => {
|
||||
315c240
|
||||
315c241
|
||||
< myif: function(conditional, options) {
|
||||
---
|
||||
> myif(conditional, options) {
|
||||
321c246
|
||||
> myif(conditional, options: Handlebars.HelperOptions) {
|
||||
321c247
|
||||
< }
|
||||
---
|
||||
> },
|
||||
326,343c251,252
|
||||
326,343c252,253
|
||||
< it('GH-1186: Support block params for existing programs', function() {
|
||||
< expectTemplate(
|
||||
< '{{#*inline "test"}}{{> @partial-block }}{{/inline}}' +
|
||||
|
@ -360,13 +361,13 @@
|
|||
---
|
||||
> it('GH-1319: "unless" breaks when "each" value equals "null"', () => {
|
||||
> expectTemplate('{{#each list}}{{#unless ./prop}}parent={{../value}} {{/unless}}{{/each}}')
|
||||
346c255
|
||||
346c256
|
||||
< list: [null, 'a']
|
||||
---
|
||||
> list: [null, 'a'],
|
||||
348d256
|
||||
348d257
|
||||
< .withMessage('')
|
||||
352,457c260
|
||||
352,457c261
|
||||
< it('GH-1341: 4.0.7 release breaks {{#if @partial-block}} usage', function() {
|
||||
< expectTemplate('template {{>partial}} template')
|
||||
< .withPartials({
|
||||
|
@ -475,15 +476,15 @@
|
|||
< it('should allow hash with protected array names', function() {
|
||||
---
|
||||
> it('should allow hash with protected array names', () => {
|
||||
461c264
|
||||
461c265
|
||||
< helpa: function(options) {
|
||||
---
|
||||
> helpa(options) {
|
||||
463c266
|
||||
> helpa(options: Handlebars.HelperOptions) {
|
||||
463c267
|
||||
< }
|
||||
---
|
||||
> },
|
||||
468,496c271,272
|
||||
468,496c272,273
|
||||
< describe('GH-1598: Performance degradation for partials since v4.3.0', function() {
|
||||
< // Do not run test for runs without compiler
|
||||
< if (!Handlebars.compile) {
|
||||
|
|
|
@ -98,9 +98,17 @@ describe('helpers', () => {
|
|||
expect(toHaveProperties.calls).toEqual(blockTemplates.length * 2 * factor);
|
||||
});
|
||||
|
||||
it('should pass expected "this" and arguments to helper functions', () => {
|
||||
it('should pass expected "this" to helper functions (without input)', () => {
|
||||
expectTemplate('{{hello "world" 12 true false}}')
|
||||
.withHelper('hello', function (this: any, ...args) {
|
||||
.withHelper('hello', function (this: any, ...args: any[]) {
|
||||
expect(this).toMatchInlineSnapshot(`Object {}`);
|
||||
})
|
||||
.toCompileTo('');
|
||||
});
|
||||
|
||||
it('should pass expected "this" to helper functions (with input)', () => {
|
||||
expectTemplate('{{hello "world" 12 true false}}')
|
||||
.withHelper('hello', function (this: any, ...args: any[]) {
|
||||
expect(this).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"people": Array [
|
||||
|
@ -115,6 +123,19 @@ describe('helpers', () => {
|
|||
],
|
||||
}
|
||||
`);
|
||||
})
|
||||
.withInput({
|
||||
people: [
|
||||
{ name: 'Alan', id: 1 },
|
||||
{ name: 'Yehuda', id: 2 },
|
||||
],
|
||||
})
|
||||
.toCompileTo('');
|
||||
});
|
||||
|
||||
it('should pass expected "this" and arguments to helper functions (non-block helper)', () => {
|
||||
expectTemplate('{{hello "world" 12 true false}}')
|
||||
.withHelper('hello', function (this: any, ...args: any[]) {
|
||||
expect(args).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"world",
|
||||
|
@ -161,6 +182,58 @@ describe('helpers', () => {
|
|||
})
|
||||
.toCompileTo('');
|
||||
});
|
||||
|
||||
it('should pass expected "this" and arguments to helper functions (block helper)', () => {
|
||||
expectTemplate('{{#hello "world" 12 true false}}{{/hello}}')
|
||||
.withHelper('hello', function (this: any, ...args: any[]) {
|
||||
expect(args).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"world",
|
||||
12,
|
||||
true,
|
||||
false,
|
||||
Object {
|
||||
"data": Object {
|
||||
"root": Object {
|
||||
"people": Array [
|
||||
Object {
|
||||
"id": 1,
|
||||
"name": "Alan",
|
||||
},
|
||||
Object {
|
||||
"id": 2,
|
||||
"name": "Yehuda",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
"fn": [Function],
|
||||
"hash": Object {},
|
||||
"inverse": [Function],
|
||||
"loc": Object {
|
||||
"end": Object {
|
||||
"column": 42,
|
||||
"line": 1,
|
||||
},
|
||||
"start": Object {
|
||||
"column": 0,
|
||||
"line": 1,
|
||||
},
|
||||
},
|
||||
"lookupProperty": [Function],
|
||||
"name": "hello",
|
||||
},
|
||||
]
|
||||
`);
|
||||
})
|
||||
.withInput({
|
||||
people: [
|
||||
{ name: 'Alan', id: 1 },
|
||||
{ name: 'Yehuda', id: 2 },
|
||||
],
|
||||
})
|
||||
.toCompileTo('');
|
||||
});
|
||||
});
|
||||
|
||||
// Extra "blocks" tests
|
||||
|
@ -190,11 +263,11 @@ describe('blocks', () => {
|
|||
const render = compile('{{*decorator}}');
|
||||
|
||||
let calls = 0;
|
||||
expect(render({})).toEqual('');
|
||||
expect(render()).toEqual('');
|
||||
expect(calls).toEqual(1);
|
||||
|
||||
calls = 0;
|
||||
expect(render({})).toEqual('');
|
||||
expect(render()).toEqual('');
|
||||
expect(calls).toEqual(1);
|
||||
|
||||
global.kbnHandlebarsEnv = null;
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
* See `packages/kbn-handlebars/LICENSE` for more information.
|
||||
*/
|
||||
|
||||
// The handlebars module uses `export =`, so we should technically use `import OriginalHandlebars = require('handlebars')`, but Babel will not allow this.
|
||||
import OriginalHandlebars from 'handlebars';
|
||||
// The handlebars module uses `export =`, so we should technically use `import Handlebars = require('handlebars')`, but Babel will not allow this.
|
||||
import Handlebars from 'handlebars';
|
||||
import {
|
||||
createProtoAccessControl,
|
||||
resultIsAllowed,
|
||||
|
@ -17,20 +17,59 @@ import { indexOf, createFrame } from 'handlebars/dist/cjs/handlebars/utils';
|
|||
// @ts-expect-error: Could not find a declaration file for module
|
||||
import { moveHelperToHooks } from 'handlebars/dist/cjs/handlebars/helpers';
|
||||
|
||||
const originalCreate = OriginalHandlebars.create;
|
||||
const originalCreate = Handlebars.create;
|
||||
|
||||
/**
|
||||
* A custom version of the Handlesbars module with an extra `compileAST` function.
|
||||
* A custom version of the Handlesbars module with an extra `compileAST` function and fixed typings.
|
||||
*/
|
||||
const Handlebars: typeof ExtendedHandlebars & typeof OriginalHandlebars = OriginalHandlebars as any;
|
||||
declare module 'handlebars' {
|
||||
export function compileAST(
|
||||
input: string | hbs.AST.Program,
|
||||
options?: ExtendedCompileOptions
|
||||
): (context?: any, options?: ExtendedRuntimeOptions) => string;
|
||||
|
||||
// --------------------------------------------------------
|
||||
// Override/Extend inherited types below that are incorrect
|
||||
// --------------------------------------------------------
|
||||
|
||||
export interface TemplateDelegate<T = any> {
|
||||
(context?: T, options?: RuntimeOptions): string; // Override to ensure `context` is optional
|
||||
blockParams?: number; // TODO: Can this really be optional?
|
||||
}
|
||||
|
||||
export interface HelperOptions {
|
||||
name: string;
|
||||
loc: { start: hbs.AST.SourceLocation['start']; end: hbs.AST.SourceLocation['end'] };
|
||||
lookupProperty: LookupProperty;
|
||||
}
|
||||
|
||||
export interface HelperDelegate {
|
||||
// eslint-disable-next-line @typescript-eslint/prefer-function-type
|
||||
(...params: any[]): any;
|
||||
}
|
||||
}
|
||||
|
||||
const kHelper = Symbol('helper');
|
||||
const kAmbiguous = Symbol('ambiguous');
|
||||
const kSimple = Symbol('simple');
|
||||
type NodeType = typeof kHelper | typeof kAmbiguous | typeof kSimple;
|
||||
|
||||
type ProcessableNode = hbs.AST.MustacheStatement | hbs.AST.BlockStatement | hbs.AST.SubExpression;
|
||||
type LookupProperty = <T = any>(parent: { [name: string]: any }, propertyName: string) => T;
|
||||
|
||||
type ProcessableStatementNode = hbs.AST.MustacheStatement | hbs.AST.SubExpression;
|
||||
type ProcessableBlockStatementNode = hbs.AST.BlockStatement | hbs.AST.PartialBlockStatement;
|
||||
type ProcessableNode = ProcessableStatementNode | ProcessableBlockStatementNode;
|
||||
type ProcessableNodeWithPathParts = ProcessableNode & { path: hbs.AST.PathExpression };
|
||||
type ProcessableNodeWithPathPartsOrLiteral = ProcessableNode & {
|
||||
path: hbs.AST.PathExpression | hbs.AST.Literal;
|
||||
};
|
||||
|
||||
export type NonBlockHelperOptions = Omit<Handlebars.HelperOptions, 'fn' | 'inverse'>;
|
||||
export type AmbiguousHelperOptions = Handlebars.HelperOptions | NonBlockHelperOptions;
|
||||
|
||||
export interface DecoratorOptions extends Omit<Handlebars.HelperOptions, 'lookupProperties'> {
|
||||
args?: any[];
|
||||
}
|
||||
|
||||
/**
|
||||
* If the `unsafe-eval` CSP is set, this string constant will be `compile`,
|
||||
|
@ -85,19 +124,6 @@ export interface DecoratorsHash {
|
|||
[name: string]: DecoratorFunction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normally this namespace isn't used directly. It's required to be present by
|
||||
* TypeScript when calling the `Handlebars.create()` function.
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
export declare namespace ExtendedHandlebars {
|
||||
export function compileAST(
|
||||
input: string | hbs.AST.Program,
|
||||
options?: ExtendedCompileOptions
|
||||
): (context: any, options?: ExtendedRuntimeOptions) => string;
|
||||
export function create(): typeof Handlebars; // eslint-disable-line @typescript-eslint/no-shadow
|
||||
}
|
||||
|
||||
// The handlebars module uses `export =`, so it can't be re-exported using `export *`.
|
||||
// However, because of Babel, we're not allowed to use `export =` ourselves.
|
||||
// So we have to resort to using `exports default` even though eslint doesn't like it.
|
||||
|
@ -151,7 +177,7 @@ interface Container {
|
|||
helpers: HelpersHash;
|
||||
decorators: DecoratorsHash;
|
||||
strict: (obj: { [name: string]: any }, name: string, loc: hbs.AST.SourceLocation) => any;
|
||||
lookupProperty: <T = any>(parent: { [name: string]: any }, propertyName: string) => T;
|
||||
lookupProperty: LookupProperty;
|
||||
lambda: (current: any, context: any) => any;
|
||||
data: (value: any, depth: number) => any;
|
||||
hooks: {
|
||||
|
@ -172,8 +198,7 @@ class ElasticHandlebarsVisitor extends Handlebars.Visitor {
|
|||
private blockParamValues: any[][] = [];
|
||||
private ast?: hbs.AST.Program;
|
||||
private container: Container;
|
||||
// @ts-expect-error
|
||||
private defaultHelperOptions: Handlebars.HelperOptions = {};
|
||||
private defaultHelperOptions: Pick<NonBlockHelperOptions, 'lookupProperty'>;
|
||||
private processedRootDecorators = false; // Root decorators should not have access to input arguments. This flag helps us detect them.
|
||||
private processedDecoratorsForProgram = new Set(); // It's important that a given program node only has its decorators run once, we use this Map to keep track of them
|
||||
|
||||
|
@ -257,8 +282,9 @@ class ElasticHandlebarsVisitor extends Handlebars.Visitor {
|
|||
hooks: {},
|
||||
});
|
||||
|
||||
// @ts-expect-error
|
||||
this.defaultHelperOptions.lookupProperty = container.lookupProperty;
|
||||
this.defaultHelperOptions = {
|
||||
lookupProperty: container.lookupProperty,
|
||||
};
|
||||
}
|
||||
|
||||
render(context: any, options: ExtendedRuntimeOptions = {}): string {
|
||||
|
@ -397,16 +423,15 @@ class ElasticHandlebarsVisitor extends Handlebars.Visitor {
|
|||
|
||||
const result = this.container.lookupProperty<DecoratorFunction>(
|
||||
this.container.decorators,
|
||||
// @ts-expect-error: Property 'name' does not exist on type 'HelperOptions' - The types are wrong
|
||||
options.name
|
||||
)(prog, props, this.container, options);
|
||||
|
||||
Object.assign(result || prog, props);
|
||||
}
|
||||
|
||||
private processStatementOrExpression(node: ProcessableNode) {
|
||||
private processStatementOrExpression(node: ProcessableNodeWithPathPartsOrLiteral) {
|
||||
// Calling `transformLiteralToPath` has side-effects!
|
||||
// It converts a node from type `ProcessableNode` to `ProcessableNodeWithPathParts`
|
||||
// It converts a node from type `ProcessableNodeWithPathPartsOrLiteral` to `ProcessableNodeWithPathParts`
|
||||
transformLiteralToPath(node);
|
||||
|
||||
switch (this.classifyNode(node as ProcessableNodeWithPathParts)) {
|
||||
|
@ -534,7 +559,7 @@ class ElasticHandlebarsVisitor extends Handlebars.Visitor {
|
|||
const name = node.path.parts[0];
|
||||
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!.call(helper.context, ...helper.params, helper.options);
|
||||
this.output.push(result);
|
||||
}
|
||||
|
||||
|
@ -560,7 +585,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!.call(helper.context, ...helper.params, helper.options);
|
||||
|
||||
this.output.push(result);
|
||||
}
|
||||
|
@ -613,7 +638,7 @@ class ElasticHandlebarsVisitor extends Handlebars.Visitor {
|
|||
}
|
||||
|
||||
return typeof helper.fn === 'function'
|
||||
? helper.fn.apply(helper.context, helper.params)
|
||||
? helper.fn.call(helper.context, ...helper.params, helper.options)
|
||||
: helper.fn;
|
||||
}
|
||||
|
||||
|
@ -623,70 +648,74 @@ class ElasticHandlebarsVisitor extends Handlebars.Visitor {
|
|||
|
||||
if (!helper.fn) {
|
||||
const context = this.scopes[0];
|
||||
const options = helper.params[helper.params.length - 1];
|
||||
value = this.container.hooks.blockHelperMissing!.call(context, value, options);
|
||||
value = this.container.hooks.blockHelperMissing!.call(context, value, helper.options);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
private setupHelper(node: ProcessableNodeWithPathParts, helperName: string) {
|
||||
private setupHelper(
|
||||
node: ProcessableNode,
|
||||
helperName: string
|
||||
): {
|
||||
fn?: Handlebars.HelperDelegate;
|
||||
context: any[];
|
||||
params: any[];
|
||||
options: AmbiguousHelperOptions;
|
||||
} {
|
||||
return {
|
||||
fn: this.container.lookupProperty(this.container.helpers, helperName),
|
||||
context: this.scopes[0],
|
||||
params: [...this.resolveNodes(node.params), this.setupParams(node, helperName)],
|
||||
params: this.resolveNodes(node.params),
|
||||
options: this.setupParams(node, helperName),
|
||||
};
|
||||
}
|
||||
|
||||
private setupDecoratorOptions(
|
||||
decorator: hbs.AST.Decorator | hbs.AST.DecoratorBlock
|
||||
): Handlebars.HelperOptions {
|
||||
private setupDecoratorOptions(decorator: hbs.AST.Decorator | hbs.AST.DecoratorBlock) {
|
||||
// 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 options = this.setupParams(decorator as hbs.AST.DecoratorBlock, name);
|
||||
const options = toDecoratorOptions(this.setupParams(decorator, name));
|
||||
|
||||
if (decorator.params.length > 0) {
|
||||
if (!this.processedRootDecorators) {
|
||||
// When processing the root decorators, temporarily remove the root context so it's not accessible to the decorator
|
||||
const context = this.scopes.shift();
|
||||
// @ts-expect-error: Property 'args' does not exist on type 'HelperOptions'. The 'args' property is expected in decorators
|
||||
options.args = this.resolveNodes(decorator.params);
|
||||
this.scopes.unshift(context);
|
||||
} else {
|
||||
// @ts-expect-error: Property 'args' does not exist on type 'HelperOptions'. The 'args' property is expected in decorators
|
||||
options.args = this.resolveNodes(decorator.params);
|
||||
}
|
||||
}
|
||||
|
||||
// @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
|
||||
return options;
|
||||
}
|
||||
|
||||
private setupParams(node: ProcessableBlockStatementNode, name: string): Handlebars.HelperOptions;
|
||||
private setupParams(node: ProcessableStatementNode, name: string): NonBlockHelperOptions;
|
||||
private setupParams(node: ProcessableNode, name: string): AmbiguousHelperOptions;
|
||||
private setupParams(node: ProcessableNode, name: string): AmbiguousHelperOptions {
|
||||
const options = {
|
||||
name,
|
||||
hash: this.getHash(node),
|
||||
data: this.runtimeOptions!.data,
|
||||
loc: { start: node.loc.start, end: node.loc.end },
|
||||
...this.defaultHelperOptions,
|
||||
};
|
||||
|
||||
if (isBlock(node)) {
|
||||
// TODO: Is there a way in TypeScript to infer that `options` is `Handlebars.HelperOptions` inside this if-statement. If not, is there a way to just cast once?
|
||||
(options as Handlebars.HelperOptions).fn = this.generateProgramFunction(node.program);
|
||||
if (node.program)
|
||||
this.processDecorators(node.program, (options as Handlebars.HelperOptions).fn);
|
||||
(options as Handlebars.HelperOptions).inverse = this.generateProgramFunction(node.inverse);
|
||||
if (node.inverse)
|
||||
this.processDecorators(node.inverse, (options as Handlebars.HelperOptions).inverse);
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
private setupParams(
|
||||
node: ProcessableNodeWithPathParts,
|
||||
helperName: string
|
||||
): Handlebars.HelperOptions {
|
||||
const options: Handlebars.HelperOptions = {
|
||||
// @ts-expect-error: Name should be on there, but the offical types doesn't know this
|
||||
name: helperName,
|
||||
hash: this.getHash(node),
|
||||
data: this.runtimeOptions!.data,
|
||||
loc: { start: node.loc.start, end: node.loc.end },
|
||||
};
|
||||
|
||||
if (isBlock(node)) {
|
||||
options.fn = this.generateProgramFunction(node.program);
|
||||
if (node.program) this.processDecorators(node.program, options.fn);
|
||||
options.inverse = this.generateProgramFunction(node.inverse);
|
||||
if (node.inverse) this.processDecorators(node.inverse, options.inverse);
|
||||
}
|
||||
|
||||
return Object.assign(options, this.defaultHelperOptions);
|
||||
}
|
||||
|
||||
private generateProgramFunction(program?: hbs.AST.Program) {
|
||||
private generateProgramFunction(program: hbs.AST.Program) {
|
||||
if (!program) return noop;
|
||||
|
||||
const prog: Handlebars.TemplateDelegate = (
|
||||
|
@ -722,7 +751,6 @@ class ElasticHandlebarsVisitor extends Handlebars.Visitor {
|
|||
return result;
|
||||
};
|
||||
|
||||
// @ts-expect-error: Property 'blockParams' does not exist on type 'TemplateDelegate<any>' - The types are too strict
|
||||
prog.blockParams = program.blockParams?.length ?? 0;
|
||||
return prog;
|
||||
}
|
||||
|
@ -795,6 +823,13 @@ function isDecorator(node: hbs.AST.Node): node is hbs.AST.Decorator | hbs.AST.De
|
|||
return node.type === 'Decorator' || node.type === 'DecoratorBlock';
|
||||
}
|
||||
|
||||
function toDecoratorOptions(options: AmbiguousHelperOptions) {
|
||||
// There's really no tests/documentation on this, but to match the upstream codebase we'll remove `lookupProperty` from the decorator context
|
||||
delete (options as any).lookupProperty;
|
||||
|
||||
return options as DecoratorOptions;
|
||||
}
|
||||
|
||||
function noop() {
|
||||
return '';
|
||||
}
|
||||
|
|
|
@ -64,12 +64,12 @@ class HandlebarsTestBench {
|
|||
return this;
|
||||
}
|
||||
|
||||
withHelper(name: string, helper?: Handlebars.HelperDelegate) {
|
||||
withHelper<F extends Handlebars.HelperDelegate>(name: string, helper?: F) {
|
||||
this.helpers[name] = helper;
|
||||
return this;
|
||||
}
|
||||
|
||||
withHelpers(helperFunctions: { [name: string]: Handlebars.HelperDelegate }) {
|
||||
withHelpers<F extends Handlebars.HelperDelegate>(helperFunctions: { [name: string]: F }) {
|
||||
for (const [name, helper] of Object.entries(helperFunctions)) {
|
||||
this.withHelper(name, helper);
|
||||
}
|
||||
|
|
|
@ -200,12 +200,11 @@ describe('blocks', () => {
|
|||
describe('decorators', () => {
|
||||
it('should apply mustache decorators', () => {
|
||||
expectTemplate('{{#helper}}{{*decorator}}{{/helper}}')
|
||||
.withHelper('helper', function (options) {
|
||||
return options.fn.run;
|
||||
.withHelper('helper', function (options: Handlebars.HelperOptions) {
|
||||
return (options.fn as any).run;
|
||||
})
|
||||
.withDecorator('decorator', function (fn) {
|
||||
// @ts-expect-error: Property 'run' does not exist on type 'TemplateDelegate<any>'
|
||||
fn.run = 'success';
|
||||
(fn as any).run = 'success';
|
||||
return fn;
|
||||
})
|
||||
.toCompileTo('success');
|
||||
|
@ -213,24 +212,22 @@ describe('blocks', () => {
|
|||
|
||||
it('should apply allow undefined return', () => {
|
||||
expectTemplate('{{#helper}}{{*decorator}}suc{{/helper}}')
|
||||
.withHelper('helper', function (options) {
|
||||
return options.fn() + options.fn.run;
|
||||
.withHelper('helper', function (options: Handlebars.HelperOptions) {
|
||||
return options.fn() + (options.fn as any).run;
|
||||
})
|
||||
.withDecorator('decorator', function (fn) {
|
||||
// @ts-expect-error: Property 'run' does not exist on type 'TemplateDelegate<any>'
|
||||
fn.run = 'cess';
|
||||
(fn as any).run = 'cess';
|
||||
})
|
||||
.toCompileTo('success');
|
||||
});
|
||||
|
||||
it('should apply block decorators', () => {
|
||||
expectTemplate('{{#helper}}{{#*decorator}}success{{/decorator}}{{/helper}}')
|
||||
.withHelper('helper', function (options) {
|
||||
return options.fn.run;
|
||||
.withHelper('helper', function (options: Handlebars.HelperOptions) {
|
||||
return (options.fn as any).run;
|
||||
})
|
||||
.withDecorator('decorator', function (fn, props, container, options) {
|
||||
// @ts-expect-error: Property 'run' does not exist on type 'TemplateDelegate<any>'
|
||||
fn.run = options.fn();
|
||||
(fn as any).run = options.fn();
|
||||
return fn;
|
||||
})
|
||||
.toCompileTo('success');
|
||||
|
@ -240,13 +237,12 @@ describe('blocks', () => {
|
|||
expectTemplate(
|
||||
'{{#helper}}{{#*decorator}}{{#*nested}}suc{{/nested}}cess{{/decorator}}{{/helper}}'
|
||||
)
|
||||
.withHelper('helper', function (options) {
|
||||
return options.fn.run;
|
||||
.withHelper('helper', function (options: Handlebars.HelperOptions) {
|
||||
return (options.fn as any).run;
|
||||
})
|
||||
.withDecorators({
|
||||
decorator(fn, props, container, options) {
|
||||
// @ts-expect-error: Property 'run' does not exist on type 'TemplateDelegate<any>'
|
||||
fn.run = options.fn.nested + options.fn();
|
||||
(fn as any).run = options.fn.nested + options.fn();
|
||||
return fn;
|
||||
},
|
||||
nested(fn, props, container, options) {
|
||||
|
@ -260,12 +256,11 @@ describe('blocks', () => {
|
|||
expectTemplate(
|
||||
'{{#helper}}{{#*decorator}}suc{{/decorator}}{{#*decorator}}cess{{/decorator}}{{/helper}}'
|
||||
)
|
||||
.withHelper('helper', function (options) {
|
||||
return options.fn.run;
|
||||
.withHelper('helper', function (options: Handlebars.HelperOptions) {
|
||||
return (options.fn as any).run;
|
||||
})
|
||||
.withDecorator('decorator', function (fn, props, container, options) {
|
||||
// @ts-expect-error: Property 'run' does not exist on type 'TemplateDelegate<any>'
|
||||
fn.run = (fn.run || '') + options.fn();
|
||||
(fn as any).run = ((fn as any).run || '') + options.fn();
|
||||
return fn;
|
||||
})
|
||||
.toCompileTo('success');
|
||||
|
@ -273,12 +268,11 @@ describe('blocks', () => {
|
|||
|
||||
it('should access parent variables', () => {
|
||||
expectTemplate('{{#helper}}{{*decorator foo}}{{/helper}}')
|
||||
.withHelper('helper', function (options) {
|
||||
return options.fn.run;
|
||||
.withHelper('helper', function (options: Handlebars.HelperOptions) {
|
||||
return (options.fn as any).run;
|
||||
})
|
||||
.withDecorator('decorator', function (fn, props, container, options) {
|
||||
// @ts-expect-error: Property 'run' does not exist on type 'TemplateDelegate<any>'
|
||||
fn.run = options.args;
|
||||
(fn as any).run = options.args;
|
||||
return fn;
|
||||
})
|
||||
.withInput({ foo: 'success' })
|
||||
|
|
|
@ -29,7 +29,7 @@ describe('compiler', () => {
|
|||
|
||||
it('should include the location in the error (row and column)', () => {
|
||||
try {
|
||||
compile(' \n {{#if}}\n{{/def}}')({});
|
||||
compile(' \n {{#if}}\n{{/def}}')();
|
||||
expect(true).toEqual(false);
|
||||
} catch (err) {
|
||||
expect(err.message).toEqual("if doesn't match def - 2:5");
|
||||
|
@ -45,7 +45,7 @@ describe('compiler', () => {
|
|||
|
||||
it('should include the location as enumerable property', () => {
|
||||
try {
|
||||
compile(' \n {{#if}}\n{{/def}}')({});
|
||||
compile(' \n {{#if}}\n{{/def}}')();
|
||||
expect(true).toEqual(false);
|
||||
} catch (err) {
|
||||
expect(Object.prototype.propertyIsEnumerable.call(err, 'column')).toEqual(true);
|
||||
|
@ -57,18 +57,18 @@ describe('compiler', () => {
|
|||
compile({
|
||||
type: 'Program',
|
||||
body: [{ type: 'ContentStatement', value: 'Hello' }],
|
||||
})({})
|
||||
})()
|
||||
).toEqual('Hello');
|
||||
});
|
||||
|
||||
it('can pass through an empty string', () => {
|
||||
expect(compile('')({})).toEqual('');
|
||||
expect(compile('')()).toEqual('');
|
||||
});
|
||||
|
||||
it('should not modify the options.data property(GH-1327)', () => {
|
||||
// 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(
|
||||
JSON.stringify({ data: [{ a: 'foo' }, { a: 'bar' }] }, null, 2)
|
||||
);
|
||||
|
@ -76,7 +76,7 @@ describe('compiler', () => {
|
|||
|
||||
it('should not modify the options.knownHelpers property(GH-1327)', () => {
|
||||
const options = { knownHelpers: {} };
|
||||
compile('{{#each data}}{{@index}}:{{a}} {{/each}}', options)({});
|
||||
compile('{{#each data}}{{@index}}:{{a}} {{/each}}', options)();
|
||||
expect(JSON.stringify(options, null, 2)).toEqual(
|
||||
JSON.stringify({ knownHelpers: {} }, null, 2)
|
||||
);
|
||||
|
|
|
@ -32,7 +32,7 @@ describe('helpers', () => {
|
|||
it('helper for raw block gets raw content', () => {
|
||||
expectTemplate('{{{{raw}}}} {{test}} {{{{/raw}}}}')
|
||||
.withInput({ test: 'hello' })
|
||||
.withHelper('raw', function (options) {
|
||||
.withHelper('raw', function (options: Handlebars.HelperOptions) {
|
||||
return options.fn();
|
||||
})
|
||||
.toCompileTo(' {{test}} ');
|
||||
|
@ -41,7 +41,7 @@ describe('helpers', () => {
|
|||
it('helper for raw block gets parameters', () => {
|
||||
expectTemplate('{{{{raw 1 2 3}}}} {{test}} {{{{/raw}}}}')
|
||||
.withInput({ test: 'hello' })
|
||||
.withHelper('raw', function (a, b, c, options) {
|
||||
.withHelper('raw', function (a, b, c, options: Handlebars.HelperOptions) {
|
||||
const ret = options.fn() + a + b + c;
|
||||
return ret;
|
||||
})
|
||||
|
@ -51,7 +51,7 @@ describe('helpers', () => {
|
|||
describe('raw block parsing (with identity helper-function)', () => {
|
||||
function runWithIdentityHelper(template: string, expected: string) {
|
||||
expectTemplate(template)
|
||||
.withHelper('identity', function (options) {
|
||||
.withHelper('identity', function (options: Handlebars.HelperOptions) {
|
||||
return options.fn();
|
||||
})
|
||||
.toCompileTo(expected);
|
||||
|
@ -95,7 +95,7 @@ describe('helpers', () => {
|
|||
it('helper block with identical context', () => {
|
||||
expectTemplate('{{#goodbyes}}{{name}}{{/goodbyes}}')
|
||||
.withInput({ name: 'Alan' })
|
||||
.withHelper('goodbyes', function (this: any, options) {
|
||||
.withHelper('goodbyes', function (this: any, options: Handlebars.HelperOptions) {
|
||||
let out = '';
|
||||
const byes = ['Goodbye', 'goodbye', 'GOODBYE'];
|
||||
for (let i = 0, j = byes.length; i < j; i++) {
|
||||
|
@ -109,7 +109,7 @@ describe('helpers', () => {
|
|||
it('helper block with complex lookup expression', () => {
|
||||
expectTemplate('{{#goodbyes}}{{../name}}{{/goodbyes}}')
|
||||
.withInput({ name: 'Alan' })
|
||||
.withHelper('goodbyes', function (options) {
|
||||
.withHelper('goodbyes', function (options: Handlebars.HelperOptions) {
|
||||
let out = '';
|
||||
const byes = ['Goodbye', 'goodbye', 'GOODBYE'];
|
||||
for (let i = 0, j = byes.length; i < j; i++) {
|
||||
|
@ -126,7 +126,7 @@ describe('helpers', () => {
|
|||
prefix: '/root',
|
||||
goodbyes: [{ text: 'Goodbye', url: 'goodbye' }],
|
||||
})
|
||||
.withHelper('link', function (this: any, prefix, options) {
|
||||
.withHelper('link', function (this: any, prefix, options: Handlebars.HelperOptions) {
|
||||
return '<a href="' + prefix + '/' + this.url + '">' + options.fn(this) + '</a>';
|
||||
})
|
||||
.toCompileTo('<a href="/root/goodbye">Goodbye</a>');
|
||||
|
@ -138,7 +138,7 @@ describe('helpers', () => {
|
|||
prefix: '/root',
|
||||
goodbyes: [{ text: 'Goodbye', url: 'goodbye' }],
|
||||
})
|
||||
.withHelper('link', function (this: any, prefix, options) {
|
||||
.withHelper('link', function (this: any, prefix, options: Handlebars.HelperOptions) {
|
||||
return '<a href="' + prefix + '/' + this.url + '">' + options.fn(this) + '</a>';
|
||||
})
|
||||
.toCompileTo('<a href="/root/goodbye">Goodbye</a>');
|
||||
|
@ -161,7 +161,7 @@ describe('helpers', () => {
|
|||
it('block helper', () => {
|
||||
expectTemplate('{{#goodbyes}}{{text}}! {{/goodbyes}}cruel {{world}}!')
|
||||
.withInput({ world: 'world' })
|
||||
.withHelper('goodbyes', function (options) {
|
||||
.withHelper('goodbyes', function (options: Handlebars.HelperOptions) {
|
||||
return options.fn({ text: 'GOODBYE' });
|
||||
})
|
||||
.toCompileTo('GOODBYE! cruel world!');
|
||||
|
@ -170,7 +170,7 @@ describe('helpers', () => {
|
|||
it('block helper staying in the same context', () => {
|
||||
expectTemplate('{{#form}}<p>{{name}}</p>{{/form}}')
|
||||
.withInput({ name: 'Yehuda' })
|
||||
.withHelper('form', function (this: any, options) {
|
||||
.withHelper('form', function (this: any, options: Handlebars.HelperOptions) {
|
||||
return '<form>' + options.fn(this) + '</form>';
|
||||
})
|
||||
.toCompileTo('<form><p>Yehuda</p></form>');
|
||||
|
@ -201,7 +201,7 @@ describe('helpers', () => {
|
|||
it('block helper passing a new context', () => {
|
||||
expectTemplate('{{#form yehuda}}<p>{{name}}</p>{{/form}}')
|
||||
.withInput({ yehuda: { name: 'Yehuda' } })
|
||||
.withHelper('form', function (context, options) {
|
||||
.withHelper('form', function (context, options: Handlebars.HelperOptions) {
|
||||
return '<form>' + options.fn(context) + '</form>';
|
||||
})
|
||||
.toCompileTo('<form><p>Yehuda</p></form>');
|
||||
|
@ -210,7 +210,7 @@ describe('helpers', () => {
|
|||
it('block helper passing a complex path context', () => {
|
||||
expectTemplate('{{#form yehuda/cat}}<p>{{name}}</p>{{/form}}')
|
||||
.withInput({ yehuda: { name: 'Yehuda', cat: { name: 'Harold' } } })
|
||||
.withHelper('form', function (context, options) {
|
||||
.withHelper('form', function (context, options: Handlebars.HelperOptions) {
|
||||
return '<form>' + options.fn(context) + '</form>';
|
||||
})
|
||||
.toCompileTo('<form><p>Harold</p></form>');
|
||||
|
@ -221,10 +221,10 @@ describe('helpers', () => {
|
|||
.withInput({
|
||||
yehuda: { name: 'Yehuda' },
|
||||
})
|
||||
.withHelper('link', function (this: any, options) {
|
||||
.withHelper('link', function (this: any, options: Handlebars.HelperOptions) {
|
||||
return '<a href="' + this.name + '">' + options.fn(this) + '</a>';
|
||||
})
|
||||
.withHelper('form', function (context, options) {
|
||||
.withHelper('form', function (context, options: Handlebars.HelperOptions) {
|
||||
return '<form>' + options.fn(context) + '</form>';
|
||||
})
|
||||
.toCompileTo('<form><p>Yehuda</p><a href="Yehuda">Hello</a></form>');
|
||||
|
@ -477,7 +477,7 @@ describe('helpers', () => {
|
|||
it('block multi-params work', () => {
|
||||
expectTemplate('Message: {{#goodbye cruel world}}{{greeting}} {{adj}} {{noun}}{{/goodbye}}')
|
||||
.withInput({ cruel: 'cruel', world: 'world' })
|
||||
.withHelper('goodbye', function (cruel, world, options) {
|
||||
.withHelper('goodbye', function (cruel, world, options: Handlebars.HelperOptions) {
|
||||
return options.fn({ greeting: 'Goodbye', adj: cruel, noun: world });
|
||||
})
|
||||
.toCompileTo('Message: Goodbye cruel world');
|
||||
|
@ -487,7 +487,7 @@ describe('helpers', () => {
|
|||
describe('hash', () => {
|
||||
it('helpers can take an optional hash', () => {
|
||||
expectTemplate('{{goodbye cruel="CRUEL" world="WORLD" times=12}}')
|
||||
.withHelper('goodbye', function (options) {
|
||||
.withHelper('goodbye', function (options: Handlebars.HelperOptions) {
|
||||
return (
|
||||
'GOODBYE ' +
|
||||
options.hash.cruel +
|
||||
|
@ -523,7 +523,7 @@ describe('helpers', () => {
|
|||
|
||||
it('block helpers can take an optional hash', () => {
|
||||
expectTemplate('{{#goodbye cruel="CRUEL" times=12}}world{{/goodbye}}')
|
||||
.withHelper('goodbye', function (this: any, options) {
|
||||
.withHelper('goodbye', function (this: any, options: Handlebars.HelperOptions) {
|
||||
return (
|
||||
'GOODBYE ' +
|
||||
options.hash.cruel +
|
||||
|
@ -539,7 +539,7 @@ describe('helpers', () => {
|
|||
|
||||
it('block helpers can take an optional hash with single quoted stings', () => {
|
||||
expectTemplate('{{#goodbye cruel="CRUEL" times=12}}world{{/goodbye}}')
|
||||
.withHelper('goodbye', function (this: any, options) {
|
||||
.withHelper('goodbye', function (this: any, options: Handlebars.HelperOptions) {
|
||||
return (
|
||||
'GOODBYE ' +
|
||||
options.hash.cruel +
|
||||
|
@ -582,7 +582,7 @@ describe('helpers', () => {
|
|||
it('if a context is not found, custom helperMissing is used', () => {
|
||||
expectTemplate('{{hello}} {{link_to world}}')
|
||||
.withInput({ hello: 'Hello', world: 'world' })
|
||||
.withHelper('helperMissing', function (mesg, options) {
|
||||
.withHelper('helperMissing', function (mesg, options: Handlebars.HelperOptions) {
|
||||
if (options.name === 'link_to') {
|
||||
return new Handlebars.SafeString('<a>' + mesg + '</a>');
|
||||
}
|
||||
|
@ -593,7 +593,7 @@ describe('helpers', () => {
|
|||
it('if a value is not found, custom helperMissing is used', () => {
|
||||
expectTemplate('{{hello}} {{link_to}}')
|
||||
.withInput({ hello: 'Hello', world: 'world' })
|
||||
.withHelper('helperMissing', function (options) {
|
||||
.withHelper('helperMissing', function (options: Handlebars.HelperOptions) {
|
||||
if (options.name === 'link_to') {
|
||||
return new Handlebars.SafeString('<a>winning</a>');
|
||||
}
|
||||
|
@ -788,7 +788,7 @@ describe('helpers', () => {
|
|||
|
||||
it('helpers take precedence over same-named context properties$', () => {
|
||||
expectTemplate('{{#goodbye}} {{cruel world}}{{/goodbye}}')
|
||||
.withHelper('goodbye', function (this: any, options) {
|
||||
.withHelper('goodbye', function (this: any, options: Handlebars.HelperOptions) {
|
||||
return this.goodbye.toUpperCase() + options.fn(this);
|
||||
})
|
||||
.withHelper('cruel', function (world) {
|
||||
|
@ -818,7 +818,7 @@ describe('helpers', () => {
|
|||
|
||||
it('Scoped names take precedence over block helpers', () => {
|
||||
expectTemplate('{{#goodbye}} {{cruel world}}{{/goodbye}} {{this.goodbye}}')
|
||||
.withHelper('goodbye', function (this: any, options) {
|
||||
.withHelper('goodbye', function (this: any, options: Handlebars.HelperOptions) {
|
||||
return this.goodbye.toUpperCase() + options.fn(this);
|
||||
})
|
||||
.withHelper('cruel', function (world) {
|
||||
|
@ -836,7 +836,7 @@ describe('helpers', () => {
|
|||
it('should take presedence over context values', () => {
|
||||
expectTemplate('{{#goodbyes as |value|}}{{value}}{{/goodbyes}}{{value}}')
|
||||
.withInput({ value: 'foo' })
|
||||
.withHelper('goodbyes', function (options) {
|
||||
.withHelper('goodbyes', function (options: Handlebars.HelperOptions) {
|
||||
expect(options.fn.blockParams).toEqual(1);
|
||||
return options.fn({ value: 'bar' }, { blockParams: [1, 2] });
|
||||
})
|
||||
|
@ -848,7 +848,7 @@ describe('helpers', () => {
|
|||
.withHelper('value', function () {
|
||||
return 'foo';
|
||||
})
|
||||
.withHelper('goodbyes', function (options) {
|
||||
.withHelper('goodbyes', function (options: Handlebars.HelperOptions) {
|
||||
expect(options.fn.blockParams).toEqual(1);
|
||||
return options.fn({}, { blockParams: [1, 2] });
|
||||
})
|
||||
|
@ -861,7 +861,7 @@ describe('helpers', () => {
|
|||
.withHelper('value', function () {
|
||||
return 'foo';
|
||||
})
|
||||
.withHelper('goodbyes', function (this: any, options) {
|
||||
.withHelper('goodbyes', function (this: any, options: Handlebars.HelperOptions) {
|
||||
expect(options.fn.blockParams).toEqual(1);
|
||||
return options.fn(this, { blockParams: [1, 2] });
|
||||
})
|
||||
|
@ -879,7 +879,7 @@ describe('helpers', () => {
|
|||
}
|
||||
)
|
||||
.withInput({ value: 'foo' })
|
||||
.withHelper('goodbyes', function (options) {
|
||||
.withHelper('goodbyes', function (options: Handlebars.HelperOptions) {
|
||||
return options.fn(
|
||||
{ value: 'bar' },
|
||||
{
|
||||
|
@ -893,7 +893,7 @@ describe('helpers', () => {
|
|||
it('should allow block params on chained helpers', () => {
|
||||
expectTemplate('{{#if bar}}{{else goodbyes as |value|}}{{value}}{{/if}}{{value}}')
|
||||
.withInput({ value: 'foo' })
|
||||
.withHelper('goodbyes', function (options) {
|
||||
.withHelper('goodbyes', function (options: Handlebars.HelperOptions) {
|
||||
expect(options.fn.blockParams).toEqual(1);
|
||||
return options.fn({ value: 'bar' }, { blockParams: [1, 2] });
|
||||
})
|
||||
|
@ -942,9 +942,12 @@ describe('helpers', () => {
|
|||
describe('the lookupProperty-option', () => {
|
||||
it('should be passed to custom helpers', () => {
|
||||
expectTemplate('{{testHelper}}')
|
||||
.withHelper('testHelper', function testHelper(this: any, options) {
|
||||
return options.lookupProperty(this, 'testProperty');
|
||||
})
|
||||
.withHelper(
|
||||
'testHelper',
|
||||
function testHelper(this: any, options: Handlebars.HelperOptions) {
|
||||
return options.lookupProperty(this, 'testProperty');
|
||||
}
|
||||
)
|
||||
.withInput({ testProperty: 'abc' })
|
||||
.toCompileTo('abc');
|
||||
});
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
* See `packages/kbn-handlebars/LICENSE` for more information.
|
||||
*/
|
||||
|
||||
import Handlebars from '../..';
|
||||
import { expectTemplate } from '../__jest__/test_bench';
|
||||
|
||||
describe('Regressions', () => {
|
||||
|
@ -237,7 +238,7 @@ describe('Regressions', () => {
|
|||
)
|
||||
.withInput({ array: [1], name: 'John' })
|
||||
.withHelpers({
|
||||
myif(conditional, options) {
|
||||
myif(conditional, options: Handlebars.HelperOptions) {
|
||||
if (conditional) {
|
||||
return options.fn(this);
|
||||
} else {
|
||||
|
@ -261,7 +262,7 @@ describe('Regressions', () => {
|
|||
expectTemplate('{{helpa length="foo"}}')
|
||||
.withInput({ array: [1], name: 'John' })
|
||||
.withHelpers({
|
||||
helpa(options) {
|
||||
helpa(options: Handlebars.HelperOptions) {
|
||||
return options.hash.length;
|
||||
},
|
||||
})
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue