From 7c016f6ba6de5951ed3aa4807c4496e6b2b5f5b6 Mon Sep 17 00:00:00 2001 From: MiaoWoo Date: Tue, 23 Jun 2020 15:51:55 +0800 Subject: [PATCH] feat: optimize plugin scan & load Signed-off-by: MiaoWoo --- packages/api/src/interfaces/plugin.ts | 61 +++++++++++++++---- packages/ployfill/package.json | 3 +- packages/ployfill/src/index.ts | 2 +- packages/plugin/src/loader/basic-loader.ts | 11 +++- packages/plugin/src/loader/ioc-loader.ts | 9 ++- packages/plugin/src/manager.ts | 35 ++++++----- packages/plugin/src/scanner/file-scanner.ts | 17 ++++-- .../src/internal/scanner/mysql-scanner.ts | 32 ++++++---- 8 files changed, 120 insertions(+), 50 deletions(-) diff --git a/packages/api/src/interfaces/plugin.ts b/packages/api/src/interfaces/plugin.ts index 60780fa9..c02a35a0 100644 --- a/packages/api/src/interfaces/plugin.ts +++ b/packages/api/src/interfaces/plugin.ts @@ -30,6 +30,44 @@ export namespace plugin { getPlugins(): Map } export const PluginScanner = Symbol("PluginScanner") + /** + * 插件加载元信息 + */ + export interface PluginLoadMetadata { + /** + * 插件加载类型 + */ + type: string + /** + * 插件名称 + */ + name?: string + /** + * 插件文件 + */ + file?: string + /** + * 插件实例 + */ + instance?: any + /** + * 插件元信息 + */ + metadata?: PluginMetadata + /** + * 插件扫描器 + */ + scanner: PluginScanner + /** + * 插件加载器 + */ + loader?: PluginLoader + /** + * 是否已加载 + */ + loaded?: boolean + [key: string]: any + } /** * 插件扫描器 */ @@ -39,15 +77,20 @@ export namespace plugin { */ type: string /** - * 扫描插件 返回插件加载列表 + * 扫描插件目录 返回插件加载元信息列表 * @param target 扫描目标 */ - scan(target: any): string[] + scan(target: any): PluginLoadMetadata[] + /** + * 读取插件 返回插件加载元信息 + * @param target + */ + read(target: any): PluginLoadMetadata /** * 加载扫描到的目标 * @param target 加载目标 */ - load(target: any): any + load(target: PluginLoadMetadata): PluginLoadMetadata } export const PluginLoader = Symbol("PluginLoader") /** @@ -63,12 +106,12 @@ export namespace plugin { * @param target 加载目标 * @param result 扫描器加载的结果 */ - require(target: any, result: any): PluginMetadata + require(loadMetadata: PluginLoadMetadata): PluginLoadMetadata /** * 构建插件 * @param metadata 插件元信息 */ - build(metadata: any): Plugin + build(metadata: PluginMetadata): Plugin /** * Load 阶段 * @param plugin 插件 @@ -133,13 +176,9 @@ export namespace plugin { */ type?: string /** - * 插件扫描器 + * 插件加载信息 */ - scanner?: PluginScanner - /** - * 插件加载器 - */ - loader?: PluginLoader + loadMetadata?: PluginLoadMetadata /** * 插件本体 */ diff --git a/packages/ployfill/package.json b/packages/ployfill/package.json index 04ab0195..3cd5cb2d 100644 --- a/packages/ployfill/package.json +++ b/packages/ployfill/package.json @@ -15,6 +15,7 @@ }, "dependencies": { "@ccms/i18n": "^0.8.0", + "@ccms/nodejs": "^0.8.0", "@ccms/nashorn": "^0.8.0", "core-js": "^3.6.5" }, @@ -23,4 +24,4 @@ "rimraf": "^3.0.2", "typescript": "^3.9.2" } -} +} \ No newline at end of file diff --git a/packages/ployfill/src/index.ts b/packages/ployfill/src/index.ts index 90588190..3260df29 100644 --- a/packages/ployfill/src/index.ts +++ b/packages/ployfill/src/index.ts @@ -1,5 +1,5 @@ /// - +import '@ccms/nodejs' import i18n from '@ccms/i18n' let ployfillStartTime = new Date().getTime() i18n.initialize() diff --git a/packages/plugin/src/loader/basic-loader.ts b/packages/plugin/src/loader/basic-loader.ts index 33863174..80b7645c 100644 --- a/packages/plugin/src/loader/basic-loader.ts +++ b/packages/plugin/src/loader/basic-loader.ts @@ -10,9 +10,14 @@ export class BasicLoader implements plugin.PluginLoader { constructor() { this.pluginRequireMap = new Map() } - require(target: any, result: any) { - this.pluginRequireMap.set(target.toString(), result) - return result + require(loadMetadata: plugin.PluginLoadMetadata) { + let metadata = loadMetadata.instance.description + if (metadata && metadata.type == this.type) { + loadMetadata.metadata = metadata + loadMetadata.loaded = true + this.pluginRequireMap.set(metadata.source.toString(), loadMetadata.instance) + } + return loadMetadata } build(metadata: plugin.PluginMetadata) { return this.pluginRequireMap.get(metadata.source.toString()) diff --git a/packages/plugin/src/loader/ioc-loader.ts b/packages/plugin/src/loader/ioc-loader.ts index 38795d9e..1344dbd5 100644 --- a/packages/plugin/src/loader/ioc-loader.ts +++ b/packages/plugin/src/loader/ioc-loader.ts @@ -18,8 +18,13 @@ export class IocLoader implements plugin.PluginLoader { this.pluginMetadataMap = getPluginSources() } - require(target: any, result: any) { - return this.pluginMetadataMap.get(target.toString()) + require(loadMetadata: plugin.PluginLoadMetadata) { + let metadata = this.pluginMetadataMap.get(loadMetadata.file.toString()) + if (metadata && metadata.type == this.type) { + loadMetadata.metadata = metadata + loadMetadata.loaded = true + } + return loadMetadata } build(metadata: plugin.PluginMetadata) { diff --git a/packages/plugin/src/manager.ts b/packages/plugin/src/manager.ts index 090cf78f..1508a25a 100644 --- a/packages/plugin/src/manager.ts +++ b/packages/plugin/src/manager.ts @@ -64,8 +64,13 @@ export class PluginManagerImpl implements plugin.PluginManager { this.initialize() for (const [, scanner] of this.sacnnerMap) { try { - scanner.scan(folder).forEach(file => { - this.loadPlugin(file, scanner) + scanner.scan(folder).forEach(loadMetadata => { + try { + this.loadPlugin(scanner.load(loadMetadata)) + } catch (error) { + console.error(`plugin scanner ${scanner.type} load ${loadMetadata.name} occurred error ${error}`) + console.ex(error) + } }) } catch (error) { console.error(`plugin scanner ${scanner.type} occurred error ${error}`) @@ -89,28 +94,28 @@ export class PluginManagerImpl implements plugin.PluginManager { ext() this.runCatch(plugin, stage) this.runCatch(plugin, `${this.serverType}${stage}`) - plugin.description.loader[stage](plugin) + plugin.description.loadMetadata.loader[stage](plugin) } catch (ex) { console.i18n("ms.plugin.manager.stage.exec.error", { plugin: plugin.description.name, executor: stage, error: ex }) } } - private loadPlugin(file: string, scanner: plugin.PluginScanner) { + private loadPlugin(loadMetadata: plugin.PluginLoadMetadata) { try { - let requireInstance = scanner.load(file) for (const [, loader] of this.loaderMap) { - let metadata = loader.require(file, requireInstance) - if (metadata && metadata.source && metadata.name) { - metadata.loader = loader + if (loader.require(loadMetadata).loaded) { + loadMetadata.loader = loader + let metadata = loadMetadata.metadata this.metadataMap.set(metadata.name, metadata) + metadata.loadMetadata = loadMetadata return metadata } } } catch (error) { - console.i18n("ms.plugin.manager.initialize.error", { name: file, ex: error }) + console.i18n("ms.plugin.manager.initialize.error", { name: loadMetadata.file, ex: error }) console.ex(error) } - console.console(`§efile §b${file} §ccan't load metadata. §eskip load!`) + console.console(`§6scanner: §b${loadMetadata.scanner.type} §ccan\'t load §6file §b${loadMetadata.file}. §eskip!`) } /** @@ -120,8 +125,8 @@ export class PluginManagerImpl implements plugin.PluginManager { loadFromFile(file: string, scanner = this.sacnnerMap.get('file')): plugin.Plugin { if (!file) { throw new Error('plugin file can\'t be null!') } if (!scanner) { throw new Error('plugin scanner can\'t be null!') } - let metadata = this.loadPlugin(file, scanner) - let plugin = metadata.loader.build(metadata) + let metadata = this.loadPlugin(scanner.read(file)) + let plugin = metadata.loadMetadata.loader.build(metadata) this.load(plugin) this.enable(plugin) return plugin @@ -157,7 +162,7 @@ export class PluginManagerImpl implements plugin.PluginManager { reload(...args: any[]): void { this.checkAndGet(args[0]).forEach((pl: plugin.Plugin) => { this.disable(pl) - this.loadFromFile(pl.description.source, pl.description.scanner) + this.loadFromFile(pl.description.source, pl.description.loadMetadata.scanner) }) } @@ -206,7 +211,9 @@ export class PluginManagerImpl implements plugin.PluginManager { console.error(`§4无法加载插件 §c${metadata.name} §4请检查 §c${metadata.type} §4加载器是否正常启用!`) continue } - (pluginInstance = this.loaderMap.get(metadata.type).build(metadata)) && this.instanceMap.set(metadata.name, pluginInstance) + pluginInstance = this.loaderMap.get(metadata.type).build(metadata) + if (!pluginInstance) { console.error(`§4加载器 §c${metadata.type} §4加载插件 §c${metadata.name} §4失败!`); continue } + this.instanceMap.set(metadata.name, pluginInstance) } } diff --git a/packages/plugin/src/scanner/file-scanner.ts b/packages/plugin/src/scanner/file-scanner.ts index 2c1a680d..17f2ba79 100644 --- a/packages/plugin/src/scanner/file-scanner.ts +++ b/packages/plugin/src/scanner/file-scanner.ts @@ -6,15 +6,20 @@ import { provideSingletonNamed } from "@ccms/container" export class JSFileScanner implements plugin.PluginScanner { type: string = 'file' - scan(target: any): string[] { - return this.scanFolder(fs.concat(root, target)) + scan(target: any): plugin.PluginLoadMetadata[] { + return this.scanFolder(fs.concat(root, target)).map((file) => this.read(file)) } - load(file: string) { - if (typeof file === "string") { return } - this.updatePlugin(file) + read(file: any): plugin.PluginLoadMetadata { + return { file, type: this.type, scanner: this, loaded: false } + } + + load(metadata: plugin.PluginLoadMetadata): plugin.PluginLoadMetadata { + if (metadata.type !== this.type) { return } + this.updatePlugin(metadata.file) //@ts-ignore - return require(file.toString(), { cache: false }) + metadata.instance = require(metadata.file.toString(), { cache: false }) + return metadata } private scanFolder(folder: any): string[] { diff --git a/packages/spring/src/internal/scanner/mysql-scanner.ts b/packages/spring/src/internal/scanner/mysql-scanner.ts index 3c4cf467..e1b960f6 100644 --- a/packages/spring/src/internal/scanner/mysql-scanner.ts +++ b/packages/spring/src/internal/scanner/mysql-scanner.ts @@ -4,29 +4,37 @@ import { provideSingleton, inject } from '@ccms/container' import * as fs from '@ccms/common/dist/fs' +interface MySQLPlugin { + name: string + source: string +} + @provideSingleton(plugin.PluginScanner) export class MySQLScanner implements plugin.PluginScanner { type: string = "mysql" + private cacheDir = 'mysql-plugin-cache' private target: string @inject(database.DataBaseManager) private databaseManager: DataBaseManager - scan(target: any): string[] { + scan(target: any): plugin.PluginLoadMetadata[] { this.target = target - let plugins = this.databaseManager.getMainDatabase().query<{ name: string }>(`SELECT name FROM ${this.target} WHERE LENGTH(source) != 0 AND deleted = 0`) - return plugins.map(p => `mysql:${p.name}`) + let plugins = this.databaseManager.getMainDatabase().query(`SELECT name FROM ${this.target} WHERE LENGTH(source) != 0 AND deleted = 0`) + return plugins.map(p => this.read(p)) } - load(target: any) { - if (typeof target !== "string" || !target.startsWith('mysql:')) { return } - let name = target.split("mysql:")[1] - if (!name) { console.warn(`[PluginScanner][mysql] plugin name can't be null!`); return } - var plugin: any = this.databaseManager.getMainDatabase().query<{ source: string }>(`SELECT source FROM ${this.target} WHERE name = ? AND deleted = 0`, name) - if (plugin.length == 0) { console.warn(`[PluginScanner][mysql] plugin ${target} not found at mysql database...`); return } - let temp = fs.concat(root, 'mysql-plugin-cache', target, `${plugin[0]}.js`) - base.save(temp, plugin[0].source) + read(mysqlPlugin: MySQLPlugin): plugin.PluginLoadMetadata { + return { name: mysqlPlugin.name, file: fs.concat(root, this.cacheDir, `${mysqlPlugin.name}.js`), type: this.type, mysqlPlugin, scanner: this } + } + load(metadata: plugin.PluginLoadMetadata) { + if (metadata.type !== this.type) { return } + var plugin: any = this.databaseManager.getMainDatabase().query(`SELECT source FROM ${this.target} WHERE name = ? AND deleted = 0`, metadata.name) + if (plugin.length == 0) { throw new Error(`[MySQLScanner] plugin ${metadata.name} not found at mysql database...`) }; plugin = plugin[0] + if (!plugin.source) { throw new Error(`[MySQLScanner] plugin ${metadata.name} source can\'t be undefiend or empty...`) } + base.save(metadata.file, plugin.source) //@ts-ignore - return require(temp, { cache: false }) + metadata.instance = require(metadata.file, { cache: false }) + return metadata } }