lint 工具用來檢查編程錯誤,最初是從 C 語言中發展起來的。在 C 語言最初時期,編譯器沒法捕獲一些常見的編程錯誤,所以開發出了一個叫作 lint
的輔助程序,經過掃描源文件來查找問題。javascript
當咱們在 linting 的時候咱們到底在幹什麼?實際上,最終目標是但願代碼更加健壯,而且不論團隊有多少成員,代碼就像同一我的寫出來的同樣,加強可讀性強。css
能夠將衆多 linters 的檢查目標大體分爲三類:html
下圖展現了 JavaScript linters 的進化史:前端
2002 年由 Douglas Crockford 建立,用來進行 JavaScript 語法檢查和校驗。JSLint 定義了一個比 ECMAScript 編程語言標準更爲嚴格的子集,是一種更高的標準。JSLint 徹底是用 JavaScript 編寫的。vue
JSLint 接收 JavaScript 源代碼並對其進行掃描。若是發現問題,它將返回一條消息來描述問題以及源代碼中的大概位置。這些問題多數時候是語法錯誤,但不全是語法錯誤,也多是代碼風格和結構的問題。它不能證實程序是正確的,只是提供了一個方式來幫助發現問題。JSLint 更加關心代碼質量,所以即便瀏覽器能夠正常運行的代碼,JSLint 也可能不會經過。使用 JSLint 就意味着要欣然接受它全部的建議。java
JSLint 能夠對 JavaScript 源代碼或 JSON 文本進行操做。 JSLint 將會承認 ES6 的一部分優秀的特性,例如 let
、 const
等等。react
2010年基於 JSLint 誕生了 JSHint ,它主要解決了 JSLint 過於獨斷的問題,提供了一些配置以及添加一些 rules 。相較之下更友好,也更容易配置,因此很快就發展了起來,也獲得了衆多 IDE 和編輯器的支持。git
JSHint 掃描用 JavaScript 編寫的程序,並報告常見的錯誤和潛在的錯誤。 潛在的問題多是語法錯誤、因爲隱式類型轉換致使的錯誤、變量泄漏等。能夠經過指定任意數量的 linting 選項或在源代碼中聲明指令來控制 JSHint 的行爲。es6
JSHint 附帶了一組默認的警告,但這些也是可配置的。能夠在配置文件中指定要打開或關閉的 JSHint 選項。 例如,如下文件將啓用有關未定義和未使用的變量的警告,並告知 JSHint 一個名爲 MY_GLOBAL 的全局變量。github
{
"undef": true,
"unused": true,
"globals": {
"MY_GLOBAL": true
}
}
複製代碼
可是,因爲它是基於 JSLint 開發的,天然原有的一些問題它也繼承下來了,好比不易擴展,不容易直接根據報錯定位到具體的規則配置等。
2013年,Nicholas C. Zakas 建立了 ESLint ,提供了更好的 es6 支持,以及更多的 rules ,尤爲是一些代碼風格方面的,以及一個靈活的插件系統,可讓開發者建立本身的 rules ,同時能夠方便的根據報錯定位到具體的規則配置。
規則的錯誤等級分爲三級,能夠更加細粒度地控制如何應用規則:
"off"
或 0
- 關閉此條規則檢查"warn"
或 1
- 警告,不會影響 exit code"error"
或 2
- 錯誤,exit code 爲 1默認狀況下全部規則都是關閉的,"extends": "eslint:recommended"
會打開全部有「√」標記的規則,這些規則只跟着主版本更新,也能夠在 npm 中查找以 eslint-config
開頭的共享配置,經過 extends
配置項來添加。
ESLint 默認使用 Espree 做爲 JavaScript 解析器,能夠在 parser
配置項中更改解析器。解析器會將源代碼解析成抽象語法樹 AST (Abstract Syntax Tree),而後插件會根據這個 AST 來建立一些稱爲 lint rules 的斷言,來描述代碼應該是怎樣的。
用來檢查 TypeScript 的,可是2019年已經廢棄了,如今使用的是ESLint,配合 typescript-eslint
。TypeScript 團隊也宣佈將 TypeScript 代碼庫從 TSLint 遷移到 typescript-eslint
。
ESLint 使用一個 parser 將 source code 轉成抽象語法樹 Abstract Syntax Tree (AST) 的數據格式,而後插件根據這個 AST 來進行 lint rules 的檢查。
TypeScript 是 JavaScript 的靜態代碼分析器,在基礎的 JavaScript 上添加了一些額外的語法。TypeScript 使用一個 parser 將 source code 轉成 AST ,而後 TypeScript Compiler 的其餘部分使用這個 AST 來執行其餘操做,例如給出類型檢查後的問題反饋等等。
然而,ESLint 和 TypeScript 使用的是不一樣格式的 AST ,這就是 typescript-eslint
這個項目存在的主要緣由。typescript-eslint
就是爲了可以一塊兒使用 ESLint 和 TypeScript 。 TSLint 使用的就是 TypeScript AST 格式,其優勢是不須要一個調和 AST 格式之間差別的工具,可是主要缺點是 TSLint 沒法重用 JavaScript 生態中圍繞 linting 已經作好的工做,而是從頭開始從新實現全部的功能,從規則到自動修復功能等等。所以,TypeScript AST 不兼容 ESLint 用戶寫成並使用的 1000 多條規則。
因爲TypeScript 是 JavaScript 的超集,它包含了全部 JavaScript 語法之外,還額外添加了一些語法,例如:
var x: number = 1;
複製代碼
當 TypeScript Compiler 解析這段代碼生成 TypeScript AST 時,: number
語法也會出如今語法樹中,ESLint 不借助其餘工具是沒法理解的。但 ESLint 在設計時就考慮到了這些用例。ESLint 不只僅是一個庫,而是由許多重要的移動部件組成。其中一個就是 parser 。ESLint 有一個內置的 parser 叫作 espree
,若是想支持非標準的 JavaScript 語法,只須要提供另一個 parser 給 ESLint ,它須要將 TypeScript source code 解析爲 ESLint 能夠兼容的 AST 。 @typescript-eslint/parser
就是這樣一個自定義的 ESLint parser 的實現。流程以下:
@typescript-eslint/parser
)。@typescript-eslint/parser
處理全部特定於 ESLint 的配置,而後調用 @typescript-eslint/typescript-estree
。這個包只用來將 TypeScript source code 轉爲一個適當的 AST 。@typescript-eslint/typescript-estree
經過調用 TypeScript Compiler 將源代碼生成一個 TypeScript AST ,而後將這個 AST 轉換爲 ESLint 須要的格式。這種 AST 格式不只僅用於 ESLint,還有更普遍的用途。它有本身的規範,也就是 ESTree 。@typescript-eslint/typescript-estree
還被其餘工具重用,例如 Prettier 的 TypeScript 使用。TypeScript 和 ESLint 有相似的目標,所以可能出現 TypeScript 解決的一些問題本來是依賴 ESLint 解決的,二者可能會不兼容,最佳的解決方式是禁用相關的 ESLint 規則,轉而交由 TypeScript Compiler 。
因爲 TypeScript 是 JavaScript 的超集,即便 AST 進行了轉換,最終的 AST 可能還會包含一部分讓 ESLint 沒法理解的部分,因此有些 ESLint rules 可能沒法正常工做。有幾種解決方案:要麼解決 ESLint rules 的兼容性,要麼使用另外的規則,即 @typescript-eslint/eslint-plugin
。
module.exports = {
root: true,
parser: '@typescript-eslint/parser',
plugins: [
'@typescript-eslint',
],
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
],
};
複製代碼
Babel 如今支持解析 TypeScript source code 可是不進行類型檢查。這是使用 TypeScript Compiler 的一個替代方法。經過插件,它一樣也能夠支持許多其餘 TypeScript Compiler 不支持的語法。
typescript-eslint
是由 TypeScript Compiler 提供支持的。babel-eslint
支持了一些 TypeScript 自己不支持的額外的語法,可是 typescript-eslint 利用類型信息能夠支持建立 rules ,而這是 babel 作不到的,由於 babel 沒有類型檢查。由於它們是由不一樣的底層工具驅動的獨立項目,因此目前不打算將它們一塊兒使用。
用來檢查樣式,幫助避免錯誤和強制代碼風格。能夠理解最新的 CSS 語法,從 HTML 、 markdown 及 CSS-in-JS 對象和模板中提取內聯的樣式,能夠解析類 CSS 語法,如 SCSS 、 Sass 、 Less 和 SugarSS 。支持插件,支持自定義規則。能夠自動修復大多數違反代碼風格的問題。而且是徹底可配置的,經過在根目錄添加配置文件 .stylelintrc.json
來按需配置。
commitlint 用來檢查 commit message ,幫助團隊遵照 commit 約定,同一代碼提交風格。支持經過 npm 安裝已有的配置,或經過配置文件定義配置。使用 husky 來添加 git hooks :
// package.json
{
"husky": {
"hooks": {
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
}
}
}
複製代碼
commit-msg
鉤子會在一個新的 commit 建立時執行,經過 -E|--env
傳遞 husky 的 HUSKY_GIT_PARAMS
到 commitlint
,並將其定向到相關的編輯文件。
目前主流的代碼風格主要有 Airbnb JavaScript Style Guide 、 Google JavaScript Style Guide 以及 JavaScript Standard 。
standard --fix
便可自動格式化代碼,還能夠進行代碼檢查。對應的 ESLint 配置:eslint-config-standard目前最主流的是 Prettier ,它是一個獨斷的代碼格式化工具,專一於 style issues 。經過解析代碼生成 AST ,而後再按照本身的規則從新輸出格式化後的代碼。Prettier 執行的時機能夠是在編輯器保存時、在 pre-commit hook 中或使用 CLI 工具在命令行中執行,以確保代碼風格的一致。
因爲歷史緣由,Prettier 仍然有一小部分選項,但官方更加推崇更少的配置項,由於其初衷就是終止團隊關於代碼風格的爭論。這些選項一部分是初期添加的,一部分是需求增大致使的,還有一部分則與兼容性有關。選項越多,越有可能在團隊內部引起爭論。
Prettier 僅僅對 style issues 起做用。Prettier fork 了 recast 項目,並在內部使用了 Philip Wadler 提出的算法,該算法考慮了最大行寬(line width),最大行寬影響了代碼的佈局和包裝代碼,因此決定了最終輸出的代碼格式,而這是其餘現有的代碼格式化工具所欠缺的。例如,即使 eslint 會給出超出設定的最大行寬的警告,也沒法自動修復。例如以下代碼:
foo(reallyLongArg(), omgSoManyParameters(), IShouldRefactorThis(), isThereSeriouslyAnotherOne());
複製代碼
最終想要的效果多是這樣:
foo(
reallyLongArg(),
omgSoManyParameters(),
IShouldRefactorThis(),
isThereSeriouslyAnotherOne()
);
複製代碼
Wadler 算法是一個簡單的基於約束的代碼佈局系統。它「測量」代碼,若是代碼跨越了最大的行寬,就會中斷它。Prettier 禁止全部自定義樣式,方法是將源代碼解析成 AST 後,使用本身的規則同時考慮最大行寬,並在必要時包裝代碼,從新輸出格式化的代碼。
linters 有兩類 rules :
Prettier 承擔的是 Formatting rules 的工做,是一個「全自動」的風格指南。 所以,可使用 Prettier 來進行代碼風格的格式化,使用 linters 來檢查 bugs!
因爲 linters 一般會包含樣式相關的規則,使用Prettier時,大多數樣式規則都是沒必要要的,並且更糟糕的是,它們可能與 Prettier 衝突!所以能夠將Prettier用於代碼格式問題,將linter用於代碼質量問題。
對於 ESLint ,能夠安裝 eslint-config-prettier ,來關閉全部不須要的或者可能會跟 Prettier 衝突的 ESLint rules。 stylelint 同理可使用 stylelint-config-prettier 。
linter + code style + code formatter 的組合:ESLint + Airbnb + Prettier 。
具體作法:
yarn add eslint --dev
複製代碼
yarn run eslint --init
複製代碼
配置文件:
// .eslintrc.js
module.exports = {
"env": {
"browser": true,
"es2021": true
},
"extends": [
"plugin:react/recommended",
"airbnb",
"plugin:@typescript-eslint/recommended"
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaFeatures": {
"jsx": true
},
"ecmaVersion": 12,
"sourceType": "module"
},
"plugins": [
"react",
"@typescript-eslint"
],
"rules": {
}
};
複製代碼
yarn add --dev --exact prettier
# 若是須要配置文件(若是須要忽略文件,則建立一個 .prettierignore 文件)
echo {}> .prettierrc.json
複製代碼
最好能在保存時就自動格式化代碼,讓開發者無需關注代碼格式,強迫症必備。
以 WebStorm 爲例:對於 2020.1 及以上的版本:Preferences | Languages & Frameworks | JavaScript | Prettier 而後啓用選項 Run on save for files
。
如今保存時就能夠自動格式化代碼了。
須要安裝 eslint-config-prettier ,這個包會關閉全部不須要的或可能和 Prettier 衝突的ESLint rules。
yarn add eslint-config-prettier --dev
複製代碼
而後將其添加到 ESLint 配置文件中的 extends
配置項的最後,好讓它可以覆蓋其餘配置。這裏的 prettier
除了關閉一些核心的 ESLint rules,還會關閉如下插件的部分 rules:
因爲這個配置只會關閉 rules,因此跟其餘配置一塊兒使用纔有意義,注意要放到最後:
{
"extends": [
"plugin:react/recommended",
"airbnb",
"plugin:@typescript-eslint/recommended",
"prettier"
]
}
複製代碼
注意,還可使用 eslint-plugin-prettier 插件把 Prettier 當作一個 linter rule 來運行,但這不是官方推薦的作法,主要有如下幾個劣勢:
保險起見,最好在代碼提交前也格式化一下。也就是能夠在 pre-commit hook 裏運行 Prettier 。
執行如下命令,會依賴 package.json 的 devDependencies
中的代碼檢查工具來自動安裝並配置 husky 和 lint-staged ,所以須要確保事先已經安裝了 Prettier 和 ESlint 。
npx mrm lint-staged
複製代碼
package.json
會自動添加相關配置(默認生成的配置針對 .js
文件的,能夠按需改成 .ts
或 .tsx
等),這樣在每次提交前,都會經過 lint-staged 和 husky 運行 ESLint 和 Prettier 。
{
"devDependencies": {
"husky": ">=4",
"lint-staged": ">=10",
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"*.js": "eslint --cache --fix",
"*.{js,css,md}": "prettier --write"
}
}
複製代碼
關於 lint-staged 的做用:在代碼提交以前 linting 纔有意義,這樣能夠確保不會將糟糕的代碼上傳到倉庫中,以及強制統一風格。可是對整個項目運行一個 lint 過程很慢,而 linting 結果多是可有可無的。因此,只須要 lint 即將要提交的文件。 lint-staged 包含一個腳本,該腳本會運行任何shell任務,將staged files 做爲參數,經過一個特定的 glob pattern 進行過濾。
husky(哈士奇)主要做用是添加 git hook(git 在特定的重要動做發生時觸發自定義腳本),用來阻止很差的 git commit
、git push
等等。