創建工程
我們創建一個叫eslint-demo的工程,然後執行npm初始化
https://github.com/913453448/eslint-demo
npm init
安裝使用
安裝eslint
$ npm install eslint --save-dev
創建配置文件
執行eslint的初始化
npx eslint --init
執行完畢後可以看到一個配置文件,我選的是json格式的配置文件,還有package.json中直接引用、yaml、js格式的配置文件,後面我們會講到。
.eslintrc.json
{
}
可以看到,我們這裏是一個空的配置文件。
運行命令
我們創建一個叫src的目錄,然後創建一個demo1.js的文件測試
demo1.js:
我們隨便寫點代碼,比如直接document頁面輸出一個字符串
document.write("hello eslint");
運行eslint測試:
npx eslint ./src/demo1.js
運行完畢後你會發現,沒有報錯跟提示,這是因爲我們還沒有進行任何eslint的配置,下面我們就結合demo對eslint配置逐個進行解析。
爲了更好的理解eslint,我們直接clone一份源碼,https://github.com/eslint/eslint.git
配置
我們這裏以一個前端vue+webpack+ts+es2020的工程爲demo爲例子進行eslint的配置。
env&parserOptions
ESLint 允許你指定你想要支持的 JavaScript 語言選項。默認情況下,ESLint 支持 ECMAScript 5 語法。你可以覆蓋該設置,以啓用對 ECMAScript 其它版本和 JSX 的支持,爲什麼要把env跟parserOptions放在一起講呢? 因爲env中包含了對parserOptions的配置,最終兩個參數傳入供給parse解析器使用。
env
我們的demo需要運行在es2020的瀏覽器環境中,所以我們的env配置爲:
{
"env": {
"browser": true,
"es2020": true
}
}
那麼,env到底可以爲設置爲哪些呢?我們直接找到eslint的源碼。
conf/environments.js:
...
const newGlobals2015 = getDiff(globals.es2015, globals.es5); // 19 variables such as Promise,
const newGlobals2017 = {
Atomics: false,
SharedArrayBuffer: false
};
const newGlobals2020 = {
BigInt: false,
BigInt64Array: false,
BigUint64Array: false,
globalThis: false
};
//------------------------------------------------------------------------------
// Public Interface
//------------------------------------------------------------------------------
/** @type {Map<string, import("../lib/shared/types").Environment>} */
module.exports = new Map(Object.entries({
// Language
builtin: {
globals: globals.es5
},
es6: {
globals: newGlobals2015,
parserOptions: {
ecmaVersion: 6
}
},
es2015: {
globals: newGlobals2015,
parserOptions: {
ecmaVersion: 6
}
},
es2017: {
globals: { ...newGlobals2015, ...newGlobals2017 },
parserOptions: {
ecmaVersion: 8
}
},
es2020: {
globals: { ...newGlobals2015, ...newGlobals2017, ...newGlobals2020 },
parserOptions: {
ecmaVersion: 11
}
},
// Platforms
browser: {
globals: globals.browser
},
node: {
globals: globals.node,
parserOptions: {
ecmaFeatures: {
globalReturn: true
}
}
},
"shared-node-browser": {
globals: globals["shared-node-browser"]
},
worker: {
globals: globals.worker
},
serviceworker: {
globals: globals.serviceworker
},
// Frameworks
commonjs: {
globals: globals.commonjs,
parserOptions: {
ecmaFeatures: {
globalReturn: true
}
}
},
amd: {
globals: globals.amd
},
mocha: {
globals: globals.mocha
},
jasmine: {
globals: globals.jasmine
},
jest: {
globals: globals.jest
},
phantomjs: {
globals: globals.phantomjs
},
jquery: {
globals: globals.jquery
},
qunit: {
globals: globals.qunit
},
prototypejs: {
globals: globals.prototypejs
},
shelljs: {
globals: globals.shelljs
},
meteor: {
globals: globals.meteor
},
mongo: {
globals: globals.mongo
},
protractor: {
globals: globals.protractor
},
applescript: {
globals: globals.applescript
},
nashorn: {
globals: globals.nashorn
},
atomtest: {
globals: globals.atomtest
},
embertest: {
globals: globals.embertest
},
webextensions: {
globals: globals.webextensions
},
greasemonkey: {
globals: globals.greasemonkey
}
}));
可以看到,默認是“builtin” 也就是es5,我們可以看到“es6”還可以叫“es2015”,然後還有一個“parserOptions”的配置:
es2015: {
globals: newGlobals2015,
parserOptions: {
ecmaVersion: 6
}
},
那麼parserOptions到底是什麼呢? 其實是給解析器用的參數,告訴解析器你需要利用ecmaVersion:6的語法去解析我們的源文件,那麼globals屬性裏面又是什麼東西呢?我們直接找到newGlobals2015然後點開源碼,我們找到一個叫globals的第三方庫,然後找到了一個叫globals.json的文件:
{
...
"es2015": {
"Array": false,
"ArrayBuffer": false,
"Boolean": false,
"constructor": false,
"DataView": false,
"Date": false,
"decodeURI": false,
"decodeURIComponent": false,
"encodeURI": false,
"encodeURIComponent": false,
"Error": false,
"escape": false,
"eval": false,
"EvalError": false,
"Float32Array": false,
"Float64Array": false,
"Function": false,
"hasOwnProperty": false,
"Infinity": false,
"Int16Array": false,
"Int32Array": false,
"Int8Array": false,
"isFinite": false,
"isNaN": false,
"isPrototypeOf": false,
"JSON": false,
"Map": false,
"Math": false,
"NaN": false,
"Number": false,
"Object": false,
"parseFloat": false,
"parseInt": false,
"Promise": false,
"propertyIsEnumerable": false,
"Proxy": false,
"RangeError": false,
"ReferenceError": false,
"Reflect": false,
"RegExp": false,
"Set": false,
"String": false,
"Symbol": false,
"SyntaxError": false,
"toLocaleString": false,
"toString": false,
"TypeError": false,
"Uint16Array": false,
"Uint32Array": false,
"Uint8Array": false,
"Uint8ClampedArray": false,
"undefined": false,
"unescape": false,
"URIError": false,
"valueOf": false,
"WeakMap": false,
"WeakSet": false
},
...
}
可以看到,其實就是我們es6中內置的對象、屬性、方法,所以env是提供了一個es環境,parserOptions則是負責解析es語法。我們可以看到每一個變量都是一個boolean值,false
代表這個變量不允許修改,true
代表可以修改。
我們繼續運行一下我們的demo:
$npx eslint ./src/demo1.js
我們可以發現,我們控制檯還是沒啥反應,這是爲什麼呢?因爲我們還沒配置我們的rules,我們繼續往下走。
parserOptions
sourceType
- 設置爲"script"
(默認) 或"module"
(如果你的代碼是 ECMAScript 模塊)。
在看env的時候我們看到了parserOptions的一個參數“ecmaVersion”,那麼ecmaVersion還有哪些配置呢?
ecmaVersion:
globalReturn
- 允許在全局作用域下使用return
語句impliedStrict
- 啓用全局 strict mode (如果ecmaVersion
是 5 或更高)jsx
- 啓用 JSXexperimentalObjectRestSpread
- 啓用實驗性的 object rest/spread properties 支持。(**重要:**這是一個實驗性的功能,在未來可能會有明顯改變。 建議你寫的規則 不要 依賴該功能,除非當它發生改變時你願意承擔維護成本。)
我們可能會用到jsx,所以我們把jsx
配置成爲true
.eslintrc.json:
{
"env": {
"browser": true,
"es2020": true
},
"parserOptions": {
"ecmaFeatures": {
"jsx": true
}
},
}
因爲我們demo需要用到webpack模塊打包,所以我們需要把sourceType
設置成爲module
{
"env": {
"browser": true,
"es2020": true
},
"parserOptions": {
"ecmaFeatures": {
"jsx": true
},
"sourceType": "module"
}
}
解析器(Parser)
抽象語法樹(Abstract Syntax Tree,AST),parse會把我們的源代碼轉換成抽象語法樹,然後再對每個節點做eslint的校驗,可以說是eslint中最重要的模塊了,eslint默認使用的Esprima作爲ast工具,ast的工具還有acorn等,之前寫過一篇關於babel的文章,感興趣的小夥伴可以去看看babel源碼解析一,裏面用到的就是acorn。
{
"parser": "esprima",
"rules": {
"semi": "error"
}
}
以下解析器與 ESLint 兼容:
- Esprima
- Babel-ESLint - 一個對Babel解析器的包裝,使其能夠與 ESLint 兼容。
- @typescript-eslint/parser - 將 TypeScript 轉換成與 estree 兼容的形式,以便在ESLint中使用。
注意,在使用自定義解析器時,爲了讓 ESLint 在處理非 ECMAScript 5 特性時正常工作,配置屬性 parserOptions
仍然是必須的。解析器會被傳入 parserOptions
,但是不一定會使用它們來決定功能特性的開關。
在線轉換工具:https://astexplorer.net/
打開esprima的文檔我們簡單看一下:
README.md
```javascript
const espree = require("espree");
const ast = espree.parse(code, options);
```js
const options = {
// attach range information to each node
range: false,
// attach line/column location information to each node
loc: false,
// create a top-level comments array containing all comments
comment: false,
// create a top-level tokens array containing all tokens
tokens: false,
// Set to 3, 5 (default), 6, 7, 8, 9, or 10 to specify the version of ECMAScript syntax you want to use.
// You can also set to 2015 (same as 6), 2016 (same as 7), 2017 (same as 8), 2018 (same as 9), 2019 (same as 10), or 2020 (same as 11) to use the year-based naming.
ecmaVersion: 5,
// specify which type of script you're parsing ("script" or "module")
sourceType: "script",
// specify additional language features
ecmaFeatures: {
// enable JSX parsing
jsx: false,
// enable return in global scope
globalReturn: false,
// enable implied strict mode (if ecmaVersion >= 5)
impliedStrict: false
}
}
```
可以看到,esprima接受的其實就是我們傳遞的parserOptions參數,那麼在eslint的配置中我們怎麼使用parse呢?
因爲我們demo是需要加載.vue文件的,所以用默認的esprima解析肯定是不行的,所以我們安裝一個vue-eslint-parser,
因爲vue-eslint-parser直接是包含在eslint-plugin-vue中的,所以我們直接安裝一個eslint-plugin-vue:
npm install -D eslint-plugin-vue
然後直接把vue-eslint-parser配置到eslint中
.eslintrc.json:
{
"env": {
"browser": true,
"es2020": true
},
"parserOptions": {
"ecmaFeatures": {
"jsx": true
},
"sourceType": "module"
},
"parser": "vue-eslint-parser"
}
我們繼續運行eslint:
npx eslint ./src/*
運行後還是沒任何反應,這是爲什麼呢? 別慌,還沒到我們的rules,我們繼續~
處理器(Processor)
processor可以理解爲在parse解析器要解析源文件之前跟eslint的rules處理過後的構造函數,也就是說可以在parse解析之前跟eslint的rules處理過後做一些事情,processor提供了兩個鉤子函數,我們先看一眼eslint-plugin-vue中提供的processor:
xxx/node_modules/eslint-plugin-vue/lib/processor.js
/**
* @author Toru Nagashima <https://github.com/mysticatea>
*/
'use strict'
module.exports = {
preprocess (code) {
console.log("code",code)
return [code]
},
postprocess (messages) {
const state = {
block: {
disableAll: false,
disableRules: new Set()
},
line: {
disableAll: false,
disableRules: new Set()
}
}
// Filter messages which are in disabled area.
return messages[0].filter(message => {
if (message.ruleId === 'vue/comment-directive') {
const rules = message.message.split(' ')
const type = rules.shift()
const group = rules.shift()
switch (type) {
case '--':
state[group].disableAll = true
break
case '++':
state[group].disableAll = false
break
case '-':
for (const rule of rules) {
state[group].disableRules.add(rule)
}
break
case '+':
for (const rule of rules) {
state[group].disableRules.delete(rule)
}
break
case 'clear':
state.block.disableAll = false
state.block.disableRules.clear()
state.line.disableAll = false
state.line.disableRules.clear()
break
}
return false
} else {
return !(
state.block.disableAll ||
state.line.disableAll ||
state.block.disableRules.has(message.ruleId) ||
state.line.disableRules.has(message.ruleId)
)
}
})
},
supportsAutofix: true
}
裏面的具體代碼我們就一一解析了,我們後面在做自定義plugin的時候會詳細說明一下processor,我們可以簡單的看到兩個回調函數,preprocess跟postprocess,preprocess是在parse解析源文件之前調用的方法,postprocess則是eslint的rules處理完畢後的回調函數。
那我們用一下eslint-plugin-vue的processor.
如果插件提供了processor的話,eslint會自動根據文件後綴調用processor,比如在eslint-plugin-vue的清單文件中我們可以看到:
xxx/node_modules/eslint-plugin-vue/lib/index.js
/*
* IMPORTANT!
* This file has been automatically generated,
* in order to update it's content execute "npm run update"
*/
'use strict'
module.exports = {
rules: {
....
},
configs: {
'base': require('./configs/base'),
'essential': require('./configs/essential'),
'no-layout-rules': require('./configs/no-layout-rules'),
'recommended': require('./configs/recommended'),
'strongly-recommended': require('./configs/strongly-recommended')
},
processors: {
'.vue': require('./processor')
}
}
我們可以看到vue提供了一個針對.vue文件的processors:
processors: {
'.vue': require('./processor')
}
接下來,我們用一下eslint-plugin-vue的processors,我們直接創建一個叫demo2.vue的文件:
demo2.vue
<template>
</template>
<script>
export default{
name: 'demo2',
};
</script>
<style lang='scss' scoped>
</style>
然後直接在eslint配置中使用eslint-plugin-vue插件:
.eslintrc.json:
{
"env": {
"browser": true,
"es2020": true
},
"parserOptions": {
"ecmaFeatures": {
"jsx": true
},
"sourceType": "module"
},
"parser": "vue-eslint-parser",
"plugins": [
"vue"
]
}
爲了看到點效果,我們直接在x x/node_modules/eslint-plugin-vue/lib/processor.js源代碼中打個console:
module.exports = {
preprocess (code) {
console.log("code",code)
return [code]
},
...
然後運行eslint:
➜ eslint-demo git:(v0.0.1) ✗ npx eslint ./src/*
code <template>
</template>
<script>
export default{
name: 'demo2',
};
</script>
<style lang='scss' scoped>
</style>
➜ eslint-demo git:(v0.0.1) ✗
可以看到,processor打印了我們的源代碼。
plugin引入的話只會針對.vue文件做處理,那如果我們需要對項目中所有的文件做處理的話該怎麼做呢?
我們直接修改一下配置:
{
"env": {
"browser": true,
"es2020": true
},
"parserOptions": {
"ecmaFeatures": {
"jsx": true
},
"sourceType": "module"
},
"parser": "vue-eslint-parser",
"plugins": [
"vue"
],
"processor": "vue/.vue"
}
我們繼續運行eslint:
➜ eslint-demo git:(v0.0.1) ✗ npx eslint ./src/*
code document.write("hello eslint");
code <template>
</template>
<script>
export default{
name: 'demo2',
};
</script>
<style lang='scss' scoped>
</style>
➜ eslint-demo git:(v0.0.1) ✗
可以看到,我們demo1.js跟demo2.vue的源碼都打印了一遍。
好啦,processor就先講到這裏了,後面在自定義plugin的時候具體在對每個方法做解析。
全局變量(Globals)
這個配置比較好理解,就是我們在env配置的全局變量,如果我們需要添加、禁用某些全局變量的話,我們可以放在Globals配置中,
當訪問當前源文件內未定義的變量時,no-undef 規則將發出警告。如果你想在一個源文件裏使用全局變量,推薦你在 ESLint 中定義這些全局變量,這樣 ESLint 就不會發出警告了。你可以使用註釋或在配置文件中定義全局變量。
要在你的 JavaScript 文件中,用註釋指定全局變量,格式如下:
/* global var1, var2 */
這定義了兩個全局變量,var1
和 var2
。如果你想選擇性地指定這些全局變量可以被寫入(而不是隻被讀取),那麼你可以用一個 "writable"
的標誌來設置它們:
/* global var1:writable, var2:writable */
要在配置文件中配置全局變量,請將 globals
配置屬性設置爲一個對象,該對象包含以你希望使用的每個全局變量。對於每個全局變量鍵,將對應的值設置爲 "writable"
以允許重寫變量,或 "readonly"
不允許重寫變量。例如:
{
"globals": {
"var1": "writable",
"var2": "readonly"
}
}
在 YAML 中:
---
globals:
var1: writable
var2: readonly
在這些例子中 var1
允許被重寫,var2
不允許被重寫。
可以使用字符串 "off"
禁用全局變量。例如,在大多數 ES2015 全局變量可用但 Promise
不可用的環境中,你可以使用以下配置:
{
"env": {
"es6": true
},
"globals": {
"Promise": "off"
}
}
由於歷史原因,布爾值 false
和字符串值 "readable"
等價於 "readonly"
。類似地,布爾值 true
和字符串值 "writeable"
等價於 "writable"
。但是,不建議使用舊值。
比如我們有一個全局的fox變量,我們需要在globals聲明一下,並且爲只讀。我們創建一個demo-global-fox.js文件:
demo-global-fox.js
fox.say("hello");
修改一下我們的配置信息:
.eslintrc.json
{
"env": {
"browser": true,
"es2020": true
},
"parserOptions": {
"ecmaFeatures": {
"jsx": true
},
"sourceType": "module"
},
"parser": "vue-eslint-parser",
"plugins": [
"vue"
],
"processor": "vue/.vue",
"globals": {
"fox": "readonly"
}
}
插件(Plugins)
ESLint 支持使用第三方插件。在使用插件之前,你必須使用 npm 安裝它。比如我們這裏使用了eslint-plugin-vue插件,在配置文件裏配置插件時,可以使用 plugins
關鍵字來存放插件名字的列表。插件名稱可以省略 eslint-plugin-
前綴。
{
"plugins": [
"vue",
"eslint-plugin-vue"
]
}
兩種方式都是可以的,有些第三方插件可能還帶有命名空間,比如@typescirpt-eslint/eslint-plugin插件,每個插件是一個命名格式爲 eslint-plugin-
的 npm 模塊,比如 eslint-plugin-jquery
。你也可以用這樣的格式 @/eslint-plugin-
限定在包作用域下,比如 @jquery/eslint-plugin-jquery
。
我們引用的時候可以直接帶上命名空間即可,比如引用@typescirpt-eslint/eslint-plugin插件和@jquery/eslint-plugin-jquery
插件:
{
"plugins": [
"@typescirpt-eslint",
"@jquery/jquery`"
]
}
前面說了我們demo需要支持typescript,所以我們demo中裝一下@typescirpt-eslint/eslint-plugin插件:
npm install -S typescript && npm install -D @typescript-eslint/eslint-plugin && npm install -D @typescript-eslint/parser
package.json
{
"name": "eslint-demo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"typescript": "^3.9.5"
},
"devDependencies": {
"@typescript-eslint/eslint-plugin": "^3.3.0",
"@typescript-eslint/parser": "^3.3.0",
"eslint": "^7.3.0",
"eslint-plugin-vue": "^6.2.2"
}
}
.eslintrc.json:
{
"env": {
"browser": true,
"es2020": true
},
"parserOptions": {
"ecmaFeatures": {
"jsx": true
},
"sourceType": "module"
},
"parser": "vue-eslint-parser",
"plugins": [
"vue",
"@typescript-eslint"
],
"processor": "vue/.vue",
"globals": {
"fox": "readonly"
}
}
規則(Rules)
終於是講到rules了,rules其實就是eslint中定義的代碼規則,其實eslint也就是一系列的規則的組合,由於我們前面並沒有對我們的demo做任何rules配置,所以我們每次執行eslint的時候都沒有一點反應,接下里我們結合demo做一下rules的配置。
比如我們需要在代碼語句結束強制加分號“;”
我們創建一個demo-semi.js文件:
demo-semi.js
document.write("hello eslint")
然後在配置文件中配置semi規則:
{
"env": {
"browser": true,
"es2020": true
},
"parserOptions": {
"ecmaFeatures": {
"jsx": true
},
"sourceType": "module"
},
"parser": "vue-eslint-parser",
"plugins": [
"vue",
"@typescript-eslint"
],
"processor": "vue/.vue",
"globals": {
"fox": "readonly"
},
"rules": {
"semi": [
"error",
"always"
]
}
}
運行eslint:
➜ eslint-demo git:(v0.0.1) ✗ npx eslint ./src/*
code fox.say("hello");
code document.write("hello eslint")
code document.write("hello eslint");
code <template>
</template>
<script>
export default{
name: 'demo2',
};
</script>
<style lang='scss' scoped>
</style>
xx/Study/eslint-demo/src/demo-semi.js
1:31 error Missing semicolon semi
✖ 1 problem (1 error, 0 warnings)
1 error and 0 warnings potentially fixable with the `--fix` option.
➜ eslint-demo git:(v0.0.1) ✗
可以看到,我們終端中直接報錯了
xx/eslint-demo/src/demo-semi.js
1:31 error Missing semicolon semi
✖ 1 problem (1 error, 0 warnings)
1 error and 0 warnings potentially fixable with the `--fix` option.
哈哈,終於是看點東西了,不容易啊~
再來一個,比如我們需要在代碼中用雙引號“”
.eslintrc.json:
{
"env": {
"browser": true,
"es2020": true
},
"parserOptions": {
"ecmaFeatures": {
"jsx": true
},
"sourceType": "module"
},
"parser": "vue-eslint-parser",
"plugins": [
"vue",
"@typescript-eslint"
],
"processor": "vue/.vue",
"globals": {
"fox": "readonly"
},
"rules": {
"semi": [
"error",
"always"
],
"quotes": ["error", "double"]
}
}
運行eslint:
➜ eslint-demo git:(v0.0.1) ✗ npx eslint ./src/*
code fox.say("hello");
code document.write("hello eslint")
code document.write("hello eslint");
code <template>
</template>
<script>
export default{
name: 'demo2',
};
</script>
<style lang='scss' scoped>
</style>
xx/Study/eslint-demo/src/demo-semi.js
1:31 error Missing semicolon semi
xx/Study/eslint-demo/src/demo2.vue
5:11 error Strings must use doublequote quotes
✖ 2 problems (2 errors, 0 warnings)
2 errors and 0 warnings potentially fixable with the `--fix` option.
➜ eslint-demo git:(v0.0.1) ✗
可以看到,我們代碼中有兩處報錯了,demo-semi.js跟demo2.vue
demo-semi.js(沒有加分號):
document.write("hello eslint")
demo2.vue(沒有用雙引號):
<template>
</template>
<script>
export default{
name: 'demo2',
};
</script>
<style lang='scss' scoped>
</style>
有小夥伴可能要疑問了,semi跟quotes從哪來的呢? 我可不可以定義一個叫xxx的規則呢?
下面一個一個解答:
semi跟quotes從哪來的呢?
我們翻開eslint的源碼:
lib/rules/index.js
/**
* @fileoverview Collects the built-in rules into a map structure so that they can be imported all at once and without
* using the file-system directly.
* @author Peter (Somogyvari) Metz
*/
"use strict";
/* eslint sort-keys: ["error", "asc"] */
const { LazyLoadingRuleMap } = require("./utils/lazy-loading-rule-map");
/** @type {Map<string, import("../shared/types").Rule>} */
module.exports = new LazyLoadingRuleMap(Object.entries({
"accessor-pairs": () => require("./accessor-pairs"),
"array-bracket-newline": () => require("./array-bracket-newline"),
"array-bracket-spacing": () => require("./array-bracket-spacing"),
"array-callback-return": () => require("./array-callback-return"),
"array-element-newline": () => require("./array-element-newline"),
"arrow-body-style": () => require("./arrow-body-style"),
"arrow-parens": () => require("./arrow-parens"),
"arrow-spacing": () => require("./arrow-spacing"),
"block-scoped-var": () => require("./block-scoped-var"),
"block-spacing": () => require("./block-spacing"),
"brace-style": () => require("./brace-style"),
"callback-return": () => require("./callback-return"),
camelcase: () => require("./camelcase"),
"capitalized-comments": () => require("./capitalized-comments"),
"class-methods-use-this": () => require("./class-methods-use-this"),
"comma-dangle": () => require("./comma-dangle"),
"comma-spacing": () => require("./comma-spacing"),
"comma-style": () => require("./comma-style"),
complexity: () => require("./complexity"),
"computed-property-spacing": () => require("./computed-property-spacing"),
"consistent-return": () => require("./consistent-return"),
"consistent-this": () => require("./consistent-this"),
"constructor-super": () => require("./constructor-super"),
curly: () => require("./curly"),
"default-case": () => require("./default-case"),
"default-case-last": () => require("./default-case-last"),
"default-param-last": () => require("./default-param-last"),
"dot-location": () => require("./dot-location"),
"dot-notation": () => require("./dot-notation"),
"eol-last": () => require("./eol-last"),
eqeqeq: () => require("./eqeqeq"),
"for-direction": () => require("./for-direction"),
"func-call-spacing": () => require("./func-call-spacing"),
"func-name-matching": () => require("./func-name-matching"),
"func-names": () => require("./func-names"),
"func-style": () => require("./func-style"),
"function-call-argument-newline": () => require("./function-call-argument-newline"),
"function-paren-newline": () => require("./function-paren-newline"),
"generator-star-spacing": () => require("./generator-star-spacing"),
"getter-return": () => require("./getter-return"),
"global-require": () => require("./global-require"),
"grouped-accessor-pairs": () => require("./grouped-accessor-pairs"),
"guard-for-in": () => require("./guard-for-in"),
"handle-callback-err": () => require("./handle-callback-err"),
"id-blacklist": () => require("./id-blacklist"),
"id-length": () => require("./id-length"),
"id-match": () => require("./id-match"),
"implicit-arrow-linebreak": () => require("./implicit-arrow-linebreak"),
indent: () => require("./indent"),
"indent-legacy": () => require("./indent-legacy"),
"init-declarations": () => require("./init-declarations"),
"jsx-quotes": () => require("./jsx-quotes"),
"key-spacing": () => require("./key-spacing"),
"keyword-spacing": () => require("./keyword-spacing"),
"line-comment-position": () => require("./line-comment-position"),
"linebreak-style": () => require("./linebreak-style"),
"lines-around-comment": () => require("./lines-around-comment"),
"lines-around-directive": () => require("./lines-around-directive"),
"lines-between-class-members": () => require("./lines-between-class-members"),
"max-classes-per-file": () => require("./max-classes-per-file"),
"max-depth": () => require("./max-depth"),
"max-len": () => require("./max-len"),
"max-lines": () => require("./max-lines"),
"max-lines-per-function": () => require("./max-lines-per-function"),
"max-nested-callbacks": () => require("./max-nested-callbacks"),
"max-params": () => require("./max-params"),
"max-statements": () => require("./max-statements"),
"max-statements-per-line": () => require("./max-statements-per-line"),
"multiline-comment-style": () => require("./multiline-comment-style"),
"multiline-ternary": () => require("./multiline-ternary"),
"new-cap": () => require("./new-cap"),
"new-parens": () => require("./new-parens"),
"newline-after-var": () => require("./newline-after-var"),
"newline-before-return": () => require("./newline-before-return"),
"newline-per-chained-call": () => require("./newline-per-chained-call"),
"no-alert": () => require("./no-alert"),
"no-array-constructor": () => require("./no-array-constructor"),
"no-async-promise-executor": () => require("./no-async-promise-executor"),
"no-await-in-loop": () => require("./no-await-in-loop"),
"no-bitwise": () => require("./no-bitwise"),
"no-buffer-constructor": () => require("./no-buffer-constructor"),
"no-caller": () => require("./no-caller"),
"no-case-declarations": () => require("./no-case-declarations"),
"no-catch-shadow": () => require("./no-catch-shadow"),
"no-class-assign": () => require("./no-class-assign"),
"no-compare-neg-zero": () => require("./no-compare-neg-zero"),
"no-cond-assign": () => require("./no-cond-assign"),
"no-confusing-arrow": () => require("./no-confusing-arrow"),
"no-console": () => require("./no-console"),
"no-const-assign": () => require("./no-const-assign"),
"no-constant-condition": () => require("./no-constant-condition"),
"no-constructor-return": () => require("./no-constructor-return"),
"no-continue": () => require("./no-continue"),
"no-control-regex": () => require("./no-control-regex"),
"no-debugger": () => require("./no-debugger"),
"no-delete-var": () => require("./no-delete-var"),
"no-div-regex": () => require("./no-div-regex"),
"no-dupe-args": () => require("./no-dupe-args"),
"no-dupe-class-members": () => require("./no-dupe-class-members"),
"no-dupe-else-if": () => require("./no-dupe-else-if"),
"no-dupe-keys": () => require("./no-dupe-keys"),
"no-duplicate-case": () => require("./no-duplicate-case"),
"no-duplicate-imports": () => require("./no-duplicate-imports"),
"no-else-return": () => require("./no-else-return"),
"no-empty": () => require("./no-empty"),
"no-empty-character-class": () => require("./no-empty-character-class"),
"no-empty-function": () => require("./no-empty-function"),
"no-empty-pattern": () => require("./no-empty-pattern"),
"no-eq-null": () => require("./no-eq-null"),
"no-eval": () => require("./no-eval"),
"no-ex-assign": () => require("./no-ex-assign"),
"no-extend-native": () => require("./no-extend-native"),
"no-extra-bind": () => require("./no-extra-bind"),
"no-extra-boolean-cast": () => require("./no-extra-boolean-cast"),
"no-extra-label": () => require("./no-extra-label"),
"no-extra-parens": () => require("./no-extra-parens"),
"no-extra-semi": () => require("./no-extra-semi"),
"no-fallthrough": () => require("./no-fallthrough"),
"no-floating-decimal": () => require("./no-floating-decimal"),
"no-func-assign": () => require("./no-func-assign"),
"no-global-assign": () => require("./no-global-assign"),
"no-implicit-coercion": () => require("./no-implicit-coercion"),
"no-implicit-globals": () => require("./no-implicit-globals"),
"no-implied-eval": () => require("./no-implied-eval"),
"no-import-assign": () => require("./no-import-assign"),
"no-inline-comments": () => require("./no-inline-comments"),
"no-inner-declarations": () => require("./no-inner-declarations"),
"no-invalid-regexp": () => require("./no-invalid-regexp"),
"no-invalid-this": () => require("./no-invalid-this"),
"no-irregular-whitespace": () => require("./no-irregular-whitespace"),
"no-iterator": () => require("./no-iterator"),
"no-label-var": () => require("./no-label-var"),
"no-labels": () => require("./no-labels"),
"no-lone-blocks": () => require("./no-lone-blocks"),
"no-lonely-if": () => require("./no-lonely-if"),
"no-loop-func": () => require("./no-loop-func"),
"no-loss-of-precision": () => require("./no-loss-of-precision"),
"no-magic-numbers": () => require("./no-magic-numbers"),
"no-misleading-character-class": () => require("./no-misleading-character-class"),
"no-mixed-operators": () => require("./no-mixed-operators"),
"no-mixed-requires": () => require("./no-mixed-requires"),
"no-mixed-spaces-and-tabs": () => require("./no-mixed-spaces-and-tabs"),
"no-multi-assign": () => require("./no-multi-assign"),
"no-multi-spaces": () => require("./no-multi-spaces"),
"no-multi-str": () => require("./no-multi-str"),
"no-multiple-empty-lines": () => require("./no-multiple-empty-lines"),
"no-native-reassign": () => require("./no-native-reassign"),
"no-negated-condition": () => require("./no-negated-condition"),
"no-negated-in-lhs": () => require("./no-negated-in-lhs"),
"no-nested-ternary": () => require("./no-nested-ternary"),
"no-new": () => require("./no-new"),
"no-new-func": () => require("./no-new-func"),
"no-new-object": () => require("./no-new-object"),
"no-new-require": () => require("./no-new-require"),
"no-new-symbol": () => require("./no-new-symbol"),
"no-new-wrappers": () => require("./no-new-wrappers"),
"no-obj-calls": () => require("./no-obj-calls"),
"no-octal": () => require("./no-octal"),
"no-octal-escape": () => require("./no-octal-escape"),
"no-param-reassign": () => require("./no-param-reassign"),
"no-path-concat": () => require("./no-path-concat"),
"no-plusplus": () => require("./no-plusplus"),
"no-process-env": () => require("./no-process-env"),
"no-process-exit": () => require("./no-process-exit"),
"no-promise-executor-return": () => require("./no-promise-executor-return"),
"no-proto": () => require("./no-proto"),
"no-prototype-builtins": () => require("./no-prototype-builtins"),
"no-redeclare": () => require("./no-redeclare"),
"no-regex-spaces": () => require("./no-regex-spaces"),
"no-restricted-exports": () => require("./no-restricted-exports"),
"no-restricted-globals": () => require("./no-restricted-globals"),
"no-restricted-imports": () => require("./no-restricted-imports"),
"no-restricted-modules": () => require("./no-restricted-modules"),
"no-restricted-properties": () => require("./no-restricted-properties"),
"no-restricted-syntax": () => require("./no-restricted-syntax"),
"no-return-assign": () => require("./no-return-assign"),
"no-return-await": () => require("./no-return-await"),
"no-script-url": () => require("./no-script-url"),
"no-self-assign": () => require("./no-self-assign"),
"no-self-compare": () => require("./no-self-compare"),
"no-sequences": () => require("./no-sequences"),
"no-setter-return": () => require("./no-setter-return"),
"no-shadow": () => require("./no-shadow"),
"no-shadow-restricted-names": () => require("./no-shadow-restricted-names"),
"no-spaced-func": () => require("./no-spaced-func"),
"no-sparse-arrays": () => require("./no-sparse-arrays"),
"no-sync": () => require("./no-sync"),
"no-tabs": () => require("./no-tabs"),
"no-template-curly-in-string": () => require("./no-template-curly-in-string"),
"no-ternary": () => require("./no-ternary"),
"no-this-before-super": () => require("./no-this-before-super"),
"no-throw-literal": () => require("./no-throw-literal"),
"no-trailing-spaces": () => require("./no-trailing-spaces"),
"no-undef": () => require("./no-undef"),
"no-undef-init": () => require("./no-undef-init"),
"no-undefined": () => require("./no-undefined"),
"no-underscore-dangle": () => require("./no-underscore-dangle"),
"no-unexpected-multiline": () => require("./no-unexpected-multiline"),
"no-unmodified-loop-condition": () => require("./no-unmodified-loop-condition"),
"no-unneeded-ternary": () => require("./no-unneeded-ternary"),
"no-unreachable": () => require("./no-unreachable"),
"no-unreachable-loop": () => require("./no-unreachable-loop"),
"no-unsafe-finally": () => require("./no-unsafe-finally"),
"no-unsafe-negation": () => require("./no-unsafe-negation"),
"no-unused-expressions": () => require("./no-unused-expressions"),
"no-unused-labels": () => require("./no-unused-labels"),
"no-unused-vars": () => require("./no-unused-vars"),
"no-use-before-define": () => require("./no-use-before-define"),
"no-useless-backreference": () => require("./no-useless-backreference"),
"no-useless-call": () => require("./no-useless-call"),
"no-useless-catch": () => require("./no-useless-catch"),
"no-useless-computed-key": () => require("./no-useless-computed-key"),
"no-useless-concat": () => require("./no-useless-concat"),
"no-useless-constructor": () => require("./no-useless-constructor"),
"no-useless-escape": () => require("./no-useless-escape"),
"no-useless-rename": () => require("./no-useless-rename"),
"no-useless-return": () => require("./no-useless-return"),
"no-var": () => require("./no-var"),
"no-void": () => require("./no-void"),
"no-warning-comments": () => require("./no-warning-comments"),
"no-whitespace-before-property": () => require("./no-whitespace-before-property"),
"no-with": () => require("./no-with"),
"nonblock-statement-body-position": () => require("./nonblock-statement-body-position"),
"object-curly-newline": () => require("./object-curly-newline"),
"object-curly-spacing": () => require("./object-curly-spacing"),
"object-property-newline": () => require("./object-property-newline"),
"object-shorthand": () => require("./object-shorthand"),
"one-var": () => require("./one-var"),
"one-var-declaration-per-line": () => require("./one-var-declaration-per-line"),
"operator-assignment": () => require("./operator-assignment"),
"operator-linebreak": () => require("./operator-linebreak"),
"padded-blocks": () => require("./padded-blocks"),
"padding-line-between-statements": () => require("./padding-line-between-statements"),
"prefer-arrow-callback": () => require("./prefer-arrow-callback"),
"prefer-const": () => require("./prefer-const"),
"prefer-destructuring": () => require("./prefer-destructuring"),
"prefer-exponentiation-operator": () => require("./prefer-exponentiation-operator"),
"prefer-named-capture-group": () => require("./prefer-named-capture-group"),
"prefer-numeric-literals": () => require("./prefer-numeric-literals"),
"prefer-object-spread": () => require("./prefer-object-spread"),
"prefer-promise-reject-errors": () => require("./prefer-promise-reject-errors"),
"prefer-reflect": () => require("./prefer-reflect"),
"prefer-regex-literals": () => require("./prefer-regex-literals"),
"prefer-rest-params": () => require("./prefer-rest-params"),
"prefer-spread": () => require("./prefer-spread"),
"prefer-template": () => require("./prefer-template"),
"quote-props": () => require("./quote-props"),
quotes: () => require("./quotes"),
radix: () => require("./radix"),
"require-atomic-updates": () => require("./require-atomic-updates"),
"require-await": () => require("./require-await"),
"require-jsdoc": () => require("./require-jsdoc"),
"require-unicode-regexp": () => require("./require-unicode-regexp"),
"require-yield": () => require("./require-yield"),
"rest-spread-spacing": () => require("./rest-spread-spacing"),
semi: () => require("./semi"),
"semi-spacing": () => require("./semi-spacing"),
"semi-style": () => require("./semi-style"),
"sort-imports": () => require("./sort-imports"),
"sort-keys": () => require("./sort-keys"),
"sort-vars": () => require("./sort-vars"),
"space-before-blocks": () => require("./space-before-blocks"),
"space-before-function-paren": () => require("./space-before-function-paren"),
"space-in-parens": () => require("./space-in-parens"),
"space-infix-ops": () => require("./space-infix-ops"),
"space-unary-ops": () => require("./space-unary-ops"),
"spaced-comment": () => require("./spaced-comment"),
strict: () => require("./strict"),
"switch-colon-spacing": () => require("./switch-colon-spacing"),
"symbol-description": () => require("./symbol-description"),
"template-curly-spacing": () => require("./template-curly-spacing"),
"template-tag-spacing": () => require("./template-tag-spacing"),
"unicode-bom": () => require("./unicode-bom"),
"use-isnan": () => require("./use-isnan"),
"valid-jsdoc": () => require("./valid-jsdoc"),
"valid-typeof": () => require("./valid-typeof"),
"vars-on-top": () => require("./vars-on-top"),
"wrap-iife": () => require("./wrap-iife"),
"wrap-regex": () => require("./wrap-regex"),
"yield-star-spacing": () => require("./yield-star-spacing"),
yoda: () => require("./yoda")
}));
我們可以找到我們的semi跟quotes,還有一些其它的rule,每個rule的具體含義小夥伴可以自己去看eslint的官網或源碼哦,裏面都有說明的。
ESLint 附帶有大量的規則。你可以使用註釋或配置文件修改你項目中要使用的規則。要改變一個規則設置,你必須將規則 ID 設置爲下列值之一:
"off"
或0
- 關閉規則"warn"
或1
- 開啓規則,使用警告級別的錯誤:warn
(不會導致程序退出)"error"
或2
- 開啓規則,使用錯誤級別的錯誤:error
(當被觸發的時候,程序會退出)
在代碼中配置(Comments)
爲了在文件註釋裏配置規則,使用以下格式的註釋:
/* eslint eqeqeq: "off", curly: "error" */
在這個例子裏,eqeqeq
規則被關閉,curly
規則被打開,定義爲錯誤級別。你也可以使用對應的數字定義規則嚴重程度:
/* eslint eqeqeq: 0, curly: 2 */
這個例子和上個例子是一樣的,只不過它是用的數字而不是字符串。eqeqeq
規則是關閉的,curly
規則被設置爲錯誤級別。
如果一個規則有額外的選項,你可以使用數組字面量指定它們,比如:
/* eslint quotes: ["error", "double"], curly: 2 */
這條註釋爲規則 quotes
指定了 “double”選項。數組的第一項總是規則的嚴重程度(數字或字符串)。
比如我們的demo2.vue文件:
<template>
</template>
<script>
/* eslint quotes: ["error", "double"] */
export default{
name: 'demo2',
};
</script>
<style lang='scss' scoped>
</style>
我們添加了一個/* eslint quotes: [“error”, “double”] */註釋,其實跟配置文件加rules一樣的效果,只是配置文件加rules是作用於所有的文件。
在config文件中配置(rules)
這個我們已經演示過了,比如:
{
"rules": {
"eqeqeq": "off",
"curly": "error",
"quotes": ["error", "double"]
}
}
我可不可以定義一個叫xxx的規則呢?
可以! 自定義規則我們會在自定義plugin的時候詳細講解。
禁止規則(Disabling Rules with Inline Comments)
既然可以在代碼中使用規則,當然也可以在代碼中禁用規則。
比如我們創建一個demo-disable.js:
document.write("hello eslint")
如果我們現在運行一下eslint的話會直接報錯,提示我們需要加上“;”符號。
如果我們需要在demo-disable.js文件中禁用掉eslint的話,我們該怎麼做呢?
禁止掉所有規則
我們直接把/* eslint-disable */添加到文件的頭部,這樣會禁止掉所有的eslint規則
/* eslint-disable */
document.write("hello eslint")
禁止掉指定的規則
/* eslint-disable semi*/,如果有多個規則的話逗號隔開 /* eslint-disable no-alert, no-console */
/* eslint-disable semi*/
document.write("hello esslint")
單行註釋
document.write("hello esslint") // eslint-disable-line semi
document.write("hello esslint") //eslint-disable-line
document.write("hello esslint") /* eslint-disable-line no-alert */
/* eslint-disable-next-line no-alert */
document.write("hello esslint")
document.write("hello esslint") // eslint-disable-line semi,quotes 多個註釋逗號隔開
以文件分組禁止(Disabling Rules Only for a Group of Files)
比如我們要禁止demo-disable.js
.eslintrc.json:
{
"env": {
"browser": true,
"es2020": true
},
"parserOptions": {
"ecmaFeatures": {
"jsx": true
},
"sourceType": "module"
},
"parser": "vue-eslint-parser",
"plugins": [
"vue",
"@typescript-eslint"
],
"processor": "vue/.vue",
"globals": {
"fox": "readonly"
},
"rules": {
"semi": [
"error",
"always"
],
"quotes": ["error", "double"]
},
"overrides": [
{
"files": ["demo-disable.js"],
"rules": {
"semi": "off"
}
}
]
}
共享設置(Settings)
ESLint 支持在配置文件添加共享設置。你可以添加 settings
對象到配置文件,它將提供給每一個將被執行的規則。如果你想添加的自定義規則而且使它們可以訪問到相同的信息,在每個規則的回調函數的context對象中包含settings屬性。
在 JSON配置文件中:
{
"settings": {
"sharedData": "Hello"
}
}
自定義rules會在後面的自定義plugin文章中詳細介紹。
配置文件格式(Configuration File Formats)
ESLint 支持幾種格式的配置文件:
- JavaScript - 使用
.eslintrc.js
然後輸出一個配置對象。 - YAML - 使用
.eslintrc.yaml
或.eslintrc.yml
去定義配置的結構。 - JSON - 使用
.eslintrc.json
去定義配置的結構,ESLint 的 JSON 文件允許 JavaScript 風格的註釋。 - (棄用) - 使用
.eslintrc
,可以使 JSON 也可以是 YAML。 - package.json - 在
package.json
裏創建一個eslintConfig
屬性,在那裏定義你的配置。
如果同一個目錄下有多個配置文件,ESLint 只會使用一個。優先級順序如下:
.eslintrc.js
.eslintrc.yaml
.eslintrc.yml
.eslintrc.json
.eslintrc
package.json
配置文件層級和嵌套(Configuration Cascading and Hierarchy)
當使用 .eslintrc.*
和 package.json
文件的配置時,你可以利用層疊配置。例如,假如你有以下結構:
your-project
├── .eslintrc
├── lib
│ └── source.js
└─┬ tests
├── .eslintrc
└── test.js
層疊配置使用離要檢測的文件最近的 .eslintrc
文件作爲最高優先級,然後纔是父目錄裏的配置文件,等等。當你在這個項目中允許 ESLint 時,lib/
下面的所有文件將使用項目根目錄裏的 .eslintrc
文件作爲它的配置文件。當 ESLint 遍歷到 test/
目錄,your-project/.eslintrc
之外,它還會用到 your-project/tests/.eslintrc
。所以 your-project/tests/test.js
是基於它的目錄層次結構中的兩個.eslintrc
文件的組合,並且離的最近的一個優先。通過這種方式,你可以有項目級 ESLint 設置,也有覆蓋特定目錄的 ESLint 設置。
同樣的,如果在根目錄的 package.json
文件中有一個 eslintConfig
字段,其中的配置將使用於所有子目錄,但是當 tests
目錄下的 .eslintrc
文件中的規則與之發生衝突時,就會覆蓋它。
your-project
├── package.json
├── lib
│ └── source.js
└─┬ tests
├── .eslintrc
└── test.js
如果同一目錄下 .eslintrc
和 package.json
同時存在,.eslintrc
優先級高會被使用,package.json
文件將不會被使用。
**注意:**如果在你的主目錄下有一個自定義的配置文件 (~/.eslintrc
) ,如果沒有其它配置文件時它纔會被使用。因爲個人配置將適用於用戶目錄下的所有目錄和文件,包括第三方的代碼,當 ESLint 運行時可能會導致問題。
默認情況下,ESLint 會在所有父級目錄裏尋找配置文件,一直到根目錄。如果你想要你所有項目都遵循一個特定的約定時,這將會很有用,但有時候會導致意想不到的結果。爲了將 ESLint 限制到一個特定的項目,在你項目根目錄下的 package.json
文件或者 .eslintrc.*
文件裏的 eslintConfig
字段下設置 "root": true
。ESLint 一旦發現配置文件中有 "root": true
,它就會停止在父級目錄中尋找。
{
"root": true
}
在 YAML 中:
---
root: true
例如,projectA
的 lib/
目錄下的 .eslintrc
文件中設置了 "root": true
。這種情況下,當檢測 main.js
時,lib/
下的配置將會被使用,projectA/
下的 .eslintrc
將不會被使用。
home
└── user
├── .eslintrc <- Always skipped if other configs present
└── projectA
├── .eslintrc <- Not used
└── lib
├── .eslintrc <- { "root": true }
└── main.js
完整的配置層次結構,從最高優先級最低的優先級,如下:
- 行內配置
/*eslint-disable*/
和/*eslint-enable*/
/*global*/
/*eslint*/
/*eslint-env*/
- 命令行選項(或 CLIEngine 等價物):
--global
--rule
--env
-c
、--config
- 項目級配置:
- 與要檢測的文件在同一目錄下的
.eslintrc.*
或package.json
文件 - 繼續在父級目錄尋找
.eslintrc
或package.json
文件,直到根目錄(包括根目錄)或直到發現一個有"root": true
的配置。
- 與要檢測的文件在同一目錄下的
- 如果不是(1)到(3)中的任何一種情況,退回到
~/.eslintrc
中自定義的默認配置。
繼承配置文件(Extending Configuration Files)
我們可以使用extends字段去繼承一個配置文件,這個配置文件可以是單獨的一個文件、第三方依賴、插件。
單獨的一個config文件
我們在項目根目錄創建一個config目錄,然後在config目錄中創建一個cust-config.js,然後添加一條“不允許出現console”的規則。
cust-config.js:
module.exports = {
rules: {
"no-console": "error", //禁止使用console
}
};
然後創建一個demo-console.js測試
demo-console.js:
console.log("hello eslint");
然後使用extends字段繼承我們的配置信息
.eslintrc.json:
{
"env": {
"browser": true,
"es2020": true
},
"parserOptions": {
"ecmaFeatures": {
"jsx": true
},
"sourceType": "module"
},
"parser": "vue-eslint-parser",
"plugins": [
"vue",
"@typescript-eslint"
],
"processor": "vue/.vue",
"globals": {
"fox": "readonly"
},
"rules": {
"semi": [
"error",
"always"
],
"quotes": ["error", "double"]
},
"overrides": [
{
"files": ["demo-disable.js"],
"rules": {
"semi": "off"
}
}
],
"extends": [
"./config/cust-config.js"
]
}
運行eslint:
192:eslint-demo yinqingyang$ npx eslint ./src/*
/Users/yinqingyang/doc/h5/study/高級程序設計/eslint-demo/src/demo-console.js
1:1 error Unexpected console statement no-console
/Users/yinqingyang/doc/h5/study/高級程序設計/eslint-demo/src/demo-semi.js
1:31 error Missing semicolon semi
/Users/yinqingyang/doc/h5/study/高級程序設計/eslint-demo/src/demo2.vue
5:11 error Strings must use doublequote quotes
✖ 3 problems (3 errors, 0 warnings)
2 errors and 0 warnings potentially fixable with the `--fix` option.
192:eslint-demo yinqingyang$
可以發現有報錯信息“error Unexpected console statement no-console”,說明我們的自定義config文件起作用了。
第三方依賴
其實跟我們第一種方式是一樣的,也就是把我們的config文件發佈,然後利用npm依賴,我就不演示了。
插件繼承
這種方式其實跟第二種有點像,但是插件的功能大於配置文件功能,前面我們使用了兩個插件:eslint-plugin-vue跟@t y pescript-eslint/eslint-plugin,我們也提前已經使用了插件:
.eslintrc.json
{
...
"plugins": [
"vue",
"@typescript-eslint"
],
...
}
我們打開vue的插件看一下清單文件:
Xxx/eslint-demo/node_modules/eslint-plugin-vue/lib/index.js
/*
* IMPORTANT!
* This file has been automatically generated,
* in order to update it's content execute "npm run update"
*/
'use strict'
module.exports = {
...
configs: {
'base': require('./configs/base'),
'essential': require('./configs/essential'),
'no-layout-rules': require('./configs/no-layout-rules'),
'recommended': require('./configs/recommended'),
'strongly-recommended': require('./configs/strongly-recommended')
},
...
}
可以看到,vue插件提供了configs字段給外部調用,裏面有“base”、“essential”、“no-layout-rules”、“recommended”
、“strongly-recommended”,我們打開base看看:
/*
* IMPORTANT!
* This file has been automatically generated,
* in order to update it's content execute "npm run update"
*/
module.exports = {
parser: require.resolve('vue-eslint-parser'),
parserOptions: {
ecmaVersion: 2018,
sourceType: 'module',
ecmaFeatures: {
jsx: true
}
},
env: {
browser: true,
es6: true
},
plugins: [
'vue'
],
rules: {
'vue/comment-directive': 'error',
'vue/jsx-uses-vars': 'error'
}
}
其實這就是一個eslint的配置文件,我們可以選擇繼承並使用它。比如我們使用一下vue的recommended配置:
.eslintrc.json
{
"env": {
"browser": true,
"es2020": true
},
"parserOptions": {
"ecmaFeatures": {
"jsx": true
},
"sourceType": "module"
},
"parser": "vue-eslint-parser",
"plugins": [
"vue",
"@typescript-eslint"
],
"processor": "vue/.vue",
"globals": {
"fox": "readonly"
},
"rules": {
"semi": [
"error",
"always"
],
"quotes": ["error", "double"]
},
"overrides": [
{
"files": ["demo-disable.js"],
"rules": {
"semi": "off"
}
}
],
"extends": [
"./config/cust-config.js", //繼承自定義config文件
"plugin:vue/recommended" //繼承vue的recommended配置
]
}
192:eslint-demo yinqingyang$ npx eslint ./src/*
xx/doc/h5/study/高級程序設計/eslint-demo/src/demo-console.js
1:1 error Unexpected console statement no-console
xx/doc/h5/study/高級程序設計/eslint-demo/src/demo-semi.js
1:31 error Missing semicolon semi
xx/doc/h5/study/高級程序設計/eslint-demo/src/demo2.vue
1:1 error The template root requires exactly one element vue/valid-template-root
1:1 warning Require self-closing on HTML elements (<template>) vue/html-self-closing
5:11 error Strings must use doublequote quotes
5:11 warning Property name "demo2" is not PascalCase vue/name-property-casing
✖ 6 problems (4 errors, 2 warnings)
2 errors and 2 warnings potentially fixable with the `--fix` option.
192:eslint-demo yinqingyang$
可以看到,我們的demo2.vue文件中多了很多報錯
demo2.vue:
<template>
</template>
<script>
export default{
name: 'demo2',
};
</script>
<style lang='scss' scoped>
</style>
-
The template root requires exactly one element vue/valid-template-root
(模版必須包含至少一個元素)
-
warning Require self-closing on HTML elements () vue/html-self-closing
(如果爲空元素的話使用自閉合)
-
error Strings must use doublequote quotes
-
warning Property name “demo2” is not PascalCase vue/name-property-casing
(組件的name屬性必須是駝峯命名)
忽略文件(Ignoring Files and Directories)
demo中我們可以看到,我們每次執行eslint的時候執行的命令:
npx eslint ./src/*
也就是說會校驗./src目錄下的所有文件,如果有些文件不需要校驗的話該怎麼辦呢?
.eslintignore
在根目錄創建一個.eslintignore
文件。
.eslintignore:
src/demo-ignore.js
然後我們在src下面創建一個demo-ignore.js:
demo-ignore.js
document.write("hello eslint")
運行eslint:
192:eslint-demo yinqingyang$ npx eslint ./src/*
xx/study/高級程序設計/eslint-demo/src/demo-console.js
1:1 error Unexpected console statement no-console
xx/h5/study/高級程序設計/eslint-demo/src/demo-ignore.js
0:0 warning File ignored because of a matching ignore pattern. Use "--no-ignore" to override
xx/study/高級程序設計/eslint-demo/src/demo-semi.js
1:31 error Missing semicolon semi
xxy/高級程序設計/eslint-demo/src/demo2.vue
2:1 warning Expected indentation of 2 spaces but found 4 spaces vue/html-indent
6:11 error Strings must use doublequote quotes
6:11 warning Property name "demo2" is not PascalCase vue/name-property-casing
✖ 6 problems (3 errors, 3 warnings)
2 errors and 2 warnings potentially fixable with the `--fix` option.
192:eslint-demo yinqingyang$
可以看到一條警告:
xx/h5/study/高級程序設計/eslint-demo/src/demo-ignore.js
0:0 warning File ignored because of a matching ignore pattern. Use "--no-ignore" to override
當 ESLint 運行時,在確定哪些文件要檢測之前,它會在當前工作目錄中查找一個 .eslintignore
文件。如果發現了這個文件,當遍歷目錄時,將會應用這些偏好設置。一次只有一個 .eslintignore
文件會被使用,所以,不是當前工作目錄下的 .eslintignore
文件將不會被用到。
Globs 匹配使用 node-ignore,所以大量可用的特性有:
- 以
#
開頭的行被當作註釋,不影響忽略模式。 - 路徑是相對於
.eslintignore
的位置或當前工作目錄。通過--ignore-pattern
command 傳遞的路徑也是如此。 - 忽略模式同
.gitignore
規範 - 以
!
開頭的行是否定模式,它將會重新包含一個之前被忽略的模式。 - 忽略模式依照
.gitignore
規範.
特別值得注意的是,就像 .gitignore
文件,所有用作 .eslintignore
和 --ignore-pattern
模式的路徑必須使用前斜槓作爲它們的路徑分隔符。
# Valid
/root/src/*.js
# Invalid
\root\src\*.js
請參參閱 .gitignore
規範查看有關有效語法的更多示例。
除了 .eslintignore
文件中的模式,ESLint總是忽略 /node_modules/*
和 /bower_components/*
中的文件。
例如:把下面 .eslintignore
文件放到當前工作目錄裏,將忽略項目根目錄下的 node_modules
,bower_components
以及 build/
目錄下除了 build/index.js
的所有文件。
# /node_modules/* and /bower_components/* in the project root are ignored by default
# Ignore built files except build/index.js
build/*
!build/index.js
**重要:**注意代碼庫的 node_modules
目錄,比如,一個 packages
目錄,默認情況下不會被忽略,需要手動添加到 .eslintignore
。
Using an Alternate File
如果相比於當前工作目錄下 .eslintignore
文件,你更想使用一個不同的文件,你可以在命令行使用 --ignore-path
選項指定它。例如,你可以使用 .jshintignore
文件,因爲它有相同的格式:
eslint --ignore-path .jshintignore file.js
你也可以使用你的 .gitignore
文件:
eslint --ignore-path .gitignore file.js
任何文件只要滿足標準忽略文件格式都可以用。記住,指定 --ignore-path
意味着任何現有的 .eslintignore
文件將不被使用。請注意,.eslintignore
中的匹配規則比 .gitignore
中的更嚴格。
Using eslintIgnore in package.json
如果沒有發現 .eslintignore
文件,也沒有指定替代文件,ESLint 將在 package.json 文件中查找 eslintIgnore
鍵,來檢查要忽略的文件。
{
"name": "mypackage",
"version": "0.0.1",
"eslintConfig": {
"env": {
"browser": true,
"node": true
}
},
"eslintIgnore": ["hello.js", "world.js"]
}
Ignored File Warnings
當你傳遞目錄給 ESLint,文件和目錄是默默被忽略的。如果你傳遞一個指定的文件給 ESLint,你會看到一個警告,表明該文件被跳過了。例如,假如你有一個像這樣的 .eslintignore
文件:
foo.js
然後你執行:
eslint foo.js
你將會看到這個警告:
foo.js
0:0 warning File ignored because of your .eslintignore file. Use --no-ignore to override.
✖ 1 problem (0 errors, 1 warning)
這種消息出現是因爲 ESLint 不確定你是否想檢測文件。正如這個消息表明的那樣,你可以使用 --no-ignore
覆蓋忽略的規則。
好啦,eslint的配置文件環節我們算是全部分析了一遍,文章有點長,我們算結合demo跟着官網走了一遍,也算是收穫滿滿,不得不佩服寫eslint框架的大神,使得功能這麼強大的一個工具變得這麼通俗易懂,下一節我們將重點針對eslint的plugin做解析,打造一個屬於你自己的plugin,敬請期待!