Spaces:
Sleeping
Sleeping
| import chokidar from 'chokidar' | |
| import moment from 'moment' | |
| import fs from 'node:fs' | |
| import util from 'node:util' | |
| import lodash from 'lodash' | |
| import schedule from 'node-schedule' | |
| export default 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 | |
| } | |
| } | |