Last year, I wrote an article about Rspack and compared its performance with Webpack, and found out that Rspack is 2 to 3 times faster than Webpack. However, I did not use it because I could not configure Rspack to ignore CSS URLs, which Webpack can do using css-loader. You can find a similar issue reported here.
After a year had passed, Rspack was upgraded from version 3.x to 7.x. I spent some time reading the official documentation again, trying to find a way to configure css-loader, and found this:
Let’s take a look at the explaination of it:
Rspack currently does not support mini-css-extract-plugin, but it can be replaced with this plugin and used in conjunction with css-loader to extract CSS into separate files.
If your project does not depend on css-loader, it is recommended to use the built-in CSS solution experiments.css of Rspack for better performance.
The only reason I couldn’t replace Webpack with Rspack was because I needed css-loader in my project. But now, Rspack allows us to do it!
After finding out this good news, I started configuring my rspack.config.js
again, hoping I could now build my CSS and JavaScript without any errors.
And I did it!
So let’s take a look at my rspack.config.js
:
const path = require("path");
const rspack = require("@rspack/core");
const { VueLoaderPlugin } = require("vue-loader");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const PreventOutputJSPlugin = require("./src/webpack/plugins/PreventOutputJSPlugin");
const entryGenerator = require("./src/webpack/plugins/entryGenerator");
module.exports = (env, options) => {
const isProd = options.mode === "production";
const cssSetting = {
mode: "development",
context: __dirname,
devtool: false,
entry: {
...entryGenerator.css(),
},
output: {
path: path.resolve(__dirname, ""),
},
module: {
rules: [
{
test: /\.scss$/,
use: [
rspack.CssExtractRspackPlugin.loader,
{ loader: "css-loader", options: { url: false } },
"postcss-loader",
{
loader: "sass-loader",
options: {
sassOptions: {
outputStyle: "expanded",
},
},
},
],
},
],
},
plugins: [
new PreventOutputJSPlugin(),
new rspack.CssExtractRspackPlugin({}),
],
resolve: {
alias: {
src: path.resolve(__dirname, "src/"),
apps: path.resolve(__dirname, "apps/"),
},
},
optimization: {
minimize: false,
},
experiments: {
css: false,
},
};
const jsSetting = {
context: __dirname,
devtool: false,
entry: {
...entryGenerator.js(),
},
output: {
path: path.resolve(__dirname, "apps/statics/dist/"),
filename: "js/[name].js",
},
// target: ["web", "es5"],
module: {
rules: [
{
test: /\.vue$/,
loader: "vue-loader",
},
{
test: /\.scss$/,
use: [
{
loader: "style-loader",
options: {
esModule: false,
},
},
"css-loader",
"postcss-loader",
"sass-loader",
],
type: "javascript/auto",
},
{
test: /\.css$/,
use: [
{
loader: "style-loader",
options: {
esModule: false,
},
},
"css-loader",
],
type: "javascript/auto",
},
{
test: /\.(png|jpe?g|gif|svg)$/i,
type: "asset/inline",
},
{
test: /\.(woff|woff2|eot|ttf|otf|)$/,
type: "asset/resource",
},
],
},
plugins: [new VueLoaderPlugin()],
resolve: {
alias: {
src: path.resolve(__dirname, "src/"),
apps: path.resolve(__dirname, "apps/"),
node_modules: path.resolve(__dirname, "node_modules/"),
},
},
experiments: {
css: false,
},
optimization: {
minimize: isProd,
},
};
if (isProd) {
jsSetting.plugins.push(new CleanWebpackPlugin({ verbose: true }));
}
return [cssSetting, jsSetting];
};
There’s no need to worry about the source code of entryGenerator
, since it’s just a JavaScript function that generates key-value pairs like this:
{
"index": "src/index.js"
}
If you’re familiar with Webpack, you might know that it tells Webpack to find the source file in src/index.js
, pack it into index.js
and output it in /dist
folder.
The key configuration would be here:
module: {
rules: [
{
test: /\.scss$/,
use: [
// added rspack.CssExtractRspackPlugin.loader
rspack.CssExtractRspackPlugin.loader,
// added css-loader with configurations
{ loader: "css-loader", options: { url: false } },
"postcss-loader",
{
loader: "sass-loader",
options: {
sassOptions: {
outputStyle: "expanded",
},
},
},
],
},
],
},
plugins: [
new PreventOutputJSPlugin(),
// added rspack.CssExtractRspackPlugin
new rspack.CssExtractRspackPlugin({}),
],
resolve: {
alias: {
src: path.resolve(__dirname, "src/"),
apps: path.resolve(__dirname, "apps/"),
},
},
optimization: {
minimize: false,
},
experiments: {
// you need to set this to false
css: false,
},
It’s very easy and simple, as you can see.
You might still wonder about the performance difference between Rspack and Webpack. I have 142 CSS files and 85 JavaScript files, and here’s the build time:
Rspack
- Starts dev server: 11s
- Production build: 13s
- Dev server hot build time: ~112ms
Webpack
- Starts dev server: 25s
- Production build: 42s
- Dev server hot build time: ~645ms
As you can see, Rspack outperforms Webpack in various scenarios, and it’s EVEN FASTER than it was in the 0.3.x version!
Did I replace Webpack with Rspack already?
Unfortunately, currently not yet. It has nothing to do with Rspack; everything works just fine and just as expected.
There’s a PHP JavaScript minify tool called JSMin in our project, which throws an exception when minifying JavaScript regex with a single quote (‘ ) inside. This causes 2 pages of our project to be unable to generate minified JavaScript.
Since my team is not planning to eliminate this old tool, I chose to keep this branch and go back to my other development tasks. Maybe I’ll mention it again some other day.
Conclusion
So, if you’re facing the css-loader issue as I did, I’m going to say: “Yes! You can start writing your Rspack config and show your team how much faster it is now!” Rspack has outstanding packing performance, and I’m sure you won’t regret replacing Webpack with it!
I hope this article helps, and if you have any suggestions, please let me know. See you next month!
References: