You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Injector.ts 2.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. import 'reflect-metadata';
  2. import { ERR_NO_INITIALIZE_WITH_PRIORITY, ERR_NO_INJECTION_TOKEN } from './Strings';
  3. import { Constructor, Type, SingletonDefinition, InjectionError, InjectionResolutionError } from './Internals';
  4. class _Injector {
  5. private injectionQueue: any[] = []
  6. private singletonDefinitions: SingletonDefinition[] = []
  7. private singletonObjects: { [classname in string]: any } = {}
  8. private tokenLookupTable: { [token in string]: Constructor<any> } = {}
  9. private initialized = false
  10. /**
  11. * Resolves instances by injecting required services
  12. * @param {Type<any>} request
  13. * @returns {T}
  14. */
  15. public resolve<T>(request: Constructor<T>): T {
  16. if (!this.initialized) {
  17. this.initialize()
  18. this.initialized = true
  19. }
  20. return this.singletonObjects[request.name] as any
  21. }
  22. public async resolveAsync<T>(target: Type<T>): Promise<T> {
  23. if (!this.initialized) {
  24. this.initialize()
  25. this.initialized = true
  26. }
  27. return this.singletonObjects[target.name] as any
  28. }
  29. private initialize = () => {
  30. this.createSingletons()
  31. this.injectDependencies()
  32. this.initializeSingletons()
  33. this.cleanup()
  34. }
  35. private createSingletons = () => {
  36. this.singletonDefinitions.forEach(def => {
  37. const obj = new def.ctor()
  38. if (def.initializationPriority != undefined && !obj.initialize) {
  39. throw new InjectionError(ERR_NO_INITIALIZE_WITH_PRIORITY(def.ctor))
  40. }
  41. this.singletonObjects[def.ctor.name] = obj
  42. })
  43. }
  44. private injectDependencies = () => {
  45. this.injectionQueue.forEach(inj => {
  46. if (inj.token.name in this.tokenLookupTable) { //injection alias was used
  47. inj.token = this.tokenLookupTable[inj.token.name]
  48. }
  49. if (this.singletonObjects[inj.token.name]) {
  50. this.singletonObjects[inj.receiver.constructor.name][inj.key] = this.singletonObjects[inj.token.name]
  51. } else {
  52. throw new InjectionResolutionError(ERR_NO_INJECTION_TOKEN(inj.injectionType))
  53. }
  54. })
  55. }
  56. private initializeSingletons = () => {
  57. this.singletonDefinitions
  58. .sort((a, b) => (a.initializationPriority ?? 0) - (b.initializationPriority ?? 0))
  59. .map(def => this.singletonObjects[def.ctor.name])
  60. .forEach(obj => obj.initialize ? obj.initialize() : undefined)
  61. }
  62. private cleanup = () => {
  63. while (this.singletonDefinitions.length > 0)
  64. this.singletonDefinitions.pop()
  65. while (this.injectionQueue.length > 0)
  66. this.injectionQueue.pop()
  67. }
  68. }
  69. /**
  70. * The Injector stores services and resolves requested instances.
  71. */
  72. export const Injector = new _Injector()