ESLint と Prettier のアップデート
- javascript
はじめに
個人プロジェクトの ESLint と Prettier のアップデートを行いました(コミット履歴を確認ところ実に一年ぶり!)。yarn upgrade-interactive --latest
で一括アップデートしたものの、エラーが 0 件から一気に 38 件になってしまいました。公式ドキュメントで調べつつ、エラーの解消と設定の見直しを行いました。
npm パッケージのバージョンの差異
ESLint と Prettier の npm パッケージの差異は以下の通りです。
"@storybook/preset-create-react-app": "^2.1.1",
"@storybook/react": "^5.3.17",
"@types/babel-plugin-macros": "^2.8.1",
- "@types/eslint-plugin-prettier": "^2.2.0",
- "@types/prettier": "^1.19.0",
+ "@types/prettier": "^2.1.6",
"@types/react-test-renderer": "^16.9.2",
"@types/storybook__addon-info": "^5.2.1",
"@types/stylelint": "^9.10.1",
- "@typescript-eslint/eslint-plugin": "^2.10.0",
- "@typescript-eslint/parser": "^2.10.0",
+ "@typescript-eslint/eslint-plugin": "^4.13.0",
+ "@typescript-eslint/parser": "^4.13.0",
"axios-mock-adapter": "^1.17.0",
"babel-plugin-macros": "^2.8.0",
- "eslint-config-airbnb": "^18.0.1",
- "eslint-config-prettier": "^6.7.0",
- "eslint-plugin-import": "^2.19.1",
- "eslint-plugin-jest": "^23.1.1",
- "eslint-plugin-jsx-a11y": "^6.2.3",
- "eslint-plugin-prefer-arrow": "^1.1.7",
- "eslint-plugin-prettier": "^3.1.1",
- "eslint-plugin-react": "^7.17.0",
- "eslint-plugin-react-hooks": "^2.3.0",
+ "eslint-config-airbnb": "^18.2.1",
+ "eslint-config-prettier": "^7.1.0",
+ "eslint-plugin-import": "^2.22.1",
+ "eslint-plugin-jest": "^24.1.3",
+ "eslint-plugin-jsx-a11y": "^6.4.1",
+ "eslint-plugin-prefer-arrow": "^1.2.2",
+ "eslint-plugin-react": "^7.22.0",
+ "eslint-plugin-react-hooks": "^4.2.0",
"jest-fetch-mock": "^3.0.1",
- "prettier": "^1.19.1",
+ "prettier": "^2.2.1",
"prettier-stylelint": "^0.4.2",
"react-docgen-typescript-loader": "^3.7.1",
"react-docgen-typescript-webpack-plugin": "^1.1.0",
エラーの解消
npm パッケージのアップデートに伴い発生したエラーは主に3つのルール由来のものでした。それぞれ @typescript-eslint/explicit-module-boundary-types
@typescript-eslint/camelcase
no-use-before-define
です。
@typescript-eslint/explicit-module-boundary-types
アップデートに伴いルールのデフォルト設定が変更になり、エクスポートする関数の返り値の型の明記が必須となりました。これまでは型推論に任せていたため、返り値の型を明記するよう変更しました。
@typescript-eslint/camelcase
アップデートに伴いルールが削除されました。
ESLint のルールに camelcase
のルールがあるため、そちらを使うように変更しました。
no-use-before-define
アップデートに伴い react
のインポート文 import React from 'react'
がエラー扱いとなってしまいました。
'React' was used before it was defined no-use-before-define
@typescript-eslint
の公式ドキュメントによると、ESLint の no-use-before-define
ルールをオフにし、@typescript-eslint/no-use-before-define
の拡張ルールを適用する必要があるようです。
module.exports = {
// ...
rules: {
// ...
"no-use-before-define": 'off',
"@typescript-eslint/no-use-before-define": ['error']
}
}
In some cases, ESLint provides a rule itself, but it doesn't support TypeScript syntax; either it crashes, or it ignores the syntax, or it falsely reports against it. In these cases, we create what we call an extension rule; a rule within our plugin that has the same functionality, but also supports TypeScript.
https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/README.md#extension-rules
設定の見直し
エラー解消後、設定の見直しを行いました。
.eslintignore を --ignore-path .gitignore で代用
--ignore-path .gitignore
とすることで .gitignore
を ESLint のチェックの対象外とするファイルの指定に使うことができます。対象外としたいファイルが .gitignore
に含まれる場合、 .eslintignore
を別途用意する必要がないため便利です。
$ npx eslint --ignore-path .gitignore .
Prettier を直接実行
Prettier 導入時は Prettier を ESLint のルールとして実行するために eslint-plugin-prettier
を使っていました。しかし、2021年1月現在の公式ドキュメントではその方法が非推奨となり、Prettier を直接実行する方法に変更されています。
そこで eslint-plugin-prettier
をアンインストールし、prettier --write
を直接実行するようにしました。
$ npx prettier --write .
.eslintrc.js を更新
.eslintrc.js
を更新しました。.eslintrc.js
の設定は『りあクト! TypeScript で始めるつらくない React 開発 第3.1版』の .eslintrc.js を参考にさせていただきました。
module.exports = {
env: {
browser: true,
es2020: true
},
extends: [
'airbnb',
'airbnb/hooks',
'plugin:import/errors',
'plugin:import/warnings',
'plugin:import/typescript',
'plugin:@typescript-eslint/recommended',
'plugin:@typescript-eslint/recommended-requiring-type-checking',
'plugin:jest/recommended',
'prettier',
'prettier/@typescript-eslint',
'prettier/react'
],
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaFeatures: {
jsx: true
},
project: './tsconfig.eslint.json',
sourceType: 'module',
tsconfigRootDir: __dirname
},
plugins: [
'@typescript-eslint',
'import',
'jest',
'jsx-a11y',
'prefer-arrow',
'react',
'react-hooks'
],
root: true,
rules: {
'no-restricted-syntax': [
'error',
{
selector: 'ForInStatement',
message:
'for..in loops iterate over the entire prototype chain, which is virtually never what you want. Use Object.{keys,values,entries}, and iterate over the resulting array.'
},
{
selector: 'LabeledStatement',
message:
'Labels are a form of GOTO; using them makes code confusing and hard to maintain and understand.'
},
{
selector: 'WithStatement',
message:
'`with` is disallowed in strict mode because it makes code impossible to predict and optimize.'
},
],
'no-use-before-define': 'off',
'@typescript-eslint/no-use-before-define': ['error'],
'no-void': [
'error',
{
allowAsStatement: true,
},
],
'padding-line-between-statements': [
'error',
{
blankLine: 'always',
prev: '*',
next: 'return'
}
],
'@typescript-eslint/no-unused-vars': [
'error',
{
'vars': 'all',
'args': 'after-used',
'argsIgnorePattern': '_',
'ignoreRestSiblings': false,
'varsIgnorePattern': '_'
}
],
'import/extensions': [
'error',
'ignorePackages',
{
js: 'never',
jsx: 'never',
ts: 'never',
tsx: 'never'
}
],
'import/no-extraneous-dependencies': [
'error',
{
devDependencies: [
'.storybook/**',
'stories/**',
'**/*/*.story.*',
'**/*/*.stories.*',
'**/__specs__/**',
'**/*/*.spec.*',
'**/__tests__/**',
'**/*/*.test.*',
'src/setupTests.*'
]
}
],
'import/prefer-default-export': 'off',
'prefer-arrow/prefer-arrow-functions': [
'error',
{
disallowPrototype: true,
singleReturnOnly: false,
classPropertiesAllowed: false
}
],
'react/jsx-filename-extension': [
'error',
{
extensions: ['jsx', 'tsx']
}
],
'react/jsx-props-no-spreading': [
'warn',
{
html: 'enforce',
custom: 'enforce',
explicitSpread: 'ignore'
}
]
},
overrides: [
{
'files': ['*.tsx'],
'rules': {
'react/prop-types': 'off'
}
}
],
settings: {
'import/resolver': {
node: {
paths: ['src']
}
},
'react': {
version: 'detect'
}
}
}
2021/03/14 更新
2021/02/21 に eslintrc-config-prettier
の v8.0.0 がリリースされました。すべての設定が1つに統合され、.eslintrc.js
をよりシンプルに記述できるようになりました。
'plugin:@typescript-eslint/recommended',
'plugin:@typescript-eslint/recommended-requiring-type-checking',
'plugin:jest/recommended',
- 'prettier',
- 'prettier/@typescript-eslint',
- 'prettier/react'
+ 'prettier'
],
globals: {
Atomics: 'readonly',
参考
- camelcase - Rules - ESLint - Pluggable JavaScript linter
- no-use-before-define - Rules - ESLint - Pluggable JavaScript linter
- Configuring ESLint - ESLint - Pluggable JavaScript linter
- Riakuto-StartingReact-ja3.1/.eslintrc.js at master · oukayuka/Riakuto-StartingReact-ja3.1
- eslint-config-prettier/CHANGELOG.md at main · prettier/eslint-config-prettier
- typescript-eslint/README.md at master · typescript-eslint/typescript-eslint
- typescript-eslint/explicit-module-boundary-types.md at master · typescript-eslint/typescript-eslint
- typescript-eslint/no-use-before-define.md at master · typescript-eslint/typescript-eslint
- Integrating with Linters · Prettier