UNPKG

miter

Version:

A typescript web framework based on ExpressJs based loosely on SailsJs

203 lines 8.74 kB
"use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; Object.defineProperty(exports, "__esModule", { value: true }); require("reflect-metadata"); const injectable_decorator_1 = require("../decorators/services/injectable.decorator"); const metadata_1 = require("../metadata/services/metadata"); const injectable_1 = require("../metadata/services/injectable"); const logger_core_1 = require("../services/logger-core"); const logger_1 = require("../services/logger"); const _ = require("lodash"); let Injector = Injector_1 = class Injector { constructor(_loggerCore) { this._loggerCore = _loggerCore; this.temporaryValue = Symbol.for('recursive-injection'); this.cache = new Map(); this.metaStack = []; this.metaDefaults = new Map(); this.cache.set(logger_core_1.LoggerCore, () => this._loggerCore); this.cache.set(Injector_1, () => this); this.logger = logger_1.Logger.fromSubsystem(this._loggerCore, 'injector'); } resolveInjectable(ctorFn) { if (!ctorFn) { throw new Error('Attempted to inject a falsey type.'); } this.logger.verbose(`Resolving ${ctorFn.name || ctorFn}`); if (this.cache.get(ctorFn) === this.temporaryValue) { throw new Error(`Detected circular dependency. Recursive injection of type ${ctorFn.name || ctorFn}`); } if (!this.cache.has(ctorFn)) { let injectableMeta = Reflect.getOwnMetadata(injectable_1.InjectableMetadataSym, ctorFn.prototype) || {}; if (injectableMeta.provide) { let source = injectableMeta.provide; source.provide = ctorFn; if (this.isClassSource(source) || this.isValueSource(source)) throw new Error(`Injectable metadata { provide: ... } can only be used to specify a callback function.`); this.provide(source); } else { this.cache.set(ctorFn, this.temporaryValue); let inst = this.construct(ctorFn); this.cache.set(ctorFn, () => inst); return inst; } } return this.cache.get(ctorFn)(); } construct(ctorFn) { let types = Reflect.getOwnMetadata('design:paramtypes', ctorFn); let values = this.resolveDependencies(types, ctorFn); return new ctorFn(...values); } constructCallback(provideMeta) { let types = _.clone(provideMeta.deps); let ctor = provideMeta.provide; let name = `${ctor.name || ctor}`; let injecting = false; let invokeCallback = () => { if (injecting) { throw new Error(`Detected circular dependency. Recursive injection of type ${name}`); } injecting = true; let values = this.resolveDependencies(types, provideMeta.provide); let result = provideMeta.useCallback(...values); injecting = false; return result; }; if (!provideMeta.cache) return invokeCallback; let cachedValue; let hasCachedValue = false; return () => { if (!hasCachedValue) { hasCachedValue = true; cachedValue = invokeCallback(); } return cachedValue; }; } resolveDependencies(deps, ctor) { let map = Reflect.getOwnMetadata(metadata_1.MetadataMetadataSym, ctor.prototype); this.metaStack.push([ctor, map]); let len = this.metaStack.length; let failed = false; try { let name = `${(ctor && ctor.name) || ctor}`; if (!deps) deps = []; let types = deps.map(dep => this.getSimpleDependency(dep)); let typesStr = `[${types.map(type => this.stringifyDependency(type)).join(', ')}]`; this.logger.verbose(`Constructing ${name} with types ${typesStr}`); for (let q = 0; q < types.length; q++) { let type = types[q]; if (!type || type === Object || type === String) { throw new Error(`Could not dependency inject ${name}. Failed to resolve dependencies. Reflected: ${typesStr}`); } } let values = types.map(type => this.resolveDependency(type)); return values; } catch (e) { failed = true; throw e; } finally { if (len != this.metaStack.length) { if (!failed) throw new Error(`Metadata stack is the wrong size in Injector.resolveDependencies!`); else this.logger.error(`BIG PROBLEMS! Metadata stack is the wrong size in Injector.resolveDependencies! Throwing an exception would hide another one.`); } else this.metaStack.pop(); } } getSimpleDependency(dep) { if (typeof dep === 'function' && !dep.prototype) { let result = dep(); return result; } return dep; } stringifyDependency(type) { if (typeof type === 'undefined') return 'undefined'; if (typeof type === 'string') return `'${type}'`; return (type && type.name) || type; } resolveDependency(type) { if (typeof type === 'string') return this.resolveMetadata(type); return this.resolveInjectable(type); } resolveMetadata(name) { let metaFrame = (this.metaStack.length > 1 && this.metaStack[this.metaStack.length - 2]) || [undefined, undefined]; let [ctor, map] = metaFrame; if (map && map.has(name)) return map.get(name); if (ctor && name === 'name') return `${ctor.name || ctor}`; if (this.metaDefaults.has(name)) return this.metaDefaults.get(name); return undefined; } provide(provideMeta) { if (!provideMeta) throw new Error(`Invalid ProvideMetadata: ${provideMeta}`); let ctorFn = provideMeta.provide; if (!ctorFn) throw new Error('Attempted to provide a value for a falsey type.'); if (this.cache.has(ctorFn)) throw new Error(`Duplicate value provided for ${ctorFn.name || ctorFn}.`); let tFn; if (this.isClassSource(provideMeta)) { this.logger.verbose(`Providing ${ctorFn.name || ctorFn} using class ${provideMeta.useClass.name || provideMeta.useClass}`); let t = this.construct(provideMeta.useClass); tFn = () => t; } else if (this.isValueSource(provideMeta)) { this.logger.verbose(`Providing ${ctorFn.name || ctorFn} using value ${JSON.stringify(provideMeta.useValue)}`); let t = provideMeta.useValue; tFn = () => t; } else if (this.isCallbackSource(provideMeta)) { this.logger.verbose(`Providing ${ctorFn.name || ctorFn} using callback ${provideMeta.useCallback.name || provideMeta.useCallback}`); tFn = this.constructCallback(provideMeta); } else throw new Error(`Could not resolve dependency injection provider source.`); this.cache.set(ctorFn, tFn); return this; } provideMetadata(name, value) { if (this.metaDefaults.has(name)) throw new Error(`A value has already been provided for metadata '${name}'`); this.metaDefaults.set(name, value); } isClassSource(meta) { return !!meta.useClass; } isValueSource(meta) { return typeof meta.useValue !== 'undefined'; } isCallbackSource(meta) { return !!meta.useCallback; } }; Injector = Injector_1 = __decorate([ injectable_decorator_1.Injectable(), __metadata("design:paramtypes", [logger_core_1.LoggerCore]) ], Injector); exports.Injector = Injector; var Injector_1; //# sourceMappingURL=injector.js.map