优化 插件异步加载
This commit is contained in:
parent
8a17b35e08
commit
b63598af98
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue