feat(core): initialize on preview allowlist

This commit is contained in:
Kamil Myśliwiec
2023-02-08 10:42:52 +01:00
parent a64b9cc9cb
commit bf5b7ad394
8 changed files with 57 additions and 33 deletions

View File

@@ -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';

View File

@@ -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);
}
}

View File

@@ -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;
}

View File

@@ -63,6 +63,7 @@ export class Module {
>();
private readonly _exports = new Set<InstanceToken>();
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<InstanceToken, InstanceWrapper<Injectable>> {
return this._providers;
}

View File

@@ -0,0 +1,2 @@
export * from './graph-inspector';
export * from './initialize-on-preview.allowlist';

View File

@@ -0,0 +1,13 @@
import { Type } from '@nestjs/common';
export class InitializeOnPreviewAllowlist {
private static readonly allowlist = new WeakMap<Type, boolean>();
public static add(type: Type) {
this.allowlist.set(type, true);
}
public static has(type: Type) {
return this.allowlist.has(type);
}
}

View File

@@ -52,7 +52,7 @@ export class NestApplicationContext<
private readonly moduleCompiler = new ModuleCompiler();
private shutdownCleanupRef?: (...args: unknown[]) => unknown;
private _instanceLinksHost: InstanceLinksHost;
private _moduleRefsByDistance?: Array<Module>;
private _moduleRefsForHooksByDistance?: Array<Module>;
protected get instanceLinksHost() {
if (!this._instanceLinksHost) {
@@ -372,10 +372,7 @@ export class NestApplicationContext<
* modules and its children.
*/
protected async callInitHook(): Promise<void> {
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<void> {
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<void> {
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<void> {
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<void> {
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() {

View File

@@ -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<NestApplicationContextOptions, 'snapshot'> = {},
options: Pick<NestApplicationContextOptions, 'snapshot' | 'preview'> = {},
): Promise<TestingModule> {
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(