Skip to content

针对暂存的 git 文件运行格式化器和代码检查器等任务,并且不要让 💩潜入你的代码库!

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

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

✔ Backed up original state in git stash (5bda95f)
❯ 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...
See asciinema video

asciicast

为什么

¥Why

在提交代码之前运行格式化器和代码检查器等代码质量任务会更有意义。通过这样做,你可以确保没有错误进入存储库并强制执行代码样式。但是在整个项目上运行任务可能会很慢,并且诸如 linting 之类的有态度的任务有时会产生不相关的结果。最终,你只想检查将要提交的文件。

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

此项目包含一个脚本,它将以暂存文件列表作为参数运行任意 shell 任务,并按指定的 glob 模式进行过滤。

¥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

如果你已经编写了一个,请提交带有其链接的 PR!

¥If you've written one, please submit a PR with the link to it!

安装和设置

¥Installation and setup

要以推荐的方式安装 lint-staged,你需要:

¥To install lint-staged in the recommended way, you need to:

  1. 安装 lint-staged 本身:

    ¥Install lint-staged itself:

    • npm install --save-dev lint-staged
  2. 设置 pre-commit git hook 以运行 lint-staged

    ¥Set up the pre-commit git hook to run lint-staged

    • Husky 是配置 git hooks 的流行选择

      ¥Husky is a popular choice for configuring git hooks

    • 阅读有关 git hooks 此处 的更多信息

      ¥Read more about git hooks here

  3. 安装一些工具,如 ESLintPrettier

    ¥Install some tools like ESLint or Prettier

  4. 配置 lint-staged 以运行代码检查器和其他任务:

    ¥Configure lint-staged to run code checkers and other tasks:

    • 例如:{ "*.js": "eslint" } 为所有暂存的 JS 文件运行 ESLint

      ¥for example: { "*.js": "eslint" } to run ESLint for all staged JS files

    • 有关更多信息,请参阅 配置

      ¥See Configuration for more info

不要忘记提交对 package.json.husky 的更改以与你的团队共享此设置!

¥Don't forget to commit changes to package.json and .husky to share this setup with your team!

现在更改一些文件,将其中一些 git addgit add --patch 更改为你的提交,并尝试 git commit 它们。

¥Now change a few files, git add or git add --patch some of them to your commit, and try to git commit them.

有关更多信息,请参阅 examplesconfiguration

¥See examples and configuration for more information.

Lint-staged 运行影响存储库中文件的 `git` 操作。默认情况下,lint-staged 在运行任何配置的任务之前会创建一个 `git stash` 作为原始状态的备份,以帮助防止数据丢失。

¥[!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

有关重大变更,请参阅 MIGRATION.md

¥For breaking changes, see MIGRATION.md.

命令行标志

¥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. 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)
  -v, --verbose                      show task output even when tasks succeed; by default only failed output is
                                     shown (default: false)
  -h, --help                         display help for command

Any lost modifications can be restored from a git stash:

  > git stash list
  stash@{0}: automatic lint-staged backup
  > git stash apply --index stash@{0}
  • --allow-empty:默认情况下,当任务撤消所有暂存更改时,lint-staged 将以错误退出并中止提交。使用此标志允许创建空的 git 提交。

    ¥--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]:控制 lint-staged 运行的 任务并发。注意:这不会影响子任务的并发性(它们将始终按顺序运行)。可能的值包括:

    ¥--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:按顺序运行所有任务

      ¥false: Run all tasks serially

    • true(默认):无限并发。尽可能并行运行尽可能多的任务。

      ¥true (default) : Infinite concurrency. Runs as many tasks in parallel as possible.

    • {number}:并行运行指定数量的任务,其中 1 相当于 false

      ¥{number}: Run the specified number of tasks in parallel, where 1 is equivalent to false.

  • --config [path]:手动指定配置文件或 npm 包名称的路径。注意:使用时,lint-staged 不会执行配置文件搜索,如果找不到指定的文件,则会打印错误。如果提供 '*' 作为文件名,则将从 stdin 读取配置,允许像 cat my-config.json | npx lint-staged --config - 一样在配置中进行管道传输。

    ¥--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]:默认情况下,任务在当前工作目录中运行。使用 --cwd some/directory 覆盖此问题。路径可以是绝对路径,也可以是相对于当前工作目录的相对路径。

    ¥--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:在调试模式下运行。设置后,它会执行以下操作:

    ¥--debug: Run in debug mode. When set, it does the following:

    • 内部使用 debug 记录有关暂存文件、正在执行的命令、二进制文件位置等的其他信息。通过传递标志自动启用的调试日志也可以通过将环境变量 $DEBUG 设置为 lint-staged* 来启用。

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

    • verbose 渲染器 用于 listr2;这会导致向终端输出串行、无色输出,而不是默认(美化、动态)输出。(也可以通过设置 TERM=dumbNODE_ENV=test 环境变量来激活 verbose 渲染器

      ¥uses verbose renderer for listr2; this causes serial, uncoloured output to the terminal, instead of the default (beautified, dynamic) output. (the verbose renderer can also be activated by setting the TERM=dumb or NODE_ENV=test environment variables)

  • --diff:默认情况下,将针对从 git diff --staged 生成的 git 中暂存的所有文件对任务进行过滤。此选项允许你使用任意修订覆盖 --staged 标志。例如,要获取两个分支之间已更改文件的列表,请使用 --diff="branch1...branch2"。你还可以阅读有关 git diffgitrevisions 的更多信息。此选项还暗示了 --no-stash

    ¥--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:默认情况下,只包含添加、复制、修改或重命名的文件。使用此标志用其他内容覆盖默认的 ACMR 值:已添加 (A)、已复制 (C)、已删除 (D)、已修改 (M)、已重命名 (R)、已更改类型 (T)、已取消合并 (U)、未知 (X) 或已断开配对 (B)。另请参阅 --diff-filtergit diff 文档。

    ¥--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:当检测到当前 shell 无法处理长命令(大量文件)时,会自动将其拆分为多个块。使用此标志覆盖生成的命令字符串的最大长度。

    ¥--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:默认情况下,将在运行任务之前创建备份存储,并且在发生错误时将还原所有任务修改。此选项将禁用创建存储,而是在中止提交时将所有修改保留在索引中。可以使用 --stash 重新启用。此选项还暗示了 --no-hide-partially-staged

    ¥--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. This option also implies --no-hide-partially-staged.

  • --no-hide-partially-staged:默认情况下,部分暂存文件的未暂存更改将被隐藏。此选项将禁用此行为并将所有未暂存的更改包含在部分暂存的文件中。可以使用 --hide-partially-staged 重新启用

    ¥--no-hide-partially-staged: By default, unstaged changes from partially staged files will be hidden. This option will disable this behavior and include all unstaged changes in partially staged files. Can be re-enabled with --hide-partially-staged

  • --quiet:抑制所有 CLI 输出,任务除外。

    ¥--quiet: Supress all CLI output, except from tasks.

  • --relative:将相对于 process.cwd()lint-staged 运行的位置)的文件路径传递给任务。默认为 false

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

  • --verbose:即使任务成功也显示任务输出。默认情况下,只显示失败的输出。

    ¥--verbose: Show task output even when tasks succeed. By default only failed output is shown.

配置

¥Configuration

Lint-staged 可以通过多种方式配置:

¥Lint-staged can be configured in many ways:

  • package.jsonpackage.yaml 中的 lint-staged 对象

    ¥lint-staged object in your package.json, or package.yaml

  • .lintstagedrc 文件采用 JSON 或 YML 格式,或者你可以使用文件扩展名明确说明:

    ¥.lintstagedrc file in JSON or YML format, or you can be explicit with the file extension:

    • .lintstagedrc.json

    • .lintstagedrc.yaml

    • .lintstagedrc.yml

  • ESM 格式的 .lintstagedrc.mjslint-staged.config.mjs 文件

    ¥.lintstagedrc.mjs or lint-staged.config.mjs file in ESM format

    • 默认导出值应为配置:export default { ... }

      ¥the default export value should be a configuration: export default { ... }

  • CommonJS 格式的 .lintstagedrc.cjslint-staged.config.cjs 文件

    ¥.lintstagedrc.cjs or lint-staged.config.cjs file in CommonJS format

    • 导出值应为配置:module.exports = { ... }

      ¥the exports value should be a configuration: module.exports = { ... }

  • lint-staged.config.js.lintstagedrc.js 为 ESM 或 CommonJS 格式,具体取决于项目的 package.json 是否包含 "type": "module" 选项。

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

  • 使用 --config-c 标志传递配置文件

    ¥Pass a configuration file using the --config or -c flag

配置应该是一个对象,其中每个值都是要运行的命令,其键是用于此命令的 glob 模式。此包使用 micromatch 进行 glob 模式。JavaScript 文件还可以将高级配置导出为函数。有关更多信息,请参阅 使用 JS 配置文件

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

你还可以将多个配置文件放在项目内的不同目录中。对于给定的暂存文件,将始终使用最近的配置文件。有关更多信息和示例,请参阅 “如何在多包 monorepo 中使用 lint-staged?”

¥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?" for more info and an example.

package.json 示例:

¥package.json example:

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

.lintstagedrc 示例

¥.lintstagedrc example

json
{
  "*": "your-cmd"
}

此配置将使用作为参数传递的当前暂存文件列表执行 your-cmd

¥This config will execute your-cmd with the list of currently staged files passed as arguments.

因此,考虑到你执行了 git add file1.ext file2.ext,lint-staged 将运行以下命令:

¥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 为配置和主要 Node.js API 提供 TypeScript 类型。你可以在 JS 配置文件中使用 JSDoc 语法:

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

js
/**

 * @filename: lint-staged.config.js

 * @type {import('lint-staged').Configuration}
 */
export default {
  '*': 'prettier --write',
}

如果你的 Node.js 版本支持,也可以使用 .ts 文件扩展名进行配置。--experimental-strip-types 标志是在 Node.js v22.6.0 中引入的,并在 v23.6.0 中取消标记,使 Node.js 无需额外配置即可执行 TypeScript 文件。

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

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

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

任务并发性

¥Task concurrency

默认情况下,lint-staged 将同时运行配置的任务。这意味着对于每个 glob,所有命令将同时启动。使用以下配置,eslintprettier 将同时运行:

¥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"
}

这通常不是问题,因为 glob 不会重叠,并且命令不会更改文件,而只会报告可能的错误(中止 git 提交)。如果你想对同一组文件运行多个命令,则可以使用数组语法来确保命令按顺序运行。在下面的示例中,prettier 将针对两个 glob 运行,此外 eslint 将针对其后的 *.ts 文件运行。两组命令(对于每个 glob)仍同时启动(但不重叠)。

¥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"
}

当配置的 glob 重叠且任务对文件进行编辑时,请特别注意。例如,在此配置中,prettiereslint 可能会尝试同时对同一个 *.ts 文件进行更改,从而导致竞争条件:

¥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"
}

你可以使用否定模式和数组语法解决它:

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

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

另一个示例是任务对文件进行编辑,并且 glob 匹配多个文件但不重叠:

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

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

或者,如果有必要,你可以使用 --concurrent <number> 限制并发性或使用 --concurrent false 完全禁用它。

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

过滤文件

¥Filtering files

任务命令适用于由 glob 模式定义的所有暂存文件的子集。lint-staged 使用 micromatch 匹配文件,规则如下:

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

  • 如果 glob 模式不包含斜杠(/),则将启用 micromatch 的 matchBase 选项,因此无论目录如何,globs 都会匹配文件的基本名称:

    ¥If the glob pattern contains no slashes (/), micromatch's matchBase option will be enabled, so globs match a file's basename regardless of directory:

    • "*.js" 将匹配所有 JS 文件,如 /test.js/foo/bar/test.js

      ¥"*.js" will match all JS files, like /test.js and /foo/bar/test.js

    • "!(*test).js" 将匹配所有 JS 文件,但以 test.js 结尾的文件除外,因此匹配 foo.js 但不匹配 foo.test.js

      ¥"!(*test).js" will match all JS files, except those ending in test.js, so foo.js but not foo.test.js

    • "!(*.css|*.js)" 将匹配除 CSS 和 JS 文件之外的所有文件

      ¥"!(*.css|*.js)" will match all files except CSS and JS files

  • 如果 glob 模式确实包含斜杠(/),它也会匹配路径:

    ¥If the glob pattern does contain a slash (/), it will match for paths as well:

    • "./*.js" 将匹配 git repo 根目录中的所有 JS 文件,因此匹配 /test.js 但不匹配 /foo/bar/test.js

      ¥"./*.js" will match all JS files in the git repo root, so /test.js but not /foo/bar/test.js

    • "foo/**/*.js" 将匹配 /foo 目录中的所有 JS 文件,因此匹配 /foo/bar/test.js 但不匹配 /test.js

      ¥"foo/**/*.js" will match all JS files inside the /foo directory, so /foo/bar/test.js but not /test.js

匹配时,lint-staged 将执行以下操作

¥When matching, lint-staged will do the following

  • 自动解析 git 根,无需配置。

    ¥Resolve the git root automatically, no configuration needed.

  • 选择项目目录中存在的暂存文件。

    ¥Pick the staged files which are present inside the project directory.

  • 使用指定的 glob 模式过滤它们。

    ¥Filter them using the specified glob patterns.

  • 将绝对路径作为参数传递给任务。

    ¥Pass absolute paths to the tasks as arguments.

注意:lint-staged 将向任务传递绝对路径,以避免在不同的工作目录中执行时产生任何混淆(即当你的 .git 目录与你的 package.json 目录不同时)。

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

另请参阅 如何在多包 monorepo 中使用 lint-staged

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

忽略文件

¥Ignoring files

lint-staged 的概念是在 git 中暂存的文件上运行配置的 linter 任务(或其他任务)。lint-staged 将始终将所有暂存文件的列表传递给任务,并且应在任务本身中配置忽略任何文件。

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

考虑一个使用 prettier 保持所有文件的代码格式一致的项目。该项目还将最小化的第三方供应商库存储在 vendor/ 目录中。为了防止 prettier 在这些文件上抛出错误,应将供应商目录添加到 prettier 的忽略配置 .prettierignore 文件中。运行 npx prettier . 将忽略整个供应商目录,不会抛出任何错误。当 lint-staged 添加到项目并配置为运行 prettier 时,即使 prettier 接收了供应商目录中所有已修改和暂存的文件作为输入,它也会忽略它们。

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

在高级场景中,无法配置 linter 任务本身来忽略文件,但某些暂存文件仍应被 lint-staged 忽略,可以使用函数语法在将文件路径传递给任务之前对其进行过滤。参见 示例:忽略匹配的文件

¥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?

支持通过 npm 在本地或全局安装的任何可执行文件以及来自 $PATH 的任何可执行文件。

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

不鼓励使用全局安装的脚本,因为 lint-staged 可能不适用于未安装它的人。

¥Using globally installed scripts is discouraged, since lint-staged may not work for someone who doesn't have it installed.

lint-staged 使用 execa 来定位本地安装的脚本。因此,在你的 .lintstagedrc 中你可以写入:

¥lint-staged uses execa to locate locally installed scripts. So in your .lintstagedrc you can write:

json
{
  "*.js": "eslint --fix"
}

这将导致 lint-staged 在你暂存文件 file-1.jsfile-2.jsREADME.md 时运行 eslint --fix file-1.js file-2.js

¥This will result in lint-staged running eslint --fix file-1.js file-2.js, when you have staged files file-1.js, file-2.js and README.md.

将参数传递给你的命令,并用空格分隔,就像在 shell 中一样。请参阅下面的 examples

¥Pass arguments to your commands separated by space as you would do in the shell. See examples below.

按顺序运行多个命令

¥Running multiple commands in a sequence

你可以在每个 glob 上按顺序运行多个命令。为此,请传递一个命令数组,而不是单个命令。这对于运行 eslint --fixstylefmt 等自动格式化工具很有用,但可以用于任何任意序列。

¥You can run multiple commands in a sequence on every glob. To do so, pass an array of commands instead of a single one. This is useful for running autoformatting tools like eslint --fix or stylefmt but can be used for any arbitrary sequences.

例如:

¥For example:

json
{
  "*.js": ["eslint", "prettier --write"]
}

将执行 eslint,如果它以 0 代码退出,它将在所有暂存的 *.js 文件上执行 prettier --write

¥going to execute eslint and if it exits with 0 code, it will execute prettier --write on all staged *.js files.

这将导致 lint-staged 在你暂存文件 file-1.jsfile-2.jsREADME.md 时运行 eslint file-1.js file-2.js,如果通过,则运行 prettier --write file-1.js file-2.js

¥This will result in lint-staged running eslint file-1.js file-2.js, when you have staged files file-1.js, file-2.js and README.md, and if it passes, prettier --write file-1.js file-2.js.

使用 JS 配置文件

¥Using JS configuration files

用 JavaScript 编写配置文件是配置 lint-staged(lint-staged.config.jssimilar 或通过 --config 传递)的最有效方式。从配置文件中,你可以导出单个函数或对象。

¥Writing the configuration file in JavaScript is the most powerful way to configure lint-staged (lint-staged.config.js, similar, or passed via --config). From the configuration file, you can export either a single function or an object.

如果 exports 值是一个函数,它将接收所有暂存文件名的数组。然后,你可以为文件构建自己的匹配器并返回命令字符串或命令字符串数组。这些字符串被认为是完整的,如果需要,应该包括文件名参数。

¥If the exports value is a function, it will receive an array of all staged filenames. You can then build your own matchers for the files and return a command string or an array of command strings. These strings are considered complete and should include the filename arguments, if wanted.

如果 exports 值是一个对象,则其键应该是 glob 匹配(就像在正常的非 js 配置格式中一样)。值可以像在正常配置中一样,也可以是如上所述的单个函数。导出对象中的函数不会接收所有匹配的文件,而是仅接收与相应 glob 键匹配的暂存文件。

¥If the exports value is an object, its keys should be glob matches (like in the normal non-js config format). The values can either be like in the normal config or individual functions like described above. Instead of receiving all matched files, the functions in the exported object will only receive the staged files matching the corresponding glob key.

总而言之,默认情况下,lint-staged 会自动将匹配的暂存文件列表添加到你的命令中,但在使用 JS 函数构建命令时,需要手动执行此操作。例如:

¥To summarize, by default lint-staged automatically adds the list of matched staged files to your command, but when building the command using JS functions it is expected to do this manually. For example:

js
export default {
  '*.js': (stagedFiles) => [`eslint .`, `prettier --write ${stagedFiles.join(' ')}`],
}

这将导致 lint-staged 首先运行 eslint .(匹配所有文件),如果通过,则在你暂存文件 file-1.jsfile-2.jsREADME.md 时运行 prettier --write file-1.js file-2.js

¥This will result in lint-staged first running eslint . (matching all files), and if it passes, prettier --write file-1.js file-2.js, when you have staged files file-1.js, file-2.js and README.md.

JavaScript 函数

¥JavaScript Functions

你还可以配置 lint-staged 直接运行 JavaScript/Node.js 脚本,并将暂存文件列表作为参数传递:

¥You can also configure lint-staged to run a JavaScript/Node.js script directly, passing the list of staged files as an argument:

js
export default {
  '*.js': {
    title: 'Log staged JS files to console',
    task: async (files) => {
      console.log('Staged JS files:', files)
    },
  },
}

示例:导出函数以构建自己的匹配器

¥Example: Export a function to build your own matchers

Click to expand
js
// lint-staged.config.js
import micromatch from 'micromatch'

export default (allStagedFiles) => {
  const shFiles = micromatch(allStagedFiles, ['**/src/**/*.sh'])
  if (shFiles.length) {
    return `printf '%s\n' "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(' ')}`]
}

示例:将文件名括在单引号中并每个文件运行一次

¥Example: Wrap filenames in single quotes and run once per file

Click to expand
js
// .lintstagedrc.js
export default {
  '**/*.js?(x)': (filenames) => filenames.map((filename) => `prettier --write '${filename}'`),
}

示例:对 TypeScript 文件的更改运行 tsc,但不传递任何文件名参数

¥Example: Run tsc on changes to TypeScript files, but do not pass any filename arguments

Click to expand
js
// lint-staged.config.js
export default {
  '**/*.ts?(x)': () => 'tsc -p tsconfig.json --noEmit',
}

示例:如果暂存文件超过 10 个,则在整个 repo 上运行 ESLint

¥Example: Run ESLint on entire repo if more than 10 staged files

Click to expand
js
// .lintstagedrc.js
export default {
  '**/*.js?(x)': (filenames) =>
    filenames.length > 10 ? 'eslint .' : `eslint ${filenames.join(' ')}`,
}

示例:使用你自己的 glob

¥Example: Use your own globs

Click to expand

如果你的用例是这样的,最好使用 基于函数的配置(如上所示)

¥It's better to use the function-based configuration (seen above), if your use case is this.

js
// 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(' ')}`]
  },
}

示例:忽略匹配的文件

¥Example: Ignore files from match

Click to expand

如果出于某种原因你想要忽略 glob 匹配中的文件,则可以使用 micromatch.not()

¥If for some reason you want to ignore files from the glob match, you can use micromatch.not():

js
// 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(' ')}`
  },
}

请注意,在大多数情况下,globs 可以达到相同的效果。对于上述示例,匹配的 glob 将是 !(*test).js

¥Please note that for most cases, globs can achieve the same effect. For the above example, a matching glob would be !(*test).js.

示例:对命令使用相对路径

¥Example: Use relative paths for commands

Click to expand
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(' ')}`
  },
}

重新格式化代码

¥Reformatting the code

Prettier、ESLint/TSLint 或 stylelint 等工具可以通过运行 prettier --write/eslint --fix/tslint --fix/stylelint --fix 根据适当的配置重新格式化你的代码。只要没有错误,Lint-staged 就会自动将任何修改添加到提交中。

¥Tools like Prettier, ESLint/TSLint, or stylelint can reformat your code according to an appropriate config by running prettier --write/eslint --fix/tslint --fix/stylelint --fix. Lint-staged will automatically add any modifications to the commit as long as there are no errors.

json
{
  "*.js": "prettier --write"
}

在版本 10 之前,任务必须手动包含 git add 作为最后一步。此行为已集成到 lint-staged 本身中,以防止多个任务编辑相同文件时出现竞争条件。如果 lint-staged 在任务配置中检测到 git add,它将在控制台中显示警告。升级后,请从配置中删除 git add

¥Prior to version 10, tasks had to manually include git add as the final step. This behavior has been integrated into lint-staged itself in order to prevent race conditions with multiple tasks editing the same files. If lint-staged detects git add in task configurations, it will show a warning in the console. Please remove git add from your configuration after upgrading.

示例

¥Examples

所有示例都假设你已经在 package.json 文件中设置了 lint-staged,并在其自己的配置文件中设置了 husky

¥All examples assume you've already set up lint-staged in the package.json file and husky in its own config file.

json
{
  "name": "My project",
  "version": "0.1.0",
  "scripts": {
    "my-custom-script": "linter --arg1 --arg2"
  },
  "lint-staged": {}
}

.husky/pre-commit

¥In .husky/pre-commit

shell
# .husky/pre-commit

npx lint-staged

注意:我们不将路径作为参数传递给运行器。这很重要,因为 lint-staged 会为你完成此操作。

¥Note: we don't pass a path as an argument for the runners. This is important since lint-staged will do this for you.

具有 *.js*.jsx 默认参数的 ESLint 作为预提交钩子运行

¥ESLint with default parameters for *.js and *.jsx running as a pre-commit hook

Click to expand
json
{
  "*.{js,jsx}": "eslint"
}

使用 --fix 自动修复代码样式并添加到提交

¥Automatically fix code style with --fix and add to commit

Click to expand
json
{
  "*.js": "eslint --fix"
}

这将运行 eslint --fix 并自动将更改添加到提交。

¥This will run eslint --fix and automatically add changes to the commit.

重复使用 npm 脚本

¥Reuse npm script

Click to expand

如果你希望重用 package.json 中定义的 npm 脚本:

¥If you wish to reuse a npm script defined in your package.json:

json
{
  "*.js": "npm run my-custom-script --"
}

以下是等效的:

¥The following is equivalent:

json
{
  "*.js": "linter --arg1 --arg2"
}

在任务命令中使用环境变量

¥Use environment variables with task commands

Click to expand

任务命令不支持扩展环境变量的 shell 约定。要自己启用约定,请使用 cross-env 之类的工具。

¥Task commands do not support the shell convention of expanding environment variables. To enable the convention yourself, use a tool like cross-env.

例如,这里是 jest 在所有 .js 文件上运行,其中 NODE_ENV 变量设置为 "test"

¥For example, here is jest running on all .js files with the NODE_ENV variable being set to "test":

json
{
  "*.js": ["cross-env NODE_ENV=test jest --bail --findRelatedTests"]
}

使用 prettier 自动修复 Prettier 支持的任何格式的代码样式

¥Automatically fix code style with prettier for any format Prettier supports

Click to expand
json
{
  "*": "prettier --ignore-unknown --write"
}

使用 prettier 自动修复 JavaScript、TypeScript、Markdown、HTML 或 CSS 的代码样式

¥Automatically fix code style with prettier for JavaScript, TypeScript, Markdown, HTML, or CSS

Click to expand
json
{
  "*.{js,jsx,ts,tsx,md,html,css}": "prettier --write"
}

使用默认值的 CSS 的 Stylelint 和使用 SCSS 语法的 SCSS 的 Stylelint

¥Stylelint for CSS with defaults and for SCSS with SCSS syntax

Click to expand
json
{
  "*.css": "stylelint",
  "*.scss": "stylelint --syntax=scss"
}

运行 PostCSS 排序和 Stylelint 进行检查

¥Run PostCSS sorting and Stylelint to check

Click to expand
json
{
  "*.scss": ["postcss --config path/to/your/config --replace", "stylelint"]
}

缩小图片

¥Minify the images

Click to expand
json
{
  "*.{png,jpeg,jpg,gif,svg}": "imagemin-lint-staged"
}
More about imagemin-lint-staged

imagemin-lint-staged 是一个 CLI 工具,专为 lint-staged 使用而设计,具有合理的默认值。

¥imagemin-lint-staged is a CLI tool designed for lint-staged usage with sensible defaults.

有关此方法的好处,请参阅 这篇博文 上的更多信息。

¥See more on this blog post for benefits of this approach.

使用 flow 检查暂存文件的类型

¥Typecheck your staged files with flow

Click to expand
json
{
  "*.{js,jsx}": "flow focus-check"
}

与 Next.js 集成

¥Integrate with Next.js

Click to expand
js
// .lintstagedrc.js
// See https://next.nodejs.cn/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],
}

常见问题

¥Frequently Asked Questions

提交钩子的输出看起来很奇怪(没有颜色、重复的行、Windows 上的详细输出等)

¥The output of commit hook looks weird (no colors, duplicate lines, verbose output on Windows, …)

Click to expand

Git 2.36.0 对钩子进行了更改,它们不再在原始 TTY 中运行。这已在 2.37.0 中修复:

¥Git 2.36.0 introduced a change to hooks where they were no longer run in the original TTY. This was fixed in 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 合并到 maint)。

    ¥In Git 2.36 we revamped the way how hooks are invoked. One change that is end-user visible is that the output of a hook is no longer directly connected to the standard output of "git" that spawns the hook, which was noticed post release. This is getting corrected. (merge a082345372 ab/hooks-regression-fix later to maint).

如果更新 Git 没有帮助,你可以尝试手动重定向 Git 钩子中的输出;例如:

¥If updating Git doesn't help, you can try to manually redirect the output in your Git hook; for example:

shell
# .husky/pre-commit

if sh -c ": >/dev/tty" >/dev/null 2>/dev/null; then exec >/dev/tty 2>&1; fi

npx lint-staged

来源:https://github.com/typicode/husky/issues/968#issuecomment-1176848345

¥Source: https://github.com/typicode/husky/issues/968#issuecomment-1176848345

我可以通过 node 使用 lint-staged 吗?

¥Can I use lint-staged via node?

Click to expand

是的!

¥Yes!

js
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 对应项:

¥Parameters to lintStaged are equivalent to their CLI counterparts:

js
const success = await lintStaged({
  allowEmpty: false,
  concurrent: true,
  configPath: './path/to/configuration/file',
  cwd: process.cwd(),
  debug: false,
  maxArgLength: null,
  quiet: false,
  relative: false,
  stash: true,
  verbose: false,
})

你也可以使用 config 选项直接传递配置:

¥You can also pass config directly with config option:

js
const success = await lintStaged({
  allowEmpty: false,
  concurrent: true,
  config: { '*.js': 'eslint --fix' },
  cwd: process.cwd(),
  debug: false,
  maxArgLength: null,
  quiet: false,
  relative: false,
  stash: true,
  verbose: false,
})

maxArgLength 选项将任务分块配置为多个部分,一个接一个地运行。这是为了避免在 Windows 平台上出现问题,因为 Windows 平台上命令行参数字符串的最大长度限制为 8192 个字符。当有许多暂存文件时,Lint-staged 可能会生成非常长的参数字符串。此选项从 cli 自动设置,但默认情况下不通过 Node.js API 设置。

¥The maxArgLength option configures chunking of tasks into multiple parts that are run one after the other. This is to avoid issues on Windows platforms where the maximum length of the command line argument string is limited to 8192 characters. Lint-staged might generate a very long argument string when there are many staged files. This option is set automatically from the cli, but not via the Node.js API by default.

与 JetBrains IDE(WebStorm、PyCharm、IntelliJ IDEA、RubyMine 等)一起使用

¥Using with JetBrains IDEs (WebStorm, PyCharm, IntelliJ IDEA, RubyMine, etc.)

Click to expand

更新:正如你所期望的那样,最新版本的 JetBrains IDE 现在支持运行钩子。

¥Update: The latest version of JetBrains IDEs now support running hooks as you would expect.

使用 IDE 的 GUI 通过 precommit 钩子提交更改时,你可能会看到 IDE 和命令行中的不一致。这是 JetBrains 的 已知问题,因此如果你希望修复此问题,请在 YouTrack 上投票。

¥When using the IDE's GUI to commit changes with the precommit hook, you might see inconsistencies in the IDE and command line. This is known issue at JetBrains so if you want this fixed, please vote for it on YouTrack.

在 IDE 中解决问题之前,你可以使用以下配置来解决它:

¥Until the issue is resolved in the IDE, you can use the following config to work around it:

husky v1.x

json
{
  "husky": {
    "hooks": {
      "pre-commit": "lint-staged",
      "post-commit": "git update-index --again"
    }
  }
}

husky v0.x

json
{
  "scripts": {
    "precommit": "lint-staged",
    "postcommit": "git update-index --again"
  }
}

感谢 此评论 的修复!

¥Thanks to this comment for the fix!

如何在多包 monorepo 中使用 lint-staged

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

Click to expand

在 monorepo 根级别安装 lint-staged,并在每个包中添加单独的配置文件。运行时,lint-staged 将始终使用最接近暂存文件的配置,因此拥有单独的配置文件可确保任务不会 "leak" 进入其他包。

¥Install lint-staged on the monorepo root level, and add separate configuration files in each package. When running, lint-staged will always use the configuration closest to a staged file, so having separate configuration files makes sure tasks do not "leak" into other packages.

例如,在具有 packages/frontend/.lintstagedrc.jsonpackages/backend/.lintstagedrc.json 的 monorepo 中,packages/frontend/ 内的暂存文件将仅匹配该配置,而不匹配 packages/backend/ 中的配置。

¥For example, in a monorepo with packages/frontend/.lintstagedrc.json and packages/backend/.lintstagedrc.json, a staged file inside packages/frontend/ will only match that configuration, and not the one in packages/backend/.

注意:lint-staged 发现与每个暂存文件最接近的配置,即使该配置不包含任何匹配的 glob。给出这些示例配置:

¥Note: lint-staged discovers the closest configuration to each staged file, even if that configuration doesn't include any matching globs. Given these example configurations:

js
// ./.lintstagedrc.json
{ "*.md": "prettier --write" }
js
// ./packages/frontend/.lintstagedrc.json
{ "*.js": "eslint --fix" }

提交 ./packages/frontend/README.md 时,它不会运行 prettier,因为 frontend/ 目录中的配置更接近该文件并且不包含它。你应该将所有 lint-staged 配置文件视为彼此隔离和分离的。你始终可以将 JS 文件用于 "extend" 配置,例如:

¥When committing ./packages/frontend/README.md, it will not run prettier, because the configuration in the frontend/ directory is closer to the file and doesn't include it. You should treat all lint-staged configuration files as isolated and separated from each other. You can always use JS files to "extend" configurations, for example:

js
import baseConfig from '../.lintstagedrc.js'

export default {
  ...baseConfig,
  '*.js': 'eslint --fix',
}

为了支持向后兼容性,monorepo 功能需要 git repo 中存在多个 lint-staged 配置文件。如果你仍想在 monorepo 中的一个包中运行 lint-staged,则可以使用 --cwd 选项(例如 lint-staged --cwd packages/frontend)。

¥To support backwards-compatibility, monorepo features require multiple lint-staged configuration files present in the git repo. If you still want to run lint-staged in only one of the packages in a monorepo, you can use the --cwd option (for example, lint-staged --cwd packages/frontend).

我可以对当前项目文件夹之外的文件进行 lint 吗?

¥Can I lint files outside of the current project folder?

Click to expand

tl;dr:是的,但模式应以 ../ 开头。

¥tl;dr: Yes, but the pattern should start with ../.

默认情况下,lint-staged 仅对项目文件夹(安装和运行 lint-staged 的位置)内的文件执行任务。因此,只有当项目文件夹是 git repo 内的子文件夹时,这个问题才有意义。在某些项目设置中,可能需要绕过此限制。有关更多上下文,请参阅 #425#487

¥By default, lint-staged executes tasks only on the files present inside the project folder(where lint-staged is installed and run from). So this question is relevant only when the project folder is a child folder inside the git repo. In certain project setups, it might be desirable to bypass this restriction. See #425, #487 for more context.

lint-staged 为相同(>= v7.3.0)提供了一个逃生舱。对于以 ../ 开头的模式,所有暂存文件都可以与该模式匹配。请注意,像 *.js**/*.js 这样的模式仍然只匹配项目文件,而不匹配父级或同级目录中的任何文件。

¥lint-staged provides an escape hatch for the same(>= v7.3.0). For patterns that start with ../, all the staged files are allowed to match against the pattern. Note that patterns like *.js, **/*.js will still only match the project files and not any of the files in parent or sibling directories.

示例 repo:sudo-suhas/lint-staged-django-react-demo

¥Example repo: sudo-suhas/lint-staged-django-react-demo.

我可以在 CI 中运行 lint-staged 吗,或者在没有暂存文件时运行 lint-staged 吗?

¥Can I run lint-staged in CI, or when there are no staged files?

Click to expand

Lint-staged 默认将针对 git 中暂存的文件运行,例如,应该在 git 预提交钩子期间运行。也可以覆盖此默认行为并针对特定差异中的文件运行,例如两个不同分支之间所有更改的文件。如果你想在 CI 中运行 lint-staged,也许你可​​以将其设置为将 Pull 请求/合并请求中的分支与目标分支进行比较。

¥Lint-staged will by default run against files staged in git, and should be run during the git pre-commit hook, for example. It's also possible to override this default behaviour and run against files in a specific diff, for example all changed files between two different branches. If you want to run lint-staged in the CI, maybe you can set it up to compare the branch in a Pull Request/Merge Request to the target branch.

尝试 git diff 命令,直到你对结果满意为止,例如:

¥Try out the git diff command until you are satisfied with the result, for example:

git diff --diff-filter=ACMR --name-only main...my-branch

这将打印 mainmy-branch 之间添加、更改、修改和重命名的文件列表。

¥This will print a list of added, changed, modified, and renamed files between main and my-branch.

然后,你可以使用以下方法对相同的文件运行 lint-staged:

¥You can then run lint-staged against the same files with:

npx lint-staged --diff="main...my-branch"

我可以将 lint-stagedng lint 一起使用吗?

¥Can I use lint-staged with ng lint

Click to expand

你不应该通过 lint-staged 使用 ng lint,因为它旨在对整个项目进行 lint。相反,你可以像运行 lint-staged 一样将 ng lint 添加到你的 git pre-commit 钩子中。

¥You should not use ng lint through lint-staged, because it's designed to lint an entire project. Instead, you can add ng lint to your git pre-commit hook the same way as you would run lint-staged.

有关更多详细信息和可能的解决方法,请参阅问题 !951

¥See issue !951 for more details and possible workarounds.

如何忽略来自 .eslintignore 的文件?

¥How can I ignore files from .eslintignore?

Click to expand

ESLint 抛出 warning File ignored because of a matching ignore pattern. Use "--no-ignore" to override 警告,破坏了 linting 过程(如果你使用了推荐的 --max-warnings=0)。

¥ESLint throws out warning File ignored because of a matching ignore pattern. Use "--no-ignore" to override warnings that breaks the linting process ( if you used --max-warnings=0 which is recommended ).

ESLint < 7

Click to expand

根据 这个问题 中的讨论,决定使用 概述的脚本 是解决此问题的最佳途径。

¥Based on the discussion from this issue, it was decided that using the outlined scriptis the best route to fix this.

因此,你可以设置一个 .lintstagedrc.js 配置文件来执行此操作:

¥So you can setup a .lintstagedrc.js config file to do this:

js
import { CLIEngine } from 'eslint'

export default {
  '*.js': (files) => {
    const cli = new CLIEngine({})
    return 'eslint --max-warnings=0 ' + files.filter((file) => !cli.isPathIgnored(file)).join(' ')
  },
}

ESLint >= 7

Click to expand

在 ESLint > 7 版本中,isPathIgnored 是一个异步函数,现在返回一个 promise。下面的代码可用于恢复上述功能。

¥In versions of ESLint > 7, isPathIgnored is an async function and now returns a promise. The code below can be used to reinstate the above functionality.

由于 10.5.3,由于错误的 ESLint 配置导致的任何错误都将通过控制台传输。

¥Since 10.5.3, any errors due to a bad ESLint config will come through to the console.

js
import { ESLint } from 'eslint'

const removeIgnoredFiles = async (files) => {
  const eslint = new ESLint()
  const isIgnored = await Promise.all(
    files.map((file) => {
      return eslint.isPathIgnored(file)
    })
  )
  const filteredFiles = files.filter((_, i) => !isIgnored[i])
  return filteredFiles.join(' ')
}

export default {
  '**/*.{ts,tsx,js,jsx}': async (files) => {
    const filesToLint = await removeIgnoredFiles(files)
    return [`eslint --max-warnings=0 ${filesToLint}`]
  },
}

ESLint >= 8.51.0 && Flat ESLint 配置

¥ESLint >= 8.51.0 && Flat ESLint config

Click to expand

ESLint v8.51.0 引入了 --no-warn-ignored CLI 标志。它会抑制 warning File ignored because of a matching ignore pattern. Use "--no-ignore" to override 警告,因此不再需要通过 eslint.isPathIgnored 手动忽略文件。

¥ESLint v8.51.0 introduced --no-warn-ignored CLI flag. It suppresses the warning File ignored because of a matching ignore pattern. Use "--no-ignore" to override warning, so manually ignoring files via eslint.isPathIgnored is no longer necessary.

json
{
  "*.js": "eslint --max-warnings=0 --no-warn-ignored"
}

注意:--no-warn-ignored 标志仅在使用 Flat ESLint 配置 时可用。

¥NOTE: --no-warn-ignored flag is only available when Flat ESLint config is used.

lint-staged 通过 Husky 钩子运行时,如何解决 TypeScript (tsc) 忽略 tsconfig.json 的问题?

¥How can I resolve TypeScript (tsc) ignoring tsconfig.json when lint-staged runs via Husky hooks?

Click to expand

通过 Husky 钩子运行 lint-staged 时,TypeScript 可能会忽略 tsconfig.json,从而导致以下错误:

¥When running lint-staged via Husky hooks, TypeScript may ignore tsconfig.json, leading to errors like:

TS17004:除非提供 '--jsx' 标志,否则无法使用 JSX。TS1056:访问器仅在针对 ECMAScript 5 及更高版本时可用。

¥TS17004: Cannot use JSX unless the '--jsx' flag is provided.
TS1056: Accessors are only available when targeting ECMAScript 5 and higher.

有关更多详细信息,请参阅问题 #825

¥See issue #825 for more details.

根本原因

¥Root Cause

  1. lint-staged 自动将匹配的暂存文件作为参数传递给命令。

    ¥lint-staged automatically passes matched staged files as arguments to commands.

  2. 某些输入文件可能会导致 TypeScript 忽略 tsconfig.json。有关更多详细信息,请参阅此 TypeScript 问题:指定输入文件时允许 tsconfig.json

    ¥Certain input files can cause TypeScript to ignore tsconfig.json. For more details, see this TypeScript issue: Allow tsconfig.json when input files are specified.

解决方法:对 tsc 命令使用 函数签名

¥Workaround: Use a function signature for the tsc command

正如 @antoinerousseau 在 #825 (评论) 中所建议的,使用函数可防止 lint-staged 附加文件参数:

¥As suggested by @antoinerousseau in #825 (comment), using a function prevents lint-staged from appending file arguments:

之前:

¥Before:

js
// package.json

"lint-staged": {
    "*.{ts,tsx}":[
      "tsc --noEmit",
      "prettier --write"
    ]
  }

之后:

¥After:

js
// lint-staged.config.js
module.exports = {
  '*.{ts,tsx}': [() => 'tsc --noEmit', 'prettier --write'],
}