From bea99bbe9aca7b05989559bfe4f9eb47a58b5d16 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Sun, 3 Aug 2025 17:58:59 +0900 Subject: [PATCH 1/3] fix(rsc): keep manually added link stylesheet during dev --- packages/plugin-rsc/src/plugin.ts | 10 ++++++++-- packages/plugin-rsc/src/ssr.tsx | 5 ++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/packages/plugin-rsc/src/plugin.ts b/packages/plugin-rsc/src/plugin.ts index 1e0f84df8..64f8ae9c9 100644 --- a/packages/plugin-rsc/src/plugin.ts +++ b/packages/plugin-rsc/src/plugin.ts @@ -834,9 +834,15 @@ window.__vite_plugin_react_preamble_installed__ = true; // technically this doesn't have to wait for "vite:beforeUpdate" and should do it right after browser css import. // TODO: there migth be a clever way to let Vite deduplicate itself. // cf. https://github.com/withastro/astro/blob/acb9b302f56e38833a1ab01147f7fde0bf967889/packages/astro/src/vite-plugin-astro-server/pipeline.ts#L133-L135 - code += ` + code += /* js */ ` const ssrCss = document.querySelectorAll("link[rel='stylesheet']"); -import.meta.hot.on("vite:beforeUpdate", () => ssrCss.forEach(node => node.remove())); +import.meta.hot.on("vite:beforeUpdate", () => { + ssrCss.forEach(node => { + if (node.dataset.precedence?.startsWith("vite-rsc/")) { + node.remove(); + } + }) +}); ` // close error overlay after syntax error is fixed and hmr is triggered. // https://github.com/vitejs/vite/blob/8033e5bf8d3ff43995d0620490ed8739c59171dd/packages/vite/src/client/client.ts#L318-L320 diff --git a/packages/plugin-rsc/src/ssr.tsx b/packages/plugin-rsc/src/ssr.tsx index dec5e6c17..9cbaf8545 100644 --- a/packages/plugin-rsc/src/ssr.tsx +++ b/packages/plugin-rsc/src/ssr.tsx @@ -60,6 +60,9 @@ function preloadDeps(deps: ResolvedAssetDeps) { }) } for (const href of deps.css) { - ReactDOM.preinit(href, { as: 'style' }) + ReactDOM.preinit(href, { + as: 'style', + precedence: 'vite-rsc/client-reference', + }) } } From 0bb4bcf60e2b03619e7c13da3cf76595193944c6 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Sun, 3 Aug 2025 18:03:14 +0900 Subject: [PATCH 2/3] chore: cleanup --- packages/plugin-rsc/src/plugin.ts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/packages/plugin-rsc/src/plugin.ts b/packages/plugin-rsc/src/plugin.ts index 64f8ae9c9..01a7e1630 100644 --- a/packages/plugin-rsc/src/plugin.ts +++ b/packages/plugin-rsc/src/plugin.ts @@ -829,11 +829,7 @@ 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 - // should remove only the ones we injected during ssr, which are duplicated by browser imports for HMR. - // technically this doesn't have to wait for "vite:beforeUpdate" and should do it right after browser css import. - // TODO: there migth be a clever way to let Vite deduplicate itself. - // cf. https://github.com/withastro/astro/blob/acb9b302f56e38833a1ab01147f7fde0bf967889/packages/astro/src/vite-plugin-astro-server/pipeline.ts#L133-L135 + // TODO: this doesn't have to wait for "vite:beforeUpdate" and should do it right after browser css import. code += /* js */ ` const ssrCss = document.querySelectorAll("link[rel='stylesheet']"); import.meta.hot.on("vite:beforeUpdate", () => { @@ -841,7 +837,7 @@ import.meta.hot.on("vite:beforeUpdate", () => { if (node.dataset.precedence?.startsWith("vite-rsc/")) { node.remove(); } - }) + }); }); ` // close error overlay after syntax error is fixed and hmr is triggered. From 152250aba176ba63e35be176affa02f5bc4776ac Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Sun, 3 Aug 2025 18:14:38 +0900 Subject: [PATCH 3/3] test: add e2e --- packages/plugin-rsc/e2e/basic.test.ts | 12 ++++++++++++ packages/plugin-rsc/examples/basic/public/test.css | 3 +++ .../basic/src/routes/style-server/server.tsx | 6 ++++++ packages/plugin-rsc/examples/basic/vite.config.ts | 2 ++ 4 files changed, 23 insertions(+) create mode 100644 packages/plugin-rsc/examples/basic/public/test.css diff --git a/packages/plugin-rsc/e2e/basic.test.ts b/packages/plugin-rsc/e2e/basic.test.ts index a2c9e4c20..e228b5914 100644 --- a/packages/plugin-rsc/e2e/basic.test.ts +++ b/packages/plugin-rsc/e2e/basic.test.ts @@ -326,6 +326,10 @@ function defineTest(f: Fixture) { 'color', 'rgb(255, 165, 0)', ) + await expect(page.locator('.test-style-server-manual')).toHaveCSS( + 'color', + 'rgb(255, 165, 0)', + ) }) testNoJs('css @nojs', async ({ page }) => { @@ -335,6 +339,10 @@ function defineTest(f: Fixture) { 'color', 'rgb(255, 165, 0)', ) + await expect(page.locator('.test-style-server-manual')).toHaveCSS( + 'color', + 'rgb(255, 165, 0)', + ) }) async function testCss(page: Page, color = 'rgb(255, 165, 0)') { @@ -453,6 +461,10 @@ function defineTest(f: Fixture) { 'color', 'rgb(255, 165, 0)', ) + await expect(page.locator('.test-style-server-manual')).toHaveCSS( + 'color', + 'rgb(255, 165, 0)', + ) }) // 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.css new file mode 100644 index 000000000..f1c9489d9 --- /dev/null +++ b/packages/plugin-rsc/examples/basic/public/test.css @@ -0,0 +1,3 @@ +.test-style-server-manual { + color: orange; +} 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 6684e6b5f..661669924 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 @@ -8,6 +8,12 @@ export function TestStyleServer() {
test-css-module-server
+ +
test-style-server-manual
) } diff --git a/packages/plugin-rsc/examples/basic/vite.config.ts b/packages/plugin-rsc/examples/basic/vite.config.ts index d4ee08d3b..94df04e00 100644 --- a/packages/plugin-rsc/examples/basic/vite.config.ts +++ b/packages/plugin-rsc/examples/basic/vite.config.ts @@ -123,6 +123,8 @@ export default { fetch: handler }; source: `\ /favicon.ico Cache-Control: public, max-age=3600, s-maxage=3600 +/test.css + Cache-Control: public, max-age=3600, s-maxage=3600 /assets/* Cache-Control: public, max-age=31536000, immutable `,