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

feat: vite cloudflare websocket proxy #8834

Open
wants to merge 5 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
78 changes: 78 additions & 0 deletions docs/future/vite.md
Expand Up @@ -230,6 +230,84 @@ export const onRequest = createPagesFunctionHandler({
});
```

#### Durable Objects

<docs-warning>
Currently, Remix Cloudflare Plugin does not support Cloudflare Pages Functions advanced mode. Therefore, to use a Durable Object you need a separate Cloudflare Workers that export the Durable Object class and adjust your `wrangler.toml` accordingly.
</docs-warning>

<docs-info>
Cloudflare Workers supports WebSocket connection via Durable Object binding which is also supported by Remix Cloudflare Plugin via a WebSocket proxy.
<docs-info>

First, create a Cloudflare Workers script that exports a Durable Objects:

```ts filename=durable-objects.ts
export default {
async fetch() {},
};

export class MyDo {
#state;
#env;
constructor(state: DurableObjectState, env: Env) {
this.#state = state;
this.#env = env;
}

async fetch(request: Request) {}
}
```

Next, create another wrangler.toml for that script:

```toml filename=wrangler.durable-objects.toml
name = "durable-objects"
main = "durable-objects.ts"

[[durable_objects.bindings]]
name = "MyDo"
class_name = "MyDo"
script_name = "durable-objects"

[[migrations]]
tag = "v1"
new_classes = ["MyDo"]
```

And adjust your main wrangler.toml to bind the Durable Objects.

```toml filename=wrangler.toml
[[durable_objects.bindings]]
name = "MyDo"
class_name = "MyDo"
script_name = "durable-objects"

[[migrations]]
tag = "v1"
new_classes = ["MyDo"]
```

Next, add a package.json script to run the Cloudflare Workers script:

```json filename=package.json

```

To start a Vite dev server, start the Cloudflare Workers script first:

```bash
npm run dev:durable-objects
```

Next, start the dev server in separate terminal pane or window:

```bash
npm run dev
```

The `durable-objects.ts` script is a dependency of your main app. So, make sure it is already deployed before you deploy the main app.

## 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
87 changes: 87 additions & 0 deletions integration/helpers/vite.ts
Expand Up @@ -182,6 +182,41 @@ export const wranglerPagesDev = async ({
return () => proc.kill();
};

export const wranglerDev = async ({
cwd,
port,
config,
}: {
cwd: string;
port: number;
config?: string;
}) => {
let nodeBin = process.argv[0];

// grab wrangler bin from remix-run/remix root node_modules since its not copied into integration project's node_modules
let wranglerBin = path.resolve("node_modules/wrangler/bin/wrangler.js");

let proc = spawn(
nodeBin,
[
wranglerBin,
"dev",
"--config",
config || "wrangler.toml",
"--port",
String(port),
],
{
cwd,
stdio: "pipe",
env: { NODE_ENV: "development" },
}
);
await waitForServer(proc, { port });

return () => proc.kill();
};

type ServerArgs = {
cwd: string;
port: number;
Expand Down Expand Up @@ -223,6 +258,13 @@ type Fixtures = {
port: number;
cwd: string;
}>;
viteDevWaitOnWranglerService: (
files: Files,
serviceConfig?: string
) => Promise<{
port: number;
cwd: string;
}>;
customDev: (files: Files) => Promise<{
port: number;
cwd: string;
Expand All @@ -235,6 +277,13 @@ type Fixtures = {
port: number;
cwd: string;
}>;
wranglerPagesDevWaitOnWranglerService: (
files: Files,
serviceConfig?: string
) => Promise<{
port: number;
cwd: string;
}>;
};

export const test = base.extend<Fixtures>({
Expand All @@ -255,6 +304,24 @@ export const test = base.extend<Fixtures>({
stop?.();
},
// eslint-disable-next-line no-empty-pattern
viteDevWaitOnWranglerService: async ({}, use) => {
let stopVite: (() => unknown) | undefined;
let stopWrangler: (() => unknown) | undefined;
await use(async (files, serviceConfig) => {
let port = await getPort();
let cwd = await createProject(await files({ port }));
stopWrangler = await wranglerDev({
cwd,
port: await getPort(),
config: serviceConfig,
});
stopVite = await viteDev({ cwd, port });
return { port, cwd };
});
stopVite?.();
stopWrangler?.();
},
// eslint-disable-next-line no-empty-pattern
customDev: async ({}, use) => {
let stop: (() => unknown) | undefined;
await use(async (files) => {
Expand Down Expand Up @@ -291,6 +358,26 @@ export const test = base.extend<Fixtures>({
});
stop?.();
},
// eslint-disable-next-line no-empty-pattern
wranglerPagesDevWaitOnWranglerService: async ({}, use) => {
let stopWrangler: (() => unknown) | undefined;
let stopWranglerPages: (() => unknown) | undefined;
await use(async (files, serviceConfig) => {
let port = await getPort();
let cwd = await createProject(await files({ port }));
let { status } = viteBuild({ cwd });
expect(status).toBe(0);
stopWrangler = await wranglerDev({
cwd,
port: await getPort(),
config: serviceConfig,
});
stopWranglerPages = await wranglerPagesDev({ cwd, port });
return { port, cwd };
});
stopWranglerPages?.();
stopWrangler?.();
},
});

function node(
Expand Down