101 lines
3.2 KiB
TypeScript
101 lines
3.2 KiB
TypeScript
import { createDecorator, CyclicDependencyError, Disposable, Graph } from '@alilc/lowcode-shared';
|
|
import { type Plugin, type PluginContext } from './plugin';
|
|
import { BoostsManager } from './boosts';
|
|
import { IPackageManagementService } from '../package';
|
|
import { ISchemaService } from '../schema';
|
|
import { ICodeRuntimeService } from '../code-runtime';
|
|
import { IRuntimeIntlService } from '../intl';
|
|
import { IRuntimeUtilService } from '../util';
|
|
|
|
export interface IExtensionHostService {
|
|
readonly boostsManager: BoostsManager;
|
|
|
|
registerPlugin(plugin: Plugin | Plugin[]): Promise<void>;
|
|
|
|
getPlugin(name: string): Plugin | undefined;
|
|
}
|
|
|
|
export const IExtensionHostService = createDecorator<IExtensionHostService>('pluginManagementService');
|
|
|
|
export class ExtensionHostService extends Disposable implements IExtensionHostService {
|
|
boostsManager: BoostsManager;
|
|
|
|
private _activePlugins = new Set<string>();
|
|
private _pluginStore = new Map<string, Plugin>();
|
|
private _pluginDependencyGraph = new Graph<string>((name) => name);
|
|
private _pluginSetupContext: PluginContext;
|
|
|
|
constructor(
|
|
@IPackageManagementService packageManagementService: IPackageManagementService,
|
|
@ISchemaService schemaService: ISchemaService,
|
|
@ICodeRuntimeService codeRuntimeService: ICodeRuntimeService,
|
|
@IRuntimeIntlService runtimeIntlService: IRuntimeIntlService,
|
|
@IRuntimeUtilService runtimeUtilService: IRuntimeUtilService,
|
|
) {
|
|
super();
|
|
|
|
this.boostsManager = new BoostsManager(codeRuntimeService, runtimeIntlService, runtimeUtilService);
|
|
|
|
this._pluginSetupContext = {
|
|
globalState: new Map(),
|
|
boosts: this.boostsManager.toExpose(),
|
|
schema: schemaService,
|
|
packageManager: packageManagementService,
|
|
};
|
|
}
|
|
|
|
async registerPlugin(plugins: Plugin | Plugin[]) {
|
|
const items = (Array.isArray(plugins) ? plugins : [plugins]).filter(
|
|
(plugin) => !this._pluginStore.has(plugin.name),
|
|
);
|
|
|
|
for (const item of items) {
|
|
this._pluginStore.set(item.name, item);
|
|
}
|
|
|
|
await this._doRegisterPlugins(items);
|
|
}
|
|
|
|
private async _doRegisterPlugins(plugins: Plugin[]) {
|
|
for (const plugin of plugins) {
|
|
this._pluginDependencyGraph.lookupOrInsertNode(plugin.name);
|
|
|
|
if (plugin.dependsOn) {
|
|
for (const dependency of plugin.dependsOn) {
|
|
this._pluginDependencyGraph.insertEdge(plugin.name, dependency);
|
|
}
|
|
}
|
|
}
|
|
|
|
while (true) {
|
|
const roots = this._pluginDependencyGraph.roots();
|
|
|
|
if (roots.length === 0 || roots.every((node) => !this._pluginStore.has(node.data))) {
|
|
if (this._pluginDependencyGraph.isEmpty()) {
|
|
throw new CyclicDependencyError(this._pluginDependencyGraph);
|
|
}
|
|
break;
|
|
}
|
|
|
|
for (const { data } of roots) {
|
|
const plugin = this._pluginStore.get(data);
|
|
if (plugin) {
|
|
await this._doSetupPlugin(plugin);
|
|
this._pluginDependencyGraph.removeNode(plugin.name);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private async _doSetupPlugin(plugin: Plugin) {
|
|
if (this._activePlugins.has(plugin.name)) return;
|
|
|
|
this._addDispose(await plugin.setup(this._pluginSetupContext));
|
|
this._activePlugins.add(plugin.name);
|
|
}
|
|
|
|
getPlugin(name: string): Plugin | undefined {
|
|
return this._pluginStore.get(name);
|
|
}
|
|
}
|