From 1d12a96b4dc3ca77e6d9f2ffce49f19fc43d350f Mon Sep 17 00:00:00 2001 From: Jack Ellis Date: Mon, 7 Oct 2024 14:55:16 +0100 Subject: [PATCH] feat: defer --- src/__tests__/defer.test.ts | 75 +++++++++++++++++++++++++++++++++++++ src/defer.ts | 5 +++ src/makeJpex.ts | 2 + src/types/JpexInstance.ts | 3 ++ yarn.lock | 6 +-- 5 files changed, 88 insertions(+), 3 deletions(-) create mode 100644 src/__tests__/defer.test.ts create mode 100644 src/defer.ts diff --git a/src/__tests__/defer.test.ts b/src/__tests__/defer.test.ts new file mode 100644 index 0000000..16580ef --- /dev/null +++ b/src/__tests__/defer.test.ts @@ -0,0 +1,75 @@ +import _jpex, { Jpex } from '..'; + +let jpex: Jpex; +type Foo = (v: string) => string; +type FooAsync = (v: string) => Promise; +type Bar = string; + +beforeEach(() => { + jpex = _jpex.extend(); + + jpex.factory((bar: Bar) => (v: string) => `${v}foo${bar}`); + jpex.factory(() => 'bar'); + jpex.factory((foo: Foo) => async (v: string) => `${foo(v)}async`); +}); + +it('returns a function', () => { + const foo = jpex.defer(); + + expect(foo).toBeInstanceOf(Function); +}); + +it('does not resolve any dependencies at creation time', () => { + jpex.defer(); + + expect(jpex.$$factories[jpex.infer()]?.resolved).toBeFalsy(); + expect(jpex.$$resolved[jpex.infer()]).toBeFalsy(); + expect(jpex.$$factories[jpex.infer()]?.resolved).toBeFalsy(); + expect(jpex.$$resolved[jpex.infer()]).toBeFalsy(); +}); + +it('resolves and calls the factory at call time', () => { + const foo = jpex.defer(); + + const result = foo('provided'); + + expect(result).toBe('providedfoobar'); +}); + +it('works with async factories', async () => { + const foo = jpex.defer(); + + const result = await foo('provided'); + + expect(result).toBe('providedfoobarasync'); +}); + +it('caches the inner function', () => { + const spyFactory = jest.fn(() => () => 'spy'); + + jpex.factory(spyFactory); + + const foo = jpex.defer(); + + expect(spyFactory).not.toHaveBeenCalled(); + + foo('provided'); + + expect(spyFactory).toHaveBeenCalledTimes(1); + + foo('provided'); + + expect(spyFactory).toHaveBeenCalledTimes(1); + + jpex.clearCache(); + + foo('provided'); + expect(spyFactory).toHaveBeenCalledTimes(2); +}); + +it('keeps a list of deferred dependencies', () => { + jpex.defer(); + + expect(jpex.$$deps).toContain(jpex.infer()); + expect(jpex.$$deps).toContain(jpex.infer()); +}); diff --git a/src/defer.ts b/src/defer.ts new file mode 100644 index 0000000..85fa57a --- /dev/null +++ b/src/defer.ts @@ -0,0 +1,5 @@ +import { Dependency, JpexInstance } from './types'; + +export default function defer(this: JpexInstance, name: Dependency) { + return this.encase([name], (fn) => fn); +} diff --git a/src/makeJpex.ts b/src/makeJpex.ts index 64e3ca8..68af872 100644 --- a/src/makeJpex.ts +++ b/src/makeJpex.ts @@ -3,6 +3,7 @@ import { constant, factory, service, alias, factoryAsync } from './registers'; import { resolve, getFactory, resolveAsync } from './resolver'; import encase from './encase'; import clearCache from './clearCache'; +import defer from './defer'; const defaultConfig = { lifecycle: 'container' as const, @@ -35,6 +36,7 @@ export default function makeJpex( resolve, resolveAsync, encase, + defer, clearCache, extend(config?: SetupConfig): IJpex { return makeJpex(config, this); diff --git a/src/types/JpexInstance.ts b/src/types/JpexInstance.ts index 6cbbca6..2d6b665 100644 --- a/src/types/JpexInstance.ts +++ b/src/types/JpexInstance.ts @@ -205,6 +205,9 @@ export interface JpexInstance { fn: F, ): ReturnType & { encased: F }; + defer(): T; + defer(name: Dependency): T; + raw(name: Dependency): AnyFunction; raw(): AnyFunction; raw(name: Dependency): AnyFunction; diff --git a/yarn.lock b/yarn.lock index c0ef0cf..9d4e716 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1275,9 +1275,9 @@ chalk "^4.0.0" "@jpex-js/babel-plugin@^1.3.0": - version "1.8.0" - resolved "https://registry.yarnpkg.com/@jpex-js/babel-plugin/-/babel-plugin-1.8.0.tgz#c0318f45762fdccc440e851be04c7e7216e6564b" - integrity sha512-B5SGp2PcdCXWgob3eV2YmA205xqG8qxJuYg3ZFiKvFGJCMvjqnrHx8HAG9NH43IFGry3rt3lCZCiito/dEy9jw== + version "1.9.0" + resolved "https://registry.yarnpkg.com/@jpex-js/babel-plugin/-/babel-plugin-1.9.0.tgz#758fe356db62179465c88f05fc0b5e2df9dbbaac" + integrity sha512-q4LIMl/CW5BoReuYfKvNGSxXAmR/bmqM2ekmJlUmG/aFCgEZIYh5FhLYGU4cQ1dcBcIZBB94m2d9+Esu36RBng== "@marionebl/sander@^0.6.0": version "0.6.1"