From b63598af987c92cc15627bd844d06511ca40d727 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9F=8C=8C?= Date: Mon, 11 Mar 2024 09:25:43 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=20=E6=8F=92=E4=BB=B6?= =?UTF-8?q?=E5=BC=82=E6=AD=A5=E5=8A=A0=E8=BD=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/plugins/loader.js | 315 ++++++++++++++++++------------------------ 1 file changed, 132 insertions(+), 183 deletions(-) diff --git a/lib/plugins/loader.js b/lib/plugins/loader.js index 37bdd44..38ff303 100644 --- a/lib/plugins/loader.js +++ b/lib/plugins/loader.js @@ -1,5 +1,5 @@ import util from 'node:util' -import fs from 'node:fs' +import fs from 'node:fs/promises' import lodash from 'lodash' import cfg from '../config/config.js' import plugin from './plugin.js' @@ -26,7 +26,7 @@ class PluginsLoader { this.priority = [] this.handler = {} this.task = [] - this.dir = './plugins' + this.dir = 'plugins' /** 命令冷却cd */ this.groupGlobalCD = {} @@ -41,6 +41,39 @@ class PluginsLoader { this.srReg = /^#?(\*|星铁|星轨|穹轨|星穹|崩铁|星穹铁道|崩坏星穹铁道|铁道)+/ } + async getPlugins() { + const files = await fs.readdir(this.dir, { withFileTypes: true }) + const ret = [] + for (const val of files) { + if (val.isFile()) continue + const tmp = { + name: val.name, + path: `../../${this.dir}/${val.name}`, + } + + try { + if (await fs.stat(`${this.dir}/${val.name}/index.js`)) { + tmp.path = `${tmp.path}/index.js` + ret.push(tmp) + continue + } + } catch (err) {} + + const apps = await fs.readdir(`${this.dir}/${val.name}`, { withFileTypes: true }) + for (const app of apps) { + if (!app.isFile()) continue + if (!app.name.endsWith('.js')) continue + ret.push({ + name: `${tmp.name}/${app.name}`, + path: `${tmp.path}/${app.name}`, + }) + /** 监听热更新 */ + this.watch(val.name, app.name) + } + } + return ret + } + /** * 监听事件加载 * @param isRefresh 是否刷新 @@ -53,59 +86,75 @@ class PluginsLoader { logger.info('-----------') logger.info('加载插件中...') - let pluCount = 0 - + this.pluginCount = 0 const packageErr = [] - for (const file of files) try { - let apps = await import(file.path) - if (apps.apps) apps = { ...apps.apps } - lodash.forEach(apps, async (p, i) => { - if (!p?.prototype) return - pluCount++ - const plugin = new p - logger.debug(`载入插件 [${file.name}][${plugin.name}]`) - /** 执行初始化,返回 return 则跳过加载 */ - if (plugin.init && await plugin.init() == 'return') return - /** 初始化定时任务 */ - this.collectTask(plugin.task) - this.priority.push({ - class: p, - key: file.name, - name: plugin.name, - priority: plugin.priority - }) - if (plugin.handler) { - lodash.forEach(plugin.handler, ({ fn, key, priority }) => { - Handler.add({ - ns: plugin.namespace || file.name, - key, - self: plugin, - property: priority || plugin.priority || 500, - fn: plugin[fn] - }) - }) - } - }) - } catch (error) { - if (error.stack.includes('Cannot find package')) { - packageErr.push({ error, file }) - } else { - logger.error(`载入插件错误:${logger.red(file.name)}`) - logger.error(decodeURI(error.stack)) - } - } + const pluginArray = [] + for (const file of files) + pluginArray.push(this.importPlugin(file, packageErr)) + await Promise.allSettled(pluginArray) this.packageTips(packageErr) this.creatTask() logger.info(`加载定时任务[${this.task.length}个]`) - logger.info(`加载插件[${pluCount}个]`) + logger.info(`加载插件[${this.pluginCount}个]`) /** 优先级排序 */ this.priority = lodash.orderBy(this.priority, ['priority'], ['asc']) } + async importPlugin(file, packageErr) { + try { + let app = await import(file.path) + if (app.apps) app = { ...app.apps } + const pluginArray = [] + lodash.forEach(app, p => + pluginArray.push(this.loadPlugin(file, p)) + ) + for (const i of await Promise.allSettled(pluginArray)) + if (i?.status && i.status != 'fulfilled') { + logger.error(`加载插件错误:${logger.red(file.name)}`) + logger.error(decodeURI(i.reason)) + } + } catch (error) { + if (packageErr && error.stack.includes('Cannot find package')) { + packageErr.push({ error, file }) + } else { + logger.error(`加载插件错误:${logger.red(file.name)}`) + logger.error(decodeURI(error.stack)) + } + } + } + + async loadPlugin(file, p) { + if (!p?.prototype) return + this.pluginCount++ + const plugin = new p + logger.debug(`加载插件 [${file.name}][${plugin.name}]`) + /** 执行初始化,返回 return 则跳过加载 */ + if (plugin.init && await plugin.init() == 'return') return + /** 初始化定时任务 */ + this.collectTask(plugin.task) + this.priority.push({ + class: p, + key: file.name, + name: plugin.name, + priority: plugin.priority + }) + if (plugin.handler) { + lodash.forEach(plugin.handler, ({ fn, key, priority }) => { + Handler.add({ + ns: plugin.namespace || file.name, + key, + self: plugin, + property: priority || plugin.priority || 500, + fn: plugin[fn] + }) + }) + } + } + packageTips(packageErr) { if (!packageErr || packageErr.length <= 0) return logger.mark('--------插件载入错误--------') @@ -119,47 +168,6 @@ class PluginsLoader { logger.mark('---------------------') } - getPlugins () { - let ignore = ['index.js'] - let files = fs.readdirSync(this.dir, { withFileTypes: true }) - let ret = [] - for (let val of files) { - let filepath = '../../plugins/' + val.name - let tmp = { - name: val.name - } - if (val.isFile()) { - if (!val.name.endsWith('.js')) continue - if (ignore.includes(val.name)) continue - tmp.path = filepath - ret.push(tmp) - continue - } - - if (fs.existsSync(`${this.dir}/${val.name}/index.js`)) { - tmp.path = filepath + '/index.js' - ret.push(tmp) - continue - } - - let apps = fs.readdirSync(`${this.dir}/${val.name}`, { withFileTypes: true }) - for (let app of apps) { - if (!app.name.endsWith('.js')) continue - if (ignore.includes(app.name)) continue - - ret.push({ - name: `${val.name}/${app.name}`, - path: `../../plugins/${val.name}/${app.name}` - }) - - /** 监听热更新 */ - this.watch(val.name, app.name) - } - } - - return ret - } - /** * 处理事件 * @@ -744,132 +752,73 @@ class PluginsLoader { return true } - /** 监听热更新 */ - watch (dirName, appName) { - this.watchDir(dirName) - if (this.watcher[`${dirName}.${appName}`]) return - - let file = `./plugins/${dirName}/${appName}` - const watcher = chokidar.watch(file) - let key = `${dirName}/${appName}` - - /** 监听修改 */ - watcher.on('change', async path => { - logger.mark(`[修改插件][${dirName}][${appName}]`) - - let tmp = {} - try { - tmp = await import(`../../plugins/${dirName}/${appName}?${moment().format('x')}`) - } catch (error) { - logger.error(`载入插件错误:${logger.red(dirName + '/' + appName)}`) - logger.error(decodeURI(error.stack)) - return - } - - if (tmp.apps) tmp = { ...tmp.apps } - lodash.forEach(tmp, (p) => { - /* eslint-disable new-cap */ - let plugin = new p() - for (let i in this.priority) { - if (this.priority[i].key === key) { + async changePlugin(key) { + try { + let app = await import(`../../${this.dir}/${key}?${moment().format('x')}`) + if (app.apps) app = { ...app.apps } + lodash.forEach(app, p => { + const plugin = new p + for (const i in this.priority) + if (this.priority[i].key == key && this.priority[i].name == plugin.name) { this.priority[i].class = p this.priority[i].priority = plugin.priority } - } - - if (plugin.handler) { - lodash.forEach(plugin.handler, ({ fn, key, priority }) => { - Handler.add({ - ns: plugin.namespace || File.name, - key, - self: plugin, - property: priority || plugin.priority || 500, - fn: plugin[fn] - }) - }) - } }) - this.priority = lodash.orderBy(this.priority, ['priority'], ['asc']) + } catch (error) { + logger.error(`加载插件错误:${logger.red(key)}`) + logger.error(decodeURI(error.stack)) + } + } + + /** 监听热更新 */ + watch(dirName, appName) { + this.watchDir(dirName) + if (this.watcher[`${dirName}.${appName}`]) return + + const file = `./${this.dir}/${dirName}/${appName}` + const watcher = chokidar.watch(file) + const key = `${dirName}/${appName}` + + /** 监听修改 */ + watcher.on('change', path => { + logger.mark(`[修改插件][${dirName}][${appName}]`) + this.changePlugin(key) }) /** 监听删除 */ watcher.on('unlink', async path => { logger.mark(`[卸载插件][${dirName}][${appName}]`) - for (let i in this.priority) { - if (this.priority[i].key == key) { + /** 停止更新监听 */ + this.watcher[`${dirName}.${appName}`].removeAllListeners('change') + for (const i in this.priority) + if (this.priority[i].key == key) this.priority.splice(i, 1) - /** 停止更新监听 */ - this.watcher[`${dirName}.${appName}`].removeAllListeners('change') - break - } - } }) - this.watcher[`${dirName}.${appName}`] = watcher } /** 监听文件夹更新 */ - watchDir (dirName) { + watchDir(dirName) { if (this.watcher[dirName]) return - - let file = `./plugins/${dirName}/` - const watcher = chokidar.watch(file) - + const watcher = chokidar.watch(`./${this.dir}/${dirName}/`) /** 热更新 */ setTimeout(() => { /** 新增文件 */ watcher.on('add', async PluPath => { - let appName = path.basename(PluPath) + const appName = path.basename(PluPath) if (!appName.endsWith('.js')) return - if (!fs.existsSync(`${this.dir}/${dirName}/${appName}`)) return - - let key = `${dirName}/${appName}` - - this.watch(dirName, appName) - - /** 太快了延迟下 */ - await common.sleep(500) - logger.mark(`[新增插件][${dirName}][${appName}]`) - let tmp = {} - try { - tmp = await import(`../../plugins/${dirName}/${appName}?${moment().format('X')}`) - } catch (error) { - logger.error(`载入插件错误:${logger.red(dirName + '/' + appName)}`) - logger.error(decodeURI(error.stack)) - return - } - - if (tmp.apps) tmp = { ...tmp.apps } - - lodash.forEach(tmp, (p) => { - if (!p.prototype) { - logger.error(`[载入失败][${dirName}][${appName}] 格式错误已跳过`) - return - } - /* eslint-disable new-cap */ - let plugin = new p() - - for (let i in this.priority) { - if (this.priority[i].key == key) { - return - } - } - - this.priority.push({ - class: p, - key, - name: plugin.name, - priority: plugin.priority - }) + const key = `${dirName}/${appName}` + await this.importPlugin({ + name: key, + path: `../../${this.dir}/${key}?${moment().format('X')}`, }) - /** 优先级排序 */ this.priority = lodash.orderBy(this.priority, ['priority'], ['asc']) + this.watch(dirName, appName) }) - }, 500) - + }, 10000) this.watcher[dirName] = watcher } }