From bf5b7ad394a059fc16a558f29ed86fbe6eba054a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20My=C5=9Bliwiec?= Date: Wed, 8 Feb 2023 10:42:52 +0100 Subject: [PATCH] feat(core): initialize on preview allowlist --- packages/core/index.ts | 1 + packages/core/injector/container.ts | 6 +++ packages/core/injector/injector.ts | 2 +- packages/core/injector/module.ts | 9 ++++ packages/core/inspector/index.ts | 2 + .../initialize-on-preview.allowlist.ts | 13 ++++++ packages/core/nest-application-context.ts | 42 +++++++------------ packages/testing/testing-module.builder.ts | 15 ++++--- 8 files changed, 57 insertions(+), 33 deletions(-) create mode 100644 packages/core/inspector/index.ts create mode 100644 packages/core/inspector/initialize-on-preview.allowlist.ts diff --git a/packages/core/index.ts b/packages/core/index.ts index 75c7156e3..511eac90d 100644 --- a/packages/core/index.ts +++ b/packages/core/index.ts @@ -13,6 +13,7 @@ export * from './discovery'; export * from './exceptions'; export * from './helpers'; export * from './injector'; +export * from './inspector'; export * from './metadata-scanner'; export * from './middleware'; export * from './nest-application'; diff --git a/packages/core/injector/container.ts b/packages/core/injector/container.ts index ba544ad26..2522eabed 100644 --- a/packages/core/injector/container.ts +++ b/packages/core/injector/container.ts @@ -10,6 +10,7 @@ import { UndefinedForwardRefException, UnknownModuleException, } from '../errors/exceptions'; +import { InitializeOnPreviewAllowlist } from '../inspector/initialize-on-preview.allowlist'; import { SerializedGraph } from '../inspector/serialized-graph'; import { REQUEST } from '../router/request/request-constants'; import { ModuleCompiler } from './compiler'; @@ -80,6 +81,7 @@ export class NestContainer { } const moduleRef = new Module(type, this); moduleRef.token = token; + moduleRef.initOnPreview = this.shouldInitOnPreview(type); this.modules.set(token, moduleRef); const updatedScope = [].concat(scope, type); @@ -255,4 +257,8 @@ export class NestContainer { isResolved: true, }); } + + private shouldInitOnPreview(type: Type) { + return InitializeOnPreviewAllowlist.has(type); + } } diff --git a/packages/core/injector/injector.ts b/packages/core/injector/injector.ts index 4930d4768..281acd8b9 100644 --- a/packages/core/injector/injector.ts +++ b/packages/core/injector/injector.ts @@ -717,7 +717,7 @@ export class Injector { wrapper.isLazyTransient(contextId, inquirer) || wrapper.isExplicitlyRequested(contextId, inquirer); - if (this.options?.preview) { + if (this.options?.preview && !wrapper.host?.initOnPreview) { instanceHost.isResolved = true; return instanceHost.instance; } diff --git a/packages/core/injector/module.ts b/packages/core/injector/module.ts index 60884d737..22190b3f1 100644 --- a/packages/core/injector/module.ts +++ b/packages/core/injector/module.ts @@ -63,6 +63,7 @@ export class Module { >(); private readonly _exports = new Set(); private _distance = 0; + private _initOnPreview = false; private _isGlobal = false; private _token: string; @@ -98,6 +99,14 @@ export class Module { this._isGlobal = global; } + get initOnPreview() { + return this._initOnPreview; + } + + set initOnPreview(initOnPreview: boolean) { + this._initOnPreview = initOnPreview; + } + get providers(): Map> { return this._providers; } diff --git a/packages/core/inspector/index.ts b/packages/core/inspector/index.ts new file mode 100644 index 000000000..c6efeff84 --- /dev/null +++ b/packages/core/inspector/index.ts @@ -0,0 +1,2 @@ +export * from './graph-inspector'; +export * from './initialize-on-preview.allowlist'; diff --git a/packages/core/inspector/initialize-on-preview.allowlist.ts b/packages/core/inspector/initialize-on-preview.allowlist.ts new file mode 100644 index 000000000..27d6ad593 --- /dev/null +++ b/packages/core/inspector/initialize-on-preview.allowlist.ts @@ -0,0 +1,13 @@ +import { Type } from '@nestjs/common'; + +export class InitializeOnPreviewAllowlist { + private static readonly allowlist = new WeakMap(); + + public static add(type: Type) { + this.allowlist.set(type, true); + } + + public static has(type: Type) { + return this.allowlist.has(type); + } +} diff --git a/packages/core/nest-application-context.ts b/packages/core/nest-application-context.ts index 64dffff74..ac4d64957 100644 --- a/packages/core/nest-application-context.ts +++ b/packages/core/nest-application-context.ts @@ -52,7 +52,7 @@ export class NestApplicationContext< private readonly moduleCompiler = new ModuleCompiler(); private shutdownCleanupRef?: (...args: unknown[]) => unknown; private _instanceLinksHost: InstanceLinksHost; - private _moduleRefsByDistance?: Array; + private _moduleRefsForHooksByDistance?: Array; protected get instanceLinksHost() { if (!this._instanceLinksHost) { @@ -372,10 +372,7 @@ export class NestApplicationContext< * modules and its children. */ protected async callInitHook(): Promise { - if (this.appOptions.preview) { - return; - } - const modulesSortedByDistance = this.getModulesSortedByDistance(); + const modulesSortedByDistance = this.getModulesToTriggerHooksOn(); for (const module of modulesSortedByDistance) { await callModuleInitHook(module); } @@ -386,10 +383,7 @@ export class NestApplicationContext< * modules and its children. */ protected async callDestroyHook(): Promise { - if (this.appOptions.preview) { - return; - } - const modulesSortedByDistance = this.getModulesSortedByDistance(); + const modulesSortedByDistance = this.getModulesToTriggerHooksOn(); for (const module of modulesSortedByDistance) { await callModuleDestroyHook(module); } @@ -400,10 +394,7 @@ export class NestApplicationContext< * modules and its children. */ protected async callBootstrapHook(): Promise { - if (this.appOptions.preview) { - return; - } - const modulesSortedByDistance = this.getModulesSortedByDistance(); + const modulesSortedByDistance = this.getModulesToTriggerHooksOn(); for (const module of modulesSortedByDistance) { await callModuleBootstrapHook(module); } @@ -414,10 +405,7 @@ export class NestApplicationContext< * modules and children. */ protected async callShutdownHook(signal?: string): Promise { - if (this.appOptions.preview) { - return; - } - const modulesSortedByDistance = this.getModulesSortedByDistance(); + const modulesSortedByDistance = this.getModulesToTriggerHooksOn(); for (const module of modulesSortedByDistance) { await callAppShutdownHook(module, signal); } @@ -428,10 +416,7 @@ export class NestApplicationContext< * modules and children. */ protected async callBeforeShutdownHook(signal?: string): Promise { - if (this.appOptions.preview) { - return; - } - const modulesSortedByDistance = this.getModulesSortedByDistance(); + const modulesSortedByDistance = this.getModulesToTriggerHooksOn(); for (const module of modulesSortedByDistance) { await callBeforeAppShutdownHook(module, signal); } @@ -445,17 +430,20 @@ export class NestApplicationContext< } } - private getModulesSortedByDistance(): Module[] { - if (this._moduleRefsByDistance) { - return this._moduleRefsByDistance; + private getModulesToTriggerHooksOn(): Module[] { + if (this._moduleRefsForHooksByDistance) { + return this._moduleRefsForHooksByDistance; } const modulesContainer = this.container.getModules(); const compareFn = (a: Module, b: Module) => b.distance - a.distance; - - this._moduleRefsByDistance = Array.from(modulesContainer.values()).sort( + const modulesSortedByDistance = Array.from(modulesContainer.values()).sort( compareFn, ); - return this._moduleRefsByDistance; + + this._moduleRefsForHooksByDistance = this.appOptions?.preview + ? modulesSortedByDistance.filter(moduleRef => moduleRef.initOnPreview) + : modulesSortedByDistance; + return this._moduleRefsForHooksByDistance; } private printInPreviewModeWarning() { diff --git a/packages/testing/testing-module.builder.ts b/packages/testing/testing-module.builder.ts index 9dafa121a..673d7455d 100644 --- a/packages/testing/testing-module.builder.ts +++ b/packages/testing/testing-module.builder.ts @@ -26,7 +26,6 @@ import { TestingModule } from './testing-module'; export class TestingModuleBuilder { private readonly applicationConfig = new ApplicationConfig(); private readonly container = new NestContainer(this.applicationConfig); - private readonly injector = new TestingInjector(); private readonly overloadsMap = new Map(); private readonly module: any; private testingLogger: LoggerService; @@ -70,7 +69,7 @@ export class TestingModuleBuilder { } public async compile( - options: Pick = {}, + options: Pick = {}, ): Promise { this.applyLogger(); @@ -92,7 +91,7 @@ export class TestingModuleBuilder { await scanner.scan(this.module); this.applyOverloadsMap(); - await this.createInstancesOfDependencies(graphInspector); + await this.createInstancesOfDependencies(graphInspector, options); scanner.applyApplicationProviders(); const root = this.getRootModule(); @@ -137,10 +136,16 @@ export class TestingModuleBuilder { return modules.next().value; } - private async createInstancesOfDependencies(graphInspector: GraphInspector) { + private async createInstancesOfDependencies( + graphInspector: GraphInspector, + options: { preview?: boolean }, + ) { + const injector = new TestingInjector({ + preview: options?.preview ?? false, + }); const instanceLoader = new TestingInstanceLoader( this.container, - this.injector, + injector, graphInspector, ); await instanceLoader.createInstancesOfDependencies(