Nuxt TypeScript を docker-compose で構築する

# はじめに
[Nuxt TypeScript](https://typescript.nuxtjs.org/) でセットアップの手順は公式にて公開しているが、docker-composeを用いた構築の情報が少なかったので纏めてみた。

また、Ver.2.9以降からTypeScriptへの導入手順が変わり、個人的に少し躓いた部分もあったのでその点も踏まえて共有しようと思う。

# 1. 実行環境
* macOS Catalina Ver.10.15.1
* Docker version 19.03.4, build 9013bf5
* docker-compose version 1.24.1, build 4667896b
* Nuxt.js Ver.2.10.2
* node.js 12.13.0-alpine

# 2. 前提条件
* Nuxt.js のバージョンが 2.9 以上である事。
* PC内に docker, docker-compose, node.js がインストールされている事。

docker , docker-compose , node.js がインストールされていない場合は下記を実行すると良い。

## 2.1. Docker のインストール
Docker をインストールする為に [DockerHub](https://hub.docker.com/editions/community/docker-ce-desktop-mac) にアクセスし、`Docker.dmg`をダウンロードする。
※ DockerHubを初めて利用する場合はアカウント作成が必要である。

`Docker.dmg`を起動すると、Dockerのインストールが行われるので、インストール完了後に Docker を Applications にコピーする。

また、他の方法として[Homebrew](https://brew.sh/index_ja)と呼ばれるパッケージ管理システムを用いてインストールする事ができる。以下にインストール手順を示す。

```terminal
# Homebrew をインストール
$ /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

# Homebrew が正しくインストールされているかどうか確認する
$ brew doctor

# Docker をインストールする
$ brew install docker
$ brew cask install docker
```

docker, Homebrew のバージョンの確認は以下のコマンドを実行する事で確認できる。

```terminal
# Docker
$ docker --version                                                                                                                                                                             
Docker version 19.03.4, build 9013bf5

# Homebrew
$ brew --version                                                                                                                                                                               
Homebrew 2.1.16
Homebrew/homebrew-core (git revision 00c2c; last commit XXXX-XX-XX)
Homebrew/homebrew-cask (git revision 9e283; last commit XXXX-XX-XX)
```

## 2.2. docker-compose のインストール
docker-compose のインストールは以下のコマンドを実行すると良い。

```terminal
$ curl -L https://github.com/docker/compose/releases/download/1.3.1/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose

# docker-compose コマンドを実行できるように、権限を設定する。
$ chmod +x /usr/local/bin/docker-compose
```

補足：実行中、`Permission denied` エラーが表示された場合、`/usr/local/bin` ディレクトリが書き込み可能が許可されていない可能性がある。その際、Compose のインストールをスーパーユーザで行う必要がある。`sudo -i` を実行してから、上記の2つのコマンドを実行し、`exit`する。

# 3. Nuxt.js 環境構築
TypeScript に変換する前に、まず Nuxt.js のプロジェクトを作成する必要があるので作成手順に関して説明する。

## 3.1. ファイル構成
今回のファイル構成は以下の通りである。1つのディレクトリに異なるファイルが複数混同する事を避けるため、project ディレクトリを新規し、その中に`Dockerfile`を作成する。

また、Nuxt.js のプロジェクト内のファイル階層も綺麗にしたかったので、`src`ディレクトリを新規作成し、その中に纏めている。

以下に、ファイル構成を示す。

```terminal
.
├── README.md
├── docker-compose.yml
└── project
    ├── Dockerfile
    ├── README.md
    ├── node_modules
    ├── nuxt.config.ts
    ├── package.json
    ├── src
    │   ├── assets
    │   ├── components
    │   ├── layouts
    │   ├── middleware
    │   ├── pages
    │   ├── plugins
    │   ├── static
    │   ├── store
    │   └── vue-shim.d.ts
    ├── tsconfig.json
    ├── yarn-error.log
    └── yarn.lock
```

## 3.2. Dockerfile
はじめに、Dockerfileを作成する。`node.js`に関しては、[DockerHub](https://hub.docker.com/_/node/) にイメージが公開されているのでそれを使用する。

```docker:Dockerfile

# イメージ指定
FROM node:12.13.0-alpine

# コマンド実行
RUN apk update && \
    apk add git && \
    npm install -g @vue/cli nuxt create-nuxt-app && \

```

今回はイメージの軽量化の為に、`alpine`を使用した。

※ **alpine** : Alpine Linuxと呼ばれる、`BusyBox`と`musl`をベースにしたLinuxディストリビューションを指す

## 3.3. docker-compose.yml 
次に、docker-compose.ymlを作成する。

```docker:docker-compose.yml
version: '3'
services:
  node:
    # Dockerfileの場所
    build: 
        context: ./
        dockerfile: ./project/Dockerfile
    working_dir: /home/node/app/project
    # ホストOSとコンテナ内でソースコードを共有する
    volumes:
      - ./:/home/node/app
    # コンテナ内部の3000を外部から5000でアクセスする
    ports:
      - 5000:3000
    environment:
      - HOST=0.0.0.0
```

## 3.4. dockerイメージ作成
Dockerfile, docker-compose.ymlを作成後、以下のコマンドを実行し docker イメージを作成する。

```terminal
$ docker-compose build
```

実行後、イメージができているかどうかは以下のコマンドで確認できる。

```terminal
$ docker images
REPOSITORY                    TAG              IMAGE ID        CREATED       SIZE
docker-nuxt-typescript_node   latest           7f8324973b48    3 days ago    434MB
node                          12.13.0-alpine   5d187500daae    3 weeks ago   80.1MB
```

## 3.5. Nuxt.jsの起動
コンテナ内で`npm`、`yarn`コマンドを実行したい場合は下記の様に指定する。

```terminal
$ docker-compose run --rm <Container Name> <Command>
```

以下のコマンドを実行し、Nuxt.jsを起動させセットアップを行う。

```terminal
$ docker-compose run --rm nuxt npx create-nuxt-app ./project
Project name (your-title)
Project description (My wicked Nuxt.js project)
Author name (author-name)
Choose the package manager 
> Yarn
  Npm
Choose UI framework (Use arrow keys)
> None
  Ant Design Vue
  Bootstrap Vue
  Buefy
  Bulma
  Element
  Framevuerk
  iView
  Tachyons
  Tailwind CSS
  Vuetify.js
Choose custom server framework (Use arrow keys)
> None (Recommended)
  AdonisJs
  Express
  Fastify
  Feathers
  hapi
  Koa
  Micro
# スペースで選択する事ができる。(Enterキーではないので注意)
Choose Nuxt.js modules
>(*) Axios
 ( ) Progressive Web App (PWA) Support
Choose linting tools 
>(*) ESLint
 ( ) Prettier
 ( ) Lint staged files
Choose test framework 
> None
  Jest
  AVA
Choose rendering mode (Use arrow keys)
  Universal (SSR) // WEB SITE
> Single Page App // WEB APPLICATION
Choose development tools
>(*) jsconfig.json (Recommended for VS Code)
```

上記の設定は、筆者がセットアップに選択したものである。設定に関しては各々で必要なものを導入すると良い。

## 3.6. コンテナの立ち上げ
以下のコマンドを実行し、コンテナを起動させる。

```terminal
$ docker-compose run node npm run dev
# OR
$ docker-compose run node yarn run dev
```

起動後、http://localhost:5000/ にアクセスし、サンプルアプリ画面が確認できれば一旦完了である。

# 4. TypeScript化
最後に、Nuxt.js を TypeScript に変更する手順について説明する。

## 4.1. @nuxt/typescript-build, @nuxt/typescript-runtime のインストール
TypeScript化を進める前に、まずはじめに`@nuxt/typescript-build`、`@nuxt/typescript-runtime`の2つを以下のコマンドを実行してインストールする。
(どちらか一方でも動くが、今回は両方インストールしておく)

※ **@nuxt/typescript-build** : `nuxt build`で TypeScript を扱うためのもの。
※ **@nuxt/typescript-runtime** : Node.js 環境下で TypeScript を処理するためのもの。

```terminal
$ docker-compose run node npm install --save-dev @nuxt/typescript-build
$ docker-compose run node npm install @nuxt/typescript-runtime
# OR
$ docker-compose run node yarn add --dev @nuxt/typescript-build
$ docker-compose run node yarn add @nuxt/typescript-runtime
```

`@nuxt/types`に関しては、上記の2つに内包されているのでインストールをする必要は無い。

## 4.2. tsconfig.json の作成
Nuxt.js で作成されている`jsconfig.json`を削除し、`tsconfig.json`を新規作成する。
`tsconfig.json`のソースは以下の通りである。

```json:tsconfig.json
{
  "include": [
    "./src/**/*"
  ],
  "compilerOptions": {
    "target": "esnext",
    "module": "esnext",
    "moduleResolution": "node",
    "lib": [
      "esnext",
      "esnext.asynciterable",
      "dom"
    ],
    "esModuleInterop": true,
    "experimentalDecorators": true,
    "allowJs": true,
    "sourceMap": true,
    "strict": true,
    "noEmit": true,
    "baseUrl": ".",
    "paths": {
      "~/*": [
        "./src/*"
      ],
      "@/*": [
        "./src/*"
      ]
    },
    "types": [
      "@types/node",
      "@nuxt/types",
      "@nuxtjs/axios"
    ]
  },
  "exclude": [
    "node_modules"
  ]
}
```

今回は、プロジェクト内のディレクトリを`src`ディレクトリに移動させているので、`include`、`paths`などの設定を変更している。

## 4.3. nuxt.config.ts の作成
次に、`nuxt.config.js`を削除し、`nuxt.config.ts`を新規作成する。
`nuxt.config.ts`のソースは以下の通りである。

```ts:nuxt.config.ts
import { Configuration } from '@nuxt/types'
 
const nuxtConfig: Configuration = {
  mode: 'universal',
  buildModules: ['@nuxt/typescript-build'],
  server: {
    port: 5000,
    host: 'localhost',
  },
  head: {
    title: process.env.npm_package_name || '',
    meta: [
      { charset: 'utf-8' },
      { name: 'viewport', content: 'width=device-width, initial-scale=1' },
      { hid: 'description', name: 'description', content: process.env.npm_package_description || '' }
    ],
    link: [
      { rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }
    ]
  },
  loading: { color: '#fff' },
  css: [
  ],
  plugins: [
  ],
  typescript: {
    typeCheck: true,
    ignoreNotFoundWarnings: true
  },
  modules: [
    '@nuxtjs/axios',
    '@nuxtjs/eslint-module',
  ],
  build: {
    extend(config, ctx) {
    }
  },
  srcDir: 'src/'
}
module.exports = nuxtConfig
```

Ver.2.9からは、`nuxt.config.ts`内の Configuration の型定義が`@nuxt/types`からExportされるようになった為、はじめに Import している。
また、追加された [buildModules](https://nuxtjs.org/api/configuration-modules#-code-buildmodules-code-) に`@nuxt/typescript-build`を指定している。`buildModules`に変更になった事で、`build`内で使用する`module`を`devDependencies`に指定できるようになった。

## 4.4. vue-shim.d.ts の作成
`vue-shim.d.ts`は、記述されているコードを TypeScript として認識させる為に必要となるファイルである。しかし、`.vue`ファイル中にインポート等をする必要は無く、`src`ディレクトリ内にあれば問題ない。
`vue-shim.d.ts`のソースは以下の通りである。

```ts:vue-shim.d.ts
declare module "*.vue" {
  import Vue from "vue";
  export default Vue;
}
```

## 4.5. package.json の修正
`package.json`は、Nuxt.js のセットアップ時に作成されるが、TypeScript が適用されていないのでその点を修正する。修正する部分としては`package.json`内の`scripts`が、nuxt コマンドなので、そこを`nuxt-ts`コマンドに変更する。
修正後のソースは以下の通りである。

```json:package.json
# 一部省略
{
  ...
  "scripts": {
    "dev": "nuxt-ts",
    "build": "nuxt-ts build",
    "start": "nuxt-ts start",
    "generate": "nuxt-ts generate",
    "lint": "eslint --ext .js,.vue --ignore-path .gitignore ."
  }
  ...
}
```

## 4.6. Nuxt TypeScript の実行
変更を踏まえ、再度 Nuxt.js を起動する。

```terminal
$ docker-compose run node yarn run dev                                                                                                                 
yarn run v1.19.1
$ nuxt-ts

   ╭─────────────────────────────────────────────╮
   │                                             │
   │   Nuxt.js v2.10.2                           │
   │   Running in development mode (universal)   │
   │                                             │
   │   Listening on: http://localhost:5000/      │
   │                                             │
   ╰─────────────────────────────────────────────╯

ℹ Preparing project for development
ℹ Initial build may take a while
✔ Builder initialized
✔ Nuxt files generated
ℹ Starting type checking service...
ℹ Using 1 worker with 2048MB memory limit                                                                                                                                         

✔ Client
  Compiled successfully in 13.16s

✔ Server
  Compiled successfully in 11.98s

ℹ No type errors found
ℹ Version: typescript 3.6.4 
ℹ Time: 11648ms
ℹ Waiting for file changes
ℹ Memory usage: 254 MB (RSS: 315 MB)
```

上記を見て貰うと、`nuxt-ts`コマンドで実行している事が確認できる。
実行後、先程と同じく http://localhost:5000/ にアクセスし画面が表示する事を確認する。

以上で、TypeScript化は完了である。

# 5. まとめ
今回は、Nuxt TypeScript を docker-comnpose で構築した。Ver.2.9 からの変更で docker-compose 下で動かなくなった人を少しでもサポートできるなら幸いである。

また、今回のサンプルは [GitHub](https://github.com/t4i5uKE/docker-nuxt-typescript) に公開しているので、何か不明点や修正点があれば随時対応していこうと思う。

◆追記◆
11/24 : Githubの`project/README.md`を英語翻訳化しました。