Skip to content

Commit ba78d17

Browse files
authored
feat: add react-server-dom-vite-example (#79)
1 parent ecb5541 commit ba78d17

26 files changed

+2333
-0
lines changed

.github/workflows/ci.yml

+19
Original file line numberDiff line numberDiff line change
@@ -118,3 +118,22 @@ jobs:
118118
- run: pnpm -C examples/react test-e2e
119119
- run: pnpm -C examples/mpa test-e2e
120120
- run: pnpm -C examples/ssr test-e2e
121+
122+
react-server-dom-vite:
123+
runs-on: ubuntu-latest
124+
defaults:
125+
run:
126+
working-directory: ./react-server-dom-vite-example
127+
steps:
128+
- uses: actions/checkout@v4
129+
- uses: actions/setup-node@v4
130+
with:
131+
node-version: 22
132+
- run: corepack enable
133+
- run: pnpm i
134+
- run: pnpm lint-check
135+
- run: pnpm tsc
136+
- run: pnpm exec playwright install chromium
137+
- run: pnpm test-e2e
138+
- run: pnpm build
139+
- run: pnpm test-e2e-preview
+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
node_modules
2+
dist
3+
test-results
4+
*.tsbuildinfo
+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
Bare minimal example of `react-server-dom-vite` https://github.com/facebook/react/pull/31768
2+
3+
## implemented
4+
5+
- client component
6+
- server action
7+
- browser hmr
8+
- server hmr
9+
10+
## to be improved
11+
12+
- "use client" transform
13+
- "use server" transform
14+
- normalize reference id
15+
- css on server
16+
- ...
+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"$schema": "https://biomejs.dev/schemas/1.8.1/schema.json",
3+
"vcs": {
4+
"enabled": true,
5+
"clientKind": "git",
6+
"useIgnoreFile": true
7+
},
8+
"linter": { "enabled": false }
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import { type Page, test } from "@playwright/test";
2+
import { createEditor } from "./helper";
3+
4+
test("client reference", async ({ page }) => {
5+
await page.goto("/");
6+
await page.getByText("[hydrated: 1]").click();
7+
await page.getByText("Client counter: 0").click();
8+
await page
9+
.getByTestId("client-counter")
10+
.getByRole("button", { name: "+" })
11+
.click();
12+
await page.getByText("Client counter: 1").click();
13+
await page.reload();
14+
await page.getByText("Client counter: 0").click();
15+
});
16+
17+
test("server reference @js", async ({ page }) => {
18+
await testServerAction(page);
19+
});
20+
21+
test.describe(() => {
22+
test.use({ javaScriptEnabled: false });
23+
test("server reference @nojs", async ({ page }) => {
24+
await testServerAction(page);
25+
});
26+
});
27+
28+
async function testServerAction(page: Page) {
29+
await page.goto("/");
30+
await page.getByText("Server counter: 0").click();
31+
await page
32+
.getByTestId("server-counter")
33+
.getByRole("button", { name: "+" })
34+
.click();
35+
await page.getByText("Server counter: 1").click();
36+
await page.goto("/");
37+
await page.getByText("Server counter: 1").click();
38+
await page
39+
.getByTestId("server-counter")
40+
.getByRole("button", { name: "-" })
41+
.click();
42+
await page.getByText("Server counter: 0").click();
43+
}
44+
45+
test("client hmr @dev", async ({ page }) => {
46+
await page.goto("/");
47+
await page.getByText("[hydrated: 1]").click();
48+
// client +1
49+
await page.getByText("Client counter: 0").click();
50+
await page
51+
.getByTestId("client-counter")
52+
.getByRole("button", { name: "+" })
53+
.click();
54+
await page.getByText("Client counter: 1").click();
55+
// edit client
56+
using file = createEditor("src/app/client.tsx");
57+
file.edit((s) => s.replace("Client counter", "Client [EDIT] counter"));
58+
await page.getByText("Client [EDIT] counter: 1").click();
59+
});
60+
61+
test("server hmr @dev", async ({ page }) => {
62+
await page.goto("/");
63+
await page.getByText("[hydrated: 1]").click();
64+
65+
// server +1
66+
await page.getByText("Server counter: 0").click();
67+
await page
68+
.getByTestId("server-counter")
69+
.getByRole("button", { name: "+" })
70+
.click();
71+
await page.getByText("Server counter: 1").click();
72+
73+
// client +1
74+
await page.getByText("Client counter: 0").click();
75+
await page
76+
.getByTestId("client-counter")
77+
.getByRole("button", { name: "+" })
78+
.click();
79+
await page.getByText("Client counter: 1").click();
80+
81+
// edit server
82+
using file = createEditor("src/app/index.tsx");
83+
file.edit((s) => s.replace("Server counter", "Server [EDIT] counter"));
84+
await page.getByText("Server [EDIT] counter: 1").click();
85+
await page.getByText("Client counter: 1").click();
86+
87+
// server -1
88+
await page
89+
.getByTestId("server-counter")
90+
.getByRole("button", { name: "-" })
91+
.click();
92+
await page.getByText("Server [EDIT] counter: 0").click();
93+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import fs from "node:fs";
2+
3+
export function createEditor(filepath: string) {
4+
let init = fs.readFileSync(filepath, "utf-8");
5+
let data = init;
6+
return {
7+
edit(editFn: (data: string) => string) {
8+
data = editFn(data);
9+
fs.writeFileSync(filepath, data);
10+
},
11+
[Symbol.dispose]() {
12+
fs.writeFileSync(filepath, init);
13+
},
14+
};
15+
}
+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
{
2+
"private": true,
3+
"type": "module",
4+
"scripts": {
5+
"dev": "vite dev",
6+
"build": "vite build --app",
7+
"preview": "vite preview",
8+
"tsc": "tsc -b",
9+
"test-e2e": "playwright test",
10+
"test-e2e-preview": "E2E_PREVIEW=1 playwright test",
11+
"lint": "biome check --write .",
12+
"lint-check": "biome check ."
13+
},
14+
"dependencies": {
15+
"@jacob-ebey/react-server-dom-vite": "19.0.0-experimental.14",
16+
"react": "^19.0.0",
17+
"react-dom": "^19.0.0"
18+
},
19+
"devDependencies": {
20+
"@biomejs/biome": "^1.9.4",
21+
"@playwright/test": "^1.49.1",
22+
"@types/node": "^22.10.2",
23+
"@types/react": "^19.0.1",
24+
"@types/react-dom": "^19.0.1",
25+
"@vitejs/plugin-react": "^4.3.4",
26+
"typescript": "^5.7.2",
27+
"vite": "6.0.3"
28+
},
29+
"packageManager": "[email protected]+sha512.76e2379760a4328ec4415815bcd6628dee727af3779aaa4c914e3944156c4299921a89f976381ee107d41f12cfa4b66681ca9c718f0668fa0831ed4c6d8ba56c"
30+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { defineConfig, devices } from "@playwright/test";
2+
3+
const port = Number(process.env.E2E_PORT || 6174);
4+
const isPreview = Boolean(process.env.E2E_PREVIEW);
5+
const command = isPreview
6+
? `pnpm preview --port ${port} --strict-port`
7+
: `pnpm dev --port ${port} --strict-port`;
8+
9+
export default defineConfig({
10+
testDir: "e2e",
11+
use: {
12+
trace: "on-first-retry",
13+
},
14+
projects: [
15+
{
16+
name: "chromium",
17+
use: {
18+
...devices["Desktop Chrome"],
19+
viewport: null,
20+
deviceScaleFactor: undefined,
21+
},
22+
},
23+
],
24+
webServer: {
25+
command,
26+
port,
27+
},
28+
grepInvert: isPreview ? /@dev/ : /@build/,
29+
forbidOnly: !!process.env["CI"],
30+
retries: process.env["CI"] ? 2 : 0,
31+
reporter: "list",
32+
});

0 commit comments

Comments
 (0)