lint-staged README

The project's README from github.com/lint-staged/lint-staged. As one markdown file: README.md.

🚫💩 lint-staged

Run tasks like formatters and linters against staged git files and don't let :poop: slip into your code base!

npm install --save-dev lint-staged # requires further setup
$ git commit

⋯ Backing up original state…
✔ Done backing up original state (1f4c047d)!
⋯ Running tasks for staged files…
    *.{json,md} — 1 file
      ⋯ prettier --write

✔ prettier --write

✔ Done running tasks for staged files!
⋯ Staging changes from tasks…
✔ Done staging changes from tasks!
⋯ Cleaning up temporary files…
✔ Done cleaning up temporary files!

[!Tip] Do you only want to check staged files for errors, but not edit them automatically?
You might be interested in this simpler shell script: lint-staged.sh.

Table of Contents

Why

Code quality tasks like formatters and linters make 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 task on a whole project can be slow, and opinionated tasks such as linting can sometimes produce irrelevant results. Ultimately you only want to check 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.

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
  2. 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
  3. Install some tools like ESLint or Prettier
  4. Configure lint-staged to run code checkers 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.

[!CAUTION]
Lint-staged runs git operations affecting the files in your repository. By default lint-staged creates a git stash as a backup of the original state before running any configured tasks to help prevent data loss.

Changelog

See Releases.

Migration

For breaking changes, see MIGRATION.md.

Command line flags

❯ npx lint-staged --help
Usage: lint-staged [options]

-h, --help                         display this help message
-V, --version                      display the current version number
--all                              include all files tracked in Git instead of only staged (default: false). Implies "--no-stash" and "--allow-empty".
--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
--continue-on-error                run all tasks to completion even if one fails (default: false)
--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
--fail-on-changes                  fail with exit code 1 when tasks modify tracked files (default: false)
--no-hide-partially-staged         hide unstaged changes from partially staged files (default: true)
--hide-unstaged                    hide all unstaged changes, instead of just partially staged (default: false)
--hide-all                         hide all unstaged changes and untracked files (default: false)
--max-arg-length [number]          maximum length of the command-line argument string (default: 0)
-q, --quiet                        disable lint-staged's own console output (default: false)
-r, --relative                     pass relative filepaths to tasks (default: false)
--no-revert                        revert to original state in case of errors (default: true)
--no-stash                         enable the backup stash (default: true)
-v, --verbose                      show task output even when tasks succeed; by default only failed output is shown (default: false)

Any lost modifications can be restored from a git stash:

  > git stash list --format="%h %s"
  <git-hash> On main: lint-staged automatic backup
  > git apply --index <git-hash>

--all

By default lint-staged only runs tasks on files that include staged changes (hence the name). Use this flag to include all files tracked in Git version control (standard exclusions apply). Using this flag implies the --no-stash flag, disabling the automatic backup, and the --allow-empty flag so that lint-staged doesn't fail when there are no changes after running. This makes it easier to run npx lint-staged --all on a clean state, for example in CI.

--allow-empty

By default, when 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:

--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]

Change the working directory lint-staged runs tasks in. Defaults to process.cwd() and the value can be absolute or relative to the default value.

--debug

Log additional information about staged files, commands being executed, location of binaries, etc.

--diff

By default tasks 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 [string]

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.

--continue-on-error

By default lint-staged will "exit early" when any of the configured tasks fails, to make sure the runtime is short. With this flag, lint-staged will instead run all tasks to completion and only fail at the end, allowing all task output to be seen.

--fail-on-changes

By default changes made by tasks are automatically staged and added to the commit. This flag disables the behavior and makes lint-staged exit with code 1, failing the commit instead. Using this flag also implies the --no-revert flag which means any changes made my tasks will be left in the working tree after failing, so that they can be manually staged and the commit tried again.

--max-arg-length [number]

The list of staged files are appended to configured tasks as arguments, and the resulting string might be too long for the current shell, especially on Windows. Lint-staged tries to avoid this by splitting the list of staged files into chunks resulting in the commands being run multiple times. This behavior affects all tasks, including functions. Use the --max-arg-length to override the platform-specific default value if you want to avoid this. Setting --max-arg-length=Infinity will disable chunking completely.

--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.

--no-hide-partially-staged

By default, unstaged changes from partially staged files will be hidden and applied back after running tasks. This option will disable this behavior, causing those changes to also be committed.

--hide-unstaged

Use this option to hide all unstaged changes in tracked files, instead of just those which are also partially staged, before running tasks. The changes will be applied back after running the tasks.

--hide-all

Add new option --hide-all for hiding all unstaged changes and untracked files, before running tasks. This makes it easier to run tools like Knip which check for unused code. Untracked files are included in the backup stash and restored automatically after running.

--quiet

Suppress all CLI output, except from tasks.

--relative

Pass filepaths relative to process.cwd() (where lint-staged runs) to tasks. Default is false.

--no-revert

By default all task modifications will be reverted in case of an error. This option will disable the behavior, and apply task modifications to the index before aborting the commit.

--verbose

Show task output even when tasks succeed. By default only failed output is shown.

Configuration

Lint-staged can be configured in many ways:

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 picomatch 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 and tasks will by default run in the directory of the config (for example, the directory of a specific package in a monorepo). See "How to use lint-staged in a multi-package monorepo?" for more info and an example.

package.json example:

{
  "lint-staged": {
    "*": "your-cmd"
  }
}

.lintstagedrc.json example

{
  "*": "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

TypeScript

Lint-staged provides TypeScript types for the configuration and main Node.js API. You can use the defineConfig helper in your JS configuration files:

import { defineConfig } from 'lint-staged/config'

export default defineConfig({
  '*': 'prettier --write',
})

It's also possible to use the .ts file extension for the configuration if your Node.js version supports it. The --experimental-strip-types flag was introduced in Node.js v22.6.0 and unflagged in v23.6.0, enabling Node.js to execute TypeScript files without additional configuration.

export NODE_OPTIONS="--experimental-strip-types"

npx lint-staged --config lint-staged.config.ts

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:

{
  "*.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).

{
  "*.ts": ["prettier --list-different", "eslint"],
  "*.md": "prettier --list-different"
}

If you want to run tasks parallely for a specific glob, you can nest one extra layer of arrays, inside the array. In the following example, prettier and eslint will run in parallel for *.ts files, while the two globs also run in parallel.

{
  "*.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:

{
  "*": "prettier --write",
  "*.ts": "eslint --fix"
}

You can solve it using the negation pattern and the array syntax:

{
  "!(*.ts)": "prettier --write",
  "*.ts": ["eslint --fix", "prettier --write"]
}

Another example in which tasks make edits to files and globs match multiple files but don't overlap:

{
  "*.css": ["stylelint --fix", "prettier --write"],
  "*.{js,jsx}": ["eslint --fix", "prettier --write"],
  "!(*.css|*.js|*.jsx)": ["prettier --write"]
}

Or, if necessary, you can limit the concurrency using --concurrent <number> or disable it entirely with --concurrent false.

Filtering files

Task commands work on a subset of all staged files, defined by a glob pattern. lint-staged uses picomatch for matching files with the following rules:

When matching, lint-staged will do the following

NOTE: lint-staged will pass absolute paths to the tasks to avoid any confusion in case they're executed in a different working directory (i.e. when your .git directory isn't the same as your package.json directory).

Also see How to use lint-staged in a multi-package monorepo?

Ignoring files

The concept of lint-staged is to run configured linter tasks (or other tasks) on files that are staged in git. lint-staged will always pass a list of all staged files to the task, and ignoring any files should be configured in the task itself.

Consider a project that uses prettier to keep code format consistent across all files. The project also stores minified 3rd-party vendor libraries in the vendor/ directory. To keep prettier from throwing errors on these files, the vendor directory should be added to prettier's ignore configuration, the .prettierignore file. Running npx prettier . will ignore the entire vendor directory, throwing no errors. When lint-staged is added to the project and configured to run prettier, all modified and staged files in the vendor directory will be ignored by prettier, even though it receives them as input.

In advanced scenarios, where it is impossible to configure the linter task itself to ignore files, but some staged files should still be ignored by lint-staged, it is possible to filter filepaths before passing them to tasks by using the function syntax. See Example: Ignore files from match.

What commands are supported?

Supported are any executables installed locally or globally via npm as well as any executable from your $PATH.

Using globally installed sc

lint-staged (lint-staged/lint-staged)

Searching Database…

What is this?lint-staged runs linters and formatters only on the files you have staged in git, so you check and clean up just what you're about to commit instead of the whole project.
Is this a standalone tool or does it need something else to trigger it?It's a CLI task runner, not a git hook manager itself — you pair it with a pre-commit hook (commonly via Husky) that invokes lint-staged.
How do I add it to a project?Install it with 'npm install --save-dev lint-staged', wire up a pre-commit hook (e.g. with Husky) to run it, install your linters/formatters, then map file globs to commands in a config like '{ ".js": "eslint" }'.
Do I need ESLint or Prettier installed separately?Yes — lint-staged just orchestrates commands against staged files; you still need to install the actual tools like ESLint or Prettier yourself.
Is it safe if a task fails midway?By default it creates a git stash backup before running tasks and reverts changes on error, but you can disable this safety net with flags like --no-stash and --no-revert.
Does it work the same on Windows as on Mac/Linux?Yes, though Windows shells have stricter argument length limits, so lint-staged automatically chunks the staged file list into multiple command runs to avoid hitting that limit.
Can I run multiple commands on the same files in a specific order?Yes, use array syntax to chain commands sequentially for a glob (e.g. prettier then eslint), or nest arrays for commands you want to run in parallel instead.
How are config files written?Configuration can live in package.json, a .lintstagedrc file (JSON/YAML/JS/CJS/MJS), or a lint-staged.config.js/mjs/cjs file, mapping glob patterns (matched with picomatch) to shell commands.
What happens if a task undoes all my staged changes?lint-staged exits with an error and aborts the commit by default; use the --allow-empty flag if you want to permit an empty commit in that case.
Can it handle a monorepo with multiple packages?Yes, you can place multiple config files in different directories, and for each staged file lint-staged uses the closest config, running tasks in that config's directory.
Size
7 MB
Stars
14.7K
Age
10y
Commits · 90 Days