GatsbyやStorybookなどwebpackを利用しているツールで特定の拡張子ファイル用のwebpack loaderを入れ替えたい


前振り

タイトルが複雑ですよね。自分でもなんでこんなことせんとアカンの...って気持ちです。

例えば、svgファイルをアプリケーション上で利用したいとします。
通常であればGatsbyやStorybookがデフォルトでurl-loaderやfile-loaderを利用してよしなにしてくれます。
ですが、svgファイルを 別のloaderで処理したい!という時どうする?というのを解決していきたいと思います。

単にwebpack loaderを追加したい!という場合には公式にある通りに進めていけば問題ないです。
https://www.gatsbyjs.com/docs/add-custom-webpack-config/
https://storybook.js.org/docs/react/configure/webpack

Gatsby

まずGatsbyのほうからやっていきます。

公式にあるとおりGatsbyではgatsby-node.jsというファイルからwebpackのconfigを操作できます。

svgファイルのloaderを以下のものに変更したいなら
GitHub - JetBrains/svg-sprite-loader: Webpack loader for creating SVG sprites.


exports.onCreateWebpackConfig = ({actions, getConfig}) => {
  const config = getConfig();

  config.module.rules = config.module.rules.map(rule => {
    if (
      rule.test &&
      rule.test.toString() === '/\\.(ico|svg|jpg|jpeg|png|gif|webp)(\\?.*)?$/'
    ) {
      return {
        ...rule,
        test: /(\.(ico|jpg|jpeg|png|gif|webp)(\?.*)?$)/,
      };
    } else {
      return rule;
    }
  });

  config.module.rules.push({
    test: /\.svg$/,
    loader: 'svg-sprite-loader',
  });

  actions.replaceWebpackConfig(config);
};


のようにすれば動きます。
ポイントは以下になります。


if (
  rule.test &&
  rule.test.toString() === '/\\.(ico|svg|jpg|jpeg|png|gif|webp)(\\?.*)?$/'
) {
  return {
    ...rule,
    test: /(\.(ico|jpg|jpeg|png|gif|webp)(\?.*)?$)/,
  };
}

渡されたtestフィールドでsvgファイルが該当するようになるloaderから愚直にsvgが該当にならないようにします。
その上で新しく入れ替えたいloaderを利用する宣言をしています。

ずいぶん力業だな〜。と私は思っちゃいましたが
svg-sprite-loaderをgatsby-pluginとして読み込む
GitHub - stldo/gatsby-plugin-svg-sprite-loader: Gatsby plugin for creating SVG sprites using SVG sprite loader
でも同じことしていました。なので私は採用しちゃいました。
gatsby-plugin-svg-sprite-loader/gatsby-node.js at master · stldo/gatsby-plugin-svg-sprite-loader · GitHub

Storybookの場合

察しの良い、ここまで読んでくださった人ならもうお分かりだと思いますが、Storybookでも同じような手順で入れ替えられます。
Storybookの場合は.storybook/main.jsでwebpackのconfigが操作できます。



module.exports = {
  stories: ['../src/**/*.stories.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
  addons: [],
  webpackFinal: config => {
    config.module.rules = config.module.rules.map(rule => {
      if (test &&
            test.toString() === '/\\.(ico|svg|jpg|jpeg|png|gif|webp)(\\?.*)?$/') 
        {
        return { ...rule, test: /(\.(ico|jpg|jpeg|png|gif|webp)(\?.*)?$)/ }
      } else {
        return rule
      }
    })

    config.module.rules.push({
      test: /\.svg$/,
      loader: 'svg-sprite-loader',
    });

    return config;
  },
};

GatsbyとStorybookに限らず、webpackを利用しているツールでは
webpackを操作するポイントさえどうにでもloaderが入れ替えできるよ。というのがわかりましたね。
めでたしめでたし。