lint-staged
Run linters against staged git files
README
🚫💩 lint-staged
Run linters against staged git files and don't let :poop: slip into your code base!
- ```bash
- 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...
- ```
Why
Linting makes more sense when run before committing your code. By doing so you can ensure no errors go into the repository and enforce code style. But running a lint process on a whole project is slow, and linting results can be irrelevant. Ultimately you only want to lint files that will be committed.
This project contains a script that will run arbitrary shell tasks with a list of staged files as an argument, filtered by a specified glob pattern.
Related blog posts and talks
If you've written one, please submit a PR with the link to it!
Installation and setup
To install _lint-staged_ in the recommended way, you need to:
1. Install _lint-staged_ itself:
- npm install --save-dev lint-staged
1. Set up the pre-commit git hook to run _lint-staged_
- Husky is a popular choice for configuring git hooks
- Read more about git hooks here
1. Configure _lint-staged_ to run linters and other tasks:
- for example: { "*.js": "eslint" } to run ESLint for all staged JS files
- See Configuration for more info
Don't forget to commit changes to package.json and .husky to share this setup with your team!
Now change a few files, git add or git add --patch some of them to your commit, and try to git commit them.
See examples and configuration for more information.
Changelog
See Releases.
Migration
v14
- Since v14.0.0 _lint-staged_ no longer supports Node.js 14. Please upgrade your Node.js version to at least 16.14.0.
v13
- Since v13.0.0 _lint-staged_ no longer supports Node.js 12. Please upgrade your Node.js version to at least 14.13.1, or 16.0.0 onward.
v12
- Since v12.0.0 _lint-staged_ is a pure ESM module, so make sure your Node.js version is at least 12.20.0, 14.13.1, or 16.0.0. Read more about ESM modules from the official Node.js Documentation site here.
v10
- From v10.0.0 onwards any new modifications to originally staged files will be automatically added to the commit.
If your task previously contained a git add step, please remove this.
The automatic behaviour ensures there are less race-conditions,
since trying to run multiple git operations at the same time usually results in an error.
- From v10.0.0 onwards, lint-staged uses git stashes to improve speed and provide backups while running.
Since git stashes require at least an initial commit, you shouldn't run lint-staged in an empty repo.
- From v10.0.0 onwards, lint-staged requires Node.js version 10.13.0 or later.
- From v10.0.0 onwards, lint-staged will abort the commit if linter tasks undo all staged changes. To allow creating an empty commit, please use the --allow-empty option.
Command line flags
- ```
- ❯ 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
- -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: By default, when linter tasks undo all staged changes, lint-staged will exit with an error and abort the commit. Use this flag to allow creating empty git commits.
- --concurrent [number|boolean]: Controls the concurrency of tasks being run by lint-staged.NOTE: This does NOT affect the concurrency of subtasks (they will always be run sequentially). Possible values are:
- false: Run all tasks serially
- true (default) : _Infinite_ concurrency. Runs as many tasks in parallel as possible.
- {number}: Run the specified number of tasks in parallel, where 1 is equivalent to false.
- --config [path]: Manually specify a path to a config file or npm package name. Note: when used, lint-staged won't perform the config file search and will print an error if the specified file cannot be found. If '-' is provided as the filename then the config will be read from stdin, allowing piping in the config like cat my-config.json | npx lint-staged --config -.
- --cwd [path]: By default tasks run in the current working directory. Use the --cwd some/directory to override this. The path can be absolute or relative to the current working directory.
- --debug: Run in debug mode. When set, it does the following:
- uses debug internally to log additional information about staged files, commands being executed, location of binaries, etc. Debug logs, which are automatically enabled by passing the flag, can also be enabled by setting the environment variable$DEBUG to lint-staged*.
- uses [verbose renderer](https://listr2.kilic.dev/renderers/verbose-renderer/) for listr2; this causes serial, uncoloured output to the terminal, instead of the default (beautified, dynamic) output.
(the [verbose renderer](https://listr2.kilic.dev/renderers/verbose-renderer/) can also be activated by setting the TERM=dumb or NODE_ENV=test environment variables)
- --diff: By default linters are filtered against all files staged in git, generated from git diff --staged. This option allows you to override the --staged flag with arbitrary revisions. For example to get a list of changed files between two branches, use --diff="branch1...branch2". You can also read more from about git diff and gitrevisions. This option also implies--no-stash.
- --diff-filter: By default only files that are _added_, _copied_, _modified_, or _renamed_ are included. Use this flag to override the default ACMR value with something else: _added_ (A), _copied_ (C), _deleted_ (D), _modified_ (M), _renamed_ (R), _type changed_ (T), _unmerged_ (U), _unknown_ (X), or _pairing broken_ (B). See also the git diff docs for --diff-filter.
- --max-arg-length: long commands (a lot of files) are automatically split into multiple chunks when it detects the current shell cannot handle them. Use this flag to override the maximum length of the generated command string.
- --no-stash: By default a backup stash will be created before running the tasks, and all task modifications will be reverted in case of an error. This option will disable creating the stash, and instead leave all modifications in the index when aborting the commit. Can be re-enabled with --stash.
- --quiet: Supress all CLI output, except from tasks.
- --relative: Pass filepaths relative to process.cwd() (where lint-staged runs) to tasks. Default is false.
- --shell: By default linter commands will be parsed for speed and security. This has the side-effect that regular shell scripts might not work as expected. You can skip parsing of commands with this option. To use a specific shell, use a path like --shell "/bin/bash".
- --verbose: Show task output even when tasks succeed. By default only failed output is shown.
Configuration
_Lint-staged_ can be configured in many ways:
- lint-staged object in your package.json
- .lintstagedrc file in JSON or YML format, or you can be explicit with the file extension:
- .lintstagedrc.json
- .lintstagedrc.yaml
- .lintstagedrc.yml
- .lintstagedrc.mjs or lint-staged.config.mjs file in ESM format
- the default export value should be a configuration: export default { ... }
- .lintstagedrc.cjs or lint-staged.config.cjs file in CommonJS format
- the exports value should be a configuration: module.exports = { ... }
- lint-staged.config.js or .lintstagedrc.js in either ESM or CommonJS format, depending on
whether your project's _package.json_ contains the "type": "module" option or not.
- Pass a configuration file using the --config or -c flag
Configuration should be an object where each value is a command to run and its key is a glob pattern to use for this command. This package uses micromatch for glob patterns. JavaScript files can also export advanced configuration as a function. See Using JS configuration files for more info.
You can also place multiple configuration files in different directories inside a project. For a given staged file, the closest configuration file will always be used. See ["How to use lint-staged in a multi-package monorepo?"](#how-to-use-lint-staged-in-a-multi-package-monorepo) for more info and an example.
package.json example:
- ```json
- {
- "lint-staged": {
- "*": "your-cmd"
- }
- }
- ```
.lintstagedrc example
- ```json
- {
- "*": "your-cmd"
- }
- ```
This config will execute your-cmd with the list of currently staged files passed as arguments.
So, considering you did git add file1.ext file2.ext, lint-staged will run the following command:
your-cmd file1.ext file2.ext
Task concurrency
By default _lint-staged_ will run configured tasks concurrently. This means that for every glob, all the commands will be started at the same time. With the following config, both eslint and prettier will run at the same time:
- ```json
- {
- "*.ts": "eslint",
- "*.md": "prettier --list-different"
- }
- ```
This is typically not a problem since the globs do not overlap, and the commands do not make changes to the files, but only report possible errors (aborting the git commit). If you want to run multiple commands for the same set of files, you can use the array syntax to make sure commands are run in order. In the following example, prettier will run for both globs, and in addition eslint will run for *.ts files _after_ it. Both sets of commands (for each glob) are still started at the same time (but do not overlap).
- ```json
- {
- "*.ts": ["prettier --list-different", "eslint"],
- "*.md": "prettier --list-different"
- }
- ```
Pay extra attention when the configured globs overlap, and tasks make edits to files. For example, in this configuration prettier and eslint might try to make changes to the same *.ts file at the same time, causing a _race condition_:
- ```json
- {
- "*": "prettier --write",
- "*.ts": "eslint --fix"
- }
- ```