| unit | system | 
|---|---|
专为团队设计的 lint 工具
目录:
- ELint
 
在使用 ELint 之前,需要先了解几个核心概念。
elint 是一款代码校验工具,通过插件系统可以将 ESLint、Stylelint、commitlint、Prettier 等工具整合使用。Elint 本身不包含任何校验规则,而是通过 preset 定义各个 lint 工具的配置。elint 的主要功能包括:
- 支持对 js,css 的校验 (eslint, stylelint)。
 - 支持对 git commit message 的校验 (husky, commitlint)。
 - 支持对代码进行格式化 (prettier)。
 - 编写定制化、场景化的 preset,preset 包含所有验证规则,保证团队内部校验规则的一致性和可复用。
 
ELint 3.0 后不再内部依赖 ESLint、Stylelint 等 lint 工具,对于这些工具的支持将由 elint 插件提供。
| 名称 | 依赖 | 描述 | 
|---|---|---|
@elint/plugin-commitlint | 
"@commitlint/core": ">=16.0.0" | 
commitlint 插件,支持校验 git commit message | 
@elint/plugin-eslint | 
"eslint": ">=8.0.0" | 
eslint 插件 | 
@elint/plugin-prettier | 
"prettier": ">=2.0.0" | 
prettier 插件,检查代码格式 | 
@elint/plugin-stylelint | 
"stylelint": ">=14.0.0" | 
stylelint 插件 | 
简单来说,preset 就是一个 npm package,可以理解为”规则集“。
只有 elint 是不够的,因为它不知道具体的校验规则,而 preset 就是用来定义规则的。preset 大概长这样:
elint-preset-<name>
├── .husky
├── configs             # 存放真正规则文件的地方
├── node_modules        # 定义 git hooks
├── .commitlintrc.js    # 定义 commitlint 规则,用于 git commit message 的校验
├── .eslintignore       # 定义 eslint 忽略规则
├── .eslintrc.js        # 定义 eslint 规则,用于 js 的校验
├── index.js            # preset 配置
├── package.json
├── package-lock.json
├── .prettierignore     # 定义 prettier 忽略规则
├── .prettierrc.js      # 定义 prettier 的规则
├── .stylelintignore    # 定义 stylelint 忽略规则
└── .stylelintrc.js     # 定义 stylelint 的规则,用于 css 的校验要求:
- npm package name 需要以 
elint-preset-开头,如:elint-preset-<name>或@team/elint-preset-<name>。 - 入口文件需要默认导出 preset 配置
 - preset 配置中需要配置启用的插件列表,并将插件和插件的依赖定义在 
dependencies中 - lint 工具配置的依赖(例如 eslint-plugin-react),必须明确定义在 
package.json的dependencies中。 
满足以上要求的 npm package 就是一个合法的 elint preset。
一般来说,不建议把
.eslintignore,.stylelintignore,.prettierignore添加到 preset 中,因为 preset 应该对所有项目都是适用的(除非你的 preset 限定在一个很小的范围内使用,各个项目的目录结构都是一致的,此时可以使用它们来定义需要忽略校验的文件和文件夹)。
在
package.json中添加关键字elint-preset会方便大家找到。这里可以查找现有的 preset。
eslint, stylelint, commitlint, husky 等配置文件的语法规则,请阅读参考一节提供的文档。
如果仅为了测试或不方便发布 npm 包,可以参考 本地 preset 的方法。
开始使用 elint 之前,请先检查下你使用 node 和 npm 版本。运行 elint 需要:
- node:
^14.13.1 || >=16.0.0,建议尽量使用新版本。 
下面我们一起从零开始,一步一步的看看如何将 elint 集成到项目中。
首先要安装 elint:
# 假设项目是 test-project
cd test-project
# 安装 elint
npm install elint --save-dev强烈不建议全局安装(具体原因,请阅读常见问题)。
安装完 elint 后,我们需要安装 preset,因为启用插件和校验规则(rules)都定义在 preset 中。一般来说,一个团队应该有一套编码规范,根据编码规范编写好 preset 后,团队中的各个项目直接安装使用即可,不必为每个项目都新建一个 preset。
在一个团队里,可能一个 preset 并不能适应全部场景,那么我们可以根据不同的场景定义多个 preset。例如
@team/elint-preset-h5,@team/elint-preset-node等。要覆盖所有项目,并不需要多少 preset。
这里我们假设团队还没有任何 preset,一起看下如何新建它:
上文已经讲过了,preset 本质上还是一个 npm package,所以新建 preset 的第一步就是新建 npm package:
# 假设叫 elint-preset-test
mkdir elint-preset-test
cd elint-preset-test
# 为了方便演示,全部使用默认值,实际项目中酌情修改
npm init -y默认入口文件为 index.js
配置其内容为:
module.exports = {
  plugins: [
    // ...插件列表
  ]
}这步是可选的,如果你不需要校验 js 文件,可以直接跳过(下同)
首先安装 @elint/plugin-eslint 和插件的依赖 eslint:
npm i @elint/plugin-eslint eslint之后在入口文件 index.js 中添加插件名称:
module.exports = {
  plugins: ['@elint/plugin-eslint']
}新建 eslint 配置文件,下文使用 .eslintrc.js
touch .eslintrc.js.eslintrc.js 写入如下内容:
module.exports = {
  rules: {
    // 禁止 console
    'no-console': 2
  }
}使用 eslint 插件或预设配置
由于不同包管理器依赖安装和解析方式不同,如果配置中需要继承预设配置,直接写插件名可能出现在运行时无法找到对应依赖的错误。
推荐将 eslint 的实际配置写在 preset 中并导出,并在复制到外层的 linter 配置文件中引入 preset 内的配置,可以参考 elint-preset-self 中 eslint 配置的处理方式。
由于 eslint 一直没有支持以继承的配置文件为根目录引入插件 Support having plugins as dependencies in shareable config,如果我们将 eslint 所需的插件依赖都放在 preset 内时,可能会出现找不到插件的报错提示。目前可以选择的一种方案是使用 @rushstack/eslint-patch,在对应的 eslintrc 文件顶部调用:
require('@rushstack/eslint-patch/modern-module-resolution')为什么
elint项目本身没有使用@rushstack/eslint-patch却没有问题呢,因为pnpm会默认将eslint相关的依赖都自动提升,可以参考 public-hoist-pattern
首先安装 @elint/plugin-stylelint 和插件的依赖 stylelint:
npm i @elint/plugin-stylelint stylelint之后在入口文件 index.js 中添加插件名称:
module.exports = {
  plugins: ['@elint/plugin-stylelint']
}添加对应的 stylelint 配置
首先安装 @elint/plugin-prettier 和插件的依赖 prettier:
npm i @elint/plugin-prettier prettier之后在入口文件 index.js 中添加插件名称:
module.exports = {
  plugins: ['@elint/plugin-prettier']
}添加对应的 prettier 配置
首先安装 @elint/plugin-commitlint 和插件的依赖 @commitlint/core:
npm i @elint/plugin-commitlint @commitlint/core之后在入口文件 index.js 中添加插件名称:
module.exports = {
  plugins: ['@elint/plugin-commitlint']
}新建配置文件 .commitlintrc.js:
touch .commitlintrc.js.commitlintrc.js 写入如下内容:
module.exports = {
  rules: {
    // type 不能为空
    'type-empty': [2, 'never'],
    // type 必须是 build 或 ci
    'type-enum': [2, 'always', ['build', 'ci']]
  }
}通过命令添加 commit 信息校验 hook
npx husky add .husky/commit-msg "npm run beforecommit"更多使用方式可以参考
husky官方文档 Create a hook,创建对应的 hooks
从 1.10.0 版本开始,elint 支持更新检测功能,提示用户更新到新版本的 preset。
要开启此功能,只需在 preset 的 package.json 文件中添加如下配置:
{
  "elint": {
    "updateCheckInterval": "3d"
  }
}上述配置会让 elint 每三天检测一次是否有新版本 preset。
如果 preset 中使用了非官方支持的插件,需要将其配置文件移动到项目目录,可以配置入口文件:
module.exports = {
  plugins: [
    /* ... */
  ],
  configFiles: ['xxx.js']
}这样在安装 preset 时就会将 xxx.js 移动到项目目录
elint 各个插件内部有一个默认的扩展名支持列表,但是这个列表不一定能满足各种场景(例如使用 stylelint 检测 JS 文件中的内联样式)。这种情况下可以配置 overridePluginConfig,用以覆盖某个插件的具体配置
module.exports = {
  plugins: ['@elint/plugin-stylelint'],
  overridePluginConfig: {
    '@elint/plugin-stylelint': {
      activateConfig: {
        extensions: ['.js']
      }
    }
  }
}发布 npm package,执行:
npm publish如果使用
pnpm来发布包,需要将husky配置文件列表添加到publishConfig.executableFiles列表中,详情参考 package.json | pnpm
如果希望更多人发现你的 preset,可以添加关键字
elint-preset,点击此处可以查看现有可用的 preset。
刚刚我们已经编写并发布了 preset,现在安装到项目中就好了:
cd test-project
# 安装我们刚才新建的 elint-preset-test
npm install elint-preset-test --save-devpreset 安装完成后,还有一个重要的步骤,就是将 preset 内的各种配置文件复制到项目目录里,这是为了兼容各种 IDE 和 build 工具(如 webpack)。 同时,我们也需要将 git hooks 进行初始化。
在项目的 package.json 中添加 prepare 定义:
{
  "scripts": {
    "prepare": "elint prepare"
  }
}如果你用过旧版本的
elint,可以知道以前配置文件的复制过程是由 preset 配置的postinstall命令完成的,但是从 v3 版本开始,elint 不再推荐使用这种方法,而是推荐在项目中主动配置安装命令。为什么要做这样的改动呢?
postinstall不总是能够成功运行,且其输出会被包管理工具隐藏,难以发现一些问题;- 新版本
 husky移除了自动安装功能,也需要主动配置安装命令。可以参考 Why husky doesn't autoinstall anymore因此 elint 也推荐主动配置安装命令来完成 preset 初始化的流程。
注意:Yarn 2 不支持 prepare 生命周期,可以参考 Yarn 2 | husky 进行修改
按照常规套路,需要在 package.json 中定义 npm test,如果项目只有 lint,可以这样写:
{
  "scripts": {
    "test": "elint lint 'src/**/*.js' 'src/**/*.css'"
  }
}如果除了 lint 还有其他测试,可以这样写:
{
  "scripts": {
    "test": "npm run test:lint && npm run test:other",
    "test:lint": "elint lint 'src/**/*.js' 'src/**/*.css'",
    "test:other": "..."
  }
}注意: glob 最好加上引号,详见 ELint CLI
刚才编写 preset 的时候,定义了在 commit 前执行 npm run beforecommit,所以我们必须定义好 beforecommit,否则会报错:
{
  "scripts": {
    "beforecommit": "npm run test:lint && elint lint commit",
    "test": "npm run test:lint && npm run test:other",
    "test:lint": "elint lint 'src/**/*.js' 'src/**/*.css'",
    "test:other": "..."
  }
}按照上面的写法,commit 之前会执行校验代码和 commit message。至此,elint 已经成功添加到项目中,执行 npm test 会校验代码,在 commit 前会校验代码和 commit message
当 lint 运行在 git hooks 中时,文件的范围限定在 git 暂存区,也就是你将要提交的文件(详见 ELint CLI)。
如果为了测试 elint 或迁移之前没有使用 elint 的项目,可以采用本地 preset 的方案。
在项目中创建一个 JS 文件作为 preset 配置,参考 2.2.3 - 2.2.8 完成对应的操作。在执行 lint 操作时,传入 --preset 参数指定本地的 preset 配置文件即可。
lint 命令用来执行代码校验和 git commit message 校验。当 lint 运行在 git hooks 中时,文件的搜索范围限定在 git 暂存区,也就是只从你将要 commit 的文件中,找到需要校验的文件,执行校验。
elint lint [options] [type] [files...]type 可选的值:
- file: 检测文件
 - commit: 检测提交信息
 - common: 检测所有 common 类型的插件
 
如果不指定 type,默认执行检测文件
options 可选的值:
- -f, --fix: 自动修复错误
 - --cache: 使用缓存
 - --cache-location: 指定缓存位置
 - --preset: 指定 preset 位置
 - --no-ignore: 忽略 elint 遍历文件时的默认忽略规则
 - --no-notifier: 忽略 preset 更新检查
 - --force-notifier: 强制检查 preset 更新
 
当添加 --fix 时,会尝试自动修复问题,无法自动修复的问题依旧会输出出来。
例子:
# 校验 js 和 scss
$ elint lint "**/*.js" "**/*.scss"
# 校验 js 和 scss,执行自动修复
$ elint lint "**/*.js" "**/*.scss" --fix
# 校验 js
$ elint lint "**/*.js"
# 校验 less
$ elint lint "**/*.less"
# 校验 commit message
$ elint lint commit注意:
当你在 Terminal(或者 npm scripts) 中运行 elint lint **/*.js 的时候,glob 会被 Terminal 解析,然后输入给 elint。glob 语法解析的差异可能会导致文件匹配的差异。所以建议,glob 使用引号包裹,防止在输入到 elint 前,被意外解析。
hooks 命令用来安装 & 卸载 git hooks:
elint hooks [action]支持的 action:
- install: 安装
 - uninstall: 卸载
 
例子:
$ elint hooks install
$ elint hooks uninstall输出版本信息
$ elint -v
> elint version
    elint          : 3.0.0
    husky(builtIn) : 8.0.1
  Preset:
    elint-preset-self : unknown
  Plugins:
    @elint/plugin-eslint     : 3.0.0
      eslint                : 8.16.0
    @elint/plugin-stylelint  : 3.0.0
      stylelint             : 14.8.5
    @elint/plugin-prettier   : 3.0.0
      prettier              : 2.6.2
    @elint/plugin-commitlint : 3.0.0
      @commitlint/core      : 17.0.0reset 用于重置缓存,执行此命令将会清空 elint 缓存,同时如果插件配置了 reset 方法,也会同时执行。
插件分为三种类型:
formatter格式化工具,如prettierlinterlint 检查,如eslint、stylelintcommon公共检查工具,如commitlint
其中 formatter 和 linter 是基于文件粒度,每个文件均会经过插件检查;而 common 是全局粒度,一次检查仅会执行一次
formatter 的特殊行为:格式化工具只能将代码格式化,自身没有检查的能力,因此当 preset 中使用了任意 formatter 时,会自动在所有 formatter 和 linter 执行结束后执行 elint 内部的插件 builtIn:format-checker 插件,判断代码是否被格式化
如果官方插件无法满足需求,例如需要支持旧版本 eslint 或添加其他文件的 lint 规则,可以自行编写一个插件。
type Result = string
const elintPluginExample: ElintPlugin<Result> = {
  /**
   * plugin 名称(唯一)
   */
  id: 'elint-plugin-example',
  /**
   * 可读名称,用于控制台输出
   */
  name: '插件示例',
  /**
   * elint 插件类型
   *
   * - `linter` lint 检查工具
   * - `formatter` 格式化工具
   * - `common` 公共检查工具
   **/
  type: 'linter',
  /**
   * 插件激活配置
   **/
  activateConfig: {
    /**
     * 使用扩展名确定是否激活插件
     **/
    extensions: ['.js'],
    /**
     * 判断插件激活函数,如果传入了则会忽略上面的 extensions
     */
    activate: (options) => {
      return true
    }
  },
  execute: (text, options) => {
    return {
      source: '',
      output: '',
      errorCount: 0,
      warningCount: 0,
      message: '命令行输出文本',
      result: 'lint 工具执行结果'
    }
  },
  /**
   * 可选实现,用于重置插件的一些缓存
   */
  reset: () => {}
}作为一个项目的维护者,当你将 elint 集成到你的项目中时,了解一些细节会非常有帮助。
如果你编写好了用于自己团队的 preset,并且按照前面介绍的安装方式安装完成,你会发现,elint 将所有的配置文件从 preset 复制到了项目的根目录。这是通过定义在 postinstall 中的 elint-helpers install 命令完成的。
这么做的目的是为了兼容在 IDE、build 工具中使用 lint。所以使用 elint 的同时,你仍然可以按照原来的方式,配置你的 IDE,webpack 等,他们与 elint 完全不冲突。
安装(并初始化)完成后,可以根据你的项目的实际情况,添加 npm scripts,例如 test 时执行 elint lint '**/*.js' '**/*.less'
执行过程比较简单,对代码的 lint 的过程可以概括为一句话:“elint 根据你输入的 glob,收集并整理文件,交由各个插件执行,然后将结果输出至命令行展示”。
对 git commit 信息的 lint,主要借助于 husky 和 commitlint。安装过程中,会自动添加 git hooks,当 hooks 触发时,执行 husky 配置中的相应命令,就这么简单。
执行 commit lint 时,git hook 可以选择
commit-msg。
elint 在逻辑上和 husky 并没有绑定,因此如果想使用旧版本 husky,可以直接在项目中安装和配置。
注意:由于 husky 的特殊性,无法在 preset 中指定其版本
如果你能想到这个问题,那么说明你真的理解了 elint 的运作方式。忽略配置文件,防止意外被修改,所有团队成员使用 preset 定义的配置,听起来非常不错。那么从 preset 复制到项目中的各种配置文件,是否可以添加到 .gitignore 呢?这要看你的使用场景:
- 如果代码提交到 git 仓库,执行 ci 的过程中会安装依赖。
 - 如果部署项目时,build 过程中不再执行 lint,或者先安装依赖,然后再执行 build。
 
总之,只要执行 npm install 安装依赖,配置文件就会自动添加到项目里;只要你能保证需要用到配置文件的时候它存在(例如你在 webpack 里用了 eslint-loader),就可以忽略它。
再次强调,elint 的设计原则是保证团队规范的严格执行,如果你觉得规则有问题,那么应该提出并推动修改 preset,而不是直接改项目里的配置。
不可以。安装过程中,如果两个 preset 存在相同的配置文件,后安装会覆盖之前的。在 elint 执行时,如果发现有多个 preset,会抛出错误
- 检查你的 glob 写法是否有问题。
 - 可能是 glob 被传入 elint 之前就被意外解析了,参考 lint 命令。
 - windows 7 + git bash 下测试时发现,npm scripts 里,glob 必须使用双引号包裹
 
{
  "scripts": {
    "test:lint": "elint lint \"src/**/*.js\""
  }
}- elint 在遍历文件时,会应用一些默认的忽略规则,如果你的文件刚好命中这些规则,可以使用 
--no-ignore选项。 
const defaultIgnore = [
  '**/node_modules/**',
  '**/bower_components/**',
  '**/flow-typed/**',
  '**/.nyc_output/**',
  '**/coverage/**',
  '**/.git',
  '**/*.min.js',
  '**/*.min.css'
]
// 除此之外还有 .gitignore 定义的忽略规则并不是所有规则都支持自动修复,具体可以查看 eslint rules 和 stylelint rules,可以自动修复的规则都有标识。
设置环境变量 FORCE_COLOR 为 0 即可,例如:
$ FORCE_COLOR=0 elint lint "src/**/*.js"代码经过 fix 以后,有可能会在修复了现有问题的同时引入新的问题。
eslint 为此会对 fix 后的代码重新执行 fix,以确保最终代码里的错误尽可能被修复,可以参考 Applying Fixes。
但是 stylelint 一直没有实现这个功能,因此可能出现 fix 后的代码依然存在问题。可以通过这个 issue 追踪此问题解决状态。
如果在开启缓存后执行了 fix 命令,但是代码中依然有问题且无法被 lint 出来,可以通过 elint reset 来重置缓存。
v2.x 到 v3 有很多 BREAKING CHANGE,可以按照以下流程进行升级
- 修改 
package.json中的peerDependencieselint为^3.0.0 - 移除依赖 
elint-helpers - 创建入口文件 
index.js,并将package.json中的main指向该文件 - 按需安装插件 
@elint/plugin-eslint,@elint/plugin-stylelint,@elint/plugin-prettier,@elint/plugin-commitlint并安装插件对应的依赖(即eslint,stylelint,prettier,@commitlint/core) - 参考 2.2.2 - 2.2.10 添加对应配置
 - 调整由于升级 lint 库而失效或者新增的规则
 - 调整 
husky配置,可以参考 Migrate from v4 to v8 
- 不再支持 
elint lint es和elint lint style命令,如果在 lint 时需要区分文件类型,将自行通过 glob 语法匹配。 - 参考 安装 preset 和 git hooks 在 
package.json的script里添加"prepare": "elint install && elint hooks install"命令。 - lint 命令不再支持 
--prettier参数,是否检查格式化的行为将由 preset 决定,如果 preset 内部包含 formatter,格式化检查就会被执行。 - 根据需求,可以添加 
--cache参数配置缓存,用来提升 lint 速度 
参考 CONTRIBUTING。