UNPKG

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