Skip to content

Commit

Permalink
feat(workerd): use new transport (#144)
Browse files Browse the repository at this point in the history
  • Loading branch information
hi-ogawa authored Nov 7, 2024
1 parent b5c4861 commit 633b74d
Show file tree
Hide file tree
Showing 12 changed files with 53 additions and 139 deletions.
24 changes: 2 additions & 22 deletions examples/browser-cli/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import {
createServer,
parseAstAsync,
} from "vite";
import type { FetchFunction, ModuleRunner } from "vite/module-runner";
import type { ModuleRunner } from "vite/module-runner";
import { vitePluginFetchModuleServer } from "../../web-worker/src/lib/fetch-module-server";

const headless = !process.env["CLI_HEADED"];
const extension = process.env["CLI_EXTENSION"] ?? "tsx";
Expand Down Expand Up @@ -185,25 +186,4 @@ function vitePluginBrowserRunner(): Plugin {
};
}

// https://github.com/vitejs/vite/discussions/18191
function vitePluginFetchModuleServer(): Plugin {
return {
name: vitePluginFetchModuleServer.name,
configureServer(server) {
server.middlewares.use(async (req, res, next) => {
const url = new URL(req.url ?? "/", "https://any.local");
if (url.pathname === "/@vite/fetchModule") {
const [name, ...args] = JSON.parse(url.searchParams.get("payload")!);
const result = await server.environments[name]!.fetchModule(
...(args as Parameters<FetchFunction>),
);
res.end(JSON.stringify(result));
return;
}
next();
});
},
};
}

main();
21 changes: 3 additions & 18 deletions examples/browser-cli/src/runner.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
import {
ESModulesEvaluator,
type FetchFunction,
ModuleRunner,
} from "vite/module-runner";
import { ESModulesEvaluator, ModuleRunner } from "vite/module-runner";
import { fetchClientFetchModule } from "../../web-worker/src/lib/fetch-module-client";

export async function start(options: { root: string }) {
const runner = new ModuleRunner(
{
root: options.root,
sourcemapInterceptor: false,
transport: {
fetchModule: fetchModuleFetchClient("custom"),
invoke: fetchClientFetchModule("custom"),
},
hmr: false,
},
Expand All @@ -19,15 +16,3 @@ export async function start(options: { root: string }) {

return runner;
}

// https://github.com/vitejs/vite/discussions/18191
function fetchModuleFetchClient(environmentName: string): FetchFunction {
return async (...args) => {
const payload = JSON.stringify([environmentName, ...args]);
const response = await fetch(
"/@vite/fetchModule?" + new URLSearchParams({ payload }),
);
const result = response.json();
return result as any;
};
}
12 changes: 0 additions & 12 deletions examples/web-worker-rsc/src/lib/fetch-module-client.ts

This file was deleted.

21 changes: 0 additions & 21 deletions examples/web-worker-rsc/src/lib/fetch-module-server.ts

This file was deleted.

4 changes: 2 additions & 2 deletions examples/web-worker-rsc/src/lib/runner.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ESModulesEvaluator, ModuleRunner } from "vite/module-runner";
import { fetchClientFetchModule } from "./fetch-module-client";
import { fetchClientFetchModule } from "../../../web-worker/src/lib/fetch-module-client";

export function createFetchRunner(options: {
root: string;
Expand All @@ -10,7 +10,7 @@ export function createFetchRunner(options: {
root: options.root,
sourcemapInterceptor: false,
transport: {
fetchModule: fetchClientFetchModule(options.environmentName),
invoke: fetchClientFetchModule(options.environmentName),
},
hmr: false,
},
Expand Down
2 changes: 1 addition & 1 deletion examples/web-worker-rsc/vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import react from "@vitejs/plugin-react";
import { defineConfig } from "vite";
import { vitePluginFetchModuleServer } from "../web-worker/src/lib/fetch-module-server";
import { vitePluginWorkerEnvironment } from "../web-worker/vite.config";
import { vitePluginFetchModuleServer } from "./src/lib/fetch-module-server";

export default defineConfig((_env) => ({
clearScreen: false,
Expand Down
12 changes: 7 additions & 5 deletions examples/web-worker/src/lib/fetch-module-client.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import type { FetchFunction } from "vite";
import type { ModuleRunnerTransport } from "vite/module-runner";

export function fetchClientFetchModule(environmentName: string): FetchFunction {
return async (...args) => {
const payload = JSON.stringify([environmentName, ...args]);
export function fetchClientFetchModule(
environmentName: string,
): ModuleRunnerTransport["invoke"] {
return async (payload) => {
const data = JSON.stringify([environmentName, payload]);
const response = await fetch(
"/@vite/fetchModule?" + new URLSearchParams({ payload }),
"/@vite/invoke?" + new URLSearchParams({ data }),
);
const result = response.json();
return result as any;
Expand Down
11 changes: 5 additions & 6 deletions examples/web-worker/src/lib/fetch-module-server.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
import type { FetchFunction, Plugin } from "vite";
import { type Plugin } from "vite";

export function vitePluginFetchModuleServer(): Plugin {
return {
name: vitePluginFetchModuleServer.name,
configureServer(server) {
server.middlewares.use(async (req, res, next) => {
const url = new URL(req.url ?? "/", "https://any.local");
if (url.pathname === "/@vite/fetchModule") {
const [name, ...args] = JSON.parse(url.searchParams.get("payload")!);
const result = await server.environments[name]!.fetchModule(
...(args as Parameters<FetchFunction>),
);
if (url.pathname === "/@vite/invoke") {
const [name, payload] = JSON.parse(url.searchParams.get("data")!);
const devEnv = server.environments[name]!;
const result = await devEnv.hot.handleInvoke(payload);
res.end(JSON.stringify(result));
return;
}
Expand Down
2 changes: 1 addition & 1 deletion examples/web-worker/src/lib/runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export function createFetchRunner(options: {
root: options.root,
sourcemapInterceptor: false,
transport: {
fetchModule: fetchClientFetchModule(options.environmentName),
invoke: fetchClientFetchModule(options.environmentName),
},
hmr: false,
},
Expand Down
50 changes: 17 additions & 33 deletions packages/workerd/src/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,19 +123,14 @@ export async function createWorkerdDevEnvironment(
},
unsafeEvalBinding: "__viteUnsafeEval",
serviceBindings: {
__viteFetchModule: async (request) => {
const args = await request.json();
try {
const result = await devEnv.fetchModule(...(args as [any, any]));
return MiniflareResponse.json(result);
} catch (error) {
console.error("[fetchModule]", args, error);
throw error;
}
__viteInvoke: async (request) => {
const payload = (await request.json()) as HotPayload;
const result = await devEnv.hot.handleInvoke(payload);
return MiniflareResponse.json(result);
},
__viteRunnerSend: async (request) => {
const payload = (await request.json()) as HotPayload;
hotListener.dispatch(payload);
hotListener.dispatch(payload, { send: runnerObject.__viteServerSend });
return MiniflareResponse.json(null);
},
},
Expand Down Expand Up @@ -179,15 +174,12 @@ export async function createWorkerdDevEnvironment(

// hmr channel
const hotListener = createHotListenerManager();
const hot: HotChannel = {
const transport: HotChannel = {
listen: () => {},
close: () => {},
on: hotListener.on,
off: hotListener.off,
send(...args: any[]) {
const payload = normalizeServerSendPayload(...args);
runnerObject.__viteServerSend(payload);
},
send: runnerObject.__viteServerSend,
};

// TODO: move initialization code to `init`?
Expand All @@ -199,7 +191,10 @@ export async function createWorkerdDevEnvironment(
}
}

const devEnv = new WorkerdDevEnvironmentImpl(name, config, { hot });
const devEnv = new WorkerdDevEnvironmentImpl(name, config, {
transport,
hot: true,
});

// custom environment api
const api: WorkerdDevApi = {
Expand Down Expand Up @@ -231,7 +226,10 @@ export async function createWorkerdDevEnvironment(

// wrapper to simplify listener management
function createHotListenerManager(): Pick<HotChannel, "on" | "off"> & {
dispatch: (payload: HotPayload) => void;
dispatch: (
payload: HotPayload,
client: { send: (payload: HotPayload) => void },
) => void;
} {
const listerMap: Record<string, Set<Function>> = {};
const getListerMap = (e: string) => (listerMap[e] ??= new Set());
Expand All @@ -243,26 +241,12 @@ function createHotListenerManager(): Pick<HotChannel, "on" | "off"> & {
off(event, listener: any) {
getListerMap(event).delete(listener);
},
dispatch(payload) {
dispatch(payload, client) {
if (payload.type === "custom") {
for (const lister of getListerMap(payload.event)) {
lister(payload.data);
lister(payload.data, client);
}
}
},
};
}

function normalizeServerSendPayload(...args: any[]) {
let payload: HotPayload;
if (typeof args[0] === "string") {
payload = {
type: "custom",
event: args[0],
data: args[1],
};
} else {
payload = args[0];
}
return payload;
}
2 changes: 1 addition & 1 deletion packages/workerd/src/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export type RunnerEnv = {
__viteUnsafeEval: {
eval: (code: string, filename?: string) => any;
};
__viteFetchModule: {
__viteInvoke: {
fetch: (request: Request) => Promise<Response>;
};
__viteRunnerSend: {
Expand Down
31 changes: 14 additions & 17 deletions packages/workerd/src/worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,28 +58,25 @@ export class RunnerObject extends DurableObject implements RunnerRpc {
root: env.__viteRoot,
sourcemapInterceptor: "prepareStackTrace",
transport: {
fetchModule: async (...args) => {
const response = await env.__viteFetchModule.fetch(
requestJson(args),
);
invoke: async (payload) => {
// we still need to implement fetchModule on top of service binding
// since websocket and rpc have tighter payload size limit
// (for example, rpc 1MB is not enough for large pre-bundled deps with source map)
const response = await env.__viteInvoke.fetch(requestJson(payload));
tinyassert(response.ok);
return response.json();
},
},
hmr: {
connection: {
isReady: () => true,
onUpdate: (callback) => {
this.#viteServerSendHandler = callback;
},
send: async (payload) => {
const response = await env.__viteRunnerSend.fetch(
requestJson(payload),
);
tinyassert(response.ok);
},
connect: async (handlers) => {
this.#viteServerSendHandler = handlers.onMessage;
},
send: async (payload) => {
const response = await env.__viteRunnerSend.fetch(
requestJson(payload),
);
tinyassert(response.ok);
},
},
hmr: true,
},
{
runInlinedModule: async (context, transformed) => {
Expand Down

0 comments on commit 633b74d

Please sign in to comment.