/** * This file is part of the NocoBase (R) project. * Copyright (c) 2020-2024 NocoBase Co., Ltd. * Authors: NocoBase Team. * * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License. * For more information, please refer to: https://www.nocobase.com/agreement. */ import type { Application } from './Application'; import type { Plugin } from './Plugin'; import { getPlugins } from './utils/remotePlugins'; export type PluginOptions = { name?: string; packageName?: string; config?: T }; export type PluginType = typeof Plugin | [typeof Plugin, PluginOptions]; export type PluginData = { name: string; packageName: string; version: string; url: string; type: 'local' | 'upload' | 'npm'; }; export class PluginManager { protected pluginInstances: Map = new Map(); protected pluginsAliases: Record = {}; private initPlugins: Promise; constructor( protected _plugins: PluginType[], protected loadRemotePlugins: boolean, protected app: Application, ) { this.app = app; this.initPlugins = this.init(_plugins); } /** * @internal */ async init(_plugins: PluginType[]) { await this.initStaticPlugins(_plugins); if (this.loadRemotePlugins) { await this.initRemotePlugins(); } } private async initStaticPlugins(_plugins: PluginType[] = []) { for await (const plugin of _plugins) { const pluginClass = Array.isArray(plugin) ? plugin[0] : plugin; const opts = Array.isArray(plugin) ? plugin[1] : undefined; await this.add(pluginClass, opts); } } private async initRemotePlugins() { const res = await this.app.apiClient.request({ url: 'pm:listEnabled' }); const pluginList: PluginData[] = res?.data?.data || []; const plugins = await getPlugins({ requirejs: this.app.requirejs, pluginData: pluginList, devDynamicImport: this.app.devDynamicImport, }); for await (const [name, pluginClass] of plugins) { const info = pluginList.find((item) => item.name === name); await this.add(pluginClass, info); } } async add(plugin: typeof Plugin, opts: PluginOptions = {}) { const instance = this.getInstance(plugin, opts); this.pluginInstances.set(plugin, instance); if (opts.name) { this.pluginsAliases[opts.name] = instance; } if (opts.packageName) { this.pluginsAliases[opts.packageName] = instance; } await instance.afterAdd(); } get(PluginClass: T): InstanceType; get(name: string): T; get(nameOrPluginClass: any) { if (typeof nameOrPluginClass === 'string') { return this.pluginsAliases[nameOrPluginClass]; } return this.pluginInstances.get(nameOrPluginClass.default || nameOrPluginClass); } private getInstance(plugin: typeof Plugin, opts?: T) { return new plugin(opts, this.app); } /** * @internal */ async load() { await this.initPlugins; for (const plugin of this.pluginInstances.values()) { await plugin.beforeLoad(); } for (const plugin of this.pluginInstances.values()) { await plugin.load(); } } }