1 | "use strict";
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | const constants_1 = require("@nestjs/common/constants");
|
4 | const shared_utils_1 = require("@nestjs/common/utils/shared.utils");
|
5 | const runtime_exception_1 = require("../errors/exceptions/runtime.exception");
|
6 | const undefined_dependency_exception_1 = require("../errors/exceptions/undefined-dependency.exception");
|
7 | const unknown_dependencies_exception_1 = require("../errors/exceptions/unknown-dependencies.exception");
|
8 | const constants_2 = require("./constants");
|
9 | const inquirer_1 = require("./inquirer");
|
10 | const instance_wrapper_1 = require("./instance-wrapper");
|
11 | class 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 |
|
160 |
|
161 |
|
162 |
|
163 |
|
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) {
|
298 | const wrapper = collection.get(instance.constructor && instance.constructor.name);
|
299 | await this.loadInstance(wrapper, collection, module, ctx, wrapper);
|
300 | await this.loadEnhancersPerContext(wrapper, ctx, wrapper);
|
301 | const host = wrapper.getInstanceByContextId(ctx, wrapper.id);
|
302 | return host && host.instance;
|
303 | }
|
304 | async loadEnhancersPerContext(wrapper, ctx, inquirer) {
|
305 | const enhancers = wrapper.getEnhancersMetadata() || [];
|
306 | const loadEnhancer = (item) => {
|
307 | const hostModule = item.host;
|
308 | return this.loadInstance(item, hostModule.injectables, hostModule, ctx, inquirer);
|
309 | };
|
310 | await Promise.all(enhancers.map(loadEnhancer));
|
311 | }
|
312 | async loadCtorMetadata(metadata, contextId, inquirer, parentInquirer) {
|
313 | const hosts = await Promise.all(metadata.map(async (item) => this.resolveScopedComponentHost(item, contextId, inquirer, parentInquirer)));
|
314 | const inquirerId = this.getInquirerId(inquirer);
|
315 | return hosts.map(item => item.getInstanceByContextId(contextId, inquirerId).instance);
|
316 | }
|
317 | async loadPropertiesMetadata(metadata, contextId, inquirer) {
|
318 | const dependenciesHosts = await Promise.all(metadata.map(async ({ wrapper: item, key }) => ({
|
319 | key,
|
320 | host: await this.resolveComponentHost(item.host, item, contextId, inquirer),
|
321 | })));
|
322 | const inquirerId = this.getInquirerId(inquirer);
|
323 | return dependenciesHosts.map(({ key, host }) => ({
|
324 | key,
|
325 | name: key,
|
326 | instance: host.getInstanceByContextId(contextId, inquirerId).instance,
|
327 | }));
|
328 | }
|
329 | getInquirerId(inquirer) {
|
330 | return inquirer && inquirer.id;
|
331 | }
|
332 | resolveScopedComponentHost(item, contextId, inquirer, parentInquirer) {
|
333 | return this.isInquirerRequest(item, parentInquirer)
|
334 | ? parentInquirer
|
335 | : this.resolveComponentHost(item.host, item, contextId, inquirer);
|
336 | }
|
337 | isInquirerRequest(item, parentInquirer) {
|
338 | return item.isTransient && item.name === inquirer_1.INQUIRER && parentInquirer;
|
339 | }
|
340 | isInquirer(param, parentInquirer) {
|
341 | return param === inquirer_1.INQUIRER && parentInquirer;
|
342 | }
|
343 | addDependencyMetadata(keyOrIndex, hostWrapper, instanceWrapper) {
|
344 | shared_utils_1.isString(keyOrIndex)
|
345 | ? hostWrapper.addPropertiesMetadata(keyOrIndex, instanceWrapper)
|
346 | : hostWrapper.addCtorMetadata(keyOrIndex, instanceWrapper);
|
347 | }
|
348 | }
|
349 | exports.Injector = Injector;
|