BunでReact関連の開発環境を構築する
はじめに
この記事では、Bunを用いてReact周辺の環境構築をしていきます。
今回は以下の構成でいきたいと思います。
- react
- typescript
- vite@4.0.3
- vitest
- react testing library
- storybook
Reactを立ち上げるまで
Bunとは
Bunとは、「速くてAll in OneなJavaScriptランタイム」のことです。
Bundler、Transpiler、Package Managerなどが初めから統合されています。
JSエンジンとしてJavaScriptCoreが導入されていたり、denoやnodeと比較してかなり速いそうです。
詳細は公式docからお願いします。
ディレクトリ構成
.
├── .storybook
│ ├── main.js
│ └── preview.js
├── src
├── bun.lockb
├── index.html
├── package.json
├── vite.config.ts
├── vitest.setup.ts
└── tsconfig.json
実際のサンプルコードも載せておきます。
TypeScriptのインストール
package.jsonのdevDependenciesにインストールするには、bunでは-d
のオプションを使用します。
bun add -d typescript
{
"compilerOptions": {
"target": "esnext",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "preserve",
"types": ["vitest/globals"]
},
"include": ["**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"]
}
Viteのインストール
2022年12月にViteのv4系がリリースされて触ってみたいのでViteを利用していきます。
ReactのSWC用に新しくプラグインが導入されていたり、Viteそもそものサイズが小さくなっていたりするようです。
bun add -d vite
Reactのインストール
ReactをSWCプラグイン付きで入れていきます。
bun add react react-dom
bun add -d @types/react @types/react-dom @vitejs/plugin-react-swc
react-swcのプラグインを使えるように設定を書きます。
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react-swc'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()]
})
爆速で立ち上がると思います。
bun run dev
Reactを立ち上げる用初期ファイル
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite App</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/index.tsx"></script>
</body>
</html>
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
function App() {
return (
<div className="App" role="main">
<article className="App-article">
<h3>Welcome to React!</h3>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</article>
</div>
);
}
export default App;
{
...
"scripts": {
"dev": "vite",
"build": "tsc && vite build"
}
}
Test導入
テスト環境として、Vitestを導入してみます。
Vitestのインストール
bun add -d vitest
vitestをグローバルで使えるように、vite.config.tsに記述していきます。
+ /// <reference types="vitest" />
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react-swc'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()],
+ test: {
+ globals: true,
+ },
})
なお、vite@4.0.2では、defineConfigからtestがないと型エラーが発生していました。
4.0.3
に上げることで無事解決されました。
適当にテストファイルを書いて走らせてみます。
export const add = (a: number, b: number): number => a + b;
describe("add", () => {
it("1 + 2 = 3", () => {
const result = add(1, 2);
expect(result).toBe(3);
});
});
{
"scripts": {
+ "test": "vitest",
+ "test:run": "vitest run"
}
}
~/bun-vite-react (main*) » bun run test
$ vitest
DEV v0.26.2 ~/bun-vite-react
✓ src/sample.spec.ts (1)
Test Files 1 passed (1)
Tests 1 passed (1)
Start at 17:20:00
Duration 5.65s (transform 406ms, setup 1.56s, collect 17ms, tests 3ms)
PASS Waiting for file changes...
press h to show help, press q to quit
React Testing Library
React Testing Libraryを用いてUIのテストも書きます。
bun add -d @testing-library/jest-dom @testing-library/react @testing-library/user-event jsdom
export default defineConfig({
plugins: [react()],
test: {
globals: true,
+ environment: 'jsdom',
+ setupFiles: './vitest.setup.ts'
},
})
+ import '@testing-library/jest-dom'
お試しにButtonコンポーネントを作成してテストを書いてみます。
type Props = {
title: string;
onClick: () => void;
};
export const Button = ({ title, onClick }: Props) => (
<button onClick={onClick}>{title}</button>
);
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { Button } from './Button';
describe('Button', () => {
it('render', async () => {
const mockOnClick = vi.fn();
const props = {
onClick: mockOnClick,
title: 'button title',
};
render(<Button {...props} />);
const button = screen.getByRole('button');
await userEvent.click(button);
expect(button).toBeInTheDocument();
expect(button).toHaveTextContent('button title');
expect(mockOnClick).toBeCalled();
});
});
~/bun-vite-react (main*) » bun run test
$ vitest
DEV v0.26.2 ~/bun-vite-react
✓ src/sample.spec.ts (1)
✓ src/atoms/Button.spec.tsx (1)
Test Files 2 passed (2)
Tests 2 passed (2)
Start at 17:33:16
Duration 1.05s (transform 234ms, setup 194ms, collect 142ms, tests 52ms)
PASS Waiting for file changes...
press h to show help, press q to quit
Storybook導入
最後に、Storybookを導入していきます。
ViteでStorybookを動かしたかったのですが、エラー解決に少し手間がかかりそうでした。
一旦はWebpack5系で動かすようにします。
Storybookをインストール
bun add -d @babel/core @storybook/addon-actions @storybook/addon-essentials @storybook/addon-interactions @storybook/addon-links @storybook/builder-webpack5 @storybook/manager-webpack5 @storybook/react @storybook/testing-library
Storybook用の設定を書いていきます
module.exports = {
stories: ["../src/**/*.stories.mdx", "../src/**/*.stories.@(js|jsx|ts|tsx)"],
addons: [
"@storybook/addon-links",
"@storybook/addon-essentials",
"@storybook/addon-interactions",
],
framework: "@storybook/react",
core: {
builder: {
name: "webpack5",
options: {
lazyCompilation: true,
},
},
},
};
お試しにButtonのStoriesを書いてみます
import type { ComponentMeta, ComponentStoryObj } from "@storybook/react";
import { Button } from "./Button";
export default {
title: "Atoms/Button",
component: Button,
args: {
title: "button",
onClick: () => {},
},
} as ComponentMeta<typeof Button>;
export const Default: ComponentStoryObj<typeof Button> = {
args: {
title: "Default ボタン!!",
},
};
export const DifferentText: ComponentStoryObj<typeof Button> = {
args: {
title: "違うテキスト",
},
};
bun run storybook
"scripts": {
+ "storybook": "start-storybook -p 6006",
+ "build-storybook": "build-storybook"
}
おまけ: Viteで頑張って動かしてみる
Storybookをviteで動かすには、vite用のbuilderをインストールする必要があります。
またwebpack関連のライブラリは削除します。
bun add -d @storybook/builder-vite
bun remove @storybook/builder-webpack5 @storybook/manager-webpack5
module.exports = {
stories: ["../src/**/*.stories.mdx", "../src/**/*.stories.@(js|jsx|ts|tsx)"],
framework: "@storybook/react",
addons: ["@storybook/addon-links", "@storybook/addon-essentials", "@storybook/addon-interactions"],
core: {
- builder: {
- name: "webpack5",
- options: {
- lazyCompilation: true,
- },
- },
+ builder: '@storybook/builder-vite'
},
};
ここで走らせてみると、以下のエラーが発生しました。
TypeError: Cannot read properties of undefined (reading 'createSnapshot')
まだissueがオープン状態ですが、以下の状態と類似と思われます。
以下のリリースから、v7系のαないしはβであれば、動かせるかもしれないということで、現時点最新であるbeta.15を入れていきます。
Storybookの7系からChangesがいくつか入っているので、それの対応も行います。
まず、start-storybook
やbuild-storybook
が廃止され、Storybook`s ClIに置き換わっています。
さらには、main.js
のframework
が必須となり、指定のフレームワークを入れる必要があります。
@storybook/react
ではなく、今回は@storybook/react-vite
を指定します。
ここで実行してみると次のエラーが発生しました。
[vite] Internal server error: Failed to resolve import "@storybook/preview-web" from "../../../../../../virtual:/@storybook/builder-vite/vite-app.js". Does the file exist?
ここで一旦お手上げ。
Discussion