TypeScriptのNuxt.jsをDockerに乗せて動かし、Netlifyにデプロイする

Nuxt.jsを動かす手順をまとめました。
この記事では、数多あるNuxt.jsを動かす系の記事の中で以下の特徴があります。

- TypeScript化している
- ローカル開発環境をDockerで構築している
- パッケージマネージャにyarnを利用している
- 最終的にNetlifyにデプロイしている
- VesionはNuxt.js(v2.12.2)/Node.js(v12.16.1)

今回はUI frameworkとしてVuetify.jsを選択したので、
下の画像のようなNuxt.js + Vuetify.js のデフォルトTOPページがNetlifyのURL(https://[xxx].netlify.app)で表示できるところまでをまとめています。
![netlify.app.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/609518/52931d88-fd1c-1c3f-db1e-350a2168bb8a.png)

### 流れ

1. [Dockerfileおよびdocker-compose.ymlの作成](#1-dockerfileおよびdocker-composeymlの作成)
2. [Dockerイメージビルド & Nuxt.jsプロジェクトの作成](#2-dockerイメージビルド--nuxtjsプロジェクトの作成)
3. [Nuxt.jsの各種パッケージの最新化](#3-nuxtjsの各種パッケージの最新化)
4. [srcディレクトリの作成](#4-srcディレクトリの作成)
5. [TypeScript化](#5-typescript化)
6. [Netlifyへデプロイ](#6-netlifyへデプロイ)

### ディレクトリ構成
最終的なディレクトリ構成およびファイルです。

```bash
./[current_directory]
├── README.md
├── docker-compose.yml
├── netlify.toml
├── project               # Nuxt.jsのrootDir(ルートディレクトリ)
│   ├── README.md
│   ├── node_modules      # (ファイルは省略しています)
│   ├── nuxt.config.ts
│   ├── package.json
│   ├── src               # Nuxt.jsのsrcDir(ソースディレクトリ)
│   │   ├── assets        # ここから
│   │   ├── components    # ↓
│   │   ├── layouts       # ↓
│   │   ├── middleware    # ↓  (すべてファイルは省略しています)
│   │   ├── pages         # ↓
│   │   ├── plugins       # ↓
│   │   ├── static        # ↓
│   │   ├── store         # ここまで
│   │   └── vue-shim.d.ts
│   ├── tsconfig.json
│   └── yarn.lock
└── vue                   # Dockerfile格納用
    └── Dockerfile
```

### 1. Dockerfileおよびdocker-compose.ymlの作成
`current_directory`(名前は任意)を作成し、
その中に`vue`ディレクトリおよび`Dockerfile`ファイルを作成します。
そして`docker-compose.yml`ファイルを作成します。

```dockerfile:vue/Dockerfile
FROM node:12.16.1-alpine

RUN apk update && \
    apk upgrade && \
    yarn global add @vue/cli
```
ベースイメージとして`node:12.16.1-alpine`を使用しています。
パッケージを最新化し、Vue.js(`@vue/cli`)を導入します。

```yaml:docker-compose.yml
version: '3'
services:
  vue:
    build: ./vue
    working_dir: /home/node/app/project
    volumes:
      - ./project:/home/node/app/project
    ports:
      - 3000:3000
    environment:
      - HOST=0.0.0.0
```
この記事ではservice名`vue`のコンテナを立てて、`project`ディレクトリにこの後Nuxt.jsプロジェクトを作成します。

オプション|説明
---|---
build|作成したvue/Dockerfile
working_dir|今回は`/home/node/app/project`をコンテナ側の作業ディレクトリとしています。
volumes|「ローカル側のNuxt.jsプロジェクト用の`project`ディレクトリ」と「コンテナ側の`/home/node/app/project`ディレクトリ」をマウントしています。
ports|3000番ポート同士をポートフォワーディングしています。
environment|環境変数HOSTを0.0.0.0として定義しlocalhost宛等の自ホスト宛の通信をコンテナに転送するようにしています。

### 2. Dockerイメージビルド & Nuxt.jsプロジェクトの作成
`project`ディレクトリを作成し、[Nuxt.jsをインストール](https://ja.nuxtjs.org/guide/installation/)する。

```bash
$ mkdir project
$ cd project
$ docker-compose run vue yarn create nuxt-app .
##### (略) ビルド&create-nuxt-appが実行される ######

create-nuxt-app v2.15.0
✨  Generating Nuxt.js project in project
? Project name: project
? Project description: My finest Nuxt.js project
? Author name: 
? Choose programming language: TypeScript
? Choose the package manager: Yarn
? Choose UI framework: Vuetify.js
? Choose custom server framework: None
? Choose the runtime for TypeScript: @nuxt/typescript-runtime
? Choose Nuxt.js modules: Axios, Progressive Web App (PWA) Support, DotEnv
? Choose linting tools: None
? Choose test framework: None
? Choose rendering mode: Universal (SSR)
? Choose development tools: None
```
プロジェクト作成時の質問は、上記のように選択しました。
質問の内容等は以下の記事を参考とさせていただきました。
> - [【Nuxt.js】create-nuxt-appのすすめ 〜create-nuxt-appの質問に負けたくないあなたへ〜](https://qiita.com/cheez921/items/fdfd224099f686e3173d)
> - [Nuxt.js(v2)でgenerate納品する前にやっておきたい設定](https://qiita.com/amishiro/items/11bd642728f6b5838189)

### 3. Nuxt.jsの各種パッケージの最新化
作成されたプロジェクトのNuxt.jsは`v2.0.0`なので、[npm-check-updates](https://github.com/tjunnone/npm-check-updates)というパッケージを利用してNuxt.jsを含め各種パッケージを最新化します。
`[current_directory]`で操作してください。

【 1 】 `vue`コンテナへ入る

```ash
$ docker-compose run vue ash
```
【 2 】 `npm-check-updates`をインストールする

```text
# yarn global add npm-check-updates
```
【 3 】`ncu`コマンドが利用可能となるので、`ncu`をコマンドを実行して各種パッケージの最新バージョンの一覧を表示する

```text
# ncu
Checking /home/node/app/project/package.json
[====================] 7/7 100%

 nuxt                        ^2.0.0  →         ^2.12.2 
 @nuxt/typescript-runtime    ^0.4.0  →          ^0.4.5 
 @nuxtjs/axios               ^5.3.6  →         ^5.10.1 
 @nuxtjs/pwa               ^3.0.0-0  →  ^3.0.0-beta.20 
 @nuxtjs/dotenv              ^1.4.0  →          ^1.4.1 
 @nuxt/typescript-build      ^0.6.0  →          ^0.6.5 
 @nuxtjs/vuetify             ^1.0.0  →         ^1.11.0 

Run ncu -u to upgrade package.json
```
【 4 】各種パッケージの最新バージョンで`package.json`を書き換える

```text
# ncu -u
Upgrading /home/node/app/project/package.json
[====================] 7/7 100%

 nuxt                        ^2.0.0  →         ^2.12.2 
 @nuxt/typescript-runtime    ^0.4.0  →          ^0.4.5 
 @nuxtjs/axios               ^5.3.6  →         ^5.10.1 
 @nuxtjs/pwa               ^3.0.0-0  →  ^3.0.0-beta.20 
 @nuxtjs/dotenv              ^1.4.0  →          ^1.4.1 
 @nuxt/typescript-build      ^0.6.0  →          ^0.6.5 
 @nuxtjs/vuetify             ^1.0.0  →         ^1.11.0 

Run npm install to install new versions.
```
【 5 】依存関係を削除したうえで、インストールする

```text
# rm yarn.lock
# rm -r node_modules
# yarn install
```
`npm-check-updates`の使い方は、以下のサイトを参考にさせていただきました。
> [npm-check-updatesを使ってnpmライブラリのアップデートを確認する方法](https://tech.willgate.co.jp/entry/2019/10/24/180000)

<details><summary>※ここまでで一旦ローカル環境で動作可能です。</summary>
<div>

```bash
$ docker-compose run --service-ports vue yarn dev
# http://localhost:3000 でデフォルトページが確認できます。
```
</div></details>

### 4. srcディレクトリの作成
この手順の実施は任意ですが、ディレクトリ構造が複雑化しないようにsrcディレクトリを作成してソースファイルを移動しています。

```bash
$ mkdir src
$ mv assets components layouts pages plugins static store middleware src/
```
そして`nuxt.cofig.js`に[`srcDir`プロパティ](https://ja.nuxtjs.org/api/configuration-srcdir/)を追記します。

```javascript:nuxt.cofig.js
module.exports = {
  mode: 'universal',
  srcDir: 'src/',     // 追記!!
  ---(略)---
}
```

### 5. TypeScript化
ここからは、作成したNuxt.jsプロジェクトをTypeScript化していきます。
まずは、新しく`vue-shim.d.ts`というファイルを作成します。

```bash
$ touch src/vue-shim.d.ts
$ vi src/vue-shim.d.ts
```
作成することで`.vue`ファイルの型がTypeScriptのコンパイラによって拾われるようになります。
内容は下記の通り記載します。(詳細は[公式ページ](https://typescript.nuxtjs.org/ja/guide/setup.html)参照)

```typescript:vue-shim.d.ts
declare module "*.vue" {
  import Vue from 'vue'
  export default Vue
}
```
`nuxt.config.js`を`nuxt.config.ts`に変更し、内容を下記の通り追記および変更をします。

```typescript:nuxt.cofig.ts
import colors from 'vuetify/es5/util/colors'
import { Configuration } from '@nuxt/types' // 追記!!

const config: Configuration = {             // 変更!!
  mode: 'universal',
  ---(略)---
}
export default config                       // 追記!!
```
TypeScript化は、以下の記事を参考にさせていただきました。
> [Nuxt.js TypeScript - 実践TypeScript アップデート -](https://qiita.com/Takepepe/items/6e99d2f64f0814704e0f)

ローカルホストへ`--service-ports`(3000番ポート)を開きつつDockerで起動します。

```bash
$ docker-compose run --service-ports vue yarn dev
# http://localhost:3000 でデフォルトページが確認できます。
```
### 6. Netlifyへデプロイ
[Netlify](https://app.netlify.com/)へデプロイしていきます。

#####【事前準備】`netlify.toml`を作成し、GitHubへ登録する
`netlify.toml`をリポジトリのルートに配置することで、Netlifyのビルド設定、デプロイ設定、環境変数などをファイルベースで設定できます。(詳細は[公式ページ](https://docs.netlify.com/configure-builds/file-based-configuration/)参照)

```toml:netlify.toml
[build]
  base = "/project"
[build.environment]
  YARN_VERSION = "1.22.0"
  YARN_FLAGS = "--no-ignore-optional"
  NODE_VERSION = "12.16.1"
```

変数名|説明
----|----
base|package.jsonの配置されているディレクトリを指定(デフォルトではルートディレクトリ)
YARN_VERSION|yarnのバージョン(デフォルトでnpmはあるがyarnはインストールされていない、指定することでインストールされる)
YARN_FLAGS|yarn installにつけるオプション(デフォルトで依存関係をインストールしない`--ignore-optional`となっているので、`--no-ignore-optional`を指定)
NODE_VERSION|node.jsのバージョン(デフォルトではv10であるため)
`[build.environment]`の詳細は[公式ページ](https://docs.netlify.com/configure-builds/environment-variables/)参照

その後、GitHubなどにリポジトリを作成し`[current_directory]`をリポジトリ登録してください。(この記事ではGitHubを利用)
![github.com_nuxt_ssr_typescript_on_docker.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/609518/2338c936-1a96-0e8f-fdfb-a21992493e47.png)


##### 【 1 】Netlifyへアクセスし、ログインをする
https://app.netlify.com/
![www.netlify.com_.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/609518/245b6732-38ec-302a-6645-c500f8595a2a.png)


##### 【 2 】Sitesページで「New site from Git」を押下し、登録したリポジトリを選択します。
![app.netlify.com_teams_numasa.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/609518/78962bc3-91bd-8cb5-92f5-59ce59010fc6.png)
![app.netlify.com_teams_numasa_create_site_.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/609518/c40f2a4a-da54-ccd6-8627-6068d9269273.png)
![app.netlify.com_teams_numasa_pick_repository.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/609518/08e9ec3e-e45e-28c0-72b3-bb0d38ec032e.png)
##### 【 3 】デプロイ設定をして、デプロイする
Branch to deploy(デプロイ対象のブランチ)に`master`を選択し、
Build commandに`yarn generate`、Publish directoryに`dist`を入力して「Deploy site」を押下します。
![app.netlify.com_teams_numasa_deploy.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/609518/b852abb9-eab0-c52a-77b0-84069ffb8882.png)
しばらくすると「Deploys」タブでデプロイが`Published`となり、URLが払い出されています。
![app.netlify.com_sites_clever-snyder_deploy.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/609518/2bc175a7-38f4-cd48-1ce2-27ef3a8654c7.png)
##### 【 4 】デプロイ完了
払い出されたURLにアクセスし、Nuxt.js + Vuetify.js のデフォルトTOPページが表示されます。
![netlify.app.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/609518/52931d88-fd1c-1c3f-db1e-350a2168bb8a.png)
以下の記事を参考にさせていただきました。
> [Nuxt.js + Netlifyで爆速構築するサーバーレス開発入門](https://qiita.com/isihigameKoudai/items/e3b136e9964f1d30d73d)

### 参考リンク
記事中にも記載していますが、参考にさせていただいたリンクです。
> - [【Nuxt.js】create-nuxt-appのすすめ 〜create-nuxt-appの質問に負けたくないあなたへ〜](https://qiita.com/cheez921/items/fdfd224099f686e3173d)
> - [Nuxt.js(v2)でgenerate納品する前にやっておきたい設定](https://qiita.com/amishiro/items/11bd642728f6b5838189)
> - [npm-check-updatesを使ってnpmライブラリのアップデートを確認する方法](https://tech.willgate.co.jp/entry/2019/10/24/180000)
> - [Nuxt.js TypeScript - 実践TypeScript アップデート -](https://qiita.com/Takepepe/items/6e99d2f64f0814704e0f)
> - [Nuxt.js + Netlifyで爆速構築するサーバーレス開発入門](https://qiita.com/isihigameKoudai/items/e3b136e9964f1d30d73d)