📝

VSCode 上で TS のファイルを編集保存すると重い問題の備忘録

2024/04/10に公開

この記事は

ある日、開発中に VSCode 上で TypeScript のコードを変更して保存するとファイル保存が重いという話がチーム内で上がりました。
調査して ESLint の拡張機能に問題があることがわかりましたが、それまでの過程や調べた内容を備忘録として書き残したものです。

ファイル保存が重い原因を特定する

ファイル保存という特定のタイミングだったので、ESLint や Prettier あたりの拡張機能のコードアクションが問題だろうとある程度予測はついていましたが、VSCode の Extension Bisect を使って特定しました。

Extension Bisect の使い方は下記を参考にしました。

https://qiita.com/ragi_chanchan/items/07945231274c505285b3

結果、やはり ESLint の拡張機能で重くなっていて、保存時のコードアクションをオフにすると VSCode の動作が軽くなったことを確認できました。

"editor.codeActionsOnSave": {
  "source.fixAll.eslint": "never"
},

ESLint の重いルールがないか確認する

ESLint の何が重いのか、問題を切り分けるためにまず実行に時間がかかっているルールが無いかを確認しました。

CLI 上であれば $ TIMING=1 yarn eslint で時間のかかっている順にルールの一覧を出力してくれます。
今回は VSCode 上での動作が問題で、CLI 上での動作にも影響があるのか切り分けられていないため本当は拡張機能で上記を実行したいところですが、その術がないのでひとまず CLI 上から実行します。

Rule                                             | Time (ms) | Relative
:------------------------------------------------|----------:|--------:
import/no-cycle                                  |  5613.100 |    30.0%
@typescript-eslint/no-unnecessary-qualifier      |  2113.626 |    11.3%
import/no-relative-packages                      |  1441.976 |     7.7%
import/no-extraneous-dependencies                |   562.034 |     3.0%
import/no-unresolved                             |   539.839 |     2.9%
@typescript-eslint/naming-convention             |   455.879 |     2.4%
jest/no-conditional-expect                       |   345.154 |     1.8%
@typescript-eslint/no-unnecessary-type-assertion |   329.406 |     1.8%
jest/no-disabled-tests                           |   328.686 |     1.8%
jest/no-identical-title                          |   304.464 |     1.6%

Done in 34.86s.

実行してみると import/no-cycle が他と比べ時間がかかっていたため、.eslintrc からルールをオフにしてみましたが動作は変わらず重く、改善されませんでした。

ESLint のデバッグログを探る

拡張機能では $ TIMING=1 yarn eslint を実行する術がないと書きましたがデバッグ方法が全くないわけではなく、$ yarn eslint --debug に相当するデバッグオプションが存在するので、デバッグログから実行対象のファイルに問題がないか、どこに実行時間がかかっていないかを探ります。

オプションを VSCode の settings.json から有効にします。

{
  ...
  "eslint.debug": true
}

すると VSCode の出力パネルにデバッグログが表示されるようになります。
出力されない場合 VSCode か ESLint サーバーを再起動してみてください。

細かくみてみると、ESLint の実行対象ファイルは VSCode 上で編集したファイルのみで問題ありませんが、Lint 対象のファイルのパースに実行時間のほとんどを費やしていることがわかりました。

2024-03-27T09:36:16.187Z eslint:linter Linting code for /path/to/project/app/src/hoge/foo.ts (pass 1)
2024-03-27T09:36:16.187Z eslint:linter Verify
2024-03-27T09:36:16.187Z eslint:linter With ConfigArray: /path/to/project/app/src/hoge/foo.ts
2024-03-27T09:36:16.188Z eslint:linter Parsing: /path/to/project/app/src/hoge/foo.ts
2024-03-27T09:36:19.229Z eslint:linter Parsing successful: /path/to/project/app/src/hoge/foo.ts
...
2024-03-27T09:36:19.348Z eslint:cli-engine Linting complete in: 3188ms

また CLI 上でも ESLint を実行しログを確認しましたが、同様にパースに時間がかかっており拡張特有の問題ではないことがわかりました。

ここまで調べ、TS の型情報は重いと耳にしたことがあったので原因は TS 関連で、ESLint 実行時に TS をパースしている @typescript-eslint/parser が怪しいのではと考えました。
さらに、パース時に使う tsconfig.json.eslintrcparserOptions.project に指定していますが、 このファイルを直近で変更していたためここに問題がありそうと当たりをつけました。

その後も調査を続け、直近の変更で tsconfig.jsoninclude オプションを指定するように変更したことで重くなったことがわかりました。

ちなみに include の指定は下記の通りで、include の指定がない場合は全ての TS ファイルが対象になる様なので、変更前後で対象ファイルは変わらず動作に違いはないはずですが、それ以上のことはわからず。

   },
+  "include": ["**/*.ts", "**/*.tsx"],
   "exclude": ["node_modules", "storybook-static"]

@typescript-eslint は v7 のバージョンがリリースされていますが、今のプロジェクトではまだ v5 を使っているためバージョンを上げてみるなどの手もありますが、ESLint 用の TSConfig を用意するなど現状で回避策はあるので、ひとまず tsconfig.json を前の状態に戻して調査はここまでにしました。

最後に

最終的に根本的な原因まではわかりませんでしたが、同様の事象が発生した方に ESLint のデバッグ方法などが参考になれば幸いです。

今回の問題は tsconfig.json の変更から問題に気づくまで時間がかかりましたが、CLI 上での ESLint のパフォーマンスは悪くなっているものの数秒増えた程度だったため気づくのに遅れたのではと考えています。
自分は VSCode が重いものの自分の PC に問題があるのかと思っていたので、何かおかしいと思ったらチーム内で気軽に共有することが大事ですね。

Social PLUS Tech Blog

Discussion