Closed10

webpack-dev-server にRailsアプリケーションからプロキシされない問題を解決する

Yuma ItoYuma Ito

背景

(時代的にはwebpack離れが進んでいるのですが、誰かの役に立つかもしれないのでメモ)

Rails × Webpackerを使ってアプリケーション開発をする際に、Webpackのコンパイルが遅いと開発効率が落ちてしまいます。
そこで webpack-dev-server を使うと変更があったJavaScriptファイルを自動でコンパイル、リロードをしてくれるので待ち時間が少なく開発を進めることができます。
webpack-dev-serverを使わなくてもWebpackerがオンデマンドでコンパイルしてくれますが、10秒くらいかかっていたので効率が落ちていました。

https://github.com/webpack/webpack-dev-server

問題

webpack-dev-server を起動しているのにも関わらず、Railsアプリケーションからプロキシされず、Webpackerがコンパイルをしてしまうのが問題でした。

コンソールには以下のようなログが出て、オンデマンドでコンパイルしていることがわかります。

[Webpacker] Compiling...

Webpackerのドキュメントには以下のように記載があり、webpack-dev-serverが起動していれば自動的にプロキシを行ってくれると記載がありました。

Once you start this development server, Webpacker will automatically start proxying all webpack asset requests to this server. When you stop the server, it'll revert back to on-demand compilation.
https://github.com/rails/webpacker/tree/5-x-stable#development

環境

  • Webpacker: 5.2.1
  • webpack-cli: 4.10.0
  • webpack-dev-server: 4.9.0
Yuma ItoYuma Ito

簡単なまとめ

  • webpack-dev-serverがどのポートでリッスンしているか確認(設定値ではなくlsofコマンド)して、Webpackerの設定と一致しているか確認する
  • Webpacker v5ではwebpack-dev-server v4に対応していない
    • webpack-dev-serverをv3に落とすか、Webpacker v6にバージョンアップする必要あり。
    • Webpackerの設定値が適用されていないので、webpack-dev-serverはlocalhost:8080で起動していた。
  • config/webpack/development.jsで設定値を適用することで任意のポートで起動させることができる。
Yuma ItoYuma Ito

調査の過程

以下、調査したメモを記録

webpack-dev-serverの設定

設定できる項目は以下のドキュメントにまとめられています。

https://webpack.js.org/configuration/dev-server/

私は環境変数で webpack-dev-server のホストとポートを指定していました。

WEBPACKER_DEV_SERVER_HOST=localhost
WEBPACKER_DEV_SERVER_PORT=3035

この状態で webpack-dev-serverを起動すると localhost:3035で起動するはずです。

Yuma ItoYuma Ito

Webpacker がプロキシしているコード

Webpackerのソースコードを確認すると、以下の箇所で webpack-dev-serverにプロキシしていることが分かりました。

https://github.com/rails/webpacker/blob/1cec8408d9c30e458c9f83b0c50ef53a255a4352/lib/webpacker/dev_server_proxy.rb#L12-L27

ログを仕込んでみると13行目のdev_server.running?falseを返却していることが分かりました。

dev_server.running? のソースコードは以下です。

https://github.com/rails/webpacker/blob/1cec8408d9c30e458c9f83b0c50ef53a255a4352/lib/webpacker/dev_server.rb#L14-L23

設定したホストとポートに関してTCPで接続して応答があればtrueを返却しています。
しかし、dev_server.running?falseということは設定したlocalhost:3035の応答がないということが分かりました。

Yuma ItoYuma Ito

手動で応答を確認

curlでリクエストを送っても当然のごとく、接続できませんでした。

curl localhost:3035
curl: (7) Failed to connect to localhost port 3035 after 9 ms: Connection refused

lsofコマンドで確認

今回の調査で初めて知ったのですが、lsofというどのプロセスがファイルやポートを利用しているか分かるコマンドがあるようです。
https://qiita.com/toshihirock/items/c6a09575c2d88c210483

lsof -i :3035

何も反応なし。

Yuma ItoYuma Ito

ログを確認していると 8080 番ポートを利用していることが判明!

webpack-dev-serverのログを確認してみたところ、起動時に以下のようなログがありました。

❯ bin/webpack-dev-server
<i> [webpack-dev-server] Project is running at:
<i> [webpack-dev-server] Loopback: http://localhost:8080/
<i> [webpack-dev-server] On Your Network (IPv4): http://192.168.3.7:8080/
<i> [webpack-dev-server] On Your Network (IPv6): http://[fe80::1]:8080/
<i

おや? http://localhost:8080/
もしや 8080 番ポートが使われているのかと思い調べてみたところ、

lsof -i :8080
COMMAND   PID     USER   FD   TYPE             DEVICE SIZE/OFF NODE NAME
node    69268 yuma.ito   25u  IPv6 0x79ce86334b27cdbb      0t0  TCP *:http-alt (LISTEN)

nodeのプロセスが使っていることが分かりました!(webpack-dev-serverは内部でexpress.jsを使ってWebサーバを起動しています)

そこで、Railsアプリケーションの起動時にポートを指定してみると、

WEBPACKER_DEV_SERVER_PORT=8080 bundle exec rails server

無事に dev_server.running?trueを返却し、webpack-dev-serverにプロキシされました!👏

Yuma ItoYuma Ito

ですが、もともとの設定していた3035番ポートで webpack-dev-server を起動したいです。

環境変数WEBPACKER_DEV_SERVER_PORT=3035を指定してwebpack-dev-serverを起動したところ、残念なことに8080番ポートで起動しました。

WEBPACKER_DEV_SERVER_PORT=3035 bin/webpack-dev-server
<i> [webpack-dev-server] Project is running at:
<i> [webpack-dev-server] Loopback: http://localhost:8080/
<i> [webpack-dev-server] On Your Network (IPv4): http://192.168.3.7:8080/
<i> [webpack-dev-server] On Your Network (IPv6): http://[fe80::1]:8080/
<i>

bin/webpack-dev-serverコマンドはWebpacker経由で yarn webpack-dev-serverを起動しているのですが、yarn webpack-dev-serverの実行時にはうまくポートの情報が伝わっていない様子。

Webpackerのドキュメントにちゃんと載っているのだが...。

You can use environment variables as options supported by webpack-dev-server in the form WEBPACKER_DEV_SERVER_<OPTION>. Please note that these environmental variables will always take precedence over the ones already set in the configuration file, and that the same environmental variables must be available to the rails server process.
https://github.com/rails/webpacker/tree/5-x-stable#development

Yuma ItoYuma Ito

Webpackerのソースコードを読み進めると、デフォルトの設定ファイルを見つけた。
ここで3035番ポートで起動するように設定されている。

https://github.com/rails/webpacker/blob/1cec8408d9c30e458c9f83b0c50ef53a255a4352/lib/install/config/webpacker.yml#L55-L59

そして、以下の箇所でWEBPACK_DEV_SERVERという環境変数をセットするとデフォルトの設定を読み込んでくれるようだ。

https://github.com/rails/webpacker/blob/5-x-stable/package/environments/development.js#L15-L16

実行してみたところ、webpack-cliからwatchOptionsというオプションがないというエラーが発生した。

WEBPACK_DEV_SERVER=true  bin/webpack-dev-server
[webpack-cli] Invalid options object. Dev Server has been initialized using an options object that does not match the API schema.
 - options has an unknown property 'watchOptions'. These properties are valid:
   object { allowedHosts?, bonjour?, client?, compress?, devMiddleware?, headers?, historyApiFallback?, host?, hot?, http2?, https?, ipc?, liveReload?, magicHtml?, onAfterSetupMiddleware?, onBeforeSetupMiddleware?, onListening?, open?, port?, proxy?, server?, setupExitSignals?, setupMiddlewares?, static?, watchFiles?, webSocketServer? }

webpack-dev-serverにv4へ移行するための手順が載っており、そこにもwatchOptionsオプションが変更されたことが記載されていた。
https://github.com/webpack/webpack-dev-server/blob/master/migration-v4.md?plain=1#L286

(手元の環境では webpack-dev-server: v4.9.0, webpack-cli: v4.10.0であった。)

Yuma ItoYuma Ito

Webpacker v6で解消済みであった

以下のPRで webpack-dev-server v4への対応が行われていた。

https://github.com/rails/webpacker/pull/3122

しかし、Webpacker v6のstableバージョンが出る前に開発が終了してしまった。

Webpacker v5では webpack-dev-serverは v3 にしか対応していないと判明

webpack-dev-serverはv3に留めるようなPRがマージされている。
https://github.com/rails/webpacker/pull/3121

つまり、Webpacker v5と webpack-dev-server v4はデフォルトの設定が反映されない。

Yuma ItoYuma Ito

解決案

かなり強引だが、下記のようにconfig/webpack/development.jsdevServerの設定して対応できそう。

config/webpack/development.js
const environment = require('./environment')

+ const devServerConfig = {
+   host: process.env.WEBPACKER_DEV_SERVER_HOST,
+   port: process.env.WEBPACKER_DEV_SERVER_PORT,
+ }
+ environment.config.set('devServer.host', devServerConfig.host)
+ environment.config.set('devServer.port', devServerConfig.port)

module.exports = environment.toWebpackConfig()

実行してみると指定した通りのポートでwebpack-dev-serverが起動した。

WEBPACKER_DEV_SERVER_PORT=3035 bin/webpack-dev-server
<i> [webpack-dev-server] Project is running at:
<i> [webpack-dev-server] Loopback: http://localhost:3035/, http://127.0.0.1:3035/
このスクラップは2023/03/05にクローズされました