插件的命名有以下几种方式:
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')
}
}