ウェブエンジニア珍道中

日々の技術的に関する経験を書いていきます。脱線もしますが助けになれば幸いです。

dockerコンテナ内でParcelをwatchモードで使った時にエラーが出る

Parcelについてはこちら

www.te-nu.com

dockerコンテナ内でParcelを使う際に詰まったので残しておきます。

症状

dockerコンテナ内にてparcelをwatchコマンド(監視モード)で動かすと、ブラウザ上で以下のエラーが出力されました。

Uncaught DOMException: Failed to construct 'WebSocket': The URL 'hoge://:33541/' is invalid.

dockerコンテナを起動して操作する際に入力したコマンドは以下の通りです。

docker run -it  -v $(pwd):/app node bash

# コンテナ内
npx parcel watch --public-url ./ index.html

解決策

まずdockerを起動時に-pオプションでホストと共有するポートを設定します。ポート番号はすでに使ってあるもの以外なら何でも良いです。例として5050で起動します。

docker run -it -v $(pwd):/app -p 5050:5050 node bash

そしてparcel起動時に --hmr-port [docker起動時に指定したport番号]オプションをつけます。先ほどと同様に5050で起動します。

また、hostも指定する必要があるのでhostnameを localhostに指定しておきます。

npx parcel watch --hmr-port 5050 --hmr-hostname localhost --public-url ./ index.html

これで動くようになりました。

原因

watchモードではソースコードの変更を感知して以下の2つを行います。

  • ビルド
  • 変更した場所をブラウザに適応する(リロード無し)

変更箇所をブラウザに適応する機能はHot Module Replacement(HMR)と呼ばれており、内部でWebSocketの技術が使われています。

WebSocketはポートを一つ使って通信を行います。そしてこれはParcelの仕様なのかは把握していませんが、WebSocketを用いて通信をする際のポート番号が 起動する度に変わるようです(自分の環境では30000台で毎回変わりました)。

そしてdockerコンテナとホストでポートの共有はしていないため通信できずにエラーとなっていました。

--hmr-portオプションでポート番号、 --hmr-hostnameでホスト名が指定できたので、dockerコンテナとうまく合わせることで動くようになりました。

おまけ

「いや……HMRとかいいです……もう毎回ブラウザリロードします」って人は--no-hmrオプションで無効化できます。今回のエラーはこれでも解決します。

npx parcel watch --no-hmr --public-url ./ index.html