Skip to content

Add playground for vite-plugin-cloudflare #13151

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 4, 2025
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
15 changes: 10 additions & 5 deletions integration/helpers/vite-plugin-cloudflare-template/.gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
node_modules
.DS_Store
/node_modules/
*.tsbuildinfo

/.cache
/build
.env
.react-router
# React Router
/.react-router/
/build/

# Cloudflare
.mf
.wrangler
2 changes: 0 additions & 2 deletions integration/helpers/vite-plugin-cloudflare-template/env.d.ts

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ declare module "react-router" {
}

const requestHandler = createRequestHandler(
// @ts-expect-error - virtual module provided by React Router at build time
// @ts-expect-error
() => import("virtual:react-router/server-build"),
import.meta.env.MODE
);
Expand Down
11 changes: 11 additions & 0 deletions playground/vite-plugin-cloudflare/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
.DS_Store
/node_modules/
*.tsbuildinfo

# React Router
/.react-router/
/build/

# Cloudflare
.mf
.wrangler
43 changes: 43 additions & 0 deletions playground/vite-plugin-cloudflare/app/entry.server.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import type { AppLoadContext, EntryContext } from "react-router";
import { ServerRouter } from "react-router";
import { isbot } from "isbot";
import { renderToReadableStream } from "react-dom/server";

export default async function handleRequest(
request: Request,
responseStatusCode: number,
responseHeaders: Headers,
routerContext: EntryContext,
_loadContext: AppLoadContext
) {
let shellRendered = false;
const userAgent = request.headers.get("user-agent");

const body = await renderToReadableStream(
<ServerRouter context={routerContext} url={request.url} />,
{
onError(error: unknown) {
responseStatusCode = 500;
// Log streaming rendering errors from inside the shell. Don't log
// errors encountered during initial shell rendering since they'll
// reject and get logged in handleDocumentRequest.
if (shellRendered) {
console.error(error);
}
},
}
);
shellRendered = true;

// Ensure requests from bots and SPA Mode renders wait for all content to load before responding
// https://react.dev/reference/react-dom/server/renderToPipeableStream#waiting-for-all-content-to-load-for-crawlers-and-static-generation
if ((userAgent && isbot(userAgent)) || routerContext.isSpaMode) {
await body.allReady;
}

responseHeaders.set("Content-Type", "text/html");
return new Response(body, {
headers: responseHeaders,
status: responseStatusCode,
});
}
19 changes: 19 additions & 0 deletions playground/vite-plugin-cloudflare/app/root.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Links, Meta, Outlet, Scripts, ScrollRestoration } from "react-router";

export default function App() {
return (
<html lang="en">
<head>
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<Meta />
<Links />
</head>
<body>
<Outlet />
<ScrollRestoration />
<Scripts />
</body>
</html>
);
}
4 changes: 4 additions & 0 deletions playground/vite-plugin-cloudflare/app/routes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { type RouteConfig } from "@react-router/dev/routes";
import { flatRoutes } from "@react-router/fs-routes";

export default flatRoutes() satisfies RouteConfig;
24 changes: 24 additions & 0 deletions playground/vite-plugin-cloudflare/app/routes/_index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import type { MetaFunction } from "react-router";
import type { Route } from "./+types/_index"

export const meta: MetaFunction = () => {
return [
{ title: "New React Router App" },
{ name: "description", content: "Welcome to React Router!" },
];
};

export async function loader({ context }: Route.LoaderArgs) {
return {
message: context.cloudflare.env.VALUE_FROM_CLOUDFLARE,
};
}

export default function Index({ loaderData }: Route.ComponentProps) {
return (
<div style={{ fontFamily: "system-ui, sans-serif", lineHeight: "1.8" }}>
<h1>Welcome to React Router</h1>
<p>{loaderData.message}</p>
</div>
);
}
37 changes: 37 additions & 0 deletions playground/vite-plugin-cloudflare/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"name": "@playground/vite-plugin-cloudflare",
"version": "0.0.0",
"private": true,
"sideEffects": false,
"type": "module",
"scripts": {
"dev": "react-router dev",
"build": "react-router build",
"typecheck": "react-router typegen && tsc"
},
"dependencies": {
"express": "^4.19.2",
"isbot": "^5.1.11",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router": "workspace:*",
"serialize-javascript": "^6.0.1"
},
"devDependencies": {
"@cloudflare/vite-plugin": "^0.1.1",
"@cloudflare/workers-types": "^4.20250214.0",
"@react-router/dev": "workspace:*",
"@react-router/fs-routes": "workspace:*",
"@types/node": "^20.0.0",
"@types/react": "^18.2.20",
"@types/react-dom": "^18.2.7",
"eslint": "^8.38.0",
"typescript": "^5.1.6",
"vite": "^6.1.0",
"vite-tsconfig-paths": "^4.2.1",
"wrangler": "^3.109.2"
},
"engines": {
"node": ">=20.0.0"
}
}
Binary file not shown.
7 changes: 7 additions & 0 deletions playground/vite-plugin-cloudflare/react-router.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import type { Config } from "@react-router/dev/config";

export default {
future: {
unstable_viteEnvironmentApi: true,
},
} satisfies Config;
28 changes: 28 additions & 0 deletions playground/vite-plugin-cloudflare/tsconfig.cloudflare.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"extends": "./tsconfig.json",
"include": [
".react-router/types/**/*",
"app/**/*",
"app/**/.server/**/*",
"app/**/.client/**/*",
"workers/**/*",
"worker-configuration.d.ts"
],
"compilerOptions": {
"composite": true,
"strict": true,
"lib": ["DOM", "DOM.Iterable", "ES2022"],
"types": ["@cloudflare/workers-types", "vite/client"],
"target": "ES2022",
"module": "ES2022",
"moduleResolution": "bundler",
"jsx": "react-jsx",
"baseUrl": ".",
"rootDirs": [".", "./.react-router/types"],
"paths": {
"~/*": ["./app/*"]
},
"esModuleInterop": true,
"resolveJsonModule": true
}
}
14 changes: 14 additions & 0 deletions playground/vite-plugin-cloudflare/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"files": [],
"references": [
{ "path": "./tsconfig.node.json" },
{ "path": "./tsconfig.cloudflare.json" }
],
"compilerOptions": {
"checkJs": true,
"verbatimModuleSyntax": true,
"skipLibCheck": true,
"strict": true,
"noEmit": true
}
}
13 changes: 13 additions & 0 deletions playground/vite-plugin-cloudflare/tsconfig.node.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"extends": "./tsconfig.json",
"include": ["react-router.config.ts", "vite.config.ts"],
"compilerOptions": {
"composite": true,
"strict": true,
"types": ["node"],
"lib": ["ES2022"],
"target": "ES2022",
"module": "ES2022",
"moduleResolution": "bundler"
}
}
12 changes: 12 additions & 0 deletions playground/vite-plugin-cloudflare/vite.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { reactRouter } from "@react-router/dev/vite";
import { defineConfig } from "vite";
import tsconfigPaths from "vite-tsconfig-paths";
import { cloudflare } from "@cloudflare/vite-plugin";

export default defineConfig({
plugins: [
cloudflare({ viteEnvironment: { name: "ssr" } }),
reactRouter(),
tsconfigPaths(),
],
});
5 changes: 5 additions & 0 deletions playground/vite-plugin-cloudflare/worker-configuration.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// Generated by Wrangler by running `wrangler types`

interface Env {
VALUE_FROM_CLOUDFLARE: "Hello from Cloudflare";
}
28 changes: 28 additions & 0 deletions playground/vite-plugin-cloudflare/workers/app.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { createRequestHandler } from "react-router";

declare global {
interface CloudflareEnvironment extends Env {}
}

declare module "react-router" {
export interface AppLoadContext {
cloudflare: {
env: CloudflareEnvironment;
ctx: ExecutionContext;
};
}
}

const requestHandler = createRequestHandler(
// @ts-expect-error
() => import("virtual:react-router/server-build"),
import.meta.env.MODE
);

export default {
async fetch(request, env, ctx) {
return requestHandler(request, {
cloudflare: { env, ctx },
});
},
} satisfies ExportedHandler<CloudflareEnvironment>;
8 changes: 8 additions & 0 deletions playground/vite-plugin-cloudflare/wrangler.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
name = "react-router-app"
compatibility_date = "2024-11-18"
main = "./workers/app.ts"

assets = {}

[vars]
VALUE_FROM_CLOUDFLARE = "Hello from Cloudflare"
58 changes: 58 additions & 0 deletions pnpm-lock.yaml

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

Loading