StorybookでlazyCompilationを使うとエラーになる
StorybookでlazyCompilationを有効にしたところ、このようなエラーが出た
結論から言うと自分はVSCodeの設定に"remote.autoForwardPortsSource": "process"
を追加することで解決した。
VSCodeのRemote Development (Remote SSHやWSL、Devcontainerなど)のようなport fowardingが必要な環境で接続するとき、localhost:6006で接続する開発サーバー以外にwebpackのlazy compilation用のサーバーに接続する必要があり、このポートが転送されずクライアントから接続できないのが原因だった
どのように調査したか
まずエラーが出ているときにDevToolsを見るとhttp://localhost:34527/lazy-compilation-using-{.......}
へのリクエストでnet::ERR_CONNECTION_REFUSEDが出ていた。そして実際にエラー画面に表示されているスタックトレースをもとにデバッガで確認した
Problem communicating active modules to the server: undefined undefined:undefined:undefined undefined
Error: Problem communicating active modules to the server: undefined undefined:undefined:undefined undefined
at http://localhost:6006/Button-stories.iframe.bundle.js:35:6
at Set.forEach (<anonymous>)
at activeEventSource.onerror (http://localhost:6006/Button-stories.iframe.bundle.js:33:18)
エラーが出た周りの実装を読んでいくと、EventSourceを使って__resourceQueryに入っているURLをもとに接続していて、ここで接続できないことが直接的な原因だと断定できた。
URLエンコードされていてわかりずらいが、__resourceQueryをdecodeURLComponentすると'http://localhost:34527/lazy-compilation-using-'ようなURLが含まれている
この時点でVSCodeのport fowardingを手動で追加すればつながることも確認できた。しかしこのポートは起動するたびに変わるエフェメラルポートなので、毎回DevToolsを開いて接続しようとしているポートを手動で設定し続けるわけにもいかないので調査を続けた
そして実際にNext.jsなどほかのフレームワークだと問題がなかったことから、storybook側のバグを疑いbug reportを書くために再現コードを作りながら背景調査をしていると、この辺りのコードは全てwebpack本体によるものだと分かった
そこからはwebpackの関連するコードを探し、http://localhost:34527/lazy-compilation-using-
をハンドリングするサーバーの実装を読み進めていき、実際にエフェメラルポートでサーバーを起動すると思われるコードを見つけた
解決までの道のり
webpackのドキュメントを見るとlistenオプションでポートを指定できるので、固定のポートを指定しport forwardingさせることで一応解決はするのだけど、自分の環境のために全てのプロジェクトに設定を追加して回るのも手間がかかるので、他の方法を模索しはじめた
本来はNext.jsなどと同じように単独のサーバー/ポートで全てハンドリングするのが望ましいと思ったが、
サーバー実装を見るとrouteの判定が全くないことから、experiments機能であるとはいえ完全に単独で動作するサーバーとして作られていたので、experiments.lazyCompilationとして使うにはwebpackに機能を改善してもらうしかないことがわかった
ここまでで一旦webpack側の調査をやめてVSCode側のAuto Forward Ports機能について調べてみると、/procからプロセスが使用しているポートを自動検出する機能があり、これを選択することで解決に至った
とはいえこれはVSCode以外でport fowardingしたいケース、例えばngrokやcloudflare tunnelのようなツールを使って一時的に誰かと共有したいときなど、別ポートに依存していると困るケースはあると思っているので、単独のサーバーにすることができないか引き続き調査中