From 0f196b97988f0d2916d8da79d90780b799355fc9 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Mon, 14 Oct 2024 16:45:50 +0900 Subject: [PATCH] refactor(child-process): IPC via stdio --- .../src/lib/vite/bridge-client.js | 32 ++++++++++++++++++- .../child-process/src/lib/vite/environment.ts | 14 ++++++-- 2 files changed, 43 insertions(+), 3 deletions(-) diff --git a/examples/child-process/src/lib/vite/bridge-client.js b/examples/child-process/src/lib/vite/bridge-client.js index 85079d1e..732ef6f6 100644 --- a/examples/child-process/src/lib/vite/bridge-client.js +++ b/examples/child-process/src/lib/vite/bridge-client.js @@ -1,6 +1,9 @@ // @ts-check import assert from "node:assert"; +import fs from "node:fs"; +import readline from "node:readline"; +import { Writable } from "node:stream"; import { ESModulesEvaluator, ModuleRunner } from "vite/module-runner"; /** @@ -22,12 +25,39 @@ export function createBridgeClient(options) { return result; } + // TODO: birpc + const childIn = readline.createInterface(process.stdin); + const childOut = new Writable({ + write(chunk, _encoding, callback) { + fs.write(3, chunk, callback); + }, + }); + const runner = new ModuleRunner( { root: options.root, sourcemapInterceptor: "prepareStackTrace", transport: { - fetchModule: (...args) => rpc("fetchModule", ...args), + fetchModule: async (...args) => { + const id = Math.random().toString(36).slice(2); + const promise = new Promise((resolve, _reject) => { + /** + * @param {string} line + */ + function handler(line) { + const event = JSON.parse(line); + if (event.id === id) { + childIn.off("line", handler); + resolve(event.result); + } + } + childIn.on("line", handler); + }); + childOut.write( + JSON.stringify({ type: "fetchModule", id, args }) + "\n", + ); + return promise; + }, }, hmr: false, }, diff --git a/examples/child-process/src/lib/vite/environment.ts b/examples/child-process/src/lib/vite/environment.ts index 2324801f..2f1eb915 100644 --- a/examples/child-process/src/lib/vite/environment.ts +++ b/examples/child-process/src/lib/vite/environment.ts @@ -3,7 +3,7 @@ import childProcess from "node:child_process"; import http from "node:http"; import { join } from "node:path"; import readline from "node:readline"; -import { Readable } from "node:stream"; +import { Readable, Writable } from "node:stream"; import { webToNodeHandler } from "@hiogawa/utils-node"; import { DevEnvironment, type DevEnvironmentOptions } from "vite"; import type { BridgeClientOptions } from "./types"; @@ -100,11 +100,13 @@ export class ChildProcessFetchDevEnvironment extends DevEnvironment { // 4th stdio to ease startup communication // TODO: use 1st stdio to make bidirection? // https://github.com/cloudflare/workers-sdk/blob/e5037b92ac13b1b8a94434e1f9bfa70d4abf791a/packages/miniflare/src/runtime/index.ts#L141 - stdio: ["ignore", "inherit", "inherit", "pipe"], + stdio: ["pipe", "inherit", "inherit", "pipe"], }, ); this.child = child; + assert(child.stdio[0] instanceof Writable); assert(child.stdio[3] instanceof Readable); + const childIn = child.stdio[0]; const childOut = readline.createInterface(child.stdio[3]); await new Promise((resolve, reject) => { const timeout = setTimeout( @@ -127,6 +129,14 @@ export class ChildProcessFetchDevEnvironment extends DevEnvironment { } }); }); + childOut.on("line", async (line) => { + const event = JSON.parse(line); + if (event.type === "fetchModule") { + const { id, args } = event; + const result = await this.fetchModule(...(args as [any])); + childIn.write(JSON.stringify({ id, result }) + "\n"); + } + }); console.log("[environment.init]", { bridgeUrl: this.bridgeUrl, childUrl: this.childUrl,