sveltejs / eslint-plugin-svelte

ESLint plugin for Svelte using AST
https://sveltejs.github.io/eslint-plugin-svelte/
MIT License
304 stars 38 forks source link

How to specify rules for js and ts components differently? #152

Open shirotech opened 2 years ago

shirotech commented 2 years ago

For example config below:

{
"overrides": [
    {
      "files": ["*.ts", "*.tsx", "*.svelte"],
      "parserOptions": {
        "project": ["**/tsconfig.json"],
        "extraFileExtensions": [".svelte"]
      },
      "rules": {
        "@typescript-eslint/ts-specific-rules": "error"

      }
    },
    {
      "files": ["*.svelte"],
      "parser": "svelte-eslint-parser",
      "parserOptions": {
        "parser": {
          "ts": "@typescript-eslint/parser",
          "js": "espree",
          "typescript": "@typescript-eslint/parser"
        }
      },
      "rules": {
        "@ota-meshi/svelte/no-at-html-tags": "off",
        "@ota-meshi/svelte/button-has-type": "warn",
        "@ota-meshi/svelte/no-useless-mustaches": "error",
        "@ota-meshi/svelte/require-optimized-style-attribute": "warn",
        "@ota-meshi/svelte/html-quotes": "error",
        "@ota-meshi/svelte/indent": "error",
        "@ota-meshi/svelte/max-attributes-per-line": [
          "error",
          {
            "multiline": 1,
            "singleline": 5
          }
        ],
        "@ota-meshi/svelte/mustache-spacing": [
          "error",
          {
            "textExpressions": "never",
            "attributesAndProps": "never",
            "directiveExpressions": "never",
            "tags": {
              "openingBrace": "never",
              "closingBrace": "never"
            }
          }
        ],
        "@ota-meshi/svelte/prefer-class-directive": "error",
        "@ota-meshi/svelte/prefer-style-directive": "error",
        "@ota-meshi/svelte/shorthand-attribute": "error",
        "@ota-meshi/svelte/shorthand-directive": "error",
        "@ota-meshi/svelte/spaced-html-comment": "error"
      }
    }
  ]
}

The above works fine for lang="ts" components, but not for normal js components. Since it is still a svelte file, and will inherit all the typescript rules, giving some weird parsing errors etc. also its listing typescript specific rules for non-typescript segments. Hope this all makes sense.

ota-meshi commented 2 years ago

Thank you for posting this issue!

Currently it does not support changing rules on each lang. I think that to do that, we need to use the eslint processor to assign a virtual file name. https://eslint.org/docs/developer-guide/working-with-plugins#processors-in-plugins

But using virtual filenames presents various problems. For example, typescript-eslint cannot get the correct type information, eslint-plugin-prettier cannot select the correct parser, eslint-plugin-import cannot get the correct PATH information, etc.

However, ESLint has provided a new API in v7.28 that may solve these problems. It is worth trying these again after this plugin discontinues support for v7. https://eslint.org/blog/2021/06/eslint-v7.28.0-released#contextgetphysicalfilename

shirotech commented 2 years ago

Hi @ota-meshi thank you very much for the fast response, the information is very insightful. Hope there is a smart way to work around this. I really like your plugin better than the official one, much cleaner. Great job on it! 👍

shirotech commented 2 years ago

Here is a brute force solution if anyone is interested:

import glob from 'fast-glob';
import fs from 'fs';
import path from 'path';

const scriptTs = 'lang="ts"';
const svelteTsFiles = glob
  .sync(['**/*.svelte', '!**/node_modules/**'], {cwd: path.resolve()})
  .filter((filePath) => fs.readFileSync(filePath, 'utf8').includes(scriptTs));

export const baseOverrides = {
  files: ['*.ts', '*.mts', '*.cts', '*.tsx'].concat(svelteTsFiles),
  parser: '@typescript-eslint/parser',
  parserOptions: {
    project: ['**/tsconfig.json'],
    extraFileExtensions: ['.svelte'],
  },
  // other configs and rules here
};

Might have some performance impact if you have thousands of svelte files, but if not should be fine. The idea is to scan all svelte files for lang="ts" which will assume that it is a typescript svelte component and list it in the files to serve your typescript rules. The above code can be injected to an existing or new config.

dyllandry commented 4 months ago

Coming from the referenced issue in vuejs/vue-eslint-parser, shirotech's solution above was a big deal for me. I'm starting a migration to incrementally add TS to a vue codebase, so only applying TS eslint rules to lang="ts" vue files is helpful.

I just noticed though that after adding or removing lang="ts" the eslint server needs to be restarted to reload the configuration file. It's good enough though :)