插件的命名有以下几种方式:
eslint-plugin-<plugin-name>@scope/eslint-plugin-<plugin-name>@scope/eslint-plugin
1.plugins属性
在开始介绍自定义 plugin 之前,先说明下 Eslint 配置文件(如 .eslintrc.*)中的 plugins 属性。
以 eslint-plugin-vue 中的 configs/essential 核心代码为例(省略了大部分 rules):
// configs/essential
module.exports = {
parser: require.resolve('vue-eslint-parser'),
parserOptions: {
ecmaVersion: 2020,
sourceType: 'module'
},
env: {
browser: true,
es6: true
},
plugins: ['vue'],
rules: {
'vue/require-v-for-key': 'error'
}
}可以发现,在 configs/essential 中已经默认声明了 plugins: ['vue']。
该配置可通过 extends 自动引入到 .eslintrc.* 相关配置文件中。
那么在 .eslintrc.js 中引入 eslint-plugin-vue 时:
// .eslintrc.js
module.exports = {
root: true,
// 引入configs/essential
extends: [
'plugin:vue/essential'
],
// 由于configs/essential已经默认声明,所以这里其实可以省略
plugins: [
'vue'
],
rules: {
// 可以覆盖设置rules
'vue/require-v-for-key': ['warn']
}
}如果 configs/essential 中没有默认声明 plugins: ['vue'],
而且 .eslintrc.* 等配置文件中也没有声明 plugins: ['vue'],
那么此时检测 .vue 文件时,Eslint 对于 vue/<rule-name> 等 rules 会定位失败。
可想而知,plugins: ['vue'] 声明,相当于对应 rules 的 Base 设置。

2.插件中的规则
插件中可以声明 rules 属性以实现为 Eslint 提供额外的规则。
插件中的 rules 定义规则,需要在 .eslintrc.* 引入时来设置等级,进而启用。
这里以 eslint-plugin-vue 举例:
// eslint-plugin-vue
module.exports = {
configs: {},
processors: {
'.vue': require('./processor')
}
rules: {
// 插件中,声明了该规则
"require-v-for-key": {
create (context) { ... }
}
},
environments: {
'setup-compiler-macros': {
globals: {
defineProps: 'readonly',
defineEmits: 'readonly',
defineExpose: 'readonly',
withDefaults: 'readonly'
}
}
}
}那么此时,在 .eslintrc.js 中,可以在 rules 中设置 <plugin-name>/<rule-name> 以对该规则进行具体设置:
// .eslintrc.js
module.exports = {
root: true,
// plugins引入自定义规则、处理器、环境
plugins: ['vue'],
// 规则解析,需要使用AST,vue相关文件有特有的解析器
parser: 'vue-eslint-parser',
parserOptions: {
ecmaVersion: 2020,
sourceType: 'module'
},
// 配置自定义规则 off、warn、error或者其他options
rules: {
'vue/require-v-for-key': ['error']
}
}3.插件中的配置
如果我们想要自定义插件本身就携带配置好的规则,则可以利用 configs 属性声明多个 config。
要注意的一点,通过 plugins: [] 来引入插件时,并不能设置 configs 默认配置。
必须手动在 extends 中引入 plugin:<plugin-name>/<config-name>,配置才会生效。
以 eslint-plugin-vue 中的 base 和 essential 为例:
// eslint-plugin-vue
module.exports = {
rules: { ... },
configs: {
base: require('./configs/base'),
essential: require('./configs/essential'),
},
processors: {
'.vue': require('./processor')
},
environments: { ... }
}plugin:vue/base 的代码如下:
module.exports = {
parser: require.resolve('vue-eslint-parser'),
parserOptions: {
ecmaVersion: 2020,
sourceType: 'module'
},
env: {
browser: true,
es6: true
},
plugins: ['vue'],
rules: {
'vue/comment-directive': 'error',
'vue/jsx-uses-vars': 'error'
}
}plugin:vue/essential 的代码如下(忽略了部分 rules):
module.exports = {
extends: require.resolve('./base'),
rules: {
'vue/multi-word-component-names': 'error',
'vue/no-arrow-functions-in-watch': 'error',
'vue/no-async-in-computed-properties': 'error',
'vue/no-child-content': 'error',
'vue/no-computed-properties-in-data': 'error',
'vue/no-custom-modifiers-on-v-model': 'error',
'vue/no-dupe-keys': 'error',
'vue/no-dupe-v-else-if': 'error'
}
}4.插件中的环境
插件中可以定义具体的环境,以供 Eslint 使用。
需要声明 environments 属性。
module.exports = {
environments: {
jquery: {
globals: {
// false表示不允许重写
$: false
},
parserOptions: {
ecmaVersion: 2020
}
}
}
}environments 中可声明的配置有两部分:
globals全局变量。parserOptions解析器配置。
这俩个属性的设置方法与 .eslintrc.* 配置文件相同。
假设我们的插件名为 eslint-plugin-myPlugin,当使用上述插件环境时,需要利用 env 属性:
// .eslintrc.js
module.exports = {
root: true,
env: {
'myPlugin/jquery': true
}
}5.插件中的处理器
Eslint 本身只支持处理 JavaScript 文件。
像 markdown 或者 vue 这种类型的文件,必须提供额外的处理器,Eslint 才能处理。
处理 markdown 类型文件的插件可以使用eslint-plugin-markdown。
处理 vue 类型文件的插件则可以使用eslin-plugin-vue。
5-1.定义处理器
我们可以通过设置 processors 属性来定义插件的处理器。
module.exports = {
processors: {
"processor-name": {
// takes text of the file and filename
preprocess: function(text, filename) {
// here, you can strip out any non-JS content
// and split into multiple strings to lint
return [ // return an array of code blocks to lint
{ text: code1, filename: "0.js" },
{ text: code2, filename: "1.js" },
]
},
// takes a Message[][] and filename
postprocess: function(messages, filename) {
// `messages` argument contains two-dimensional array of Message objects
// where each top-level array item contains array of lint messages related
// to the text that was returned in array from preprocess() method
// you need to return a one-dimensional array of the messages you want to keep
return [].concat(...messages);
},
supportsAutofix: true // (optional, defaults to false)
}
}
}可以看出,定义处理器的属性有:
preprocess前置处理器。postprocess后置处理器。supportsAutofix是否支持自动修复。
值得一提的是,Eslint v7 及以上版本对于 processors 的定义和使用方式有过一次升级。
具体文章可参考2018-processors-improvements。
markdown 文件中通常会有代码块,如 ````js` 等。
eslint-plugin-markdown 会将这些代码块抽离出来,形成虚拟文件。
譬如 README.md 中的 \```js 对应的是 README.md/*.js。
而 preprocess 方法,就是负责将源代码,抽离成可供 Eslint 检测的自定义代码块。
然后 postprocess 方法中,会接收到 Eslint 的检测结果,以供插件进行二次处理。
5-2.配置文件中使用处理器
假设,已经使用上一节中的方式定义了插件 eslint-plugin-myPlugin。
那么在 .eslintrc.* 等配置文件中,就可以这样引入处理器:
// .eslintrc.js
module.exports = {
processor: 'myPlugin/processor-name'
}当然,在实际开发中,JavaScript 文件是不用处理器的。
而像 markdown 或者 vue 这类特殊文件则需要单独处理,那么我们可以使用 overrides 属性来定义特殊文件的特有处理方式:
这里,以 eslint-plugin-markdown 为例:
module.exports = {
root: true,
extends: [
'eslint:recommended'
],
plugins: ['markdown'],
overrides: [
{
files: ['**/*.md'],
processor: 'markdown/markdown'
},
{
files: ['**/*.md/*.js'],
rules: {
// 对于md文档中的js代码块,自定义规则
semi: ['error']
}
}
]
}但是一般插件作者,为了方便用户使用,会将这些配置直接定义到 configs 中,然后用户直接引入到 extends 中即可。
譬如 eslint-plugin-markdown 的实际定义为:
const processor = require("./processor")
module.exports = {
configs: {
recommended: {
plugins: ['markdown'],
overrides: [
{
files: ['*.md'],
processor: 'markdown/markdown'
},
{
files: ['**/*.md/**'],
parserOptions: {
ecmaFeatures: {
impliedStrict: true
}
},
rules: {}
}
]
}
},
processors: {
markdown: processor
}
}这样,当我们使用 eslint-plugin-markdown 时,只需要:
module.exports = {
root: true,
extends: [
'eslint:recommended'
],
overrides: [
{
files: ['**/*.md/*.js'],
rules: {
// 对于md文档中的js代码块,自定义规则
semi: ['error']
}
}
]
}5-3.文件扩展名处理器
上几节中,都是显式的提供处理器名称,如 processor-name。
这种方式,在实际使用的时候,都是通过定义 processor 为 plugin/processor-name 来引入处理器。
除此以外,有一种更方便的方式:
将文件扩展名作为处理器的定义键。
这种形式的处理器,在检测到对应符合类型的文件时,就会自动应用该处理器。
譬如eslint-plugin-vue中的处理器定义:
// 此处只描述核心代码,详细内容可见上方链接
module.exports = {
processors: {
'.vue': require('./processor')
}
}