VSCode Remote Developmentで、ホットリロードのできるNode.js開発環境の作り方

# はじめに
VSCode Remote Developmentで開発環境をコンテナ化しているのですが、ファイルシステムの変更通知がうまく機能しないらしく、ホットリロードができなくて困ったので、その改善方法のまとめです。

結論としては、Docker volumeを使用すればよいです。

本記事では、DockerやNode.jsがなんとなくわかる人向けに、シンプルなNode.js開発環境の作成方法をご紹介します。

# 環境
- Windows11
- Docker Desctop for Windows v4.34.1 (on WSL2)
- Visual Studio Code
- [Remote Development Extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.vscode-remote-extensionpack)

# ディレクトリ構成
ファイルの中身や作り方は後述しますが、最終的に目指すディレクトリ構成は以下のようになります。
```:環境設定情報のディレクトリ
Dockerfile
docker-compose.yml
.devcontainer/
    devcontainer.json
    docker-compose.yml
```
docker-compose.ymlが2つありますが、.devcontainerディレクトリ内のものは自動生成されたものです。
必要かは正直わからないので、詳しい方がいたらコメントで教えてください。。。

```:開発成果物のディレクトリ（Docker volume）
package.json
package-lock.json
src/
    index.js
node_modules/
    たくさんのファイル
```

# 作成手順

## ステップ1: Dockerfileの作成
ディレクトリルートに以下の内容でDockerfileを作成します。
```Dockerfile:Dockerfile
FROM node:22-alpine

RUN apk add --no-cache git

ENTRYPOINT tail -F /dev/null
```
上記では node:22-alpine を使用していますが、お好みのイメージを使用してください。

gitを使用できるようにしたいので、インストールコマンドを実行するようにしています。
イメージにalpineを選択したので```apk add```です。
特にいらない方は```RUN```の行は不要です。

```ENTRYPOINT tail -F /dev/null```は、コンテナを起動したままにするための記載らしいです。

## ステップ2: docker-compose.ymlの作成
ディレクトリルートに以下の内容でdocker-compose.ymlを作成します。
```yml:docker-compose.yml
version: '3.8'
services:
  app:
    build: .
    ports:
      - 3000:3000
    volumes:
      - .:/workspace:cached
      - home:/home
volumes:
  home:
```
重要なのは```volumes```の設定で、今回はhomeというvolumeを作り、リモートの```/home```に割り当てています。

## ステップ3: devcontainer.jsonの作成
devcontainer.jsonというのは、リモート接続のための構成ファイルですが、自動作成できます。
VSCode左下のRemoteアイコンもしくはコマンドパレットから以下のコマンドを実行します。
```
Dev Containers:Add Dev Container Configuration Files...
```
Where would you like to create your container configuration? には ```Add configuration to workspace``` を選択し、
How would you like to crate your container configuration? には ```From 'docker-compose.yml'``` を選択します。
その後の質問はそのまま OK を選択すればよいです。
.devcontainerディレクトリと、その中にdevcontainer.jsonとdocker-compose.ymlファイルが作成されます。

作成後、devcontainer.jsonの```name```と```workspaceFolder```を以下のように変更します。
```diff_json:devcontainer.jsonの一部
- "name": "Existing Docker Compose (Extend)",
+ "name": "[任意の名前]",

- "workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}"
+ "workspaceFolder": "/home"
```
```workspaceFolder```は、リモートコンテナ接続時に開く既定のディレクトリの設定です。

## ステップ4: コンテナのビルドと接続
VSCode左下のRemoteアイコンもしくはコマンドパレットから以下のコマンドを実行します。
```
Dev Containers: Reopen in Container
```
実行してしばらくすると、VSCodeのウィンドウが開きなおされ、コンテナに接続されます。

## ステップ5: Node.jsの開発を始める
接続されたコンテナで、```npm init```や```npm install```等を行い、開発を始めます。

ちなみにpackage.jsonのライセンスの指定ですが、個人の練習用開発環境等であれば、```"license": "UNLICENSED"```を指定し、ついでに```"private": true```を指定しておけばよいみたいです。

Node.jsの初期設定が終わったら、今回はホットリロードができるかの確認をしたいだけなので、以下のような適当なsrc/index.jsを作成します。
```javascript:src/index.js
console.log('Hello world!');
```

## ステップ6: ホットリロードの確認をする
今回はNode.jsのv22を使用しているので、```node ---watch```が使用できます。
※古いバージョンを使用している方はnodemon等を利用すればよいかと思います。

以下のコマンドで、src/index.jsを実行します。
```
node --watch src/index.js
```
その状態で、src/index.jsを編集し、保存したときに出力内容が変われば成功です。

### Typescriptの場合
もし、Typescriptを使用し、tsxでホットリロードを行いたい場合は、以下のどちらかを実行すればよいです。
```
node --watch --import tsx src/index.ts
```
```
npx tsx watch src/index.ts
```

# 環境を削除するとき
この手順では、/home 配下はDocker volumeとして永続化しています。
そのため、Dockerのimageやcontainerを作り直しても、/home 配下のデータは消えません。

この環境が不要になった場合、imageとcontainerの削除の他に、volumeの削除が必要になるので注意してください。

# おわりに
今回はVSCode remote developmentを使用して、コンテナ化されたシンプルなNode.js開発環境を作成してみましたがうまくいきましたでしょうか？
なにか補足情報等ありましたら、コメントいただけると幸いです。

# 参考サイト
- [VSCode の Remote Container で React開発環境を構築する](https://qiita.com/takashiuesaka/items/4012fee49ee0e06db72b)
- [永遠に終了せず何もしないDockerコンテナを立ち上げる方法](https://genzouw.com/entry/2023/03/24/083011/3332/)
- [プライベートプロダクトのpackage.jsonのlicenseフィールドには "license: UNLICENSED" と指定すればいいらしい](https://blog.pinkumohikan.com/entry/set-unlicensed-to-license-field-when-private-product)
- [まだts-node使ってるの？tsxの紹介](https://qiita.com/ssssota/items/115a906e960bcfabb46b)