ESLint 是一個開源的 JavaScript 代碼檢查工具,由 Nicholas C. Zakas 於2013年6月建立。代碼檢查是一種靜態的分析,經常使用於尋找有問題的模式或者代碼,而且不依賴於具體的編碼風格。對大多數編程語言來講都會有代碼檢查,通常來講編譯程序會內置檢查工具。html
JavaScript 是一個動態的弱類型語言,在開發中比較容易出錯。由於沒有編譯程序,爲了尋找 JavaScript 代碼錯誤一般須要在執行過程當中不斷調試。像 ESLint 這樣的可讓程序員在編碼的過程當中發現問題而不是在執行的過程當中。vue
ESLint 的初衷是爲了讓程序員能夠建立本身的檢測規則。ESLint 的全部規則都被設計成可插拔的。爲了便於人們使用,ESLint 內置了一些規則,固然,你能夠在使用過程當中自定義規則。全部的規則默認都是禁用的。node
ESLint 使用 Node.js 編寫。git
以使用項目爲例,簡單介紹一下eslint的具體配置及做用:程序員
module.exports = {
parser: 'babel-eslint', // parser指定解析器,默認的爲espree。babel-eslint是一個Babel parser的包裝器,這個包裝器使得 Babel parser 能夠和 ESLint 協調工做
parserOptions: {
sourceType: 'module', // 設置爲 "script" (默認) 或 "module"(ES6)。
ecmaFeatures: { // 這是個對象,表示你想使用的額外的語言特性:
jsx: true // 啓用 JSX
}
},
extends: ['eslint:recommended'], // 使用eslint推薦的規則做爲基礎配置,能夠在rules中覆蓋
plugins: ['html', 'vue', 'prettier', 'import'], // vue是eslint-plugin-vue的簡寫,此插件的做用是可讓eslint識別.vue中的script代碼
rules: { // 0或者off表示規則關閉,出錯也被忽略;1或者warn表示若是出錯會給出警告(不會致使程序退出);2或者error表示若是出錯會報出錯誤(會致使程序退出,退出碼是1)
'no-console': 'off',
'prefer-const': 'error',
'prettier/prettier': 'warn',
'prefer-arrow-callback': 'warn',
'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0
},
globals: { // 容許在代碼中使用全局變量
location: true,
setTimeout: true
}
};
複製代碼
具體的配置文檔:eslint.cn/docs/user-g… 具體的eslint:recommended支持的規則:cn.eslint.org/docs/rules/es6
「extends」除了能夠引入推薦規則,還能夠以文件形式引入其它的自定義規則,而後在這些自定義規則的基礎上用rules去定義個別規則,從而覆蓋掉」extends」中引入的規則。github
{
"extends": [
"./node_modules/coding-standard/eslintDefaults.js",
// Override eslintDefaults.js
"./node_modules/coding-standard/.eslintrc-es6",
// Override .eslintrc-es6
"./node_modules/coding-standard/.eslintrc-jsx",
],
"rules": {
// Override any settings from the "parent" configuration
"eqeqeq": "warn"
}
}
複製代碼
除了在配置文件中指定規則外,還能夠在代碼中指定規則,代碼文件內以註釋配置的規則會覆蓋配置文件裏的規則,即優先級要更高。平時咱們經常使用的就是 eslint-disable-next-line
express
能夠經過在項目目錄下創建.eslintignore文件,並在其中配置忽略掉對哪些文件的檢查。須要注意的是,無論你有沒有在.eslintignore中進行配置,eslint都會默認忽略掉對/node_modules/** 的檢查。也能夠在package.json文件的 eslintIgnore 字段進行配置。npm
要實現靜態分析則須要自建一個預編譯階段對代碼進行解析。編程
首先咱們看看大部分編譯器工做時的三個階段:
解析:將未經處理的代碼解析成更爲抽象的表達式,一般爲抽象語法樹,即 AST。 轉換:經過修改解析後的代碼表達式,將其轉換爲符合預期的新格式。 代碼生成:將轉換後的表達式生成爲新的目標代碼。
對於eslint來講,規則校驗發生在將JavaScript 代碼解析爲 AST 以後,遍歷 AST 的過程當中。eslint採用 Espree 來生成AST。具體的生成方法在這裏。 咱們能夠使用AST explorer來查看代碼被解析後生成的AST。
首先來看看eslint源碼中關於rules的編寫。eslint中的rules源碼存在於lib/rules下。每個rules都是一個node模塊,用module.exports導出一個meta對象及一個create函數。
module.exports = {
meta: {
type: "suggestion",
docs: {
description: "disallow unnecessary semicolons",
category: "Possible Errors",
recommended: true,
url: "https://eslint.org/docs/rules/no-extra-semi"
},
fixable: "code",
schema: [] // no options
},
create: function(context) {
return {
// callback functions
};
}
};
複製代碼
meta 表明了這條規則的元數據,如這條規則的類別,文檔,可接收的參數 schema 等等。
create 返回一個對象,其中定義了一些在 AST 遍歷訪問到對應節點須要執行的方法等等。函數接受一個context對象做爲參數,裏面包含了例如能夠報告錯誤或者警告的context.report()、能夠獲取源代碼的context.getSourceCode()等方法,能夠簡化規則的編寫。
function checkLastSegment (node) {
// report problem for function if last code path segment is reachable
}
module.exports = {
meta: { ... },
create: function(context) {
// declare the state of the rule
return {
ReturnStatement: function(node) {
// 在AST從上向下遍歷到ReturnStatement node 時執行
},
// 在AST 從下向上遍歷到 function expression node 時執行:
"FunctionExpression:exit": checkLastSegment,
"ArrowFunctionExpression:exit": checkLastSegment,
onCodePathStart: function (codePath, node) {
// 在分析代碼路徑開始時執行
},
onCodePathEnd: function(codePath, node) {
// 在分析代碼路徑結束時執行
}
};
}
};
複製代碼
遍歷 AST 的過程當中會以「從上至下」再「從下至上」的順序通過節點兩次,selector 默認會在下行的過程當中執行對應的訪問函數,若是須要再上行的過程當中執行,則須要添加:exit。
詳細的原理在官方文檔中有說明,點這裏。 詳細的代碼路徑分析在這裏。
知道了rules的原理,接下來能夠自定義一個rules。每個rules須要有三個以該規則名命名的文件,分別是:
在 lib/rules 目錄下: 一個源文件(例如,no-extra-semi.js)
在 tests/lib/rules 目錄下: 一個測試文件 (例如, no-extra-semi.js)
在 docs/rules 目錄: 一個 markdown 文檔文件 (例如, no-extra-semi)
接下來咱們來編寫一個簡單的rules,例如禁止塊級註釋,當代碼中使用了塊級註釋,eslint將報錯。
rules文件:
// lib/rules/no-block-comments.js
module.exports = {
meta: {
docs: {
description: '禁止塊級註釋',
category: 'Stylistic Issues',
recommended: true
}
},
create (context) {
// 獲取源代碼
const sourceCode = context.getSourceCode()
return {
Program () {
// 獲取源代碼中全部的註釋
const comments = sourceCode.getAllComments()
const blockComments = comments.filter(({ type }) => type === 'Block')
blockComments.length && context.report({
node: node,
message: 'No block comments'
})
}
}
}
}
複製代碼
rules的測試文件:
// tests/lib/rules/no-block-comments.js
const RuleTester = require("eslint").RuleTester;
const rule = require("../../../lib/rules/no-block-comments");
const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 2018 } }); // You do have to tell eslint what js you're using ruleTester.run("no-block-comments", rule, { valid: ["var a = 1; console.log(a)"], invalid: [ { code: "var a = 1; /* block comments */ console.log(a)", errors: [ { messageId: "blockComments", line: 1, nodeType: "Block" } ] } ] }); 複製代碼
官網的working with rules文檔中有關於如何編寫一個rules的詳細介紹。
編寫好的rules須要發佈到npm上,做爲一個eslint-plugin,在項目中下載下來纔可以使用。例子中代碼的npm在這裏。
在項目中的配置:
// .eslintrc.js
module.exports = {
...
"plugins": [
"eslint-plugin-no-block-comments"
// 你 publish 的 npm 包名稱,能夠省略 eslint-plugin
],
"rules": { // 啓用的規則及其各自的錯誤級別
'no-console': 'off',
"no-block-comments/no-block-comments": 2 // 引用no-block-comments插件中的no-block-comments規則
}
};
複製代碼
以後就能夠對代碼進行檢查了。好比我要檢查的代碼以下:
// src/index.js
const a = 1;
/*
這裏是塊級註釋
*/
console.log(a);
複製代碼
在命令行中執行eslint src
,就能夠看到報錯結果。 evernotecid://43E55629-1A51-495A-878E-7C9D5C55CC19/appyinxiangcom/15304103/ENResource/p297