UNPKG

15.8 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3exports.DependenciesScanner = void 0;
4const common_1 = require("@nestjs/common");
5const constants_1 = require("@nestjs/common/constants");
6const interfaces_1 = require("@nestjs/common/interfaces");
7const random_string_generator_util_1 = require("@nestjs/common/utils/random-string-generator.util");
8const shared_utils_1 = require("@nestjs/common/utils/shared.utils");
9const iterare_1 = require("iterare");
10const application_config_1 = require("./application-config");
11const constants_2 = require("./constants");
12const circular_dependency_exception_1 = require("./errors/exceptions/circular-dependency.exception");
13const invalid_class_module_exception_1 = require("./errors/exceptions/invalid-class-module.exception");
14const invalid_module_exception_1 = require("./errors/exceptions/invalid-module.exception");
15const undefined_module_exception_1 = require("./errors/exceptions/undefined-module.exception");
16const get_class_scope_1 = require("./helpers/get-class-scope");
17const internal_core_module_factory_1 = require("./injector/internal-core-module/internal-core-module-factory");
18class DependenciesScanner {
19 constructor(container, metadataScanner, applicationConfig = new application_config_1.ApplicationConfig()) {
20 this.container = container;
21 this.metadataScanner = metadataScanner;
22 this.applicationConfig = applicationConfig;
23 this.applicationProvidersApplyMap = [];
24 }
25 async scan(module) {
26 await this.registerCoreModule();
27 await this.scanForModules(module);
28 await this.scanModulesForDependencies();
29 this.calculateModulesDistance();
30 this.addScopedEnhancersMetadata();
31 this.container.bindGlobalScope();
32 }
33 async scanForModules(moduleDefinition, scope = [], ctxRegistry = []) {
34 const moduleInstance = await this.insertModule(moduleDefinition, scope);
35 moduleDefinition =
36 moduleDefinition instanceof Promise
37 ? await moduleDefinition
38 : moduleDefinition;
39 ctxRegistry.push(moduleDefinition);
40 if (this.isForwardReference(moduleDefinition)) {
41 moduleDefinition = moduleDefinition.forwardRef();
42 }
43 const modules = !this.isDynamicModule(moduleDefinition)
44 ? this.reflectMetadata(constants_1.MODULE_METADATA.IMPORTS, moduleDefinition)
45 : [
46 ...this.reflectMetadata(constants_1.MODULE_METADATA.IMPORTS, moduleDefinition.module),
47 ...(moduleDefinition.imports || []),
48 ];
49 let registeredModuleRefs = [];
50 for (const [index, innerModule] of modules.entries()) {
51 // In case of a circular dependency (ES module system), JavaScript will resolve the type to `undefined`.
52 if (innerModule === undefined) {
53 throw new undefined_module_exception_1.UndefinedModuleException(moduleDefinition, index, scope);
54 }
55 if (!innerModule) {
56 throw new invalid_module_exception_1.InvalidModuleException(moduleDefinition, index, scope);
57 }
58 if (ctxRegistry.includes(innerModule)) {
59 continue;
60 }
61 const moduleRefs = await this.scanForModules(innerModule, [].concat(scope, moduleDefinition), ctxRegistry);
62 registeredModuleRefs = registeredModuleRefs.concat(moduleRefs);
63 }
64 if (!moduleInstance) {
65 return registeredModuleRefs;
66 }
67 return [moduleInstance].concat(registeredModuleRefs);
68 }
69 async insertModule(moduleDefinition, scope) {
70 const moduleToAdd = this.isForwardReference(moduleDefinition)
71 ? moduleDefinition.forwardRef()
72 : moduleDefinition;
73 if (this.isInjectable(moduleToAdd) ||
74 this.isController(moduleToAdd) ||
75 this.isExceptionFilter(moduleToAdd)) {
76 throw new invalid_class_module_exception_1.InvalidClassModuleException(moduleDefinition, scope);
77 }
78 return this.container.addModule(moduleToAdd, scope);
79 }
80 async scanModulesForDependencies(modules = this.container.getModules()) {
81 for (const [token, { metatype }] of modules) {
82 await this.reflectImports(metatype, token, metatype.name);
83 this.reflectProviders(metatype, token);
84 this.reflectControllers(metatype, token);
85 this.reflectExports(metatype, token);
86 }
87 }
88 async reflectImports(module, token, context) {
89 const modules = [
90 ...this.reflectMetadata(constants_1.MODULE_METADATA.IMPORTS, module),
91 ...this.container.getDynamicMetadataByToken(token, constants_1.MODULE_METADATA.IMPORTS),
92 ];
93 for (const related of modules) {
94 await this.insertImport(related, token, context);
95 }
96 }
97 reflectProviders(module, token) {
98 const providers = [
99 ...this.reflectMetadata(constants_1.MODULE_METADATA.PROVIDERS, module),
100 ...this.container.getDynamicMetadataByToken(token, constants_1.MODULE_METADATA.PROVIDERS),
101 ];
102 providers.forEach(provider => {
103 this.insertProvider(provider, token);
104 this.reflectDynamicMetadata(provider, token);
105 });
106 }
107 reflectControllers(module, token) {
108 const controllers = [
109 ...this.reflectMetadata(constants_1.MODULE_METADATA.CONTROLLERS, module),
110 ...this.container.getDynamicMetadataByToken(token, constants_1.MODULE_METADATA.CONTROLLERS),
111 ];
112 controllers.forEach(item => {
113 this.insertController(item, token);
114 this.reflectDynamicMetadata(item, token);
115 });
116 }
117 reflectDynamicMetadata(obj, token) {
118 if (!obj || !obj.prototype) {
119 return;
120 }
121 this.reflectInjectables(obj, token, constants_1.GUARDS_METADATA);
122 this.reflectInjectables(obj, token, constants_1.INTERCEPTORS_METADATA);
123 this.reflectInjectables(obj, token, constants_1.EXCEPTION_FILTERS_METADATA);
124 this.reflectInjectables(obj, token, constants_1.PIPES_METADATA);
125 this.reflectParamInjectables(obj, token, constants_1.ROUTE_ARGS_METADATA);
126 }
127 reflectExports(module, token) {
128 const exports = [
129 ...this.reflectMetadata(constants_1.MODULE_METADATA.EXPORTS, module),
130 ...this.container.getDynamicMetadataByToken(token, constants_1.MODULE_METADATA.EXPORTS),
131 ];
132 exports.forEach(exportedProvider => this.insertExportedProvider(exportedProvider, token));
133 }
134 reflectInjectables(component, token, metadataKey) {
135 const controllerInjectables = this.reflectMetadata(metadataKey, component);
136 const methodsInjectables = this.metadataScanner.scanFromPrototype(null, component.prototype, this.reflectKeyMetadata.bind(this, component, metadataKey));
137 const flattenMethodsInjectables = this.flatten(methodsInjectables);
138 const combinedInjectables = [
139 ...controllerInjectables,
140 ...flattenMethodsInjectables,
141 ].filter(shared_utils_1.isFunction);
142 const injectables = Array.from(new Set(combinedInjectables));
143 injectables.forEach(injectable => this.insertInjectable(injectable, token, component));
144 }
145 reflectParamInjectables(component, token, metadataKey) {
146 const paramsMetadata = this.metadataScanner.scanFromPrototype(null, component.prototype, method => Reflect.getMetadata(metadataKey, component, method));
147 const paramsInjectables = this.flatten(paramsMetadata).map((param) => (0, common_1.flatten)(Object.keys(param).map(k => param[k].pipes)).filter(shared_utils_1.isFunction));
148 (0, common_1.flatten)(paramsInjectables).forEach((injectable) => this.insertInjectable(injectable, token, component));
149 }
150 reflectKeyMetadata(component, key, method) {
151 let prototype = component.prototype;
152 do {
153 const descriptor = Reflect.getOwnPropertyDescriptor(prototype, method);
154 if (!descriptor) {
155 continue;
156 }
157 return Reflect.getMetadata(key, descriptor.value);
158 } while ((prototype = Reflect.getPrototypeOf(prototype)) &&
159 prototype !== Object.prototype &&
160 prototype);
161 return undefined;
162 }
163 async calculateModulesDistance() {
164 const modulesGenerator = this.container.getModules().values();
165 // Skip "InternalCoreModule" from calculating distance
166 modulesGenerator.next();
167 const modulesStack = [];
168 const calculateDistance = (moduleRef, distance = 1) => {
169 if (modulesStack.includes(moduleRef)) {
170 return;
171 }
172 modulesStack.push(moduleRef);
173 const moduleImports = moduleRef.imports;
174 moduleImports.forEach(importedModuleRef => {
175 if (importedModuleRef) {
176 importedModuleRef.distance = distance;
177 calculateDistance(importedModuleRef, distance + 1);
178 }
179 });
180 };
181 const rootModule = modulesGenerator.next().value;
182 calculateDistance(rootModule);
183 }
184 async insertImport(related, token, context) {
185 if ((0, shared_utils_1.isUndefined)(related)) {
186 throw new circular_dependency_exception_1.CircularDependencyException(context);
187 }
188 if (this.isForwardReference(related)) {
189 return this.container.addImport(related.forwardRef(), token);
190 }
191 await this.container.addImport(related, token);
192 }
193 isCustomProvider(provider) {
194 return provider && !(0, shared_utils_1.isNil)(provider.provide);
195 }
196 insertProvider(provider, token) {
197 const isCustomProvider = this.isCustomProvider(provider);
198 if (!isCustomProvider) {
199 return this.container.addProvider(provider, token);
200 }
201 const applyProvidersMap = this.getApplyProvidersMap();
202 const providersKeys = Object.keys(applyProvidersMap);
203 const type = provider.provide;
204 if (!providersKeys.includes(type)) {
205 return this.container.addProvider(provider, token);
206 }
207 const providerToken = `${type} (UUID: ${(0, random_string_generator_util_1.randomStringGenerator)()})`;
208 let scope = provider.scope;
209 if ((0, shared_utils_1.isNil)(scope) && provider.useClass) {
210 scope = (0, get_class_scope_1.getClassScope)(provider.useClass);
211 }
212 this.applicationProvidersApplyMap.push({
213 type,
214 moduleKey: token,
215 providerKey: providerToken,
216 scope,
217 });
218 const newProvider = Object.assign(Object.assign({}, provider), { provide: providerToken, scope });
219 const factoryOrClassProvider = newProvider;
220 if (this.isRequestOrTransient(factoryOrClassProvider.scope)) {
221 return this.container.addInjectable(newProvider, token);
222 }
223 this.container.addProvider(newProvider, token);
224 }
225 insertInjectable(injectable, token, host) {
226 this.container.addInjectable(injectable, token, host);
227 }
228 insertExportedProvider(exportedProvider, token) {
229 this.container.addExportedProvider(exportedProvider, token);
230 }
231 insertController(controller, token) {
232 this.container.addController(controller, token);
233 }
234 reflectMetadata(metadataKey, metatype) {
235 return Reflect.getMetadata(metadataKey, metatype) || [];
236 }
237 async registerCoreModule() {
238 const moduleDefinition = internal_core_module_factory_1.InternalCoreModuleFactory.create(this.container, this, this.container.getModuleCompiler(), this.container.getHttpAdapterHostRef());
239 const [instance] = await this.scanForModules(moduleDefinition);
240 this.container.registerCoreModuleRef(instance);
241 }
242 /**
243 * Add either request or transient globally scoped enhancers
244 * to all controllers metadata storage
245 */
246 addScopedEnhancersMetadata() {
247 (0, iterare_1.iterate)(this.applicationProvidersApplyMap)
248 .filter(wrapper => this.isRequestOrTransient(wrapper.scope))
249 .forEach(({ moduleKey, providerKey }) => {
250 const modulesContainer = this.container.getModules();
251 const { injectables } = modulesContainer.get(moduleKey);
252 const instanceWrapper = injectables.get(providerKey);
253 (0, iterare_1.iterate)(modulesContainer.values())
254 .map(module => module.controllers.values())
255 .flatten()
256 .forEach(controller => controller.addEnhancerMetadata(instanceWrapper));
257 });
258 }
259 applyApplicationProviders() {
260 const applyProvidersMap = this.getApplyProvidersMap();
261 const applyRequestProvidersMap = this.getApplyRequestProvidersMap();
262 const getInstanceWrapper = (moduleKey, providerKey, collectionKey) => {
263 const modules = this.container.getModules();
264 const collection = modules.get(moduleKey)[collectionKey];
265 return collection.get(providerKey);
266 };
267 // Add global enhancers to the application config
268 this.applicationProvidersApplyMap.forEach(({ moduleKey, providerKey, type, scope }) => {
269 let instanceWrapper;
270 if (this.isRequestOrTransient(scope)) {
271 instanceWrapper = getInstanceWrapper(moduleKey, providerKey, 'injectables');
272 return applyRequestProvidersMap[type](instanceWrapper);
273 }
274 instanceWrapper = getInstanceWrapper(moduleKey, providerKey, 'providers');
275 applyProvidersMap[type](instanceWrapper.instance);
276 });
277 }
278 getApplyProvidersMap() {
279 return {
280 [constants_2.APP_INTERCEPTOR]: (interceptor) => this.applicationConfig.addGlobalInterceptor(interceptor),
281 [constants_2.APP_PIPE]: (pipe) => this.applicationConfig.addGlobalPipe(pipe),
282 [constants_2.APP_GUARD]: (guard) => this.applicationConfig.addGlobalGuard(guard),
283 [constants_2.APP_FILTER]: (filter) => this.applicationConfig.addGlobalFilter(filter),
284 };
285 }
286 getApplyRequestProvidersMap() {
287 return {
288 [constants_2.APP_INTERCEPTOR]: (interceptor) => this.applicationConfig.addGlobalRequestInterceptor(interceptor),
289 [constants_2.APP_PIPE]: (pipe) => this.applicationConfig.addGlobalRequestPipe(pipe),
290 [constants_2.APP_GUARD]: (guard) => this.applicationConfig.addGlobalRequestGuard(guard),
291 [constants_2.APP_FILTER]: (filter) => this.applicationConfig.addGlobalRequestFilter(filter),
292 };
293 }
294 isDynamicModule(module) {
295 return module && !!module.module;
296 }
297 /**
298 * @param metatype
299 * @returns `true` if `metatype` is annotated with the `@Injectable()` decorator.
300 */
301 isInjectable(metatype) {
302 return !!Reflect.getMetadata(constants_1.INJECTABLE_WATERMARK, metatype);
303 }
304 /**
305 * @param metatype
306 * @returns `true` if `metatype` is annotated with the `@Controller()` decorator.
307 */
308 isController(metatype) {
309 return !!Reflect.getMetadata(constants_1.CONTROLLER_WATERMARK, metatype);
310 }
311 /**
312 * @param metatype
313 * @returns `true` if `metatype` is annotated with the `@Catch()` decorator.
314 */
315 isExceptionFilter(metatype) {
316 return !!Reflect.getMetadata(constants_1.CATCH_WATERMARK, metatype);
317 }
318 isForwardReference(module) {
319 return module && !!module.forwardRef;
320 }
321 flatten(arr) {
322 return arr.flat(1);
323 }
324 isRequestOrTransient(scope) {
325 return scope === interfaces_1.Scope.REQUEST || scope === interfaces_1.Scope.TRANSIENT;
326 }
327}
328exports.DependenciesScanner = DependenciesScanner;