
當 ESLint 在 2013 年首次發佈時,配置系統相當簡單。您可以在 .eslintrc
檔案中定義您想要啟用或停用的規則。當程式碼檔案被檢查時,ESLint 會先在該檔案所在的目錄中尋找 .eslintrc
檔案,然後繼續向上尋找至根目錄,合併沿途找到的所有 .eslintrc
檔案的配置。這個我們稱之為配置級聯的系統,讓您可以輕鬆地覆蓋特定目錄的規則,這是 JSHint 無法做到的。您也可以在 package.json
內的 eslintConfig
鍵中加入更多配置。
然而,多年來,配置系統逐漸變成了一個難以駕馭的混亂狀態。這就是為什麼我在 2019 年提議創建一個新的配置系統,使其在 JavaScript 專案日益複雜的世界中更容易配置 ESLint。新配置系統的很大一部分已合併到主分支中,因此現在是開始了解未來如何配置 ESLint 的時候了。但是,為了做到這一點,回顧一下我們如何走到目前這種狀態是有幫助的。
導致最大複雜性的增量變更
回顧目前配置系統(稱為 eslintrc 系統)的演變,當時的每一步都有其邏輯道理。ESLint 一直以來都採用增量開發的方式,我們著眼於如何改進我們已有的東西,而不是拋棄一切重新開始。eslintrc 系統也不例外。
extends
鍵
eslintrc 的第一個重大變更是引入了 extends
鍵。 extends
鍵,借鑒自 JSHint,允許使用者導入另一個配置並對其進行擴充,例如:
{
"extends": ["./other-config.json"],
"rules": {
"semi": "warn"
}
}
因此,假設 ./other-config.json
有一些配置資料,您可以導入它,然後在其之上新增您自己的 rules
設定。事實證明,這對 ESLint 來說是向前邁進的一大步,原因有很多。
首先,extends
實際上早於可透過 npm 分發的共享配置的想法。正是在實作 extends
時,我們意識到共享配置是可行的。在 extends
中指定的檔案是透過 Node.js 的 require()
函數載入的,因此任何 Node.js 可以透過該函數載入的東西也可以作為擴充的配置使用。
其次,extends
允許我們實作 eslint:recommended
,這是一組我們認為每個人都應該啟用的重要規則。最初,ESLint 預設啟用了一些規則,但這對使用者來說成為一種負擔。因此,我們改為預設關閉所有規則,這對於沒有看到任何規則的新使用者來說也很令人困惑。加入 eslint:recommended
讓我們可以明確表示您正在包含我們推薦的一堆規則,但如果您不想要,可以將其移除。
事後看來,如果我們多考慮一下,我們本應在此時移除配置級聯。引入 extends
啟用了許多與級聯相同的用例,而同時保留兩者最終變成了一個混亂的局面,我們將花費數年時間來嘗試解決。
個人配置
當人們要求我們新增在 ~/.eslintrc
中設定個人配置檔案的功能時,就新增了下一層複雜性。因此,我們新增了一個額外的檢查:如果我們在檔案位置的祖先目錄中找不到配置檔案,那麼我們將自動尋找個人配置檔案。
多種配置檔案格式
作為重構的一部分,我們發現允許不同的配置檔案格式是微不足道的。我們可以將 JSON 格式正式化為 .eslintrc.json
,同時新增對 YAML (.eslintrc.yml
或 .eslintrc.yaml
) 和 JavaScript (.eslintrc.js
) 的支援,而不是強迫每個人都使用非標準的 .eslintrc
檔案。為了向後相容,我們繼續支援 .eslintrc
,因為保留它只需要少量程式碼。
事後看來,這也不是一個好主意。新增 JavaScript 配置檔案格式在它和非 JS 格式之間產生了不相容性:任何 JavaScript 物件都可以傳遞到配置中,並在規則中使用。由於我們沒有正確驗證配置以完全匹配非 JS 格式,我們最終得到了一些規則,需要傳入正規表示式物件才能正確配置。雖然這可以在 JS 配置檔案格式中使用,但在非 JS 配置檔案中無法正確配置規則。不幸的是,由於外掛規則依賴於此功能,我們無法回頭修復它而不破壞任何東西。
共享配置和相依性
我們早期面臨的最大問題可能是當 npm 決定停止在 v3 中安裝同級相依性時。在此之前,我們建議共享配置將它們所依賴的任何外掛程式包含為同級相依性,而不是常規相依性。這是 extends
實作方式的一個怪癖:使用 require()
。
由於共享配置僅是資料,無法直接參照 Node.js 相依性,因此 require()
不會自動將直接相依性載入 ESLint 解析它們的路徑。另一方面,同級相依性可以完美運作,只需使用 require()
,因為它們安裝在正常套件查找可以運作的位置。
當 npm v3 預設停止安裝同級相依性時,所有依賴此行為的共享配置都停止正常運作。有一個長期存在的問題,要求允許共享配置直接使用相依性,但 eslintrc 的架構不允許這樣做。我們基本上必須在 ESLint 內部重新建立整個 require()
功能,才能解決共享配置的設計方式。我們建議共享配置建立一個安裝後指令碼來安裝它們的同級相依性。絕非理想狀況。
我們新增了 --resolve-plugins-relative-to
命令列選項,以嘗試幫助解決這個問題,但這還不夠。在我們的 Discord #help 頻道中最常見的求助請求都與配置檔案中外掛程式的解析不當有關。
npm 最終在 v7 中改回預設安裝同級相依性,但在那時,ESLint 生態系統的損害已經造成。
root
鍵
隨著時間的推移,配置級聯繼續為使用者帶來問題。最常見的情況是,人們沒有意識到他們在正在處理的專案的祖先目錄中擁有一個配置檔案。這會造成混淆,因為他們會得到他們似乎沒有配置的 ESLint 設定。
為了幫助解決這個問題,我們為配置檔案引入了 root
屬性。當在配置中指定 root: true
時,將不再搜尋祖先目錄的更多配置檔案。這阻止了一些混亂,我們最終自動在 ESLint 透過舊的 --init
命令產生的配置中包含 root: true
,以幫助使用者以最少的混亂開始。
overrides
鍵
ESLint 繼續收到對更強大的配置專案方式的請求。更具體地說,有人要求在現有的配置檔案中提供基於 glob 的配置。這導致建立了一個 overrides
鍵,讓您可以進一步修改 ESLint 正在檢查的特定檔案子集的配置。以下是一個範例:
{
"rules": {
"quotes": ["error", "double"]
},
"overrides": [
{
"files": ["bin/*.js", "lib/*.js"],
"excludedFiles": "*.test.js",
"rules": {
"quotes": ["error", "single"]
}
}
]
}
在這個例子中,bin
和 lib
中的 JavaScript 檔案偏好使用單引號,而不是其他地方偏好的雙引號。
基於 glob 配置的 overrides
鍵,事實證明,是一種比配置級聯所嘗試實現的更好的方式。事後看來,這將是嘗試移除級聯的完美時機......但我們沒有。而複雜性並未止步於此。
將 extends
新增到 overrides
eslintrc 開發的最後一步是將 extends
鍵新增到 overrides
配置中,允許使用者將其他配置資料注入基於 glob 的配置物件,如下所示:
{
"rules": {
"quotes": ["error", "double"]
},
"overrides": [
{
"files": ["bin/*.js", "lib/*.js"],
"excludedFiles": "*.test.js",
"extends": ["eslint:recommended"],
"rules": {
"quotes": ["error", "single"]
}
}
]
}
此新增也引入了許多額外的複雜性,因為我們必須弄清楚如何合併兩個不同配置之間的 glob 模式。最終結果是,overrides
配置內的 extends
將使用 AND 運算子來合併 files
和 excludedFiles
。如果您不確定這到底是什麼意思,您並不孤單。即使對我們來說也很混亂。
簡化的必要性
在 2019 年的新年左右,我越來越擔心 eslintrc 系統的複雜性。我們收到越來越多關於載入配置檔案相關的模糊錯誤訊息,這些檔案找不到其他配置檔案或外掛程式。此外,團隊也集體害怕觸碰任何與配置系統有關的東西。沒有人真正理解計算任何給定檔案的最終配置的所有不同排列組合。我們陷入了許多軟體專案的陷阱:我們不斷新增新功能,卻沒有退後一步來整體看待問題(和解決方案)。這導致了我們程式碼庫中幾乎無法維護的部分。
正是在這個時候,我做了一個思想實驗:如果我今天從頭開始,並了解我現在對 ESLint 的所有了解,配置系統會是什麼樣子?隨後發生的是 ESLint 歷史上最具爭議的RFC 提案。當時,團隊幾乎平均分為兩派,一派希望拋棄 eslintrc 並從頭開始,另一派則認為 eslintrc 可以透過更多迭代來挽救。最終,經過 18 個月的修訂和辯論,我們決定是時候著手建立一個完全新的配置系統,並以當今的現實為基礎。
未來的道路
現在是 2022 年,我們終於在 v8.21.0 中發佈了新配置系統的第一個實作。我們暱稱為「扁平配置」的新系統,旨在讓現有的 ESLint 使用者感到熟悉,同時大幅簡化配置檔案的設定過程。由於我們仍在努力解決錯誤並收集回饋,因此扁平配置目前還無法透過 CLI 使用,但使用 API 的開發人員可以使用它。我將在本系列的第二部分中討論扁平配置的設計。