优化 插件异步加载

This commit is contained in:
🌌 2024-03-11 09:25:43 +08:00
parent 8a17b35e08
commit b63598af98
1 changed files with 132 additions and 183 deletions

View File

@ -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,18 +86,52 @@ 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) => {
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(`加载插件[${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
pluCount++
this.pluginCount++
const plugin = new p
logger.debug(`载入插件 [${file.name}][${plugin.name}]`)
logger.debug(`载插件 [${file.name}][${plugin.name}]`)
/** 执行初始化,返回 return 则跳过加载 */
if (plugin.init && await plugin.init() == 'return') return
/** 初始化定时任务 */
@ -86,24 +153,6 @@ class PluginsLoader {
})
})
}
})
} 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))
}
}
this.packageTips(packageErr)
this.creatTask()
logger.info(`加载定时任务[${this.task.length}个]`)
logger.info(`加载插件[${pluCount}个]`)
/** 优先级排序 */
this.priority = lodash.orderBy(this.priority, ['priority'], ['asc'])
}
packageTips(packageErr) {
@ -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
}
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
}
})
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
let file = `./plugins/${dirName}/${appName}`
const file = `./${this.dir}/${dirName}/${appName}`
const watcher = chokidar.watch(file)
let key = `${dirName}/${appName}`
const key = `${dirName}/${appName}`
/** 监听修改 */
watcher.on('change', async path => {
watcher.on('change', 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) {
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'])
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.priority.splice(i, 1)
/** 停止更新监听 */
this.watcher[`${dirName}.${appName}`].removeAllListeners('change')
break
}
}
for (const i in this.priority)
if (this.priority[i].key == key)
this.priority.splice(i, 1)
})
this.watcher[`${dirName}.${appName}`] = watcher
}
/** 监听文件夹更新 */
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
}
}