| 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 |
| } |
| |
| 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) => { |
| |
| 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 |