Запускайте линтеры для проиндексированных файлов git и не позволяйте ? проникните в свою кодовую базу!
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...
Линтинг имеет больше смысла, если его запускать до фиксации кода. Поступая так, вы можете гарантировать отсутствие ошибок в репозитории и обеспечить соблюдение стиля кода. Но выполнение процесса проверки всего проекта происходит медленно, и результаты проверки могут быть несущественными. В конечном итоге вам нужно проверять только те файлы, которые будут зафиксированы.
Этот проект содержит сценарий, который будет запускать произвольные задачи оболочки со списком промежуточных файлов в качестве аргумента, отфильтрованных по заданному шаблону glob.
dotnet-format
и lint-staged
Если вы его написали, отправьте PR со ссылкой на него!
Чтобы установить lint-staged рекомендуемым способом, вам необходимо:
npm install --save-dev lint-staged
pre-commit
для запуска lint-staged{ "*.js": "eslint" }
для запуска ESLint для всех подготовленных файлов JS. Не забудьте зафиксировать изменения в package.json
и .husky
чтобы поделиться этой настройкой со своей командой!
Теперь измените несколько файлов, git add
или git add --patch
некоторые из них в свой коммит и попытайтесь git commit
.
Дополнительные сведения см. в примерах и конфигурации.
См. Релизы.
v15.0.0
больше не поддерживает Node.js 16. Обновите версию Node.js как минимум до 18.12.0
. v14.0.0
больше не поддерживает Node.js 14. Обновите версию Node.js как минимум до 16.14.0
. v13.0.0
больше не поддерживает Node.js 12. Обновите версию Node.js как минимум до 14.13.1
или 16.0.0
и выше.v13.3.0
была выпущена неправильно, включая код версии v14.0.0
. Это означает, что критические изменения v14
также включены в v13.3.0
, последнюю выпущенную версию v13
v12.0.0
lint-staged — это чистый модуль ESM, убедитесь, что ваша версия Node.js не ниже 12.20.0
, 14.13.1
или 16.0.0
. Дополнительную информацию о модулях ESM можно найти на официальном сайте документации Node.js здесь. v10.0.0
, любые новые изменения в изначально подготовленных файлах будут автоматически добавляться в коммит. Если ваша задача ранее содержала шаг git add
, удалите его. Автоматическое поведение гарантирует меньшее количество состояний гонки, поскольку попытка одновременного выполнения нескольких операций git обычно приводит к ошибке.v10.0.0
, lint-staged использует тайники git для повышения скорости и обеспечения резервного копирования во время работы. Поскольку тайники git требуют как минимум первоначальной фиксации, вам не следует запускать lint-staged в пустом репозитории.v10.0.0
, для lint-staged требуется Node.js версии 10.13.0 или новее.v10.0.0
, lint-staged прервет фиксацию, если задачи linter отменяют все поэтапные изменения. Чтобы разрешить создание пустого коммита, используйте опцию --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
: по умолчанию, когда задачи линтера отменяют все проиндексированные изменения, 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*
.verbose
рендерер для listr2
; это приводит к последовательному неокрашенному выводу на терминал вместо вывода по умолчанию (украшенного, динамического). ( verbose
рендерер также можно активировать, установив переменные среды TERM=dumb
или NODE_ENV=test
)--diff
: по умолчанию линтеры фильтруются по всем файлам, размещенным в 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
). См. также документацию git diff
для --diff-filter.--max-arg-length
: длинные команды (много файлов) автоматически разбиваются на несколько фрагментов, когда обнаруживается, что текущая оболочка не может их обработать. Используйте этот флаг, чтобы переопределить максимальную длину сгенерированной командной строки.--no-stash
: по умолчанию перед запуском задач создается резервная копия, и все изменения задачи будут отменены в случае ошибки. Эта опция отключит создание тайника и вместо этого оставит все изменения в индексе при прерывании фиксации. Можно повторно включить с помощью --stash
. Эта опция также подразумевает --no-hide-partially-staged
.--no-hide-partially-staged
: по умолчанию неиндексированные изменения в частично подготовленных файлах будут скрыты. Эта опция отключит это поведение и включит все неподготовленные изменения в частично подготовленных файлах. Можно повторно включить с помощью --hide-partially-staged
--quiet
: Подавить весь вывод CLI, кроме задач.--relative
: передавать в задачи пути к файлам относительно process.cwd()
(где выполняется lint-staged
). По умолчанию — false
.--shell
: по умолчанию команды линтера анализируются на предмет скорости и безопасности. Это имеет побочный эффект: обычные сценарии оболочки могут работать не так, как ожидалось. С помощью этой опции вы можете пропустить синтаксический анализ команд. Чтобы использовать конкретную оболочку, используйте путь типа --shell "/bin/bash"
.--verbose
: показывать выходные данные задачи, даже если задача выполнена успешно. По умолчанию отображаются только неудачные выходные данные. Lint-staged можно настроить разными способами:
lint-staged
, в вашем package.json
или package.yaml
.lintstagedrc
в формате JSON или YML, или вы можете явно указать расширение файла:.lintstagedrc.json
.lintstagedrc.yaml
.lintstagedrc.yml
.lintstagedrc.mjs
или lint-staged.config.mjs
в формате ESM.export default { ... }
.lintstagedrc.cjs
или lint-staged.config.cjs
в формате CommonJS.module.exports = { ... }
lint-staged.config.js
или .lintstagedrc.js
в формате ESM или CommonJS, в зависимости от того, содержит ли package.json вашего проекта параметр "type": "module"
или нет.--config
или -c
Конфигурация должна представлять собой объект, где каждое значение представляет собой команду для запуска, а ее ключ — это шаблон glob, используемый для этой команды. Этот пакет использует микросоответствие для шаблонов glob. Файлы JavaScript также могут экспортировать расширенную конфигурацию как функцию. Дополнительную информацию см. в разделе «Использование файлов конфигурации JS».
Вы также можете разместить несколько файлов конфигурации в разных каталогах внутри проекта. Для данного промежуточного файла всегда будет использоваться ближайший файл конфигурации. См. «Как использовать 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 "
}
Обычно это не проблема, поскольку globs не перекрываются, а команды не вносят изменения в файлы, а только сообщают о возможных ошибках (прерывание фиксации git). Если вы хотите запустить несколько команд для одного и того же набора файлов, вы можете использовать синтаксис массива, чтобы убедиться, что команды выполняются по порядку. В следующем примере prettier
будет работать для обоих glob, а eslint
будет запускаться для файлов *.ts
после него. Оба набора команд (для каждого glob) по-прежнему запускаются одновременно (но не перекрываются).
{
"*.ts" : [ " prettier --list-different " , " eslint " ],
"*.md" : " prettier --list-different "
}
Обратите особое внимание, когда настроенные globs перекрываются, а задачи вносят изменения в файлы. Например, в этой конфигурации prettier
и eslint
могут одновременно попытаться внести изменения в один и тот же файл *.ts
, что приведет к возникновению состояния гонки :
{
"*" : " prettier --write " ,
"*.ts" : " eslint --fix "
}
Вы можете решить эту проблему, используя шаблон отрицания и синтаксис массива:
{
"!(*.ts)" : " prettier --write " ,
"*.ts" : [ " eslint --fix " , " prettier --write " ]
}
Другой пример, в котором задачи вносят изменения в файлы, а globs соответствуют нескольким файлам, но не перекрываются:
{
"*.css" : [ " stylelint --fix " , " prettier --write " ],
"*.{js,jsx}" : [ " eslint --fix " , " prettier --write " ],
"!(*.css|*.js|*.jsx)" : [ " prettier --write " ]
}
Или, при необходимости, вы можете ограничить параллелизм с помощью --concurrent <number>
или полностью отключить его с помощью --concurrent false
.
Команды ЛИНТЕРА работают с подмножеством всех промежуточных файлов, определенных шаблоном glob . lint-staged использует микроматч для сопоставления файлов по следующим правилам:
/
), опция 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"
будет соответствовать всем файлам JS в корне репозитория git, поэтому /test.js
, но не /foo/bar/test.js
"foo/**/*.js"
будет соответствовать всем файлам JS в каталоге /foo
, поэтому /foo/bar/test.js
, но не /test.js
При сопоставлении lint-staged выполнит следующее
ПРИМЕЧАНИЕ. lint-staged
передает абсолютные пути к линтерам, чтобы избежать путаницы в случае, если они выполняются в другом рабочем каталоге (т. е. когда ваш каталог .git
не совпадает с вашим каталогом package.json
).
Также см. Как использовать lint-staged
в монорепозитории с несколькими пакетами?
Концепция lint-staged
заключается в запуске настроенных задач линтера (или других задач) над файлами, размещенными в git. lint-staged
всегда передает задаче список всех подготовленных файлов, и игнорирование любых файлов должно быть настроено в самой задаче.
Рассмотрим проект, в котором используется prettier
для обеспечения единообразия формата кода во всех файлах. Проект также хранит минифицированные библиотеки сторонних поставщиков в каталогеvendor vendor/
. Чтобы prettier
не выдавал ошибок в этих файлах, каталог поставщика следует добавить в конфигурацию игнорирования Prettier, файл .prettierignore
. Запуск npx prettier .
будет игнорировать весь каталог поставщика, не выдавая ошибок. Когда lint-staged
добавляется в проект и настроен для запуска prettier, все измененные и подготовленные файлы в каталоге поставщика будут игнорироваться prettier, даже если он получает их в качестве входных данных.
В расширенных сценариях, когда невозможно настроить саму задачу линтера на игнорирование файлов, но некоторые промежуточные файлы по-прежнему должны игнорироваться lint-staged
, можно фильтровать пути к файлам перед их передачей задачам с помощью синтаксиса функции. См. Пример: игнорировать файлы из совпадения.
Поддерживаются любые исполняемые файлы, установленные локально или глобально через npm
, а также любой исполняемый файл из вашего $PATH.
Использование глобально установленных скриптов не рекомендуется, поскольку lint-staged может не работать для тех, у кого он не установлен.
lint-staged
использует execa для поиска локально установленных скриптов. Итак, в вашем .lintstagedrc
вы можете написать:
{
"*.js" : " eslint --fix "
}
Это приведет к запуску eslint --fix file-1.js file-2.js
в режиме lint , если у вас есть подготовленные файлы file-1.js
, file-2.js
и README.md
.
Передавайте аргументы вашим командам, разделенные пробелом, как если бы вы это делали в оболочке. См. примеры ниже.
Вы можете последовательно запускать несколько команд для каждого glob. Для этого передайте массив команд вместо одной. Это полезно для запуска инструментов автоформатирования, таких как eslint --fix
или stylefmt
но может использоваться для любых произвольных последовательностей.
Например:
{
"*.js" : [ " eslint " , " prettier --write " ]
}
собирается выполнить eslint
, и если он завершит работу с кодом 0
, он будет выполнять prettier --write
для всех подготовленных файлов *.js
.
Это приведет к этапному запуску eslint file-1.js file-2.js
, когда у вас есть проиндексированные файлы file-1.js
, file-2.js
и README.md
, и если он пройдет, prettier --write file-1.js file-2.js
.
Написание файла конфигурации на JavaScript — самый мощный способ настройки lint-staged ( lint-staged.config.js
, аналогичный или передаваемый через --config
). Из файла конфигурации вы можете экспортировать либо одну функцию, либо объект.
Если значение exports
является функцией, оно получит массив всех проиндексированных имен файлов. Затем вы можете создать свои собственные средства сопоставления для файлов и возвращать командную строку или массив командных строк. Эти строки считаются полными и при необходимости должны включать аргументы имени файла.
Если значение exports
является объектом, его ключи должны соответствовать glob (как в обычном формате конфигурации, отличном от JS). Значения могут быть как в обычной конфигурации, так и в отдельных функциях, как описано выше. Вместо получения всех совпадающих файлов функции в экспортированном объекте будут получать только промежуточные файлы, соответствующие соответствующему глобальному ключу.
Подводя итог, по умолчанию lint-staged автоматически добавляет список совпадающих промежуточных файлов в вашу команду, но при построении команды с использованием функций JS ожидается, что это будет сделано вручную. Например:
export default {
'*.js' : ( stagedFiles ) => [ `eslint .` , `prettier --write ${ stagedFiles . join ( ' ' ) } ` ] ,
}
Это приведет к первому запуску eslint на этапе lint eslint .
(соответствует всем файлам), и если он пройдет, prettier --write file-1.js file-2.js
, когда у вас есть проиндексированные файлы file-1.js
, file-2.js
и README.md
.
Функция также может быть асинхронной:
( 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
при изменении файлов TypeScript, но не передавайте аргументы имени файла. // 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
из вашей конфигурации после обновления.
Во всех примерах предполагается, что вы уже настроили lint-staged в файле package.json
и хаски в его собственном файле конфигурации.
{
"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
и автоматически добавит изменения в коммит.
Если вы хотите повторно использовать скрипт npm, определенный в вашем package.json:
{
"*.js" : " npm run my-custom-script -- "
}
Следующее эквивалентно:
{
"*.js" : " linter --arg1 --arg2 "
}
Команды линтинга не поддерживают соглашение оболочки о расширении переменных среды. Чтобы включить соглашение самостоятельно, используйте такой инструмент, как cross-env
.
Например, вот jest
запущенный для всех файлов .js
с переменной 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, предназначенный для поэтапного использования с разумными настройками по умолчанию.
Дополнительные сведения о преимуществах этого подхода см. в этом сообщении блога.
{
"*.{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.githbusercontent.com/git/git/master/Documentation/RelNotes/2.37.0.txt
- В Git 2.36 мы изменили способ вызова хуков. Одно изменение, видимое для конечного пользователя, заключается в том, что вывод перехватчика больше не связан напрямую со стандартным выводом «git», который порождает перехватчик, что было замечено после выпуска. Это исправляется. (позже объедините a082345372 ab/hooks-reгрессия-fix с maint).
Если обновление 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