Next.js+TS+ESLint+Prettier+husky環境構築【2024 - 全量版】
前述
以下、Nextプロジェクトの環境構築手順書であり、私の備忘録である。
なるべく順番通りに書いてる。
詳細に書いてるので、コピペ用には向いてない。
[OS]
mac M2
[使用技術]
・Next.js - 14.1.0
・TypeScript - 5
・ESLint - 8.56.0
・Prettier - 3.2.4
・Tailwind - 3.4.1
・antd - 5.13.2
・husky - 8.0.3
[前提]
・node ,yarn インストール済み
└ brewで入れた方が便利
create-next-app
npx create-next-app@latest [PJ名] --ts
--実行後--
✔ 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 //Yes
✔ Would you like to use App Router? (recommended) … No / Yes // No
✔ Would you like to customize the default import alias (@/*)? … No / Yes // No
✔ What import alias would you like configured? … @/* //上記Yesなら表示。基本的に「@/」がエイリアスにして良いでEnter押すだけ
Path alias の設定
絶対パスを綺麗にする設定
...
"baseUrl": "./",
"paths": {
"@/*": ["./src/*"]
}
■使用例
// before
import { Button } from '../../../components/button';
// after
import { Button } from '@/components/button';
Prettier設定
prettier
をインストール。
eslint-config-prettier
を入れることで、Eslintと被るルールに対して、Prettierを優先することができる。
yarn add -D prettier eslint-config-prettie
.prettierrcの設定
.prettierrc
を新規作成。ここで成形ルールを設定する。
設定は以下の通り。
{
"singleQuote": true,
"semi": true,
"trailingComma": "all",
"useTabs": false,
"tabWidth": 2,
"bracketSpacing": true
}
- 上記のそれぞれの意味
- singleQuote - ダブルクォートの代わりにシングルクォートを使用する
- semi - ステートメントの最後にセミコロンを追加する。
- trailingComma - 末尾のカンマの設定。allは可能な限り末尾にカンマを付ける
- useTabs - スペースではなくタブで行をインデント。falseならスペース
- tabWidth - インデントのスペースの数を指定
- bracketSpacing - オブジェクトリテラルの角かっこの内側にスペースを入れる
💡 .prettierignore
で無視するファイルを作成することもできる
【追記】ただしv9以降のeslintの場合、prettierignoreファイルは廃止されている
その他設定値について
.eslintrc.jsonの設定
Eslintを使用しているとPrettierで被るルールがある場合、
Prettier側のルールが優先される設定を施します。
{
"extends": ["next/core-web-vitals", "prettier"]
}
package.json設定
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint",
"lint:fix": "eslint src/**/*.{ts,tsx} --fix",
// 追加↓//
"format": "prettier --write .",
// 追加↑//
"prepare": "husky install"
},
保存時に実行されるようにする
.vscode/settings.json
に以下を追記する(存在しなければ作成)
{
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true. //セーブ時に実行するおっていう設定
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
},
"typescript.preferences.importModuleSpecifier": "non-relative"
}
- editor.defaultFormatter - フォーマッターの指定
- editor.formatOnSave - セーブ時にフォーマットを実行するっていう設定
- editor.codeActionsOnSave - セーブ時のアクション
└ source.fixAll.eslint - ESLintによるフォーマットを行う(eslint付与でESLint機能拡張の対象ファイルのみ有効) - typescript.preferences.importModuleSpecifier - 自動インポートする際に絶対パスで指定する
Eslint 設定
tsconfig.eslint.json設定
{
"extends": "./tsconfig.json",
"includes": ["src/**/*.ts", "src/**/*.tsx", ".eslintrc.json"],
"exclude": ["node_modules", "dist"]
}
package.json設定
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
// 追加↓//
"lint": "next lint",
"lint:fix": "eslint src/**/*.{ts,tsx} --fix",
// 追加↑//
"format": "prettier --write .",
"prepare": "husky install"
},
[.eslintrc.json設定]env
グローバル変数を使っていいよという設定。
ESlintはブラウザ上で動くものがないためwindow
やalert
をいきなり使われても
定義されてないよ!と怒られるため。
{
"env": {
"browser": true,
"node": true,
"es6": true
},
...
}
- browser - ブラウザのグローバル変数を理解
- node -node.jsのグローバル変数を理解
- es6 - ECMAScript 6(ES6)の機能をサポート
[.eslintrc.json設定]settings
ESLintが適用するReact関連のルールが、使用しているReactのバージョンに対して適切であるかの確認。detect
を設定することで、インストールされている React のバージョンを自動的に検出。
"settings": {
"react": {
"version": "detect"
}
},
[.eslintrc.json設定]parser
ESLintでTypeScriptのチェックをできるようにするために、@typescript-eslint/parser
をインストール
yarn add -D @typescript-eslint/parser
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": 2020,
"project": "./tsconfig.json",
"ecmaFeatures": {
"jsx": true
},
"sourceType": "module"
},
- ecmaVersion - 使用するECMAScriptのバージョンを指定
- project - parserが読み込むtsconfig.jsonファイルを指定
- ecmaFeatures - jsxをtrueにすると、.js、.jsx、.tsxのファイルが解析される
- .eslintrc ファイルの parserOptions セクション内の sourceType は、ESLintが解析するコードのモジュールの種類を指定
[.eslintrc.json設定]rules
コアルール(eslint標準)
"no-empty-function": "off",
"no-restricted-imports": [
"error",
{
"patterns": [".*"]
}
]
- no-empty-function - 中身が空などで意味のないコンストラクタを許可
- no-restricted-imports - 指定したインポート方法をエラーとする。
[.eslintrc.json設定]rules - プラグイン
@typescript-eslint
npm i @typescript-eslint/eslint-plugin
TypeScript 専用のセット
"@typescript-eslint/explicit-function-return-type": 0,
"@typescript-eslint/no-explicit-any": 0,
"@typescript-eslint/no-empty-function": 0,
"@typescript-eslint/ban-ts-comment": 0,
"@typescript-eslint/no-unused-vars": 0,
"@typescript-eslint/naming-convention": [
"error",
{
"selector": "typeAlias",
"format": ["PascalCase"],
"custom": {
"regex": "^.*Props$",
"match": true
}
}
],
- @typescript-eslint/explicit-function-return-type - 関数が戻り値の型を明示的に宣言することを要求(ex.function example(): number {...})
- @typescript-eslint/no-explicit-any” - any 型の使用を禁止し、より具体的な型の使用を強制
- @typescript-eslint/no-empty-function - 空の関数(中身のない関数)を作成することを禁止
- @typescript-eslint/ban-ts-comment - TypeScriptの特殊コメントの使用を制限または禁止
- @typescript-eslint/no-unused-vars - 宣言されたが使用されていない変数を検出し、警告またはエラーとして報告
eslint-plugin-simple-import-sort
npm install --save-dev eslint-plugin-simple-import-sort
"parserOptions": {
"sourceType": "module"
},
・・・
"plugins": ["simple-import-sort", "import"],
"rules": {
"simple-import-sort/imports": "error",
"simple-import-sort/exports": "error",
"import/first": "error",
"import/newline-after-import": "error",
"import/no-duplicates": "error"
}
・simple-import-sort/imports - 順番通りか確認。
・import/first - すべてのインポートがファイルの先頭にあることを確認
・import/newline-after-import - インポートの後に改行があることを確認
・import/no-duplicates - 同じファイルの import ステートメントをマージ
eslint-plugin-react,eslint-plugin-react-hooks
npm install eslint eslint-plugin-react --save-dev
yarn add eslint-plugin-react-hooks --dev
"react/prop-types": "off",
"react/react-in-jsx-scope": "off",
"react/jsx-sort-props": [
"error",
{
"callbacksLast": true,
"shorthandFirst": true
}
],
"react-hooks/exhaustive-deps": "off"
react/react-in-jsx-scope - 「React' must be in scope when using JSX」に対応するため
react/jsx-sort-props - ReactのJSX要素のプロパティ(props)の順序を制御
└callbacksLast - コールバック関数(例:イベントハンドラ)を他のプロパティより後に配置
└shorthandFirst - 短縮形(shorthand)のプロパティを通常のプロパティより先に配置
useEffect has amissing dependencyを解決
└ useEffect
内で依存関係を引き起こす変数(概ねuseStateの変数)を使用しており、かつuseEffect
の第二引数に空配列を指定した場合、このwarning
が発生します。
[ソート系]sort-keys-fix
npm install eslint-plugin-sort-keys-fix --save-dev
npm install eslint-plugin-sort-destructure-keys --save-dev
yarn add -D eslint-plugin-typescript-sort-keys
ソートが正しくない場合エラー
"sort-keys-fix/sort-keys-fix": "error",
"sort-destructure-keys/sort-destructure-keys": "error",
"typescript-sort-keys/interface": "error",
"typescript-sort-keys/string-enum": "error",
sort-keys-fix/sort-keys-fix - オブジェクトリテラル内のキーをアルファベット順にソート
sort-destructure-keys/sort-destructure-keys - 分割代入(destructuring assignment)の際にオブジェクトのキーをアルファベット順にソートするよう要求
typescript-sort-keys/interface - TypeScriptのインターフェース内のキーをアルファベット順にソートすることを要求
typescript-sort-keys/string-enum - TypeScriptの文字列列挙型(string enum)の値をアルファベット順にソートすることを要求
unused-imports
npm install eslint-plugin-unused-imports --save-dev
"unused-imports/no-unused-imports": "error",
• 未使用のimportを検出し、削除してくれる
antd
yarn add antd
yarn add @ant-design/icons
Tailwind CSS
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
(ファイルが存在しない場合)
tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
"./app/**/*.{js,ts,jsx,tsx,mdx}",
"./pages/**/*.{js,ts,jsx,tsx,mdx}",
"./components/**/*.{js,ts,jsx,tsx,mdx}",
// Or if using `src` directory:
"./src/**/*.{js,ts,jsx,tsx,mdx}",
],
theme: {
extend: {},
},
plugins: [],
}
autoprefixer - CSSにベンダープレフィックスを自動で追加するためのツール.
ベンダープレフィックスとは、特定のブラウザが新しいCSS機能をサポートするために必要な、特別な接頭辞のこと.
postcss - CSSを変換するためのツールキット
postcss.config.js
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};
Husky, lint-staged
コマンド
yarn add --dev husky lint-staged
yarn husky install
npx husky add .husky/pre-commit "yarn lint-staged --config .lintstagedrc.js”
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
yarn lint-staged --config .lintstagedrc.js
package.json設定
"scripts": {
・・・
"lint": "next lint",
"lint:fix": "eslint src/**/*.{ts,tsx} --fix",
"format": "prettier --write .",
// 追加↓//
"prepare": "husky install"
// 追加↑//
・・・
},
// 追加↓//
"lint-staged": {
"*.{ts,tsx}": [
"yarn lint",
"yarn format",
"yarn lint:fix"
]
},
// 追加↑//
ステージングファイルのみにeslintを適用する方法
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],
}
.eslintignore
ファイルを無視する方法
const { ESLint } = require('eslint')
const removeIgnoredFiles = async files => {
const eslint = new ESLint()
const isIgnored = await Promise.all(
files.map(file => {
return eslint.isPathIgnored(file)
})
)
const filteredFiles = files.filter((_, i) => !isIgnored[i])
return filteredFiles.join(' ')
}
module.exports = {
'**/*.{ts,tsx,js,jsx}': async files => {
const filesToLint = await removeIgnoredFiles(files)
return [`eslint --max-warnings=0 ${filesToLint}`]
},
}
特定のファイルをlint-stagedの対象から除外する - Qiita
⚠️依存関係がおかしい?
"resolutions": {
"strip-ansi": "6.0.1",
"string-width": "4.2.3"
}
実行権限の付与
$ chmod a+x .husky/pre-push
$ chmod a+x .husky/pre-commit
[補足]punycodeのwrningが出た場合
(node:85791) [DEP0040] DeprecationWarning: The `punycode` module is deprecated. Please use a userland alternative instead.
(Use `node --trace-deprecation ...` to show where the warning was created)
yarn info yarn description
Discussion