Skip to content
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

vite: cloudflare preset load context #8649

Merged
merged 5 commits into from
Jan 31, 2024
Merged
Show file tree
Hide file tree
Changes from 3 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
5 changes: 5 additions & 0 deletions .changeset/spicy-buses-play.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@remix-run/dev": patch
---

Vite: add dev load context option to Cloudflare preset
32 changes: 30 additions & 2 deletions docs/future/vite.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ To simulate the Cloudflare environment in Vite, Wrangler provides [Node proxies
```ts filename=vite.config.ts lines=[3,10]
import {
unstable_vitePlugin as remix,
unstable_vitePluginPresetCloudflare as cloudflare,
unstable_cloudflarePreset as cloudflare,
} from "@remix-run/dev";
import { defineConfig } from "vite";

Expand All @@ -147,6 +147,34 @@ The Cloudflare team is working to improve their Node proxies to support:

<docs-info>Vite will not use your Cloudflare Pages Functions (`functions/*`) in development as those are purely for Wrangler routing.</docs-info>

#### Augmenting Cloudflare load context in development

The Cloudflare preset accepts a `getRemixDevLoadContext` function that can be used to augment the load context in development:

```ts filename=vite.config.ts lines=[6-8]
export default defineConfig({
plugins: [
remix({
presets: [
cloudflare({
getRemixDevLoadContext: (context) => ({
...context,
extra: "add on whatever else you want",
}),
}),
],
}),
],
});
```

As the name implies, it **only augments the load context within Vite's dev server**, not within Wrangler nor in production.
This limitation exists because Vite is not yet able to delegate server code execution to other non-Node runtimes like Cloudflare's `workerd` runtime.

To get a consistent load context across Vite, Wrangler, and production you can define a module like `get-load-context.ts` that exports
shared logic for augmenting the load context.
Then you can apply the same logic within `getRemixDevLoadContext` and within `functions/[[page]].ts`.

## Splitting up client and server code

Remix lets you write code that [runs on both the client and the server][server-vs-client].
Expand Down Expand Up @@ -490,7 +518,7 @@ The Remix Vite plugin only officially supports [Cloudflare Pages][cloudflare-pag
```ts filename=vite.config.ts lines=[3,8-12,15]
import {
unstable_vitePlugin as remix,
unstable_vitePluginPresetCloudflare as cloudflare,
unstable_cloudflarePreset as cloudflare,
} from "@remix-run/dev";
import { defineConfig } from "vite";

Expand Down
14 changes: 11 additions & 3 deletions integration/vite-cloudflare-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,13 @@ test.describe("Vite / cloudflare", async () => {
"vite.config.ts": await VITE_CONFIG({
port,
viteSsrResolveExternalConditions: ["workerd", "worker"],
pluginOptions: `{ presets: [(await import("@remix-run/dev")).unstable_vitePluginPresetCloudflare()] }`,
pluginOptions: `{
presets: [
(await import("@remix-run/dev")).unstable_cloudflarePreset({
getRemixDevLoadContext: (ctx) => ({ ...ctx, extra: "stuff" })
})
]
}`,
}),
"functions/[[page]].ts": `
import { createPagesFunctionHandler } from "@remix-run/cloudflare-pages";
Expand Down Expand Up @@ -83,7 +89,7 @@ test.describe("Vite / cloudflare", async () => {
export async function loader({ context }: LoaderFunctionArgs) {
const { MY_KV } = context.env;
const value = await MY_KV.get(key);
return json({ value });
return json({ value, extra: context.extra });
}

export async function action({ request, context }: ActionFunctionArgs) {
Expand All @@ -105,10 +111,11 @@ test.describe("Vite / cloudflare", async () => {
}

export default function Index() {
const { value } = useLoaderData<typeof loader>();
const { value, extra } = useLoaderData<typeof loader>();
return (
<div>
<h1>Welcome to Remix</h1>
<p data-extra>Extra: {extra}</p>
{value ? (
<>
<p data-text>Value: {value}</p>
Expand Down Expand Up @@ -142,6 +149,7 @@ test.describe("Vite / cloudflare", async () => {
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");
Expand Down
5 changes: 1 addition & 4 deletions packages/remix-dev/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,4 @@ export * as cli from "./cli/index";
export type { Manifest as AssetsManifest } from "./manifest";
export { getDependenciesToBundle } from "./dependencies";
export type { Unstable_BuildManifest, Unstable_VitePluginPreset } from "./vite";
export {
unstable_vitePlugin,
unstable_vitePluginPresetCloudflare,
} from "./vite";
export { unstable_vitePlugin, unstable_cloudflarePreset } from "./vite";
2 changes: 1 addition & 1 deletion packages/remix-dev/vite/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@ export const unstable_vitePlugin: RemixVitePlugin = (...args) => {
return remixVitePlugin(...args);
};

export { preset as unstable_vitePluginPresetCloudflare } from "./presets/cloudflare";
export { preset as unstable_cloudflarePreset } from "./presets/cloudflare";
30 changes: 25 additions & 5 deletions packages/remix-dev/vite/presets/cloudflare.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,34 @@
import { type VitePluginPreset, setRemixDevLoadContext } from "../plugin";

export const preset: () => VitePluginPreset = () => ({
type GetRemixDevLoadContext = (
loadContext: Record<string, unknown>
) => Record<string, unknown> | Promise<Record<string, unknown>>;

const importWrangler = async () => {
try {
return await import("wrangler");
} catch (_) {
throw Error("Could not import `wrangler`. Do you have it installed?");
}
};

/**
* @param options.getRemixDevLoadContext - Augment the load context.
*/
export const preset = (
options: {
getRemixDevLoadContext?: GetRemixDevLoadContext;
} = {}
): VitePluginPreset => ({
name: "cloudflare",
remixConfig: async () => {
let { getBindingsProxy } = await import("wrangler");
let { getBindingsProxy } = await importWrangler();
let { bindings } = await getBindingsProxy();
let loadContext = bindings && { env: bindings };

let loadContext: Record<string, unknown> = { env: bindings };
if (options.getRemixDevLoadContext) {
loadContext = await options.getRemixDevLoadContext(loadContext);
}
setRemixDevLoadContext(loadContext);

return {};
},
});
Loading