Skip to content
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
7 changes: 7 additions & 0 deletions .changeset/fix-child-compiler-websocket-assertion.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@cloudflare/vite-plugin": patch
---

Avoid `The WebSocket is undefined` error when frameworks create a child Vite server during build

React Router creates a child Vite dev server during production builds to compile route files. This could previously cause an intermittent `The WebSocket is undefined` assertion error.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import "../base-tests";
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { cloudflare } from "@cloudflare/vite-plugin";
import react from "@vitejs/plugin-react";
import { createServer, defineConfig } from "vite";
import type { Plugin } from "vite";

/**
* Emulates React Router's child compiler setup, which create a child Vite dev server and
* removes `configureServer` hooks from plugins.
*
* @see https://github.com/cloudflare/workers-sdk/issues/8909
*/
function childCompilerPlugin(): Plugin {
return {
name: "test-child-compiler",
async configResolved(resolvedConfig) {
if (resolvedConfig.command !== "build") {
return;
}

const childServer = await createServer({
root: resolvedConfig.root,
configFile: false,
plugins: resolvedConfig.plugins
.filter((plugin) => plugin.name !== "test-child-compiler")
.map((plugin) => ({ ...plugin, configureServer: undefined })),
});

// This is the code path that triggers the bug: Vite's dep optimizer
// or module pruning calls hot.send() on the worker environment, but
// the WebSocket was never initialized because configureServer (which
// calls initRunner) was stripped.
const workerEnvironment = childServer.environments.worker;
if (workerEnvironment) {
workerEnvironment.hot.send({ type: "full-reload", path: "*" });
}

await childServer.close();
},
};
}

export default defineConfig({
plugins: [
react(),
cloudflare({ inspectorPort: false, persistState: false }),
childCompilerPlugin(),
],
});
13 changes: 13 additions & 0 deletions packages/vite-plugin-cloudflare/src/cloudflare-environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,19 @@ export function createCloudflareEnvironmentOptions({
define,
dev: {
createEnvironment(name, config) {
// CloudflareDevEnvironment requires initRunner() to be called
// via configureServer. If that hook was stripped, fall back
// to a standard Vite environment. This is needed to support
// React Router's child compiler, which is a separate Vite dev server
// that runs during the build.
const hasConfigureServer = config.plugins.some(
(plugin) =>
plugin.name === "vite-plugin-cloudflare:dev" &&
plugin.configureServer !== undefined
);
if (!hasConfigureServer) {
return vite.createRunnableDevEnvironment(name, config);
}
return new CloudflareDevEnvironment(name, config);
},
},
Expand Down
Loading