Comparing version 4.3.0 to 4.4.0
@@ -15,6 +15,22 @@ "use strict"; | ||
if (injection_token_1.isTokenDescriptor(type)) { | ||
return type.multiple | ||
? dependency_container_1.instance.resolveAll(type.token) | ||
: dependency_container_1.instance.resolve(type.token); | ||
if (injection_token_1.isTransformDescriptor(type)) { | ||
return type.multiple | ||
? dependency_container_1.instance | ||
.resolve(type.transform) | ||
.transform(dependency_container_1.instance.resolveAll(type.token), ...type.transformArgs) | ||
: dependency_container_1.instance | ||
.resolve(type.transform) | ||
.transform(dependency_container_1.instance.resolve(type.token), ...type.transformArgs); | ||
} | ||
else { | ||
return type.multiple | ||
? dependency_container_1.instance.resolveAll(type.token) | ||
: dependency_container_1.instance.resolve(type.token); | ||
} | ||
} | ||
else if (injection_token_1.isTransformDescriptor(type)) { | ||
return dependency_container_1.instance | ||
.resolve(type.transform) | ||
.transform(dependency_container_1.instance.resolve(type.token), ...type.transformArgs); | ||
} | ||
return dependency_container_1.instance.resolve(type); | ||
@@ -21,0 +37,0 @@ } |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var auto_injectable_1 = require("./auto-injectable"); | ||
exports.autoInjectable = auto_injectable_1.default; | ||
Object.defineProperty(exports, "autoInjectable", { enumerable: true, get: function () { return auto_injectable_1.default; } }); | ||
var inject_1 = require("./inject"); | ||
exports.inject = inject_1.default; | ||
Object.defineProperty(exports, "inject", { enumerable: true, get: function () { return inject_1.default; } }); | ||
var injectable_1 = require("./injectable"); | ||
exports.injectable = injectable_1.default; | ||
Object.defineProperty(exports, "injectable", { enumerable: true, get: function () { return injectable_1.default; } }); | ||
var registry_1 = require("./registry"); | ||
exports.registry = registry_1.default; | ||
Object.defineProperty(exports, "registry", { enumerable: true, get: function () { return registry_1.default; } }); | ||
var singleton_1 = require("./singleton"); | ||
exports.singleton = singleton_1.default; | ||
Object.defineProperty(exports, "singleton", { enumerable: true, get: function () { return singleton_1.default; } }); | ||
var inject_all_1 = require("./inject-all"); | ||
exports.injectAll = inject_all_1.default; | ||
Object.defineProperty(exports, "injectAll", { enumerable: true, get: function () { return inject_all_1.default; } }); | ||
var inject_all_with_transform_1 = require("./inject-all-with-transform"); | ||
Object.defineProperty(exports, "injectAllWithTransform", { enumerable: true, get: function () { return inject_all_with_transform_1.default; } }); | ||
var inject_with_transform_1 = require("./inject-with-transform"); | ||
Object.defineProperty(exports, "injectWithTransform", { enumerable: true, get: function () { return inject_with_transform_1.default; } }); | ||
var scoped_1 = require("./scoped"); | ||
exports.scoped = scoped_1.default; | ||
Object.defineProperty(exports, "scoped", { enumerable: true, get: function () { return scoped_1.default; } }); |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.instance = exports.typeInfo = void 0; | ||
const providers_1 = require("./providers"); | ||
@@ -11,2 +12,3 @@ const provider_1 = require("./providers/provider"); | ||
const lazy_helpers_1 = require("./lazy-helpers"); | ||
const interceptors_1 = require("./interceptors"); | ||
exports.typeInfo = new Map(); | ||
@@ -17,2 +19,3 @@ class InternalDependencyContainer { | ||
this._registry = new registry_1.default(); | ||
this.interceptors = new interceptors_1.default(); | ||
} | ||
@@ -27,2 +30,20 @@ register(token, providerOrConstructor, options = { lifecycle: lifecycle_1.default.Transient }) { | ||
} | ||
if (providers_1.isTokenProvider(provider)) { | ||
const path = [token]; | ||
let tokenProvider = provider; | ||
while (tokenProvider != null) { | ||
const currentToken = tokenProvider.useToken; | ||
if (path.includes(currentToken)) { | ||
throw new Error(`Token registration cycle detected! ${[...path, currentToken].join(" -> ")}`); | ||
} | ||
path.push(currentToken); | ||
const registration = this._registry.get(currentToken); | ||
if (registration && providers_1.isTokenProvider(registration.provider)) { | ||
tokenProvider = registration.provider; | ||
} | ||
else { | ||
tokenProvider = null; | ||
} | ||
} | ||
} | ||
if (options.lifecycle === lifecycle_1.default.Singleton || | ||
@@ -80,10 +101,39 @@ options.lifecycle == lifecycle_1.default.ContainerScoped || | ||
} | ||
this.executePreResolutionInterceptor(token, "Single"); | ||
if (registration) { | ||
return this.resolveRegistration(registration, context); | ||
const result = this.resolveRegistration(registration, context); | ||
this.executePostResolutionInterceptor(token, result, "Single"); | ||
return result; | ||
} | ||
if (injection_token_1.isConstructorToken(token)) { | ||
return this.construct(token, context); | ||
const result = this.construct(token, context); | ||
this.executePostResolutionInterceptor(token, result, "Single"); | ||
return result; | ||
} | ||
throw new Error("Attempted to construct an undefined constructor. Could mean a circular dependency problem. Try using `delay` function."); | ||
} | ||
executePreResolutionInterceptor(token, resolutionType) { | ||
if (this.interceptors.preResolution.has(token)) { | ||
const remainingInterceptors = []; | ||
for (const interceptor of this.interceptors.preResolution.getAll(token)) { | ||
if (interceptor.options.frequency != "Once") { | ||
remainingInterceptors.push(interceptor); | ||
} | ||
interceptor.callback(token, resolutionType); | ||
} | ||
this.interceptors.preResolution.setAll(token, remainingInterceptors); | ||
} | ||
} | ||
executePostResolutionInterceptor(token, result, resolutionType) { | ||
if (this.interceptors.postResolution.has(token)) { | ||
const remainingInterceptors = []; | ||
for (const interceptor of this.interceptors.postResolution.getAll(token)) { | ||
if (interceptor.options.frequency != "Once") { | ||
remainingInterceptors.push(interceptor); | ||
} | ||
interceptor.callback(token, result, resolutionType); | ||
} | ||
this.interceptors.postResolution.setAll(token, remainingInterceptors); | ||
} | ||
} | ||
resolveRegistration(registration, context) { | ||
@@ -129,6 +179,11 @@ if (registration.options.lifecycle === lifecycle_1.default.ResolutionScoped && | ||
} | ||
this.executePreResolutionInterceptor(token, "All"); | ||
if (registrations) { | ||
return registrations.map(item => this.resolveRegistration(item, context)); | ||
const result = registrations.map(item => this.resolveRegistration(item, context)); | ||
this.executePostResolutionInterceptor(token, result, "All"); | ||
return result; | ||
} | ||
return [this.construct(token, context)]; | ||
const result = [this.construct(token, context)]; | ||
this.executePostResolutionInterceptor(token, result, "All"); | ||
return result; | ||
} | ||
@@ -143,2 +198,4 @@ isRegistered(token, recursive = false) { | ||
this._registry.clear(); | ||
this.interceptors.preResolution.clear(); | ||
this.interceptors.postResolution.clear(); | ||
} | ||
@@ -172,2 +229,14 @@ clearInstances() { | ||
} | ||
beforeResolution(token, callback, options = { frequency: "Always" }) { | ||
this.interceptors.preResolution.set(token, { | ||
callback: callback, | ||
options: options | ||
}); | ||
} | ||
afterResolution(token, callback, options = { frequency: "Always" }) { | ||
this.interceptors.postResolution.set(token, { | ||
callback: callback, | ||
options: options | ||
}); | ||
} | ||
getRegistration(token) { | ||
@@ -195,8 +264,10 @@ if (this.isRegistered(token)) { | ||
} | ||
if (ctor.length === 0) { | ||
return new ctor(); | ||
} | ||
const paramInfo = exports.typeInfo.get(ctor); | ||
if (!paramInfo || paramInfo.length === 0) { | ||
throw new Error(`TypeInfo not known for "${ctor.name}"`); | ||
if (ctor.length === 0) { | ||
return new ctor(); | ||
} | ||
else { | ||
throw new Error(`TypeInfo not known for "${ctor.name}"`); | ||
} | ||
} | ||
@@ -210,6 +281,16 @@ const params = paramInfo.map(this.resolveParams(context, ctor)); | ||
if (injection_token_1.isTokenDescriptor(param)) { | ||
return param.multiple | ||
? this.resolveAll(param.token) | ||
: this.resolve(param.token, context); | ||
if (injection_token_1.isTransformDescriptor(param)) { | ||
return param.multiple | ||
? this.resolve(param.transform).transform(this.resolveAll(param.token), ...param.transformArgs) | ||
: this.resolve(param.transform).transform(this.resolve(param.token, context), ...param.transformArgs); | ||
} | ||
else { | ||
return param.multiple | ||
? this.resolveAll(param.token) | ||
: this.resolve(param.token, context); | ||
} | ||
} | ||
else if (injection_token_1.isTransformDescriptor(param)) { | ||
return this.resolve(param.transform, context).transform(this.resolve(param.token, context), ...param.transformArgs); | ||
} | ||
return this.resolve(param, context); | ||
@@ -216,0 +297,0 @@ } |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.formatErrorCtor = void 0; | ||
function formatDependency(params, idx) { | ||
@@ -4,0 +5,0 @@ if (params === null) { |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var instance_caching_factory_1 = require("./instance-caching-factory"); | ||
exports.instanceCachingFactory = instance_caching_factory_1.default; | ||
Object.defineProperty(exports, "instanceCachingFactory", { enumerable: true, get: function () { return instance_caching_factory_1.default; } }); | ||
var predicate_aware_class_factory_1 = require("./predicate-aware-class-factory"); | ||
exports.predicateAwareClassFactory = predicate_aware_class_factory_1.default; | ||
Object.defineProperty(exports, "predicateAwareClassFactory", { enumerable: true, get: function () { return predicate_aware_class_factory_1.default; } }); |
@@ -8,3 +8,3 @@ "use strict"; | ||
var types_1 = require("./types"); | ||
exports.Lifecycle = types_1.Lifecycle; | ||
Object.defineProperty(exports, "Lifecycle", { enumerable: true, get: function () { return types_1.Lifecycle; } }); | ||
tslib_1.__exportStar(require("./decorators"), exports); | ||
@@ -14,4 +14,4 @@ tslib_1.__exportStar(require("./factories"), exports); | ||
var lazy_helpers_1 = require("./lazy-helpers"); | ||
exports.delay = lazy_helpers_1.delay; | ||
Object.defineProperty(exports, "delay", { enumerable: true, get: function () { return lazy_helpers_1.delay; } }); | ||
var dependency_container_1 = require("./dependency-container"); | ||
exports.container = dependency_container_1.instance; | ||
Object.defineProperty(exports, "container", { enumerable: true, get: function () { return dependency_container_1.instance; } }); |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.delay = exports.DelayedConstructor = void 0; | ||
class DelayedConstructor { | ||
@@ -4,0 +5,0 @@ constructor(wrap) { |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.isClassProvider = void 0; | ||
function isClassProvider(provider) { | ||
@@ -4,0 +5,0 @@ return !!provider.useClass; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.isFactoryProvider = void 0; | ||
function isFactoryProvider(provider) { | ||
@@ -4,0 +5,0 @@ return !!provider.useFactory; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var class_provider_1 = require("./class-provider"); | ||
exports.isClassProvider = class_provider_1.isClassProvider; | ||
Object.defineProperty(exports, "isClassProvider", { enumerable: true, get: function () { return class_provider_1.isClassProvider; } }); | ||
var factory_provider_1 = require("./factory-provider"); | ||
exports.isFactoryProvider = factory_provider_1.isFactoryProvider; | ||
Object.defineProperty(exports, "isFactoryProvider", { enumerable: true, get: function () { return factory_provider_1.isFactoryProvider; } }); | ||
var injection_token_1 = require("./injection-token"); | ||
exports.isNormalToken = injection_token_1.isNormalToken; | ||
Object.defineProperty(exports, "isNormalToken", { enumerable: true, get: function () { return injection_token_1.isNormalToken; } }); | ||
var token_provider_1 = require("./token-provider"); | ||
exports.isTokenProvider = token_provider_1.isTokenProvider; | ||
Object.defineProperty(exports, "isTokenProvider", { enumerable: true, get: function () { return token_provider_1.isTokenProvider; } }); | ||
var value_provider_1 = require("./value-provider"); | ||
exports.isValueProvider = value_provider_1.isValueProvider; | ||
Object.defineProperty(exports, "isValueProvider", { enumerable: true, get: function () { return value_provider_1.isValueProvider; } }); |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.isConstructorToken = exports.isTransformDescriptor = exports.isTokenDescriptor = exports.isNormalToken = void 0; | ||
const lazy_helpers_1 = require("../lazy-helpers"); | ||
@@ -14,2 +15,8 @@ function isNormalToken(token) { | ||
exports.isTokenDescriptor = isTokenDescriptor; | ||
function isTransformDescriptor(descriptor) { | ||
return (typeof descriptor === "object" && | ||
"token" in descriptor && | ||
"transform" in descriptor); | ||
} | ||
exports.isTransformDescriptor = isTransformDescriptor; | ||
function isConstructorToken(token) { | ||
@@ -16,0 +23,0 @@ return typeof token === "function" || token instanceof lazy_helpers_1.DelayedConstructor; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.isProvider = void 0; | ||
const class_provider_1 = require("./class-provider"); | ||
@@ -4,0 +5,0 @@ const value_provider_1 = require("./value-provider"); |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.isTokenProvider = void 0; | ||
function isTokenProvider(provider) { | ||
@@ -4,0 +5,0 @@ return !!provider.useToken; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.isValueProvider = void 0; | ||
function isValueProvider(provider) { | ||
@@ -4,0 +5,0 @@ return provider.useValue != undefined; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.defineInjectionTokenMetadata = exports.getParamInfo = exports.INJECTION_TOKEN_METADATA_KEY = void 0; | ||
exports.INJECTION_TOKEN_METADATA_KEY = "injectionTokens"; | ||
@@ -13,9 +14,15 @@ function getParamInfo(target) { | ||
exports.getParamInfo = getParamInfo; | ||
function defineInjectionTokenMetadata(data) { | ||
function defineInjectionTokenMetadata(data, transform) { | ||
return function (target, _propertyKey, parameterIndex) { | ||
const injectionTokens = Reflect.getOwnMetadata(exports.INJECTION_TOKEN_METADATA_KEY, target) || {}; | ||
injectionTokens[parameterIndex] = data; | ||
Reflect.defineMetadata(exports.INJECTION_TOKEN_METADATA_KEY, injectionTokens, target); | ||
const descriptors = Reflect.getOwnMetadata(exports.INJECTION_TOKEN_METADATA_KEY, target) || {}; | ||
descriptors[parameterIndex] = transform | ||
? { | ||
token: data, | ||
transform: transform.transformToken, | ||
transformArgs: transform.args || [] | ||
} | ||
: data; | ||
Reflect.defineMetadata(exports.INJECTION_TOKEN_METADATA_KEY, descriptors, target); | ||
}; | ||
} | ||
exports.defineInjectionTokenMetadata = defineInjectionTokenMetadata; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
class Registry { | ||
constructor() { | ||
this._registryMap = new Map(); | ||
} | ||
entries() { | ||
return this._registryMap.entries(); | ||
} | ||
getAll(key) { | ||
this.ensure(key); | ||
return this._registryMap.get(key); | ||
} | ||
get(key) { | ||
this.ensure(key); | ||
const value = this._registryMap.get(key); | ||
return value[value.length - 1] || null; | ||
} | ||
set(key, value) { | ||
this.ensure(key); | ||
this._registryMap.get(key).push(value); | ||
} | ||
setAll(key, value) { | ||
this._registryMap.set(key, value); | ||
} | ||
has(key) { | ||
this.ensure(key); | ||
return this._registryMap.get(key).length > 0; | ||
} | ||
clear() { | ||
this._registryMap.clear(); | ||
} | ||
ensure(key) { | ||
if (!this._registryMap.has(key)) { | ||
this._registryMap.set(key, []); | ||
} | ||
} | ||
const registry_base_1 = require("./registry-base"); | ||
class Registry extends registry_base_1.default { | ||
} | ||
exports.default = Registry; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var lifecycle_1 = require("./lifecycle"); | ||
exports.Lifecycle = lifecycle_1.default; | ||
Object.defineProperty(exports, "Lifecycle", { enumerable: true, get: function () { return lifecycle_1.default; } }); |
import { getParamInfo } from "../reflection-helpers"; | ||
import { instance as globalContainer } from "../dependency-container"; | ||
import { isTokenDescriptor } from "../providers/injection-token"; | ||
import { isTokenDescriptor, isTransformDescriptor } from "../providers/injection-token"; | ||
import { formatErrorCtor } from "../error-helpers"; | ||
@@ -13,6 +13,22 @@ function autoInjectable() { | ||
if (isTokenDescriptor(type)) { | ||
return type.multiple | ||
? globalContainer.resolveAll(type.token) | ||
: globalContainer.resolve(type.token); | ||
if (isTransformDescriptor(type)) { | ||
return type.multiple | ||
? globalContainer | ||
.resolve(type.transform) | ||
.transform(globalContainer.resolveAll(type.token), ...type.transformArgs) | ||
: globalContainer | ||
.resolve(type.transform) | ||
.transform(globalContainer.resolve(type.token), ...type.transformArgs); | ||
} | ||
else { | ||
return type.multiple | ||
? globalContainer.resolveAll(type.token) | ||
: globalContainer.resolve(type.token); | ||
} | ||
} | ||
else if (isTransformDescriptor(type)) { | ||
return globalContainer | ||
.resolve(type.transform) | ||
.transform(globalContainer.resolve(type.token), ...type.transformArgs); | ||
} | ||
return globalContainer.resolve(type); | ||
@@ -19,0 +35,0 @@ } |
@@ -7,2 +7,4 @@ export { default as autoInjectable } from "./auto-injectable"; | ||
export { default as injectAll } from "./inject-all"; | ||
export { default as injectAllWithTransform } from "./inject-all-with-transform"; | ||
export { default as injectWithTransform } from "./inject-with-transform"; | ||
export { default as scoped } from "./scoped"; |
import { isClassProvider, isFactoryProvider, isNormalToken, isTokenProvider, isValueProvider } from "./providers"; | ||
import { isProvider } from "./providers/provider"; | ||
import { isConstructorToken, isTokenDescriptor } from "./providers/injection-token"; | ||
import { isConstructorToken, isTokenDescriptor, isTransformDescriptor } from "./providers/injection-token"; | ||
import Registry from "./registry"; | ||
@@ -9,2 +9,3 @@ import Lifecycle from "./types/lifecycle"; | ||
import { DelayedConstructor } from "./lazy-helpers"; | ||
import Interceptors from "./interceptors"; | ||
export const typeInfo = new Map(); | ||
@@ -15,2 +16,3 @@ class InternalDependencyContainer { | ||
this._registry = new Registry(); | ||
this.interceptors = new Interceptors(); | ||
} | ||
@@ -25,2 +27,20 @@ register(token, providerOrConstructor, options = { lifecycle: Lifecycle.Transient }) { | ||
} | ||
if (isTokenProvider(provider)) { | ||
const path = [token]; | ||
let tokenProvider = provider; | ||
while (tokenProvider != null) { | ||
const currentToken = tokenProvider.useToken; | ||
if (path.includes(currentToken)) { | ||
throw new Error(`Token registration cycle detected! ${[...path, currentToken].join(" -> ")}`); | ||
} | ||
path.push(currentToken); | ||
const registration = this._registry.get(currentToken); | ||
if (registration && isTokenProvider(registration.provider)) { | ||
tokenProvider = registration.provider; | ||
} | ||
else { | ||
tokenProvider = null; | ||
} | ||
} | ||
} | ||
if (options.lifecycle === Lifecycle.Singleton || | ||
@@ -78,10 +98,39 @@ options.lifecycle == Lifecycle.ContainerScoped || | ||
} | ||
this.executePreResolutionInterceptor(token, "Single"); | ||
if (registration) { | ||
return this.resolveRegistration(registration, context); | ||
const result = this.resolveRegistration(registration, context); | ||
this.executePostResolutionInterceptor(token, result, "Single"); | ||
return result; | ||
} | ||
if (isConstructorToken(token)) { | ||
return this.construct(token, context); | ||
const result = this.construct(token, context); | ||
this.executePostResolutionInterceptor(token, result, "Single"); | ||
return result; | ||
} | ||
throw new Error("Attempted to construct an undefined constructor. Could mean a circular dependency problem. Try using `delay` function."); | ||
} | ||
executePreResolutionInterceptor(token, resolutionType) { | ||
if (this.interceptors.preResolution.has(token)) { | ||
const remainingInterceptors = []; | ||
for (const interceptor of this.interceptors.preResolution.getAll(token)) { | ||
if (interceptor.options.frequency != "Once") { | ||
remainingInterceptors.push(interceptor); | ||
} | ||
interceptor.callback(token, resolutionType); | ||
} | ||
this.interceptors.preResolution.setAll(token, remainingInterceptors); | ||
} | ||
} | ||
executePostResolutionInterceptor(token, result, resolutionType) { | ||
if (this.interceptors.postResolution.has(token)) { | ||
const remainingInterceptors = []; | ||
for (const interceptor of this.interceptors.postResolution.getAll(token)) { | ||
if (interceptor.options.frequency != "Once") { | ||
remainingInterceptors.push(interceptor); | ||
} | ||
interceptor.callback(token, result, resolutionType); | ||
} | ||
this.interceptors.postResolution.setAll(token, remainingInterceptors); | ||
} | ||
} | ||
resolveRegistration(registration, context) { | ||
@@ -127,6 +176,11 @@ if (registration.options.lifecycle === Lifecycle.ResolutionScoped && | ||
} | ||
this.executePreResolutionInterceptor(token, "All"); | ||
if (registrations) { | ||
return registrations.map(item => this.resolveRegistration(item, context)); | ||
const result = registrations.map(item => this.resolveRegistration(item, context)); | ||
this.executePostResolutionInterceptor(token, result, "All"); | ||
return result; | ||
} | ||
return [this.construct(token, context)]; | ||
const result = [this.construct(token, context)]; | ||
this.executePostResolutionInterceptor(token, result, "All"); | ||
return result; | ||
} | ||
@@ -141,2 +195,4 @@ isRegistered(token, recursive = false) { | ||
this._registry.clear(); | ||
this.interceptors.preResolution.clear(); | ||
this.interceptors.postResolution.clear(); | ||
} | ||
@@ -170,2 +226,14 @@ clearInstances() { | ||
} | ||
beforeResolution(token, callback, options = { frequency: "Always" }) { | ||
this.interceptors.preResolution.set(token, { | ||
callback: callback, | ||
options: options | ||
}); | ||
} | ||
afterResolution(token, callback, options = { frequency: "Always" }) { | ||
this.interceptors.postResolution.set(token, { | ||
callback: callback, | ||
options: options | ||
}); | ||
} | ||
getRegistration(token) { | ||
@@ -193,8 +261,10 @@ if (this.isRegistered(token)) { | ||
} | ||
if (ctor.length === 0) { | ||
return new ctor(); | ||
} | ||
const paramInfo = typeInfo.get(ctor); | ||
if (!paramInfo || paramInfo.length === 0) { | ||
throw new Error(`TypeInfo not known for "${ctor.name}"`); | ||
if (ctor.length === 0) { | ||
return new ctor(); | ||
} | ||
else { | ||
throw new Error(`TypeInfo not known for "${ctor.name}"`); | ||
} | ||
} | ||
@@ -208,6 +278,16 @@ const params = paramInfo.map(this.resolveParams(context, ctor)); | ||
if (isTokenDescriptor(param)) { | ||
return param.multiple | ||
? this.resolveAll(param.token) | ||
: this.resolve(param.token, context); | ||
if (isTransformDescriptor(param)) { | ||
return param.multiple | ||
? this.resolve(param.transform).transform(this.resolveAll(param.token), ...param.transformArgs) | ||
: this.resolve(param.transform).transform(this.resolve(param.token, context), ...param.transformArgs); | ||
} | ||
else { | ||
return param.multiple | ||
? this.resolveAll(param.token) | ||
: this.resolve(param.token, context); | ||
} | ||
} | ||
else if (isTransformDescriptor(param)) { | ||
return this.resolve(param.transform, context).transform(this.resolve(param.token, context), ...param.transformArgs); | ||
} | ||
return this.resolve(param, context); | ||
@@ -214,0 +294,0 @@ } |
@@ -10,4 +10,9 @@ import { DelayedConstructor } from "../lazy-helpers"; | ||
} | ||
export function isTransformDescriptor(descriptor) { | ||
return (typeof descriptor === "object" && | ||
"token" in descriptor && | ||
"transform" in descriptor); | ||
} | ||
export function isConstructorToken(token) { | ||
return typeof token === "function" || token instanceof DelayedConstructor; | ||
} |
@@ -10,8 +10,14 @@ export const INJECTION_TOKEN_METADATA_KEY = "injectionTokens"; | ||
} | ||
export function defineInjectionTokenMetadata(data) { | ||
export function defineInjectionTokenMetadata(data, transform) { | ||
return function (target, _propertyKey, parameterIndex) { | ||
const injectionTokens = Reflect.getOwnMetadata(INJECTION_TOKEN_METADATA_KEY, target) || {}; | ||
injectionTokens[parameterIndex] = data; | ||
Reflect.defineMetadata(INJECTION_TOKEN_METADATA_KEY, injectionTokens, target); | ||
const descriptors = Reflect.getOwnMetadata(INJECTION_TOKEN_METADATA_KEY, target) || {}; | ||
descriptors[parameterIndex] = transform | ||
? { | ||
token: data, | ||
transform: transform.transformToken, | ||
transformArgs: transform.args || [] | ||
} | ||
: data; | ||
Reflect.defineMetadata(INJECTION_TOKEN_METADATA_KEY, descriptors, target); | ||
}; | ||
} |
@@ -1,36 +0,3 @@ | ||
export default class Registry { | ||
constructor() { | ||
this._registryMap = new Map(); | ||
} | ||
entries() { | ||
return this._registryMap.entries(); | ||
} | ||
getAll(key) { | ||
this.ensure(key); | ||
return this._registryMap.get(key); | ||
} | ||
get(key) { | ||
this.ensure(key); | ||
const value = this._registryMap.get(key); | ||
return value[value.length - 1] || null; | ||
} | ||
set(key, value) { | ||
this.ensure(key); | ||
this._registryMap.get(key).push(value); | ||
} | ||
setAll(key, value) { | ||
this._registryMap.set(key, value); | ||
} | ||
has(key) { | ||
this.ensure(key); | ||
return this._registryMap.get(key).length > 0; | ||
} | ||
clear() { | ||
this._registryMap.clear(); | ||
} | ||
ensure(key) { | ||
if (!this._registryMap.has(key)) { | ||
this._registryMap.set(key, []); | ||
} | ||
} | ||
import RegistryBase from "./registry-base"; | ||
export default class Registry extends RegistryBase { | ||
} |
import { __extends, __read, __spread } from "tslib"; | ||
import { getParamInfo } from "../reflection-helpers"; | ||
import { instance as globalContainer } from "../dependency-container"; | ||
import { isTokenDescriptor } from "../providers/injection-token"; | ||
import { isTokenDescriptor, isTransformDescriptor } from "../providers/injection-token"; | ||
import { formatErrorCtor } from "../error-helpers"; | ||
@@ -17,8 +17,21 @@ function autoInjectable() { | ||
return _super.apply(this, __spread(args.concat(paramInfo.slice(args.length).map(function (type, index) { | ||
var _a, _b, _c; | ||
try { | ||
if (isTokenDescriptor(type)) { | ||
return type.multiple | ||
? globalContainer.resolveAll(type.token) | ||
: globalContainer.resolve(type.token); | ||
if (isTransformDescriptor(type)) { | ||
return type.multiple | ||
? (_a = globalContainer | ||
.resolve(type.transform)).transform.apply(_a, __spread([globalContainer.resolveAll(type.token)], type.transformArgs)) : (_b = globalContainer | ||
.resolve(type.transform)).transform.apply(_b, __spread([globalContainer.resolve(type.token)], type.transformArgs)); | ||
} | ||
else { | ||
return type.multiple | ||
? globalContainer.resolveAll(type.token) | ||
: globalContainer.resolve(type.token); | ||
} | ||
} | ||
else if (isTransformDescriptor(type)) { | ||
return (_c = globalContainer | ||
.resolve(type.transform)).transform.apply(_c, __spread([globalContainer.resolve(type.token)], type.transformArgs)); | ||
} | ||
return globalContainer.resolve(type); | ||
@@ -25,0 +38,0 @@ } |
@@ -7,2 +7,4 @@ export { default as autoInjectable } from "./auto-injectable"; | ||
export { default as injectAll } from "./inject-all"; | ||
export { default as injectAllWithTransform } from "./inject-all-with-transform"; | ||
export { default as injectWithTransform } from "./inject-with-transform"; | ||
export { default as scoped } from "./scoped"; |
import { __read, __spread, __values } from "tslib"; | ||
import { isClassProvider, isFactoryProvider, isNormalToken, isTokenProvider, isValueProvider } from "./providers"; | ||
import { isProvider } from "./providers/provider"; | ||
import { isConstructorToken, isTokenDescriptor } from "./providers/injection-token"; | ||
import { isConstructorToken, isTokenDescriptor, isTransformDescriptor } from "./providers/injection-token"; | ||
import Registry from "./registry"; | ||
@@ -10,2 +10,3 @@ import Lifecycle from "./types/lifecycle"; | ||
import { DelayedConstructor } from "./lazy-helpers"; | ||
import Interceptors from "./interceptors"; | ||
export var typeInfo = new Map(); | ||
@@ -16,2 +17,3 @@ var InternalDependencyContainer = (function () { | ||
this._registry = new Registry(); | ||
this.interceptors = new Interceptors(); | ||
} | ||
@@ -27,2 +29,20 @@ InternalDependencyContainer.prototype.register = function (token, providerOrConstructor, options) { | ||
} | ||
if (isTokenProvider(provider)) { | ||
var path = [token]; | ||
var tokenProvider = provider; | ||
while (tokenProvider != null) { | ||
var currentToken = tokenProvider.useToken; | ||
if (path.includes(currentToken)) { | ||
throw new Error("Token registration cycle detected! " + __spread(path, [currentToken]).join(" -> ")); | ||
} | ||
path.push(currentToken); | ||
var registration = this._registry.get(currentToken); | ||
if (registration && isTokenProvider(registration.provider)) { | ||
tokenProvider = registration.provider; | ||
} | ||
else { | ||
tokenProvider = null; | ||
} | ||
} | ||
} | ||
if (options.lifecycle === Lifecycle.Singleton || | ||
@@ -81,10 +101,61 @@ options.lifecycle == Lifecycle.ContainerScoped || | ||
} | ||
this.executePreResolutionInterceptor(token, "Single"); | ||
if (registration) { | ||
return this.resolveRegistration(registration, context); | ||
var result = this.resolveRegistration(registration, context); | ||
this.executePostResolutionInterceptor(token, result, "Single"); | ||
return result; | ||
} | ||
if (isConstructorToken(token)) { | ||
return this.construct(token, context); | ||
var result = this.construct(token, context); | ||
this.executePostResolutionInterceptor(token, result, "Single"); | ||
return result; | ||
} | ||
throw new Error("Attempted to construct an undefined constructor. Could mean a circular dependency problem. Try using `delay` function."); | ||
}; | ||
InternalDependencyContainer.prototype.executePreResolutionInterceptor = function (token, resolutionType) { | ||
var e_1, _a; | ||
if (this.interceptors.preResolution.has(token)) { | ||
var remainingInterceptors = []; | ||
try { | ||
for (var _b = __values(this.interceptors.preResolution.getAll(token)), _c = _b.next(); !_c.done; _c = _b.next()) { | ||
var interceptor = _c.value; | ||
if (interceptor.options.frequency != "Once") { | ||
remainingInterceptors.push(interceptor); | ||
} | ||
interceptor.callback(token, resolutionType); | ||
} | ||
} | ||
catch (e_1_1) { e_1 = { error: e_1_1 }; } | ||
finally { | ||
try { | ||
if (_c && !_c.done && (_a = _b.return)) _a.call(_b); | ||
} | ||
finally { if (e_1) throw e_1.error; } | ||
} | ||
this.interceptors.preResolution.setAll(token, remainingInterceptors); | ||
} | ||
}; | ||
InternalDependencyContainer.prototype.executePostResolutionInterceptor = function (token, result, resolutionType) { | ||
var e_2, _a; | ||
if (this.interceptors.postResolution.has(token)) { | ||
var remainingInterceptors = []; | ||
try { | ||
for (var _b = __values(this.interceptors.postResolution.getAll(token)), _c = _b.next(); !_c.done; _c = _b.next()) { | ||
var interceptor = _c.value; | ||
if (interceptor.options.frequency != "Once") { | ||
remainingInterceptors.push(interceptor); | ||
} | ||
interceptor.callback(token, result, resolutionType); | ||
} | ||
} | ||
catch (e_2_1) { e_2 = { error: e_2_1 }; } | ||
finally { | ||
try { | ||
if (_c && !_c.done && (_a = _b.return)) _a.call(_b); | ||
} | ||
finally { if (e_2) throw e_2.error; } | ||
} | ||
this.interceptors.postResolution.setAll(token, remainingInterceptors); | ||
} | ||
}; | ||
InternalDependencyContainer.prototype.resolveRegistration = function (registration, context) { | ||
@@ -132,8 +203,13 @@ if (registration.options.lifecycle === Lifecycle.ResolutionScoped && | ||
} | ||
this.executePreResolutionInterceptor(token, "All"); | ||
if (registrations) { | ||
return registrations.map(function (item) { | ||
var result_1 = registrations.map(function (item) { | ||
return _this.resolveRegistration(item, context); | ||
}); | ||
this.executePostResolutionInterceptor(token, result_1, "All"); | ||
return result_1; | ||
} | ||
return [this.construct(token, context)]; | ||
var result = [this.construct(token, context)]; | ||
this.executePostResolutionInterceptor(token, result, "All"); | ||
return result; | ||
}; | ||
@@ -149,5 +225,7 @@ InternalDependencyContainer.prototype.isRegistered = function (token, recursive) { | ||
this._registry.clear(); | ||
this.interceptors.preResolution.clear(); | ||
this.interceptors.postResolution.clear(); | ||
}; | ||
InternalDependencyContainer.prototype.clearInstances = function () { | ||
var e_1, _a; | ||
var e_3, _a; | ||
try { | ||
@@ -164,3 +242,3 @@ for (var _b = __values(this._registry.entries()), _c = _b.next(); !_c.done; _c = _b.next()) { | ||
} | ||
catch (e_1_1) { e_1 = { error: e_1_1 }; } | ||
catch (e_3_1) { e_3 = { error: e_3_1 }; } | ||
finally { | ||
@@ -170,7 +248,7 @@ try { | ||
} | ||
finally { if (e_1) throw e_1.error; } | ||
finally { if (e_3) throw e_3.error; } | ||
} | ||
}; | ||
InternalDependencyContainer.prototype.createChildContainer = function () { | ||
var e_2, _a; | ||
var e_4, _a; | ||
var childContainer = new InternalDependencyContainer(this); | ||
@@ -196,3 +274,3 @@ try { | ||
} | ||
catch (e_2_1) { e_2 = { error: e_2_1 }; } | ||
catch (e_4_1) { e_4 = { error: e_4_1 }; } | ||
finally { | ||
@@ -202,6 +280,20 @@ try { | ||
} | ||
finally { if (e_2) throw e_2.error; } | ||
finally { if (e_4) throw e_4.error; } | ||
} | ||
return childContainer; | ||
}; | ||
InternalDependencyContainer.prototype.beforeResolution = function (token, callback, options) { | ||
if (options === void 0) { options = { frequency: "Always" }; } | ||
this.interceptors.preResolution.set(token, { | ||
callback: callback, | ||
options: options | ||
}); | ||
}; | ||
InternalDependencyContainer.prototype.afterResolution = function (token, callback, options) { | ||
if (options === void 0) { options = { frequency: "Always" }; } | ||
this.interceptors.postResolution.set(token, { | ||
callback: callback, | ||
options: options | ||
}); | ||
}; | ||
InternalDependencyContainer.prototype.getRegistration = function (token) { | ||
@@ -232,8 +324,10 @@ if (this.isRegistered(token)) { | ||
} | ||
if (ctor.length === 0) { | ||
return new ctor(); | ||
} | ||
var paramInfo = typeInfo.get(ctor); | ||
if (!paramInfo || paramInfo.length === 0) { | ||
throw new Error("TypeInfo not known for \"" + ctor.name + "\""); | ||
if (ctor.length === 0) { | ||
return new ctor(); | ||
} | ||
else { | ||
throw new Error("TypeInfo not known for \"" + ctor.name + "\""); | ||
} | ||
} | ||
@@ -246,8 +340,18 @@ var params = paramInfo.map(this.resolveParams(context, ctor)); | ||
return function (param, idx) { | ||
var _a, _b, _c; | ||
try { | ||
if (isTokenDescriptor(param)) { | ||
return param.multiple | ||
? _this.resolveAll(param.token) | ||
: _this.resolve(param.token, context); | ||
if (isTransformDescriptor(param)) { | ||
return param.multiple | ||
? (_a = _this.resolve(param.transform)).transform.apply(_a, __spread([_this.resolveAll(param.token)], param.transformArgs)) : (_b = _this.resolve(param.transform)).transform.apply(_b, __spread([_this.resolve(param.token, context)], param.transformArgs)); | ||
} | ||
else { | ||
return param.multiple | ||
? _this.resolveAll(param.token) | ||
: _this.resolve(param.token, context); | ||
} | ||
} | ||
else if (isTransformDescriptor(param)) { | ||
return (_c = _this.resolve(param.transform, context)).transform.apply(_c, __spread([_this.resolve(param.token, context)], param.transformArgs)); | ||
} | ||
return _this.resolve(param, context); | ||
@@ -254,0 +358,0 @@ } |
@@ -10,4 +10,9 @@ import { DelayedConstructor } from "../lazy-helpers"; | ||
} | ||
export function isTransformDescriptor(descriptor) { | ||
return (typeof descriptor === "object" && | ||
"token" in descriptor && | ||
"transform" in descriptor); | ||
} | ||
export function isConstructorToken(token) { | ||
return typeof token === "function" || token instanceof DelayedConstructor; | ||
} |
@@ -10,8 +10,14 @@ export var INJECTION_TOKEN_METADATA_KEY = "injectionTokens"; | ||
} | ||
export function defineInjectionTokenMetadata(data) { | ||
export function defineInjectionTokenMetadata(data, transform) { | ||
return function (target, _propertyKey, parameterIndex) { | ||
var injectionTokens = Reflect.getOwnMetadata(INJECTION_TOKEN_METADATA_KEY, target) || {}; | ||
injectionTokens[parameterIndex] = data; | ||
Reflect.defineMetadata(INJECTION_TOKEN_METADATA_KEY, injectionTokens, target); | ||
var descriptors = Reflect.getOwnMetadata(INJECTION_TOKEN_METADATA_KEY, target) || {}; | ||
descriptors[parameterIndex] = transform | ||
? { | ||
token: data, | ||
transform: transform.transformToken, | ||
transformArgs: transform.args || [] | ||
} | ||
: data; | ||
Reflect.defineMetadata(INJECTION_TOKEN_METADATA_KEY, descriptors, target); | ||
}; | ||
} |
@@ -1,38 +0,10 @@ | ||
var Registry = (function () { | ||
import { __extends } from "tslib"; | ||
import RegistryBase from "./registry-base"; | ||
var Registry = (function (_super) { | ||
__extends(Registry, _super); | ||
function Registry() { | ||
this._registryMap = new Map(); | ||
return _super !== null && _super.apply(this, arguments) || this; | ||
} | ||
Registry.prototype.entries = function () { | ||
return this._registryMap.entries(); | ||
}; | ||
Registry.prototype.getAll = function (key) { | ||
this.ensure(key); | ||
return this._registryMap.get(key); | ||
}; | ||
Registry.prototype.get = function (key) { | ||
this.ensure(key); | ||
var value = this._registryMap.get(key); | ||
return value[value.length - 1] || null; | ||
}; | ||
Registry.prototype.set = function (key, value) { | ||
this.ensure(key); | ||
this._registryMap.get(key).push(value); | ||
}; | ||
Registry.prototype.setAll = function (key, value) { | ||
this._registryMap.set(key, value); | ||
}; | ||
Registry.prototype.has = function (key) { | ||
this.ensure(key); | ||
return this._registryMap.get(key).length > 0; | ||
}; | ||
Registry.prototype.clear = function () { | ||
this._registryMap.clear(); | ||
}; | ||
Registry.prototype.ensure = function (key) { | ||
if (!this._registryMap.has(key)) { | ||
this._registryMap.set(key, []); | ||
} | ||
}; | ||
return Registry; | ||
}()); | ||
}(RegistryBase)); | ||
export default Registry; |
@@ -7,2 +7,4 @@ export { default as autoInjectable } from "./auto-injectable"; | ||
export { default as injectAll } from "./inject-all"; | ||
export { default as injectAllWithTransform } from "./inject-all-with-transform"; | ||
export { default as injectWithTransform } from "./inject-with-transform"; | ||
export { default as scoped } from "./scoped"; |
@@ -1,2 +0,2 @@ | ||
export { DependencyContainer, Lifecycle, RegistrationOptions } from "./types"; | ||
export { DependencyContainer, Lifecycle, RegistrationOptions, Frequency } from "./types"; | ||
export * from "./decorators"; | ||
@@ -3,0 +3,0 @@ export * from "./factories"; |
import constructor from "../types/constructor"; | ||
import { DelayedConstructor } from "../lazy-helpers"; | ||
import Transform from "../types/transform"; | ||
declare type InjectionToken<T = any> = constructor<T> | string | symbol | DelayedConstructor<T>; | ||
export declare function isNormalToken(token?: InjectionToken<any>): token is string | symbol; | ||
export declare function isTokenDescriptor(descriptor: any): descriptor is TokenDescriptor; | ||
export declare function isTransformDescriptor(descriptor: any): descriptor is TransformDescriptor; | ||
export declare function isConstructorToken(token?: InjectionToken<any>): token is constructor<any> | DelayedConstructor<any>; | ||
@@ -11,2 +13,7 @@ export interface TokenDescriptor { | ||
} | ||
export interface TransformDescriptor { | ||
token: InjectionToken<any>; | ||
transform: InjectionToken<Transform<any, any>>; | ||
transformArgs: any[]; | ||
} | ||
export default InjectionToken; |
import constructor from "./types/constructor"; | ||
import InjectionToken from "./providers/injection-token"; | ||
import { ParamInfo } from "./dependency-container"; | ||
import Transform from "./types/transform"; | ||
export declare const INJECTION_TOKEN_METADATA_KEY = "injectionTokens"; | ||
export declare function getParamInfo(target: constructor<any>): ParamInfo[]; | ||
export declare function defineInjectionTokenMetadata(data: any): (target: any, propertyKey: string | symbol, parameterIndex: number) => any; | ||
export declare function defineInjectionTokenMetadata(data: any, transform?: { | ||
transformToken: InjectionToken<Transform<any, any>>; | ||
args: any[]; | ||
}): (target: any, propertyKey: string | symbol, parameterIndex: number) => any; |
@@ -1,13 +0,4 @@ | ||
import { InjectionToken } from "."; | ||
import { Registration } from "./dependency-container"; | ||
export default class Registry { | ||
protected _registryMap: Map<InjectionToken<any>, Registration<any>[]>; | ||
entries(): IterableIterator<[InjectionToken<any>, Registration[]]>; | ||
getAll(key: InjectionToken<any>): Registration[]; | ||
get(key: InjectionToken<any>): Registration | null; | ||
set(key: InjectionToken<any>, value: Registration): void; | ||
setAll(key: InjectionToken<any>, value: Registration[]): void; | ||
has(key: InjectionToken<any>): boolean; | ||
clear(): void; | ||
private ensure; | ||
import RegistryBase from "./registry-base"; | ||
export default class Registry extends RegistryBase<Registration> { | ||
} |
@@ -8,2 +8,19 @@ import FactoryProvider from "../providers/factory-provider"; | ||
import RegistrationOptions from "./registration-options"; | ||
import InterceptorOptions from "./interceptor-options"; | ||
export declare type ResolutionType = "Single" | "All"; | ||
export interface PreResolutionInterceptorCallback<T = any> { | ||
/** | ||
* @param token The InjectionToken that was intercepted | ||
* @param resolutionType The type of resolve that was called (i.e. All or Single) | ||
*/ | ||
(token: InjectionToken<T>, resolutionType: ResolutionType): void; | ||
} | ||
export interface PostResolutionInterceptorCallback<T = any> { | ||
/** | ||
* @param token The InjectionToken that was intercepted | ||
* @param result The object that was resolved from the container | ||
* @param resolutionType The type of resolve that was called (i.e. All or Single) | ||
*/ | ||
(token: InjectionToken<T>, result: T | T[], resolutionType: ResolutionType): void; | ||
} | ||
export default interface DependencyContainer { | ||
@@ -41,2 +58,16 @@ register<T>(token: InjectionToken<T>, provider: ValueProvider<T>): DependencyContainer; | ||
createChildContainer(): DependencyContainer; | ||
/** | ||
* Registers a callback that is called when a specific injection token is resolved | ||
* @param token The token to intercept | ||
* @param callback The callback that is called before the token is resolved | ||
* @param options Options for under what circumstances the callback will be called | ||
*/ | ||
beforeResolution<T>(token: InjectionToken<T>, callback: PreResolutionInterceptorCallback<T>, options?: InterceptorOptions): void; | ||
/** | ||
* Registers a callback that is called after a successful resolution of the token | ||
* @param token The token to intercept | ||
* @param callback The callback that is called after the token is resolved | ||
* @param options Options for under what circumstances the callback will be called | ||
*/ | ||
afterResolution<T>(token: InjectionToken<T>, callback: PostResolutionInterceptorCallback<T>, options?: InterceptorOptions): void; | ||
} |
@@ -6,1 +6,4 @@ export { default as constructor } from "./constructor"; | ||
export { default as Lifecycle } from "./lifecycle"; | ||
export { default as InterceptionOptions } from "./interceptor-options"; | ||
export { default as Frequency } from "./frequency"; | ||
export { default as Transform } from "./transform"; |
import Lifecycle from "./lifecycle"; | ||
declare type RegistrationOptions = { | ||
/** | ||
* Customize the lifecycle of the registration | ||
* See https://github.com/microsoft/tsyringe#available-scopes for more information | ||
*/ | ||
lifecycle: Lifecycle; | ||
}; | ||
export default RegistrationOptions; |
{ | ||
"name": "tsyringe", | ||
"version": "4.3.0", | ||
"version": "4.4.0", | ||
"description": "Lightweight dependency injection container for JavaScript/TypeScript", | ||
@@ -5,0 +5,0 @@ "main": "dist/cjs/index.js", |
207
README.md
@@ -21,2 +21,4 @@ [![Travis](https://img.shields.io/travis/Microsoft/tsyringe.svg)](https://travis-ci.org/Microsoft/tsyringe/) | ||
- [injectAll()](#injectall) | ||
- [injectWithTransform()](#injectWithTransform) | ||
- [injectAllWithTransform()](#injectAllWithTransform) | ||
- [scoped()](#scoped) | ||
@@ -29,11 +31,13 @@ - [Container](#container) | ||
- [Resolution](#resolution) | ||
- [Interception](#interception) | ||
- [Child Containers](#child-containers) | ||
- [Clearing Instances](#clearing-instances) | ||
- [Circular dependencies](#circular-dependencies) | ||
- [The `delay` helper function](#the-delay-helper-function) | ||
- [Interfaces and circular dependencies](#interfaces-and-circular-dependencies) | ||
- [Circular dependencies](#circular-dependencies) | ||
- [The `delay` helper function](#the-delay-helper-function) | ||
- [Interfaces and circular dependencies](#interfaces-and-circular-dependencies) | ||
- [Full examples](#full-examples) | ||
- [Example without interfaces](#example-without-interfaces) | ||
- [Example with interfaces](#example-with-interfaces) | ||
- [Non Goals](#non-goals) | ||
- [Injecting primitive values (Named injection)](#injecting-primitive-values-named-injection) | ||
- [Non goals](#non-goals) | ||
- [Contributing](#contributing) | ||
@@ -204,2 +208,52 @@ | ||
### injectWithTransform | ||
Parameter decorator which allows for a transformer object to take an action on the resolved object | ||
before returning the result. | ||
```typescript | ||
class FeatureFlags { | ||
public getFlagValue(flagName: string): boolean { | ||
// ... | ||
} | ||
class Foo() {} | ||
class FeatureFlagsTransformer implements Transform<FeatureFlags, bool> { | ||
public transform(flags: FeatureFlags, flag: string) { | ||
return flags.getFlagValue(flag); | ||
} | ||
} | ||
@injectable() | ||
class MyComponent(foo: Foo, @injectWithTransform(FeatureFlags, FeatureFlagsTransformer, "IsBlahEnabled") blahEnabled: boolean){ | ||
// ... | ||
} | ||
``` | ||
### injectAllWithTransform | ||
This parameter decorator allows for array contents to be passed through a transformer. The transformer can return any type, so this | ||
can be used to map or fold an array. | ||
```typescript | ||
@injectable | ||
class Foo { | ||
public value; | ||
} | ||
class FooTransform implements Transform<Foo[], string[]>{ | ||
public transform(foos: Foo[]): string[]{ | ||
return foos.map(f => f.value)); | ||
} | ||
} | ||
@injectable | ||
class Bar { | ||
constructor(@injectAllWithTransform(Foo, FooTransform) stringArray: string[]) { | ||
// ... | ||
} | ||
} | ||
``` | ||
### scoped() | ||
@@ -210,9 +264,14 @@ | ||
#### Available scopes | ||
- Transient | ||
- The **default** registration scope, a new instance will be created with each resolve | ||
- Singleton | ||
- Each resolve will return the same instance (including resolves from child containers) | ||
- ResolutionScoped | ||
- The same instance will be resolved for each resolution of this dependency during a single | ||
resolution chain | ||
resolution chain | ||
- ContainerScoped | ||
- The dependency container will return the same instance each time a resolution for this dependency | ||
is requested. This is similar to being a singleton, however if a child container is made, that child | ||
container will resolve an instance unique to it. | ||
is requested. This is similar to being a singleton, however if a child container is made, that child | ||
container will resolve an instance unique to it. | ||
@@ -239,3 +298,7 @@ #### Usage | ||
```typescript | ||
type InjectionToken<T = any> = constructor<T> | DelayedConstructor<T> | string | symbol; | ||
type InjectionToken<T = any> = | ||
| constructor<T> | ||
| DelayedConstructor<T> | ||
| string | ||
| symbol; | ||
``` | ||
@@ -289,2 +352,3 @@ | ||
can be used as a factory: | ||
```typescript | ||
@@ -304,3 +368,3 @@ type FactoryFunction<T> = (dependencyContainer: DependencyContainer) => T; | ||
token: "SingletonFoo"; | ||
useFactory: instanceCachingFactory<Foo>(c => c.resolve(Foo)) | ||
useFactory: instanceCachingFactory<Foo>(c => c.resolve(Foo)); | ||
} | ||
@@ -311,3 +375,3 @@ ``` | ||
This factory is used to provide conditional behavior upon resolution. It caches the result by default, but | ||
This factory is used to provide conditional behavior upon resolution. It caches the result by default, but | ||
has an optional parameter to resolve fresh each time. | ||
@@ -319,10 +383,10 @@ | ||
{ | ||
token: | ||
useFactory: predicateAwareClassFactory<Foo>( | ||
c => c.resolve(Bar).useHttps, | ||
FooHttps, // A FooHttps will be resolved from the container | ||
FooHttp | ||
) | ||
token: useFactory: predicateAwareClassFactory<Foo>( | ||
c => c.resolve(Bar).useHttps, // Predicate for evaluation | ||
FooHttps, // A FooHttps will be resolved from the container if predicate is true | ||
FooHttp // A FooHttp will be resolved if predicate is false | ||
); | ||
} | ||
``` | ||
#### Token Provider | ||
@@ -351,2 +415,8 @@ | ||
#### Registration options | ||
As an optional parameter to `.register()` you may provide [`RegistrationOptions`](./src/types/registration-options.ts) | ||
which customize how the registration behaves. See the linked source code for up to date documentation | ||
on available options. | ||
### Registry | ||
@@ -372,3 +442,3 @@ | ||
Lastly you might choose to use this to register 3rd party instances instead of the `container.register(...)` method. | ||
note: if you want this class to be `@injectable` you must put the decorator before `@registry`, this annotation is not | ||
note: if you want this class to be `@injectable` you must put the decorator before `@registry`, this annotation is not | ||
required though. | ||
@@ -398,5 +468,6 @@ | ||
@registry([ // registry is optional, all you need is to use the same token when registering | ||
{ token: 'Bar', useToken: Foo }, // can be any provider | ||
{ token: 'Bar', useToken: Baz }, | ||
@registry([ | ||
// registry is optional, all you need is to use the same token when registering | ||
{token: "Bar", useToken: Foo}, // can be any provider | ||
{token: "Bar", useToken: Baz} | ||
]) | ||
@@ -408,2 +479,42 @@ class MyRegistry {} | ||
### Interception | ||
Interception allows you to register a callback that will be called before or after the resolution of a specific token. | ||
This callback can be registered to execute only once (to perform initialization, for example), | ||
on each resolution to do logging, for example. | ||
`beforeResolution` is used to take an action before an object is resolved. | ||
```typescript | ||
class Bar {} | ||
container.beforeResolution( | ||
Bar, | ||
// Callback signature is (token: InjectionToken<T>, resolutionType: ResolutionType) => void | ||
() => { | ||
console.log("Bar is about to be resolved!"); | ||
}, | ||
{frequency: "Always"} | ||
); | ||
``` | ||
`afterResolution` is used to take an action after the object has been resolved. | ||
```typescript | ||
class Bar { | ||
public init(): void { | ||
// ... | ||
} | ||
} | ||
container.afterResolution( | ||
Bar, | ||
// Callback signature is (token: InjectionToken<T>, result: T | T[], resolutionType: ResolutionType) | ||
(_t, result) => { | ||
result.init(); | ||
}, | ||
{frequency: "Once"} | ||
); | ||
``` | ||
### Child Containers | ||
@@ -471,3 +582,2 @@ | ||
} | ||
``` | ||
@@ -478,19 +588,20 @@ | ||
```typescript | ||
container.resolve(Foo) | ||
container.resolve(Foo); | ||
``` | ||
``` | ||
Error: Cannot inject the dependency at position #0 of "Foo" constructor. Reason: | ||
Attempted to construct an undefined constructor. Could mean a circular dependency problem. Try using `delay` function. | ||
``` | ||
### The `delay` helper function | ||
``` | ||
The best way to deal with this situation is to do some kind of refactor to avoid the cyclic dependencies. Usually this implies introducing additional services to cut the cycles. | ||
### The `delay` helper function | ||
But when refactor is not an option you can use the `delay` function helper. The `delay` function wraps the constructor in an instance of `DelayedConstructor`. | ||
The best way to deal with this situation is to do some kind of refactor to avoid the cyclic dependencies. Usually this implies introducing additional services to cut the cycles. | ||
The *delayed constructor* is a kind of special `InjectionToken` that will eventually be evaluated to construct an intermediate proxy object wrapping a factory for the real object. | ||
When the proxy object is used for the first time it will construct a real object using this factory and any usage will be forwarded to the real object. | ||
But when refactor is not an option you can use the `delay` function helper. The `delay` function wraps the constructor in an instance of `DelayedConstructor`. | ||
The _delayed constructor_ is a kind of special `InjectionToken` that will eventually be evaluated to construct an intermediate proxy object wrapping a factory for the real object. | ||
When the proxy object is used for the first time it will construct a real object using this factory and any usage will be forwarded to the real object. | ||
```typescript | ||
@@ -510,10 +621,8 @@ @injectable() | ||
// property bar will hold a proxy that looks and acts as a real Bar instance. | ||
// property bar will hold a proxy that looks and acts as a real Bar instance. | ||
foo.bar instanceof Bar; // true | ||
``` | ||
### Interfaces and circular dependencies | ||
### Interfaces and circular dependencies | ||
We can rest in the fact that a `DelayedConstructor` could be used in the same contexts that a constructor and will be handled transparently by tsyringe. Such idea is used in the next example involving interfaces: | ||
@@ -547,3 +656,3 @@ | ||
} | ||
``` | ||
``` | ||
@@ -628,4 +737,32 @@ # Full examples | ||
## Injecting primitive values (Named injection) | ||
Primitive values can also be injected by utilizing named injection | ||
```typescript | ||
import {singleton, inject} from "tsyringe"; | ||
@singleton() | ||
class Foo { | ||
private str: string; | ||
constructor(@inject("SpecialString") value: string) { | ||
this.str = value; | ||
} | ||
} | ||
// some other file | ||
import "reflect-metadata"; | ||
import {container} from "tsyringe"; | ||
import {Foo} from "./foo"; | ||
const str = "test"; | ||
container.register("SpecialString", {useValue: str}); | ||
const instance = container.resolve(Foo); | ||
``` | ||
# Non goals | ||
The following is a list of features we explicitly plan on not adding: | ||
- Property Injection | ||
@@ -632,0 +769,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
131065
159
2541
765