WebpackerとWebpacker::DevServerProxy
Rails6がリリースされた。
Rails6からjavascriptの管理はWebpackerで行うみたい。
Webpackerを導入すると、webpack-dev-serverコマンドが使えるようになる。
jsファイルの変更を監視してビルドし、ファイルの配信、ブラウザのリロードまでしてくれる。
ファイル配信について疑問があった。
webpack-dev-serverを起動するとlocalhost:3035でリクエストを待ち受けてる。
ただ、よくあるRailsアプリを起動するとlocalhost:3000でリクエストを待ち受けてる。
で、packs以下のファイルを http://localhost:3000/packs/js/application-106dae08824fadbcd45a.js とかで取得することができる。
『あれ http://localhost:3035/packs/js/application-106dae08824fadbcd45a.js が正しいのではないか?』と思ったわけ。
どうやらミドルウェアで処理をしてるみたいだ。
https://github.com/rails/webpacker/blob/master/lib/webpacker/dev_server_proxy.rb
DevServerProxyで受け取ったリクエストを弄ってる。
DevServerProxyはRack::Proxy( https://github.com/ncr/rack-proxy )を拡張したモノだ。
Rack::Proxyのperform_requestメソッド( https://github.com/ncr/rack-proxy/blob/master/lib/rack/proxy.rb#L72 )でNet::HTTPを使って、改変されたリクエスト情報を投げ、結果を取得してる。
DevServerProxyのコードは下記の通り。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
require "rack/proxy" class Webpacker::DevServerProxy < Rack::Proxy delegate :config, :dev_server, to: :@webpacker def initialize(app = nil, opts = {}) @webpacker = opts.delete(:webpacker) || Webpacker.instance super end def perform_request(env) if env["PATH_INFO"].start_with?("/#{public_output_uri_path}") && dev_server.running? env["HTTP_HOST"] = env["HTTP_X_FORWARDED_HOST"] = env["HTTP_X_FORWARDED_SERVER"] = dev_server.host_with_port env["HTTP_X_FORWARDED_PROTO"] = env["HTTP_X_FORWARDED_SCHEME"] = dev_server.protocol unless dev_server.https? env["HTTPS"] = env["HTTP_X_FORWARDED_SSL"] = "off" end env["SCRIPT_NAME"] = "" super(env) else @app.call(env) end end private def public_output_uri_path config.public_output_path.relative_path_from(config.public_path) end end |
if env["PATH_INFO"].start_with?("/#{public_output_uri_path}") && dev_server.running?
でURLのパスと、webpack-dev-serverの起動をチェックしてる。
public_output_uri_pathはconfigのwebpacker.ymlで指定されてるディレクトリだ。
特に何も設定してなければpacksになってる。
つまり、packs以下のファイルにアクセスしようとするとリクエストがwebpack-dev-serverにフォワーディングされるわけだ。
if文の中ではenvの書き換えを行ってる。
env[“HTTP_HOST”]はwebpack-dev-serverのホストに書き換えられてる。
最後にsuper(env)を呼び出してるので、Rack::Proxyのperform_requestの中で改めてリクエストを投げて、その結果を返してくれる。
余談だが、webpack-dev-serverを起動してもCannot GET XXX[ファイル名]と表示される場合は、webpack-dev-serverが複数起動してる可能性がある。
私は複数のプロジェクトをdockerを使って開発していたため、webpack-dev-serverを同ポートで複数起動していた。
当たり前なんだけど、これが原因で別プロジェクトのpacks以下のファイルが取得できず困っていた。