ロゴテキスト ロゴ

    Vite + React + SWC + ESLintの環境を整える

    Vite + React + SWC + ESLintの環境を整える

    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を選択

    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

    プラグインの詳細については公式ページに

    プラグイン | Vite



    プラグインの違いを比較すると

    @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環境が起動します。

    yarn dev実行後、ローカルホストが立ち上がった事が分かる


    http://localhost:5173/ にアクセスすると

    ローカルホストにアクセスするとViteのReact環境が立ち上がる

    無事Vite+Reactでアプリケーションが立ち上がりました!🎉


    Prettier導入

    Prettierを導入します

    $ yarn add -D prettier

    設定ファイルを追加 (設定内容はお好みで)

    .prettierrc
    {
      "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

    それぞれ以下のようになります

    1. Use a popular style guide(人気のスタイルガイドを使用)
    2. 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関連のパッケージは以下

    package.json
    {
      "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はこのようになりました。

    .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 導入

    eslint-config-airbnb - npm


    当初入れたかった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を更新

    .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箇所あります

    .eslintrc.cjs
    // 変更前
    extends: ['plugin:react/recommended', 'airbnb', 'airbnb/hooks'],
     
    // 変更後
    extends: ['plugin:react/recommended', 'airbnb', 'airbnb-typescript', 'airbnb/hooks'],


    tsconfigを読み込むようにします

    これによって読み込んだtsconfigの設定に準拠してくれるようです

    .eslintrc.cjs
    // 変更前
    parserOptions: {
      ecmaVersion: 'latest',
      sourceType: 'module'
    },
     
    // 変更後
    parserOptions: {
      ecmaVersion: 'latest',
      sourceType: 'module',
      project: './tsconfig.json',
    },


    eslint-config-prettier 導入

    eslint-config-prettier - npm


    ここまでの設定のみだと、Prettierの設定とESLintの設定がバッティングしている場合、

    • Prettierを実行 → ESLintにPrettierの自動修正部分を怒られる → 手動で直す → Prettierを実行 → ESLintにPrettierの自動修正部分を怒られる ・・

    という無限ループが起きます🫠



    これを回避するために「Prettierの設定はESLintで警告しない」という設定を追加します。


    パッケージをインストール

    $ yarn add -D eslint-config-prettier


    .eslintrc.cjsを変更

    .eslintrc.cjs
    // 変更前
    extends: ['plugin:react/recommended', 'airbnb', 'airbnb-typescript', 'airbnb/hooks'],
     
    // 変更後
    extends: ['plugin:react/recommended', 'airbnb', 'airbnb-typescript', 'airbnb/hooks', 'prettier'],


    これで導入前、

    例えばPrettierの設定でセミコロンを文末に表示しない設定をし

    .prettierrc
    {
      "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
    3.9k
    353
    JavaScript


    公式ページ

    More than 100 powerful ESLint rules

    と書かれているように、ESLintをより強力にするルール集です!


    かなり色々なルールが追加され、Lintに対応する時間が大変になる可能性もあるので、

    速度を求められるようなプロジェクトは入れない判断をするのもあり、だと思います。




    パッケージのインストール

    $ yarn add -D eslint-plugin-unicorn

    .eslintrc.cjsを変更

    .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は以下のようになっています

    .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に追加

    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の場合、

    キャメルケース・パスカルケースでファイル名を記載する事が多いため以下のルールを追加します

    .eslintrc.cjs
    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以降はこの記載をしないても問題なくなったためこのルールは無視します

    .eslintrc.cjs
    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()

    getElementByIdgetElementsByClassNameは使わずに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に設定した単語はエラーにならなくなります

    .eslintrc.cjs
    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 を用いて除外します

    .eslintrc.cjs
    ignorePatterns: ['vite.config.ts'],


    これでオールグリーンになりました!

    $ yarn run lint
    yarn run v1.22.19
    $ eslint . --ext .js,.jsx,.ts,.tsx
      Done in 1.56s.

    最終的な状態

    主要ファイルはそれぞれ最終的に以下のようになりました!


    package.json

    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

    .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'],
    };
    プロフィールの背景画像 プロフィール画像
    Yuki Takara
    都内でフリーランスのエンジニアをやってます。フロントとアプリ開発メインに幅広くやってます。