redux-providers
Version:
Minimalist dependency injection system for redux. Create providers to be injected and used in redux reducers.
74 lines (62 loc) • 2.86 kB
text/typescript
import "reflect-metadata";
import { Constructable } from "../types/constructable";
import { METADATA_KEY } from "../constants/metadata.keys";
import { InstanceContainer } from "../container/instance-container";
import { ActionHandler } from "../decorators/action/action";
import { Provider } from "../decorators/provider/provider";
export namespace Resolver {
/**
* Resolve provided target class (either @Provider or @ReduxAction)
*
* @param target the target to resolve
* @param container the container to keep the instance in
* @param parentModuleName name of the module the target is provided in (used i.e. for error messages)
*/
export function resolve<T>(target: Constructable<T>, container: InstanceContainer, parentModuleName: string): T {
const isProvider = Reflect.hasOwnMetadata(METADATA_KEY.PROVIDER_PARAM_TYPES, target);
if (isProvider) {
return resolveProvider<T>(target, container, parentModuleName);
}
const isReduxAction = Reflect.hasOwnMetadata(METADATA_KEY.ACTION_HANDLER_PARAM_TYPES, target);
if (isReduxAction) {
return resolveAction<T>(target, container, parentModuleName);
}
const allowedDecorators = [Provider.name, ActionHandler.name];
throw new Error("Target must be one of " + allowedDecorators + "!");
}
function resolveProvider<T>(target: Constructable<T>, container: InstanceContainer, parentModuleName: string): T {
if (!Reflect.hasOwnMetadata(METADATA_KEY.PROVIDER_PARAM_TYPES, target)) {
throw new Error("Attempting to resolve target with name: " + target.name + ". " + target.name + " is not a provider in module: " + parentModuleName + "!");
}
if (containerHasElement(container, target.name)) {
return container.get(target.name);
}
let designParamTypes = Reflect.getMetadata(METADATA_KEY.PROVIDER_PARAM_TYPES, target) || [];
let injections = designParamTypes.map((token: Constructable<T>) => {
return resolveProvider<T>(token, container, parentModuleName);
});
let newInstance = new target(...injections);
container.add(target.name, newInstance);
return newInstance;
}
function resolveAction<T>(target: Constructable<T>, container: InstanceContainer, parentModuleName: string): T {
if (containerHasElement(container, target.name)) {
return container.get(target.name);
}
let reducActionDesignParamTypes = Reflect.getMetadata(METADATA_KEY.ACTION_HANDLER_PARAM_TYPES, target) || [];
let injections = reducActionDesignParamTypes.map((token: Constructable<T>) => {
return resolve<T>(token, container, parentModuleName);
});
let newInstance = new target(...injections);
container.add(target.name, newInstance);
return newInstance;
}
function containerHasElement(container: InstanceContainer, instanceIdentifier: string) {
try {
container.get(instanceIdentifier);
return true;
} catch (e) {
return false;
}
}
}