diff --git a/.eslintrc.js b/.eslintrc.js index 084deb91..e3b50a27 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -22,6 +22,7 @@ module.exports = { "@typescript-eslint/no-explicit-any": "off", "@typescript-eslint/no-useless-constructor": "error", "@typescript-eslint/no-unused-vars": "off", + "@typescript-eslint/triple-slash-reference": "off", "prefer-const": "off", }, }, @@ -39,7 +40,11 @@ module.exports = { }, }, { - "files": ["packages/web_ui/src/**/*.ts?(x)", "packages/lib/browser.js", "plugins/*/web/**/*.ts?(x)"], + "files": [ + "packages/web_ui/src/**/*.ts?(x)", + "packages/lib/browser.ts", + "{packages,plugins}/*/web/**/*.ts?(x)", + ], "env": { "browser": true, }, @@ -315,7 +320,11 @@ module.exports = { "space-in-parens": ["error", "never"], "space-infix-ops": "off", "space-unary-ops": "error", - "spaced-comment": ["error", "always"], + "spaced-comment": ["error", "always", { + "line": { + "markers": ["/"], + }, + }], "strict": "error", "switch-colon-spacing": "error", "symbol-description": "error", diff --git a/packages/controller/.npmignore b/packages/controller/.npmignore index 6de001d8..ebd99907 100644 --- a/packages/controller/.npmignore +++ b/packages/controller/.npmignore @@ -1 +1,2 @@ webpack.config.js +dist/web/build diff --git a/packages/controller/src/routes.ts b/packages/controller/src/routes.ts index d5f9dfbe..45d5dbc4 100644 --- a/packages/controller/src/routes.ts +++ b/packages/controller/src/routes.ts @@ -93,20 +93,8 @@ async function getMetrics(req: Request, res: Response, next: any) { } -export type PluginWebApi = { - name: string; - version: string; - enabled: boolean; - loaded: boolean; - web: { - main: string; - error?: string; - }; - requirePath: string; -} - function getPlugins(req: Request, res: Response) { - let plugins: PluginWebApi[] = []; + let plugins: lib.PluginWebApi[] = []; for (let pluginInfo of req.app.locals.controller.pluginInfos) { let name = pluginInfo.name; let loaded = req.app.locals.controller.plugins.has(name); diff --git a/packages/controller/tsconfig.web.json b/packages/controller/tsconfig.web.json new file mode 100644 index 00000000..01d0c42f --- /dev/null +++ b/packages/controller/tsconfig.web.json @@ -0,0 +1,12 @@ +{ + "extends": "../../tsconfig.web.json", + "references": [ + { "path": "../lib" }, + { "path": "../web_ui/tsconfig.web.json" }, + ], + "include": [ "web/**/*.tsx", "web/**/*.ts", "package.json" ], + "compilerOptions": { + "rootDir": "..", + "outDir": "dist/web/build", + } +} diff --git a/packages/controller/web/index.html b/packages/controller/web/index.html index fde58715..5a5a8af0 100644 --- a/packages/controller/web/index.html +++ b/packages/controller/web/index.html @@ -17,22 +17,33 @@ margin: auto; margin-top: 1em; width: fit-content; + white-space: pre; } -
Loading web interface...
- + + diff --git a/packages/controller/web/index.jsx b/packages/controller/web/index.jsx deleted file mode 100644 index 64be3a06..00000000 --- a/packages/controller/web/index.jsx +++ /dev/null @@ -1,3 +0,0 @@ -import("@clusterio/web_ui").then(webUi => { - return webUi.bootstrap(); -}).catch(console.error); diff --git a/packages/controller/web/index.tsx b/packages/controller/web/index.tsx new file mode 100644 index 00000000..59e6ae18 --- /dev/null +++ b/packages/controller/web/index.tsx @@ -0,0 +1,5 @@ +declare function scriptError(err: any): void; +import("@clusterio/web_ui") + .then(webUi => webUi.bootstrap()) + .catch(scriptError) +; diff --git a/packages/controller/webpack.config.js b/packages/controller/webpack.config.js index cd0155c4..25182879 100644 --- a/packages/controller/webpack.config.js +++ b/packages/controller/webpack.config.js @@ -7,7 +7,7 @@ const common = require("@clusterio/web_ui/webpack.common"); module.exports = (env = {}) => merge(common(env), { context: __dirname, - entry: "./web/index.jsx", + entry: "./web/index.tsx", devServer: { contentBase: "./dist/web", }, diff --git a/packages/lib/.gitignore b/packages/lib/.gitignore new file mode 100644 index 00000000..34d90646 --- /dev/null +++ b/packages/lib/.gitignore @@ -0,0 +1 @@ +/src/data/message_validate.js diff --git a/plugins/global_chat/web/index.jsx b/packages/lib/.npmignore similarity index 100% rename from plugins/global_chat/web/index.jsx rename to packages/lib/.npmignore diff --git a/packages/lib/browser.js b/packages/lib/browser.js deleted file mode 100644 index ad4ccaf1..00000000 --- a/packages/lib/browser.js +++ /dev/null @@ -1,16 +0,0 @@ -export * from "./dist/src/config"; -export * from "./dist/src/data"; -export * from "./dist/src/errors"; -export * from "./dist/src/factorio/exchange_string"; -export * from "./dist/src/helpers"; -export * from "./dist/src/ini"; -export * from "./dist/src/link"; -export * from "./dist/src/logging"; -export * from "./dist/src/plugin"; -export * from "./dist/src/schema"; -export * from "./dist/src/users"; -export * from "./dist/src/subscriptions"; - -export { default as ExponentialBackoff } from "./dist/src/ExponentialBackoff"; -export { default as PlayerStats } from "./dist/src/PlayerStats"; -export { default as RateLimiter } from "./dist/src/RateLimiter"; diff --git a/packages/lib/browser.ts b/packages/lib/browser.ts new file mode 100644 index 00000000..18cbc868 --- /dev/null +++ b/packages/lib/browser.ts @@ -0,0 +1,17 @@ +export * from "./src/api"; +export * from "./src/config"; +export * from "./src/data"; +export * from "./src/errors"; +export * from "./src/factorio/exchange_string"; +export * from "./src/helpers"; +export * from "./src/ini"; +export * from "./src/link"; +export * from "./src/logging"; +export * from "./src/plugin"; +export * from "./src/schema"; +export * from "./src/users"; +export * from "./src/subscriptions"; + +export { default as ExponentialBackoff } from "./src/ExponentialBackoff"; +export { default as PlayerStats } from "./src/PlayerStats"; +export { default as RateLimiter } from "./src/RateLimiter"; diff --git a/packages/lib/index.ts b/packages/lib/index.ts index 428086e3..4740305f 100644 --- a/packages/lib/index.ts +++ b/packages/lib/index.ts @@ -3,6 +3,7 @@ * @module lib */ export * from "./build_mod"; +export * from "./src/api"; export * from "./src/command"; export * from "./src/config"; export * from "./src/data"; diff --git a/packages/lib/package.json b/packages/lib/package.json index 68b4bf52..dc981417 100644 --- a/packages/lib/package.json +++ b/packages/lib/package.json @@ -9,7 +9,7 @@ "compile_validator": "node scripts/compile_validator.js" }, "main": "dist/index.js", - "browser": "browser.js", + "browser": "browser.ts", "keywords": [ "factorio" ], diff --git a/packages/lib/scripts/compile_validator.js b/packages/lib/scripts/compile_validator.js index 61421e01..e2eafeb3 100644 --- a/packages/lib/scripts/compile_validator.js +++ b/packages/lib/scripts/compile_validator.js @@ -22,7 +22,11 @@ const ajv = new Ajv({ }); const validate = ajv.compile(Message.jsonSchema); let moduleCode = standaloneCode(ajv, validate); -fs.outputFileSync( - path.join(__dirname, "../dist/src/data/message_validate.js"), - `// generated by scripts/compile_validator.js\n// @ts-nocheck\n/* eslint-disable */\n${moduleCode}` -); +function write(output) { + fs.outputFileSync( + path.join(__dirname, output), + `// generated by scripts/compile_validator.js\n// @ts-nocheck\n/* eslint-disable */\n${moduleCode}` + ); +} +write("../dist/src/data/message_validate.js"); +write("../src/data/message_validate.js"); diff --git a/packages/lib/src/api.ts b/packages/lib/src/api.ts new file mode 100644 index 00000000..b9ecc4d0 --- /dev/null +++ b/packages/lib/src/api.ts @@ -0,0 +1,11 @@ +export type PluginWebApi = { + name: string; + version: string; + enabled: boolean; + loaded: boolean; + web: { + main: string; + error?: string; + }; + requirePath: string; +} diff --git a/packages/web_ui/global.d.ts b/packages/web_ui/global.d.ts index 2bb8472e..9ab63040 100644 --- a/packages/web_ui/global.d.ts +++ b/packages/web_ui/global.d.ts @@ -1,12 +1,10 @@ declare module "*.png" { - const value: import("react-native").ImageSourcePropType; + const value: string; export default value; } -declare interface Window { - webRoot: string; - staticRoot: string; -} +declare const webRoot: string; +declare const staticRoot: string; declare const __webpack_init_sharing__: (shareScope: string) => Promise; declare const __webpack_share_scopes__: { default: any }; diff --git a/packages/web_ui/package.json b/packages/web_ui/package.json index fec64b31..87f93b92 100644 --- a/packages/web_ui/package.json +++ b/packages/web_ui/package.json @@ -5,6 +5,7 @@ "license": "MIT", "version": "2.0.0-alpha.13", "browser": "src/index.ts", + "main": "dist/src/index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, diff --git a/packages/web_ui/src/BaseWebPlugin.ts b/packages/web_ui/src/BaseWebPlugin.ts index 755aac9b..5a3a9b98 100644 --- a/packages/web_ui/src/BaseWebPlugin.ts +++ b/packages/web_ui/src/BaseWebPlugin.ts @@ -1,5 +1,6 @@ import type React from "react"; -import type { AccountRole, Link, Logger, PluginWebpackEnvInfo } from "@clusterio/lib"; +import type { AccountRole, Logger, PluginWebpackEnvInfo } from "@clusterio/lib"; +import type { Control } from "./util/websocket"; /** * Plugin supplied login form @@ -19,7 +20,7 @@ export interface PluginLoginForm { * supplied the setToken function via its props which should be called * when an authentication token is aquired via this form. */ - Component: typeof React.Component; + Component: React.ComponentType<{ setToken(token: string): void }>; }; export type UserAccount = { @@ -77,11 +78,6 @@ export default class BaseWebPlugin { * Contents of the plugin's package.json file */ package: any; - /** - * Control link to the controller, not available until the - * connect event in onControllerConnectionEvent is signaled. - */ - control?: Link; /** * Logger for this plugin * @@ -152,6 +148,12 @@ export default class BaseWebPlugin { * The plugin's own info module */ public info: PluginWebpackEnvInfo, + /** + * Control link to the controller + * + * Not connected at the time init is invoked. + */ + public control: Control, logger: Logger, ) { this.package = packageData; diff --git a/packages/web_ui/src/bootstrap.tsx b/packages/web_ui/src/bootstrap.tsx index d97fb4ff..6db05d58 100644 --- a/packages/web_ui/src/bootstrap.tsx +++ b/packages/web_ui/src/bootstrap.tsx @@ -1,10 +1,9 @@ -import "./index.css"; +import "./style.css"; import React from "react"; import { createRoot } from "react-dom/client"; import * as lib from "@clusterio/lib"; -import type { PluginWebApi } from "@clusterio/controller/src/routes"; import App from "./components/App"; import BaseWebPlugin from "./BaseWebPlugin"; @@ -28,8 +27,8 @@ async function loadScript(url: string) { } async function loadPluginInfos(): Promise { - let response = await fetch(`${window.webRoot}api/plugins`); - let pluginList: PluginWebApi[]; + let response = await fetch(`${webRoot}api/plugins`); + let pluginList: lib.PluginWebApi[]; if (response.ok) { pluginList = await response.json(); @@ -46,7 +45,7 @@ async function loadPluginInfos(): Promise { continue; } try { - await loadScript(`${window.webRoot}${meta.web.main}`); + 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}`); @@ -68,8 +67,8 @@ async function loadPluginInfos(): Promise { return pluginInfos; } -async function loadPlugins(pluginInfos: lib.PluginWebpackEnvInfo[]) { - let plugins = new Map(); +async function loadPlugins(pluginInfos: lib.PluginWebpackEnvInfo[], control: Control) { + let plugins = new Map(); for (let pluginInfo of pluginInfos) { if (!pluginInfo.enabled) { continue; @@ -84,7 +83,7 @@ async function loadPlugins(pluginInfos: lib.PluginWebpackEnvInfo[]) { WebPluginClass = webModule.WebPlugin; } - let plugin = new WebPluginClass(pluginInfo.container, pluginInfo.package, pluginInfo, logger); + let plugin = new WebPluginClass(pluginInfo.container, pluginInfo.package, pluginInfo, control, logger); await plugin.init(); plugins.set(pluginInfo.name, plugin); @@ -107,11 +106,11 @@ export default async function bootstrap() { lib.registerPluginMessages(pluginInfos); lib.registerPluginConfigGroups(pluginInfos); lib.finalizeConfigs(); - let plugins = await loadPlugins(pluginInfos); - let wsUrl = new URL(window.webRoot, document.location.href); + let wsUrl = new URL(webRoot, document.location.href); let controlConnector = new ControlConnector(wsUrl.href, 120, undefined); - let control = new Control(controlConnector, plugins); + let control = new Control(controlConnector); + control.plugins = await loadPlugins(pluginInfos, control); const root = createRoot(document.getElementById("root") as HTMLDivElement); root.render(); diff --git a/packages/web_ui/src/components/App.tsx b/packages/web_ui/src/components/App.tsx index 85af41bf..02e9c726 100644 --- a/packages/web_ui/src/components/App.tsx +++ b/packages/web_ui/src/components/App.tsx @@ -109,7 +109,7 @@ export default function App(props: AppProps) { theme={{ algorithm: theme.darkAlgorithm }} > - + {page} diff --git a/packages/web_ui/src/components/HostsPage.tsx b/packages/web_ui/src/components/HostsPage.tsx index 8b70a713..23082644 100644 --- a/packages/web_ui/src/components/HostsPage.tsx +++ b/packages/web_ui/src/components/HostsPage.tsx @@ -3,7 +3,6 @@ import { useNavigate } from "react-router-dom"; import { Button, Form, InputNumber, Modal, Table, Tag, Typography } from "antd"; import CopyOutlined from "@ant-design/icons/lib/icons/CopyOutlined"; -import type { PluginWebApi } from "@clusterio/controller/src/routes"; import * as lib from "@clusterio/lib"; import { useAccount } from "../model/account"; @@ -23,10 +22,10 @@ function GenerateHostTokenButton() { let [token, setToken] = useState(null); let [hostId, setHostId] = useState(null); let [form] = Form.useForm(); - let [pluginList, setPluginList] = useState([]); + let [pluginList, setPluginList] = useState([]); useEffect(() => { (async () => { - let response = await fetch(`${window.webRoot}api/plugins`); + let response = await fetch(`${webRoot}api/plugins`); if (response.ok) { const plugins = await response.json(); setPluginList(plugins); diff --git a/packages/web_ui/src/components/ModPackViewPage.tsx b/packages/web_ui/src/components/ModPackViewPage.tsx index 39ad5c06..e75b705c 100644 --- a/packages/web_ui/src/components/ModPackViewPage.tsx +++ b/packages/web_ui/src/components/ModPackViewPage.tsx @@ -623,7 +623,7 @@ function useExportedAsset(modPack: lib.ModPack, asset: "settings"|"locale"): Map return; } - let response = await fetch(`${window.staticRoot}static/${assetFilename}`); + let response = await fetch(`${staticRoot}static/${assetFilename}`); if (response.ok) { let data = await response.json(); setAssetData(asset === "locale" ? new Map(data) : data); diff --git a/packages/web_ui/src/components/ModsPage.tsx b/packages/web_ui/src/components/ModsPage.tsx index a7272366..fb3133c4 100644 --- a/packages/web_ui/src/components/ModsPage.tsx +++ b/packages/web_ui/src/components/ModsPage.tsx @@ -140,7 +140,7 @@ export default function ModsPage() { control.send( new lib.ModDownloadRequest(mod.name, mod.version) ).then((streamId: string) => { - let url = new URL(window.webRoot, document.location.origin); + let url = new URL(webRoot, document.location.origin); url.pathname += `api/stream/${streamId}`; document.location = url.href; }).catch( @@ -175,7 +175,7 @@ export default function ModsPage() { headers={{ "X-Access-Token": control.connector.token||"", }} - action={`${window.webRoot}api/upload-mod`} + action={`${webRoot}api/upload-mod`} > ; diff --git a/packages/web_ui/src/components/PluginViewPage.tsx b/packages/web_ui/src/components/PluginViewPage.tsx index 429a5222..ef33ef45 100644 --- a/packages/web_ui/src/components/PluginViewPage.tsx +++ b/packages/web_ui/src/components/PluginViewPage.tsx @@ -2,7 +2,7 @@ import React, { useEffect, useContext, useState } from "react"; import { useParams } from "react-router-dom"; import { Alert, Descriptions, Spin } from "antd"; -import type { PluginWebApi } from "@clusterio/controller/src/routes"; +import type { PluginWebApi } from "@clusterio/lib"; import ControlContext from "./ControlContext"; import PageLayout from "./PageLayout"; @@ -16,7 +16,7 @@ export default function PluginViewPage() { useEffect(() => { (async () => { - let response = await fetch(`${window.webRoot}api/plugins`); + let response = await fetch(`${webRoot}api/plugins`); if (response.ok) { setPluginList(await response.json()); } else { diff --git a/packages/web_ui/src/components/PluginsPage.tsx b/packages/web_ui/src/components/PluginsPage.tsx index f47546b1..e3339f38 100644 --- a/packages/web_ui/src/components/PluginsPage.tsx +++ b/packages/web_ui/src/components/PluginsPage.tsx @@ -2,7 +2,7 @@ import React, { useContext, useEffect, useState } from "react"; import { useNavigate } from "react-router-dom"; import { Table } from "antd"; -import type { PluginWebApi } from "@clusterio/controller/src/routes"; +import type { PluginWebApi } from "@clusterio/lib"; import notify from "../util/notify"; import ControlContext from "./ControlContext"; @@ -18,7 +18,7 @@ export default function PluginsPage() { useEffect(() => { (async () => { - let response = await fetch(`${window.webRoot}api/plugins`); + let response = await fetch(`${webRoot}api/plugins`); if (response.ok) { setPluginList(await response.json()); } else { diff --git a/packages/web_ui/src/components/SavesList.tsx b/packages/web_ui/src/components/SavesList.tsx index bc707dd3..fe8d60e4 100644 --- a/packages/web_ui/src/components/SavesList.tsx +++ b/packages/web_ui/src/components/SavesList.tsx @@ -268,7 +268,7 @@ export default function SavesList(props: { instance: InstanceState }) { control.send( new lib.InstanceDownloadSaveRequest(props.instance.id!, save.name) ).then(streamId => { - let url = new URL(window.webRoot, document.location.href); + let url = new URL(webRoot, document.location.href); url.pathname += `api/stream/${streamId}`; document.location.assign(url); }).catch( @@ -339,7 +339,7 @@ export default function SavesList(props: { instance: InstanceState }) { instance_id: props.instance.id, }, showUploadList: false, - action: `${window.webRoot}api/upload-save`, + action: `${webRoot}api/upload-save`, onChange, }; diff --git a/packages/web_ui/src/index.ts b/packages/web_ui/src/index.ts index ff2e4fe8..03e4e6cd 100644 --- a/packages/web_ui/src/index.ts +++ b/packages/web_ui/src/index.ts @@ -1,6 +1,8 @@ +/// export { default as bootstrap } from "./bootstrap"; export { default as notify, notifyErrorHandler } from "./util/notify"; export { default as BaseWebPlugin } from "./BaseWebPlugin"; +export * from "./util/websocket"; export * from "./model/instance"; export * from "./model/host"; diff --git a/packages/web_ui/src/model/item_metadata.ts b/packages/web_ui/src/model/item_metadata.ts index 2651f60a..6ef564f3 100644 --- a/packages/web_ui/src/model/item_metadata.ts +++ b/packages/web_ui/src/model/item_metadata.ts @@ -7,6 +7,7 @@ type Metadata = { x: number; y: number; size: number; + localised_name?: any[]; }; let itemMetadataCache: Map | null = null; export function useItemMetadata() { @@ -22,7 +23,7 @@ export function useItemMetadata() { ) { return; } - let response = await fetch(`${window.staticRoot}static/${exportManifest.assets["item-metadata"]}`); + let response = await fetch(`${staticRoot}static/${exportManifest.assets["item-metadata"]}`); if (response.ok) { let data = await response.json(); itemMetadataCache = new Map(data); @@ -31,7 +32,7 @@ export function useItemMetadata() { for (let [name, meta] of itemMetadataCache) { style.sheet!.insertRule(`\ .item-${CSS.escape(name)} { - background-image: url("${window.staticRoot}static/${exportManifest.assets["item-spritesheet"]}"); + background-image: url("${staticRoot}static/${exportManifest.assets["item-spritesheet"]}"); background-repeat: no-repeat; background-position: -${meta.x}px -${meta.y}px; height: ${meta.size}px; diff --git a/packages/web_ui/src/model/locale.ts b/packages/web_ui/src/model/locale.ts index 86f229cd..cce48be2 100644 --- a/packages/web_ui/src/model/locale.ts +++ b/packages/web_ui/src/model/locale.ts @@ -12,7 +12,7 @@ export function useLocale(): Map { if (!exportManifest?.assets?.["locale"]) { return; } - let response = await fetch(`${window.staticRoot}static/${exportManifest.assets["locale"]}`); + let response = await fetch(`${staticRoot}static/${exportManifest.assets["locale"]}`); if (response.ok) { let data = await response.json(); localeCache = new Map(data); diff --git a/packages/web_ui/src/index.css b/packages/web_ui/src/style.css similarity index 100% rename from packages/web_ui/src/index.css rename to packages/web_ui/src/style.css diff --git a/packages/web_ui/src/util/websocket.ts b/packages/web_ui/src/util/websocket.ts index c2fbbdf1..a61386d3 100644 --- a/packages/web_ui/src/util/websocket.ts +++ b/packages/web_ui/src/util/websocket.ts @@ -52,6 +52,9 @@ export class Control extends lib.Link { /** Roles of the account this control link is connected as. */ accountRoles: lib.AccountRole[] | null = null; + /** Plugins loaded in the web interface */ + public plugins = new Map(); + /** Updates not handled by the subscription service */ accountUpdateHandlers: accountHandler[] = []; logHandlers: Map = new Map(); @@ -85,14 +88,9 @@ export class Control extends lib.Link { constructor( connector: ControlConnector, - public plugins: Map, ) { super(connector); - for (let plugin of plugins.values()) { - plugin.control = this; - } - this.connector.on("connect", data => { this.accountName = data.account.name; this.accountRoles = data.account.roles; diff --git a/packages/web_ui/tsconfig.json b/packages/web_ui/tsconfig.json deleted file mode 100644 index 9c7d97f9..00000000 --- a/packages/web_ui/tsconfig.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "include": [ "src/**/*.tsx", "src/**/*.ts", "package.json", "global.d.ts" ], - "compilerOptions": { - "jsx": "react", - "lib": ["dom", "es2023"], - "module": "commonjs", - "target": "es2022", - "rootDir": "./", - "outDir": "dist", - "composite": true, - "sourceMap": true, - "strict": true, - "alwaysStrict": true, - "noImplicitAny": true, - "esModuleInterop": true, - "skipLibCheck": true, - "resolveJsonModule": true, - "noEmitOnError": true - } -} diff --git a/packages/web_ui/tsconfig.web.json b/packages/web_ui/tsconfig.web.json new file mode 100644 index 00000000..3beee793 --- /dev/null +++ b/packages/web_ui/tsconfig.web.json @@ -0,0 +1,10 @@ +{ + "extends": "../../tsconfig.web.json", + "references": [ + { "path": "../lib" }, + ], + "include": [ "src/**/*.tsx", "src/**/*.ts", "package.json", "global.d.ts" ], + "compilerOptions": { + "outDir": "dist", + } +} diff --git a/packages/web_ui/webpack.common.js b/packages/web_ui/webpack.common.js index 3648acaf..e8a5d8b7 100644 --- a/packages/web_ui/webpack.common.js +++ b/packages/web_ui/webpack.common.js @@ -78,6 +78,10 @@ module.exports = (env = {}) => ({ exclude: /node_modules/, use: { loader: require.resolve("ts-loader"), + options: { + configFile: "tsconfig.web.json", + projectReferences: true, + }, }, }, { diff --git a/plugins/global_chat/.npmignore b/plugins/global_chat/.npmignore index 6de001d8..ebd99907 100644 --- a/plugins/global_chat/.npmignore +++ b/plugins/global_chat/.npmignore @@ -1 +1,2 @@ webpack.config.js +dist/web/build diff --git a/plugins/global_chat/info.ts b/plugins/global_chat/info.ts index 192a31ba..092a3287 100644 --- a/plugins/global_chat/info.ts +++ b/plugins/global_chat/info.ts @@ -2,7 +2,7 @@ import type * as lib from "@clusterio/lib"; import { ChatEvent } from "./messages"; -const info: lib.PluginDeclaration = { +export default { name: "global_chat", title: "Global Chat", description: "Forwards chat between instances.", @@ -12,6 +12,4 @@ const info: lib.PluginDeclaration = { messages: [ ChatEvent, ], -}; - -export default info; +} satisfies lib.PluginDeclaration; diff --git a/plugins/global_chat/package.json b/plugins/global_chat/package.json index 6c1f9e5c..240af799 100644 --- a/plugins/global_chat/package.json +++ b/plugins/global_chat/package.json @@ -24,6 +24,7 @@ "@clusterio/lib": "^2.0.0-alpha.13", "@clusterio/web_ui": "^2.0.0-alpha.13", "@types/node": "^20.4.5", + "@types/react": "^18.2.21", "typescript": "^5.1.6", "webpack": "^5.88.2", "webpack-cli": "^5.1.4", diff --git a/plugins/global_chat/tsconfig.web.json b/plugins/global_chat/tsconfig.web.json new file mode 100644 index 00000000..64a8f2a5 --- /dev/null +++ b/plugins/global_chat/tsconfig.web.json @@ -0,0 +1,11 @@ +{ + "extends": "../../tsconfig.web.json", + "references": [ + { "path": "../../packages/lib" }, + { "path": "../../packages/web_ui/tsconfig.web.json" }, + ], + "include": [ "web/**/*.tsx", "web/**/*.ts", "messages.ts", "package.json" ], + "compilerOptions": { + "outDir": "dist/web/build", + } +} diff --git a/plugins/inventory_sync/web/index.css b/plugins/global_chat/web/index.tsx similarity index 100% rename from plugins/inventory_sync/web/index.css rename to plugins/global_chat/web/index.tsx diff --git a/plugins/global_chat/webpack.config.js b/plugins/global_chat/webpack.config.js index 4f084bda..6870a94b 100644 --- a/plugins/global_chat/webpack.config.js +++ b/plugins/global_chat/webpack.config.js @@ -7,7 +7,7 @@ const common = require("@clusterio/web_ui/webpack.common"); module.exports = (env = {}) => merge(common(env), { context: __dirname, - entry: "./web/index.jsx", + entry: "./web/index.tsx", output: { path: path.resolve(__dirname, "dist", "web"), }, @@ -16,7 +16,7 @@ module.exports = (env = {}) => merge(common(env), { name: "global_chat", library: { type: "var", name: "plugin_global_chat" }, exposes: { - "./info": "./dist/plugin/info.js", + "./info": "./info.ts", "./package.json": "./package.json", }, shared: { diff --git a/plugins/inventory_sync/.npmignore b/plugins/inventory_sync/.npmignore index 6de001d8..ebd99907 100644 --- a/plugins/inventory_sync/.npmignore +++ b/plugins/inventory_sync/.npmignore @@ -1 +1,2 @@ webpack.config.js +dist/web/build diff --git a/plugins/inventory_sync/info.ts b/plugins/inventory_sync/info.ts index 29b11f2e..d3090271 100644 --- a/plugins/inventory_sync/info.ts +++ b/plugins/inventory_sync/info.ts @@ -40,7 +40,7 @@ lib.definePermission({ grantByDefault: true, }); -const info: lib.PluginDeclaration = { +export default { name: "inventory_sync", title: "Inventory sync", description: "Synchronizes players inventories between instances", @@ -60,6 +60,4 @@ const info: lib.PluginDeclaration = { ], webEntrypoint: "./web", routes: ["/inventory"], -}; - -export default info; +} satisfies lib.PluginDeclaration; diff --git a/plugins/inventory_sync/messages.ts b/plugins/inventory_sync/messages.ts index 787fc75f..a98b194f 100644 --- a/plugins/inventory_sync/messages.ts +++ b/plugins/inventory_sync/messages.ts @@ -139,7 +139,7 @@ export class DownloadRequest { } } -class DatabaseStatsResponse { +export class DatabaseStatsResponse { constructor( public databaseSize: number, public databaseEntries: number, diff --git a/plugins/inventory_sync/package.json b/plugins/inventory_sync/package.json index b4ad895a..28cc8055 100644 --- a/plugins/inventory_sync/package.json +++ b/plugins/inventory_sync/package.json @@ -29,6 +29,7 @@ "@clusterio/web_ui": "^2.0.0-alpha.13", "@types/fs-extra": "^11.0.1", "@types/node": "^20.4.5", + "@types/react": "^18.2.21", "antd": "^5.7.3", "react": "^18.2.0", "react-dom": "^18.2.0", diff --git a/plugins/inventory_sync/tsconfig.web.json b/plugins/inventory_sync/tsconfig.web.json new file mode 100644 index 00000000..6b077616 --- /dev/null +++ b/plugins/inventory_sync/tsconfig.web.json @@ -0,0 +1,11 @@ +{ + "extends": "../../tsconfig.web.json", + "references": [ + { "path": "../../packages/lib" }, + { "path": "../../packages/web_ui/tsconfig.web.json" }, + ], + "include": [ "web/**/*.tsx", "web/**/*.ts", "messages.ts", "instance.ts", "package.json" ], + "compilerOptions": { + "outDir": "dist/web/build", + } +} diff --git a/plugins/inventory_sync/web/index.jsx b/plugins/inventory_sync/web/index.tsx similarity index 82% rename from plugins/inventory_sync/web/index.jsx rename to plugins/inventory_sync/web/index.tsx index 25b405c3..bdfad9a7 100644 --- a/plugins/inventory_sync/web/index.jsx +++ b/plugins/inventory_sync/web/index.tsx @@ -1,14 +1,14 @@ import React, { useContext, useEffect, useState } from "react"; import { BaseWebPlugin, PageLayout, ControlContext } from "@clusterio/web_ui"; -import { DatabaseStatsRequest } from "../dist/plugin/messages"; +import { DatabaseStatsRequest, DatabaseStatsResponse } from "../messages"; -import "./index.css"; +import "./style.css"; function InventoryPage() { let control = useContext(ControlContext); - let [statsData, updateStatsData] = useState(); + let [statsData, updateStatsData] = useState(); useEffect(() => { (async () => { @@ -38,10 +38,4 @@ export class WebPlugin extends BaseWebPlugin { }, ]; } - - onControllerConnectionEvent(event) { - if (event === "connect") { - - } - } } diff --git a/plugins/research_sync/web/index.jsx b/plugins/inventory_sync/web/style.css similarity index 100% rename from plugins/research_sync/web/index.jsx rename to plugins/inventory_sync/web/style.css diff --git a/plugins/inventory_sync/webpack.config.js b/plugins/inventory_sync/webpack.config.js index 857e098b..90f35fef 100644 --- a/plugins/inventory_sync/webpack.config.js +++ b/plugins/inventory_sync/webpack.config.js @@ -7,7 +7,7 @@ const common = require("@clusterio/web_ui/webpack.common"); module.exports = (env = {}) => merge(common(env), { context: __dirname, - entry: "./web/index.jsx", + entry: "./web/index.tsx", output: { path: path.resolve(__dirname, "dist", "web"), }, @@ -16,9 +16,9 @@ module.exports = (env = {}) => merge(common(env), { name: "inventory_sync", library: { type: "var", name: "plugin_inventory_sync" }, exposes: { - "./info": "./dist/plugin/info.js", + "./info": "./info.ts", "./package.json": "./package.json", - "./web": "./web/index.jsx", + "./web": "./web/index.tsx", }, shared: { "@clusterio/lib": { import: false }, diff --git a/plugins/player_auth/.npmignore b/plugins/player_auth/.npmignore index 6de001d8..ebd99907 100644 --- a/plugins/player_auth/.npmignore +++ b/plugins/player_auth/.npmignore @@ -1 +1,2 @@ webpack.config.js +dist/web/build diff --git a/plugins/player_auth/controller.ts b/plugins/player_auth/controller.ts index dbdb2c9c..f9863008 100644 --- a/plugins/player_auth/controller.ts +++ b/plugins/player_auth/controller.ts @@ -46,10 +46,10 @@ export class ControllerPlugin extends BaseControllerPlugin { }, 60e3).unref(); this.controller.app.get("/api/player_auth/servers", (req: Request, res: Response) => { - let servers = []; + let servers: string[] = []; for (let instance of this.controller.instances!.values()) { if (instance.status === "running" && instance.config.get("player_auth.load_plugin")) { - servers.push(instance.config.get("factorio.settings")["name"] || "unnamed server"); + servers.push(instance.config.get("factorio.settings")["name"] as string || "unnamed server"); } } res.send(servers); diff --git a/plugins/player_auth/info.ts b/plugins/player_auth/info.ts index 5de172e0..8289657a 100644 --- a/plugins/player_auth/info.ts +++ b/plugins/player_auth/info.ts @@ -20,7 +20,7 @@ ControllerConfigGroup.define({ }); ControllerConfigGroup.finalize(); -const info: lib.PluginDeclaration = { +export default { name: "player_auth", title: "Player Auth", description: "Provides authentication to the cluster via logging into a Factorio server.", @@ -33,6 +33,4 @@ const info: lib.PluginDeclaration = { messages.FetchPlayerCodeRequest, messages.SetVerifyCodeRequest, ], -}; - -export default info; +} satisfies lib.PluginDeclaration; diff --git a/plugins/player_auth/package.json b/plugins/player_auth/package.json index 32025629..0a497811 100644 --- a/plugins/player_auth/package.json +++ b/plugins/player_auth/package.json @@ -31,6 +31,7 @@ "@types/express": "^4.17.17", "@types/jsonwebtoken": "^9.0.2", "@types/node": "^20.4.5", + "@types/react": "^18.2.21", "antd": "^5.7.3", "phin": "^3.7.0", "react": "^18.2.0", diff --git a/plugins/player_auth/tsconfig.web.json b/plugins/player_auth/tsconfig.web.json new file mode 100644 index 00000000..64a8f2a5 --- /dev/null +++ b/plugins/player_auth/tsconfig.web.json @@ -0,0 +1,11 @@ +{ + "extends": "../../tsconfig.web.json", + "references": [ + { "path": "../../packages/lib" }, + { "path": "../../packages/web_ui/tsconfig.web.json" }, + ], + "include": [ "web/**/*.tsx", "web/**/*.ts", "messages.ts", "package.json" ], + "compilerOptions": { + "outDir": "dist/web/build", + } +} diff --git a/plugins/player_auth/web/index.jsx b/plugins/player_auth/web/index.tsx similarity index 87% rename from plugins/player_auth/web/index.jsx rename to plugins/player_auth/web/index.tsx index 266450ef..9dac7de1 100644 --- a/plugins/player_auth/web/index.jsx +++ b/plugins/player_auth/web/index.tsx @@ -3,17 +3,20 @@ import React, { useEffect, useState } from "react"; import { BaseWebPlugin, notifyErrorHandler } from "@clusterio/web_ui"; import { Button, Form, Input, Spin, Typography } from "antd"; -import "./index.css"; +import "./style.css"; const { Paragraph, Text } = Typography; +interface LoginFormProps { + setToken(token: string): void, +} -function LoginForm(props) { - let [servers, setServers] = useState(null); - let [playerCode, setPlayerCode] = useState(null); - let [playerCodeError, setPlayerCodeError] = useState(null); - let [verifyCode, setVerifyCode] = useState(null); - let [verifyToken, setVerifyToken] = useState(null); +function LoginForm(props: LoginFormProps) { + let [servers, setServers] = useState(null); + let [playerCode, setPlayerCode] = useState(null); + let [playerCodeError, setPlayerCodeError] = useState(null); + let [verifyCode, setVerifyCode] = useState(); + let [verifyToken, setVerifyToken] = useState(null); useEffect(() => { (async () => { @@ -137,7 +140,7 @@ function LoginForm(props) {
  • Enter this code into the in-game dialog: - +
  • diff --git a/plugins/player_auth/web/index.css b/plugins/player_auth/web/style.css similarity index 100% rename from plugins/player_auth/web/index.css rename to plugins/player_auth/web/style.css diff --git a/plugins/player_auth/webpack.config.js b/plugins/player_auth/webpack.config.js index 04c092d2..6a3fc528 100644 --- a/plugins/player_auth/webpack.config.js +++ b/plugins/player_auth/webpack.config.js @@ -7,7 +7,7 @@ const common = require("@clusterio/web_ui/webpack.common"); module.exports = (env = {}) => merge(common(env), { context: __dirname, - entry: "./web/index.jsx", + entry: "./web/index.tsx", output: { path: path.resolve(__dirname, "dist", "web"), }, @@ -16,9 +16,9 @@ module.exports = (env = {}) => merge(common(env), { name: "player_auth", library: { type: "var", name: "plugin_player_auth" }, exposes: { - "./info": "./dist/plugin/info.js", + "./info": "./info.ts", "./package.json": "./package.json", - "./web": "./web/index.jsx", + "./web": "./web/index.tsx", }, shared: { "@clusterio/lib": { import: false }, diff --git a/plugins/research_sync/.npmignore b/plugins/research_sync/.npmignore index 6de001d8..ebd99907 100644 --- a/plugins/research_sync/.npmignore +++ b/plugins/research_sync/.npmignore @@ -1 +1,2 @@ webpack.config.js +dist/web/build diff --git a/plugins/research_sync/info.ts b/plugins/research_sync/info.ts index 55e7f4e8..b6692e12 100644 --- a/plugins/research_sync/info.ts +++ b/plugins/research_sync/info.ts @@ -2,7 +2,7 @@ import type * as lib from "@clusterio/lib"; import * as messages from "./messages"; -const info: lib.PluginDeclaration = { +export default { name: "research_sync", title: "Research Sync", description: "Synchronises technology research progress between instances.", @@ -15,6 +15,4 @@ const info: lib.PluginDeclaration = { messages.FinishedEvent, messages.SyncTechnologiesRequest, ], -}; - -export default info; +} satisfies lib.PluginDeclaration; diff --git a/plugins/research_sync/package.json b/plugins/research_sync/package.json index 099fd3a5..eeaa70a3 100644 --- a/plugins/research_sync/package.json +++ b/plugins/research_sync/package.json @@ -27,8 +27,9 @@ "devDependencies": { "@clusterio/lib": "^2.0.0-alpha.13", "@clusterio/web_ui": "^2.0.0-alpha.13", - "@types/node": "^20.4.5", "@types/fs-extra": "^11.0.1", + "@types/node": "^20.4.5", + "@types/react": "^18.2.21", "typescript": "^5.1.6", "webpack": "^5.88.2", "webpack-cli": "^5.1.4", diff --git a/plugins/research_sync/tsconfig.web.json b/plugins/research_sync/tsconfig.web.json new file mode 100644 index 00000000..64a8f2a5 --- /dev/null +++ b/plugins/research_sync/tsconfig.web.json @@ -0,0 +1,11 @@ +{ + "extends": "../../tsconfig.web.json", + "references": [ + { "path": "../../packages/lib" }, + { "path": "../../packages/web_ui/tsconfig.web.json" }, + ], + "include": [ "web/**/*.tsx", "web/**/*.ts", "messages.ts", "package.json" ], + "compilerOptions": { + "outDir": "dist/web/build", + } +} diff --git a/plugins/statistics_exporter/web/index.jsx b/plugins/research_sync/web/index.tsx similarity index 100% rename from plugins/statistics_exporter/web/index.jsx rename to plugins/research_sync/web/index.tsx diff --git a/plugins/research_sync/webpack.config.js b/plugins/research_sync/webpack.config.js index 069b6dac..6c04b3d1 100644 --- a/plugins/research_sync/webpack.config.js +++ b/plugins/research_sync/webpack.config.js @@ -7,7 +7,7 @@ const common = require("@clusterio/web_ui/webpack.common"); module.exports = (env = {}) => merge(common(env), { context: __dirname, - entry: "./web/index.jsx", + entry: "./web/index.tsx", output: { path: path.resolve(__dirname, "dist", "web"), }, @@ -16,7 +16,7 @@ module.exports = (env = {}) => merge(common(env), { name: "research_sync", library: { type: "var", name: "plugin_research_sync" }, exposes: { - "./info": "./dist/plugin/info.js", + "./info": "./info.ts", "./package.json": "./package.json", }, shared: { diff --git a/plugins/statistics_exporter/.npmignore b/plugins/statistics_exporter/.npmignore index 6de001d8..ebd99907 100644 --- a/plugins/statistics_exporter/.npmignore +++ b/plugins/statistics_exporter/.npmignore @@ -1 +1,2 @@ webpack.config.js +dist/web/build diff --git a/plugins/statistics_exporter/info.ts b/plugins/statistics_exporter/info.ts index 9f668cb5..138e3024 100644 --- a/plugins/statistics_exporter/info.ts +++ b/plugins/statistics_exporter/info.ts @@ -12,7 +12,7 @@ InstanceConfigGroup.define({ }); InstanceConfigGroup.finalize(); -const info: lib.PluginDeclaration = { +export default { name: "statistics_exporter", title: "Statistics Exporter", description: @@ -20,6 +20,4 @@ const info: lib.PluginDeclaration = { "statistics to the cluster's Prometheus endpoint.", instanceEntrypoint: "dist/plugin/instance", InstanceConfigGroup, -}; - -export default info; +} satisfies lib.PluginDeclaration; diff --git a/plugins/statistics_exporter/package.json b/plugins/statistics_exporter/package.json index 1e3c1a81..50a09732 100644 --- a/plugins/statistics_exporter/package.json +++ b/plugins/statistics_exporter/package.json @@ -24,6 +24,7 @@ "@clusterio/lib": "^2.0.0-alpha.13", "@clusterio/web_ui": "^2.0.0-alpha.13", "@types/node": "^20.4.5", + "@types/react": "^18.2.21", "typescript": "^5.1.6", "webpack": "^5.88.2", "webpack-cli": "^5.1.4", diff --git a/plugins/statistics_exporter/tsconfig.web.json b/plugins/statistics_exporter/tsconfig.web.json new file mode 100644 index 00000000..f70c4e0d --- /dev/null +++ b/plugins/statistics_exporter/tsconfig.web.json @@ -0,0 +1,11 @@ +{ + "extends": "../../tsconfig.web.json", + "references": [ + { "path": "../../packages/lib" }, + { "path": "../../packages/web_ui/tsconfig.web.json" }, + ], + "include": [ "web/**/*.tsx", "web/**/*.ts", "package.json" ], + "compilerOptions": { + "outDir": "dist/web/build", + } +} diff --git a/plugins/statistics_exporter/web/index.tsx b/plugins/statistics_exporter/web/index.tsx new file mode 100644 index 00000000..e69de29b diff --git a/plugins/statistics_exporter/webpack.config.js b/plugins/statistics_exporter/webpack.config.js index 515da3ef..3f768eaa 100644 --- a/plugins/statistics_exporter/webpack.config.js +++ b/plugins/statistics_exporter/webpack.config.js @@ -7,7 +7,7 @@ const common = require("@clusterio/web_ui/webpack.common"); module.exports = (env = {}) => merge(common(env), { context: __dirname, - entry: "./web/index.jsx", + entry: "./web/index.tsx", output: { path: path.resolve(__dirname, "dist", "web"), }, @@ -16,7 +16,7 @@ module.exports = (env = {}) => merge(common(env), { name: "statistics_exporter", library: { type: "var", name: "plugin_statistics_exporter" }, exposes: { - "./info": "./dist/plugin/info.js", + "./info": "./info.ts", "./package.json": "./package.json", }, shared: { diff --git a/plugins/subspace_storage/.npmignore b/plugins/subspace_storage/.npmignore index 6de001d8..ebd99907 100644 --- a/plugins/subspace_storage/.npmignore +++ b/plugins/subspace_storage/.npmignore @@ -1 +1,2 @@ webpack.config.js +dist/web/build diff --git a/plugins/subspace_storage/info.ts b/plugins/subspace_storage/info.ts index 58d7a60d..c54da3c2 100644 --- a/plugins/subspace_storage/info.ts +++ b/plugins/subspace_storage/info.ts @@ -47,7 +47,7 @@ lib.definePermission({ grantByDefault: true, }); -module.exports.default = { +export default { name: "subspace_storage", title: "Subspace Storage", description: "Provides shared storage across instances for the Subspace Storage mod", @@ -66,4 +66,4 @@ module.exports.default = { ], webEntrypoint: "./web", routes: ["/storage"], -}; +} satisfies lib.PluginDeclaration; diff --git a/plugins/subspace_storage/package.json b/plugins/subspace_storage/package.json index 6d7581a0..258668c7 100644 --- a/plugins/subspace_storage/package.json +++ b/plugins/subspace_storage/package.json @@ -30,6 +30,7 @@ "@types/express": "^4.17.17", "@types/fs-extra": "^11.0.1", "@types/node": "^20.4.5", + "@types/react": "^18.2.21", "antd": "^5.7.3", "react": "^18.2.0", "react-dom": "^18.2.0", diff --git a/plugins/subspace_storage/tsconfig.web.json b/plugins/subspace_storage/tsconfig.web.json new file mode 100644 index 00000000..64a8f2a5 --- /dev/null +++ b/plugins/subspace_storage/tsconfig.web.json @@ -0,0 +1,11 @@ +{ + "extends": "../../tsconfig.web.json", + "references": [ + { "path": "../../packages/lib" }, + { "path": "../../packages/web_ui/tsconfig.web.json" }, + ], + "include": [ "web/**/*.tsx", "web/**/*.ts", "messages.ts", "package.json" ], + "compilerOptions": { + "outDir": "dist/web/build", + } +} diff --git a/plugins/subspace_storage/web/index.jsx b/plugins/subspace_storage/web/index.tsx similarity index 78% rename from plugins/subspace_storage/web/index.jsx rename to plugins/subspace_storage/web/index.tsx index 04cd6b76..3950b627 100644 --- a/plugins/subspace_storage/web/index.jsx +++ b/plugins/subspace_storage/web/index.tsx @@ -3,18 +3,18 @@ import { Input, Table, Typography } from "antd"; import * as lib from "@clusterio/lib"; import { - BaseWebPlugin, PageLayout, ControlContext, + BaseWebPlugin, PageLayout, Control, ControlContext, notifyErrorHandler, useItemMetadata, useLocale, } from "@clusterio/web_ui"; -import { GetStorageRequest, SetStorageSubscriptionRequest } from "../dist/plugin/messages"; +import { GetStorageRequest, Item, SetStorageSubscriptionRequest, UpdateStorageEvent } from "../messages"; -import "./index.css"; +import "./style.css"; const { Paragraph } = Typography; -function useStorage(control) { - let plugin = control.plugins.get("subspace_storage"); +function useStorage(control: Control) { + let plugin = control.plugins.get("subspace_storage") as WebPlugin; let [storage, setStorage] = useState([...plugin.storage]); useEffect(() => { @@ -35,14 +35,15 @@ function StoragePage() { let locale = useLocale(); let itemMetadata = useItemMetadata(); let storage = useStorage(control); - let [filter, setFilter] = useState(null); + type ItemFilter = ([name, count]: [string, number]) => boolean; + let [filter, setFilter] = useState(null); - function getLocaleName(itemName) { + function getLocaleName(itemName: string) { let localeName = itemName; let meta = itemMetadata.get(itemName); - if (meta && meta.localized_name) { + if (meta && meta.localised_name) { // TODO: implement the locale to name conversion. - localeName = locale.get(meta.localized_name[0]); + localeName = locale.get(meta.localised_name[0])!; } else { for (let section of ["item-name", "entity-name", "fluid-name", "equipment-name"]) { let name = locale.get(`${section}.${itemName}`); @@ -72,7 +73,7 @@ function StoragePage() { search = search.replace(/(^| )(\w)/g, "$1\\b$2"); search = search.replace(/ +/g, ".*"); let filterExpr = new RegExp(search, "i"); - setFilter(() => (item => { + setFilter(() => ((item: [string, number]) => { let name = getLocaleName(item[0]); return filterExpr.test(name) || filterExpr.test(item[0]); })); @@ -118,6 +119,9 @@ function StoragePage() { } export class WebPlugin extends BaseWebPlugin { + storage = new Map(); + callbacks: (() => void)[] = []; + async init() { this.pages = [ { @@ -127,29 +131,27 @@ export class WebPlugin extends BaseWebPlugin { content: , }, ]; - - this.storage = new Map(); - this.callbacks = []; + this.control.handle(UpdateStorageEvent, this.handleUpdateStorageEvent.bind(this)); } - onControllerConnectionEvent(event) { + onControllerConnectionEvent(event: "connect" | "drop" | "resume" | "close") { if (event === "connect") { this.updateSubscription(); } } - async updateStorageEventHandler(event) { + async handleUpdateStorageEvent(event: UpdateStorageEvent) { this.updateStorage(event.items); } - onUpdate(callback) { + onUpdate(callback: () => void) { this.callbacks.push(callback); if (this.callbacks.length) { this.updateSubscription(); } } - offUpdate(callback) { + offUpdate(callback: () => void) { let index = this.callbacks.lastIndexOf(callback); if (index === -1) { throw new Error("callback is not registered"); @@ -161,7 +163,7 @@ export class WebPlugin extends BaseWebPlugin { } } - updateStorage(items) { + updateStorage(items: Item[]) { for (let item of items) { this.storage.set(item.name, item.count); } @@ -180,7 +182,7 @@ export class WebPlugin extends BaseWebPlugin { ).catch(notifyErrorHandler("Error subscribing to storage")); if (this.callbacks.length) { - this.control.send(new GetStorageRequest()).then( + this.control!.send(new GetStorageRequest()).then( items => { this.updateStorage(items); } diff --git a/plugins/subspace_storage/web/index.css b/plugins/subspace_storage/web/style.css similarity index 100% rename from plugins/subspace_storage/web/index.css rename to plugins/subspace_storage/web/style.css diff --git a/plugins/subspace_storage/webpack.config.js b/plugins/subspace_storage/webpack.config.js index 87a02824..a2669ef6 100644 --- a/plugins/subspace_storage/webpack.config.js +++ b/plugins/subspace_storage/webpack.config.js @@ -7,7 +7,7 @@ const common = require("@clusterio/web_ui/webpack.common"); module.exports = (env = {}) => merge(common(env), { context: __dirname, - entry: "./web/index.jsx", + entry: "./web/index.tsx", output: { path: path.resolve(__dirname, "dist", "web"), }, @@ -16,9 +16,9 @@ module.exports = (env = {}) => merge(common(env), { name: "subspace_storage", library: { type: "var", name: "plugin_subspace_storage" }, exposes: { - "./info": "./dist/plugin/info.js", + "./info": "./info.ts", "./package.json": "./package.json", - "./web": "./web/index.jsx", + "./web": "./web/index.tsx", }, shared: { "@clusterio/lib": { import: false }, diff --git a/tsconfig.base.json b/tsconfig.base.json index 02016d99..dd597d2d 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -6,6 +6,7 @@ "incremental": true, "sourceMap": true, "strict": true, + "esModuleInterop": true, "resolveJsonModule": true, "skipLibCheck": true, }, diff --git a/tsconfig.json b/tsconfig.json index 35abcdb1..f2af308f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,14 +3,21 @@ "references": [ { "path": "./packages/lib" }, { "path": "./packages/controller" }, + { "path": "./packages/controller/tsconfig.web.json" }, { "path": "./packages/host" }, { "path": "./packages/ctl" }, - { "path": "./packages/web_ui" }, + { "path": "./packages/web_ui/tsconfig.web.json" }, { "path": "./plugins/global_chat" }, + { "path": "./plugins/global_chat/tsconfig.web.json" }, { "path": "./plugins/inventory_sync" }, + { "path": "./plugins/inventory_sync/tsconfig.web.json" }, { "path": "./plugins/player_auth" }, + { "path": "./plugins/player_auth/tsconfig.web.json" }, { "path": "./plugins/research_sync" }, + { "path": "./plugins/research_sync/tsconfig.web.json" }, { "path": "./plugins/statistics_exporter" }, + { "path": "./plugins/statistics_exporter/tsconfig.web.json" }, { "path": "./plugins/subspace_storage" }, + { "path": "./plugins/subspace_storage/tsconfig.web.json" }, ], } diff --git a/tsconfig.node.json b/tsconfig.node.json index daa42608..700d02da 100644 --- a/tsconfig.node.json +++ b/tsconfig.node.json @@ -5,8 +5,6 @@ "module": "node16", "moduleResolution": "node16", "target": "es2022", - "esModuleInterop": true, - "alwaysStrict": true, "forceConsistentCasingInFileNames": true, }, } diff --git a/tsconfig.web.json b/tsconfig.web.json new file mode 100644 index 00000000..7edb5691 --- /dev/null +++ b/tsconfig.web.json @@ -0,0 +1,11 @@ +{ + "extends": "./tsconfig.base.json", + "compilerOptions": { + "lib": ["dom", "es2023"], + "module": "es2022", + "moduleResolution": "bundler", + "target": "es2022", + "jsx": "react", + "noEmitOnError": true, + }, +}