From 59709280da8d4d6d30cc855ed0b67360c33f6d4d Mon Sep 17 00:00:00 2001 From: Danielv123 Date: Fri, 1 Aug 2025 21:12:46 +0000 Subject: [PATCH 01/10] Update dependencies in package.json --- package.json | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/package.json b/package.json index 9ba48740..c185e0f2 100644 --- a/package.json +++ b/package.json @@ -41,21 +41,21 @@ "@clusterio/host": "workspace:*", "@clusterio/lib": "workspace:*", "@clusterio/web_ui": "workspace:*", - "@swc/core": "^1.11.1", - "@typescript-eslint/eslint-plugin": "^6.4.1", - "@typescript-eslint/parser": "^6.4.1", - "eslint": "^8.57.1", + "@swc/core": "^1.13.3", + "@typescript-eslint/eslint-plugin": "^8.38.0", + "@typescript-eslint/parser": "^8.38.0", + "eslint": "^9.32.0", "eslint-plugin-node": "^11.1.0", - "express": "^4.21.2", - "form-data": "^4.0.2", + "express": "^5.1.0", + "form-data": "^4.0.4", "fs-extra": "^11.3.0", - "jsonwebtoken": "^9.0.1", - "jszip": "^3.10.0", - "mocha": "^10.8.2", - "nyc": "^15.1.0", - "phin": "^3.7.0", - "typedoc": "^0.26.11", - "typescript": "^5.7.3", - "yargs": "^17.7.2" + "jsonwebtoken": "^9.0.2", + "jszip": "^3.10.1", + "mocha": "^11.7.1", + "nyc": "^17.1.0", + "phin": "^3.7.1", + "typedoc": "^0.28.9", + "typescript": "^5.9.2", + "yargs": "^18.0.0" } } From 10a87d47da10229b467ea4f030b40ecccf0d607d Mon Sep 17 00:00:00 2001 From: Danielv123 Date: Fri, 1 Aug 2025 21:19:58 +0000 Subject: [PATCH 02/10] Convert ArrayBufferLike to Buffer --- packages/lib/src/data/ModPack.ts | 2 +- packages/lib/src/factorio/exchange_string.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/lib/src/data/ModPack.ts b/packages/lib/src/data/ModPack.ts index 26fe2565..ff30bab3 100644 --- a/packages/lib/src/data/ModPack.ts +++ b/packages/lib/src/data/ModPack.ts @@ -218,7 +218,7 @@ export default class ModPack { let buf = Buffer.from(modPackString, "base64"); try { // eslint-disable-next-line node/no-sync - buf = zlib.inflateSync(buf); + buf = Buffer.from(zlib.inflateSync(buf)); } catch (err: any) { if (err.code.startsWith("Z_")) { throw new Error("Malformed mod pack string: zlib inflate failed"); diff --git a/packages/lib/src/factorio/exchange_string.ts b/packages/lib/src/factorio/exchange_string.ts index e5c2e7b1..6f7e3380 100644 --- a/packages/lib/src/factorio/exchange_string.ts +++ b/packages/lib/src/factorio/exchange_string.ts @@ -414,7 +414,7 @@ export function readMapExchangeString(exchangeString: string) { let buf = Buffer.from(exchangeString.slice(3, -3), "base64"); try { // eslint-disable-next-line node/no-sync - buf = zlib.inflateSync(buf); + buf = Buffer.from(zlib.inflateSync(buf)); } catch (err: any) { if (err.code.startsWith("Z_")) { throw new Error("Malformed map exchange string: zlib inflate failed"); From 7979dfaa167b5ce79ad12221879264712465f0c6 Mon Sep 17 00:00:00 2001 From: Danielv123 Date: Fri, 1 Aug 2025 21:25:13 +0000 Subject: [PATCH 03/10] Fix missing import --- packages/web_ui/src/components/UsersPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/web_ui/src/components/UsersPage.tsx b/packages/web_ui/src/components/UsersPage.tsx index ac7e238d..40bc4ceb 100644 --- a/packages/web_ui/src/components/UsersPage.tsx +++ b/packages/web_ui/src/components/UsersPage.tsx @@ -4,7 +4,7 @@ import { Static } from "@sinclair/typebox"; import { Button, Form, FormInstance, Input, Modal, - Radio, Upload, UploadProps, + Radio, Space, Upload, UploadProps, } from "antd"; import { InboxOutlined } from "@ant-design/icons"; From 256407b9fdaebc6a526ba3faa73738dfbd174cb5 Mon Sep 17 00:00:00 2001 From: Danielv123 Date: Fri, 1 Aug 2025 21:32:23 +0000 Subject: [PATCH 04/10] Update eslint config file for eslint 9 --- .eslintignore | 1 - .eslintrc.js => eslint.config.js | 460 ++++++++++++++++++------------- package.json | 5 + 3 files changed, 279 insertions(+), 187 deletions(-) delete mode 100644 .eslintignore rename .eslintrc.js => eslint.config.js (53%) diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index 849ddff3..00000000 --- a/.eslintignore +++ /dev/null @@ -1 +0,0 @@ -dist/ diff --git a/.eslintrc.js b/eslint.config.js similarity index 53% rename from .eslintrc.js rename to eslint.config.js index d002fa69..587ffe36 100644 --- a/.eslintrc.js +++ b/eslint.config.js @@ -1,118 +1,47 @@ -"use strict"; -module.exports = { - "plugins": [ - "@typescript-eslint", - "node", - ], - "root": true, - "env": { - "node": true, - "commonjs": true, - "es2022": true, +import { + defineConfig, + globalIgnores, +} from "eslint/config"; + +import typescriptEslint from "@typescript-eslint/eslint-plugin"; +import node from "eslint-plugin-node"; +import globals from "globals"; +import tsParser from "@typescript-eslint/parser"; +import js from "@eslint/js"; +import stylistic from "@stylistic/eslint-plugin"; + +import { + FlatCompat, +} from "@eslint/eslintrc"; + +const compat = new FlatCompat({ + baseDirectory: import.meta.dirname, + recommendedConfig: js.configs.recommended, + allConfig: js.configs.all, +}); + +export default defineConfig([{ + plugins: { + "@typescript-eslint": typescriptEslint, + "@stylistic": stylistic, + node, }, - "overrides": [ - { - "files": ["{packages,plugins}/**/*.ts?(x)"], - "parser": "@typescript-eslint/parser", - "extends": [ - "plugin:@typescript-eslint/recommended", - ], - "rules": { - "@typescript-eslint/no-explicit-any": "off", - "@typescript-eslint/no-useless-constructor": "error", - "@typescript-eslint/no-unused-vars": "off", - "@typescript-eslint/triple-slash-reference": "off", - "@typescript-eslint/no-shadow": ["error", { "allow": ["Event", "Request", "yargs"] }], - "prefer-const": "off", - "no-shadow": "off", - }, + languageOptions: { + globals: { + ...globals.node, + ...globals.commonjs, }, - { - "files": ["test/**/*.js", "plugins/*/test/**/*.js"], - "env": { - "mocha": "true", - }, - "parserOptions": { sourceType: "commonjs" }, - "rules": { - "prefer-arrow-callback": "off", - "no-invalid-this": "off", - "valid-jsdoc": "off", - "node/no-unpublished-import": "off", - "node/no-unpublished-require": "off", - }, - }, - { - "files": [ - "packages/web_ui/src/**/*.ts?(x)", - "packages/lib/browser.ts", - "packages/create/templates/*/web/**/*.{t,j}s?(x)", - "{packages,plugins}/*/web/**/*.ts?(x)", - ], - "env": { - "browser": true, - }, - "parserOptions": { - "sourceType": "module", - "ecmaFeatures": { - "jsx": true, - }, - }, - "rules": { - "node/callback-return": "off", - "node/exports-style": "off", - "node/file-extension-in-import": "off", - "node/global-require": "off", - "node/handle-callback-err": "off", - "node/no-callback-literal": "off", - "node/no-deprecated-api": "off", - "node/no-exports-assign": "off", - "node/no-extraneous-import": "off", - "node/no-extraneous-require": "off", - "node/no-missing-import": "off", - "node/no-missing-require": "off", - "node/no-mixed-requires": "off", - "node/no-new-require": "off", - "node/no-path-concat": "off", - "node/no-process-env": "off", - "node/no-process-exit": "off", - "node/no-restricted-require": "off", - "node/no-sync": "off", - "node/no-unpublished-bin": "off", - "node/no-unpublished-import": "off", - "node/no-unpublished-require": "off", - "node/no-unsupported-features/es-builtins": "off", - "node/no-unsupported-features/es-syntax": "off", - "node/no-unsupported-features/node-builtins": "off", - "node/prefer-global/buffer": "off", - "node/prefer-global/console": "off", - "node/prefer-global/process": "off", - "node/prefer-global/text-decoder": "off", - "node/prefer-global/text-encoder": "off", - "node/prefer-global/url-search-params": "off", - "node/prefer-global/url": "off", - "node/prefer-promises/dns": "off", - "node/prefer-promises/fs": "off", - "node/process-exit-as-throw": "off", - "node/shebang": "off", - }, - }, - { - "files": [ - "packages/create/templates/**/*.{t,j}s?(x)", - ], - "rules": { - "node/no-missing-require": "off", - "node/no-missing-import": "off", - "spaced-comment": "off", - "comma-spacing": "off", - "max-len": "off", - "indent": "off", - }, - }, - ], + }, "rules": { + "@stylistic/indent": ["error", "tab", { + "ignoredNodes": [ + "JSXFragment", // TODO: Linter crashes if this is removed + ], + "ignoreComments": true, + "SwitchCase": 1, // Indent case statements one level from switch + }], "accessor-pairs": "error", "array-bracket-newline": "off", "array-bracket-spacing": ["error", "never"], @@ -120,26 +49,34 @@ module.exports = { "array-element-newline": "off", "arrow-body-style": "error", "arrow-parens": "off", - "arrow-spacing": ["error", { "after": true, "before": true }], + + "arrow-spacing": ["error", { + "after": true, + "before": true, + }], + "block-scoped-var": "error", "block-spacing": "error", - "brace-style": ["error", "1tbs", { "allowSingleLine": true }], + + "brace-style": ["error", "1tbs", { + "allowSingleLine": true, + }], + "camelcase": "off", "capitalized-comments": "off", "class-methods-use-this": "off", - "comma-dangle": [ - "error", - { - "arrays": "always-multiline", - "objects": "always-multiline", - "imports": "always-multiline", - "exports": "always-multiline", - "functions": "only-multiline", - }, - ], + + "comma-dangle": ["error", { + "arrays": "always-multiline", + "objects": "always-multiline", + "imports": "always-multiline", + "exports": "always-multiline", + "functions": "only-multiline", + }], + "comma-spacing": "error", "comma-style": ["error", "last"], - "complexity": "error", + "complexity": ["error", { "max": 25 }], "computed-property-spacing": ["error", "never"], "consistent-return": "error", "consistent-this": "error", @@ -148,27 +85,36 @@ module.exports = { "default-case-last": "error", "default-param-last": "off", "dot-location": ["error", "property"], - "dot-notation": ["error", { "allowPattern": "^[a-z]+(_[a-z]+)*$" }], + + "dot-notation": ["error", { + "allowPattern": "^[a-z]+(_[a-z]+)*$", + }], + "eol-last": "error", "eqeqeq": "error", "func-call-spacing": "error", "func-name-matching": "error", "func-names": "off", + "func-style": ["error", "declaration", { - // Allow arrow functions as it better expresses referencing this - // of the containing scope, which is unweilding in plain functions. "allowArrowFunctions": true, }], + "function-call-argument-newline": "off", "function-paren-newline": "off", - "generator-star-spacing": ["error", { "before": false, "after": true }], + + "generator-star-spacing": ["error", { + "before": false, + "after": true, + }], + "grouped-accessor-pairs": "error", "guard-for-in": "error", "id-blacklist": "error", "id-length": "off", "id-match": "error", "implicit-arrow-linebreak": "error", - "indent": ["error", "tab", { "SwitchCase": 1 }], + "indent-legacy": "off", "init-declarations": "off", "jsx-quotes": "error", @@ -178,10 +124,18 @@ module.exports = { "linebreak-style": ["error", "unix"], "lines-around-comment": "off", "lines-around-directive": "off", - "lines-between-class-members": ["error", "always", { "exceptAfterSingleLine": true }], + + "lines-between-class-members": ["error", "always", { + "exceptAfterSingleLine": true, + }], + "max-classes-per-file": "off", "max-depth": "error", - "max-len": ["error", { "code": 120 }], + + "max-len": ["error", { + "code": 120, + }], + "max-lines": "off", "max-lines-per-function": "off", "max-nested-callbacks": "error", @@ -190,10 +144,12 @@ module.exports = { "max-statements-per-line": "off", "multiline-comment-style": ["error", "separate-lines"], "multiline-ternary": "off", + "new-cap": ["error", { "capIsNewExceptions": ["StringEnum", "StringKey"], "capIsNewExceptionPattern": "^Type\\.", }], + "new-parens": "error", "newline-after-var": "off", "newline-before-return": "off", @@ -207,7 +163,11 @@ module.exports = { "no-catch-shadow": "off", "no-confusing-arrow": "error", "no-console": "error", - "no-constant-condition": ["error", { "checkLoops": false }], + + "no-constant-condition": ["error", { + "checkLoops": false, + }], + "no-constructor-return": "error", "no-continue": "off", "no-div-regex": "error", @@ -232,16 +192,15 @@ module.exports = { "no-lonely-if": "error", "no-loop-func": "error", "no-magic-numbers": "off", - "no-mixed-operators": [ - "error", - { - "groups": [ - ["&", "|", "^", "~", "<<", ">>", ">>>"], - ["==", "!=", "===", "!==", ">", ">=", "<", "<="], - ["in", "instanceof"], - ], - }, - ], + + "no-mixed-operators": ["error", { + "groups": [ + ["&", "|", "^", "~", "<<", ">>", ">>>"], + ["==", "!=", "===", "!==", ">", ">=", "<", "<="], + ["in", "instanceof"], + ], + }], + "no-multi-assign": "error", "no-multi-spaces": "error", "no-multi-str": "error", @@ -256,7 +215,11 @@ module.exports = { "no-new-wrappers": "error", "no-octal-escape": "error", "no-param-reassign": "off", - "no-plusplus": ["error", { "allowForLoopAfterthoughts": true }], + + "no-plusplus": ["error", { + "allowForLoopAfterthoughts": true, + }], + "no-proto": "error", "no-restricted-exports": "error", "no-restricted-globals": "error", @@ -268,9 +231,15 @@ module.exports = { "no-script-url": "error", "no-self-compare": "error", "no-sequences": "error", - "no-shadow": ["error", { "allow": ["Event", "Request", "yargs"] }], + + "no-shadow": ["error", { + "allow": ["Event", "Request", "yargs"], + }], + "no-spaced-func": "error", - "no-tabs": ["error", { "allowIndentationTabs": true }], + + "no-tabs": "off", + "no-template-curly-in-string": "error", "no-ternary": "off", "no-throw-literal": "error", @@ -289,7 +258,11 @@ module.exports = { "no-useless-rename": "error", "no-useless-return": "error", "no-var": "error", - "no-void": ["error", { "allowAsStatement": true }], + + "no-void": ["error", { + "allowAsStatement": true, + }], + "no-warning-comments": "off", "no-whitespace-before-property": "error", "nonblock-statement-body-position": "error", @@ -299,6 +272,7 @@ module.exports = { "one-var": "off", "one-var-declaration-per-line": "error", "operator-assignment": "off", + "operator-linebreak": ["error", "after", { "overrides": { "?": "before", @@ -308,6 +282,7 @@ module.exports = { "??": "before", }, }], + "padded-blocks": "off", "padding-line-between-statements": "error", "prefer-arrow-callback": "error", @@ -324,9 +299,13 @@ module.exports = { "prefer-spread": "error", "prefer-template": "error", "quote-props": "off", - "quotes": ["error", "double", { "avoidEscape": true }], + + "quotes": ["error", "double", { + "avoidEscape": true, + }], + "radix": ["error", "always"], - "require-atomic-updates": "off", // XXX Not sure why this triggers on process.title = ... + "require-atomic-updates": "off", "require-await": "off", "require-jsdoc": "off", "require-unicode-regexp": "off", @@ -342,73 +321,182 @@ module.exports = { "space-in-parens": ["error", "never"], "space-infix-ops": "off", "space-unary-ops": "error", + "spaced-comment": ["error", "always", { "line": { "markers": ["/"], }, }], - "strict": "error", + "switch-colon-spacing": "error", "symbol-description": "error", "template-curly-spacing": ["error", "never"], "template-tag-spacing": "error", "unicode-bom": ["error", "never"], - "valid-jsdoc": ["error", { - "requireReturn": false, - "requireReturnType": false, - "requireParamType": false, - }], + "vars-on-top": "error", "wrap-iife": "error", "wrap-regex": "off", "yield-star-spacing": "error", - "yoda": ["error", "never", { "exceptRange": true }], + + "yoda": ["error", "never", { + "exceptRange": true, + }], "node/callback-return": "off", "node/exports-style": "off", "node/file-extension-in-import": "off", "node/global-require": "off", - // Node.js callback passing style of (err, value) is not used in this project "node/handle-callback-err": "off", "node/no-callback-literal": "off", - "node/no-deprecated-api": "error", - "node/no-exports-assign": "error", + "node/no-deprecated-api": "off", + "node/no-exports-assign": "off", "node/no-extraneous-import": "off", "node/no-extraneous-require": "off", - "node/no-missing-import": ["error", { - "tryExtensions": [".js", ".ts"], - }], - "node/no-missing-require": "error", - "node/no-mixed-requires": "error", - "node/no-new-require": "error", - "node/no-path-concat": "error", - "node/no-process-env": "error", - "node/no-process-exit": "error", + + "node/no-missing-import": "off", + + "node/no-missing-require": "off", + "node/no-mixed-requires": "off", + "node/no-new-require": "off", + "node/no-path-concat": "off", + "node/no-process-env": "off", + "node/no-process-exit": "off", "node/no-restricted-require": "off", "node/no-sync": "error", - "node/no-unpublished-bin": "error", - "node/no-unpublished-import": "error", - "node/no-unpublished-require": [ - "error", - { "allowModules": ["webpack", "webpack-merge", "webpack-dev-middleware"] }, - ], + "node/no-unpublished-bin": "off", + "node/no-unpublished-import": "off", + + "node/no-unpublished-require": "off", + "node/no-unsupported-features/es-builtins": "off", "node/no-unsupported-features/es-syntax": "off", "node/no-unsupported-features/node-builtins": "off", - "node/prefer-global/buffer": "error", - "node/prefer-global/console": "error", - "node/prefer-global/process": "error", - "node/prefer-global/text-decoder": "error", - "node/prefer-global/text-encoder": "error", - "node/prefer-global/url-search-params": "error", - "node/prefer-global/url": "error", + "node/prefer-global/buffer": "off", + "node/prefer-global/console": "off", + "node/prefer-global/process": "off", + "node/prefer-global/text-decoder": "off", + "node/prefer-global/text-encoder": "off", + "node/prefer-global/url-search-params": "off", + "node/prefer-global/url": "off", "node/prefer-promises/dns": "off", "node/prefer-promises/fs": "off", - "node/process-exit-as-throw": "error", - "node/shebang": ["error", { - "convertPath": { - "*.ts": ["^(.+)\\.ts$", "dist/node/$1.js"], - }, - }], + "node/process-exit-as-throw": "off", + + "node/shebang": "off", }, -}; +}, { + files: ["{packages,plugins}/**/*.ts?(x)"], + + languageOptions: { + parser: tsParser, + }, + + extends: compat.extends("plugin:@typescript-eslint/recommended"), + + "rules": { + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-useless-constructor": "error", + "@typescript-eslint/no-unused-vars": "off", + "@typescript-eslint/triple-slash-reference": "off", + + "@typescript-eslint/no-shadow": ["error", { + "allow": ["Event", "Request", "yargs"], + }], + + // Allow require imports where necessary (dynamic loading, webpack configs, etc.) + "@typescript-eslint/no-require-imports": "off", + + "prefer-const": "off", + "no-shadow": "off", + }, +}, { + files: ["test/**/*.js", "plugins/*/test/**/*.js"], + + languageOptions: { + globals: { + ...globals.mocha, + }, + + sourceType: "commonjs", + parserOptions: {}, + }, + + "rules": { + "prefer-arrow-callback": "off", + "no-invalid-this": "off", + "node/no-unpublished-import": "off", + "node/no-unpublished-require": "off", + }, +}, { + files: [ + "packages/web_ui/src/**/*.ts?(x)", + "packages/lib/browser.ts", + "packages/create/templates/*/web/**/*.{t,j}s?(x)", + "{packages,plugins}/*/web/**/*.ts?(x)", + ], + + languageOptions: { + globals: { + ...globals.browser, + }, + + "sourceType": "module", + + parserOptions: { + "ecmaFeatures": { + "jsx": true, + }, + }, + }, + + "rules": { + "node/callback-return": "off", + "node/exports-style": "off", + "node/file-extension-in-import": "off", + "node/global-require": "off", + "node/handle-callback-err": "off", + "node/no-callback-literal": "off", + "node/no-deprecated-api": "off", + "node/no-exports-assign": "off", + "node/no-extraneous-import": "off", + "node/no-extraneous-require": "off", + "node/no-missing-import": "off", + "node/no-missing-require": "off", + "node/no-mixed-requires": "off", + "node/no-new-require": "off", + "node/no-path-concat": "off", + "node/no-process-env": "off", + "node/no-process-exit": "off", + "node/no-restricted-require": "off", + "node/no-sync": "error", + "node/no-unpublished-bin": "off", + "node/no-unpublished-import": "off", + "node/no-unpublished-require": "off", + "node/no-unsupported-features/es-builtins": "off", + "node/no-unsupported-features/es-syntax": "off", + "node/no-unsupported-features/node-builtins": "off", + "node/prefer-global/buffer": "off", + "node/prefer-global/console": "off", + "node/prefer-global/process": "off", + "node/prefer-global/text-decoder": "off", + "node/prefer-global/text-encoder": "off", + "node/prefer-global/url-search-params": "off", + "node/prefer-global/url": "off", + "node/prefer-promises/dns": "off", + "node/prefer-promises/fs": "off", + "node/process-exit-as-throw": "off", + "node/shebang": "off", + }, +}, { + files: ["packages/create/templates/**/*.{t,j}s?(x)"], + + "rules": { + "node/no-missing-require": "off", + "node/no-missing-import": "off", + "spaced-comment": "off", + "comma-spacing": "off", + "max-len": "off", + "indent": "off", + }, +}, globalIgnores(["**/dist/", "dist/"])]); diff --git a/package.json b/package.json index c185e0f2..d941cee2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,7 @@ { "name": "root", "private": true, + "type": "module", "files": [], "scripts": { "build": "tsc --build", @@ -41,6 +42,9 @@ "@clusterio/host": "workspace:*", "@clusterio/lib": "workspace:*", "@clusterio/web_ui": "workspace:*", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "^9.32.0", + "@stylistic/eslint-plugin": "^5.2.2", "@swc/core": "^1.13.3", "@typescript-eslint/eslint-plugin": "^8.38.0", "@typescript-eslint/parser": "^8.38.0", @@ -49,6 +53,7 @@ "express": "^5.1.0", "form-data": "^4.0.4", "fs-extra": "^11.3.0", + "globals": "^16.3.0", "jsonwebtoken": "^9.0.2", "jszip": "^3.10.1", "mocha": "^11.7.1", From 7e2f10e8426fc5819a8172f93f9c20f4f75a3a00 Mon Sep 17 00:00:00 2001 From: Danielv123 Date: Fri, 1 Aug 2025 22:26:25 +0000 Subject: [PATCH 05/10] Fix linting issues with new rules --- packages/controller/controller.ts | 4 ++-- packages/controller/src/BaseConnection.ts | 2 +- packages/controller/src/ControlConnection.ts | 2 +- packages/controller/src/Controller.ts | 6 +++--- packages/controller/src/HostConnection.ts | 2 +- packages/create/create.js | 4 ++-- packages/create/escape_arg.js | 2 +- packages/create/templates/plugin-js/index.js | 2 +- packages/create/templates/plugin-js/web/plugin.jsx | 4 ++-- packages/create/templates/plugin-ts/web/plugin.tsx | 4 ++-- packages/ctl/ctl.ts | 2 +- packages/host/host.ts | 4 ++-- packages/host/src/Host.ts | 4 ++-- packages/host/src/export.ts | 2 +- packages/host/src/server.ts | 2 +- packages/lib/build_mod.d.ts | 14 +++++++------- packages/lib/src/data/ModPack.ts | 2 +- packages/lib/src/data/messages_core.ts | 6 +++--- packages/lib/src/datastore.ts | 4 ++-- packages/lib/src/is_deep_strict_equal.js | 6 +++--- packages/lib/src/link/WebSocket.js | 2 +- packages/lib/src/link/connectors.ts | 4 ++-- packages/lib/src/logging_utils.ts | 4 ++-- packages/lib/src/plugin_loader.ts | 2 +- packages/lib/src/shared_commands.ts | 2 +- packages/web_ui/src/components/BaseConfigTree.tsx | 2 +- plugins/subspace_storage/dole_nn_base.ts | 14 +++++++------- test/create/escape_arg.js | 2 +- test/integration/index.js | 8 ++++---- test/lib/ModStore.test.js | 8 ++++---- test/lib/data/ModPack.js | 3 +-- test/lib/plugin_loader.js | 2 +- 32 files changed, 65 insertions(+), 66 deletions(-) diff --git a/packages/controller/controller.ts b/packages/controller/controller.ts index 3d3e7cb3..e4299534 100644 --- a/packages/controller/controller.ts +++ b/packages/controller/controller.ts @@ -422,7 +422,7 @@ async function startup() { if (secondSigint) { setBlocking(true); logger.fatal("Caught second interrupt, terminating immediately"); - // eslint-disable-next-line node/no-process-exit + process.exit(1); } @@ -435,7 +435,7 @@ async function startup() { if (secondSigterm) { setBlocking(true); logger.fatal("Caught second termination, terminating immediately"); - // eslint-disable-next-line node/no-process-exit + process.exit(1); } diff --git a/packages/controller/src/BaseConnection.ts b/packages/controller/src/BaseConnection.ts index 18b02086..bda042aa 100644 --- a/packages/controller/src/BaseConnection.ts +++ b/packages/controller/src/BaseConnection.ts @@ -16,7 +16,7 @@ import * as routes from "./routes"; * @alias module:controller/src/BaseConnection */ export default class BaseConnection extends lib.Link { - declare ["connector"]: WsServerConnector; + declare "connector": WsServerConnector; constructor( connector: lib.WebSocketBaseConnector, diff --git a/packages/controller/src/ControlConnection.ts b/packages/controller/src/ControlConnection.ts index e26855c5..8b6f4f28 100644 --- a/packages/controller/src/ControlConnection.ts +++ b/packages/controller/src/ControlConnection.ts @@ -901,7 +901,7 @@ export default class ControlConnection extends BaseConnection { } // A large number of small if statements are unavoidable when reducing the number of updates sent - // eslint-disable-next-line complexity + async handleUserBulkImportRequest(request: lib.UserBulkImportRequest) { let backup: undefined | Awaited>; const updated = new Map(); diff --git a/packages/controller/src/Controller.ts b/packages/controller/src/Controller.ts index 024b0c51..b1ba92e2 100644 --- a/packages/controller/src/Controller.ts +++ b/packages/controller/src/Controller.ts @@ -399,13 +399,13 @@ export default class Controller { async _startDevServer(args: ControllerArgs) { logger.warn("Webpack development mode enabled"); - /* eslint-disable @typescript-eslint/no-var-requires, node/no-unpublished-require */ + const webpack = require("webpack"); const webpackDevMiddleware = require("webpack-dev-middleware"); const webpackConfigs = []; if (args.dev) { - // eslint-disable-next-line node/no-missing-require + webpackConfigs.push(require("../../../webpack.config")({})); // Path outside of build } if (args.devPlugin) { @@ -421,7 +421,7 @@ export default class Controller { } this.app.locals.devPlugins = devPlugins; } - /* eslint-enable @typescript-eslint/no-var-requires, node/no-unpublished-require */ + const compiler = webpack(webpackConfigs); this.devMiddleware = webpackDevMiddleware(compiler, { serverSideRender: true }); diff --git a/packages/controller/src/HostConnection.ts b/packages/controller/src/HostConnection.ts index 86dc1c56..ac23de34 100644 --- a/packages/controller/src/HostConnection.ts +++ b/packages/controller/src/HostConnection.ts @@ -47,7 +47,7 @@ export default class HostConnection extends BaseConnection { this._checkPluginVersions(); for (let event of ["connect", "drop", "resume", "close"] as const) { - // eslint-disable-next-line no-loop-func + this.connector.on(event, () => { for (let plugin of this._controller.plugins.values()) { plugin.onHostConnectionEvent(this, event); diff --git a/packages/create/create.js b/packages/create/create.js index a102c784..84d2ad2a 100755 --- a/packages/create/create.js +++ b/packages/create/create.js @@ -114,7 +114,7 @@ async function execFile(cmd, args) { escaped, { shell: true, - // eslint-disable-next-line node/no-process-env + env: process.platform === "win32" ? { ...process.env, pct: "%" } : undefined, }, (err, stdout, stderr) => { @@ -396,7 +396,7 @@ async function installClusterio(mode, plugins) { } for (let plugin of plugins) { if (!pluginList.has(plugin)) { - // eslint-disable-next-line node/global-require + let pluginInfo = require(require.resolve(plugin, { paths: [process.cwd()] })).plugin; pluginList.set(pluginInfo.name, plugin); } diff --git a/packages/create/escape_arg.js b/packages/create/escape_arg.js index 143257d2..3767ae82 100644 --- a/packages/create/escape_arg.js +++ b/packages/create/escape_arg.js @@ -50,7 +50,7 @@ function escapeArg(arg) { // For all other platforms the command is /bin/sh -c // The command and its arguments are passed as a single argument to the shell. // See https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_02 - // eslint-disable-next-line no-lonely-if + if (!/[|&;<>()$`\\"' \t*?\[#~=%]/.test(arg)) { return arg; } diff --git a/packages/create/templates/plugin-js/index.js b/packages/create/templates/plugin-js/index.js index 26c5387d..edfa6a0e 100644 --- a/packages/create/templates/plugin-js/index.js +++ b/packages/create/templates/plugin-js/index.js @@ -40,7 +40,7 @@ const plugin = { //%endif //%if controller -controllerEntrypoint: "./dist/node/controller", + controllerEntrypoint: "./dist/node/controller", //%endif //%if controller & config controllerConfigFields: { diff --git a/packages/create/templates/plugin-js/web/plugin.jsx b/packages/create/templates/plugin-js/web/plugin.jsx index 87c59767..8dc03690 100644 --- a/packages/create/templates/plugin-js/web/plugin.jsx +++ b/packages/create/templates/plugin-js/web/plugin.jsx @@ -35,9 +35,9 @@ function MyTemplatePage() { return -//%if controller // Subscribing requires web content and the controller + //%if controller // Subscribing requires web content and the controller Synced: {String(synced)} Data: {JSON.stringify([...subscribableData.values()])} -//%endif + //%endif ; } diff --git a/packages/create/templates/plugin-ts/web/plugin.tsx b/packages/create/templates/plugin-ts/web/plugin.tsx index e54e0a7c..c8b830fe 100644 --- a/packages/create/templates/plugin-ts/web/plugin.tsx +++ b/packages/create/templates/plugin-ts/web/plugin.tsx @@ -35,9 +35,9 @@ function MyTemplatePage() { return -//%if controller // Subscribing requires web content and the controller + //%if controller // Subscribing requires web content and the controller Synced: {String(synced)} Data: {JSON.stringify([...subscribableData.values()])} -//%endif + //%endif ; } diff --git a/packages/ctl/ctl.ts b/packages/ctl/ctl.ts index 349cc7ec..c2e0c08d 100755 --- a/packages/ctl/ctl.ts +++ b/packages/ctl/ctl.ts @@ -310,7 +310,7 @@ async function startControl() { control.shutdown().catch(err => { setBlocking(true); logger.error(err.stack); - // eslint-disable-next-line node/no-process-exit + process.exit(1); }); }); diff --git a/packages/host/host.ts b/packages/host/host.ts index ec706390..6a3b1a3f 100755 --- a/packages/host/host.ts +++ b/packages/host/host.ts @@ -243,7 +243,7 @@ async function startHost() { if (secondSigint) { setBlocking(true); logger.fatal("Caught second interrupt, terminating immediately"); - // eslint-disable-next-line node/no-process-exit + process.exit(1); } @@ -256,7 +256,7 @@ async function startHost() { if (secondSigterm) { setBlocking(true); logger.fatal("Caught second termination, terminating immediately"); - // eslint-disable-next-line node/no-process-exit + process.exit(1); } diff --git a/packages/host/src/Host.ts b/packages/host/src/Host.ts index 5e113251..839fac5d 100644 --- a/packages/host/src/Host.ts +++ b/packages/host/src/Host.ts @@ -250,7 +250,7 @@ export class HostRouter { * Connects to the controller over the WebSocket and manages intsances. */ export default class Host extends lib.Link { - declare ["connector"]: HostConnector; + declare "connector": HostConnector; router = new HostRouter(this); /** @@ -1201,7 +1201,7 @@ export default class Host extends lib.Link { +------------------------------------------------------------+ ${err.stack}` ); - // eslint-disable-next-line node/no-process-exit + process.exit(1); } } diff --git a/packages/host/src/export.ts b/packages/host/src/export.ts index 262a4fa2..f4fe3846 100644 --- a/packages/host/src/export.ts +++ b/packages/host/src/export.ts @@ -221,7 +221,7 @@ async function loadLayeredIcon( iconLayer.scale(realScale); } - // eslint-disable-next-line no-loop-func + iconLayer.scan(0, 0, iconLayer.bitmap.width, iconLayer.bitmap.height, (x, y, sidx) => { x += xs; y += ys; diff --git a/packages/host/src/server.ts b/packages/host/src/server.ts index 29f81e30..479037c9 100644 --- a/packages/host/src/server.ts +++ b/packages/host/src/server.ts @@ -747,7 +747,7 @@ export class FactorioServer extends events.EventEmitter { this._rconClient.on("authenticated", () => { this._rconReady = true; this.emit("rcon-ready"); - }); + }); this._rconClient.on("end", () => { this._rconClient = null; if (!this._rconReady) { diff --git a/packages/lib/build_mod.d.ts b/packages/lib/build_mod.d.ts index 064d7e81..38717dab 100644 --- a/packages/lib/build_mod.d.ts +++ b/packages/lib/build_mod.d.ts @@ -1,10 +1,10 @@ export interface BuildArgs { - factorioVersion?: string; - dependencies?: Record; - clean?: boolean; - pack?: boolean; - build?: boolean; - outputDir: string; - sourceDir: string; + factorioVersion?: string; + dependencies?: Record; + clean?: boolean; + pack?: boolean; + build?: boolean; + outputDir: string; + sourceDir: string; }; export function build(args: BuildArgs): Promise; diff --git a/packages/lib/src/data/ModPack.ts b/packages/lib/src/data/ModPack.ts index ff30bab3..f6aa2c65 100644 --- a/packages/lib/src/data/ModPack.ts +++ b/packages/lib/src/data/ModPack.ts @@ -291,7 +291,7 @@ export default class ModPack { const sizeBytes = uint32Bytes(entries.length); const itemBytes = entries.flatMap(([key, item]) => [ immutableStringBytes(key), - // eslint-disable-next-line no-use-before-define + propertyTreeBytes(item), ]); return Buffer.concat([ diff --git a/packages/lib/src/data/messages_core.ts b/packages/lib/src/data/messages_core.ts index b4b3f91e..36b85720 100644 --- a/packages/lib/src/data/messages_core.ts +++ b/packages/lib/src/data/messages_core.ts @@ -1,6 +1,6 @@ // Note: Run compile_validator script after making changes to this file. import { TSchema, Type, Static } from "@sinclair/typebox"; -// eslint-disable-next-line node/no-missing-import + import messageValidate from "./message_validate"; // generated file import { StringEnum } from "./composites"; @@ -154,7 +154,7 @@ export class Message { static validate = messageValidate; static fromJSON(json: any) { - /* eslint-disable no-use-before-define */ + if (json.type === "hello") { return MessageHello.fromJSON(json); } if (json.type === "registerHost") { return MessageRegisterHost.fromJSON(json); } if (json.type === "registerControl") { return MessageRegisterControl.fromJSON(json); } @@ -168,7 +168,7 @@ export class Message { if (json.type === "responseError") { return MessageResponseError.fromJSON(json); } if (json.type === "event") { return MessageEvent.fromJSON(json); } if (json.type === "disconnect") { return MessageDisconnect.fromJSON(json); } - /* eslint-enable no-use-before-define */ + throw new Error(`Unrecognized message type ${json.type}`); } } diff --git a/packages/lib/src/datastore.ts b/packages/lib/src/datastore.ts index a305e960..2eb56343 100644 --- a/packages/lib/src/datastore.ts +++ b/packages/lib/src/datastore.ts @@ -137,8 +137,8 @@ export abstract class Datastore< V extends DatastoreValue, U = V, > extends EventEmitter<{ - "update": [ updates: U[] ], -}> { + "update": [ updates: U[] ], + }> { private dirty = false; constructor( diff --git a/packages/lib/src/is_deep_strict_equal.js b/packages/lib/src/is_deep_strict_equal.js index 87e010d4..0f695078 100644 --- a/packages/lib/src/is_deep_strict_equal.js +++ b/packages/lib/src/is_deep_strict_equal.js @@ -1,8 +1,8 @@ -/* eslint-disable node/global-require */ + "use strict"; -// eslint-disable-next-line node/no-process-env + if (process.env.APP_ENV === "browser") { - // eslint-disable-next-line node/no-extraneous-require + module.exports = require("fast-deep-equal/es6"); } else { module.exports = require("util").isDeepStrictEqual; diff --git a/packages/lib/src/link/WebSocket.js b/packages/lib/src/link/WebSocket.js index b85227a1..06879e25 100644 --- a/packages/lib/src/link/WebSocket.js +++ b/packages/lib/src/link/WebSocket.js @@ -1,3 +1,3 @@ "use strict"; -// eslint-disable-next-line node/no-process-env + module.exports = process.env.APP_ENV === "browser" ? window.WebSocket : require("ws"); diff --git a/packages/lib/src/link/connectors.ts b/packages/lib/src/link/connectors.ts index 17806b7d..dd7df5d4 100644 --- a/packages/lib/src/link/connectors.ts +++ b/packages/lib/src/link/connectors.ts @@ -271,7 +271,7 @@ export abstract class WebSocketBaseConnector< this._check("connected"); if (Date.now() - this._lastHeartbeatMs! > 2000 * this._heartbeatInterval!) { logger.verbose("Connector | closing after heartbeat timed out"); - // eslint-disable-next-line node/no-process-env + if (process.env.APP_ENV === "browser") { this._socket!.close(ConnectionClosed.Timeout, "Heartbeat timeout"); } else { @@ -544,7 +544,7 @@ export abstract class WebSocketClientConnector extends WebSocketBaseConnector { logger.fatal(`Uncaught exception:\n${err.stack}`); process.exit(1); @@ -528,7 +528,7 @@ export function handleUnhandledErrors() { logger.fatal(`Unhandled rejection:\n${reason.stack ? reason.stack : reason}`); process.exit(1); }); - /* eslint-enable node/no-process-exit */ + } // for testing only diff --git a/packages/lib/src/plugin_loader.ts b/packages/lib/src/plugin_loader.ts index fa5307cc..45279946 100644 --- a/packages/lib/src/plugin_loader.ts +++ b/packages/lib/src/plugin_loader.ts @@ -1,4 +1,4 @@ -/* eslint-disable @typescript-eslint/no-var-requires */ + /** * Helpers for loading plugins in Node.js * @module lib/plugin_loader diff --git a/packages/lib/src/shared_commands.ts b/packages/lib/src/shared_commands.ts index 71b8b509..e65f925f 100644 --- a/packages/lib/src/shared_commands.ts +++ b/packages/lib/src/shared_commands.ts @@ -58,7 +58,7 @@ export async function handlePluginCommand( let pluginInfo: { name: string }; try { - // eslint-disable-next-line @typescript-eslint/no-var-requires + pluginInfo = require(pluginPath).plugin; } catch (err: any) { logger.error(`Unable to import plugin info from ${args.path}:\n${err.stack}`); diff --git a/packages/web_ui/src/components/BaseConfigTree.tsx b/packages/web_ui/src/components/BaseConfigTree.tsx index 23d03cf5..07e3c6ab 100644 --- a/packages/web_ui/src/components/BaseConfigTree.tsx +++ b/packages/web_ui/src/components/BaseConfigTree.tsx @@ -226,7 +226,7 @@ export default function BaseConfigTree(props: BaseConfigTreeProps) { ; } -// eslint-disable-next-line complexity + function computeTreeData( control: Control, form: FormInstance, diff --git a/plugins/subspace_storage/dole_nn_base.ts b/plugins/subspace_storage/dole_nn_base.ts index 6db51d02..7277e63d 100644 --- a/plugins/subspace_storage/dole_nn_base.ts +++ b/plugins/subspace_storage/dole_nn_base.ts @@ -61,13 +61,13 @@ export function dose( let inp1 = [numreq, instore_adj, store_last_tick / numreq, dole, carry]; // 0..4 let inp2 = [ prev_req / numreq, - numreq / numreq_total_adj, - store_last_tick / numreq_total_adj, - prev_req/instore_adj, - b, - numreq_total_adj, - store_last_tick, - debt, + numreq / numreq_total_adj, + store_last_tick / numreq_total_adj, + prev_req/instore_adj, + b, + numreq_total_adj, + store_last_tick, + debt, ]; // 5..12 let inputs = inp1.concat(inp2); let outputs = [0, 0, 0, 0]; diff --git a/test/create/escape_arg.js b/test/create/escape_arg.js index 808036c5..496737e3 100644 --- a/test/create/escape_arg.js +++ b/test/create/escape_arg.js @@ -11,7 +11,7 @@ async function exec(file, args) { const { stdout, stderr } = await execFile( file, args.map(escapeArg), - // eslint-disable-next-line node/no-process-env + { shell: true, cwd: __dirname, env: { ...process.env, pct: "%"} } ); return { stdout: JSON.parse(stdout), stderr }; diff --git a/test/integration/index.js b/test/integration/index.js index 7a31d23e..fbbc5fb1 100644 --- a/test/integration/index.js +++ b/test/integration/index.js @@ -120,7 +120,7 @@ class TestHostConnector extends lib.WebSocketClientConnector { // Mark that this test takes a lot of time, or depeneds on a test // that takes a lot of time. function slowTest(test) { - // eslint-disable-next-line node/no-process-env + if (process.env.FAST_TEST) { test.skip(); } @@ -221,7 +221,7 @@ function getControl() { } function spawn(name, cmd, waitFor) { - // eslint-disable-next-line node/no-process-env + const silent = process.env.SILENT_TEST; const bootstrap = !controllerProcess || !hostProcess; function log(...args) { @@ -264,7 +264,7 @@ async function spawnNode(name, cmd, waitFor) { before(async function() { this.timeout(40000); - // eslint-disable-next-line node/no-process-env + const silent = process.env.SILENT_TEST; if (silent) { console.log("SILENT_TEST is present in env, loggers after bootstrap will be muted."); @@ -278,7 +278,7 @@ before(async function() { })); // If fast test is enabled then output that it is - // eslint-disable-next-line node/no-process-env + if (process.env.FAST_TEST) { console.log("FAST_TEST is present in env, slow tests will be skipped."); } diff --git a/test/lib/ModStore.test.js b/test/lib/ModStore.test.js index 7730eadd..75c2d1c9 100644 --- a/test/lib/ModStore.test.js +++ b/test/lib/ModStore.test.js @@ -816,24 +816,24 @@ describe("lib/ModStore", function () { assert.equal( typeof firstMod.latest_release.version, "string", - "latest_release version should be string" // eslint-disable-line max-len + "latest_release version should be string" ); assert.equal( typeof firstMod.latest_release.sha1, "string", - "latest_release should have a sha1 string" // eslint-disable-line max-len + "latest_release should have a sha1 string" ); assert.ok(firstMod.latest_release.info_json, "latest_release should have info_json"); assert.equal( typeof firstMod.latest_release.info_json.factorio_version, "string", - "info_json should have factorio_version string" // eslint-disable-line max-len + "info_json should have factorio_version string" ); // Check for an expected field from the user example assert.equal( typeof firstMod.downloads_count, "number", - "First mod should have downloads_count number" // eslint-disable-line max-len + "First mod should have downloads_count number" ); } else { // This should not happen for v1.1 but handles edge cases diff --git a/test/lib/data/ModPack.js b/test/lib/data/ModPack.js index e0dfe8cc..b63b9794 100644 --- a/test/lib/data/ModPack.js +++ b/test/lib/data/ModPack.js @@ -303,7 +303,7 @@ describe("lib/data/ModPack", function() { Buffer.from(str), ]; } - /* eslint-disable indent */ + /* eslint-disable @stylistic/indent */ assert.deepEqual( pack.toModSettingsDat(), Buffer.concat([ @@ -373,7 +373,6 @@ describe("lib/data/ModPack", function() { new Uint8Array(Float64Array.from([1]).buffer), ]) ); - /* eslint-enable indent */ }); }); describe("defaultModPacks", function() { diff --git a/test/lib/plugin_loader.js b/test/lib/plugin_loader.js index fd5126bb..308e3716 100644 --- a/test/lib/plugin_loader.js +++ b/test/lib/plugin_loader.js @@ -46,7 +46,7 @@ describe("lib/plugin_loader", function() { it("should reject on broken plugin", async function() { let brokenMessage; try { - // eslint-disable-next-line node/global-require + require(path.resolve(brokenPlugin)); } catch (err) { brokenMessage = err.message; From 7ac8cd3781517af2634cf4964f0dca76af79dd00 Mon Sep 17 00:00:00 2001 From: Danielv123 Date: Fri, 1 Aug 2025 23:26:47 +0000 Subject: [PATCH 06/10] Fix mocha arguments leaking into yargs parser --- package.json | 1 - packages/ctl/ctl.ts | 26 ++++++++++++++++++-------- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/package.json b/package.json index d941cee2..068ef25e 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,6 @@ { "name": "root", "private": true, - "type": "module", "files": [], "scripts": { "build": "tsc --build", diff --git a/packages/ctl/ctl.ts b/packages/ctl/ctl.ts index c2e0c08d..bafb43af 100755 --- a/packages/ctl/ctl.ts +++ b/packages/ctl/ctl.ts @@ -5,7 +5,7 @@ * @module ctl/ctl */ import fs from "fs-extra"; -import yargs from "yargs"; +import yargs, { type Argv } from "yargs"; import path from "path"; import { version } from "./package.json"; import setBlocking from "set-blocking"; @@ -156,7 +156,17 @@ export async function initialize( ctlPlugins?: Map, noLoggerTransport?: boolean, ): Promise { - yargs + // Build a fresh, isolated yargs parser each time this function is called. + // If the currently loaded yargs object supports .reset() we use that (older versions), + // otherwise we fall back to creating a brand-new parser instance by invoking the + // yargs factory function directly with an empty argv array. This avoids Mocha’s + // own CLI flags (or anything else that touched yargs earlier in the same process) + // from leaking into subsequent parses inside the test runner. + const parser: Argv = typeof (yargs as any).reset === "function" + ? (yargs as any).reset() + : (yargs as any)([]); + + parser .scriptName("clusterioctl") .usage("$0 [options]") .option("log-level", { @@ -181,13 +191,13 @@ export async function initialize( }) .command("plugin", "Manage available plugins", lib.pluginCommand) .command("control-config", "Manage Control config", lib.configCommand) - .wrap(yargs.terminalWidth()) + .wrap(parser.terminalWidth()) .help(false) // Disable help to avoid triggering it on the first parse. ; // Parse the args first to get the configured plugin list. // eslint-disable-next-line node/no-sync - let args = yargs.parseSync(argv) as CtlArguments; + let args = parser.parseSync(argv) as CtlArguments; // Log stream for the ctl session. Skipped in testing. if (!noLoggerTransport) { @@ -217,8 +227,8 @@ export async function initialize( } // Add all commands including from plugins and reparse with help and strict checking. - const rootCommands = await commands.registerCommands(ctlPlugins, yargs); - args = yargs + const rootCommands = await commands.registerCommands(ctlPlugins, parser); + args = parser .help() .strict() .parse(argv) as CtlArguments @@ -241,8 +251,8 @@ export async function initialize( } if (args._.length === 0) { - yargs.showHelp(); - yargs.exit(1, undefined as unknown as Error); // Type definition file is wrong. + parser.showHelp(); + parser.exit(1, undefined as unknown as Error); // Type definition file is wrong. } // Handle the control-config command before trying to connect. From b9e2acaaa5727591d55c7f9530cc82866ae49c30 Mon Sep 17 00:00:00 2001 From: Danielv123 Date: Fri, 1 Aug 2025 23:29:41 +0000 Subject: [PATCH 07/10] eslint needs to be ecmascript module --- eslint.config.js => eslint.config.mjs | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename eslint.config.js => eslint.config.mjs (100%) diff --git a/eslint.config.js b/eslint.config.mjs similarity index 100% rename from eslint.config.js rename to eslint.config.mjs From 9840f22688519a971c54c96a0f848108cc11716b Mon Sep 17 00:00:00 2001 From: Danielv123 Date: Sat, 2 Aug 2025 00:13:04 +0000 Subject: [PATCH 08/10] Wait for savefile to be moved --- test/integration/clusterio.js | 37 +++++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/test/integration/clusterio.js b/test/integration/clusterio.js index ce6b8099..0a34f6b3 100644 --- a/test/integration/clusterio.js +++ b/test/integration/clusterio.js @@ -92,6 +92,16 @@ async function uploadSave(instanceId, name, content) { }); } +async function waitForFileExists(filePath, timeoutMs = 5000) { + const start = Date.now(); + while (!(await fs.pathExists(filePath))) { + if (Date.now() - start > timeoutMs) { + throw new Error(`Timed out waiting for file ${filePath}`); + } + await new Promise(resolve => setTimeout(resolve, 100)); + } +} + async function deleteSave(instanceId, save) { await getControl().send(new lib.InstanceDeleteSaveRequest(instanceId, save)); } @@ -1055,13 +1065,13 @@ describe("Integration of Clusterio", function() { describe("instance save transfer", function() { before(async function() { slowTest(this); - await execCtl("instance create spam --id 66"); - await execCtl("instance assign spam 4"); - await execCtl("instance create unassign --id 77"); + await execCtlProcess("instance create spam --id 66"); + await execCtlProcess("instance assign spam 4"); + await execCtlProcess("instance create unassign --id 77"); }); after(async function() { try { - await execCtl("instance delete spam"); + await execCtlProcess("instance delete spam"); } catch (err) { // Ignore } @@ -1075,14 +1085,16 @@ describe("Integration of Clusterio", function() { : path.join("temp", "test", "instances", "spam", "saves") ; describe(remote ? "remote" : "local", function() { + // Allow extra time for file transfers on slower Node versions + this.timeout(10000); if (remote) { let hostProcess; before(async function() { slowTest(this); hostProcess = await startAltHost(); - await execCtl("instance create save-test --id 88"); - await execCtl("instance assign save-test 5"); + await execCtlProcess("instance create save-test --id 88"); + await execCtlProcess("instance assign save-test 5"); }); after(async function() { await stopAltHost(hostProcess); @@ -1090,14 +1102,16 @@ describe("Integration of Clusterio", function() { } it("should transfers a save", async function() { await uploadSave(pri, "transfer.zip", "transfer.zip"); - await execCtl(`instance save transfer ${pri} transfer.zip ${sec}`); + await execCtlProcess(`instance save transfer ${pri} transfer.zip ${sec}`); + await waitForFileExists(path.join(secSaves, "transfer.zip")); assert(!await fs.pathExists(path.join(priSaves, "transfer.zip")), "save still at pri"); assert(await fs.pathExists(path.join(secSaves, "transfer.zip")), "save not at sec"); await deleteSave(sec, "transfer.zip"); }); it("should support rename", async function() { await uploadSave(pri, "transfer.zip", "transfer.zip"); - await execCtl(`instance save transfer ${pri} transfer.zip ${sec} rename.zip`); + await execCtlProcess(`instance save transfer ${pri} transfer.zip ${sec} rename.zip`); + await waitForFileExists(path.join(secSaves, "rename.zip")); assert(!await fs.pathExists(path.join(priSaves, "transfer.zip")), "save still at pri"); assert(await fs.pathExists(path.join(secSaves, "rename.zip")), "save not at sec"); await deleteSave(sec, "rename.zip"); @@ -1105,15 +1119,18 @@ describe("Integration of Clusterio", function() { it("should auto-rename if target exists", async function() { await uploadSave(pri, "transfer.zip", "transfer.zip"); await uploadSave(sec, "transfer.zip", "transfer.zip"); - await execCtl(`instance save transfer ${pri} transfer.zip ${sec}`); + await execCtlProcess(`instance save transfer ${pri} transfer.zip ${sec}`); + await waitForFileExists(path.join(secSaves, "transfer.zip")); assert(!await fs.pathExists(path.join(priSaves, "transfer.zip")), "save still at pri"); + await waitForFileExists(path.join(secSaves, "transfer-2.zip")); assert(await fs.pathExists(path.join(secSaves, "transfer-2.zip")), "save not at sec"); await deleteSave(sec, "transfer.zip"); await deleteSave(sec, "transfer-2.zip"); }); it("should copy when using --copy", async function() { await uploadSave(pri, "transfer.zip", "transfer.zip"); - await execCtl(`instance save transfer ${pri} transfer.zip ${sec} --copy`); + await execCtlProcess(`instance save transfer ${pri} transfer.zip ${sec} --copy`); + await waitForFileExists(path.join(secSaves, "transfer.zip")); assert(await fs.pathExists(path.join(priSaves, "transfer.zip")), "save not at pri"); assert(await fs.pathExists(path.join(secSaves, "transfer.zip")), "save not at sec"); await deleteSave(pri, "transfer.zip"); From 80983662aaa4fcd1caf708ccef3d61737cb9f850 Mon Sep 17 00:00:00 2001 From: Danielv123 Date: Mon, 4 Aug 2025 16:14:07 +0000 Subject: [PATCH 09/10] Wait for promise to resolve before returning --- packages/ctl/src/commands.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ctl/src/commands.ts b/packages/ctl/src/commands.ts index 3255b881..070e63f5 100644 --- a/packages/ctl/src/commands.ts +++ b/packages/ctl/src/commands.ts @@ -2062,7 +2062,7 @@ userCommands.add(new lib.Command({ exportType = "whitelist"; } - fs.writeJSON(args.filepath, await control.send( + await fs.writeJSON(args.filepath, await control.send( new lib.UserBulkExportRequest(exportType) ), { spaces: 2 }); print(`Exported ${exportType} to ${args.filepath}`); From 376eaeba5201e2c5d6624c3cad90ca36b35fd7d0 Mon Sep 17 00:00:00 2001 From: Danielv123 Date: Mon, 4 Aug 2025 16:25:56 +0000 Subject: [PATCH 10/10] Revert save writing delay during tests --- test/integration/clusterio.js | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/test/integration/clusterio.js b/test/integration/clusterio.js index 0a34f6b3..43fe317c 100644 --- a/test/integration/clusterio.js +++ b/test/integration/clusterio.js @@ -92,16 +92,6 @@ async function uploadSave(instanceId, name, content) { }); } -async function waitForFileExists(filePath, timeoutMs = 5000) { - const start = Date.now(); - while (!(await fs.pathExists(filePath))) { - if (Date.now() - start > timeoutMs) { - throw new Error(`Timed out waiting for file ${filePath}`); - } - await new Promise(resolve => setTimeout(resolve, 100)); - } -} - async function deleteSave(instanceId, save) { await getControl().send(new lib.InstanceDeleteSaveRequest(instanceId, save)); } @@ -1085,7 +1075,6 @@ describe("Integration of Clusterio", function() { : path.join("temp", "test", "instances", "spam", "saves") ; describe(remote ? "remote" : "local", function() { - // Allow extra time for file transfers on slower Node versions this.timeout(10000); if (remote) { let hostProcess; @@ -1103,7 +1092,6 @@ describe("Integration of Clusterio", function() { it("should transfers a save", async function() { await uploadSave(pri, "transfer.zip", "transfer.zip"); await execCtlProcess(`instance save transfer ${pri} transfer.zip ${sec}`); - await waitForFileExists(path.join(secSaves, "transfer.zip")); assert(!await fs.pathExists(path.join(priSaves, "transfer.zip")), "save still at pri"); assert(await fs.pathExists(path.join(secSaves, "transfer.zip")), "save not at sec"); await deleteSave(sec, "transfer.zip"); @@ -1111,7 +1099,6 @@ describe("Integration of Clusterio", function() { it("should support rename", async function() { await uploadSave(pri, "transfer.zip", "transfer.zip"); await execCtlProcess(`instance save transfer ${pri} transfer.zip ${sec} rename.zip`); - await waitForFileExists(path.join(secSaves, "rename.zip")); assert(!await fs.pathExists(path.join(priSaves, "transfer.zip")), "save still at pri"); assert(await fs.pathExists(path.join(secSaves, "rename.zip")), "save not at sec"); await deleteSave(sec, "rename.zip"); @@ -1120,9 +1107,7 @@ describe("Integration of Clusterio", function() { await uploadSave(pri, "transfer.zip", "transfer.zip"); await uploadSave(sec, "transfer.zip", "transfer.zip"); await execCtlProcess(`instance save transfer ${pri} transfer.zip ${sec}`); - await waitForFileExists(path.join(secSaves, "transfer.zip")); assert(!await fs.pathExists(path.join(priSaves, "transfer.zip")), "save still at pri"); - await waitForFileExists(path.join(secSaves, "transfer-2.zip")); assert(await fs.pathExists(path.join(secSaves, "transfer-2.zip")), "save not at sec"); await deleteSave(sec, "transfer.zip"); await deleteSave(sec, "transfer-2.zip"); @@ -1130,7 +1115,6 @@ describe("Integration of Clusterio", function() { it("should copy when using --copy", async function() { await uploadSave(pri, "transfer.zip", "transfer.zip"); await execCtlProcess(`instance save transfer ${pri} transfer.zip ${sec} --copy`); - await waitForFileExists(path.join(secSaves, "transfer.zip")); assert(await fs.pathExists(path.join(priSaves, "transfer.zip")), "save not at pri"); assert(await fs.pathExists(path.join(secSaves, "transfer.zip")), "save not at sec"); await deleteSave(pri, "transfer.zip");