😇

既存のアプリケーションに Rspack を導入しようとして失敗した話

2023/08/10に公開

※2023/09/09 最下部に追記あり

この記事では既存のアプリケーションに Rspack を導入しようとして試行錯誤をした話と、なぜ導入できなかったのかについてを書く。

Why Rspack?

webpack によるビルドの遅さによって開発体験が悪化しており、その改善策の一つとして Rspack を試すことにした。Rspack はまだまだ開発途上のため、production ビルドには採用せず、開発時のみ利用することを想定して導入検証を行った。
余談だが、実は昨年に Vite を導入できないか検証していたのだが、Vite で scss 上から scss を composes で読み込んだ時に scss が css として取り扱われてビルドに失敗する問題に直面して断念した。(この問題については修正 PR も提出したのだが残念ながらマージには至っていない)
https://github.com/vitejs/vite/issues/10340
https://github.com/vitejs/vite/pull/12076

Rspack の導入

Rspack の config は webpack の config と大体同じなので、webpack の config を読み込んで、rspack 固有の設定を足したり、不要な設定を消していく方針をとった。

import { defineConfig } from "@rspack/cli";
import webpackConfig from "./webpack.config";
export default defineConfig({
  ...webpackConfig,
  // 追加の設定を書く
});

基本的にはマイグレーションガイドに従えば良いが、前述の通り webpack.config.js から読み込んだコンフィグを書き換えて使う方針なので rspack.config.js に以下のようなコードを書いたりしてコンフィグを生成した。

webpackConfig.plugins?.filter(plugin => {
  if (plugin instanceof MiniCssExtractPlugin) {
    return false;
  }
  if (plugin instanceof webpack.ProvidePlugin) {
    return false;
  }
  if (plugin instanceof BundleAnalyzerPlugin) {
    return false;
  }
  return true;
});

ハマりどころとしては、webpack の config 上で function を渡すことが許容されている設定が、rspack では許容されていない点だ。多くの場合は正規表現を使うなど別の設定方法で代替できるので問題とはならないだろう。rust で書かれたバイナリから js の関数を呼び出す複雑さとパフォーマンスの低下を考えればこの設定方法が削られたのは納得できる。

なぜ導入に失敗したのか

一言で言うとビルドされた css が壊れていて表示が崩れるからだ。いくつかの問題は解決できたものの、残念ながら解決できなかった問題もあった。

最初に遭遇した問題は、css modules の :global セレクターが存在する場合に生成された css からクラス名が欠落する問題だ。
https://github.com/web-infra-dev/rspack/issues/3611
こちらについては rspack の Collaborator の方が css modules のビルドに使用している swc_css_modules に修正 PR を投げてくれて改善した
https://github.com/swc-project/swc/pull/7600

次に遭遇した問題は :global セレクターの後ろに付与されたセレクターが欠落する問題だ。
https://github.com/web-infra-dev/rspack/issues/3777
こちらについては解決方法がすぐに分かったので自分で PR を作成して改善した。
https://github.com/swc-project/swc/pull/7670

次に遭遇した問題は css modules の composes が連鎖した時にクラス名が欠落する問題だ。
https://github.com/web-infra-dev/rspack/issues/3875
https://github.com/swc-project/swc/issues/7737
この問題は残念ながら現状解決できていない。私が携わるプロダクトでは composes が多用されているため、この問題が解決しない限り rspack は導入できないと言う結論に至った。
一応 workaround として webpack の css-loader と style-loader を使うことで問題を回避することはできる。しかし rspack は mini-css-extract-plugin が現状利用できないため私が携わるプロダクトにおいては利用できなかった。

まとめ

残念ながら私が携わるプロダクトには導入できなかったものの、 ほとんどのアプリケーションでは大きな問題なく導入できると思われる。開発体験の向上を目的とする場合は Vite が第一選択肢であることは変わりないが、 Vite を導入できない事情がある場合は検討してみても良いだろう。
(Rspack とは別の話だが)css modules の composes はあまり利用率が高くないのか、swc_css_modules においても Vite においてもうまく動かないケースが多々あるので、ビルドシステムを乗り換える際には足枷になっている。個人的には新規プロダクトでは極力使わない方向にしていきたい。

2023/09/09 追記

その後 swc_css の実装を読み進めて修正できそうなことがわかったので PR を出してマージされた。
https://github.com/swc-project/swc/pull/7917
これにより css 周りに問題がなくなったので導入することに成功した。
しかしその後の検証で、Rspack は filesystem ベースのキャッシュ機構を持っていないため、キャッシュが活用できる incremental build 時は webpack + babel-loader の組み合わせより遅いことが分かり、残念ながら現状普段遣いは厳しいだろうと判断した。
Rspack を擁護しておくと、初回ビルドは3~4倍程度早いというのと、minify などが行われる production ビルドにおいては圧倒的に早い。
incremental build の高速化に期待したいが、キャッシュ周りの仕様策定はあまり進んでいないのでしばらく時間が掛かるだろう

Discussion