From 0ca8eb40c1f01fe8d29968ea12f400bb6a9be280 Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Tue, 7 Apr 2026 11:37:54 +0100 Subject: [PATCH] refactor(plugins): stream boundary prep step output --- ...e-extension-package-boundary-artifacts.mjs | 55 +++++++++++++++---- ...tension-package-boundary-artifacts.test.ts | 19 +++++++ 2 files changed, 63 insertions(+), 11 deletions(-) create mode 100644 test/scripts/prepare-extension-package-boundary-artifacts.test.ts diff --git a/scripts/prepare-extension-package-boundary-artifacts.mjs b/scripts/prepare-extension-package-boundary-artifacts.mjs index 87f7968c3c4..0399dc5fbe7 100644 --- a/scripts/prepare-extension-package-boundary-artifacts.mjs +++ b/scripts/prepare-extension-package-boundary-artifacts.mjs @@ -6,6 +6,33 @@ const require = createRequire(import.meta.url); const repoRoot = resolve(import.meta.dirname, ".."); const tscBin = require.resolve("typescript/bin/tsc"); +export function createPrefixedOutputWriter(label, target) { + let buffered = ""; + const prefix = `[${label}] `; + + return { + write(chunk) { + buffered += chunk; + while (true) { + const newlineIndex = buffered.indexOf("\n"); + if (newlineIndex === -1) { + return; + } + const line = buffered.slice(0, newlineIndex + 1); + buffered = buffered.slice(newlineIndex + 1); + target.write(`${prefix}${line}`); + } + }, + flush() { + if (!buffered) { + return; + } + target.write(`${prefix}${buffered}`); + buffered = ""; + }, + }; +} + function runNodeStep(label, args, timeoutMs) { return new Promise((resolvePromise, rejectPromise) => { const child = spawn(process.execPath, args, { @@ -14,27 +41,27 @@ function runNodeStep(label, args, timeoutMs) { stdio: ["ignore", "pipe", "pipe"], }); - let stdout = ""; - let stderr = ""; let settled = false; + const stdoutWriter = createPrefixedOutputWriter(label, process.stdout); + const stderrWriter = createPrefixedOutputWriter(label, process.stderr); const timer = setTimeout(() => { if (settled) { return; } child.kill("SIGTERM"); settled = true; - rejectPromise( - new Error(`${label}\n${stdout}${stderr}\n${label} timed out after ${timeoutMs}ms`.trim()), - ); + stdoutWriter.flush(); + stderrWriter.flush(); + rejectPromise(new Error(`${label} timed out after ${timeoutMs}ms`)); }, timeoutMs); child.stdout.setEncoding("utf8"); child.stderr.setEncoding("utf8"); child.stdout.on("data", (chunk) => { - stdout += chunk; + stdoutWriter.write(chunk); }); child.stderr.on("data", (chunk) => { - stderr += chunk; + stderrWriter.write(chunk); }); child.on("error", (error) => { if (settled) { @@ -42,7 +69,9 @@ function runNodeStep(label, args, timeoutMs) { } clearTimeout(timer); settled = true; - rejectPromise(new Error(`${label}\n${stdout}${stderr}\n${error.message}`.trim())); + stdoutWriter.flush(); + stderrWriter.flush(); + rejectPromise(new Error(`${label} failed to start: ${error.message}`)); }); child.on("close", (code) => { if (settled) { @@ -50,16 +79,18 @@ function runNodeStep(label, args, timeoutMs) { } clearTimeout(timer); settled = true; + stdoutWriter.flush(); + stderrWriter.flush(); if (code === 0) { resolvePromise(); return; } - rejectPromise(new Error(`${label}\n${stdout}${stderr}`.trim())); + rejectPromise(new Error(`${label} failed with exit code ${code ?? 1}`)); }); }); } -async function main() { +export async function main() { try { await Promise.all([ runNodeStep( @@ -84,4 +115,6 @@ async function main() { } } -await main(); +if (import.meta.main) { + await main(); +} diff --git a/test/scripts/prepare-extension-package-boundary-artifacts.test.ts b/test/scripts/prepare-extension-package-boundary-artifacts.test.ts new file mode 100644 index 00000000000..4f4b8fdcbf7 --- /dev/null +++ b/test/scripts/prepare-extension-package-boundary-artifacts.test.ts @@ -0,0 +1,19 @@ +import { describe, expect, it } from "vitest"; +import { createPrefixedOutputWriter } from "../../scripts/prepare-extension-package-boundary-artifacts.mjs"; + +describe("prepare-extension-package-boundary-artifacts", () => { + it("prefixes each completed line and flushes the trailing partial line", () => { + let output = ""; + const writer = createPrefixedOutputWriter("boundary", { + write(chunk: string) { + output += chunk; + }, + }); + + writer.write("first line\nsecond"); + writer.write(" line\nthird"); + writer.flush(); + + expect(output).toBe("[boundary] first line\n[boundary] second line\n[boundary] third"); + }); +});