From f6f5a9307cb29bdb6262ca0a1d94032366cf6fe5 Mon Sep 17 00:00:00 2001 From: Zack Jackson <25274700+ScriptedAlchemy@users.noreply.github.com> Date: Sat, 27 Jun 2026 05:54:53 +0000 Subject: [PATCH 1/2] perf: prewarm route transform workers after compiler creation --- src/index.ts | 6 +++++ src/parallel-route-transforms.ts | 36 +++++++++++++++++++++++++ tests/parallel-route-transforms.test.ts | 26 ++++++++++++++++++ 3 files changed, 68 insertions(+) diff --git a/src/index.ts b/src/index.ts index 8dcbbbb..1e08aab 100644 --- a/src/index.ts +++ b/src/index.ts @@ -480,6 +480,12 @@ export const pluginReactRouter = ( }); }); + if (!isBuild) { + api.onAfterCreateCompiler(() => { + routeTransformExecutor.prewarm(); + }); + } + api.onCloseDevServer(async () => { await closeRouteTopologyWatcher?.(); closeRouteTopologyWatcher = undefined; diff --git a/src/parallel-route-transforms.ts b/src/parallel-route-transforms.ts index 2146223..e671408 100644 --- a/src/parallel-route-transforms.ts +++ b/src/parallel-route-transforms.ts @@ -29,6 +29,7 @@ export type RouteTransformExecutorOptions = RouteTransformTaskOptions & { export type RouteTransformExecutor = { run: (task: RouteTransformTask) => Promise; + prewarm: () => void; close: () => Promise; }; @@ -124,6 +125,7 @@ class ParallelRouteTransformExecutor implements RouteTransformExecutor { #nextSplitRouteAnalysisWorkerIndex = 0; #splitRouteAnalysisWorkers = new Map(); #workers: Array | undefined; + #prewarmScheduled = false; constructor( private readonly workerCount: number, @@ -150,6 +152,38 @@ class ParallelRouteTransformExecutor implements RouteTransformExecutor { } } + prewarm(): void { + if (this.#prewarmScheduled || this.#closed || this.#workersDisabled) { + return; + } + this.#prewarmScheduled = true; + void this.#prewarmWorkers(); + } + + async #prewarmWorkers(): Promise { + try { + for (let index = 0; index < this.workerCount; index += 1) { + if (this.#closed || this.#workersDisabled) { + return; + } + try { + this.#getWorker(index); + } catch (error) { + const startupError = new WorkerStartupError( + error instanceof Error ? error.message : String(error) + ); + if (error instanceof Error) { + startupError.stack = error.stack; + } + this.#disableWorkers(startupError); + return; + } + } + } finally { + this.#prewarmScheduled = false; + } + } + close(): Promise { if (this.#closePromise) { return this.#closePromise; @@ -360,6 +394,7 @@ const createRouteTransformExecutorWithWorkerFactory = ( if (parallelTransforms === false) { return { run: task => executeRouteTransformTask(task, options), + prewarm: () => {}, close: async () => {}, }; } @@ -371,6 +406,7 @@ const createRouteTransformExecutorWithWorkerFactory = ( if (workerCount < 1) { return { run: task => executeRouteTransformTask(task, options), + prewarm: () => {}, close: async () => {}, }; } diff --git a/tests/parallel-route-transforms.test.ts b/tests/parallel-route-transforms.test.ts index 09a6092..e23934b 100644 --- a/tests/parallel-route-transforms.test.ts +++ b/tests/parallel-route-transforms.test.ts @@ -211,6 +211,32 @@ describe('parallel route transforms', () => { expect(workers.map(worker => worker.terminateCalls)).toEqual([1, 1]); }); + it('can prewarm worker slots before the first route transform', async () => { + const workers: FakeRouteTransformWorker[] = []; + const executor = createRouteTransformExecutorForTesting( + { + parallelTransforms: 4, + }, + () => { + const worker = new FakeRouteTransformWorker(); + workers.push(worker); + return worker; + } + ); + + executor.prewarm(); + executor.prewarm(); + expect(workers).toHaveLength(4); + + executor.prewarm(); + expect(workers).toHaveLength(4); + + await executor.close(); + expect(workers.map(worker => worker.terminateCalls)).toEqual([ + 1, 1, 1, 1, + ]); + }); + it('executes route client entry tasks through the shared task executor', async () => { await expect( executeRouteTransformTask({ From 668fbf8f43cdc492bfb7e991a28fd1971e7b224a Mon Sep 17 00:00:00 2001 From: Zack Jackson <25274700+ScriptedAlchemy@users.noreply.github.com> Date: Sun, 28 Jun 2026 19:58:01 +0000 Subject: [PATCH 2/2] chore: simplify route worker prewarm tests --- src/parallel-route-transforms.ts | 15 ++++----------- tests/parallel-route-transforms.test.ts | 25 +++++++++++++++---------- 2 files changed, 19 insertions(+), 21 deletions(-) diff --git a/src/parallel-route-transforms.ts b/src/parallel-route-transforms.ts index e671408..1729149 100644 --- a/src/parallel-route-transforms.ts +++ b/src/parallel-route-transforms.ts @@ -1,9 +1,6 @@ import { Worker } from 'node:worker_threads'; import { setBoundedCacheEntry } from './bounded-cache.js'; -import { - getAvailableCpuCount, - getDefaultConcurrency, -} from './concurrency.js'; +import { getAvailableCpuCount, getDefaultConcurrency } from './concurrency.js'; import { executeRouteTransformTask, type RouteTransformResult, @@ -273,13 +270,9 @@ class ParallelRouteTransformExecutor implements RouteTransformExecutor { return existingWorker; } - try { - const worker = this.#createWorkerState(); - this.#workers[index] = worker; - return worker; - } catch (error) { - throw error; - } + const worker = this.#createWorkerState(); + this.#workers[index] = worker; + return worker; } #runInWorker(task: RouteTransformTask): Promise { diff --git a/tests/parallel-route-transforms.test.ts b/tests/parallel-route-transforms.test.ts index e23934b..c564ea3 100644 --- a/tests/parallel-route-transforms.test.ts +++ b/tests/parallel-route-transforms.test.ts @@ -3,6 +3,7 @@ import { mapWithConcurrency } from '../src/concurrency'; import { getExportNames } from '../src/export-utils'; import { executeRouteTransformTask, + type RouteTransformResult, type RouteModuleTransformTask, } from '../src/route-transform-tasks'; import { @@ -76,6 +77,18 @@ class FakeRouteTransformWorker { } } +const resolveWorkerMessage = ( + worker: FakeRouteTransformWorker, + result: RouteTransformResult, + messageIndex = worker.messages.length - 1 +): void => { + worker.emit('message', { + id: worker.messages[messageIndex]!.id, + ok: true, + result, + } satisfies WorkerResponse); +}; + describe('parallel route transforms', () => { it.each([ [1, 0], @@ -162,11 +175,7 @@ describe('parallel route transforms', () => { const pending = executor.run(createRouteModuleTask()); expect(createdWorkers).toBe(1); - worker.emit('message', { - id: worker.messages[0]!.id, - ok: true, - result: { code: 'created lazily' }, - } satisfies WorkerResponse); + resolveWorkerMessage(worker, { code: 'created lazily' }); await expect(pending).resolves.toEqual({ code: 'created lazily' }); await executor.close(); @@ -195,11 +204,7 @@ describe('parallel route transforms', () => { expect(workers).toHaveLength(2); for (const worker of workers) { - worker.emit('message', { - id: worker.messages[0]!.id, - ok: true, - result: { code: 'done' }, - } satisfies WorkerResponse); + resolveWorkerMessage(worker, { code: 'done' }); } await expect(Promise.all([first, second])).resolves.toEqual([