diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 000000000..6be85b2f6 --- /dev/null +++ b/.prettierignore @@ -0,0 +1 @@ +*.[tj]s diff --git a/eslint.config.js b/eslint.config.js index 05c378288..b0f93af1d 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -1,5 +1,6 @@ import { includeIgnoreFile } from "@eslint/compat"; import pluginJs from "@eslint/js"; +import stylisticTs from "@stylistic/eslint-plugin"; import eslintConfigPrettier from "eslint-config-prettier/flat"; import globals from "globals"; import path from "node:path"; @@ -46,8 +47,12 @@ export default [ }, }, { + plugins: { + "@stylistic/ts": stylisticTs, + }, rules: { // Enable rules + "@stylistic/ts/indent": ["error", 2], "@typescript-eslint/consistent-type-definitions": [ "error", "type", @@ -64,6 +69,7 @@ export default [ "@typescript-eslint/prefer-literal-enum-member": "error", "@typescript-eslint/prefer-nullish-coalescing": "error", eqeqeq: "error", + indent: "off", // @stylistic/ts/indent "sort-keys": "error", }, }, diff --git a/package-lock.json b/package-lock.json index 07eac317f..2612dafad 100644 --- a/package-lock.json +++ b/package-lock.json @@ -42,6 +42,7 @@ "@datastructures-js/priority-queue": "^6.3.3", "@eslint/compat": "^1.2.7", "@eslint/js": "^9.21.0", + "@stylistic/eslint-plugin": "^5.2.3", "@swc/jest": "^0.2.39", "@total-typescript/ts-reset": "^0.6.1", "@types/benchmark": "^2.1.5", @@ -6359,6 +6360,54 @@ "node": ">=18.0.0" } }, + "node_modules/@stylistic/eslint-plugin": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-5.2.3.tgz", + "integrity": "sha512-oY7GVkJGVMI5benlBDCaRrSC1qPasafyv5dOBLLv5MTilMGnErKhO6ziEfodDDIZbo5QxPUNW360VudJOFODMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.7.0", + "@typescript-eslint/types": "^8.38.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "estraverse": "^5.3.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "peerDependencies": { + "eslint": ">=9.0.0" + } + }, + "node_modules/@stylistic/eslint-plugin/node_modules/@typescript-eslint/types": { + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.39.1.tgz", + "integrity": "sha512-7sPDKQQp+S11laqTrhHqeAbsCfMkwJMrV7oTDvtDds4mEofJYir414bYKUEb8YPUm9QL3U+8f6L6YExSoAGdQw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@stylistic/eslint-plugin/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/@swc/core": { "version": "1.13.3", "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.13.3.tgz", diff --git a/package.json b/package.json index 1b82703dd..0d1603dea 100644 --- a/package.json +++ b/package.json @@ -29,9 +29,10 @@ "@datastructures-js/priority-queue": "^6.3.3", "@eslint/compat": "^1.2.7", "@eslint/js": "^9.21.0", + "@stylistic/eslint-plugin": "^5.2.3", "@swc/jest": "^0.2.39", - "@types/benchmark": "^2.1.5", "@total-typescript/ts-reset": "^0.6.1", + "@types/benchmark": "^2.1.5", "@types/chai": "^4.3.17", "@types/d3": "^7.4.3", "@types/express": "^4.17.23", diff --git a/src/client/HostLobbyModal.ts b/src/client/HostLobbyModal.ts index 05905b922..d684939b6 100644 --- a/src/client/HostLobbyModal.ts +++ b/src/client/HostLobbyModal.ts @@ -296,8 +296,8 @@ export class HostLobbyModal extends LitElement { ${typeof o === "string" ? translateText(`public_lobby.teams_${o}`) : translateText("public_lobby.teams", { - num: o, - })} + num: o, + })} `, @@ -441,9 +441,9 @@ export class HostLobbyModal extends LitElement { style="display: flex; flex-wrap: wrap; justify-content: center; gap: 12px;" > ${renderUnitTypeOptions({ - disabledUnits: this.disabledUnits, - toggleUnit: this.toggleUnit.bind(this), - })} + disabledUnits: this.disabledUnits, + toggleUnit: this.toggleUnit.bind(this), + })} diff --git a/src/client/LangSelector.ts b/src/client/LangSelector.ts index 2e6c5d51a..ed27a1328 100644 --- a/src/client/LangSelector.ts +++ b/src/client/LangSelector.ts @@ -276,16 +276,16 @@ export class LangSelector extends LitElement { this.languageList.find((l) => l.code === this.currentLang) ?? (this.currentLang === "debug" ? { - code: "debug", - native: "Debug", - en: "Debug", - svg: "xx", - } + code: "debug", + native: "Debug", + en: "Debug", + svg: "xx", + } : { - native: "English", - en: "English", - svg: "uk_us_flag", - }); + native: "English", + en: "English", + svg: "uk_us_flag", + }); return html`
diff --git a/src/client/PublicLobby.ts b/src/client/PublicLobby.ts index 6ab99d2cd..6397fe0a9 100644 --- a/src/client/PublicLobby.ts +++ b/src/client/PublicLobby.ts @@ -156,8 +156,8 @@ export class PublicLobby extends LitElement { ? typeof teamCount === "string" ? translateText(`public_lobby.teams_${teamCount}`) : translateText("public_lobby.teams", { - num: teamCount ?? 0, - }) + num: teamCount ?? 0, + }) : translateText("game_mode.ffa")} (this.settingsMode = "basic")} > ${translateText("user_setting.tab_basic")} @@ -253,8 +253,8 @@ export class UserSettingModal extends LitElement {
diff --git a/src/client/graphics/layers/SettingsModal.ts b/src/client/graphics/layers/SettingsModal.ts index a8e6b359b..69b53ec9f 100644 --- a/src/client/graphics/layers/SettingsModal.ts +++ b/src/client/graphics/layers/SettingsModal.ts @@ -374,8 +374,8 @@ export class SettingsModal extends LitElement implements Layer { ${this.userSettings.performanceOverlay() ? translateText("user_setting.performance_overlay_enabled") : translateText( - "user_setting.performance_overlay_disabled", - )} + "user_setting.performance_overlay_disabled", + )}
diff --git a/src/client/graphics/layers/StructureIconsLayer.ts b/src/client/graphics/layers/StructureIconsLayer.ts index 83499e0b9..5c6169ab4 100644 --- a/src/client/graphics/layers/StructureIconsLayer.ts +++ b/src/client/graphics/layers/StructureIconsLayer.ts @@ -353,12 +353,12 @@ export class StructureIconsLayer implements Layer { const shape = STRUCTURE_SHAPES[structureType]; const texture = shape ? this.createIcon( - unit.owner(), - structureType, - isConstruction, - shape, - renderIcon, - ) + unit.owner(), + structureType, + isConstruction, + shape, + renderIcon, + ) : PIXI.Texture.EMPTY; this.textureCache.set(cacheKey, texture); diff --git a/src/core/GameRunner.ts b/src/core/GameRunner.ts index 9a794aac7..186232380 100644 --- a/src/core/GameRunner.ts +++ b/src/core/GameRunner.ts @@ -57,13 +57,13 @@ export async function createGameRunner( const nations = gameStart.config.disableNPCs ? [] : gameMap.manifest.nations.map( - (n) => - new Nation( - new Cell(n.coordinates[0], n.coordinates[1]), - n.strength, - new PlayerInfo(n.name, PlayerType.FakeHuman, null, random.nextID()), - ), - ); + (n) => + new Nation( + new Cell(n.coordinates[0], n.coordinates[1]), + n.strength, + new PlayerInfo(n.name, PlayerType.FakeHuman, null, random.nextID()), + ), + ); const game: Game = createGame( humans, diff --git a/src/core/execution/FakeHumanExecution.ts b/src/core/execution/FakeHumanExecution.ts index 0f41f7e86..b70bbae13 100644 --- a/src/core/execution/FakeHumanExecution.ts +++ b/src/core/execution/FakeHumanExecution.ts @@ -455,8 +455,8 @@ export class FakeHumanExecution implements Execution { const tiles = type === UnitType.Port ? Array.from(this.player.borderTiles()).filter((t) => - this.mg.isOceanShore(t), - ) + this.mg.isOceanShore(t), + ) : Array.from(this.player.tiles()); if (tiles.length === 0) return null; return this.random.randElement(tiles); diff --git a/src/core/execution/RailroadExecution.ts b/src/core/execution/RailroadExecution.ts index 268e39ba0..133bef25a 100644 --- a/src/core/execution/RailroadExecution.ts +++ b/src/core/execution/RailroadExecution.ts @@ -43,9 +43,9 @@ export class RailroadExecution implements Execution { railType: tiles.length > 0 ? this.computeExtremityDirection( - tiles[tiles.length - 1], - tiles[tiles.length - 2], - ) + tiles[tiles.length - 1], + tiles[tiles.length - 2], + ) : RailType.VERTICAL, }); } diff --git a/src/core/pathfinding/AStar.ts b/src/core/pathfinding/AStar.ts index 4056c684f..63fbf907f 100644 --- a/src/core/pathfinding/AStar.ts +++ b/src/core/pathfinding/AStar.ts @@ -11,19 +11,19 @@ export enum PathFindResultType { } export type AStarResult = | { - type: PathFindResultType.NextTile; - node: NodeType; - } + type: PathFindResultType.NextTile; + node: NodeType; + } | { - type: PathFindResultType.Pending; - } + type: PathFindResultType.Pending; + } | { - type: PathFindResultType.Completed; - node: NodeType; - } + type: PathFindResultType.Completed; + node: NodeType; + } | { - type: PathFindResultType.PathNotFound; - }; + type: PathFindResultType.PathNotFound; + }; export type Point = { x: number; diff --git a/src/server/jwt.ts b/src/server/jwt.ts index 98ebb518f..72c5d36da 100644 --- a/src/server/jwt.ts +++ b/src/server/jwt.ts @@ -11,9 +11,9 @@ import { PersistentIdSchema } from "../core/Schemas"; type TokenVerificationResult = | { - persistentId: string; - claims: TokenPayload | null; - } + persistentId: string; + claims: TokenPayload | null; + } | false; export async function verifyClientToken( diff --git a/src/server/worker/websocket/handler/message/PreJoinHandler.ts b/src/server/worker/websocket/handler/message/PreJoinHandler.ts index 1d09f4529..c08ae2e7e 100644 --- a/src/server/worker/websocket/handler/message/PreJoinHandler.ts +++ b/src/server/worker/websocket/handler/message/PreJoinHandler.ts @@ -63,14 +63,14 @@ async function handleJoinMessage( ): Promise< | undefined | { - success: true; - } + success: true; + } | { - success: false; - code: 1002; - description: string; - error?: string; - reason: + success: false; + code: 1002; + description: string; + error?: string; + reason: | "ClientJoinMessageSchema" | "Flag invalid" | "Flag restricted" @@ -80,20 +80,20 @@ async function handleJoinMessage( | "Pattern restricted" | "Pattern unlisted" | "Unauthorized"; - } + } | { - success: false; - code: 1011; - reason: "Internal server error"; - error: string; - description: string; - } + success: false; + code: 1011; + reason: "Internal server error"; + error: string; + description: string; + } > { const forwarded = req.headers["x-forwarded-for"]; const ip = Array.isArray(forwarded) ? forwarded[0] : // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - forwarded || req.socket.remoteAddress || "unknown"; + forwarded || req.socket.remoteAddress || "unknown"; try { // Parse and handle client messages diff --git a/webpack.config.js b/webpack.config.js index 0820e8cd0..ca0708e92 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -111,13 +111,13 @@ export default async (env, argv) => { // Add optimization for HTML minify: isProduction ? { - collapseWhitespace: true, - removeComments: true, - removeRedundantAttributes: true, - removeScriptTypeAttributes: true, - removeStyleLinkTypeAttributes: true, - useShortDoctype: true, - } + collapseWhitespace: true, + removeComments: true, + removeRedundantAttributes: true, + removeScriptTypeAttributes: true, + removeStyleLinkTypeAttributes: true, + useShortDoctype: true, + } : false, }), new webpack.DefinePlugin({ @@ -160,91 +160,91 @@ export default async (env, argv) => { devServer: isProduction ? {} : { - devMiddleware: { writeToDisk: true }, - static: { - directory: path.join(__dirname, "static"), - }, - historyApiFallback: true, - compress: true, - port: 9000, - proxy: [ - // WebSocket proxies - { - context: ["/socket"], - target: "ws://localhost:3000", - ws: true, - changeOrigin: true, - logLevel: "debug", - }, - // Worker WebSocket proxies - using direct paths without /socket suffix - { - context: ["/w0"], - target: "ws://localhost:3001", - ws: true, - secure: false, - changeOrigin: true, - logLevel: "debug", - }, - { - context: ["/w1"], - target: "ws://localhost:3002", - ws: true, - secure: false, - changeOrigin: true, - logLevel: "debug", - }, - { - context: ["/w2"], - target: "ws://localhost:3003", - ws: true, - secure: false, - changeOrigin: true, - logLevel: "debug", - }, - // Worker proxies for HTTP requests - { - context: ["/w0"], - target: "http://localhost:3001", - pathRewrite: { "^/w0": "" }, - secure: false, - changeOrigin: true, - logLevel: "debug", - }, - { - context: ["/w1"], - target: "http://localhost:3002", - pathRewrite: { "^/w1": "" }, - secure: false, - changeOrigin: true, - logLevel: "debug", - }, - { - context: ["/w2"], - target: "http://localhost:3003", - pathRewrite: { "^/w2": "" }, - secure: false, - changeOrigin: true, - logLevel: "debug", - }, - // Original API endpoints - { - context: [ - "/api/env", - "/api/game", - "/api/public_lobbies", - "/api/join_game", - "/api/start_game", - "/api/create_game", - "/api/archive_singleplayer_game", - "/api/auth/callback", - "/api/auth/discord", - "/api/kick_player", - ], - target: "http://localhost:3000", - secure: false, - changeOrigin: true, - }, - ], + devMiddleware: { writeToDisk: true }, + static: { + directory: path.join(__dirname, "static"), }, + historyApiFallback: true, + compress: true, + port: 9000, + proxy: [ + // WebSocket proxies + { + context: ["/socket"], + target: "ws://localhost:3000", + ws: true, + changeOrigin: true, + logLevel: "debug", + }, + // Worker WebSocket proxies - using direct paths without /socket suffix + { + context: ["/w0"], + target: "ws://localhost:3001", + ws: true, + secure: false, + changeOrigin: true, + logLevel: "debug", + }, + { + context: ["/w1"], + target: "ws://localhost:3002", + ws: true, + secure: false, + changeOrigin: true, + logLevel: "debug", + }, + { + context: ["/w2"], + target: "ws://localhost:3003", + ws: true, + secure: false, + changeOrigin: true, + logLevel: "debug", + }, + // Worker proxies for HTTP requests + { + context: ["/w0"], + target: "http://localhost:3001", + pathRewrite: { "^/w0": "" }, + secure: false, + changeOrigin: true, + logLevel: "debug", + }, + { + context: ["/w1"], + target: "http://localhost:3002", + pathRewrite: { "^/w1": "" }, + secure: false, + changeOrigin: true, + logLevel: "debug", + }, + { + context: ["/w2"], + target: "http://localhost:3003", + pathRewrite: { "^/w2": "" }, + secure: false, + changeOrigin: true, + logLevel: "debug", + }, + // Original API endpoints + { + context: [ + "/api/env", + "/api/game", + "/api/public_lobbies", + "/api/join_game", + "/api/start_game", + "/api/create_game", + "/api/archive_singleplayer_game", + "/api/auth/callback", + "/api/auth/discord", + "/api/kick_player", + ], + target: "http://localhost:3000", + secure: false, + changeOrigin: true, + }, + ], + }, }; };