【ESLint】Flat Config に eslint-plugin-jsx-a11y を追加する

# はじめに
この記事では、ESLint の Flat Config にアクセシビリティ向けルールのプラグインである `eslint-plugin-jsx-a11y` を追加する手順について記載します。

https://github.com/jsx-eslint/eslint-plugin-jsx-a11y

# 開発環境
開発環境は以下の通りです。

- Windows 11
- VSCode
- TypeScript 5.2.2
- React 18.3.1
- Vite 5.3.1
- Node.js 20.15.0
- npm 10.8.1
- ESLint 8.57.0
- Prettier 3.3.2
- @eslint/js 9.6.0
- eslint-config-prettier 9.1.0
- eslint-plugin-jsx-a11y 6.9.0
- eslint-plugin-react 7.34.3
- globals 15.8.0
- typescript-eslint 7.15.0

# 事前準備
プロジェクトに ESLint のインストール・設定をしておきます。
React / TypeScript 環境における詳細手順はこちら。

https://qiita.com/Yasushi-Mo/items/c2e259f8e2a86b79cb8a

Prettier を追加する手順はこちら。

https://qiita.com/Yasushi-Mo/items/27d324f601edebb63019

# `eslint-plugin-jsx-a11y` のインストール
`eslint-plugin-jsx-a11y` をインストールします。

```powershell
npm install eslint-plugin-jsx-a11y --save-dev
```

# 一括設定
`eslint.config.js` で `eslint-plugin-jsx-a11y` をインポートして `jsxA11y.flatConfigs.recommended` を追加します。

```eslint.config.js
import globals from "globals";
import pluginJs from "@eslint/js";
import tseslint from "typescript-eslint";
import pluginReactConfig from "eslint-plugin-react/configs/recommended.js";
import pluginReactJSXRuntime from "eslint-plugin-react/configs/jsx-runtime.js";
import jsxA11y from "eslint-plugin-jsx-a11y"; // Add
import eslintConfigPrettier from "eslint-config-prettier";

export default [
  {
    files: ["**/*.{js,mjs,cjs,ts,jsx,tsx}"],
    settings: {
      react: {
        version: "detect",
      },
    },
  },
  { languageOptions: { parserOptions: { ecmaFeatures: { jsx: true } } } },
  { languageOptions: { globals: globals.browser } },
  pluginJs.configs.recommended,
  ...tseslint.configs.recommended,
  pluginReactConfig,
  pluginReactJSXRuntime,
  jsxA11y.flatConfigs.recommended, // Add
  eslintConfigPrettier,
];

```

# 動作確認
`img` タグの `alt` がない状態で、ESLint を実行してみます。

```App.tsx
import { useState } from "react";
import reactLogo from "./assets/react.svg";
import viteLogo from "/vite.svg";
import "./App.css";

function App() {
  const [count, setCount] = useState(0);

  return (
    <>
      <div>
        <a href="https://vitejs.dev" target="_blank" rel="noreferrer">
          {/* alt を削除 */}
          <img src={viteLogo} className="logo" />
        </a>
        <a href="https://react.dev" target="_blank" rel="noreferrer">
          <img src={reactLogo} className="logo react" alt="React logo" />
        </a>
      </div>
      <h1>Vite + React</h1>
      <div className="card">
        <button onClick={() => setCount((count) => count + 1)}>
          count is {count}
        </button>
        <p>
          Edit <code>src/App.tsx</code> and save to test HMR
        </p>
      </div>
      <p className="read-the-docs">
        Click on the Vite and React logos to learn more
      </p>
    </>
  );
}

export default App;

```

以下のエラーが表示されます。

```powershell
13:11  error  img elements must have an alt prop, either with meaningful text, or an empty string for decorative images
jsx-a11y/alt-text
```

`alt` を追加するとエラーが消えます。

```App.tsx
import { useState } from "react";
import reactLogo from "./assets/react.svg";
import viteLogo from "/vite.svg";
import "./App.css";

function App() {
  const [count, setCount] = useState(0);

  return (
    <>
      <div>
        <a href="https://vitejs.dev" target="_blank" rel="noreferrer">
          {/* alt を追加 */}
          <img src={viteLogo} className="logo" alt="Vite logo" />
        </a>
        <a href="https://react.dev" target="_blank" rel="noreferrer">
          <img src={reactLogo} className="logo react" alt="React logo" />
        </a>
      </div>
      <h1>Vite + React</h1>
      <div className="card">
        <button onClick={() => setCount((count) => count + 1)}>
          count is {count}
        </button>
        <p>
          Edit <code>src/App.tsx</code> and save to test HMR
        </p>
      </div>
      <p className="read-the-docs">
        Click on the Vite and React logos to learn more
      </p>
    </>
  );
}

export default App;

```

# 個別設定
`eslint-plugin-jsx-a11y` の各ルールを個別に設定することもできます。

- `pugins` に `"jsx-a11y": jsxA11y` を追加
- `rules` に `"jsx-a11y/alt-text": "error"` を追加

```eslint.config.js
import globals from "globals";
import pluginJs from "@eslint/js";
import tseslint from "typescript-eslint";
import pluginReactConfig from "eslint-plugin-react/configs/recommended.js";
import pluginReactJSXRuntime from "eslint-plugin-react/configs/jsx-runtime.js";
import jsxA11y from "eslint-plugin-jsx-a11y";
import eslintConfigPrettier from "eslint-config-prettier";

export default [
  {
    files: ["**/*.{js,mjs,cjs,ts,jsx,tsx}"],
    settings: {
      react: {
        version: "detect",
      },
    },
    plugins: {
      "jsx-a11y": jsxA11y, // Add
    },
    rules: {
      "jsx-a11y/alt-text": "error", // Add
    },
  },
  { languageOptions: { parserOptions: { ecmaFeatures: { jsx: true } } } },
  { languageOptions: { globals: globals.browser } },
  pluginJs.configs.recommended,
  ...tseslint.configs.recommended,
  pluginReactConfig,
  pluginReactJSXRuntime,
  eslintConfigPrettier,
];

```

# 参考
- [eslint-plugin-jsx-a11y](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y)

# 関連記事
https://qiita.com/Yasushi-Mo/items/c2e259f8e2a86b79cb8a

https://qiita.com/Yasushi-Mo/items/27d324f601edebb63019

https://qiita.com/Yasushi-Mo/items/524330da3723ab1018d7