core: use cordis to manage app lifecycle

This commit is contained in:
undefined committed 2022-09-23 17:52:27 +08:00
1 parent d7ec26b772
commit 5b7067d77d
19 files changed
+307 -375

No files matched your search

+76 -4
View File
@@ -1,3 +1,5 @@
/* eslint-disable import/no-dynamic-require */
/* eslint-disable consistent-return */
/* eslint-disable simple-import-sort/imports */
/* eslint-disable no-await-in-loop */
/* eslint-disable no-eval */
@@ -5,15 +7,18 @@ import './init';
import './interface';
import Schema from 'schemastery';
import path from 'path';
// eslint-disable-next-line import/no-duplicates
import './utils';
import cac from 'cac';
import { Logger } from './logger';
import './ui';
import { Logger } from './logger';
import { ApiMixin, Context } from './context';
// eslint-disable-next-line import/no-duplicates
import { unwrapExports } from './utils';
// This is the main entry. So let's re-export some modules.
export * from './interface';
export { Schema, Logger };
export { requestConfig } from './settings';
const argv = cac().parse();
const logger = new Logger('loader');
@@ -22,6 +27,66 @@ logger.debug('%o', argv);
process.on('unhandledRejection', logger.error);
process.on('uncaughtException', logger.error);
export function resolveConfig(plugin: any, config: any) {
if (config === false) return;
if (config === true) config = undefined;
config ??= {};
const schema = plugin['Config'] || plugin['schema'];
if (schema && plugin['schema'] !== false) config = schema(config);
return config;
}
Context.service('loader');
export class Loader {
static readonly kUpdate = Symbol.for('koishi.loader.update');
static readonly kRecord = Symbol.for('koishi.loader.record');
public app: Context;
public config: Context.Config;
public suspend = false;
public cache: Record<string, string> = Object.create(null);
unloadPlugin(ctx: Context, key: string) {
const fork = ctx.state[Loader.kRecord][key];
if (fork) {
fork.dispose();
delete ctx.state[Loader.kRecord][key];
logger.info('unload plugin %c', key);
}
}
async reloadPlugin(parent: Context, key: string, config: any, asName = '') {
let fork = parent.state[Loader.kRecord]?.[key];
if (fork) {
logger.info('reload plugin %c', key);
fork[Loader.kUpdate] = true;
fork.update(config);
} else {
logger.info('apply plugin %c', key);
const plugin = await this.resolvePlugin(key);
if (!plugin) return;
resolveConfig(plugin, config);
if (asName) plugin.name = asName;
// fork = parent.plugin(plugin, this.interpolate(config));
fork = parent.plugin(plugin, config);
if (!fork) return;
parent.state[Loader.kRecord] ||= Object.create(null);
parent.state[Loader.kRecord][key] = fork;
}
return fork;
}
async resolvePlugin(name: string) {
try {
this.cache[name] ||= require.resolve(name);
} catch (err) {
logger.error(err.message);
return;
}
return unwrapExports(require(this.cache[name]));
}
}
export function addon(addonPath: string, prepend = false) {
try {
// Is a npm package
@@ -52,15 +117,22 @@ export function addScript(name: string, description: string) {
};
}
Context.service('loader');
app.api = new ApiMixin(app);
const loader = new Loader();
app.loader = loader;
loader.app = app;
app.state[Loader.kRecord] = Object.create(null);
export async function load() {
addon(path.resolve(__dirname, '..'), true);
Error.stackTraceLimit = 50;
require('./entry/worker').load();
await require('./entry/worker').apply(app);
global.gc?.();
}
export async function loadCli() {
process.env.HYDRO_CLI = 'true';
await require('./entry/cli').load();
await require('./entry/cli').load(app);
setTimeout(() => process.exit(0), 300);
}