diff --git a/AGENTS.md b/AGENTS.md index aca39741..3fd24a95 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -18,7 +18,7 @@ Use the detailed guides for the affected area: - Read the relevant source and tests before changing behavior. If documentation and implementation disagree, report the discrepancy and distinguish observed behavior from intended policy. Update affected documentation in the same change. -- Follow nearby code and `eslint.config.mjs`: tabs, double quotes, semicolons, and `.js` extensions in relative TypeScript imports. +- Follow nearby code and the `lint` section of `vite.config.ts`: tabs, double quotes, semicolons, and `.js` extensions in relative TypeScript imports. Import shared public APIs through `@clusterio/lib` and check Node/browser exports when changing them. - Run commands from the root using the pinned pnpm version and the [documented Node requirements](docs/contributing.md#supported-nodejs-and-factorio-versions). Install with `pnpm install --no-frozen-lockfile`; the lockfile is intentionally ignored. diff --git a/docs/decisions.md b/docs/decisions.md index 088c0ed1..3afb7fc6 100644 --- a/docs/decisions.md +++ b/docs/decisions.md @@ -18,7 +18,7 @@ If there is a particular issue or concern you would like the team to comment on - [Naming Times and Durations](#naming-times-and-durations) - [Strings](#strings) 3. Code Style (TS / JS / Web) - - [ESLint](#eslint) + - [Oxlint](#oxlint) - [Imports Within `lib` Package](#imports-within-lib-package) - [Name Style](#name-style-ts) 4. Code Style (Lua) @@ -157,14 +157,15 @@ Therefore, we selected double quotes as our standard. ## Code Style (TS / JS / Web) -### ESLint +### Oxlint -🧾 Time Immemorial +🧾 20th October 2026 -> Use ESLint for style checking. +> Use Oxlint through `vp lint` for style checking, with formatting rules from `@stylistic/eslint-plugin`. -We needed to maintain a consistent style across all our files. -Therefore, we utilise the industry-standard ESLint, as it is familiar and highly configurable. +We needed to maintain a consistent style across all our files and used ESLint for it from the start. +Oxlint runs the same rules about three times faster, reads ESLint disable comments, and does not depend on the TypeScript compiler API. +The formatting rules ESLint deprecated run through `@stylistic/eslint-plugin` loaded as an Oxlint JS plugin, so the code style did not change. ### Imports Within `lib` Package diff --git a/eslint.config.mjs b/eslint.config.mjs deleted file mode 100644 index e75d2f0c..00000000 --- a/eslint.config.mjs +++ /dev/null @@ -1,504 +0,0 @@ -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, - }, - - languageOptions: { - globals: { - ...globals.node, - ...globals.commonjs, - }, - }, - - "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"], - "array-callback-return": "error", - "array-element-newline": "off", - "arrow-body-style": "error", - "arrow-parens": "off", - - "arrow-spacing": ["error", { - "after": true, - "before": true, - }], - - "block-scoped-var": "error", - "block-spacing": "error", - - "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-spacing": "error", - "comma-style": ["error", "last"], - "complexity": ["error", { "max": 25 }], - "computed-property-spacing": ["error", "never"], - "consistent-return": "error", - "consistent-this": "error", - "curly": "error", - "default-case": "error", - "default-case-last": "error", - "default-param-last": "off", - "dot-location": ["error", "property"], - - "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", { - "allowArrowFunctions": true, - }], - - "function-call-argument-newline": "off", - "function-paren-newline": "off", - - "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-legacy": "off", - "init-declarations": "off", - "jsx-quotes": "error", - "key-spacing": "error", - "keyword-spacing": "error", - "line-comment-position": "off", - "linebreak-style": ["error", "unix"], - "lines-around-comment": "off", - "lines-around-directive": "off", - - "lines-between-class-members": ["error", "always", { - "exceptAfterSingleLine": true, - }], - - "max-classes-per-file": "off", - "max-depth": "error", - - "max-len": ["error", { - "code": 120, - }], - - "max-lines": "off", - "max-lines-per-function": "off", - "max-nested-callbacks": "error", - "max-params": "off", - "max-statements": "off", - "max-statements-per-line": "off", - "multiline-comment-style": ["error", "separate-lines"], - "multiline-ternary": "off", - - "new-cap": ["error", { - "capIsNewExceptions": ["StringEnum", "StringKey"], - "capIsNewExceptionPattern": "^Type\\.", - "newIsCapExceptions": ["constructor"], - }], - - "new-parens": "error", - "newline-after-var": "off", - "newline-before-return": "off", - "newline-per-chained-call": "off", - "no-alert": "error", - "no-array-constructor": "error", - "no-await-in-loop": "off", - "no-bitwise": "off", - "no-buffer-constructor": "error", - "no-caller": "error", - "no-catch-shadow": "off", - "no-confusing-arrow": "error", - "no-console": "error", - - "no-constant-condition": ["error", { - "checkLoops": false, - }], - - "no-constructor-return": "error", - "no-continue": "off", - "no-div-regex": "error", - "no-duplicate-imports": "error", - "no-else-return": "error", - "no-empty-function": "off", - "no-eq-null": "error", - "no-eval": "error", - "no-extend-native": "error", - "no-extra-bind": "error", - "no-extra-label": "error", - "no-extra-parens": "off", - "no-floating-decimal": "error", - "no-implicit-coercion": "error", - "no-implicit-globals": "error", - "no-implied-eval": "error", - "no-inline-comments": "off", - "no-invalid-this": "error", - "no-iterator": "error", - "no-label-var": "error", - "no-lone-blocks": "error", - "no-lonely-if": "error", - "no-loop-func": "error", - "no-magic-numbers": "off", - - "no-mixed-operators": ["error", { - "groups": [ - ["&", "|", "^", "~", "<<", ">>", ">>>"], - ["==", "!=", "===", "!==", ">", ">=", "<", "<="], - ["in", "instanceof"], - ], - }], - - "no-multi-assign": "error", - "no-multi-spaces": "error", - "no-multi-str": "error", - "no-multiple-empty-lines": "error", - "no-native-reassign": "error", - "no-negated-condition": "off", - "no-negated-in-lhs": "error", - "no-nested-ternary": "error", - "no-new": "error", - "no-new-func": "error", - "no-new-object": "error", - "no-new-wrappers": "error", - "no-octal-escape": "error", - "no-param-reassign": "off", - - "no-plusplus": ["error", { - "allowForLoopAfterthoughts": true, - }], - - "no-proto": "error", - "no-restricted-exports": "error", - "no-restricted-globals": "error", - "no-restricted-imports": "error", - "no-restricted-properties": "error", - "no-restricted-syntax": "error", - "no-return-assign": "error", - "no-return-await": "off", - "no-script-url": "error", - "no-self-compare": "error", - "no-sequences": "error", - - "no-shadow": ["error", { - "allow": ["Event", "Request", "yargs"], - }], - - "no-spaced-func": "error", - - "no-tabs": "off", - - "no-template-curly-in-string": "error", - "no-ternary": "off", - "no-throw-literal": "error", - "no-trailing-spaces": "error", - "no-undef-init": "error", - "no-undefined": "off", - "no-unmodified-loop-condition": "error", - "no-unneeded-ternary": "error", - "no-unused-expressions": "error", - "no-use-before-define": "off", - "no-useless-backreference": "error", - "no-useless-call": "error", - "no-useless-computed-key": "error", - "no-useless-concat": "error", - "no-useless-constructor": "off", - "no-useless-rename": "error", - "no-useless-return": "error", - "no-var": "error", - - "no-void": ["error", { - "allowAsStatement": true, - }], - - "no-warning-comments": "off", - "no-whitespace-before-property": "error", - "nonblock-statement-body-position": "error", - "object-curly-newline": "error", - "object-curly-spacing": "off", - "object-shorthand": "off", - "one-var": "off", - "one-var-declaration-per-line": "error", - "operator-assignment": "off", - - "operator-linebreak": ["error", "after", { - "overrides": { - "?": "before", - ":": "before", - "||": "before", - "&&": "before", - "??": "before", - }, - }], - - "padded-blocks": "off", - "padding-line-between-statements": "error", - "prefer-arrow-callback": "error", - "prefer-const": "off", - "prefer-destructuring": "off", - "prefer-exponentiation-operator": "error", - "prefer-named-capture-group": "off", - "prefer-numeric-literals": "error", - "prefer-object-spread": "error", - "prefer-promise-reject-errors": "error", - "prefer-reflect": "off", - "prefer-regex-literals": "error", - "prefer-rest-params": "error", - "prefer-spread": "error", - "prefer-template": "error", - "quote-props": "off", - - "quotes": ["error", "double", { - "avoidEscape": true, - }], - - "radix": ["error", "always"], - "require-atomic-updates": "off", - "require-await": "off", - "require-jsdoc": "off", - "require-unicode-regexp": "off", - "rest-spread-spacing": ["error", "never"], - "semi": "error", - "semi-spacing": "error", - "semi-style": "off", - "sort-imports": "off", - "sort-keys": "off", - "sort-vars": "off", - "space-before-blocks": "error", - "space-before-function-paren": "off", - "space-in-parens": ["error", "never"], - "space-infix-ops": "off", - "space-unary-ops": "error", - - "spaced-comment": ["error", "always", { - "line": { - "markers": ["/"], - }, - }], - - "switch-colon-spacing": "error", - "symbol-description": "error", - "template-curly-spacing": ["error", "never"], - "template-tag-spacing": "error", - "unicode-bom": ["error", "never"], - - "vars-on-top": "error", - "wrap-iife": "error", - "wrap-regex": "off", - "yield-star-spacing": "error", - - "yoda": ["error", "never", { - "exceptRange": true, - }], - - "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,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: "module", - 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/**/*.?(c){t,j}s?(x)"], - - "rules": { - "node/no-missing-require": "off", - "node/no-missing-import": "off", - "@stylistic/indent": "off", - "spaced-comment": "off", - "comma-spacing": "off", - "max-len": "off", - "indent": "off", - }, -}, globalIgnores(["**/dist/", "dist/"])]); diff --git a/package.json b/package.json index 503e2c4c..d436a0ca 100644 --- a/package.json +++ b/package.json @@ -15,8 +15,8 @@ "cover": "c8 pnpm test", "fast-cover": "FAST_TEST=y c8 pnpm test", "ci-cover": "c8 --reporter=lcovonly pnpm test", - "lint": "eslint packages plugins test", - "lint-fix": "eslint --fix packages plugins test", + "lint": "vp lint packages plugins test", + "lint-fix": "vp lint --fix packages plugins test", "clean": "node ./clean.js", "docs": "typedoc", "bundle-dependencies": "bundle-dependencies" @@ -43,22 +43,16 @@ "@clusterio/host": "workspace:^", "@clusterio/lib": "workspace:^", "@clusterio/web_ui": "workspace:^", - "@eslint/eslintrc": "^3.3.7", - "@eslint/js": "^10.0.1", "@stylistic/eslint-plugin": "^5.10.0", "@types/node": "catalog:", "c8": "^12.0.0", - "@typescript-eslint/eslint-plugin": "^8.71.2", - "@typescript-eslint/parser": "^8.71.2", - "eslint": "^10.13.0", - "eslint-plugin-node": "^11.1.0", "express": "catalog:", "form-data": "^4.0.6", - "globals": "^17.13.0", "jsonwebtoken": "catalog:", "mocha": "^11.8.0", "typedoc": "^0.28.20", "typescript": "catalog:", + "vite-plus": "^1.3.0", "yargs": "catalog:", "yauzl": "catalog:", "yazl": "catalog:" diff --git a/packages/controller/src/BaseControllerPlugin.ts b/packages/controller/src/BaseControllerPlugin.ts index 8a74db3a..5b5d0874 100644 --- a/packages/controller/src/BaseControllerPlugin.ts +++ b/packages/controller/src/BaseControllerPlugin.ts @@ -107,7 +107,7 @@ export class ControllerHooks extends lib.AsyncHookCollection { * @param curr - The current value of the field. * @param prev - The previous value of the field. */ - // eslint-disable-next-line max-len + // eslint-disable-next-line @stylistic/max-len readonly instanceConfigFieldChanged: lib.AsyncHook<[instance: InstanceRecord, field: string, curr: unknown, prev: unknown]>; /** @@ -165,7 +165,7 @@ export class ControllerHooks extends lib.AsyncHookCollection { * The connection the event occured on. * @param event - one of connect, drop, resume and close */ - // eslint-disable-next-line max-len + // eslint-disable-next-line @stylistic/max-len readonly hostConnectionEvent: lib.AsyncHook<[connection: HostConnection, event: "connect" | "drop" | "resume" | "close"]>; /** @@ -202,7 +202,7 @@ export class ControllerHooks extends lib.AsyncHookCollection { * The connection the event occured on. * @param event - one of connect, drop, resume, and close. */ - // eslint-disable-next-line max-len + // eslint-disable-next-line @stylistic/max-len readonly controlConnectionEvent: lib.AsyncHook<[connection: ControlConnection, event: "connect" | "drop" | "resume" | "close"]>; /** diff --git a/packages/controller/src/Controller.ts b/packages/controller/src/Controller.ts index 3cce5b8f..bee2f319 100644 --- a/packages/controller/src/Controller.ts +++ b/packages/controller/src/Controller.ts @@ -52,7 +52,7 @@ type ControllerDebugEvents = { type ControllerEvents = { "stop": [], -} +}; /** * Manages all controller related operations diff --git a/packages/controller/src/routes.ts b/packages/controller/src/routes.ts index 505eea63..ac262712 100644 --- a/packages/controller/src/routes.ts +++ b/packages/controller/src/routes.ts @@ -141,7 +141,7 @@ function getPlugins(req: Request, res: Response) { // Note: Cast through unknown is needed because load_plugin is // defined at runtime and no other fields in the controller config // currently have the boolean type. - let web: { main:any, error:string|undefined } = { main: undefined, error: undefined}; + let web: { main: any, error: string|undefined } = { main: undefined, error: undefined}; let devPlugins = req.app.locals.devPlugins; if (devPlugins && devPlugins.has(name)) { web.main = devPlugins.get(name); @@ -305,7 +305,7 @@ type ProxyStreamEvents = { "close": [], "timeout": [], "source": [], -} +}; export interface ProxyStream { id: string; diff --git a/packages/controller/web/index.tsx b/packages/controller/web/index.tsx index 59e6ae18..a701e46c 100644 --- a/packages/controller/web/index.tsx +++ b/packages/controller/web/index.tsx @@ -1,3 +1,4 @@ +// eslint-disable-next-line no-implicit-globals declare function scriptError(err: any): void; import("@clusterio/web_ui") .then(webUi => webUi.bootstrap()) diff --git a/packages/ctl/src/config_ops.ts b/packages/ctl/src/config_ops.ts index 965b247a..6a876cd4 100644 --- a/packages/ctl/src/config_ops.ts +++ b/packages/ctl/src/config_ops.ts @@ -2,7 +2,7 @@ import * as lib from "@clusterio/lib"; export async function getEditor(argsEditor: string) { // eslint-disable-next-line - return argsEditor || process.env.EDITOR || process.env.VISUAL || undefined + return argsEditor || process.env.EDITOR || process.env.VISUAL || undefined; // needed for the process.env statements to not be flagged by eslint // priority for editors is CLI argument > env.EDITOR > env.VISUAL } diff --git a/packages/lib/build_mod.d.ts b/packages/lib/build_mod.d.ts index 38717dab..1ff1b1dd 100644 --- a/packages/lib/build_mod.d.ts +++ b/packages/lib/build_mod.d.ts @@ -6,5 +6,5 @@ export interface BuildArgs { build?: boolean; outputDir: string; sourceDir: string; -}; +} export function build(args: BuildArgs): Promise; diff --git a/packages/lib/src/LockFile.ts b/packages/lib/src/LockFile.ts index d1ba9fed..91fec84b 100644 --- a/packages/lib/src/LockFile.ts +++ b/packages/lib/src/LockFile.ts @@ -264,7 +264,9 @@ export class LockFile { // Event handler for synchronous process exit, readLock can't be used as it is async private handleExitSync = (): void => { try { + // eslint-disable-next-line node/no-sync if (readFileSync(this.filePath, "utf8") === LockFile.content) { + // eslint-disable-next-line node/no-sync unlinkSync(this.filePath); } } catch (err) { diff --git a/packages/lib/src/api.ts b/packages/lib/src/api.ts index 86a25393..94df8acf 100644 --- a/packages/lib/src/api.ts +++ b/packages/lib/src/api.ts @@ -14,4 +14,4 @@ export type PluginWebApi = { * package. */ npmPackage?: string; -} +}; diff --git a/packages/lib/src/config/classes.ts b/packages/lib/src/config/classes.ts index 6e2e9807..19a7dbe0 100644 --- a/packages/lib/src/config/classes.ts +++ b/packages/lib/src/config/classes.ts @@ -24,7 +24,7 @@ type OldConfigGroupSchema = Static; const OldConfigSchema = Type.Object({ "groups": Type.Array(OldConfigGroupSchema), }); -export type OldConfigSchema = Static +export type OldConfigSchema = Static; export const ConfigSchema = Type.Record(Type.String(), Type.Union([ Type.Null(), @@ -38,11 +38,11 @@ export type ConfigSchema = Static; type FieldValue = null | boolean | string | number | object; type FieldType = - T extends boolean ? "boolean" : - T extends string ? "string" : - T extends number ? "number" : - T extends object ? "object" : - never; + T extends boolean ? "boolean" + : T extends string ? "string" + : T extends number ? "number" + : T extends object ? "object" + : never; export interface FieldDefinition< Type extends FieldValue = any, @@ -134,7 +134,7 @@ export type ConfigDefs< Fields extends Record > = { [Prop in keyof Fields]: FieldDefinition; -} +}; function defaultValue(def: FieldDefinition) { if (def.initialValue === undefined) { @@ -148,7 +148,7 @@ function defaultValue(def: FieldDefinition) { export type ConfigEvents = { fieldChanged: [ name: string, curr: FieldValue, prev: FieldValue ], -} +}; export enum ConfigAccess { read = 0x1, diff --git a/packages/lib/src/datastore.ts b/packages/lib/src/datastore.ts index c5f58282..212a7bbb 100644 --- a/packages/lib/src/datastore.ts +++ b/packages/lib/src/datastore.ts @@ -15,8 +15,8 @@ export abstract class DatastoreProvider< K extends DatastoreKey, V extends DatastoreValue, > { - abstract save(data: Map): Promise - abstract load(): Promise> + abstract save(data: Map): Promise; + abstract load(): Promise>; } // Store all data in memory, used as a default and should be avoided diff --git a/packages/lib/src/export.ts b/packages/lib/src/export.ts index 2ae9eb3f..6086af09 100644 --- a/packages/lib/src/export.ts +++ b/packages/lib/src/export.ts @@ -35,7 +35,7 @@ export type FactorioModSettingType = "startup" | "runtime-global" | "runtime-per * Boolean mod setting prototype. * @see [bool-setting](https://wiki.factorio.com/Tutorial:Mod_settings#bool-setting) */ -export interface FactorioBoolSettingPrototype extends FactorioPrototypeBase{ +export interface FactorioBoolSettingPrototype extends FactorioPrototypeBase { type: "bool-setting", setting_type?: FactorioModSettingType, default_value: boolean, @@ -46,7 +46,7 @@ export interface FactorioBoolSettingPrototype extends FactorioPrototypeBase{ * Integer mod setting prototype. * @see [int-setting](https://wiki.factorio.com/Tutorial:Mod_settings#int-setting) */ -export interface FactorioIntSettingPrototype extends FactorioPrototypeBase{ +export interface FactorioIntSettingPrototype extends FactorioPrototypeBase { type: "int-setting", setting_type?: FactorioModSettingType, default_value: number, @@ -59,7 +59,7 @@ export interface FactorioIntSettingPrototype extends FactorioPrototypeBase{ * Real number mod setting prototype. * @see [double-setting](https://wiki.factorio.com/Tutorial:Mod_settings#double-setting) */ -export interface FactorioDoubleSettingPrototype extends FactorioPrototypeBase{ +export interface FactorioDoubleSettingPrototype extends FactorioPrototypeBase { type: "double-setting", setting_type?: FactorioModSettingType, default_value: number, @@ -72,7 +72,7 @@ export interface FactorioDoubleSettingPrototype extends FactorioPrototypeBase{ * String mod setting prototype. * @see [string-setting](https://wiki.factorio.com/Tutorial:Mod_settings#string-setting) */ -export interface FactorioStringSettingPrototype extends FactorioPrototypeBase{ +export interface FactorioStringSettingPrototype extends FactorioPrototypeBase { type: "string-setting", setting_type?: FactorioModSettingType, default_value: string, @@ -85,7 +85,7 @@ export interface FactorioStringSettingPrototype extends FactorioPrototypeBase{ * Color mod setting prototype. * @see [color-setting](https://wiki.factorio.com/Tutorial:Mod_settings#color-setting) */ -export interface FactorioColorSettingPrototype extends FactorioPrototypeBase{ +export interface FactorioColorSettingPrototype extends FactorioPrototypeBase { type: "color-setting", setting_type?: FactorioModSettingType, default_value: FactorioColor, @@ -104,7 +104,7 @@ export type FactorioModSettingPrototype = * A list of prototypes as it appears in Factorio's Lua API. * @see [Data.raw](https://lua-api.factorio.com/stable/types/Data.html#raw) */ -export type FactorioPrototypes = Record> +export type FactorioPrototypes = Record>; export type FactorioDefinesValue = number | string | { [index: string]: FactorioDefinesValue }; export type FactorioDefines = { [index: string]: FactorioDefinesValue, diff --git a/packages/lib/src/external/FactorioVersions.ts b/packages/lib/src/external/FactorioVersions.ts index ae1f2e39..ba6902cb 100644 --- a/packages/lib/src/external/FactorioVersions.ts +++ b/packages/lib/src/external/FactorioVersions.ts @@ -12,7 +12,7 @@ export const ExternalFactorioVersionSchema = Type.Object({ headlessUrl: Type.String(), }); -export type ExternalFactorioVersion = Static +export type ExternalFactorioVersion = Static; /** * Fetch all factorio versions that support headless @@ -47,7 +47,7 @@ function parseFactorioVersions(html: string): ExternalFactorioVersion[] { const versions = new Map(); // Match 2.0.72 - // eslint-disable-next-line max-len + // eslint-disable-next-line @stylistic/max-len const anchorRegex = /]*class="[^"]*version-button-(?stable|experimental)[^"]*"[^>]*href="[^"]*\/download\/archive\/(?\d+\.\d+.\d+)"[^>]*>/g; let match: RegExpExecArray | null; diff --git a/packages/lib/src/external/LatestReleases.ts b/packages/lib/src/external/LatestReleases.ts index 885565ae..466a59a0 100644 --- a/packages/lib/src/external/LatestReleases.ts +++ b/packages/lib/src/external/LatestReleases.ts @@ -15,7 +15,7 @@ export const LatestReleaseBuildsSchema = Type.Record(Type.String(), FullVersionS */ export const LatestReleasesSchema = Type.Record(Type.String(), LatestReleaseBuildsSchema); -export type LatestReleases = Static +export type LatestReleases = Static; /** * Fetch the latest stable and experimental factorio releases diff --git a/packages/lib/src/logging.ts b/packages/lib/src/logging.ts index 473888fd..fb836429 100644 --- a/packages/lib/src/logging.ts +++ b/packages/lib/src/logging.ts @@ -177,9 +177,9 @@ export const logger = winston.createLogger({ // Who in their right mind thought that log levels should be hard coded // into the type when you can define custom levels you want to support? export type Logger = Omit & { fatal: winston.LeveledLogMethod, error: winston.LeveledLogMethod, diff --git a/packages/lib/src/plugin.ts b/packages/lib/src/plugin.ts index 0acbb84c..3bd83a27 100644 --- a/packages/lib/src/plugin.ts +++ b/packages/lib/src/plugin.ts @@ -46,7 +46,7 @@ export type PluginDeclaration = { errors?: ErrorClass[]; permissions?: PermissionDefinition[]; routes?: string[]; -} +}; /** * Check if a plugin is expected to ship a web build. diff --git a/packages/lib/src/shared_commands.ts b/packages/lib/src/shared_commands.ts index 24eb1f78..7767ce8e 100644 --- a/packages/lib/src/shared_commands.ts +++ b/packages/lib/src/shared_commands.ts @@ -175,7 +175,7 @@ export async function handlePluginCommand( try { const pluginPackage = (await import( pathToFileURL(path.posix.join(pluginPath, "package.json")).href, - { with: { type: "json" }}, + { with: { type: "json" }} )).default; pluginInfo = (await import( pathToFileURL(path.posix.join(pluginPath, pluginPackage.main ?? "index.js")).href diff --git a/packages/lib/src/subscriptions.ts b/packages/lib/src/subscriptions.ts index 6fbb8710..cc66781e 100644 --- a/packages/lib/src/subscriptions.ts +++ b/packages/lib/src/subscriptions.ts @@ -7,7 +7,7 @@ import { SessionLost, ValidationError } from "./errors.js"; import { logger } from "./logging.js"; export type SubscriptionRequestHandler = RequestHandler | null>; -export type EventSubscriberCallback = (event: T | null, synced: boolean) => void +export type EventSubscriberCallback = (event: T | null, synced: boolean) => void; /** * Represents a set of string subscription filters. @@ -241,7 +241,7 @@ type Subscriber = { link: Link, address: Address, filters: SubscriptionFilters, -} +}; type EventData = { subscriptionUpdate?: SubscriptionRequestHandler, diff --git a/packages/web_ui/src/components/HostsPage.tsx b/packages/web_ui/src/components/HostsPage.tsx index 2176e6ec..40684bc5 100644 --- a/packages/web_ui/src/components/HostsPage.tsx +++ b/packages/web_ui/src/components/HostsPage.tsx @@ -157,7 +157,7 @@ npm init "@clusterio" -- --controller-token ${token} --mode "host" --download-he ; } -function CopyButton({ text, message }: { text:string, message:string }) { +function CopyButton({ text, message }: { text: string, message: string }) { let [clipboardPermision, setClipboardPermission] = useState("granted"); useEffect(() => { (async () => { diff --git a/packages/web_ui/src/components/ModPackViewPage.tsx b/packages/web_ui/src/components/ModPackViewPage.tsx index fd7c7986..3aba7619 100644 --- a/packages/web_ui/src/components/ModPackViewPage.tsx +++ b/packages/web_ui/src/components/ModPackViewPage.tsx @@ -264,7 +264,7 @@ type DownloadDependenciesProps = { mods: lib.ModRecord[]; onChange: (change: ModChange) => void; builtInModNames: string[]; -} +}; function DownloadDependenciesButton(props: DownloadDependenciesProps) { const control = useContext(ControlContext); const [modInfos] = useMods(); @@ -904,7 +904,7 @@ type SettingsTableFieldProps = { allowedValues: any; locale: Map; changed: boolean; -} +}; const SettingsTableField = memo((props: SettingsTableFieldProps) => { const name = props.name; const isBoolean = props.type === "bool-setting"; diff --git a/packages/web_ui/src/components/SavesList.tsx b/packages/web_ui/src/components/SavesList.tsx index 9c7b79af..3ec8863d 100644 --- a/packages/web_ui/src/components/SavesList.tsx +++ b/packages/web_ui/src/components/SavesList.tsx @@ -25,7 +25,7 @@ type ModalProps = { disabled: boolean; save: lib.SaveDetails; instanceId: number; -} +}; function RenameModal(props: ModalProps) { let control = useContext(ControlContext); @@ -198,7 +198,7 @@ type File = { name: string; percent: number; status: string; -} +}; export default function SavesList(props: { instance: lib.InstanceDetails }) { let account = useAccount(); diff --git a/packages/web_ui/src/components/UsersPage.tsx b/packages/web_ui/src/components/UsersPage.tsx index 87399c80..e8910729 100644 --- a/packages/web_ui/src/components/UsersPage.tsx +++ b/packages/web_ui/src/components/UsersPage.tsx @@ -65,7 +65,7 @@ type UserBulkActionProps = { setApplyAction(func: () => Promise): void, form: FormInstance, restore?: boolean, -} +}; function UserBulkActionImport({ setApplyAction, form, restore }: UserBulkActionProps) { const control = useContext(ControlContext); diff --git a/plugins/player_auth/instance.ts b/plugins/player_auth/instance.ts index d38b4b45..f9943e52 100644 --- a/plugins/player_auth/instance.ts +++ b/plugins/player_auth/instance.ts @@ -9,7 +9,7 @@ type IpcPlayerAuth = { type: "set_verify_code", player: string, verify_code: string, -} +}; export default async function loadInstancePlugin(context: InstancePluginContext) { const { instance, host, logger, plugin } = context; diff --git a/plugins/player_sync/instance.ts b/plugins/player_sync/instance.ts index 83895621..30ed0223 100644 --- a/plugins/player_sync/instance.ts +++ b/plugins/player_sync/instance.ts @@ -10,12 +10,12 @@ import { type Component, enabledComponents, stripPlayerData } from "./components type IpcPlayerName = { player_name: string -} +}; type IpcDownloadRequest = { player_name: string, recipe_notifications?: string, -} +}; /** Components as an escaped JSON object for the scenario. */ function componentsJson(enabled: Set) { @@ -30,7 +30,7 @@ type IpcAcquireResponse = { message?: string, epoch?: number, token?: string, -} +}; /** * Splits string into array of strings with max of a certain length @@ -64,7 +64,7 @@ export type RecipeNotificationDelta = { remove?: string[], /** add is the whole stored list, sent without the instance's: the save works out what to remove */ full?: true, -} +}; /** * Compute the changes needed to turn the current cleared recipe list into the stored one diff --git a/plugins/player_sync/messages.ts b/plugins/player_sync/messages.ts index 0f12a9ce..5a92591d 100644 --- a/plugins/player_sync/messages.ts +++ b/plugins/player_sync/messages.ts @@ -30,7 +30,7 @@ export type IpcPlayerData = { // Components the upload was serialized with, all when missing components?: Record | unknown[], destination_instance_id?: number, // set_destination's instance, on the leave upload only; not player data -} +}; // Marks tombstones in player_sync.json const jsonReserved = { deleted: Type.Optional(Type.Never()) }; diff --git a/plugins/research_sync/controller.ts b/plugins/research_sync/controller.ts index a617b2c1..3f29a3a3 100644 --- a/plugins/research_sync/controller.ts +++ b/plugins/research_sync/controller.ts @@ -22,7 +22,7 @@ type Technology = { level: number, progress: number | null, researched: boolean, -} +}; async function loadTechnologies( diff --git a/plugins/research_sync/test/controller.js b/plugins/research_sync/test/controller.js index 5fbbfb91..faef14c5 100644 --- a/plugins/research_sync/test/controller.js +++ b/plugins/research_sync/test/controller.js @@ -145,21 +145,21 @@ describe("research_sync controller", function() { }); describe("technology sync unlocking several levels", function() { - const techSync = (name, level, researched) => mock.getHandler(controller, SyncTechnologiesRequest)( + const syncTech = (name, level, researched) => mock.getHandler(controller, SyncTechnologiesRequest)( new SyncTechnologiesRequest([new TechnologySync(name, level, null, researched)]), src ); it("should drop one entry per level unlocked on a known technology", async function() { - await techSync("m", 4, true); + await syncTech("m", 4, true); await update(["m", "m", "m", "x"]); sent.length = 0; - await techSync("m", 7, true); + await syncTech("m", 7, true); assert.deepEqual(await sync(), ["x"]); assert.equal(queueEvents().length, 1); assert.deepEqual(queueEvents()[0][1], new QueueEvent(["x"], null, null)); }); it("should drop one entry per level unlocked on a new technology", async function() { await update(["m", "m", "x"]); - await techSync("m", 2, true); + await syncTech("m", 2, true); assert.deepEqual(await sync(), ["x"]); }); }); diff --git a/plugins/statistics_exporter/instance.ts b/plugins/statistics_exporter/instance.ts index 74ac0003..75c857da 100644 --- a/plugins/statistics_exporter/instance.ts +++ b/plugins/statistics_exporter/instance.ts @@ -26,12 +26,12 @@ type IpcStats = { weight: number, } } -} +}; type IpcFlowType = { "input": Record, "output": Record, -} +}; const instancePlayerCount = new Gauge( "clusterio_statistics_exporter_instance_player_count", diff --git a/plugins/subspace_storage/dole.ts b/plugins/subspace_storage/dole.ts index f01f20e9..0f5879c4 100644 --- a/plugins/subspace_storage/dole.ts +++ b/plugins/subspace_storage/dole.ts @@ -78,7 +78,9 @@ export class NeuralDole { } } - divider(object: { name:string, quality:string, count:number, instanceId:number, instanceName:string }): number { + divider( + object: { name: string, quality: string, count: number, instanceId: number, instanceName: string } + ): number { let magicData = doleNN.dose( object.count, // numReq this.items.getItemCount(object.name, object.quality) || 0, @@ -118,7 +120,7 @@ export function doleDivider( logItemTransfers, logger, } :{ - object: { name:string, quality:string, count:number, instanceId:number, instanceName:string }, + object: { name: string, quality: string, count: number, instanceId: number, instanceName: string }, items: lib.ItemDatabase, logItemTransfers: boolean, logger: lib.Logger, diff --git a/test/host/server.js b/test/host/server.js index 20d2ae5c..da6169a9 100644 --- a/test/host/server.js +++ b/test/host/server.js @@ -181,7 +181,7 @@ describe("host/server", function() { let port = hostServer._randomDynamicPort(); assert.equal(typeof port, "number"); assert(Number.isInteger(port)); - assert(0 <= port && port < 2**16); + assert(port >= 0 && port < 2**16); }); it("should return a port number in the dynamic range", function() { diff --git a/test/integration/clusterio.js b/test/integration/clusterio.js index e2c74fec..64f0563f 100644 --- a/test/integration/clusterio.js +++ b/test/integration/clusterio.js @@ -1313,7 +1313,7 @@ describe("Integration of Clusterio", function() { } else if (isV2) { exchangeString = testStrings.modified_v2; } - // eslint-disable-next-line max-len + // eslint-disable-next-line @stylistic/max-len const args = `base/freeplay --seed 1234 --map-exchange-string "${exchangeString.replace(/[\n\r]+/g, "")}"`; await execCtl("instance stop 44"); diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 00000000..5c6eba5a --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,371 @@ +import { defineConfig } from "vite-plus"; + +// Formatting rules come from @stylistic as Oxlint does not implement them. +export default defineConfig({ + lint: { + plugins: ["node"], + jsPlugins: ["@stylistic/eslint-plugin"], + categories: { + correctness: "off", + }, + env: { + builtin: true, + node: true, + }, + ignorePatterns: ["**/dist/", "dist/"], + rules: { + "@stylistic/indent": [ + "error", + "tab", + { + ignoredNodes: ["JSXFragment"], + ignoreComments: true, + SwitchCase: 1, + }, + ], + "accessor-pairs": "error", + "array-callback-return": "error", + "arrow-body-style": "error", + "block-scoped-var": "error", + complexity: [ + "error", + { + max: 25, + }, + ], + curly: "error", + "default-case": "error", + "default-case-last": "error", + eqeqeq: "error", + "func-name-matching": "error", + "func-style": [ + "error", + "declaration", + { + allowArrowFunctions: true, + }, + ], + "grouped-accessor-pairs": "error", + "guard-for-in": "error", + "id-match": "error", + "max-depth": "error", + "max-nested-callbacks": "error", + "new-cap": [ + "error", + { + capIsNewExceptions: ["StringEnum", "StringKey"], + capIsNewExceptionPattern: "^Type\\.", + newIsCapExceptions: ["constructor"], + }, + ], + "no-alert": "error", + "no-array-constructor": "error", + "no-caller": "error", + "no-console": "error", + "no-constant-condition": [ + "error", + { + checkLoops: false, + }, + ], + "no-constructor-return": "error", + "no-div-regex": "error", + "no-duplicate-imports": "error", + "no-else-return": "error", + "no-eq-null": "error", + "no-eval": "error", + "no-extend-native": "error", + "no-extra-bind": "error", + "no-extra-label": "error", + "no-implicit-coercion": "error", + "no-implicit-globals": "error", + "no-implied-eval": "error", + "no-iterator": "error", + "no-label-var": "error", + "no-lone-blocks": "error", + "no-lonely-if": "error", + "no-loop-func": "error", + "no-multi-assign": "error", + "no-multi-str": "error", + "no-nested-ternary": "error", + "no-new": "error", + "no-new-func": "error", + "no-new-wrappers": "error", + "no-plusplus": [ + "error", + { + allowForLoopAfterthoughts: true, + }, + ], + "no-proto": "error", + "no-restricted-globals": "error", + "no-restricted-imports": "error", + "no-restricted-properties": "error", + "no-return-assign": "error", + "no-script-url": "error", + "no-self-compare": "error", + "no-sequences": "error", + "no-shadow": [ + "error", + { + allow: ["Event", "Request", "yargs"], + }, + ], + "no-template-curly-in-string": "error", + "no-throw-literal": "error", + "no-unmodified-loop-condition": "error", + "no-unneeded-ternary": "error", + "no-unused-expressions": "error", + "no-useless-backreference": "error", + "no-useless-call": "error", + "no-useless-computed-key": "error", + "no-useless-concat": "error", + "no-useless-rename": "error", + "no-useless-return": "error", + "no-var": "error", + "no-void": [ + "error", + { + allowAsStatement: true, + }, + ], + "prefer-arrow-callback": "error", + "prefer-exponentiation-operator": "error", + "prefer-numeric-literals": "error", + "prefer-object-spread": "error", + "prefer-promise-reject-errors": "error", + "prefer-regex-literals": "error", + "prefer-rest-params": "error", + "prefer-spread": "error", + "prefer-template": "error", + radix: ["error", "always"], + "symbol-description": "error", + "unicode-bom": ["error", "never"], + "vars-on-top": "error", + yoda: [ + "error", + "never", + { + exceptRange: true, + }, + ], + "node/no-sync": [ + "error", + { + // Oxlint also reports identifiers ending in Sync that are not calls + ignores: ["TechnologySync", "techsToSync"], + }, + ], + "@stylistic/array-bracket-newline": "off", + "@stylistic/array-bracket-spacing": ["error", "never"], + "@stylistic/array-element-newline": "off", + "@stylistic/arrow-parens": "off", + "@stylistic/arrow-spacing": [ + "error", + { + after: true, + before: true, + }, + ], + "@stylistic/block-spacing": "error", + "@stylistic/brace-style": [ + "error", + "1tbs", + { + allowSingleLine: true, + }, + ], + "@stylistic/comma-dangle": [ + "error", + { + arrays: "always-multiline", + objects: "always-multiline", + imports: "always-multiline", + exports: "always-multiline", + functions: "only-multiline", + generics: "only-multiline", + enums: "always-multiline", + tuples: "always-multiline", + }, + ], + "@stylistic/comma-spacing": "error", + "@stylistic/comma-style": ["error", "last"], + "@stylistic/computed-property-spacing": ["error", "never"], + "@stylistic/dot-location": ["error", "property"], + "@stylistic/eol-last": "error", + "@stylistic/function-call-argument-newline": "off", + "@stylistic/function-paren-newline": "off", + "@stylistic/generator-star-spacing": [ + "error", + { + before: false, + after: true, + }, + ], + "@stylistic/implicit-arrow-linebreak": "error", + "@stylistic/jsx-quotes": "error", + "@stylistic/key-spacing": "error", + "@stylistic/keyword-spacing": "error", + "@stylistic/line-comment-position": "off", + "@stylistic/linebreak-style": ["error", "unix"], + "@stylistic/lines-around-comment": "off", + "@stylistic/lines-between-class-members": [ + "error", + "always", + { + exceptAfterSingleLine: true, + }, + ], + "@stylistic/max-len": [ + "error", + { + code: 120, + }, + ], + "@stylistic/max-statements-per-line": "off", + "@stylistic/multiline-comment-style": ["error", "separate-lines"], + "@stylistic/multiline-ternary": "off", + "@stylistic/new-parens": "error", + "@stylistic/newline-per-chained-call": "off", + "@stylistic/no-confusing-arrow": "error", + "@stylistic/no-extra-parens": "off", + "@stylistic/no-floating-decimal": "error", + "@stylistic/no-mixed-operators": [ + "error", + { + groups: [ + ["&", "|", "^", "~", "<<", ">>", ">>>"], + ["==", "!=", "===", "!==", ">", ">=", "<", "<="], + ["in", "instanceof"], + ], + }, + ], + "@stylistic/no-multi-spaces": "error", + "@stylistic/no-multiple-empty-lines": "error", + "@stylistic/no-tabs": "off", + "@stylistic/no-trailing-spaces": "error", + "@stylistic/no-whitespace-before-property": "error", + "@stylistic/nonblock-statement-body-position": "error", + "@stylistic/object-curly-newline": "error", + "@stylistic/object-curly-spacing": "off", + "@stylistic/one-var-declaration-per-line": "error", + "@stylistic/operator-linebreak": [ + "error", + "after", + { + overrides: { + "?": "before", + ":": "before", + "||": "before", + "&&": "before", + "??": "before", + "|": "before", + "&": "before", + }, + }, + ], + "@stylistic/padded-blocks": "off", + "@stylistic/padding-line-between-statements": "error", + "@stylistic/quote-props": "off", + "@stylistic/quotes": [ + "error", + "double", + { + avoidEscape: true, + }, + ], + "@stylistic/rest-spread-spacing": ["error", "never"], + "@stylistic/semi": "error", + "@stylistic/semi-spacing": "error", + "@stylistic/semi-style": "off", + "@stylistic/space-before-blocks": "error", + "@stylistic/space-before-function-paren": "off", + "@stylistic/space-in-parens": ["error", "never"], + "@stylistic/space-infix-ops": "off", + "@stylistic/space-unary-ops": "error", + "@stylistic/spaced-comment": [ + "error", + "always", + { + line: { + markers: ["/"], + }, + }, + ], + "@stylistic/switch-colon-spacing": "error", + "@stylistic/template-curly-spacing": ["error", "never"], + "@stylistic/template-tag-spacing": "error", + "@stylistic/wrap-iife": "error", + "@stylistic/wrap-regex": "off", + "@stylistic/yield-star-spacing": "error", + }, + overrides: [ + { + files: ["{packages,plugins}/**/*.{ts,tsx}"], + rules: { + "no-unused-vars": "off", + "typescript/ban-ts-comment": "error", + "typescript/no-duplicate-enum-values": "error", + "typescript/no-empty-object-type": "error", + "typescript/no-explicit-any": "off", + "typescript/no-extra-non-null-assertion": "error", + "typescript/no-misused-new": "error", + "typescript/no-namespace": "error", + "typescript/no-non-null-asserted-optional-chain": "error", + "typescript/no-require-imports": "off", + "typescript/no-this-alias": "error", + "typescript/no-unnecessary-type-constraint": "error", + "typescript/no-unsafe-declaration-merging": "error", + "typescript/no-unsafe-function-type": "error", + "typescript/no-wrapper-object-types": "error", + "typescript/prefer-as-const": "error", + "typescript/prefer-namespace-keyword": "error", + "typescript/triple-slash-reference": "off", + "no-shadow": [ + "error", + { + allow: ["Event", "Request", "yargs"], + }, + ], + "no-useless-constructor": "error", + }, + plugins: ["typescript"], + }, + { + files: ["test/**/*.js", "plugins/*/test/**/*.js"], + rules: { + "prefer-arrow-callback": "off", + }, + env: { + mocha: true, + }, + }, + { + files: [ + "packages/web_ui/src/**/*.{ts,tsx}", + "packages/lib/browser.ts", + "packages/create/templates/*/web/**/*.{ts,js,tsx,jsx}", + "{packages,plugins}/*/web/**/*.{ts,tsx}", + ], + globals: { + SpeechRecognitionAlternative: "readonly", + SpeechRecognitionResult: "readonly", + SpeechRecognitionResultList: "readonly", + }, + env: { + browser: true, + }, + }, + { + files: ["packages/create/templates/**/*.{js,cjs,ts,cts,jsx,tsx}"], + rules: { + "@stylistic/indent": "off", + "@stylistic/spaced-comment": "off", + "@stylistic/comma-spacing": "off", + "@stylistic/max-len": "off", + }, + jsPlugins: ["@stylistic/eslint-plugin"], + }, + ], + }, +});