Skip to content

Commit

Permalink
Merge branch 'main' into fix-react-server-client-hmr
Browse files Browse the repository at this point in the history
  • Loading branch information
hi-ogawa authored Apr 10, 2024
2 parents a27dded + 3de66bf commit 4bde14e
Show file tree
Hide file tree
Showing 33 changed files with 2,044 additions and 29 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,14 @@ jobs:
- run: corepack enable
- run: pnpm i
- run: pnpm lint-check
- run: pnpm -C examples/workerd build
- run: pnpm tsc
- run: npx playwright install chromium
- run: pnpm -C examples/react-ssr test-e2e
- run: pnpm -C examples/react-ssr build
- run: pnpm -C examples/react-ssr test-e2e-preview
- run: pnpm -C examples/react-ssr test-e2e-workerd
- run: pnpm -C examples/react-ssr-workerd test-e2e
- run: pnpm -C examples/react-server test-e2e
- run: pnpm -C examples/react-server build
- run: pnpm -C examples/react-server test-e2e-preview
3 changes: 1 addition & 2 deletions examples/react-server/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ pnpm cf-release
## todo

- [x] custom react-server environment
- with a patch for noExternal and optimizeDeps
- [x] rsc stream
- [x] rsc ssr
- [x] dev
Expand All @@ -25,7 +24,7 @@ pnpm cf-release
- [x] dev
- [x] build
- [x] hmr
- [x] browser
- [x] browser (?)
- [x] react-server
- [ ] integrate to `@hiogawa/react-server`

Expand Down
5 changes: 5 additions & 0 deletions examples/react-ssr-workerd/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# react-ssr-workerd

```sh
pnpm dev
```
9 changes: 9 additions & 0 deletions examples/react-ssr-workerd/e2e/basic.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { test, expect } from "@playwright/test";

test("basic", async ({ page }) => {
await page.goto("/");
await expect(page.locator("#root")).toContainText("hydrated: true");
await expect(page.locator("#root")).toContainText("Count: 0");
await page.getByRole("button", { name: "+" }).click();
await expect(page.locator("#root")).toContainText("Count: 1");
});
14 changes: 14 additions & 0 deletions examples/react-ssr-workerd/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<!doctype html>
<html>
<head>
<meta charset="UTF-8" />
<title>react-ssr-workerd</title>
<meta
name="viewport"
content="width=device-width, height=device-height, initial-scale=1.0"
/>
</head>
<body>
<script src="/src/entry-client" type="module"></script>
</body>
</html>
22 changes: 22 additions & 0 deletions examples/react-ssr-workerd/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"name": "@hiogawa/vite-environment-examples-react-ssr-workerd",
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"test-e2e": "playwright test"
},
"dependencies": {
"react": "18.3.0-canary-6c3b8dbfe-20240226",
"react-dom": "18.3.0-canary-6c3b8dbfe-20240226"
},
"devDependencies": {
"@cloudflare/workers-types": "^4.20240405.0",
"@hiogawa/vite-plugin-workerd": "workspace:*",
"@types/react": "18.2.72",
"@types/react-dom": "18.2.22"
},
"volta": {
"extends": "../../package.json"
}
}
28 changes: 28 additions & 0 deletions examples/react-ssr-workerd/playwright.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { defineConfig, devices } from "@playwright/test";

const port = Number(process.env["E2E_PORT"] || 6174);
const command = process.env["E2E_PREVIEW"]
? `pnpm preview --port ${port} --strict-port`
: process.env["E2E_WORKERD"]
? `pnpm dev-workerd --port ${port} --strict-port`
: `pnpm dev --port ${port} --strict-port`;

export default defineConfig({
testDir: "e2e",
use: {
trace: "on-first-retry",
},
projects: [
{
name: "chromium",
use: devices["Desktop Chrome"],
},
],
webServer: {
command,
port,
},
forbidOnly: !!process.env["CI"],
retries: process.env["CI"] ? 2 : 0,
reporter: "list",
});
4 changes: 4 additions & 0 deletions examples/react-ssr-workerd/src/adapters/node.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { createMiddleware } from "@hattip/adapter-node/native-fetch";
import { handler } from "../entry-server";

export default createMiddleware((ctx) => handler(ctx.request));
8 changes: 8 additions & 0 deletions examples/react-ssr-workerd/src/adapters/workerd.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { handler } from "../entry-server";

export default {
fetch(request: Request, env: unknown) {
Object.assign(globalThis, { env });
return handler(request);
},
};
14 changes: 14 additions & 0 deletions examples/react-ssr-workerd/src/entry-client.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { tinyassert } from "@hiogawa/utils";
import ReactDomClient from "react-dom/client";
import Page from "./routes/page";
import React from "react";

async function main() {
const el = document.getElementById("root");
tinyassert(el);
React.startTransition(() => {
ReactDomClient.hydrateRoot(el, <Page />);
});
}

main();
28 changes: 28 additions & 0 deletions examples/react-ssr-workerd/src/entry-server.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import ReactDomServer from "react-dom/server.edge";
import Page from "./routes/page";

export async function handler(request: Request) {
const url = new URL(request.url);
if (url.pathname === "/api") {
return apiHandler(request);
}
if (url.pathname === "/nodejs-compat") {
const util = await import("node:util");
return new Response(util.format("hello %s", "world"));
}

const ssrHtml = ReactDomServer.renderToString(<Page />);
let html = (await import("virtual:index-html")).default;
html = html.replace(/<body>/, `<body><div id="root">${ssrHtml}</div>`);
return new Response(html, { headers: { "content-type": "text/html" } });
}

async function apiHandler(request: Request) {
let count = Number(await env.kv.get("count"));
if (request.method === "POST") {
const { delta } = await request.json();
count += delta;
await env.kv.put("count", String(count));
}
return new Response(JSON.stringify({ count }));
}
35 changes: 35 additions & 0 deletions examples/react-ssr-workerd/src/routes/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import React from "react";

export default function Page() {
const [count, setCount] = React.useState<number>();

const [hydrated, setHydrated] = React.useState(false);
React.useEffect(() => {
setHydrated(true);
getCount().then(setCount);
}, []);

return (
<div>
<div>hydrated: {String(hydrated)}</div>
<div>Count: {count ?? "..."}</div>
<button onClick={async () => changeCount(-1).then(setCount)}>-1</button>
<button onClick={async () => changeCount(+1).then(setCount)}>+1</button>
</div>
);
}

async function getCount() {
const res = await fetch("/api");
const { count } = await res.json();
return count as number;
}

async function changeCount(delta: number) {
const res = await fetch("/api", {
method: "POST",
body: JSON.stringify({ delta }),
});
const { count } = await res.json();
return count as number;
}
3 changes: 3 additions & 0 deletions examples/react-ssr-workerd/src/types/react.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
declare module "react-dom/server.edge" {
export * from "react-dom/server";
}
4 changes: 4 additions & 0 deletions examples/react-ssr-workerd/src/types/virtual.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
declare module "virtual:index-html" {
const src: string;
export default src;
}
3 changes: 3 additions & 0 deletions examples/react-ssr-workerd/src/types/workerd.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
declare const env: {
kv: import("@cloudflare/workers-types").KVNamespace;
};
15 changes: 15 additions & 0 deletions examples/react-ssr-workerd/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"extends": "@tsconfig/strictest/tsconfig.json",
"include": ["src", "vite.config.ts", "e2e", "playwright.config.ts"],
"compilerOptions": {
"exactOptionalPropertyTypes": false,
"verbatimModuleSyntax": true,
"noEmit": true,
"moduleResolution": "Bundler",
"module": "ESNext",
"target": "ESNext",
"lib": ["ESNext", "DOM"],
"types": ["vite/client"],
"jsx": "react-jsx"
}
}
47 changes: 47 additions & 0 deletions examples/react-ssr-workerd/vite.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import { vitePluginWorkerd } from "@hiogawa/vite-plugin-workerd";
import { vitePluginVirtualIndexHtml } from "../react-ssr/vite.config";
import { Log } from "miniflare";

export default defineConfig((_env) => ({
clearScreen: false,
appType: "custom",
plugins: [
react(),
vitePluginWorkerd({
entry: "/src/adapters/workerd.ts",
miniflare: {
log: new Log(),
},
wrangler: {
configPath: "./wrangler.toml",
},
}),
vitePluginVirtualIndexHtml(),
],
environments: {
workerd: {
// [feedback] how to prevent deps optimization to inject this? still `ssr.target: "webworker"` needed?
// import { createRequire } from 'module';const require = createRequire(import.meta.url);
nodeCompatible: false,
webCompatible: true,
resolve: {
noExternal: true,
},
dev: {
optimizeDeps: {
include: [
"react",
"react/jsx-runtime",
"react/jsx-dev-runtime",
"react-dom/server.edge",
],
},
},
},
},
ssr: {
target: "webworker",
},
}));
5 changes: 5 additions & 0 deletions examples/react-ssr-workerd/wrangler.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
compatibility_date = "2024-01-01"
compatibility_flags = ["nodejs_compat"]
kv_namespaces = [
{ binding = "kv", id = "test-namespace" }
]
3 changes: 3 additions & 0 deletions examples/react-ssr/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@
"type": "module",
"scripts": {
"dev": "vite",
"dev-workerd": "vite --config vite.config.workerd.ts",
"build": "vite build --all",
"preview": "vite preview",
"test-e2e": "playwright test",
"test-e2e-preview": "E2E_PREVIEW=1 playwright test",
"test-e2e-workerd": "E2E_WORKERD=1 playwright test",
"vc-build": "SERVER_ENTRY=/src/adapters/vercel-edge.ts pnpm build && bash misc/vercel-edge/build.sh",
"vc-release": "vercel deploy --prebuilt misc/vercel-edge --prod"
},
Expand All @@ -16,6 +18,7 @@
"react-dom": "18.3.0-canary-6c3b8dbfe-20240226"
},
"devDependencies": {
"@hiogawa/vite-plugin-workerd": "workspace:*",
"@types/react": "18.2.72",
"@types/react-dom": "18.2.22"
},
Expand Down
8 changes: 4 additions & 4 deletions examples/react-ssr/playwright.config.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { defineConfig, devices } from "@playwright/test";

const port = Number(process.env["E2E_PORT"] || 6174);
const isPreview = Boolean(process.env["E2E_PREVIEW"]);
const command = isPreview
const command = process.env["E2E_PREVIEW"]
? `pnpm preview --port ${port} --strict-port`
: `pnpm dev --port ${port} --strict-port`;
: process.env["E2E_WORKERD"]
? `pnpm dev-workerd --port ${port} --strict-port`
: `pnpm dev --port ${port} --strict-port`;

export default defineConfig({
testDir: "e2e",
Expand All @@ -21,7 +22,6 @@ export default defineConfig({
command,
port,
},
grepInvert: isPreview ? /@dev/ : /@build/,
forbidOnly: !!process.env["CI"],
retries: process.env["CI"] ? 2 : 0,
reporter: "list",
Expand Down
5 changes: 5 additions & 0 deletions examples/react-ssr/src/adapters/workerd.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { handler } from "../entry-server";

export default {
fetch: handler,
};
8 changes: 7 additions & 1 deletion examples/react-ssr/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
{
"extends": "@tsconfig/strictest/tsconfig.json",
"include": ["src", "vite.config.ts", "e2e", "playwright.config.ts"],
"include": [
"src",
"vite.config.ts",
"vite.config.workerd.ts",
"e2e",
"playwright.config.ts"
],
"compilerOptions": {
"exactOptionalPropertyTypes": false,
"verbatimModuleSyntax": true,
Expand Down
Loading

0 comments on commit 4bde14e

Please sign in to comment.