Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using volar together with @typescript-eslint/parser for type checking rules to understand SFC component types #2189

Closed
DrJume opened this issue Dec 10, 2022 · 8 comments

Comments

@DrJume
Copy link

DrJume commented Dec 10, 2022

I'm using @typescript-eslint in a TS project with type checking enabled (https://typescript-eslint.io/linting/configs#recommended-requiring-type-checking).

This allows eslint to understand typescript types and make suggestions.

Sadly Vue SFC files are interpreted with type any, they are not correctly loaded.
I found an option for @typescript-eslint/parser to specify a TS program in the options: (https://typescript-eslint.io/architecture/parser/#program). So it should be possible to pass the Volar proxy into @typescript-eslint/parser. This should allow eslint to also run type linting for Vue SFCs.
But sadly I didn't get it to work.

How can I get a TS program which uses Volar and pass it to @typescript-eslint/parser?

This is my eslint config override for '*.vue' files:

...
overrides: [
    {
        files: ['*.vue'],
        extends: [
            'plugin:@typescript-eslint/recommended-requiring-type-checking',
            'plugin:@typescript-eslint/strict',
        ],
        parser: 'vue-eslint-parser',
        parserOptions: {
            parser: '@typescript-eslint/parser',
            parserOptions: {
                extraFileExtensions: ['.vue'],
                tsconfigRootDir: __dirname,
                project: ['./tsconfig.app.json'],
                program: vueTsc.createProgramProxy({
                    rootNames: ['./src/main.ts'],
                    options: compilerOptions,
                    host: ts.createCompilerHost(compilerOptions),
                }),
            },
        }
    }
]
...

I also tried to add the typescript-vue-plugin as a typescript plugin, to get SFCs to work with eslint type checking, but it also didn't work...

"compilerOptions": {
    "plugins": [
        {
            "name": "typescript-vue-plugin"
        }
    ]
}
@johnsoncodehk
Copy link
Member

This is my eslint config override for '*.vue' files:

Can you provide this project? It can help me quickly understand, thanks!

@DrJume
Copy link
Author

DrJume commented Dec 10, 2022

Yeah sure!
https://github.com/DrJume/vue-volar-eslint

  1. Enable Takeover Mode
  2. Active ESLint VSCode Plugin
  3. Go to main.ts
  4. @typescript/eslint can't read type of .vue file correctly

It would be awesome if it would work with Volar!

image

@DrJume
Copy link
Author

DrJume commented Dec 10, 2022

Is integrating Volar with @typescript-eslint/parser possible? I thought the program option would be the solution, but I couldn't get it to work... This is my current state of experimentation:

.eslintrc.cjs:

require('@rushstack/eslint-patch/modern-module-resolution')

const parser = require('@typescript-eslint/parser')
const ts = require('typescript')
const vueTsc = require('vue-tsc/out/proxy')

const tsProgram = parser.createProgram('./tsconfig.json', __dirname)

module.exports = {
  root: true,
  extends: [
    'plugin:vue/vue3-essential',
    'eslint:recommended',
    'plugin:@typescript-eslint/recommended',
    '@vue/eslint-config-typescript/recommended',
    '@vue/eslint-config-prettier',
  ],
  parser: 'vue-eslint-parser',
  parserOptions: {
    tsconfigRootDir: __dirname,
  },

  overrides: [
    ...
    {
      files: ['*.ts', '*.mts'],
      extends: [
        'plugin:@typescript-eslint/recommended-requiring-type-checking',
        'plugin:@typescript-eslint/strict',
      ],
      parserOptions: {
        // project: ['./tsconfig.json'],
        program: vueTsc.createProgramProxy({ // <-- it would be so cool, if this would work!
            rootNames: ['./src/main.ts'], // tsProgram.getSourceFiles().map((sf) => sf.fileName) ?
            options: compilerOptions,
            host: ts.createCompilerHost(compilerOptions),
        }),
      },
    },
  ],
}

@johnsoncodehk
Copy link
Member

johnsoncodehk commented Dec 10, 2022

It seems that ESLint TypeScript document incorrect, you should use parserOptions.programs instead of parserOptions.program, and this is works to me.

const { createComponentMetaChecker } = require('vue-component-meta')
const tsConfig = require.resolve('./tsconfig.json')
const checker = createComponentMetaChecker(tsConfig)
const program = checker.__internal__.tsLs.getProgram()

// ...
parserOptions: {
  programs: [program]
},

@johnsoncodehk
Copy link
Member

Now you can use https://github.com/johnsoncodehk/volar-plugins/tree/master/packages/eslint with takeover mode, I will write more information in Discussions for it.

Please note that you need to remove require('@rushstack/eslint-patch... from your repro. (But I don't know the reason)

@DrJume
Copy link
Author

DrJume commented Dec 15, 2022

Thanks for getting back with this elegant solution!

I have tried it with https://github.com/DrJume/vue-volar-eslint and it seems to work (.vue files are not any anymore!) but still gives the following error for every .vue file:

Parsing error: "parserOptions.programs" has been provided for @typescript-eslint/parser.
The file was not found in any of the provided program instance(s): src/components/icons/IconDocumentation.vue

I dug around in the code with the help of debug output (DEBUG=* pnpm lint) and the error is thrown inside

@typescript-eslint/typescript-estree -> createProgram -> useProvidedPrograms(), because

getAstFromProgram(programInstance, parseSettings) returns undefined.

Inside getAstFromProgram() it tries to get programInstance.getSourceFile(parseSettings.filePath),

but it returns undefined!

getSourceFile() is implemented in typescript/src/compiler/program.ts. It essentially tries to access the internal Map filesByName which should be returned by program.getFilesByNameMap().

But running getFilesByNameMap() on the Volar program instance does not return a Map!


So it seems to work, and I think the error is caused by Volar for having an incompatible interface with ts.Program.

@DrJume
Copy link
Author

DrJume commented Mar 16, 2023

@johnsoncodehk

It still doesn't work completely. The issue stated above still exists. Can you reopen this issue?
I hope my possible explanation above can help.

@johnsoncodehk
Copy link
Member

johnsoncodehk commented Mar 29, 2023

That issue cannot be solved here. It belongs to eslint-plugin-vue. For progress updates, please follow DrJume/vue-volar-eslint#1.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants