React + Rails API 環境構築 ①React側の設定 〜ESLintの設定まで

## 概要

- 1からSPAアプリケーションの環境を作っていくので、その手順を残す
- 構築していく途中で調べた内容も別途記事にしてまとめているので良ければ見てください![下にリンク貼ってます](#リンク集)

## フロントエンド環境構築

### 1. node.jsをインストール〜Linter&Formatterの設定前まで

- anyenv[^1]をインストール(env系をマネージするパッケージマネージャー)

```
brew install anyenv
echo ‘eval “$(anyenv init -)”’ >> ~/.zshrc
Exec $SHELL -l
```
 
- anyenv経由でnodenvをインストールする

```
anyenv install nodenv
exec $SHELL -l
```

- anyenvとnodenv用のプラグインを入れる
- nodenvを含めたenv系をまとめてアップデートしてくれるanyenvプラグインのanyenv-update
- npmインストール時にデフォルトで一緒にインストールしておくパッケージを指定できるnodenvプラグインのnodenv-default-packages[^2]

```
mkdir -p $(anyenv root)/plugins
git clone https://github.com/znz/anyenv-update.git $(anyenv root)/plugins/anyenv-update
mkdir -p "$(nodenv root)"/plugins
git clone https://github.com/nodenv/nodenv-default-packages.git "$(nodenv root)/plugins/nodenv-default-packages"
```

- nodenv rootにdefault-packagesを作成し、中身を以下のように編集する

touch $(nodenv root)/default-packages

``` Default-packages
yarn
Typescript
ts-node
typesync
```

- node.jsをインストールする

```
anyenv update # インストール可能なリストを更新する(これしないとNode最新版にあげられない)
nodenv install -l #インストールできるバージョンを一覧で確認
nodenv install 14.4.0 #バージョンを指定してインストール
nodenv global 14.4.0 #端末上でデフォルトで使用するnode.jsのバージョンを指定
nodenv local 14.4.0 #カレントディレクトリ配下で使用するnode.jsのバージョン指定
```

- create-react-appでreactアプリの雛形を作成する

```
npx create-react-app <directry name> —template typescript
#reactアプリを始めるためのディレクトリが既にある場合は「.」、まだない場合はディレクトリ名を設定する
#—template typescriptでTypeScriptのためのテンプレートを指定する
```

- typesync[^3]をインストールする
- reactのプラグインを入れた後にtypescriptの型情報を一緒にインストールできるようにするプラグイン

```
yarn add type sync
```

- tsconig.jsonファイルを編集する

```json:tsconfig.json
“Jsx”: “react-jsx”,
+ “baseUrl”: “src”, #絶対パスでimportできるようにする設定
+ “downlevelItteration”: true #
```

- vscodeで開発するときは上記の設定をした後にコンソールから touchtsconig.json をして設定を読み込ませる

### 2. Linter&Formatterの設定

- TypeScript他パッケージを最新にする

```
yarn upgrade-interactive —latest #対話的にyarnで管理しているライブラリを更新する
yarn upgarade typescript@latest #typescriptを最新にする
```

- ESLint[^4]をインタラクティブにインストールする

```
$ yarn eslint --init
? How would you like to use ESLint?
》To check syntax, find problems, and enforce code style

? What type of modules does your project use? JavaScript modules (import/export)
》JavaScript modules (import/export)

? Which framework does your project use?
》React

? Does your project use TypeScript?
》Yes

? Where does your code run?
》Browser

? How would you like to define a style for your project?
》Use a popular style guide

? Which style guide do you want to follow?
》Airbnb: https://github.com/airbnb/javascript

? What format do you want your config file to be in?
》JavaScript

The config that you've selected requires the following dependencies:

eslint-plugin-react@^VERSION @typescript-eslint/eslint-plugin@latest eslint-config-airbnb@latest eslint@^VERSION eslint-plugin-import@^VERSION eslint-plugin-jsx-a11y@^VERSION eslint-plugin-react-hooks@^VERSION @typescript-eslint/parser@latest
? Would you like to install them now with npm?
》No
```

- yarnで先ほどの拡張ルールやプラグインをインストールする

```
yarn add -D eslint-plugin-react @typescript-eslint/eslint-plugin \
 eslint-config-airbnb eslint-plugin-import eslint-plugin-jsx-a11y \
 eslint-plugin-react-hooks @typescript-eslint/parser
yarn #package.jsonのscriptsにpreinstallでtypesyncする設定にしていない場合はこれの前にtypesyncする
```

#### .eslintrc.jsファイルをカスタマイズする
##### extendsに記述するのは各プラグインルールの推奨の共有設定
- プラグインは読み込んだだけでは何のルールも適用されない
- プラグイン開発者がパッケージ内で共有設定を一緒に提供しているので、それをextendsで適用する必要がある
- 共有設定間でルールが衝突した場合は後の記述が上書きするため、順番には注意する

```js:.eslintrc.js
   extends: [
      'plugin:react/recommended',
      'airbnb',
+     'airbnb/hooks',
+     'plugin:import/errors',
+     'plugin:import/warnings',
+     'plugin:import/typescript',
+     'plugin:@typescript-eslint/recommended',
+     'plugin:@typescript-eslint/recommended-requiring-type-checking',
    ],
```

##### parserの設定

- @typescript-eslint/parserに渡すオプションを定義する

``` js:.eslintrc.js
    parser: '@typescript-eslint/parser',
    parserOptions: {
      ecmaFeatures: {
        jsx: true
      },
      ecmaVersion: 12,
+     project: './tsconfig.eslint.json',
      sourceType: 'module',
+     tsconfigRootDir: __dirname,
    },
```

- parserに読ませるファイルを作成する(tsconfig.jsonはそのまま読ませない)
- パーサがnpmパッケージのファイルまでパースし、VSCodeと連携させたときのパフォーマンス低下、新規ファイルのパースの失敗を防ぐため
- 現状ではこうするしかないらしい(今後対応が変わる可能性は大いにある)

```tsconfig.eslint.json
{
  "extends": "./tsconfig.json",
  "include": [
    "src/**/*.js",
    "src/**/*.jsx",
    "src/**/*.ts",
    "src/**/*.tsx"
  ],
  "exclude": [
    "node_modules"
  ]
}
```

##### pluginsの設定

- yarn addしたプラグインのルールを適用するための内容を追加する
- 追加ルールのプラグインはyarn addしただけでは適用されないのでここにルールを追加する必要がある
- ESLintはデフォルトでは親ディレクトリの設定ファイルまで読み込むようになっているので、それを抑止するために**root**のオプションを追加している

```js:.eslintrc.js
    plugins: [
      '@typescript-eslint',
+     'import',
+     'jsx-a11y',
      'react',
+     'react-hooks',
    ],
+   root: true,
```

##### rulesの設定

- rulesに各種ルールを設定する
- 各ルールの適用の可否やエラーレベルを設定する
- 主にextendsで読み込んだ共有設定の値を書き換えるときにここに記述する
- 各種ルールの説明

###### 1. no-use-before-define, @typescript-eslint/no-use-before-define

- 定義前の変数の使用を禁じる ESLint と TypeScript ESLint のルール。本来なら上記でやっているような記述は必要なく TypeScript で正常に動作するはずだが、不具合が解消されてはリグレッションが繰り返されており、react-scripts 4.0.1 の環境ではこうしないと import React from 'react' のインポート文でエラーになってしまう

###### 2. lines-between-class-members

- クラスメンバーの定義の間に空行を入れるかどうかを定義するルール。eslint-config-airbnb で常に空行を入れるように設定されていたのを、ここでは 1 行記述のメンバーのときは空行を入れなくていいようにしている

###### 3. no-void

- void 演算子の（式としての）使用を禁ずるルール。Effect Hook20 内で非同期処理を記述する際、@typescript-eslint/no-floating-promises ルールに抵触してしまうのを回避するのに void 文を記述する必要があるため、文としての使用のみを許可している

###### 4. padding-line-between-statements

- 任意の構文の間に区切りの空行を入れるかどうかを定義するルール。ここでは return 文の前に常に空行を入れるよう設定している

###### 5. @typescript-eslint/no-unused-vars

- 使用していない変数の定義を許さないルール。ここでは変数名を _ にしたときのみ許容するように設定

###### 6. import/extensions *

- インポートの際のファイル拡張子を記述するかを定義するルール。npm パッケージ以外のファイルについて .js、.jsx、.ts、.tsx のファイルのみ拡張子を省略し、他のファイルは拡張子を記述させるように設定

###### 7. react/jsx-filename-extension *

- JSX のファイル拡張子を制限するルール。eslint-config-airbnb で .jsx のみに限定されているので、.tsx を追加

###### 8. react/jsx-props-no-spreading

- JSX でコンポーネントを呼ぶときのprops の記述にスプレッド構文を許さないルール。eslint-config-airbnb にてすべて禁止されているが、<Foo {...{ bar, baz } /}> のように個々の props を明記する書き方のみ許容するように設定

###### 9. react/react-in-jsx-scope

- JSX 記述を使用する場合に react モジュールを React としてインポートすることを強制する。新しい JSX 変換形式を用いる場合はインポートが不要になるためこの設定を無効化

###### 10. react/prop-types *

- コンポーネントの props に型チェックを行うための propTypes プロパティ21の定義を強制するルール。eslint-config-airbnb で設定されているが、TypeScript の場合は不要なのでファイル拡張子が .tsx の場合に無効化するよう設定を上書き

```js:.eslintrc.js
  rules: {
    'no-use-before-define': 'off',
    '@typescript-eslint/no-use-before-define': [
      'error',
    ],
    'lines-between-class-members': [
      'error',
      'always',
      {
        exceptAfterSingleLine: true
      }
    ],
    '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',
    }
  ],
  'react/jsx-filename-extension': [
    'error',
    {
      extensions: ['.jsx', '.tsx']
    }
  ],
  'react/jsx-props-no-spreading': [
    'error',
    {
      html: 'enforce',
      custom: 'enforce',
      explicitSpread: 'ignore',
    },
  ],
  'react/react-in-jsx-scope': 'off',
},
overrides: [
  {
    'files': ['*.tsx'],
    'rules': {
      'react/prop-types': 'off',
    },
  },
],
settings: {
  'import/resolver': {
    node: {
      paths: ['src'],
    },
  },
},
};
```

#### .eslintignoreファイルを作成する

- ESLintチェックの対象外にするための設定ファイル
- 関係ないコードまでlintが走って不要な場面でエラーが出るのを防ぐため

```.eslintignore
build/
Public/
**/coverage/
**/node_modules/
**/*.min.js
*.config.js
.*lintrc.js
```

#### setings.jsonに追加で設定をする

- ファイル保存時にVSCode内蔵のものではなく、ESLintの自動整形が走るようにする
- 最後の行はプロジェクトにTypeScriptがインストールsれている場合はどっちを使うか尋ねさせるための設定

```json:settings.json
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": true
  },
  "editor.formatOnSave": false,
  "eslint.packageManager": "yarn",
  "typescript.enablePromptUseWorkspaceTsdk": true,
```

## ...[React + Rails API 環境構築 ②React側の設定 〜Prittier,stylelintの設定](https://qiita.com/Islanders-Treasure0969/items/6086ddc466bdeb256fea)　に続く

#リンク集

[^1]: [anyenvまとめ](https://qiita.com/Islanders-Treasure0969/items/bd9e300ae1bd41362533)
[^2]: [nodenv-default-packagesまとめ](https://qiita.com/Islanders-Treasure0969/items/f8e70f5f801b54a98806)
[^3]: [typesyncまとめ](https://qiita.com/Islanders-Treasure0969/items/760d3c667c1e9e6948ba)
[^4]: [ESLintまとめ](https://qiita.com/Islanders-Treasure0969/items/8f1e5281b9850b9c3cfe)