🍺

ホットリロードが動かなくて時間を融かした話

2021/12/31に公開

きっかけ

https://zenn.dev/nobokko/articles/tech_deno_firststep

の続きとして、alephjs使ってみようと思ったのです。
alephjs、本来なら実行中に変更をしたファイルを検知して最新化してくれる機能(ホットリロード)があるはずなのに、全然反応しなかったのです。

なんでだろう?

前置き

Denoの話ですが、Denoに限らずファイルの変更検知が動作しなかった場合は参考になるかもしれません。
原因は何となくわかりました。
私の望む解決には至りませんでした。

環境

  • Windows10 Pro
  • WSL2 (Debian)
  • Docker Desktop 4.3.1
  • image:denoland/deno:alpine-1.16.4
  • ローカルのフォルダをvolumeでマウントして使用

ホットリロードが動いていないことに気付くまで

FROM denoland/deno:alpine-1.16.4

RUN apk update && \
    apk add \
        git

RUN deno run -A https://deno.land/x/aleph/install.ts

RUN adduser -D deno-user
USER deno-user

こんな感じのDockerfileを用意して、VSCodeのRemote-Containers経由で中で作業してました。
作業はマウントしているディレクトリで行いました。

aleph init

実行することで各種ファイルが配置されます。

aleph dev

実行することでサーバーが立ち上がります。この時点でブラウザからアクセス可能です。ここまでは順調です。

次に
pages/index.tsx
のreturnに適当に文字を書き足して保存します。

本来であればここでブラウザに表示されている内容に書き足した分が追加されるはずでした。

検証

再起動

aleph devの立ち上がりに何か問題あったのかな?ととりあえずプロセスを切ったり立ち上げたりを数回繰り返しました。
立ち上がり時点のソースから反映されることはありませんでした。

ユーザかな

deno-userという別のユーザを作成して使うつもりでしたが、権限に引っかかってるのかな?とrootのままにしてみます。
ソースが反映されることはありませんでした。

(なお色々検証していた過程でdenoのimport関連で権限に引っかかったこともあったので一旦検証中は基本的にrootで作業することにしました)

alephjsのバグかな?

alephjsはベータって書いてるしね、バグかもね
とバージョンを下げて試してみることにしました。

deno.land/x/aleph/install.tsを見ると
versions.json
が拾えました。

現時点のlatestはv0.3.0-beta.19でしたので、とりあえずv0.2.28を試してみます。

上記install.tsから-vや--versionオプションや始めの引数にバージョンを付ければ任意のバージョンが使えそうです。

vv0.2.28になりました。vは不要だったようです。

ソースが反映されることはありませんでした。

同様に0.3.0-beta.1を試してみましたが、やはり
ソースが反映されることはありませんでした。

最新版だから起きている問題では無さそうです。

Denoのバージョンと相性が悪いのかな?

Deno側で何か動作不良しているのかな? HTMLDialogElementが何故か無くなってたからバージョン戻したこともあったな、ということで

#FROM denoland/deno:alpine-1.16.4
FROM denoland/deno:alpine-1.14.3

バージョンを下げてみます。

ソースが反映されることはありませんでした。

同様に1.13.2を試してみましたが、やはり
ソースが反映されることはありませんでした。

alpineが悪いのかな?

alpineはイメージサイズが小さい事で有名ですが、そのコンパクトさ故なのか度々alpineだと上手く動かんな~ということがありました。
opensslの古いバージョンが簡単に使えないからレガシー資産をalpineで環境構築するのを諦めた、など。

#FROM denoland/deno:alpine-1.16.4
FROM denoland/deno:debian-1.16.4

とりあえずdebian。

ソースが反映されることはありませんでした。

同様に無記名1.16.4(Ubuntuだと思ってたけどdebianでした💦)を試してみましたが、やはり
ソースが反映されることはありませんでした。

同様にubuntu-1.16.4を試してみましたが、やはり
ソースが反映されることはありませんでした。

そもそもどうやってホットリロードを実現してるの?

サーバー実行時に

INFO Start watching code changes...
INFO Server ready on http://localhost:8080/

と表示されるので、「Start watching code changes...」と表示している辺りでホットリロードの準備をしているのかな?と想像したのですが、aleph自体のソースはどこに配置されているのでしょう?
ローカルのどこかにある気がしましたが、GitHubへのリンクが載っているので今回はそっちを見ることにしました。

https://github.com/alephjs/aleph.js

「Start watching code changes...」で検索を掛けるとserver/aleph.tsというソースが該当していそうです。

    const w = Deno.watchFs(srcDir, { recursive: true })
    log.info('Start watching code changes...')

「Deno.watchFs」という処理が名前的にもホットリロードに関係していそうです。これは何かな?

Deno.watchFs

https://deno-ja.vercel.app/manual/examples/file_system_events

どうやらポーリングではなくファイルシステムイベントを監視しているらしい。Windows以外でもそんなイベントあったんだ。

  • Linux: inotify
  • macOS: FSEvents
  • Windows: ReadDirectoryChangesW

inotify

inotifyとはなんぞや? とりあえず「inotify-tools」を導入してみる。

apk add inotify-tools

「inotifywait」というコマンドで「pages/index.tsx」を監視してみる。
はんのうがない、ただのしかばねのようだ。

と、そういえば昔ネットワークフォルダで監視しようとして上手く行かなかった事があったような、と試しに

cd /tmp && touch file.txt && inotifywait file.txt

と実行してfile.txtに「echo x >> file.txt」と流してあげたら

file.txt OPEN 

反応しました。これっぽい。

cd /tmp && aleph init test && cd /tmp/test/ && aleph dev

ソースが反映されました。これっぽい。

何ともならない気がしてきました。

Mac(M1)なら事情が変わるかな?

とりあえずMac(M1)で同じ(Docker内でvolumeマウント先で実行)ことをしてみました。
Deno.watchFsが未実装のエラーで落ちました💦 ピンポイント過ぎる

現環境のままで代替案はあるだろうか?

nodemonのDeno版的なものとして、denonというものがあるらしい。この際ライブリロード[1]でもええやろ。

deno install -qAf --unstable https://deno.land/x/denon@2.4.10/denon.ts

error: TS2339 [ERROR]: Property 'addSignalListener' does not exist on type 'typeof Deno'.
        Deno.addSignalListener(s, () => {
             ~~~~~~~~~~~~~~~~~
    at https://deno.land/x/denon@2.4.10/src/daemon.ts:156:14

ええ……
どうやら数日前にissueにも上がってるようで、加えてバージョンを下げても同様のエラーが発生する事をみると依存関係のある別の何かが更新されたのかな。タイミングが悪い……

Deno.watchFsで検索したらヒットしたので仮にインストールできたとしてもダメっぽい気がしますね。


追記

私の環境では未だに上記のエラーは解消されていませんが、こちらの方でインストールと確認が行えました。

deno install -qAf --unstable https://raw.githubusercontent.com/denosaurs/denon/main/denon.ts

独自にポーリングもしているようで、反応はイマイチながらもライブリロード動作しました!

まとめ

  • Denoにはファイル監視の処理(Deno.watchFs)が用意されているけど、inotifyが動作可能なフォルダか確認する必要があります。
    • 逆にDenoでファイル監視の処理を行いたい場合は、安易にDeno.watchFsを使用すると同じことがおきえることは気にした方がよい(自前ポーリング処理の用意の検討など)のかもしれません。
  • 今回のような、Docker内で作業する場合はVolumeを使うのは避けて、Git等からコンテナ内に直接ファイルを配置したり反映したりを行うのが無難かもしれません。

お疲れさまでした。

脚注
  1. ホットリロード/Hot reloadingはプロセスの再起動を伴わない反映、ライブリロード/Live reloadingはプロセスの再起動を伴う反映と思っておけば、概ね会話は成立するのではないでしょうか ↩︎

Discussion