lint-staged とは?husky v9 での設定方法と動かない時の原因と対策【npx lint-staged / pre-commit】

公開日:2026-06-01/カテゴリ:Git・Node.js・開発環境/AnchorUp 技術ブログ

結論: lint-staged とは?husky とどう組み合わせるのか

lint-staged は「git add でステージしたファイルだけ」に ESLint や Prettier などのコマンドを実行するツールです。 単体では動かず、husky が作る Git の pre-commit フックから npx lint-staged を呼ぶことで「コミットするたびに変更ファイルだけ自動で lint・整形する」仕組みになります。

ツール 役割 設定する場所
husky Git フック(pre-commit など)を npm プロジェクトで管理・共有する .husky/pre-commit、package.json の "prepare": "husky"
lint-staged ステージ済みファイルを glob で絞り込み、指定コマンドを実行する package.json の "lint-staged" または lint-staged.config.mjs など

最小構成(husky v9 + lint-staged)はこれだけです。

npm install --save-dev husky lint-staged
npx husky init
echo "npx lint-staged" > .husky/pre-commit
{
  "scripts": {
    "prepare": "husky"
  },
  "lint-staged": {
    "*.{js,jsx,ts,tsx}": ["eslint --fix", "prettier --write"],
    "*.{json,md,css}": "prettier --write"
  }
}

リポジトリ全体ではなく変更したファイルだけを対象にするので、大きなプロジェクトでもコミット時のチェックが数秒で終わるのが最大のメリットです。


husky v9 + lint-staged の設定方法は?(ステップ別)

手順1: パッケージをインストールする

パッケージマネージャー インストール husky 初期化
npm npm install --save-dev husky lint-staged npx husky init
pnpm pnpm add --save-dev husky lint-staged pnpm exec husky init
Bun bun add --dev husky lint-staged bunx husky init
Yarn yarn add --dev husky lint-staged 下記「Yarn の注意」参照

手順2: husky init が何をするかを理解する

husky v9 の husky init は次の3つを行います(husky のソースコード bin.js で確認できます)。

  1. package.json の scripts.prepare を "husky" に書き換える
  2. git config core.hooksPath .husky/_ を設定し、.husky/_/ にフックの中継スクリプトを生成する
  3. .husky/pre-commit を作成し、中身を npm test(pnpm なら pnpm test)にする

ここが最初のハマりポイントです。 husky init 直後の pre-commit は lint-staged ではなく npm test を実行します。test スクリプトがないと、コミット時に npm error Missing script: "test" で失敗します。必ず中身を書き換えてください。

# .husky/pre-commit(v9 はこの1行だけでよい。shebang も不要)
npx lint-staged

手順3: lint-staged の設定を書く

設定は以下のいずれかに書けます(lint-staged 公式 README より)。

形式 ファイル名
package.json "lint-staged" キー
JSON / YAML .lintstagedrc, .lintstagedrc.json, .lintstagedrc.yaml, .lintstagedrc.yml
ESM lint-staged.config.mjs, .lintstagedrc.mjs
CommonJS lint-staged.config.cjs, .lintstagedrc.cjs
JS("type" 次第) lint-staged.config.js, .lintstagedrc.js

関数形式を使うと、TypeScript の型チェックのように「ファイル名を渡したくない」コマンドも扱えます。

// lint-staged.config.mjs
export default {
  '*.{ts,tsx}': [
    // 関数で返すとファイル名が付与されない → tsconfig.json が使われる
    () => 'tsc --noEmit',
    'eslint --fix',
    'prettier --write',
  ],
  '*.{json,md,css}': 'prettier --write',
}

tsc にファイル名を渡すと tsconfig.json が無視されるため、この書き方が公式 README でも推奨されています。

手順4: Yarn の注意

husky 公式ドキュメント(How To)には「Yarn doesn’t support prepare script」とあり、Yarn では prepare の代わりに postinstall を使います。公開パッケージの場合は pinst で publish 時に無効化します。

{
  "scripts": {
    "postinstall": "husky",
    "prepack": "pinst --disable",
    "postpack": "pinst --enable"
  }
}

バージョン別の Node.js 要件(2026年10月時点)

パッケージ メジャー 必要な Node.js(engines)
lint-staged v15 >=18.12.0
lint-staged v16(2025年5月〜) >=20.17
lint-staged v17(2026年5月〜) >=22.22.1
husky v9(最新 9.1.x) —

古い Node.js の CI イメージやチームメンバーの環境で lint-staged が動かない場合、まず node -v とインストールされた lint-staged のメジャーバージョンを照合してください。


husky + lint-staged が動かない時の原因一覧

症状 原因 該当セクション
husky - install command is DEPRECATED が出る v8 の手順(husky install)のまま 原因1
husky - add command is DEPRECATED で失敗 v8 の husky add を使っている 原因1
コミット時に husky - DEPRECATED が出る 古いフックの husky.sh 読み込み行が残っている 原因1
コミットしても何も起きない core.hooksPath が設定されていない/prepare が走っていない 原因2・原因6
hint: ... hook was ignored because it's not set as executable フックファイルの実行権限がない 原因2
.git can't be found package.json が Git ルートにない(monorepo) 原因3
husky - command not found in PATH=... GUI クライアントや nvm 環境で PATH が通っていない 原因4
lint-staged could not find any staged files matching configured tasks. glob パターンが合っていない 原因5
lint-staged failed due to a git error. stash/部分ステージの処理に失敗 原因5
CI や本番 npm ci で失敗する CI で husky が起動している/devDependencies がない 原因6

原因1: husky v8 の手順のまま設定している(v9 の破壊的変更)

結論: husky v9 では husky install / husky add は非推奨または廃止です。prepare は "husky"、フックファイルはコマンドを直接書くだけにしてください。

ネット上の古い記事は v8 の手順で書かれていることが多く、そのままコピーすると次のようなメッセージが出ます(いずれも husky v9 のソースコードにある文言です)。

husky - install command is DEPRECATED
husky - add command is DEPRECATED

husky install は警告を出しつつ動きますが、husky add / husky set / husky uninstall は 終了コード1で失敗します。

v8(旧)と v9(新)の比較

項目 v8 v9
初期化コマンド npx husky install npx husky init
prepare スクリプト "prepare": "husky install" "prepare": "husky"
フック追加 npx husky add .husky/pre-commit "npx lint-staged" .husky/pre-commit を直接編集
フックファイルの形式 _/husky.sh を source する コマンドを直接書く
core.hooksPath .husky .husky/_

古いフックファイルの2行は削除する

v8 から v9 にアップグレードしたプロジェクトでは、フックの先頭にこの2行が残っていることがあります。

#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

npx lint-staged

v9 はこの husky.sh を「警告を表示するだけのファイル」に置き換えるため、コミット時に次のメッセージが出ます。

husky - DEPRECATED

Please remove the following two lines from .husky/pre-commit:

#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

They WILL FAIL in v10.0.0

対策: 2行を削除し、コマンドだけを残します。

# .husky/pre-commit
npx lint-staged

原因2: フックが実行されない(core.hooksPath・実行権限)

結論: まず git config core.hooksPath が .husky/_ になっているか確認します。なっていなければ npm install(= prepare)か npx husky を実行してください。

git config core.hooksPath
# v9 なら → .husky/_

何も表示されない場合、Git は .git/hooks を見ているため husky のフックは一切実行されません。clone 直後に npm install をしていない、--ignore-scripts でインストールした、などが典型です。

npx husky   # core.hooksPath の設定と .husky/_ の生成をやり直す

「hook was ignored because it’s not set as executable」が出る場合

macOS / Linux では、Git が直接実行するフックファイルに実行権限がないと無視され、次のヒントが出ます。

hint: The '.husky/_/pre-commit' hook was ignored because it's not set as executable.
hint: You can disable this warning with `git config advice.ignoredHook false`.

v9 では Git が直接実行するのは .husky/_/ 配下の中継スクリプトで、あなたが書く .husky/pre-commit は中継スクリプトから sh -e で読み込まれます。そのため通常は .husky/pre-commit 自体に実行権限は不要です。中継スクリプトは husky が 0755 で生成するので、権限が壊れた場合は npx husky で再生成するのが確実です。

v8 系を使っている場合は、Git が .husky/pre-commit を直接実行するため実行権限が必要です。

chmod +x .husky/pre-commit
# Git に実行ビットを記録してチームに共有する
git update-index --chmod=+x .husky/pre-commit

原因3: monorepo で「.git can’t be found」と出る

結論: husky は package.json のあるディレクトリに .git がないとインストールされません。prepare で Git ルートに移動してから husky <サブディレクトリ>/.husky を実行します。

frontend/ のようなサブディレクトリに package.json があるリポジトリで npm install すると、husky は次のメッセージを出してフックを設定しません(エラー終了しないので見落としがちです)。

.git can't be found

husky はセキュリティ上の理由で親ディレクトリ(../)を指定したインストールを許可していません(.. not allowed)。公式ドキュメントの方法は次のとおりです。

{
  "scripts": {
    "prepare": "cd .. && husky frontend/.husky"
  }
}
# frontend/.husky/pre-commit
cd frontend
npx lint-staged

フックは Git ルートで実行されるため、cd frontend を忘れると lint-staged が設定ファイルを見つけられず lint-staged could not find any valid configuration. になります。


原因4: 「command not found」で lint-staged が実行されない

結論: Git GUI クライアント(VS Code のソース管理、Sourcetree など)から nvm / fnm / Volta 管理の Node.js が見えていません。~/.config/husky/init.sh に PATH の初期化を書きます。

husky v9 はフック失敗時に次のように出力します。終了コード 127 はシェルの「コマンドが見つからない」です(PATH= 以降は環境によって異なります)。

husky - pre-commit script failed (code 127)
husky - command not found in PATH=node_modules/.bin:/usr/bin:/bin:...

ターミナルからの git commit は通るのに GUI からだと失敗する、という場合はほぼこれです。husky は各フックの実行前に ~/.config/husky/init.sh を読み込むので、そこにバージョンマネージャーの初期化を書きます。

# ~/.config/husky/init.sh(nvm の例)
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"

なお、v8 時代の ~/.huskyrc は v9 では非推奨で、残っていると husky - '~/.huskyrc' is DEPRECATED, please move your code to ~/.config/husky/init.sh が表示されます。


原因5: lint-staged の設定が認識されない・対象ファイルがない

結論: npx lint-staged --verbose を実行し、出力メッセージで「設定がない」のか「マッチするファイルがない」のかを切り分けます。

メッセージ 意味 対策
lint-staged could not find any valid configuration. 設定ファイルが見つからない/読めない ファイル名・置き場所(monorepo なら cd)を確認
lint-staged could not find any staged files. git add されたファイルがない git add してから実行
lint-staged could not find any staged files matching configured tasks. glob にマッチしない パターンを見直す
lint-staged prevented an empty git commit. 修正の結果、差分がゼロになった 想定どおりなら --allow-empty

glob パターンのルール(よくある設定ミス)

lint-staged は picomatch でマッチングし、スラッシュを含まないパターンはファイル名(basename)だけで判定、スラッシュを含むパターンは設定ファイルの場所からの相対パスで判定します。

{
  "lint-staged": {
    "*.ts": "eslint --fix",
    "src/**/*.ts": "eslint --fix",
    "./*.ts": "eslint --fix"
  }
}
パターン マッチする範囲
*.ts リポジトリ内のすべての .ts(どの階層でも)
src/**/*.ts src/ 配下の .ts のみ
./*.ts 設定ファイルと同じ階層の .ts のみ

「src/ 以外のファイルがチェックされない」という場合は、*.{ts,tsx} のようにスラッシュなしで書くのが簡単です。

設定にある git add は削除する

lint-staged v10 以降は、タスクによる修正を自動でコミットに含めます。古い設定の "eslint --fix", "git add" が残っていると、次の警告が出ます。

⚠ Some of your tasks use `git add` command. Please remove it from the config since all modifications made by tasks will be automatically added to the git commit index.

「lint-staged failed due to a git error」が出た時の復元方法

lint-staged は実行前に変更をバックアップの stash として退避し、部分的にステージしたファイルの未ステージ部分も一時的に隠します。この処理で Git がエラーになると次のように出ます。

✖ lint-staged failed due to a git error.
Any lost modifications can be restored from a git stash:

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

表示どおり git stash list で lint-staged automatic backup を探し、git apply --index <ハッシュ> で戻します。初回コミット前のリポジトリ(コミットが1つもない状態)ではバックアップ自体がスキップされ、⚠ Skipping backup because there’s no initial commit yet. This might result in data loss. と表示されるので注意してください。


原因6: CI・本番環境で husky がエラーになる・prepare が走らない

結論: CI では HUSKY=0 でフックを無効化し、devDependencies を入れない本番インストールでは prepare を失敗させない書き方にします。

GitHub Actions では HUSKY=0 を設定する

# .github/workflows/ci.yml
jobs:
  build:
    runs-on: ubuntu-latest
    env:
      HUSKY: 0
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '22'
      - run: npm ci

HUSKY=0 のとき husky はインストールをスキップし、HUSKY=0 skip install と表示します。これはエラーではなく正常な動作です。

devDependencies がない環境で prepare が失敗する

npm ci --omit=dev や Docker の本番ステージでは husky がインストールされていないため、prepare の husky が sh: husky: command not found で失敗します。公式ドキュメントの対策は2つです。

{
  "scripts": {
    "prepare": "husky || true"
  }
}

または .husky/install.mjs で環境を判定します。

// .husky/install.mjs
if (process.env.NODE_ENV === 'production' || process.env.CI === 'true') {
  process.exit(0)
}
const husky = (await import('husky')).default
console.log(husky())
{
  "scripts": {
    "prepare": "node .husky/install.mjs"
  }
}

–ignore-scripts でインストールすると hooks が設定されない

npm ci --ignore-scripts や pnpm install --ignore-scripts では prepare が実行されないため、core.hooksPath が設定されずローカルでもフックが動きません。セキュリティ目的で ignore-scripts=true を .npmrc に入れている場合は、インストール後に npx husky を手動で実行してください。


動作確認・デバッグの手順

設定後、上から順に確認すると原因をほぼ特定できます。

  1. hooksPath の確認: git config core.hooksPath → .husky/_ ならOK
  2. フックの中身の確認: cat .husky/pre-commit → npx lint-staged になっているか(npm test のままになっていないか)
  3. lint-staged 単体で実行: git add してから npx lint-staged --verbose --debug
  4. husky のデバッグモード: HUSKY=2 git commit -m "test" でフック実行時のシェルを set -x 付きで表示
  5. コミットせずにテスト: .husky/pre-commit の末尾に一時的に exit 1 を入れてコミットし、lint-staged の実行だけを確認(公式推奨の方法)

緊急時に一度だけフックを飛ばしたい場合は git commit -n(--no-verify)が使えます。


まとめ

症状 原因 対処法
install/add command is DEPRECATED v8 の手順のまま "prepare": "husky" に変更し、フックは直接編集
husky - DEPRECATED(コミット時) 古い husky.sh の2行が残っている 2行を削除
コミットしても何も起きない core.hooksPath 未設定 npx husky を実行
npm error Missing script: "test" husky init の npm test のまま pre-commit を npx lint-staged に書き換え
.git can't be found monorepo のサブディレクトリ cd .. && husky frontend/.husky
command not found in PATH GUI + nvm 等 ~/.config/husky/init.sh で PATH を設定
could not find any staged files matching glob の書き方 スラッシュなしの *.{ts,tsx} などに
CI で失敗 CI で husky が起動 HUSKY=0 を設定

lint-staged 自体はシンプルなツールですが、「husky v9 の新しい手順」と「glob のマッチングルール」の2点を押さえれば大半のトラブルは解消します。lint-staged から呼ぶ ESLint 側でエラーになる場合は ESLint v9 で動かない・エラーになる時の対処法、ESLint + Prettier を Biome に置き換えて高速化したい場合は Biome への移行で動かない時の対処法 も参考にしてください。

技術ブログの記事一覧へ