🛠️

Next.js 14 + TypeScript + Tailwind + ESLint + Prettier 環境構築手順

2023/03/01に公開

Next.js プロジェクトにおけるベーシックな環境として TypeScript、Tailwind、ESLint、Prettier を導入する手順をまとめます。

また、Next.js は App Router を採用します。

前提

  • Node.js 20.10.0
  • pnpm 8.10.2

Next.js インストール

公式のコマンド通りに Next.js のプロジェクトをセットアップします。

https://nextjs.org/docs/pages/api-reference/create-next-app

pnpm create next-app

対話式で以下のように回答。

? What is your project named? › my-app
# プロジェクト名を入力

? Would you like to use TypeScript? › No / Yes
# Yes を選択

? Would you like to use ESLint? › No / Yes
# Yes を選択

? Would you like to use Tailwind CSS? › No / Yes
# Yes を選択

? Would you like to use `src/` directory? › No / Yes
# 好みで選択(個人的に最近は No にしている)

? Would you like to use App Router? (recommended) … No / Yes
# Yes を選択

? Would you like to customize the default import alias (@/*)? … No / Yes
# No を選択

セットアップが完了すると、以下のディレクトリ構成でプロジェクトが作成されます。

.
├── public
│   ├── next.svg
│   └── vercel.svg
├── src
│   └── app
│       ├── favicon.ico
│       ├── globals.css
│       ├── layout.tsx
│       └── page.tsx
├── .eslintrc.json
├── .gitignore
├── next-env.d.ts
├── next.config.js
├── package.json
├── pnpm-lock.yaml
├── postcss.config.js
├── README.md
├── tailwind.config.ts
└── tsconfig.json

表示確認し、問題なければここで git push しておきます。

pnpm dev

SSG エクスポート設定

SSG によるエクスポートをおこなう場合は、next.config.js にエクスポート設定を追加します。v13 までの next export コマンドは v14 では廃止されています。

next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
+ output: 'export',
+ distDir: 'dist'
}

module.exports = nextConfig

これで next build を実行すると dist ディレクトリに静的ファイルが出力されますので、.gitignore にも /dist/ を追加しておきます。

.gitignore
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js
.yarn/install-state.gz

# testing
/coverage

# next.js
/.next/
/out/
+ /dist/

# production
/build

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# local env files
.env*.local

# vercel
.vercel

# typescript
*.tsbuildinfo
next-env.d.ts

パスエイリアスの設定

プロジェクト作成時の対話式コマンドで import のパスエイリアスを設定済ですが、ドキュメントルート直下のデータを参照するためのパスエイリアスも追加しておきます。

また、target の値を esnext に変更しておきます。

tsconfig.json
{
  "compilerOptions": {
-   "target": "es5",
+   "target": "esnext",
    "lib": ["dom", "dom.iterable", "esnext"],
    "allowJs": true,
    "skipLibCheck": true,
    "strict": true,
    "noEmit": true,
    "esModuleInterop": true,
    "module": "esnext",
    "moduleResolution": "bundler",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "jsx": "preserve",
    "incremental": true,
    "plugins": [
      {
        "name": "next"
      }
    ],
    "paths": {
      "@/*": ["./src/*"]
+     "@@/*": ["./*"]
    }
  },
  "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
  "exclude": ["node_modules"]
}

これにより ESModules で下記のような記述でのインポートができるようになります。

import buttonA from '@/components/buttonA'
import config from '@@/package.json'

.node-version の追加

自分の環境では nodenv を利用しているので、利用中の Node バージョンに合わせて .node-version ファイルを作成します。

node -v
v20.10.0

nodenv local 20.10.0

.editorconfig の追加

.editorconfig を追加します。

.editorconfig
# http://editorconfig.org
root = true

[*]
charset = utf-8
end_of_line = lf
indent_style = space
indent_size = 2
trim_trailing_whitespace = true
insert_final_newline = true

[*.{md,markdown}]
trim_trailing_whitespace = false

Prettier

コードフォーマットの環境を整えるために、以下のコマンドで Prettier と関連プラグインをインストールします。

pnpm add -D prettier prettier-plugin-organize-imports

Prettier の設定ファイル .prettierrc を以下の内容をでプロジェクトのルートに配置します。

{
  "trailingComma": "all",
  "tabWidth": 2,
  "useTabs": false,
  "semi": false,
  "singleQuote": true,
  "jsxSingleQuote": false,
  "arrowParens": "always",
  "printWidth": 80,
  "bracketSpacing": true,
  "plugins": ["prettier-plugin-organize-imports"],
  "overrides": [
    {
      "files": "*.html",
      "options": {
        "printWidth": 360
      }
    },
    {
      "files": ["*.css", "*.scss"],
      "options": {
        "singleQuote": false
      }
    }
  ]
}

フォーマットの対象外を定義した .prettierignore も合わせて配置します。

.prettierignore
node_modules/
.next/
.nuxt/
.astro/
build/
dist/
out/
public/
package-lock.json
yarn.lock
pnpm-lock.yaml
next.config.js

VS Code 設定

ファイル保存時に Prettier が動作するように、以下の内容で .vscode/settings.json をドキュメントルートに作成します。ついでに import 文の補完処理も追記しておきます。

settings.json
{
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "editor.formatOnSave": true,
  "editor.formatOnPaste": true,
  "editor.codeActionsOnSave": {
    "source.addMissingImports": "explicit",
  },
  "javascript.preferences.importModuleSpecifier": "non-relative",
  "typescript.preferences.importModuleSpecifier": "non-relative",
}

ESLint

ESLint では以下のパッケージをインストールして利用します。

過去には eslint-config-standard-with-typescript をはじめ、複数の共有設定やプラグインを導入して細かいルールを設定していましたが、導入や保守に手間をかけたくはないので、Next.js が採用している next/core-web-vitals をベースに以下のプラグインをインストールすることにしました。

pnpm add -D @typescript-eslint/parser @typescript-eslint/eslint-plugin eslint-plugin-tailwindcss eslint-config-prettier

以下の内容で .eslintrc.json を更新します。

.eslintrc.json
{
  "extends": [
    "next/core-web-vitals",
    "plugin:@typescript-eslint/recommended",
    "plugin:tailwindcss/recommended",
    "prettier"
  ],
  "parser": "@typescript-eslint/parser",
  "plugins": ["@typescript-eslint"],
  "rules": {
    "@typescript-eslint/consistent-type-imports": [
      "error",
      {
        "prefer": "type-imports",
        "fixStyle": "separate-type-imports"
      }
    ],
    "@typescript-eslint/no-unused-vars": [
      "warn",
      {
        "vars": "all",
        "varsIgnorePattern": "^_",
        "args": "after-used",
        "argsIgnorePattern": "^_"
      }
    ],
    "object-shorthand": "error",
    "tailwindcss/no-custom-classname": "off",
    "react/jsx-curly-brace-presence": "error",
    "react/self-closing-comp": [
      "error",
      {
        "component": true,
        "html": false
      }
    ],
    "@next/next/no-img-element": "off",
  }
}

.eslintignore も追加します。こちらも .prettierrc 同様、パースエラーによる不具合の出る可能性のある設定系のファイルは対象外に含めています。

.eslintignore
node_modules/
.next/
.nuxt/
.astro/
build/
dist/
out/
public/
package-lock.json
yarn.lock
pnpm-lock.yaml
vite.config.ts
next.config.js
tsconfig.json
src/env.d.ts
*.cjs
*.mjs

VS Code 追加設定

VS Code の設定に ESLint 関連の設定を追加します。

settings.json
{
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "editor.formatOnSave": true,
  "editor.formatOnPaste": true,
  "editor.codeActionsOnSave": {
    "source.addMissingImports": "explicit",
+   "source.fixAll.eslint": "explicit",
  },
  "javascript.preferences.importModuleSpecifier": "non-relative",
  "typescript.preferences.importModuleSpecifier": "non-relative",
+ "eslint.validate": [
+   "html",
+   "javascript",
+   "javascriptreact",
+   "typescript",
+   "typescriptreact"
+ ],
}

フォーマットコマンドの追加

npm-run-all パッケージをインストールし、npm scripts から実行できるフォーマットコマンドを追加します。

pnpm add -D npm-run-all
package.json
{
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
-   "lint": "next lint"
+   "lint": "run-p -l -c --aggregate-output lint:*",
+   "lint:eslint": "eslint .",
+   "lint:prettier": "prettier --check .",
+   "fix": "run-s fix:prettier fix:eslint",
+   "fix:eslint": "npm run lint:eslint -- --fix",
+   "fix:prettier": "npm run lint:prettier -- --write",
  },
}

上記コマンドは以下の記事を参考にしました。

https://zenn.dev/teppeis/articles/2021-02-eslint-prettier-vscode

記事で言及されている通り、ポイントは以下となります。

  • フォーマットの対象ファイルは .eslintignore.prettierignore で管理し、npm scripts には記述しない
  • npm-run-all を利用して処理のための記述をシンプルにする
  • fix は Prettier → ESLint の順に順次実行させる

Husky + lint-staged

Prettier と VS Code の設定により、コーディング中に自動フォーマットが行われるようになっていますが、Husky と lint-staged を導入してコミット前に自動フォーマットと検知・修正をおこなうようにします。

pnpm add -D husky lint-staged

Husky のインストール後のセットアップ手順は公式サイトも参考にしてください。

https://typicode.github.io/husky/getting-started.html

以下のコマンドで Husky による Git フックを有効化します。フックが有効化されるとドキュメントルートに .husky ディレクトリが作られます。

npx husky install

npm scripts に prepare コマンドを追加します。これによりプロジェクトをインストールした開発者が意識しなくても Husky を利用する準備が自動的におこなわれます。

npm pkg set scripts.prepare="husky install"

コミット前に lint-staged を実行する Git フックを husky コマンドから追加します。

npx husky add .husky/pre-commit "npx lint-staged"

package.json に lint-staged でおこなう処理を追加します。

package.json
{
  ...
  "devDependencies": {
    ...
  },
+ "lint-staged": {
+   "src/**/*.{js,jsx,ts,tsx}": [
+     "eslint --fix",
+     "prettier --write"
+   ]
+ }
}

ここまでの設定を完了してからファイルのコミットを実行すると、ステージされているファイルに対して pre-commit で定義した処理が自動実行されます。ここでは pre-commitnpx lint-staged の処理を定義しているため、ステージングされたファイルの拡張子に応じて package.json に記述した lint-staged の処理、すなわち ESLint と Prettier が実行されます。

Discussion