UNPKG

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