Skip to content

ESLint

ESLint is a widely adopted linter, mainly for script files.

Installation

You have to install ESLint first. Currently ESLint v8 is supported.

shell
npm install eslint -D
shell
yarn add eslint -D
shell
pnpm install eslint -D
shell
bun install eslint -d

Configuration

Use other config files

Try eslint-ts-patch if you want to use other config files.

ESM

javascript
// eslint.config.mjs
// or eslint.config.js with "type": "module" in package.json
import { eslint } from '@modyqyw/fabric';
// or
// import { eslint } from '@modyqyw/fabric/eslint';

export default eslint();

CJS

javascript
// eslint.config.cjs
// or eslint.config.js without "type": "module" in package.json
const { eslint } = require('@modyqyw/fabric');
// or
// const { eslint } = require('@modyqyw/fabric/eslint');

module.exports = eslint();

CLI

Update your package.json and add lint:eslint script.

json
{
  "scripts": {
    "lint:eslint": "eslint . --fix --cache"
  }
}

Customization

Parameters

Passing parameters to the exported eslint method can customize, and the eslint method takes two parameters.

The first parameter is used for basic customization, you can pass either undefined or an object. To explicitly enable or disable a plugin, you need to explicitly set the boolean value in the passed object.

The following plugins are currently supported:

javascript
// eslint.config.mjs
// or eslint.config.js with "type": "module" in package.json
import {
  eslint,
  hasNext,
  hasNuxt,
  hasReact,
  hasReactNative,
  hasSolid,
  hasTypeScript,
  hasUnoCss,
  hasVue,
} from '@modyqyw/fabric';

export default eslint({
  // based on eslint-plugin-flat-gitignore
  // true by default, enabled
  gitignore: true,
  // based on ignores option
  // true by default, enabled
  ignores: true,
  // based on eslint-plugin-i and eslint-plugin-import
  // true by default, enabled
  imports: true,
  // based on @eslint/js
  // true by default, enabled
  javascript: true,
  // based on eslint-plugin-jsdoc
  // false by default, disabled
  jsdoc: false,
  // based on eslint-plugin-jsonc
  // true by default, enabled
  jsonc: true,
  // based on eslint-plugin-markdown
  // true by default, enabled
  markdown: true,
  // based on @next/eslint-plugin-next
  // enabled by default if you have Next installed
  next: hasNext,
  // based on eslint-plugin-n
  // true by default, enabled
  node: true,
  // based on eslint-plugin-nuxt
  // enabled by default if you have Nuxt installed
  nuxt: hasNuxt,
  // based on eslint-plugin-perfectionist
  // true by default, enabled
  perfectionist: true,
  // based on eslint-plugin-prettier 和 eslint-config-prettier
  // true by default, enabled
  prettier: true,
  // based on eslint-plugin-react、eslint-plugin-react-hooks 和 eslint-plugin-react-refresh
  // enabled by default if you have React installed
  react: hasReact,
  // based on eslint-plugin-react-native
  // enabled by default if you have ReactNative installed
  reactNative: hasReactNative,
  // based on eslint-plugin-regexp
  // true by default, enabled
  regexp: true,
  // based on eslint-plugin-solid
  // enabled by default if you have solid installed
  solid: hasSolid,
  // based on @typescript-eslint/eslint-plugin
  // enabled by default if you have TypeScript installed
  typescript: hasTypeScript,
  // based on eslint-plugin-unicorn
  // true by default, enabled
  unicorn: true,
  // based on eslint-plugin-unocss
  // enabled by default if you have UnoCSS installed
  unocss: hasUnoCss,
  // based on eslint-plugin-vue 和 eslint-plugin-vue-scoped-css
  // enabled by default if you have Vue installed
  vue: hasVue,
  // based on eslint-plugin-yml
  // true by default, enabled
  yml: true,
});

In addition to passing Boolean values, you can also pass configuration items directly. This will be treated as enabling the configuration and the passed-in configuration item will be used.

javascript
// eslint.config.mjs
// or eslint.config.js with "type": "module" in package.json
import { eslint } from '@modyqyw/fabric';

export default eslint({
  gitignore: {
    files: ['.gitignore', '.eslintignore'],
    strict: false,
  },
});

Potential conflicts

  1. eslint-plugin-perfectionist may conflict with @ianvs/prettier-plugin-sort-imports, @trivago/prettier-plugin-sort-imports, prettier-plugin-organize-imports, prettier-plugin-organize-attributes. If you want to enable one of the four, turn off eslint-plugin-perfectionist, see Prettier chapter customization section.

  2. eslint-plugin-jsonc does not conflict with prettier-plugin-packagejson by default. If you want to use eslint-plugin-jsonc to sort package.json, see Combination section and disable prettier-plugin-packagejson.

  3. eslint-plugin-unocss may conflict with prettier-plugin-tailwindcss. When you use both TailwindCSS and UnoCSS, please disable one of them manually. If you want to disable prettier-plugin-tailwindcss, see Prettier chapter Customization.

The second parameter is used for further customization, you can pass an object to override the generated configuration.

javascript
// eslint.config.mjs
// or eslint.config.js with "type": "module" in package.json
import { eslint } from '@modyqyw/fabric';

export default eslint(undefined, [
  // By default, using console in scripts/**/* and cli.* does not report errors and warnings.
  // Adjusted so that using console in scripts/**/* and cli.* gives an error.
  // Need to switch to specialized logging libraries such as winston, consola, pino, etc.
  {
    files: ['scripts/**/*', 'cli.*'],
    rules: {
      'no-console': 'error',
    },
  },
]);

Combination

In addition to customizing with parameters, you can also customize with combinations.

For example, using only JavaScript and TypeScript rules:

javascript
// eslint.config.mjs
// or eslint.config.js with "type": "module" in package.json
import { combine } from '@modyqyw/fabric';
import { javascript, typescript } from '@modyqyw/fabric/eslint';

export default combine(javascript(), typescript());

You can also pass parameters to adjust some of the rules.

javascript
// eslint.config.mjs
// or eslint.config.js with "type": "module" in package.json
import { combine } from '@modyqyw/fabric';
import { javascript, typescript } from '@modyqyw/fabric/eslint';

export default combine(
  javascript({ rules: { 'no-console': 'error' } }),
  typescript(),
);

Add Svelte support to the above (you need to install the dependencies yourself):

javascript
// eslint.config.mjs
import * as _parserBabel from '@babel/eslint-parser';
// or eslint.config.js with "type": "module" in package.json
import {
  GLOB_DTS,
  GLOB_SCRIPT,
  GLOB_SVELTE,
  GLOB_TS,
  GLOB_TSX,
  GLOB_VUE,
  combine,
  interopDefault,
} from '@modyqyw/fabric';
import { javascript, typescript } from '@modyqyw/fabric/eslint';
import * as _parserTypeScript from '@typescript-eslint/parser';
import * as _pluginSvelte from 'eslint-plugin-svelte';
import * as _parserSvelte from 'svelte-eslint-parser';

const parserSvelte = interopDefault(_parserSvelte);
const parserBabel = interopDefault(_parserBabel);
const parserTypeScript = interopDefault(_parserTypeScript);
const pluginSvelte = interopDefault(_pluginSvelte);

export default combine(
  // match Svelte files
  javascript({ files: [GLOB_SCRIPT, GLOB_VUE, GLOB_SVELTE] }),
  typescript({ files: [GLOB_DTS, GLOB_TS, GLOB_TSX, GLOB_VUE, GLOB_SVELTE] }),
  // support Svelte
  [
    {
      files: ['**/*.svelte'],
      languageOptions: {
        ecmaFeatures: {
          globalReturn: false,
          jsx: false,
        },
        ecmaVersion: 'latest',
        extraFileExtensions: ['.svelte'],
        parser: {
          js: parserBabel,
          jsx: parserBabel,
          ts: parserTypeScript,
          tsx: parserTypeScript,
        },
        requireConfigFile: false,
        sourceType: 'module',
      },
      plugins: {
        svelte: pluginSvelte,
      },
      processor: pluginSvelte.processors['.svelte'],
      rules: {
        // ESLint core rules known to cause problems with `.svelte`.
        'no-inner-declarations': 'off', // The AST generated by svelte-eslint-parser will false positives in it rule because the root node of the script is not the `Program`.
        // "no-irregular-whitespace": "off",
        // Self assign is one of way to update reactive value in Svelte.
        'no-self-assign': 'off',
        // eslint-plugin-svelte rules
        'svelte/comment-directive': 'error',
        'svelte/system': 'error',
        ...pluginSvelte.configs.recommended.rules,
        ...pluginSvelte.configs.prettier.rules,
      },
    },
  ],
);

Integration

VSC

Install the corresponding ESLint plugin first.

Update user settings or workspace settings as appropriate.

jsonc
{
  // Enable ESLint flat config.
  "eslint.experimental.useFlatConfig": true,

  // Languages that ESLint validates.
  "eslint.validate": [
    "javascript",
    "javascriptreact",
    "typescript",
    "typescriptreact",
    "vue",
    "markdown",
    "json",
    "jsonc",
    "yaml",
  ],

  // ESLint auto-fix after JavaScript, JSX, TypeScript, TypeScript JSX manual save.
  "[javascript][javascriptreact][typescript][typescriptreact]": {
    "editor.codeActionsOnSave": {
      "source.fixAll.eslint": "explicit",
    },
  },

  // ESLint auto-fix after Vue manual save.
  "[vue]": {
    "editor.codeActionsOnSave": {
      "source.fixAll.eslint": "explicit",
    },
  },

  // ESLint auto-fix after markdown manual save.
  "[markdown][yaml][json][jsonc]": {
    "editor.codeActionsOnSave": {
      "source.fixAll.eslint": "explicit",
    },
  },

  // ESLint auto-fix after JSON, JSONC manual save.
  "[json][jsonc]": {
    "editor.codeActionsOnSave": {
      "source.fixAll.eslint": "explicit",
    },
  },

  // ESLint auto-fix after YAML manual save.
  "[yaml]": {
    "editor.codeActionsOnSave": {
      "source.fixAll.eslint": "explicit",
    },
  },
}

WebStorm

WebStorm comes with Prettier, see VSC? to tweak it yourself.

lint-staged

If you are using the lint-staged configuration provided by the package, see the lint-staged chapter.

If you are not, you can refer to the following configuration.

javascript
// lint-staged.config.mjs
// or lint-staged.config.js with "type": "module" in package.json
export default {
  '*.{js,cjs,mjs,jsx,ts,cts,mts,tsx,vue}':
    'eslint --fix --cache --no-error-on-unmatched-pattern',
  '*.{json,jsonc,json5}':
    'eslint --fix --cache --no-error-on-unmatched-pattern',
  '*.{yaml,yml}': 'eslint --fix --cache --no-error-on-unmatched-pattern',
};

Why not check types?

Typed rules come with a catch. By including parserOptions.project in your config, you incur the performance penalty of asking TypeScript to do a build of your project before ESLint can do its linting. For small projects this takes a negligible amount of time (a few seconds or less); for large projects, it can take longer.

Quoted from typescript-eslint.io/linting/typed-linting#how-is-performance.

Running tsc / vue-tsc directly avoids this performance loss and allows for more complete type checking. See the tsc chapter.

Released under the MIT License.