Nuxt3 + TypeScript + TailwindCSS + ESLint + API Routesの導入

## はじめに

Nuxt3を学習するにあたって導入に色々とつまずいたので、備忘録として導入手順を記しておきます。

## 環境

VSCode: v1.67.2

node: v18.0.0
yarn: v1.22.18

nuxt: v3.0.0-rc.3
typescript: v4.6.4
tailwindcss: v3.0.24
eslint: v8.15.0

## 導入

### Nuxt3

#### インストール

今回作成するアプリ名は「nuxt-app」とします。

```zsh
$ npx nuxi init nuxt-app
```

```zsh
Need to install the following packages:
  nuxi
Ok to proceed? (y) y
```

上記の確認が表示された場合は、yを入力。

```zsh
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: 'nuxi@3.0.0-rc.3',
npm WARN EBADENGINE   required: { node: '^14.16.0 || ^16.11.0 || ^17.0.0 || ^18.0.0' },
npm WARN EBADENGINE   current: { node: 'v16.10.0', npm: '7.24.0' }
npm WARN EBADENGINE }
```

ここでこのようなエラーが発生した場合はnodeのバージョンによるものなので、requiredにあるバージョンのnodeを使用するようにしてください。

作成が完了したら、

```zsh
$ yarn install
```

を実行して必要moduleをインストール。

```zsh
$ yarn run dev
```

そして動作確認。

現時点でのディレクトリ構造は以下のようになる。

```zsh
.
├── .gitignore
├── .nuxt //yarn run devを実行すると作成される
├── README.md
├── app.vue
├── node_modules
├── nuxt.config.ts
├── package.json
├── tsconfig.json
└── yarn.lock
```

結構簡素ですね。componentsフォルダやpagesフォルダなどは適宜自分で作成するっぽい。

#### nuxt.config.tsの設定

とりあえず、よくわからないけど`nitro`を設定。

```ts
import { defineNuxtConfig } from 'nuxt';

// https://v3.nuxtjs.org/api/configuration/nuxt.config
export default defineNuxtConfig({
  nitro: {
    preset: 'node',
  },
});
```

次に、`srcDir`を設定。これでcomponentsやpagesなどのディレクトリを`./src`以下に収めることができる。ワークスペースに`src`ディレクトリがないと許さない呪いにかかっている場合は必須の設定。

### TypeScript

#### インストール

Nuxt3ではTypeScriptが標準で対応されているため、インストールは特に不要(後にeslintで必要になるが)。設定だけしていく。

#### tsconfig.jsonの設定

初期時点でのtsconfigの中身は

```json:./tsconfig.json
{
  // https://v3.nuxtjs.org/concepts/typescript
  "extends": "./.nuxt/tsconfig.json"
}
```

このようにめっちゃ簡素。`extends`からわかる通り、実体は`.nuxt`にある。

```json:.nuxt/tsconfig.json
// Generated by nuxi
{
  "compilerOptions": {
    "jsx": "preserve",
    "target": "ESNext",
    "module": "ESNext",
    "moduleResolution": "Node",
    "skipLibCheck": true,
    "strict": false,
    "allowJs": true,
    "noEmit": true,
    "resolveJsonModule": true,
    "allowSyntheticDefaultImports": true,
    "types": [
      "node"
    ],
    "baseUrl": "..",
    "paths": {
      "~~": [
        "."
      ],
      "~~/*": [
        "./*"
      ],
      "@@": [
        "."
      ],
      "@@/*": [
        "./*"
      ],
      "~": [
        "."
      ],
      "~/*": [
        "./*"
      ],
      "@": [
        "."
      ],
      "@/*": [
        "./*"
      ],
      "assets": [
        "assets"
      ],
      "public": [
        "public"
      ],
      "#app": [
        "node_modules/nuxt/dist/app"
      ],
      "#app/*": [
        "node_modules/nuxt/dist/app/*"
      ],
      "vue-demi": [
        "node_modules/nuxt/dist/app/compat/vue-demi"
      ],
      "#head": [
        "node_modules/nuxt/dist/head/runtime"
      ],
      "#head/*": [
        "node_modules/nuxt/dist/head/runtime/*"
      ],
      "#components": [
        ".nuxt/components"
      ],
      "#imports": [
        ".nuxt/imports"
      ],
      "#build": [
        ".nuxt"
      ]
    }
  },
  "include": [
    "./nuxt.d.ts",
    "../**/*"
  ]
}
```

`.nuxt/tsconfig.json`から加えたり変更したい設定を`./tsconfig.json`に記述する。これはそれぞれお好みの設定でOK。今回は型チェックの強化を行う。

```json:./tsconfig.json
{
  // https://v3.nuxtjs.org/concepts/typescript
  "extends": "./.nuxt/tsconfig.json",
  "compilerOptions": {
    "strict": true,
    "exactOptionalPropertyTypes": true,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true,
    "noUncheckedIndexedAccess": true,
    "noImplicitOverride": true,
    "noPropertyAccessFromIndexSignature": true
  }
}
```

### TailwindCSS

#### インストール

```zsh
$ yarn add -D tailwindcss@latest
```
`@nuxtjs/tailwindcss`、`postcss`、`autoprefixer`は無くても動作した。

```zsh
$ npx tailwindcss init
```

その後`src`に`assets`フォルダを作成し、`assets`に`css`フォルダを作成し、さらに`css`に`tailwind.css`を作成する。

```
./src
└── css
    └── tailwind.css
```

```css:tailwind.css
@tailwind base;
@tailwind components;
@tailwind utilities;
```

#### tailwind.config.jsの設定

```js:./tailwind.config.js
module.exports = {
  content: [
    './app.vue',
    './src/components/**/*.{vue,js}',
    './src/layouts/**/*.vue',
    './src/pages/**/*.vue',
    './src/plugins/**/*.{js,ts}',
  ],
  darkMode: false,
  theme: {
    extend: {},
  },
  variants: {
    extend: {},
  },
  plugins: [],
};
```

`content`に`nuxt.config.ts`を指定するとエラーが出るので注意。`darkMode`もなんかwarnが出るけどとりあえず無視。

#### nuxt.config.tsの修正

`nuxt.config.ts`にTailwindCSSの設定を加える。

```ts
import { defineNuxtConfig } from 'nuxt';

// https://v3.nuxtjs.org/api/configuration/nuxt.config
export default defineNuxtConfig({
  srcDir: 'src/',
  nitro: {
    preset: 'node',
  },
  css: ['@/assets/css/tailwind.css'],
  build: {
    postcss: {
      postcssOptions: {
        plugins: {
          tailwindcss: {},
        },
      },
    },
  },
});
```

`@`には`srcDir`に指定したディレクトリが勝手に入る模様。なので`@/src/assets/tailwind.css`とする必要はない。

### ESLint

#### インストール

```zsh
$ yarn add -D eslint eslint-plugin-vue typescript @typescript-eslint/eslint-plugin @typescript-eslint/parser @nuxtjs/eslint-config-typescript
```

```zsh
$ yarn run eslint --init
```

```zsh
Need to install the following packages:
  @eslint/create-config
Ok to proceed? (y) y

? How would you like to use ESLint?
❯ To check syntax and find problems

? What type of modules does your project use?
❯ JavaScript modules (import/export)

? Which framework does your project use?
❯ Vue.js

? Does your project use TypeScript? › Yes

? Where does your code run?

? Where does your code run?
✔ Browser
✔ Node

? What format do you want your config file to be in?
❯ JavaScript

? Would you like to install them now with npm? › No

$ yarn install

// もしprettierを使用しているなら以下もインストール
$ yarn add -D eslint-config-prettier
```

```js
module.exports = {
  env: {
    browser: true,
    es2021: true,
    node: true,
  },
  extends: [
    'eslint:recommended',
    'plugin:vue/essential',
    'plugin:@typescript-eslint/recommended',
    '@nuxtjs/eslint-config-typescript', // 手動で追加
    'prettier', // prettierを使用しているなら追加
  ],
  parserOptions: {
    ecmaVersion: 'latest',
    parser: '@typescript-eslint/parser',
    sourceType: 'module',
  },
  plugins: ['vue', '@typescript-eslint'],
  rules: {
  },
};
```

### API Routes

地味にここに時間がかかった。
正式名称がよくわからないけどとりあえずAPI Routesと呼ばれていたのでそれに倣うこととする。

他のところにはapiは`~/server/api/`に置けと書いてあったけど、実際には`@/server/api/`だった。後、`index`という名前は認識してくれないっぽい。