GT-Manual / lib /loader /loader.js
wuliya's picture
upload
072e993
import chokidar from 'chokidar'
import moment from 'moment'
import fs from 'node:fs'
import lodash from 'lodash'
class PluginsLoader {
constructor () {
this.priority = []
this.task = []
this.watcher = {}
this.dir = './plugins'
}
async pluginsLoader () {
const files = this.getPlugins()
logger.info('加载插件中..')
let pluCount = 0
let packageErr = []
for (let File of files) {
try {
let tmp = await import(File.path)
if (tmp.apps) tmp = { ...tmp.apps }
let isAdd = false
lodash.forEach(tmp, (p, i) => {
if (!p.prototype) {
return
}
/* eslint-disable new-cap */
let plugin = new p()
if (!plugin.priority) return
isAdd = true
logger.debug(`载入插件 [${plugin.name}]`)
/** 执行初始化 */
this.runInit(plugin)
/** 初始化定时任务 */
this.collectTask(plugin.task)
this.priority.push({
class: p,
key: File.name,
self: plugin,
name: plugin.name,
protocol: plugin.protocol || 'http',
priority: plugin.priority,
rule: plugin.rule
})
})
if (isAdd) pluCount++
} catch (error) {
if (error.stack.includes('Cannot find package')) {
packageErr.push({ error, File })
} else {
logger.error(`载入插件错误:${File.name}`)
logger.error(decodeURI(error.stack))
}
}
}
this.packageTips(packageErr)
this.creatTask()
this.priority = lodash.orderBy(this.priority, ['priority'], ['asc'])
logger.info(`加载定时任务[${this.task.length}个]`)
logger.info(`加载插件完成[${pluCount}个]`)
logger.info(`-----------`)
}
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'
tmp.name = val.name + '/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}`
})
continue
}
}
/** 监听热更新 */
ret.forEach(v => {
this.watch(v.name, v.path)
})
return ret
}
async runInit (plugin) {
plugin.init && plugin.init()
}
packageTips (packageErr) {
if (!packageErr || packageErr.length <= 0) return
logger.mark('--------插件载入错误--------')
packageErr.forEach(v => {
let pack = v.error.stack.match(/'(.+?)'/g)[0].replace(/'/g, '')
logger.mark(`${v.File.name} 缺少依赖:${logger.red(pack)}`)
logger.mark(`请执行安装依赖命令:${logger.red('pnpm add ' + pack + ' -w')}`)
})
logger.mark('---------------------')
}
/** 收集定时任务 */
collectTask (task) {
if (Array.isArray(task)) {
task.forEach((val) => {
if (!val.cron) return
if (!val.name) throw new Error('插件任务名称错误')
this.task.push(val)
})
} else {
if (task.fnc && task.cron) {
if (!task.name) throw new Error('插件任务名称错误')
this.task.push(task)
}
}
}
/** 创建定时任务 */
creatTask () {
this.task.forEach((val) => {
val.job = schedule.scheduleJob(val.cron, async () => {
try {
if (val.log === true) {
logger.mark(`开始定时任务:${val.name}`)
}
let res = val.fnc()
if (util.types.isPromise(res)) res = await res
if (val.log === true) {
logger.mark(`定时任务完成:${val.name}`)
}
} catch (error) {
logger.error(`定时任务报错:${val.name}`)
logger.error(error)
}
})
})
}
/** 监听热更新 */
watch (appName, appPath) {
if (this.watcher[`${appName}`]) return
let file = `./plugins/${appName}`
const watcher = chokidar.watch(file)
let key = appName
/** 监听修改 */
watcher.on('change', async path => {
logger.mark(`[修改插件][${appName}]`)
let tmp = {}
try {
tmp = await import(`${appPath}?${moment().format('x')}`)
} catch (error) {
logger.error(`载入插件错误:${logger.red(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].rule = plugin.rule
}
}
})
})
this.watcher[`${appName}`] = watcher
}
}
export default PluginsLoader