ELint
unit | system |
---|---|
专为团队设计的 lint 工具
目录:
- 1. 核心概念
- 2. 使用指南
- 3. ELint CLI
- 4. 细节 & 原理
- 5. 常见问题
- 6. 参考
1. 核心概念
在使用 elint 之前,需要先了解几个核心概念。
1.1. ELint
elint 是一款代码校验工具,基于 eslint、stylelint、commitlint、prettier 等工具封装而成。elint 本身不包含任何校验规则,校验规则通过 preset 定义。elint 的主要功能包括:
- 支持对 js,css 的校验 (eslint, stylelint)。
- 支持对 git commit message 的校验 (husky, commitlint)。
- 支持对代码进行格式化 (prettier)。
- 编写定制化、场景化的 preset,preset 包含所有验证规则,保证团队内部校验规则的一致性和可复用。
1.2. Preset
简单来说,preset 就是一个 npm package,可以理解为”规则集“。
只有 elint 是不够的,因为它不知道具体的校验规则,而 preset 就是用来定义规则的。preset 大概长这样:
elint-preset-<name>
├── .commitlintrc.js # 定义 commitlint 规则,用于 git commit message 的校验
├── .eslintignore # 定义 eslint 忽略规则
├── .eslintrc.js # 定义 eslint 规则,用于 js 的校验
├── .huskyrc.js # 定义 git hooks, 可在 commit 前执行 commitlint 等操作
├── node_modules
├── 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>
。 - linter 的配置文件名必须是
.commitlintrc.js
,.eslintrc.js
,.stylelintrc.js
,.prettierrc.js
。 - 对于 eslint、stylelint 和 prettier,支持使用
.eslintignore
,.stylelintignore
,.prettierignore
定义需要忽略校验的文件和文件夹。 - git hooks (使用 husky) 的配置文件名必须是
.huskyrc.js
。 - 所有配置文件必须放在 preset 的根目录。
- 依赖的 linter plugin(例如 eslint-plugin-react),必须明确定义在
package.json
的dependencies
中。 - 安装
elint-helpers
并添加 postinstall scripts:elint-helpers install
满足以上要求的 npm package 就是一个合法的 elint preset。
一般来说,不建议把
.eslintignore
,.stylelintignore
,.prettierignore
添加到 preset 中,因为 preset 应该对所有项目都是适用的(除非你的 preset 限定在一个很小的范围内使用,各个项目的目录结构都是一致的,此时可以使用它们来定义需要忽略校验的文件和文件夹)。
在
package.json
中添加关键字elint-preset
会方便大家找到。这里可以查找现有的 preset。
eslint, stylelint, commitlint, husky 等配置文件的语法规则,请阅读参考一节提供的文档。
2. 使用指南
开始使用 elint 之前,请先检查下你使用 node 和 npm 版本。运行 elint 需要:
- node:>= 10.12.0,建议尽量使用新版本。
下面我们一起从零开始,一步一步的看看如何将 elint 集成到项目中。
2.1. 安装 elint
首先要安装 elint:
# 假设项目是 test-project
cd test-project
# 安装 elint
npm install elint --save-dev
强烈不建议全局安装(具体原因,请阅读常见问题)。
2.2. 编写 preset
安装完 elint 后,我们需要安装 preset,因为所有的校验规则(rules)都定义在 preset 中。一般来说,一个团队应该有一套编码规范,根据编码规范编写好 preset 后,团队中的各个项目直接安装使用即可,不必为每个项目都新建一个 preset。
在一个团队里,可能一个 preset 并不能适应全部场景,那么我们可以根据不同的场景定义多个 preset。例如
@team/elint-preset-h5
,@team/elint-preset-node
等。要覆盖所有项目,并不需要多少 preset。
这里我们假设团队还没有任何 preset,一起看下如何新建它:
2.2.1. 新建一个 npm package
上文已经讲过了,preset 本质上还是一个 npm package,所以新建 preset 的第一步就是新建 npm package:
# 假设叫 elint-preset-test
mkdir elint-preset-test
cd elint-preset-test
# 为了方便演示,全部使用默认值,实际项目中酌情修改
npm init -y
2.2.2. 安装 elint-helpers
preset 包需要安装 elint-helpers
, 并添加 npm scripts 以完成初始化工作:
npm i elint-helpers
{
"scripts": {
"postinstall": "elint-helpers install"
}
}
2.2.3. 添加 eslint 配置文件(可选)
这步是可选的,如果你不需要校验 js 文件,可以直接跳过(下同)
首先新建配置文件,名称必须是 .eslintrc.js
, 参考上文的 preset 约定:
touch .eslintrc.js
.eslintrc.js
写入如下内容:
module.exports = {
'extends': [
// 使用 plugin eslint-plugin-react
'plugin:react/recommended'
],
'rules': {
// 禁止 console
'no-console': 2
}
};
因为使用了 plugin eslint-plugin-react
,所以必须使用 npm 安装,且保存到 package.json
的 dependencies
中(无论是从语义方面考虑,还是上文关于 preset 的约定,都应该保存到 dependencies
中):
npm install eslint-plugin-react --save
因为我们使用 elint 执行校验,而 elint 已经集成了 eslint,stylelint 等工具,所以这里不需要再安装了,只需安装额外的 plugin。
2.2.4. 添加 stylelint 配置文件(可选)
此处省略,和 eslint 一样的做法。
2.2.5. 添加 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'
]]
}
};
2.2.6. 添加 git hooks(可选)
新建配置文件 .huskyrc.js
:
touch .huskyrc.js
.huskyrc.js
写入如下内容
module.exports = {
'hooks': {
/**
* beforecommit 可以在项目的 package.json 中自由定义
* 例如可以执行代码校验和 commitlint,或者都执行
*/
'commit-msg': 'npm run beforecommit'
}
}
这里配置 git hooks 执行
npm run beforecommit
,所以你可以在项目中自由定义要执行的操作(下面会详细解释)。
注意:
可能你会觉得,命名为
precommit
比beforecommit
更合适。没错,单纯从命名的角度讲,precommit
确实更好。但这会与 husky 的规则产生冲突。旧版本的 husky 支持在 package.json 的 scripts 中定义 git hooks。
2.2.7. 设置更新检测周期(可选)
从 1.10.0 版本开始,elint 支持更新检测功能,提示用户更新到新版本的 preset。
要开启此功能,只需在 preset 的 package.json 文件中添加如下配置:
{
"elint": {
"updateCheckInterval": "3d"
}
}
上述配置会让 elint 每三天检测一次是否有新版本 preset。
2.2.8. 发布 npm package
此时项目的目录结构应该是:
elint-preset-test
├── .commitlintrc.js
├── .eslintrc.js
├── .huskyrc.js
├── node_modules
├── package.json
└── package-lock.json
1 directory, 5 files
发布 npm package,执行:
npm publish
如果希望更多人发现你的 preset,可以添加关键字
elint-preset
,点击此处可以查看现有可用的 preset。
2.3. 安装 preset
刚刚我们已经编写并发布了 preset,现在安装到项目中就好了:
cd test-project
# 安装我们刚才新建的 elint-preset-test
npm install elint-preset-test --save-dev
安装完成后,你会发现刚才定义在 preset 中的各种配置文件(.eslintrc.js
等),都拷贝到了项目的根目录,这是为了兼容各种 IDE 和 build 工具(如 webpack),elint 与他们是可以共存的。
2.4. 定义 npm scripts
2.4.1. npm test
按照常规套路,需要在 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
2.4.2. npm beforecommit
刚才编写 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
万一(虽然不建议)你在 commit 之前什么都不想执行,为了不报错,也要写
exit 0
。
当 lint 运行在 git hooks 中时,文件的范围限定在 git 暂存区,也就是你将要提交的文件(详见 ELint CLI)。
3. ELint CLI
因为我们不推荐全局安装,除了在 npm scripts 和 .huskyrc.js
中使用,下面的 elint
均代指 ./node_modules/.bin/elint
(或者也可以使用 npm 5.2.0 起内置的 npx 命令):
3.1. lint
lint
命令用来执行代码校验和 git commit message 校验。当 lint 运行在 git hooks 中时,文件的搜索范围限定在 git 暂存区,也就是只从你将要 commit 的文件中,找到需要校验的文件,执行校验。
elint lint [options] [type] [files...]
type 可选的值:
- es: 执行 eslint
- style: 执行 stylelint
- commit: 执行 commitlint
如果不指定 type,默认执行 eslint 和 stylelint
options 可选的值:
- -f, --fix: 自动修复错误
- --no-ignore: 忽略 elint 遍历文件时的默认忽略规则
- -p, --prettier: 检测代码格式,与 fix 共用时会对代码进行格式化
当添加 --fix
时,会尝试自动修复问题,无法自动修复的问题依旧会输出出来。
例子:
# 校验 js 和 scss
$ elint lint "**/*.js" "**/*.scss"
# 校验 js 和 scss,执行自动修复
$ elint lint "**/*.js" "**/*.scss" --fix
# 校验 js
$ elint lint "**/*.js"
$ elint lint es "**/*.js"
# 校验 less
$ elint lint "**/*.less"
$ elint lint style "**/*.js"
# 校验 commit message
$ elint lint commit
注意:
当你在 Terminal(或者 npm scripts) 中运行 elint lint **/*.js
的时候,glob 会被 Terminal 解析,然后输入给 elint。glob 语法解析的差异可能会导致文件匹配的差异。所以建议,glob 使用引号包裹,防止在输入到 elint 前,被意外解析。
3.2. hooks
hooks
命令用来安装 & 卸载 git hooks(一般不会用到):
elint hooks [action]
支持的 action:
- install: 安装
- uninstall: 卸载
例子:
$ elint hooks install
$ elint hooks uninstall
3.3. version
输出版本信息
$ elint -v
> elint version
elint : 1.2.0-rc.1
elint-preset-test : 1.0.15
Dependencies:
eslint : 4.19.1
stylelint : 9.2.1
commitlint : 7.0.0
husky : 1.0.0-rc.8
4. 细节 & 原理
作为一个项目的维护者,当你将 elint 集成到你的项目中时,了解一些细节会非常有帮助。
4.1. 安装 & 初始化过程
如果你编写好了用于自己团队的 preset,并且按照前面介绍的安装方式安装完成,你会发现,elint 将所有的配置文件从 preset 复制到了项目的根目录,这么做的目的是为了兼容在 IDE、build 工具中使用 lint。所以使用 elint 的同时,你仍然可以按照原来的方式,配置你的 IDE,webpack 等,他们与 elint 完全不冲突。
具体的 preset 初始化过程(install 后自动执行),分为如下两步:
- 将配置文件 (
.eslintrc.js
等) 复制到项目的根目录。 - 安装 preset 的
dependencies
,并保存到项目的devDependencies
中。
安装(并初始化)完成后,可以根据你的项目的实际情况,添加 npm scripts,例如 test 时执行 elint lint '**/*.js' '**/*.less'
无论是先安装 elint,还是先安装 preset,亦或者同时安装,elint 都能准确的感知到 preset 的存在,并完成所有初始化操作。这项功能主要借助于 npm hook scripts,这也是当你使用 cnpm 时需要格外注意的原因(解决办法参考下面的常见问题)。
4.2. 执行过程
执行过程比较简单,对代码的 lint 的过程可以概括为一句话:“elint 根据你输入的 glob,收集并整理文件,交由 eslint、stylelint 执行,然后将结果输出至命令行展示”。
对 git commit 信息的 lint,主要借助于 husky 和 commitlint。安装过程中,会自动添加 git hooks,当 hooks 触发时,执行配置在 .huskyrc.js
中的相应命令,就这么简单。
执行 commit lint 时,git hook 可以选择
commit-msg
。
因为 git hooks 的注册是在 npm install 后自动执行的,所以,如果万一你的项目还未 git init,hooks 注册必然是失败的。解决办法是在 git init 之后,手动执行
./node_modules/.bin/elint hooks install
。
5. 常见问题
5.1. yarn
已支持 yarn 1.x
,无需任何改动即可使用。
但是目前对 yarn pnp
支持较为有限,需要自行处理以下工作
- 由于
eslint
是作为elint
的依赖存在的,因此默认配置下如果使用eslint
的插件时会找不到eslint
,可以在.yarnrc.yml
中添加以下配置,以放开依赖间的查找能力:
pnpMode: "loose"
pnpFallbackMode: "all"
- 由于目前
husky
还在 2.x 版本不支持pnp
,可以通过单独安装husky
的最新版本来解决(注:如果同时安装elint
和husky
,可能面临 git hooks 被旧版本替换的问题,可以通过yarn rebuild
重建) - 当前 preset 版本检测功能还没有支持
pnp
5.2. 为什么不建议全局安装
elint 强依赖 stylelint, eslint 等工具。而对于 eslint,其文档中写到:
Any plugins or shareable configs that you use must also be installed globally to work with a globally-installed ESLint.
全局安装 lint 工具和所有的 plugin,项目数量较多时容易引起混乱,且可能对 ci、部署等带来麻烦。
5.3. git hooks 不执行或报错
如果 git hooks 不执行或者报错,尝试下面的方法:
- 项目处于 git 管理的前提下,执行
npm install
安装依赖。 - 如果你在新建项目,先执行
npm install
,然后执行git init
的话,手动注册 git hooks(上文的 hooks 命令)。 - 检查
.git/hooks
目录,是否存在非.sample
结尾的文件,如果不存在,手动注册 git hooks。 - 还是有问题?报 bug。
5.4. 配置文件是不是可以 git ignore
如果你能想到这个问题,那么说明你真的理解了 elint 的运作方式。忽略配置文件,防止意外被修改,所有团队成员使用 preset 定义的配置,听起来非常不错。那么从 preset 复制到项目中的各种配置文件,是否可以添加到 .gitignore
呢?这要看你的使用场景:
- 如果代码提交到 git 仓库,执行 ci 的过程中会安装依赖。
- 如果部署项目时,build 过程中不再执行 lint,或者先安装依赖,然后再执行 build。
总之,只要执行 npm install
安装依赖,配置文件就会自动添加到项目里;只要你能保证需要用到配置文件的时候它存在(例如你在 webpack 里用了 eslint-loader),就可以忽略它。
再次强调,elint 的设计原则是保证团队规范的严格执行,如果你觉得规则有问题,那么应该提出并推动修改 preset,而不是直接改项目里的配置。
5.5. 是否可以安装多个 preset
不可以。安装过程中,如果两个 preset 存在相同的配置文件,后安装会覆盖之前的。如果 package.json 中的依赖包含多个 preset,先后顺序由 npm 决定。
5.6. 某些文件没有被校验到
- 检查你的 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 定义的忽略规则
5.7. 为什么添加了 fix 选项还是有问题输出
并不是所有规则都支持自动修复,具体可以查看 eslint rules 和 stylelint rules,可以自动修复的规则都有标识。
5.8. 如何禁用颜色输出
设置环境变量 FORCE_COLOR
为 0
即可,例如:
$ FORCE_COLOR=0 elint lint "src/**/*.js"
5.9. lint 执行失败,提示包缺失
如果遇到如下错误,特别在 CI 环境下。可能是 package-lock.json
引起的。
Error: Cannot find module 'eslint-config-xxx'
请按照如下步骤尝试修复:
- 删除
node_modules
和package-lock.json
。 - 查看
package.json
文件,使用 elint 时只需安装 elint 和 preset,清理掉遗留的 eslint 等依赖。 - 重新执行
npm install