從0到1開發(fā)一個簡單的 eslint 插件
?前言:eslint 我們常應用在代碼靜態(tài)掃描中,通過設定的 eslint 的語法規(guī)則,來對代碼進行檢查,通過規(guī)則來約束代碼的風格,以此來提高代碼的健壯性,避免因為代碼不規(guī)范導致應用出現(xiàn) bug 的可能。而規(guī)則是自由的,你可以設定內(nèi)部自己團隊適用的規(guī)則,也可以直接使用開源社區(qū)比較熱門的
?規(guī)則集合, 比如 airbnb、eslint-plugin-vue 等
1.eslint 的配置
?手寫規(guī)則前,讓我們重溫下 eslint 配置,通常我們是使用
?.eslintrc.js來配置 eslint 的,或者也可以直接package.json中定義 eslintConfig 的屬性

上圖 ?? 是 eslint 主要的配置,我們簡單回顧下每個配置的背后包含的意義
1.1 parse
?parse 是用來定義 eslint 所使用的解析器,默認是使用
?Espree??, 解析器的作用是將代碼 code 轉(zhuǎn)化成為一種 AST 抽象語法樹,eslint 中叫ESTree,你可以理解為將 code 翻譯為ESLint能聽 ?? 懂的話
關于 Espree 可以參考下面這個例子

而常用的解析器還有包括以下幾種
Esprima: 上文提到 espree 就是基于 Esprima 改良的
Babel-esLint:一個對 Babel 解析器的包裝,當你項目中使用了 babel,babel 的解析器會把你的 code 轉(zhuǎn)換為 AST,然后該解析器會將其轉(zhuǎn)換為 ESLint 能懂的 ESTree。這個目前我們應用的較多,目前也不再維護和更新,升級為
@babel/eslint-parser@typescript-eslint/parser: 將 TypeScript 轉(zhuǎn)換成與 estree 兼容的形式,以便在 ESLint 中使用。
對于 AST 的模擬生成,感興趣的同學可以使用astexplorer在線嘗試

總結:無論你使用那種解析器,本質(zhì)是都是為了將 code 轉(zhuǎn)換為 ESLint 能夠閱讀的語言ESTree??
1.2 parseOption
?parserOptions 參數(shù)是用來控制 parse 解析器, 主要包括以下幾個屬性 ??,我們挑重點的講講
?

ecmaVersion:用來指定你想要使用的 ECMAScript 版本,默認設置為 5,舉個例子:默認情況下,ESLint 支持 ECMAScript 5 語法,但如果你想讓 eslint 使用 es6 特征來支持,就可以通過修改 parserOptions 中"ecmaVersion": 6
1.3 rules
?rules 就是 eslint 的規(guī)則,你可以在 rules 配置中根據(jù)在不同場景、不同規(guī)范下添加自定義規(guī)則詳情可參考之前 ?? 樹醬的 前端規(guī)范那些事
?
1.4 extends(擴展) 與 plugins(插件)
?extends 和 plugins 很容易混淆,本質(zhì)是為了加強 eslint 的擴展性,使得我們可以直接使用別人已經(jīng)寫好的 eslint 規(guī)則,方便快捷的應用到項目中,有點類似之前文章中Babel 配置傻傻看不懂?提及的 babel 配置中的 present 和 plugin
?
比如你使用 extends 去擴展 { "extends": [ "eslint:recommended", "plugin:react/recommended", "eslint-config-standard", ]}
但是如果你想用插件,其實等價于 {"plugin": ['react','standard']}
? 提醒:官方規(guī)定 npm 包的擴展必須以 eslint-config- 開頭,我們使用過程中可以省略這個開頭,上面案例中 eslint-config-standard 可以直接簡寫成 standard。同樣,如果要開發(fā)一個 eslint 插件,也是需要以這種形式來命名,下節(jié)會介紹
我們再舉個列子

上圖我們通過上面這個配置例子,我們可以看到要么是plugins:[]要么是extends:[],通過上圖所示的配置二相對于配置一少了parser, parserOptions 和 plugins 等的信息配置,但其實這兩個配置最終實現(xiàn)的結果是一致的,這是因為配置二中定義的 extends:plugin:@typescript-eslint/recommended 會自動加載上述提到的其他幾個配置信息
2 開發(fā) eslint 插件
?通過上一節(jié)對 eslint 的配置的了解,接下來看看如何從 0 到 1 開發(fā)一個 eslint 插件。
?
2.1 eslint 插件初始化
?ESLint 官方為了方便開發(fā)者,提供了使用 Yeoman 腳手架的模板(generator-eslint??)。以此方便我們通過該腳手架拉取 eslint 插件模版,對 Yeoman 進一步了解可以閱讀 ?? 樹醬的前端工程化那些事 - yeoman
?

第一步:安裝 npm install -g yo generator-eslint第二步:創(chuàng)建一個文件夾并然后通過命令行初始化 ESLint 插件的項目結構 yo eslint:plugin第三步:完成插件初始化創(chuàng)建
2.2 創(chuàng)建 rule 規(guī)則
?完成插件項目結構初始化創(chuàng)建后,開始生成 ESLint 插件中具體規(guī)則,在 ESLint 插件的項目中執(zhí)行命令行
?yo eslint:rule,來生成 eslint 規(guī)則的模版,實際效果如下所示

創(chuàng)建成功后,我們看下最終的目錄結構

docs: 使用文檔,描述你編寫的規(guī)則 lib/rules 目錄:規(guī)則開發(fā)源碼文件 (例如,no-extra-semi.js) tests/lib/rules 目錄:單元測試文件 (例如,no-extra-semi.js)
2.3 編寫規(guī)則
?當完成上面一系列操作之后,eslint 插件模版初步完成,接下來我們找到目錄中
?lib/rules中對剛剛創(chuàng)建的 rule 進行開發(fā)
假設我們有個場景,我們想創(chuàng)建一個規(guī)則,用來判讀代碼中是否存在console方法的調(diào)用,首先回到第一節(jié)提到的parse解析器,本質(zhì)上 rule 的邏輯判斷是通過識別 Espree 返回的抽象語法 ?? 去判斷,分別針對各種類型定義檢查方法。寫代碼之前,我們先看下 console 返回的 AST 是長啥樣?

通過上圖我們可以清晰的看到 console.log()是屬于 ExpressionStatement(表達式語句)中的 CallExpression(調(diào)用語句),可以通過 callee 屬性中的 object 來判斷是否為console, 同時也可以利用其 property 屬性來判斷是 console 的哪種方法,比如log、info等
so~ 我們開始造玩具,我們通過在 create 返回的對象中,定義一個 CallExpression 方法,當 ESLint 開始對 esTree 遍歷時,通過對調(diào)用語句的監(jiān)聽,來檢查該調(diào)用語句是否為 console 調(diào)用,代碼如下 ??

每條 rule 就是一個 node 模塊,其主要由 meta 和 create 兩部分組成,重點講下下面兩個 ??
meta: 代表了這條規(guī)則的元數(shù)據(jù),包含類別,文檔,可接收的參數(shù)的 schema 等, 其中主要提下 schema,如果指定該選項,ESLint 可以通過識別的傳參,避免無效的規(guī)則配置(排除校驗),可參考下節(jié)介紹的單元測試的中傳遞的 options context.report():它用來發(fā)布警告或錯誤(取決于你所使用的配置)
?? 推薦閱讀:
Eslint - Working with Rules
2.4 單元測試
?當完成 eslit 插件開發(fā)后,我們需要對開發(fā)完的插件進行驗證,以此來保證規(guī)則校驗功能的正常使用。eslint 插件開發(fā)項目結構中默認使用了
?mocha作為單元測試框架
我們對tests/rules/treegogo.js單元測試文件進行修改,定義 invalid 與 valid 的不同例子

最后執(zhí)行

2.5 關于發(fā)布
?在發(fā)布之前,還需要對 packjson 中 main 定義入口文件即
?lib/index.js,暴露出 rules 與 configs

????? 啊寬同學:那我如何定義一個包含配置的集合呢?
?
是的,官方文檔描述:你可以在一個插件中在 configs 鍵下指定打包的配置。當你想提供不止代碼風格,而且希望提供一些自定義規(guī)則來支持它時,會非常有用。每個插件支持多配置,然后當你使用的時候,可以通過這樣使用 { "extends": ["plugin:tree-eslint/myConfig"] },這就包含預設好的規(guī)則配置
最后是 npm 發(fā)布 npm pulish
2.6 如何使用
?通過第一節(jié)的配置的介紹,我們需要有個
?.eslintrc文件,如果目錄沒用可以通過命令行eslint -init初始化,配置好后,安裝剛剛開放好的 eslint 插件
配置一可以對我們開發(fā)的那個 rule 進行配置:error,warn,off,如果需要對部分做排除就加上 option,也可以像配置二引用預設好的擴展 extends
