0
1

More than 5 years have passed since last update.

lint-staged で大量ファイルをlintしようとすると死ぬのを回避する (windows)

Posted at

とあるプロジェクトで lint-staged を仕掛けて楽しようとしたら思わぬところでハマったので解決策を書いておく。

やったこと

プロジェクトで使ってるlinterとformatterを手動でかけるのがめんどくさいので git commit 時に走るようにセットアップしていた。
使っていたツールは以下。

  • angular-cli@7.3.9 : angular のプロジェクトなので ng lint をlinterとして使っている。
  • husky@2.7.0 : pre-commit hook を仕掛けるのに使っている。
  • prettier@1.18.2 : formatter
  • pretty-quick@1.11.1 : git add されたファイルにだけ prettier を実行するのに使っている。
  • lint-staged@9.2.3 : git add されたファイルにだけ linter を実行するのに使っている。
  • ng-lint-staged@0.1.6 : ng lintlint-staged と一緒に使うために必要。

セットアップ

package.json
{
  // (省略)
  "husky": {
    "hooks": {
      "pre-commit": "lint-staged"
    }
  },
  "lint-staged": {
    "*": [
      "pretty-quick --staged",
      "ng-lint-staged lint --fix --",
      "git add"
    ]
  }
}

ふつうはこれでだいたい動く。

ハマったこと

ある日突然チームメンバーの一人が「コミットしようとしたらよく分からないエラーが出る」とのこと。
よく分からないとはなんぞやと思ってエラーメッセージを見せてもらうと、文字化けしていて本当によく分からない。(最後まで文字化けは解読できなかった。)
その時の状況は下記のような感じ。

  • 死んだコマンドは ng-lint-staged lint --fix -- らしい
  • そのメンバーが使用していた PC は Windows10 で、ターミナルには PowerShell を使用
  • 僕が使用しているのは MacBook Pro で、ターミナルには fish を使用
  • git commit --no-verify でとりあえずコミットしてもらい、僕のPCで再現させようとするも再現しない。
  • 手元に Windows 機もあったのでそっちで試してみると再現。
  • コミットしようとしていたファイルは 150ファイル超

実行中のログをよくよく眺めてみると、途中で 「ステージングされているファイルが多すぎて、この環境ではコマンドが意図しない動作をすることがあるよ」 というようなメッセージがあることを発見。
調べてみると、 Windows の cmd.exe ではコマンドとして渡せる文字列は 8192 文字が限界 という記述を発見。
PowerShell でも似たような制限があるのではないかと推測した。

解決策

lint-staged では lint-staged.config.js という名前で設定ファイルが書け、こちらだと実行するコマンドを自由に書き換えることができるようです。
package.json から lint-staged に関する記述を削除し、 lint-staged.config.js に移行します。

lint-staged.config.js
const path = require("path");

const isTooManyFiles = (filenames) => filenames.length > 20;
const toRelative = (absPaths) => absPaths.map((file) => path.relative(process.cwd(), file));

module.exports = {
  "{src,cypress}/**/*.ts": (filenames) => {
    const relativePaths = toRelative(filenames);
    console.log(`{src,cypress}/**/*.ts: targets are ${relativePaths.length} files.`);

    const lintCmd = isTooManyFiles(relativePaths) ? "ng lint --fix" : `ng lint --fix --files "${relativePaths.join(",")}"`;
    const gitCmd = isTooManyFiles(relativePaths) ? "git add src/ cypress/" : `git add ${relativePaths.join(" ")}`;

    return ["pretty-quick --staged", lintCmd, gitCmd];
  },
  "package.json": (filenames) => ["fixpack", `git add ${toRelative(filenames).join(" ")}`],
};

すごく見づらいスクリプトですが、やっていることはざっくり以下。

  1. ステージングされているファイルが20以上あったら、個別にlint/formatするのを諦めて全体をlint/formatする。
  2. ついでに ng lint --files オプションに合うように引数リストをパースする。これによって ng-lint-staged を使う必要がなくなる。
  3. ついでのついでに package.json に変更があったら fixpack して整形しておく。

まとめ

コミットはこまめにしようね…。
(squash したらこうなることもあるのかな?)

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1