![article hero](https://react-japan.dev/assets/blog/eslint-foundation-640.webp)
![Steven Sacks](https://react-japan.dev/authors/steven.webp)
ESLint - 基盤
以下はESLint、Prettier、Stylelint、およびeditorconfigに関する私の推奨事項です。
ESLint v8.57.0
まず第一に、ESLint v9が利用可能であるにもかかわらず、ほとんどのプラグインがv9と互換性がないため、互換性のあるプラグインが増えるまで、v8.57.0を使用することを強くお勧めします。
プラグイン
これらは、あなたのESLintセットアップの基盤となるルールセットを持つプラグインです(一部カスタマイズされています)。
- Airbnb TypeScript
- Unicorn
- SonarJS
- Comments
- Import および Import Resolver TypeScript
- Config Prettier(plugin prettier や eslint prettier は使用しないでください)
- React および React Hooks
- jsx-a11y
こちらが.eslintrc.cjs
ファイルでの私の推奨設定です。ルールの設定を必要に応じて変更することもできますが、これらのルールはすべて何らかの値に設定されているべきだと考えています。このセットアップはjs
およびts
ファイル(cjs
、mjs
なども含む)をサポートしています。この記事のためのコメントも追加してありますが、削除しても構いません。
const shared = {
extends: [
'eslint:recommended',
'plugin:eslint-comments/recommended',
'plugin:react/recommended',
'plugin:react/jsx-runtime',
'plugin:react-hooks/recommended',
'plugin:jsx-a11y/recommended',
'plugin:sonarjs/recommended-legacy',
'plugin:tailwindcss/recommended',
'plugin:unicorn/recommended',
'prettier',
],
plugins: [
'react',
'sonarjs',
'unicorn',
'tailwindcss', // tailwindを使っている場合
'you-dont-need-lodash-underscore', // lodashを使っている場合
],
rules: [
'consistent-return': 'off',
curly: ['error', 'all'],
'eslint-comments/disable-enable-pair': 'off',
'eslint-comments/no-unused-disable': 'error',
'import/extensions': 'off',
'import/no-anonymous-default-export': [
'error',
{
allowArray: true,
allowLiteral: true,
allowObject: true,
},
],
'import/order': 'off', // 他のpluginにより処理される
'import/prefer-default-export': 'off',
'jsx-a11y/anchor-is-valid': [
'error',
{
components: ['Link'], // RemixやNextなどを使っている場合
specialLink: ['to'],
},
],
'max-params': ['error'], // デフォルトは3です
'padding-line-between-statements': [
'error',
{
blankLine: 'always',
next: ['block-like', 'export', 'return'],
prev: '*',
},
],
quotes: [
'error',
'single',
{
allowTemplateLiterals: false,
avoidEscape: true,
},
],
'react/boolean-prop-naming': [
'error',
{
propTypeNames: ['bool', 'mutuallyExclusiveTrueProps'],
rule: '^((is|has|can|show|hide|no)[A-Z]([A-Za-z0-9]?)+|(show|hide|disabled|required))',
},
],
'react/function-component-definition': 'off',
'react/jsx-boolean-value': ['error', 'always'],
'react/jsx-filename-extension': ['error', {extensions: ['.tsx']}],
'react/jsx-newline': ['error', {prevent: true}],
'react/jsx-props-no-spreading': 'off',
'react/prop-types': 'off', // propsにはTypeScriptを使いましょう
'react/require-default-props': 'off',
'spaced-comment': 'off', // 論理としてはいいのですが、実際には使いにくいです
// prettier-plugin-tailwindcssによって処理されます
'tailwindcss/classnames-order': 'off',
'unicorn/consistent-destructuring': 'error',
// これらはReactとの互換性のためにoffであるべきものです
// このうちのいくつかは私の意見として問題になるものです
'unicorn/new-for-builtins': 'off',
'unicorn/no-array-callback-reference': 'off',
'unicorn/no-array-for-each': 'off',
'unicorn/no-array-reduce': 'off',
'unicorn/no-null': 'off',
'unicorn/no-useless-undefined': 'off',
'unicorn/prefer-export-from': 'off',
'unicorn/prefer-set-has': 'off',
'unicorn/prefer-switch': 'off',
'unicorn/prefer-ternary': 'off',
'unicorn/prevent-abbreviations': [
'error',
{
// これらは私の趣向です
ignore: [
'acc',
'ctx',
'e2e',
'env',
'obj',
'prev',
'req',
'res',
/args/i,
/fn/i,
/params/i,
/props/i,
/ref/i,
/utils/i,
],
},
],
'unicorn/text-encoding-identifier-case': 'off',
],
};
module.exports = {
root: true,
env: {
browser: true,
commonjs: true,
es2024: true,
node: true,
},
parserOptions: {
ecmaFeatures: {
jsx: true,
},
ecmaVersion: 'latest',
sourceType: 'module',
},
ignorePatterns: ['node_modules', 'build'],
extends: ['airbnb', ...shared.extends],
plugins: shared.plugins,
rules: shared.rules,
overrides: [
{
files: ['**/*.ts?(x)'],
extends: [
'airbnb',
'airbnb-typescript',
'plugin:@typescript-eslint/recommended',
'plugin:import/recommended',
'plugin:import/typescript',
...shared.extends,
],
parser: '@typescript-eslint/parser',
parserOptions: {
project: './tsconfig.json',
},
plugins: [
...shared.plugins,
'@typescript-eslint',
'import',
],
settings: {
'import/internal-regex': '^~/', // なぜチルダなのかについては下参照
'import/resolver': {
node: {
extensions: ['.ts', '.tsx'],
},
typescript: {
alwaysTryTypes: true,
},
},
react: {
pragma: 'React',
version: 'detect',
},
tailwindcss: {
callees: ['twJoin', 'twMerge'], // tailwind-merge
},
},
rules: {
...shared.rules,
'no-undef': 'off', // TypeScriptが処理します
'no-unused-vars': 'off', // TypeScriptが処理します
'@typescript-eslint/array-type': ['error', {default: 'generic'}],
'@typescript-eslint/ban-ts-comment': 'off',
'@typescript-eslint/consistent-type-definitions': ['error', 'type'],
'@typescript-eslint/method-signature-style': 'error',
'@typescript-eslint/no-throw-literal': 'off',
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/no-unnecessary-boolean-literal-compare': ['error'],
'@typescript-eslint/no-unused-vars': 'error',
},
},
{
files: ['*.tsx', '**/hooks/*.ts?(x)'],
rules: {
// コンポーネントやフックで違反しやすいものです
'sonarjs/cognitive-complexity': 'off',
},
},
{
files: ['src/?(components|pages|services|utils)/**/*.ts?(x)'],
rules: {
'max-lines': ['error', 200],
},
},
{
files: ['./*.ts'], // プロジェクトのルートにあるファイルのみ
rules: {
'global-require': 'off',
'import/no-extraneous-dependencies': 'off',
'import/no-unresolved': 'error',
'import/prefer-default-export': 'off',
'no-void': 'off',
'unicorn/prefer-module': 'off',
'unicorn/prevent-abbreviations': 'off',
},
},
{
files: ['**/*.d.ts'],
rules: {
'@typescript-eslint/consistent-type-definitions': 'off',
'@typescript-eslint/method-signature-style': 'error',
'@typescript-eslint/no-unused-vars': 'off',
},
},
],
};
詳細
ルールの詳細を調べ、設定を変更するかどうかはあなたにお任せします。以下はいくつかのルールについての私の持論です。
- max-params - 関数型プログラミングでは、理想的には関数ごとにパラメーターを1つだけ持つべきです。実際には、これはやや制限が厳しすぎるかもしれませんが、3つのパラメーターの制限は、名前付きパラメーターオブジェクトに変換する前に合理的です。多すぎるパラメーターは、開発者に順序を覚えさせる必要があり、複数のオプションパラメーターも面倒です。シグネチャを調べられるかどうか、またはTypeScriptが助けてくれるかどうかは重要ではありません。実際には、この制限の方が良いです。Reactコンポーネントは、propsが単一のオブジェクトパラメーターであるため、これが実践的な良い例です。
- padding-line-between-statements - このルールは、一貫したコードスタイルと可読性を保証します。これは「prettier」タイプのルールであり、それが行うことを受け入れるだけで、誰もが同じようにコードをフォーマットされ、すぐに慣れることができます。
- quotes - すべての3種類の引用符(シングル、ダブル、バッククォート)に最適な引用符を保証します。
- react/jsx-boolean-value - 明示的であることが良いし、propsを一貫した見た目にします。これを好まない人にとっても、保存時に修正されるため、明示的に書かなくても、保存時に明示的になり、したがって誰にとっても一貫性があります。
- import/internal-regex - Remixは
~
チルダを使用し、私はこれが優れていると考えています。多くのインストールされたパッケージが@
を使用しているため、~
を使用することで、あなたのコードとnode_modules
からのコードを区別する明確な違いがあります。これはtsconfig.json
内でも構成する必要があります(paths)。 - @typescript-eslint/array-type - 好みに応じた設定を選択してください。array と generic の両方には妥当な議論があります。これは自動的に片方を強制し、一貫性が目標です。開発者は、より快適な方を書くことができるように、保存時に好ましい方に自動的に修正されます。
- max-lines - コンポーネントが大きすぎる場合、パフォーマンス、可読性、および関心の分離のために、通常はそれらをより小さなコンポーネントに分割すべきサインです。例外はあるでしょう。その場合、開発者はファイル内でこのルールを無効にすることができ、コードレビュー中に議論することができます。私の経験では、ほとんどのコンポーネントファイルは200行未満ですが、これを250行に増やすか、デフォルトの300行を使用することもできます。
- @typescript-eslint/consistent-type-definitions - 関数型プログラミングとイミュータビリティには
type
を使用すべきです。interface
はOOPとミュータビリティのためです。この記事 に良い説明があります。どちらを使用するかに関係なく、一貫して1つを使用することが望ましいです。サードパーティライブラリがinterface
を要求する場合があるので、ルールは.d.ts
ファイルでは無効になっています。
.prettierrc
これは私のPrettierの設定です
{
"bracketSpacing": false,
"experimentalTernaries": true,
"plugins": ["prettier-plugin-tailwindcss"], // tailwindを使っている場合
"singleQuote": true,
"tailwindFunctions": ["twJoin", "twMerge"], // tailwindを使っている場合
"trailingComma": "es5"
}
bracket-spacing
一部の人はブラケットのスペースを好むかもしれません。私はコードを必要以上に広くすると思いますし、それが可読性を向上させるとは思いません。もしあなたの好みならば、デフォルト設定を使い続けてください。
experimentalTernaries
これについてはこちらで読むことができます。これらは最終的にデフォルトになる予定なので、早めに慣れるために有効にしています。Prettierチームと同意見で、これらの方が優れていると考えています。
plugins
Tailwindを使用している場合は、Tailwind Prettier Pluginを使用する必要があります。
single-quote
シングルクォートを入力するのは簡単です(2つのキーを同時に押す必要がありません)、文字列内でアポストロフィやダブルクォートを使用する頻度よりも、コーディング中にシングルクォートを入力する頻度の方が上回ります。シングルクォートは長い間標準とされており、私の経験では、誰もがこれをtrue
に設定しています。
tailwindFunctions
Tailwindを使用している場合は、Tailwind Mergeを使用するべきです。clsxやclassnamesの代わりに。
trailingComma
トレイリングコンマは素晴らしいです!ただし、関数のパラメータでは好きではありません。閉じ括弧とアローの前に見づらくなると思います const foo = (a, b, c,) =>
。そのため、これを「es5」に設定しています。
.stylelintrc.json
こちらは私のStylelintの提案設定です。
{
"extends": [
"stylelint-config-standard",
"stylelint-config-idiomatic-order",
"stylelint-config-tailwindcss" // tailwindを使っている場合
],
"ignoreFiles": ["build/**/*.css", "public/build/**/*.css"],
"overrides": [
{
"files": ["**/*.module.css"],
"rules": {
// cssモジュールでキャメルケースの名前を強制する
"selector-class-pattern": "^[a-z][a-zA-Z0-9]+$"
}
}
],
"plugins": ["stylelint-order"],
"rules": {
"no-descending-specificity": null,
"selector-pseudo-class-no-unknown": [
true,
{
"ignorePseudoClasses": ["global", "local"]
}
]
}
}
.editorconfig
IDEはプロジェクトのルートにこのファイルを追加することで自動的にあなたのルールに従います。これは私の提案設定です。
# editorconfig.org
root = true
# これらに変更を加えないことを推奨します
[*]
charset = utf-8
end_of_line = lf
indent_size = 2
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
trim_trailing_whitespace = false
次の記事: ESLint - ソート