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 <https://eslint.org/docs/latest/use/migrate-to-9.0.0>.

Removed deprecated `packageManager` for VSCode Extension
(microsoft/vscode-eslint#1646).
This commit is contained in:
Tiago de Paula 2025-10-28 04:53:51 -03:00
commit fa85bb9a0f
No known key found for this signature in database
GPG key ID: 55BD118E42C985CF
4 changed files with 160 additions and 203 deletions

View file

@ -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"}

View file

@ -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/"])
);

View file

@ -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",

View file

@ -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"