diff --git a/addon/initializers/install-function-helper-manager.ts b/addon/initializers/install-function-helper-manager.ts index 64be1e1..0aa585b 100644 --- a/addon/initializers/install-function-helper-manager.ts +++ b/addon/initializers/install-function-helper-manager.ts @@ -3,7 +3,10 @@ // typed-ember doesn't have types for `@ember/helper` yet // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore -import { capabilities as helperCapabilities, setHelperManager } from '@ember/helper'; +import { + capabilities as helperCapabilities, + setHelperManager, +} from '@ember/helper'; import type { Arguments } from '../-private/local-glimmer-interfaces-types'; @@ -18,6 +21,8 @@ export default { initialize, }; +type Owner = unknown; + type FnArgs = | [...Args['positional'], Args['named']] | [...Args['positional']]; @@ -36,6 +41,8 @@ export class FunctionHelperManager { hasScheduledEffect: false, }); + owner?: Owner = undefined; + createHelper(fn: AnyFunction, args: Arguments): State { return { fn, args }; } @@ -44,10 +51,10 @@ export class FunctionHelperManager { if (Object.keys(args.named).length > 0) { let argsForFn: FnArgs = [...args.positional, args.named]; - return fn(...argsForFn); + return fn.apply(this.owner, argsForFn); } - return fn(...args.positional); + return fn.apply(this.owner, args.positional); } getDebugName(fn: AnyFunction): string { @@ -61,4 +68,10 @@ export class FunctionHelperManager { const FUNCTIONAL_HELPER_MANAGER = new FunctionHelperManager(); -setHelperManager(() => FUNCTIONAL_HELPER_MANAGER, Function.prototype); +setHelperManager( + (owner: Owner) => + Object.create(FUNCTIONAL_HELPER_MANAGER, { + owner: { value: owner, writable: false, configurable: false }, + }), + Function.prototype +); diff --git a/tests/rendering/functions-test.ts b/tests/rendering/functions-test.ts index 4c4bc38..23ef64a 100644 --- a/tests/rendering/functions-test.ts +++ b/tests/rendering/functions-test.ts @@ -4,6 +4,16 @@ import { hbs } from 'ember-cli-htmlbars'; import { module, test } from 'qunit'; import { setupRenderingTest } from 'ember-qunit'; +interface Owner { + lookup( + key: string, + options?: { + singleton?: boolean | undefined; + instantiate?: boolean | undefined; + } + ): unknown; +} + module('function helpers', function (hooks) { setupRenderingTest(hooks); @@ -100,4 +110,29 @@ module('function helpers', function (hooks) { 'x is 4, and opts.a is 6', ]); }); + + test('the owner is bound as `this` context', async function (assert) { + function lookup( + this: Owner, + identifier: string, + options?: object + ): unknown { + return this.lookup(identifier, options); + } + + const testIdentifier = 'test-value:foo'; + const testValue = 'foo'; + + this.owner.register( + testIdentifier, + { string: testValue }, + { instantiate: false } + ); + + this.setProperties({ lookup, testIdentifier }); + + await render(hbs`{{get (this.lookup this.testIdentifier) "string"}}`); + + assert.dom().hasText(testValue); + }); });