Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion integration/helpers/rsc-parcel/src/routes/root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export function Layout({ children }: { children: React.ReactNode }) {
<head>
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Vite (RSC)</title>
<title>Parcel (RSC)</title>
<Links />
</head>
<body>
Expand Down
2 changes: 1 addition & 1 deletion integration/helpers/rsc-vite-framework/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"@vanilla-extract/css": "^1.17.4",
"@vanilla-extract/vite-plugin": "^5.1.1",
"@vitejs/plugin-react": "^4.5.2",
"@vitejs/plugin-rsc": "0.4.11",
"@vitejs/plugin-rsc": "0.4.21",
"cross-env": "^7.0.3",
"typescript": "^5.1.6",
"vite": "^6.2.0",
Expand Down
2 changes: 1 addition & 1 deletion integration/helpers/rsc-vite/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"typecheck": "tsc"
},
"devDependencies": {
"@vitejs/plugin-rsc": "0.4.11",
"@vitejs/plugin-rsc": "0.4.21",
"@types/express": "^5.0.0",
"@types/node": "^22.13.1",
"@types/react": "^19.1.8",
Expand Down
107 changes: 66 additions & 41 deletions integration/vite-css-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,18 @@ const fixtures = [
templateDisplayName: string;
}>;

type RouteBasePath = "css" | "rsc-server-first-css";
type RouteBasePath =
| "css-with-links-export"
| "css-with-floated-link"
| "rsc-server-first-route";
const getRouteBasePaths = (templateName: TemplateName) => {
if (templateName.includes("rsc")) {
return ["css", "rsc-server-first-css"] as const satisfies RouteBasePath[];
}
return ["css"] as const satisfies RouteBasePath[];
return [
"css-with-links-export",
"css-with-floated-link",
...(templateName.includes("rsc")
? (["rsc-server-first-route"] as const)
: []),
] as const satisfies RouteBasePath[];
};

const files = ({ templateName }: { templateName: TemplateName }) => ({
Expand Down Expand Up @@ -112,7 +118,7 @@ const files = ({ templateName }: { templateName: TemplateName }) => ({
...Object.assign(
{},
...getRouteBasePaths(templateName).map((routeBasePath) => {
const isServerFirstRoute = routeBasePath === "rsc-server-first-css";
const isServerFirstRoute = routeBasePath === "rsc-server-first-route";
const exportName = isServerFirstRoute ? "ServerComponent" : "default";

return {
Expand Down Expand Up @@ -162,14 +168,17 @@ const files = ({ templateName }: { templateName: TemplateName }) => ({
return null;
}

export function links() {
return [{ rel: "stylesheet", href: postcssLinkedStyles }];
${
routeBasePath === "css-with-links-export"
? `export function links() { return [{ rel: "stylesheet", href: postcssLinkedStyles }]; }`
: ""
}

function TestRoute() {
return (
<>
<input />
${routeBasePath !== "css-with-links-export" ? `<link rel="stylesheet" href={postcssLinkedStyles} precedence="default" />` : ""}
<div id="entry-client" className="entry-client">
<div id="css-modules" className={cssModulesStyles.index}>
<div id="css-postcss-linked" className="${routeBasePath}-postcss-linked">
Expand Down Expand Up @@ -517,11 +526,6 @@ async function hmrWorkflow({
base?: string;
templateName: TemplateName;
}) {
if (templateName.includes("rsc")) {
// TODO: Fix CSS HMR support in RSC Framework mode
return;
}

for (const routeBase of getRouteBasePaths(templateName)) {
let pageErrors: Error[] = [];
page.on("pageerror", (error) => pageErrors.push(error));
Expand All @@ -532,8 +536,6 @@ async function hmrWorkflow({

let input = page.locator("input");
await expect(input).toBeVisible();
await input.type("stateful");
await expect(input).toHaveValue("stateful");

let edit = createEditor(cwd);
let modifyCss = (contents: string) =>
Expand All @@ -544,34 +546,57 @@ async function hmrWorkflow({
"NEW_PADDING_INJECTED_VIA_POSTCSS",
);

await Promise.all([
edit(`app/routes/${routeBase}/styles-bundled.css`, modifyCss),
edit(`app/routes/${routeBase}/styles.module.css`, modifyCss),
edit(`app/routes/${routeBase}/styles-vanilla-global.css.ts`, modifyCss),
edit(`app/routes/${routeBase}/styles-vanilla-local.css.ts`, modifyCss),
edit(`app/routes/${routeBase}/styles-postcss-linked.css`, modifyCss),
]);

await Promise.all(
[
"#css-bundled",
"#css-postcss-linked",
"#css-modules",
"#css-vanilla-global",
"#css-vanilla-local",
].map(
async (selector) =>
await expect(page.locator(selector)).toHaveCSS(
"padding",
NEW_PADDING,
),
),
);
const testCases = [
{ file: "styles-bundled.css", selector: "#css-bundled" },
// TODO: Fix HMR for CSS Modules in server-first routes in RSC Framework mode
...(routeBase === "rsc-server-first-route"
? []
: [{ file: "styles.module.css", selector: "#css-modules" }]),
// TODO: Fix HMR for `?url` CSS imports in RSC Framework mode: https://github.com/vitejs/vite-plugin-react/issues/772
// Once fixed, check if this also fixes HMR for Vanilla Extract
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For vanilla extract, we found a user land workaround https://github.com/pawelblaszczyk5/vite-rsc-experiments/blob/643649f2e6562c859d9612126bfc3a183e03c7b5/apps/vanilla-extract/vite.config.ts

I'm not sure yet whether rsc plugin should handle this in some general way or vanilla extract plugin should adjust it somehow.

...(templateName.includes("rsc")
? []
: [
{
file: "styles-postcss-linked.css",
selector: "#css-postcss-linked",
},
{
file: "styles-vanilla-global.css.ts",
selector: "#css-vanilla-global",
},
{
file: "styles-vanilla-local.css.ts",
selector: "#css-vanilla-local",
},
]),
] as const satisfies Array<{
file: string;
selector: string;
}>;

for (const { file, selector } of testCases) {
const routeFile = `app/routes/${routeBase}/${file}`;
await input.fill(routeFile);
await edit(routeFile, modifyCss);
await expect(
page.locator(selector),
`CSS update for ${routeFile}`,
).toHaveCSS("padding", NEW_PADDING);

// TODO: Fix state preservation when changing CSS Modules in RSC Framework mode
if (templateName.includes("rsc") && file === "styles.module.css") {
continue;
}

// Ensure CSS updates were handled by HMR
await expect(input).toHaveValue("stateful");
// Ensure CSS updates were handled by HMR
await expect(input, `State preservation for ${routeFile}`).toHaveValue(
routeFile,
);
}

if (routeBase === "css") {
// RSC Framework mode doesn't support custom entries yet
if (!templateName.includes("rsc")) {
// The following change triggers a full page reload, so we check it after all the checks for HMR state preservation
await edit("app/entry.client.css", modifyCss);
await expect(page.locator("#entry-client")).toHaveCSS(
Expand Down
2 changes: 1 addition & 1 deletion packages/react-router-dev/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@
"@babel/types": "^7.27.7",
"@npmcli/package-json": "^4.0.1",
"@react-router/node": "workspace:*",
"@vitejs/plugin-rsc": "0.4.11",
"@vitejs/plugin-rsc": "0.4.21",
"arg": "^5.0.1",
"babel-dead-code-elimination": "^1.0.6",
"chokidar": "^4.0.0",
Expand Down
9 changes: 8 additions & 1 deletion packages/react-router/lib/rsc/server.ssr.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@ export async function routeRSCServerRequest({
throw new Error("Missing body in server response");
}

const detectRedirectResponse = serverResponse.clone();

let serverResponseB: Response | null = null;
if (hydrate) {
serverResponseB = serverResponse.clone();
Expand All @@ -126,7 +128,12 @@ export async function routeRSCServerRequest({
};

try {
const payload = await getPayload();
if (!detectRedirectResponse.body) {
throw new Error("Failed to clone server response");
}
const payload = (await createFromReadableStream(
detectRedirectResponse.body,
)) as RSCPayload;
if (
serverResponse.status === SINGLE_FETCH_REDIRECT_STATUS &&
payload.type === "redirect"
Expand Down
2 changes: 1 addition & 1 deletion playground/rsc-vite-framework/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"@types/react": "^19.1.8",
"@types/react-dom": "^19.1.6",
"@vitejs/plugin-react": "^4.5.2",
"@vitejs/plugin-rsc": "0.4.11",
"@vitejs/plugin-rsc": "0.4.21",
"cross-env": "^7.0.3",
"remark-frontmatter": "^5.0.0",
"remark-mdx-frontmatter": "^5.2.0",
Expand Down
2 changes: 1 addition & 1 deletion playground/rsc-vite/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"@types/react": "^19.1.8",
"@types/react-dom": "^19.1.6",
"@vitejs/plugin-react": "^4.5.2",
"@vitejs/plugin-rsc": "0.4.11",
"@vitejs/plugin-rsc": "0.4.21",
"cross-env": "^7.0.3",
"typescript": "^5.1.6",
"vite": "^6.2.0"
Expand Down
42 changes: 21 additions & 21 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading