mirror of
https://github.com/clusterio/clusterio.git
synced 2026-11-11 23:04:52 +09:00
144 lines
4.4 KiB
TypeScript
144 lines
4.4 KiB
TypeScript
import "./style.css";
|
|
|
|
import React from "react";
|
|
import { createRoot } from "react-dom/client";
|
|
|
|
import * as lib from "@clusterio/lib";
|
|
|
|
import App from "./components/App";
|
|
import BaseWebPlugin, { InputComponent } from "./BaseWebPlugin";
|
|
import InputRole from "./components/InputRole";
|
|
import InputModPack from "./components/InputModPack";
|
|
import { Control, ControlConnector } from "./util/websocket";
|
|
|
|
const { ConsoleTransport, WebConsoleFormat, logger } = lib;
|
|
|
|
async function loadScript(url: string) {
|
|
let script = document.createElement("script");
|
|
script.src = url;
|
|
script.type = "text/javascript";
|
|
script.async = true;
|
|
|
|
let result = new Promise((resolve, reject) => {
|
|
script.onload = resolve;
|
|
script.onerror = reject;
|
|
});
|
|
|
|
document.head.appendChild(script);
|
|
return result;
|
|
}
|
|
|
|
async function loadPluginInfos(): Promise<lib.PluginWebpackEnvInfo[]> {
|
|
let response = await fetch(`${webRoot}api/plugins`);
|
|
let pluginList: lib.PluginWebApi[];
|
|
if (response.ok) {
|
|
pluginList = await response.json();
|
|
|
|
} else {
|
|
logger.error("Failed to get plugin data, running without plugins");
|
|
pluginList = [];
|
|
}
|
|
|
|
let pluginInfos: lib.PluginWebpackEnvInfo[] = [];
|
|
await __webpack_init_sharing__("default");
|
|
for (let meta of pluginList) {
|
|
if (meta.web.error) {
|
|
logger.error(`Failed to load plugin ${meta.name}: ${meta.web.error}`);
|
|
continue;
|
|
}
|
|
try {
|
|
await loadScript(`${webRoot}${meta.web.main}`);
|
|
let container: any = (window as { [key: string]: any })[`plugin_${meta.name}`];
|
|
if (!container) {
|
|
throw new Error(`Plugin did not expose its container via plugin_${meta.name}`);
|
|
}
|
|
await container.init(__webpack_share_scopes__.default);
|
|
let pluginInfo = (await container.get("./"))().plugin;
|
|
pluginInfo.container = container;
|
|
pluginInfo.package = (await container.get("./package.json"))();
|
|
pluginInfo.enabled = meta.enabled;
|
|
pluginInfos.push(pluginInfo);
|
|
|
|
} catch (err: any) {
|
|
pluginInfos.push({
|
|
name: meta.name,
|
|
title: meta.name,
|
|
enabled: false,
|
|
error: err.message,
|
|
});
|
|
logger.error(`Failed to load plugin info for ${meta.name}`);
|
|
if (err.stack) {
|
|
logger.error(err.stack);
|
|
}
|
|
}
|
|
}
|
|
return pluginInfos;
|
|
}
|
|
|
|
async function loadPlugins(pluginInfos: lib.PluginWebpackEnvInfo[], control: Control) {
|
|
let plugins = new Map<string, BaseWebPlugin>();
|
|
for (let pluginInfo of pluginInfos) {
|
|
if (!pluginInfo.enabled) {
|
|
continue;
|
|
}
|
|
try {
|
|
let WebPluginClass = BaseWebPlugin;
|
|
if (pluginInfo.webEntrypoint) {
|
|
let webModule = (await pluginInfo.container.get(pluginInfo.webEntrypoint))();
|
|
if (!webModule.WebPlugin) {
|
|
pluginInfo.error = "Plugin webEntrypoint does not export WebPlugin class";
|
|
throw new Error(pluginInfo.error);
|
|
}
|
|
WebPluginClass = webModule.WebPlugin;
|
|
}
|
|
|
|
let plugin = new WebPluginClass(pluginInfo.container, pluginInfo.package, pluginInfo, control, logger);
|
|
await plugin.init();
|
|
plugins.set(pluginInfo.name, plugin);
|
|
|
|
} catch (err: any) {
|
|
pluginInfo.error = `Error loading plugin: ${err.message}`;
|
|
logger.error(`Failed to load plugin ${pluginInfo.name}`);
|
|
if (err.stack) {
|
|
logger.error(err.stack);
|
|
}
|
|
}
|
|
}
|
|
return plugins;
|
|
}
|
|
|
|
function inputComponentsFromPlugins(plugins: Map<string, BaseWebPlugin>) {
|
|
const inputComponents: Record<string, InputComponent> = {
|
|
"mod_pack": InputModPack,
|
|
"role": InputRole,
|
|
};
|
|
for (let [pluginName, plugin] of plugins) {
|
|
for (const [name, Component] of Object.entries(plugin.inputComponents)) {
|
|
if (Object.prototype.hasOwnProperty.call(inputComponents, name)) {
|
|
lib.logger.warn(`Plugin ${pluginName} is redefining config inputComponent ${name}`);
|
|
}
|
|
inputComponents[name] = Component;
|
|
}
|
|
}
|
|
return inputComponents;
|
|
}
|
|
|
|
export default async function bootstrap() {
|
|
logger.add(new ConsoleTransport({
|
|
level: "verbose",
|
|
format: new WebConsoleFormat(),
|
|
}));
|
|
let pluginInfos = await loadPluginInfos();
|
|
lib.registerPluginMessages(pluginInfos);
|
|
lib.addPluginConfigFields(pluginInfos);
|
|
|
|
let wsUrl = new URL(webRoot, document.location.href);
|
|
let controlConnector = new ControlConnector(wsUrl.href, 120, undefined);
|
|
let control = new Control(controlConnector, new Map(pluginInfos.map(p => [p.name, p])));
|
|
control.plugins = await loadPlugins(pluginInfos, control);
|
|
control.inputComponents = inputComponentsFromPlugins(control.plugins);
|
|
|
|
const root = createRoot(document.getElementById("root") as HTMLDivElement);
|
|
root.render(<App control={control}/>);
|
|
}
|