Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: load extend #279

Merged
merged 6 commits into from
Dec 28, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 9 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@eggjs/core",
"version": "6.2.4",
"version": "6.3.0-beta.0",
"publishConfig": {
"access": "public"
},
Expand All @@ -12,13 +12,14 @@
},
"description": "A core plugin framework based on @eggjs/koa",
"scripts": {
"clean": "rimraf dist",
"lint": "eslint src test --ext ts",
"pretest": "npm run lint -- --fix && npm run prepublishOnly",
"pretest": "npm run clean && npm run lint -- --fix && npm run prepublishOnly",
"test": "npm run test-local",
"test-local": "egg-bin test",
"preci": "npm run lint && npm run prepublishOnly && attw --pack",
"preci": "npm run clean && npm run lint && npm run prepublishOnly",
"ci": "egg-bin cov",
"prepublishOnly": "tshy && tshy-after"
"prepublishOnly": "tshy && tshy-after && attw --pack"
},
"repository": {
"type": "git",
Expand All @@ -37,7 +38,7 @@
"dependencies": {
"@eggjs/koa": "^2.20.2",
"@eggjs/router": "^3.0.5",
"@eggjs/utils": "^4.0.2",
"@eggjs/utils": "^4.1.5",
"egg-logger": "^3.5.0",
"egg-path-matching": "^2.0.0",
"extend2": "^4.0.0",
Expand All @@ -52,19 +53,21 @@
},
"devDependencies": {
"@arethetypeswrong/cli": "^0.17.1",
"@eggjs/bin": "^7.0.0",
"@eggjs/tsconfig": "1",
"@types/js-yaml": "4",
"@types/mocha": "10",
"@types/node": "20",
"@types/supertest": "6",
"await-event": "2",
"coffee": "5",
"egg-bin": "6",
"eslint": "8",
"eslint-config-egg": "14",
"gals": "1",
"js-yaml": "3",
"mm": "3",
"pedding": "^2.0.0",
"rimraf": "6",
"supertest": "7",
"ts-node": "10",
"tshy": "3",
Expand Down
6 changes: 3 additions & 3 deletions src/egg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -320,17 +320,17 @@ export class EggCore extends KoaApplication {
* Register a function that will be called when app close.
*
* Notice:
* This method is now NOT recommanded directly used,
* This method is now NOT recommended directly used,
* Developers SHOULDN'T use app.beforeClose directly now,
* but in the form of class to implement beforeClose instead.
*
* @see https://eggjs.org/en/advanced/loader.html#beforeclose
*
* @param {Function} fn - the function that can be generator function or async function.
*/
beforeClose(fn: Fun) {
beforeClose(fn: Fun, name?: string) {
this.deprecate('`beforeClose` was deprecated, please use "Life Cycles" instead, see https://www.eggjs.org/advanced/loader#life-cycles');
this.lifecycle.registerBeforeClose(fn);
this.lifecycle.registerBeforeClose(fn, name);
}

/**
Expand Down
55 changes: 41 additions & 14 deletions src/lifecycle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import utils from './utils/index.js';
import type { Fun } from './utils/index.js';
import type { EggCore } from './egg.js';

const debug = debuglog('@eggjs/core:lifecycle');
const debug = debuglog('@eggjs/core/lifecycle');

export interface ILifecycleBoot {
// loader auto set 'fullPath' property on boot class
Expand Down Expand Up @@ -62,13 +62,15 @@ export interface LifecycleOptions {
logger: EggConsoleLogger;
}

export type FunWithFullPath = Fun & { fullPath?: string };

export class Lifecycle extends EventEmitter {
#init: boolean;
#readyObject: ReadyObject;
#bootHooks: (BootImplClass | ILifecycleBoot)[];
#boots: ILifecycleBoot[];
#isClosed: boolean;
#closeFunctionSet: Set<Fun>;
#closeFunctionSet: Set<FunWithFullPath>;
loadReady: Ready;
bootReady: Ready;
options: LifecycleOptions;
Expand Down Expand Up @@ -96,10 +98,10 @@ export class Lifecycle extends EventEmitter {
this.#initReady();
this
.on('ready_stat', data => {
this.logger.info('[egg:core:ready_stat] end ready task %s, remain %j', data.id, data.remain);
this.logger.info('[@eggjs/core/lifecycle:ready_stat] end ready task %s, remain %j', data.id, data.remain);
})
.on('ready_timeout', id => {
this.logger.warn('[egg:core:ready_timeout] %s seconds later %s was still unable to finish.', this.readyTimeout / 1000, id);
this.logger.warn('[@eggjs/core/lifecycle:ready_timeout] %s seconds later %s was still unable to finish.', this.readyTimeout / 1000, id);
});

this.ready(err => {
Expand Down Expand Up @@ -149,37 +151,47 @@ export class Lifecycle extends EventEmitter {
this.#bootHooks.push(bootHootOrBootClass);
}

addFunctionAsBootHook<T = EggCore>(hook: (app: T) => void) {
addFunctionAsBootHook<T = EggCore>(hook: (app: T) => void, fullPath?: string) {
assert(this.#init === false, 'do not add hook when lifecycle has been initialized');
// app.js is exported as a function
// call this function in configDidLoad
this.#bootHooks.push(class Boot implements ILifecycleBoot {
class Boot implements ILifecycleBoot {
static fullPath?: string;
app: T;
constructor(app: T) {
this.app = app;
}
configDidLoad() {
hook(this.app);
}
});
}
Boot.fullPath = fullPath;
this.#bootHooks.push(Boot);
}

/**
* init boots and trigger config did config
*/
init() {
debug('%s init lifecycle', this.app.type);
assert(this.#init === false, 'lifecycle have been init');
this.#init = true;
this.#boots = this.#bootHooks.map(BootHootOrBootClass => {
let instance = BootHootOrBootClass as ILifecycleBoot;
if (isClass(BootHootOrBootClass)) {
return new BootHootOrBootClass(this.app);
instance = new BootHootOrBootClass(this.app);
if (!instance.fullPath && 'fullPath' in BootHootOrBootClass) {
instance.fullPath = BootHootOrBootClass.fullPath as string;
}
}
return BootHootOrBootClass;
debug('[init] add boot instance: %o', instance.fullPath);
return instance;
});
}

registerBeforeStart(scope: Fun, name: string) {
debug('add registerBeforeStart, name: %o', name);
debug('%s add registerBeforeStart, name: %o',
this.options.app.type, name);
this.#registerReadyCallback({
scope,
ready: this.loadReady,
Expand All @@ -188,16 +200,24 @@ export class Lifecycle extends EventEmitter {
});
}

registerBeforeClose(fn: Fun) {
registerBeforeClose(fn: FunWithFullPath, fullPath?: string) {
assert(typeof fn === 'function', 'argument should be function');
assert(this.#isClosed === false, 'app has been closed');
if (fullPath) {
fn.fullPath = fullPath;
}
this.#closeFunctionSet.add(fn);
debug('%s register beforeClose at %o, count: %d',
this.app.type, fullPath, this.#closeFunctionSet.size);
}

async close() {
// close in reverse order: first created, last closed
const closeFns = Array.from(this.#closeFunctionSet);
debug('%s start trigger %d beforeClose functions',
this.app.type, closeFns.length);
for (const fn of closeFns.reverse()) {
debug('%s trigger beforeClose at %o', this.app.type, fn.fullPath);
await utils.callFn(fn);
this.#closeFunctionSet.delete(fn);
}
Expand All @@ -206,12 +226,14 @@ export class Lifecycle extends EventEmitter {
this.removeAllListeners();
this.app.removeAllListeners();
this.#isClosed = true;
debug('%s closed', this.app.type);
}

triggerConfigWillLoad() {
debug('trigger configWillLoad start');
for (const boot of this.#boots) {
if (typeof boot.configWillLoad === 'function') {
debug('trigger configWillLoad at %o', boot.fullPath);
boot.configWillLoad();
}
}
Expand All @@ -223,12 +245,13 @@ export class Lifecycle extends EventEmitter {
debug('trigger configDidLoad start');
for (const boot of this.#boots) {
if (typeof boot.configDidLoad === 'function') {
debug('trigger configDidLoad at %o', boot.fullPath);
boot.configDidLoad();
}
// function boot hook register after configDidLoad trigger
if (typeof boot.beforeClose === 'function') {
const beforeClose = boot.beforeClose.bind(boot);
this.registerBeforeClose(beforeClose);
this.registerBeforeClose(beforeClose, boot.fullPath);
}
}
debug('trigger configDidLoad end');
Expand Down Expand Up @@ -274,10 +297,12 @@ export class Lifecycle extends EventEmitter {
return (async () => {
for (const boot of this.#boots) {
if (typeof boot.didReady === 'function') {
debug('trigger didReady at %o', boot.fullPath);
try {
await boot.didReady(err);
} catch (e) {
this.emit('error', e);
} catch (err) {
debug('trigger didReady error at %o, error: %s', boot.fullPath, err);
this.emit('error', err);
}
}
}
Expand All @@ -292,9 +317,11 @@ export class Lifecycle extends EventEmitter {
if (typeof boot.serverDidReady !== 'function') {
continue;
}
debug('trigger serverDidReady at %o', boot.fullPath);
try {
await boot.serverDidReady();
} catch (err) {
debug('trigger serverDidReady error at %o, error: %s', boot.fullPath, err);
this.emit('error', err);
}
}
Expand Down
20 changes: 12 additions & 8 deletions src/loader/egg_loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -781,7 +781,7 @@ export class EggLoader {
const pluginPkgFile = utils.resolvePath(`${name}/package.json`, { paths: [ ...this.lookupDirs ] });
return path.dirname(pluginPkgFile);
} catch (err) {
debug('[resolvePluginPath] error: %o', err);
debug('[resolvePluginPath] error: %o, plugin info: %o', err, plugin);
throw new Error(`Can not find plugin ${name} in "${[ ...this.lookupDirs ].join(', ')}"`, {
cause: err,
});
Expand Down Expand Up @@ -1166,19 +1166,23 @@ export class EggLoader {
async #loadBootHook(fileName: string) {
this.timing.start(`Load ${fileName}.js`);
for (const unit of this.getLoadUnits()) {
const bootFilePath = this.resolveModule(path.join(unit.path, fileName));
const bootFile = path.join(unit.path, fileName);
const bootFilePath = this.resolveModule(bootFile);
if (!bootFilePath) {
// debug('[loadBootHook] %o not found', bootFile);
continue;
}
const bootHook = await this.requireFile(bootFilePath);
if (isClass(bootHook)) {
bootHook.prototype.fullPath = bootFilePath;
// if is boot class, add to lifecycle
this.lifecycle.addBootHook(bootHook);
debug('[loadBootHook] add BootHookClass from %o', bootFilePath);
} else if (typeof bootHook === 'function') {
// if is boot function, wrap to class
// for compatibility
this.lifecycle.addFunctionAsBootHook(bootHook);
this.lifecycle.addFunctionAsBootHook(bootHook, bootFilePath);
debug('[loadBootHook] add bootHookFunction from %o', bootFilePath);
} else {
this.options.logger.warn('[@eggjs/core:egg_loader] %s must exports a boot class', bootFilePath);
}
Expand Down Expand Up @@ -1595,13 +1599,13 @@ export class EggLoader {
let fullPath;
try {
fullPath = utils.resolvePath(filepath);
} catch (e) {
return undefined;
}

if (process.env.EGG_TYPESCRIPT !== 'true' && fullPath.endsWith('.ts')) {
} catch (err: any) {
// debug('[resolveModule] Module %o resolve error: %s', filepath, err.stack);
return undefined;
}
// if (process.env.EGG_TYPESCRIPT !== 'true' && fullPath.endsWith('.ts')) {
// return undefined;
// }
return fullPath;
}
}
Expand Down
Loading
Loading