Skip to content

Commit 2fcd871

Browse files
committed
build(deps-dev): Migrate husky and lint-staged to lefthook (#2988)
1 parent 6571c11 commit 2fcd871

File tree

6 files changed

+394
-233
lines changed

6 files changed

+394
-233
lines changed

.husky/pre-commit

Lines changed: 0 additions & 1 deletion
This file was deleted.

CONTRIBUTING.md

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
- 文法およびフォーマットチェッカー
5050
- [ESLint](https://eslint.org/)
5151
- [Prettier](https://prettier.io/)
52+
- [lefthook](https://github.com/evilmartians/lefthook): Git hooks 管理ツール(コミット前の自動フォーマット・リント)
5253
- Search Engine Optimization (SEO) 対策
5354
- [Svelte Meta Tags](https://github.com/oekazuma/svelte-meta-tags): メタタグ、Open Graph などの設定
5455
- [super-sitemap](https://github.com/jasongitmail/super-sitemap): SvelteKit 専用の sitemap ジェネレータ
@@ -268,6 +269,35 @@
268269

269270
6. プルリクエストを作成します。
270271

272+
### Git Hooks(フォーマット・リント)
273+
274+
本プロジェクトでは、[lefthook](https://github.com/evilmartians/lefthook)を使用して、コミット前に自動的にコードの書式チェック・フォーマットを行います。
275+
276+
- **Pre-commit Hook**: ステージ済みファイルのみに対して以下を実行
277+
- `prettier --write`: コード書式の自動修正(JavaScript、TypeScript、Markdown、Svelte)
278+
- `eslint`: リント(JavaScript、TypeScript、Svelte)
279+
280+
Hook は自動的にセットアップされるため、特別な操作は不要です。
281+
282+
#### Hook を実行したくない場合
283+
284+
環境変数 `LEFTHOOK=0` を設定して commit してください。
285+
286+
```bash
287+
LEFTHOOK=0 git commit -m "コミットメッセージ"
288+
```
289+
290+
#### (既存ユーザ向け) husky から lefthook への移行
291+
292+
husky でセットアップ済みの開発環境で新しい PR をマージした場合、以下を実行してください:
293+
294+
```bash
295+
git config --unset core.hooksPath
296+
pnpm exec lefthook install
297+
```
298+
299+
これにより、古い husky の設定をクリアして lefthook に切り替わります。
300+
271301
### トラブルシューティング
272302

273303
- エラー: ローカル環境で開発用サーバを立ち上げても、ブラウザに表示されない
@@ -283,6 +313,3 @@
283313
- エラー: Docker Desktop で Vite を利用したときに Segmentation Fault が発生
284314
- 対処方法: Docker Desktopで「Use Visualization Framework」のチェックを外す
285315
- 参考資料: https://qiita.com/naoto24kawa/items/160aad0ca58642216a0a
286-
287-
- エラー: コミットを実行したときに、`hint: The '.husky/pre-commit' hook was ignored because it's not set as executable.`と表示される
288-
- 対処方法: ターミナルで`chmod ug+x .husky/*`を実行する
Lines changed: 249 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,249 @@
1+
# husky and lint-staged から lefthook への移行計画
2+
3+
**Issue:** [#2988](https://github.com/AtCoder-NoviSteps/AtCoderNoviSteps/issues/2988)
4+
5+
## 背景・目的
6+
7+
### 問題点
8+
9+
- **実行時間が遅い:** commit 時に約 30秒かかる(手動実行は 12.83秒)
10+
- **ステージ済みファイルのみなのに全体をチェック:** 無関係なファイル処理で時間浪費
11+
- **npm ツールのオーバーヘッド:** JavaScript の起動時間が重い
12+
13+
### 目的
14+
15+
- **高速化:** 30秒 → **5秒以下**
16+
- **効率化:** ステージ済みファイルのみをチェック
17+
- **メンテナンス性向上:** 高速で活発にメンテナンスされているツールに移行
18+
19+
---
20+
21+
## 現状分析
22+
23+
| 項目 | 詳細 |
24+
| ---------------- | --------------------------------------------------------------------------------------------------- |
25+
| **ツール** | husky 9.1.7 + lint-staged 16.2.7 |
26+
| **実行内容** | `pnpm format` + `pnpm lint` (順序実行) |
27+
| **実行時間** | 手動: 12.83秒、commit: 30秒 |
28+
| **スコープ** | 全体ファイル |
29+
| **チェック対象** | JS/TS: 146ファイル(47,688行)<br>Markdown: 42ファイル(16,623行)<br>Svelte: 73ファイル(5,565行) |
30+
31+
---
32+
33+
## 代替ツール調査
34+
35+
### 調査結果
36+
37+
| ツール | 言語 | Stars | メンテナンス | 採用 |
38+
| ---------------- | ---- | ----- | ------------ | ------- |
39+
| **lefthook** | Go | 7.1k | ✅ 活発 | ✅ 採用 |
40+
| simple-git-hooks | JS | 1.6k | ✅ 活発 ||
41+
| rusty-hook | Rust | 228 | ❌ 中止 ||
42+
43+
### 採用理由
44+
45+
**Go 言語実装で高速**
46+
47+
**並列実行対応**で format + lint を同時実行可能
48+
49+
**ステージ済みファイルのみをチェック**`{staged_files}` 変数)
50+
51+
**設定ファイルが簡潔** (YAML ベース)
52+
53+
**Windows/WSL 対応**
54+
55+
**継続的にメンテナンス中**(v2.0.12、更新: 1 週間前)
56+
57+
---
58+
59+
## Q&A
60+
61+
### Q1: npm script はそのままにするのか?
62+
63+
**A:** はい、現状のままにします。速度改善後、必要に応じて見直します。
64+
65+
- 現在: `pnpm format` = `prettier --write .`
66+
- 修正時期: 後日検討
67+
68+
### Q2: Prettier の check と write は分離するのか?
69+
70+
**A:** はい、以下のように分離して並列実行します。
71+
72+
- **format:** `prettier --write` (修正)
73+
- **lint:** `prettier --check` + `eslint` (チェック)
74+
75+
### Q3: `.gitignore` に lefthook 生成ファイルを追加する必要があるのか?
76+
77+
**A:** いいえ、不要です。
78+
79+
lefthook は以下のように動作するため:
80+
81+
- `lefthook.yml`**git 管理**(設定ファイル)
82+
- `.git/hooks/`**自動生成**(git 管理外)
83+
84+
`.gitignore` への追加は不要です。
85+
86+
### Q4: lefthook.yml で `exclude``node_modules/` 等を指定すべきか?
87+
88+
**A:** いいえ、不要です。
89+
90+
理由:
91+
92+
- ステージ済みファイルのみ(`{staged_files}`)なので、そもそも `node_modules/` は対象外
93+
- `exclude` はローカルのみで、CI では効果がない
94+
- 除外ルールは `.gitignore` で一元管理が推奨
95+
96+
**結論:** `exclude` は削除し、设定ファイルをシンプルに保つ
97+
98+
### Q5: package.json に lefthook 関連のコマンドを追加すべきか?
99+
100+
**A:** いいえ、不要です。
101+
102+
理由:
103+
104+
- husky の `"prepare": "husky"` は削除
105+
- lefthook は npm が自動でインストール・セットアップ
106+
- package.json に `prepare` script は不要
107+
108+
**オプション:** チーム開発の場合、以下を任意で追加
109+
110+
```json
111+
"scripts": {
112+
"hooks:install": "lefthook install" // 手動セットアップ用
113+
}
114+
```
115+
116+
---
117+
118+
## 実装計画
119+
120+
### タスク一覧
121+
122+
#### Phase 1: 準備
123+
124+
- [ ] lefthook をインストール(`pnpm add -D lefthook`**30m**
125+
- [ ] `lefthook.yml` を作成 **1h**
126+
- [ ] ローカルで動作確認・実行時間測定 **1h**
127+
128+
#### Phase 2: husky/lint-staged 削除
129+
130+
- [ ] `package.json` から以下を削除 **30m**
131+
- `"lint-staged"` 設定
132+
- `"prepare": "husky"` スクリプト
133+
- [ ] pnpm パッケージ削除(`pnpm remove husky lint-staged`**15m**
134+
- [ ] `.husky/` ディレクトリ削除 **10m**
135+
136+
#### Phase 3: 環境構築
137+
138+
- [ ] lefthook をインストール(`lefthook install`**30m**
139+
- [ ] テスト commit で動作確認 **1h**
140+
141+
#### Phase 4: ドキュメント更新
142+
143+
- [ ] `CONTRIBUTING.md` を更新 **30m**
144+
145+
#### Phase 5: PR・レビュー・merge
146+
147+
- [ ] PR 作成・レビュー待ち・merge **1~2h**
148+
149+
**合計工数:** **5~7時間 = 1 日以内**
150+
151+
---
152+
153+
## 実装詳細
154+
155+
### lefthook.yml(予想設定)
156+
157+
```yaml
158+
version: 2
159+
160+
pre-commit:
161+
parallel: true
162+
jobs:
163+
- name: format
164+
run: pnpm format {staged_files}
165+
glob: '**/*.{js,jsx,ts,tsx,md,svelte}'
166+
167+
- name: lint
168+
run: pnpm lint {staged_files}
169+
glob: '**/*.{js,jsx,ts,tsx,svelte}'
170+
```
171+
172+
### package.json 修正内容
173+
174+
**削除対象:**
175+
176+
```json
177+
"lint-staged": { ... },
178+
"prepare": "husky"
179+
```
180+
181+
**削除パッケージ:**
182+
183+
```bash
184+
pnpm remove husky lint-staged
185+
```
186+
187+
---
188+
189+
## 実装開始の判定
190+
191+
- ✅ 前提・調査内容の要約完了
192+
- ✅ 移行計画の確定
193+
- ✅ 実装内容の詳細化
194+
- ✅ 工数見積もり(1 日以内)
195+
196+
**実装開始可能**
197+
198+
---
199+
200+
## 実装完了報告
201+
202+
### 実装内容
203+
204+
**実施完了タスク:**
205+
206+
- ✅ lefthook インストール
207+
- ✅ lefthook.yml 作成
208+
- ✅ package.json から husky/lint-staged 削除
209+
- ✅ .husky ディレクトリ削除
210+
- ✅ lefthook セットアップ
211+
- ✅ 動作確認・時間測定
212+
- ✅ CONTRIBUTING.md 更新
213+
214+
### パフォーマンス測定結果
215+
216+
| 項目 ||
217+
| ---------------------------- | ---------- |
218+
| **Pre-commit hook 実行時間** | **0.69秒** |
219+
| **Format 処理時間** | **0.55秒** |
220+
| **改善前** | 約30秒 |
221+
| **改善後** | 約0.7秒 |
222+
| **削減率** | **97.7%** |
223+
224+
### 教訓
225+
226+
1. **Go 言語実装による高速化の効果大**
227+
- JavaScript ツール(husky)から Go 言語ツール(lefthook)への移行で、98%の時間削減を実現
228+
- 環境起動時間が大幅に削減される
229+
230+
2. **Prettier/ESLint は直接呼び出しが必須**
231+
- npm scripts 経由(`pnpm format`)では、全ファイル処理のため高速化できない
232+
- `prettier --write {staged_files}` のように直接コマンド呼び出しすることで、ステージ済みファイルのみ処理可能
233+
234+
3. **Glob パターンとファイルリスト指定の組み合わせ**
235+
- lefthook の `glob` で対象ファイル型を指定
236+
- `{staged_files}` で実行ファイルリストを動的に生成
237+
- この組み合わせにより、効率的かつ柔軟な hook 設定が可能
238+
239+
4. **Hook 設定ファイルのシンプル化**
240+
- `.husky/` ディレクトリを削除し、`lefthook.yml` 1 ファイルで一元管理
241+
- 設定の可読性・メンテナンス性が向上
242+
243+
5. **並列実行による時間短縮**
244+
- `parallel: true` で format と lint を同時実行
245+
- Sequential 実行なら 1.24秒かかるところ、0.7秒で完了
246+
247+
6. **Git config のクリーンアップが重要**
248+
- husky の古い `core.hooksPath` 設定が残っていると、lefthook が正常に動作しない
249+
- 環境切り替え時は既存設定の削除が必須

lefthook.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
version: 2
2+
3+
pre-commit:
4+
parallel: true
5+
jobs:
6+
- name: format
7+
run: pnpm exec prettier --write {staged_files}
8+
glob: '**/*.{js,jsx,ts,tsx,md,svelte}'
9+
10+
- name: lint
11+
run: pnpm exec sh -c 'prettier --check {staged_files} && eslint {staged_files}'
12+
glob: '**/*.{js,jsx,ts,tsx,svelte}'

package.json

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,6 @@
66
"bugs": "https://github.com/AtCoder-NoviSteps/AtCoderNoviSteps/issues",
77
"homepage": "",
88
"license": "MIT",
9-
"lint-staged": {
10-
"**/*.{js,jsx,ts,tsx}": [
11-
"pnpm format",
12-
"pnpm lint"
13-
]
14-
},
159
"scripts": {
1610
"dev": "vite dev",
1711
"build": "prisma generate && vite build",
@@ -26,7 +20,6 @@
2620
"postinstall": "prisma generate",
2721
"db:seed": "pnpm exec tsx ./prisma/seed.ts",
2822
"db:studio": "pnpm exec prisma studio",
29-
"prepare": "husky",
3023
"coverage": "vitest run --coverage"
3124
},
3225
"devDependencies": {
@@ -49,9 +42,8 @@
4942
"eslint-config-prettier": "10.1.8",
5043
"eslint-plugin-svelte": "3.10.1",
5144
"globals": "16.5.0",
52-
"husky": "9.1.7",
5345
"jsdom": "27.3.0",
54-
"lint-staged": "16.2.7",
46+
"lefthook": "2.0.12",
5547
"nock": "14.0.10",
5648
"pnpm": "10.26.2",
5749
"prettier": "3.7.4",

0 commit comments

Comments
 (0)