
當 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 中停止安裝 peer dependencies 時。在此之前,我們建議可共享配置將它們依賴的任何插件作為 peer dependencies 而不是 regular dependencies 包含在內。這是 extends
實作方式的一個怪癖:使用 require()
。
由於可共享配置是純數據的,無法直接引用 Node.js 依賴項,因此 require()
不會自動將直接依賴項載入到路徑中,以供 ESLint 解析它們。另一方面,peer dependencies 通過僅使用 require()
完美運作,因為它們安裝在正常套件查找運作的位置。
當 npm v3 預設停止安裝 peer dependencies 時,所有依賴於此行為的共享配置都停止正常運作。有一個 長期存在的問題 要求允許可共享配置直接使用依賴項,但 eslintrc 的架構根本不允許這樣做。我們基本上必須在 ESLint 內部重新創建整個 require()
功能,才能解決可共享配置的設計方式問題。我們建議可共享配置創建一個 post-install 腳本來安裝它們的 peer dependencies。絕非理想的做法。
我們添加了 --resolve-plugins-relative-to
command line 選項,試圖幫助解決這個問題,但這還不夠。在我們的 Discord #help 頻道 中,最常見的求助請求與配置檔案中插件的不正確解析有關。
npm 最終在 v7 中改回預設安裝 peer dependencies,但到那時,對 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 中發布了新配置系統的第一個實作。新系統,我們暱稱為「flat config」,旨在讓現有的 ESLint 使用者感到熟悉,同時大幅簡化設定配置檔案的過程。Flat config 目前還無法通過 CLI 使用,因為我們仍在繼續修復錯誤並收集回饋,但它適用於直接使用 API 的開發人員。我將在本系列的 第 2 部分 中討論 flat config 的設計。