From 4522ae7a01f885d19e81c50f3ead060252e14352 Mon Sep 17 00:00:00 2001 From: JerrysShan <1670303003@qq.com> Date: Mon, 11 Jul 2022 21:05:26 +0800 Subject: [PATCH] refactor: app can add loader hook --- src/application.ts | 34 +++++++++++++++++++++-- src/constant.ts | 5 ++-- src/loader/factory.ts | 50 +++++++++++++++------------------- src/loader/loader_event.ts | 55 ++++++++++++++++++++++++++++++++++++++ src/types.ts | 1 + 5 files changed, 112 insertions(+), 33 deletions(-) create mode 100644 src/loader/loader_event.ts diff --git a/src/application.ts b/src/application.ts index 7df5f30d..5f94a936 100644 --- a/src/application.ts +++ b/src/application.ts @@ -1,6 +1,6 @@ import 'reflect-metadata'; import { Container } from '@artus/injection'; -import { ArtusInjectEnum } from './constant'; +import { ArtusInjectEnum, ARTUS_SERVER_ENV, ARTUS_DEFAULT_CONFIG_ENV } from './constant'; import { ArtusStdError, ExceptionHandler } from './exception'; import { HookFunction, LifecycleManager } from './lifecycle'; import { LoaderFactory, Manifest } from './loader'; @@ -16,12 +16,14 @@ export class ArtusApplication implements Application { protected lifecycleManager: LifecycleManager; protected loaderFactory: LoaderFactory; protected defaultClazzLoaded: boolean = false; + protected env: string; constructor(opts?: ApplicationInitOptions) { this.container = new Container(opts?.containerName ?? ArtusInjectEnum.DefaultContainerName); this.lifecycleManager = new LifecycleManager(this, this.container); this.loaderFactory = LoaderFactory.create(this.container); - + this.env = opts?.env ?? process.env[ARTUS_SERVER_ENV] ?? ARTUS_DEFAULT_CONFIG_ENV.DEV; + this.addLoaderListener(); process.on('SIGINT', () => this.close(true)); process.on('SIGTERM', () => this.close(true)); } @@ -115,6 +117,34 @@ export class ArtusApplication implements Application { createException(code: string): ArtusStdError { return this.exceptionHandler.create(code); } + + protected addLoaderListener() { + this.loaderFactory + .addLoaderListener('config', { + before: () => this.lifecycleManager.emitHook('configWillLoad'), + after: () => { + this.container.set({ + id: ArtusInjectEnum.Config, + value: this.configurationHandler.getMergedConfig(this.env), + }); + this.lifecycleManager.emitHook('configDidLoad'); + }, + }) + .addLoaderListener('framework-config', { + after: () => + this.container.set({ + id: ArtusInjectEnum.Frameworks, + value: this.configurationHandler.getFrameworkConfig(), + }), + }) + .addLoaderListener('package-json', { + after: () => + this.container.set({ + id: ArtusInjectEnum.Packages, + value: this.configurationHandler.getPackages(), + }), + }); + } } export { Application }; diff --git a/src/constant.ts b/src/constant.ts index 59a92bdd..b47b5774 100644 --- a/src/constant.ts +++ b/src/constant.ts @@ -22,7 +22,7 @@ export enum ARTUS_DEFAULT_CONFIG_ENV { DEV = 'development', PROD = 'production', DEFAULT = 'default', -}; +} export const HOOK_NAME_META_PREFIX = 'hookName:'; export const CONSTRUCTOR_PARAMS = 'constructor:params'; @@ -40,7 +40,8 @@ export const DEFAULT_EXCLUDES = [ '*.d.ts', 'jest.config.*', 'meta.*', - 'LICENSE' + 'LICENSE', + 'pnpm-lock.yaml', ]; export const FRAMEWORK_PATTERN = 'framework.*'; diff --git a/src/loader/factory.ts b/src/loader/factory.ts index f7bb0473..3d85e109 100644 --- a/src/loader/factory.ts +++ b/src/loader/factory.ts @@ -1,14 +1,23 @@ import * as path from 'path'; import { Container } from '@artus/injection'; import { ArtusInjectEnum, DEFAULT_LOADER, HOOK_FILE_LOADER, LOADER_NAME_META } from '../constant'; -import { Manifest, ManifestItem, LoaderConstructor, Loader, LoaderHookUnit, LoaderFindOptions, LoaderFindResult } from './types'; +import { + Manifest, + ManifestItem, + LoaderConstructor, + Loader, + LoaderFindOptions, + LoaderFindResult, +} from './types'; import ConfigurationHandler from '../configuration'; import { LifecycleManager } from '../lifecycle'; import compatibleRequire from '../utils/compatible_require'; +import LoaderEventEmitter, { LoaderEventListener } from './loader_event'; export class LoaderFactory { private container: Container; private static loaderClazzMap: Map = new Map(); + private loaderEvent: LoaderEventEmitter; static register(clazz: LoaderConstructor) { const loaderName = Reflect.getMetadata(LOADER_NAME_META, clazz); @@ -17,6 +26,7 @@ export class LoaderFactory { constructor(container: Container) { this.container = container; + this.loaderEvent = new LoaderEventEmitter(); } static create(container: Container): LoaderFactory { @@ -31,6 +41,11 @@ export class LoaderFactory { return this.container.get(ConfigurationHandler); } + addLoaderListener(eventName: string, listener: LoaderEventListener) { + this.loaderEvent.addListener(eventName, listener); + return this; + } + getLoader(loaderName: string): Loader { const LoaderClazz = LoaderFactory.loaderClazzMap.get(loaderName); if (!LoaderClazz) { @@ -40,48 +55,25 @@ export class LoaderFactory { } async loadManifest(manifest: Manifest, root?: string): Promise { - await this.loadItemList(manifest.items, { - config: { - before: () => this.lifecycleManager.emitHook('configWillLoad'), - after: () => { - this.container.set({ - id: ArtusInjectEnum.Config, - value: this.configurationHandler.getMergedConfig() - }); - this.lifecycleManager.emitHook('configDidLoad'); - } - }, - 'framework-config': { - after: () => this.container.set({ - id: ArtusInjectEnum.Frameworks, - value: this.configurationHandler.getFrameworkConfig() - }) - }, - 'package-json': { - after: () => this.container.set({ - id: ArtusInjectEnum.Packages, - value: this.configurationHandler.getPackages() - }) - } - }, root); + await this.loadItemList(manifest.items, root); } - async loadItemList(itemList: ManifestItem[] = [], hookMap?: Record, root?: string): Promise { + async loadItemList(itemList: ManifestItem[] = [], root?: string): Promise { let prevLoader: string = ''; for (const item of itemList) { item.path = root ? path.join(root, item.path) : item.path; const curLoader = item.loader ?? DEFAULT_LOADER; if (item.loader !== prevLoader) { if (prevLoader) { - await hookMap?.[prevLoader]?.after?.(); + await this.loaderEvent.emitAfter(prevLoader); } - await hookMap?.[curLoader]?.before?.(); + await this.loaderEvent.emitBefore(curLoader); prevLoader = curLoader; } await this.loadItem(item); } if (prevLoader) { - await hookMap?.[prevLoader]?.after?.(); + await this.loaderEvent.emitAfter(prevLoader); } } diff --git a/src/loader/loader_event.ts b/src/loader/loader_event.ts new file mode 100644 index 00000000..db12384a --- /dev/null +++ b/src/loader/loader_event.ts @@ -0,0 +1,55 @@ +export interface LoaderEventListener { + before?: CallableFunction; + after?: CallableFunction; +} + +export default class LoaderEventEmitter { + private listeners: Record> = {}; + + addListener(eventName, listener: LoaderEventListener) { + if (!this.listeners[eventName]) { + this.listeners[eventName] = { before: [], after: [] }; + } + + if (listener.before) { + this.listeners[eventName].before.push(listener.before); + } + + if (listener.after) { + this.listeners[eventName].after.push(listener.after); + } + } + + removeListener(eventName: string, stage: keyof LoaderEventListener) { + if (!this.listeners[eventName]) { + return; + } + if (stage) { + this.listeners[eventName][stage] = []; + return; + } + + delete this.listeners[eventName]; + } + + async emitBefore(eventName, ...args) { + const { before = [] } = this.listeners[eventName] ?? {}; + if (!before || before.length === 0) { + return; + } + for (const listener of before) { + await listener(...args); + } + } + + async emitAfter(eventName, ...args) { + const { after = [] } = this.listeners[eventName] ?? {}; + if (!after || after.length === 0) { + return; + } + + for (const listener of after) { + await listener(...args); + } + } +} diff --git a/src/types.ts b/src/types.ts index 7e4760e5..eea357c8 100644 --- a/src/types.ts +++ b/src/types.ts @@ -14,6 +14,7 @@ export interface ApplicationLifecycle { export interface ApplicationInitOptions { containerName?: string; + env: string; } export interface Application {