Create React App(TypeScript) + SWC + MUI v5 の環境構築


はじめに

Create React App(TypeScript) + SWC + MUI v5 のSPA環境を構築したフローを記事として記述しています。

利用フレームワーク

本記事で利用するフレームワークの公式サイトを以下に記載します。

  • Create React App
    • React アプリケーションの開発環境構築
  • SWC
    • Jestのトランスパイルを高速にしてくれるツール
  • MUI
    • React UI Library

対象読者

ローカル開発環境でnode.js versionを指定して開発環境を構築できる方向けの記事になります。

利用技術

執筆者のローカル開発環境の各パッケージのバージョンは以下です。

  • Mac: 10.15.7
  • nodevnv: 1.4.0
  • node: 14.17.0
  • npm: 8.5.2
  • yarn: 1.22.10

ローカル開発環境の準備

ローカル開発環境のnode versionを 14系にしました。
14系にした理由として、執筆者がこのアプリケーションをホスティングする環境を vercel or Firebse Hosting の利用を予定していたため、各サービスのドキュメントに記載されたランタイムバージョンを合わせました。

参考文献

ローカル開発環境のnode versionを 14系にする

$ node -v
14.17.0
$ npm -v
8.5.2
$ yarn -v
1.22.10

各開発環境のパッケージのバージョンが確認できたので実際にアプリケーションのパッケージをインストールしていこうと思います。

Create React App(Typescript) のインストール

適当な作業ディレクトにて以下コマンドを実行します。

参考文献

$ yarn create react-app project-name --template typescript
インストール実行
Happy hacking!
✨  Done in xx.xxs.

$ cd project-name && yarn start

React App

ブラウザのhttp://localhost:3000でReactアプリケーションが立ち上がればインストールは完了です。

Lintの設定

つぎにLintの設定をしたいと思います。

$ npx eslint --init

? How would you like to use ESLint? …
❯ To check syntax and find problems

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

? Which framework does your project use? …
❯ React

? Does your project use TypeScript?
Yes

? Where does your code run? …
✔ Browser
✔ Node

? What format do you want your config file to be in? …
❯ JavaScript # ここはお好みでいいと思います

? Would you like to install them now with npm?
Yes

.eslintrc.jsがプロジェクトディレクトリのルートに出力されます。

.eslintrc.js
module.exports = {
    "env": {
        "browser": true,
        "es2021": true,
        "node": true
    },
    "extends": [
        "eslint:recommended",
        "plugin:react/recommended",
        "plugin:@typescript-eslint/recommended"
    ],
    "parser": "@typescript-eslint/parser",
    "parserOptions": {
        "ecmaFeatures": {
            "jsx": true
        },
        "ecmaVersion": "latest",
        "sourceType": "module"
    },
    "plugins": [
        "react",
        "@typescript-eslint"
    ],
    "rules": {
    }
}

Prettier のインストール

$ yarn add -D prettier eslint-config-prettier

prettierの設定ファイルを作成します。

$ touch prettier.config.js

設定内容はプロジェクト等によってお好みに記述してみてください。

prettier.config.js
module.exports = {
  printWidth: 80,
  tabWidth: 2,
  singleQuote: true,
  trailingComma: 'none',
  semi: false,
  parser: 'typescript'
}

.eslintrc.js の更新します。

.eslintrc.js
module.exports = {
	...
  extends: [
    'eslint:recommended',
    'plugin:react/recommended',
    'plugin:@typescript-eslint/recommended',
    'prettier'  // <追記
  ],
	...
};

eslintのプラグインをインストール

  • eslint-import-resolver-typescript
  • eslint-plugin-react-hooks
$ yarn add -D eslint-import-resolver-typescript eslint-plugin-react-hooks
.eslintrc.js
module.exports = {
  ...
  // settingsに追記
  settings: {
    'import/resolver': {
      typescript: {
        project: './tsconfig.json'
      }
    }
  },
  plugins: [
    'react',
    '@typescript-eslint',
    'react-hooks' // 追記
  ],
  ...
};

ルールの設定をします。

.eslintrc.js
module.exports = {
...
// ルールの設定これはプロジェクト等で話し合って設定がいいと思います
rules: {
    // Enable
    quotes: ['warn', 'single'],
    'no-extra-semi': 'warn',
    '@typescript-eslint/no-unused-vars': ['error'],
    'react-hooks/rules-of-hooks': 'error',
    'react-hooks/exhaustive-deps': 'warn',

    // Disable
    'no-unused-vars': 'off',
    'react/react-in-jsx-scope': 'off',
    '@typescript-eslint/no-empty-function': 'off'
  }
...
};

package.jsonの修正

package.json
{
  ...
    "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "lint:eslint": "eslint --fix src/**/*.{ts,tsx}", // < 追記
    "eject": "react-scripts eject" // 削除
  },
  ...
}

動作確認を以下で実行してみます。

$ yarn lint:eslint

@swc/jestのインストール

SWC is an extensible Rust-based platform for the next generation of fast developer tools. It's used by tools like Next.js, Parcel, and Deno, as well as companies like Vercel, ByteDance, Tencent, Shopify, and more.

SWCはSpeedy Web Compilerの略称で、Jestの実行速度をあげてくれるトランスパイラーを利用してみます。

$ yarn test

PASS  src/App.test.tsx
  ✓ renders learn react link (30 ms)

SWC関連パッケージのインストールします。

$ yarn add -D @swc/core @swc/jest 

SVG ファイル利用と、jest-dom関連のパッケージもインストールしておきます。

$ yarn add -D jest-svg-transformer @testing-library/jest-dom

Jestの設定ファイルを定義

$ yarn jest --init

? Would you like to use Jest when running "test" script in "package.json"?
no

? Would you like to use Typescript for the configuration file?
no

? Choose the test environment that will be used for testing
jsdom (browser-like)

? Do you want Jest to add coverage reports?
yes

? Which provider should be used to instrument code for coverage?
babel

? Automatically clear mock calls, instances and results before every test?
yes

jest.config.js がプロジェクトディレクトリのルートに出力されます。

jest.config.js
module.exports = {
  clearMocks: true,
  collectCoverage: true,
  coverageDirectory: 'coverage',
  moduleNameMapper: {
    // Aliasの設定
    '^@/(.+)': '<rootDir>/src/$1',
    // Jest で svgファイルを扱えるようにする
    '^.+\\.svg$': 'jest-svg-transformer'
  },
  roots: ['.'],
  testEnvironment: 'jsdom',
  testMatch: ['**/*.test.js', '**/*.test.ts', '**/*.test.tsx'],
  testPathIgnorePatterns: ['/node_modules/'],
  transform: {
    '.+\\.(t|j)sx?$': [
      '@swc/jest',
      {
        sourceMaps: true, 
        module: {
          type: 'commonjs'
        },
        jsc: {
          parser: {
            syntax: 'typescript', 
            tsx: true
          },
          transform: {
            react: {
              runtime: 'automatic'
            }
          }
        }
      }
    ]
  },
  transformIgnorePatterns: ['/node_modules/']
}

Create React Appを rewired する

react-app-rewiredcustomize-craパッケージを利用してちょっと上書きます。

$ yarn add -D react-app-rewired customize-cra
$ touch config-overrides.js
config-overrides.js
const {
  override,
  addWebpackResolve
} = require('customize-cra')
const path = require('path')

module.exports = override(
  addWebpackResolve({
    alias: {
      '@': path.resolve(__dirname, './src/')
    }
  })
)

package.jsonを修正してreact-app-rewiredで実行するように修正します。

package.json
{
  ...
  "scripts": {
    "start": "react-app-rewired start",
    "build": "react-app-rewired build",
    "test": "jest",
    "test:watch": "jest --watch",
    "test:coverage": "jest test --coverage",
    "lint:eslint": "eslint --fix src/**/*.{ts,tsx}"
  },
  ...
}

ちょっとReactアプリ側を軽い感じにしておきます。

App.tsx
import logo from './logo.svg'

function App() {
  return (
    <div>
      <img src={logo} className="App-logo" alt="logo" />
    </div>
  )
}

export default App

App.test.tsx
import { render } from '@testing-library/react'

import App from './App'

describe('App Component', () => {
  test('renders main page correctly', async () => {
    render(<App />)
  })
})

jestを動かしてみます。

$ yarn test

 PASS  src/App.test.tsx
  App Component
    ✓ renders main page correctly (46 ms)

----------|---------|----------|---------|---------|-------------------
File      | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
----------|---------|----------|---------|---------|-------------------
All files |     100 |      100 |     100 |     100 |                   
 App.tsx  |     100 |      100 |     100 |     100 |                   
----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        3.252 s
Ran all test suites.
✨  Done in 5.62s.

小ネタ config-overrides.js がeslintで怒られているので、.eslintignoreを作成して読み込まないようにしちゃいました。

touch .eslintignore
.eslintignore
config-overrides.js

MUI v5をインストール

$ yarn add @mui/material @emotion/react @emotion/styled

MUIボタンのスタイルを適用してみる。

App.tsx
import logo from './logo.svg'
import Button from '@mui/material/Button' //テストで追記

function App() {
  return (
    <div>
      <Button variant="contained">Hello World</Button>
      <img src={logo} className="App-logo" alt="logo" />
    </div>
  )
}

export default App

MUIのスタイルがあたるのか確認します。

$ yarn start

ボタンにスタイルがあたっていれば完了です。

Emotionのスタイルを利用する

MUIではEmotionのスタイル適用ができるためその設定もしておく。

yarn add @emotion/css
yarn add -D @babel/preset-react @emotion/babel-plugin
config-overrides.js
const {
  override,
  addWebpackResolve,
  addBabelPresets, // 追記
  addBabelPlugins //  追記
} = require('customize-cra')
const path = require('path')

module.exports = override(
  addBabelPresets(['@babel/preset-react']), //追記
  addBabelPlugins(['@emotion/babel-plugin']),  //追記
  addWebpackResolve({
    alias: {
      '@': path.resolve(__dirname, './src/')
    }
  })
)

一旦ここまでで骨組みはできたと思います。