优化 插件异步加载
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