Spaces:
Sleeping
Sleeping
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 |