对暂存的 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 16。请将 Node.js 版本至少升级到18.12.0
。 v14.0.0
起, lint-staged不再支持 Node.js 14。请将 Node.js 版本至少升级到16.14.0
。 v13.0.0
起, lint-staged不再支持 Node.js 12。请将 Node.js 版本至少升级到14.13.1
或16.0.0
及以上。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