靠譜的前端團隊通常都會引入本身的代碼風格規範,但真實項目中的問題經常不是統一了空格數量和是否加分號就能解決的,而是有不少看不見的暗坑。咱們是否有代碼風格之上,對代碼質量的更高級把控呢?ESLint 插件或許可以爲你打開新世界的大門。前端
在前端發展突飛猛進的這個時代,咱們實際上正在面臨日益嚴峻的前端代碼腐化問題。譬如,但凡接手維護過前端項目的同窗,應該多少都遇到過這樣的場景:node
按照破窗理論,若是環境中的不良現象若是被聽任存在,會誘令人們仿效,甚至變本加厲。但代碼中的熵增現象並不意味着程序員們都是犯罪分子,不少時候上面這些問題,實際上源自於項目中對新人來講陌生的隱式潛規則:react
lodash/xxx
來減小包體積,那麼他可能會全量導入 lodash。這個層面上的一致性問題已經超出了是用空格仍是 tab,或者行尾加不加分號的範疇,市面上已有的代碼風格檢查工具是「管不到這麼寬」的。固然了,這個層面的問題也確實能夠用 Code Review 來解決,但人工評審未必在每一個標榜敏捷和 Moving Fast 的團隊中都推得開。那麼,咱們是否有更高效率的手段,來將這些隱式的「潛規則」沉澱下來呢?這裏咱們嘗試給出一種答案:編寫本身的業務 Lint 插件。git
在 2018 年,ESLint 基本已是靠譜前端腳手架中的必備依賴了。但多數狀況下咱們都是使用一個現成的代碼風格規範。但 ESLint 實際上並不只僅能夠用於檢測空格、換行等風格問題,在與業務開發規範相結合後,就會發現它還具備很是大的潛力。而本身從頭編寫一個 ESLint 插件的過程其實也並不複雜,讓咱們來看看如何實踐吧:程序員
和普通的前端項目同樣,ESLint 插件也提供了一套開箱即用的腳手架。只須要安裝全局依賴:github
npm install -g yo generator-eslint
複製代碼
就能夠建立咱們本身的插件了:npm
mkdir eslint-plugin-demo
cd eslint-plugin-demo
yo eslint:plugin
? What is your name? ...
? What is the plugin ID? demo
? Type a short description of this plugin: ...
? Does this plugin contain custom ESLint rules? Yes
? Does this plugin contain one or more processors? No
npm install
複製代碼
初始化一個插件,就和 create-react-app
同樣簡單對吧?json
如今是時候來建立咱們的第一條 Lint 規則了!做爲例子,空格排版強迫症患者的筆者不喜歡在代碼裏看到這樣的註釋:bash
// 獲取abc數據2次
複製代碼
Web 上中文排版的慣例實際上是這樣的:微信
// 獲取 abc 數據 2 次
複製代碼
可是並非誰都和筆者同樣甚至在微信聊天裏都堅持手動插入空格,而在 commit 記錄裏強行改動別人的註釋,也有種替人挖鼻孔的不適感。那麼咱們把這個約定升級爲 ESLint 的規則呢?咱們須要理解一點 ESLint 的工做原理。
ESLint 使用 Espree 這個 JavaScript parser 來解析你的項目源碼。Parser 會將源代碼字符串解析爲一棵抽象語法樹(AST),對於樹中的每個節點,ESLint 都會尋找是否存在與之匹配的規則,若匹配則計算出該規則是否知足。而 AST 是什麼樣的呢?譬如一行 // hello world
的 JS 代碼文件,AST 格式形如:
{
"type": "Program",
"start": 14,
"end": 14,
"body": [],
"innerComments": [
{
"type": "Line",
"value": " hello world",
"start": 0,
"end": 14
"range": [
0,
12
]
}
],
"//": "......"
}
複製代碼
基於這個數據結構,若是但願對全部的變量聲明語句添加規則,那麼咱們的插件規則就形如:
module.exports = function (context) {
return {
'VariableDeclaration' (node) {
// 在這裏搞事情
// ...
}
}
}
複製代碼
這個 VariableDeclaration
是哪來的呢?這就是時候展現你做爲資深前端,對於 ES Spec 的熟悉了!實際上,JavaScript 中的每一種語句,在規範中都定義了相應的類型,咱們按照類型名稱便可編寫對其進行校驗的規則了。若是咱們但願對註釋作校驗,那麼將上面示例中的名稱換成 Comment
便可。是否是很符合直覺呢?
上面的這種方式能夠理解爲很是經典的 Visitor 模式,它在方便聲明式地編寫規則的同時,也有相鄰節點之間徹底透明,不方便一些複雜操做的問題。所以你也可使用一些更過程式的 API 來輔助規則的編寫:
module.exports = {
create: function (context) {
const sourceCode = context.getSourceCode()
return {
// Program 至關於 AST 根節點
Program () {
const comments = sourceCode.getAllComments()
comments.forEach(node => {
if (/* 知足校驗規則 */) {
context.report(node, 'Something WRONG!')
}
})
}
}
}
}
複製代碼
對於咱們如今檢測空格的需求,一個現成的依賴是 pangu.js
。咱們在上面的註釋處調用 pangu 的格式化 API 就可以實現校驗了。但在實際編寫本身的插件時,具體的業務規則每每不是難點,難點實際上在於對 JS 語法樹結構的熟悉。這裏特別推薦 astexplorer 這個工具,它可以直觀地讓你瞭解源碼對應的 AST 結構,方便校驗規則的編寫。
到這裏,咱們應該已經對編寫規則有了一些直觀的認識了。回到開頭提出的問題,咱們就能夠用 ESLint 對症下藥了:
fetch
一類的問題。_
或 lodash
賦值時,這多半會帶來包體積的劇增,能夠編寫規則來避免。咱們已經知道了怎麼編寫靈活的校驗規則,但這些代碼多半在平常的業務開發中不會遇到,該怎麼保證它靠譜呢?這就須要咱們引入測試驅動的開發模式了。
在插件的 package.json 裏,會有這樣的腳本:
"scripts": {
"test": "mocha ./tests/**/*.js"
}
複製代碼
做爲示例,咱們在 /tests
目錄下添加 spacing-test.js
測試用例,填入這樣的內容:
const rule = require('../lib/rules/spacing')
const RuleTester = require('eslint').RuleTester
const ruleTester = new RuleTester()
ruleTester.run('comment', rule, {
valid: [
'// 白色相簿 2'
],
invalid: [
{
code: '// 白色相簿2',
errors: [{
message: 'Something WRONG!',
type: 'Line'
}]
}
]
})
複製代碼
這就是經過測試驅動 ESLint 插件開發的基礎方式了。對於規則所但願覆蓋到的代碼片斷,能夠經過測試用例的形式提供,這會在很大程度上便利後來者的理解和維護。編寫完測試用例後,執行用例的方式也很是簡單:
npm test
複製代碼
測試用例所有經過,就表明着插件大功告成了!剩下的就是將它發佈到 NPM 上,按照 ESLint 插件的配置方式,在你的項目中引入就行啦。在第一步腳手架爲你生成的 README 中,這個過程已經有了很詳盡的文檔,在此就不贅述了。
不少前端同窗爲了鑽研技術深度,會去閱讀 ES Spec 的規範文檔。但惋惜的是這個層面的內容不少時候對於通常的業務開發用處不是很大。但在你具有了開發(而不是使用)ESLint 插件的能力後,配合上你對 JS 自己的熟悉,就會有種解鎖了「控制代碼的代碼」技能的船新感受:用代碼去約束和優化代碼自己,這就是 Meta Programming 的威力了吧。
上文中編寫的註釋插件也已經發布到 GitHub,歡迎參考或供強迫症同窗試用哦。最後忽然想沒來由地提一句…
願意爲你在微信里加空格的妹子,必定是真愛了。