visual-studio-codeeslintprettiertypescript-eslint

Eslint, Prettier and VSCode config not wokring


I am trying to convert my old eslint configuration file into the new eslint configuration, however I can't get it to work.

I am suing VScode and my old setup works that it underlines the wrongly formted code and in the context menu ALT + Enter it shows Fix all auto-fixable options. However the new version doesnt do this.

My old .exlintrc.cjs config:

const fs = require("fs");
const path = require("path");

const prettierOptions = JSON.parse(fs.readFileSync(path.resolve(__dirname, ".prettierrc.json"), "utf8"));

module.exports = {
    root: true,
    env: { browser: true, es2021: true, es6: true },
    overrides: [
        {
            files: ["**/*.ts?(x)"],
            rules: {
                "@typescript-eslint/explicit-function-return-type": "off",
                "max-lines": "off",
                "max-lines-per-function": "off",
                "no-magic-numbers": "off",
                "no-undef": "off",
            },
        },
    ],
    extends: ["airbnb-base", "eslint:recommended", "plugin:@typescript-eslint/recommended", "prettier"],
    ignorePatterns: ["dist", ".eslintrc.cjs"],
    parser: "@typescript-eslint/parser",
    parserOptions: {
        ecmaFeatures: {
            jsx: true,
        },
        ecmaVersion: "latest",
        sourceType: "module",
        project: "./tsconfig.json",
    },
    plugins: ["prettier", "unused-imports"],
    settings: {
        "import/resolver": {
            node: {
                extensions: [".js", ".ts"],
                moduleDirectory: ["src", "node_modules"],
            },
        },
    },
    rules: {
        "@typescript-eslint/no-non-null-assertion": "error",
        "prettier/prettier": ["warn", { prettierOptions }],
        "@typescript-eslint/no-explicit-any": "off",
        "no-unused-vars": "off",
        "no-use-before-define": "off",
        "import/no-default-export": 0,
        "@typescript-eslint/no-use-before-define": ["error"],
        "import/no-unresolved": "off",
        "no-shadow": "off",
        "@typescript-eslint/no-shadow": "off",
        "no-loop-func": "off",
        "no-param-reassign": "off",
        "no-nested-ternary": "off",
        "import/no-extraneous-dependencies": "off",
        "no-useless-constructor": "off",
        "max-classes-per-file": "off",
        "no-empty-function": "off",
        "import/prefer-default-export": "off",
        "import/extensions": ["error", "ignorePackages", { "js": "never", "jsx": "never", "ts": "never", "tsx": "never" }],
        "import/order": [
            "warn",
            {
                groups: [
                    ["builtin", "external"],
                    "internal",
                    ["parent", "sibling", "index"],
                    "object",
                    "type",
                ],
                "newlines-between": "always",
                alphabetize: {
                    order: "asc",
                },
                pathGroups: [
                    {
                        pattern: "./**/*.less",
                        group: "object",
                    },
                    {
                        pattern: "**/*.less",
                        group: "object",
                    },
                    {
                        pattern: "./**/*.{jpg,jpeg,png,gif,svg,ico}",
                        group: "type",
                    },
                    {
                        pattern: "**/*.{jpg,jpeg,png,gif,svg,ico}",
                        group: "type",
                    },
                ],
            },
        ],
    },
};


My .prettierrc.json config:

{
    "arrowParens": "always",
    "bracketSpacing": true,
    "jsxSingleQuote": true,
    "quoteProps": "as-needed",
    "htmlWhitespaceSensitivity": "css",
    "singleQuote": false,
    "semi": true,
    "printWidth": 120,
    "useTabs": true,
    "tabWidth": 8,
    "trailingComma": "all",
    "endOfLine": "auto",
    "proseWrap": "never"
}

And this is the new .eslint.config.js file generated by Vite:

import js from '@eslint/js'
import globals from 'globals'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'
import tseslint from 'typescript-eslint'

export default tseslint.config(
  { ignores: ['dist'] },
  {
    extends: [js.configs.recommended, ...tseslint.configs.recommended],
    files: ['**/*.{ts,tsx}'],
    languageOptions: {
      ecmaVersion: 2020,
      globals: globals.browser,
    },
    plugins: {
      'react-hooks': reactHooks,
      'react-refresh': reactRefresh,
    },
    rules: {
      ...reactHooks.configs.recommended.rules,
      'react-refresh/only-export-components': [
        'warn',
        { allowConstantExport: true },
      ],
    },
  },
)

I want to use the new version of eslint generated by Vite but I can't get it to work properly, VSCode either totaly ignored the rules or just threw an error in the output teminal with missing plugin (which was installed) or somekind of "0" error.


Solution

  • Here's a working configuration, which took a while to collect everything. It's tailored towards TypeScript (with strict type checks), but has everything working as a start:

    import eslint from "@eslint/js";
    import tslint from "typescript-eslint";
    import stylistic from "@stylistic/eslint-plugin";
    import jsdoc from "eslint-plugin-jsdoc";
    import preferArrow from "eslint-plugin-prefer-arrow";
    import importPlugin from "eslint-plugin-import";
    
    export default tslint.config(
        eslint.configs.recommended,
        ...tslint.configs.strictTypeChecked,
        ...tslint.configs.stylisticTypeChecked,
        jsdoc.configs["flat/recommended"],
        {
            // This must be in its own object to avoid a bug in the ESLint parser.
            ignores: ["src/generated/*"],
        },
        {
            plugins: {
                "@stylistic": stylistic,
                "jsdoc": jsdoc,
                "prefer-arrow": preferArrow,
                "import": importPlugin,
            },
            languageOptions: {
                parser: tslint.parser,
                parserOptions: {
                    projectService: {
                        defaultProject: "tsconfig.json",
                        allowDefaultProject: ["*.mjs", "jest.config.ts"],
                        project: "tsconfig.json",
                    },
                    tsconfigRootDir: import.meta.dirname,
                    sourceType: "module",
                },
            },
            rules: {
                "no-fallthrough": [
                    "warn",
                    {
                        "commentPattern": "\\[falls?-through\\]",
                        "allowEmptyCase": true
                    }
                ],
                "max-len": [
                    "error",
                    {
                        "ignoreRegExpLiterals": false,
                        "ignoreStrings": false,
                        "code": 120
                    }
                ],
                "brace-style": ["error", "1tbs", { "allowSingleLine": false }],
                "curly": ["error", "all"],
                "arrow-body-style": ["error", "always"],
                "@stylistic/padding-line-between-statements": [
                    "error",
                    {
                        "blankLine": "always",
                        "prev": "*",
                        "next": "return"
                    }
                ],
                "@stylistic/quotes": [
                    "error",
                    "double",
                    {
                        "avoidEscape": true,
                        "allowTemplateLiterals": true
                    }
                ],
    
    ... etc.
            },
        }
    );
    
    

    I stored it in a file eslint.config.mjs, because I use ESM. ESLint should automatically pick it up, but sometimes I had to restart it manually (Command pallette -> ESLint: Restart ESLint Server) or even restarted VS Code. For more details check also the "ESLint" OUTPUT channel, which gives a lot of information, if something with the config file is wrong. If that channel doesn't exist it means your ESLint isn't even starting.