Vueでmodule cssを使いたいけど、依存するnode_modulesのCSSが反映できなくなるみたいな話

# 概要

- Vue(前バージョン共通)でwebpackビルドしている
- Vueファイル内のstyleタグではCSSmoduleを使いたい
- node_modulesに入っているCSSは純粋なCSSとしてグローバル反映させたい
- よくあるgetting started章のドキュメントの通りにコピペするとCSSmoduleにならないか、直接クラス指定したいスタイルが反映されなくなってしまう

上記の状況の際に、雑にCSSmoduleを有効化するとnode_modulesのCSSまでクラス名がハッシュ化されてしまい、不便が発生する場合の対処法。

もしかしたらReactでもelement-plusのような設計のライブラリがあれば起こるかもしれない

自分の場合だと [elementUI](https://element-plus.org/#/jp) にて発生したので綴る。

## 環境

- Vue 3.0.7
- Webpack 5.26.2
- css-loader 5.1.3
- element-plus 1.0.2-beta.35

# 対策

## node_modules内のCSSはnot CSSmoduleとして読み込む

※以下例ではsass-loaderも使っていますが、sassを使用しない場合は無視してください

```webpack.config.js
{
    /* 〜〜〜〜略〜〜〜〜 */
    module: {
        rules: [
            /* 〜〜〜〜略〜〜〜〜 */
            {
                test: /\.(sa|sc|c)ss$/,
                use: [
                    'vue-style-loader',
                    {
                        loader: 'css-loader',
                        options: {
                            modules: true
                        }
                    },
                    'sass-loader',
                ]
            }
            /* 〜〜〜〜略〜〜〜〜 */
        ]
    }
    /* 〜〜〜〜略〜〜〜〜 */
}
```

こんな風にwebpack.config.jsが書かれていると思うが(vue-loaderのオプションに書いている人はvue-loaderの `loader.options.loaders.[任意の拡張子]` 等を参照)、このままwebpackを通すと、node_modules内のCSSもCSSmoduleと解釈されるため、CSSライブラリを使うなどしてどうしてもVueテンプレート内に生のクラスで書きたい場合や、 `element-plus` あるいは `element-ui` のようにVueファイル内で別途CSSを外部から当てないとimportしたコンポーネントにスタイルが当たらない ~~使いづらい~~ 仕様のライブラリでは外部CSSまでCSSmoduleとしてクラス名がハッシュ化されてしまうとコンポーネントライブラリのデザインが適用されなくなってしまう。

ここでの問題は、 `node_modules` の中にあるCSSまでCSSmoduleになることが問題なので、ならば`node_modules` 内はCSSmoduleでは無いものとしてwebpackに食わせれば良い。すなわちこうなる

```webpack.config.js
{
    /* 〜〜〜〜略〜〜〜〜 */
    module: {
        rules: [
            /* 〜〜〜〜略〜〜〜〜 */
            {
                test: /\.(sa|sc|c)ss$/,
                use: [
                    'vue-style-loader',
                    'css-loader',
                    'sass-loader',
                ],
                include: /node_modules/,
            },
            {
                test: /\.(sa|sc|c)ss$/,
                use: [
                    'vue-style-loader',
                    {
                        loader: 'css-loader',
                        options: {
                            modules: true
                        }
                    },
                    'sass-loader',
                ],
                exclude: /node_modules/,
            },
            /* 〜〜〜〜略〜〜〜〜 */
        ]
    }
    /* 〜〜〜〜略〜〜〜〜 */
}
```

webpackは配列のconfigでは末尾から処理されていくので、末尾側に `options.modules: true` にしたcss-loaderをセットして、 `exclude: /node_modules/` する。

次に、先頭側に `options.modules: false` にした `include: /node_modules/` する。

webpack5のドキュメントによれば

[rule.exclude](https://webpack.js.org/configuration/module/#ruleexclude)

> Exclude all modules matching any of these conditions. If you supply a Rule.exclude option, you cannot also supply a Rule.resource. 

マッチした全てのモジュールを除外する

[rule.include](https://webpack.js.org/configuration/module/#ruleinclude)

> Include all modules matching any of these conditions. If you supply a Rule.include option, you cannot also supply a Rule.resource. 

マッチした全てのモジュールを含める

つまり、これを組み合わせたルールを2重に定義することで、特定ディレクトリは純粋なCSSファイルとして、特定ディレクトリはCSSmoduleとしてwebpackに食わせることができる。

ちなみに、先頭側のcss-loaderルールから `include: /node_modules/` を外してしまうと、既にwebpackを通しているcssが再度読み込み直されてしまうので注意。

# 所感

(カスタム性上げたい気持ちはわかるが)令和の時代にESmodule importで完結しない上にbabelを通さないとCSSをグローバル反映させる必要があるようなコンポーネントライブラリを作るな・・・