diff --git a/packages/plugin-rsc/e2e/basic.test.ts b/packages/plugin-rsc/e2e/basic.test.ts index 5cf1c21f..3860cf80 100644 --- a/packages/plugin-rsc/e2e/basic.test.ts +++ b/packages/plugin-rsc/e2e/basic.test.ts @@ -473,6 +473,22 @@ function defineTest(f: Fixture) { 'color', 'rgb(255, 165, 0)', ) + await expectNoDuplicateServerCss(page) + }) + + async function expectNoDuplicateServerCss(page: Page) { + // check only manually inserted stylesheet link exists + // (toHaveAttribute passes only when locator matches single element) + await expect(page.locator('link[rel="stylesheet"]')).toHaveAttribute( + 'href', + '/test-style-server-manual.css', + ) + } + + test('no duplicate server css', async ({ page }) => { + await page.goto(f.url()) + await waitForHydration(page) + await expectNoDuplicateServerCss(page) }) test('adding/removing css client @js', async ({ page }) => { @@ -557,6 +573,7 @@ function defineTest(f: Fixture) { 'color', 'rgb(255, 165, 0)', ) + await expectNoDuplicateServerCss(page) }) // TODO: need a way to add/remove links on server hmr. for now, it requires a manually reload. diff --git a/packages/plugin-rsc/examples/basic/public/test.css b/packages/plugin-rsc/examples/basic/public/test-style-server-manual.css similarity index 100% rename from packages/plugin-rsc/examples/basic/public/test.css rename to packages/plugin-rsc/examples/basic/public/test-style-server-manual.css diff --git a/packages/plugin-rsc/examples/basic/src/routes/style-server/server.tsx b/packages/plugin-rsc/examples/basic/src/routes/style-server/server.tsx index 66166992..4a6e2180 100644 --- a/packages/plugin-rsc/examples/basic/src/routes/style-server/server.tsx +++ b/packages/plugin-rsc/examples/basic/src/routes/style-server/server.tsx @@ -10,7 +10,7 @@ export function TestStyleServer() {
test-style-server-manual
diff --git a/packages/plugin-rsc/src/plugin.ts b/packages/plugin-rsc/src/plugin.ts index 1181c7d1..71fc7c00 100644 --- a/packages/plugin-rsc/src/plugin.ts +++ b/packages/plugin-rsc/src/plugin.ts @@ -837,7 +837,8 @@ window.__vite_plugin_react_preamble_installed__ = true; const resolvedEntry = await this.resolve(source) assert(resolvedEntry, `[vite-rsc] failed to resolve entry '${source}'`) code += `await import(${JSON.stringify(resolvedEntry.id)});` - // TODO: this doesn't have to wait for "vite:beforeUpdate" and should do it right after browser css import. + // server css is normally removed via `RemoveDuplicateServerCss` on useEffect. + // this also makes sure they are removed on hmr in case initial rendering failed. code += /* js */ ` const ssrCss = document.querySelectorAll("link[rel='stylesheet']"); import.meta.hot.on("vite:beforeUpdate", () => { @@ -1910,6 +1911,36 @@ export function vitePluginRscCss( } }, }, + createVirtualPlugin( + 'vite-rsc/remove-duplicate-server-css', + async function () { + // Remove duplicate css during dev due to server rendered and client inline