UNPKG

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