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");
12class DependenciesScanner {
13 constructor(container, metadataScanner, applicationConfig = new application_config_1.ApplicationConfig()) {
14 this.container = container;
15 this.metadataScanner = metadataScanner;
16 this.applicationConfig = applicationConfig;
17 this.applicationProvidersApplyMap = [];
18 }
19 async scan(module) {
20 await this.registerCoreModule();
21 await this.scanForModules(module);
22 await this.scanModulesForDependencies();
23 this.addScopedEnhancersMetadata();
24 this.container.bindGlobalScope();
25 }
26 async scanForModules(module, scope = [], ctxRegistry = []) {
27 const moduleInstance = await this.insertModule(module, scope);
28 ctxRegistry.push(module);
29 if (this.isForwardReference(module)) {
30 module = module.forwardRef();
31 }
32 const modules = !this.isDynamicModule(module)
33 ? this.reflectMetadata(module, constants_1.METADATA.IMPORTS)
34 : [
35 ...this.reflectMetadata(module.module, constants_1.METADATA.IMPORTS),
36 ...(module.imports || []),
37 ];
38 for (const innerModule of modules) {
39 if (ctxRegistry.includes(innerModule)) {
40 continue;
41 }
42 await this.scanForModules(innerModule, [].concat(scope, module), ctxRegistry);
43 }
44 return moduleInstance;
45 }
46 async insertModule(module, scope) {
47 if (module && module.forwardRef) {
48 return this.container.addModule(module.forwardRef(), scope);
49 }
50 return this.container.addModule(module, scope);
51 }
52 async scanModulesForDependencies() {
53 const modules = this.container.getModules();
54 for (const [token, { metatype }] of modules) {
55 await this.reflectImports(metatype, token, metatype.name);
56 this.reflectProviders(metatype, token);
57 this.reflectControllers(metatype, token);
58 this.reflectExports(metatype, token);
59 }
60 this.calculateModulesDistance(modules);
61 }
62 async reflectImports(module, token, context) {
63 const modules = [
64 ...this.reflectMetadata(module, constants_1.METADATA.IMPORTS),
65 ...this.container.getDynamicMetadataByToken(token, constants_1.METADATA.IMPORTS),
66 ];
67 for (const related of modules) {
68 await this.insertImport(related, token, context);
69 }
70 }
71 reflectProviders(module, token) {
72 const providers = [
73 ...this.reflectMetadata(module, constants_1.METADATA.PROVIDERS),
74 ...this.container.getDynamicMetadataByToken(token, constants_1.METADATA.PROVIDERS),
75 ];
76 providers.forEach(provider => {
77 this.insertProvider(provider, token);
78 this.reflectDynamicMetadata(provider, token);
79 });
80 }
81 reflectControllers(module, token) {
82 const controllers = [
83 ...this.reflectMetadata(module, constants_1.METADATA.CONTROLLERS),
84 ...this.container.getDynamicMetadataByToken(token, constants_1.METADATA.CONTROLLERS),
85 ];
86 controllers.forEach(item => {
87 this.insertController(item, token);
88 this.reflectDynamicMetadata(item, token);
89 });
90 }
91 reflectDynamicMetadata(obj, token) {
92 if (!obj || !obj.prototype) {
93 return;
94 }
95 this.reflectInjectables(obj, token, constants_1.GUARDS_METADATA);
96 this.reflectInjectables(obj, token, constants_1.INTERCEPTORS_METADATA);
97 this.reflectInjectables(obj, token, constants_1.EXCEPTION_FILTERS_METADATA);
98 this.reflectInjectables(obj, token, constants_1.PIPES_METADATA);
99 this.reflectParamInjectables(obj, token, constants_1.ROUTE_ARGS_METADATA);
100 }
101 reflectExports(module, token) {
102 const exports = [
103 ...this.reflectMetadata(module, constants_1.METADATA.EXPORTS),
104 ...this.container.getDynamicMetadataByToken(token, constants_1.METADATA.EXPORTS),
105 ];
106 exports.forEach(exportedProvider => this.insertExportedProvider(exportedProvider, token));
107 }
108 reflectInjectables(component, token, metadataKey) {
109 const controllerInjectables = this.reflectMetadata(component, metadataKey);
110 const methodsInjectables = this.metadataScanner.scanFromPrototype(null, component.prototype, this.reflectKeyMetadata.bind(this, component, metadataKey));
111 const flattenMethodsInjectables = this.flatten(methodsInjectables);
112 const combinedInjectables = [
113 ...controllerInjectables,
114 ...flattenMethodsInjectables,
115 ].filter(shared_utils_1.isFunction);
116 const injectables = Array.from(new Set(combinedInjectables));
117 injectables.forEach(injectable => this.insertInjectable(injectable, token, component));
118 }
119 reflectParamInjectables(component, token, metadataKey) {
120 const paramsMetadata = this.metadataScanner.scanFromPrototype(null, component.prototype, method => Reflect.getMetadata(metadataKey, component, method));
121 const paramsInjectables = this.flatten(paramsMetadata).map((param) => common_1.flatten(Object.keys(param).map(k => param[k].pipes)).filter(shared_utils_1.isFunction));
122 common_1.flatten(paramsInjectables).forEach((injectable) => this.insertInjectable(injectable, token, component));
123 }
124 reflectKeyMetadata(component, key, method) {
125 let prototype = component.prototype;
126 do {
127 const descriptor = Reflect.getOwnPropertyDescriptor(prototype, method);
128 if (!descriptor) {
129 continue;
130 }
131 return Reflect.getMetadata(key, descriptor.value);
132 } while ((prototype = Reflect.getPrototypeOf(prototype)) &&
133 prototype !== Object.prototype &&
134 prototype);
135 return undefined;
136 }
137 async calculateModulesDistance(modules) {
138 const modulesGenerator = modules.values();
139 const rootModule = modulesGenerator.next().value;
140 const modulesStack = [rootModule];
141 const calculateDistance = (moduleRef, distance = 1) => {
142 if (modulesStack.includes(moduleRef)) {
143 return;
144 }
145 modulesStack.push(moduleRef);
146 const moduleImports = rootModule.relatedModules;
147 moduleImports.forEach(module => {
148 module.distance = distance;
149 calculateDistance(module, distance + 1);
150 });
151 };
152 calculateDistance(rootModule);
153 }
154 async insertImport(related, token, context) {
155 if (shared_utils_1.isUndefined(related)) {
156 throw new circular_dependency_exception_1.CircularDependencyException(context);
157 }
158 if (related && related.forwardRef) {
159 return this.container.addImport(related.forwardRef(), token);
160 }
161 await this.container.addImport(related, token);
162 }
163 isCustomProvider(provider) {
164 return provider && !shared_utils_1.isNil(provider.provide);
165 }
166 insertProvider(provider, token) {
167 const isCustomProvider = this.isCustomProvider(provider);
168 if (!isCustomProvider) {
169 return this.container.addProvider(provider, token);
170 }
171 const applyProvidersMap = this.getApplyProvidersMap();
172 const providersKeys = Object.keys(applyProvidersMap);
173 const type = provider.provide;
174 if (!providersKeys.includes(type)) {
175 return this.container.addProvider(provider, token);
176 }
177 const providerToken = `${type} (UUID: ${random_string_generator_util_1.randomStringGenerator()})`;
178 let scope = provider.scope;
179 if (shared_utils_1.isNil(scope) && provider.useClass) {
180 scope = get_class_scope_1.getClassScope(provider.useClass);
181 }
182 this.applicationProvidersApplyMap.push({
183 type,
184 moduleKey: token,
185 providerKey: providerToken,
186 scope,
187 });
188 const newProvider = Object.assign(Object.assign({}, provider), { provide: providerToken, scope });
189 if (this.isRequestOrTransient(newProvider.scope)) {
190 return this.container.addInjectable(newProvider, token);
191 }
192 this.container.addProvider(newProvider, token);
193 }
194 insertInjectable(injectable, token, host) {
195 this.container.addInjectable(injectable, token, host);
196 }
197 insertExportedProvider(exportedProvider, token) {
198 this.container.addExportedProvider(exportedProvider, token);
199 }
200 insertController(controller, token) {
201 this.container.addController(controller, token);
202 }
203 reflectMetadata(metatype, metadataKey) {
204 return Reflect.getMetadata(metadataKey, metatype) || [];
205 }
206 async registerCoreModule() {
207 const module = this.container.createCoreModule();
208 const instance = await this.scanForModules(module);
209 this.container.registerCoreModuleRef(instance);
210 }
211 /**
212 * Add either request or transient globally scoped enhancers
213 * to all controllers metadata storage
214 */
215 addScopedEnhancersMetadata() {
216 const scopedGlobalProviders = this.applicationProvidersApplyMap.filter(wrapper => this.isRequestOrTransient(wrapper.scope));
217 scopedGlobalProviders.forEach(({ moduleKey, providerKey }) => {
218 const modulesContainer = this.container.getModules();
219 const { injectables } = modulesContainer.get(moduleKey);
220 const instanceWrapper = injectables.get(providerKey);
221 const modules = [...modulesContainer.values()];
222 const controllersArray = modules.map(module => [
223 ...module.controllers.values(),
224 ]);
225 const controllers = this.flatten(controllersArray);
226 controllers.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;