Skip to content

Commit

Permalink
feat: add LifecyclePreStandaloneLoad
Browse files Browse the repository at this point in the history
  • Loading branch information
akitaSummer committed Sep 9, 2024
1 parent 27fd9ff commit a737bfa
Show file tree
Hide file tree
Showing 8 changed files with 151 additions and 2 deletions.
5 changes: 5 additions & 0 deletions core/lifecycle/src/LifycycleUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,4 +98,9 @@ export class LifecycleUtil<T extends LifecycleContext, R extends LifecycleObject
const LIFECYCLE_HOOK = Symbol.for(`EggPrototype#Lifecycle${hookName}`);
return proto.getMetaData<string>(LIFECYCLE_HOOK);
}

static getStaticLifecycleHook(hookName: LifecycleHookName, clazz: EggProtoImplClass) {
const LIFECYCLE_HOOK = Symbol.for(`EggPrototype#Lifecycle${hookName}`);
return MetadataUtil.getMetaData<string>(LIFECYCLE_HOOK, clazz);
}
}
13 changes: 13 additions & 0 deletions core/lifecycle/src/decorator/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,22 @@ function createLifecycle(hookName: LifecycleHookName) {
};
}

function createStaticLifecycle(hookName: LifecycleHookName) {
return () => {
return function(target: EggProtoImplClass, methodName: string) {
if (typeof target !== 'function') {
throw new Error(`${hookName} must be a static function`);
}
LifecycleUtil.setLifecycleHook(methodName, hookName, target);
};
};
}


export const LifecyclePostConstruct = createLifecycle('postConstruct');
export const LifecyclePreInject = createLifecycle('preInject');
export const LifecyclePostInject = createLifecycle('postInject');
export const LifecycleInit = createLifecycle('init');
export const LifecyclePreDestroy = createLifecycle('preDestroy');
export const LifecycleDestroy = createLifecycle('destroy');
export const LifecyclePreStandaloneLoad = createStaticLifecycle('preStandaloneLoad');
4 changes: 4 additions & 0 deletions core/types/lifecycle/EggObjectLifecycle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ import type { EggObject, EggObjectLifeCycleContext } from '@eggjs/tegg-runtime';
* lifecycle hook interface for egg object
*/
export interface EggObjectLifecycle {
/**
* call before standalone load
*/
preStandaloneLoad?(ctx: EggObjectLifeCycleContext, eggObj: EggObject): Promise<void>;
/**
* call after construct
*/
Expand Down
20 changes: 20 additions & 0 deletions standalone/standalone/src/Runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
ModuleConfigs,
ConfigSourceQualifierAttribute,
Logger,
LifecycleUtil,
} from '@eggjs/tegg';
import { StandaloneUtil, MainRunner } from '@eggjs/tegg/standalone';
import { CrosscutAdviceFactory } from '@eggjs/tegg/aop';
Expand All @@ -40,6 +41,7 @@ import { MysqlDataSourceManager } from '@eggjs/tegg-dal-plugin/lib/MysqlDataSour
import { SqlMapManager } from '@eggjs/tegg-dal-plugin/lib/SqlMapManager';
import { TableModelManager } from '@eggjs/tegg-dal-plugin/lib/TableModelManager';
import { TransactionPrototypeHook } from '@eggjs/tegg-dal-plugin/lib/TransactionPrototypeHook';
import { LoaderFactory } from '@eggjs/tegg-loader';

export interface ModuleDependency extends ReadModuleReferenceOptions {
baseDir: string;
Expand Down Expand Up @@ -189,6 +191,24 @@ export class Runner {
return [ standaloneLoadUnit, ...loadUnits ];
}

static async preLoad(cwd: string, dependencies?: RunnerOptions['dependencies']) {
const moduleDirs = (dependencies || []).concat(cwd);
const moduleReferences = moduleDirs.reduce((list, baseDir) => {
const module = typeof baseDir === 'string' ? { baseDir } : baseDir;
return list.concat(...ModuleConfigUtil.readModuleReference(module.baseDir, module));
}, [] as readonly ModuleReference[]);
for (const module of moduleReferences) {
const protoClassList = LoaderFactory.createLoader(module.path, 'MODULE').load();
// lifecycle preStandaloneLoad hook
for (const protoClass of protoClassList) {
const fnName = LifecycleUtil.getStaticLifecycleHook('preStandaloneLoad', protoClass);
if (fnName) {
await protoClass[fnName]();
}
}
}
}

async init() {
this.loadUnits = await this.load();
const instances: LoadUnitInstance[] = [];
Expand Down
9 changes: 9 additions & 0 deletions standalone/standalone/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
import { Runner, RunnerOptions } from './Runner';

export async function preLoad(cwd: string, dependencies?: RunnerOptions['dependencies']) {
try {
await Runner.preLoad(cwd, dependencies);
} catch (e) {
e.message = `[tegg/standalone] bootstrap standalone preLoad failed: ${e.message}`;
throw e;
}
}

export async function main<T = void>(cwd: string, options?: RunnerOptions): Promise<T> {
const runner = new Runner(cwd, options);
try {
Expand Down
70 changes: 70 additions & 0 deletions standalone/standalone/test/fixtures/lifecycle/foo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import {
SingletonProto,
LifecyclePreStandaloneLoad,
LifecyclePostConstruct,
LifecyclePreInject,
LifecyclePostInject,
LifecycleInit,
LifecyclePreDestroy,
LifecycleDestroy,
} from '@eggjs/tegg';
import { Runner, MainRunner } from '@eggjs/tegg/standalone';

@Runner()
@SingletonProto()
export class Foo implements MainRunner<string[]> {
private called: string[] = [];

static staticCalled: string[] = [];

getLifecycleCalled() {
return this.called;
}

@LifecyclePreStandaloneLoad()
static async _preStandaloneLoad() {
Foo.staticCalled.push('preStandaloneLoad');
}

constructor() {
this.called.push('construct');
}

@LifecyclePostConstruct()
protected async _postConstruct() {
this.called.push('postConstruct');
}

@LifecyclePreInject()
protected async _preInject() {
this.called.push('preInject');
}

@LifecyclePostInject()
protected async _postInject() {
this.called.push('postInject');
}

protected async init() {
this.called.push('init should not called');
}

@LifecycleInit()
protected async _init() {
this.called.push('init');
}

@LifecyclePreDestroy()
protected async _preDestroy() {
this.called.push('preDestroy');
}

@LifecycleDestroy()
protected async _destroy() {
this.called.push('destroy');
}

async main(): Promise<string[]> {
return this.called;
}
}
6 changes: 6 additions & 0 deletions standalone/standalone/test/fixtures/lifecycle/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"name": "lifecycle",
"eggModule": {
"name": "lifecycle"
}
}
26 changes: 24 additions & 2 deletions standalone/standalone/test/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { strict as assert } from 'node:assert';
import { strict as assert, deepStrictEqual } from 'node:assert';
import path from 'node:path';
import fs from 'node:fs/promises';
import { ModuleConfig, ModuleConfigs } from '@eggjs/tegg/helper';
import { main, StandaloneContext, Runner } from '..';
import { main, StandaloneContext, Runner, preLoad } from '..';
import { crosscutAdviceParams, pointcutAdviceParams } from './fixtures/aop-module/Hello';
import { Foo } from './fixtures/dal-module/src/Foo';

Expand Down Expand Up @@ -276,4 +276,26 @@ describe('standalone/standalone/test/index.test.ts', () => {
assert.equal(result, '{"body":{"fullname":"mock fullname","skipDependencies":true,"registryName":"ok"}}');
});
});

describe('lifecycle', () => {
const fixturePath = path.join(__dirname, './fixtures/lifecycle');

it('preLoad should work', async () => {
await preLoad(fixturePath);
// eslint-disable-next-line @typescript-eslint/no-var-requires
const { Foo } = require(path.join(fixturePath, './foo'));
deepStrictEqual(Foo.staticCalled, [ 'preStandaloneLoad' ]);
});

it('should work', async () => {
const result = await main<string[]>(fixturePath);
assert.deepStrictEqual(result, [
'construct',
'postConstruct',
'preInject',
'postInject',
'init',
]);
});
});
});

0 comments on commit a737bfa

Please sign in to comment.