ESLint 用户指南

盖马鲁
2023-12-01

官方文档

ESLint 是一个根据方案识别并报告 ECMAScript/JavaScript 代码问题的工具,其目的是使代码风格更加一致并避免错误。

ESLint 是完全插件化的。每一条规则都是一个插件,你可以在运行时添加更多的插件。你也可以添加社区插件、配置和解析器来扩展 ESLint 的功能。

使用前提

要使用 ESLint,你必须使用内置 SSL 支持的 Node.js 版本(^12.22.0、^14.17.0 或 >=16.0.0),如果你使用的是官方 Node.js 发行版,那么已经内置了 SSL 支持。

快速开始

你可以使用该命令安装并配置 ESLint:

npm init @eslint/config
1

如果你想使用托管在 npm 上的指定可共享配置,你可以使用 --config 选项并指定包名:

# 使用 `eslint-config-semistandard` 可共享配置# npm 7+npm init @eslint/config -- --config semistandard
# 或(可以省略 `eslint-config` 前缀)npm init @eslint/config -- --config eslint-config-semistandard
# ⚠ npm 6.x 无需使用额外的双杠:npm init @eslint/config --config semistandard
1234567

--config 标志也支持传递数组:

npm init @eslint/config -- --config semistandard,standard
# 或npm init @eslint/config -- --config semistandard --config standard
123

注意:使用 npm init @eslint/config 时,运行目录需要已经有 package.json 文件了。如果还没有该文件,请确保在此之前运行 npm init 或 yarn init。

在此之后,你可以像这样使用 ESLint 检查任一文件或目录:

npx eslint yourfile.js

# 或yarn run eslint yourfile.js

配置

注意:如果你正在使用 1.0.0 以前的版本,请阅读迁移指南

在运行 npm init @eslint/config 后,你的目录下会有 .eslintrc.{js,yml,json} 文件。在里面你可以看到类似于这样的一些已经配置好了的规则:

{"rules":{"semi":["error","always"],"quotes":["error","double"]}}123456

"semi" 和 "quotes" 是 ESLint 规则的名称。第一个值代表规则的错误级别,有以下几种可供选择:

  • "off" 或 0 - 关闭该规则

  • "warn" 或 1 - 启用并警告(不影响现有代码)

  • "error" 或 2 - 启用并报错(错误代码 1)

你可以调节这三档错误级别以精准控制 ESLint 规则实施方式(更多配置项和细节,参见配置文档)。

你的 .eslintrc.{js,yml,json} 配置文件也会包括这一行:

{"extends":"eslint:recommended"}123

这一行将启用所有标记为“推荐”的规则。另外,你也可以通过在 npmjs.com 上搜索“eslint-config”并使用别人创建的配置。在没有使用别人的可共享配置或在配置中明确启用规则时,ESLint 不会检查你的代码。

全局安装

也可以全局安装 ESLint 而不仅限于羡慕本地(使用 npm install eslint --global)。但并不推荐这样做,因为即使全局安装 ESLint,你仍需要在本地安装插件和可共享配置。

手动设置

你也可以在项目中手动设置 ESLint.

在开始前你必须确保存在 package.json 文件。如果不存在,请优先运行 npm init 或 yarn init 来创建此文件。

  1. 在项目中安装 ESLint 包:

npminstall --save-dev eslint
1
  1. 添加任一支持的配置文件格式的 .eslintrc 文件。

# 创建 JavaScript 配置文件touch .eslintrc.js
12
  1. 在 .eslintrc 文件中添加配置。阅读配置 ESLint 文档学习如何添加规则、环境、自定义配置、插件以及其他内容。

// .eslintrc.js 示例
module.exports ={"env":{"browser":true,"es2021":true},"extends":"eslint:recommended","parserOptions":{"ecmaVersion":"latest","sourceType":"module"},}123456789101112
  1. 使用 ESLint 命令行检查代码:

npx eslint project-dir/ file1.js

Angular ESLint

github地址

Packages included in this project

Please follow the links below for the packages you care about.

  • @angular-eslint/builder - An Angular CLI Builder which is used to execute ESLint on your Angular projects using standard commands such as ng lint

  • @angular-eslint/eslint-plugin - An ESLint-specific plugin that contains rules which are specific to Angular projects. It can be combined with any other ESLint plugins in the normal way.

  • @angular-eslint/eslint-plugin-template - An ESLint-specific plugin which, when used in conjunction with @angular-eslint/template-parser, allows for Angular template-specific linting rules to run.

  • @angular-eslint/schematics - Schematics which are used to add and update configuration files which are relevant for running ESLint on an Angular workspace.

  • @angular-eslint/template-parser - An ESLint-specific parser which leverages the @angular/compiler to allow for custom ESLint rules to be written which assert things about your Angular templates.

  • @angular-eslint/utils - Utilities which are helpful when writing and testing custom ESLint rules for Angular workspaces.

Notes on Supported ESLint Configuration File Types

We strongly recommend you stick to using .eslintrc.json.

This is not a constraint we force upon you, and you are more than welcome to use any of ESLint's supported file types for your ESLint config files, e.g. .eslintrc.js, .eslintrc.yml however please note that you will not receive any automated updates to your config from this toolset if you choose to use something other than .eslintrc.json. We will also only generate .eslintrc.json files from our code generators (which you could then convert yourself if you wanted to).

The reason for this is very simple - JSON is a format which is very easy to statically analyze and write transformations for and it is beyond the scope of this community-run project to provide multiple implementations of every possible migration for every possible ESLint configuration file type for every version we release.

Notes on ESLint Configuration Itself


It's important to understand up front that using Angular with ESLint is actually an advanced/complex use-case because of the nature of the files involved:

  • Angular projects use TypeScript files for source code

  • Angular projects use a custom/extended form of HTML for templates (be they inline or external HTML files)

The thing is: ESLint understands neither of these things out of the box.

Fortunately, however, ESLint has clearly defined points of extensibility that we can leverage to make this all work.

For detailed information about ESLint plugins, parsers etc please review the official ESLint documentation: https://eslint.org

The key principle of our configuration required for Angular projects is that we need to run different blocks of configuration for different file types/extensions. In other words, we don't want the same rules to apply on TypeScript files that we do on HTML/inline-templates.

Therefore, the critical part of our configuration is the "overrides" array:

{
  "overrides": [
    /**
     * -----------------------------------------------------
     * TYPESCRIPT FILES (COMPONENTS, SERVICES ETC) (.ts)
     * -----------------------------------------------------
     */
    {
      "files": ["*.ts"],

      // ... applies a special processor to extract inline Component templates
      // and treat them like HTML files
      "extends": ["plugin:@angular-eslint/template/process-inline-templates"]

      // ... other config specific to TypeScript files
    },

    /**
     * -----------------------------------------------------
     * COMPONENT TEMPLATES
     * -----------------------------------------------------
     */
    {
      "files": ["*.html"],
      // ... config specific to Angular Component templates
    }
  ]
}

By setting up our config in this way, we have complete control over what rules etc apply to what file types and our separate concerns remain clearer and easier to maintain.

Seriously, move (mostly) all configuration into overrides

Even though you may be more familiar with including ESLint rules, plugins etc at the top level of your config object, we strongly recommend only really having overrides (and a couple of other things like ignorePatterns, root etc) at the top level and including all plugins, rules etc within the relevant block in the overrides array.

Anything you apply at the top level will apply to ALL files, and as we've said above there is a really strict separation of concerns between source code and templates in Angular projects, so it is very rare that things apply to all files.

Let's take a look at full (but minimal), manual example of a config file (although we recommend deferring to the schematics for automatic config generation whenever possible):

.eslintrc.json

{
  "root": true,
  "ignorePatterns": ["projects/**/*"],
  "overrides": [
    {
      "files": ["*.ts"],
      "extends": [
        "eslint:recommended",
        "plugin:@typescript-eslint/recommended",
        "plugin:@angular-eslint/recommended",
        // This is required if you use inline templates in Components
        "plugin:@angular-eslint/template/process-inline-templates"
      ],
      "rules": {
        /**
         * Any TypeScript source code (NOT TEMPLATE) related rules you wish to use/reconfigure over and above the
         * recommended set provided by the @angular-eslint project would go here.
         */
        "@angular-eslint/directive-selector": [
          "error",
          { "type": "attribute", "prefix": "app", "style": "camelCase" }
        ],
        "@angular-eslint/component-selector": [
          "error",
          { "type": "element", "prefix": "app", "style": "kebab-case" }
        ]
      }
    },
    {
      "files": ["*.html"],
      "extends": ["plugin:@angular-eslint/template/recommended"],
      "rules": {
        /**
         * Any template/HTML related rules you wish to use/reconfigure over and above the
         * recommended set provided by the @angular-eslint project would go here.
         */
      }
    }
  ]
}
If I wanted to include other source code related rules extends etc, such as extending from eslint:recommended, then I would include that in the "extends": [] within the *.ts override block, NOT the root of the config object.

If you are looking for general help in migrating specific rules from TSLint to ESLint, you can check out this incredible project that we depend on in our conversion schematic: https://github.com/typescript-eslint/tslint-to-eslint-config

//pakage.json
{
  ...
  "scripts": {
    ...
    "lint": "ng lint",
    "lint-fix": "ng lint --fix",
    ...
  },
...
  "devDependencies": {
...
    "eslint": "7.32.0",
    "eslint-plugin-import": "2.25.4",
    "eslint-plugin-jsdoc": "37.7.0",
    "eslint-plugin-prefer-arrow": "1.2.3",
    "@angular-eslint/builder": "4.3.0",
    "@angular-eslint/eslint-plugin": "4.3.0",
    "@angular-eslint/eslint-plugin-template": "4.3.0",
    "@angular-eslint/schematics": "4.3.0",
    "@angular-eslint/template-parser": "4.3.0",
    "@typescript-eslint/eslint-plugin": "4.16.1",
    "@typescript-eslint/parser": "4.16.1"
  }
}

// angular.json
 "lint": {
                    "builder": "@angular-eslint/builder:lint",
                    "options": {
                        "lintFilePatterns": [
                            "src/**/*.ts",
                            "src/**/*.html"
                        ]
                    }
                }

//.eslintrc.json
{
    "root": true,
    "ignorePatterns": [
        "src/lib/**/*"
    ],
    "overrides": [
        {
            "files": [
                "*.ts"
            ],
            "parser": "@typescript-eslint/parser",
            "parserOptions": {
                "project": [
                    "tsconfig.json"
                ],
                "createDefaultProgram": false
            },
            "extends": [
                "plugin:@angular-eslint/recommended",
                "plugin:@angular-eslint/template/process-inline-templates"
            ],
            "rules": {
                "@angular-eslint/component-selector": [
                    "off",
                    {
                        "type": "element",
                        "prefix": "usm",
                        "style": "kebab-case"
                    }
                ],
                "@angular-eslint/directive-selector": [
                    "off",
                    {
                        "type": "attribute",
                        "prefix": "app",
                        "style": "camelCase"
                    }
                ],
                "@angular-eslint/no-input-rename": "off",
                "@angular-eslint/no-output-on-prefix": "off",
                "@typescript-eslint/adjacent-overload-signatures": "off",
                "@typescript-eslint/consistent-type-definitions": "error",
                "@typescript-eslint/consistent-type-assertions": "off",
                "@typescript-eslint/dot-notation": "off",
                "@typescript-eslint/explicit-member-accessibility": [
                    "off",
                    {
                        "accessibility": "explicit"
                    }
                ],
                "@typescript-eslint/no-unused-expressions": [
                    "error",
                    {
                        "allowShortCircuit": true,
                        "allowTernary": true
                    }
                ],
                "@typescript-eslint/naming-convention": [
                    "error",
                    {
                        "selector": "variable",
                        "format": [
                            "camelCase",
                            "UPPER_CASE"
                        ]
                    },
                    {
                        "selector": "enum",
                        "format": [
                            "PascalCase"
                        ]
                    },
                    {
                        "selector": "enumMember",
                        "format": [
                            "PascalCase"
                        ]
                    }
                ],
                "no-shadow": "off",
                "@typescript-eslint/no-shadow": [
                    "error",
                    {
                        "ignoreTypeValueShadow": true
                    }
                ],
                "arrow-body-style": [
                    "off"
                ],
                "brace-style": [
                    "error",
                    "1tbs"
                ],
                "comma-dangle": [
                    "error",
                    {
                        "objects": "never",
                        "arrays": "never",
                        "functions": "never"
                    }
                ],
                "id-blacklist": "off",
                "id-match": "off",
                "max-len": [
                    "warn",
                    {
                        "code": 160
                    }
                ],
                "no-multiple-empty-lines": "error",
                "no-underscore-dangle": "off"
            }
        },
        {
            "files": [
                "*.html"
            ],
            "extends": [
                "plugin:@angular-eslint/template/recommended"
            ],
            "rules": {
                "@angular-eslint/template/no-negated-async": "off"
            }
        }
    ]
}

 类似资料: