Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .changeset/config-watch-failed-restart.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
"@cloudflare/vite-plugin": patch
---

Keep watching config changes after a failed dev server restart

Previously, when a config change made the dev server restart fail — for example because the updated Worker config was invalid — the plugin stopped watching config changes entirely: the change handler (covering the Worker config files, local dev vars, and the assets configuration) removed itself before restarting, and only a successfully created server would register a fresh one. Since Vite keeps the current server running when a restart fails, every subsequent config change (including the one that fixes the config) was silently ignored for the rest of the session.

The handler now stays registered and guards against re-entrant restarts instead, so fixing the config restarts the dev server as expected.
Original file line number Diff line number Diff line change
Expand Up @@ -70,4 +70,53 @@ describe("config-changes", () => {
}, WAIT_FOR_OPTIONS);
}
);

test.runIf(!isBuild)(
"applies further Worker config changes after a broken config update",
async ({ expect }) => {
await vi.waitFor(
async () =>
expect(await getTextResponse()).toContain(
'The value of MY_VAR is "one"'
),
WAIT_FOR_OPTIONS
);

mockFileChange(path.join(__dirname, "../wrangler.json"), (content) =>
JSON.stringify({
...JSON.parse(content),
main: "./src/missing-after-broken-update.ts",
})
);

await vi.waitFor(
() =>
expect(serverLogs.errors.join()).toContain(
"missing-after-broken-update"
),
WAIT_FOR_OPTIONS
);

// The restart triggered by the broken config fails and keeps the
// current server running. A subsequent config change must still be
// picked up.
mockFileChange(path.join(__dirname, "../wrangler.json"), (content) =>
JSON.stringify({
...JSON.parse(content),
main: "./src/index.ts",
vars: {
MY_VAR: "three",
},
})
);

await vi.waitFor(
async () =>
expect(await getTextResponse()).toContain(
'The value of MY_VAR is "three"'
),
WAIT_FOR_OPTIONS
);
}
);
});
21 changes: 19 additions & 2 deletions packages/vite-plugin-cloudflare/src/plugins/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,9 +113,22 @@ export const configPlugin = createPlugin("config", (ctx) => {
ctx.setHasShownWorkerConfigWarnings(false);
},
configureServer(viteDevServer) {
// This variable is used to guard against config changes triggering
// a restart while another restart is already in flight. Note that we are
// deliberately not calling `watcher.off` since on failed restarts
// (e.g. the changed config is invalid) vite would resolve without replacing
// the server, so a removed handler would never be re-registered and
// config changes, including the one that fixes the config, would be
// ignored for the rest of the session.
let restartInFlight = false;

const configChangedHandler = async (changedFilePath: string) => {
assertIsNotPreview(ctx);

if (restartInFlight) {
return;
}

if (
ctx.resolvedPluginConfig.configPaths.has(changedFilePath) ||
hasLocalDevVarsFileChanged(
Expand All @@ -129,9 +142,13 @@ export const configPlugin = createPlugin("config", (ctx) => {
)
) {
debuglog("Config changed: " + changedFilePath);
viteDevServer.watcher.off("change", configChangedHandler);
restartInFlight = true;
debuglog("Restarting dev server and aborting previous setup");
await viteDevServer.restart();
try {
await viteDevServer.restart();
} finally {
restartInFlight = false;
}
}
};

Expand Down
Loading