您當前的位置:首頁 > 舞蹈

Eslint 的實現原理,其實挺簡單

作者:由 神說要有光 發表于 舞蹈時間:2021-11-01

Eslint 是我們每天都在用的工具,我們會用它的 cli 或 api 來做程式碼錯誤檢查和格式檢查,有時候也會寫一些 rule 來做自定義的檢查和修復。

雖然每天都用,但我們卻很少去了解它是怎麼實現的。而瞭解 Eslint 的實現原理能幫助我們更好的使用它,更好的寫一些外掛。

所以,這篇文章我們就透過原始碼來探究下 Eslint 的實現原理吧。

Linter

Linter 是 eslint 最核心的類了,它提供了這幾個 api:

verify // 檢查

verifyAndFix // 檢查並修復

getSourceCode // 獲取 AST

defineParser // 定義 Parser

defineRule // 定義 Rule

getRules // 獲取所有的 Rule

SourceCode 就是指的 AST(抽象語法樹),Parser 是把原始碼字串解析成 AST 的,而 Rule 則是我們配置的那些對 AST 進行檢查的規則。這幾個 api 比較容易理解。

Linter 主要的功能是在 verify 和 verifyAndFix 裡實現的,當命令列指定

——fix

或者配置檔案指定

fix: true

就會呼叫 verifyAndFix 對程式碼進行檢查並修復,否則會呼叫 verify 來進行檢查。

那 verify 和 fix 是怎麼實現的呢?這就是 eslint 最核心的部分了:

確定 parser

我們知道 Eslint 的 rule 是基於 AST 進行檢查的,那就要先把原始碼 parse 成 AST。而 eslint 的 parser 也是可以切換的,需要先找到用啥 parser:

預設是 Eslint 自帶的 espree,也可以透過配置來切換成別的 parser,比如 @eslint/babel-parser、@typescript/eslint-parser 等。

下面是 resolve parser 的邏輯:

Eslint 的實現原理,其實挺簡單

確定了 parser 之後,就是呼叫 parse 方法了。

parse 成 SourceCode

parser 的 parse 方法會把原始碼解析為 AST,在 eslint 裡是透過 SourceCode 來封裝 AST 的。後面看到 SourceCode 就是指 AST。

Eslint 的實現原理,其實挺簡單

有了 AST,就可以呼叫 rules 對 AST 進行檢查了

呼叫 rule 對 SourceCode 進行檢查,獲得 lintingProblems

parse 之後,會呼叫 runRules 方法對 AST 進行檢查,返回結果就是 problems,也就是有什麼錯誤和怎麼修復的資訊。

Eslint 的實現原理,其實挺簡單

那 runRules 是怎麼執行的 rule 呢?

rule 的實現如下,就是註冊了對什麼 AST 做什麼檢查,這點和 babel 外掛很類似。

Eslint 的實現原理,其實挺簡單

runRules 會遍歷 AST,然後遇到不同的 AST 會 emit 不同的事件。rule 裡處理什麼 AST 就會監聽什麼事件,這樣透過事件監聽的方式,就可以在遍歷 AST 的過程中,執行不同的 rule 了。

註冊 listener:

Eslint 的實現原理,其實挺簡單

遍歷 AST,emit 不同的事件,觸發 listener:

Eslint 的實現原理,其實挺簡單

這樣,

遍歷完一遍 AST,也就呼叫了所有的 rules,這就是 rule 的執行機制

還有,遍歷的過程中會傳入 context,rule 裡可以拿到,比如 scope、settings 等。

Eslint 的實現原理,其實挺簡單

還有 ruleContext,呼叫 AST 的 listener 的時候可以拿到:

Eslint 的實現原理,其實挺簡單

而 rule 裡面就是透過這個 report 的 api 進行報錯的,那這樣就可以把所有的錯誤收集起來,然後進行列印。

這個 problem 是什麼呢?

linting problem

lint problem 是檢查的結果,也就是從哪一行(line)哪一列(column)到哪一行(endLine)哪一列(endColumn),有什麼錯誤(message)。

還有就是怎麼修復(fix),修復其實就是 從那個下標到哪個下標(range),替換成什麼文字(text)。

Eslint 的實現原理,其實挺簡單

為什麼 fix 是 range 返回和 text 這樣的結構呢?因為它的實現就是簡單的字串替換。

透過字串替換實現自動 fix

遍歷完 AST,呼叫了所有的 rules,收集到了 linting problems 之後,就可以進行 fix 了。

fix 部分的相關原始碼是這樣的:

Eslint 的實現原理,其實挺簡單

也就是 verify 進行檢查,然後根據 fix 資訊自動 fix。

fix 其實就是個字串替換:

Eslint 的實現原理,其實挺簡單

有的同學可能注意到了,字串替換為什麼要加個 while 迴圈呢?

因為多個 fix 之間的 range 也就是替換的範圍可能是有重疊的,如果有重疊就放到下一次來修復,這樣 while 迴圈最多修復 10 次,如果還有 fix 沒修復就不修了。

這就是 fix 的實現原理,透過字串替換來實現的,如果有重疊就迴圈來 fix。

preprocess 和 postprocess

其實核心的 verify 和 fix 的流程就是上面那些,但是 Eslint 還支援之前和之後做一些處理。也就是 pre 和 post 的 process,這些也是在外掛裡定義的。

module。exports = {

processors: {

“。txt”: {

preprocess: function(text, filename) {

return [ // return an array of code blocks to lint

{ text: code1, filename: “0。js” },

{ text: code2, filename: “1。js” },

];

},

postprocess: function(messages, filename) {

return []。concat(。。。messages);

}

}

}

};

之前的處理是把非 js 檔案解析出其中的一個個 js 檔案來,這和 webpack 的 loader 很像,這使得 Eslint 可以處理非 JS 檔案的 lint。

之後的處理呢?那肯定是處理 problems 啊,也就是 messages,可以過濾掉一些 messages,或者做一些修改之類的。

那 preprocess 和 postprocess 是怎麼實現的呢?

這個就比較簡單了,就是在 verify 之前和之後呼叫就行。

Eslint 的實現原理,其實挺簡單

Eslint 的實現原理,其實挺簡單

透過 comment directives 來過濾掉一些 problems

我們知道 eslint 還支援透過註釋來配置,比如

/* eslint-disable */

/*eslint-enable*/

這種。

那它是怎麼實現的呢?

註釋的配置是透過掃描 AST 來收集所有的配置的,這種配置叫做 commentDirective,也就是哪行那列 Eslint 是否生效。

然後在 verify 結束的時候,對收集到的 linting problems 做一次過濾即可。

Eslint 的實現原理,其實挺簡單

上面講的這些就是 Eslint 的實現原理:

Eslint 的實現原理,其實挺簡單

Eslint 和 CLIEngine 類

Linter 是實現核心功能的,上面我們介紹過了,但是在命令列的場景下還需要處理一些命令列引數,也就需要再包裝一層 CLIEngine,用來做檔案的讀寫,命令列引數的解析。

它有 executeOnFiles 和 executeOnText 等 api,是基於 Linter 類的上層封裝。

但是 CLIEngine 並沒有直接暴露出去,而是又包裝了一層 EsLint 類,它只是一層比較好用的門面,隱藏了一些無關資訊。

Eslint 的實現原理,其實挺簡單

我們看下 eslint 最終暴露出來的這幾個 api:

Linter 是核心的類,直接對文字進行 lint

ESLint 是處理配置、讀寫檔案等,然後呼叫 Linter 進行 lint(中間的那層 CLIEngine 並沒有暴露出來)

SourceCode 就是封裝 AST 用的

RuleTester 是用於 rule 測試的一些 api。

Eslint 的實現原理,其實挺簡單

總結

我們透過原始碼理清了 eslint 的實現原理:

ESLint 的核心類是 Linter,它分為這樣幾步:

preprocess,把非 js 文字處理成 js

確定 parser(預設是 espree)

呼叫 parser,把原始碼 parse 成 SourceCode(ast)

呼叫 rules,對 SourceCode 進行檢查,返回 linting problems

掃描出註釋中的 directives,對 problems 進行過濾

postprocess,對 problems 做一次處理

基於字串替換實現自動 fix

除了核心的 Linter 類外,還有用於處理配置和讀寫檔案的 CLIEngine 類,以及最終暴露出去的 Eslint 類。

這就是 Eslint 的實現原理,其實還是挺簡單的:

基於 AST 做檢查,基於字串做 fix,之前之後還有 pre 與 post 的process,支援註釋來配置過濾掉一些 problems。

把這些理清楚之後,就算是原始碼層面掌握了 Eslint 了。

標簽: AST  fix  ESLint  rule