Skip to content

Commit

Permalink
refactor: app can add loader hook
Browse files Browse the repository at this point in the history
  • Loading branch information
JerrysShan committed Jul 11, 2022
1 parent ba604a1 commit 4522ae7
Show file tree
Hide file tree
Showing 5 changed files with 112 additions and 33 deletions.
34 changes: 32 additions & 2 deletions src/application.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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));
}
Expand Down Expand Up @@ -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 };
5 changes: 3 additions & 2 deletions src/constant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -40,7 +40,8 @@ export const DEFAULT_EXCLUDES = [
'*.d.ts',
'jest.config.*',
'meta.*',
'LICENSE'
'LICENSE',
'pnpm-lock.yaml',
];

export const FRAMEWORK_PATTERN = 'framework.*';
Expand Down
50 changes: 21 additions & 29 deletions src/loader/factory.ts
Original file line number Diff line number Diff line change
@@ -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<string, LoaderConstructor> = new Map();
private loaderEvent: LoaderEventEmitter;

static register(clazz: LoaderConstructor) {
const loaderName = Reflect.getMetadata(LOADER_NAME_META, clazz);
Expand All @@ -17,6 +26,7 @@ export class LoaderFactory {

constructor(container: Container) {
this.container = container;
this.loaderEvent = new LoaderEventEmitter();
}

static create(container: Container): LoaderFactory {
Expand All @@ -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) {
Expand All @@ -40,48 +55,25 @@ export class LoaderFactory {
}

async loadManifest(manifest: Manifest, root?: string): Promise<void> {
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<string, LoaderHookUnit>, root?: string): Promise<void> {
async loadItemList(itemList: ManifestItem[] = [], root?: string): Promise<void> {
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);
}
}

Expand Down
55 changes: 55 additions & 0 deletions src/loader/loader_event.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
export interface LoaderEventListener {
before?: CallableFunction;
after?: CallableFunction;
}

export default class LoaderEventEmitter {
private listeners: Record<string, Record<'before' | 'after', CallableFunction[]>> = {};

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);
}
}
}
1 change: 1 addition & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export interface ApplicationLifecycle {

export interface ApplicationInitOptions {
containerName?: string;
env: string;
}

export interface Application {
Expand Down

0 comments on commit 4522ae7

Please sign in to comment.