技術メモ

プログラミングとか電子工作とか

Next.js+TypeScript+CSS Modulesの環境でStorybookを使う

去年の10月に同じような記事を書いた気がするんですが、もううまく動かなくなってました。
元々フロントは専門外なのでちょくちょくしか触らないのですが、なかなかに辛い・・・
と思ってたのも1日。やはり世界には優秀な方々がたくさんいるようで、既に解決策を提示して下さっております。

Configure Storybook to work with Next.js, TypeScript, and CSS Modules · GitHub

main.jswebpackFinal/\.module\.css$/CSS Modulesのファイル)をstyle-loader+css-loaderで読み込ませようという処理を試みている記事はウェブ上にいくらか見つけていたんですが、何故かうまくいってませんでした。
この記事では一旦普通のCSSファイルをエスケープするために

~
    newConfig.module.rules.find(
      rule => rule.test.toString() === '/\\.css$/'
    ).exclude = /\.module\.css$/;
~

という処理を挟んでいるので、コレが効いてくるのでしょう。
おそらくStorybookのCSSをWebpackのどこかの処理で読み込んでいるのがCSS Modulesと競合してうにゃうにゃなっているんでしょうか。フロントエンド何も分からないマンとしては意味不明です。

上記のgistとその参照元を参考に、CSS Modulesだけ適応したいので、.storybook内にあるファイルは下記のように変更しました。
一応style-loaderとcss-loaderが入ってない場合は入れといて下さい。

yarn add -D style-loader css-loader

まずはmain.js

const path = require('path')

module.exports = {
  stories: ['../src/**/*.stories.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
  addons: ['@storybook/addon-links', '@storybook/addon-essentials'],
  presets: [path.resolve(__dirname, './next-preset.js')],
}

次にnext-preset.jsを作成して、下記の通りにしました。

module.exports = {
  webpackFinal: async (config) => {
    const { module = {} } = config

    const newConfig = {
      ...config,
      module: {
        ...module,
        rules: [...(module.rules || [])],
      },
    }

    newConfig.module.rules.find(
      (rule) => rule.test.toString() === '/\\.css$/'
    ).exclude = /\.module\.css$/

    newConfig.module.rules.push({
      test: /\.module\.css$/,
      use: [
        'style-loader',
        {
          loader: 'css-loader',
          options: {
            importLoaders: 1,
            modules: true,
          },
        },
      ],
    })

    return newConfig
  },
}

preview.jsはとりあえずそのままです。グローバルでCSSを読み込ませたい場合はここで読み込ませるみたいです。
一応例を挙げるなら

import '../src/styles.css';

とか。