UNPKG

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