优化 插件异步加载

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 util from 'node:util'
import fs from 'node:fs' import fs from 'node:fs/promises'
import lodash from 'lodash' import lodash from 'lodash'
import cfg from '../config/config.js' import cfg from '../config/config.js'
import plugin from './plugin.js' import plugin from './plugin.js'
@ -26,7 +26,7 @@ class PluginsLoader {
this.priority = [] this.priority = []
this.handler = {} this.handler = {}
this.task = [] this.task = []
this.dir = './plugins' this.dir = 'plugins'
/** 命令冷却cd */ /** 命令冷却cd */
this.groupGlobalCD = {} this.groupGlobalCD = {}
@ -41,6 +41,39 @@ class PluginsLoader {
this.srReg = /^#?(\*|星铁|星轨|穹轨|星穹|崩铁|星穹铁道|崩坏星穹铁道|铁道)+/ 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 是否刷新 * @param isRefresh 是否刷新
@ -53,59 +86,75 @@ class PluginsLoader {
logger.info('-----------') logger.info('-----------')
logger.info('加载插件中...') logger.info('加载插件中...')
let pluCount = 0 this.pluginCount = 0
const packageErr = [] 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 = []
if (!p?.prototype) return for (const file of files)
pluCount++ pluginArray.push(this.importPlugin(file, packageErr))
const plugin = new p await Promise.allSettled(pluginArray)
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))
}
}
this.packageTips(packageErr) this.packageTips(packageErr)
this.creatTask() this.creatTask()
logger.info(`加载定时任务[${this.task.length}个]`) logger.info(`加载定时任务[${this.task.length}个]`)
logger.info(`加载插件[${pluCount}个]`) logger.info(`加载插件[${this.pluginCount}个]`)
/** 优先级排序 */ /** 优先级排序 */
this.priority = lodash.orderBy(this.priority, ['priority'], ['asc']) 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) { packageTips(packageErr) {
if (!packageErr || packageErr.length <= 0) return if (!packageErr || packageErr.length <= 0) return
logger.mark('--------插件载入错误--------') logger.mark('--------插件载入错误--------')
@ -119,47 +168,6 @@ class PluginsLoader {
logger.mark('---------------------') 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 return true
} }
/** 监听热更新 */ async changePlugin(key) {
watch (dirName, appName) { try {
this.watchDir(dirName) let app = await import(`../../${this.dir}/${key}?${moment().format('x')}`)
if (this.watcher[`${dirName}.${appName}`]) return if (app.apps) app = { ...app.apps }
lodash.forEach(app, p => {
let file = `./plugins/${dirName}/${appName}` const plugin = new p
const watcher = chokidar.watch(file) for (const i in this.priority)
let key = `${dirName}/${appName}` if (this.priority[i].key == key && this.priority[i].name == plugin.name) {
/** 监听修改 */
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) {
this.priority[i].class = p this.priority[i].class = p
this.priority[i].priority = plugin.priority 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.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 => { watcher.on('unlink', async path => {
logger.mark(`[卸载插件][${dirName}][${appName}]`) 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.priority.splice(i, 1)
/** 停止更新监听 */
this.watcher[`${dirName}.${appName}`].removeAllListeners('change')
break
}
}
}) })
this.watcher[`${dirName}.${appName}`] = watcher this.watcher[`${dirName}.${appName}`] = watcher
} }
/** 监听文件夹更新 */ /** 监听文件夹更新 */
watchDir (dirName) { watchDir(dirName) {
if (this.watcher[dirName]) return if (this.watcher[dirName]) return
const watcher = chokidar.watch(`./${this.dir}/${dirName}/`)
let file = `./plugins/${dirName}/`
const watcher = chokidar.watch(file)
/** 热更新 */ /** 热更新 */
setTimeout(() => { setTimeout(() => {
/** 新增文件 */ /** 新增文件 */
watcher.on('add', async PluPath => { watcher.on('add', async PluPath => {
let appName = path.basename(PluPath) const appName = path.basename(PluPath)
if (!appName.endsWith('.js')) return 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}]`) logger.mark(`[新增插件][${dirName}][${appName}]`)
let tmp = {} const key = `${dirName}/${appName}`
try { await this.importPlugin({
tmp = await import(`../../plugins/${dirName}/${appName}?${moment().format('X')}`) name: key,
} catch (error) { path: `../../${this.dir}/${key}?${moment().format('X')}`,
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
})
}) })
/** 优先级排序 */ /** 优先级排序 */
this.priority = lodash.orderBy(this.priority, ['priority'], ['asc']) this.priority = lodash.orderBy(this.priority, ['priority'], ['asc'])
this.watch(dirName, appName)
}) })
}, 500) }, 10000)
this.watcher[dirName] = watcher this.watcher[dirName] = watcher
} }
} }