React Storybookを導入するときに知っておくと良いconfigまわりのこと

  • 15
    Like
  • 0
    Comment

この記事は React Advent Calendar 2016 の 23 日目の記事として投稿しようと思っていたものです。
クリスマスに全然間に合いませんでした...

はじめに

先日、弊社で React をテーマとした LT イベント を開催したのですが、
そのときに React Storybook について話をしました。
(そのときの資料はこちらです: React Storybook を触ってみる // Speaker Deck)

React Storybook とは?や試してみた経緯については上記スライドで紹介していますが
実際にプロジェクトに導入しようとしたときに、割とハマりどころというかドキュメントをちゃんと読まないとわからないことが多かったので、そのあたりをまとめてみました。

まずは Default Config を理解する

参考:Default Config - React Storybook

React Storybook は webpack を使用しています。
内部で定義された webpack.config.js を使用しているようで、そのビルドの挙動については上記ページに記載されています。

JavaScript については

  • Babel でトランスパイルされる。ES2016+ の文法が使える
  • プロジェクトルートに .babelrc があると自動的に読み込んでくれる
    • ので、JavaScript については特に意識せずともプロジェクトの設定をそのまま流用できそう

詳しくは ここ を見ろと記載がありますが、どうも現在は babel-preset-react-app というプリセットを読み込んでいるだけに見えます。
babel-preset-react-app はこれのことでしょうか。
https://github.com/facebookincubator/create-react-app/tree/master/packages/babel-preset-react-app
Create React App で使用されているプリセットのようです。

また、CSSや画像などの静的リソースについても

index.js
import './styles.css'

という形式で普通にインポート可能です。

余談:デフォルトの webpack.config.js はどこで確認できる?

「挙動はなんとなくわかったけど実際の webpack.config.js の内容を確認したいなー」と思って色々調べてみたんですが
ドキュメントからはわかりませんでした。
しょうがないので、カスタムの webpack.config.js(後述)を定義せずに storybook を起動したときの

=> Using default webpack setup based on "Create React App".

というログをリポジトリで検索すると以下が見つかります。
https://github.com/storybooks/react-storybook/blob/1711fd58d17795e14c02a68ee61b016052a44792/src/server/config.js#L71-L72

また、相対パス ./config/defaults/webpack.config.js をたどって見つかるのがこれです。
https://github.com/storybooks/react-storybook/blob/1711fd58d17795e14c02a68ee61b016052a44792/src/server/config/defaults/webpack.config.js

たしかに .css.jpg などのファイルに対する loader が定義されてますね。
ちなみに JS に対する loader は このあたり で定義されてますが、ソースを深く読み込んでいないので両者のファイルの関係性とかはわかってないです。

カスタムの webpack.config.js を定義する

参考:Custom Webpack Config - React Storybook

デフォルトの webpack.config.js を挙動を変更するには、.storybook ディレクトリの下にプロジェクト用とは別の webpack.config.js を用意する必要があります。

たとえば、デフォルトの設定では SASS などの CSS プリプロセッサに対応していないため
そういったファイルにも対応したい場合は以下のように記述します。

.storybook/webpack.config.js
const path = require('path');

module.exports = {
  module: {
    loaders: [
      {
        test: /.scss$/,
        loaders: ["style", "css", "sass"],
        include: path.resolve(__dirname, '../')
      }
    ]
  }
}

通常の webpack.config.js と違うところとしては、このファイルは .storybook ディレクトリの下にあるので include オプションを↑のように指定する必要があるところですかね。

注)...とドキュメントには記載されてますが、今のところ include の指定を忘れてうまくいかなかった場面に遭遇していません。プロジェクトの構成とかスタイルファイルをどのように扱っているかとかによる気がします。

また、

Once you create this webpack.config.js file, Storybook won't load the default Webpack config other than loading JS files with the Babel loader.

ということなので、カスタムの webpack.config.js を用意した場合、デフォルトの設定は無効になります(デフォルトの設定に追加、ではなく)
「SCSS読み込ませたいけど画像とかはデフォルトの設定のままでいいや」という場合でも画像などの拡張子に対する loader もしっかり定義してあげないということですね。

まあ基本的にはプロジェクトの webpack.config.js の内容をコピーしてくることになりそうです。

カスタム webpack.config.js で指定できないもの

webpack の option のうち、以下は変更できません。

  • entry
  • output
  • js の loader(Babel 使うのは変更できないので、.babelrc で設定をカスタマイズする)

余談:プロジェクトの webpack.config.js の内容を使うことはできないの?

Using Your Existing Config で軽く言及されてます。
結論としては、プロジェクトルートの webpack.config.js をそのまま使うということはできないので

  • .storybook/webpack.config.js 側ではプロジェクトの webpack.config.js を import する
  • 共通の config を作ってそれぞれで import する

など工夫してくれとのこと。

HTML の<link>タグでリソースを読み込んでいる場合

参考:Add Custom Head Tags - React Storybook

プロジェクトによっては、HTML ファイルで

index.html
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500">

のようにリソースを読み込んでいるケースがあると思います。
その場合、.storybook/head.html というファイルを用意し、そこに HTML の <head> タグの中に書く内容を直接書いていきます。

.storybook/head.html
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500">

ローカルのリソースを相対パスで読み込んでいる場合は?

CDN などから絶対パスでリソースを読み込んでいる場合は上記の通りでいいのですが、相対パスで指定したリソースがある場合は注意が必要です。

たとえば、ディレクトリ構成が以下のようになっていて、

.
├── .storybook
├── build .......... webpack でビルドした後のファイルの出力先
│   ├── assets
│   ├── index.html
│   └── bundle.js
├── node_modules
├── package.json
├── src ............ ビルド前のファイル置き場
└── webpack.config.js

npm 経由でインストールした CSS ライブラリなどを build/assets 以下にコピーし、index.html で読み込んでいる場合です。

build/index.html
<link rel="stylesheet" href="assets/styles/foo/bar/main.css>

この場合、この <link> タグをそのまま .storybook/head.html にコピーしても正しく読み込んでくれません。

これについては今でも正解がわかっていないのですが、1つの解決策として start-storybook-s オプションを使うという方法があります。
参考:Serving Static Files - React Storybook

start-storybook コマンド実行時に

{
  "scripts": {
    "storybook": "start-storybook -s ./build"
  }
}

のように指定すると、指定したディレクトリ内の静的リソースを利用することができるようになります。
あとは -s で指定したディレクトリを起点とした相対パスを .storybook/head.html に書けば OK です。