-
-
Notifications
You must be signed in to change notification settings - Fork 10.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
c4c48af
commit 280d65c
Showing
30 changed files
with
1,451 additions
and
22 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"@react-router/cloudflare": major | ||
--- | ||
|
||
For Remix consumers migrating to React Router, all exports from `@remix-run/cloudflare-pages` are now provided for React Router consumers in the `@react-router/cloudflare` package. There is no longer a separate package for Cloudflare Pages. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"@react-router/cloudflare": minor | ||
--- | ||
|
||
The `@remix-run/cloudflare-workers` package has been deprecated. Remix consumers migrating to React Router should use the `@react-router/cloudflare` package directly. For guidance on how to use `@react-router/cloudflare` within a Cloudflare Workers context, refer to the Cloudflare Workers template. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
node_modules | ||
|
||
/build | ||
.env |
35 changes: 35 additions & 0 deletions
35
integration/helpers/vite-cloudflare-template/app/entry.server.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
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 | ||
) { | ||
const body = await renderToReadableStream( | ||
<ServerRouter context={routerContext} url={request.url} />, | ||
{ | ||
signal: request.signal, | ||
onError(error: unknown) { | ||
// Log streaming rendering errors from inside the shell | ||
console.error(error); | ||
responseStatusCode = 500; | ||
}, | ||
} | ||
); | ||
|
||
const userAgent = request.headers.get("user-agent"); | ||
if (userAgent && isbot(userAgent)) { | ||
await body.allReady; | ||
} | ||
|
||
responseHeaders.set("Content-Type", "text/html"); | ||
return new Response(body, { | ||
headers: responseHeaders, | ||
status: responseStatusCode, | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import { Links, Meta, Outlet, Scripts, ScrollRestoration } from "react-router"; | ||
|
||
export function Layout({ children }: { children: React.ReactNode }) { | ||
return ( | ||
<html lang="en"> | ||
<head> | ||
<meta charSet="utf-8" /> | ||
<meta name="viewport" content="width=device-width, initial-scale=1" /> | ||
<Meta /> | ||
<Links /> | ||
</head> | ||
<body> | ||
{children} | ||
<ScrollRestoration /> | ||
<Scripts /> | ||
</body> | ||
</html> | ||
); | ||
} | ||
|
||
export default function App() { | ||
return <Outlet />; | ||
} |
16 changes: 16 additions & 0 deletions
16
integration/helpers/vite-cloudflare-template/app/routes/_index.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import type { MetaFunction } from "react-router"; | ||
|
||
export const meta: MetaFunction = () => { | ||
return [ | ||
{ title: "New React Router App" }, | ||
{ name: "description", content: "React Router + Cloudflare" }, | ||
]; | ||
}; | ||
|
||
export default function Index() { | ||
return ( | ||
<div style={{ fontFamily: "system-ui, sans-serif", lineHeight: "1.8" }}> | ||
<h1>Welcome to React Router + Cloudflare</h1> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
{ | ||
"name": "integration-vite-cloudflare-template", | ||
"version": "0.0.0", | ||
"private": true, | ||
"sideEffects": false, | ||
"type": "module", | ||
"scripts": { | ||
"dev": "react-router dev", | ||
"build": "react-router build", | ||
"start": "wrangler pages dev ./build/client", | ||
"tsc": "tsc" | ||
}, | ||
"dependencies": { | ||
"@react-router/cloudflare": "workspace:*", | ||
"isbot": "^4.1.0", | ||
"miniflare": "^3.20231030.4", | ||
"react": "^18.2.0", | ||
"react-dom": "^18.2.0", | ||
"react-router": "workspace:*" | ||
}, | ||
"devDependencies": { | ||
"@cloudflare/workers-types": "^4.20230518.0", | ||
"@react-router/dev": "workspace:*", | ||
"@types/react": "^18.2.20", | ||
"@types/react-dom": "^18.2.7", | ||
"typescript": "^5.1.6", | ||
"vite": "^5.1.0", | ||
"wrangler": "^3.28.2" | ||
}, | ||
"engines": { | ||
"node": ">=18.0.0" | ||
} | ||
} |
Binary file not shown.
20 changes: 20 additions & 0 deletions
20
integration/helpers/vite-cloudflare-template/tsconfig.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
{ | ||
"include": ["env.d.ts", "**/*.ts", "**/*.tsx"], | ||
"compilerOptions": { | ||
"lib": ["DOM", "DOM.Iterable", "ES2022"], | ||
"types": ["vite/client"], | ||
"isolatedModules": true, | ||
"esModuleInterop": true, | ||
"jsx": "react-jsx", | ||
"module": "ESNext", | ||
"moduleResolution": "Bundler", | ||
"resolveJsonModule": true, | ||
"target": "ES2022", | ||
"strict": true, | ||
"allowJs": true, | ||
"skipLibCheck": true, | ||
"forceConsistentCasingInFileNames": true, | ||
"baseUrl": ".", | ||
"noEmit": true | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import { | ||
vitePlugin as reactRouter, | ||
cloudflareDevProxyVitePlugin as reactRouterCloudflareDevProxy, | ||
} from "@react-router/dev"; | ||
import { defineConfig } from "vite"; | ||
|
||
export default defineConfig({ | ||
plugins: [reactRouterCloudflareDevProxy(), reactRouter()], | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,156 @@ | ||
import type { Page } from "@playwright/test"; | ||
import { expect } from "@playwright/test"; | ||
|
||
import type { Files } from "./helpers/vite.js"; | ||
import { test, viteConfig } from "./helpers/vite.js"; | ||
|
||
const files: Files = async ({ port }) => ({ | ||
"vite.config.ts": ` | ||
import { | ||
vitePlugin as reactRouter, | ||
cloudflareDevProxyVitePlugin as reactRouterCloudflareDevProxy, | ||
} from "@react-router/dev"; | ||
import { getLoadContext } from "./load-context"; | ||
export default { | ||
${await viteConfig.server({ port })} | ||
plugins: [ | ||
reactRouterCloudflareDevProxy({ getLoadContext }), | ||
reactRouter(), | ||
], | ||
} | ||
`, | ||
"load-context.ts": ` | ||
import { type AppLoadContext } from "@react-router/cloudflare"; | ||
import { type PlatformProxy } from "wrangler"; | ||
type Env = { | ||
MY_KV: KVNamespace; | ||
} | ||
type Cloudflare = Omit<PlatformProxy<Env>, 'dispose'>; | ||
declare module "@react-router/cloudflare" { | ||
interface AppLoadContext { | ||
cloudflare: Cloudflare; | ||
env2: Cloudflare["env"]; | ||
extra: string; | ||
} | ||
} | ||
type GetLoadContext = (args: { | ||
request: Request; | ||
context: { cloudflare: Cloudflare }; | ||
}) => AppLoadContext; | ||
export const getLoadContext: GetLoadContext = ({ context }) => { | ||
return { | ||
...context, | ||
env2: context.cloudflare.env, | ||
extra: "stuff", | ||
}; | ||
}; | ||
`, | ||
"functions/[[page]].ts": ` | ||
import { createPagesFunctionHandler } from "@react-router/cloudflare"; | ||
// @ts-ignore - the server build file is generated by \`react-router build\` | ||
import * as build from "../build/server"; | ||
import { getLoadContext } from "../load-context"; | ||
export const onRequest = createPagesFunctionHandler({ | ||
build, | ||
getLoadContext, | ||
}); | ||
`, | ||
"wrangler.toml": ` | ||
kv_namespaces = [ | ||
{ id = "abc123", binding="MY_KV" } | ||
] | ||
`, | ||
"app/routes/_index.tsx": ` | ||
import { | ||
type LoaderFunctionArgs, | ||
type ActionFunctionArgs, | ||
json, | ||
Form, | ||
useLoaderData, | ||
} from "react-router"; | ||
const key = "__my-key__"; | ||
export async function loader({ context }: LoaderFunctionArgs) { | ||
const { MY_KV } = context.cloudflare.env; | ||
const value = await MY_KV.get(key); | ||
return json({ value, extra: context.extra }); | ||
} | ||
export async function action({ request, context }: ActionFunctionArgs) { | ||
const { MY_KV } = context.env2; | ||
if (request.method === "POST") { | ||
const formData = await request.formData(); | ||
const value = formData.get("value") as string; | ||
await MY_KV.put(key, value); | ||
return null; | ||
} | ||
if (request.method === "DELETE") { | ||
await MY_KV.delete(key); | ||
return null; | ||
} | ||
throw new Error(\`Method not supported: "\${request.method}"\`); | ||
} | ||
export default function Index() { | ||
const { value, extra } = useLoaderData<typeof loader>(); | ||
return ( | ||
<div> | ||
<h1>Welcome to React Router + Cloudflare</h1> | ||
<p data-extra>Extra: {extra}</p> | ||
{value ? ( | ||
<> | ||
<p data-text>Value: {value}</p> | ||
<Form method="DELETE"> | ||
<button>Delete</button> | ||
</Form> | ||
</> | ||
) : ( | ||
<> | ||
<p data-text>No value</p> | ||
<Form method="POST"> | ||
<label htmlFor="value">Set value:</label> | ||
<input type="text" name="value" id="value" required /> | ||
<br /> | ||
<button>Save</button> | ||
</Form> | ||
</> | ||
)} | ||
</div> | ||
); | ||
} | ||
`, | ||
}); | ||
|
||
test("vite dev", async ({ page, dev }) => { | ||
let { port } = await dev(files, "vite-cloudflare-template"); | ||
await workflow({ page, port }); | ||
}); | ||
|
||
test("wrangler pages dev", async ({ page, wranglerPagesDev }) => { | ||
let { port } = await wranglerPagesDev(files); | ||
await workflow({ page, port }); | ||
}); | ||
|
||
async function workflow({ page, port }: { page: Page; port: number }) { | ||
await page.goto(`http://localhost:${port}/`, { | ||
waitUntil: "networkidle", | ||
}); | ||
await expect(page.locator("[data-extra]")).toHaveText("Extra: stuff"); | ||
await expect(page.locator("[data-text]")).toHaveText("No value"); | ||
|
||
await page.getByLabel("Set value:").fill("my-value"); | ||
await page.getByRole("button").click(); | ||
await expect(page.locator("[data-text]")).toHaveText("Value: my-value"); | ||
expect(page.errors).toEqual([]); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
# React Router Cloudflare | ||
|
||
Cloudflare platform abstractions for [React Router.](https://reactrouter.com) | ||
|
||
```bash | ||
npm install @react-router/cloudflare @cloudflare/workers-types | ||
``` |
Oops, something went wrong.