UNPKG

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