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

Linting a Vue SFC containing JSX throws a parsing error #150

Closed
leonsilicon opened this issue Apr 15, 2022 · 9 comments
Closed

Linting a Vue SFC containing JSX throws a parsing error #150

leonsilicon opened this issue Apr 15, 2022 · 9 comments

Comments

@leonsilicon
Copy link

leonsilicon commented Apr 15, 2022

The following Vue SFC causes a lint error:

<script setup lang="jsx">
const JsxElement = (
	<div>
		Rendered from JSX!
	</div>
)
</script>

<template>
	<JsxElement />
</template>

image

Repro: https://github.com/leonzalion/vue-eslint-plugin-jsx-bug

I did some debugging, and I think the problem has to do with the .vue file extension when the file is passed to @typescript-eslint/parser.

This is the parserOptions configuration passed to @typescript-eslint/parser at this line:

? parser.parseForESLint(code, parserOptions)

 {
  comment: true,
  loc: true,
  range: true,
  tokens: true,
  ecmaVersion: undefined,
  sourceType: 'module',
  parser: '@typescript-eslint/parser',
  project: '/Users/leonzalion/projects/jsx-test/tsconfig.json',
  extraFileExtensions: [ '.vue' ],
  ecmaFeatures: { jsx: true, globalReturn: false },
  raw: true,
  eslintVisitorKeys: true,
  eslintScopeManager: true,
  filePath: '/Users/leonzalion/projects/jsx-test/src/app.vue'
}

When I modified the JavaScript code and only changed the above parserOptions.filePath parameter from app.vue to app.jsx, the linter didn't throw any error.

I then traced down the bug through the @typescript-eslint/parser code and I think it has to do with the following line: https://github.com/typescript-eslint/typescript-eslint/blob/5e794512bf124c39de76d4e2cf8a3d6cfb08f1a8/packages/typescript-estree/src/create-program/createWatchProgram.ts#L351

I think this line causes TypeScript to try and infer the type of the file based on the file extension, and because the file extension in .vue (which TypeScript doesn't recognize), TypeScript treats the file as a .ts file by default, causing the parsing to fail since the file is actually a JSX file.

(Sidenote: when I tried changing that line to ts.ScriptKind.TSX, it gave me the following error:)
Screen Shot 2022-04-15 at 3 58 37 PM

When I modified the TypeScript compiler code and added a case ".vue": under this line, the lint then started working again (you can test this by running npx patch-package && npm lint in the repro).

As somebody who isn't really experienced with the internals of ESLint, I'm not exactly sure what the best way to fix this is, so please let me know what you think!

@leonsilicon leonsilicon changed the title Linting a Vue SFC containing JSX does not work Linting a Vue SFC containing JSX throws a parsing error Apr 15, 2022
@douira
Copy link

douira commented May 23, 2022

I tried jsx: false as suggested in this related issue vuejs/eslint-plugin-vue#1223 but it didn't fix it for me. I think I'm experiencing the same issue as you are.

@ota-meshi
Copy link
Member

Thank you for posting this issue.

I think it's a typescript-eslint/parser spec. It is documented in the following link.
https://github.com/typescript-eslint/typescript-eslint/tree/main/packages/parser#parseroptionsecmafeaturesjsx

I think you need to stop using JSX in .vue file, empty parserOptions.project, or use another parser.

@ota-meshi
Copy link
Member

By the way, even if we change the extension virtually on the parser, parsing will fail because it is a file that does not actually exist.

@leonsilicon
Copy link
Author

leonsilicon commented May 24, 2022

Would it be possible to do something like vue-tsc where they "patch" the list of extensions? https://github.com/johnsoncodehk/volar/blob/7975af0076523a3deb75f1ade2c2d5cd81043b53/packages/vue-tsc/bin/vue-tsc.js#L15

@ota-meshi
Copy link
Member

I'm not familiar with the API inside typescript so I don't know 😓

@leonsilicon
Copy link
Author

leonsilicon commented May 24, 2022

When I modified the TypeScript compiler code and added a case ".vue": under this line, the lint then started working again (you can test this by running npx patch-package && npm lint in the repro).

I'm not sure if this is a foolproof solution, but maybe it might be possible to do that at runtime using vue-tsc's "patching" approach? Would a PR attempting to do that be accepted?

Maybe @johnsoncodehk (creator of vue-tsc) could offer some insights into a potential solution :)

@ota-meshi
Copy link
Member

I don't accept the patch approach PR because I don't think it's guaranteed. I think you'll need to ask the TypeScript team to add a guaranteed API if needed.

@ota-meshi
Copy link
Member

You may be able to patch it personally by putting the following script in .eslintrc.js.

        const ts = require('typescript')
        const { ensureScriptKind } = ts
        ts.ensureScriptKind = function (fileName, ...args) {
            if (fileName.endsWith(".vue")) {
                return ts.ScriptKind.TSX
            }
            return ensureScriptKind.call(this, fileName, ...args)
        }

@VitoBryliano
Copy link

You can try uses default settings .eslintrc.js after install step-by-step vue.js with npm.

// .eslintrc.js

'extends': [
    'plugin:vue/vue3-essential',
    'eslint:recommended',
    '@vue/eslint-config-typescript',
    '@vue/eslint-config-prettier/skip-formatting'
  ],
  parserOptions: {
    ecmaVersion: 'latest'
  },

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

4 participants