【Docker】Yarn v3を使用する【React】【Next.js】

## Yarn v3を使用する
DockerでYarn v3を使用するNext.jsの環境を構築する方法について記載します。
Next.jsで記載しますがReactでもほとんど同じです。

### コンテナ起動の準備をする
まず、`Dockerfile`と`docker-compose.yml`を作成します。
それぞれ以下のようにします。
```Dockerfile:Dockerfile
FROM node:16.15.0

ENV USER_NAME=myuser
ENV TZ=Asia/Tokyo

WORKDIR /myapp

USER ${USER_NAME}

EXPOSE 3000
CMD ["yarn", "dev"]
```
```docker-compose.yml
version: '3.9'
services:
  front:
    build: .
    volumes:
      - .:/myapp
    environment:
      NODE_ENV: development
    ports:
      - 3000:3000
```

### Yarnのバージョンを変更する
`Dockerfile`と`docker-compose.yml`を作成すればコンテナを起動できます。
ターミナルで`docker compose run front bash`を実行します。
```:ターミナル
$ docker compose run front bash
```
コンテナが起動したらコンテナ内で`yarn init -2`を実行します。
```:ターミナル
myuser@rs6437j9n45a:/myapp$ yarn init -2
```
すると下画像のようにファイルが作成されます。
![image220502_172124.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2342443/359f8ce2-5d83-ac49-872e-f71e5ad853ab.png)

試しに`yarn -v`を実行します。
```:ターミナル
myuser@2246ae8aafde:/myapp$ yarn -v
3.2.0
```
Yarnのバージョンが`3.2.0`になっています。

### `.yarnrc.yml`を編集する
`yarn init -2`で作成されたファイルのうち`.yarnrc.yml`を編集します。  
以下のように記述します。  
```.yarnrc.yml
yarnPath: .yarn/releases/yarn-3.2.0.cjs
cacheFolder: "/myapp/.yarn/cache"
```
上記のうち`yarnPath`は`yarn init -2`を実行すると記述されます。  
`cacheFolder`にはコンテナ側の`cache`へのパスを記述します。  
これを記述しておくとホスト側で`yarn add`などをしてしまっても以下のようなエラーが出ます。  
```:ターミナル
$ yarn add <package>
Internal Error: ENOENT: no such file or directory, mkdir '/myapp'
Error: ENOENT: no such file or directory, mkdir '/myapp'
```

ホスト側で`yarn add`を実行するとエラーの原因となることがあります。  
詳細については以下の記事をご参照ください。  

https://qiita.com/P-man_Brown/items/db1996bdee33ee667741

### `package.json`を編集する
`package.json`を以下のように変更します。  
```package.json
{
  "workspaces": [
    "/myapp/*"
  ]
}
```
ワークスペースの設定をしています。  
パスはコンテナ側のパスを記述します。  
これがないと`create-next-app`した際に以下のようなエラーが出ます。  
```:ターミナル
Usage Error: The nearest package directory (/myapp/app_name) doesn't seem to be part of the project declared in /myapp.

- If /myapp isn't intended to be a project, remove any yarn.lock and/or package.json file there.
- If /myapp is intended to be a project, it might be that you forgot to list app_name in its workspace configuration.
- Finally, if /myapp is fine and you intend app_name to be treated as a completely separate project (not even a workspace), create an empty yarn.lock file in it.
```

### TypeScriptを使用する場合
TypeScriptを使用する場合には以下のコマンドを実行します。
```:ターミナル
myuser@2246ae8aafde:/myapp$ yarn plugin import typescript
```
実行すると下画像のようにファイルが追加されます。

![image220502_181232.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2342443/d0569441-554e-0c40-e359-795d9dcbd99e.png)


プラグインの詳細については以下をご参照ください。

https://yarnpkg.com/api/modules/plugin_typescript.html

### ファイルを整理する
`create-next-app`した際に上書きされてしまうファイルがあるので削除します。  
削除するのが面倒な場合はそのままでも問題はありません。  
ただし`.gitignore`の内容についてはのちほど使用するため別のディレクトリに移すなどしてください。  
削除するファイルは以下のとおりです。  
- `.gitignore` (内容はのちほど使用します。)
- `README.md`
- `yarn.lock`

上記を削除すると以下のようになります。  
![image220502_185559.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2342443/b71e992d-0360-7638-6200-78ee1a412379.png)
### `create-next-app`する
コンテナ内にいる場合には`exit`で抜けます。  
```:ターミナル
myuser@2246ae8aafde:/myapp$ exit
```
ホスト側で以下を実行します。  
```:ターミナル
$ docker compose run --rm front yarn create next-app --ts app_name && \
  cd $_ && \
  mv * .* .. && \
  rmdir $(pwd) && \
  cd ../ && \
  docker compose run --rm front sh -c "yarn install && yarn dlx @yarnpkg/sdks vscode"
```
カレントディレクトリに展開するため、上記のように実行しています。  
また上記のように記述すると`app_name`の箇所のみ変更すれば別のプロジェクトでも対応できます。  

ワークスペースを`/myapp/*`と設定したため、ここで`yarn install`をしないと`docker compose up`した際に以下のようなエラーが出ます。  
```:ターミナル
$ docker compose up
[+] Running 1/1
 ⠿ Container next-yarnv3-front-1  Created                  0.1s
Attaching to next-yarnv3-front-1
next-yarnv3-front-1  | Internal Error: Assertion failed: Expected workspace yarn-test (/myapp/package.json) to have been resolved. Run "yarn install" to update the lockfile
next-yarnv3-front-1  |     at ze.refreshWorkspaceDependencies (/myapp/.yarn/releases/yarn-3.2.0.cjs:441:3811)
next-yarnv3-front-1  |     at ze.restoreInstallState (/myapp/.yarn/releases/yarn-3.2.0.cjs:447:1240)
next-yarnv3-front-1  |     at processTicksAndRejections (node:internal/process/task_queues:96:5)
next-yarnv3-front-1  |     at async xm.execute (/myapp/.yarn/releases/yarn-3.2.0.cjs:574:1879)
next-yarnv3-front-1  |     at async xm.validateAndExecute (/myapp/.yarn/releases/yarn-3.2.0.cjs:349:673)
next-yarnv3-front-1  |     at async Is.run (/myapp/.yarn/releases/yarn-3.2.0.cjs:363:2087)
next-yarnv3-front-1  |     at async GN.execute (/myapp/.yarn/releases/yarn-3.2.0.cjs:474:531)
next-yarnv3-front-1  |     at async GN.validateAndExecute (/myapp/.yarn/releases/yarn-3.2.0.cjs:349:673)
next-yarnv3-front-1  |     at async Is.run (/myapp/.yarn/releases/yarn-3.2.0.cjs:363:2087)
next-yarnv3-front-1  |     at async Is.runExit (/myapp/.yarn/releases/yarn-3.2.0.cjs:363:2271)
next-yarnv3-front-1 exited with code 1
```

`yarn dlx @yarnpkg/sdks vscode`についてはVSCode用の設定を生成するために実行しています。  
実行するコマンドはエディタごとに異なります。  
詳細については以下をご参照ください。  

https://yarnpkg.com/getting-started/editor-sdks

コマンドを実行するだけでは不十分で設定を明示的に有効にする必要があります。  
有効にする方法については後述します。  

現時点でファイルは以下のようになっています。  
![image220502_182532.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2342443/7ae25849-8b6f-6fd0-06be-409c10a3a6fa.png)

### `.gitignore`を編集する
`.gitignore`に`yarn init -2`で作成された`.gitignore`の内容を反映します。
`# dependencies`を以下のようにします。
```.gitignore
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/sdks
!.yarn/versions
!.yarn/cache

<以下略>
```

### `npm install`できないようにする
Yarnを使用するため、`npm install`できないようにします。
`npm install`を使用不可にする方法は以下の記事にまとめています。

https://qiita.com/P-man_Brown/items/625f55f615fb97293d1f

上記の記事に記載しているため、簡単に記載します。

```package.json
{
  "engines": {
    "npm": "npmではなくyarnを使用してください"
  }
}
```
```.npmrc
engine-strict=true
```

また`package.json`に以下を追記し、Corepackを有効化することでpnpmの使用を制限できます。
```package.json
{
  "packageManager": "yarn@3.2.0"
}
```
Corepackを有効化するには以下のコマンドを実行します。
```:ターミナル
$ corepack enable
```
`packageManager`の箇所に設定したパッケージマネージャが使用されるようになります。
そのほかのパッケージマネージャを使用するとエラーが出ます。
`packageManager`を上記のように記述した状態でpnpmを使用するとエラーが出ます。
しかし、npmは使用できてしまいます。
npmでもエラーが出るようにするためには。`corepack enable npm`を実行する必要があります。
またバージョンの管理もできますが、Yarnのバージョンは`yarn init -2`を実行した段階で固定されています。
そのため、`packageManager`を変更してもバージョンの変更はできません。
バージョン変更は`yarn set version <version>`で行います。

### VSCodeを設定する
前述したとおりVSCodeの設定について`yarn dlx @yarnpkg/sdks vscode`を実行するだけでは不十分です。
設定を明示的に有効にする必要があります。

VSCodeを開き、TypeScriptのファイルを開きます。
`cmd + sft + p`を入力します。
![image220502_194049.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2342443/4414c0b4-343b-b7fa-8de8-66bd607aa3cf.png)
表示された入力欄に`Select TypeScript Version`と入力し`Enter`を入力します。
すると下画像のように表示されるので`ワークスペースのバージョンを使用`をクリックします。
![image220502_194224.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2342443/c26ce693-3341-11d4-af5d-287095153fda.png)
`ワークスペースのバージョンを使用`が表示されない場合にはVSCodeを開き直してください。

### `docker compose up`する
`docker compose up`して正常に起動するか確認します。
```:ターミナル
$ docker compose up
```

エラーなく起動したら http://localhost:3000/ を確認します。

以下のように表示されたらOKです。

![image220502_212210.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2342443/eb5c896e-626e-d357-8e49-a6c3f3410416.png)