Skip to content

Commit

Permalink
fix: load extend (#279)
Browse files Browse the repository at this point in the history
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **New Features**
	- Updated package version to `6.3.0-beta.1`.
- Added new scripts for cleaning build artifacts and improved dependency
management.
	- Enhanced lifecycle management with path tracking for boot hooks.

- **Bug Fixes**
- Improved error handling and logging for plugin loading and lifecycle
events.

- **Tests**
- Refactored tests to use async/await syntax for better readability and
error handling.
- Enhanced assertions with custom error messages for clarity in test
failures.

- **Chores**
- Removed strict mode directives from several files to simplify
execution context.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
  • Loading branch information
fengmk2 authored Dec 28, 2024
1 parent 064c6ee commit 0f0a13c
Show file tree
Hide file tree
Showing 36 changed files with 337 additions and 322 deletions.
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.1",
"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

0 comments on commit 0f0a13c

Please sign in to comment.