From 4fd1b656b44e520179047859363aa01223b67c76 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Wed, 16 Jul 2025 12:32:45 +0900 Subject: [PATCH 1/8] fix(rsc): exclude CSS imports with special queries from collection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CSS imports with special queries (?url, ?inline, ?raw) should not be collected for server-side rendering as they return strings rather than applying styles. This fix adds a hasSpecialCssQuery() helper function to detect and exclude these imports from the collectCss function, preventing unexpected behavior while ensuring normal CSS imports continue to work correctly. - Add hasSpecialCssQuery() helper to detect ?url, ?inline, ?raw queries - Modify collectCss() to exclude CSS imports with special queries - Add comprehensive e2e tests for both server and client components - Test all query patterns with separate CSS files to ensure proper handling Fixes #571 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- packages/plugin-rsc/e2e/basic.test.ts | 74 +++++++++++++++++++ .../src/routes/css-queries/client-inline.css | 4 + .../src/routes/css-queries/client-raw.css | 3 + .../src/routes/css-queries/client-url.css | 3 + .../basic/src/routes/css-queries/client.css | 4 + .../basic/src/routes/css-queries/client.tsx | 34 +++++++++ .../src/routes/css-queries/server-inline.css | 4 + .../src/routes/css-queries/server-raw.css | 3 + .../src/routes/css-queries/server-url.css | 3 + .../basic/src/routes/css-queries/server.css | 4 + .../basic/src/routes/css-queries/server.tsx | 39 ++++++++++ .../examples/basic/src/routes/root.tsx | 2 + packages/plugin-rsc/src/plugin.ts | 6 +- 13 files changed, 182 insertions(+), 1 deletion(-) create mode 100644 packages/plugin-rsc/examples/basic/src/routes/css-queries/client-inline.css create mode 100644 packages/plugin-rsc/examples/basic/src/routes/css-queries/client-raw.css create mode 100644 packages/plugin-rsc/examples/basic/src/routes/css-queries/client-url.css create mode 100644 packages/plugin-rsc/examples/basic/src/routes/css-queries/client.css create mode 100644 packages/plugin-rsc/examples/basic/src/routes/css-queries/client.tsx create mode 100644 packages/plugin-rsc/examples/basic/src/routes/css-queries/server-inline.css create mode 100644 packages/plugin-rsc/examples/basic/src/routes/css-queries/server-raw.css create mode 100644 packages/plugin-rsc/examples/basic/src/routes/css-queries/server-url.css create mode 100644 packages/plugin-rsc/examples/basic/src/routes/css-queries/server.css create mode 100644 packages/plugin-rsc/examples/basic/src/routes/css-queries/server.tsx diff --git a/packages/plugin-rsc/e2e/basic.test.ts b/packages/plugin-rsc/e2e/basic.test.ts index cc6cadd0..93979ded 100644 --- a/packages/plugin-rsc/e2e/basic.test.ts +++ b/packages/plugin-rsc/e2e/basic.test.ts @@ -1023,4 +1023,78 @@ function defineTest(f: Fixture) { 'test-browser-only: loading...', ) }) + + test('css queries @js', async ({ page }) => { + await page.goto(f.url()) + await waitForHydration(page) + await testCssQueries(page) + }) + + testNoJs('css queries @nojs', async ({ page }) => { + await page.goto(f.url()) + await testCssQueries(page) + }) + + async function testCssQueries(page: Page) { + // Test that CSS with special queries (?url, ?inline, ?raw) don't break the page + // and that normal CSS imports still work for both server and client components + + // Server component tests + await expect( + page.getByTestId('test-css-queries-server-normal'), + ).toBeVisible() + + // Client component tests + await expect( + page.getByTestId('test-css-queries-client-normal'), + ).toBeVisible() + + // Test that normal CSS imports are applied correctly to server component + await expect(page.locator('.test-css-query-server-normal')).toHaveCSS( + 'background-color', + 'rgb(255, 0, 0)', + ) + await expect(page.locator('.test-css-query-server-normal')).toHaveCSS( + 'color', + 'rgb(0, 255, 0)', + ) + + // Test that normal CSS imports are applied correctly to client component + await expect(page.locator('.test-css-query-client-normal')).toHaveCSS( + 'background-color', + 'rgb(0, 0, 255)', + ) + await expect(page.locator('.test-css-query-client-normal')).toHaveCSS( + 'color', + 'rgb(255, 255, 0)', + ) + + // Verify that URL query returns a string URL for server component (with filename pattern) + const serverUrlText = await page + .getByTestId('test-css-queries-server-url') + .textContent() + expect(serverUrlText).toMatch(/CSS URL \(server\): .*server-url.*\.css/) + + // Verify that URL query returns a string URL for client component (with filename pattern) + const clientUrlText = await page + .getByTestId('test-css-queries-client-url') + .textContent() + expect(clientUrlText).toMatch(/CSS URL \(client\): .*client-url.*\.css/) + + // Verify that inline and raw queries return strings for server component (should not be collected for SSR) + await expect( + page.getByTestId('test-css-queries-server-inline'), + ).toContainText('CSS Inline (server): string') + await expect(page.getByTestId('test-css-queries-server-raw')).toContainText( + 'CSS Raw (server): string', + ) + + // Verify that inline and raw queries return strings for client component (should not be collected for SSR) + await expect( + page.getByTestId('test-css-queries-client-inline'), + ).toContainText('CSS Inline (client): string') + await expect(page.getByTestId('test-css-queries-client-raw')).toContainText( + 'CSS Raw (client): string', + ) + } } diff --git a/packages/plugin-rsc/examples/basic/src/routes/css-queries/client-inline.css b/packages/plugin-rsc/examples/basic/src/routes/css-queries/client-inline.css new file mode 100644 index 00000000..d536fdcb --- /dev/null +++ b/packages/plugin-rsc/examples/basic/src/routes/css-queries/client-inline.css @@ -0,0 +1,4 @@ +.test-css-query-client-inline { + padding: 8px; + margin: 4px; +} diff --git a/packages/plugin-rsc/examples/basic/src/routes/css-queries/client-raw.css b/packages/plugin-rsc/examples/basic/src/routes/css-queries/client-raw.css new file mode 100644 index 00000000..e7081b56 --- /dev/null +++ b/packages/plugin-rsc/examples/basic/src/routes/css-queries/client-raw.css @@ -0,0 +1,3 @@ +.test-css-query-client-raw { + font-weight: bold; +} diff --git a/packages/plugin-rsc/examples/basic/src/routes/css-queries/client-url.css b/packages/plugin-rsc/examples/basic/src/routes/css-queries/client-url.css new file mode 100644 index 00000000..3c75763d --- /dev/null +++ b/packages/plugin-rsc/examples/basic/src/routes/css-queries/client-url.css @@ -0,0 +1,3 @@ +.test-css-query-client-url { + border: 2px solid rgb(0, 0, 255); +} diff --git a/packages/plugin-rsc/examples/basic/src/routes/css-queries/client.css b/packages/plugin-rsc/examples/basic/src/routes/css-queries/client.css new file mode 100644 index 00000000..ea2ffa8d --- /dev/null +++ b/packages/plugin-rsc/examples/basic/src/routes/css-queries/client.css @@ -0,0 +1,4 @@ +.test-css-query-client-normal { + background-color: rgb(0, 0, 255); + color: rgb(255, 255, 0); +} diff --git a/packages/plugin-rsc/examples/basic/src/routes/css-queries/client.tsx b/packages/plugin-rsc/examples/basic/src/routes/css-queries/client.tsx new file mode 100644 index 00000000..2a30fed3 --- /dev/null +++ b/packages/plugin-rsc/examples/basic/src/routes/css-queries/client.tsx @@ -0,0 +1,34 @@ +'use client' + +// Test CSS imports with special queries (?url, ?inline, ?raw) in client components +// These should not be collected for server-side rendering +import cssUrl from './client-url.css?url' +import cssInline from './client-inline.css?inline' +import cssRaw from './client-raw.css?raw' +import './client.css' // Normal import should still work + +export default function CssQueriesClientTest() { + return ( +
+
+ CSS URL (client): {cssUrl} +
+
+ CSS Inline (client):{' '} + {typeof cssInline === 'string' ? 'string' : 'other'} +
+
+ CSS Raw (client): {typeof cssRaw === 'string' ? 'string' : 'other'} +
+
+ Normal CSS import works (client) +
+
+ ) +} diff --git a/packages/plugin-rsc/examples/basic/src/routes/css-queries/server-inline.css b/packages/plugin-rsc/examples/basic/src/routes/css-queries/server-inline.css new file mode 100644 index 00000000..1148cefc --- /dev/null +++ b/packages/plugin-rsc/examples/basic/src/routes/css-queries/server-inline.css @@ -0,0 +1,4 @@ +.test-css-query-server-inline { + padding: 4px; + margin: 2px; +} diff --git a/packages/plugin-rsc/examples/basic/src/routes/css-queries/server-raw.css b/packages/plugin-rsc/examples/basic/src/routes/css-queries/server-raw.css new file mode 100644 index 00000000..8b7a3d38 --- /dev/null +++ b/packages/plugin-rsc/examples/basic/src/routes/css-queries/server-raw.css @@ -0,0 +1,3 @@ +.test-css-query-server-raw { + text-decoration: underline; +} diff --git a/packages/plugin-rsc/examples/basic/src/routes/css-queries/server-url.css b/packages/plugin-rsc/examples/basic/src/routes/css-queries/server-url.css new file mode 100644 index 00000000..d872b248 --- /dev/null +++ b/packages/plugin-rsc/examples/basic/src/routes/css-queries/server-url.css @@ -0,0 +1,3 @@ +.test-css-query-server-url { + border: 2px solid rgb(255, 0, 0); +} diff --git a/packages/plugin-rsc/examples/basic/src/routes/css-queries/server.css b/packages/plugin-rsc/examples/basic/src/routes/css-queries/server.css new file mode 100644 index 00000000..e73f20d5 --- /dev/null +++ b/packages/plugin-rsc/examples/basic/src/routes/css-queries/server.css @@ -0,0 +1,4 @@ +.test-css-query-server-normal { + background-color: rgb(255, 0, 0); + color: rgb(0, 255, 0); +} diff --git a/packages/plugin-rsc/examples/basic/src/routes/css-queries/server.tsx b/packages/plugin-rsc/examples/basic/src/routes/css-queries/server.tsx new file mode 100644 index 00000000..465fc6ed --- /dev/null +++ b/packages/plugin-rsc/examples/basic/src/routes/css-queries/server.tsx @@ -0,0 +1,39 @@ +// Test CSS imports with special queries (?url, ?inline, ?raw) in server component +// These should not be collected for server-side rendering +import cssUrl from './server-url.css?url' +import cssInline from './server-inline.css?inline' +import cssRaw from './server-raw.css?raw' +import './server.css' // Normal import should still work +import CssQueriesClientTest from './client' + +export default function CssQueriesTest() { + return ( +
+ {/* Server component test */} +
+
+ CSS URL (server): {cssUrl} +
+
+ CSS Inline (server):{' '} + {typeof cssInline === 'string' ? 'string' : 'other'} +
+
+ CSS Raw (server): {typeof cssRaw === 'string' ? 'string' : 'other'} +
+
+ Normal CSS import works (server) +
+
+ + {/* Client component test */} + +
+ ) +} diff --git a/packages/plugin-rsc/examples/basic/src/routes/root.tsx b/packages/plugin-rsc/examples/basic/src/routes/root.tsx index 5a3421ed..b3918838 100644 --- a/packages/plugin-rsc/examples/basic/src/routes/root.tsx +++ b/packages/plugin-rsc/examples/basic/src/routes/root.tsx @@ -29,6 +29,7 @@ import { TestTemporaryReference } from './temporary-reference/client' import { TestUseCache } from './use-cache/server' import { TestHydrationMismatch } from './hydration-mismatch/server' import { TestBrowserOnly } from './browser-only/client' +import CssQueriesTest from './css-queries/server' export function Root(props: { url: URL }) { return ( @@ -70,6 +71,7 @@ export function Root(props: { url: URL }) { + ) diff --git a/packages/plugin-rsc/src/plugin.ts b/packages/plugin-rsc/src/plugin.ts index 8f7f7f0f..9e85ddfb 100644 --- a/packages/plugin-rsc/src/plugin.ts +++ b/packages/plugin-rsc/src/plugin.ts @@ -1459,6 +1459,10 @@ export async function findSourceMapURL( export function vitePluginRscCss( rscCssOptions?: Pick, ): Plugin[] { + function hasSpecialCssQuery(id: string): boolean { + return /[?&](url|inline|raw)(\b|=|&|$)/.test(id) + } + function collectCss(environment: DevEnvironment, entryId: string) { const visited = new Set() const cssIds = new Set() @@ -1475,7 +1479,7 @@ export function vitePluginRscCss( } for (const next of mod?.importedModules ?? []) { if (next.id) { - if (isCSSRequest(next.id)) { + if (isCSSRequest(next.id) && !hasSpecialCssQuery(next.id)) { cssIds.add(next.id) } else { recurse(next.id) From 92608772fae7da233e459c5d7e15161c9f0e16a0 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Thu, 14 Aug 2025 18:06:26 +0900 Subject: [PATCH 2/8] test: cleanup --- .../basic/src/routes/css-queries/client.css | 4 ---- .../basic/src/routes/css-queries/client.tsx | 17 ++++--------- .../basic/src/routes/css-queries/server.css | 4 ---- .../basic/src/routes/css-queries/server.tsx | 24 +++++-------------- .../examples/basic/src/routes/root.tsx | 4 ++-- 5 files changed, 12 insertions(+), 41 deletions(-) delete mode 100644 packages/plugin-rsc/examples/basic/src/routes/css-queries/client.css delete mode 100644 packages/plugin-rsc/examples/basic/src/routes/css-queries/server.css diff --git a/packages/plugin-rsc/examples/basic/src/routes/css-queries/client.css b/packages/plugin-rsc/examples/basic/src/routes/css-queries/client.css deleted file mode 100644 index ea2ffa8d..00000000 --- a/packages/plugin-rsc/examples/basic/src/routes/css-queries/client.css +++ /dev/null @@ -1,4 +0,0 @@ -.test-css-query-client-normal { - background-color: rgb(0, 0, 255); - color: rgb(255, 255, 0); -} diff --git a/packages/plugin-rsc/examples/basic/src/routes/css-queries/client.tsx b/packages/plugin-rsc/examples/basic/src/routes/css-queries/client.tsx index 2a30fed3..bd2b8e8e 100644 --- a/packages/plugin-rsc/examples/basic/src/routes/css-queries/client.tsx +++ b/packages/plugin-rsc/examples/basic/src/routes/css-queries/client.tsx @@ -1,29 +1,20 @@ 'use client' -// Test CSS imports with special queries (?url, ?inline, ?raw) in client components -// These should not be collected for server-side rendering import cssUrl from './client-url.css?url' import cssInline from './client-inline.css?inline' import cssRaw from './client-raw.css?raw' -import './client.css' // Normal import should still work -export default function CssQueriesClientTest() { +export function TestCssQueriesClient() { return ( -
+
CSS URL (client): {cssUrl}
-
+
CSS Inline (client):{' '} {typeof cssInline === 'string' ? 'string' : 'other'}
-
+
CSS Raw (client): {typeof cssRaw === 'string' ? 'string' : 'other'}
diff --git a/packages/plugin-rsc/examples/basic/src/routes/css-queries/server.css b/packages/plugin-rsc/examples/basic/src/routes/css-queries/server.css deleted file mode 100644 index e73f20d5..00000000 --- a/packages/plugin-rsc/examples/basic/src/routes/css-queries/server.css +++ /dev/null @@ -1,4 +0,0 @@ -.test-css-query-server-normal { - background-color: rgb(255, 0, 0); - color: rgb(0, 255, 0); -} diff --git a/packages/plugin-rsc/examples/basic/src/routes/css-queries/server.tsx b/packages/plugin-rsc/examples/basic/src/routes/css-queries/server.tsx index 465fc6ed..274cc058 100644 --- a/packages/plugin-rsc/examples/basic/src/routes/css-queries/server.tsx +++ b/packages/plugin-rsc/examples/basic/src/routes/css-queries/server.tsx @@ -1,39 +1,27 @@ -// Test CSS imports with special queries (?url, ?inline, ?raw) in server component -// These should not be collected for server-side rendering import cssUrl from './server-url.css?url' import cssInline from './server-inline.css?inline' import cssRaw from './server-raw.css?raw' -import './server.css' // Normal import should still work -import CssQueriesClientTest from './client' +import { TestCssQueriesClient } from './client' -export default function CssQueriesTest() { +export function TestCssQueries() { return (
- {/* Server component test */} -
+
CSS URL (server): {cssUrl}
-
+
CSS Inline (server):{' '} {typeof cssInline === 'string' ? 'string' : 'other'}
-
+
CSS Raw (server): {typeof cssRaw === 'string' ? 'string' : 'other'}
Normal CSS import works (server)
- - {/* Client component test */} - +
) } diff --git a/packages/plugin-rsc/examples/basic/src/routes/root.tsx b/packages/plugin-rsc/examples/basic/src/routes/root.tsx index 1fe79563..04b74343 100644 --- a/packages/plugin-rsc/examples/basic/src/routes/root.tsx +++ b/packages/plugin-rsc/examples/basic/src/routes/root.tsx @@ -30,12 +30,12 @@ import { TestUseCache } from './use-cache/server' import { TestReactCache } from './react-cache/server' import { TestHydrationMismatch } from './hydration-mismatch/server' import { TestBrowserOnly } from './browser-only/client' -import CssQueriesTest from './css-queries/server' import { TestTransitiveCjsClient } from './deps/transitive-cjs/client' import TestDepCssInServer from '@vitejs/test-dep-css-in-server/server' import { TestHmrSharedServer } from './hmr-shared/server' import { TestHmrSharedClient } from './hmr-shared/client' import { TestHmrSharedAtomic } from './hmr-shared/atomic/server' +import { TestCssQueries } from './css-queries/server' export function Root(props: { url: URL }) { return ( @@ -84,7 +84,7 @@ export function Root(props: { url: URL }) { - + ) From 4b783f31c9921eb2dc7942463fd2431a2a8fb4ea Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Thu, 14 Aug 2025 18:14:46 +0900 Subject: [PATCH 3/8] test: cleanup --- .../src/routes/css-queries/client-inline.css | 2 +- .../basic/src/routes/css-queries/client-raw.css | 2 +- .../basic/src/routes/css-queries/client-url.css | 2 +- .../basic/src/routes/css-queries/client.tsx | 15 +++++---------- .../src/routes/css-queries/server-inline.css | 2 +- .../basic/src/routes/css-queries/server-raw.css | 2 +- .../basic/src/routes/css-queries/server-url.css | 2 +- .../basic/src/routes/css-queries/server.tsx | 15 +++++---------- 8 files changed, 16 insertions(+), 26 deletions(-) diff --git a/packages/plugin-rsc/examples/basic/src/routes/css-queries/client-inline.css b/packages/plugin-rsc/examples/basic/src/routes/css-queries/client-inline.css index d536fdcb..8497860b 100644 --- a/packages/plugin-rsc/examples/basic/src/routes/css-queries/client-inline.css +++ b/packages/plugin-rsc/examples/basic/src/routes/css-queries/client-inline.css @@ -1,4 +1,4 @@ -.test-css-query-client-inline { +.test-css-inline-client { padding: 8px; margin: 4px; } diff --git a/packages/plugin-rsc/examples/basic/src/routes/css-queries/client-raw.css b/packages/plugin-rsc/examples/basic/src/routes/css-queries/client-raw.css index e7081b56..3f8ab757 100644 --- a/packages/plugin-rsc/examples/basic/src/routes/css-queries/client-raw.css +++ b/packages/plugin-rsc/examples/basic/src/routes/css-queries/client-raw.css @@ -1,3 +1,3 @@ -.test-css-query-client-raw { +.test-css-raw-client { font-weight: bold; } diff --git a/packages/plugin-rsc/examples/basic/src/routes/css-queries/client-url.css b/packages/plugin-rsc/examples/basic/src/routes/css-queries/client-url.css index 3c75763d..33bae49f 100644 --- a/packages/plugin-rsc/examples/basic/src/routes/css-queries/client-url.css +++ b/packages/plugin-rsc/examples/basic/src/routes/css-queries/client-url.css @@ -1,3 +1,3 @@ -.test-css-query-client-url { +.test-css-url-client { border: 2px solid rgb(0, 0, 255); } diff --git a/packages/plugin-rsc/examples/basic/src/routes/css-queries/client.tsx b/packages/plugin-rsc/examples/basic/src/routes/css-queries/client.tsx index bd2b8e8e..0259459f 100644 --- a/packages/plugin-rsc/examples/basic/src/routes/css-queries/client.tsx +++ b/packages/plugin-rsc/examples/basic/src/routes/css-queries/client.tsx @@ -7,18 +7,13 @@ import cssRaw from './client-raw.css?raw' export function TestCssQueriesClient() { return (
-
- CSS URL (client): {cssUrl} -
-
- CSS Inline (client):{' '} +
test-css-url-client: {cssUrl}
+
+ test-css-inline-client:{' '} {typeof cssInline === 'string' ? 'string' : 'other'}
-
- CSS Raw (client): {typeof cssRaw === 'string' ? 'string' : 'other'} -
-
- Normal CSS import works (client) +
+ test-css-raw-client: {typeof cssRaw === 'string' ? 'string' : 'other'}
) diff --git a/packages/plugin-rsc/examples/basic/src/routes/css-queries/server-inline.css b/packages/plugin-rsc/examples/basic/src/routes/css-queries/server-inline.css index 1148cefc..2ee4fb29 100644 --- a/packages/plugin-rsc/examples/basic/src/routes/css-queries/server-inline.css +++ b/packages/plugin-rsc/examples/basic/src/routes/css-queries/server-inline.css @@ -1,4 +1,4 @@ -.test-css-query-server-inline { +.test-css-inline-server { padding: 4px; margin: 2px; } diff --git a/packages/plugin-rsc/examples/basic/src/routes/css-queries/server-raw.css b/packages/plugin-rsc/examples/basic/src/routes/css-queries/server-raw.css index 8b7a3d38..5217741c 100644 --- a/packages/plugin-rsc/examples/basic/src/routes/css-queries/server-raw.css +++ b/packages/plugin-rsc/examples/basic/src/routes/css-queries/server-raw.css @@ -1,3 +1,3 @@ -.test-css-query-server-raw { +.test-css-raw-server { text-decoration: underline; } diff --git a/packages/plugin-rsc/examples/basic/src/routes/css-queries/server-url.css b/packages/plugin-rsc/examples/basic/src/routes/css-queries/server-url.css index d872b248..394a1819 100644 --- a/packages/plugin-rsc/examples/basic/src/routes/css-queries/server-url.css +++ b/packages/plugin-rsc/examples/basic/src/routes/css-queries/server-url.css @@ -1,3 +1,3 @@ -.test-css-query-server-url { +.test-css-url-server { border: 2px solid rgb(255, 0, 0); } diff --git a/packages/plugin-rsc/examples/basic/src/routes/css-queries/server.tsx b/packages/plugin-rsc/examples/basic/src/routes/css-queries/server.tsx index 274cc058..3556c651 100644 --- a/packages/plugin-rsc/examples/basic/src/routes/css-queries/server.tsx +++ b/packages/plugin-rsc/examples/basic/src/routes/css-queries/server.tsx @@ -7,18 +7,13 @@ export function TestCssQueries() { return (
-
- CSS URL (server): {cssUrl} -
-
- CSS Inline (server):{' '} +
test-css-url-server: {cssUrl}
+
+ test-css-inline-server:{' '} {typeof cssInline === 'string' ? 'string' : 'other'}
-
- CSS Raw (server): {typeof cssRaw === 'string' ? 'string' : 'other'} -
-
- Normal CSS import works (server) +
+ test-css-raw-server: {typeof cssRaw === 'string' ? 'string' : 'other'}
From afa4da6e7b4678914630876708391df8dd034cf7 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Thu, 14 Aug 2025 18:22:17 +0900 Subject: [PATCH 4/8] fix: fix css skip --- .../basic/src/routes/css-queries/client-inline.css | 3 +-- .../basic/src/routes/css-queries/client-raw.css | 2 +- .../basic/src/routes/css-queries/client-url.css | 2 +- .../examples/basic/src/routes/css-queries/client.tsx | 12 ++++++++++++ packages/plugin-rsc/examples/basic/src/server.tsx | 2 +- packages/plugin-rsc/src/plugin.ts | 5 ++++- 6 files changed, 20 insertions(+), 6 deletions(-) diff --git a/packages/plugin-rsc/examples/basic/src/routes/css-queries/client-inline.css b/packages/plugin-rsc/examples/basic/src/routes/css-queries/client-inline.css index 8497860b..dbbc8070 100644 --- a/packages/plugin-rsc/examples/basic/src/routes/css-queries/client-inline.css +++ b/packages/plugin-rsc/examples/basic/src/routes/css-queries/client-inline.css @@ -1,4 +1,3 @@ .test-css-inline-client { - padding: 8px; - margin: 4px; + color: rgb(255, 50, 0); } diff --git a/packages/plugin-rsc/examples/basic/src/routes/css-queries/client-raw.css b/packages/plugin-rsc/examples/basic/src/routes/css-queries/client-raw.css index 3f8ab757..19b0428d 100644 --- a/packages/plugin-rsc/examples/basic/src/routes/css-queries/client-raw.css +++ b/packages/plugin-rsc/examples/basic/src/routes/css-queries/client-raw.css @@ -1,3 +1,3 @@ .test-css-raw-client { - font-weight: bold; + color: rgb(255, 0, 0); } diff --git a/packages/plugin-rsc/examples/basic/src/routes/css-queries/client-url.css b/packages/plugin-rsc/examples/basic/src/routes/css-queries/client-url.css index 33bae49f..95c67acb 100644 --- a/packages/plugin-rsc/examples/basic/src/routes/css-queries/client-url.css +++ b/packages/plugin-rsc/examples/basic/src/routes/css-queries/client-url.css @@ -1,3 +1,3 @@ .test-css-url-client { - border: 2px solid rgb(0, 0, 255); + color: rgb(255, 100, 0); } diff --git a/packages/plugin-rsc/examples/basic/src/routes/css-queries/client.tsx b/packages/plugin-rsc/examples/basic/src/routes/css-queries/client.tsx index 0259459f..dac1d537 100644 --- a/packages/plugin-rsc/examples/basic/src/routes/css-queries/client.tsx +++ b/packages/plugin-rsc/examples/basic/src/routes/css-queries/client.tsx @@ -3,10 +3,22 @@ import cssUrl from './client-url.css?url' import cssInline from './client-inline.css?inline' import cssRaw from './client-raw.css?raw' +import React from 'react' export function TestCssQueriesClient() { + const [enabled, setEnabled] = React.useState(false) return (
+ + {enabled && ( + <> + + + + + )}
test-css-url-client: {cssUrl}
test-css-inline-client:{' '} diff --git a/packages/plugin-rsc/examples/basic/src/server.tsx b/packages/plugin-rsc/examples/basic/src/server.tsx index e4ba468d..27808ff1 100644 --- a/packages/plugin-rsc/examples/basic/src/server.tsx +++ b/packages/plugin-rsc/examples/basic/src/server.tsx @@ -26,7 +26,7 @@ export default async function handler(request: Request): Promise { `default-src 'self';`, // `unsafe-eval` is required during dev since React uses eval for findSourceMapURL feature `script-src 'self' 'nonce-${nonce}' ${import.meta.env.DEV ? `'unsafe-eval'` : ``};`, - `style-src 'self' 'nonce-${nonce}';`, + `style-src 'self' 'unsafe-inline';`, // allow blob: worker for Vite server ping shared worker import.meta.hot && `worker-src 'self' blob:;`, ] diff --git a/packages/plugin-rsc/src/plugin.ts b/packages/plugin-rsc/src/plugin.ts index c8c29a90..acdf6ab8 100644 --- a/packages/plugin-rsc/src/plugin.ts +++ b/packages/plugin-rsc/src/plugin.ts @@ -1684,7 +1684,10 @@ export function vitePluginRscCss( } for (const next of mod?.importedModules ?? []) { if (next.id) { - if (isCSSRequest(next.id) && !hasSpecialCssQuery(next.id)) { + if (isCSSRequest(next.id)) { + if (hasSpecialCssQuery(next.id)) { + continue + } cssIds.add(next.id) } else { recurse(next.id) From 31de4d72c7eeb816af89ac6830efc88c32ad6051 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Thu, 14 Aug 2025 18:32:24 +0900 Subject: [PATCH 5/8] test: wip --- .../basic/src/routes/css-queries/client.tsx | 20 ++++++++++++++----- .../src/routes/css-queries/server-inline.css | 3 +-- .../src/routes/css-queries/server-raw.css | 2 +- .../src/routes/css-queries/server-url.css | 2 +- .../basic/src/routes/css-queries/server.tsx | 6 +++++- 5 files changed, 23 insertions(+), 10 deletions(-) diff --git a/packages/plugin-rsc/examples/basic/src/routes/css-queries/client.tsx b/packages/plugin-rsc/examples/basic/src/routes/css-queries/client.tsx index dac1d537..8f95452f 100644 --- a/packages/plugin-rsc/examples/basic/src/routes/css-queries/client.tsx +++ b/packages/plugin-rsc/examples/basic/src/routes/css-queries/client.tsx @@ -5,18 +5,28 @@ import cssInline from './client-inline.css?inline' import cssRaw from './client-raw.css?raw' import React from 'react' -export function TestCssQueriesClient() { +export function TestCssQueriesClient(props: { + serverUrl: string + serverInline: string + serverRaw: string +}) { const [enabled, setEnabled] = React.useState(false) + + function urlWithHmr(href: string) { + return href + (import.meta.env.BASE_URL ? '?t=' + Date.now() : '') + } + return (
- + {enabled && ( <> - + + + + )}
test-css-url-client: {cssUrl}
diff --git a/packages/plugin-rsc/examples/basic/src/routes/css-queries/server-inline.css b/packages/plugin-rsc/examples/basic/src/routes/css-queries/server-inline.css index 2ee4fb29..4f007865 100644 --- a/packages/plugin-rsc/examples/basic/src/routes/css-queries/server-inline.css +++ b/packages/plugin-rsc/examples/basic/src/routes/css-queries/server-inline.css @@ -1,4 +1,3 @@ .test-css-inline-server { - padding: 4px; - margin: 2px; + color: rgb(0, 255, 50); } diff --git a/packages/plugin-rsc/examples/basic/src/routes/css-queries/server-raw.css b/packages/plugin-rsc/examples/basic/src/routes/css-queries/server-raw.css index 5217741c..c7cdd3a5 100644 --- a/packages/plugin-rsc/examples/basic/src/routes/css-queries/server-raw.css +++ b/packages/plugin-rsc/examples/basic/src/routes/css-queries/server-raw.css @@ -1,3 +1,3 @@ .test-css-raw-server { - text-decoration: underline; + color: rgb(0, 255, 0); } diff --git a/packages/plugin-rsc/examples/basic/src/routes/css-queries/server-url.css b/packages/plugin-rsc/examples/basic/src/routes/css-queries/server-url.css index 394a1819..12f89bc5 100644 --- a/packages/plugin-rsc/examples/basic/src/routes/css-queries/server-url.css +++ b/packages/plugin-rsc/examples/basic/src/routes/css-queries/server-url.css @@ -1,3 +1,3 @@ .test-css-url-server { - border: 2px solid rgb(255, 0, 0); + color: rgb(0, 200, 100); } diff --git a/packages/plugin-rsc/examples/basic/src/routes/css-queries/server.tsx b/packages/plugin-rsc/examples/basic/src/routes/css-queries/server.tsx index 3556c651..0b75e8d8 100644 --- a/packages/plugin-rsc/examples/basic/src/routes/css-queries/server.tsx +++ b/packages/plugin-rsc/examples/basic/src/routes/css-queries/server.tsx @@ -6,6 +6,11 @@ import { TestCssQueriesClient } from './client' export function TestCssQueries() { return (
+
test-css-url-server: {cssUrl}
@@ -16,7 +21,6 @@ export function TestCssQueries() { test-css-raw-server: {typeof cssRaw === 'string' ? 'string' : 'other'}
-
) } From 1fc1bab96c0a5437b4c2500c01fe1f890ffe09f3 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Thu, 14 Aug 2025 18:43:03 +0900 Subject: [PATCH 6/8] test: tweak --- packages/plugin-rsc/e2e/basic.test.ts | 87 +++++-------------- .../src/routes/css-queries/server-url.css | 2 +- 2 files changed, 21 insertions(+), 68 deletions(-) diff --git a/packages/plugin-rsc/e2e/basic.test.ts b/packages/plugin-rsc/e2e/basic.test.ts index b6885165..0c174c1f 100644 --- a/packages/plugin-rsc/e2e/basic.test.ts +++ b/packages/plugin-rsc/e2e/basic.test.ts @@ -1156,74 +1156,27 @@ function defineTest(f: Fixture) { test('css queries @js', async ({ page }) => { await page.goto(f.url()) await waitForHydration(page) - await testCssQueries(page) - }) - - testNoJs('css queries @nojs', async ({ page }) => { - await page.goto(f.url()) - await testCssQueries(page) - }) - - async function testCssQueries(page: Page) { - // Test that CSS with special queries (?url, ?inline, ?raw) don't break the page - // and that normal CSS imports still work for both server and client components - - // Server component tests - await expect( - page.getByTestId('test-css-queries-server-normal'), - ).toBeVisible() - - // Client component tests - await expect( - page.getByTestId('test-css-queries-client-normal'), - ).toBeVisible() - - // Test that normal CSS imports are applied correctly to server component - await expect(page.locator('.test-css-query-server-normal')).toHaveCSS( - 'background-color', - 'rgb(255, 0, 0)', - ) - await expect(page.locator('.test-css-query-server-normal')).toHaveCSS( - 'color', - 'rgb(0, 255, 0)', - ) - // Test that normal CSS imports are applied correctly to client component - await expect(page.locator('.test-css-query-client-normal')).toHaveCSS( - 'background-color', - 'rgb(0, 0, 255)', - ) - await expect(page.locator('.test-css-query-client-normal')).toHaveCSS( - 'color', - 'rgb(255, 255, 0)', - ) - - // Verify that URL query returns a string URL for server component (with filename pattern) - const serverUrlText = await page - .getByTestId('test-css-queries-server-url') - .textContent() - expect(serverUrlText).toMatch(/CSS URL \(server\): .*server-url.*\.css/) - - // Verify that URL query returns a string URL for client component (with filename pattern) - const clientUrlText = await page - .getByTestId('test-css-queries-client-url') - .textContent() - expect(clientUrlText).toMatch(/CSS URL \(client\): .*client-url.*\.css/) + const tests = [ + ['.test-css-url-client', 'rgb(255, 100, 0)'], + ['.test-css-inline-client', 'rgb(255, 50, 0)'], + ['.test-css-raw-client', 'rgb(255, 0, 0)'], + ['.test-css-url-server', 'rgb(0, 255, 100)'], + ['.test-css-inline-server', 'rgb(0, 255, 50)'], + ['.test-css-raw-server', 'rgb(0, 255, 0)'], + ] as const + + // css with queries are not injected automatically + for (const [selector] of tests) { + await expect(page.locator(selector)).toHaveCSS('color', 'rgb(0, 0, 0)') + } - // Verify that inline and raw queries return strings for server component (should not be collected for SSR) - await expect( - page.getByTestId('test-css-queries-server-inline'), - ).toContainText('CSS Inline (server): string') - await expect(page.getByTestId('test-css-queries-server-raw')).toContainText( - 'CSS Raw (server): string', - ) + // inject css manually + await page.getByRole('button', { name: 'test-css-queries' }).click() - // Verify that inline and raw queries return strings for client component (should not be collected for SSR) - await expect( - page.getByTestId('test-css-queries-client-inline'), - ).toContainText('CSS Inline (client): string') - await expect(page.getByTestId('test-css-queries-client-raw')).toContainText( - 'CSS Raw (client): string', - ) - } + // verify styles + for (const [selector, color] of tests) { + await expect(page.locator(selector)).toHaveCSS('color', color) + } + }) } diff --git a/packages/plugin-rsc/examples/basic/src/routes/css-queries/server-url.css b/packages/plugin-rsc/examples/basic/src/routes/css-queries/server-url.css index 12f89bc5..167620c4 100644 --- a/packages/plugin-rsc/examples/basic/src/routes/css-queries/server-url.css +++ b/packages/plugin-rsc/examples/basic/src/routes/css-queries/server-url.css @@ -1,3 +1,3 @@ .test-css-url-server { - color: rgb(0, 200, 100); + color: rgb(0, 255, 100); } From b6eae4206454b870531df6fb3745bdbce8cd42f1 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Thu, 14 Aug 2025 18:43:41 +0900 Subject: [PATCH 7/8] test: tweak --- packages/plugin-rsc/e2e/basic.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/plugin-rsc/e2e/basic.test.ts b/packages/plugin-rsc/e2e/basic.test.ts index 0c174c1f..6a9bf957 100644 --- a/packages/plugin-rsc/e2e/basic.test.ts +++ b/packages/plugin-rsc/e2e/basic.test.ts @@ -1153,7 +1153,7 @@ function defineTest(f: Fixture) { ) }) - test('css queries @js', async ({ page }) => { + test('css queries', async ({ page }) => { await page.goto(f.url()) await waitForHydration(page) From c9ec294a927f9ab971147a6767710db675d01a7a Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Thu, 14 Aug 2025 19:02:22 +0900 Subject: [PATCH 8/8] test: cleanup --- .../basic/src/routes/css-queries/client.tsx | 16 +++++++--------- .../basic/src/routes/css-queries/server.tsx | 11 +++-------- 2 files changed, 10 insertions(+), 17 deletions(-) diff --git a/packages/plugin-rsc/examples/basic/src/routes/css-queries/client.tsx b/packages/plugin-rsc/examples/basic/src/routes/css-queries/client.tsx index 8f95452f..73e0004e 100644 --- a/packages/plugin-rsc/examples/basic/src/routes/css-queries/client.tsx +++ b/packages/plugin-rsc/examples/basic/src/routes/css-queries/client.tsx @@ -13,7 +13,10 @@ export function TestCssQueriesClient(props: { const [enabled, setEnabled] = React.useState(false) function urlWithHmr(href: string) { - return href + (import.meta.env.BASE_URL ? '?t=' + Date.now() : '') + if (import.meta.hot) { + href += '?t=' + Date.now() + } + return href } return ( @@ -29,14 +32,9 @@ export function TestCssQueriesClient(props: { )} -
test-css-url-client: {cssUrl}
-
- test-css-inline-client:{' '} - {typeof cssInline === 'string' ? 'string' : 'other'} -
-
- test-css-raw-client: {typeof cssRaw === 'string' ? 'string' : 'other'} -
+
test-css-url-client
+
test-css-inline-client
+
test-css-raw-client
) } diff --git a/packages/plugin-rsc/examples/basic/src/routes/css-queries/server.tsx b/packages/plugin-rsc/examples/basic/src/routes/css-queries/server.tsx index 0b75e8d8..5b3e1c2b 100644 --- a/packages/plugin-rsc/examples/basic/src/routes/css-queries/server.tsx +++ b/packages/plugin-rsc/examples/basic/src/routes/css-queries/server.tsx @@ -12,14 +12,9 @@ export function TestCssQueries() { serverRaw={cssRaw} />
-
test-css-url-server: {cssUrl}
-
- test-css-inline-server:{' '} - {typeof cssInline === 'string' ? 'string' : 'other'} -
-
- test-css-raw-server: {typeof cssRaw === 'string' ? 'string' : 'other'} -
+
test-css-url-server
+
test-css-inline-server
+
test-css-raw-server
)