🦑

lint-staged + husky でコミットするファイルだけ cspell を走らせる

2022/06/21に公開

🌼 はじめに

サイドプロジェクトの諸々セットアップ中、 cspellを投入してタイポを検査することにしました(タイポ魔王なので)

https://cspell.org/

scpellの基本的な使い方はcspell "**"cspell "src/**/*.js"などコマンドにファイルのパスを指定することです…が、これだと範囲が広すぎます。

毎回たくさんのファイルのタイポ検査するのは無駄なので、lint-staged と husky を使ってコミットするファイルだけタイポ検査できる設定方法を紹介します!

1. lint-staged

https://github.com/okonet/lint-staged

lint-staged はソースコード全体ではなく git stage されてるファイルに対して linter を走らせるライブラリーです。

README.mdになぜこういうものを作ったかも説明してくれてます。

Linting makes 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 lint process on a whole project is slow, and linting results can be irrelevant. Ultimately you only want to lint files that will be committed.

私とやりたいことの方向性が一致してますね。

つまり lint-staged がコミットするファイルに対して linter をかけることを利用したら、コミットするファイルだけ cspell を走らせることも可能ということです。

2. husky

今回 git hook は husky を使います。他の git hook ライブラリーを使ってもできるはずですが、この記事では husky を使った設定方法を紹介します。

https://github.com/typicode/husky

3. 設定方法

3-1. パッケージインストール

cspell、lint-staged、huskyをインストールしてください。

yarn add --dev cspell lint-staged husky

+) husky の init もしておきます

yarn husky-init

3-2. .lintstagedrc.js作成

まず lint-staged の設定をします。方法は色々ありますが、今回は JavaScript で設定ファイルを作成する方法を使います。便利なことに公式がたくさんのサンプルを準備してくれてるので、それを使って楽していきましょう。

プロジェクトのルートに.lintstagedrc.jsファイルを生成してください。

そして公式が準備してくれてるサンプルコードの一つである「Use relative paths for commands」を一旦コピペします。

.lintstagedrc.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(' ')}`
  },
}

このコードは「ステージングされてるファイルの中でも.ts拡張子のファイルに対して関数の方でリターンしてるコマンドを実行する」意味だと思われます。そのコマンドで使うための相対パスも計算していますね。
(このサンプルでは引数をabsolutePathsと表現してますが、他のサンプルを見てみると正確にはステージングされてるファイルたちの名前の配列のようです)

とりあえず相対パスの計算が既にあるので、これを使って cspell コマンドを書きます

const path = require("path");

module.exports = {
  // 対象拡張子をカスタム
  "*.{js,ts,tsx}": (absolutePaths) => { 
    const cwd = process.cwd();
    const relativePaths = absolutePaths.map((file) => path.relative(cwd, file)).join(" "); // join までここでやっちゃう
    return `cspell ${relativePaths}`; // コマンド修正
  },
};

検査したい拡張子を自分のプロジェクトに合わせて、コマンドも cspell に修正しました。かんたん!

+) ちなみにimportexport defaultを使ったら以下のエラーが出たのでrequiremodule.exportsに修正しました

  • SyntaxError: Cannot use import statement outside a module
  • SyntaxError: Unexpected token 'export'

cspell コマンドだけ実行したいのならここまででも十分です。

が、実は複数のコマンドを実行することも可能です。複数の場合はコマンドの配列をリターンします。

const path = require("path");

module.exports = {
  "*.{js,ts,tsx}": (absolutePaths) => {
    const cwd = process.cwd();
    const relativePaths = absolutePaths.map((file) => path.relative(cwd, file)).join(" ");
    return [
      `eslint ${relativePaths}`,
      `prettier --write ${relativePaths}`,
      `cspell ${relativePaths}`,
    ];
  },
};

eslint と prettier のコマンドも追加してみました。動き的には最初のコマンドが全部成功したら次のコマンドが実行されることになります。(途中でコケたらその後のコマンドは実行されずコミットが中断される)

これで lint-staged 設定が終わりました。

3-3. pre-commit 設定

設定した lint-staged をコミットの前に走らせたいので.husky/pre-commityarn lint-stagedを追加します。

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

yarn lint-staged

.husky/pre-commitに馴染みがない方は私が以前書いた husky 記事をご参考にどうぞ
https://zenn.dev/luvmini511/articles/a47b1aa0310d1a

4. 結果

設定が終わったので、ちゃんと動くのかテストします。
検証のため、わざとpages/about.tsxファイルにHoemというタイポを混ぜておきました。

それではgit add pages/about.tsxした後、コミットしてみます。

yarn run v1.22.18
$ lint-staged
✔ Preparing lint-staged...
❯ Running tasks for staged files...
  ❯ .lintstagedrc.js — 1 file
    ❯ *.{js,ts,tsx}1 file
      ✔ eslint pages/about.tsx
      ✔ prettier --write pages/about.tsx
      ✖ cspell pages/about.tsx [FAILED]
↓ Skipped because of errors from tasks. [SKIPPED]
✔ Reverting to original state because of errors...
✔ Cleaning up temporary files...

✖ cspell pages/about.tsx:
1/1 ./pages/about.tsx 768.94ms X
CSpell: Files checked: 1, Issues found: 1 in 1 files
/Users/<ユーザー名>/<プロジェクト名>/pages/about.tsx:9:12 - Unknown word (Hoem) # タイポ発見
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
husky - pre-commit hook exited with code 1 (error)

キタ――(゚∀゚)――!!

3つのコマンド全部コミットするファイルであるpages/about.tsxだけ見てるかつ、タイポが検知され無事コミットが失敗しました👏👏👏

これで余計なファイルまでコマンド走らせなくて済みました、やった!

🌷 終わり

これ設定しないと大きいプロジェクトなら毎回数百・数千ファイル検査することになるのでぜひこの方法使ってみてください

GitHubで編集を提案

Discussion