From 7f5243f9f174d3b4d64cb922436de1758d270352 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Sun, 14 Sep 2025 16:10:29 +0900 Subject: [PATCH 1/5] fix(rsc): remove server style when css import is removed --- .../examples/basic/src/routes/style-server/server.tsx | 4 ++++ packages/plugin-rsc/src/plugin.ts | 11 +++++++++++ 2 files changed, 15 insertions(+) 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 da4bafaf..97b5fa36 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 @@ -27,3 +27,7 @@ export function TestStyleServer() { ) } + +// add no-op `import.meta.hot` to trigger `prune` event +// https://github.com/vitejs/vite/blob/84079a84ad94de4c1ef4f1bdb2ab448ff2c01196/packages/vite/src/node/plugins/importAnalysis.ts#L829-L840 +import.meta.hot diff --git a/packages/plugin-rsc/src/plugin.ts b/packages/plugin-rsc/src/plugin.ts index acabe24c..5d07764c 100644 --- a/packages/plugin-rsc/src/plugin.ts +++ b/packages/plugin-rsc/src/plugin.ts @@ -2030,6 +2030,17 @@ function vitePluginRscCss( }, { name: 'rsc:importer-resources', + configureServer(server) { + const hot = server.environments.rsc!.hot + const original = hot.send + hot.send = function (this, ...args: any[]) { + const e = args[0] as vite.PrunePayload + if (e && typeof e === 'object' && e.type === 'prune') { + console.log('[rsc:prune]', e) + } + return original.apply(this, args as any) + } + }, async transform(code, id) { if (!code.includes('import.meta.viteRsc.loadCss')) return From 00327c8562627524ec46e72de26722572acd764f Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Sun, 14 Sep 2025 17:59:18 +0900 Subject: [PATCH 2/5] wip --- .../examples/basic/src/routes/style-server/server.tsx | 4 ++-- packages/plugin-rsc/src/plugin.ts | 11 ++++++++++- 2 files changed, 12 insertions(+), 3 deletions(-) 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 97b5fa36..a7132153 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 @@ -28,6 +28,6 @@ export function TestStyleServer() { ) } -// add no-op `import.meta.hot` to trigger `prune` event -// https://github.com/vitejs/vite/blob/84079a84ad94de4c1ef4f1bdb2ab448ff2c01196/packages/vite/src/node/plugins/importAnalysis.ts#L829-L840 +// add no-op `import.meta.hot` to trigger `prune` event. +// this is needed until we land https://github.com/vitejs/vite/pull/20768 import.meta.hot diff --git a/packages/plugin-rsc/src/plugin.ts b/packages/plugin-rsc/src/plugin.ts index 5d07764c..a1e28aa6 100644 --- a/packages/plugin-rsc/src/plugin.ts +++ b/packages/plugin-rsc/src/plugin.ts @@ -1026,6 +1026,11 @@ import.meta.hot.on("rsc:update", () => { document.querySelectorAll("vite-error-overlay").forEach((n) => n.close()) }); ` + // remove stylesheet links when css import is removed on rsc envrionment + const onRscPrune = (e: vite.PrunePayload) => { + console.log('[rsc:prune]', e) + } + code += `import.meta.hot.on("rsc:prune", ${onRscPrune});` return code }, ), @@ -2036,7 +2041,11 @@ function vitePluginRscCss( hot.send = function (this, ...args: any[]) { const e = args[0] as vite.PrunePayload if (e && typeof e === 'object' && e.type === 'prune') { - console.log('[rsc:prune]', e) + server.environments.client.hot.send({ + type: 'custom', + event: 'rsc:prune', + data: e, + }) } return original.apply(this, args as any) } From 5f9aec41e60c6d4eff9ec06eeafdcca10fc458ce Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Sun, 14 Sep 2025 18:11:50 +0900 Subject: [PATCH 3/5] wip --- packages/plugin-rsc/src/plugin.ts | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/packages/plugin-rsc/src/plugin.ts b/packages/plugin-rsc/src/plugin.ts index a1e28aa6..8b06b2e3 100644 --- a/packages/plugin-rsc/src/plugin.ts +++ b/packages/plugin-rsc/src/plugin.ts @@ -1027,10 +1027,16 @@ import.meta.hot.on("rsc:update", () => { }); ` // remove stylesheet links when css import is removed on rsc envrionment - const onRscPrune = (e: vite.PrunePayload) => { - console.log('[rsc:prune]', e) - } - code += `import.meta.hot.on("rsc:prune", ${onRscPrune});` + code += `import.meta.hot.on("rsc:prune", ${(e: vite.PrunePayload) => { + const nodes = document.querySelectorAll( + "link[rel='stylesheet']", + ) + nodes.forEach((node) => { + if (e.paths.includes(node.dataset.rscCssHref!)) { + node.remove() + } + }) + }});` return code }, ), @@ -2228,6 +2234,7 @@ function generateResourcesCode(depsCode: string, manager: RscPluginManager) { rel: 'stylesheet', precedence: 'vite-rsc/importer-resources', href: href, + 'data-rsc-css-href': href, }), ), RemoveDuplicateServerCss && From 21d86a5c92b726e8f8dea5718782d465f190d7f3 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Sun, 14 Sep 2025 19:00:14 +0900 Subject: [PATCH 4/5] test: update --- packages/plugin-rsc/e2e/basic.test.ts | 43 ++++++++++++------- .../basic/src/routes/style-server/server2.css | 3 ++ 2 files changed, 30 insertions(+), 16 deletions(-) create mode 100644 packages/plugin-rsc/examples/basic/src/routes/style-server/server2.css diff --git a/packages/plugin-rsc/e2e/basic.test.ts b/packages/plugin-rsc/e2e/basic.test.ts index d3cdc551..1306ee31 100644 --- a/packages/plugin-rsc/e2e/basic.test.ts +++ b/packages/plugin-rsc/e2e/basic.test.ts @@ -855,31 +855,42 @@ function defineTest(f: Fixture) { 'rgb(255, 165, 0)', ) - // remove css import const editor = f.createEditor('src/routes/style-server/server.tsx') - editor.edit((s) => - s.replaceAll(`import './server.css'`, `/* import './server.css' */`), - ) + + // removing and adding new css works via hmr + { + await using _ = await expectNoReload(page) + + // remove css import + editor.edit((s) => + s.replaceAll(`import './server.css'`, `/* import './server.css' */`), + ) + await expect(page.locator('.test-style-server')).toHaveCSS( + 'color', + 'rgb(0, 0, 0)', + ) + + // add new css + editor.edit((s) => + s.replaceAll(`/* import './server.css' */`, `import './server2.css'`), + ) + await expect(page.locator('.test-style-server')).toHaveCSS( + 'color', + 'rgb(0, 255, 165)', + ) + } + + // TODO: React doesn't re-inert same css link. so manual reload is required. + editor.reset() await page.waitForTimeout(100) await expect(async () => { - // TODO: shouldn't require reload await page.reload() await expect(page.locator('.test-style-server')).toHaveCSS( 'color', - 'rgb(0, 0, 0)', + 'rgb(255, 165, 0)', { timeout: 10 }, ) }).toPass() - - // adding css works without reload - await waitForHydration(page) - await using _ = await expectNoReload(page) - - editor.reset() - await expect(page.locator('.test-style-server')).toHaveCSS( - 'color', - 'rgb(255, 165, 0)', - ) }) testNoJs('adding/removing css server @nojs', async ({ page }) => { diff --git a/packages/plugin-rsc/examples/basic/src/routes/style-server/server2.css b/packages/plugin-rsc/examples/basic/src/routes/style-server/server2.css new file mode 100644 index 00000000..46ded0c8 --- /dev/null +++ b/packages/plugin-rsc/examples/basic/src/routes/style-server/server2.css @@ -0,0 +1,3 @@ +.test-style-server { + color: rgb(0, 255, 165); +} From a3a0cafd7863f5fa5d9bf9240ec60af99837949f Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Sun, 14 Sep 2025 19:01:44 +0900 Subject: [PATCH 5/5] chore: comment --- packages/plugin-rsc/src/plugin.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/plugin-rsc/src/plugin.ts b/packages/plugin-rsc/src/plugin.ts index 8b06b2e3..d07c3d44 100644 --- a/packages/plugin-rsc/src/plugin.ts +++ b/packages/plugin-rsc/src/plugin.ts @@ -2042,6 +2042,7 @@ function vitePluginRscCss( { name: 'rsc:importer-resources', configureServer(server) { + // delegate 'prune' event from rsc environment to browser const hot = server.environments.rsc!.hot const original = hot.send hot.send = function (this, ...args: any[]) {