one
Version:
One is a new React Framework that makes Vite serve both native and web.
104 lines (103 loc) • 3.64 kB
JavaScript
import { Worker } from "node:worker_threads";
import { cpus } from "node:os";
import { fileURLToPath } from "node:url";
import { dirname, join } from "node:path";
const __filename = fileURLToPath(import.meta.url), __dirname = dirname(__filename);
class BuildWorkerPool {
workers = [];
available = [];
taskQueue = [];
pendingById = /* @__PURE__ */ new Map();
nextId = 0;
readyCount = 0;
initCount = 0;
_ready;
_resolveReady;
_initialized;
_resolveInitialized;
_terminated = !1;
constructor(size = Math.max(1, cpus().length - 1)) {
this._ready = new Promise((resolve) => {
this._resolveReady = resolve;
}), this._initialized = new Promise((resolve) => {
this._resolveInitialized = resolve;
});
const workerPath = join(__dirname, "buildPageWorker.mjs");
for (let i = 0; i < size; i++) {
const worker = new Worker(workerPath);
worker.on("message", (msg) => {
if (msg.type === "ready")
this.readyCount++, this.available.push(worker), this.readyCount === size && this._resolveReady();
else if (msg.type === "init-done")
this.initCount++, this.initCount === size && this._resolveInitialized(), this.dispatch();
else if (msg.type === "done" || msg.type === "error") {
const pending = this.pendingById.get(msg.id);
pending && (this.pendingById.delete(msg.id), msg.type === "done" ? pending.resolve(msg.result) : pending.reject(new Error(msg.error))), this.available.push(worker), this.dispatch();
}
}), worker.on("error", (err) => {
console.error("[BuildWorkerPool] Worker error:", err);
}), this.workers.push(worker);
}
}
get size() {
return this.workers.length;
}
// initialize all workers - they load config themselves from vite.config
async initialize() {
await this._ready;
for (const worker of this.workers)
worker.postMessage({ type: "init", id: this.nextId++ });
await this._initialized;
}
dispatch() {
for (; this.available.length > 0 && this.taskQueue.length > 0; ) {
const worker = this.available.shift(), { msg, pending } = this.taskQueue.shift();
this.pendingById.set(pending.id, pending), worker.postMessage(msg);
}
}
async buildPage(args) {
if (this._terminated)
throw new Error("Worker pool has been terminated");
const serializedRoute = {
type: args.foundRoute.type,
file: args.foundRoute.file,
// only keep serializable layout data
layouts: args.foundRoute.layouts?.map((layout) => ({
contextKey: layout.contextKey,
loaderServerPath: layout.loaderServerPath,
layoutRenderMode: layout.layoutRenderMode
})),
// only keep contextKey from middlewares
middlewares: args.foundRoute.middlewares?.map((mw) => ({
contextKey: mw.contextKey
}))
}, id = this.nextId++, msg = {
type: "build",
id,
args: {
...args,
foundRoute: serializedRoute
}
};
return new Promise((resolve, reject) => {
const pending = { id, resolve, reject };
this.taskQueue.push({ msg, pending }), this.dispatch();
});
}
async terminate() {
this._terminated = !0, await Promise.all(this.workers.map((w) => w.terminate())), this.workers = [], this.available = [];
}
}
let pool = null;
function getWorkerPool(size) {
return pool || (pool = new BuildWorkerPool(size)), pool;
}
async function terminateWorkerPool() {
pool && (await pool.terminate(), pool = null);
}
export {
BuildWorkerPool,
getWorkerPool,
terminateWorkerPool
};
//# sourceMappingURL=workerPool.js.map