webpackに比べ高速と評判のViteを用いてReact開発環境を作成しつつ、
ESLintを導入して更にDXを上げたReactの開発環境の作成方法をご紹介します!
検証した環境
1 | react | 18.2.0 |
2 | vite | 4.1.4 |
3 | eslint | 8.36.0 |
node環境の設定
2023/03/11現在、nodeのLTSが18.15.0なのでnodenvを使って固定していきます
# ローカル環境になければインストール
$ nodenv install 18.15.0
# v18.15.0を使用する
$ nodenv local 18.15.0
# nodeのバージョンを指定出来た事を確認
$ node -v
v18.15.0
Reactの開発環境を用意
Vite + React のプロジェクトを用意します
$ npm create vite
# or
$ yarn create vite
React公式で紹介されているcreate-react-app
を用いると
webpackで作られてしまうため注意です!
表示された順に入力・選択していきます
プロジェクト名(後でも変えられるので適当でもOK)
frameworkでReactを選択
迷いポイント
- JavaScript
- TypeScript
- JavaScript + SWC
- TypeScript + SWC
の4つから選択します。
今のご時世だとほぼほぼTypeScriptを選択するプロジェクトが多いと思われるので、下記どちらかを選択するケースが多いはず。
- TypeScript
- TypeScript + SWC (多分こっちがおすすめ)
上記2つの違いとしては、それぞれ導入されるViteのプラグインが変わってきます
- TypeScript・・
@vitejs/plugin-react
- TypeScript + SWC・・
@vitejs/plugin-react-swc
プラグインの詳細については公式ページに
プラグインの違いを比較すると
@vitejs/plugin-react
esbuild と Babel を使用し、小さなパッケージフットプリントで高速な HMR や、Babel 変換パイプラインを使用できる柔軟性を実現します。Babel プラグインを追加しない場合、ビルド時には esbuild のみが使用されます。
- esbuild + Babel
@vitejs/plugin-react-swc
開発中は Babel を SWC に置き換えます。ビルド時には、プラグインを使用する場合は SWC+esbuild、それ以外は esbuild を使用します。非標準の React 拡張を必要としない大きなプロジェクトでは、コールドスタートやホットモジュールリプレースメント(HMR)が大幅に高速化されます。
- esbuild + SWC
と読み取れそうです。
SWCはBabelを置き換えるために開発された高速なビルドツールなので、
Babelの文脈でSWCを使って問題なければ TypeScript + SWC
を用いるのが良いのかなと感じました!
最後に表示された通りコマンドを実行
cd vite-project
yarn
yarn dev
dev環境が起動します。
http://localhost:5173/ にアクセスすると
無事Vite+Reactでアプリケーションが立ち上がりました!🎉
Prettier導入
Prettierを導入します
$ yarn add -D prettier
設定ファイルを追加 (設定内容はお好みで)
{
"semi": true,
"singleQuote": true,
"bracketSameLine": true,
"printWidth": 100
}
既存のファイルにPrettierを実行
$ npx prettier --write .
ESLintを導入
本題の1つ、ESLintを導入していきます!
ESLintを対話形式で初期設定
まずESLintのパッケージを追加
$ yarn add -D eslint
ESLintの設定ファイルを作成していきます
Getting Started with ESLint - ESLint - Pluggable JavaScript Linter
$ npm init @eslint/config
initを実行する際にnpm
を使いますが
最終的にパッケージをインストール前にnpm・yarn・pnpmのどれでインストールするかを選択出来ます
ここから対話形式で進んでいきます
必要なパッケージのインストール
Need to install the following packages:
@eslint/create-config@0.4.2
Ok to proceed? (y)
@eslint/create-config
が必要だからインストールしていいか?
と聞かれるのでEnterを押してyを実行
プロジェクト内のpackage.jsonに追加されるのではなく、
グローバルに一時的かもですがインストールされているようです。
ESLintの使い方
? How would you like to use ESLint? …
To check syntax only
To check syntax and find problems
❯ To check syntax, find problems, and enforce code style
1番下のTo check syntax, find problems, and enforce code style
がおすすめ
構文の解析・問題の発見・コードスタイルの強制の全部入り。
「コードスタイルの強制」というのは
この後の項目で出てくる、AirbnbやGoogleが公開しているESLintのコーディングスタイルをextendし、それを守る事になります。
有名所のスタイルガイドに則れるので、基本はコードスタイルの強制を選択して良いかなと思います。
プロジェクトで用いるモジュールについて
? What type of modules does your project use? …
❯ JavaScript modules (import/export)
CommonJS (require/exports)
None of these
ReactなのでJavaScript modules
を選択
- JavaScript modules・・ES2015のimport/export
- CommonJS・・CommonJSのrequire
フレームワーク
? Which framework does your project use? ...
❯ React
Vue.js
None of these
React
を選択
TypeScriptの利用有無
? Does your project use TypeScript? › No / Yes
TypeScriptを使うのでYes
を選択
コードの実行場所
? Where does your code run? … (Press <space> to select, <a> to toggle all, <i> to invert selection)
✔ Browser
✔ Node
Browser
を選択
どのようにESLintのスタイルを定義するか
? How would you like to define a style for your project? …
❯ Use a popular style guide
Answer questions about your style
それぞれ以下のようになります
- Use a popular style guide(人気のスタイルガイドを使用)
- Answer questions about your style(自分で選択する)
有名なスタイルガイドを利用する場合は1、完全に1からカスタムする場合は2.
ですね。
私はUse a popular style guide
を選択
利用するスタイルガイド
? Which style guide do you want to follow? …
❯ Standard: https://github.com/standard/eslint-config-standard-with-typescript
XO: https://github.com/xojs/eslint-config-xo-typescript
なぬ・・以前あったAirbnbがない💦
XOはzero-configで始められるESLintのラッパーとして開発されたライブラリなので、
XOのいい感じのESLintの設定が導入されるという予想。
個人的に馴染みのあるAirbnbを入れたいので、
ここではStandard
を選択、後でAirbnbを入れる事にします
設定ファイルのフォーマット
? What format do you want your config file to be in? …
❯ JavaScript
YAML
JSON
ESLintの設定ファイル(eslintrc)はJSONファイルで紹介されてる事が多いイメージ、
かつJSON形式とほぼ同じように書けてコメントが出来るJavaScript
を選択
.eslintrc.cjs
が作成されます
.eslintrc.cjs
を馴染みある.eslintrc.js
にしてeslintを実行するとエラーが起きます
$ npx eslint . --ext .js,.jsx,.ts,.tsx
Oops! Something went wrong! :(
ESLint: 8.36.0
Error [ERR_REQUIRE_ESM]:
require() of ES Module /Users/yuu/src/github.com/yuki-takara/react-vite-sample/.eslintrc.js
from /Users/yuu/src/github.com/yuki-takara/react-vite-sample/node_modules/@eslint/eslintrc/dist/eslintrc.cjs not supported.
.eslintrc.js is treated as an ES module file as
it is a .js file whose nearest parent package.json
contains "type": "module" which declares all .js files in that package scope as ES modules.
Instead rename .eslintrc.js to end in .cjs,
change the requiring code to use dynamic import() which is available in all CommonJS modules,
or change "type": "module" to "type": "commonjs" in /Users/yuu/src/github.com/yuki-takara/react-vite-sample/package.json
to treat all .js files as CommonJS (using .mjs for all ES modules instead).
なるほど、.cjsである必要があるんですね
インストールされるパッケージの確認
eslint-plugin-react@latest eslint-config-standard-with-typescript@latest @typescript-eslint/eslint-plugin@^5.0.0 eslint@^8.0.1 eslint-plugin-import@^2.25.2 eslint-plugin-n@^15.0.0 eslint-plugin-promise@^6.0.0 typescript@*
? Would you like to install them now? › No / Yes
? Which package manager do you want to use? …
npm
❯ yarn
pnpm
ここまでの選択でインストールパッケージの一覧が表示されるのでYES
を選択
更にその後、そのインストールを行うパッケージマネージャーを選択出来ます。
私はyarn
を使っているのでyarn
を選択。
導入したパッケージ、.eslintrc.cjsの確認
インストールされたESLint関連のパッケージは以下
{
"name": "vite-project",
・・・
"devDependencies": {
・・・
"@typescript-eslint/eslint-plugin": "^5.0.0",
"eslint": "^8.0.1",
"eslint-config-standard-with-typescript": "^34.0.0",
"eslint-plugin-import": "^2.25.2",
"eslint-plugin-n": "^15.0.0",
"eslint-plugin-promise": "^6.0.0",
"eslint-plugin-react": "^7.32.2",
・・・
}
}
.eslintrc.cjsはこのようになりました。
module.exports = {
env: {
browser: true,
es2021: true,
},
extends: ['plugin:react/recommended', 'standard-with-typescript'],
overrides: [],
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
},
plugins: ['react'],
rules: {},
}
eslint-config-airbnb 導入
当初入れたかったAirbnbのESLint設定を入れていきます。
AirbnbのESLint設定公式ページに必要なパッケージが書かれていて
Our default export contains most of our ESLint rules, including ECMAScript 6+ and React. It requires eslint, eslint-plugin-import, eslint-plugin-react, eslint-plugin-react-hooks, and eslint-plugin-jsx-a11y.
- eslint-plugin-react-hooks
- eslint-plugin-jsx-a11y
の2つがこの段階だと足りていないので、合わせて追加します。
$ yarn add -D eslint-config-airbnb eslint-plugin-react-hooks eslint-plugin-jsx-a11y
もし必要なパッケージの導入を忘れても
eslint
実行した際に警告メッセージが表示されるので気付く事が出来ます
$ npx eslint . --ext .js,.jsx,.ts,.tsx
Oops! Something went wrong! :(
ESLint: 8.36.0
ESLint couldn't find the plugin "eslint-plugin-react-hooks".
合わせて.eslintrc.cjsを更新
// 変更前
extends: ['plugin:react/recommended', 'standard-with-typescript'],
// 変更後
extends: ['plugin:react/recommended', 'airbnb', 'airbnb/hooks'],
使わなくなったeslint-config-standard-with-typescript
は削除します
$ yarn remove eslint-config-standard-with-typescript
eslintコマンドを実行出来る事が確認できれば、eslint-config-airbnb導入成功です!
(この段階だといっぱいエラーが起きます)$ npx eslint . --ext .js,.jsx,.ts,.tsx
/Users/yuu/src/github.com/yuki-takara/react-vite-sample/src/App.tsx
9:5 error 'React' must be in scope when using JSX react/react-in-jsx-scope
9:5 error JSX not allowed in files with extension '. react/jsx-filename-extension
・・・
eslint-config-airbnb-typesciprt 導入
eslint-config-airbnb-typescript - npm
Airbnbの設定をTypeScriptにも適用するためのものです!
必要なパッケージをインストール
$ yarn add -D eslint-config-airbnb-typescript @typescript-eslint/parser
.eslintrc.cjsを変更します、2箇所あります
// 変更前
extends: ['plugin:react/recommended', 'airbnb', 'airbnb/hooks'],
// 変更後
extends: ['plugin:react/recommended', 'airbnb', 'airbnb-typescript', 'airbnb/hooks'],
tsconfigを読み込むようにします
これによって読み込んだtsconfigの設定に準拠してくれるようです
// 変更前
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module'
},
// 変更後
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
project: './tsconfig.json',
},
eslint-config-prettier 導入
ここまでの設定のみだと、Prettierの設定とESLintの設定がバッティングしている場合、
- Prettierを実行 → ESLintにPrettierの自動修正部分を怒られる → 手動で直す → Prettierを実行 → ESLintにPrettierの自動修正部分を怒られる ・・
という無限ループが起きます🫠
これを回避するために「Prettierの設定はESLintで警告しない」という設定を追加します。
パッケージをインストール
$ yarn add -D eslint-config-prettier
.eslintrc.cjsを変更
// 変更前
extends: ['plugin:react/recommended', 'airbnb', 'airbnb-typescript', 'airbnb/hooks'],
// 変更後
extends: ['plugin:react/recommended', 'airbnb', 'airbnb-typescript', 'airbnb/hooks', 'prettier'],
これで導入前、
例えばPrettierの設定でセミコロンを文末に表示しない設定をし
{
"semi": false,
・・・
}
ESLintを実行すると以下のように怒られていたのが
$ npx eslint . --ext .js,.jsx,.ts,.tsx
1:26 error Missing semicolon @typescript-eslint/semi
・・・
怒られなくなります 🙌
eslint-plugin-unicorn 導入
More than 100 powerful ESLint rules
と書かれているように、ESLintをより強力にするルール集です!
かなり色々なルールが追加され、Lintに対応する時間が大変になる可能性もあるので、
速度を求められるようなプロジェクトは入れない判断をするのもあり、だと思います。
パッケージのインストール
$ yarn add -D eslint-plugin-unicorn
.eslintrc.cjsを変更
// 変更前
extends: ['plugin:react/recommended', 'airbnb', 'airbnb-typescript', 'airbnb/hooks', 'prettier'],
// 変更後
extends: [
'plugin:react/recommended',
'airbnb',
'airbnb-typescript',
'airbnb/hooks',
'plugin:unicorn/recommended',
'prettier',
],
ESLintを実行すると
$ npx eslint . --ext .js,.jsx,.ts,.tsx
6:30 error Prefer `.querySelector()` over `.getElementById()` unicorn/prefer-query-selector
のように unicorn/
で始まるメッセージが表示されるようになっていれば成功です!
ここまでで.eslint.cjsは以下のようになっています
module.exports = {
env: {
browser: true,
es2021: true,
},
extends: [
'plugin:react/recommended',
'airbnb',
'airbnb-typescript',
'airbnb/hooks',
'plugin:unicorn/recommended',
'prettier',
],
overrides: [],
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
project: './tsconfig.json',
},
plugins: ['react'],
rules: {},
}
npm-scriptのコマンド追加
npm-scriptsで実行出来るようにしておきます。
"lint": "eslint . --ext .js,.jsx,.ts,.tsx"
をpackage.jsonに追加
{
・・・
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview",
"lint": "eslint . --ext .js,.jsx,.ts,.tsx"
},
・・・
}
これで
$ yarn run lint
でESLintが実行出来るようになります
ESLintの修正(ruleの設定)
エラー内容を修正していきます。
本題とは若干それるので、パパっと書いていきます
unicorn/filename-case
eslint-plugin-unicorn/filename-case.md at main · sindresorhus/eslint-plugin-unicorn
Filename is not in kebab case. Rename it to app.tsx
App.tsxというファイル名をケバブケースにしよう、というエラーです。
Reactの場合、
キャメルケース・パスカルケースでファイル名を記載する事が多いため以下のルールを追加します
rules: {
'unicorn/filename-case': [
'error',
{
cases: {
kebabCase: true,
pascalCase: true,
},
},
],
},
react/react-in-jsx-scope
eslint-plugin-react/react-in-jsx-scope.md at master · jsx-eslint/eslint-plugin-react
'React' must be in scope when using JSX
JSXのファイルでimport React from 'react';
を記載しないとこちらのエラーが起きます。
しかし React v17以降はこの記載をしないても問題なくなったためこのルールは無視します
rules: {
・・・
'react/react-in-jsx-scope': 'off',
},
react/jsx-no-target-blank
eslint-plugin-react/jsx-no-target-blank.md at master · jsx-eslint/eslint-plugin-react
Using target="_blank" without rel="noreferrer" (which implies rel="noopener") is a security risk in older browsers: see https://mathiasbynens.github.io/rel-noopener/#recommendations
長い文章ですが、要は「target=_"blank"
を設定しているリンクは rel="noreferrer"
を設定していないとセキュリティ上の問題があるよ」という内容です。
そのため素直にaタグの修正します
// 修正前
<a href="https://vitejs.dev" target="_blank">
</a>
// 修正後
<a href="https://vitejs.dev" target="_blank" rel="noreferrer">
</a>
react/button-has-type
eslint-plugin-react/button-has-type.md at master · jsx-eslint/eslint-plugin-react
Missing an explicit type attribute for button
ボタンには必ずtypeを設定しよう、というエラーです。
デフォルトのtype=“button”を設定します
// 修正前
<button onClick={() => setCount((count) => count + 1)}>count is {count}</button>
// 修正後
<button onClick={() => setCount((count) => count + 1)} type="button">
count is {count}
</button>
@typescript-eslint/no-shadow
typescript-eslint/no-shadow.md at main · typescript-eslint/typescript-eslint
'count' is already declared in the upper scope on line 6 column 10
6行目で既にcount
という変数を使用しているのに、useStateのsetCount()
内で同じ変数名を使用してエラーになっています
どちらかの変数名を変更すれば解決ですね、今回はsetCount内の変数名を変更します
// 修正前
<button onClick={() => setCount((count) => count + 1)} type="button">
count is {count}
</button>
// 修正後
<button onClick={() => setCount((previousCount) => previousCount + 1)} type="button">
count is {count}
</button>
unicorn/prefer-query-selector
eslint-plugin-unicorn/prefer-query-selector.md at main · sindresorhus/eslint-plugin-unicorn
Prefer.querySelector() over .getElementById()
getElementById
やgetElementsByClassName
は使わずにquerySelector
を使おう、というエラーです
// 修正前
ReactDOM.createRoot(document.getElementById('root') as HTMLElement)
// 修正後
ReactDOM.createRoot(document.querySelector('#root') as HTMLElement)
unicorn/prevent-abbreviations
eslint-plugin-unicorn/prevent-abbreviations.md at main · sindresorhus/eslint-plugin-unicorn
The filename vite-env.d.ts should be named vite-environment.d.ts. A more descriptive name will do too
envやprev、といった短縮された変数やファイル名は使わずに、environmentやpreviousと正式名を記載しよう、というエラーです。
なるほどなぁ、と思う内容なものの、とはいえ.envなど普段から一般的に使っているファイル名でもあるため env という単語は使えていい気がする 🤔
ここではESLintのルールを設定する事にします。
allowList
に設定した単語はエラーにならなくなります
rules: {
・・・
'unicorn/prevent-abbreviations': [
'error',
{
allowList: {
env: true,
},
},
],
},
vite.config.tsのParsing error
Parsing error: ESLint was configured to run on `<tsconfigRootDir>/vite.config.ts`
using `parserOptions.project`: /users/yuu/src/github.com/yuki-takara/react-vite-sample/tsconfig.json
However, that TSConfig does not include this file. Either:
- Change ESLint's list of included files to not include this file
- Change that TSConfig to include this file
- Create a new TSConfig that includes this file and include it in your parserOptions.project
ESLintが<tsconfigRootDir>/vite.config.tsというファイルを使用して構成されているが、対応するTSConfigにそのファイルが含まれていないため、エラーが発生しています。
vite.config.tsを対象から外せばいいのでESLintの ignorePatterns を用いて除外します
ignorePatterns: ['vite.config.ts'],
これでオールグリーンになりました!
$ yarn run lint
yarn run v1.22.19
$ eslint . --ext .js,.jsx,.ts,.tsx
✨ Done in 1.56s.
最終的な状態
主要ファイルはそれぞれ最終的に以下のようになりました!
package.json
{
"name": "vite-project",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview",
"lint": "eslint . --ext .js,.jsx,.ts,.tsx"
},
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
"@types/react": "^18.0.27",
"@types/react-dom": "^18.0.10",
"@typescript-eslint/eslint-plugin": "^5.55.0",
"@typescript-eslint/parser": "^5.55.0",
"@vitejs/plugin-react-swc": "^3.0.0",
"eslint": "^8.0.1",
"eslint-config-airbnb": "^19.0.4",
"eslint-config-airbnb-typescript": "^17.0.0",
"eslint-config-prettier": "^8.7.0",
"eslint-plugin-import": "^2.25.2",
"eslint-plugin-jsx-a11y": "^6.7.1",
"eslint-plugin-n": "^15.0.0",
"eslint-plugin-promise": "^6.0.0",
"eslint-plugin-react": "^7.32.2",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-unicorn": "^46.0.0",
"prettier": "^2.8.4",
"typescript": "*",
"vite": "^4.1.0"
}
}
.eslintrc.cjs
module.exports = {
env: {
browser: true,
es2021: true,
},
extends: [
'plugin:react/recommended',
'airbnb',
'airbnb-typescript',
'airbnb/hooks',
'plugin:unicorn/recommended',
'prettier',
],
overrides: [],
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
project: './tsconfig.json',
},
plugins: ['react'],
rules: {
'unicorn/filename-case': [
'error',
{
cases: {
kebabCase: true,
pascalCase: true,
},
},
],
'react/react-in-jsx-scope': 'off',
'unicorn/prevent-abbreviations': [
'error',
{
allowList: {
env: true,
},
},
],
},
ignorePatterns: ['vite.config.ts'],
};
- React 製アプリケーションのビルドシステムを webpack から Vite に移行して爆速な開発体験を手に入れよう – PSYENCE:MEDIA
- Viteで作成したReact(TypeScript)プロジェクトにEsLintとPrettierを導入する(2022/02)
- ESLintと「eslint —init」による「.eslintrc.json」の生成 - このすみノート
- create-exposed-app/.eslintrc.js at master · iamturns/create-exposed-app
- JavaScriptのコード品質をさらに高めるeslint-plugin-unicornのススメ - Qiita