UNPKG

18.3 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3exports.Injector = void 0;
4const constants_1 = require("@nestjs/common/constants");
5const shared_utils_1 = require("@nestjs/common/utils/shared.utils");
6const iterare_1 = require("iterare");
7const runtime_exception_1 = require("../errors/exceptions/runtime.exception");
8const undefined_dependency_exception_1 = require("../errors/exceptions/undefined-dependency.exception");
9const unknown_dependencies_exception_1 = require("../errors/exceptions/unknown-dependencies.exception");
10const constants_2 = require("./constants");
11const inquirer_1 = require("./inquirer");
12const instance_wrapper_1 = require("./instance-wrapper");
13class Injector {
14 loadPrototype({ token }, collection, contextId = constants_2.STATIC_CONTEXT) {
15 if (!collection) {
16 return;
17 }
18 const target = collection.get(token);
19 const instance = target.createPrototype(contextId);
20 if (instance) {
21 const wrapper = new instance_wrapper_1.InstanceWrapper(Object.assign(Object.assign({}, target), { instance }));
22 collection.set(token, wrapper);
23 }
24 }
25 async loadInstance(wrapper, collection, moduleRef, contextId = constants_2.STATIC_CONTEXT, inquirer) {
26 const inquirerId = this.getInquirerId(inquirer);
27 const instanceHost = wrapper.getInstanceByContextId(contextId, inquirerId);
28 if (instanceHost.isPending) {
29 return instanceHost.donePromise;
30 }
31 const done = this.applyDoneHook(instanceHost);
32 const token = wrapper.token || wrapper.name;
33 const { inject } = wrapper;
34 const targetWrapper = collection.get(token);
35 if (shared_utils_1.isUndefined(targetWrapper)) {
36 throw new runtime_exception_1.RuntimeException();
37 }
38 if (instanceHost.isResolved) {
39 return done();
40 }
41 const callback = async (instances) => {
42 const properties = await this.resolveProperties(wrapper, moduleRef, inject, contextId, wrapper, inquirer);
43 const instance = await this.instantiateClass(instances, wrapper, targetWrapper, contextId, inquirer);
44 this.applyProperties(instance, properties);
45 done();
46 };
47 await this.resolveConstructorParams(wrapper, moduleRef, inject, callback, contextId, wrapper, inquirer);
48 }
49 async loadMiddleware(wrapper, collection, moduleRef, contextId = constants_2.STATIC_CONTEXT, inquirer) {
50 const { metatype, token } = wrapper;
51 const targetWrapper = collection.get(token);
52 if (!shared_utils_1.isUndefined(targetWrapper.instance)) {
53 return;
54 }
55 targetWrapper.instance = Object.create(metatype.prototype);
56 await this.loadInstance(wrapper, collection, moduleRef, contextId, inquirer || wrapper);
57 }
58 async loadController(wrapper, moduleRef, contextId = constants_2.STATIC_CONTEXT) {
59 const controllers = moduleRef.controllers;
60 await this.loadInstance(wrapper, controllers, moduleRef, contextId, wrapper);
61 await this.loadEnhancersPerContext(wrapper, contextId, wrapper);
62 }
63 async loadInjectable(wrapper, moduleRef, contextId = constants_2.STATIC_CONTEXT, inquirer) {
64 const injectables = moduleRef.injectables;
65 await this.loadInstance(wrapper, injectables, moduleRef, contextId, inquirer);
66 }
67 async loadProvider(wrapper, moduleRef, contextId = constants_2.STATIC_CONTEXT, inquirer) {
68 const providers = moduleRef.providers;
69 await this.loadInstance(wrapper, providers, moduleRef, contextId, inquirer);
70 await this.loadEnhancersPerContext(wrapper, contextId, wrapper);
71 }
72 applyDoneHook(wrapper) {
73 let done;
74 wrapper.donePromise = new Promise((resolve, reject) => {
75 done = resolve;
76 });
77 wrapper.isPending = true;
78 return done;
79 }
80 async resolveConstructorParams(wrapper, moduleRef, inject, callback, contextId = constants_2.STATIC_CONTEXT, inquirer, parentInquirer) {
81 const inquirerId = this.getInquirerId(inquirer);
82 const metadata = wrapper.getCtorMetadata();
83 if (metadata && contextId !== constants_2.STATIC_CONTEXT) {
84 const deps = await this.loadCtorMetadata(metadata, contextId, inquirer, parentInquirer);
85 return callback(deps);
86 }
87 const dependencies = shared_utils_1.isNil(inject)
88 ? this.reflectConstructorParams(wrapper.metatype)
89 : inject;
90 const optionalDependenciesIds = shared_utils_1.isNil(inject)
91 ? this.reflectOptionalParams(wrapper.metatype)
92 : [];
93 let isResolved = true;
94 const resolveParam = async (param, index) => {
95 try {
96 if (this.isInquirer(param, parentInquirer)) {
97 return parentInquirer && parentInquirer.instance;
98 }
99 const paramWrapper = await this.resolveSingleParam(wrapper, param, { index, dependencies }, moduleRef, contextId, inquirer, index);
100 const instanceHost = paramWrapper.getInstanceByContextId(contextId, inquirerId);
101 if (!instanceHost.isResolved && !paramWrapper.forwardRef) {
102 isResolved = false;
103 }
104 return instanceHost === null || instanceHost === void 0 ? void 0 : instanceHost.instance;
105 }
106 catch (err) {
107 const isOptional = optionalDependenciesIds.includes(index);
108 if (!isOptional) {
109 throw err;
110 }
111 return undefined;
112 }
113 };
114 const instances = await Promise.all(dependencies.map(resolveParam));
115 isResolved && (await callback(instances));
116 }
117 reflectConstructorParams(type) {
118 const paramtypes = Reflect.getMetadata(constants_1.PARAMTYPES_METADATA, type) || [];
119 const selfParams = this.reflectSelfParams(type);
120 selfParams.forEach(({ index, param }) => (paramtypes[index] = param));
121 return paramtypes;
122 }
123 reflectOptionalParams(type) {
124 return Reflect.getMetadata(constants_1.OPTIONAL_DEPS_METADATA, type) || [];
125 }
126 reflectSelfParams(type) {
127 return Reflect.getMetadata(constants_1.SELF_DECLARED_DEPS_METADATA, type) || [];
128 }
129 async resolveSingleParam(wrapper, param, dependencyContext, moduleRef, contextId = constants_2.STATIC_CONTEXT, inquirer, keyOrIndex) {
130 if (shared_utils_1.isUndefined(param)) {
131 throw new undefined_dependency_exception_1.UndefinedDependencyException(wrapper.name, dependencyContext, moduleRef);
132 }
133 const token = this.resolveParamToken(wrapper, param);
134 return this.resolveComponentInstance(moduleRef, token, dependencyContext, wrapper, contextId, inquirer, keyOrIndex);
135 }
136 resolveParamToken(wrapper, param) {
137 if (!param.forwardRef) {
138 return param;
139 }
140 wrapper.forwardRef = true;
141 return param.forwardRef();
142 }
143 async resolveComponentInstance(moduleRef, token, dependencyContext, wrapper, contextId = constants_2.STATIC_CONTEXT, inquirer, keyOrIndex) {
144 const providers = moduleRef.providers;
145 const instanceWrapper = await this.lookupComponent(providers, moduleRef, Object.assign(Object.assign({}, dependencyContext), { name: token }), wrapper, contextId, inquirer, keyOrIndex);
146 return this.resolveComponentHost(moduleRef, instanceWrapper, contextId, inquirer);
147 }
148 async resolveComponentHost(moduleRef, instanceWrapper, contextId = constants_2.STATIC_CONTEXT, inquirer) {
149 const inquirerId = this.getInquirerId(inquirer);
150 const instanceHost = instanceWrapper.getInstanceByContextId(contextId, inquirerId);
151 if (!instanceHost.isResolved && !instanceWrapper.forwardRef) {
152 await this.loadProvider(instanceWrapper, moduleRef, contextId, inquirer);
153 }
154 else if (!instanceHost.isResolved &&
155 instanceWrapper.forwardRef &&
156 (contextId !== constants_2.STATIC_CONTEXT || !!inquirerId)) {
157 /**
158 * When circular dependency has been detected between
159 * either request/transient providers, we have to asynchronously
160 * resolve instance host for a specific contextId or inquirer, to ensure
161 * that eventual lazily created instance will be merged with the prototype
162 * instantiated beforehand.
163 */
164 instanceHost.donePromise &&
165 instanceHost.donePromise.then(() => this.loadProvider(instanceWrapper, moduleRef, contextId, inquirer));
166 }
167 if (instanceWrapper.async) {
168 const host = instanceWrapper.getInstanceByContextId(contextId, inquirerId);
169 host.instance = await host.instance;
170 instanceWrapper.setInstanceByContextId(contextId, host, inquirerId);
171 }
172 return instanceWrapper;
173 }
174 async lookupComponent(providers, moduleRef, dependencyContext, wrapper, contextId = constants_2.STATIC_CONTEXT, inquirer, keyOrIndex) {
175 const { name } = dependencyContext;
176 if (wrapper && wrapper.name === name) {
177 throw new unknown_dependencies_exception_1.UnknownDependenciesException(wrapper.name, dependencyContext, moduleRef);
178 }
179 if (providers.has(name)) {
180 const instanceWrapper = providers.get(name);
181 this.addDependencyMetadata(keyOrIndex, wrapper, instanceWrapper);
182 return instanceWrapper;
183 }
184 return this.lookupComponentInParentModules(dependencyContext, moduleRef, wrapper, contextId, inquirer, keyOrIndex);
185 }
186 async lookupComponentInParentModules(dependencyContext, moduleRef, wrapper, contextId = constants_2.STATIC_CONTEXT, inquirer, keyOrIndex) {
187 const instanceWrapper = await this.lookupComponentInImports(moduleRef, dependencyContext.name, wrapper, [], contextId, inquirer, keyOrIndex);
188 if (shared_utils_1.isNil(instanceWrapper)) {
189 throw new unknown_dependencies_exception_1.UnknownDependenciesException(wrapper.name, dependencyContext, moduleRef);
190 }
191 return instanceWrapper;
192 }
193 async lookupComponentInImports(moduleRef, name, wrapper, moduleRegistry = [], contextId = constants_2.STATIC_CONTEXT, inquirer, keyOrIndex, isTraversing) {
194 let instanceWrapperRef = null;
195 const imports = moduleRef.imports || new Set();
196 const identity = (item) => item;
197 let children = [...imports.values()].filter(identity);
198 if (isTraversing) {
199 const contextModuleExports = moduleRef.exports;
200 children = children.filter(child => contextModuleExports.has(child.metatype));
201 }
202 for (const relatedModule of children) {
203 if (moduleRegistry.includes(relatedModule.id)) {
204 continue;
205 }
206 moduleRegistry.push(relatedModule.id);
207 const { providers, exports } = relatedModule;
208 if (!exports.has(name) || !providers.has(name)) {
209 const instanceRef = await this.lookupComponentInImports(relatedModule, name, wrapper, moduleRegistry, contextId, inquirer, keyOrIndex, true);
210 if (instanceRef) {
211 this.addDependencyMetadata(keyOrIndex, wrapper, instanceRef);
212 return instanceRef;
213 }
214 continue;
215 }
216 instanceWrapperRef = providers.get(name);
217 this.addDependencyMetadata(keyOrIndex, wrapper, instanceWrapperRef);
218 const inquirerId = this.getInquirerId(inquirer);
219 const instanceHost = instanceWrapperRef.getInstanceByContextId(contextId, inquirerId);
220 if (!instanceHost.isResolved && !instanceWrapperRef.forwardRef) {
221 await this.loadProvider(instanceWrapperRef, relatedModule, contextId, wrapper);
222 break;
223 }
224 }
225 return instanceWrapperRef;
226 }
227 async resolveProperties(wrapper, moduleRef, inject, contextId = constants_2.STATIC_CONTEXT, inquirer, parentInquirer) {
228 if (!shared_utils_1.isNil(inject)) {
229 return [];
230 }
231 const metadata = wrapper.getPropertiesMetadata();
232 if (metadata && contextId !== constants_2.STATIC_CONTEXT) {
233 return this.loadPropertiesMetadata(metadata, contextId, inquirer);
234 }
235 const properties = this.reflectProperties(wrapper.metatype);
236 const instances = await Promise.all(properties.map(async (item) => {
237 try {
238 const dependencyContext = {
239 key: item.key,
240 name: item.name,
241 };
242 if (this.isInquirer(item.name, parentInquirer)) {
243 return parentInquirer && parentInquirer.instance;
244 }
245 const paramWrapper = await this.resolveSingleParam(wrapper, item.name, dependencyContext, moduleRef, contextId, inquirer, item.key);
246 if (!paramWrapper) {
247 return undefined;
248 }
249 const inquirerId = this.getInquirerId(inquirer);
250 const instanceHost = paramWrapper.getInstanceByContextId(contextId, inquirerId);
251 return instanceHost.instance;
252 }
253 catch (err) {
254 if (!item.isOptional) {
255 throw err;
256 }
257 return undefined;
258 }
259 }));
260 return properties.map((item, index) => (Object.assign(Object.assign({}, item), { instance: instances[index] })));
261 }
262 reflectProperties(type) {
263 const properties = Reflect.getMetadata(constants_1.PROPERTY_DEPS_METADATA, type) || [];
264 const optionalKeys = Reflect.getMetadata(constants_1.OPTIONAL_PROPERTY_DEPS_METADATA, type) || [];
265 return properties.map((item) => (Object.assign(Object.assign({}, item), { name: item.type, isOptional: optionalKeys.includes(item.key) })));
266 }
267 applyProperties(instance, properties) {
268 if (!shared_utils_1.isObject(instance)) {
269 return undefined;
270 }
271 iterare_1.iterate(properties)
272 .filter(item => !shared_utils_1.isNil(item.instance))
273 .forEach(item => (instance[item.key] = item.instance));
274 }
275 async instantiateClass(instances, wrapper, targetMetatype, contextId = constants_2.STATIC_CONTEXT, inquirer) {
276 const { metatype, inject } = wrapper;
277 const inquirerId = this.getInquirerId(inquirer);
278 const instanceHost = targetMetatype.getInstanceByContextId(contextId, inquirerId);
279 const isStatic = wrapper.isStatic(contextId, inquirer);
280 const isInRequestScope = wrapper.isInRequestScope(contextId, inquirer);
281 const isLazyTransient = wrapper.isLazyTransient(contextId, inquirer);
282 const isExplicitlyRequested = wrapper.isExplicitlyRequested(contextId, inquirer);
283 const isInContext = isStatic || isInRequestScope || isLazyTransient || isExplicitlyRequested;
284 if (shared_utils_1.isNil(inject) && isInContext) {
285 instanceHost.instance = wrapper.forwardRef
286 ? Object.assign(instanceHost.instance, new metatype(...instances))
287 : new metatype(...instances);
288 }
289 else if (isInContext) {
290 const factoryReturnValue = targetMetatype.metatype(...instances);
291 instanceHost.instance = await factoryReturnValue;
292 }
293 instanceHost.isResolved = true;
294 return instanceHost.instance;
295 }
296 async loadPerContext(instance, moduleRef, collection, ctx, wrapper) {
297 if (!wrapper) {
298 const injectionToken = instance.constructor;
299 wrapper = collection.get(injectionToken);
300 }
301 await this.loadInstance(wrapper, collection, moduleRef, ctx, wrapper);
302 await this.loadEnhancersPerContext(wrapper, ctx, wrapper);
303 const host = wrapper.getInstanceByContextId(ctx, wrapper.id);
304 return host && host.instance;
305 }
306 async loadEnhancersPerContext(wrapper, ctx, inquirer) {
307 const enhancers = wrapper.getEnhancersMetadata() || [];
308 const loadEnhancer = (item) => {
309 const hostModule = item.host;
310 return this.loadInstance(item, hostModule.injectables, hostModule, ctx, inquirer);
311 };
312 await Promise.all(enhancers.map(loadEnhancer));
313 }
314 async loadCtorMetadata(metadata, contextId, inquirer, parentInquirer) {
315 const hosts = await Promise.all(metadata.map(async (item) => this.resolveScopedComponentHost(item, contextId, inquirer, parentInquirer)));
316 const inquirerId = this.getInquirerId(inquirer);
317 return hosts.map(item => item.getInstanceByContextId(contextId, inquirerId).instance);
318 }
319 async loadPropertiesMetadata(metadata, contextId, inquirer) {
320 const dependenciesHosts = await Promise.all(metadata.map(async ({ wrapper: item, key }) => ({
321 key,
322 host: await this.resolveComponentHost(item.host, item, contextId, inquirer),
323 })));
324 const inquirerId = this.getInquirerId(inquirer);
325 return dependenciesHosts.map(({ key, host }) => ({
326 key,
327 name: key,
328 instance: host.getInstanceByContextId(contextId, inquirerId).instance,
329 }));
330 }
331 getInquirerId(inquirer) {
332 return inquirer && inquirer.id;
333 }
334 resolveScopedComponentHost(item, contextId, inquirer, parentInquirer) {
335 return this.isInquirerRequest(item, parentInquirer)
336 ? parentInquirer
337 : this.resolveComponentHost(item.host, item, contextId, inquirer);
338 }
339 isInquirerRequest(item, parentInquirer) {
340 return item.isTransient && item.name === inquirer_1.INQUIRER && parentInquirer;
341 }
342 isInquirer(param, parentInquirer) {
343 return param === inquirer_1.INQUIRER && parentInquirer;
344 }
345 addDependencyMetadata(keyOrIndex, hostWrapper, instanceWrapper) {
346 shared_utils_1.isString(keyOrIndex)
347 ? hostWrapper.addPropertiesMetadata(keyOrIndex, instanceWrapper)
348 : hostWrapper.addCtorMetadata(keyOrIndex, instanceWrapper);
349 }
350}
351exports.Injector = Injector;