webpackのローダの設定を混ぜるな

概要

小ネタです。webpackには、動作を拡張できる「ローダ」という仕組みがあるのですが、その動作を設定ファイル(webpack.config.js)で変更することができます。今回はここの書き方でハマったという話です。

対応バージョン

$ webpack --version
4.8.1

ローダでのオプション設定、2つの方法

css-loaderというローダがあります。これを使うと、複数のCSSファイルをまとめたり、JavaScriptの中でCSSのStyleを適用できたりします(CSS modules)。今回、このCSS modulesを利用している中でCSSのクラス名の命名について、kebab-caseからcamelCaseに変更したいと思った時にハマりました。

css-loaderをwebpackに組み込む最もシンプルなコードは以下です。

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ['css-loader'],
      },
    ],
  },
};

これで、.cssで終わるファイルに関して、css-loaderが動作するようになります。これらの動作を変更するためのオプションが用意されているのだが、その書き方には大きく2種類あって、1つ目は以下のように、ローダ名文字列をoption属性を持つオブジェクトに置き換える方法です。

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          {
            loader: 'css-loader',
            options: {
              modules: true
            }
          }
        ]
      }
    ]
  }
};

そしてもう一つ、「インライン」方式があって、URLのクエリパラメータと同様の形式の文字列を与えることができます。上記と同様の内容をインライン形式で記述すると、以下のようになります。

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ['css-loader?modules=true'],
      },
    ],
  },
};

2つの指定方法を混ぜてはいけない

本題に入ります。css-loaderにはlocalsConventionというオプションがあります。

少し説明します。(少なくとも個人的には)CSSファイルに記述するクラス名は通常kebab-caseで、JavaScriptでの属性名はcamelCaseで記述するのですが、css-loaderのデフォルトではCSSクラス名からJS属性名へ何の変換も行いません。したがって、どちらかのケースに統一する必要があります。でもCSSにcamelCaseが書いてあるのも、JSにkebab-caseが堂々と出てくるのもあまり好きではないので、名称の変換をしたいわけです。

今回、希望の動作を叶えるためには、localsConventiondashesを指定します。

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          {
            loader: 'css-loader?modules',
            options: {
              localsConvention: 'dashes'
            }
          }
        ]
      }
    ]
  }
};

しかしながら、実は上記の設定は正しく動作しません。実際には、該当するCSSクラスが全て効かなくなってしまいました。2つの方式を混ぜて書いてしまっているからです。しかもタチの悪いことに、ビルド時エラーにもなりません。

正しい指定方法

2つの方式は共存できないので、以下のどちらかで統一しましょう。 ちなみにインライン方式ではmodule=trueではなく、moduleになっています。オプション名だけだと=trueを付けたのと同じことになるみたいですね。初歩的な内容かもしれません。あんまり詳しくなくてごめんなさい。

オブジェクトでの指定

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          {
            loader: 'css-loader',
            options: {
              modules: true
              localsConvention: 'dashes'
            }
          }
        ]
      }
    ]
  }
};

インライン方式での指定

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ['css-loader?modules&localsConvention=dashes'],
      },
    ],
  },
};