17

I am trying to upgrade to eslint 9 on an existing project. I use vue3 and typescript 5 as well as prettier.

The issue lies with the new flat config which I haven't used before. I have figured it out that I should be using a different plugin, specifically this one https://eslint.vuejs.org/user-guide/, but it only seems to serve javascript .vue files and not typescript ones.

How can I write eslint.config.js in order to work with vue3, typescript and eslint 9 ?

This is what I have tried:

import eslint from "@eslint/js";
import tseslint from "typescript-eslint";
import pluginVue from "eslint-plugin-vue";
import eslintConfigPrettier from "eslint-config-prettier";

export default tseslint.config(
  {
    ignores: ["dist/**", "node_modules/**", "**/cypress/**", "html/", "coverage/"],
    files: ["**/*.vue", "**/*.js", "**/*.ts", "**/*.jsx", "**/*.tsx"],
    languageOptions: {
      ecmaVersion: 2020,
    },
  },
  eslint.configs.recommended,
  {
    plugins: {
      "@typescript-eslint": tseslint.plugin,
    },
    languageOptions: {
      parser: tseslint.parser,
      parserOptions: {
        project: true,
      },
    },
  },
  ...pluginVue.configs["flat/recommended"],
  ...pluginVue.configs["flat/strongly-recommended"],
  ...pluginVue.configs["flat/essential"],
  {
    rules: {
      "vue/match-component-import-name": "warn",
      "vue/match-component-file-name": [
        "error",
        {
          extensions: ["vue"],
          shouldMatchCase: true,
        },
      ],
      "vue/component-definition-name-casing": ["error", "PascalCase"],
      "vue/block-tag-newline": [
        "warn",
        {
          singleline: "always",
          multiline: "always",
          maxEmptyLines: 0,
        },
      ],
      "vue/html-self-closing": [
        "error",
        {
          html: {
            void: "always",
            normal: "never",
            component: "always",
          },
          svg: "always",
          math: "always",
        },
      ],
      "vue/require-default-prop": "off",
    },
  },
  eslintConfigPrettier
);
1

2 Answers 2

24

I encountered similar issues and here's what I've managed to figure out so far:

  1. Eslint Version Compatibility: Eslint is listed as a peer dependency of typescript-eslint v7.7.0 with a semantic versioning number ^8.56.0. I believe that implies version 9.x.x of Eslint shouldn't actually be used. To ensure compatibility with all types of configuration files, I suggest upgrading to either version 8.57.0 at most, especially if you're using .mjs or .cjs configs. More details about this can be found in the Eslint release notes.

  2. Vue Plugin Configuration: The documentation for eslint-plugin-vue primarily covers the legacy configuration format. However, it does provide an example in the flat config format:

    import pluginVue from 'eslint-plugin-vue';
    export default [
      ...pluginVue.configs['flat/recommended'],
    ];
    

    According to the documentation, configs['flat/recommended'] is a superset of .configs['flat/strongly-recommended'], which is a superset of .configs['flat/essential']. Thus, it only makes sense to add one of these configs to your file. As you have already done, one should follow the recommendation from the typescript-eslint documentation by usingtseslint.config and @ts-check, and including eslint's and typescript-eslint's recommended configuration objects:

    // @ts-check
    
    import eslint from '@eslint/js';
    import tseslint from 'typescript-eslint';
    import pluginVue from 'eslint-plugin-vue';
    
    export default tseslint.config(
      eslint.configs.recommended,
      ...tseslint.configs.recommended,
      ...pluginVue.configs['flat/recommended']
    );
    
  3. Custom Parser for Typescript: Currently, we're utilizing the vue-eslint-parser that comes bundled with eslint-plugin-vue. By default, it only parses JavaScript within Vue files. The documentation states

    If you're already using another parser, such as "parser": "@typescript-eslint/parser", it's essential to relocate it into parserOptions to prevent collision with the vue-eslint-parser used by this plugin's configuration...

    As you've already done, we need to use the languageOptions property in the flat format, as described in the documentation, to tailor the parser for TypeScript. Lastly, to avoid conflicts with Prettier, make sure to include eslint-config-prettier as the final element in the configuration, as suggested by the documentation. This results in the following configuration:

     //@ts-check
     import eslint from '@eslint/js';
     import tseslint from 'typescript-eslint';
     import pluginVue from 'eslint-plugin-vue';
     import eslintConfigPrettier from 'eslint-config-prettier';
    
     export default tseslint.config(
       eslint.configs.recommended,
       ...tseslint.configs.recommended,
       ...pluginVue.configs['flat/recommended'],
       {
         plugins: {
           'typescript-eslint': tseslint.plugin,
         },
         languageOptions: {
           parserOptions: {
             parser: tseslint.parser,
             project: './tsconfig.app.json',
             extraFileExtensions: ['.vue'],
             sourceType: 'module',
           },
         },
       },
       eslintConfigPrettier
     );
    

Make sure you reference a tsconfig file that includes .vue files. A minimal example extending off of @vue/tsconfig is

{
  "extends": "@vue/tsconfig/tsconfig.dom.json",
  "include": ["src/**/*", "src/**/*.vue"]
}

One more thing. If you are using VSCode and the official ESLint extension, make sure to set eslint.experimental.useFlatConfig to true in your settings.json file. Otherwise, the ESLint extension will try to search for the legacy configuration file. Hope this helps!

Sign up to request clarification or add additional context in comments.

1 Comment

The eslint.experimental.useFlatConfig option is not needed anymore since version 3.0.10 of the vscode-eslint plugin.
4

It look me hours to come up with this (works as of now!) I have removed all the things that didn't seem to be doing anything, so this is as barebones as possible for general use. Name this eslint.config.js. I put it in the root of my monorepo, and plan to forget about it as much as possible.

import eslintJs from '@eslint/js'
import tsEslint from 'typescript-eslint'
import vueParser from 'vue-eslint-parser'
import pluginVue from 'eslint-plugin-vue'

export default [
    {
        //---- GLOBAL IGNORES
        // note folders can only be ignored at the global level, per-cfg you must do: '**/dist/**/*'
        ignores: [
            '**/dist/',
            '**/vendor/',
        ],
    },
    // general defaults
    eslintJs.configs['recommended'],
    // general
    {
        files: ['**/*.{js,ts,jsx,tsx,vue}'],
        languageOptions: {
            ecmaVersion: 'latest',
            sourceType: 'module',
        },
        rules: {},
    },

    // chosen typescript defaults - could not get this working
    // ...tsEslint.configs['recommended'],
    // typescript
    {
        files: ['**/*.{ts,tsx,vue}'],
        languageOptions: {
            parser: tsEslint.parser,
        },
    },

    // chosen vue defaults
    ...pluginVue.configs['flat/essential'],
    // vue
    {
        files: ['**/*.vue'],
        languageOptions: {
            parser: vueParser,
            parserOptions: {
                parser: tsEslint.parser,  // parse TS inside VUE
            },
        },
    },
]

3 Comments

the hint with the 'ignores' was very important. Thanks for that. I'ts not obvious if you read the docs for eslint, then typescript-eslint then for eslint-plugin-vue. To clarify even more: the 'ignores' directive should come first - before any other config
I tried your solution with your setup + default vite/vue installation. However it does not lint/mark errors in vue or TS files. Does this still work for you or do you have any other changes?
Thank You; this helped me tremendously. I believe that tsEslint.configs['recommended'] appears to be working for me.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.