Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 65 additions & 13 deletions packages/plugin-sdk/scripts/build-bundled-dts.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand Down Expand Up @@ -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)}`);
}
}
}

Expand Down
Loading