mirror of
https://github.com/elastic/kibana.git
synced 2025-06-28 03:01:21 -04:00
[@kbn/handlebars] Refactor types (#150520)
This commit is contained in:
parent
50b83014a3
commit
77ed48a75a
14 changed files with 304 additions and 216 deletions
|
@ -11,6 +11,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import Handlebars from '.';
|
import Handlebars from '.';
|
||||||
|
import type { HelperOptions, TemplateDelegate } from './src/types';
|
||||||
import { expectTemplate, forEachCompileFunctionName } from './src/__jest__/test_bench';
|
import { expectTemplate, forEachCompileFunctionName } from './src/__jest__/test_bench';
|
||||||
|
|
||||||
it('Handlebars.create', () => {
|
it('Handlebars.create', () => {
|
||||||
|
@ -419,7 +420,7 @@ describe('blocks', () => {
|
||||||
.withInput({ me: 'my' })
|
.withInput({ me: 'my' })
|
||||||
.withDecorator(
|
.withDecorator(
|
||||||
'decorator',
|
'decorator',
|
||||||
(fn): Handlebars.TemplateDelegate =>
|
(fn): TemplateDelegate =>
|
||||||
(context, options) => {
|
(context, options) => {
|
||||||
expect(context).toMatchInlineSnapshot(`
|
expect(context).toMatchInlineSnapshot(`
|
||||||
Object {
|
Object {
|
||||||
|
@ -446,7 +447,7 @@ describe('blocks', () => {
|
||||||
.withInput({ arr: ['my'] })
|
.withInput({ arr: ['my'] })
|
||||||
.withDecorator(
|
.withDecorator(
|
||||||
'decorator',
|
'decorator',
|
||||||
(fn): Handlebars.TemplateDelegate =>
|
(fn): TemplateDelegate =>
|
||||||
(context, options) => {
|
(context, options) => {
|
||||||
expect(context).toMatchInlineSnapshot(`"my"`);
|
expect(context).toMatchInlineSnapshot(`"my"`);
|
||||||
expect(options).toMatchInlineSnapshot(`
|
expect(options).toMatchInlineSnapshot(`
|
||||||
|
@ -483,12 +484,12 @@ describe('blocks', () => {
|
||||||
|
|
||||||
it('decorator nested inside of custom helper', () => {
|
it('decorator nested inside of custom helper', () => {
|
||||||
expectTemplate('{{#helper}}{{*decorator}}world{{/helper}}')
|
expectTemplate('{{#helper}}{{*decorator}}world{{/helper}}')
|
||||||
.withHelper('helper', function (options: Handlebars.HelperOptions) {
|
.withHelper('helper', function (options: HelperOptions) {
|
||||||
return options.fn('my', { foo: 'bar' } as any);
|
return options.fn('my', { foo: 'bar' } as any);
|
||||||
})
|
})
|
||||||
.withDecorator(
|
.withDecorator(
|
||||||
'decorator',
|
'decorator',
|
||||||
(fn): Handlebars.TemplateDelegate =>
|
(fn): TemplateDelegate =>
|
||||||
(context, options) => {
|
(context, options) => {
|
||||||
expect(context).toMatchInlineSnapshot(`"my"`);
|
expect(context).toMatchInlineSnapshot(`"my"`);
|
||||||
expect(options).toMatchInlineSnapshot(`
|
expect(options).toMatchInlineSnapshot(`
|
||||||
|
@ -519,7 +520,7 @@ describe('blocks', () => {
|
||||||
})
|
})
|
||||||
.withDecorator('decorator', (fn) => {
|
.withDecorator('decorator', (fn) => {
|
||||||
const decoratorCallOrder = ++decoratorCall;
|
const decoratorCallOrder = ++decoratorCall;
|
||||||
const ret: Handlebars.TemplateDelegate = () => {
|
const ret: TemplateDelegate = () => {
|
||||||
const progCallOrder = ++progCall;
|
const progCallOrder = ++progCall;
|
||||||
return `(decorator: ${decoratorCallOrder}, prog: ${progCallOrder}, fn: "${fn()}")`;
|
return `(decorator: ${decoratorCallOrder}, prog: ${progCallOrder}, fn: "${fn()}")`;
|
||||||
};
|
};
|
||||||
|
|
|
@ -24,7 +24,10 @@ export default Handlebars;
|
||||||
export const compileFnName: 'compile' | 'compileAST' = allowUnsafeEval() ? 'compile' : 'compileAST';
|
export const compileFnName: 'compile' | 'compileAST' = allowUnsafeEval() ? 'compile' : 'compileAST';
|
||||||
|
|
||||||
export type {
|
export type {
|
||||||
DecoratorFunction,
|
CompileOptions,
|
||||||
ExtendedCompileOptions,
|
RuntimeOptions,
|
||||||
ExtendedRuntimeOptions,
|
HelperDelegate,
|
||||||
|
TemplateDelegate,
|
||||||
|
DecoratorDelegate,
|
||||||
|
HelperOptions,
|
||||||
} from './src/types';
|
} from './src/types';
|
||||||
|
|
|
@ -3,13 +3,13 @@
|
||||||
* See `packages/kbn-handlebars/LICENSE` for more information.
|
* See `packages/kbn-handlebars/LICENSE` for more information.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import Handlebars from '../..';
|
import Handlebars, {
|
||||||
import type {
|
type CompileOptions,
|
||||||
DecoratorFunction,
|
type DecoratorDelegate,
|
||||||
DecoratorsHash,
|
type HelperDelegate,
|
||||||
ExtendedCompileOptions,
|
type RuntimeOptions,
|
||||||
ExtendedRuntimeOptions,
|
} from '../..';
|
||||||
} from '../types';
|
import type { DecoratorsHash, HelpersHash, PartialsHash, Template } from '../types';
|
||||||
|
|
||||||
type CompileFns = 'compile' | 'compileAST';
|
type CompileFns = 'compile' | 'compileAST';
|
||||||
const compileFns: CompileFns[] = ['compile', 'compileAST'];
|
const compileFns: CompileFns[] = ['compile', 'compileAST'];
|
||||||
|
@ -40,10 +40,10 @@ export function forEachCompileFunctionName(
|
||||||
class HandlebarsTestBench {
|
class HandlebarsTestBench {
|
||||||
private template: string;
|
private template: string;
|
||||||
private options: TestOptions;
|
private options: TestOptions;
|
||||||
private compileOptions?: ExtendedCompileOptions;
|
private compileOptions?: CompileOptions;
|
||||||
private runtimeOptions?: ExtendedRuntimeOptions;
|
private runtimeOptions?: RuntimeOptions;
|
||||||
private helpers: { [name: string]: Handlebars.HelperDelegate | undefined } = {};
|
private helpers: HelpersHash = {};
|
||||||
private partials: { [name: string]: Handlebars.Template } = {};
|
private partials: PartialsHash = {};
|
||||||
private decorators: DecoratorsHash = {};
|
private decorators: DecoratorsHash = {};
|
||||||
private input: any = {};
|
private input: any = {};
|
||||||
|
|
||||||
|
@ -52,12 +52,12 @@ class HandlebarsTestBench {
|
||||||
this.options = options;
|
this.options = options;
|
||||||
}
|
}
|
||||||
|
|
||||||
withCompileOptions(compileOptions?: ExtendedCompileOptions) {
|
withCompileOptions(compileOptions?: CompileOptions) {
|
||||||
this.compileOptions = compileOptions;
|
this.compileOptions = compileOptions;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
withRuntimeOptions(runtimeOptions?: ExtendedRuntimeOptions) {
|
withRuntimeOptions(runtimeOptions?: RuntimeOptions) {
|
||||||
this.runtimeOptions = runtimeOptions;
|
this.runtimeOptions = runtimeOptions;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
@ -67,36 +67,36 @@ class HandlebarsTestBench {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
withHelper<F extends Handlebars.HelperDelegate>(name: string, helper?: F) {
|
withHelper<F extends HelperDelegate>(name: string, helper: F) {
|
||||||
this.helpers[name] = helper;
|
this.helpers[name] = helper;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
withHelpers<F extends Handlebars.HelperDelegate>(helperFunctions: { [name: string]: F }) {
|
withHelpers<F extends HelperDelegate>(helperFunctions: Record<string, F>) {
|
||||||
for (const [name, helper] of Object.entries(helperFunctions)) {
|
for (const [name, helper] of Object.entries(helperFunctions)) {
|
||||||
this.withHelper(name, helper);
|
this.withHelper(name, helper);
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
withPartial(name: string | number, partial: Handlebars.Template) {
|
withPartial(name: string | number, partial: Template) {
|
||||||
this.partials[name] = partial;
|
this.partials[name] = partial;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
withPartials(partials: { [name: string]: Handlebars.Template }) {
|
withPartials(partials: Record<string, Template>) {
|
||||||
for (const [name, partial] of Object.entries(partials)) {
|
for (const [name, partial] of Object.entries(partials)) {
|
||||||
this.withPartial(name, partial);
|
this.withPartial(name, partial);
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
withDecorator(name: string, decoratorFunction: DecoratorFunction) {
|
withDecorator(name: string, decoratorFunction: DecoratorDelegate) {
|
||||||
this.decorators[name] = decoratorFunction;
|
this.decorators[name] = decoratorFunction;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
withDecorators(decoratorFunctions: { [key: string]: DecoratorFunction }) {
|
withDecorators(decoratorFunctions: Record<string, DecoratorDelegate>) {
|
||||||
for (const [name, decoratorFunction] of Object.entries(decoratorFunctions)) {
|
for (const [name, decoratorFunction] of Object.entries(decoratorFunctions)) {
|
||||||
this.withDecorator(name, decoratorFunction);
|
this.withDecorator(name, decoratorFunction);
|
||||||
}
|
}
|
||||||
|
@ -154,9 +154,9 @@ class HandlebarsTestBench {
|
||||||
private compileAndExecuteEval() {
|
private compileAndExecuteEval() {
|
||||||
const renderEval = this.compileEval();
|
const renderEval = this.compileEval();
|
||||||
|
|
||||||
const runtimeOptions: ExtendedRuntimeOptions = {
|
const runtimeOptions: RuntimeOptions = {
|
||||||
helpers: this.helpers as Record<string, Function>,
|
helpers: this.helpers,
|
||||||
partials: this.partials as Record<string, HandlebarsTemplateDelegate>,
|
partials: this.partials,
|
||||||
decorators: this.decorators,
|
decorators: this.decorators,
|
||||||
...this.runtimeOptions,
|
...this.runtimeOptions,
|
||||||
};
|
};
|
||||||
|
@ -169,9 +169,9 @@ class HandlebarsTestBench {
|
||||||
private compileAndExecuteAST() {
|
private compileAndExecuteAST() {
|
||||||
const renderAST = this.compileAST();
|
const renderAST = this.compileAST();
|
||||||
|
|
||||||
const runtimeOptions: ExtendedRuntimeOptions = {
|
const runtimeOptions: RuntimeOptions = {
|
||||||
helpers: this.helpers as Record<string, Function>,
|
helpers: this.helpers,
|
||||||
partials: this.partials as Record<string, HandlebarsTemplateDelegate>,
|
partials: this.partials,
|
||||||
decorators: this.decorators,
|
decorators: this.decorators,
|
||||||
...this.runtimeOptions,
|
...this.runtimeOptions,
|
||||||
};
|
};
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
// https://www.typescriptlang.org/docs/handbook/modules.html#export--and-import--require
|
// https://www.typescriptlang.org/docs/handbook/modules.html#export--and-import--require
|
||||||
import Handlebars from 'handlebars';
|
import Handlebars from 'handlebars';
|
||||||
|
|
||||||
import type { ExtendedCompileOptions, ExtendedRuntimeOptions } from './types';
|
import type { CompileOptions, RuntimeOptions, TemplateDelegate } from './types';
|
||||||
import { ElasticHandlebarsVisitor } from './visitor';
|
import { ElasticHandlebarsVisitor } from './visitor';
|
||||||
|
|
||||||
const originalCreate = Handlebars.create;
|
const originalCreate = Handlebars.create;
|
||||||
|
@ -30,15 +30,10 @@ Handlebars.create = function (): typeof Handlebars {
|
||||||
return SandboxedHandlebars;
|
return SandboxedHandlebars;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Compiles the given Handlbars template without the use of `eval`.
|
|
||||||
*
|
|
||||||
* @returns A render function with the same API as the return value from the regular Handlebars `compile` function.
|
|
||||||
*/
|
|
||||||
Handlebars.compileAST = function (
|
Handlebars.compileAST = function (
|
||||||
input: string | hbs.AST.Program,
|
input: string | hbs.AST.Program,
|
||||||
options?: ExtendedCompileOptions
|
options?: CompileOptions
|
||||||
) {
|
): TemplateDelegate {
|
||||||
if (input == null || (typeof input !== 'string' && input.type !== 'Program')) {
|
if (input == null || (typeof input !== 'string' && input.type !== 'Program')) {
|
||||||
throw new Handlebars.Exception(
|
throw new Handlebars.Exception(
|
||||||
`You must pass a string or Handlebars AST to Handlebars.compileAST. You passed ${input}`
|
`You must pass a string or Handlebars AST to Handlebars.compileAST. You passed ${input}`
|
||||||
|
@ -48,6 +43,5 @@ Handlebars.compileAST = function (
|
||||||
// If `Handlebars.compileAST` is reassigned, `this` will be undefined.
|
// If `Handlebars.compileAST` is reassigned, `this` will be undefined.
|
||||||
const visitor = new ElasticHandlebarsVisitor(this ?? Handlebars, input, options);
|
const visitor = new ElasticHandlebarsVisitor(this ?? Handlebars, input, options);
|
||||||
|
|
||||||
return (context: any, runtimeOptions?: ExtendedRuntimeOptions) =>
|
return (context: any, runtimeOptions?: RuntimeOptions) => visitor.render(context, runtimeOptions);
|
||||||
visitor.render(context, runtimeOptions);
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
* See `packages/kbn-handlebars/LICENSE` for more information.
|
* See `packages/kbn-handlebars/LICENSE` for more information.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import Handlebars from '../..';
|
import Handlebars, { type HelperOptions } from '../..';
|
||||||
import { expectTemplate } from '../__jest__/test_bench';
|
import { expectTemplate } from '../__jest__/test_bench';
|
||||||
|
|
||||||
describe('blocks', () => {
|
describe('blocks', () => {
|
||||||
|
@ -200,7 +200,7 @@ describe('blocks', () => {
|
||||||
describe('decorators', () => {
|
describe('decorators', () => {
|
||||||
it('should apply mustache decorators', () => {
|
it('should apply mustache decorators', () => {
|
||||||
expectTemplate('{{#helper}}{{*decorator}}{{/helper}}')
|
expectTemplate('{{#helper}}{{*decorator}}{{/helper}}')
|
||||||
.withHelper('helper', function (options: Handlebars.HelperOptions) {
|
.withHelper('helper', function (options: HelperOptions) {
|
||||||
return (options.fn as any).run;
|
return (options.fn as any).run;
|
||||||
})
|
})
|
||||||
.withDecorator('decorator', function (fn) {
|
.withDecorator('decorator', function (fn) {
|
||||||
|
@ -212,7 +212,7 @@ describe('blocks', () => {
|
||||||
|
|
||||||
it('should apply allow undefined return', () => {
|
it('should apply allow undefined return', () => {
|
||||||
expectTemplate('{{#helper}}{{*decorator}}suc{{/helper}}')
|
expectTemplate('{{#helper}}{{*decorator}}suc{{/helper}}')
|
||||||
.withHelper('helper', function (options: Handlebars.HelperOptions) {
|
.withHelper('helper', function (options: HelperOptions) {
|
||||||
return options.fn() + (options.fn as any).run;
|
return options.fn() + (options.fn as any).run;
|
||||||
})
|
})
|
||||||
.withDecorator('decorator', function (fn) {
|
.withDecorator('decorator', function (fn) {
|
||||||
|
@ -223,7 +223,7 @@ describe('blocks', () => {
|
||||||
|
|
||||||
it('should apply block decorators', () => {
|
it('should apply block decorators', () => {
|
||||||
expectTemplate('{{#helper}}{{#*decorator}}success{{/decorator}}{{/helper}}')
|
expectTemplate('{{#helper}}{{#*decorator}}success{{/decorator}}{{/helper}}')
|
||||||
.withHelper('helper', function (options: Handlebars.HelperOptions) {
|
.withHelper('helper', function (options: HelperOptions) {
|
||||||
return (options.fn as any).run;
|
return (options.fn as any).run;
|
||||||
})
|
})
|
||||||
.withDecorator('decorator', function (fn, props, container, options) {
|
.withDecorator('decorator', function (fn, props, container, options) {
|
||||||
|
@ -237,7 +237,7 @@ describe('blocks', () => {
|
||||||
expectTemplate(
|
expectTemplate(
|
||||||
'{{#helper}}{{#*decorator}}{{#*nested}}suc{{/nested}}cess{{/decorator}}{{/helper}}'
|
'{{#helper}}{{#*decorator}}{{#*nested}}suc{{/nested}}cess{{/decorator}}{{/helper}}'
|
||||||
)
|
)
|
||||||
.withHelper('helper', function (options: Handlebars.HelperOptions) {
|
.withHelper('helper', function (options: HelperOptions) {
|
||||||
return (options.fn as any).run;
|
return (options.fn as any).run;
|
||||||
})
|
})
|
||||||
.withDecorators({
|
.withDecorators({
|
||||||
|
@ -256,7 +256,7 @@ describe('blocks', () => {
|
||||||
expectTemplate(
|
expectTemplate(
|
||||||
'{{#helper}}{{#*decorator}}suc{{/decorator}}{{#*decorator}}cess{{/decorator}}{{/helper}}'
|
'{{#helper}}{{#*decorator}}suc{{/decorator}}{{#*decorator}}cess{{/decorator}}{{/helper}}'
|
||||||
)
|
)
|
||||||
.withHelper('helper', function (options: Handlebars.HelperOptions) {
|
.withHelper('helper', function (options: HelperOptions) {
|
||||||
return (options.fn as any).run;
|
return (options.fn as any).run;
|
||||||
})
|
})
|
||||||
.withDecorator('decorator', function (fn, props, container, options) {
|
.withDecorator('decorator', function (fn, props, container, options) {
|
||||||
|
@ -268,7 +268,7 @@ describe('blocks', () => {
|
||||||
|
|
||||||
it('should access parent variables', () => {
|
it('should access parent variables', () => {
|
||||||
expectTemplate('{{#helper}}{{*decorator foo}}{{/helper}}')
|
expectTemplate('{{#helper}}{{*decorator foo}}{{/helper}}')
|
||||||
.withHelper('helper', function (options: Handlebars.HelperOptions) {
|
.withHelper('helper', function (options: HelperOptions) {
|
||||||
return (options.fn as any).run;
|
return (options.fn as any).run;
|
||||||
})
|
})
|
||||||
.withDecorator('decorator', function (fn, props, container, options) {
|
.withDecorator('decorator', function (fn, props, container, options) {
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
* See `packages/kbn-handlebars/LICENSE` for more information.
|
* See `packages/kbn-handlebars/LICENSE` for more information.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import Handlebars from '../..';
|
import Handlebars, { type HelperOptions } from '../..';
|
||||||
import { expectTemplate } from '../__jest__/test_bench';
|
import { expectTemplate } from '../__jest__/test_bench';
|
||||||
|
|
||||||
describe('data', () => {
|
describe('data', () => {
|
||||||
|
@ -30,7 +30,7 @@ describe('data', () => {
|
||||||
global.kbnHandlebarsEnv = Handlebars.create();
|
global.kbnHandlebarsEnv = Handlebars.create();
|
||||||
const helpers = Handlebars.createFrame(kbnHandlebarsEnv!.helpers);
|
const helpers = Handlebars.createFrame(kbnHandlebarsEnv!.helpers);
|
||||||
|
|
||||||
helpers.let = function (options: Handlebars.HelperOptions) {
|
helpers.let = function (options: HelperOptions) {
|
||||||
const frame = Handlebars.createFrame(options.data);
|
const frame = Handlebars.createFrame(options.data);
|
||||||
|
|
||||||
for (const prop in options.hash) {
|
for (const prop in options.hash) {
|
||||||
|
@ -138,7 +138,7 @@ describe('data', () => {
|
||||||
expectTemplate('{{>myPartial}}')
|
expectTemplate('{{>myPartial}}')
|
||||||
.withCompileOptions({ data: true })
|
.withCompileOptions({ data: true })
|
||||||
.withPartial('myPartial', '{{hello}}')
|
.withPartial('myPartial', '{{hello}}')
|
||||||
.withHelper('hello', function (this: any, options: Handlebars.HelperOptions) {
|
.withHelper('hello', function (this: any, options: HelperOptions) {
|
||||||
return options.data.adjective + ' ' + this.noun;
|
return options.data.adjective + ' ' + this.noun;
|
||||||
})
|
})
|
||||||
.withInput({ noun: 'cat' })
|
.withInput({ noun: 'cat' })
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
* See `packages/kbn-handlebars/LICENSE` for more information.
|
* See `packages/kbn-handlebars/LICENSE` for more information.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import Handlebars from '../..';
|
import Handlebars, { type HelperOptions } from '../..';
|
||||||
import { expectTemplate } from '../__jest__/test_bench';
|
import { expectTemplate } from '../__jest__/test_bench';
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
@ -32,7 +32,7 @@ describe('helpers', () => {
|
||||||
it('helper for raw block gets raw content', () => {
|
it('helper for raw block gets raw content', () => {
|
||||||
expectTemplate('{{{{raw}}}} {{test}} {{{{/raw}}}}')
|
expectTemplate('{{{{raw}}}} {{test}} {{{{/raw}}}}')
|
||||||
.withInput({ test: 'hello' })
|
.withInput({ test: 'hello' })
|
||||||
.withHelper('raw', function (options: Handlebars.HelperOptions) {
|
.withHelper('raw', function (options: HelperOptions) {
|
||||||
return options.fn();
|
return options.fn();
|
||||||
})
|
})
|
||||||
.toCompileTo(' {{test}} ');
|
.toCompileTo(' {{test}} ');
|
||||||
|
@ -41,7 +41,7 @@ describe('helpers', () => {
|
||||||
it('helper for raw block gets parameters', () => {
|
it('helper for raw block gets parameters', () => {
|
||||||
expectTemplate('{{{{raw 1 2 3}}}} {{test}} {{{{/raw}}}}')
|
expectTemplate('{{{{raw 1 2 3}}}} {{test}} {{{{/raw}}}}')
|
||||||
.withInput({ test: 'hello' })
|
.withInput({ test: 'hello' })
|
||||||
.withHelper('raw', function (a, b, c, options: Handlebars.HelperOptions) {
|
.withHelper('raw', function (a, b, c, options: HelperOptions) {
|
||||||
const ret = options.fn() + a + b + c;
|
const ret = options.fn() + a + b + c;
|
||||||
return ret;
|
return ret;
|
||||||
})
|
})
|
||||||
|
@ -51,7 +51,7 @@ describe('helpers', () => {
|
||||||
describe('raw block parsing (with identity helper-function)', () => {
|
describe('raw block parsing (with identity helper-function)', () => {
|
||||||
function runWithIdentityHelper(template: string, expected: string) {
|
function runWithIdentityHelper(template: string, expected: string) {
|
||||||
expectTemplate(template)
|
expectTemplate(template)
|
||||||
.withHelper('identity', function (options: Handlebars.HelperOptions) {
|
.withHelper('identity', function (options: HelperOptions) {
|
||||||
return options.fn();
|
return options.fn();
|
||||||
})
|
})
|
||||||
.toCompileTo(expected);
|
.toCompileTo(expected);
|
||||||
|
@ -95,7 +95,7 @@ describe('helpers', () => {
|
||||||
it('helper block with identical context', () => {
|
it('helper block with identical context', () => {
|
||||||
expectTemplate('{{#goodbyes}}{{name}}{{/goodbyes}}')
|
expectTemplate('{{#goodbyes}}{{name}}{{/goodbyes}}')
|
||||||
.withInput({ name: 'Alan' })
|
.withInput({ name: 'Alan' })
|
||||||
.withHelper('goodbyes', function (this: any, options: Handlebars.HelperOptions) {
|
.withHelper('goodbyes', function (this: any, options: HelperOptions) {
|
||||||
let out = '';
|
let out = '';
|
||||||
const byes = ['Goodbye', 'goodbye', 'GOODBYE'];
|
const byes = ['Goodbye', 'goodbye', 'GOODBYE'];
|
||||||
for (let i = 0, j = byes.length; i < j; i++) {
|
for (let i = 0, j = byes.length; i < j; i++) {
|
||||||
|
@ -109,7 +109,7 @@ describe('helpers', () => {
|
||||||
it('helper block with complex lookup expression', () => {
|
it('helper block with complex lookup expression', () => {
|
||||||
expectTemplate('{{#goodbyes}}{{../name}}{{/goodbyes}}')
|
expectTemplate('{{#goodbyes}}{{../name}}{{/goodbyes}}')
|
||||||
.withInput({ name: 'Alan' })
|
.withInput({ name: 'Alan' })
|
||||||
.withHelper('goodbyes', function (options: Handlebars.HelperOptions) {
|
.withHelper('goodbyes', function (options: HelperOptions) {
|
||||||
let out = '';
|
let out = '';
|
||||||
const byes = ['Goodbye', 'goodbye', 'GOODBYE'];
|
const byes = ['Goodbye', 'goodbye', 'GOODBYE'];
|
||||||
for (let i = 0, j = byes.length; i < j; i++) {
|
for (let i = 0, j = byes.length; i < j; i++) {
|
||||||
|
@ -126,7 +126,7 @@ describe('helpers', () => {
|
||||||
prefix: '/root',
|
prefix: '/root',
|
||||||
goodbyes: [{ text: 'Goodbye', url: 'goodbye' }],
|
goodbyes: [{ text: 'Goodbye', url: 'goodbye' }],
|
||||||
})
|
})
|
||||||
.withHelper('link', function (this: any, prefix, options: Handlebars.HelperOptions) {
|
.withHelper('link', function (this: any, prefix, options: HelperOptions) {
|
||||||
return '<a href="' + prefix + '/' + this.url + '">' + options.fn(this) + '</a>';
|
return '<a href="' + prefix + '/' + this.url + '">' + options.fn(this) + '</a>';
|
||||||
})
|
})
|
||||||
.toCompileTo('<a href="/root/goodbye">Goodbye</a>');
|
.toCompileTo('<a href="/root/goodbye">Goodbye</a>');
|
||||||
|
@ -138,7 +138,7 @@ describe('helpers', () => {
|
||||||
prefix: '/root',
|
prefix: '/root',
|
||||||
goodbyes: [{ text: 'Goodbye', url: 'goodbye' }],
|
goodbyes: [{ text: 'Goodbye', url: 'goodbye' }],
|
||||||
})
|
})
|
||||||
.withHelper('link', function (this: any, prefix, options: Handlebars.HelperOptions) {
|
.withHelper('link', function (this: any, prefix, options: HelperOptions) {
|
||||||
return '<a href="' + prefix + '/' + this.url + '">' + options.fn(this) + '</a>';
|
return '<a href="' + prefix + '/' + this.url + '">' + options.fn(this) + '</a>';
|
||||||
})
|
})
|
||||||
.toCompileTo('<a href="/root/goodbye">Goodbye</a>');
|
.toCompileTo('<a href="/root/goodbye">Goodbye</a>');
|
||||||
|
@ -161,7 +161,7 @@ describe('helpers', () => {
|
||||||
it('block helper', () => {
|
it('block helper', () => {
|
||||||
expectTemplate('{{#goodbyes}}{{text}}! {{/goodbyes}}cruel {{world}}!')
|
expectTemplate('{{#goodbyes}}{{text}}! {{/goodbyes}}cruel {{world}}!')
|
||||||
.withInput({ world: 'world' })
|
.withInput({ world: 'world' })
|
||||||
.withHelper('goodbyes', function (options: Handlebars.HelperOptions) {
|
.withHelper('goodbyes', function (options: HelperOptions) {
|
||||||
return options.fn({ text: 'GOODBYE' });
|
return options.fn({ text: 'GOODBYE' });
|
||||||
})
|
})
|
||||||
.toCompileTo('GOODBYE! cruel world!');
|
.toCompileTo('GOODBYE! cruel world!');
|
||||||
|
@ -170,14 +170,14 @@ describe('helpers', () => {
|
||||||
it('block helper staying in the same context', () => {
|
it('block helper staying in the same context', () => {
|
||||||
expectTemplate('{{#form}}<p>{{name}}</p>{{/form}}')
|
expectTemplate('{{#form}}<p>{{name}}</p>{{/form}}')
|
||||||
.withInput({ name: 'Yehuda' })
|
.withInput({ name: 'Yehuda' })
|
||||||
.withHelper('form', function (this: any, options: Handlebars.HelperOptions) {
|
.withHelper('form', function (this: any, options: HelperOptions) {
|
||||||
return '<form>' + options.fn(this) + '</form>';
|
return '<form>' + options.fn(this) + '</form>';
|
||||||
})
|
})
|
||||||
.toCompileTo('<form><p>Yehuda</p></form>');
|
.toCompileTo('<form><p>Yehuda</p></form>');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('block helper should have context in this', () => {
|
it('block helper should have context in this', () => {
|
||||||
function link(this: any, options: Handlebars.HelperOptions) {
|
function link(this: any, options: HelperOptions) {
|
||||||
return '<a href="/people/' + this.id + '">' + options.fn(this) + '</a>';
|
return '<a href="/people/' + this.id + '">' + options.fn(this) + '</a>';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -201,7 +201,7 @@ describe('helpers', () => {
|
||||||
it('block helper passing a new context', () => {
|
it('block helper passing a new context', () => {
|
||||||
expectTemplate('{{#form yehuda}}<p>{{name}}</p>{{/form}}')
|
expectTemplate('{{#form yehuda}}<p>{{name}}</p>{{/form}}')
|
||||||
.withInput({ yehuda: { name: 'Yehuda' } })
|
.withInput({ yehuda: { name: 'Yehuda' } })
|
||||||
.withHelper('form', function (context, options: Handlebars.HelperOptions) {
|
.withHelper('form', function (context, options: HelperOptions) {
|
||||||
return '<form>' + options.fn(context) + '</form>';
|
return '<form>' + options.fn(context) + '</form>';
|
||||||
})
|
})
|
||||||
.toCompileTo('<form><p>Yehuda</p></form>');
|
.toCompileTo('<form><p>Yehuda</p></form>');
|
||||||
|
@ -210,7 +210,7 @@ describe('helpers', () => {
|
||||||
it('block helper passing a complex path context', () => {
|
it('block helper passing a complex path context', () => {
|
||||||
expectTemplate('{{#form yehuda/cat}}<p>{{name}}</p>{{/form}}')
|
expectTemplate('{{#form yehuda/cat}}<p>{{name}}</p>{{/form}}')
|
||||||
.withInput({ yehuda: { name: 'Yehuda', cat: { name: 'Harold' } } })
|
.withInput({ yehuda: { name: 'Yehuda', cat: { name: 'Harold' } } })
|
||||||
.withHelper('form', function (context, options: Handlebars.HelperOptions) {
|
.withHelper('form', function (context, options: HelperOptions) {
|
||||||
return '<form>' + options.fn(context) + '</form>';
|
return '<form>' + options.fn(context) + '</form>';
|
||||||
})
|
})
|
||||||
.toCompileTo('<form><p>Harold</p></form>');
|
.toCompileTo('<form><p>Harold</p></form>');
|
||||||
|
@ -221,10 +221,10 @@ describe('helpers', () => {
|
||||||
.withInput({
|
.withInput({
|
||||||
yehuda: { name: 'Yehuda' },
|
yehuda: { name: 'Yehuda' },
|
||||||
})
|
})
|
||||||
.withHelper('link', function (this: any, options: Handlebars.HelperOptions) {
|
.withHelper('link', function (this: any, options: HelperOptions) {
|
||||||
return '<a href="' + this.name + '">' + options.fn(this) + '</a>';
|
return '<a href="' + this.name + '">' + options.fn(this) + '</a>';
|
||||||
})
|
})
|
||||||
.withHelper('form', function (context, options: Handlebars.HelperOptions) {
|
.withHelper('form', function (context, options: HelperOptions) {
|
||||||
return '<form>' + options.fn(context) + '</form>';
|
return '<form>' + options.fn(context) + '</form>';
|
||||||
})
|
})
|
||||||
.toCompileTo('<form><p>Yehuda</p><a href="Yehuda">Hello</a></form>');
|
.toCompileTo('<form><p>Yehuda</p><a href="Yehuda">Hello</a></form>');
|
||||||
|
@ -232,7 +232,7 @@ describe('helpers', () => {
|
||||||
|
|
||||||
it('block helper inverted sections', () => {
|
it('block helper inverted sections', () => {
|
||||||
const string = "{{#list people}}{{name}}{{^}}<em>Nobody's here</em>{{/list}}";
|
const string = "{{#list people}}{{name}}{{^}}<em>Nobody's here</em>{{/list}}";
|
||||||
function list(this: any, context: any, options: Handlebars.HelperOptions) {
|
function list(this: any, context: any, options: HelperOptions) {
|
||||||
if (context.length > 0) {
|
if (context.length > 0) {
|
||||||
let out = '<ul>';
|
let out = '<ul>';
|
||||||
for (let i = 0, j = context.length; i < j; i++) {
|
for (let i = 0, j = context.length; i < j; i++) {
|
||||||
|
@ -477,7 +477,7 @@ describe('helpers', () => {
|
||||||
it('block multi-params work', () => {
|
it('block multi-params work', () => {
|
||||||
expectTemplate('Message: {{#goodbye cruel world}}{{greeting}} {{adj}} {{noun}}{{/goodbye}}')
|
expectTemplate('Message: {{#goodbye cruel world}}{{greeting}} {{adj}} {{noun}}{{/goodbye}}')
|
||||||
.withInput({ cruel: 'cruel', world: 'world' })
|
.withInput({ cruel: 'cruel', world: 'world' })
|
||||||
.withHelper('goodbye', function (cruel, world, options: Handlebars.HelperOptions) {
|
.withHelper('goodbye', function (cruel, world, options: HelperOptions) {
|
||||||
return options.fn({ greeting: 'Goodbye', adj: cruel, noun: world });
|
return options.fn({ greeting: 'Goodbye', adj: cruel, noun: world });
|
||||||
})
|
})
|
||||||
.toCompileTo('Message: Goodbye cruel world');
|
.toCompileTo('Message: Goodbye cruel world');
|
||||||
|
@ -487,7 +487,7 @@ describe('helpers', () => {
|
||||||
describe('hash', () => {
|
describe('hash', () => {
|
||||||
it('helpers can take an optional hash', () => {
|
it('helpers can take an optional hash', () => {
|
||||||
expectTemplate('{{goodbye cruel="CRUEL" world="WORLD" times=12}}')
|
expectTemplate('{{goodbye cruel="CRUEL" world="WORLD" times=12}}')
|
||||||
.withHelper('goodbye', function (options: Handlebars.HelperOptions) {
|
.withHelper('goodbye', function (options: HelperOptions) {
|
||||||
return (
|
return (
|
||||||
'GOODBYE ' +
|
'GOODBYE ' +
|
||||||
options.hash.cruel +
|
options.hash.cruel +
|
||||||
|
@ -502,7 +502,7 @@ describe('helpers', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('helpers can take an optional hash with booleans', () => {
|
it('helpers can take an optional hash with booleans', () => {
|
||||||
function goodbye(options: Handlebars.HelperOptions) {
|
function goodbye(options: HelperOptions) {
|
||||||
if (options.hash.print === true) {
|
if (options.hash.print === true) {
|
||||||
return 'GOODBYE ' + options.hash.cruel + ' ' + options.hash.world;
|
return 'GOODBYE ' + options.hash.cruel + ' ' + options.hash.world;
|
||||||
} else if (options.hash.print === false) {
|
} else if (options.hash.print === false) {
|
||||||
|
@ -523,7 +523,7 @@ describe('helpers', () => {
|
||||||
|
|
||||||
it('block helpers can take an optional hash', () => {
|
it('block helpers can take an optional hash', () => {
|
||||||
expectTemplate('{{#goodbye cruel="CRUEL" times=12}}world{{/goodbye}}')
|
expectTemplate('{{#goodbye cruel="CRUEL" times=12}}world{{/goodbye}}')
|
||||||
.withHelper('goodbye', function (this: any, options: Handlebars.HelperOptions) {
|
.withHelper('goodbye', function (this: any, options: HelperOptions) {
|
||||||
return (
|
return (
|
||||||
'GOODBYE ' +
|
'GOODBYE ' +
|
||||||
options.hash.cruel +
|
options.hash.cruel +
|
||||||
|
@ -539,7 +539,7 @@ describe('helpers', () => {
|
||||||
|
|
||||||
it('block helpers can take an optional hash with single quoted stings', () => {
|
it('block helpers can take an optional hash with single quoted stings', () => {
|
||||||
expectTemplate('{{#goodbye cruel="CRUEL" times=12}}world{{/goodbye}}')
|
expectTemplate('{{#goodbye cruel="CRUEL" times=12}}world{{/goodbye}}')
|
||||||
.withHelper('goodbye', function (this: any, options: Handlebars.HelperOptions) {
|
.withHelper('goodbye', function (this: any, options: HelperOptions) {
|
||||||
return (
|
return (
|
||||||
'GOODBYE ' +
|
'GOODBYE ' +
|
||||||
options.hash.cruel +
|
options.hash.cruel +
|
||||||
|
@ -554,7 +554,7 @@ describe('helpers', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('block helpers can take an optional hash with booleans', () => {
|
it('block helpers can take an optional hash with booleans', () => {
|
||||||
function goodbye(this: any, options: Handlebars.HelperOptions) {
|
function goodbye(this: any, options: HelperOptions) {
|
||||||
if (options.hash.print === true) {
|
if (options.hash.print === true) {
|
||||||
return 'GOODBYE ' + options.hash.cruel + ' ' + options.fn(this);
|
return 'GOODBYE ' + options.hash.cruel + ' ' + options.fn(this);
|
||||||
} else if (options.hash.print === false) {
|
} else if (options.hash.print === false) {
|
||||||
|
@ -582,7 +582,7 @@ describe('helpers', () => {
|
||||||
it('if a context is not found, custom helperMissing is used', () => {
|
it('if a context is not found, custom helperMissing is used', () => {
|
||||||
expectTemplate('{{hello}} {{link_to world}}')
|
expectTemplate('{{hello}} {{link_to world}}')
|
||||||
.withInput({ hello: 'Hello', world: 'world' })
|
.withInput({ hello: 'Hello', world: 'world' })
|
||||||
.withHelper('helperMissing', function (mesg, options: Handlebars.HelperOptions) {
|
.withHelper('helperMissing', function (mesg, options: HelperOptions) {
|
||||||
if (options.name === 'link_to') {
|
if (options.name === 'link_to') {
|
||||||
return new Handlebars.SafeString('<a>' + mesg + '</a>');
|
return new Handlebars.SafeString('<a>' + mesg + '</a>');
|
||||||
}
|
}
|
||||||
|
@ -593,7 +593,7 @@ describe('helpers', () => {
|
||||||
it('if a value is not found, custom helperMissing is used', () => {
|
it('if a value is not found, custom helperMissing is used', () => {
|
||||||
expectTemplate('{{hello}} {{link_to}}')
|
expectTemplate('{{hello}} {{link_to}}')
|
||||||
.withInput({ hello: 'Hello', world: 'world' })
|
.withInput({ hello: 'Hello', world: 'world' })
|
||||||
.withHelper('helperMissing', function (options: Handlebars.HelperOptions) {
|
.withHelper('helperMissing', function (options: HelperOptions) {
|
||||||
if (options.name === 'link_to') {
|
if (options.name === 'link_to') {
|
||||||
return new Handlebars.SafeString('<a>winning</a>');
|
return new Handlebars.SafeString('<a>winning</a>');
|
||||||
}
|
}
|
||||||
|
@ -788,7 +788,7 @@ describe('helpers', () => {
|
||||||
|
|
||||||
it('helpers take precedence over same-named context properties$', () => {
|
it('helpers take precedence over same-named context properties$', () => {
|
||||||
expectTemplate('{{#goodbye}} {{cruel world}}{{/goodbye}}')
|
expectTemplate('{{#goodbye}} {{cruel world}}{{/goodbye}}')
|
||||||
.withHelper('goodbye', function (this: any, options: Handlebars.HelperOptions) {
|
.withHelper('goodbye', function (this: any, options: HelperOptions) {
|
||||||
return this.goodbye.toUpperCase() + options.fn(this);
|
return this.goodbye.toUpperCase() + options.fn(this);
|
||||||
})
|
})
|
||||||
.withHelper('cruel', function (world) {
|
.withHelper('cruel', function (world) {
|
||||||
|
@ -818,7 +818,7 @@ describe('helpers', () => {
|
||||||
|
|
||||||
it('Scoped names take precedence over block helpers', () => {
|
it('Scoped names take precedence over block helpers', () => {
|
||||||
expectTemplate('{{#goodbye}} {{cruel world}}{{/goodbye}} {{this.goodbye}}')
|
expectTemplate('{{#goodbye}} {{cruel world}}{{/goodbye}} {{this.goodbye}}')
|
||||||
.withHelper('goodbye', function (this: any, options: Handlebars.HelperOptions) {
|
.withHelper('goodbye', function (this: any, options: HelperOptions) {
|
||||||
return this.goodbye.toUpperCase() + options.fn(this);
|
return this.goodbye.toUpperCase() + options.fn(this);
|
||||||
})
|
})
|
||||||
.withHelper('cruel', function (world) {
|
.withHelper('cruel', function (world) {
|
||||||
|
@ -836,7 +836,7 @@ describe('helpers', () => {
|
||||||
it('should take presedence over context values', () => {
|
it('should take presedence over context values', () => {
|
||||||
expectTemplate('{{#goodbyes as |value|}}{{value}}{{/goodbyes}}{{value}}')
|
expectTemplate('{{#goodbyes as |value|}}{{value}}{{/goodbyes}}{{value}}')
|
||||||
.withInput({ value: 'foo' })
|
.withInput({ value: 'foo' })
|
||||||
.withHelper('goodbyes', function (options: Handlebars.HelperOptions) {
|
.withHelper('goodbyes', function (options: HelperOptions) {
|
||||||
expect(options.fn.blockParams).toEqual(1);
|
expect(options.fn.blockParams).toEqual(1);
|
||||||
return options.fn({ value: 'bar' }, { blockParams: [1, 2] });
|
return options.fn({ value: 'bar' }, { blockParams: [1, 2] });
|
||||||
})
|
})
|
||||||
|
@ -848,7 +848,7 @@ describe('helpers', () => {
|
||||||
.withHelper('value', function () {
|
.withHelper('value', function () {
|
||||||
return 'foo';
|
return 'foo';
|
||||||
})
|
})
|
||||||
.withHelper('goodbyes', function (options: Handlebars.HelperOptions) {
|
.withHelper('goodbyes', function (options: HelperOptions) {
|
||||||
expect(options.fn.blockParams).toEqual(1);
|
expect(options.fn.blockParams).toEqual(1);
|
||||||
return options.fn({}, { blockParams: [1, 2] });
|
return options.fn({}, { blockParams: [1, 2] });
|
||||||
})
|
})
|
||||||
|
@ -861,7 +861,7 @@ describe('helpers', () => {
|
||||||
.withHelper('value', function () {
|
.withHelper('value', function () {
|
||||||
return 'foo';
|
return 'foo';
|
||||||
})
|
})
|
||||||
.withHelper('goodbyes', function (this: any, options: Handlebars.HelperOptions) {
|
.withHelper('goodbyes', function (this: any, options: HelperOptions) {
|
||||||
expect(options.fn.blockParams).toEqual(1);
|
expect(options.fn.blockParams).toEqual(1);
|
||||||
return options.fn(this, { blockParams: [1, 2] });
|
return options.fn(this, { blockParams: [1, 2] });
|
||||||
})
|
})
|
||||||
|
@ -879,7 +879,7 @@ describe('helpers', () => {
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.withInput({ value: 'foo' })
|
.withInput({ value: 'foo' })
|
||||||
.withHelper('goodbyes', function (options: Handlebars.HelperOptions) {
|
.withHelper('goodbyes', function (options: HelperOptions) {
|
||||||
return options.fn(
|
return options.fn(
|
||||||
{ value: 'bar' },
|
{ value: 'bar' },
|
||||||
{
|
{
|
||||||
|
@ -893,7 +893,7 @@ describe('helpers', () => {
|
||||||
it('should allow block params on chained helpers', () => {
|
it('should allow block params on chained helpers', () => {
|
||||||
expectTemplate('{{#if bar}}{{else goodbyes as |value|}}{{value}}{{/if}}{{value}}')
|
expectTemplate('{{#if bar}}{{else goodbyes as |value|}}{{value}}{{/if}}{{value}}')
|
||||||
.withInput({ value: 'foo' })
|
.withInput({ value: 'foo' })
|
||||||
.withHelper('goodbyes', function (options: Handlebars.HelperOptions) {
|
.withHelper('goodbyes', function (options: HelperOptions) {
|
||||||
expect(options.fn.blockParams).toEqual(1);
|
expect(options.fn.blockParams).toEqual(1);
|
||||||
return options.fn({ value: 'bar' }, { blockParams: [1, 2] });
|
return options.fn({ value: 'bar' }, { blockParams: [1, 2] });
|
||||||
})
|
})
|
||||||
|
@ -942,12 +942,9 @@ describe('helpers', () => {
|
||||||
describe('the lookupProperty-option', () => {
|
describe('the lookupProperty-option', () => {
|
||||||
it('should be passed to custom helpers', () => {
|
it('should be passed to custom helpers', () => {
|
||||||
expectTemplate('{{testHelper}}')
|
expectTemplate('{{testHelper}}')
|
||||||
.withHelper(
|
.withHelper('testHelper', function testHelper(this: any, options: HelperOptions) {
|
||||||
'testHelper',
|
|
||||||
function testHelper(this: any, options: Handlebars.HelperOptions) {
|
|
||||||
return options.lookupProperty(this, 'testProperty');
|
return options.lookupProperty(this, 'testProperty');
|
||||||
}
|
})
|
||||||
)
|
|
||||||
.withInput({ testProperty: 'abc' })
|
.withInput({ testProperty: 'abc' })
|
||||||
.toCompileTo('abc');
|
.toCompileTo('abc');
|
||||||
});
|
});
|
||||||
|
|
|
@ -163,8 +163,7 @@ describe('partials', () => {
|
||||||
global.kbnHandlebarsEnv = Handlebars.create();
|
global.kbnHandlebarsEnv = Handlebars.create();
|
||||||
|
|
||||||
expect(() => {
|
expect(() => {
|
||||||
const undef: unknown = undefined;
|
kbnHandlebarsEnv!.registerPartial('undefined_test', undefined as any);
|
||||||
kbnHandlebarsEnv!.registerPartial('undefined_test', undef as Handlebars.Template);
|
|
||||||
}).toThrow('Attempting to register a partial called "undefined_test" as undefined');
|
}).toThrow('Attempting to register a partial called "undefined_test" as undefined');
|
||||||
|
|
||||||
global.kbnHandlebarsEnv = null;
|
global.kbnHandlebarsEnv = null;
|
||||||
|
@ -294,7 +293,6 @@ describe('partials', () => {
|
||||||
{},
|
{},
|
||||||
{
|
{
|
||||||
partials: {
|
partials: {
|
||||||
// @ts-expect-error
|
|
||||||
dude: 'fail',
|
dude: 'fail',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
* See `packages/kbn-handlebars/LICENSE` for more information.
|
* See `packages/kbn-handlebars/LICENSE` for more information.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import Handlebars from '../..';
|
import Handlebars, { type HelperOptions } from '../..';
|
||||||
import { expectTemplate, forEachCompileFunctionName } from '../__jest__/test_bench';
|
import { expectTemplate, forEachCompileFunctionName } from '../__jest__/test_bench';
|
||||||
|
|
||||||
describe('Regressions', () => {
|
describe('Regressions', () => {
|
||||||
|
@ -99,10 +99,10 @@ describe('Regressions', () => {
|
||||||
'{{#inverse}} {{#blk}} Unexpected {{/blk}} {{else}} {{#blk}} Expected {{/blk}} {{/inverse}}';
|
'{{#inverse}} {{#blk}} Unexpected {{/blk}} {{else}} {{#blk}} Expected {{/blk}} {{/inverse}}';
|
||||||
|
|
||||||
const helpers = {
|
const helpers = {
|
||||||
blk(block: Handlebars.HelperOptions) {
|
blk(block: HelperOptions) {
|
||||||
return block.fn('');
|
return block.fn('');
|
||||||
},
|
},
|
||||||
inverse(block: Handlebars.HelperOptions) {
|
inverse(block: HelperOptions) {
|
||||||
return block.inverse('');
|
return block.inverse('');
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -204,7 +204,7 @@ describe('Regressions', () => {
|
||||||
it('GH-1054: Should handle simple safe string responses', () => {
|
it('GH-1054: Should handle simple safe string responses', () => {
|
||||||
expectTemplate('{{#wrap}}{{>partial}}{{/wrap}}')
|
expectTemplate('{{#wrap}}{{>partial}}{{/wrap}}')
|
||||||
.withHelpers({
|
.withHelpers({
|
||||||
wrap(options: Handlebars.HelperOptions) {
|
wrap(options: HelperOptions) {
|
||||||
return new Handlebars.SafeString(options.fn());
|
return new Handlebars.SafeString(options.fn());
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
@ -279,7 +279,7 @@ describe('Regressions', () => {
|
||||||
)
|
)
|
||||||
.withInput({ array: [1], name: 'John' })
|
.withInput({ array: [1], name: 'John' })
|
||||||
.withHelpers({
|
.withHelpers({
|
||||||
myif(conditional, options: Handlebars.HelperOptions) {
|
myif(conditional, options: HelperOptions) {
|
||||||
if (conditional) {
|
if (conditional) {
|
||||||
return options.fn(this);
|
return options.fn(this);
|
||||||
} else {
|
} else {
|
||||||
|
@ -325,7 +325,7 @@ describe('Regressions', () => {
|
||||||
expectTemplate('{{helpa length="foo"}}')
|
expectTemplate('{{helpa length="foo"}}')
|
||||||
.withInput({ array: [1], name: 'John' })
|
.withInput({ array: [1], name: 'John' })
|
||||||
.withHelpers({
|
.withHelpers({
|
||||||
helpa(options: Handlebars.HelperOptions) {
|
helpa(options: HelperOptions) {
|
||||||
return options.hash.length;
|
return options.hash.length;
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
@ -371,7 +371,7 @@ describe('Regressions', () => {
|
||||||
describe("GH-1639: TypeError: Cannot read property 'apply' of undefined\" when handlebars version > 4.6.0 (undocumented, deprecated usage)", () => {
|
describe("GH-1639: TypeError: Cannot read property 'apply' of undefined\" when handlebars version > 4.6.0 (undocumented, deprecated usage)", () => {
|
||||||
it('should treat undefined helpers like non-existing helpers', () => {
|
it('should treat undefined helpers like non-existing helpers', () => {
|
||||||
expectTemplate('{{foo}}')
|
expectTemplate('{{foo}}')
|
||||||
.withHelper('foo', undefined)
|
.withHelper('foo', undefined as any)
|
||||||
.withInput({ foo: 'bar' })
|
.withInput({ foo: 'bar' })
|
||||||
.toCompileTo('bar');
|
.toCompileTo('bar');
|
||||||
});
|
});
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
* See `packages/kbn-handlebars/LICENSE` for more information.
|
* See `packages/kbn-handlebars/LICENSE` for more information.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import Handlebars from '../..';
|
import Handlebars, { type HelperOptions } from '../..';
|
||||||
import { expectTemplate } from '../__jest__/test_bench';
|
import { expectTemplate } from '../__jest__/test_bench';
|
||||||
|
|
||||||
describe('subexpressions', () => {
|
describe('subexpressions', () => {
|
||||||
|
@ -102,9 +102,9 @@ describe('subexpressions', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('provides each nested helper invocation its own options hash', () => {
|
it('provides each nested helper invocation its own options hash', () => {
|
||||||
let lastOptions: Handlebars.HelperOptions;
|
let lastOptions: HelperOptions;
|
||||||
const helpers = {
|
const helpers = {
|
||||||
equal(x: any, y: any, options: Handlebars.HelperOptions) {
|
equal(x: any, y: any, options: HelperOptions) {
|
||||||
if (!options || options === lastOptions) {
|
if (!options || options === lastOptions) {
|
||||||
throw new Error('options hash was reused');
|
throw new Error('options hash was reused');
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,66 +5,67 @@
|
||||||
|
|
||||||
import { kHelper, kAmbiguous, kSimple } from './symbols';
|
import { kHelper, kAmbiguous, kSimple } from './symbols';
|
||||||
|
|
||||||
|
// Unexported `CompileOptions` lifted from node_modules/handlebars/types/index.d.ts
|
||||||
|
// While it could also be extracted using `NonNullable<Parameters<typeof Handlebars.compile>[1]>`, this isn't possible since we declare the handlebars module below
|
||||||
|
interface HandlebarsCompileOptions {
|
||||||
|
data?: boolean;
|
||||||
|
compat?: boolean;
|
||||||
|
knownHelpers?: KnownHelpers;
|
||||||
|
knownHelpersOnly?: boolean;
|
||||||
|
noEscape?: boolean;
|
||||||
|
strict?: boolean;
|
||||||
|
assumeObjects?: boolean;
|
||||||
|
preventIndent?: boolean;
|
||||||
|
ignoreStandalone?: boolean;
|
||||||
|
explicitPartialContext?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A custom version of the Handlesbars module with an extra `compileAST` function and fixed typings.
|
* A custom version of the Handlebars module with an extra `compileAST` function and fixed typings.
|
||||||
*/
|
*/
|
||||||
declare module 'handlebars' {
|
declare module 'handlebars' {
|
||||||
|
/**
|
||||||
|
* Compiles the given Handlebars template without the use of `eval`.
|
||||||
|
*
|
||||||
|
* @returns A render function with the same API as the return value from the regular Handlebars `compile` function.
|
||||||
|
*/
|
||||||
export function compileAST(
|
export function compileAST(
|
||||||
input: string | hbs.AST.Program,
|
input: string | hbs.AST.Program,
|
||||||
options?: ExtendedCompileOptions
|
options?: CompileOptions
|
||||||
): (context?: any, options?: ExtendedRuntimeOptions) => string;
|
): TemplateDelegateFixed;
|
||||||
|
|
||||||
// --------------------------------------------------------
|
// --------------------------------------------------------
|
||||||
// Override/Extend inherited types below that are incorrect
|
// Override/Extend inherited funcions and interfaces below that are incorrect.
|
||||||
|
//
|
||||||
|
// Any exported `const` or `type` types can't be overwritten, so we'll just
|
||||||
|
// have to live with those and cast them to the correct types in our code.
|
||||||
|
// Some of these fixed types, we'll instead export outside the scope of this
|
||||||
|
// 'handlebars' module so consumers of @kbn/handlebars at least have a way to
|
||||||
|
// access the correct types.
|
||||||
// --------------------------------------------------------
|
// --------------------------------------------------------
|
||||||
|
|
||||||
export interface TemplateDelegate<T = any> {
|
/**
|
||||||
(context?: T, options?: RuntimeOptions): string; // Override to ensure `context` is optional
|
* A {@link https://handlebarsjs.com/api-reference/helpers.html helper-function} type.
|
||||||
blockParams?: number; // TODO: Can this really be optional?
|
*
|
||||||
partials?: any; // TODO: Narrow type to something better than any?
|
* When registering a helper function, it should be of this type.
|
||||||
}
|
*/
|
||||||
|
export interface HelperDelegate extends HelperDelegateFixed {} // eslint-disable-line @typescript-eslint/no-empty-interface
|
||||||
|
|
||||||
export interface HelperOptions {
|
/**
|
||||||
name: string;
|
* A template-function type.
|
||||||
loc: { start: hbs.AST.SourceLocation['start']; end: hbs.AST.SourceLocation['end'] };
|
*
|
||||||
lookupProperty: LookupProperty;
|
* This type is primarily used for the return value of by calls to
|
||||||
}
|
* {@link https://handlebarsjs.com/api-reference/compilation.html#handlebars-compile-template-options Handlebars.compile},
|
||||||
|
* Handlebars.compileAST and {@link https://handlebarsjs.com/api-reference/compilation.html#handlebars-precompile-template-options Handlebars.template}.
|
||||||
|
*/
|
||||||
|
export interface TemplateDelegate<T = any> extends TemplateDelegateFixed<T> {} // eslint-disable-line @typescript-eslint/no-empty-interface
|
||||||
|
|
||||||
export interface HelperDelegate {
|
/**
|
||||||
// eslint-disable-next-line @typescript-eslint/prefer-function-type
|
* Register one or more {@link https://handlebarsjs.com/api-reference/runtime.html#handlebars-registerpartial-name-partial partials}.
|
||||||
(...params: any[]): any;
|
*
|
||||||
}
|
* @param spec A key/value object where each key is the name of a partial (a string) and each value is the partial (either a string or a partial function).
|
||||||
|
*/
|
||||||
export function registerPartial(spec: { [name: string]: Handlebars.Template }): void; // Ensure `spec` object values can be strings
|
export function registerPartial(spec: Record<string, TemplateFixed>): void; // Ensure `spec` object values can be strings
|
||||||
}
|
|
||||||
|
|
||||||
export type NodeType = typeof kHelper | typeof kAmbiguous | typeof kSimple;
|
|
||||||
|
|
||||||
type LookupProperty = <T = any>(parent: { [name: string]: any }, propertyName: string) => T;
|
|
||||||
|
|
||||||
export type ProcessableStatementNode =
|
|
||||||
| hbs.AST.MustacheStatement
|
|
||||||
| hbs.AST.PartialStatement
|
|
||||||
| hbs.AST.SubExpression;
|
|
||||||
export type ProcessableBlockStatementNode = hbs.AST.BlockStatement | hbs.AST.PartialBlockStatement;
|
|
||||||
export type ProcessableNode = ProcessableStatementNode | ProcessableBlockStatementNode;
|
|
||||||
export type ProcessableNodeWithPathParts = ProcessableNode & { path: hbs.AST.PathExpression };
|
|
||||||
export type ProcessableNodeWithPathPartsOrLiteral = ProcessableNode & {
|
|
||||||
path: hbs.AST.PathExpression | hbs.AST.Literal;
|
|
||||||
};
|
|
||||||
|
|
||||||
export interface Helper {
|
|
||||||
fn?: Handlebars.HelperDelegate;
|
|
||||||
context: any[];
|
|
||||||
params: any[];
|
|
||||||
options: AmbiguousHelperOptions;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type NonBlockHelperOptions = Omit<Handlebars.HelperOptions, 'fn' | 'inverse'>;
|
|
||||||
export type AmbiguousHelperOptions = Handlebars.HelperOptions | NonBlockHelperOptions;
|
|
||||||
|
|
||||||
export interface DecoratorOptions extends Omit<Handlebars.HelperOptions, 'lookupProperties'> {
|
|
||||||
args?: any[];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -73,8 +74,8 @@ export interface DecoratorOptions extends Omit<Handlebars.HelperOptions, 'lookup
|
||||||
* This is a subset of all the compile options supported by the upstream
|
* This is a subset of all the compile options supported by the upstream
|
||||||
* Handlebars module.
|
* Handlebars module.
|
||||||
*/
|
*/
|
||||||
export type ExtendedCompileOptions = Pick<
|
export type CompileOptions = Pick<
|
||||||
CompileOptions,
|
HandlebarsCompileOptions,
|
||||||
| 'data'
|
| 'data'
|
||||||
| 'knownHelpers'
|
| 'knownHelpers'
|
||||||
| 'knownHelpersOnly'
|
| 'knownHelpersOnly'
|
||||||
|
@ -91,46 +92,134 @@ export type ExtendedCompileOptions = Pick<
|
||||||
* This is a subset of all the runtime options supported by the upstream
|
* This is a subset of all the runtime options supported by the upstream
|
||||||
* Handlebars module.
|
* Handlebars module.
|
||||||
*/
|
*/
|
||||||
export type ExtendedRuntimeOptions = Pick<
|
export interface RuntimeOptions extends Pick<Handlebars.RuntimeOptions, 'data' | 'blockParams'> {
|
||||||
RuntimeOptions,
|
// The upstream `helpers` property is too loose and allows all functions.
|
||||||
'data' | 'helpers' | 'partials' | 'decorators' | 'blockParams'
|
helpers?: HelpersHash;
|
||||||
>;
|
// The upstream `partials` property is incorrectly typed and doesn't allow
|
||||||
|
// partials to be strings.
|
||||||
|
partials?: PartialsHash;
|
||||||
|
// The upstream `decorators` property is too loose and allows all functions.
|
||||||
|
decorators?: DecoratorsHash;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* According to the [decorator docs]{@link https://github.com/handlebars-lang/handlebars.js/blob/4.x/docs/decorators-api.md},
|
* The last argument being passed to a helper function is a an {@link https://handlebarsjs.com/api-reference/helpers.html#the-options-parameter options object}.
|
||||||
* a decorator will be called with a different set of arugments than what's actually happening in the upstream code.
|
|
||||||
* So here I assume that the docs are wrong and that the upstream code is correct. In reality, `context` is the last 4
|
|
||||||
* documented arguments rolled into one object.
|
|
||||||
*/
|
*/
|
||||||
export type DecoratorFunction = (
|
export interface HelperOptions extends Omit<Handlebars.HelperOptions, 'fn' | 'inverse'> {
|
||||||
prog: Handlebars.TemplateDelegate,
|
name: string;
|
||||||
|
fn: TemplateDelegateFixed;
|
||||||
|
inverse: TemplateDelegateFixed;
|
||||||
|
loc: { start: hbs.AST.SourceLocation['start']; end: hbs.AST.SourceLocation['end'] };
|
||||||
|
lookupProperty: LookupProperty;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use the post-fix `Fixed` to allow us to acces it inside the 'handlebars' module declared above
|
||||||
|
/**
|
||||||
|
* A {@link https://handlebarsjs.com/api-reference/helpers.html helper-function} type.
|
||||||
|
*
|
||||||
|
* When registering a helper function, it should be of this type.
|
||||||
|
*/
|
||||||
|
interface HelperDelegateFixed {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/prefer-function-type
|
||||||
|
(...params: any[]): any;
|
||||||
|
}
|
||||||
|
export type { HelperDelegateFixed as HelperDelegate };
|
||||||
|
|
||||||
|
// Use the post-fix `Fixed` to allow us to acces it inside the 'handlebars' module declared above
|
||||||
|
/**
|
||||||
|
* A template-function type.
|
||||||
|
*
|
||||||
|
* This type is primarily used for the return value of by calls to
|
||||||
|
* {@link https://handlebarsjs.com/api-reference/compilation.html#handlebars-compile-template-options Handlebars.compile},
|
||||||
|
* Handlebars.compileAST and {@link https://handlebarsjs.com/api-reference/compilation.html#handlebars-precompile-template-options Handlebars.template}.
|
||||||
|
*/
|
||||||
|
interface TemplateDelegateFixed<T = any> {
|
||||||
|
(context?: T, options?: RuntimeOptions): string; // Override to ensure `context` is optional
|
||||||
|
blockParams?: number; // TODO: Can this really be optional?
|
||||||
|
partials?: PartialsHash;
|
||||||
|
}
|
||||||
|
export type { TemplateDelegateFixed as TemplateDelegate };
|
||||||
|
|
||||||
|
// According to the decorator docs
|
||||||
|
// (https://github.com/handlebars-lang/handlebars.js/blob/4.x/docs/decorators-api.md)
|
||||||
|
// a decorator will be called with a different set of arugments than what's
|
||||||
|
// actually happening in the upstream code. So here I assume that the docs are
|
||||||
|
// wrong and that the upstream code is correct. In reality, `context` is the
|
||||||
|
// last 4 documented arguments rolled into one object.
|
||||||
|
/**
|
||||||
|
* A {@link https://github.com/handlebars-lang/handlebars.js/blob/master/docs/decorators-api.md decorator-function} type.
|
||||||
|
*
|
||||||
|
* When registering a decorator function, it should be of this type.
|
||||||
|
*/
|
||||||
|
export type DecoratorDelegate = (
|
||||||
|
prog: TemplateDelegateFixed,
|
||||||
props: Record<string, any>,
|
props: Record<string, any>,
|
||||||
container: Container,
|
container: Container,
|
||||||
options: any
|
options: any
|
||||||
) => any;
|
) => any;
|
||||||
|
|
||||||
export interface HelpersHash {
|
// -----------------------------------------------------------------------------
|
||||||
[name: string]: Handlebars.HelperDelegate;
|
// INTERNAL TYPES
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export type NodeType = typeof kHelper | typeof kAmbiguous | typeof kSimple;
|
||||||
|
|
||||||
|
type LookupProperty = <T = any>(parent: Record<string, any>, propertyName: string) => T;
|
||||||
|
|
||||||
|
export type NonBlockHelperOptions = Omit<HelperOptions, 'fn' | 'inverse'>;
|
||||||
|
export type AmbiguousHelperOptions = HelperOptions | NonBlockHelperOptions;
|
||||||
|
|
||||||
|
export type ProcessableStatementNode =
|
||||||
|
| hbs.AST.MustacheStatement
|
||||||
|
| hbs.AST.PartialStatement
|
||||||
|
| hbs.AST.SubExpression;
|
||||||
|
export type ProcessableBlockStatementNode = hbs.AST.BlockStatement | hbs.AST.PartialBlockStatement;
|
||||||
|
export type ProcessableNode = ProcessableStatementNode | ProcessableBlockStatementNode;
|
||||||
|
export type ProcessableNodeWithPathParts = ProcessableNode & { path: hbs.AST.PathExpression };
|
||||||
|
export type ProcessableNodeWithPathPartsOrLiteral = ProcessableNode & {
|
||||||
|
path: hbs.AST.PathExpression | hbs.AST.Literal;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type HelpersHash = Record<string, HelperDelegateFixed>;
|
||||||
|
export type PartialsHash = Record<string, TemplateFixed>;
|
||||||
|
export type DecoratorsHash = Record<string, DecoratorDelegate>;
|
||||||
|
|
||||||
|
// Use the post-fix `Fixed` to allow us to acces it inside the 'handlebars' module declared above
|
||||||
|
type TemplateFixed = TemplateDelegateFixed | string;
|
||||||
|
export type { TemplateFixed as Template };
|
||||||
|
|
||||||
|
export interface DecoratorOptions extends Omit<HelperOptions, 'lookupProperties'> {
|
||||||
|
args?: any[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PartialsHash {
|
export interface VisitorHelper {
|
||||||
[name: string]: HandlebarsTemplateDelegate;
|
fn?: HelperDelegateFixed;
|
||||||
|
context: any[];
|
||||||
|
params: any[];
|
||||||
|
options: AmbiguousHelperOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DecoratorsHash {
|
export interface ResolvePartialOptions
|
||||||
[name: string]: DecoratorFunction;
|
extends Omit<Handlebars.ResolvePartialOptions, 'helpers' | 'partials' | 'decorators'> {
|
||||||
|
// The upstream `helpers` property is too loose and allows all functions.
|
||||||
|
helpers?: HelpersHash;
|
||||||
|
// The upstream `partials` property is incorrectly typed and doesn't allow
|
||||||
|
// partials to be strings.
|
||||||
|
partials?: PartialsHash;
|
||||||
|
// The upstream `decorators` property is too loose and allows all functions.
|
||||||
|
decorators?: DecoratorsHash;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Container {
|
export interface Container {
|
||||||
helpers: HelpersHash;
|
helpers: HelpersHash;
|
||||||
partials: PartialsHash;
|
partials: PartialsHash;
|
||||||
decorators: DecoratorsHash;
|
decorators: DecoratorsHash;
|
||||||
strict: (obj: { [name: string]: any }, name: string, loc: hbs.AST.SourceLocation) => any;
|
strict: (obj: Record<string, any>, name: string, loc: hbs.AST.SourceLocation) => any;
|
||||||
lookupProperty: LookupProperty;
|
lookupProperty: LookupProperty;
|
||||||
lambda: (current: any, context: any) => any;
|
lambda: (current: any, context: any) => any;
|
||||||
data: (value: any, depth: number) => any;
|
data: (value: any, depth: number) => any;
|
||||||
hooks: {
|
hooks: {
|
||||||
helperMissing?: Handlebars.HelperDelegate;
|
helperMissing?: HelperDelegateFixed;
|
||||||
blockHelperMissing?: Handlebars.HelperDelegate;
|
blockHelperMissing?: HelperDelegateFixed;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,13 +18,11 @@ import { moveHelperToHooks } from 'handlebars/dist/cjs/handlebars/helpers';
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
AmbiguousHelperOptions,
|
AmbiguousHelperOptions,
|
||||||
|
CompileOptions,
|
||||||
Container,
|
Container,
|
||||||
DecoratorFunction,
|
DecoratorDelegate,
|
||||||
DecoratorsHash,
|
DecoratorsHash,
|
||||||
ExtendedCompileOptions,
|
HelperOptions,
|
||||||
ExtendedRuntimeOptions,
|
|
||||||
Helper,
|
|
||||||
HelpersHash,
|
|
||||||
NodeType,
|
NodeType,
|
||||||
NonBlockHelperOptions,
|
NonBlockHelperOptions,
|
||||||
ProcessableBlockStatementNode,
|
ProcessableBlockStatementNode,
|
||||||
|
@ -32,6 +30,11 @@ import type {
|
||||||
ProcessableNodeWithPathParts,
|
ProcessableNodeWithPathParts,
|
||||||
ProcessableNodeWithPathPartsOrLiteral,
|
ProcessableNodeWithPathPartsOrLiteral,
|
||||||
ProcessableStatementNode,
|
ProcessableStatementNode,
|
||||||
|
ResolvePartialOptions,
|
||||||
|
RuntimeOptions,
|
||||||
|
Template,
|
||||||
|
TemplateDelegate,
|
||||||
|
VisitorHelper,
|
||||||
} from './types';
|
} from './types';
|
||||||
import { kAmbiguous, kHelper, kSimple } from './symbols';
|
import { kAmbiguous, kHelper, kSimple } from './symbols';
|
||||||
import {
|
import {
|
||||||
|
@ -48,8 +51,8 @@ export class ElasticHandlebarsVisitor extends Handlebars.Visitor {
|
||||||
private contexts: any[] = [];
|
private contexts: any[] = [];
|
||||||
private output: any[] = [];
|
private output: any[] = [];
|
||||||
private template?: string;
|
private template?: string;
|
||||||
private compileOptions: ExtendedCompileOptions;
|
private compileOptions: CompileOptions;
|
||||||
private runtimeOptions?: ExtendedRuntimeOptions;
|
private runtimeOptions?: RuntimeOptions;
|
||||||
private blockParamNames: any[][] = [];
|
private blockParamNames: any[][] = [];
|
||||||
private blockParamValues: any[][] = [];
|
private blockParamValues: any[][] = [];
|
||||||
private ast?: hbs.AST.Program;
|
private ast?: hbs.AST.Program;
|
||||||
|
@ -61,7 +64,7 @@ export class ElasticHandlebarsVisitor extends Handlebars.Visitor {
|
||||||
constructor(
|
constructor(
|
||||||
env: typeof Handlebars,
|
env: typeof Handlebars,
|
||||||
input: string | hbs.AST.Program,
|
input: string | hbs.AST.Program,
|
||||||
options: ExtendedCompileOptions = {}
|
options: CompileOptions = {}
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
|
@ -136,18 +139,15 @@ export class ElasticHandlebarsVisitor extends Handlebars.Visitor {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
render(context: any, options: ExtendedRuntimeOptions = {}): string {
|
render(context: any, options: RuntimeOptions = {}): string {
|
||||||
this.contexts = [context];
|
this.contexts = [context];
|
||||||
this.output = [];
|
this.output = [];
|
||||||
this.runtimeOptions = { ...options };
|
this.runtimeOptions = { ...options };
|
||||||
this.container.helpers = {
|
this.container.helpers = { ...this.env.helpers, ...options.helpers };
|
||||||
...this.env.helpers,
|
|
||||||
...(options.helpers as HelpersHash),
|
|
||||||
};
|
|
||||||
this.container.partials = { ...this.env.partials, ...options.partials };
|
this.container.partials = { ...this.env.partials, ...options.partials };
|
||||||
this.container.decorators = {
|
this.container.decorators = {
|
||||||
...(this.env.decorators as DecoratorsHash),
|
...(this.env.decorators as DecoratorsHash),
|
||||||
...(options.decorators as DecoratorsHash),
|
...options.decorators,
|
||||||
};
|
};
|
||||||
this.container.hooks = {};
|
this.container.hooks = {};
|
||||||
this.processedRootDecorators = false;
|
this.processedRootDecorators = false;
|
||||||
|
@ -170,7 +170,7 @@ export class ElasticHandlebarsVisitor extends Handlebars.Visitor {
|
||||||
// Generate a "program" function based on the root `Program` in the AST and
|
// Generate a "program" function based on the root `Program` in the AST and
|
||||||
// call it. This will start the processing of all the child nodes in the
|
// call it. This will start the processing of all the child nodes in the
|
||||||
// AST.
|
// AST.
|
||||||
const defaultMain: Handlebars.TemplateDelegate = (_context) => {
|
const defaultMain: TemplateDelegate = (_context) => {
|
||||||
const prog = this.generateProgramFunction(this.ast!);
|
const prog = this.generateProgramFunction(this.ast!);
|
||||||
return prog(_context, this.runtimeOptions);
|
return prog(_context, this.runtimeOptions);
|
||||||
};
|
};
|
||||||
|
@ -296,7 +296,7 @@ export class ElasticHandlebarsVisitor extends Handlebars.Visitor {
|
||||||
* So we have to look into the program AST body and see if it contains any decorators that we have to process
|
* So we have to look into the program AST body and see if it contains any decorators that we have to process
|
||||||
* before we can finish processing of the wrapping program.
|
* before we can finish processing of the wrapping program.
|
||||||
*/
|
*/
|
||||||
private processDecorators(program: hbs.AST.Program, prog: Handlebars.TemplateDelegate) {
|
private processDecorators(program: hbs.AST.Program, prog: TemplateDelegate) {
|
||||||
if (!this.processedDecoratorsForProgram.has(program)) {
|
if (!this.processedDecoratorsForProgram.has(program)) {
|
||||||
this.processedDecoratorsForProgram.add(program);
|
this.processedDecoratorsForProgram.add(program);
|
||||||
const props = {};
|
const props = {};
|
||||||
|
@ -312,12 +312,12 @@ export class ElasticHandlebarsVisitor extends Handlebars.Visitor {
|
||||||
|
|
||||||
private processDecorator(
|
private processDecorator(
|
||||||
decorator: hbs.AST.DecoratorBlock | hbs.AST.Decorator,
|
decorator: hbs.AST.DecoratorBlock | hbs.AST.Decorator,
|
||||||
prog: Handlebars.TemplateDelegate,
|
prog: TemplateDelegate,
|
||||||
props: Record<string, any>
|
props: Record<string, any>
|
||||||
) {
|
) {
|
||||||
const options = this.setupDecoratorOptions(decorator);
|
const options = this.setupDecoratorOptions(decorator);
|
||||||
|
|
||||||
const result = this.container.lookupProperty<DecoratorFunction>(
|
const result = this.container.lookupProperty<DecoratorDelegate>(
|
||||||
this.container.decorators,
|
this.container.decorators,
|
||||||
options.name
|
options.name
|
||||||
)(prog, props, this.container, options);
|
)(prog, props, this.container, options);
|
||||||
|
@ -499,10 +499,7 @@ export class ElasticHandlebarsVisitor extends Handlebars.Visitor {
|
||||||
? this.resolveNodes(partial.name).join('')
|
? this.resolveNodes(partial.name).join('')
|
||||||
: (partial.name as hbs.AST.PathExpression).original;
|
: (partial.name as hbs.AST.PathExpression).original;
|
||||||
|
|
||||||
const options: AmbiguousHelperOptions & Handlebars.ResolvePartialOptions = this.setupParams(
|
const options: AmbiguousHelperOptions & ResolvePartialOptions = this.setupParams(partial, name);
|
||||||
partial,
|
|
||||||
name
|
|
||||||
);
|
|
||||||
options.helpers = this.container.helpers;
|
options.helpers = this.container.helpers;
|
||||||
options.partials = this.container.partials;
|
options.partials = this.container.partials;
|
||||||
options.decorators = this.container.decorators;
|
options.decorators = this.container.decorators;
|
||||||
|
@ -516,7 +513,7 @@ export class ElasticHandlebarsVisitor extends Handlebars.Visitor {
|
||||||
// Wrapper function to get access to currentPartialBlock from the closure
|
// Wrapper function to get access to currentPartialBlock from the closure
|
||||||
partialBlock = options.data['partial-block'] = function partialBlockWrapper(
|
partialBlock = options.data['partial-block'] = function partialBlockWrapper(
|
||||||
context: any,
|
context: any,
|
||||||
wrapperOptions: { data?: Handlebars.HelperOptions['data'] } = {}
|
wrapperOptions: { data?: HelperOptions['data'] } = {}
|
||||||
) {
|
) {
|
||||||
// Restore the partial-block from the closure for the execution of the block
|
// Restore the partial-block from the closure for the execution of the block
|
||||||
// i.e. the part inside the block of the partial call.
|
// i.e. the part inside the block of the partial call.
|
||||||
|
@ -542,10 +539,17 @@ export class ElasticHandlebarsVisitor extends Handlebars.Visitor {
|
||||||
context = Object.assign({}, context, options.hash);
|
context = Object.assign({}, context, options.hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
const partialTemplate: Handlebars.Template | undefined =
|
const partialTemplate: Template | undefined =
|
||||||
this.container.partials[name] ??
|
this.container.partials[name] ??
|
||||||
partialBlock ??
|
partialBlock ??
|
||||||
Handlebars.VM.resolvePartial(undefined, undefined, options);
|
// TypeScript note: We extend ResolvePartialOptions in our types.ts file
|
||||||
|
// to fix an error in the upstream type. When calling back into the
|
||||||
|
// upstream code, we just cast back to the non-extended type
|
||||||
|
Handlebars.VM.resolvePartial(
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
options as Handlebars.ResolvePartialOptions
|
||||||
|
);
|
||||||
|
|
||||||
if (partialTemplate === undefined) {
|
if (partialTemplate === undefined) {
|
||||||
throw new Handlebars.Exception(`The partial ${name} could not be found`);
|
throw new Handlebars.Exception(`The partial ${name} could not be found`);
|
||||||
|
@ -619,7 +623,7 @@ export class ElasticHandlebarsVisitor extends Handlebars.Visitor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private setupHelper(node: ProcessableNode, helperName: string): Helper {
|
private setupHelper(node: ProcessableNode, helperName: string): VisitorHelper {
|
||||||
return {
|
return {
|
||||||
fn: this.container.lookupProperty(this.container.helpers, helperName),
|
fn: this.container.lookupProperty(this.container.helpers, helperName),
|
||||||
context: this.context,
|
context: this.context,
|
||||||
|
@ -649,11 +653,11 @@ export class ElasticHandlebarsVisitor extends Handlebars.Visitor {
|
||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
|
|
||||||
private setupParams(node: ProcessableBlockStatementNode, name: string): Handlebars.HelperOptions;
|
private setupParams(node: ProcessableBlockStatementNode, name: string): HelperOptions;
|
||||||
private setupParams(node: ProcessableStatementNode, name: string): NonBlockHelperOptions;
|
private setupParams(node: ProcessableStatementNode, name: string): NonBlockHelperOptions;
|
||||||
private setupParams(node: ProcessableNode, name: string): AmbiguousHelperOptions;
|
private setupParams(node: ProcessableNode, name: string): AmbiguousHelperOptions;
|
||||||
private setupParams(node: ProcessableNode, name: string): AmbiguousHelperOptions {
|
private setupParams(node: ProcessableNode, name: string) {
|
||||||
const options = {
|
const options: AmbiguousHelperOptions = {
|
||||||
name,
|
name,
|
||||||
hash: this.getHash(node),
|
hash: this.getHash(node),
|
||||||
data: this.runtimeOptions!.data,
|
data: this.runtimeOptions!.data,
|
||||||
|
@ -662,10 +666,10 @@ export class ElasticHandlebarsVisitor extends Handlebars.Visitor {
|
||||||
};
|
};
|
||||||
|
|
||||||
if (isBlock(node)) {
|
if (isBlock(node)) {
|
||||||
(options as Handlebars.HelperOptions).fn = node.program
|
(options as HelperOptions).fn = node.program
|
||||||
? this.processDecorators(node.program, this.generateProgramFunction(node.program))
|
? this.processDecorators(node.program, this.generateProgramFunction(node.program))
|
||||||
: noop;
|
: noop;
|
||||||
(options as Handlebars.HelperOptions).inverse = node.inverse
|
(options as HelperOptions).inverse = node.inverse
|
||||||
? this.processDecorators(node.inverse, this.generateProgramFunction(node.inverse))
|
? this.processDecorators(node.inverse, this.generateProgramFunction(node.inverse))
|
||||||
: noop;
|
: noop;
|
||||||
}
|
}
|
||||||
|
@ -676,10 +680,7 @@ export class ElasticHandlebarsVisitor extends Handlebars.Visitor {
|
||||||
private generateProgramFunction(program: hbs.AST.Program) {
|
private generateProgramFunction(program: hbs.AST.Program) {
|
||||||
if (!program) return noop;
|
if (!program) return noop;
|
||||||
|
|
||||||
const prog: Handlebars.TemplateDelegate = (
|
const prog: TemplateDelegate = (nextContext: any, runtimeOptions: RuntimeOptions = {}) => {
|
||||||
nextContext: any,
|
|
||||||
runtimeOptions: ExtendedRuntimeOptions = {}
|
|
||||||
) => {
|
|
||||||
runtimeOptions = { ...runtimeOptions };
|
runtimeOptions = { ...runtimeOptions };
|
||||||
|
|
||||||
// inherit data in blockParams from parent program
|
// inherit data in blockParams from parent program
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
* Side Public License, v 1.
|
* Side Public License, v 1.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import Handlebars from '@kbn/handlebars';
|
import Handlebars, { type HelperOptions, type HelperDelegate } from '@kbn/handlebars';
|
||||||
import { encode } from '@kbn/rison';
|
import { encode } from '@kbn/rison';
|
||||||
import dateMath from '@kbn/datemath';
|
import dateMath from '@kbn/datemath';
|
||||||
import moment, { Moment } from 'moment';
|
import moment, { Moment } from 'moment';
|
||||||
|
@ -18,9 +18,9 @@ const handlebars = Handlebars.create();
|
||||||
function createSerializationHelper(
|
function createSerializationHelper(
|
||||||
fnName: string,
|
fnName: string,
|
||||||
serializeFn: (value: unknown) => string
|
serializeFn: (value: unknown) => string
|
||||||
): Handlebars.HelperDelegate {
|
): HelperDelegate {
|
||||||
return (...args) => {
|
return (...args) => {
|
||||||
const { hash } = args.slice(-1)[0] as Handlebars.HelperOptions;
|
const { hash } = args.slice(-1)[0] as HelperOptions;
|
||||||
const hasHash = Object.keys(hash).length > 0;
|
const hasHash = Object.keys(hash).length > 0;
|
||||||
const hasValues = args.length > 1;
|
const hasValues = args.length > 1;
|
||||||
if (hasHash && hasValues) {
|
if (hasHash && hasValues) {
|
||||||
|
@ -49,7 +49,7 @@ handlebars.registerHelper(
|
||||||
|
|
||||||
handlebars.registerHelper('date', (...args) => {
|
handlebars.registerHelper('date', (...args) => {
|
||||||
const values = args.slice(0, -1) as [string | Date, string | undefined];
|
const values = args.slice(0, -1) as [string | Date, string | undefined];
|
||||||
const { hash } = args.slice(-1)[0] as Handlebars.HelperOptions;
|
const { hash } = args.slice(-1)[0] as HelperOptions;
|
||||||
// eslint-disable-next-line prefer-const
|
// eslint-disable-next-line prefer-const
|
||||||
let [date, format] = values;
|
let [date, format] = values;
|
||||||
if (typeof date === 'undefined') throw new Error(`[date]: unknown variable`);
|
if (typeof date === 'undefined') throw new Error(`[date]: unknown variable`);
|
||||||
|
|
|
@ -7,7 +7,12 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { encode } from '@kbn/rison';
|
import { encode } from '@kbn/rison';
|
||||||
import Handlebars, { type ExtendedCompileOptions, compileFnName } from '@kbn/handlebars';
|
import Handlebars, {
|
||||||
|
type CompileOptions,
|
||||||
|
type HelperOptions,
|
||||||
|
type HelperDelegate,
|
||||||
|
compileFnName,
|
||||||
|
} from '@kbn/handlebars';
|
||||||
import { i18n } from '@kbn/i18n';
|
import { i18n } from '@kbn/i18n';
|
||||||
import { emptyLabel } from '../../../../common/empty_label';
|
import { emptyLabel } from '../../../../common/empty_label';
|
||||||
|
|
||||||
|
@ -16,9 +21,9 @@ const handlebars = Handlebars.create();
|
||||||
function createSerializationHelper(
|
function createSerializationHelper(
|
||||||
fnName: string,
|
fnName: string,
|
||||||
serializeFn: (value: unknown) => string
|
serializeFn: (value: unknown) => string
|
||||||
): Handlebars.HelperDelegate {
|
): HelperDelegate {
|
||||||
return (...args) => {
|
return (...args) => {
|
||||||
const { hash } = args.slice(-1)[0] as Handlebars.HelperOptions;
|
const { hash } = args.slice(-1)[0] as HelperOptions;
|
||||||
const hasHash = Object.keys(hash).length > 0;
|
const hasHash = Object.keys(hash).length > 0;
|
||||||
const hasValues = args.length > 1;
|
const hasValues = args.length > 1;
|
||||||
if (hasHash && hasValues) {
|
if (hasHash && hasValues) {
|
||||||
|
@ -53,7 +58,7 @@ export function replaceVars(
|
||||||
str: string,
|
str: string,
|
||||||
args: Record<string, unknown> = {},
|
args: Record<string, unknown> = {},
|
||||||
vars: Record<string, unknown> = {},
|
vars: Record<string, unknown> = {},
|
||||||
compileOptions: Partial<ExtendedCompileOptions> = {}
|
compileOptions: Partial<CompileOptions> = {}
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
/** we need add '[]' for emptyLabel because this value contains special characters.
|
/** we need add '[]' for emptyLabel because this value contains special characters.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue