dependency-container.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.instance = exports.typeInfo = void 0;
  4. const tslib_1 = require("tslib");
  5. const providers_1 = require("./providers");
  6. const provider_1 = require("./providers/provider");
  7. const injection_token_1 = require("./providers/injection-token");
  8. const registry_1 = require("./registry");
  9. const lifecycle_1 = require("./types/lifecycle");
  10. const resolution_context_1 = require("./resolution-context");
  11. const error_helpers_1 = require("./error-helpers");
  12. const lazy_helpers_1 = require("./lazy-helpers");
  13. const disposable_1 = require("./types/disposable");
  14. const interceptors_1 = require("./interceptors");
  15. exports.typeInfo = new Map();
  16. class InternalDependencyContainer {
  17. constructor(parent) {
  18. this.parent = parent;
  19. this._registry = new registry_1.default();
  20. this.interceptors = new interceptors_1.default();
  21. this.disposed = false;
  22. this.disposables = new Set();
  23. }
  24. register(token, providerOrConstructor, options = { lifecycle: lifecycle_1.default.Transient }) {
  25. this.ensureNotDisposed();
  26. let provider;
  27. if (!provider_1.isProvider(providerOrConstructor)) {
  28. provider = { useClass: providerOrConstructor };
  29. }
  30. else {
  31. provider = providerOrConstructor;
  32. }
  33. if (providers_1.isTokenProvider(provider)) {
  34. const path = [token];
  35. let tokenProvider = provider;
  36. while (tokenProvider != null) {
  37. const currentToken = tokenProvider.useToken;
  38. if (path.includes(currentToken)) {
  39. throw new Error(`Token registration cycle detected! ${[...path, currentToken].join(" -> ")}`);
  40. }
  41. path.push(currentToken);
  42. const registration = this._registry.get(currentToken);
  43. if (registration && providers_1.isTokenProvider(registration.provider)) {
  44. tokenProvider = registration.provider;
  45. }
  46. else {
  47. tokenProvider = null;
  48. }
  49. }
  50. }
  51. if (options.lifecycle === lifecycle_1.default.Singleton ||
  52. options.lifecycle == lifecycle_1.default.ContainerScoped ||
  53. options.lifecycle == lifecycle_1.default.ResolutionScoped) {
  54. if (providers_1.isValueProvider(provider) || providers_1.isFactoryProvider(provider)) {
  55. throw new Error(`Cannot use lifecycle "${lifecycle_1.default[options.lifecycle]}" with ValueProviders or FactoryProviders`);
  56. }
  57. }
  58. this._registry.set(token, { provider, options });
  59. return this;
  60. }
  61. registerType(from, to) {
  62. this.ensureNotDisposed();
  63. if (providers_1.isNormalToken(to)) {
  64. return this.register(from, {
  65. useToken: to
  66. });
  67. }
  68. return this.register(from, {
  69. useClass: to
  70. });
  71. }
  72. registerInstance(token, instance) {
  73. this.ensureNotDisposed();
  74. return this.register(token, {
  75. useValue: instance
  76. });
  77. }
  78. registerSingleton(from, to) {
  79. this.ensureNotDisposed();
  80. if (providers_1.isNormalToken(from)) {
  81. if (providers_1.isNormalToken(to)) {
  82. return this.register(from, {
  83. useToken: to
  84. }, { lifecycle: lifecycle_1.default.Singleton });
  85. }
  86. else if (to) {
  87. return this.register(from, {
  88. useClass: to
  89. }, { lifecycle: lifecycle_1.default.Singleton });
  90. }
  91. throw new Error('Cannot register a type name as a singleton without a "to" token');
  92. }
  93. let useClass = from;
  94. if (to && !providers_1.isNormalToken(to)) {
  95. useClass = to;
  96. }
  97. return this.register(from, {
  98. useClass
  99. }, { lifecycle: lifecycle_1.default.Singleton });
  100. }
  101. resolve(token, context = new resolution_context_1.default(), isOptional = false) {
  102. this.ensureNotDisposed();
  103. const registration = this.getRegistration(token);
  104. if (!registration && providers_1.isNormalToken(token)) {
  105. if (isOptional) {
  106. return undefined;
  107. }
  108. throw new Error(`Attempted to resolve unregistered dependency token: "${token.toString()}"`);
  109. }
  110. this.executePreResolutionInterceptor(token, "Single");
  111. if (registration) {
  112. const result = this.resolveRegistration(registration, context);
  113. this.executePostResolutionInterceptor(token, result, "Single");
  114. return result;
  115. }
  116. if (injection_token_1.isConstructorToken(token)) {
  117. const result = this.construct(token, context);
  118. this.executePostResolutionInterceptor(token, result, "Single");
  119. return result;
  120. }
  121. throw new Error("Attempted to construct an undefined constructor. Could mean a circular dependency problem. Try using `delay` function.");
  122. }
  123. executePreResolutionInterceptor(token, resolutionType) {
  124. if (this.interceptors.preResolution.has(token)) {
  125. const remainingInterceptors = [];
  126. for (const interceptor of this.interceptors.preResolution.getAll(token)) {
  127. if (interceptor.options.frequency != "Once") {
  128. remainingInterceptors.push(interceptor);
  129. }
  130. interceptor.callback(token, resolutionType);
  131. }
  132. this.interceptors.preResolution.setAll(token, remainingInterceptors);
  133. }
  134. }
  135. executePostResolutionInterceptor(token, result, resolutionType) {
  136. if (this.interceptors.postResolution.has(token)) {
  137. const remainingInterceptors = [];
  138. for (const interceptor of this.interceptors.postResolution.getAll(token)) {
  139. if (interceptor.options.frequency != "Once") {
  140. remainingInterceptors.push(interceptor);
  141. }
  142. interceptor.callback(token, result, resolutionType);
  143. }
  144. this.interceptors.postResolution.setAll(token, remainingInterceptors);
  145. }
  146. }
  147. resolveRegistration(registration, context) {
  148. this.ensureNotDisposed();
  149. if (registration.options.lifecycle === lifecycle_1.default.ResolutionScoped &&
  150. context.scopedResolutions.has(registration)) {
  151. return context.scopedResolutions.get(registration);
  152. }
  153. const isSingleton = registration.options.lifecycle === lifecycle_1.default.Singleton;
  154. const isContainerScoped = registration.options.lifecycle === lifecycle_1.default.ContainerScoped;
  155. const returnInstance = isSingleton || isContainerScoped;
  156. let resolved;
  157. if (providers_1.isValueProvider(registration.provider)) {
  158. resolved = registration.provider.useValue;
  159. }
  160. else if (providers_1.isTokenProvider(registration.provider)) {
  161. resolved = returnInstance
  162. ? registration.instance ||
  163. (registration.instance = this.resolve(registration.provider.useToken, context))
  164. : this.resolve(registration.provider.useToken, context);
  165. }
  166. else if (providers_1.isClassProvider(registration.provider)) {
  167. resolved = returnInstance
  168. ? registration.instance ||
  169. (registration.instance = this.construct(registration.provider.useClass, context))
  170. : this.construct(registration.provider.useClass, context);
  171. }
  172. else if (providers_1.isFactoryProvider(registration.provider)) {
  173. resolved = registration.provider.useFactory(this);
  174. }
  175. else {
  176. resolved = this.construct(registration.provider, context);
  177. }
  178. if (registration.options.lifecycle === lifecycle_1.default.ResolutionScoped) {
  179. context.scopedResolutions.set(registration, resolved);
  180. }
  181. return resolved;
  182. }
  183. resolveAll(token, context = new resolution_context_1.default(), isOptional = false) {
  184. this.ensureNotDisposed();
  185. const registrations = this.getAllRegistrations(token);
  186. if (!registrations && providers_1.isNormalToken(token)) {
  187. if (isOptional) {
  188. return [];
  189. }
  190. throw new Error(`Attempted to resolve unregistered dependency token: "${token.toString()}"`);
  191. }
  192. this.executePreResolutionInterceptor(token, "All");
  193. if (registrations) {
  194. const result = registrations.map(item => this.resolveRegistration(item, context));
  195. this.executePostResolutionInterceptor(token, result, "All");
  196. return result;
  197. }
  198. const result = [this.construct(token, context)];
  199. this.executePostResolutionInterceptor(token, result, "All");
  200. return result;
  201. }
  202. isRegistered(token, recursive = false) {
  203. this.ensureNotDisposed();
  204. return (this._registry.has(token) ||
  205. (recursive &&
  206. (this.parent || false) &&
  207. this.parent.isRegistered(token, true)));
  208. }
  209. reset() {
  210. this.ensureNotDisposed();
  211. this._registry.clear();
  212. this.interceptors.preResolution.clear();
  213. this.interceptors.postResolution.clear();
  214. }
  215. clearInstances() {
  216. this.ensureNotDisposed();
  217. for (const [token, registrations] of this._registry.entries()) {
  218. this._registry.setAll(token, registrations
  219. .filter(registration => !providers_1.isValueProvider(registration.provider))
  220. .map(registration => {
  221. registration.instance = undefined;
  222. return registration;
  223. }));
  224. }
  225. }
  226. createChildContainer() {
  227. this.ensureNotDisposed();
  228. const childContainer = new InternalDependencyContainer(this);
  229. for (const [token, registrations] of this._registry.entries()) {
  230. if (registrations.some(({ options }) => options.lifecycle === lifecycle_1.default.ContainerScoped)) {
  231. childContainer._registry.setAll(token, registrations.map(registration => {
  232. if (registration.options.lifecycle === lifecycle_1.default.ContainerScoped) {
  233. return {
  234. provider: registration.provider,
  235. options: registration.options
  236. };
  237. }
  238. return registration;
  239. }));
  240. }
  241. }
  242. return childContainer;
  243. }
  244. beforeResolution(token, callback, options = { frequency: "Always" }) {
  245. this.interceptors.preResolution.set(token, {
  246. callback: callback,
  247. options: options
  248. });
  249. }
  250. afterResolution(token, callback, options = { frequency: "Always" }) {
  251. this.interceptors.postResolution.set(token, {
  252. callback: callback,
  253. options: options
  254. });
  255. }
  256. dispose() {
  257. return tslib_1.__awaiter(this, void 0, void 0, function* () {
  258. this.disposed = true;
  259. const promises = [];
  260. this.disposables.forEach(disposable => {
  261. const maybePromise = disposable.dispose();
  262. if (maybePromise) {
  263. promises.push(maybePromise);
  264. }
  265. });
  266. yield Promise.all(promises);
  267. });
  268. }
  269. getRegistration(token) {
  270. if (this.isRegistered(token)) {
  271. return this._registry.get(token);
  272. }
  273. if (this.parent) {
  274. return this.parent.getRegistration(token);
  275. }
  276. return null;
  277. }
  278. getAllRegistrations(token) {
  279. if (this.isRegistered(token)) {
  280. return this._registry.getAll(token);
  281. }
  282. if (this.parent) {
  283. return this.parent.getAllRegistrations(token);
  284. }
  285. return null;
  286. }
  287. construct(ctor, context) {
  288. if (ctor instanceof lazy_helpers_1.DelayedConstructor) {
  289. return ctor.createProxy((target) => this.resolve(target, context));
  290. }
  291. const instance = (() => {
  292. const paramInfo = exports.typeInfo.get(ctor);
  293. if (!paramInfo || paramInfo.length === 0) {
  294. if (ctor.length === 0) {
  295. return new ctor();
  296. }
  297. else {
  298. throw new Error(`TypeInfo not known for "${ctor.name}"`);
  299. }
  300. }
  301. const params = paramInfo.map(this.resolveParams(context, ctor));
  302. return new ctor(...params);
  303. })();
  304. if (disposable_1.isDisposable(instance)) {
  305. this.disposables.add(instance);
  306. }
  307. return instance;
  308. }
  309. resolveParams(context, ctor) {
  310. return (param, idx) => {
  311. try {
  312. if (injection_token_1.isTokenDescriptor(param)) {
  313. if (injection_token_1.isTransformDescriptor(param)) {
  314. return param.multiple
  315. ? this.resolve(param.transform).transform(this.resolveAll(param.token, new resolution_context_1.default(), param.isOptional), ...param.transformArgs)
  316. : this.resolve(param.transform).transform(this.resolve(param.token, context, param.isOptional), ...param.transformArgs);
  317. }
  318. else {
  319. return param.multiple
  320. ? this.resolveAll(param.token, new resolution_context_1.default(), param.isOptional)
  321. : this.resolve(param.token, context, param.isOptional);
  322. }
  323. }
  324. else if (injection_token_1.isTransformDescriptor(param)) {
  325. return this.resolve(param.transform, context).transform(this.resolve(param.token, context), ...param.transformArgs);
  326. }
  327. return this.resolve(param, context);
  328. }
  329. catch (e) {
  330. throw new Error(error_helpers_1.formatErrorCtor(ctor, idx, e));
  331. }
  332. };
  333. }
  334. ensureNotDisposed() {
  335. if (this.disposed) {
  336. throw new Error("This container has been disposed, you cannot interact with a disposed container");
  337. }
  338. }
  339. }
  340. exports.instance = new InternalDependencyContainer();
  341. exports.default = exports.instance;