storybook導入時にエラーと戦ったので忘備録


この記事について

storybookを導入する時にエラーが出て、なかなか実装が進まなかったため忘備録です。

環境

Next.js: 12.1.0
chakra-ui: 1.8.6
emotion: 11
storybook: 6.4.19

エラー1

chakra-uiで書いたコンポーネントをstorybookに読み込ませたとき、以下のようなエラーが大量に出ていました。

ModuleDependencyError: Can't import the named export 'vh' from non EcmaScript module (only default export is available)
at Compilation.reportDependencyErrorsAndWarnings (/...省略/node_modules/webpack/lib/Compilation.js:1468:21)

エラー2

こちらも同様chakra-uiで書いたコンポーネントをstorybookに読み込ませたとき、エラー自体は出てないもののコンポーネントが表示されないという現象が起きていました。

解決したコード

.storybook/main.js を以下にすると解決しました。

module.exports = {
  "stories": [
    "../stories/**/*.stories.mdx",
    "../stories/**/*.stories.@(js|jsx|ts|tsx)"
  ],
  "addons": [
    "@storybook/addon-links",
    "@storybook/addon-essentials",
  ],
  // NOTE: これ消したらコンポーネント出てこなくなる
  features: {
    emotionAlias: false,
  },
  typescript: {
    reactDocgen: false,
  },
  webpackFinal: async (config) => {
    // NOTE: これ消したらエラーになる
    config.module.rules.push({
      test: /\.mjs$/,
      include: /node_modules/,
      type: "javascript/auto",
    })
    return config
  },
};

エラー1の原因

ES Modulesファイルがうまく読み込めなかったことが原因なのかなと推測します。
nextに限らず、色々なフレームワークで似たようなエラーがでることがあるようで、その場合はwebpackのmoduleのruleを上書きすることでみなさん回避されているようでした。

webpack周りが勉強不足で全然分からないので魔法のコード感が否めない・・

エラー2の原因

storybookの内部で、emotionのバージョンv11の対応がまだ追いついてなくて表示されてなかったようです。
emotionのバージョンをv10に固定すると表示できるため、固定する機能フラグとしてemotionAlias: falseを設定すると良いとのことでした。

WebがスタイリングのためにEmotion11に移行している今、MUI5やChakraUIなどの人気のあるライブラリはemotion@10のみをサポートするStorybook6.3で壊れています。
残念ながら、semverのメジャーリリースなしでStorybookをEmotion 11にアップグレードすることはできず、その準備ができていません。
そのため、回避策として、Emotionバージョンをv10に固定するという以前の動作をオプトアウトする機能フラグを作成しました。
https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#emotion11-quasi-compatibility

おまけ

  • WebpackFinalとは何か
    • Storybookのwebpack構成をオーバーライドできる関数
    • 以下のコードを実行するとwebpackで設定されている細かい情報を得ることができる
## Development mode
yarn start-storybook --debug-webpack

## Production mode
yarn build-storybook --debug-webpack
  • mjsとは何か
    • ES Modulesの拡張子。
    • CommonJSは.cjsになる。
    • ブラウザは拡張子ではなく <script type=module> で判断しているため.mjs ではなくても読み込めるらしい。

参考