入门
速查
- 配置文件:
lint-staged.config.ts/packagge.json - 配置函数签名:
(filenames: string[]) => string | string[] | Promise<string | string[]>; - 匹配方式:
glob - 并发:
--concurrent <number>/--concurrent false
安装
pnpm add -D lint-staged配置
Lint-staged 包含多种配置方式
通过 package.json
{
"lint-staged": {
"*": "your-cmd"
}
}通过 .lintstagedrc
{
"*": "your-cmd"
}文件后缀可以是:
.lintstagedrc.json.lintstagedrc.yaml.lintstagedrc.yml
使用 ESM 或 CommonJS
ESM 格式文件:
.lintstagedrc.mjslint-staged.config.mjs
export default { ... }CommonJS 格式文件:
.lintstagedrc.cjslint-staged.config.cjs
module.exports = { ... }TIP
如果后缀是 .js 则会根据 package.json 中的 "type": "module" 选项自动判断为 ESM 或 CommonJS
TypeScript 支持
当你的 Node.js 版本小于 22.6.0 时,只能使用 JSDoc 添加类型信息
/**
* @filename: lint-staged.config.js
* @type {import('lint-staged').Configuration}
*/
export default {
'*': 'prettier --write',
}当你的 Node.js 版本支持原生执行 TS 时,可以直接运行 lint-staged.config.ts
举个例子:
// lint-staged.config.ts
import type { Configuration } from 'lint-staged';
// 定义配置对象
const config: Configuration = {
// 匹配所有文件,运行 prettier 格式化
'*': 'prettier --write --ignore-unknown',
// 针对 TypeScript 和 JavaScript 文件,先运行 ESLint 修复,再运行 Prettier
'*.{ts,tsx,js,jsx}': [
'eslint --fix',
'prettier --write',
],
// 针对 CSS 文件,运行 stylelint 修复和 prettier 格式化
'*.css': [
'stylelint --fix',
'prettier --write',
],
// 针对 Markdown 文件,只运行 prettier
'*.md': 'prettier --write',
// 示例:动态生成命令(使用函数)
'*.ts': (filenames: string[]) => {
// 仅对修改的文件运行类型检查
return `tsc --noEmit --files ${filenames.join(' ')}`;
},
};
// 默认导出配置
export default config;使用函数时的函数签名:
(filenames: string[]) => string | string[] | Promise<string | string[]>当你的 Node.js 版本介于 22.6.0 和 23.6.0 之间时,你需要添加参数用来执行 .ts 文件
export NODE_OPTIONS="--experimental-strip-types"
npx lint-staged --config lint-staged.config.ts任务并发性
默认情况下,lint-staged 会并发运行配置的任务。这意味着对于每个 Glob 模式,所有命令会同时启动
{
"*.ts": "eslint",
"*.md": "prettier --list-different"
}假如你想按顺序执行,你可以把指令放入一个数组中
{
"*.ts": ["prettier --list-different", "eslint"],
"*.md": "prettier --list-different"
}规避规则重叠导致文件竞争
当配置的 Glob 模式重叠,且任务会修改文件时,可能出现竞争条件,比如:
{
"*": "prettier --write",
"*.ts": "eslint --fix"
}该问题只能手动规避,有两种规避手段:
使用否定模式
json{ "!(*.ts)": "prettier --write", "*.ts": ["eslint --fix", "prettier --write"] }--concurrent限制并发--concurrent <number>限制并发任务数量--concurrent false完全禁用并发
筛选文件
使用 MicroMatch 来进行 glob 文件匹配
不含斜杠(/)的 Glob 模式
用 micromatch 的 matchBase 选项,仅匹配文件名(忽略目录路径)
示例:
"*.js":匹配所有 JS 文件,例如/test.js和/foo/bar/test.js。"!(*test).js":匹配除以test.js结尾的 JS 文件,例如foo.js,但不匹配foo.test.js。"!(*.css|*.js)":匹配除 CSS 和 JS 文件外的所有文件。
含斜杠(/)的 Glob 模式
会同时匹配路径和文件名
示例:
"./*.js":仅匹配 Git 仓库根目录下的 JS 文件,例如/test.js,但不匹配/foo/bar/test.js。"foo/**/*.js":匹配/foo目录及其子目录下的所有 JS 文件,例如/foo/bar/test.js,但不匹配/test.js。
文件过滤流程
- 自动解析 Git 根目录:无需手动配置。
- 获取暂存文件:从项目的暂存区(staged files)中提取文件。
- 应用 Glob 过滤:使用指定的 Glob 模式筛选文件。
- 传递绝对路径:将匹配文件的绝对路径作为参数传递给任务。
WARNING
lint-staged 总是传递文件的绝对路径给任务,避免因工作目录不同(例如 .git 目录和 package.json 目录不一致)导致的混淆。
忽略文件
优先使用工具本身(eslint、prettier)的忽略机制,而不是依赖 lint-staged
使用 Prettier 忽略文件
.prettierignore
# 忽略构建输出目录
dist/
build/
# 忽略第三方库目录
vendor/
node_modules/
# 忽略特定文件类型
*.min.js
*.log
# 忽略特定文件
package-lock.json使用 ESLint v9 忽略文件
eslint.config.js
// ESLint v9 使用 ignores 的方式
export default [
// 全局忽略配置
{
ignores: ['dist/**', 'vendor/**', '**/*.test.js'],
},
// 规则配置
{
files: ['**/*.{js,ts,jsx,tsx}'],
languageOptions: {
ecmaVersion: 2021,
sourceType: 'module',
},
rules: {
'no-unused-vars': 'error',
'no-console': 'warn',
},
},
];// ESLint v9 使用 globalIgnores() 的方式
import { defineConfig, globalIgnores } from 'eslint/config';
export default defineConfig([
// 全局忽略配置
globalIgnores(['dist/**', 'vendor/**', '**/*.test.js'], 'Custom Global Ignores'),
// 规则配置
{
files: ['**/*.{js,ts,jsx,tsx}'],
languageOptions: {
ecmaVersion: 2021,
sourceType: 'module',
},
rules: {
'no-unused-vars': 'error',
'no-console': 'warn',
},
},
]);高级场景:动态过滤文件
lint-staged.config.ts
import type { Configuration } from 'lint-staged';
const config: Configuration = {
'*.{js,ts}': (filenames: string[]) => {
const filteredFiles = filenames.filter(
(file) => !file.includes('node_modules/') && !file.includes('dist/')
);
return filteredFiles.length > 0 ? [`eslint --fix ${filteredFiles.join(' ')}`] : [];
},
};
export default config;