diff --git a/packages/plugin-rsc/e2e/basic.test.ts b/packages/plugin-rsc/e2e/basic.test.ts index f3cad316..6a9bf957 100644 --- a/packages/plugin-rsc/e2e/basic.test.ts +++ b/packages/plugin-rsc/e2e/basic.test.ts @@ -1152,4 +1152,31 @@ function defineTest(f: Fixture) { '(cacheFnCount = 4, nonCacheFnCount = 6)', ) }) + + test('css queries', async ({ page }) => { + await page.goto(f.url()) + await waitForHydration(page) + + 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)') + } + + // inject css manually + await page.getByRole('button', { name: 'test-css-queries' }).click() + + // 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/client-inline.css b/packages/plugin-rsc/examples/basic/src/routes/css-queries/client-inline.css new file mode 100644 index 00000000..dbbc8070 --- /dev/null +++ b/packages/plugin-rsc/examples/basic/src/routes/css-queries/client-inline.css @@ -0,0 +1,3 @@ +.test-css-inline-client { + 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 new file mode 100644 index 00000000..19b0428d --- /dev/null +++ b/packages/plugin-rsc/examples/basic/src/routes/css-queries/client-raw.css @@ -0,0 +1,3 @@ +.test-css-raw-client { + 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 new file mode 100644 index 00000000..95c67acb --- /dev/null +++ b/packages/plugin-rsc/examples/basic/src/routes/css-queries/client-url.css @@ -0,0 +1,3 @@ +.test-css-url-client { + 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 new file mode 100644 index 00000000..73e0004e --- /dev/null +++ b/packages/plugin-rsc/examples/basic/src/routes/css-queries/client.tsx @@ -0,0 +1,40 @@ +'use client' + +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(props: { + serverUrl: string + serverInline: string + serverRaw: string +}) { + const [enabled, setEnabled] = React.useState(false) + + function urlWithHmr(href: string) { + if (import.meta.hot) { + href += '?t=' + Date.now() + } + return href + } + + return ( +
+ + {enabled && ( + <> + + + + + + + + )} +
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-inline.css b/packages/plugin-rsc/examples/basic/src/routes/css-queries/server-inline.css new file mode 100644 index 00000000..4f007865 --- /dev/null +++ b/packages/plugin-rsc/examples/basic/src/routes/css-queries/server-inline.css @@ -0,0 +1,3 @@ +.test-css-inline-server { + 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 new file mode 100644 index 00000000..c7cdd3a5 --- /dev/null +++ b/packages/plugin-rsc/examples/basic/src/routes/css-queries/server-raw.css @@ -0,0 +1,3 @@ +.test-css-raw-server { + 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 new file mode 100644 index 00000000..167620c4 --- /dev/null +++ b/packages/plugin-rsc/examples/basic/src/routes/css-queries/server-url.css @@ -0,0 +1,3 @@ +.test-css-url-server { + color: rgb(0, 255, 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 new file mode 100644 index 00000000..5b3e1c2b --- /dev/null +++ b/packages/plugin-rsc/examples/basic/src/routes/css-queries/server.tsx @@ -0,0 +1,21 @@ +import cssUrl from './server-url.css?url' +import cssInline from './server-inline.css?inline' +import cssRaw from './server-raw.css?raw' +import { TestCssQueriesClient } from './client' + +export function TestCssQueries() { + return ( +
+ +
+
test-css-url-server
+
test-css-inline-server
+
test-css-raw-server
+
+
+ ) +} diff --git a/packages/plugin-rsc/examples/basic/src/routes/root.tsx b/packages/plugin-rsc/examples/basic/src/routes/root.tsx index 41d73ace..04b74343 100644 --- a/packages/plugin-rsc/examples/basic/src/routes/root.tsx +++ b/packages/plugin-rsc/examples/basic/src/routes/root.tsx @@ -35,6 +35,7 @@ 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 ( @@ -83,6 +84,7 @@ export function Root(props: { url: URL }) { + ) 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 68fdcce8..acdf6ab8 100644 --- a/packages/plugin-rsc/src/plugin.ts +++ b/packages/plugin-rsc/src/plugin.ts @@ -1664,6 +1664,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() @@ -1681,6 +1685,9 @@ export function vitePluginRscCss( for (const next of mod?.importedModules ?? []) { if (next.id) { if (isCSSRequest(next.id)) { + if (hasSpecialCssQuery(next.id)) { + continue + } cssIds.add(next.id) } else { recurse(next.id)