StorybookのビルダーをwebpackからViteに変えた記録（Emotionも一緒）

## この記事の概要

Storybookを使っているプロジェクトがあるのですが、ビルダーをwebpackからViteに乗り換えたかったので調べてみました。
React + Emotionの環境です。

## 環境

| モジュール | バージョン |
| --- | --- |
| @emotion/react | 11.9.0 |
| @storybook/react | 6.5.5 |
| react | 18.1.0 |
| typescript | 4.7.2 |
| vite（今回追加） | 4.0.0 |


## やったこと

まずは以下のドキュメントの通りに進めました。

https://storybook.js.org/docs/react/builders/vite

必要な依存関係をインストールします。
`@storybook/builder-vite`だけでは駄目で、自分でViteを入れる必要があります。

```shell
yarn add --dev vite @storybook/builder-vite
```

`.storybook/main.js`を更新します。
Emotionを使っている関係でbabelの設定も入っています。

```diff_javascript:.storybook/main.js
  module.exports = {
    stories: ['../src'],
    addons: ['@storybook/addon-links', '@storybook/addon-essentials'],
+   core: {
+     builder: '@storybook/builder-vite',
+   },
    framework: '@storybook/react',
    babel: async (options) => ({
      ...options,
      presets: [...options.presets, '@emotion/babel-preset-css-prop'],
    }),
  }
```

一度コマンドを叩いてみました。

```shell
yarn storybook
```

エラーが出ています。
`.storybook/preview.js`の中でJSXの構文を使っているため、拡張子を`.jsx`にするように言われています。

```shell
Failed to parse source for import analysis because the content contains invalid JS syntax. If you are using JSX, make sure to name the file with the .jsx or .tsx extension.
[vite] Internal server error: Failed to parse source for import analysis because the content contains invalid JS syntax. If you are using JSX, make sure to name the file with the .jsx or .tsx extension.
```

変更して再度Storybookを立ち上げました。

すると今度は立ち上がったものの、スタイルが当たっていません。
jsx pragmaを忘れたときと同じエラーが出ています。

```html:エラー内容（改行して見やすくしたもの）
<div css="
  You have tried to stringify object returned from `css` function.
  It isn't supposed to be used directly (e.g. as value of the `className` prop), but rather handed to emotion so it can handle it (e.g. as value of `css` prop).
"></div>
```

色々調べているうちに以下のIssueにたどり着きました。

https://github.com/storybookjs/builder-vite/issues/210

自分の環境だと`@emotion/babel-plugin`ではなく、まだ`@emotion/babel-preset-css-prop`を使っていたのでそれを変更しています。
あと[`exclude`にはデフォルトでnode_modulesが指定されている](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md#filter-which-files-use-fast-refresh)のもあり、若干怪しいです。
一応現段階だと、この指定を消しても動いているように見えています。

```diff_javascript
  const path = require('path')
+ const react = require('@vitejs/plugin-react')

  module.exports = {
    stories: ['../src'],
    addons: ['@storybook/addon-links', '@storybook/addon-essentials'],
    core: {
      builder: '@storybook/builder-vite',
    },
    framework: '@storybook/react',
    babel: async (options) => ({
      ...options,
      presets: [...options.presets, '@emotion/babel-preset-css-prop'],
    }),
+   async viteFinal(config) {
+     config.plugins = config.plugins.filter(
+       (plugin) =>
+         !(Array.isArray(plugin) && plugin[0]?.name.includes('vite:react')),
+     )
+     config.plugins.push(
+       react({
+         exclude: [/\.stories\.(t|j)sx?$/, /node_modules/],
+         jsxImportSource: '@emotion/react',
+         babel: {
+           presets: ['@emotion/babel-preset-css-prop'],
+         },
+       }),
+     )
+     return config
+   },
  }
```

上記の設定で無事に動いたので、不要な設定を消します。

```diff_javascript
  const path = require('path')
  const react = require('@vitejs/plugin-react')

  module.exports = {
    stories: ['../src'],
    addons: ['@storybook/addon-links', '@storybook/addon-essentials'],
    core: {
      builder: '@storybook/builder-vite',
    },
    framework: '@storybook/react',
-   babel: async (options) => ({
-     ...options,
-     presets: [...options.presets, '@emotion/babel-preset-css-prop'],
-   }),
    async viteFinal(config) {
      config.plugins = config.plugins.filter(
        (plugin) =>
          !(Array.isArray(plugin) && plugin[0]?.name.includes('vite:react')),
      )
      config.plugins.push(
        react({
          exclude: [/\.stories\.(t|j)sx?$/, /node_modules/],
          jsxImportSource: '@emotion/react',
          babel: {
            presets: ['@emotion/babel-preset-css-prop'],
          },
        }),
      )
      return config
    },
  }
```

## 最後に

Storybook自体というより、Emotionの設定に苦戦した感があります。
しかしこれでスピードも上がるしモダンな設定にもなるしで良かったです。

Storybookを使っているプロジェクトは多いと思うので、マイグレーションの参考になれば幸いです。

---

最後まで読んでくださってありがとうございます！
Twitterでも情報を発信しているので、良かったらフォローお願いします！

https://twitter.com/xrxoxcxox

Devトークでのお話してくださる方も募集中です！

https://jobs.qiita.com/employers/qiita-inc/dev_talks/489