Skip to content

Commit 8dcc50f

Browse files
[vite-plugin] improve inspector port handling (#8680)
* [vite-plugin] fix: make sure that the plugin keeps looking for available inspector ports by default * [vite-plugin] fix: make sure that users can use inspector port `0` to use a random port * Update packages/vite-plugin-cloudflare/README.md Co-authored-by: James Opstad <[email protected]> * move `resolvedInspectorPort` inside `cloudflare` function * resolve inspector port in configure server * remove global `resolvedInspectorPort` variable * Revert "remove global `resolvedInspectorPort` variable" This reverts commit 8382228. * remove global `resolvedInspectorPort` variable (and resolve inspector port id in debug middleware) * improve code and warn when 9229 is not available * remove unused imports * remove useless line of code * Update packages/vite-plugin-cloudflare/src/index.ts Co-authored-by: James Opstad <[email protected]> --------- Co-authored-by: James Opstad <[email protected]>
1 parent 28522ae commit 8dcc50f

File tree

9 files changed

+111
-38
lines changed

9 files changed

+111
-38
lines changed

.changeset/hip-parents-shave.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+
fix: make sure that users can specify inspector port `0` to use a random port

.changeset/warm-mammals-invent.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
"@cloudflare/vite-plugin": patch
3+
---
4+
5+
fix: make sure that the plugin keeps looking for available inspector ports by default
6+
7+
this change updates the plugin so that if an inspector port is not specified and the
8+
default inspector port (9229) is not available it keeps looking for other available
9+
port instead of crashing

packages/vite-plugin-cloudflare/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,7 @@ It accepts an optional `PluginConfig` parameter.
376376

377377
- `inspectorPort?: number | false`
378378

379-
Optional inspector port to use for debugging your workers, for more details on debugging see the [devtools section](#devtools). Can be set to `false` to disable the debugging inspector altogether. Defaults to `9229`.
379+
Optional inspector port to use for debugging your workers, for more details on debugging see the [devtools section](#devtools). By default the vite plugin will attempt to use the first available port it can find starting at `9229`.
380380

381381
> [!NOTE]
382382
> When running `wrangler deploy`, only your main (entry) Worker will be deployed.

packages/vite-plugin-cloudflare/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
"dependencies": {
4545
"@cloudflare/unenv-preset": "workspace:*",
4646
"@hattip/adapter-node": "^0.0.49",
47+
"get-port": "^7.1.0",
4748
"miniflare": "workspace:*",
4849
"picocolors": "^1.1.1",
4950
"tinyglobby": "^0.2.12",

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

Lines changed: 80 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import * as path from "node:path";
55
import { createMiddleware } from "@hattip/adapter-node";
66
import MagicString from "magic-string";
77
import { Miniflare } from "miniflare";
8+
import colors from "picocolors";
89
import * as vite from "vite";
910
import {
1011
createModuleReference,
@@ -40,6 +41,7 @@ import { resolvePluginConfig } from "./plugin-config";
4041
import { additionalModuleGlobalRE } from "./shared";
4142
import {
4243
cleanUrl,
44+
getFirstAvailablePort,
4345
getOutputDirectory,
4446
getRouterWorker,
4547
toMiniflareRequest,
@@ -315,13 +317,26 @@ export function cloudflare(pluginConfig: PluginConfig = {}): vite.Plugin[] {
315317
"Unexpected error: No Vite HTTP server"
316318
);
317319

320+
const inputInspectorPort = await getInputInspectorPortOption(
321+
pluginConfig,
322+
viteDevServer
323+
);
324+
318325
if (miniflare) {
319326
await miniflare.setOptions(
320-
getDevMiniflareOptions(resolvedPluginConfig, viteDevServer)
327+
getDevMiniflareOptions(
328+
resolvedPluginConfig,
329+
viteDevServer,
330+
inputInspectorPort
331+
)
321332
);
322333
} else {
323334
miniflare = new Miniflare(
324-
getDevMiniflareOptions(resolvedPluginConfig, viteDevServer)
335+
getDevMiniflareOptions(
336+
resolvedPluginConfig,
337+
viteDevServer,
338+
inputInspectorPort
339+
)
325340
);
326341
}
327342

@@ -352,15 +367,20 @@ export function cloudflare(pluginConfig: PluginConfig = {}): vite.Plugin[] {
352367
});
353368
};
354369
},
355-
configurePreviewServer(vitePreviewServer) {
370+
async configurePreviewServer(vitePreviewServer) {
356371
const workerConfigs = getWorkerConfigs(vitePreviewServer.config.root);
357372

373+
const inputInspectorPort = await getInputInspectorPortOption(
374+
pluginConfig,
375+
vitePreviewServer
376+
);
377+
358378
const miniflare = new Miniflare(
359379
getPreviewMiniflareOptions(
360380
vitePreviewServer,
361381
workerConfigs,
362382
pluginConfig.persistState ?? true,
363-
pluginConfig.inspectorPort
383+
inputInspectorPort
364384
)
365385
);
366386

@@ -612,7 +632,7 @@ export function cloudflare(pluginConfig: PluginConfig = {}): vite.Plugin[] {
612632
configureServer(viteDevServer) {
613633
if (
614634
resolvedPluginConfig.type === "workers" &&
615-
resolvedPluginConfig.inspectorPort !== false
635+
pluginConfig.inspectorPort !== false
616636
) {
617637
addDebugToVitePrintUrls(viteDevServer);
618638
}
@@ -624,22 +644,18 @@ export function cloudflare(pluginConfig: PluginConfig = {}): vite.Plugin[] {
624644
(worker) => worker.name
625645
);
626646

627-
viteDevServer.middlewares.use((req, res, next) => {
628-
if (
629-
req.url === debuggingPath &&
630-
resolvedPluginConfig.inspectorPort !== false
631-
) {
632-
const html = getDebugPathHtml(
633-
workerNames,
634-
resolvedPluginConfig.inspectorPort
635-
);
647+
viteDevServer.middlewares.use(async (req, res, next) => {
648+
const resolvedInspectorPort =
649+
await getResolvedInspectorPort(pluginConfig);
650+
if (req.url === debuggingPath && resolvedInspectorPort) {
651+
const html = getDebugPathHtml(workerNames, resolvedInspectorPort);
636652
res.setHeader("Content-Type", "text/html");
637653
return res.end(html);
638654
}
639655
next();
640656
});
641657
},
642-
configurePreviewServer(vitePreviewServer) {
658+
async configurePreviewServer(vitePreviewServer) {
643659
const workerConfigs = getWorkerConfigs(vitePreviewServer.config.root);
644660

645661
if (workerConfigs.length >= 1 && pluginConfig.inspectorPort !== false) {
@@ -651,15 +667,12 @@ export function cloudflare(pluginConfig: PluginConfig = {}): vite.Plugin[] {
651667
return worker.name;
652668
});
653669

654-
vitePreviewServer.middlewares.use((req, res, next) => {
655-
if (
656-
req.url === debuggingPath &&
657-
pluginConfig.inspectorPort !== false
658-
) {
659-
const html = getDebugPathHtml(
660-
workerNames,
661-
pluginConfig.inspectorPort ?? DEFAULT_INSPECTOR_PORT
662-
);
670+
vitePreviewServer.middlewares.use(async (req, res, next) => {
671+
const resolvedInspectorPort =
672+
await getResolvedInspectorPort(pluginConfig);
673+
674+
if (req.url === debuggingPath && resolvedInspectorPort) {
675+
const html = getDebugPathHtml(workerNames, resolvedInspectorPort);
663676
res.setHeader("Content-Type", "text/html");
664677
return res.end(html);
665678
}
@@ -751,6 +764,49 @@ export function cloudflare(pluginConfig: PluginConfig = {}): vite.Plugin[] {
751764
}
752765
}
753766

767+
/**
768+
* Gets the inspector port option that should be passed to miniflare based on the user's plugin config
769+
*
770+
* @param pluginConfig the user plugin configs
771+
* @param viteServer the vite (dev or preview) server
772+
* @returns the inspector port to require from miniflare or false if debugging is disabled
773+
*/
774+
async function getInputInspectorPortOption(
775+
pluginConfig: PluginConfig,
776+
viteServer: vite.ViteDevServer | vite.PreviewServer
777+
) {
778+
const inputInspectorPort =
779+
pluginConfig.inspectorPort ??
780+
(await getFirstAvailablePort(DEFAULT_INSPECTOR_PORT));
781+
782+
if (
783+
pluginConfig.inspectorPort === undefined &&
784+
inputInspectorPort !== DEFAULT_INSPECTOR_PORT
785+
) {
786+
viteServer.config.logger.warn(
787+
colors.dim(
788+
`Default inspector port ${DEFAULT_INSPECTOR_PORT} not available, using ${inputInspectorPort} instead\n`
789+
)
790+
);
791+
}
792+
793+
return inputInspectorPort;
794+
}
795+
796+
/**
797+
* Gets the resolved port of the inspector provided by miniflare
798+
*
799+
* @param pluginConfig the user's plugin configuration
800+
* @returns the resolved port of null if the user opted out of debugging
801+
*/
802+
async function getResolvedInspectorPort(pluginConfig: PluginConfig) {
803+
if (miniflare && pluginConfig.inspectorPort !== false) {
804+
const miniflareInspectorUrl = await miniflare.getInspectorURL();
805+
return Number.parseInt(miniflareInspectorUrl.port);
806+
}
807+
return null;
808+
}
809+
754810
/**
755811
* Gets the content of a the potential `.dev.vars` target file
756812
*

packages/vite-plugin-cloudflare/src/miniflare-options.ts

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import { getAssetsConfig } from "./asset-config";
1616
import {
1717
ASSET_WORKER_NAME,
1818
ASSET_WORKERS_COMPATIBILITY_DATE,
19-
DEFAULT_INSPECTOR_PORT,
2019
ROUTER_WORKER_NAME,
2120
} from "./constants";
2221
import { additionalModuleRE } from "./shared";
@@ -192,7 +191,8 @@ function getEntryWorkerConfig(
192191

193192
export function getDevMiniflareOptions(
194193
resolvedPluginConfig: ResolvedPluginConfig,
195-
viteDevServer: vite.ViteDevServer
194+
viteDevServer: vite.ViteDevServer,
195+
inspectorPort: number | false
196196
): MiniflareOptions {
197197
const resolvedViteConfig = viteDevServer.config;
198198
const entryWorkerConfig = getEntryWorkerConfig(resolvedPluginConfig);
@@ -298,8 +298,7 @@ export function getDevMiniflareOptions(
298298
worker: {
299299
...workerOptions,
300300
name: workerOptions.name ?? workerConfig.name,
301-
unsafeInspectorProxy:
302-
resolvedPluginConfig.inspectorPort !== false,
301+
unsafeInspectorProxy: inspectorPort !== false,
303302
modulesRoot: miniflareModulesRoot,
304303
unsafeEvalBinding: "__VITE_UNSAFE_EVAL__",
305304
serviceBindings: {
@@ -370,8 +369,8 @@ export function getDevMiniflareOptions(
370369

371370
return {
372371
log: logger,
373-
inspectorPort: resolvedPluginConfig.inspectorPort || undefined,
374-
unsafeInspectorProxy: resolvedPluginConfig.inspectorPort !== false,
372+
inspectorPort: inspectorPort === false ? undefined : inspectorPort,
373+
unsafeInspectorProxy: inspectorPort !== false,
375374
handleRuntimeStdio(stdout, stderr) {
376375
const decoder = new TextDecoder();
377376
stdout.forEach((data) => logger.info(decoder.decode(data)));
@@ -530,7 +529,7 @@ export function getPreviewMiniflareOptions(
530529
vitePreviewServer: vite.PreviewServer,
531530
workerConfigs: Unstable_Config[],
532531
persistState: PersistState,
533-
inspectorPort: number | false = DEFAULT_INSPECTOR_PORT
532+
inspectorPort: number | false
534533
): MiniflareOptions {
535534
const resolvedViteConfig = vitePreviewServer.config;
536535
const workers: Array<WorkerOptions> = workerConfigs.flatMap((config) => {
@@ -558,7 +557,7 @@ export function getPreviewMiniflareOptions(
558557

559558
return {
560559
log: logger,
561-
inspectorPort: inspectorPort || undefined,
560+
inspectorPort: inspectorPort === false ? undefined : inspectorPort,
562561
unsafeInspectorProxy: inspectorPort !== false,
563562
handleRuntimeStdio(stdout, stderr) {
564563
const decoder = new TextDecoder();

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

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import assert from "node:assert";
22
import * as path from "node:path";
33
import * as vite from "vite";
4-
import { DEFAULT_INSPECTOR_PORT } from "./constants";
54
import {
65
getValidatedWranglerConfigPath,
76
getWorkerConfig,
@@ -53,7 +52,6 @@ interface BasePluginConfig {
5352
configPaths: Set<string>;
5453
persistState: PersistState;
5554
cloudflareEnv: string | undefined;
56-
inspectorPort: number | false;
5755
experimental: {
5856
/** Experimental support for handling the _headers and _redirects files during Vite dev mode. */
5957
headersAndRedirectsDevModeSupport?: boolean;
@@ -92,7 +90,6 @@ export function resolvePluginConfig(
9290
): ResolvedPluginConfig {
9391
const configPaths = new Set<string>();
9492
const persistState = pluginConfig.persistState ?? true;
95-
const inspectorPort = pluginConfig.inspectorPort ?? DEFAULT_INSPECTOR_PORT;
9693
const experimental = pluginConfig.experimental ?? {};
9794
const root = userConfig.root ? path.resolve(userConfig.root) : process.cwd();
9895
const { CLOUDFLARE_ENV: cloudflareEnv } = vite.loadEnv(
@@ -120,7 +117,6 @@ export function resolvePluginConfig(
120117
type: "assets-only",
121118
config: entryWorkerResolvedConfig.config,
122119
configPaths,
123-
inspectorPort,
124120
persistState,
125121
rawConfigs: {
126122
entryWorker: entryWorkerResolvedConfig,
@@ -182,7 +178,6 @@ export function resolvePluginConfig(
182178
type: "workers",
183179
configPaths,
184180
persistState,
185-
inspectorPort,
186181
workers,
187182
entryWorkerEnvironmentName,
188183
rawConfigs: {

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import * as path from "node:path";
2+
import getPort, { portNumbers } from "get-port";
23
import { Request as MiniflareRequest } from "miniflare";
34
import * as vite from "vite";
45
import { ROUTER_WORKER_NAME } from "./constants";
@@ -66,3 +67,7 @@ export function cleanUrl(url: string): string {
6667
export type Optional<T, K extends keyof T> = Omit<T, K> & Pick<Partial<T>, K>;
6768

6869
export type MaybePromise<T> = Promise<T> | T;
70+
71+
export function getFirstAvailablePort(start: number) {
72+
return getPort({ port: portNumbers(start, 65535) });
73+
}

pnpm-lock.yaml

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)