From ebedf6b3b02ddb1b86a43d9e2999cca9338fbae6 Mon Sep 17 00:00:00 2001 From: bbassie <17990055+bbassie@users.noreply.github.com> Date: Mon, 26 Oct 2026 06:28:27 +0000 Subject: [PATCH] Run Oxlint directly instead of through Vite+ Nothing else from Vite+ is used, and Oxlint reads the same rules from .oxlintrc.json. This drops vite-plus and with it Vitest, a second copy of Vite and about 130 packages. Co-Authored-By: Claude Opus 5.5 --- .oxlintrc.json | 458 ++++++++++++++++++++++++++++++++++++++++++++++ AGENTS.md | 2 +- docs/decisions.md | 2 +- package.json | 6 +- vite.config.ts | 371 ------------------------------------- 5 files changed, 463 insertions(+), 376 deletions(-) create mode 100644 .oxlintrc.json delete mode 100644 vite.config.ts diff --git a/.oxlintrc.json b/.oxlintrc.json new file mode 100644 index 00000000..cc583689 --- /dev/null +++ b/.oxlintrc.json @@ -0,0 +1,458 @@ +// Formatting rules come from @stylistic as Oxlint does not implement them. +{ + "$schema": "./node_modules/oxlint/configuration_schema.json", + "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", + { + "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" + ] + } + ] +} diff --git a/AGENTS.md b/AGENTS.md index 3fd24a95..c386f2b7 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 the `lint` section of `vite.config.ts`: tabs, double quotes, semicolons, and `.js` extensions in relative TypeScript imports. +- Follow nearby code and `.oxlintrc.json`: 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 3afb7fc6..8e06617a 100644 --- a/docs/decisions.md +++ b/docs/decisions.md @@ -161,7 +161,7 @@ Therefore, we selected double quotes as our standard. 🧾 20th October 2026 -> Use Oxlint through `vp lint` for style checking, with formatting rules from `@stylistic/eslint-plugin`. +> Use Oxlint for style checking, with formatting rules from `@stylistic/eslint-plugin`. 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. diff --git a/package.json b/package.json index d436a0ca..845eaa79 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": "vp lint packages plugins test", - "lint-fix": "vp lint --fix packages plugins test", + "lint": "oxlint packages plugins test", + "lint-fix": "oxlint --fix packages plugins test", "clean": "node ./clean.js", "docs": "typedoc", "bundle-dependencies": "bundle-dependencies" @@ -50,9 +50,9 @@ "form-data": "^4.0.6", "jsonwebtoken": "catalog:", "mocha": "^11.8.0", + "oxlint": "^1.88.0", "typedoc": "^0.28.20", "typescript": "catalog:", - "vite-plus": "^1.3.0", "yargs": "catalog:", "yauzl": "catalog:", "yazl": "catalog:" diff --git a/vite.config.ts b/vite.config.ts deleted file mode 100644 index 5c6eba5a..00000000 --- a/vite.config.ts +++ /dev/null @@ -1,371 +0,0 @@ -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"], - }, - ], - }, -});