backend-plugin-api: make factory funcs use params and switch service factories to new pattern

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
Patrik Oldsberg
2023-01-12 01:12:22 +01:00
parent 01a318f75d
commit ce660bfd3d
4 changed files with 108 additions and 44 deletions
@@ -16,9 +16,12 @@
import { createServiceFactory, createServiceRef } from './types';
const ref = createServiceRef<string>({ id: 'x' });
const rootDep = createServiceRef<number>({ id: 'y', scope: 'root' });
const pluginDep = createServiceRef<boolean>({ id: 'z' });
describe('createServiceFactory', () => {
it('should create a meta factory with no options', () => {
const ref = createServiceRef<string>({ id: 'x' });
const metaFactory = createServiceFactory({
service: ref,
deps: {},
@@ -37,12 +40,12 @@ describe('createServiceFactory', () => {
metaFactory({ x: 1 });
// @ts-expect-error
metaFactory(null);
// @ts-expect-error
metaFactory(undefined);
metaFactory();
});
it('should create a meta factory with optional options', () => {
const ref = createServiceRef<string>({ id: 'x' });
const metaFactory = createServiceFactory((_opts?: { x: number }) => ({
service: ref,
deps: {},
@@ -66,7 +69,6 @@ describe('createServiceFactory', () => {
});
it('should create a meta factory with required options', () => {
const ref = createServiceRef<string>({ id: 'x' });
const metaFactory = createServiceFactory((_opts: { x: number }) => ({
service: ref,
deps: {},
@@ -95,7 +97,6 @@ describe('createServiceFactory', () => {
interface TestOptions {
x: number;
}
const ref = createServiceRef<string>({ id: 'x' });
const metaFactory = createServiceFactory((_opts?: TestOptions) => ({
service: ref,
deps: {},
@@ -122,7 +123,6 @@ describe('createServiceFactory', () => {
interface TestOptions {
x: number;
}
const ref = createServiceRef<string>({ id: 'x' });
const metaFactory = createServiceFactory((_opts: TestOptions) => ({
service: ref,
deps: {},
@@ -147,8 +147,91 @@ describe('createServiceFactory', () => {
metaFactory();
});
it('should create factory with required options and dependencies', () => {
interface TestOptions {
x: number;
}
function unused(..._any: any[]) {}
const metaFactory = createServiceFactory((_opts: TestOptions) => ({
service: ref,
deps: {
root: rootDep,
plugin: pluginDep,
},
async factory({ root }) {
const root1: number = root;
// @ts-expect-error
const root2: string = root;
return async ({ plugin }) => {
const plugin3: boolean = plugin;
// @ts-expect-error
const plugin4: number = plugin;
unused(root1, root2, plugin3, plugin4);
return 'x';
};
},
}));
expect(metaFactory).toEqual(expect.any(Function));
// @ts-expect-error
metaFactory('string');
// @ts-expect-error
metaFactory({});
metaFactory({ x: 1 });
// @ts-expect-error
metaFactory({ x: 1, y: 2 });
// @ts-expect-error
metaFactory(null);
// @ts-expect-error
metaFactory(undefined);
// @ts-expect-error
metaFactory();
});
it('should create factory with optional options and dependencies', () => {
interface TestOptions {
x: number;
}
function unused(..._any: any[]) {}
const metaFactory = createServiceFactory((_opts?: TestOptions) => ({
service: ref,
deps: {
root: rootDep,
plugin: pluginDep,
},
async factory({ root }) {
const root1: number = root;
// @ts-expect-error
const root2: string = root;
return async ({ plugin }) => {
const plugin3: boolean = plugin;
// @ts-expect-error
const plugin4: number = plugin;
unused(root1, root2, plugin3, plugin4);
return 'x';
};
},
}));
expect(metaFactory).toEqual(expect.any(Function));
// @ts-expect-error
metaFactory('string');
// @ts-expect-error
metaFactory({});
metaFactory({ x: 1 });
// @ts-expect-error
metaFactory({ x: 1, y: 2 });
// @ts-expect-error
metaFactory(null);
metaFactory(undefined);
metaFactory();
});
it('should only allow objects as options', () => {
const ref = createServiceRef<string>({ id: 'x' });
// @ts-expect-error
const metaFactory = createServiceFactory((_opts: string) => ({
service: ref,
@@ -14,11 +14,7 @@
* limitations under the License.
*/
import {
FactoryFunctionConfig,
FactoryFunctionWithOptions,
MaybeOptions,
} from '../../types';
import { FactoryFunctionConfig, FactoryFunction } from '../../types';
/**
* TODO
@@ -137,9 +133,7 @@ type ServiceRefsToInstances<
T extends { [key in string]: ServiceRef<unknown> },
TScope extends 'root' | 'plugin' = 'root' | 'plugin',
> = {
[name in {
[key in keyof T]: T[key] extends ServiceRef<unknown, TScope> ? key : never;
}[keyof T]]: T[name] extends ServiceRef<infer TImpl> ? TImpl : never;
[key in keyof T as T[key]['scope'] extends TScope ? key : never]: T[key]['T'];
};
/** @public */
@@ -166,16 +160,16 @@ export function createServiceFactory<
TScope extends 'root' | 'plugin',
TImpl extends TService,
TDeps extends { [name in string]: ServiceRef<unknown> },
TOpts extends MaybeOptions = undefined,
TOpts extends [options?: object] = [],
>(
config: FactoryFunctionConfig<
ServiceFactoryConfig<TService, TScope, TImpl, TDeps>,
TOpts
>,
): FactoryFunctionWithOptions<ServiceFactory<TService>, TOpts> {
): FactoryFunction<ServiceFactory<TService>, TOpts> {
if (typeof config === 'function') {
return (opts?: TOpts) => {
const c = config(opts!);
return (...opts: TOpts) => {
const c = config(...opts);
return { ...c, scope: c.service.scope } as ServiceFactory<TService>;
};
}
+5 -14
View File
@@ -15,26 +15,17 @@
*/
/**
* Base type for options objects that aren't required.
*
* @ignore
*/
export type MaybeOptions = object | undefined;
/**
* @ignore
*/
export type FactoryFunctionConfig<TConfig, TOptions> =
export type FactoryFunctionConfig<TConfig, TParams extends unknown[]> =
| TConfig
| ((options: TOptions) => TConfig)
| (() => TConfig);
| ((...params: TParams) => TConfig);
/**
* Helper type that makes the options argument optional if options are not required.
*
* @ignore
*/
export type FactoryFunctionWithOptions<TResult, TOptions> =
undefined extends TOptions
? (options?: TOptions) => TResult
: (options: TOptions) => TResult;
export type FactoryFunction<TResult, TParams extends unknown[]> = (
...params: TParams
) => TResult;
@@ -14,11 +14,7 @@
* limitations under the License.
*/
import {
FactoryFunctionWithOptions,
MaybeOptions,
FactoryFunctionConfig,
} from '../types';
import { FactoryFunction, FactoryFunctionConfig } from '../types';
import {
BackendRegistrationPoints,
BackendFeature,
@@ -53,11 +49,11 @@ export interface BackendPluginConfig {
}
/** @public */
export function createBackendPlugin<TOptions extends MaybeOptions = undefined>(
export function createBackendPlugin<TOptions extends [options?: object] = []>(
config: FactoryFunctionConfig<BackendPluginConfig, TOptions>,
): FactoryFunctionWithOptions<BackendFeature, TOptions> {
): FactoryFunction<BackendFeature, TOptions> {
if (typeof config === 'function') {
return config as FactoryFunctionWithOptions<BackendFeature, TOptions>;
return config as FactoryFunction<BackendFeature, TOptions>;
}
return () => config;
@@ -86,12 +82,12 @@ export interface BackendModuleConfig {
*
* The `pluginId` should exactly match the `id` of the plugin that the module extends.
*/
export function createBackendModule<TOptions extends MaybeOptions = undefined>(
export function createBackendModule<TOptions extends [options?: object] = []>(
config: FactoryFunctionConfig<BackendModuleConfig, TOptions>,
): FactoryFunctionWithOptions<BackendFeature, TOptions> {
): FactoryFunction<BackendFeature, TOptions> {
if (typeof config === 'function') {
return (options?: TOptions) => {
const c = config(options!);
return (...options: TOptions) => {
const c = config(...options);
return {
id: `${c.pluginId}.${c.moduleId}`,
register: c.register,