🗂

Reactのモダン環境(Typescript+Vite+tailwind+jest+cypress+storybook+etc)

2022/12/20に公開

こだわりの環境を作るまでにやったこと

目標

下記ライブラリーを入れて設定する。

  • React(v18)
  • Typescript
  • Vite
  • eslint, prettier
  • tailwindcss, scss
  • jest, testing-liblary
  • cypress
  • storybook
  • その他、便利そうなもの
    • lodash
    • axios
    • react-icons
    • swr
    • headressUI
    • husky
    • hygen

ベースを持ってきて使いやすくする(React, Typescript, Vite)

今回は、vite のテンプレートを使う

yarn create vite vitest-react-sample --template react-ts

インストールされたのはこんな感じ。

package.json
{
  "name": "vitest-react-sample",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "tsc && vite build",
    "preview": "vite preview"
  },
  "dependencies": {
    "react": "^18.2.0",
    "react-dom": "^18.2.0"
  },
  "devDependencies": {
    "@types/react": "^18.0.24",
    "@types/react-dom": "^18.0.8",
    "@vitejs/plugin-react": "^2.2.0",
    "typescript": "^4.6.4",
    "vite": "^3.2.3"
  }
}
お好みで設定

nodeのバージョンを指定したいので下記を追加

package.json
  "engines": {
    "node": "16.16.0"
  },

vite.config.tsを修正し、.envを読み込めるようにする

vite.config.tsを修正。
build時に吐き出すファイルの場所の変更と、index.htmlでenvを読み込めるように変更。
この設定を追加しとくことで、index.htmlのファイルでENVの中の変数でVITE_から始まるものを使えるようになる。
GA4なんかの設定を書く時に便利だったりするので入れておく。
参考

// @ts-nocheck
import { defineConfig, loadEnv } from 'vite'
import react from '@vitejs/plugin-react'

export default defineConfig(({ mode }) => {
	return {
		plugins: [react(), htmlPlugin(loadEnv(mode, '.'))],
		publicDir: 'public',
		build: {
			outDir: './public/build',
		},
	}
})

/**
 * Replace env variables in index.html
 * @see https://github.com/vitejs/vite/issues/3105#issuecomment-939703781
 * @example `%VITE_MY_ENV%`
 * @see https://vitejs.dev/guide/api-plugin.html#transformindexhtml
 */
function htmlPlugin(env: ReturnType<typeof loadEnv>) {
	return {
		name: 'html-transform',
		transformIndexHtml: {
			enforce: 'pre' as const,
			transform: (html: string): string => html.replace(/%(.*?)%/g, (match, p1) => env[p1] ?? match),
		},
	}
}

.envを追加

VITE_TITLE=Template

index.htmlのtitleタグを変更

index.html
    <!-- <title>Vite + React + TS</title> -->
    <title>%VITE_TITLE%</title>

ここまで確認

yarn install
yarn dev

http://localhost:5173/に接続し、画面が表示されるのを確認

eslint, prettierの設定追加

eslintを入れる

npx eslint --init

対話形式で進むので質問に答えるだけでOK
参考

prettierを入れる

yarn add -D prettier eslint-config-prettier

設定ファイルを変更する

お好みで、.prettiercと.eslintrc.ejsを編集してください。

参考までに自分の設定載せときます

eslint
// eslint-disable-next-line no-undef
module.exports = {
	env: {
		browser: true,
		es2021: true,
	},
	extends: [
		'eslint:recommended',
		'plugin:@typescript-eslint/recommended',
		'plugin:@typescript-eslint/eslint-recommended',
		'plugin:react/recommended',
		'prettier',
	],
	overrides: [],
	parser: '@typescript-eslint/parser',
	parserOptions: {
		ecmaVersion: 'latest',
		sourceType: 'module',
	},
	plugins: ['react', '@typescript-eslint'],
	rules: {
		'react/react-in-jsx-scope': 'off',
		'react/jsx-filename-extension': [1, { extensions: ['.js', '.jsx', '.ts', '.tsx'] }],
	},
}
prettierc
{
	"trailingComma": "all",
	"tabWidth": 4,
	"semi": false,
	"singleQuote": true,
	"jsxSingleQuote": true,
	"useTabs": true,
	"printWidth": 160
}
vscode自動保存設定

保存時自動prettierするためにsettings.jsonに下記追加

{
  "editor.formatOnSave": true,
  "editor.defaultFormatter": "esbenp.prettier-vscode"
}

最後にpackage.jsonを修正しコマンド追加
eslint,prettierが機能するか確認します。
scriptに下記追加

package.json
    "format": "prettier --write --ignore-path .gitignore './**/*.{js,ts,tsx,css}'",
    "eslint": "eslint --ignore-path .gitignore './**/*.{js,ts,tsx,css}'",
    "eslintfix": "eslint --fix --ignore-path .gitignore './**/*.{js,ts,tsx,css}'"

実行

yarn format
yarn eslint

tailwindとscssを入れて設定する

まずはtailwindからインストールしていく

yarn add -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
# classNameを自動で並び替えるプラグイン。もし必要ならば入れる
yarn add -D prettier-plugin-tailwindcss

上記で、インストールと2つのconfigファイルが生成される

設定ファイルの変更

  • tailwind.config.cjsの変更
tailwind.config.cjs
  // content: [],
  content: ['./index.html', './src/**/*.{js,jsx,ts,tsx}'],
  • index.cssの修正
    内容を下記に置き換える
@tailwind base;
@tailwind components;
@tailwind utilities;

ファイルに下記のような一文を追加して、tailwindが機能しているか確認する。

App.tsx
<p className="text-blue-300 bg-red-600">Hello Vite + React!</p>

テスト環境追加

参考

jestのインストール

yarn add -D jest @types/jest ts-jest jest-environment-jsdom

Testing Libraryのインストール

yarn add -D @testing-library/react @testing-library/jest-dom @testing-library/user-event @testing-library/react-hooks

configファイルで設定を書いていく
お好みで書いていただければと思います

参考までに自分の描いたものを載せてときます
  • jest.config.jsを作成する
  • jest.setup.tsを作成する
  • package.jsonに追記する
jest.config.cjs
module.exports = {
	preset: 'ts-jest/presets/js-with-babel-esm',
	testEnvironment: 'jest-environment-jsdom',
	setupFilesAfterEnv: ['./jest.setup.ts'],
	moduleNameMapper: {
		'^@/(.*)$': '<rootDir>/src/$1',
	},
	transformIgnorePatterns: ['node_modules/(?!@mui/material)/'],
}
jest.setup.ts
import '@testing-library/jest-dom'
package.json
    "test": "jest --config ./jest.config.cjs"
導入されてるか確認

下記2ファイルをsrc配下においてテスト成功するかどうか確認する

MyComponent.test.tsx
import { render, screen } from '@testing-library/react'
import { MyComponent } from './MyComponent'

test('「Hello Test」が描画されている', () => {
	render(<MyComponent />)
	screen.debug()
	expect(screen.getByText('Hello Test')).toBeInTheDocument()
})
MyComponent.tsx
export function MyComponent() {
	const title = 'Hello Test'
	return (
		<div>
			<p>{title}</p>
		</div>
	)
}

cypress導入

インストールする

yarn add -D cypress

package.jsonにコマンドを追加して、cy:open を実行。

package.json
    "cy:open": "cypress open",
    "cy:test": "cypress run",

フォルダが作成されて、実行できることが確認できる

作られたcypress.config.tsを書き換え

configファイルはお好みで変更してください。
import { defineConfig } from 'cypress'

export default defineConfig({
	e2e: {
		baseUrl: 'http://localhost:5173',
		supportFile: './cypress/support/e2e.ts',
		defaultCommandTimeout: 20000,
	},
	video: false,
})
動作確認

テストファイルを作成し実行してみる
/cypress/e2e/test.cy.ts

test.cy.ts
/// <reference types="cypress" />
context('起動テスト', () => {
	it('起動テスト', () => {
		cy.visit('/')
	})
})

yarn cy:test を実行

cypressのファイルアップロードするテストがある場合はこの記事のようにライブラリーを入れると良い

storybook導入

ストーリーブックを入れる
コマンド一発で簡単に導入できる

npx sb init

main.jsの書き換えとstorybookをtailwindに対応させる
コマンド実行後 .storybook というフォルダが出来上がっており、その中のmain.cjsとpreview.cjsをお好みに合わせて変えていく

参考
main.cjs
module.exports = {
	stories: ['../src/**/*.stories.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)', '../components/**/*.stories.@(js|jsx|ts|tsx)'],
	addons: ['@storybook/addon-links', '@storybook/addon-essentials', '@storybook/addon-interactions'],
	framework: '@storybook/react',
	core: {
		builder: '@storybook/builder-vite',
	},
	features: {
		storyStoreV7: true,
	},
}
preview.cjs
import '../src/index.scss'

export const parameters = {
	actions: { argTypesRegex: '^on[A-Z].*' },
	controls: {
		matchers: {
			color: /(background|color)$/i,
			date: /Date$/,
		},
	},
}

import '../src/index.scss' この一文でtailwindを読んでいるので注意
この記事に出てくるアドオンがなくても動いているが、必要な場合もあるかもしれない

動作確認

ストーリーの作成
src/components/ にMyComponent.tsx と MyComponent.stories.tsx を作る
動作確認で使うためreact-router-domをインストール

yarn add -D react-router-dom
MyComponent.tsx
export function MyComponent() {
	const title = 'Hello Test'
	return (
		<div>
			<p className='font-bold'>{title}</p>
		</div>
	)
}
MyComponent.stories.tsx
import { type ComponentMeta, type ComponentStoryObj } from '@storybook/react'
import { MyComponent } from './myFunc'
import { MemoryRouter } from 'react-router'
import React from 'react'

type T = typeof MyComponent
type Meta = ComponentMeta<T>
type Story = ComponentStoryObj<T>

export default {
	title: 'component/AnchorButton',
	component: MyComponent,
	args: {},
	decorators: [(Story) => <MemoryRouter>{Story()}</MemoryRouter>],
} as Meta

export const Default: Story = {}

yarn storybook を実行、localhost:6006に繋いで確認

その他必要そうなものも入れとく

yarn add -D lodash axios swr @headlessui/react husky hygen
yarn add -D react-router-dom  #これはストーリーブックのとこでも描いたので外してますが一緒にinstallしてOKです

便利ツールである、hygen や husky についてはそのうち記事を作成する予定

エイリアスの設定
ts,vite,storybook,jestにエイリアスを追加する方法をまとめた記事

Discussion