miter
Version:
A typescript web framework based on ExpressJs based loosely on SailsJs
203 lines • 8.74 kB
JavaScript
"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