diff --git a/src/ts/di/Configuration.ts b/src/ts/di/Configuration.ts new file mode 100644 --- /dev/null +++ b/src/ts/di/Configuration.ts @@ -0,0 +1,142 @@ +import { ServiceRegistration, TypeRegistration, FactoryRegistration, ServiceMap, Descriptor, isDescriptor, isDependencyRegistration, DependencyRegistration, ValueRegistration, ActivationType, isValueRegistration, isTypeRegistration, isFactoryRegistration } from "./interfaces"; +import { isNullOrEmptyString, argumentNotEmptyString, isPrimitive } from "../safe"; +import { AggregateDescriptor } from "./AggregateDescriptor"; +import { ValueDescriptor } from "./ValueDescriptor"; +import { ServiceDescriptorParams } from "./ServiceDescriptor"; +import { Container } from "./Container"; +import { Constructor } from "../interfaces"; + +interface MapOf { + [key: string]: T; +} + +type _key = string | number; + +export class Configuration { + + _hasInnerDescriptors = false; + + _container: Container; + + _path: Array<_key>; + + async _visitRegistrations(data, name: _key) { + this._path.push(name); + + if (data.constructor && + data.constructor.prototype !== Object.prototype) + throw new Error("Configuration must be a simple object"); + + const o: ServiceMap = {}; + const keys = Object.keys(data); + + const res = await Promise.all(keys.map(k => this._visit(data[k], k))); + keys.forEach((k, i) => { + o[k] = isDescriptor(res[i]) ? res[i] : new AggregateDescriptor(res[i]); + }); + + this._path.pop(); + + return o; + } + + async _visit(data, name: string): Promise { + if (isPrimitive(data) || isDescriptor(data)) + return data; + + if (isDependencyRegistration(data)) { + return this._visitDependencyRegistration(data, name); + } else if (isValueRegistration(data)) { + return this._visitValueRegistration(data, name); + } else if (isTypeRegistration(data)) { + return this._visitTypeRegistration(data, name); + } else if (isFactoryRegistration(data)) { + return this._visitFactoryRegistration(data, name); + } else if (data instanceof Array) { + return this._visitArray(data, name); + } + + return this._visitObject(data, name); + } + + async _resolveType(moduleName: string, typeName: string): Promise { + + } + + async _visitObject(data: object, name: _key): Promise { + this._path.push(name); + this._path.pop(); + } + + async _visitArray(data: any[], name: _key): Promise { + this._path.push(name); + this._path.pop(); + } + + async _makeServiceParams(data: ServiceRegistration) { + const opts: any = {}; + if (data.services) + opts.services = await this._visitRegistrations(data.services, "services"); + + if (data.inject) { + if (data.inject instanceof Array) { + this._path.push("inject"); + opts.inject = Promise.all(data.inject.map((x, i) => this._visitObject(x, i))); + this._path.pop(); + } else { + opts.inject = [this._visitObject(data.inject, "inject")]; + } + } + + if (data.params) + opts.params = await this._visit(data.params, "params"); + + if (data.activation) { + if (typeof (data.activation) === "string") { + switch (data.activation.toLowerCase()) { + case "singleton": + opts.activation = ActivationType.Singleton; + break; + case "container": + opts.activation = ActivationType.Container; + break; + case "hierarchy": + opts.activation = ActivationType.Hierarchy; + break; + case "context": + opts.activation = ActivationType.Context; + break; + case "call": + opts.activation = ActivationType.Call; + break; + default: + throw new Error("Unknown activation type: " + + data.activation); + } + } else { + opts.activation = Number(data.activation); + } + } + + if (data.cleanup) + opts.cleanup = data.cleanup; + } + + async _visitValueRegistration(item: ValueRegistration, name: _key) { + this._path.push(name); + this._path.pop(); + } + + async _visitDependencyRegistration(item: DependencyRegistration, name: _key) { + this._path.push(name); + this._path.pop(); + } + + async _visitTypeRegistration(item: TypeRegistration, name: _key) { + argumentNotEmptyString(item.$type, "item.$type"); + } + + async _visitFactoryRegistration(item: FactoryRegistration, name: _key) { + argumentNotEmptyString(item.$factory, "item.$type"); + } +} diff --git a/src/ts/di/interfaces.ts b/src/ts/di/interfaces.ts --- a/src/ts/di/interfaces.ts +++ b/src/ts/di/interfaces.ts @@ -28,9 +28,6 @@ export interface RegistrationWithService } export interface ServiceRegistration extends RegistrationWithServices { - $type?: string | Constructor; - - $factory?: string | Factory; activation?: "singleton" | "container" | "hierarchy" | "context" | "call"; @@ -38,7 +35,15 @@ export interface ServiceRegistration ext inject?: object | object[]; - cleanup: (instance) => void | string; + cleanup?: (instance) => void | string; +} + +export interface TypeRegistration extends ServiceRegistration { + $type: string | Constructor; +} + +export interface FactoryRegistration extends ServiceRegistration { + $factory: string | Factory; } export interface ValueRegistration { @@ -53,7 +58,11 @@ export interface DependencyRegistration default?; } -export function isServiceRegistration(x): x is ServiceRegistration { +export function isTypeRegistration(x): x is TypeRegistration { + return (!isPrimitive(x)) && ("$type" in x || "$factory" in x); +} + +export function isFactoryRegistration(x): x is FactoryRegistration { return (!isPrimitive(x)) && ("$type" in x || "$factory" in x); }