Skip to content

Commit 6c6afbd

Browse files
authored
Refactor preview mode and ensure compatibility with Vite 7 (cloudflare#9647)
* Add preview resolved plugin config type * Resolve preview worker configs in plugin config * Fix failing test * Improved plugin config * Tidy up * remove root property from module runner * Add changeset * Fix changeset * Fix debugging * Change import * Remove parameterised type * Update comment * Add noUnusedParameters to TSConfig * Fix mixedMode reference
1 parent b066cf8 commit 6c6afbd

File tree

21 files changed

+341
-291
lines changed

21 files changed

+341
-291
lines changed

.changeset/evil-crabs-look.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@cloudflare/vite-plugin": patch
3+
---
4+
5+
In Vite 7, the `applyToEnvironment` hook is called in preview mode. This is now accounted for to ensure compatibility.

packages/vite-plugin-cloudflare/playground/node-compat/__tests__/worker-postgres/postgres.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,6 @@ test.runIf(!process.env.CI)(
1212
"should be able to use pg library to send a query",
1313
async () => {
1414
const result = await getJsonResponse("/send-query");
15-
expect(result!.id).toEqual("1");
15+
expect(result!.id).toEqual("21");
1616
}
1717
);

packages/vite-plugin-cloudflare/src/__tests__/get-worker-config.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ describe("getWorkerConfig", () => {
5656
});
5757

5858
test("should read a simple wrangler.toml file", () => {
59-
const { config, raw, nonApplicable } = getWorkerConfig(
59+
const { config, nonApplicable } = getWorkerConfig(
6060
fileURLToPath(new URL("fixtures/simple-wrangler.jsonc", import.meta.url)),
6161
undefined,
6262
false

packages/vite-plugin-cloudflare/src/__tests__/validate_worker_environments_resolved_configs.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { describe, expect, test } from "vitest";
22
import { validateWorkerEnvironmentsResolvedConfigs } from "../worker-environments-validation";
3-
import type { WorkerPluginConfig } from "../plugin-config";
3+
import type { WorkersResolvedConfig } from "../plugin-config";
44
import type * as vite from "vite";
55

66
function getWorkerPluginConfig(
@@ -23,7 +23,7 @@ function getWorkerPluginConfig(
2323

2424
return {
2525
workers,
26-
} as WorkerPluginConfig;
26+
} as WorkersResolvedConfig;
2727
}
2828

2929
type WorkerViteConfig = {

packages/vite-plugin-cloudflare/src/asset-config.ts

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,22 @@ import {
1414
HeadersSchema,
1515
RedirectsSchema,
1616
} from "@cloudflare/workers-shared/utils/types";
17-
import type { ResolvedPluginConfig } from "./plugin-config";
17+
import type {
18+
AssetsOnlyResolvedConfig,
19+
WorkersResolvedConfig,
20+
} from "./plugin-config";
1821
import type { Logger } from "@cloudflare/workers-shared/utils/configuration/types";
1922
import type { AssetConfig } from "@cloudflare/workers-shared/utils/types";
20-
import type { ResolvedConfig } from "vite";
23+
import type * as vite from "vite";
2124
import type { Unstable_Config } from "wrangler";
2225

2326
/**
2427
* Returns true if the `changedFile` matches one of the _headers or _redirects files,
2528
* and the experimental support for these files is turned on.
2629
*/
2730
export function hasAssetsConfigChanged(
28-
resolvedPluginConfig: ResolvedPluginConfig,
29-
resolvedViteConfig: ResolvedConfig,
31+
resolvedPluginConfig: AssetsOnlyResolvedConfig | WorkersResolvedConfig,
32+
resolvedViteConfig: vite.ResolvedConfig,
3033
changedFilePath: string
3134
) {
3235
if (!resolvedPluginConfig.experimental?.headersAndRedirectsDevModeSupport) {
@@ -43,9 +46,9 @@ export function hasAssetsConfigChanged(
4346
* taking into account whether experimental _headers and _redirects support is on.
4447
*/
4548
export function getAssetsConfig(
46-
resolvedPluginConfig: ResolvedPluginConfig,
49+
resolvedPluginConfig: AssetsOnlyResolvedConfig | WorkersResolvedConfig,
4750
entryWorkerConfig: Unstable_Config | undefined,
48-
resolvedConfig: ResolvedConfig
51+
resolvedConfig: vite.ResolvedConfig
4952
): AssetConfig {
5053
const assetsConfig =
5154
resolvedPluginConfig.type === "assets-only"
@@ -130,10 +133,10 @@ export function getAssetsConfig(
130133
};
131134
}
132135

133-
function getRedirectsConfigPath(config: ResolvedConfig): string {
136+
function getRedirectsConfigPath(config: vite.ResolvedConfig): string {
134137
return path.join(config.publicDir, REDIRECTS_FILENAME);
135138
}
136139

137-
function getHeadersConfigPath(config: ResolvedConfig): string {
140+
function getHeadersConfigPath(config: vite.ResolvedConfig): string {
138141
return path.join(config.publicDir, HEADERS_FILENAME);
139142
}

packages/vite-plugin-cloudflare/src/build.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,15 @@ import assert from "node:assert";
22
import * as fs from "node:fs";
33
import * as path from "node:path";
44
import colors from "picocolors";
5-
import type { ResolvedPluginConfig } from "./plugin-config";
5+
import type {
6+
AssetsOnlyResolvedConfig,
7+
WorkersResolvedConfig,
8+
} from "./plugin-config";
69
import type * as vite from "vite";
710
import type { Unstable_Config } from "wrangler";
811

912
export function createBuildApp(
10-
resolvedPluginConfig: ResolvedPluginConfig
13+
resolvedPluginConfig: AssetsOnlyResolvedConfig | WorkersResolvedConfig
1114
): (builder: vite.ViteBuilder) => Promise<void> {
1215
return async (builder) => {
1316
const clientEnvironment = builder.environments.client;

packages/vite-plugin-cloudflare/src/cloudflare-environment.ts

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import * as vite from "vite";
33
import { isNodeCompat } from "./node-js-compat";
44
import { INIT_PATH, UNKNOWN_HOST, VITE_DEV_METADATA_HEADER } from "./shared";
55
import { getOutputDirectory } from "./utils";
6-
import type { ResolvedPluginConfig, WorkerConfig } from "./plugin-config";
6+
import type { WorkerConfig, WorkersResolvedConfig } from "./plugin-config";
77
import type { Fetcher } from "@cloudflare/workers-types/experimental";
88
import type {
99
MessageEvent,
@@ -88,7 +88,6 @@ export class CloudflareDevEnvironment extends vite.DevEnvironment {
8888

8989
async initRunner(
9090
worker: ReplaceWorkersTypes<Fetcher>,
91-
root: string,
9291
workerConfig: WorkerConfig
9392
) {
9493
this.#worker = worker;
@@ -98,7 +97,6 @@ export class CloudflareDevEnvironment extends vite.DevEnvironment {
9897
{
9998
headers: {
10099
[VITE_DEV_METADATA_HEADER]: JSON.stringify({
101-
root,
102100
entryPath: workerConfig.main,
103101
}),
104102
upgrade: "websocket",
@@ -193,14 +191,10 @@ export function createCloudflareEnvironmentOptions(
193191
}
194192

195193
export function initRunners(
196-
resolvedPluginConfig: ResolvedPluginConfig,
194+
resolvedPluginConfig: WorkersResolvedConfig,
197195
viteDevServer: vite.ViteDevServer,
198196
miniflare: Miniflare
199197
): Promise<void[]> | undefined {
200-
if (resolvedPluginConfig.type === "assets-only") {
201-
return;
202-
}
203-
204198
return Promise.all(
205199
Object.entries(resolvedPluginConfig.workers).map(
206200
async ([environmentName, workerConfig]) => {
@@ -210,7 +204,7 @@ export function initRunners(
210204
viteDevServer.environments[
211205
environmentName
212206
] as CloudflareDevEnvironment
213-
).initRunner(worker, viteDevServer.config.root, workerConfig);
207+
).initRunner(worker, workerConfig);
214208
}
215209
)
216210
);

packages/vite-plugin-cloudflare/src/constants.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ export const PUBLIC_DIR_PREFIX = "/__vite_public_dir__";
1313

1414
export const DEFAULT_INSPECTOR_PORT = 9229;
1515

16+
export const DEBUG_PATH = "/__debug";
17+
1618
export const kRequestType = Symbol("kRequestType");
1719

1820
declare module "http" {

packages/vite-plugin-cloudflare/src/debugging.ts

Lines changed: 74 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,75 @@
11
import assert from "node:assert";
2+
import getPort, { portNumbers } from "get-port";
23
import colors from "picocolors";
4+
import { DEBUG_PATH, DEFAULT_INSPECTOR_PORT } from "./constants";
5+
import type { ResolvedPluginConfig } from "./plugin-config";
6+
import type { Miniflare } from "miniflare";
37
import type * as vite from "vite";
48

5-
export const debuggingPath = "/__debug";
9+
/**
10+
* Gets the inspector port option that should be passed to Miniflare based on the user's plugin config
11+
*/
12+
export async function getInputInspectorPortOption(
13+
resolvedPluginConfig: ResolvedPluginConfig,
14+
viteServer: vite.ViteDevServer | vite.PreviewServer,
15+
miniflare?: Miniflare
16+
) {
17+
if (
18+
resolvedPluginConfig.inspectorPort === undefined ||
19+
resolvedPluginConfig.inspectorPort === 0
20+
) {
21+
const resolvedInspectorPort = await getResolvedInspectorPort(
22+
resolvedPluginConfig,
23+
miniflare
24+
);
25+
26+
if (resolvedInspectorPort !== null) {
27+
// the user is not specifying an inspector port to use and we're already
28+
// using one (this is a server restart) so let's just reuse that
29+
return resolvedInspectorPort;
30+
}
31+
}
32+
33+
const inputInspectorPort =
34+
resolvedPluginConfig.inspectorPort ??
35+
(await getFirstAvailablePort(DEFAULT_INSPECTOR_PORT));
36+
37+
if (
38+
resolvedPluginConfig.inspectorPort === undefined &&
39+
inputInspectorPort !== DEFAULT_INSPECTOR_PORT
40+
) {
41+
viteServer.config.logger.warn(
42+
colors.dim(
43+
`Default inspector port ${DEFAULT_INSPECTOR_PORT} not available, using ${inputInspectorPort} instead\n`
44+
)
45+
);
46+
}
47+
48+
return inputInspectorPort;
49+
}
650

751
/**
8-
* Modifies the url printing logic to also include a url that developers can use to open devtools to debug their Worker(s)
9-
*
10-
* @param server a vite server (dev or preview)
52+
* Gets the resolved inspector port provided by Miniflare
53+
*/
54+
export async function getResolvedInspectorPort(
55+
resolvedPluginConfig: ResolvedPluginConfig,
56+
miniflare: Miniflare | undefined
57+
) {
58+
if (miniflare && resolvedPluginConfig.inspectorPort !== false) {
59+
const miniflareInspectorUrl = await miniflare.getInspectorURL();
60+
61+
return Number.parseInt(miniflareInspectorUrl.port);
62+
}
63+
64+
return null;
65+
}
66+
67+
function getFirstAvailablePort(start: number) {
68+
return getPort({ port: portNumbers(start, 65535) });
69+
}
70+
71+
/**
72+
* Modifies the URL printing logic to also include a URL that developers can use to open DevTools to debug their Worker(s)
1173
*/
1274
export function addDebugToVitePrintUrls(
1375
server: vite.ViteDevServer | vite.PreviewServer
@@ -28,34 +90,30 @@ export function addDebugToVitePrintUrls(
2890
)
2991
);
3092
server.config.logger.info(
31-
` ${colors.green("➜")} ${colors.bold("Debug")}: ${colorDebugUrl(`${protocol}//${hostname}:${port}${debuggingPath}`)}`
93+
` ${colors.green("➜")} ${colors.bold("Debug")}: ${colorDebugUrl(`${protocol}//${hostname}:${port}${DEBUG_PATH}`)}`
3294
);
3395
}
3496
};
3597
}
3698

3799
/**
38-
* Generate an HTML text that comprises of a single script that:
39-
* - redirects the page to the devtools for the debugging of the first available worker
40-
* - opens tags to the devtools for all the remaining workers if any
41-
*
42-
* Note: this works based on the miniflare inspector proxy logic (where workers are available via
43-
* paths comprised of their names)
100+
* Generate HTML that comprises a single script that:
101+
* - redirects the page to the DevTools for debugging the first available Worker
102+
* - opens tabs to the DevTools for all the remaining workers if any
44103
*
45-
* @param workerNames the names of all the available workers
46-
* @param inspectorPort the inspector port that miniflare is using
47-
* @returns the generated html
104+
* Note: this works based on the Miniflare inspector proxy logic (where Workers are available via
105+
* their names)
48106
*/
49107
export function getDebugPathHtml(workerNames: string[], inspectorPort: number) {
50108
// this function should always be called only when there is at least one worker to debug
51109
assert(workerNames.length >= 1, "no workers present to debug");
52110

53111
const workerDevtoolsUrls = workerNames.map((workerName) => {
54-
const localHost = `localhost:${inspectorPort}/${workerName}`;
112+
const localhost = `localhost:${inspectorPort}/${workerName}`;
55113
const searchParams = new URLSearchParams({
56114
theme: "systemPreferred",
57115
debugger: "true",
58-
ws: localHost,
116+
ws: localhost,
59117
domain: workerName,
60118
});
61119
const devtoolsFrontendUrl = `https://devtools.devprod.cloudflare.dev/js_app?${searchParams}`;

packages/vite-plugin-cloudflare/src/deploy-config.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@ import * as fs from "node:fs";
33
import * as path from "node:path";
44
import * as vite from "vite";
55
import { unstable_readConfig } from "wrangler";
6-
import type { ResolvedPluginConfig } from "./plugin-config";
6+
import type {
7+
AssetsOnlyResolvedConfig,
8+
WorkersResolvedConfig,
9+
} from "./plugin-config";
710

811
interface DeployConfig {
912
configPath: string;
@@ -47,7 +50,7 @@ function getRelativePathToWorkerConfig(
4750
}
4851

4952
export function writeDeployConfig(
50-
resolvedPluginConfig: ResolvedPluginConfig,
53+
resolvedPluginConfig: AssetsOnlyResolvedConfig | WorkersResolvedConfig,
5154
resolvedViteConfig: vite.ResolvedConfig
5255
) {
5356
const deployConfigPath = getDeployConfigPath(resolvedViteConfig.root);

0 commit comments

Comments
 (0)