From fa85bb9a0f1223ffda8a88153a54debfafb3418c Mon Sep 17 00:00:00 2001 From: Tiago de Paula Date: Tue, 28 Oct 2025 04:53:51 -0300 Subject: [PATCH] eslint: migrate to Flat Config format The new format allows to easily tune options and rules for specific file groups, if desired. Also works with eslint-plugin-vue@10.7.0. See . Removed deprecated `packageManager` for VSCode Extension (microsoft/vscode-eslint#1646). --- .vscode/settings.json | 1 - eslint.config.mjs | 328 ++++++++++++++++++------------------------ package.json | 8 +- yarn.lock | 34 +++-- 4 files changed, 164 insertions(+), 207 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index d9b33397..6b0c3629 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,7 +2,6 @@ "editor.formatOnSave": true, "prettier.useEditorConfig": true, "prettier.requireConfig": true, - "eslint.packageManager": "yarn", "eslint.codeActionsOnSave.mode": "all", "[typescript]": {"editor.defaultFormatter": "esbenp.prettier-vscode"}, "[vue]": {"editor.defaultFormatter": "esbenp.prettier-vscode"} diff --git a/eslint.config.mjs b/eslint.config.mjs index a0787a4f..08e44b72 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,200 +1,150 @@ -import {FlatCompat} from "@eslint/eslintrc"; import eslint from "@eslint/js"; -import {globalIgnores} from "eslint/config"; -import {defineConfig} from "eslint-define-config"; +import {defineConfig, globalIgnores} from "eslint/config"; +import prettierConfig from "eslint-config-prettier"; +import pluginVue from "eslint-plugin-vue"; +import globals from "globals"; +import tseslint from "typescript-eslint"; -const compat = new FlatCompat({ - baseDirectory: import.meta.dirname, - resolvePluginsRelativeTo: import.meta.dirname, - recommendedConfig: eslint.configs.recommended, - allConfig: eslint.configs.all, -}); - -const {parserOptions} = defineConfig({ - parserOptions: { - projectService: true, - tsconfigRootDir: import.meta.dirname, +export default defineConfig( + eslint.configs.recommended, + { + languageOptions: { + ecmaVersion: 2022, + }, + rules: { + "block-scoped-var": "error", + curly: ["error", "all"], + "dot-notation": "error", + eqeqeq: "error", + "handle-callback-err": "error", + "no-alert": "error", + "no-catch-shadow": "error", + "no-control-regex": "off", + "no-console": "error", + "no-duplicate-imports": "error", + "no-else-return": "error", + "no-implicit-globals": "error", + "no-restricted-globals": ["error", "event", "fdescribe"], + "no-template-curly-in-string": "error", + "no-unsafe-negation": "error", + "no-useless-computed-key": "error", + "no-useless-constructor": "error", + "no-useless-return": "error", + "no-use-before-define": [ + "error", + { + functions: false, + }, + ], + "no-var": "error", + "object-shorthand": [ + "error", + "methods", + { + avoidExplicitReturnArrows: true, + }, + ], + "padding-line-between-statements": [ + "error", + { + blankLine: "always", + prev: ["block", "block-like"], + next: "*", + }, + { + blankLine: "always", + prev: "*", + next: ["block", "block-like"], + }, + ], + "prefer-const": "error", + "prefer-rest-params": "error", + "prefer-spread": "error", + "spaced-comment": ["error", "always"], + strict: "off", + yoda: "error", + }, }, -}); - -const baseRules = defineConfig({ - rules: { - "block-scoped-var": "error", - curly: ["error", "all"], - "dot-notation": "error", - eqeqeq: "error", - "handle-callback-err": "error", - "no-alert": "error", - "no-catch-shadow": "error", - "no-control-regex": "off", - "no-console": "error", - "no-duplicate-imports": "error", - "no-else-return": "error", - "no-implicit-globals": "error", - "no-restricted-globals": ["error", "event", "fdescribe"], - "no-template-curly-in-string": "error", - "no-unsafe-negation": "error", - "no-useless-computed-key": "error", - "no-useless-constructor": "error", - "no-useless-return": "error", - "no-use-before-define": [ - "error", - { - functions: false, - }, - ], - "no-var": "error", - "object-shorthand": [ - "error", - "methods", - { - avoidExplicitReturnArrows: true, - }, - ], - "padding-line-between-statements": [ - "error", - { - blankLine: "always", - prev: ["block", "block-like"], - next: "*", - }, - { - blankLine: "always", - prev: "*", - next: ["block", "block-like"], - }, - ], - "prefer-const": "error", - "prefer-rest-params": "error", - "prefer-spread": "error", - "spaced-comment": ["error", "always"], - strict: "off", - yoda: "error", + { + ignores: ["client/"], + languageOptions: { + sourceType: "commonjs", + globals: globals.node, + }, }, -}).rules; - -const vueRules = defineConfig({ - rules: { - "import/no-default-export": "off", - "import/unambiguous": "off", // vue SFC can miss script tags - "@typescript-eslint/prefer-readonly": "off", // can be used in template - "vue/block-order": [ - "error", - { - order: ["template", "style", "script"], - }, - ], - "vue/multi-word-component-names": "off", - "vue/no-mutating-props": "off", - "vue/no-v-html": "off", - "vue/require-default-prop": "off", - "vue/v-slot-style": ["error", "longform"], - // FIXME: not working with FlatCompat - "vue/valid-v-for": "off", + { + basePath: "client/", + languageOptions: { + sourceType: "module", + globals: globals.browser, + }, }, -}).rules; - -const tsRules = defineConfig({ - rules: { - // note you must disable the base rule as it can report incorrect errors - "no-shadow": "off", - "@typescript-eslint/no-shadow": ["error"], - "@typescript-eslint/no-redundant-type-constituents": "off", - }, -}).rules; - -const tsRulesTemp = defineConfig({ - rules: { - // TODO: eventually remove these - "@typescript-eslint/ban-ts-comment": "off", - "@typescript-eslint/no-explicit-any": "off", - "@typescript-eslint/no-non-null-assertion": "off", - "@typescript-eslint/no-this-alias": "off", - "@typescript-eslint/no-unnecessary-type-assertion": "off", - "@typescript-eslint/no-unsafe-argument": "off", - "@typescript-eslint/no-unsafe-assignment": "off", - "@typescript-eslint/no-unsafe-call": "off", - "@typescript-eslint/no-unsafe-member-access": "off", - "@typescript-eslint/no-unused-vars": "off", - }, -}).rules; - -const tsTestRulesTemp = defineConfig({ - rules: { - // TODO: remove these - "@typescript-eslint/no-unsafe-return": "off", - "@typescript-eslint/no-empty-function": "off", - "@typescript-eslint/no-unused-expressions": "off", - "@typescript-eslint/restrict-plus-operands": "off", - }, -}).rules; - -const baseConfig = compat.config({ - root: true, - parserOptions: { - ecmaVersion: 2022, - }, - overrides: [ - { - files: ["**/*.ts", "**/*.vue"], - parser: "@typescript-eslint/parser", + { + files: ["**/*.ts", "**/*.vue"], + languageOptions: { parserOptions: { - ...parserOptions, + projectService: true, + tsconfigRootDir: import.meta.dirname, extraFileExtensions: [".vue"], }, - plugins: ["@typescript-eslint"], - extends: [ - "eslint:recommended", - "plugin:@typescript-eslint/recommended", - "plugin:@typescript-eslint/recommended-requiring-type-checking", - "prettier", - ], - rules: { - ...baseRules, - ...tsRules, - ...tsRulesTemp, - }, }, - { - files: ["**/*.vue"], - parser: "vue-eslint-parser", - parserOptions: { - ...parserOptions, - parser: "@typescript-eslint/parser", - ecmaVersion: 2022, - ecmaFeatures: { - jsx: true, - }, - }, - plugins: ["vue"], - extends: [ - "eslint:recommended", - "plugin:vue/recommended", - "plugin:@typescript-eslint/recommended", - "plugin:@typescript-eslint/recommended-requiring-type-checking", - "prettier", - ], - rules: {...baseRules, ...tsRules, ...tsRulesTemp, ...vueRules}, + extends: [...tseslint.configs.recommended, ...tseslint.configs.recommendedTypeChecked], + rules: { + // note you must disable the base rule as it can report incorrect errors + "no-shadow": "off", + "@typescript-eslint/no-shadow": ["error"], + "@typescript-eslint/no-redundant-type-constituents": "off", + // TODO: eventually remove these + "@typescript-eslint/ban-ts-comment": "off", + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-non-null-assertion": "off", + "@typescript-eslint/no-this-alias": "off", + "@typescript-eslint/no-unnecessary-type-assertion": "off", + "@typescript-eslint/no-unsafe-argument": "off", + "@typescript-eslint/no-unsafe-assignment": "off", + "@typescript-eslint/no-unsafe-call": "off", + "@typescript-eslint/no-unsafe-member-access": "off", + "@typescript-eslint/no-unused-vars": "off", }, - { - files: ["./test/**/*.ts"], - parser: "@typescript-eslint/parser", - rules: { - ...baseRules, - ...tsRules, - ...tsRulesTemp, - ...tsTestRulesTemp, - }, - }, - ], - env: { - es6: true, - browser: true, - mocha: true, - node: true, }, - extends: ["eslint:recommended", "prettier"], - rules: baseRules, -}); - -export default [...baseConfig, globalIgnores(["**/public/", "**/coverage/", "**/dist/"])]; + ...pluginVue.configs["flat/recommended"], + { + files: ["**/*.vue"], + languageOptions: { + parserOptions: { + parser: tseslint.parser, + }, + }, + rules: { + "import/no-default-export": "off", + "import/unambiguous": "off", // vue SFC can miss script tags + "@typescript-eslint/prefer-readonly": "off", // can be used in template + "vue/block-order": [ + "error", + { + order: ["template", "style", "script"], + }, + ], + "vue/multi-word-component-names": "off", + "vue/no-mutating-props": "off", + "vue/no-v-html": "off", + "vue/require-default-prop": "off", + "vue/v-slot-style": ["error", "longform"], + }, + }, + { + basePath: "test/", + languageOptions: { + globals: globals.mocha, + }, + rules: { + // TODO: remove these + "@typescript-eslint/no-unsafe-return": "off", + "@typescript-eslint/no-empty-function": "off", + "@typescript-eslint/no-unused-expressions": "off", + "@typescript-eslint/restrict-plus-operands": "off", + }, + }, + prettierConfig, + globalIgnores(["**/public/", "**/coverage/", "**/dist/"]) +); diff --git a/package.json b/package.json index f68a2330..b57a8af5 100644 --- a/package.json +++ b/package.json @@ -89,7 +89,6 @@ "@babel/core": "^7.29.0", "@babel/plugin-transform-runtime": "^7.29.0", "@babel/preset-env": "^7.29.0", - "@eslint/eslintrc": "^3.3.3", "@eslint/js": "^9.39.2", "@fortawesome/fontawesome-free": "^7.1.0", "@istanbuljs/nyc-config-typescript": "^1.0.2", @@ -113,8 +112,6 @@ "@types/webpack-env": "^1.18.8", "@types/webpack-hot-middleware": "^2.25.12", "@types/ws": "^8.18.1", - "@typescript-eslint/eslint-plugin": "^8.54.0", - "@typescript-eslint/parser": "^8.54.0", "@vue/runtime-dom": "^3.5.27", "@vue/test-utils": "^2.4.6", "babel-loader": "^10.0.0", @@ -129,10 +126,10 @@ "emoji-regex": "^10.6.0", "eslint": "^9.39.2", "eslint-config-prettier": "^10.1.8", - "eslint-define-config": "^2.1.0", - "eslint-plugin-vue": "~10.6.2", + "eslint-plugin-vue": "^10.7.0", "fork-ts-checker-webpack-plugin": "^9.1.0", "fuzzy": "^0.1.3", + "globals": "^17.2.0", "mini-css-extract-plugin": "^2.10.0", "mocha": "^11.7.5", "mousetrap": "^1.6.5", @@ -155,6 +152,7 @@ "ts-node": "^10.9.2", "ts-sinon": "^2.0.2", "typescript": "^5.9.3", + "typescript-eslint": "^8.54.0", "undate": "^0.3.0", "vue": "^3.5.27", "vue-eslint-parser": "^10.2.0", diff --git a/yarn.lock b/yarn.lock index 42833342..33981429 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1314,7 +1314,7 @@ dependencies: "@types/json-schema" "^7.0.15" -"@eslint/eslintrc@^3.3.1", "@eslint/eslintrc@^3.3.3": +"@eslint/eslintrc@^3.3.1": version "3.3.3" resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-3.3.3.tgz#26393a0806501b5e2b6a43aa588a4d8df67880ac" integrity sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ== @@ -2090,7 +2090,7 @@ dependencies: "@types/node" "*" -"@typescript-eslint/eslint-plugin@^8.54.0": +"@typescript-eslint/eslint-plugin@8.54.0": version "8.54.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.54.0.tgz#d8899e5c2eccf5c4a20d01c036a193753748454d" integrity sha512-hAAP5io/7csFStuOmR782YmTthKBJ9ND3WVL60hcOjvtGFb+HJxH4O5huAcmcZ9v9G8P+JETiZ/G1B8MALnWZQ== @@ -2104,7 +2104,7 @@ natural-compare "^1.4.0" ts-api-utils "^2.4.0" -"@typescript-eslint/parser@^8.54.0": +"@typescript-eslint/parser@8.54.0": version "8.54.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.54.0.tgz#3d01a6f54ed247deb9982621f70e7abf1810bd97" integrity sha512-BtE0k6cjwjLZoZixN0t5AKP0kSzlGu7FctRXYuPAm//aaiZhmfq1JwdYpYr1brzEspYyFeF+8XF5j2VK6oalrA== @@ -3840,15 +3840,10 @@ eslint-config-prettier@^10.1.8: resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz#15734ce4af8c2778cc32f0b01b37b0b5cd1ecb97" integrity sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w== -eslint-define-config@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/eslint-define-config/-/eslint-define-config-2.1.0.tgz#9708b3efd57637b6fb685d9c2fb6285b9acfbd71" - integrity sha512-QUp6pM9pjKEVannNAbSJNeRuYwW3LshejfyBBpjeMGaJjaDUpVps4C6KVR8R7dWZnD3i0synmrE36znjTkJvdQ== - -eslint-plugin-vue@~10.6.2: - version "10.6.2" - resolved "https://registry.yarnpkg.com/eslint-plugin-vue/-/eslint-plugin-vue-10.6.2.tgz#5d8790fa2e1105cf06259afc14c8a55cb110f346" - integrity sha512-nA5yUs/B1KmKzvC42fyD0+l9Yd+LtEpVhWRbXuDj0e+ZURcTtyRbMDWUeJmTAh2wC6jC83raS63anNM2YT3NPw== +eslint-plugin-vue@^10.7.0: + version "10.7.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-vue/-/eslint-plugin-vue-10.7.0.tgz#108264d16a4b03b49fe7b4d446661d15aab85bd3" + integrity sha512-r2XFCK4qlo1sxEoAMIoTTX0PZAdla0JJDt1fmYiworZUX67WeEGqm+JbyAg3M+pGiJ5U6Mp5WQbontXWtIW7TA== dependencies: "@eslint-community/eslint-utils" "^4.4.0" natural-compare "^1.4.0" @@ -4449,6 +4444,11 @@ globals@^14.0.0: resolved "https://registry.yarnpkg.com/globals/-/globals-14.0.0.tgz#898d7413c29babcf6bafe56fcadded858ada724e" integrity sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ== +globals@^17.2.0: + version "17.2.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-17.2.0.tgz#41d29408d6f5408457d2ef965d29215e3026779f" + integrity sha512-tovnCz/fEq+Ripoq+p/gN1u7l6A7wwkoBT9pRCzTHzsD/LvADIzXZdjmRymh5Ztf0DYC3Rwg5cZRYjxzBmzbWg== + globby@^16.1.0: version "16.1.0" resolved "https://registry.yarnpkg.com/globby/-/globby-16.1.0.tgz#71ab8199e4fc1c4c21a59bd14ec0f31c71d7d7d4" @@ -7875,6 +7875,16 @@ typedarray-to-buffer@^3.1.5: dependencies: is-typedarray "^1.0.0" +typescript-eslint@^8.54.0: + version "8.54.0" + resolved "https://registry.yarnpkg.com/typescript-eslint/-/typescript-eslint-8.54.0.tgz#f4ef3b8882a5ddc2a41968e014194c178ab23f6a" + integrity sha512-CKsJ+g53QpsNPqbzUsfKVgd3Lny4yKZ1pP4qN3jdMOg/sisIDLGyDMezycquXLE5JsEU0wp3dGNdzig0/fmSVQ== + dependencies: + "@typescript-eslint/eslint-plugin" "8.54.0" + "@typescript-eslint/parser" "8.54.0" + "@typescript-eslint/typescript-estree" "8.54.0" + "@typescript-eslint/utils" "8.54.0" + typescript@^5.9.3: version "5.9.3" resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.9.3.tgz#5b4f59e15310ab17a216f5d6cf53ee476ede670f"