オレオレ Next.js 初期設定
最近、フロントエンドで新規プロジェクトを開始する際は、Next.js を選定することが多いと思います。
create-next-app
をした際に都度、もろもろの初期設定 (Prettier, Husky, storybook, etc...) を行う儀式がありますよね?
この儀式である程度 「最初に設定しときたいよね」と思うものが自分の中で固まってきたので、今回はオレオレ初期設定を紹介しようと思います。
やること
以下のことをやります。
- Node環境: Volta
- フォーマット: Prettier
- Lint ツール: ESLint, Husky, lint-staged
- Test ツール: Jest, React Testing Library, Storybook
- テンプレ自動化: Plop
- scripts 整理: package.json
- 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
参考
Volta でバージョンを固定する
次にやることは現在の Node.js のバージョンを固定することです。
Volta は Node や pnpm のバージョンを管理できるツールです。
volta pin
コマンドでプロジェクトごとにバージョンを固定させるだけでなく、自動でバージョンの切り替えが行わます。チーム開発における統一的な環境構築に有効です。
volta pin node
{
// ...
// 自動で作成される
+ "volta": {
+ "node": "18.17.1",
+ }
}
Prettier
Prettier は言わずと知れたコードフォーマッターです。
末尾のセミコロンの有無やインデント幅などを自動で整えてくれます。
インデントが揃っていないコードを読むと目が弾け飛ぶので目の保護のためにも必須で入れます。
今回は tailwind が入っているので class 名の順序を自動で整えてくれる prettier-plugin-tailwindcss
というプラグインも同時に入れます。
設定
pnpm add --save-dev prettier eslint-config-prettier prettier-plugin-tailwindcss
次のファイルをプロジェクト直下に配置する。
{
"extends": ["next", "prettier"]
}
{
"singleQuote": true,
"semi": false,
"plugins": ["prettier-plugin-tailwindcss"]
}
node_modules
.next
.plop
.editorconfig
.gitignore
.eslintignore
.prettierignore
*-lock.yaml
*.log*
eslint と prettier の設定が競合しないように eslint の設定を変更
{
"extends": [
"next/core-web-vitals",
+ "prettier",
],
// ...
}
参考
ESlint
デフォルトで設定されていますが、さらに追加します。
ESLint は誓約と制約。より強い縛りを付けることで強化されます。
追加する設定は3つ
- eslint:recommended: js用の推奨ルール
- @typescript-eslint/recommended-type-checked: ts用の推奨ルール
- eslint-plugin-import: module import の順番を制御
- eslint-plugin-unused-imports: 未使用の module import を削除
設定
pnpm add --save-dev @typescript-eslint/parser @typescript-eslint/eslint-plugin eslint-plugin-import eslint-plugin-unused-imports
.eslintrc.json
を変更。
ちなみに extends で入れた拡張設定は一番下のものが優先されます。
.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"
}
]
}
]
}
}
参考
Husky & lint-staged
Husky は Git の Hooks で任意のプログラムを走らせることができます。
lint-staged は Git の staging 上に lint を走らせることができます。
つまり、Husky と lint-staged を併用することで commit 時に lint を走らせて💩がコードに紛れ込むことを防げます。
設定
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"
{
// ...
// 追加する
+ "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 のテストを書くなら必要なツールです。
「テストを書く予定がないから入れなくて良いかな...」となっても、テストを書きたくなったときに書ける状況を作っておきたい派なので最初に入れます。
設定
pnpm add --save-dev jest jest-environment-jsdom @types/jest @testing-library/react @testing-library/jest-dom
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 しておきます
import '@testing-library/jest-dom'
{
// ...
+ "include": [
// ...
+ "./jest.setup.js"
+ ],
}
scripts
にも追加
{
"scripts": {
// ...
+ "test": "jest"
}
}
参考
Storybook
Storybook は UI コンポーネント単体で表示させることができるツールです。Jest ではコードベースのテストを行い、Storybookではビジュアルのテストを行います。特にデザイナーや PdM などエンジニア以外に見せる際にも有効です。
設定
Storybook 上で Tailwind を使えるように addon も入れます
pnpm dlx storybook@latest init
pnpm add --save-dev @storybook/addon-styling
pnpm addon-styling-setup
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)',
],
}
- import '../src/index.css'
+ import '../app/globals.css' // tailwind 読み込み
const config: Config = {
content: [...],
+ darkMode: ['class', '[data-mode="dark"]'],
// ...
}
参考
Plop
Plop はコードジェネレーターで、テンプレ的なコードの生成を自動化させることができます。
似たツールに Hygen がありますが、個人的な好みで Plop を使用。
こちらを使うことで pnpm gen
コマンドで対話形式で component, stories, test の雛形を自動生成できるになります。
↓こんな感じで生成できる。
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
設定
pnpm add --save-dev plop
{
"scripts": {
//...
+ "gen": "plop component",
}
// ...
}
雛形を作るために、plopfile.mjs
と .plop/\<template>.tsx.hbs
を追加する。
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/
import { FC } from 'react'
type Props = {}
export const {{pascalCase name}}: FC<Props> = () => {
return (
<div>
{{pascalCase name}}
</div>
)
}
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: {},
}
import { render, screen } from "@testing-library/react"
import { {{pascalCase name}} } from "."
test('renders {{pascalCase name}} component', () => {
render(<{{pascalCase name}} />)
})
参考
package.json の scripts を整理
複数回使うコマンドは scripts にまとめておきます。
特にdev
, build
, test
, lint
あたりは共通認識としてあるので基本この形に収めるようにします。
設定
"dev": next dev && storybook dev
みたいな書き方は見づらいので、複数コマンドを実行できるnpm-run-allを installしておきます。
pnpm add --save-dev npm-run-all
npm-run-all
により run-p dev:*
で条件にマッチする dev:next
dev:storybook
のような複数のコマンドを実行できます。
ちなみに run-p
は並行実行。run-s
は逐次実行になります。 -c
はエラーになっても処理を途中で中断せず全てのコマンドを実行するオプションです。
"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"
},
参考
IDE 設定
IDE といいつつ VS Code の話がメインです。
メンバーに VS Code ユーザーがいないなら必要ないかもですが、最近のフロントだと VS Code の使用率が高いのでとりあえず入れています。
.editorconfig
EditorConfigは文字コードやコードフォーマット設定が可能です。
Prettier を入れていると設定が重複する部分もありますが、Prettier ではカバーしきれない範囲をカバーできたり、多くの IDE でサポートされているので迷ったら入れておけば良いと思います。
.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 だとこちらのプラグインを入れてください。
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
{
"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"
]
}
参考
settings.json
settings.json はプロジェクト単位で VS Code の設定を適用させることができます。
ファイルの保存時に Prettier を自動で走らせたりなど便利な設定ができるので入れます。
自分はここらへんの設定をしています。こちらも extensions と同様に Vercel を真似しながら作っている。
- ファイル保存時に Prettier が走るように
- デフォルトのフォーマッターを Prettier に設定
- Jest が勝手に走らないように変更
- Typescript のアンケート調査が出てこなくなるように設定
- spell checker の辞書登録。一般的ではない語句が typo 判定されてしまうので、開発者がよく使う固有名詞などを登録おく。
.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"
]
}
参考
最後に
完成系のコードはこちらにあげています。
あくまでもオレオレなのでこれが全てというものではありませんが、大体似通ってくるんじゃないかなぁと思っています。
ぜひおすすめのオレオレ設定があれば教えてください!
Discussion