@tsed/di
Advanced tools
Comparing version 5.29.0 to 5.30.0
@@ -61,3 +61,3 @@ "use strict"; | ||
container.forEach(provider => { | ||
if (!this.hasProvider(provider.provide)) { | ||
if (!this.hasProvider(provider.provide) && !provider.root) { | ||
this.setProvider(provider.provide, provider.clone()); | ||
@@ -64,0 +64,0 @@ } |
import { Store, Type } from "@tsed/core"; | ||
import { ProviderScope } from "../interfaces"; | ||
import { IDIConfigurationOptions, ProviderScope } from "../interfaces"; | ||
import { IProvider } from "../interfaces/IProvider"; | ||
@@ -7,46 +7,14 @@ import { ProviderType } from "../interfaces/ProviderType"; | ||
export declare class Provider<T> implements IProvider<T> { | ||
/** | ||
* | ||
*/ | ||
root: boolean; | ||
type: ProviderType | any; | ||
/** | ||
* | ||
*/ | ||
injectable: boolean; | ||
/** | ||
* | ||
*/ | ||
instance: T; | ||
/** | ||
* | ||
*/ | ||
deps: any[]; | ||
/** | ||
* | ||
*/ | ||
imports: any[]; | ||
useFactory: Function; | ||
useAsyncFactory: Function; | ||
/** | ||
* | ||
*/ | ||
useValue: any; | ||
/** | ||
* | ||
*/ | ||
protected _provide: TokenProvider; | ||
/** | ||
* | ||
*/ | ||
protected _useClass: Type<T>; | ||
/** | ||
* | ||
*/ | ||
protected _instance: T; | ||
/** | ||
* | ||
*/ | ||
protected _scope: ProviderScope; | ||
/** | ||
* | ||
*/ | ||
private _store; | ||
@@ -56,16 +24,4 @@ [key: string]: any; | ||
readonly token: any; | ||
/** | ||
* | ||
* @returns {any} | ||
*/ | ||
/** | ||
* | ||
* @param value | ||
*/ | ||
provide: TokenProvider; | ||
/** | ||
* | ||
* @returns {Type<T>} | ||
*/ | ||
/** | ||
* Create a new store if the given value is a class. Otherwise the value is ignored. | ||
@@ -75,15 +31,4 @@ * @param value | ||
useClass: Type<T>; | ||
/** | ||
* | ||
* @returns {string} | ||
*/ | ||
readonly className: string; | ||
/** | ||
* | ||
*/ | ||
readonly name: string; | ||
/** | ||
* | ||
* @returns {Store} | ||
*/ | ||
readonly store: Store; | ||
@@ -104,8 +49,6 @@ /** | ||
scope: ProviderScope; | ||
configuration: Partial<IDIConfigurationOptions>; | ||
isAsync(): boolean; | ||
/** | ||
* | ||
*/ | ||
clone(): Provider<any>; | ||
toString(): string; | ||
} |
@@ -9,9 +9,4 @@ "use strict"; | ||
constructor(token) { | ||
/** | ||
* | ||
*/ | ||
this.root = false; | ||
this.type = ProviderType_1.ProviderType.PROVIDER; | ||
/** | ||
* | ||
*/ | ||
this.injectable = true; | ||
@@ -24,20 +19,11 @@ this.provide = token; | ||
} | ||
/** | ||
* | ||
* @returns {any} | ||
*/ | ||
get provide() { | ||
return this._provide; | ||
} | ||
/** | ||
* | ||
* @param value | ||
*/ | ||
set provide(value) { | ||
this._provide = core_1.isClass(value) ? core_1.getClass(value) : value; | ||
if (value) { | ||
this._provide = core_1.isClass(value) ? core_1.classOf(value) : value; | ||
this._store = core_1.Store.from(value); | ||
} | ||
} | ||
/** | ||
* | ||
* @returns {Type<T>} | ||
*/ | ||
get useClass() { | ||
@@ -52,23 +38,12 @@ return this._useClass; | ||
if (core_1.isClass(value)) { | ||
this._useClass = core_1.getClass(value); | ||
this._useClass = core_1.classOf(value); | ||
this._store = core_1.Store.from(value); | ||
} | ||
} | ||
/** | ||
* | ||
* @returns {string} | ||
*/ | ||
get className() { | ||
return this.name; | ||
} | ||
/** | ||
* | ||
*/ | ||
get name() { | ||
return core_1.nameOf(this.provide); | ||
} | ||
/** | ||
* | ||
* @returns {Store} | ||
*/ | ||
get store() { | ||
@@ -90,3 +65,3 @@ return this._store; | ||
} | ||
return this._store ? this.store.get("scope") : this._scope; | ||
return this.store.get("scope"); | ||
} | ||
@@ -98,14 +73,19 @@ /** | ||
set scope(scope) { | ||
this._store ? this.store.set("scope", scope) : this._scope; | ||
this.store.set("scope", scope); | ||
} | ||
get configuration() { | ||
return this.store.get("configuration"); | ||
} | ||
set configuration(configuration) { | ||
this.store.set("configuration", configuration); | ||
} | ||
isAsync() { | ||
return !!this.useAsyncFactory; | ||
} | ||
/** | ||
* | ||
*/ | ||
clone() { | ||
const provider = new (core_1.getClass(this))(this.token); | ||
const provider = new (core_1.classOf(this))(this.token); | ||
core_1.getKeys(this).forEach(key => { | ||
provider[key] = this[key]; | ||
if (this[key] !== undefined) { | ||
provider[key] = this[key]; | ||
} | ||
}); | ||
@@ -120,2 +100,6 @@ return provider; | ||
core_1.Enumerable(), | ||
tslib_1.__metadata("design:type", Boolean) | ||
], Provider.prototype, "root", void 0); | ||
tslib_1.__decorate([ | ||
core_1.Enumerable(), | ||
tslib_1.__metadata("design:type", Object) | ||
@@ -137,2 +121,6 @@ ], Provider.prototype, "type", void 0); | ||
core_1.Enumerable(), | ||
tslib_1.__metadata("design:type", Array) | ||
], Provider.prototype, "imports", void 0); | ||
tslib_1.__decorate([ | ||
core_1.Enumerable(), | ||
tslib_1.__metadata("design:type", Function) | ||
@@ -162,6 +150,2 @@ ], Provider.prototype, "useFactory", void 0); | ||
core_1.NotEnumerable(), | ||
tslib_1.__metadata("design:type", String) | ||
], Provider.prototype, "_scope", void 0); | ||
tslib_1.__decorate([ | ||
core_1.NotEnumerable(), | ||
tslib_1.__metadata("design:type", core_1.Store) | ||
@@ -179,4 +163,9 @@ ], Provider.prototype, "_store", void 0); | ||
], Provider.prototype, "scope", null); | ||
tslib_1.__decorate([ | ||
core_1.Enumerable(), | ||
tslib_1.__metadata("design:type", Object), | ||
tslib_1.__metadata("design:paramtypes", [Object]) | ||
], Provider.prototype, "configuration", null); | ||
exports.Provider = Provider; | ||
//# sourceMappingURL=Provider.js.map |
/** | ||
* Return value from ServerSettingsService. | ||
* Return value from Configuration. | ||
* | ||
@@ -4,0 +4,0 @@ * ## Example |
@@ -6,3 +6,3 @@ "use strict"; | ||
/** | ||
* Return value from ServerSettingsService. | ||
* Return value from Configuration. | ||
* | ||
@@ -9,0 +9,0 @@ * ## Example |
/** | ||
* Return value from ServerSettingsService. | ||
* Return value from Configuration. | ||
* | ||
@@ -4,0 +4,0 @@ * ## Example |
@@ -6,3 +6,3 @@ "use strict"; | ||
/** | ||
* Return value from ServerSettingsService. | ||
* Return value from Configuration. | ||
* | ||
@@ -9,0 +9,0 @@ * ## Example |
@@ -7,2 +7,3 @@ import { TokenProvider } from "../interfaces"; | ||
constructor(token: TokenProvider, origin?: any); | ||
static throwInjectorError(token: any, currentDependency: any, error: any): void; | ||
} |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const core_1 = require("@tsed/core"); | ||
const colorizeUtils_1 = require("ts-log-debug/lib/layouts/utils/colorizeUtils"); | ||
class InjectionError extends Error { | ||
@@ -31,5 +32,27 @@ constructor(token, origin) { | ||
} | ||
static throwInjectorError(token, currentDependency, error) { | ||
if (currentDependency && core_1.isClass(token)) { | ||
error.message = printDependencyInjectionError(token, Object.assign({}, currentDependency, { message: error.message })); | ||
} | ||
throw new InjectionError(token, error); | ||
} | ||
} | ||
exports.InjectionError = InjectionError; | ||
function printDependencyInjectionError(token, options) { | ||
let erroredArg = ""; | ||
const args = core_1.getConstructorArgNames(token) | ||
.map((arg, index) => { | ||
if (options.index === index) { | ||
erroredArg = arg; | ||
arg = colorizeUtils_1.colorize(arg, "red"); | ||
} | ||
return `${arg}: ${core_1.nameOf(options.deps[index])}`; | ||
}) | ||
.join(", "); | ||
const signature = core_1.nameOf(token) + "->constructor(" + args + ")"; | ||
const indexOf = signature.indexOf(erroredArg) - 5; | ||
const drawline = (indexOf) => " ".repeat(indexOf) + colorizeUtils_1.colorize("^" + "‾".repeat(erroredArg.length - 1), "red"); | ||
return "Unable to inject dependency. " + options.message + "\n\n" + signature + "\n" + (indexOf > -1 ? drawline(indexOf) : ""); | ||
} | ||
//# sourceMappingURL=InjectionError.js.map |
@@ -16,7 +16,9 @@ export * from "./class/Provider"; | ||
export * from "./decorators/interceptor"; | ||
export * from "./decorators/configuration"; | ||
export * from "./decorators/module"; | ||
export * from "./registries/ProviderRegistry"; | ||
export * from "./registries/GlobalProviders"; | ||
export * from "./services/InjectorService"; | ||
export * from "./services/DISettings"; | ||
export * from "./services/DIConfiguration"; | ||
export * from "./errors/InjectionError"; | ||
export * from "./errors/UndefinedTokenError"; |
@@ -19,6 +19,8 @@ "use strict"; | ||
tslib_1.__exportStar(require("./decorators/interceptor"), exports); | ||
tslib_1.__exportStar(require("./decorators/configuration"), exports); | ||
tslib_1.__exportStar(require("./decorators/module"), exports); | ||
tslib_1.__exportStar(require("./registries/ProviderRegistry"), exports); | ||
tslib_1.__exportStar(require("./registries/GlobalProviders"), exports); | ||
tslib_1.__exportStar(require("./services/InjectorService"), exports); | ||
tslib_1.__exportStar(require("./services/DISettings"), exports); | ||
tslib_1.__exportStar(require("./services/DIConfiguration"), exports); | ||
tslib_1.__exportStar(require("./errors/InjectionError"), exports); | ||
@@ -25,0 +27,0 @@ tslib_1.__exportStar(require("./errors/UndefinedTokenError"), exports); |
@@ -5,2 +5,3 @@ import { ProviderScope } from "./ProviderScope"; | ||
deps: any[]; | ||
imports: any[]; | ||
parent?: TokenProvider; | ||
@@ -7,0 +8,0 @@ scope: ProviderScope; |
export * from "./IDILogger"; | ||
export * from "./IDISettings"; | ||
export * from "./IDIConfigurationOptions"; | ||
export * from "./IInjectableProperties"; | ||
@@ -4,0 +4,0 @@ export * from "./IInterceptor"; |
import { Container } from "../class/Container"; | ||
import { LocalsContainer } from "../class/LocalsContainer"; | ||
import { Provider } from "../class/Provider"; | ||
import { IDILogger, IDISettings, IInjectablePropertyService, IInjectablePropertyValue, IInvokeOptions, ProviderScope, TokenProvider } from "../interfaces"; | ||
import { IDIConfigurationOptions, IDILogger, IInjectablePropertyService, IInjectablePropertyValue, IInvokeOptions, ProviderScope, TokenProvider } from "../interfaces"; | ||
import { DIConfiguration } from "./DIConfiguration"; | ||
/** | ||
@@ -30,4 +31,5 @@ * This service contain all services collected by `@Service` or services declared manually with `InjectorService.factory()` or `InjectorService.service()`. | ||
export declare class InjectorService extends Container { | ||
settings: IDISettings; | ||
settings: IDIConfigurationOptions & DIConfiguration; | ||
logger: IDILogger; | ||
private resolvedConfiguration; | ||
constructor(); | ||
@@ -101,3 +103,3 @@ /** | ||
loadAsync(locals?: LocalsContainer<any>): Promise<LocalsContainer<any>>; | ||
loadSync(locals?: LocalsContainer<any>): Promise<LocalsContainer<any>>; | ||
loadSync(locals?: LocalsContainer<any>): LocalsContainer<any>; | ||
/** | ||
@@ -110,2 +112,6 @@ * Build all providers from given container (or GlobalProviders) and emit `$onInit` event. | ||
/** | ||
* Load all configurations registered on providers | ||
*/ | ||
resolveConfiguration(): void; | ||
/** | ||
* | ||
@@ -112,0 +118,0 @@ * @param instance |
@@ -10,2 +10,3 @@ "use strict"; | ||
const Provider_1 = require("../class/Provider"); | ||
const configuration_1 = require("../decorators/configuration"); | ||
const injectable_1 = require("../decorators/injectable"); | ||
@@ -16,3 +17,3 @@ const InjectionError_1 = require("../errors/InjectionError"); | ||
const GlobalProviders_1 = require("../registries/GlobalProviders"); | ||
const DISettings_1 = require("./DISettings"); | ||
const DIConfiguration_1 = require("./DIConfiguration"); | ||
/** | ||
@@ -45,4 +46,5 @@ * This service contain all services collected by `@Service` or services declared manually with `InjectorService.factory()` or `InjectorService.service()`. | ||
super(); | ||
this.settings = new DISettings_1.DISettings(); | ||
this.settings = new DIConfiguration_1.DIConfiguration(); | ||
this.logger = console; | ||
this.resolvedConfiguration = false; | ||
const provider = this.addProvider(InjectorService_1).getProvider(InjectorService_1); | ||
@@ -131,2 +133,3 @@ provider.instance = this; | ||
let instance; | ||
locals.set(configuration_1.Configuration, this.settings); | ||
if (locals.has(token)) { | ||
@@ -172,8 +175,12 @@ instance = locals.get(token); | ||
for (const provider of providers) { | ||
if (provider.isAsync()) { | ||
yield this.invoke(provider.token, locals); | ||
if (!provider.root) { | ||
if (!locals.has(provider.token)) { | ||
if (provider.isAsync()) { | ||
yield this.invoke(provider.token, locals); | ||
} | ||
if (provider.instance) { | ||
locals.set(provider.token, provider.instance); | ||
} | ||
} | ||
} | ||
if (provider.instance) { | ||
locals.set(provider.token, provider.instance); | ||
} | ||
} | ||
@@ -184,8 +191,5 @@ return locals; | ||
loadSync(locals = new LocalsContainer_1.LocalsContainer()) { | ||
const _super = Object.create(null, { | ||
toArray: { get: () => super.toArray } | ||
}); | ||
return tslib_1.__awaiter(this, void 0, void 0, function* () { | ||
const providers = _super.toArray.call(this); | ||
for (const provider of providers) { | ||
const providers = super.toArray(); | ||
for (const provider of providers) { | ||
if (!provider.root) { | ||
if (!locals.has(provider.token) && this.scopeOf(provider) === interfaces_1.ProviderScope.SINGLETON) { | ||
@@ -198,4 +202,4 @@ this.invoke(provider.token, locals); | ||
} | ||
return locals; | ||
}); | ||
} | ||
return locals; | ||
} | ||
@@ -211,3 +215,8 @@ /** | ||
this.addProviders(container); | ||
const locals = yield this.loadSync(yield this.loadAsync()); | ||
// Resolve configuration from providers | ||
this.resolveConfiguration(); | ||
// build async and sync provider | ||
let locals = yield this.loadAsync(); | ||
// load sync provider | ||
locals = this.loadSync(locals); | ||
yield locals.emit("$onInit"); | ||
@@ -218,2 +227,20 @@ return locals; | ||
/** | ||
* Load all configurations registered on providers | ||
*/ | ||
resolveConfiguration() { | ||
if (this.resolvedConfiguration) { | ||
return; | ||
} | ||
const rawSettings = this.settings.toRawObject(); | ||
// @ts-ignore | ||
this.settings.map.clear(); | ||
super.forEach(provider => { | ||
if (provider.configuration) { | ||
this.settings.merge(provider.configuration); | ||
} | ||
}); | ||
this.settings.merge(rawSettings); | ||
this.resolvedConfiguration = true; | ||
} | ||
/** | ||
* | ||
@@ -364,3 +391,3 @@ * @param instance | ||
resolve(target, locals, options = {}) { | ||
const { token, deps, construct, isBindable } = this.mapInvokeOptions(target, options); | ||
const { token, deps, construct, isBindable, imports } = this.mapInvokeOptions(target, options); | ||
const provider = this.getProvider(target); | ||
@@ -377,12 +404,17 @@ if (provider) { | ||
let instance; | ||
let currentDependency = false; | ||
try { | ||
const services = []; | ||
for (const dependency of deps) { | ||
const service = this.invoke(dependency, locals, { parent: token }); | ||
services.push(service); | ||
} | ||
const invokeDependency = (parent) => (token, index) => { | ||
currentDependency = { token, index, deps }; | ||
return core_1.isInheritedFrom(token, Provider_1.Provider, 1) ? provider : this.invoke(token, locals, { parent }); | ||
}; | ||
// Invoke manually imported providers | ||
imports.forEach(invokeDependency()); | ||
// Inject dependencies | ||
const services = deps.map(invokeDependency(token)); | ||
currentDependency = false; | ||
instance = construct(services); | ||
} | ||
catch (error) { | ||
throw new InjectionError_1.InjectionError(token, error); | ||
InjectionError_1.InjectionError.throwInjectorError(token, currentDependency, error); | ||
} | ||
@@ -403,2 +435,3 @@ if (instance === undefined) { | ||
mapInvokeOptions(token, options) { | ||
let imports = options.imports; | ||
let deps = options.deps; | ||
@@ -414,2 +447,3 @@ let scope = options.scope; | ||
deps = deps || provider.deps; | ||
imports = imports || provider.imports; | ||
if (provider.useValue) { | ||
@@ -434,2 +468,3 @@ construct = () => (core_1.isFunction(provider.useValue) ? provider.useValue() : provider.useValue); | ||
deps: deps || [], | ||
imports: imports || [], | ||
isBindable, | ||
@@ -436,0 +471,0 @@ construct |
{ | ||
"name": "@tsed/di", | ||
"version": "5.29.0", | ||
"version": "5.30.0", | ||
"description": "DI module for Ts.ED Framework", | ||
@@ -11,3 +11,3 @@ "main": "lib/index.js", | ||
"peerDependencies": { | ||
"@tsed/core": "5.29.0" | ||
"@tsed/core": "5.30.0" | ||
}, | ||
@@ -28,3 +28,3 @@ "devDependencies": {}, | ||
"license": "MIT", | ||
"gitHead": "b3e39b1b319bea62afcd691b98865825ad821e29" | ||
"gitHead": "b370087f701b23eafc981d351e7f31157382bb24" | ||
} |
@@ -71,3 +71,3 @@ import {IProvider} from "../interfaces/IProvider"; | ||
container.forEach(provider => { | ||
if (!this.hasProvider(provider.provide)) { | ||
if (!this.hasProvider(provider.provide) && !provider.root) { | ||
this.setProvider(provider.provide, provider.clone()); | ||
@@ -74,0 +74,0 @@ } |
@@ -1,3 +0,3 @@ | ||
import {Enumerable, getClass, getKeys, isClass, nameOf, NotEnumerable, Store, Type} from "@tsed/core"; | ||
import {ProviderScope} from "../interfaces"; | ||
import {classOf, Enumerable, getKeys, isClass, nameOf, NotEnumerable, Store, Type} from "@tsed/core"; | ||
import {IDIConfigurationOptions, ProviderScope} from "../interfaces"; | ||
import {IProvider} from "../interfaces/IProvider"; | ||
@@ -8,26 +8,21 @@ import {ProviderType} from "../interfaces/ProviderType"; | ||
export class Provider<T> implements IProvider<T> { | ||
/** | ||
* | ||
*/ | ||
@Enumerable() | ||
public root: boolean = false; | ||
@Enumerable() | ||
public type: ProviderType | any = ProviderType.PROVIDER; | ||
/** | ||
* | ||
*/ | ||
@Enumerable() | ||
public injectable: boolean = true; | ||
/** | ||
* | ||
*/ | ||
@Enumerable() | ||
public instance: T; | ||
/** | ||
* | ||
*/ | ||
@Enumerable() | ||
public deps: any[]; | ||
/** | ||
* | ||
*/ | ||
@Enumerable() | ||
public imports: any[]; | ||
@Enumerable() | ||
public useFactory: Function; | ||
@@ -37,31 +32,16 @@ | ||
public useAsyncFactory: Function; | ||
/** | ||
* | ||
*/ | ||
@Enumerable() | ||
public useValue: any; | ||
/** | ||
* | ||
*/ | ||
@NotEnumerable() | ||
protected _provide: TokenProvider; | ||
/** | ||
* | ||
*/ | ||
@NotEnumerable() | ||
protected _useClass: Type<T>; | ||
/** | ||
* | ||
*/ | ||
@NotEnumerable() | ||
protected _instance: T; | ||
/** | ||
* | ||
*/ | ||
@NotEnumerable() | ||
protected _scope: ProviderScope; | ||
/** | ||
* | ||
*/ | ||
@NotEnumerable() | ||
private _store: Store; | ||
@@ -80,6 +60,2 @@ | ||
/** | ||
* | ||
* @returns {any} | ||
*/ | ||
get provide(): TokenProvider { | ||
@@ -89,14 +65,9 @@ return this._provide; | ||
/** | ||
* | ||
* @param value | ||
*/ | ||
set provide(value: TokenProvider) { | ||
this._provide = isClass(value) ? getClass(value) : value; | ||
if (value) { | ||
this._provide = isClass(value) ? classOf(value) : value; | ||
this._store = Store.from(value); | ||
} | ||
} | ||
/** | ||
* | ||
* @returns {Type<T>} | ||
*/ | ||
get useClass(): Type<T> { | ||
@@ -113,3 +84,3 @@ return this._useClass; | ||
if (isClass(value)) { | ||
this._useClass = getClass(value); | ||
this._useClass = classOf(value); | ||
this._store = Store.from(value); | ||
@@ -119,6 +90,2 @@ } | ||
/** | ||
* | ||
* @returns {string} | ||
*/ | ||
get className() { | ||
@@ -128,5 +95,2 @@ return this.name; | ||
/** | ||
* | ||
*/ | ||
get name() { | ||
@@ -136,6 +100,2 @@ return nameOf(this.provide); | ||
/** | ||
* | ||
* @returns {Store} | ||
*/ | ||
public get store(): Store { | ||
@@ -159,3 +119,3 @@ return this._store; | ||
return this._store ? this.store.get("scope") : this._scope; | ||
return this.store.get("scope"); | ||
} | ||
@@ -169,5 +129,14 @@ | ||
set scope(scope: ProviderScope) { | ||
this._store ? this.store.set("scope", scope) : this._scope; | ||
this.store.set("scope", scope); | ||
} | ||
get configuration(): Partial<IDIConfigurationOptions> { | ||
return this.store.get("configuration"); | ||
} | ||
@Enumerable() | ||
set configuration(configuration: Partial<IDIConfigurationOptions>) { | ||
this.store.set("configuration", configuration); | ||
} | ||
isAsync(): boolean { | ||
@@ -177,10 +146,9 @@ return !!this.useAsyncFactory; | ||
/** | ||
* | ||
*/ | ||
clone(): Provider<any> { | ||
const provider = new (getClass(this))(this.token); | ||
const provider = new (classOf(this))(this.token); | ||
getKeys(this).forEach(key => { | ||
provider[key] = this[key]; | ||
if (this[key] !== undefined) { | ||
provider[key] = this[key]; | ||
} | ||
}); | ||
@@ -187,0 +155,0 @@ |
@@ -6,3 +6,3 @@ import {Store} from "@tsed/core"; | ||
/** | ||
* Return value from ServerSettingsService. | ||
* Return value from Configuration. | ||
* | ||
@@ -9,0 +9,0 @@ * ## Example |
@@ -6,3 +6,3 @@ import {Store} from "@tsed/core"; | ||
/** | ||
* Return value from ServerSettingsService. | ||
* Return value from Configuration. | ||
* | ||
@@ -9,0 +9,0 @@ * ## Example |
@@ -1,2 +0,3 @@ | ||
import {isString, nameOf} from "@tsed/core"; | ||
import {getConstructorArgNames, isClass, isString, nameOf} from "@tsed/core"; | ||
import {colorize} from "ts-log-debug/lib/layouts/utils/colorizeUtils"; | ||
import {TokenProvider} from "../interfaces"; | ||
@@ -36,2 +37,31 @@ | ||
} | ||
static throwInjectorError(token: any, currentDependency: any, error: any) { | ||
if (currentDependency && isClass(token)) { | ||
error.message = printDependencyInjectionError(token, {...currentDependency, message: error.message}); | ||
} | ||
throw new InjectionError(token, error); | ||
} | ||
} | ||
function printDependencyInjectionError(token: any, options: {token: any; index: number; deps: any[]; message: string}) { | ||
let erroredArg = ""; | ||
const args = getConstructorArgNames(token) | ||
.map((arg, index) => { | ||
if (options.index === index) { | ||
erroredArg = arg; | ||
arg = colorize(arg, "red"); | ||
} | ||
return `${arg}: ${nameOf(options.deps[index])}`; | ||
}) | ||
.join(", "); | ||
const signature = nameOf(token) + "->constructor(" + args + ")"; | ||
const indexOf = signature.indexOf(erroredArg) - 5; | ||
const drawline = (indexOf: number) => " ".repeat(indexOf) + colorize("^" + "‾".repeat(erroredArg.length - 1), "red"); | ||
return "Unable to inject dependency. " + options.message + "\n\n" + signature + "\n" + (indexOf > -1 ? drawline(indexOf) : ""); | ||
} |
@@ -16,7 +16,9 @@ export * from "./class/Provider"; | ||
export * from "./decorators/interceptor"; | ||
export * from "./decorators/configuration"; | ||
export * from "./decorators/module"; | ||
export * from "./registries/ProviderRegistry"; | ||
export * from "./registries/GlobalProviders"; | ||
export * from "./services/InjectorService"; | ||
export * from "./services/DISettings"; | ||
export * from "./services/DIConfiguration"; | ||
export * from "./errors/InjectionError"; | ||
export * from "./errors/UndefinedTokenError"; |
@@ -6,2 +6,3 @@ import {ProviderScope} from "./ProviderScope"; | ||
deps: any[]; | ||
imports: any[]; | ||
parent?: TokenProvider; | ||
@@ -8,0 +9,0 @@ scope: ProviderScope; |
export * from "./IDILogger"; | ||
export * from "./IDISettings"; | ||
export * from "./IDIConfigurationOptions"; | ||
export * from "./IInjectableProperties"; | ||
@@ -4,0 +4,0 @@ export * from "./IInterceptor"; |
@@ -5,3 +5,2 @@ import {Type} from "@tsed/core"; | ||
import {TokenProvider} from "./TokenProvider"; | ||
/** | ||
@@ -8,0 +7,0 @@ * |
@@ -1,2 +0,2 @@ | ||
import {deepClone, getClass, getClassOrSymbol, isFunction, Metadata, nameOf, prototypeOf, Store} from "@tsed/core"; | ||
import {deepClone, getClass, getClassOrSymbol, isFunction, isInheritedFrom, Metadata, nameOf, prototypeOf, Store} from "@tsed/core"; | ||
@@ -7,2 +7,3 @@ import * as util from "util"; | ||
import {Provider} from "../class/Provider"; | ||
import {Configuration} from "../decorators/configuration"; | ||
import {Injectable} from "../decorators/injectable"; | ||
@@ -12,4 +13,4 @@ import {InjectionError} from "../errors/InjectionError"; | ||
import { | ||
IDIConfigurationOptions, | ||
IDILogger, | ||
IDISettings, | ||
IInjectableProperties, | ||
@@ -26,3 +27,3 @@ IInjectablePropertyService, | ||
import {GlobalProviders} from "../registries/GlobalProviders"; | ||
import {DISettings} from "./DISettings"; | ||
import {DIConfiguration} from "./DIConfiguration"; | ||
@@ -35,2 +36,3 @@ interface IInvokeSettings { | ||
deps: any[]; | ||
imports: any[]; | ||
@@ -69,4 +71,5 @@ construct(deps: TokenProvider[]): any; | ||
export class InjectorService extends Container { | ||
public settings: IDISettings = new DISettings(); | ||
public settings: IDIConfigurationOptions & DIConfiguration = new DIConfiguration() as any; | ||
public logger: IDILogger = console; | ||
private resolvedConfiguration: boolean = false; | ||
@@ -172,2 +175,4 @@ constructor() { | ||
locals.set(Configuration, this.settings); | ||
if (locals.has(token)) { | ||
@@ -214,8 +219,12 @@ instance = locals.get(token); | ||
for (const provider of providers) { | ||
if (provider.isAsync()) { | ||
await this.invoke(provider.token, locals); | ||
} | ||
if (!provider.root) { | ||
if (!locals.has(provider.token)) { | ||
if (provider.isAsync()) { | ||
await this.invoke(provider.token, locals); | ||
} | ||
if (provider.instance) { | ||
locals.set(provider.token, provider.instance); | ||
if (provider.instance) { | ||
locals.set(provider.token, provider.instance); | ||
} | ||
} | ||
} | ||
@@ -227,12 +236,14 @@ } | ||
async loadSync(locals: LocalsContainer<any> = new LocalsContainer()) { | ||
loadSync(locals: LocalsContainer<any> = new LocalsContainer()) { | ||
const providers = super.toArray(); | ||
for (const provider of providers) { | ||
if (!locals.has(provider.token) && this.scopeOf(provider) === ProviderScope.SINGLETON) { | ||
this.invoke(provider.token, locals); | ||
} | ||
if (!provider.root) { | ||
if (!locals.has(provider.token) && this.scopeOf(provider) === ProviderScope.SINGLETON) { | ||
this.invoke(provider.token, locals); | ||
} | ||
if (provider.instance) { | ||
locals.set(provider.token, provider.instance); | ||
if (provider.instance) { | ||
locals.set(provider.token, provider.instance); | ||
} | ||
} | ||
@@ -253,4 +264,11 @@ } | ||
const locals = await this.loadSync(await this.loadAsync()); | ||
// Resolve configuration from providers | ||
this.resolveConfiguration(); | ||
// build async and sync provider | ||
let locals = await this.loadAsync(); | ||
// load sync provider | ||
locals = this.loadSync(locals); | ||
await locals.emit("$onInit"); | ||
@@ -262,2 +280,26 @@ | ||
/** | ||
* Load all configurations registered on providers | ||
*/ | ||
resolveConfiguration() { | ||
if (this.resolvedConfiguration) { | ||
return; | ||
} | ||
const rawSettings = this.settings.toRawObject(); | ||
// @ts-ignore | ||
this.settings.map.clear(); | ||
super.forEach(provider => { | ||
if (provider.configuration) { | ||
this.settings.merge(provider.configuration); | ||
} | ||
}); | ||
this.settings.merge(rawSettings); | ||
this.resolvedConfiguration = true; | ||
} | ||
/** | ||
* | ||
@@ -438,3 +480,3 @@ * @param instance | ||
private resolve<T>(target: TokenProvider, locals: Map<TokenProvider, any>, options: Partial<IInvokeOptions<T>> = {}): Promise<T> { | ||
const {token, deps, construct, isBindable} = this.mapInvokeOptions(target, options); | ||
const {token, deps, construct, isBindable, imports} = this.mapInvokeOptions(target, options); | ||
const provider = this.getProvider(target); | ||
@@ -454,13 +496,22 @@ | ||
let instance: any; | ||
let currentDependency: any = false; | ||
try { | ||
const services = []; | ||
for (const dependency of deps) { | ||
const service = this.invoke(dependency, locals, {parent: token}); | ||
services.push(service); | ||
} | ||
const invokeDependency = (parent?: any) => (token: any, index: number): any => { | ||
currentDependency = {token, index, deps}; | ||
return isInheritedFrom(token, Provider, 1) ? provider : this.invoke(token, locals, {parent}); | ||
}; | ||
// Invoke manually imported providers | ||
imports.forEach(invokeDependency()); | ||
// Inject dependencies | ||
const services = deps.map(invokeDependency(token)); | ||
currentDependency = false; | ||
instance = construct(services); | ||
} catch (error) { | ||
throw new InjectionError(token, error); | ||
InjectionError.throwInjectorError(token, currentDependency, error); | ||
} | ||
@@ -488,2 +539,3 @@ | ||
private mapInvokeOptions(token: TokenProvider, options: Partial<IInvokeOptions<any>>): IInvokeSettings { | ||
let imports: TokenProvider[] | undefined = options.imports; | ||
let deps: TokenProvider[] | undefined = options.deps; | ||
@@ -502,2 +554,3 @@ let scope = options.scope; | ||
deps = deps || provider.deps; | ||
imports = imports || provider.imports; | ||
@@ -521,2 +574,3 @@ if (provider.useValue) { | ||
deps: deps! || [], | ||
imports: imports || [], | ||
isBindable, | ||
@@ -523,0 +577,0 @@ construct |
@@ -22,2 +22,3 @@ import {expect} from "chai"; | ||
expect(getKeys(provider)).to.deep.eq([ | ||
"root", | ||
"type", | ||
@@ -28,4 +29,6 @@ "injectable", | ||
"scope", | ||
"configuration", | ||
"instance", | ||
"deps", | ||
"imports", | ||
"useFactory", | ||
@@ -45,9 +48,4 @@ "useAsyncFactory", | ||
expect(!!provider.useClass).to.eq(false); | ||
expect(!!provider.store).to.eq(false); | ||
expect(!!provider.store).to.eq(true); | ||
}); | ||
it("should should return scope", () => { | ||
const provider = new Provider(T1); | ||
}); | ||
}); | ||
@@ -61,9 +59,4 @@ | ||
expect(!!provider.useClass).to.eq(false); | ||
expect(!!provider.store).to.eq(false); | ||
expect(!!provider.store).to.eq(true); | ||
}); | ||
it("should should return scope", () => { | ||
const provider = new Provider(T1); | ||
}); | ||
}); | ||
@@ -70,0 +63,0 @@ |
@@ -6,2 +6,3 @@ import {Store} from "@tsed/core"; | ||
import {GlobalProviders, LocalsContainer} from "../../src"; | ||
import {Configuration} from "../../src/decorators/configuration"; | ||
import {ProviderType} from "../../src/interfaces"; | ||
@@ -404,4 +405,8 @@ | ||
const token2 = class Ctrl { | ||
constructor() { | ||
} | ||
}; | ||
const token3 = class Test { | ||
constructor(test: any) { | ||
} | ||
}; | ||
@@ -431,4 +436,39 @@ | ||
// THEN | ||
actualError.message.should.eq("Injection failed on Test\nOrigin: Given token is undefined. Have you enabled emitDecoratorMetadata in your tsconfig.json or decorated your class with @Injectable, @Service, ... decorator ?"); | ||
actualError.message.should.contains("Injection failed on Test\nOrigin: Unable to inject dependency. Given token is undefined. Have you enabled emitDecoratorMetadata in your tsconfig.json or decorated your class with @Injectable, @Service, ... decorator ?"); | ||
}); | ||
it("should throw InjectionError > Object", async () => { | ||
// GIVEN | ||
const token2 = class Ctrl { | ||
constructor() { | ||
} | ||
}; | ||
const token3 = class Test { | ||
constructor(test: Object) { | ||
} | ||
}; | ||
const provider2 = new Provider<any>(token2); | ||
provider2.scope = ProviderScope.SINGLETON; | ||
provider2.type = ProviderType.CONTROLLER; | ||
provider2.useClass = token2; | ||
const provider3 = new Provider<any>(token3); | ||
provider3.scope = ProviderScope.SINGLETON; | ||
provider3.deps = [Object]; | ||
const injector = new InjectorService(); | ||
injector.set(token2, provider2); | ||
injector.set(token3, provider3); | ||
// WHEN | ||
let actualError; | ||
try { | ||
injector.invoke(token3); | ||
} catch (er) { | ||
actualError = er; | ||
} | ||
// THEN | ||
actualError.message.should.contains("Injection failed on Test\nOrigin: Unable to inject dependency."); | ||
}); | ||
}); | ||
@@ -441,2 +481,4 @@ describe("when error occur", () => { | ||
const token3 = class Test { | ||
constructor(dep: any) { | ||
} | ||
}; | ||
@@ -474,2 +516,47 @@ | ||
}); | ||
describe("when provider has Provider as dependencies", () => { | ||
it("should inject Provider", () => { | ||
// GIVEN | ||
const injector = new InjectorService(); | ||
const token = Symbol.for("TokenProvider1"); | ||
injector.add(token, { | ||
deps: [ | ||
Provider | ||
], | ||
configuration: { | ||
"test": "test" | ||
}, | ||
useFactory(provider: any) { | ||
return {to: provider}; | ||
} | ||
}); | ||
// WHEN | ||
const instance: any = injector.invoke(token)!; | ||
// THEN | ||
instance.should.deep.eq({to: injector.getProvider(token)}); | ||
}); | ||
}); | ||
describe("when provider has Configuration as dependencies", () => { | ||
it("should inject Provider", () => { | ||
// GIVEN | ||
const injector = new InjectorService(); | ||
const token = Symbol.for("TokenProvider1"); | ||
injector.add(token, { | ||
deps: [ | ||
Configuration | ||
], | ||
useFactory(settings: any) { | ||
return {to: settings}; | ||
} | ||
}); | ||
// WHEN | ||
const instance: any = injector.invoke(token)!; | ||
// THEN | ||
instance.should.deep.eq({to: injector.settings}); | ||
}); | ||
}); | ||
}); | ||
@@ -707,2 +794,43 @@ | ||
}); | ||
describe("resolveConfiguration()", () => { | ||
it("should load configuration from each providers", () => { | ||
// GIVEN | ||
const injector = new InjectorService(); | ||
injector.settings.set({ | ||
scopes: { | ||
[ProviderType.VALUE]: ProviderScope.SINGLETON | ||
} | ||
}); | ||
injector.add(Symbol.for("TOKEN1"), { | ||
configuration: { | ||
custom: "config", | ||
scopes: { | ||
"provider_custom": ProviderScope.SINGLETON | ||
} | ||
} | ||
}); | ||
injector.add(Symbol.for("TOKEN2"), { | ||
configuration: { | ||
scopes: { | ||
"provider_custom_2": ProviderScope.SINGLETON | ||
} | ||
} | ||
}); | ||
// WHEN | ||
injector.resolveConfiguration(); | ||
// THEN | ||
injector.settings.get<string>("custom").should.eq("config"); | ||
injector.settings.get<any>("scopes").should.deep.eq({ | ||
provider_custom_2: "singleton", | ||
provider_custom: "singleton", | ||
value: "singleton" | ||
}); | ||
}); | ||
}); | ||
}); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
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
368421
220
6096