Skip to content

Commit 3b6186e

Browse files
authored
Complete refactoring Vite plugin into separate plugins (#11166)
* Move ctx to module scope * Move miniflare into ctx * Move remaining module state into ctx * Create config plugin * Rename containers variables * Use single instance of debuglog * Moved dev to separate plugin * Moved preview to separate plugin * Simplified and moved trigger handlers plugin * Add debug plugin * Reintroduce inspector port * Add JSDoc * Tweaks * Create new PluginContext instance on server restarts * More refactoring * Add more JSDoc * Add comment about middleware placement
1 parent a586c03 commit 3b6186e

22 files changed

+1152
-1071
lines changed

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

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
11
import assert from "node:assert";
2-
import * as util from "node:util";
32
import { CoreHeaders } from "miniflare";
43
import * as vite from "vite";
54
import { additionalModuleRE } from "./plugins/additional-modules";
6-
import { VIRTUAL_WORKER_ENTRY } from "./plugins/virtual-modules";
75
import {
86
INIT_PATH,
97
IS_ENTRY_WORKER_HEADER,
108
UNKNOWN_HOST,
9+
VIRTUAL_WORKER_ENTRY,
1110
WORKER_ENTRY_PATH_HEADER,
1211
} from "./shared";
13-
import { getOutputDirectory } from "./utils";
12+
import { debuglog, getOutputDirectory } from "./utils";
1413
import type { WorkerConfig, WorkersResolvedConfig } from "./plugin-config";
1514
import type { MessageEvent, Miniflare, WebSocket } from "miniflare";
1615
import type { FetchFunctionOptions } from "vite/module-runner";
@@ -22,7 +21,6 @@ interface WebSocketContainer {
2221
}
2322

2423
const webSocketUndefinedError = "The WebSocket is undefined";
25-
const debuglog = util.debuglog("@cloudflare:vite-plugin");
2624

2725
function createHotChannel(
2826
webSocketContainer: WebSocketContainer

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,8 @@ export function getContainerOptions(options: {
5656
}
5757
});
5858
}
59+
60+
export type ContainerTagToOptionsMap = Map<
61+
string,
62+
NonNullable<ReturnType<typeof getContainerOptions>>[number]
63+
>;
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
import assert from "node:assert";
2+
import { Miniflare } from "miniflare";
3+
import { debuglog } from "./utils";
4+
import type { NodeJsCompat } from "./nodejs-compat";
5+
import type {
6+
AssetsOnlyResolvedConfig,
7+
PreviewResolvedConfig,
8+
ResolvedPluginConfig,
9+
WorkerConfig,
10+
WorkersResolvedConfig,
11+
} from "./plugin-config";
12+
import type { MiniflareOptions } from "miniflare";
13+
import type * as vite from "vite";
14+
15+
/**
16+
* Used to store state that should persist across server restarts.
17+
* This is then accessed via `PluginContext`.
18+
*/
19+
export interface SharedContext {
20+
miniflare?: Miniflare;
21+
hasShownWorkerConfigWarnings: boolean;
22+
/** Used to track whether hooks are being called because of a server restart or a server close event */
23+
isRestartingDevServer: boolean;
24+
}
25+
26+
/**
27+
* Used to provide context to internal plugins.
28+
* It should be reinstantiated each time the main plugin is created.
29+
*/
30+
export class PluginContext {
31+
#sharedContext: SharedContext;
32+
#resolvedPluginConfig?: ResolvedPluginConfig;
33+
#resolvedViteConfig?: vite.ResolvedConfig;
34+
35+
constructor(sharedContext: SharedContext) {
36+
this.#sharedContext = sharedContext;
37+
}
38+
39+
/** Creates a new Miniflare instance or updates the existing instance */
40+
async startOrUpdateMiniflare(options: MiniflareOptions): Promise<void> {
41+
if (!this.#sharedContext.miniflare) {
42+
debuglog("Creating new Miniflare instance");
43+
this.#sharedContext.miniflare = new Miniflare(options);
44+
} else {
45+
debuglog("Updating the existing Miniflare instance");
46+
await this.#sharedContext.miniflare.setOptions(options);
47+
}
48+
debuglog("Miniflare is ready");
49+
}
50+
51+
async disposeMiniflare(): Promise<void> {
52+
await this.#sharedContext.miniflare?.dispose();
53+
this.#sharedContext.miniflare = undefined;
54+
}
55+
56+
get miniflare(): Miniflare {
57+
assert(this.#sharedContext.miniflare, "Expected `miniflare` to be defined");
58+
59+
return this.#sharedContext.miniflare;
60+
}
61+
62+
/**
63+
* Gets the resolved inspector port provided by Miniflare
64+
*/
65+
async getResolvedInspectorPort(): Promise<number | null> {
66+
if (
67+
this.resolvedPluginConfig.inspectorPort === false ||
68+
!this.#sharedContext.miniflare
69+
) {
70+
return null;
71+
}
72+
73+
const miniflareInspectorUrl =
74+
await this.#sharedContext.miniflare.getInspectorURL();
75+
76+
return Number.parseInt(miniflareInspectorUrl.port);
77+
}
78+
79+
setHasShownWorkerConfigWarnings(hasShownWorkerConfigWarnings: boolean): void {
80+
this.#sharedContext.hasShownWorkerConfigWarnings =
81+
hasShownWorkerConfigWarnings;
82+
}
83+
84+
get hasShownWorkerConfigWarnings(): boolean {
85+
return this.#sharedContext.hasShownWorkerConfigWarnings;
86+
}
87+
88+
setIsRestartingDevServer(isRestartingDevServer: boolean): void {
89+
this.#sharedContext.isRestartingDevServer = isRestartingDevServer;
90+
}
91+
92+
get isRestartingDevServer(): boolean {
93+
return this.#sharedContext.isRestartingDevServer;
94+
}
95+
96+
setResolvedPluginConfig(resolvedPluginConfig: ResolvedPluginConfig): void {
97+
this.#resolvedPluginConfig = resolvedPluginConfig;
98+
}
99+
100+
get resolvedPluginConfig(): ResolvedPluginConfig {
101+
assert(
102+
this.#resolvedPluginConfig,
103+
"Expected `resolvedPluginConfig` to be defined"
104+
);
105+
106+
return this.#resolvedPluginConfig;
107+
}
108+
109+
setResolvedViteConfig(resolvedViteConfig: vite.ResolvedConfig): void {
110+
this.#resolvedViteConfig = resolvedViteConfig;
111+
}
112+
113+
get resolvedViteConfig(): vite.ResolvedConfig {
114+
assert(
115+
this.#resolvedViteConfig,
116+
"Expected `resolvedViteConfig` to be defined"
117+
);
118+
119+
return this.#resolvedViteConfig;
120+
}
121+
122+
getWorkerConfig(environmentName: string): WorkerConfig | undefined {
123+
return this.resolvedPluginConfig.type === "workers"
124+
? this.resolvedPluginConfig.workers[environmentName]
125+
: undefined;
126+
}
127+
128+
get entryWorkerConfig(): WorkerConfig | undefined {
129+
if (this.resolvedPluginConfig.type !== "workers") {
130+
return;
131+
}
132+
133+
return this.resolvedPluginConfig.workers[
134+
this.resolvedPluginConfig.entryWorkerEnvironmentName
135+
];
136+
}
137+
138+
getNodeJsCompat(environmentName: string): NodeJsCompat | undefined {
139+
return this.resolvedPluginConfig.type === "workers"
140+
? this.resolvedPluginConfig.nodeJsCompatMap.get(environmentName)
141+
: undefined;
142+
}
143+
}
144+
145+
interface NarrowedPluginContext<T extends ResolvedPluginConfig>
146+
extends PluginContext {
147+
readonly resolvedPluginConfig: T;
148+
}
149+
150+
export type AssetsOnlyPluginContext =
151+
NarrowedPluginContext<AssetsOnlyResolvedConfig>;
152+
export type WorkersPluginContext = NarrowedPluginContext<WorkersResolvedConfig>;
153+
export type PreviewPluginContext = NarrowedPluginContext<PreviewResolvedConfig>;
154+
155+
export function assertIsNotPreview(
156+
ctx: PluginContext
157+
): asserts ctx is AssetsOnlyPluginContext | WorkersPluginContext {
158+
assert(
159+
ctx.resolvedPluginConfig.type !== "preview",
160+
`Expected "assets-only" or "workers" plugin config`
161+
);
162+
}
163+
164+
export function assertIsPreview(
165+
ctx: PluginContext
166+
): asserts ctx is PreviewPluginContext {
167+
assert(
168+
ctx.resolvedPluginConfig.type === "preview",
169+
`Expected "preview" plugin config`
170+
);
171+
}

packages/vite-plugin-cloudflare/src/debugging.ts renamed to packages/vite-plugin-cloudflare/src/debug.ts

Lines changed: 9 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import assert from "node:assert";
22
import getPort, { portNumbers } from "get-port";
33
import colors from "picocolors";
4-
import type { ResolvedPluginConfig } from "./plugin-config";
5-
import type { Miniflare } from "miniflare";
4+
import type { PluginContext } from "./context";
65
import type * as vite from "vite";
76

87
export const DEBUG_PATH = "/__debug";
@@ -11,19 +10,15 @@ const DEFAULT_INSPECTOR_PORT = 9229;
1110
/**
1211
* Gets the inspector port option that should be passed to Miniflare based on the user's plugin config
1312
*/
14-
export async function getInputInspectorPortOption(
15-
resolvedPluginConfig: ResolvedPluginConfig,
16-
viteServer: vite.ViteDevServer | vite.PreviewServer,
17-
miniflare?: Miniflare
13+
export async function getInputInspectorPort(
14+
ctx: PluginContext,
15+
viteServer: vite.ViteDevServer | vite.PreviewServer
1816
) {
1917
if (
20-
resolvedPluginConfig.inspectorPort === undefined ||
21-
resolvedPluginConfig.inspectorPort === 0
18+
ctx.resolvedPluginConfig.inspectorPort === undefined ||
19+
ctx.resolvedPluginConfig.inspectorPort === 0
2220
) {
23-
const resolvedInspectorPort = await getResolvedInspectorPort(
24-
resolvedPluginConfig,
25-
miniflare
26-
);
21+
const resolvedInspectorPort = await ctx.getResolvedInspectorPort();
2722

2823
if (resolvedInspectorPort !== null) {
2924
// the user is not specifying an inspector port to use and we're already
@@ -33,11 +28,11 @@ export async function getInputInspectorPortOption(
3328
}
3429

3530
const inputInspectorPort =
36-
resolvedPluginConfig.inspectorPort ??
31+
ctx.resolvedPluginConfig.inspectorPort ??
3732
(await getFirstAvailablePort(DEFAULT_INSPECTOR_PORT));
3833

3934
if (
40-
resolvedPluginConfig.inspectorPort === undefined &&
35+
ctx.resolvedPluginConfig.inspectorPort === undefined &&
4136
inputInspectorPort !== DEFAULT_INSPECTOR_PORT
4237
) {
4338
viteServer.config.logger.warn(
@@ -50,22 +45,6 @@ export async function getInputInspectorPortOption(
5045
return inputInspectorPort;
5146
}
5247

53-
/**
54-
* Gets the resolved inspector port provided by Miniflare
55-
*/
56-
export async function getResolvedInspectorPort(
57-
resolvedPluginConfig: ResolvedPluginConfig,
58-
miniflare: Miniflare | undefined
59-
) {
60-
if (miniflare && resolvedPluginConfig.inspectorPort !== false) {
61-
const miniflareInspectorUrl = await miniflare.getInspectorURL();
62-
63-
return Number.parseInt(miniflareInspectorUrl.port);
64-
}
65-
66-
return null;
67-
}
68-
6948
function getFirstAvailablePort(start: number) {
7049
return getPort({ port: portNumbers(start, 65535) });
7150
}

0 commit comments

Comments
 (0)