From 3c3b768ff414a098b731107d47a0ce098e019d3c Mon Sep 17 00:00:00 2001 From: Qingyu Wang Date: Mon, 2 Mar 2026 10:46:30 +0800 Subject: [PATCH 1/3] test(v4): add test case for async chunks and CSS splitting --- test/async-chunks/index.test.ts | 87 +++++++++++++++++++++++++++++ test/async-chunks/rsbuild.config.ts | 6 ++ test/async-chunks/src/async.js | 6 ++ test/async-chunks/src/index.css | 6 ++ test/async-chunks/src/index.js | 10 ++++ 5 files changed, 115 insertions(+) create mode 100644 test/async-chunks/index.test.ts create mode 100644 test/async-chunks/rsbuild.config.ts create mode 100644 test/async-chunks/src/async.js create mode 100644 test/async-chunks/src/index.css create mode 100644 test/async-chunks/src/index.js diff --git a/test/async-chunks/index.test.ts b/test/async-chunks/index.test.ts new file mode 100644 index 0000000..8a5bfb7 --- /dev/null +++ b/test/async-chunks/index.test.ts @@ -0,0 +1,87 @@ +import { dirname } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { expect, test } from '@playwright/test'; +import { createRsbuild } from '@rsbuild/core'; +import { pluginTailwindCSS } from '../../src'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); + +test('should split CSS and reuse common parts', async ({ page }) => { + const rsbuild = await createRsbuild({ + cwd: __dirname, + rsbuildConfig: { + plugins: [pluginTailwindCSS()], + }, + }); + + await rsbuild.build(); + const { server, urls } = await rsbuild.preview(); + + await page.goto(urls[0]); + + // Check main chunk styles + const mainDiv = page.locator('.flex.bg-red-500'); + await expect(mainDiv).toHaveCSS('display', 'flex'); + // Check that background color is set (exact value might vary by browser/environment) + const mainBgColor = await mainDiv.evaluate( + (el) => getComputedStyle(el).backgroundColor, + ); + expect(mainBgColor).not.toBe('rgba(0, 0, 0, 0)'); + expect(mainBgColor).not.toBe('transparent'); + + // Wait for async chunk + const asyncDiv = page.locator('.flex.text-center.bg-blue-500'); + await expect(asyncDiv).toHaveCSS('display', 'flex'); + await expect(asyncDiv).toHaveCSS('text-align', 'center'); + const asyncBgColor = await asyncDiv.evaluate( + (el) => getComputedStyle(el).backgroundColor, + ); + expect(asyncBgColor).not.toBe('rgba(0, 0, 0, 0)'); + expect(asyncBgColor).not.toBe('transparent'); + + // Analyze CSS files to check for splitting and deduplication + const linkTags = await page.locator('link[rel="stylesheet"]').all(); + + // Fetch all CSS content + const cssContents: string[] = []; + for (const link of linkTags) { + const href = await link.getAttribute('href'); + if (href) { + const response = await page.request.get( + new URL(href, urls[0]).toString(), + ); + cssContents.push(await response.text()); + } + } + + // Check if .flex is defined + let flexCount = 0; + for (const css of cssContents) { + // Simple check for .flex definition + if (css.includes('.flex{') || css.includes('.flex {')) { + flexCount++; + } + } + + // We expect at least 2 CSS files (main + async) + expect(cssContents.length).toBeGreaterThanOrEqual(2); + + // We expect .flex to be defined exactly once (deduplicated) + // or if not deduplicated, it might be 2. Ideally 1. + // Let's assert it exists first. + expect(flexCount).toBeGreaterThan(0); + + // Check for Preflight duplication (checking for a common reset rule) + // Tailwind preflight usually includes `box-sizing: border-box` on `*, ::before, ::after`. + let preflightCount = 0; + for (const css of cssContents) { + if (css.includes('box-sizing:border-box')) { + preflightCount++; + } + } + + // Preflight should be deduplicated (only in main chunk) + expect(preflightCount).toBe(1); + + await server.close(); +}); diff --git a/test/async-chunks/rsbuild.config.ts b/test/async-chunks/rsbuild.config.ts new file mode 100644 index 0000000..63d284f --- /dev/null +++ b/test/async-chunks/rsbuild.config.ts @@ -0,0 +1,6 @@ + +import { pluginTailwindCSS } from '../../src'; + +export default { + plugins: [pluginTailwindCSS()], +}; diff --git a/test/async-chunks/src/async.js b/test/async-chunks/src/async.js new file mode 100644 index 0000000..08eae08 --- /dev/null +++ b/test/async-chunks/src/async.js @@ -0,0 +1,6 @@ + +export default function asyncFn() { + const el = document.createElement('div'); + el.className = 'flex text-center bg-blue-500'; + document.body.appendChild(el); +} diff --git a/test/async-chunks/src/index.css b/test/async-chunks/src/index.css new file mode 100644 index 0000000..e2719cc --- /dev/null +++ b/test/async-chunks/src/index.css @@ -0,0 +1,6 @@ +@import "tailwindcss"; + +@theme { + --color-red-500: #ef4444; + --color-blue-500: #3b82f6; +} diff --git a/test/async-chunks/src/index.js b/test/async-chunks/src/index.js new file mode 100644 index 0000000..634f531 --- /dev/null +++ b/test/async-chunks/src/index.js @@ -0,0 +1,10 @@ + +import './index.css'; + +const el = document.createElement('div'); +el.className = 'flex bg-red-500'; +document.body.appendChild(el); + +import('./async').then(({ default: asyncFn }) => { + asyncFn(); +}); From 70ac94583b6d3520c6d18259eea25d6926a14679 Mon Sep 17 00:00:00 2001 From: Qingyu Wang Date: Mon, 2 Mar 2026 10:47:05 +0800 Subject: [PATCH 2/3] style: fix lint issues --- test/async-chunks/rsbuild.config.ts | 1 - test/async-chunks/src/async.js | 1 - test/async-chunks/src/index.js | 1 - test/static-assets/index.test.ts | 5 ++++- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/async-chunks/rsbuild.config.ts b/test/async-chunks/rsbuild.config.ts index 63d284f..66a4550 100644 --- a/test/async-chunks/rsbuild.config.ts +++ b/test/async-chunks/rsbuild.config.ts @@ -1,4 +1,3 @@ - import { pluginTailwindCSS } from '../../src'; export default { diff --git a/test/async-chunks/src/async.js b/test/async-chunks/src/async.js index 08eae08..708ddb8 100644 --- a/test/async-chunks/src/async.js +++ b/test/async-chunks/src/async.js @@ -1,4 +1,3 @@ - export default function asyncFn() { const el = document.createElement('div'); el.className = 'flex text-center bg-blue-500'; diff --git a/test/async-chunks/src/index.js b/test/async-chunks/src/index.js index 634f531..8b94b02 100644 --- a/test/async-chunks/src/index.js +++ b/test/async-chunks/src/index.js @@ -1,4 +1,3 @@ - import './index.css'; const el = document.createElement('div'); diff --git a/test/static-assets/index.test.ts b/test/static-assets/index.test.ts index c46049b..ab7a6a6 100644 --- a/test/static-assets/index.test.ts +++ b/test/static-assets/index.test.ts @@ -30,7 +30,10 @@ test('should not interfere with static asset queries (?url, ?raw)', async ({ // Check ?raw // It should contain the original CSS content, NOT Tailwind's injected content (like @tailwind base etc.) - const originalCss = readFileSync(resolve(__dirname, 'src/index.css'), 'utf-8'); + const originalCss = readFileSync( + resolve(__dirname, 'src/index.css'), + 'utf-8', + ); expect(cssRaw.trim()).toBe(originalCss.trim()); await server.close(); From 8f31a3b4fd9de9132feaeadb71a1a6f4f91da051 Mon Sep 17 00:00:00 2001 From: Qingyu Wang Date: Mon, 2 Mar 2026 10:49:58 +0800 Subject: [PATCH 3/3] test(v4): remove redundant tailwind import and config file --- test/async-chunks/rsbuild.config.ts | 5 ----- test/async-chunks/src/index.css | 2 -- 2 files changed, 7 deletions(-) delete mode 100644 test/async-chunks/rsbuild.config.ts diff --git a/test/async-chunks/rsbuild.config.ts b/test/async-chunks/rsbuild.config.ts deleted file mode 100644 index 66a4550..0000000 --- a/test/async-chunks/rsbuild.config.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { pluginTailwindCSS } from '../../src'; - -export default { - plugins: [pluginTailwindCSS()], -}; diff --git a/test/async-chunks/src/index.css b/test/async-chunks/src/index.css index e2719cc..1ce0034 100644 --- a/test/async-chunks/src/index.css +++ b/test/async-chunks/src/index.css @@ -1,5 +1,3 @@ -@import "tailwindcss"; - @theme { --color-red-500: #ef4444; --color-blue-500: #3b82f6;