對暫存的 git 檔案運行 linter 並且不讓 ?滑入您的程式碼庫!
npm install --save-dev lint-staged # requires further setup
$ git commit
✔ Preparing lint-staged...
❯ Running tasks for staged files...
❯ packages/frontend/.lintstagedrc.json — 1 file
↓ *.js — no files [SKIPPED]
❯ *.{json,md} — 1 file
⠹ prettier --write
↓ packages/backend/.lintstagedrc.json — 2 files
❯ *.js — 2 files
⠼ eslint --fix
↓ *.{json,md} — no files [SKIPPED]
◼ Applying modifications from tasks...
◼ Cleaning up temporary files...
在提交程式碼之前運行 Linting 更有意義。透過這樣做,您可以確保沒有錯誤進入儲存庫並強制執行程式碼風格。但在整個專案上運行 lint 流程的速度很慢,而且 lint 結果可能無關緊要。最終您只想檢查將要提交的文件。
此專案包含一個腳本,該腳本將執行任意 shell 任務,並以暫存檔案清單作為參數,並按指定的 glob 模式進行過濾。
dotnet-format
和lint-staged
來美化你的 CSharp如果您已經寫過,請提交帶有連結的 PR!
要按照建議的方式安裝lint-staged ,您需要:
npm install --save-dev lint-staged
pre-commit
git hook 以執行lint-staged{ "*.js": "eslint" }
為所有暫存的 JS 檔案執行 ESLint不要忘記提交對package.json
和.husky
的更改,以便與您的團隊分享此設定!
現在更改一些文件, git add
或git add --patch
將其中一些文件添加到您的提交中,並嘗試git commit
它們。
有關更多信息,請參閱範例和配置。
請參閱版本。
v15.0.0
起, lint-staged不再支援 Node.js 18.12.0
。 v14.0.0
起, lint-staged不再支援 Node.js 16.14.0
。 v13.0.0
起, lint-staged 14.13.1
支援16.0.0
12。v13.3.0
被錯誤發布,包括版本v14.0.0
的程式碼。這意味著v14
的重大更改也包含在最後發布的v13
版本v13.3.0
中v12.0.0
lint-staged是純 ESM 模組,請確保您的 Node.js 版本至少為12.20.0
、 14.13.1
或16.0.0
。從官方 Node.js 文件網站閱讀有關 ESM 模組的更多資訊。 v10.0.0
開始,對原始暫存檔案的任何新修改都會自動新增至提交。如果您的任務之前包含git add
步驟,請將其刪除。自動行為可確保減少競爭條件,因為嘗試同時執行多個 git 操作通常會導致錯誤。v10.0.0
開始,lint-staged 使用 git stashes 來提高速度並在運行時提供備份。由於 git stash 至少需要一次初始提交,因此您不應該在空白儲存庫中執行 lint-staged。v10.0.0
開始,lint-staged 需要 Node.js 版本 10.13.0 或更高版本。v10.0.0
開始,如果 linter 任務撤銷所有暫存的更改,lint-staged 將中止提交。若要允許建立空提交,請使用--allow-empty
選項。 ❯ npx lint-staged --help
Usage: lint-staged [options]
Options:
-V, --version output the version number
--allow-empty allow empty commits when tasks revert all staged changes (default: false)
-p, --concurrent <number|boolean> the number of tasks to run concurrently, or false for serial (default: true)
-c, --config [path] path to configuration file, or - to read from stdin
--cwd [path] run all tasks in specific directory, instead of the current
-d, --debug print additional debug information (default: false)
--diff [string] override the default "--staged" flag of "git diff" to get list of files. Implies
"--no-stash".
--diff-filter [string] override the default "--diff-filter=ACMR" flag of "git diff" to get list of files
--max-arg-length [number] maximum length of the command-line argument string (default: 0)
--no-stash disable the backup stash, and do not revert in case of errors. Implies
"--no-hide-partially-staged".
--no-hide-partially-staged disable hiding unstaged changes from partially staged files
-q, --quiet disable lint-staged’s own console output (default: false)
-r, --relative pass relative filepaths to tasks (default: false)
-x, --shell [path] skip parsing of tasks for better shell support (default: false)
-v, --verbose show task output even when tasks succeed; by default only failed output is shown
(default: false)
-h, --help display help for command
--allow-empty
:預設情況下,當 linter 任務撤銷所有暫存變更時,lint-staged 將出現錯誤併中止提交。使用此標誌允許建立空 git 提交。--concurrent [number|boolean]
:控制 lint-staged 執行的任務的並發性。注意:這不會影響子任務的並發性(它們將始終按順序運行)。可能的值為:false
:串列運行所有任務true
(預設):無限並發。並行運行盡可能多的任務。{number}
:並行運行指定數量的任務,其中1
相當於false
。--config [path]
:手動指定設定檔或 npm 套件名稱的路徑。注意:使用時,lint-staged 不會執行設定檔搜索,如果找不到指定的文件,會列印錯誤。如果提供“-”作為文件名,則將從標準輸入讀取配置,從而允許在配置中進行管道傳輸,如cat my-config.json | npx lint-staged --config -
.--cwd [path]
:預設任務在目前工作目錄中執行。使用--cwd some/directory
來覆蓋它。此路徑可以是絕對路徑,也可以是相對於目前工作目錄的路徑。--debug
:在偵錯模式下運作。設定後,它會執行以下操作:$DEBUG
lint-staged*
進位檔案位置等的附加資訊。listr2
使用verbose
渲染器;這會導致串列、無色的輸出到終端,而不是預設的(美化的、動態的)輸出。 (也可以透過設定TERM=dumb
或NODE_ENV=test
環境變數來啟動verbose
渲染器)--diff
:預設情況下,linter 會根據 git 中暫存的所有檔案進行過濾,這些檔案是從git diff --staged
產生的。此選項可讓您使用任意修訂來覆蓋--staged
標誌。例如,要取得兩個分支之間已更改檔案的列表,請使用--diff="branch1...branch2"
。您還可以閱讀有關 git diff 和 gitrevisions 的更多資訊。此選項也意味著--no-stash
。--diff-filter
:預設僅包含新增、複製、修改或重新命名的檔案。使用此標誌可以用其他內容覆寫預設ACMR
值:新增( A
)、複製( C
)、刪除( D
)、修改( M
)、重新命名( R
)、類型變更( T
)、未合併( U
)、未知( X
),或配對損壞( B
)。另請參閱 --diff-filter 的git diff
文件。--max-arg-length
:當偵測到目前 shell 無法處理長命令(很多檔案)時,它會自動分割成多個區塊。使用此標誌可以覆蓋生成的命令字串的最大長度。--no-stash
:預設情況下,在運行任務之前將建立備份存儲,並且在發生錯誤時將恢復所有任務修改。此選項將停用建立存儲,而是在中止提交時將所有修改保留在索引中。可以使用--stash
重新啟用。此選項也意味著--no-hide-partially-staged
。--no-hide-partially-staged
:預設情況下,將隱藏部分暫存檔案的未暫存變更。此選項將停用此行為並包含部分暫存檔案中的所有未暫存變更。可以使用--hide-partially-staged
重新啟用--quiet
:禁止所有 CLI 輸出,任務除外。--relative
:將相對於process.cwd()
( lint-staged
運行的位置)的檔案路徑傳遞給任務。預設為false
。--shell
:預設情況下,將解析 linter 指令以提高速度和安全性。這會產生副作用,即常規 shell 腳本可能無法如預期運作。您可以使用此選項跳過命令解析。若要使用特定的 shell,請使用類似--shell "/bin/bash"
路徑。--verbose
:即使任務成功也顯示任務輸出。預設僅顯示失敗的輸出。 Lint-staged可以透過多種方式進行設定:
package.json
或package.yaml
中的lint-staged
對象.lintstagedrc
文件,或者您可以明確使用文件副檔名:.lintstagedrc.json
.lintstagedrc.yaml
.lintstagedrc.yml
.lintstagedrc.mjs
或lint-staged.config.mjs
文件export default { ... }
.lintstagedrc.cjs
或lint-staged.config.cjs
文件module.exports = { ... }
lint-staged.config.js
或.lintstagedrc.js
,取決於專案的package.json是否包含"type": "module"
選項。--config
或-c
標誌傳遞設定文件配置應該是一個對象,其中每個值都是要執行的命令,其鍵是用於該命令的全域模式。該套件使用 micromatch 來表示 glob 模式。 JavaScript 檔案也可以將進階配置匯出為函數。有關詳細信息,請參閱使用 JS 設定檔。
您也可以將多個設定檔放置在專案內的不同目錄中。對於給定的暫存文件,將始終使用最接近的設定檔。請參閱“如何在多包 monorepo 中使用lint-staged
?”了解更多和範例。
package.json
範例: {
"lint-staged" : {
"*" : " your-cmd "
}
}
.lintstagedrc
範例{
"*" : " your-cmd "
}
此配置將使用作為參數傳遞的目前暫存檔案清單來執行your-cmd
。
因此,考慮到您執行了git add file1.ext file2.ext
,lint-staged 將執行以下命令:
your-cmd file1.ext file2.ext
預設情況下, lint-staged將同時執行配置的任務。這意味著對於每個 glob,所有命令都將同時啟動。透過以下配置, eslint
和prettier
將同時運行:
{
"*.ts" : " eslint " ,
"*.md" : " prettier --list-different "
}
這通常不是問題,因為 glob 不重疊,並且命令不會更改文件,而僅報告可能的錯誤(中止 git 提交)。如果要對同一組文件執行多個命令,可以使用數組語法來確保命令按順序運行。在以下範例中, prettier
將為兩個 glob 運行,此外eslint
還將為其後面的*.ts
檔案運行。兩組命令(對於每個 glob)仍然同時啟動(但不重疊)。
{
"*.ts" : [ " prettier --list-different " , " eslint " ],
"*.md" : " prettier --list-different "
}
當配置的 glob 重疊以及任務對文件進行編輯時要格外注意。例如,在此配置中prettier
和eslint
可能會嘗試同時更改同一個*.ts
文件,從而導致競爭條件:
{
"*" : " prettier --write " ,
"*.ts" : " eslint --fix "
}
您可以使用否定模式和數組語法來解決它:
{
"!(*.ts)" : " prettier --write " ,
"*.ts" : [ " eslint --fix " , " prettier --write " ]
}
任務對文件進行編輯並且全域匹配多個文件但不重疊的另一個範例:
{
"*.css" : [ " stylelint --fix " , " prettier --write " ],
"*.{js,jsx}" : [ " eslint --fix " , " prettier --write " ],
"!(*.css|*.js|*.jsx)" : [ " prettier --write " ]
}
或者,如有必要,您可以使用--concurrent <number>
限制並發性,或使用--concurrent false
完全停用它。
Linter 指令適用於所有由glob 模式定義的暫存檔案的子集。 lint-staged 使用 micromatch 來符合具有以下規則的檔案:
/
),則將啟用 micromatch 的matchBase
選項,因此無論目錄為何,glob 都會符合檔案的基本名稱:"*.js"
將匹配所有 JS 文件,例如/test.js
和/foo/bar/test.js
"!(*test).js"
將匹配所有 JS 文件,除了以test.js
結尾的文件,因此foo.js
但不是foo.test.js
"!(*.css|*.js)"
將符合 CSS 和 JS 檔案以外的所有文件/
),它也會匹配路徑:"./*.js"
將匹配 git repo 根目錄中的所有 JS 文件,因此/test.js
但不匹配/foo/bar/test.js
"foo/**/*.js"
將匹配/foo
目錄中的所有 JS 文件,因此/foo/bar/test.js
但不匹配/test.js
匹配時,lint-staged 會執行以下操作
注意: lint-staged
會將絕對路徑傳遞給 linter,以避免在不同工作目錄中執行時出現任何混淆(即,當您的.git
目錄與package.json
目錄不同時)。
另請參閱如何在多包 monorepo 中使用lint-staged
?
lint-staged
的概念是在 git 中暫存的檔案上執行配置的 linter 任務(或其他任務)。 lint-staged
始終將所有暫存文件的清單傳遞給任務,並忽略任務本身中應配置的任何文件。
考慮一個使用prettier
來保持所有文件的代碼格式一致的項目。該專案還將縮小的第 3 方供應商庫儲存在vendor/
目錄中。為了防止prettier
在這些文件上拋出錯誤,供應商目錄應新增至 prettier 的忽略配置( .prettierignore
檔案)。運行npx prettier .
將忽略整個供應商目錄,不會拋出任何錯誤。當lint-staged
新增至專案並配置為執行 prettier 時,供應商目錄中的所有修改和暫存檔案都將被 prettier 忽略,即使它接收它們作為輸入。
在進階場景中,無法將 linter 任務本身配置為忽略文件,但lint-staged
仍應忽略某些暫存文件,可以使用函數語法在將文件路徑傳遞給任務之前過濾文件路徑。請參閱範例:忽略符合中的檔案。
支援透過npm
本地或全域安裝的任何可執行檔以及 $PATH 中的任何可執行檔。
不鼓勵使用全域安裝的腳本,因為 lint-staged 可能不適用於未安裝它的人。
lint-staged
使用 execa 來定位本機安裝的腳本。所以在你的.lintstagedrc
中你可以寫:
{
"*.js" : " eslint --fix "
}
當您暫存檔案file-1.js
、 file-2.js
和README.md
時,這將導致lint 暫存執行eslint --fix file-1.js file-2.js
。
將參數傳遞給命令,並用空格分隔,就像在 shell 中一樣。請參閱下面的範例。
您可以在每個 glob 上依序執行多個命令。為此,請傳遞一組命令而不是單一命令。這對於運行eslint --fix
或stylefmt
等自動格式化工具很有用,但可用於任何任意序列。
例如:
{
"*.js" : [ " eslint " , " prettier --write " ]
}
將執行eslint
,如果它以0
程式碼退出,它將在所有暫存的*.js
檔案上執行prettier --write
。
當您暫存檔案file-1.js
、 file-2.js
和README.md
時,這將導致lint-staged執行eslint file-1.js file-2.js
,如果通過,則prettier --write file-1.js file-2.js
。
用 JavaScript 編寫設定檔是配置 lint-staged 的最強大方法( lint-staged.config.js
,類似,或透過--config
傳遞)。從設定檔中,您可以匯出單一函數或物件。
如果exports
值是一個函數,它將接收所有暫存檔案名稱的陣列。然後,您可以為檔案建立自己的匹配器並傳回命令字串或命令字串陣列。這些字串被認為是完整的,並且如果需要的話應該包括檔案名稱參數。
如果exports
值是一個對象,則其鍵應該是全域匹配的(就像普通的非 js 配置格式一樣)。這些值可以是正常配置中的值,也可以是如上所述的單獨函數中的值。匯出物件中的函數不會接收所有符合的文件,而是僅接收與對應的 glob key 相符的暫存文件。
總而言之,預設情況下lint-staged會自動將匹配的暫存檔案清單新增至您的命令中,但是當使用 JS 函數建置命令時,需要手動執行此操作。例如:
export default {
'*.js' : ( stagedFiles ) => [ `eslint .` , `prettier --write ${ stagedFiles . join ( ' ' ) } ` ] ,
}
這將導致lint-staged首先運行eslint .
(符合所有檔案),如果通過,當您暫存檔案file-1.js
、 file-2.js
和README.md
時, prettier --write file-1.js file-2.js
。
此函數也可以是異步的:
( filenames : string [ ] ) => string | string [ ] | Promise < string | string [ ] >
// lint-staged.config.js
import micromatch from 'micromatch'
export default ( allStagedFiles ) => {
const shFiles = micromatch ( allStagedFiles , [ '**/src/**/*.sh' ] )
if ( shFiles . length ) {
return `printf '%sn' "Script files aren't allowed in src directory" >&2`
}
const codeFiles = micromatch ( allStagedFiles , [ '**/*.js' , '**/*.ts' ] )
const docFiles = micromatch ( allStagedFiles , [ '**/*.md' ] )
return [ `eslint ${ codeFiles . join ( ' ' ) } ` , `mdl ${ docFiles . join ( ' ' ) } ` ]
}
// .lintstagedrc.js
export default {
'**/*.js?(x)' : ( filenames ) => filenames . map ( ( filename ) => `prettier --write ' ${ filename } '` ) ,
}
tsc
,但不傳遞任何檔案名稱參數 // lint-staged.config.js
export default {
'**/*.ts?(x)' : ( ) => 'tsc -p tsconfig.json --noEmit' ,
}
// .lintstagedrc.js
export default {
'**/*.js?(x)' : ( filenames ) =>
filenames . length > 10 ? 'eslint .' : `eslint ${ filenames . join ( ' ' ) } ` ,
}
如果您的用例是這樣的話,最好使用基於函數的配置(請參閱上文)。
// lint-staged.config.js
import micromatch from 'micromatch'
export default {
'*' : ( allFiles ) => {
const codeFiles = micromatch ( allFiles , [ '**/*.js' , '**/*.ts' ] )
const docFiles = micromatch ( allFiles , [ '**/*.md' ] )
return [ `eslint ${ codeFiles . join ( ' ' ) } ` , `mdl ${ docFiles . join ( ' ' ) } ` ]
} ,
}
如果你因為某些原因想忽略全域匹配中的文件,你可以使用micromatch.not()
:
// lint-staged.config.js
import micromatch from 'micromatch'
export default {
'*.js' : ( files ) => {
// from `files` filter those _NOT_ matching `*test.js`
const match = micromatch . not ( files , '*test.js' )
return `eslint ${ match . join ( ' ' ) } `
} ,
}
請注意,在大多數情況下,glob 可以達到相同的效果。對於上面的範例,匹配的 glob 將為!(*test).js
。
import path from 'path'
export default {
'*.ts' : ( absolutePaths ) => {
const cwd = process . cwd ( )
const relativePaths = absolutePaths . map ( ( file ) => path . relative ( cwd , file ) )
return `ng lint myProjectName --files ${ relativePaths . join ( ' ' ) } `
} ,
}
Prettier、ESLint/TSLint 或 stylelint 等工具可以透過執行prettier --write
/ eslint --fix
/ tslint --fix
/ stylelint --fix
根據適當的配置重新格式化您的程式碼。只要沒有錯誤,Lint-staged 就會自動新增提交的任何修改。
{
"*.js" : " prettier --write "
}
在版本 10 之前,任務必須手動包含git add
作為最後一步。此行為已整合到 lint-staged 本身中,以防止多個任務編輯相同文件的競爭條件。如果 lint-staged 在任務配置中偵測到git add
,它將在控制台中顯示警告。升級後請從您的設定中刪除git add
。
所有範例都假設您已經在package.json
檔案中設定了 lint-staged,並在自己的設定檔中設定了 husky。
{
"name" : " My project " ,
"version" : " 0.1.0 " ,
"scripts" : {
"my-custom-script" : " linter --arg1 --arg2 "
},
"lint-staged" : {}
}
在.husky/pre-commit
中
# .husky/pre-commit
npx lint-staged
注意:我們不會傳遞路徑作為跑者的參數。這很重要,因為 lint-staged 會為你做這件事。
*.js
和*.jsx
的預設參數{
"*.{js,jsx}" : " eslint "
}
--fix
自動修復程式碼樣式並新增至提交{
"*.js" : " eslint --fix "
}
這將運行eslint --fix
並自動將變更新增至提交。
如果您希望重複使用 package.json 中定義的 npm 腳本:
{
"*.js" : " npm run my-custom-script -- "
}
以下是等效的:
{
"*.js" : " linter --arg1 --arg2 "
}
Linting 指令不支援擴展環境變數的 shell 約定。若要自行啟用約定,請使用cross-env
等工具。
例如,下面是在所有.js
檔案上運行的jest
,其中NODE_ENV
變數設定為"test"
:
{
"*.js" : [ " cross-env NODE_ENV=test jest --bail --findRelatedTests " ]
}
prettier
自動修復 Prettier 支援的任何格式的程式碼樣式{
"*" : " prettier --ignore-unknown --write "
}
prettier
自動修復 JavaScript、TypeScript、Markdown、HTML 或 CSS 的程式碼樣式{
"*.{js,jsx,ts,tsx,md,html,css}" : " prettier --write "
}
{
"*.css" : " stylelint " ,
"*.scss" : " stylelint --syntax=scss "
}
{
"*.scss" : [ " postcss --config path/to/your/config --replace " , " stylelint " ]
}
{
"*.{png,jpeg,jpg,gif,svg}" : " imagemin-lint-staged "
}
imagemin-lint-staged
更多信息imagemin-lint-staged 是一個 CLI 工具,專為具有合理預設值的 lint 階段使用而設計。
有關此方法的優點,請參閱此部落格文章的更多內容。
{
"*.{js,jsx}" : " flow focus-check "
}
// .lintstagedrc.js
// See https://nextjs.org/docs/basic-features/eslint#lint-staged for details
const path = require ( 'path' )
const buildEslintCommand = ( filenames ) =>
`next lint --fix --file ${ filenames . map ( ( f ) => path . relative ( process . cwd ( ) , f ) ) . join ( ' --file ' ) } `
module . exports = {
'*.{js,jsx,ts,tsx}' : [ buildEslintCommand ] ,
}
Git 2.36.0 引入了對鉤子的更改,它們不再在原始 TTY 中運行。該問題已在 2.37.0 中修復:
https://raw.githubusercontent.com/git/git/master/Documentation/RelNotes/2.37.0.txt
- 在 Git 2.36 中,我們改進了呼叫鉤子的方式。最終用戶可見的一項變更是鉤子的輸出不再直接連接到產生該鉤子的「git」的標準輸出,這一點在發布後注意到。這個問題正在糾正中。 (稍後合併 a082345372 ab/hooks-regression-fix 來維護)。
如果更新 Git 沒有幫助,您可以嘗試手動重定向 Git 掛鉤中的輸出;例如:
# .husky/pre-commit
if sh -c " : >/dev/tty " > /dev/null 2> /dev/null ; then exec > /dev/tty 2>&1 ; fi
npx lint-staged
來源:typicode/husky#968(評論)
lint-staged
嗎?是的!
import lintStaged from 'lint-staged'
try {
const success = await lintStaged ( )
console . log ( success ? 'Linting was successful!' : 'Linting failed!' )
} catch ( e ) {
// Failed to load configuration
console . error ( e )
}
lintStaged
的參數與其 CLI 對應參數相同:
const success = await lintStaged ( {
allowEmpty : false ,
c