UNPKG

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