🐳
[React] ESLintを設定して秩序をもたらしてみた🪡[Typescript] [Nextjs]
この記事で紹介するeslintの設定ファイルはこちらに沿って作成しています!是非そちらも見ていただけると幸いです🥣
TL;DR
husky
やlint-staged
の細かな設定は既にたくさんの方が記事にしているため割愛します🧑🔬
パッケージのインストール
npm install -D eslint eslint-config-next @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint-config-prettier eslint-plugin-import eslint-plugin-react eslint-plugin-simple-import-sort
.eslintrc.js
module.exports = {
extends: [
'next/core-web-vitals',
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:react/recommended',
'plugin:react-hooks/recommended',
'plugin:react/jsx-runtime',
'plugin:eslint-comments/recommended',
'plugin:storybook/recommended',
'prettier',
],
env: { browser: true, node: true, es6: true },
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
project: './tsconfig.json',
tsconfigRootDir: __dirname,
},
plugins: ['@typescript-eslint', 'simple-import-sort', 'import'],
ignorePatterns: ['node_modules/', '.eslintrc.js'],
rules: {
'react/jsx-curly-brace-presence': 'warn',
'simple-import-sort/imports': 'error', //importとexportのソート
'simple-import-sort/exports': 'error',
'import/first': 'error',
'import/newline-after-import': 'error',
'import/no-duplicates': 'error',
'@typescript-eslint/consistent-type-definitions': ['warn', 'type'], //型定義はtypeを使う
'@typescript-eslint/no-explicit-any': 'error', //any禁止
'@typescript-eslint/no-unused-vars': 'error', //未使用の変数禁止
'react/self-closing-comp': ['error', { component: true, html: true }], //<Component />のように自己閉タグを使う
'no-control-regex': 'off', //正規表現中のASCII制御文字ok
'react/jsx-boolean-value': 'error', //attribute={true} → attribute
'react/jsx-pascal-case': 'error', //コンポーネント名はパスカルケース
'object-shorthand': ['warn', 'properties', { avoidQuotes: true }],
'eslint-comments/require-description': 'error', //eslint-disable-next-lineのコメントは必ず説明を書く。https://mysticatea.github.io/eslint-plugin-eslint-comments/rules/require-description.html
'eslint-comments/disable-enable-pair': ['error', { allowWholeFile: true }], //https://mysticatea.github.io/eslint-plugin-eslint-comments/rules/disable-enable-pair.html
'import/no-default-export': 'error', //default export禁止
'no-nested-ternary': 'error', //三項演算子のネスト禁止
'react/function-component-definition': [
'error', //関数コンポーネントの定義はアロー関数を使う
{ namedComponents: 'arrow-function' },
],
'no-magic-numbers': [
'error',
{
ignore: [-1, 0, 1], //配列検索でindexOf === -1などは許容する
ignoreDefaultValues: true, //const { tax = 0.1 } = props
ignoreArrayIndexes: true, //data[100] ok
enforceConst: true, //マジックナンバーはconstで定義する
},
],
'@typescript-eslint/naming-convention': [
'error',
{
selector: ['variable', 'method', 'accessor'], //基本的に全てcamelCase
format: ['camelCase', 'snake_case'],
},
{
selector: ['property'], //APIリクエスト時にPascalCaseとなっている箇所がある
format: ['camelCase', 'snake_case', 'PascalCase'],
},
{
selector: 'variable', //exportされている定数やコンポーネント
modifiers: ['exported', 'const'],
format: ['PascalCase', 'strictCamelCase'],
},
{
selector: 'interface', //interfaceはIをつけない
format: ['PascalCase'],
custom: { regex: '^I[A-Z]', match: false },
},
{ selector: ['class', 'typeAlias', 'enum'], format: ['PascalCase'] },
{
selector: ['objectLiteralProperty'], //api requestのheadersの'Content-Type'などが対応するためnullで許容する
format: null,
modifiers: ['requiresQuotes'],
},
],
},
overrides: [
// Next.jsのファイルルーティングはexport defaultが必要
{
files: ['*/pages/**/**.tsx'],
rules: {
'import/no-default-export': 'off',
'import/prefer-default-export': 'error',
'@typescript-eslint/naming-convention': 'off',
},
},
{
files: ['*/**/types/**/schema.ts'],
rules: { 'no-magic-numbers': 'off' }, // schemaファイルでは許容する
},
// Storybookのファイルはdefault exportを許可
{
files: ['*/**/**.stories.tsx'],
rules: { 'import/no-default-export': 'off' },
},
],
}
.husky/pre-commit
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
npx lint-staged
.lintstagedrc.js
const path = require('path')
const buildEslintCommand = (filenames) =>
`next lint --file ${filenames
.map((f) => path.relative(process.cwd(), f))
.join(' --file ')}`
module.exports = {
'*.@{js,jsx,ts,tsx}': [buildEslintCommand, 'prettier --write'],
}
package.json
package.json
{
"scripts": {
"lint": "next lint",
}
}
以下の方法は私の環境では動きませんでした
{
"scripts": {
"lint-staged": "lint-staged"
}
}
やったこと
- 独自定義したルール一覧
- eslint-disableを使う場合はコメント必須!
- マジックナンバー禁止
- import/exportのsort
- 型定義はtypを使う
- any禁止
- 未使用の変数を残さない
- 自己閉じタグを使う
<Component />
- booleanの属性表記は省略形
- 省略可能なobject keyは省略する
{data: data} → {data}
- 原則default export禁止
- アロー関数を使う
- 命名規則の設定
- lint対象を
src/
とする - ステージングされたgitファイルに対してのみlintを実行する(
.lintstagedrc.js
)
参考
- ESLintでTypeScriptをリントしよう
- Next.js>ESLint
- eslintのpluginsとextendsの違いを理解する
- 【2023/09最新】husky + lint-staged でコミット前にlintを強制する方法
こちらも!👩🍳
Discussion