これはなに
Next.js 14でWebアプリを作るための環境構築をしたときのメモ。
環境
WSL2 : Ubuntu 22.04.3 LTS
Docker version 24.0.6
VS Code 1.85.2 x64
Next 14.1.0
VS Codeの改行設定を変更する
VSCodeの改行の設定を、念のためLFにする。
{
"files.eol": "\n"
}
devcontainer.jsonを作成する
VS Codeの拡張機能Dev Containersを使うために、devcontainer.json
を作成する。
{
"name": "Next.js",
"build": {
"dockerfile": "./Dockerfile",
"context": ".."
},
"appPort": ["3010:3000"],
"customizations": {
"vscode": {
"extensions": [
"PulkitGangwar.nextjs-snippets",
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
"bradlc.vscode-tailwindcss"
]
}
}
}
Dockerfileを作成する
Dockerfile
を作成する。
FROM node:20-slim
EXPOSE 3000
RUN apt-get update -y \
&& DEBIAN_FRONTEND=noninteractive \
apt-get install -y --no-install-recommends \
ca-certificates \
git \
locales \
tzdata \
xsel \
&& update-ca-certificates \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
RUN locale-gen ja_JP.UTF-8 \
&& localedef -f UTF-8 -i ja_JP ja_JP.UTF-8
ENV TZ=Asia/Tokyo \
LANG=ja_JP.UTF-8 \
LANGUAGE=ja_JP:jp \
LC_ALL=ja_JP.UTF-8
RUN npm install -g npm
USER node
拡張機能を指定する
VSCodeの拡張機能のうち、必須の拡張機能はdevcontainer.json
に書く。
必須でないものは、recommendationとしてプロジェクトに追加するため、.vscode/extensions.json
に記述する。
{
"recommendations": [
"streetsidesoftware.code-spell-checker",
"naumovs.color-highlight",
"igorsbitnev.error-gutters",
"saikou9901.evilinspector",
"mhutchie.git-graph",
"donjayamanne.githistory",
"oderwat.indent-rainbow",
"visualstudioexptteam.vscodeintellicode",
"christian-kohler.path-intellisense",
"gruntfuggly.todo-tree",
"shardulm94.trailing-spaces"
]
}
Dockerコンテナにアタッチする
VSCodeの拡張機能Dev Containersを使って、Dockerfileをビルドし、Dockerコンテナにアタッチする。
VS Code拡張機能のDev Containers
をローカルにインストールする。その後、Ctrl + Shift + P
でコマンドパレットを開き、Dev Containers: Reopen in Container
を実行する。これにより、Dockerfile
がビルドされ、VS Codeがコンテナにアタッチされる。
推奨拡張機能をインストールする
VS Codeの拡張機能のタブで@recommended
と検索すると、.vscode/extensions.json
に記述した拡張機能が表示される。必要に応じて拡張機能をインストールする。
Git管理する
プロジェクトをgit管理下に置く。
git init
branch名をデフォルトでmain
にしていない場合はwarningが出るので、main
にしておく。
git branch -m main
アプリを作成する
コンテナ内でアプリ作成コマンドをたたく。
npx create-next-app@latest
create-next-app
をインストールするか聞かれたらy
を入力してインストールする。
いろいろ聞かれるが、project name以外すべてデフォルトにする。project nameは任意の名前にする。
出力結果の例を示す。
$ npx create-next-app@latest
Need to install the following packages:
create-next-app@14.1.0
Ok to proceed? (y) y
✔ What is your project named? … next-app
✔ Would you like to use TypeScript? … No / Yes
✔ Would you like to use ESLint? … No / Yes
✔ Would you like to use Tailwind CSS? … No / Yes
✔ Would you like to use `src/` directory? … No / Yes
✔ Would you like to use App Router? (recommended) … No / Yes
✔ Would you like to customize the default import alias (@/*)? … No / Yes
Creating a new Next.js app in /workspaces/ff14-tune-finder/next-app.
Using npm.
Initializing project with template: app-tw
Installing dependencies:
- react
- react-dom
- next
Installing devDependencies:
- typescript
- @types/node
- @types/react
- @types/react-dom
- autoprefixer
- postcss
- tailwindcss
- eslint
- eslint-config-next
added 360 packages, and audited 361 packages in 11s
128 packages are looking for funding
run `npm fund` for details
found 0 vulnerabilities
Initialized a git repository.
Success! Created next-app at /workspaces/ff14-tune-finder/next-app
アプリを起動する
作成されたアプリディレクトリへ移動し、アプリを起動する。
cd next-app
npm run dev
http://localhost:3010/ にアクセスすると、Next.jsのデフォルトページが表示される。
ESLintをTypeScriptに対応させる
デフォルトのESLintだとTypeScriptのエラーが出ないので修正する。
必要なパッケージを入れる。
npm i -D @typescript-eslint/eslint-plugin
.eslintrc.json
を変更する。rules
の部分は必要に応じて変更してよい。
{
"root": true,
"extends": ["plugin:@typescript-eslint/recommended", "next/core-web-vitals"],
"parserOptions": {
"project": "./tsconfig.json"
},
"rules": {
"@typescript-eslint/no-unused-vars": "error",
"@typescript-eslint/no-explicit-any": "error",
"@typescript-eslint/no-unsafe-call": "error",
"@typescript-eslint/no-unsafe-member-access": "error",
"@typescript-eslint/no-unsafe-return": "error"
}
}
変更した.eslintrc.json
を参照するように、VS Codeの設定を変更する。
{
// Linter
"eslint.workingDirectories": ["./next-app"],
"editor.codeActionsOnSave": {
"source.fixAll": "never",
"source.fixAll.eslint": "explicit"
}
}
package.json
のlintコマンドを変更し、.gitignore
の対象ファイルはLint対象外にする。
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
- "lint": "next lint"
+ "lint": "next lint --ignore-path .gitignore"
},
ESLintを実行して動作を確認する。
npm run lint
Prettierを入れる
FormatterとしてPrettierを入れる。パッケージをインストールする。
npm i -D prettier eslint-config-prettier
Prettierの設定ファイル.prettierrc
を作る。
{
"semi": true,
"trailingComma": "all",
"singleQuote": true,
"printWidth": 80,
"tabWidth": 2
}
.eslintrc.json
にPrettierを追加する。必ず最後に追加する。
{
"root": true,
"extends": [
"plugin:@typescript-eslint/recommended",
"next/core-web-vitals",
+ "prettier"
],
"parserOptions": {
"project": "./tsconfig.json"
},
"rules": {
"@typescript-eslint/no-unused-vars": "error",
"@typescript-eslint/no-explicit-any": "error",
"@typescript-eslint/no-unsafe-call": "error",
"@typescript-eslint/no-unsafe-member-access": "error",
"@typescript-eslint/no-unsafe-return": "error"
}
}
VSCodeの設定に、Prettier関連の設定を追記する。
{
// formatter
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
"editor.formatOnPaste": true,
"editor.formatOnType": true
}
package.json
にformat
コマンドを追加する。
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint --ignore-path .gitignore",
+ "format": "prettier --write --ignore-path .gitignore ."
},
Prettierを実行して動作を確認する。
npm run format
コミット時にlinterとformatterを自動実行する
コミット時にnpm run lint
とnpm run format
を実行し、エラーが出る場合はコミットできないようにする。
まず、husky
とlint-staged
をインストールする。
npm i -D husky lint-staged
次に、husky-init
コマンドを実行する。このコマンドは環境により異なるため、実行するコマンドの詳細はhuskyの公式サイト
を参照すること。npm
の場合は下記になる。
npx husky-init && npm install
このとき、.git
ディレクトリとpackage.json
ファイルが同じ階層に存在しないため、下記のようなエラーが発生する。
$ npx husky-init && npm install
Need to install the following packages:
husky-init@8.0.0
Ok to proceed? (y) y
husky-init updating package.json
setting prepare script to command "husky install"
/home/node/.npm/_npx/1ab9c0f68ac2536e/node_modules/husky/lib/index.js:23
throw new Error(`.git can't be found (see ${url})`);
^
Error: .git can't be found (see https://typicode.github.io/husky/#/?id=custom-directory)
at install (/home/node/.npm/_npx/1ab9c0f68ac2536e/node_modules/husky/lib/index.js:23:15)
at Object.<anonymous> (/home/node/.npm/_npx/1ab9c0f68ac2536e/node_modules/husky-init/lib/bin.js:16:21)
at Module._compile (node:internal/modules/cjs/loader:1376:14)
at Module._extensions..js (node:internal/modules/cjs/loader:1435:10)
at Module.load (node:internal/modules/cjs/loader:1207:32)
at Module._load (node:internal/modules/cjs/loader:1023:12)
at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:135:12)
at node:internal/main/run_main_module:28:49
Node.js v20.11.0
このエラーへの対処法は、huskyの公式サイト
に書かれているとおりで、npx husky-init
によってpackage.json
に追加されたprepare
スクリプトを変更する。
たとえば、プロジェクト名がnext-app
の場合は下記のように変更する。
{
"scripts": {
- "prepare": "husky install"
+ "prepare": "cd .. && husky install next-app/.husky"
},
}
変更したら、再度npm i
(あるいはnpm install
)を実行する。
npm i
成功すると、.husky
ディレクトリが生成される。
.husky
ディレクトリが生成されたら、自動実行のためのhookを追加する。今回はpre-commit
のhookを追加する。
npx husky add .husky/pre-commit "npx lint-staged"
実行すると、.husky
ディレクトリに下記のpre_commit
ファイルが生成される。
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
npx lint-staged
今回は.git
ディレクトリとpackage.json
ファイルが同じ階層に存在しないため、下記のように、階層を移動するコマンドを追加する必要がある。
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
+ cd next-app # package.jsonのある階層に移動する
npx lint-staged
ここまでできたら、lint-staged
の設定に移る。Next.jsにおけるlint-staged
の設定は特殊である。詳細はNext.jsの公式サイト
を参照すること。
要するに、Next.jsにおいてnext lint
とlint-staged
を共存させる場合は、.lintstagedrc.js
ファイルを作成し、そこに設定を記す必要がある。
公式に載っている.lintstagedrc.js
を下記に引用する。
const path = require('path')
const buildEslintCommand = (filenames) =>
`next lint --fix --file ${filenames
.map((f) => path.relative(process.cwd(), f))
.join(' --file ')}`
module.exports = {
'*.{js,jsx,ts,tsx}': [buildEslintCommand],
}
上記のように、module.exports
に、適用するファイルと実行するコマンドのペアを記述する。たとえば、自動修正しないlinterとformatterを実行する場合は、下記のようになる。
const path = require('path');
const buildLintCommand = (filenames) =>
`next lint --file ${filenames
.map((f) => path.relative(process.cwd(), f))
.join(' --file ')}`;
const buildFormatCommand = (filenames) =>
`prettier --check --debug-check ${filenames
.map((f) => path.relative(process.cwd(), f))
.join(' ')}`;
module.exports = {
'app/**/*.{js,jsx,ts,tsx}': [buildLintCommand],
'pages/**/*.{js,jsx,ts,tsx}': [buildLintCommand],
'*.{js,jsx,ts,tsx,json,css}': [buildFormatCommand],
};
以上までの設定で、コミット時にlinterとformatterが自動で実行され、通らない場合はコミットできなくなる。
コミットメッセージを自動チェックして統一する
コミットメッセージのフォーマットを統一するため、commitlintを入れる。
まず、commitlint
を入れる。コマンドは上記リンク先を参照すること。下記は一例である。
npm install --save-dev @commitlint/{cli,config-conventional}
次に、下記のコマンドを実行してcommitlint.config.js
を作成する。
echo "module.exports = { extends: ['@commitlint/config-conventional'] };" > commitlint.config.js
作成されたcommitlint.config.js
は以下のようになる。
module.exports = { extends: ['@commitlint/config-conventional'] };
以上でcommitlint
が動作するようになったので、実行されるか確認する。
npx commitlint --from HEAD~1 --to HEAD --verbose
実行されれば、下記のようなログが出力される。
$ npx commitlint --from HEAD~1 --to HEAD --verbose
⧗ input: build(git): set up husky and lint-staged pre-commit
✔ found 0 problems, 0 warnings
commitlint
が実行できたら、コミット時にcommitlint
を自動実行するように設定する。commitlint
はcommit-msg
フックで実行する。下記のコマンドを実行して、husky
でcommit-msg
フックを追加する。
npx husky add .husky/commit-msg 'npx --no -- commitlint --edit ${1}'
これにより、.husky/commit-msg
ファイルが作成される。
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
npx --no -- commitlint --edit ${1}
.git
ディレクトリとpackage.json
が同一階層にない場合は、下記のように階層を移動するコマンドを追加する必要がある。
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
+ cd next-app # package.jsonのある階層に移動する
npx --no -- commitlint --edit ${1}
これで、コミット時にcommitlintが実行されるようになる。
最後に、commitlintのルールを変更する。commitlintのルールは、commitlint.config.js
にルールを追記することで上書きできる。デフォルトの設定および上書き方法は、下記公式サイトに記述されている。
たとえば、subject-max-length
を50
に変更する場合は、下記のようにルールを追記する。
module.exports = {
extends: ['@commitlint/config-conventional'],
+ rules: {
+ 'subject-max-length': [2, 'always', 50],
+ },
};
ルールは、{ルール名}: [{レベル:0|1|2}, '{可否:always|never}', {値}]
の形式で記述する。レベルは各数値が下記段階に該当する。
- 0: 無効
- 1: 警告
- 2: エラー
オレオレルールを下記に示す。
module.exports = {
extends: ['@commitlint/config-conventional'],
rules: {
'type-case': [2, 'always', 'lower-case'],
'scope-case': [2, 'always', 'lower-case'],
'subject-max-length': [2, 'always', 50],
'body-full-stop': [2, 'always', '.'],
'body-leading-blank': [2, 'always'],
'body-max-line-length': [2, 'always', 72],
'body-case': [2, 'always', 'sentence-case'],
'footer-leading-blank': [2, 'always'],
'footer-max-line-length': [2, 'always', 72],
},
};
参考文献・URL
参考文献・URLは適宜記事内に示した。