Execute linters em arquivos git preparados e não deixe? insira sua base de código!
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 faz mais sentido quando executado antes de confirmar seu código. Ao fazer isso, você pode garantir que nenhum erro entre no repositório e impor o estilo do código. Mas executar um processo de lint em um projeto inteiro é lento e os resultados do lint podem ser irrelevantes. Em última análise, você deseja apenas lint os arquivos que serão confirmados.
Este projeto contém um script que executará tarefas shell arbitrárias com uma lista de arquivos preparados como argumento, filtrados por um padrão glob especificado.
dotnet-format
e lint-staged
Se você escreveu um, envie um PR com o link para ele!
Para instalar o lint-staged da maneira recomendada, você precisa:
npm install --save-dev lint-staged
pre-commit
para executar o lint-staged{ "*.js": "eslint" }
para executar o ESLint para todos os arquivos JS preparados Não se esqueça de confirmar as alterações em package.json
e .husky
para compartilhar esta configuração com sua equipe!
Agora altere alguns arquivos, git add
ou git add --patch
alguns deles para o seu commit e tente git commit
eles.
Veja exemplos e configuração para obter mais informações.
Consulte Lançamentos.
v15.0.0
o lint-staged não oferece mais suporte ao Node.js 16. Atualize sua versão do Node.js para pelo menos 18.12.0
. v14.0.0
o lint-staged não oferece mais suporte ao Node.js 14. Atualize sua versão do Node.js para pelo menos 16.14.0
. v13.0.0
o lint-staged não oferece mais suporte ao Node.js 12. Atualize sua versão do Node.js para pelo menos 14.13.1
ou 16.0.0
em diante.v13.3.0
foi lançada incorretamente incluindo o código da versão v14.0.0
. Isso significa que as alterações importantes da v14
também estão incluídas na v13.3.0
, a última versão v13
lançada v12.0.0
lint-staged é um módulo ESM puro, certifique-se de que sua versão do Node.js seja pelo menos 12.20.0
, 14.13.1
ou 16.0.0
. Leia mais sobre os módulos ESM no site oficial de documentação do Node.js aqui. v10.0.0
, quaisquer novas modificações nos arquivos originalmente preparados serão automaticamente adicionadas ao commit. Se sua tarefa continha anteriormente uma etapa git add
, remova-a. O comportamento automático garante que haja menos condições de corrida, pois tentar executar várias operações git ao mesmo tempo geralmente resulta em erro.v10.0.0
, o lint-staged usa git stashes para melhorar a velocidade e fornecer backups durante a execução. Como os stashes git exigem pelo menos um commit inicial, você não deve executar o lint-staged em um repositório vazio.v10.0.0
, lint-staged requer Node.js versão 10.13.0 ou posterior.v10.0.0
, lint-staged abortará o commit se as tarefas do linter desfazerem todas as alterações preparadas. Para permitir a criação de um commit vazio, use a opção --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
: por padrão, quando as tarefas do linter desfazem todas as alterações preparadas, o lint-staged sairá com um erro e abortará o commit. Use este sinalizador para permitir a criação de commits git vazios.--concurrent [number|boolean]
: Controla a simultaneidade de tarefas executadas pelo lint-staged. NOTA : Isso NÃO afeta a simultaneidade de subtarefas (elas sempre serão executadas sequencialmente). Os valores possíveis são:false
: executa todas as tarefas em sérietrue
(padrão): simultaneidade infinita . Executa tantas tarefas em paralelo quanto possível.{number}
: execute o número especificado de tarefas em paralelo, onde 1
é equivalente a false
.--config [path]
: Especifique manualmente um caminho para um arquivo de configuração ou nome de pacote npm. Nota: quando usado, lint-staged não realizará a pesquisa do arquivo de configuração e imprimirá um erro se o arquivo especificado não puder ser encontrado. Se '-' for fornecido como o nome do arquivo, a configuração será lida em stdin, permitindo a tubulação na configuração como cat my-config.json | npx lint-staged --config -
.--cwd [path]
: Por padrão, as tarefas são executadas no diretório de trabalho atual. Use o --cwd some/directory
para substituir isso. O caminho pode ser absoluto ou relativo ao diretório de trabalho atual.--debug
: Executa em modo de depuração. Quando definido, ele faz o seguinte:$DEBUG
como lint-staged*
.verbose
para listr2
; isso causa uma saída serial sem cor para o terminal, em vez da saída padrão (embelezada, dinâmica). (o renderizador verbose
também pode ser ativado definindo as variáveis de ambiente TERM=dumb
ou NODE_ENV=test
)--diff
: Por padrão, os linters são filtrados em todos os arquivos preparados no git, gerados a partir de git diff --staged
. Esta opção permite substituir o sinalizador --staged
por revisões arbitrárias. Por exemplo, para obter uma lista de arquivos alterados entre duas ramificações, use --diff="branch1...branch2"
. Você também pode ler mais sobre git diff e gitrevisions. Esta opção também implica --no-stash
.--diff-filter
: Por padrão, apenas os arquivos adicionados , copiados , modificados ou renomeados são incluídos. Use este sinalizador para substituir o valor ACMR
padrão por outra coisa: adicionado ( A
), copiado ( C
), excluído ( D
), modificado ( M
), renomeado ( R
), tipo alterado ( T
), não mesclado ( U
), desconhecido ( X
), ou emparelhamento quebrado ( B
). Veja também a documentação git diff
para --diff-filter.--max-arg-length
: comandos longos (muitos arquivos) são automaticamente divididos em vários pedaços quando detecta que o shell atual não consegue lidar com eles. Use esse sinalizador para substituir o comprimento máximo da sequência de comandos gerada.--no-stash
: Por padrão, um stash de backup será criado antes de executar as tarefas, e todas as modificações nas tarefas serão revertidas em caso de erro. Esta opção desabilitará a criação do stash e, em vez disso, deixará todas as modificações no índice ao abortar o commit. Pode ser reativado com --stash
. Esta opção também implica --no-hide-partially-staged
.--no-hide-partially-staged
: Por padrão, as alterações não preparadas de arquivos parcialmente preparados serão ocultadas. Esta opção desativará esse comportamento e incluirá todas as alterações não preparadas em arquivos parcialmente testados. Pode ser reativado com --hide-partially-staged
--quiet
: Suprime todas as saídas CLI, exceto tarefas.--relative
: passa caminhos de arquivos relativos a process.cwd()
(onde lint-staged
é executado) para tarefas. O padrão é false
.--shell
: Por padrão, os comandos do linter serão analisados para velocidade e segurança. Isso tem o efeito colateral de que os scripts de shell regulares podem não funcionar conforme o esperado. Você pode pular a análise de comandos com esta opção. Para usar um shell específico, use um caminho como --shell "/bin/bash"
.--verbose
: mostra a saída da tarefa mesmo quando as tarefas são bem-sucedidas. Por padrão, apenas a saída com falha é mostrada. Lint-staged pode ser configurado de várias maneiras:
lint-staged
em seu package.json
ou package.yaml
.lintstagedrc
no formato JSON ou YML, ou você pode ser explícito com a extensão do arquivo:.lintstagedrc.json
.lintstagedrc.yaml
.lintstagedrc.yml
.lintstagedrc.mjs
ou lint-staged.config.mjs
no formato ESMexport default { ... }
.lintstagedrc.cjs
ou lint-staged.config.cjs
no formato CommonJSmodule.exports = { ... }
lint-staged.config.js
ou .lintstagedrc.js
no formato ESM ou CommonJS, dependendo se o package.json do seu projeto contém a opção "type": "module"
ou não.--config
ou -c
A configuração deve ser um objeto onde cada valor é um comando a ser executado e sua chave é um padrão glob a ser usado para este comando. Este pacote usa micromatch para padrões glob. Os arquivos JavaScript também podem exportar configurações avançadas como uma função. Consulte Usando arquivos de configuração JS para obter mais informações.
Você também pode colocar vários arquivos de configuração em diretórios diferentes dentro de um projeto. Para um determinado arquivo testado, o arquivo de configuração mais próximo sempre será usado. Consulte "Como usar lint-staged
em um monorepo de vários pacotes?" para mais informações e um exemplo.
package.json
: {
"lint-staged" : {
"*" : " your-cmd "
}
}
.lintstagedrc
{
"*" : " your-cmd "
}
Esta configuração executará your-cmd
com a lista de arquivos atualmente preparados passados como argumentos.
Então, considerando que você fez git add file1.ext file2.ext
, lint-staged executará o seguinte comando:
your-cmd file1.ext file2.ext
Por padrão, o lint-staged executará tarefas configuradas simultaneamente. Isso significa que para cada glob, todos os comandos serão iniciados ao mesmo tempo. Com a seguinte configuração, eslint
e prettier
serão executados ao mesmo tempo:
{
"*.ts" : " eslint " ,
"*.md" : " prettier --list-different "
}
Isso normalmente não é um problema, pois os globs não se sobrepõem e os comandos não fazem alterações nos arquivos, mas apenas relatam possíveis erros (abortando o commit do git). Se quiser executar vários comandos para o mesmo conjunto de arquivos, você pode usar a sintaxe de array para garantir que os comandos sejam executados em ordem. No exemplo a seguir, prettier
será executado para ambos os globs e, além disso, eslint
será executado para arquivos *.ts
depois dele. Ambos os conjuntos de comandos (para cada glob) ainda são iniciados ao mesmo tempo (mas não se sobrepõem).
{
"*.ts" : [ " prettier --list-different " , " eslint " ],
"*.md" : " prettier --list-different "
}
Preste atenção extra quando os globos configurados se sobrepõem e as tarefas fazem edições nos arquivos. Por exemplo, nesta configuração, prettier
e eslint
podem tentar fazer alterações no mesmo arquivo *.ts
ao mesmo tempo, causando uma condição de corrida :
{
"*" : " prettier --write " ,
"*.ts" : " eslint --fix "
}
Você pode resolver isso usando o padrão de negação e a sintaxe do array:
{
"!(*.ts)" : " prettier --write " ,
"*.ts" : [ " eslint --fix " , " prettier --write " ]
}
Outro exemplo em que as tarefas fazem edições em arquivos e os globs correspondem a vários arquivos, mas não se sobrepõem:
{
"*.css" : [ " stylelint --fix " , " prettier --write " ],
"*.{js,jsx}" : [ " eslint --fix " , " prettier --write " ],
"!(*.css|*.js|*.jsx)" : [ " prettier --write " ]
}
Ou, se necessário, você pode limitar a simultaneidade usando --concurrent <number>
ou desativá-la totalmente com --concurrent false
.
Os comandos Linter funcionam em um subconjunto de todos os arquivos preparados, definidos por um padrão glob . lint-staged usa micromatch para combinar arquivos com as seguintes regras:
/
), a opção matchBase
do micromatch será habilitada, então os globs correspondem ao nome base de um arquivo, independentemente do diretório:"*.js"
corresponderá a todos os arquivos JS, como /test.js
e /foo/bar/test.js
"!(*test).js"
corresponderá a todos os arquivos JS, exceto aqueles que terminam em test.js
, então foo.js
mas não foo.test.js
"!(*.css|*.js)"
corresponderá a todos os arquivos, exceto arquivos CSS e JS/
), ele também corresponderá aos caminhos:"./*.js"
corresponderá a todos os arquivos JS na raiz do repositório git, então /test.js
mas não /foo/bar/test.js
"foo/**/*.js"
corresponderá a todos os arquivos JS dentro do diretório /foo
, então /foo/bar/test.js
mas não /test.js
Ao combinar, lint-staged fará o seguinte
NOTA: lint-staged
passará caminhos absolutos para os linters para evitar qualquer confusão caso eles sejam executados em um diretório de trabalho diferente (ou seja, quando seu diretório .git
não for igual ao diretório package.json
).
Consulte também Como usar lint-staged
em um monorepo de vários pacotes?
O conceito de lint-staged
é executar tarefas de linter configuradas (ou outras tarefas) em arquivos que são testados no git. lint-staged
sempre passará uma lista de todos os arquivos preparados para a tarefa, e ignorar quaisquer arquivos deve ser configurado na própria tarefa.
Considere um projeto que usa prettier
para manter o formato do código consistente em todos os arquivos. O projeto também armazena bibliotecas minificadas de fornecedores de terceiros no diretório vendor/
. Para evitar que prettier
gere erros nesses arquivos, o diretório do fornecedor deve ser adicionado à configuração de ignorar do Prettier, o arquivo .prettierignore
. Executando npx prettier .
irá ignorar todo o diretório de fornecedores, sem gerar erros. Quando lint-staged
é adicionado ao projeto e configurado para ser executado de maneira mais bonita, todos os arquivos modificados e preparados no diretório vendor serão ignorados por mais bonito, mesmo que ele os receba como entrada.
Em cenários avançados, onde é impossível configurar a própria tarefa linter para ignorar arquivos, mas alguns arquivos preparados ainda devem ser ignorados por lint-staged
, é possível filtrar caminhos de arquivos antes de passá-los para tarefas usando a sintaxe da função. Veja Exemplo: Ignorar arquivos da correspondência.
São suportados quaisquer executáveis instalados local ou globalmente via npm
bem como qualquer executável do seu $PATH.
O uso de scripts instalados globalmente é desencorajado, pois o lint-staged pode não funcionar para alguém que não o tenha instalado.
lint-staged
usa execa para localizar scripts instalados localmente. Então no seu .lintstagedrc
você pode escrever:
{
"*.js" : " eslint --fix "
}
Isso resultará na execução do lint-staged eslint --fix file-1.js file-2.js
, quando você tiver preparado os arquivos file-1.js
, file-2.js
e README.md
.
Passe argumentos para seus comandos separados por espaço, como faria no shell. Veja exemplos abaixo.
Você pode executar vários comandos em sequência em cada globo. Para fazer isso, passe uma série de comandos em vez de um único. Isso é útil para executar ferramentas de autoformatação como eslint --fix
ou stylefmt
mas pode ser usado para qualquer sequência arbitrária.
Por exemplo:
{
"*.js" : [ " eslint " , " prettier --write " ]
}
vai executar eslint
e se sair com código 0
, executará prettier --write
em todos os arquivos *.js
preparados.
Isso resultará na execução do lint-staged eslint file-1.js file-2.js
, quando você tiver preparado os arquivos file-1.js
, file-2.js
e README.md
, e se passar, prettier --write file-1.js file-2.js
.
Escrever o arquivo de configuração em JavaScript é a maneira mais poderosa de configurar o lint-staged ( lint-staged.config.js
, semelhante ou passado via --config
). Do arquivo de configuração, você pode exportar uma única função ou um objeto.
Se o valor exports
for uma função, ele receberá uma matriz de todos os nomes de arquivos preparados. Você pode então construir seus próprios matchers para os arquivos e retornar uma string de comando ou uma matriz de strings de comando. Essas strings são consideradas completas e devem incluir os argumentos do nome do arquivo, se desejado.
Se o valor exports
for um objeto, suas chaves deverão ser correspondentes glob (como no formato de configuração normal não-js). Os valores podem ser como na configuração normal ou funções individuais como descrito acima. Em vez de receber todos os arquivos correspondentes, as funções no objeto exportado receberão apenas os arquivos preparados que correspondam à chave glob correspondente.
Para resumir, por padrão, lint-staged adiciona automaticamente a lista de arquivos testados correspondentes ao seu comando, mas ao construir o comando usando funções JS, espera-se que isso seja feito manualmente. Por exemplo:
export default {
'*.js' : ( stagedFiles ) => [ `eslint .` , `prettier --write ${ stagedFiles . join ( ' ' ) } ` ] ,
}
Isso resultará na primeira execução eslint .
(correspondendo a todos os arquivos) e, se for aprovado, prettier --write file-1.js file-2.js
, quando você tiver preparado os arquivos file-1.js
, file-2.js
e README.md
.
A função também pode ser assíncrona:
( 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
em alterações em arquivos TypeScript, mas não passe nenhum argumento de nome de arquivo // 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 ( ' ' ) } ` ,
}
É melhor usar a configuração baseada em função (vista acima), se o seu caso de uso for este.
// 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 ( ' ' ) } ` ]
} ,
}
Se por algum motivo você quiser ignorar os arquivos da correspondência glob, você pode usar 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 ( ' ' ) } `
} ,
}
Observe que, na maioria dos casos, os globs podem obter o mesmo efeito. Para o exemplo acima, um globo correspondente seria !(*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 ( ' ' ) } `
} ,
}
Ferramentas como Prettier, ESLint/TSLint ou stylelint podem reformatar seu código de acordo com uma configuração apropriada executando prettier --write
/ eslint --fix
/ tslint --fix
/ stylelint --fix
. Lint-staged adicionará automaticamente quaisquer modificações ao commit, desde que não haja erros.
{
"*.js" : " prettier --write "
}
Antes da versão 10, as tarefas tinham que incluir manualmente git add
como etapa final. Este comportamento foi integrado ao próprio lint-staged para evitar condições de corrida com múltiplas tarefas editando os mesmos arquivos. Se o lint-staged detectar git add
nas configurações da tarefa, ele mostrará um aviso no console. Remova git add
da sua configuração após a atualização.
Todos os exemplos pressupõem que você já configurou o lint-staged no arquivo package.json
e o husky em seu próprio arquivo de configuração.
{
"name" : " My project " ,
"version" : " 0.1.0 " ,
"scripts" : {
"my-custom-script" : " linter --arg1 --arg2 "
},
"lint-staged" : {}
}
Em .husky/pre-commit
# .husky/pre-commit
npx lint-staged
Nota: não passamos um caminho como argumento para os corredores. Isso é importante porque o lint-staged fará isso por você.
*.js
e *.jsx
rodando como um gancho de pré-confirmação{
"*.{js,jsx}" : " eslint "
}
--fix
e adicione ao commit{
"*.js" : " eslint --fix "
}
Isso executará eslint --fix
e adicionará alterações automaticamente ao commit.
Se você deseja reutilizar um script npm definido em seu package.json:
{
"*.js" : " npm run my-custom-script -- "
}
O seguinte é equivalente:
{
"*.js" : " linter --arg1 --arg2 "
}
Os comandos Linting não suportam a convenção shell de expansão de variáveis de ambiente. Para ativar a convenção você mesmo, use uma ferramenta como cross-env
.
Por exemplo, aqui está jest
sendo executado em todos os arquivos .js
com a variável NODE_ENV
definida como "test"
:
{
"*.js" : [ " cross-env NODE_ENV=test jest --bail --findRelatedTests " ]
}
prettier
para qualquer formato compatível com o mais bonito{
"*" : " prettier --ignore-unknown --write "
}
prettier
para JavaScript, TypeScript, Markdown, HTML ou 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 é uma ferramenta CLI projetada para uso em lint-staged com padrões razoáveis.
Veja mais nesta postagem do blog os benefícios dessa abordagem.
{
"*.{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 ] ,
}
O Git 2.36.0 introduziu uma mudança nos ganchos onde eles não eram mais executados no TTY original. Isso foi corrigido em 2.37.0:
https://raw.githubusercontent.com/git/git/master/Documentation/RelNotes/2.37.0.txt
- No Git 2.36, renovamos a forma como os ganchos são invocados. Uma mudança visível para o usuário final é que a saída de um gancho não está mais diretamente conectada à saída padrão do "git" que gera o gancho, o que foi observado após o lançamento. Isso está sendo corrigido. (mesclar a082345372 ab/hooks-regression-fix posteriormente para manutenção).
Se atualizar o Git não ajudar, você pode tentar redirecionar manualmente a saída no seu gancho do Git; por exemplo:
# .husky/pre-commit
if sh -c " : >/dev/tty " > /dev/null 2> /dev/null ; then exec > /dev/tty 2>&1 ; fi
npx lint-staged
Fonte: Typicode/husky#968 (comentário)
lint-staged
via node?Sim!
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 )
}
Os parâmetros para lintStaged
são equivalentes aos seus equivalentes CLI:
const success = await lintStaged ( {
allowEmpty : false ,
c