GatsbyにおけるESLintの設定




こんにちは。
本日はGatsbyのESLintの設定について書いていきたいと思います。

Gatsbyの使用用途ですが、 Web制作などいわゆるJamstack構成での使用などが多いと思います。
ですので、ESLintの設定もなるべくシンプルにガチガチに締めすぎずに設定したいです。
ESLintを導入しないというのも選択肢の一つだとは思いますが、勉強のためにもESLintを細かく設定したいというのと、Gatsbyに限らずReactは書き方が人によって大きく異なる(自分のコーティングスタイルの癖も大きいような気がしていた)ので、ESLintの導入を試してみました。
当記事ではGatsby×TypeScriptの例を紹介します。

GatsbyのESLintの日本語記事があまりなかったので、ここに記します。

GatsbyでのESLintの設定方法


GatsbyのESlintの設定方法は公式に詳しく書いてあります。ですのでここでは細かくは触れません。詳しくは以下をご覧ください。

Using ESlint

とはいっても、これを解説しないことには始まらないので簡単に解説していきたいと思います。
Gatsbyには標準でESLintが組み込まれています。

今回はこの標準ではなく、自分の設定をプロジェクトに反映させる方法を解説します。
Gatsbyで自身の設定を反映させるにはgatsby-plugin-eslintを使用します。
(npmを使用している方は適宜読み替えてください)

yarn add -D gatsby-plugin-eslint


このプラグインを使用して、設定する際には留意点があるので最後まで読んでください。
次にeslintをインストールします。

yarn add -D eslint eslint-loader


eslintはeslint --initを行うことで、初期ファイルを作成してくれる機能が備わっています。
eslintの設定ファイル.eslintrc.jsをこの機能を使って作成しましょう。.eslintrc.jsが作成されるとGatsby標準のESLint設定は上書きされます。

yarn eslint --init


質問に答えていくと、以下のようなファイルが生成されます。
質問回答は

? Which framework does your project use? React
? Does your project use TypeScript? Yes
? Would you like to install them now with npm? No

を選択しましょう。
以下のようなファイルが作成されるかと思います。
ReactとTypeScriptの標準の設定が組み込まれています。
plugin:react/recommended,plugin:@typescript-eslint_eslint-recommendedが設定されています。これは標準となるESLintのルールセットになります。
適用されるルールは以下をご覧ください。
eslint-plugin-react
@typescript-eslint/eslint-plugin

もっと厳しいルールが欲しい方はairbnbのルールセットなんかをみると良いと思います。GatsbyのWEB制作という観点でみると、そこまで厳しいのは不要かなあと個人的には思います。

module.exports = {
  "env": { 
  "browser": true, 
  "es6": true 
 }, 
 "extends": [
   "eslint:recommended",
   "plugin:react/recommended",
   "plugin:@typescript-eslint/eslint-recommended" 
 ],
 "globals": { 
  "Atomics": "readonly", 
  "SharedArrayBuffer": "readonly" }, 
  "parser": "@typescript-eslint/parser", "parserOptions": { 
  "ecmaFeatures": { 
   "jsx": true 
  },
   "ecmaVersion": 2018, "sourceType": "module" 
 },
 "plugins": [ 
  "react", 
  "@typescript-eslint" 
 ], 
 "rules": {
      'react/prop-types': 'off',
  } 
}; 


これを動かすために必要なパッケージをyarn経由でインストールします。

yarn add -D eslint-plugin-react@latest @typescript-eslint/eslint-plugin@latest @typescript-eslint/parser@latest 



gatsby-eslint-pluginの設定をgatsby-config.jsに書いていきます。optionについては公式をご覧ください。

gatsby-eslint-plugin

//gatsby-config.js
module.exports = {
  plugin: [
      {
        resolve: 'gatsby-plugin-eslint',
        options: {
          test: /\.js$|\.jsx$|\.ts$|\.tsx$/,
          exclude: /(node_modules|.cache|public)/,
          stages: ['develop'],
          options: {
            emitWarning: true,
            failOnError: true,
          },
       },
     },
  ]
}


Prettierとの連携


当然Prettierも欲しくなるので、連携を行います。設定のバッティングが起きないように設定する必要があります。コードの整形はPrettierに担当させ、ESLintの設定はそれにバッティングさせないような設定を加えます。
まずは必要なパッケージをインストールします。

yarn add -D prettier eslint-plugin-prettier eslint-config-prettier


Prettierの設定ファイルを書きます。ルールは好きなようにしてください。

//.prettierrc
{
  "semi": true,
  "singleQuote": true,
  "tabWidth": 2,
  "trailingComma": "all",
  "printWidth": 60
}


ESLintでPrettierとのルールがかぶらないように設定しましょう。
extendsに先ほどインストールしたプラグインを追加します。
//eslintrc.js

extends: [
    'plugin:prettier/recommended',
    'prettier/@typescript-eslint',
    'prettier/react',
]


これでPrettierとの連携は完了です。
package.jsonにScriptを書いて実行しましょう。

//package.json
 "scripts": {
    "format": "prettier --write \"src/**/*.{js,ts,tsx}*\"",
    "lint": "eslint 'src/**/*.{js,jsx,ts,tsx}'"
  },


yarn formatでPrettierを実行した後に、yarn lintで Linterでcheckをかけます。

その他設定

個人的に開発していて困ったこと。(のちに追記するかもしれません。)

TypeScriptだと型定義がno-unused-varsにかかってしまう。

no-unused-varsは一度も使っていない変数をチェックしてくれます。ただ、型定義の場合は(未使用でなくても)この警告がされてしまいます。
これを回避するにはこのルールをoffにします。jsファイルにはこのチェックを聞かせたいのでoverridesに設定をしました。

//.eslintrc.js
// 省略
  overrides: [
    {
      files: ['**/*.ts', '**/*.tsx'],
      rules: {
        'no-unused-vars': ['off'],
        'no-undef': ['off'],
      },
    },
  ],
}


husky,lint-stagedでコミット時にチェックを走らせる

husky,lint-stagedを使ってコミット時にチェックを走らせます。方法は簡単でpackage.jsonに以下を追記するだけです。

yarn add -D husky lint-staged


//package.json
{
// 省略 ファイルの一番下に設定を書く。
  "husky": {
    "hooks": {
      "pre-commit": "lint-staged"
    }
  },
  "lint-staged": {
    "*.{ts,tsx,js,jsx}": [
      "prettier --write",
      "eslint --fix",
      "git add"
    ]
  }
}


CIも今後設定したいと考えているので、この設定はのちに変更するかもしれません。好きなように設定してください。

Gatsbyとアクセシビリティ


上記の設定でとりあえず基本的な設定を行い、ESLintを使うことができました。ただ、まだ問題があります。
それはGatsbyが標準で用意しているルールセットを上書きしてしまっていることです。特にGatsbyの標準のルールセットはjsx-a11yというアクセシビリティに関する設定が書かれていて、これを使わないのはもったいないという印象です。

Gatsbyが設定しているESLintの設定は以下になります。
Gatsby/eslint-config.ts

簡潔にいうと

  1. eslint-config-react-app
  2. graphql
  3. jsx-a11y

に関する設定が書かれています。
ここでは先ほどの設定ファイルにこれらの設定を加えるという方針でカスタマイズしてみましょう。

まずはeslint-config-react-appです。これを動かすためにはこれまでにインストールしたプラグインに加え、以下のパッケージが必要です。

yarn add -D eslint-plugin-flowtype eslint-plugin-import eslint-plugin-jsx-a11y eslint-plugin-react-hooks


.eslintrc.jsのextendsに`eslint-config-react-app`,を追記します。
あとはreact-hooksにルールを追記したい場合はrulesに追加するなどのカスタマイズが可能です。

2,graphql/ 3,jsx-a11yに関しては先ほどのリンク先を参照して、ルールを加えてください。
最終的な.eslintrc.jsは以下のようになりました。

module.exports = {
  env: {
    browser: true,
    es6: true,
  },
  extends: [
    'eslint:recommended',
    `eslint-config-react-app`,
    'plugin:react/recommended',
    'plugin:jsx-a11y/recommended',
    'plugin:@typescript-eslint/eslint-recommended',
    'plugin:prettier/recommended',
    'prettier/@typescript-eslint',
    'prettier/react',
  ],
  globals: {
    graphql: true,
    __PATH_PREFIX__: true,
    __BASE_PATH__: true,
  },
  parser: '@typescript-eslint/parser',
  parserOptions: {
    ecmaFeatures: {
      jsx: true,
    },
    project: './tsconfig.json',
    sourceType: 'module',
    ecmaVersion: 11,
    sourceType: 'module',
  },
  plugins: [
    'react',
    '@typescript-eslint',
    'prettier',
    'graphql',
    'jsx-a11y',
    'import',
  ],
  rules: {
    'react/prop-types': 'off',
    'import/no-webpack-loader-syntax': [0],
    'graphql/template-strings': `off`,

    // https://github.com/evcohen/eslint-plugin-jsx-a11y/tree/master/docs/rules
    'jsx-a11y/accessible-emoji': `warn`,
    'jsx-a11y/alt-text': `warn`,
    'jsx-a11y/anchor-has-content': `warn`,
    'jsx-a11y/anchor-is-valid': `warn`,
    'jsx-a11y/aria-activedescendant-has-tabindex': `warn`,
    'jsx-a11y/aria-props': `warn`,
    'jsx-a11y/aria-proptypes': `warn`,
    'jsx-a11y/aria-role': `warn`,
    'jsx-a11y/aria-unsupported-elements': `warn`,

    'jsx-a11y/click-events-have-key-events': `warn`,
    'jsx-a11y/heading-has-content': `warn`,
    'jsx-a11y/html-has-lang': `warn`,
    'jsx-a11y/iframe-has-title': `warn`,
    'jsx-a11y/img-redundant-alt': `warn`,
    'jsx-a11y/interactive-supports-focus': `warn`,
    'jsx-a11y/label-has-associated-control': `warn`,
    'jsx-a11y/lang': `warn`,
    'jsx-a11y/media-has-caption': `warn`,
    'jsx-a11y/mouse-events-have-key-events': `warn`,
    'jsx-a11y/no-access-key': `warn`,
    'jsx-a11y/no-autofocus': `warn`,
    'jsx-a11y/no-distracting-elements': `warn`,
    'jsx-a11y/no-interactive-element-to-noninteractive-role': `warn`,
    'jsx-a11y/no-noninteractive-element-interactions': `warn`,
    'jsx-a11y/no-noninteractive-element-to-interactive-role': `warn`,
    'jsx-a11y/no-noninteractive-tabindex': `warn`,
    'jsx-a11y/no-onchange': `warn`,
    'jsx-a11y/no-redundant-roles': `warn`,
    'jsx-a11y/no-static-element-interactions': `warn`,
    'jsx-a11y/role-has-required-aria-props': `warn`,
    'jsx-a11y/role-supports-aria-props': `warn`,
    'jsx-a11y/scope': `warn`,
    'jsx-a11y/tabindex-no-positive': `warn`,
  },
  overrides: [
    {
      files: ['**/*.ts', '**/*.tsx'],
      rules: {
        'no-unused-vars': ['off'],
        'no-undef': ['off'],
      },
    },
  ],
};


まとめ

今回、ESLintの勉強もかねて、GatsbyのESLintの設定を考えてみましたが、とても勉強になりました。
特にGatsbyがアクセシビリティのことをよく考えて、ルール設定をしているのにとても良い印象を抱きました。

なお、今回のルール設定は私の勉強不足でルールがかぶってしまっていたりする箇所や不具合がある場合があるかもしれません。
そのような際はぜひ、ご意見くださるとありがたいです。
基本的な設定方法については変わらないと思いますので、色々と設定をしてみてください。
今回のサンプルは以下になります。

GitHub

ありがとうございました。