🙆

オレオレ Next.js 初期設定

2023/08/26に公開

最近、フロントエンドで新規プロジェクトを開始する際は、Next.js を選定することが多いと思います。

create-next-app をした際に都度、もろもろの初期設定 (Prettier, Husky, storybook, etc...) を行う儀式がありますよね?

この儀式である程度 「最初に設定しときたいよね」と思うものが自分の中で固まってきたので、今回はオレオレ初期設定を紹介しようと思います。

やること

以下のことをやります。

  1. Node環境: Volta
  2. フォーマット: Prettier
  3. Lint ツール: ESLint, Husky, lint-staged
  4. Test ツール: Jest, React Testing Library, Storybook
  5. テンプレ自動化: Plop
  6. scripts 整理: package.json
  7. IDE 設定: .editorconfig, VS Code(extensions.json, settings.json)

create-next-app

とにもかくにも、ここから始まる。

pnpm dlx create-next-app@latest

現在 (Next v13.4) は cli に従うとデフォルトで次の設定がされます。

  • App Router
  • React v18.2
  • Typescript v5.1
  • Tailwind v3.3
  • ESLint v8.47
  • typescript-eslint v6.4

参考

https://nextjs.org/docs/getting-started/installation

Volta でバージョンを固定する

次にやることは現在の Node.js のバージョンを固定することです。

Volta は Node や pnpm のバージョンを管理できるツールです。
volta pin コマンドでプロジェクトごとにバージョンを固定させるだけでなく、自動でバージョンの切り替えが行わます。チーム開発における統一的な環境構築に有効です。

terminal
volta pin node
package.json
{
   // ...
   // 自動で作成される
+  "volta": {
+    "node": "18.17.1",
+  }
}

Prettier

Prettier は言わずと知れたコードフォーマッターです。

末尾のセミコロンの有無やインデント幅などを自動で整えてくれます。
インデントが揃っていないコードを読むと目が弾け飛ぶので目の保護のためにも必須で入れます。

今回は tailwind が入っているので class 名の順序を自動で整えてくれる prettier-plugin-tailwindcss というプラグインも同時に入れます。

設定

terminal
pnpm add --save-dev prettier eslint-config-prettier prettier-plugin-tailwindcss

次のファイルをプロジェクト直下に配置する。

.eslintrc.json
{
  "extends": ["next", "prettier"]
}
.prettierrc.json
{
  "singleQuote": true,
  "semi": false,
  "plugins": ["prettier-plugin-tailwindcss"]
}
.prettierignore
node_modules
.next
.plop
.editorconfig
.gitignore
.eslintignore
.prettierignore
*-lock.yaml
*.log*

eslint と prettier の設定が競合しないように eslint の設定を変更

.eslintrc.json
{
  "extends": [
    "next/core-web-vitals",
+    "prettier",
  ],
  // ...
}

参考

https://nextjs.org/docs/app/building-your-application/configuring/eslint#prettier

ESlint

デフォルトで設定されていますが、さらに追加します。
ESLint は誓約と制約。より強い縛りを付けることで強化されます。

追加する設定は3つ

  1. eslint:recommended: js用の推奨ルール
  2. @typescript-eslint/recommended-type-checked: ts用の推奨ルール
  3. eslint-plugin-import: module import の順番を制御
  4. eslint-plugin-unused-imports: 未使用の module import を削除

設定

terminal
pnpm add --save-dev @typescript-eslint/parser @typescript-eslint/eslint-plugin eslint-plugin-import eslint-plugin-unused-imports

.eslintrc.json を変更。
ちなみに extends で入れた拡張設定は一番下のものが優先されます。

.eslintrc.json
.eslintrc.json
{
  "extends": [
    "eslint:recommended", // js用
    "plugin:@typescript-eslint/recommended-type-checked", // ts用
    "next/core-web-vitals",
    "prettier",
  ],
  "plugins": ["import", "unused-imports"], // import の順番を制御
  "parserOptions": {
    "project": ["./tsconfig.json"] // 追加
  },
  "rules": {
    "@typescript-eslint/no-unused-vars": "off", // unused-imports/no-unused-imports で代用
    "unused-imports/no-unused-imports": "error",
    "import/order": [
      "error",
      {
        "groups": [
          "builtin",
          "external",
          "internal",
          "parent",
          "sibling",
          "object",
          "type",
          "index"
        ],
        "newlines-between": "always", // import groups の間を1行あける
        "pathGroupsExcludedImportTypes": ["builtin"],
        "alphabetize": { "order": "asc", "caseInsensitive": true }, // 大文字小文字関係なくアルファベット順
        "pathGroups": [
          {
            "pattern": "@/components/**",
            "group": "internal",
            "position": "before"
          }
        ]
      }
    ]
  }
}

参考

https://zenn.dev/resistance_gowy/articles/91b4f62b9f48ec

https://zenn.dev/cybozu_frontend/articles/ts-eslint-v6-guide

https://zenn.dev/rena_h/scraps/fd330154d02f76

Husky & lint-staged

Husky は Git の Hooks で任意のプログラムを走らせることができます。
lint-staged は Git の staging 上に lint を走らせることができます。

つまり、Husky と lint-staged を併用することで commit 時に lint を走らせて💩がコードに紛れ込むことを防げます。

設定

terminal
pnpm add --save-dev husky lint-staged
pnpm pkg set scripts.prepare="husky install"
pnpm run prepare
pnpm dlx husky add .husky/pre-commit "npx lint-staged"
package.json
{
   // ...
   // 追加する
+  "lint-staged": {
+    "*.{ts, tsx}": "eslint --cache --cache-location .next/cache/eslint/ --fix", // next lint だと .envをロードしようとしてエラーになるため eslint で実行。
+    "*.{js,jsx,ts,tsx,json}": "prettier --write"
+  }
}

Jest & React Testing Library

Jest はテストコード書く上で必要な機能を提供するテスティングフレームワーク。
最近は似たところで Vitest の勢いが良いけど最新バージョンが v0.34 のためまだ見送っている。

React Testing Library は React コンポーネントを実際の DOM にレンダリングしてくれる。

つまり、難しいことを抜きにして React のテストを書くなら必要なツールです。

「テストを書く予定がないから入れなくて良いかな...」となっても、テストを書きたくなったときに書ける状況を作っておきたい派なので最初に入れます。

設定

terminal
pnpm add --save-dev jest jest-environment-jsdom @types/jest @testing-library/react @testing-library/jest-dom

jest.config.mjs をプロジェクト直下に配置する。

jest.config.mjs
import nextJest from 'next/jest.js'

const createJestConfig = nextJest({
  // Provide the path to your Next.js app to load next.config.js and .env files in your test environment
  dir: './',
})

// Add any custom config to be passed to Jest
/** @type {import('jest').Config} */
const config = {
  setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
  testEnvironment: 'jest-environment-jsdom',
}

// createJestConfig is exported this way to ensure that next/jest can load the Next.js config which is async
export default createJestConfig(config)

ほぼ確実に使うので拡張機能(jest-dom)を追加で import しておきます

jest.setup.js
import '@testing-library/jest-dom'
tsconfig.json
{
    // ...
+   "include": [
     // ...
+    "./jest.setup.js"
+  ],
}

scripts にも追加

package.json
{
  "scripts": {
      // ...
+     "test": "jest"
  }
}

参考

https://nextjs.org/docs/pages/building-your-application/optimizing/testing#jest-and-react-testing-library

Storybook

Storybook は UI コンポーネント単体で表示させることができるツールです。Jest ではコードベースのテストを行い、Storybookではビジュアルのテストを行います。特にデザイナーや PdM などエンジニア以外に見せる際にも有効です。

設定

Storybook 上で Tailwind を使えるように addon も入れます

terminal
pnpm dlx storybook@latest init
pnpm add --save-dev @storybook/addon-styling
pnpm addon-styling-setup
.storybook/main.ts
import type { StorybookConfig } from '@storybook/nextjs'

const config: StorybookConfig = {
  // stories の場所を app/*, components/* に変更
  stories: [
    '../app/**/*.stories.@(js|jsx|mjs|ts|tsx)',
    '../components/**/*.stories.@(js|jsx|mjs|ts|tsx)',
  ],
}
.storybook/preview.ts
- import '../src/index.css'
+ import '../app/globals.css' // tailwind 読み込み
tailwind.config.ts
const config: Config = {
   content: [...],
+  darkMode: ['class', '[data-mode="dark"]'],
   // ...
}

参考

https://storybook.js.org/recipes/next

https://storybook.js.org/recipes/tailwindcss

Plop

Plop はコードジェネレーターで、テンプレ的なコードの生成を自動化させることができます。

似たツールに Hygen がありますが、個人的な好みで Plop を使用。

こちらを使うことで pnpm gen コマンドで対話形式で component, stories, test の雛形を自動生成できるになります。

↓こんな感じで生成できる。

terminal
pnpm gen

> next-13-template@0.1.0 gen <DIR>/next-13-template
> plop component

? Where is the path to create components? (e.g. components) components
? What is the component name? (e.g. button) input
✔  ++ /components/Input/index.tsx
✔  ++ /components/Input/Input.test.tsx
✔  ++ /components/Input/Input.stories.tsx

設定

terminal
pnpm add --save-dev plop
package.json
{
   "scripts": {
     //...
+    "gen": "plop component",
   }
   // ...
}

雛形を作るために、plopfile.mjs.plop/\<template>.tsx.hbs を追加する。

plopfile.mjs
plopfile.mjs
export default function (plop) {
  plop.setGenerator('component', {
    description: 'create a new component',
    prompts: [
      {
        type: 'input',
        name: 'path',
        message: 'Where is the path to create components? (e.g. components)',
      },
      {
        type: 'input',
        name: 'name',
        message: 'What is the component name? (e.g. button)',
      },
    ],
    actions: [
      {
        type: 'add',
        path: '{{path}}/{{pascalCase name}}/index.tsx',
        templateFile: '.plop/index.tsx.hbs',
        skipIfExists: true,
      },
      {
        type: 'add',
        path: '{{path}}/{{pascalCase name}}/{{pascalCase name}}.test.tsx',
        templateFile: '.plop/component.test.tsx.hbs',
      },
      {
        type: 'add',
        path: '{{path}}/{{pascalCase name}}/{{pascalCase name}}.stories.tsx',
        templateFile: '.plop/component.stories.tsx.hbs',
      },
    ],
  })
}
.plop/
index.tsx.hbs
import { FC } from 'react'

type Props = {}

export const {{pascalCase name}}: FC<Props> = () => {
  return (
    <div>
      {{pascalCase name}}
    </div>
  )
}
component.stories.tsx.hbs
import type { Meta, StoryObj } from "@storybook/react"
import { {{pascalCase name}} } from '.'

type T = typeof {{pascalCase name}}

export default {
  component: {{pascalCase name}},
} satisfies Meta<T>

type Story = StoryObj<T>

export const Default: Story = {
  args: {},
}
component.test.tsx.hbs
import { render, screen } from "@testing-library/react"
import { {{pascalCase name}} } from "."

test('renders {{pascalCase name}} component', () => {
  render(<{{pascalCase name}} />)


})

参考

https://plopjs.com/documentation/#getting-started

https://zenn.dev/sum0/articles/9463d16d9d40e2

package.json の scripts を整理

複数回使うコマンドは scripts にまとめておきます。
特にdev, build, test, lint あたりは共通認識としてあるので基本この形に収めるようにします。

設定

"dev": next dev && storybook dev みたいな書き方は見づらいので、複数コマンドを実行できるnpm-run-allを installしておきます。

terminal
pnpm add --save-dev npm-run-all

npm-run-all により run-p dev:* で条件にマッチする dev:next dev:storybook のような複数のコマンドを実行できます。
ちなみに run-p は並行実行。run-s は逐次実行になります。 -c はエラーになっても処理を途中で中断せず全てのコマンドを実行するオプションです。

package.json
"scripts": {
    "dev": "run-p dev:*",
    "dev:next": "next dev",
    "dev:storybook": "storybook dev -p 6006",
    "start": "next start",
    "build": "run-s build:*",
    "build:next": "next build",
    "build:storybook": "build-storybook",
    "lint": "run-s -c lint:*",
    "lint:eslint": "next lint",
    "lint:prettier": "prettier --check './**/*.{js,jsx,ts,tsx,json}'",
    "lint-fix": "run-s -c lint-fix:*",
    "lint-fix:eslint": "npm run lint:eslint -- --fix",
    "lint-fix:prettier": "prettier --write './**/*.{js,jsx,ts,tsx,json}'",
    "test": "jest",
    "coverage": "jest --coverage",
    "gen": "plop component",
    "prepare": "husky install"
  },

参考

https://www.mizdra.net/entry/2022/03/24/093000

IDE 設定

IDE といいつつ VS Code の話がメインです。
メンバーに VS Code ユーザーがいないなら必要ないかもですが、最近のフロントだと VS Code の使用率が高いのでとりあえず入れています。

.editorconfig

EditorConfigは文字コードやコードフォーマット設定が可能です。

Prettier を入れていると設定が重複する部分もありますが、Prettier ではカバーしきれない範囲をカバーできたり、多くの IDE でサポートされているので迷ったら入れておけば良いと思います。

.editorconfigをプロジェクト直下に配置する。

.editorconfig
root = true

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

[*.{js,ts,jsx,tsx,md,json,vue,css,scss,sass,html}]
indent_size = 2

VS Code だとこちらのプラグインを入れてください。
https://marketplace.visualstudio.com/items?itemName=EditorConfig.EditorConfig

extensions.json

extensions.json は VS Code のおすすめの拡張機能をレコメンドすることができます。
Prettier 拡張機能なんかはマストだと思います。

自分はここらへんを入れています。Vercel の setting.json を真似したりしているので OSS 見てみるのも良いと思います。

  • prettier-vscode: Prettier をファイル保存時に走らせるため
  • vscode-eslint: ESLint をファイル保存時に走らせるため
  • errorlens: lint のエラーがインラインで表示される。エラーの可読性が上がる
  • code-spell-checker: typo 検知
  • vscode-jest: VS Code 上から Jest を動かせる
  • vscode-pull-request-github: PR を VS Code で見ることができる
  • vscode-tailwindcss: Tailwind の補完
.vscode/extensions.json
.vscode/extensions.json
{
  "recommendations": [
    // Linting / Formatting
    "esbenp.prettier-vscode",
    "dbaeumer.vscode-eslint",
    "usernamehw.errorlens",

    // Spell checker
    "streetsidesoftware.code-spell-checker",

    // Testing
    "orta.vscode-jest",

    // PR management / Reviewing
    "github.vscode-pull-request-github",

    // MDX Authoring
    "unifiedjs.vscode-mdx"

    // Tailwind
    "bradlc.vscode-tailwindcss"
  ]
}

参考

https://github.com/vercel/next.js/blob/canary/.vscode/extensions.json

settings.json

settings.json はプロジェクト単位で VS Code の設定を適用させることができます。

ファイルの保存時に Prettier を自動で走らせたりなど便利な設定ができるので入れます。
自分はここらへんの設定をしています。こちらも extensions と同様に Vercel を真似しながら作っている。

  • ファイル保存時に Prettier が走るように
  • デフォルトのフォーマッターを Prettier に設定
  • Jest が勝手に走らないように変更
  • Typescript のアンケート調査が出てこなくなるように設定
  • spell checker の辞書登録。一般的ではない語句が typo 判定されてしまうので、開発者がよく使う固有名詞などを登録おく。
.vscode/settings.json
.vscode/settings.json
{
  // Formatting using Prettier by default for all languages
  "editor.defaultFormatter": "esbenp.prettier-vscode",

  // Formatting using Prettier for JavaScript, overrides VSCode default.
  "[javascript]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode",
    "editor.formatOnSave": true
  },
  "[typescript]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode",
    "editor.formatOnSave": true
  },
  // Linting using ESLint.
  "eslint.validate": [
    "javascript",
    "javascriptreact",
    "typescript",
    "typescriptreact"
  ],
  // Disable Jest autoRun as otherwise it will start running all tests the first time.
  "jest.autoRun": "off",
  "jest.jestCommandLine": "pnpm test --",

  // Disable TypeScript surveys.
  "typescript.surveys.enabled": false,

  "cSpell.words": [
    "Entrypoints",
    "nextjs",
    "opentelemetry",
    "pnpm",
    "tailwindcss",
    "Threadsafe"
  ]
}

参考

https://github.com/vercel/next.js/blob/canary/.vscode/settings.json

最後に

完成系のコードはこちらにあげています。
https://github.com/wwwyo/next13-template

あくまでもオレオレなのでこれが全てというものではありませんが、大体似通ってくるんじゃないかなぁと思っています。

ぜひおすすめのオレオレ設定があれば教えてください!

GitHubで編集を提案

Discussion