From ae9f5c3100badc841da1e3ed6fa9164cca562979 Mon Sep 17 00:00:00 2001 From: Sawyer Hood Date: Sat, 22 Aug 2026 09:37:38 -0700 Subject: [PATCH] Bundle the plugin SDK declaration files in parallel worker threads build-bundled-dts.mjs ran its 13 rollup-plugin-dts bundles one after another, and each builds its own TypeScript program; the six large entries take 2-7s apiece, 27s in total. The entries are independent, so the script now re-runs itself as a worker thread per entry, as many at a time as there are cores. Output is byte-identical; on a 16-core machine the task drops to ~10s. On a 4-CPU runner that is already saturated by turbo it is neutral. Co-Authored-By: Claude --- .../plugin-sdk/scripts/build-bundled-dts.mjs | 78 +++++++++++++++---- 1 file changed, 65 insertions(+), 13 deletions(-) diff --git a/packages/plugin-sdk/scripts/build-bundled-dts.mjs b/packages/plugin-sdk/scripts/build-bundled-dts.mjs index bf78cb3a14..1ac1edf57f 100644 --- a/packages/plugin-sdk/scripts/build-bundled-dts.mjs +++ b/packages/plugin-sdk/scripts/build-bundled-dts.mjs @@ -24,8 +24,15 @@ import { rmSync, writeFileSync, } from "node:fs"; +import { availableParallelism } from "node:os"; import path from "node:path"; import { fileURLToPath } from "node:url"; +import { + Worker, + isMainThread, + parentPort, + workerData, +} from "node:worker_threads"; import { rollup } from "rollup"; import { dts } from "rollup-plugin-dts"; @@ -147,23 +154,68 @@ const HEADER = [ "// and read the real source: https://github.com/get-bb/bb", ].join("\n"); -const generated = {}; -for (const [fileName, entry] of Object.entries(outputs)) { - generated[fileName] = normalizeBundledDts( - `${HEADER}\n\n${await bundle(entry)}`, +function generateBundle(entry) { + return bundle(entry).then((code) => + normalizeBundledDts(`${HEADER}\n\n${code}`), ); } -mkdirSync(outDir, { recursive: true }); +// Each bundle builds its own TypeScript program, which is CPU-bound and +// single-threaded inside rollup-plugin-dts; the six large entries take 2–7s +// apiece. They are independent, so this file re-runs itself as a worker per +// entry, as many at a time as there are cores, and the serial ~27s becomes +// roughly the longest single bundle. +if (!isMainThread) { + parentPort.postMessage(await generateBundle(workerData.entry)); +} else { + await main(); +} + +async function main() { + const generated = {}; + const queue = Object.entries(outputs); + const workers = Math.min(queue.length, availableParallelism()); + await Promise.all( + Array.from({ length: workers }, async () => { + for (let next = queue.shift(); next; next = queue.shift()) { + const [fileName, entry] = next; + generated[fileName] = await generateInWorker(entry); + } + }), + ); + // Keep the declared order so a diff of the outputs stays readable. + writeOutputs( + Object.fromEntries( + Object.keys(outputs).map((fileName) => [fileName, generated[fileName]]), + ), + ); +} -for (const [fileName, content] of Object.entries(generated)) { - const target = path.join(outDir, fileName); - const current = existsSync(target) ? readFileSync(target, "utf8") : null; - if (current === content) { - console.log(`Unchanged ${path.relative(pkgRoot, target)}`); - } else { - writeAtomically(target, content); - console.log(`Wrote ${path.relative(pkgRoot, target)}`); +function generateInWorker(entry) { + return new Promise((resolve, reject) => { + const worker = new Worker(new URL(import.meta.url), { + workerData: { entry }, + }); + worker.once("message", resolve); + worker.once("error", reject); + worker.once("exit", (code) => { + if (code !== 0) reject(new Error(`bundle worker exited with ${code}`)); + }); + }); +} + +function writeOutputs(generated) { + mkdirSync(outDir, { recursive: true }); + + for (const [fileName, content] of Object.entries(generated)) { + const target = path.join(outDir, fileName); + const current = existsSync(target) ? readFileSync(target, "utf8") : null; + if (current === content) { + console.log(`Unchanged ${path.relative(pkgRoot, target)}`); + } else { + writeAtomically(target, content); + console.log(`Wrote ${path.relative(pkgRoot, target)}`); + } } }