diff --git a/e2e/cases/css/style-loader-lightningcss/index.test.ts b/e2e/cases/css/style-loader-lightningcss/index.test.ts
new file mode 100644
index 0000000000..040eb9fd80
--- /dev/null
+++ b/e2e/cases/css/style-loader-lightningcss/index.test.ts
@@ -0,0 +1,27 @@
+import { build, rspackOnlyTest } from '@e2e/helper';
+import { expect } from '@playwright/test';
+
+const fixtures = __dirname;
+
+rspackOnlyTest(
+ 'should use lightningcss-loader to transform and minify CSS when injectStyles is true',
+ async () => {
+ const rsbuild = await build({
+ cwd: fixtures,
+ });
+
+ // injectStyles worked
+ const files = await rsbuild.unwrapOutputJSON();
+
+ // should inline minified css
+ const indexJsFile = Object.keys(files).find(
+ (file) => file.includes('index.') && file.endsWith('.js'),
+ )!;
+
+ expect(
+ files[indexJsFile].includes(
+ '@media (-webkit-min-device-pixel-ratio:2),(min-resolution:2dppx){.item{-webkit-user-select:none;user-select:none;background:linear-gradient(#fff,#000);transition:all .5s}}',
+ ),
+ ).toBeTruthy();
+ },
+);
diff --git a/e2e/cases/css/style-loader-lightningcss/rsbuild.config.ts b/e2e/cases/css/style-loader-lightningcss/rsbuild.config.ts
new file mode 100644
index 0000000000..0d8884a67e
--- /dev/null
+++ b/e2e/cases/css/style-loader-lightningcss/rsbuild.config.ts
@@ -0,0 +1,7 @@
+import { defineConfig } from '@rsbuild/core';
+
+export default defineConfig({
+ output: {
+ injectStyles: true,
+ },
+});
diff --git a/e2e/cases/css/style-loader-lightningcss/src/index.css b/e2e/cases/css/style-loader-lightningcss/src/index.css
new file mode 100644
index 0000000000..f2b42a90bb
--- /dev/null
+++ b/e2e/cases/css/style-loader-lightningcss/src/index.css
@@ -0,0 +1,7 @@
+@media (min-resolution: 2dppx) {
+ .item {
+ transition: all 0.5s;
+ user-select: none;
+ background: linear-gradient(to bottom, white, black);
+ }
+}
diff --git a/e2e/cases/css/style-loader-lightningcss/src/index.js b/e2e/cases/css/style-loader-lightningcss/src/index.js
new file mode 100644
index 0000000000..6a9a4b1328
--- /dev/null
+++ b/e2e/cases/css/style-loader-lightningcss/src/index.js
@@ -0,0 +1 @@
+import './index.css';
diff --git a/e2e/cases/css/tailwindcss-vendor-prefix/.browserslistrc b/e2e/cases/css/tailwindcss-vendor-prefix/.browserslistrc
new file mode 100644
index 0000000000..59fbd5b04d
--- /dev/null
+++ b/e2e/cases/css/tailwindcss-vendor-prefix/.browserslistrc
@@ -0,0 +1,2 @@
+IE >= 11
+Chrome >= 10
diff --git a/e2e/cases/css/tailwindcss-vendor-prefix/index.test.ts b/e2e/cases/css/tailwindcss-vendor-prefix/index.test.ts
new file mode 100644
index 0000000000..242880701e
--- /dev/null
+++ b/e2e/cases/css/tailwindcss-vendor-prefix/index.test.ts
@@ -0,0 +1,20 @@
+import { build, rspackOnlyTest } from '@e2e/helper';
+import { expect } from '@playwright/test';
+
+rspackOnlyTest(
+ 'should generate tailwindcss utilities with vendor prefixes correctly',
+ async () => {
+ const rsbuild = await build({
+ cwd: __dirname,
+ });
+
+ const files = await rsbuild.unwrapOutputJSON();
+ const indexCssFile = Object.keys(files).find(
+ (file) => file.includes('index.') && file.endsWith('.css'),
+ )!;
+
+ expect(files[indexCssFile]).toContain('-webkit-user-select: none;');
+ expect(files[indexCssFile]).toContain('-ms-user-select: none;');
+ expect(files[indexCssFile]).toContain('user-select: none;');
+ },
+);
diff --git a/e2e/cases/css/tailwindcss-vendor-prefix/postcss.config.cjs b/e2e/cases/css/tailwindcss-vendor-prefix/postcss.config.cjs
new file mode 100644
index 0000000000..efe50fc2f0
--- /dev/null
+++ b/e2e/cases/css/tailwindcss-vendor-prefix/postcss.config.cjs
@@ -0,0 +1,9 @@
+const path = require('node:path');
+
+module.exports = {
+ plugins: {
+ tailwindcss: {
+ config: path.join(__dirname, './tailwind.config.cjs'),
+ },
+ },
+};
diff --git a/e2e/cases/css/tailwindcss-vendor-prefix/rsbuild.config.ts b/e2e/cases/css/tailwindcss-vendor-prefix/rsbuild.config.ts
new file mode 100644
index 0000000000..8858c3d3c7
--- /dev/null
+++ b/e2e/cases/css/tailwindcss-vendor-prefix/rsbuild.config.ts
@@ -0,0 +1,10 @@
+import { defineConfig } from '@rsbuild/core';
+
+export default defineConfig({
+ html: {
+ template: './src/index.html',
+ },
+ output: {
+ minify: false,
+ },
+});
diff --git a/e2e/cases/css/tailwindcss-vendor-prefix/src/index.css b/e2e/cases/css/tailwindcss-vendor-prefix/src/index.css
new file mode 100644
index 0000000000..65dd5f63a7
--- /dev/null
+++ b/e2e/cases/css/tailwindcss-vendor-prefix/src/index.css
@@ -0,0 +1 @@
+@tailwind utilities;
diff --git a/e2e/cases/css/tailwindcss-vendor-prefix/src/index.html b/e2e/cases/css/tailwindcss-vendor-prefix/src/index.html
new file mode 100644
index 0000000000..29c19a0013
--- /dev/null
+++ b/e2e/cases/css/tailwindcss-vendor-prefix/src/index.html
@@ -0,0 +1,7 @@
+
+
+
+
+ Hello world!
+
+
diff --git a/e2e/cases/css/tailwindcss-vendor-prefix/src/index.ts b/e2e/cases/css/tailwindcss-vendor-prefix/src/index.ts
new file mode 100644
index 0000000000..6a9a4b1328
--- /dev/null
+++ b/e2e/cases/css/tailwindcss-vendor-prefix/src/index.ts
@@ -0,0 +1 @@
+import './index.css';
diff --git a/e2e/cases/css/tailwindcss-vendor-prefix/tailwind.config.cjs b/e2e/cases/css/tailwindcss-vendor-prefix/tailwind.config.cjs
new file mode 100644
index 0000000000..e03a0c3e05
--- /dev/null
+++ b/e2e/cases/css/tailwindcss-vendor-prefix/tailwind.config.cjs
@@ -0,0 +1,10 @@
+const path = require('node:path');
+
+/** @type {import('tailwindcss').Config} */
+module.exports = {
+ content: [path.join(__dirname, './src/**/*.{html,js,ts,jsx,tsx}')],
+ theme: {
+ extend: {},
+ },
+ plugins: [],
+};
diff --git a/packages/compat/webpack/tests/__snapshots__/default.test.ts.snap b/packages/compat/webpack/tests/__snapshots__/default.test.ts.snap
index 3ff421417a..019cc77dcd 100644
--- a/packages/compat/webpack/tests/__snapshots__/default.test.ts.snap
+++ b/packages/compat/webpack/tests/__snapshots__/default.test.ts.snap
@@ -56,6 +56,7 @@ exports[`applyDefaultPlugins > should apply default plugins correctly 1`] = `
{
"loader": "/packages/core/compiled/css-loader/index.js",
"options": {
+ "_skipReuseAST": true,
"importLoaders": 0,
"modules": {
"auto": true,
@@ -418,6 +419,7 @@ exports[`applyDefaultPlugins > should apply default plugins correctly when produ
{
"loader": "/packages/core/compiled/css-loader/index.js",
"options": {
+ "_skipReuseAST": true,
"importLoaders": 0,
"modules": {
"auto": true,
@@ -776,6 +778,7 @@ exports[`applyDefaultPlugins > should apply default plugins correctly when targe
{
"loader": "/packages/core/compiled/css-loader/index.js",
"options": {
+ "_skipReuseAST": true,
"importLoaders": 0,
"modules": {
"auto": true,
@@ -1081,6 +1084,7 @@ exports[`applyDefaultPlugins > should apply default plugins correctly when targe
{
"loader": "/packages/core/compiled/css-loader/index.js",
"options": {
+ "_skipReuseAST": true,
"importLoaders": 0,
"modules": {
"auto": true,
diff --git a/packages/core/prebundle.config.mjs b/packages/core/prebundle.config.mjs
index f8508ac06e..81e519aeb9 100644
--- a/packages/core/prebundle.config.mjs
+++ b/packages/core/prebundle.config.mjs
@@ -5,13 +5,6 @@ import fs from 'node:fs';
*/
import { join } from 'node:path';
-// The package size of `schema-utils` is large, and validate has a performance overhead of tens of ms.
-// So we skip the validation and let TypeScript to ensure type safety.
-const writeEmptySchemaUtils = (task) => {
- const schemaUtilsPath = join(task.distPath, 'schema-utils.js');
- fs.writeFileSync(schemaUtilsPath, 'module.exports.validate = () => {};');
-};
-
// postcss-loader and css-loader use `semver` to compare PostCSS ast version,
// Rsbuild uses the same PostCSS version and do not need the comparison.
const writeEmptySemver = (task) => {
@@ -168,6 +161,21 @@ export default {
postcss: '../postcss',
picocolors: '../picocolors',
},
+ beforeBundle(task) {
+ // Temp fix for https://github.com/web-infra-dev/rspack/issues/7819
+ replaceFileContent(join(task.depPath, 'dist/index.js'), (content) =>
+ content.replaceAll(
+ 'if (meta) {',
+ 'if (meta && !rawOptions._skipReuseAST) {',
+ ),
+ );
+ replaceFileContent(join(task.depPath, 'dist/index.js'), (content) =>
+ content.replaceAll(
+ 'this.getOptions(_options.default)',
+ 'this.getOptions()',
+ ),
+ );
+ },
afterBundle: writeEmptySemver,
},
{
diff --git a/packages/core/src/plugins/css.ts b/packages/core/src/plugins/css.ts
index 5040b39854..84997c6555 100644
--- a/packages/core/src/plugins/css.ts
+++ b/packages/core/src/plugins/css.ts
@@ -173,6 +173,7 @@ const getCSSLoaderOptions = ({
localIdentName,
},
sourceMap: config.output.sourceMap.css,
+ _skipReuseAST: config.tools.lightningcssLoader !== false,
};
const mergedCssLoaderOptions = reduceConfigs({
diff --git a/packages/core/src/types/thirdParty.ts b/packages/core/src/types/thirdParty.ts
index c0f9d0af23..caff45ba17 100644
--- a/packages/core/src/types/thirdParty.ts
+++ b/packages/core/src/types/thirdParty.ts
@@ -187,6 +187,11 @@ export interface CSSLoaderOptions {
* @default 'array'
*/
exportType?: 'array' | 'string' | 'css-style-sheet';
+ /**
+ * Temp fix for https://github.com/web-infra-dev/rspack/issues/7819
+ * @private
+ */
+ _skipReuseAST?: boolean;
}
export type StyleLoaderInjectType =
diff --git a/packages/core/tests/__snapshots__/builder.test.ts.snap b/packages/core/tests/__snapshots__/builder.test.ts.snap
index 5b29011bd1..190c541dc3 100644
--- a/packages/core/tests/__snapshots__/builder.test.ts.snap
+++ b/packages/core/tests/__snapshots__/builder.test.ts.snap
@@ -43,6 +43,7 @@ exports[`should use rspack as default bundler > apply rspack correctly 1`] = `
{
"loader": "/packages/core/compiled/css-loader/index.js",
"options": {
+ "_skipReuseAST": true,
"importLoaders": 1,
"modules": {
"auto": true,
diff --git a/packages/core/tests/__snapshots__/css.test.ts.snap b/packages/core/tests/__snapshots__/css.test.ts.snap
index 3459c6a864..caf1d8f63a 100644
--- a/packages/core/tests/__snapshots__/css.test.ts.snap
+++ b/packages/core/tests/__snapshots__/css.test.ts.snap
@@ -18,6 +18,7 @@ exports[`plugin-css > should use custom cssModules rule when using output.cssMod
{
"loader": "/packages/core/compiled/css-loader/index.js",
"options": {
+ "_skipReuseAST": true,
"importLoaders": 1,
"modules": {
"auto": [Function],
@@ -68,6 +69,7 @@ exports[`plugin-css injectStyles > should apply ignoreCssLoader when injectStyle
{
"loader": "/packages/core/compiled/css-loader/index.js",
"options": {
+ "_skipReuseAST": true,
"importLoaders": 0,
"modules": {
"auto": true,
@@ -108,6 +110,7 @@ exports[`plugin-css injectStyles > should use css-loader + style-loader when inj
{
"loader": "/packages/core/compiled/css-loader/index.js",
"options": {
+ "_skipReuseAST": true,
"importLoaders": 1,
"modules": {
"auto": true,
@@ -156,6 +159,7 @@ exports[`should ensure isolation of PostCSS config objects between different bui
{
"loader": "/packages/core/compiled/css-loader/index.js",
"options": {
+ "_skipReuseAST": true,
"importLoaders": 2,
"modules": {
"auto": true,
@@ -214,6 +218,7 @@ exports[`should ensure isolation of PostCSS config objects between different bui
{
"loader": "/packages/core/compiled/css-loader/index.js",
"options": {
+ "_skipReuseAST": true,
"importLoaders": 2,
"modules": {
"auto": true,
diff --git a/packages/core/tests/__snapshots__/default.test.ts.snap b/packages/core/tests/__snapshots__/default.test.ts.snap
index a7204b500f..4e8c356549 100644
--- a/packages/core/tests/__snapshots__/default.test.ts.snap
+++ b/packages/core/tests/__snapshots__/default.test.ts.snap
@@ -43,6 +43,7 @@ exports[`applyDefaultPlugins > should apply default plugins correctly 1`] = `
{
"loader": "/packages/core/compiled/css-loader/index.js",
"options": {
+ "_skipReuseAST": true,
"importLoaders": 1,
"modules": {
"auto": true,
@@ -437,6 +438,7 @@ exports[`applyDefaultPlugins > should apply default plugins correctly when prod
{
"loader": "/packages/core/compiled/css-loader/index.js",
"options": {
+ "_skipReuseAST": true,
"importLoaders": 1,
"modules": {
"auto": true,
@@ -893,6 +895,7 @@ exports[`applyDefaultPlugins > should apply default plugins correctly when targe
{
"loader": "/packages/core/compiled/css-loader/index.js",
"options": {
+ "_skipReuseAST": true,
"importLoaders": 0,
"modules": {
"auto": true,
@@ -1227,6 +1230,7 @@ exports[`tools.rspack > should match snapshot 1`] = `
{
"loader": "/packages/core/compiled/css-loader/index.js",
"options": {
+ "_skipReuseAST": true,
"importLoaders": 1,
"modules": {
"auto": true,
diff --git a/packages/core/tests/__snapshots__/environments.test.ts.snap b/packages/core/tests/__snapshots__/environments.test.ts.snap
index 8163956c81..b36354bd55 100644
--- a/packages/core/tests/__snapshots__/environments.test.ts.snap
+++ b/packages/core/tests/__snapshots__/environments.test.ts.snap
@@ -1429,6 +1429,7 @@ exports[`environment config > tools.rspack / bundlerChain can be used in environ
{
"loader": "/packages/core/compiled/css-loader/index.js",
"options": {
+ "_skipReuseAST": true,
"importLoaders": 1,
"modules": {
"auto": true,
@@ -1770,6 +1771,7 @@ exports[`environment config > tools.rspack / bundlerChain can be used in environ
{
"loader": "/packages/core/compiled/css-loader/index.js",
"options": {
+ "_skipReuseAST": true,
"importLoaders": 0,
"modules": {
"auto": true,
diff --git a/packages/plugin-less/tests/__snapshots__/index.test.ts.snap b/packages/plugin-less/tests/__snapshots__/index.test.ts.snap
index 4a3198c1e7..ab54022fd5 100644
--- a/packages/plugin-less/tests/__snapshots__/index.test.ts.snap
+++ b/packages/plugin-less/tests/__snapshots__/index.test.ts.snap
@@ -15,6 +15,7 @@ exports[`plugin-less > should add less-loader 1`] = `
{
"loader": "/packages/core/compiled/css-loader/index.js",
"options": {
+ "_skipReuseAST": true,
"importLoaders": 2,
"modules": {
"auto": true,
@@ -70,6 +71,7 @@ exports[`plugin-less > should add less-loader and css-loader when injectStyles 1
{
"loader": "/packages/core/compiled/css-loader/index.js",
"options": {
+ "_skipReuseAST": true,
"importLoaders": 2,
"modules": {
"auto": true,
@@ -128,6 +130,7 @@ exports[`plugin-less > should add less-loader with excludes 1`] = `
{
"loader": "/packages/core/compiled/css-loader/index.js",
"options": {
+ "_skipReuseAST": true,
"importLoaders": 2,
"modules": {
"auto": true,
@@ -183,6 +186,7 @@ exports[`plugin-less > should add less-loader with tools.less 1`] = `
{
"loader": "/packages/core/compiled/css-loader/index.js",
"options": {
+ "_skipReuseAST": true,
"importLoaders": 2,
"modules": {
"auto": true,
diff --git a/packages/plugin-react/tests/__snapshots__/index.test.ts.snap b/packages/plugin-react/tests/__snapshots__/index.test.ts.snap
index 57b686f5a1..0d41ecd468 100644
--- a/packages/plugin-react/tests/__snapshots__/index.test.ts.snap
+++ b/packages/plugin-react/tests/__snapshots__/index.test.ts.snap
@@ -58,6 +58,7 @@ exports[`plugins/react > should work with swc-loader 1`] = `
{
"loader": "/packages/core/compiled/css-loader/index.js",
"options": {
+ "_skipReuseAST": true,
"importLoaders": 1,
"modules": {
"auto": true,
diff --git a/packages/plugin-sass/tests/__snapshots__/index.test.ts.snap b/packages/plugin-sass/tests/__snapshots__/index.test.ts.snap
index 9c8ca5c995..23a13f8707 100644
--- a/packages/plugin-sass/tests/__snapshots__/index.test.ts.snap
+++ b/packages/plugin-sass/tests/__snapshots__/index.test.ts.snap
@@ -15,6 +15,7 @@ exports[`plugin-sass > should add sass-loader 1`] = `
{
"loader": "/packages/core/compiled/css-loader/index.js",
"options": {
+ "_skipReuseAST": true,
"importLoaders": 3,
"modules": {
"auto": true,
@@ -72,6 +73,7 @@ exports[`plugin-sass > should add sass-loader and css-loader when injectStyles 1
{
"loader": "/packages/core/compiled/css-loader/index.js",
"options": {
+ "_skipReuseAST": true,
"importLoaders": 3,
"modules": {
"auto": true,
@@ -132,6 +134,7 @@ exports[`plugin-sass > should add sass-loader with excludes 1`] = `
{
"loader": "/packages/core/compiled/css-loader/index.js",
"options": {
+ "_skipReuseAST": true,
"importLoaders": 3,
"modules": {
"auto": true,
diff --git a/packages/plugin-stylus/tests/__snapshots__/index.test.ts.snap b/packages/plugin-stylus/tests/__snapshots__/index.test.ts.snap
index 0058fb26cf..aeef0cbf81 100644
--- a/packages/plugin-stylus/tests/__snapshots__/index.test.ts.snap
+++ b/packages/plugin-stylus/tests/__snapshots__/index.test.ts.snap
@@ -15,6 +15,7 @@ exports[`plugin-stylus > should add stylus loader config correctly 1`] = `
{
"loader": "/packages/core/compiled/css-loader/index.js",
"options": {
+ "_skipReuseAST": true,
"importLoaders": 2,
"modules": {
"auto": true,
@@ -63,6 +64,7 @@ exports[`plugin-stylus > should allow to configure stylus options 1`] = `
{
"loader": "/packages/core/compiled/css-loader/index.js",
"options": {
+ "_skipReuseAST": true,
"importLoaders": 2,
"modules": {
"auto": true,