UNPKG

13.1 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 (
133 // tslint:disable-next-line:no-conditional-assignment
134 (prototype = Reflect.getPrototypeOf(prototype)) &&
135 prototype !== Object.prototype &&
136 prototype);
137 return undefined;
138 }
139 async calculateModulesDistance(modules) {
140 const modulesGenerator = modules.values();
141 const rootModule = modulesGenerator.next().value;
142 const modulesStack = [rootModule];
143 const calculateDistance = (moduleRef, distance = 1) => {
144 if (modulesStack.includes(moduleRef)) {
145 return;
146 }
147 modulesStack.push(moduleRef);
148 const moduleImports = rootModule.relatedModules;
149 moduleImports.forEach(module => {
150 module.distance = distance;
151 calculateDistance(module, distance + 1);
152 });
153 };
154 calculateDistance(rootModule);
155 }
156 async insertImport(related, token, context) {
157 if (shared_utils_1.isUndefined(related)) {
158 throw new circular_dependency_exception_1.CircularDependencyException(context);
159 }
160 if (related && related.forwardRef) {
161 return this.container.addImport(related.forwardRef(), token);
162 }
163 await this.container.addImport(related, token);
164 }
165 isCustomProvider(provider) {
166 return provider && !shared_utils_1.isNil(provider.provide);
167 }
168 insertProvider(provider, token) {
169 const isCustomProvider = this.isCustomProvider(provider);
170 if (!isCustomProvider) {
171 return this.container.addProvider(provider, token);
172 }
173 const applyProvidersMap = this.getApplyProvidersMap();
174 const providersKeys = Object.keys(applyProvidersMap);
175 const type = provider.provide;
176 if (!providersKeys.includes(type)) {
177 return this.container.addProvider(provider, token);
178 }
179 const providerToken = `${type} (UUID: ${random_string_generator_util_1.randomStringGenerator()})`;
180 let scope = provider.scope;
181 if (shared_utils_1.isNil(scope) && provider.useClass) {
182 scope = get_class_scope_1.getClassScope(provider.useClass);
183 }
184 this.applicationProvidersApplyMap.push({
185 type,
186 moduleKey: token,
187 providerKey: providerToken,
188 scope,
189 });
190 const newProvider = Object.assign(Object.assign({}, provider), { provide: providerToken, scope });
191 if (this.isRequestOrTransient(newProvider.scope)) {
192 return this.container.addInjectable(newProvider, token);
193 }
194 this.container.addProvider(newProvider, token);
195 }
196 insertInjectable(injectable, token, host) {
197 this.container.addInjectable(injectable, token, host);
198 }
199 insertExportedProvider(exportedProvider, token) {
200 this.container.addExportedProvider(exportedProvider, token);
201 }
202 insertController(controller, token) {
203 this.container.addController(controller, token);
204 }
205 reflectMetadata(metatype, metadataKey) {
206 return Reflect.getMetadata(metadataKey, metatype) || [];
207 }
208 async registerCoreModule() {
209 const module = this.container.createCoreModule();
210 const instance = await this.scanForModules(module);
211 this.container.registerCoreModuleRef(instance);
212 }
213 /**
214 * Add either request or transient globally scoped enhancers
215 * to all controllers metadata storage
216 */
217 addScopedEnhancersMetadata() {
218 const scopedGlobalProviders = this.applicationProvidersApplyMap.filter(wrapper => this.isRequestOrTransient(wrapper.scope));
219 scopedGlobalProviders.forEach(({ moduleKey, providerKey }) => {
220 const modulesContainer = this.container.getModules();
221 const { injectables } = modulesContainer.get(moduleKey);
222 const instanceWrapper = injectables.get(providerKey);
223 const modules = [...modulesContainer.values()];
224 const controllersArray = modules.map(module => [
225 ...module.controllers.values(),
226 ]);
227 const controllers = this.flatten(controllersArray);
228 controllers.forEach(controller => controller.addEnhancerMetadata(instanceWrapper));
229 });
230 }
231 applyApplicationProviders() {
232 const applyProvidersMap = this.getApplyProvidersMap();
233 const applyRequestProvidersMap = this.getApplyRequestProvidersMap();
234 const getInstanceWrapper = (moduleKey, providerKey, collectionKey) => {
235 const modules = this.container.getModules();
236 const collection = modules.get(moduleKey)[collectionKey];
237 return collection.get(providerKey);
238 };
239 // Add global enhancers to the application config
240 this.applicationProvidersApplyMap.forEach(({ moduleKey, providerKey, type, scope }) => {
241 let instanceWrapper;
242 if (this.isRequestOrTransient(scope)) {
243 instanceWrapper = getInstanceWrapper(moduleKey, providerKey, 'injectables');
244 return applyRequestProvidersMap[type](instanceWrapper);
245 }
246 instanceWrapper = getInstanceWrapper(moduleKey, providerKey, 'providers');
247 applyProvidersMap[type](instanceWrapper.instance);
248 });
249 }
250 getApplyProvidersMap() {
251 return {
252 [constants_2.APP_INTERCEPTOR]: (interceptor) => this.applicationConfig.addGlobalInterceptor(interceptor),
253 [constants_2.APP_PIPE]: (pipe) => this.applicationConfig.addGlobalPipe(pipe),
254 [constants_2.APP_GUARD]: (guard) => this.applicationConfig.addGlobalGuard(guard),
255 [constants_2.APP_FILTER]: (filter) => this.applicationConfig.addGlobalFilter(filter),
256 };
257 }
258 getApplyRequestProvidersMap() {
259 return {
260 [constants_2.APP_INTERCEPTOR]: (interceptor) => this.applicationConfig.addGlobalRequestInterceptor(interceptor),
261 [constants_2.APP_PIPE]: (pipe) => this.applicationConfig.addGlobalRequestPipe(pipe),
262 [constants_2.APP_GUARD]: (guard) => this.applicationConfig.addGlobalRequestGuard(guard),
263 [constants_2.APP_FILTER]: (filter) => this.applicationConfig.addGlobalRequestFilter(filter),
264 };
265 }
266 isDynamicModule(module) {
267 return module && !!module.module;
268 }
269 isForwardReference(module) {
270 return module && !!module.forwardRef;
271 }
272 flatten(arr) {
273 return arr.reduce((a, b) => a.concat(b), []);
274 }
275 isRequestOrTransient(scope) {
276 return scope === interfaces_1.Scope.REQUEST || scope === interfaces_1.Scope.TRANSIENT;
277 }
278}
279exports.DependenciesScanner = DependenciesScanner;