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
8 changes: 8 additions & 0 deletions .changeset/add-explorer-shortcut-vite.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"@cloudflare/vite-plugin": minor
---

Add `e` hotkey to open local explorer during dev

Press `e` during `vite dev` to open the local explorer UI at `/cdn-cgi/explorer`, which allows you to inspect the state of your D1, R2, KV, DO and Workflow bindings.

7 changes: 7 additions & 0 deletions .changeset/silly-pears-kick.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"wrangler": minor
---

explorer: expose the local explorer hotkey

List the local explorer's hotkey `[e]` in wrangler dev output.
6 changes: 1 addition & 5 deletions fixtures/interactive-dev-tests/tests/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,7 @@ if (process.platform === "win32") {
expect(wrangler.stdout).toContain("open devtools");
expect(wrangler.stdout).toContain("clear console");
expect(wrangler.stdout).toContain("to exit");
expect(wrangler.stdout).toContain("open local explorer");
expect(wrangler.stdout).not.toContain("rebuild container");
});
it("should not show hotkeys with --show-interactive-dev-session=false", async () => {
Expand All @@ -279,11 +280,6 @@ if (process.platform === "win32") {
expect(wrangler.stdout).not.toContain("clear console");
expect(wrangler.stdout).not.toContain("to exit");
expect(wrangler.stdout).not.toContain("rebuild container");
});
// TODO: update this when we release properly
it("should not show local explorer hotkey by default", async () => {
const wrangler = await startWranglerDev(args);
wrangler.pty.kill();
expect(wrangler.stdout).not.toContain("open local explorer");
});
});
Expand Down
2 changes: 1 addition & 1 deletion packages/create-cloudflare/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@
"indent-string": "^5.0.0",
"jsonc-parser": "catalog:default",
"magic-string": "^0.30.5",
"open": "^8.4.0",
"open": "catalog:default",
"recast": "^0.23.11",
"semver": "^7.7.1",
"smol-toml": "catalog:default",
Expand Down
1 change: 1 addition & 0 deletions packages/vite-plugin-cloudflare/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
"get-port": "^7.1.0",
"magic-string": "^0.30.12",
"mlly": "^1.7.4",
"open": "catalog:default",
"picocolors": "^1.1.1",
"semver": "^7.7.1",
"tinyglobby": "^0.2.12",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ import { stripVTControlCharacters } from "node:util";
import { afterAll, beforeAll, describe, test, vi } from "vitest";
import { PluginContext } from "../../../src/context";
import { resolvePluginConfig } from "../../../src/plugin-config";
import { addBindingsShortcut } from "../../../src/plugins/shortcuts";
import {
addBindingsShortcut,
addExplorerShortcut,
} from "../../../src/plugins/shortcuts";
import {
resetServerLogs,
satisfiesViteVersion,
Expand Down Expand Up @@ -186,4 +189,33 @@ describe.skipIf(!satisfiesViteVersion("7.2.7"))("shortcuts", () => {
"
`);
});

test("registers explorer shortcut with correct URL", async ({ expect }) => {
const mockOpen = vi.hoisted(() => vi.fn(() => ({ on: vi.fn() })));
vi.mock("open", () => ({ default: mockOpen }));

const mockBindCLIShortcuts = vi.spyOn(viteServer, "bindCLIShortcuts");

addExplorerShortcut(viteServer);

expect(mockBindCLIShortcuts).toHaveBeenCalledWith({
customShortcuts: [
{
key: "e",
description: "open local explorer",
action: expect.any(Function),
},
],
});

const { customShortcuts } = mockBindCLIShortcuts.mock.calls[0]?.[0] ?? {};
const explorerShortcut = customShortcuts?.find((s) => s.key === "e");

// eslint-disable-next-line @typescript-eslint/no-explicit-any
await explorerShortcut?.action?.(viteServer as any);

expect(mockOpen).toHaveBeenCalledWith(
expect.stringMatching(/^http:\/\/localhost:\d+\/cdn-cgi\/explorer$/)
);
});
});
66 changes: 65 additions & 1 deletion packages/vite-plugin-cloudflare/src/plugins/shortcuts.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { getDefaultDevRegistryPath, getWorkerRegistry } from "miniflare";
import {
CorePaths,
getDefaultDevRegistryPath,
getWorkerRegistry,
} from "miniflare";
import open from "open";
import colors from "picocolors";
import * as wrangler from "wrangler";
import { assertIsNotPreview, assertIsPreview } from "../context";
Expand All @@ -19,6 +24,7 @@ export const shortcutsPlugin = createPlugin("shortcuts", (ctx) => {

assertIsNotPreview(ctx);
addBindingsShortcut(viteDevServer, ctx);
addExplorerShortcut(viteDevServer);
},
async configurePreviewServer(vitePreviewServer) {
if (!isCustomShortcutsSupported) {
Expand All @@ -27,6 +33,7 @@ export const shortcutsPlugin = createPlugin("shortcuts", (ctx) => {

assertIsPreview(ctx);
addBindingsShortcut(vitePreviewServer, ctx);
addExplorerShortcut(vitePreviewServer);
},
};
});
Expand Down Expand Up @@ -107,3 +114,60 @@ export function addBindingsShortcut(
customShortcuts: [printBindingsShortcut],
});
}

export function addExplorerShortcut(
server: vite.ViteDevServer | vite.PreviewServer
) {
if (!process.stdin.isTTY) {
return;
}

const openExplorerShortcut = {
key: "e",
description: "open local explorer",
action: async (viteServer) => {
const url = viteServer.resolvedUrls?.local[0];
if (!url) {
viteServer.config.logger.warn("No local URL available");
return;
}

const explorerUrl = new URL(CorePaths.EXPLORER, url).href;
const childProcess = await open(explorerUrl);
childProcess.on("error", () => {
viteServer.config.logger.warn(
"Failed to open browser, the local explorer can be accessed at " +
explorerUrl
);
});
Comment thread
emily-shen marked this conversation as resolved.
},
} satisfies vite.CLIShortcut<vite.ViteDevServer | vite.PreviewServer>;

// Wrap bindCLIShortcuts to print our shortcut hint
const bindCLIShortcuts = server.bindCLIShortcuts.bind(server);
server.bindCLIShortcuts = (
options?: vite.BindCLIShortcutsOptions<
vite.ViteDevServer | vite.PreviewServer
>
) => {
if (
server.httpServer &&
process.stdin.isTTY &&
!process.env.CI &&
options?.print
) {
server.config.logger.info(
colors.dim(colors.green(" ➜")) +
colors.dim(" press ") +
colors.bold(`${openExplorerShortcut.key} + enter`) +
colors.dim(` to ${openExplorerShortcut.description}`)
);
}

bindCLIShortcuts(options);
};

server.bindCLIShortcuts({
customShortcuts: [openExplorerShortcut],
});
Comment thread
NuroDev marked this conversation as resolved.
}
2 changes: 1 addition & 1 deletion packages/wrangler/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@
"mock-socket": "^9.3.1",
"msw": "catalog:default",
"node-forge": "^1.3.2",
"open": "^8.4.0",
"open": "catalog:default",
"p-queue": "^9.0.0",
"patch-console": "^1.0.0",
"pretty-bytes": "^6.0.0",
Expand Down
3 changes: 1 addition & 2 deletions packages/wrangler/src/dev/hotkeys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,7 @@ export default function registerDevHotKeys(
},
{
keys: ["e"],
// This makes the label hidden but still enabled
// label: "open local explorer",
label: "open local explorer",
handler: async () => {
const { url } = await primaryDevEnv.proxy.ready.promise;
const explorerUrl = new URL(CorePaths.EXPLORER, url);
Expand Down
8 changes: 4 additions & 4 deletions packages/wrangler/src/dev/inspect.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { readFileSync } from "node:fs";
import path from "node:path";
import { fileURLToPath, URL } from "node:url";
import open from "open";
import open, { apps } from "open";
import {
isAllowedSourceMapPath,
isAllowedSourcePath,
Expand Down Expand Up @@ -276,16 +276,16 @@ export const openInspector = async (
const childProcess = await open(url, {
app: [
{
name: open.apps.chrome,
name: apps.chrome,
},
{
name: braveBrowser,
},
{
name: open.apps.edge,
name: apps.edge,
},
{
name: open.apps.firefox,
name: apps.firefox,
},
],
});
Expand Down
45 changes: 10 additions & 35 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions pnpm-workspace.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ catalog:
"capnp-es": "^0.0.14"
"capnweb": "^0.5.0"
"ci-info": "^4.4.0"
"open": "^11.0.0"
# CAUTION: Most usage of @cloudflare/vitest-pool-workers in this monorepo should use workspace:* instead of this catalog version
# However, some packages (pages-shared, workers-shared, etc...) need to be tested using vitest-pool-workers but are themselves
# ultimately included in vitest-pool-workers (through Wrangler), causing a circular dependency.
Expand Down
Loading