Skip to content

Commit 16a60c2

Browse files
Add RSC Framework Mode HMR/HDR support (#14100)
1 parent af6e360 commit 16a60c2

File tree

12 files changed

+1077
-80
lines changed

12 files changed

+1077
-80
lines changed

integration/vite-hmr-hdr-rsc-test.ts

Lines changed: 431 additions & 0 deletions
Large diffs are not rendered by default.

integration/vite-hmr-hdr-test.ts

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,27 @@ import path from "node:path";
33
import type { Page, PlaywrightWorkerOptions } from "@playwright/test";
44
import { expect } from "@playwright/test";
55

6-
import type { Files } from "./helpers/vite.js";
6+
import type { Files, TemplateName } from "./helpers/vite.js";
77
import {
88
test,
99
createEditor,
1010
EXPRESS_SERVER,
1111
viteConfig,
1212
viteMajorTemplates,
13+
reactRouterConfig,
1314
} from "./helpers/vite.js";
1415

16+
const templates = [
17+
...viteMajorTemplates,
18+
{
19+
templateName: "rsc-vite-framework",
20+
templateDisplayName: "RSC Framework Mode",
21+
},
22+
] as const satisfies ReadonlyArray<{
23+
templateName: TemplateName;
24+
templateDisplayName: string;
25+
}>;
26+
1527
const indexRoute = `
1628
// imports
1729
import { useState, useEffect } from "react";
@@ -40,28 +52,36 @@ const indexRoute = `
4052
`;
4153

4254
test.describe("Vite HMR & HDR", () => {
43-
viteMajorTemplates.forEach(({ templateName, templateDisplayName }) => {
55+
templates.forEach(({ templateName, templateDisplayName }) => {
4456
test.describe(templateDisplayName, () => {
4557
test("vite dev", async ({ page, browserName, dev }) => {
4658
let files: Files = async ({ port }) => ({
47-
"vite.config.js": await viteConfig.basic({ port }),
59+
"vite.config.js": await viteConfig.basic({ port, templateName }),
60+
"react-router.config.ts": reactRouterConfig({
61+
viteEnvironmentApi: templateName.includes("rsc"),
62+
}),
4863
"app/routes/_index.tsx": indexRoute,
4964
});
5065
let { cwd, port } = await dev(files, templateName);
51-
await workflow({ page, browserName, cwd, port });
66+
await workflow({ templateName, page, browserName, cwd, port });
5267
});
5368

5469
test("express", async ({ page, browserName, customDev }) => {
70+
test.skip(templateName.includes("rsc"), "RSC is not supported");
5571
let files: Files = async ({ port }) => ({
56-
"vite.config.js": await viteConfig.basic({ port }),
72+
"vite.config.js": await viteConfig.basic({ port, templateName }),
73+
"react-router.config.ts": reactRouterConfig({
74+
viteEnvironmentApi: templateName.includes("rsc"),
75+
}),
5776
"server.mjs": EXPRESS_SERVER({ port }),
5877
"app/routes/_index.tsx": indexRoute,
5978
});
6079
let { cwd, port } = await customDev(files, templateName);
61-
await workflow({ page, browserName, cwd, port });
80+
await workflow({ templateName, page, browserName, cwd, port });
6281
});
6382

6483
test("mdx", async ({ page, dev }) => {
84+
test.skip(templateName.includes("rsc"), "RSC is not supported");
6585
let files: Files = async ({ port }) => ({
6686
"vite.config.ts": `
6787
import { defineConfig } from "vite";
@@ -120,11 +140,13 @@ test.describe("Vite HMR & HDR", () => {
120140
});
121141

122142
async function workflow({
143+
templateName,
123144
page,
124145
browserName,
125146
cwd,
126147
port,
127148
}: {
149+
templateName: TemplateName;
128150
page: Page;
129151
browserName: PlaywrightWorkerOptions["browserName"];
130152
cwd: string;
@@ -145,7 +167,12 @@ async function workflow({
145167

146168
// setup: browser state
147169
let hmrStatus = page.locator("#index [data-hmr]");
148-
await expect(page).toHaveTitle("HMR updated title: 0");
170+
171+
// RSC doesn't support meta export?
172+
if (!templateName.includes("rsc")) {
173+
await expect(page).toHaveTitle("HMR updated title: 0");
174+
}
175+
149176
await expect(hmrStatus).toHaveText("HMR updated: 0");
150177
let input = page.locator("#index input");
151178
await expect(input).toBeVisible();
@@ -159,7 +186,12 @@ async function workflow({
159186
.replace("HMR updated: 0", "HMR updated: 1"),
160187
);
161188
await page.waitForLoadState("networkidle");
162-
await expect(page).toHaveTitle("HMR updated title: 1");
189+
190+
// RSC doesn't support meta export?
191+
if (!templateName.includes("rsc")) {
192+
await expect(page).toHaveTitle("HMR updated title: 1");
193+
}
194+
163195
await expect(hmrStatus).toHaveText("HMR updated: 1");
164196
await expect(input).toHaveValue("stateful");
165197
expect(page.errors).toEqual([]);

packages/react-router-dev/config/default-rsc-entries/entry.client.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import "virtual:react-router/unstable_rsc/inject-hmr-runtime";
2+
13
import { startTransition, StrictMode } from "react";
24
import { hydrateRoot } from "react-dom/client";
35
import {

packages/react-router-dev/package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,6 @@
7878
"@babel/types": "^7.27.7",
7979
"@npmcli/package-json": "^4.0.1",
8080
"@react-router/node": "workspace:*",
81-
"@vitejs/plugin-react": "^4.5.2",
8281
"@vitejs/plugin-rsc": "0.4.11",
8382
"arg": "^5.0.1",
8483
"babel-dead-code-elimination": "^1.0.6",

packages/react-router-dev/tsup.config.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,11 @@ const entry = [
1616
"vite/cloudflare.ts",
1717
];
1818

19-
const external = ["./static/refresh-utils.mjs", /\.json$/];
19+
const external = [
20+
"./static/refresh-utils.mjs",
21+
"./static/rsc-refresh-utils.mjs",
22+
/\.json$/,
23+
];
2024

2125
export default defineConfig([
2226
{
@@ -39,6 +43,16 @@ export default defineConfig([
3943
"dist/static/refresh-utils.mjs",
4044
);
4145

46+
await fsp.mkdir("dist/static", { recursive: true });
47+
await fsp.copyFile(
48+
"vite/static/refresh-utils.mjs",
49+
"dist/static/refresh-utils.mjs",
50+
);
51+
await fsp.copyFile(
52+
"vite/static/rsc-refresh-utils.mjs",
53+
"dist/static/rsc-refresh-utils.mjs",
54+
);
55+
4256
await fsp.mkdir("dist/config/defaults", { recursive: true });
4357
const files = await fsp.readdir("config/defaults");
4458
for (const file of files) {

0 commit comments

Comments
 (0)