From 42d7cee03c969a3056c22c6b874c1910e9bdba1f Mon Sep 17 00:00:00 2001
From: Zack Jackson <25274700+ScriptedAlchemy@users.noreply.github.com>
Date: Fri, 17 Jul 2026 23:39:01 +0000
Subject: [PATCH 01/53] test: protect worker runtime boundary
---
rstest.config.ts | 12 ++++++++++++
tests/parallel-route-transforms.test.ts | 11 +++++++++++
2 files changed, 23 insertions(+)
diff --git a/rstest.config.ts b/rstest.config.ts
index 8e7ba5a1..4f792d48 100644
--- a/rstest.config.ts
+++ b/rstest.config.ts
@@ -1,6 +1,18 @@
import { defineConfig } from '@rstest/core';
export default defineConfig({
+ tools: {
+ rspack: {
+ module: {
+ rules: [
+ {
+ test: /parallel-route-transforms\.test\.ts$/,
+ parser: { url: false },
+ },
+ ],
+ },
+ },
+ },
include: [
'tests/**/*.test.ts',
'tests/react-router-framework/react-router-dev/__tests__/rsc-virtual-route-modules-test.ts',
diff --git a/tests/parallel-route-transforms.test.ts b/tests/parallel-route-transforms.test.ts
index 38abdf55..c447fa6d 100644
--- a/tests/parallel-route-transforms.test.ts
+++ b/tests/parallel-route-transforms.test.ts
@@ -1,3 +1,4 @@
+import { readFile } from 'node:fs/promises';
import { describe, expect, it } from '@rstest/core';
import { getExportNames } from '../src/export-utils';
import {
@@ -131,6 +132,16 @@ const resolveWorkerMessage = (
};
describe('parallel route transforms', () => {
+ it('keeps the worker entrypoint Effect-free', async () => {
+ const source = await readFile(
+ new URL('../src/parallel-route-transform-worker.ts', import.meta.url),
+ 'utf8'
+ );
+
+ expect(source).not.toMatch(/from ['"]effect(?:\/|['"])/);
+ expect(source).not.toContain("import('effect");
+ });
+
it('keeps route chunk tasks limited to chunk extraction', async () => {
const result = await executeRouteTransformTask(createMainRouteChunkTask());
From 617880a620bc7c92ec5ba5b0fbfbd0c369a85ad0 Mon Sep 17 00:00:00 2001
From: Zack Jackson <25274700+ScriptedAlchemy@users.noreply.github.com>
Date: Sat, 18 Jul 2026 00:08:20 +0000
Subject: [PATCH 02/53] chore: update effect
---
package.json | 2 +-
pnpm-lock.yaml | 10 +++++-----
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/package.json b/package.json
index f5987eb9..1a78aa66 100644
--- a/package.json
+++ b/package.json
@@ -138,7 +138,7 @@
"cheerio": "^1.2.0",
"cross-spawn": "^7.0.6",
"dedent": "^1.7.2",
- "effect": "^3.21.4",
+ "effect": "^3.22.0",
"es-module-lexer": "1.7.0",
"express": "^4.22.2",
"fast-glob": "^3.3.3",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 5eddec5f..13f81d21 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -142,8 +142,8 @@ importers:
specifier: ^1.7.2
version: 1.7.2
effect:
- specifier: ^3.21.4
- version: 3.21.4
+ specifier: ^3.22.0
+ version: 3.22.0
es-module-lexer:
specifier: 1.7.0
version: 1.7.0
@@ -7421,8 +7421,8 @@ packages:
ee-first@1.1.1:
resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
- effect@3.21.4:
- resolution: {integrity: sha512-B89v/xSgPbl1J2Ai2u18jxq3odpFauU1rC6/eSs4FeNHi72kwKdJp12VGigvRV2lK+kRnx+OOz41XV8guZd4gQ==}
+ effect@3.22.0:
+ resolution: {integrity: sha512-jhYFe0zTlIRqYFrKTS+6luhmS/Tm0f+JLo0K9KUxvtFab1SUGEszQi2ehOP6QzAZvy831lDmTwwzvVDZSPNz3g==}
electron-to-chromium@1.5.385:
resolution: {integrity: sha512-78sa/M08MNAYHQfjoWMvOlKQqZ0ElhSm/L5HNUc96VZ3b+KvDVnngFm8sYQy0XrhTRgAhggHr5abA7yTvRdo4Q==}
@@ -17103,7 +17103,7 @@ snapshots:
ee-first@1.1.1: {}
- effect@3.21.4:
+ effect@3.22.0:
dependencies:
'@standard-schema/spec': 1.1.0
fast-check: 3.23.2
From 73c07f4de1b1142775fc9e6a6b43becf40fbffdb Mon Sep 17 00:00:00 2001
From: Zack Jackson <25274700+ScriptedAlchemy@users.noreply.github.com>
Date: Sat, 18 Jul 2026 00:19:24 +0000
Subject: [PATCH 03/53] refactor: add managed plugin effect runtime
---
src/effect-runtime.ts | 63 ++++++++++++++++++++++++++++++++++++
tests/effect-runtime.test.ts | 44 +++++++++++++++++++++++++
2 files changed, 107 insertions(+)
diff --git a/src/effect-runtime.ts b/src/effect-runtime.ts
index cf9aca3e..59d2ee3e 100644
--- a/src/effect-runtime.ts
+++ b/src/effect-runtime.ts
@@ -1,9 +1,72 @@
import * as Cause from 'effect/Cause';
+import * as Context from 'effect/Context';
import * as Duration from 'effect/Duration';
import * as Effect from 'effect/Effect';
import * as Exit from 'effect/Exit';
import * as Fiber from 'effect/Fiber';
+import * as Layer from 'effect/Layer';
+import * as ManagedRuntime from 'effect/ManagedRuntime';
import * as Option from 'effect/Option';
+import * as Scope from 'effect/Scope';
+
+type Acquire = (
+ acquire: Effect.Effect,
+ release: (resource: A) => Effect.Effect
+) => Effect.Effect;
+
+export interface PluginScope {
+ readonly acquire: Acquire;
+}
+
+export const PluginScope: Context.Tag =
+ Context.GenericTag('rsbuild-plugin-react-router/PluginScope');
+
+const PluginScopeLive = Layer.scoped(
+ PluginScope,
+ Effect.gen(function* () {
+ const scope = yield* Effect.scope;
+ return {
+ acquire: (acquire, release) =>
+ Effect.acquireRelease(acquire, release).pipe(
+ Effect.provideService(Scope.Scope, scope)
+ ),
+ };
+ })
+);
+
+export type PluginEffectRuntime = Pick<
+ ManagedRuntime.ManagedRuntime,
+ 'runFork' | 'runPromise'
+> & {
+ readonly dispose: () => Promise;
+};
+
+export const createPluginEffectRuntime = (): PluginEffectRuntime => {
+ const runtime = ManagedRuntime.make(PluginScopeLive);
+ const supervisedFibers = new Set>();
+ const runFork: typeof runtime.runFork = (effect, options) => {
+ const fiber = runtime.runFork(effect, options);
+ supervisedFibers.add(fiber);
+ fiber.addObserver(() => {
+ supervisedFibers.delete(fiber);
+ });
+ return fiber;
+ };
+
+ // ManagedRuntime builds lazily; initialize its scoped layer before forks.
+ runtime.runSync(Effect.void);
+
+ let disposePromise: Promise | undefined;
+
+ return {
+ runPromise: runtime.runPromise,
+ runFork,
+ dispose: (): Promise =>
+ (disposePromise ??= runtime
+ .runPromise(Fiber.interruptAll(supervisedFibers))
+ .then(() => runtime.dispose())),
+ };
+};
export const DEV_BACKGROUND_STARTUP_DELAY_MS = 3_000;
diff --git a/tests/effect-runtime.test.ts b/tests/effect-runtime.test.ts
index 785dfa6d..c8d7d66d 100644
--- a/tests/effect-runtime.test.ts
+++ b/tests/effect-runtime.test.ts
@@ -1,12 +1,56 @@
import { describe, expect, it, rstest } from '@rstest/core';
import * as Effect from 'effect/Effect';
import {
+ createPluginEffectRuntime,
createDelayedPluginTask,
+ PluginScope,
runPluginEffect,
tryPluginSync,
} from '../src/effect-runtime';
describe('effect runtime helpers', () => {
+ it('releases dynamically acquired resources when the runtime is disposed', async () => {
+ const events: string[] = [];
+ const runtime = createPluginEffectRuntime();
+
+ await runtime.runPromise(
+ Effect.gen(function* () {
+ const scope = yield* PluginScope;
+ return yield* scope.acquire(
+ Effect.sync(() => {
+ events.push('acquire');
+ return 'resource';
+ }),
+ resource =>
+ Effect.sync(() => {
+ events.push(`release:${resource}`);
+ })
+ );
+ })
+ );
+
+ await runtime.dispose();
+ expect(events).toEqual(['acquire', 'release:resource']);
+ });
+
+ it('interrupts supervised fibers and disposes idempotently', async () => {
+ let finalized = 0;
+ const runtime = createPluginEffectRuntime();
+
+ runtime.runFork(
+ Effect.never.pipe(
+ Effect.ensuring(
+ Effect.sync(() => {
+ finalized += 1;
+ })
+ )
+ )
+ );
+
+ await Promise.all([runtime.dispose(), runtime.dispose()]);
+ expect(finalized).toBe(1);
+ });
+
it('preserves typed errors at promise boundaries', async () => {
const error = new Error('typed failure');
From c8aa34573e220e142de875525368f8c413ca61eb Mon Sep 17 00:00:00 2001
From: Zack Jackson <25274700+ScriptedAlchemy@users.noreply.github.com>
Date: Sat, 18 Jul 2026 00:47:25 +0000
Subject: [PATCH 04/53] refactor: scope plugin runtime and parent workers
---
rstest.config.ts | 2 +-
src/dev-background-resources.ts | 10 +------
src/index.ts | 33 ++++++++++++++++++++---
src/parallel-route-transforms.ts | 36 ++++++++++++++++++++++++-
tests/effect-runtime.test.ts | 16 +++++++++++
tests/index.test.ts | 26 ++++++++++++++++++
tests/parallel-route-transforms.test.ts | 19 +++++++++++++
tests/setup.ts | 31 +++++++++++++++++++--
8 files changed, 157 insertions(+), 16 deletions(-)
diff --git a/rstest.config.ts b/rstest.config.ts
index 4f792d48..f9e96d46 100644
--- a/rstest.config.ts
+++ b/rstest.config.ts
@@ -6,7 +6,7 @@ export default defineConfig({
module: {
rules: [
{
- test: /parallel-route-transforms\.test\.ts$/,
+ test: /(?:effect-runtime|parallel-route-transforms)\.test\.ts$/,
parser: { url: false },
},
],
diff --git a/src/dev-background-resources.ts b/src/dev-background-resources.ts
index 73ba8fca..ce476dda 100644
--- a/src/dev-background-resources.ts
+++ b/src/dev-background-resources.ts
@@ -233,20 +233,12 @@ export const registerReactRouterDevBackgroundResources = ({
);
};
- const closeRouteTransformExecutor = (): Promise =>
- routeTransformExecutor?.close() ?? Promise.resolve();
-
api.onCloseDevServer(() =>
closeAll(
'[rsbuild-plugin-react-router] Failed to close dev server resources.',
- [
- closeRouteTopologyWatcher,
- closeLazyCompilationPrewarm,
- closeRouteTransformExecutor,
- ]
+ [closeRouteTopologyWatcher, closeLazyCompilationPrewarm]
)
);
- api.onCloseBuild(closeRouteTransformExecutor);
return {
setManifest(manifest) {
diff --git a/src/index.ts b/src/index.ts
index b01f6fc4..17b94783 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -59,7 +59,10 @@ import {
isRspackSwcReactRefreshEnabled,
resolveReactRefreshRuntimePath,
} from './dev-hmr.js';
-import { runPluginEffect, tryPluginPromise } from './effect-runtime.js';
+import {
+ createPluginEffectRuntime,
+ tryPluginPromise,
+} from './effect-runtime.js';
import { registerReactRouterTypegen } from './typegen.js';
import {
createConfigImporter,
@@ -78,6 +81,10 @@ import {
setupReactRouterRscPlugin,
} from './rsc-support.js';
import { createReactRouterModePlan } from './mode-plan.js';
+import {
+ acquireRouteTransformExecutor,
+ shouldParallelizeRouteTransforms,
+} from './parallel-route-transforms.js';
import { createQuerylessRouteImportPlugin } from './route-imports.js';
export type { Config as ReactRouterRsbuildConfig } from './react-router-config.js';
@@ -113,6 +120,13 @@ export const pluginReactRouter = (
name: PLUGIN_NAME,
async setup(api) {
+ const effectRuntime = createPluginEffectRuntime();
+ const disposeEffectRuntime = (): Promise => effectRuntime.dispose();
+
+ api.onCloseBuild(disposeEffectRuntime);
+ api.onCloseDevServer(disposeEffectRuntime);
+ api.onExit(disposeEffectRuntime);
+
const defaultOptions = {
customServer: false,
lazyCompilation: true,
@@ -539,6 +553,19 @@ export const pluginReactRouter = (
: undefined,
}));
+ if (modePlan.kind === 'classic') {
+ modePlan.routeTransformExecutor = await effectRuntime.runPromise(
+ acquireRouteTransformExecutor({
+ parallelRouteTransform:
+ pluginOptions.parallelRouteTransform ??
+ shouldParallelizeRouteTransforms(routeCount),
+ routeChunkCache,
+ splitRouteModules: Boolean(splitRouteModules),
+ isBuild,
+ })
+ );
+ }
+
const { manifestChunkNames } = modePlan;
let sendRscDevUpdate: (() => void) | undefined;
@@ -682,7 +709,7 @@ export const pluginReactRouter = (
if (modePlan.kind === 'classic') {
api.onAfterBuild(({ environments }) =>
- runPluginEffect(
+ effectRuntime.runPromise(
tryPluginPromise(() =>
runReactRouterPrerenderBuild({
api,
@@ -713,7 +740,7 @@ export const pluginReactRouter = (
);
} else {
api.onAfterBuild(({ environments }) =>
- runPluginEffect(
+ effectRuntime.runPromise(
tryPluginPromise(() =>
runReactRouterRscPrerenderBuild({
api,
diff --git a/src/parallel-route-transforms.ts b/src/parallel-route-transforms.ts
index 06adae3e..f8795a15 100644
--- a/src/parallel-route-transforms.ts
+++ b/src/parallel-route-transforms.ts
@@ -1,7 +1,12 @@
import { Worker } from 'node:worker_threads';
+import * as Effect from 'effect/Effect';
import { setBoundedCacheEntry } from './bounded-cache.js';
import { getAvailableCpuCount, getDefaultConcurrency } from './concurrency.js';
-import { normalizeEffectError } from './effect-runtime.js';
+import {
+ normalizeEffectError,
+ PluginScope,
+ tryPluginPromise,
+} from './effect-runtime.js';
import {
executeRouteTransformTask,
type RouteTransformResult,
@@ -456,3 +461,32 @@ export const createRouteTransformExecutorForTesting = (
createWorker: RouteTransformWorkerFactory
): RouteTransformExecutor =>
createRouteTransformExecutorWithWorkerFactory(options, createWorker);
+
+const acquireRouteTransformExecutorWithWorkerFactory = Effect.fn(
+ 'RouteTransformExecutor.acquire'
+)(function* (
+ options: RouteTransformExecutorOptions,
+ createWorker: RouteTransformWorkerFactory
+) {
+ const pluginScope = yield* PluginScope;
+ return yield* pluginScope.acquire(
+ Effect.sync(() =>
+ createRouteTransformExecutorWithWorkerFactory(options, createWorker)
+ ),
+ executor =>
+ tryPluginPromise(() => executor.close()).pipe(
+ Effect.catchAll(() => Effect.void)
+ )
+ );
+});
+
+export const acquireRouteTransformExecutor = (
+ options: RouteTransformExecutorOptions = {}
+): Effect.Effect =>
+ acquireRouteTransformExecutorWithWorkerFactory(options, createDefaultWorker);
+
+export const acquireRouteTransformExecutorForTesting = (
+ options: RouteTransformExecutorOptions,
+ createWorker: RouteTransformWorkerFactory
+): Effect.Effect =>
+ acquireRouteTransformExecutorWithWorkerFactory(options, createWorker);
diff --git a/tests/effect-runtime.test.ts b/tests/effect-runtime.test.ts
index c8d7d66d..93f775df 100644
--- a/tests/effect-runtime.test.ts
+++ b/tests/effect-runtime.test.ts
@@ -1,5 +1,6 @@
import { describe, expect, it, rstest } from '@rstest/core';
import * as Effect from 'effect/Effect';
+import { readFile } from 'node:fs/promises';
import {
createPluginEffectRuntime,
createDelayedPluginTask,
@@ -9,6 +10,21 @@ import {
} from '../src/effect-runtime';
describe('effect runtime helpers', () => {
+ it('centralizes raw Effect runners in effect-runtime.ts', async () => {
+ const files = ['index.ts', 'manifest.ts', 'route-watch.ts'];
+ const sources = await Promise.all(
+ files.map(file =>
+ readFile(new URL(`../src/${file}`, import.meta.url), 'utf8')
+ )
+ );
+
+ for (const source of sources) {
+ expect(source).not.toMatch(/Effect\.run(?:Promise|Fork|Sync)/);
+ expect(source).not.toContain('ManagedRuntime.make');
+ }
+ expect(sources[0]).not.toContain('runPluginEffect');
+ });
+
it('releases dynamically acquired resources when the runtime is disposed', async () => {
const events: string[] = [];
const runtime = createPluginEffectRuntime();
diff --git a/tests/index.test.ts b/tests/index.test.ts
index 99525b63..9d264c2c 100644
--- a/tests/index.test.ts
+++ b/tests/index.test.ts
@@ -59,6 +59,32 @@ const captureEnv = (keys: string[]) => {
};
describe('pluginReactRouter', () => {
+ it('shares one Effect runtime and closes it from every Rsbuild shutdown path', async () => {
+ const rsbuild = await createStubRsbuild({
+ rsbuildConfig: {},
+ });
+
+ rsbuild.addPlugins([pluginReactRouter()]);
+ await rsbuild.unwrapConfig();
+
+ expect(rsbuild.shutdownHooks.onCloseBuild).toHaveLength(1);
+ expect(rsbuild.shutdownHooks.onExit).toHaveLength(1);
+ expect(rsbuild.shutdownHooks.onCloseBuild[0]).toBe(
+ rsbuild.shutdownHooks.onCloseDevServer[0]
+ );
+ expect(rsbuild.shutdownHooks.onCloseBuild[0]).toBe(
+ rsbuild.shutdownHooks.onExit[0]
+ );
+
+ await Promise.all(
+ ['onCloseBuild', 'onCloseDevServer', 'onExit'].map(
+ rsbuild.runShutdownHook
+ )
+ );
+
+ expect(rsbuild.logger.error).not.toHaveBeenCalled();
+ });
+
it('should configure basic plugin options', async () => {
const rsbuild = await createStubRsbuild({
rsbuildConfig: {},
diff --git a/tests/parallel-route-transforms.test.ts b/tests/parallel-route-transforms.test.ts
index c447fa6d..7fcc2d0b 100644
--- a/tests/parallel-route-transforms.test.ts
+++ b/tests/parallel-route-transforms.test.ts
@@ -9,10 +9,12 @@ import {
type RouteModuleTransformTask,
} from '../src/route-transform-tasks';
import {
+ acquireRouteTransformExecutorForTesting,
createRouteTransformExecutorForTesting,
createRouteTransformExecutor,
getDefaultWorkerCount,
} from '../src/parallel-route-transforms';
+import { createPluginEffectRuntime } from '../src/effect-runtime';
import type {
WorkerRequest,
WorkerResponse,
@@ -142,6 +144,23 @@ describe('parallel route transforms', () => {
expect(source).not.toContain("import('effect");
});
+ it('terminates parent-owned workers when the plugin runtime is disposed', async () => {
+ const worker = new FakeRouteTransformWorker();
+ const runtime = createPluginEffectRuntime();
+
+ const executor = await runtime.runPromise(
+ acquireRouteTransformExecutorForTesting(
+ { parallelRouteTransform: 1 },
+ () => worker
+ )
+ );
+
+ executor.prewarm();
+ await runtime.dispose();
+
+ expect(worker.terminateCalls).toBe(1);
+ });
+
it('keeps route chunk tasks limited to chunk extraction', async () => {
const result = await executeRouteTransformTask(createMainRouteChunkTask());
diff --git a/tests/setup.ts b/tests/setup.ts
index d34d9b34..89786562 100644
--- a/tests/setup.ts
+++ b/tests/setup.ts
@@ -95,6 +95,12 @@ const deepMerge = (base: any, overrides: any): any => {
return result;
};
+type ShutdownHookName = 'onCloseBuild' | 'onCloseDevServer' | 'onExit';
+type ShutdownCallback = () => unknown;
+type ShutdownHookRegistration =
+ | ShutdownCallback
+ | { handler: ShutdownCallback };
+
// Mock the @scripts/test-helper module
rstest.mock('@scripts/test-helper', () => ({
createStubRsbuild: rstest.fn().mockImplementation(async ({ action = 'dev', rsbuildConfig = {} } = {}) => {
@@ -149,6 +155,20 @@ rstest.mock('@scripts/test-helper', () => ({
const mergeRsbuildConfig = (a: any, b: any) => deepMerge(a, b);
const pending: Promise[] = [];
+ const shutdownHooks: Record = {
+ onCloseBuild: [],
+ onCloseDevServer: [],
+ onExit: [],
+ };
+ const registerShutdownHook = (hookName: ShutdownHookName) => (
+ registration: ShutdownHookRegistration
+ ): void => {
+ const callback =
+ typeof registration === 'function'
+ ? registration
+ : registration.handler;
+ shutdownHooks[hookName].push(callback);
+ };
const stub: any = {
addPlugins: rstest.fn(),
@@ -156,8 +176,9 @@ rstest.mock('@scripts/test-helper', () => ({
processAssets: rstest.fn(),
onBeforeStartDevServer: rstest.fn(),
onAfterStartDevServer: rstest.fn(),
- onCloseDevServer: rstest.fn(),
- onCloseBuild: rstest.fn(),
+ onCloseDevServer: rstest.fn(registerShutdownHook('onCloseDevServer')),
+ onCloseBuild: rstest.fn(registerShutdownHook('onCloseBuild')),
+ onExit: rstest.fn(registerShutdownHook('onExit')),
onBeforeBuild: rstest.fn(),
onAfterBuild: rstest.fn(),
onBeforeDevCompile: rstest.fn(),
@@ -190,6 +211,12 @@ rstest.mock('@scripts/test-helper', () => ({
},
},
},
+ shutdownHooks,
+ runShutdownHook: async (hookName: ShutdownHookName): Promise => {
+ await Promise.all(
+ shutdownHooks[hookName].map(callback => callback())
+ );
+ },
};
stub.modifyRsbuildConfig.mockImplementation((arg: any) => {
From 4f1b8e626f840f65b97c29fa93d97d02bb701183 Mon Sep 17 00:00:00 2001
From: Zack Jackson <25274700+ScriptedAlchemy@users.noreply.github.com>
Date: Sat, 18 Jul 2026 00:56:07 +0000
Subject: [PATCH 05/53] fix: avoid duplicate route transform executor
---
src/index.ts | 38 +++++++++++++++++++-------------------
src/mode-plan.ts | 19 +------------------
tests/index.test.ts | 10 ++++++++++
3 files changed, 30 insertions(+), 37 deletions(-)
diff --git a/src/index.ts b/src/index.ts
index 17b94783..794c8a0a 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -532,10 +532,8 @@ export const pluginReactRouter = (
finalEntryClientPath,
future,
hasServerApp,
- parallelRouteTransform: pluginOptions.parallelRouteTransform,
reactRouterConfig: resolvedConfigWithRoutes,
routeChunkCache,
- routeCount,
serverAppPath,
shouldDependOnWebCompiler,
devHmr:
@@ -553,18 +551,19 @@ export const pluginReactRouter = (
: undefined,
}));
- if (modePlan.kind === 'classic') {
- modePlan.routeTransformExecutor = await effectRuntime.runPromise(
- acquireRouteTransformExecutor({
- parallelRouteTransform:
- pluginOptions.parallelRouteTransform ??
- shouldParallelizeRouteTransforms(routeCount),
- routeChunkCache,
- splitRouteModules: Boolean(splitRouteModules),
- isBuild,
- })
- );
- }
+ const routeTransformExecutor =
+ modePlan.kind === 'classic'
+ ? await effectRuntime.runPromise(
+ acquireRouteTransformExecutor({
+ parallelRouteTransform:
+ pluginOptions.parallelRouteTransform ??
+ shouldParallelizeRouteTransforms(routeCount),
+ routeChunkCache,
+ splitRouteModules: Boolean(splitRouteModules),
+ isBuild,
+ })
+ )
+ : undefined;
const { manifestChunkNames } = modePlan;
@@ -614,10 +613,7 @@ export const pluginReactRouter = (
api,
isBuild,
lazyCompilationPrewarm: pluginOptions.unstableLazyCompilationPrewarm,
- routeTransformExecutor:
- modePlan.kind === 'classic'
- ? modePlan.routeTransformExecutor
- : undefined,
+ routeTransformExecutor,
routeRestartMarkerPath,
watchDirectory,
getRouteTopology: routeTopology.getRouteTopology,
@@ -1009,6 +1005,10 @@ export const pluginReactRouter = (
performanceProfiler,
});
} else {
+ if (!routeTransformExecutor) {
+ throw new Error('Route transform executor was not initialized.');
+ }
+
registerModifyBrowserManifestAssets(
api,
routes,
@@ -1045,7 +1045,7 @@ export const pluginReactRouter = (
getAssetPrefix: () => assetPrefix,
routeChunkOptions: modePlan.routeChunkOptions,
routeModuleAnalysis,
- routeTransformExecutor: modePlan.routeTransformExecutor,
+ routeTransformExecutor,
routeByFilePath,
routeChunkConfig: modePlan.routeChunkConfig,
isBuild,
diff --git a/src/mode-plan.ts b/src/mode-plan.ts
index 68c3e285..5ab3c398 100644
--- a/src/mode-plan.ts
+++ b/src/mode-plan.ts
@@ -13,13 +13,9 @@ import type {
} from './react-router-config.js';
import type { RouteChunkCache, RouteChunkConfig } from './route-chunks.js';
import type { DevHmrPlanOptions } from './dev-hmr.js';
-import type { PluginOptions, Route } from './types.js';
+import type { Route } from './types.js';
import { createReactRouterDevServerSetup } from './dev-server.js';
import { resolveAppPackagePath } from './plugin-utils.js';
-import {
- createRouteTransformExecutor,
- shouldParallelizeRouteTransforms,
-} from './parallel-route-transforms.js';
import { resolvePrerenderPaths } from './prerender.js';
import { createReactRouterNodeEntries } from './server-build-plan.js';
import { getSsrExternals } from './ssr-externals.js';
@@ -56,7 +52,6 @@ type CommonModePlan = {
export type ClassicModePlan = CommonModePlan & {
kind: 'classic';
artifacts: ClassicBuildArtifacts;
- routeTransformExecutor: ReturnType;
routeChunkOptions: {
splitRouteModules: Config['splitRouteModules'];
rootRouteFile: string;
@@ -99,10 +94,8 @@ type CreateClassicModePlanOptions = ModePlanContext & {
finalEntryClientPath: string;
future: Config['future'];
hasServerApp: boolean;
- parallelRouteTransform?: PluginOptions['parallelRouteTransform'];
reactRouterConfig: ResolvedReactRouterConfig;
routeChunkCache: RouteChunkCache;
- routeCount: number;
serverAppPath: string;
shouldDependOnWebCompiler: boolean;
};
@@ -248,12 +241,10 @@ const createClassicModePlan = async ({
hasServerApp,
isBuild,
isSpaMode,
- parallelRouteTransform,
prerenderConfig,
reactRouterConfig,
routeChunkCache,
routeConfig,
- routeCount,
routeDiscovery,
routes,
rootRouteFile,
@@ -267,13 +258,6 @@ const createClassicModePlan = async ({
appDirectory,
rootRouteFile,
};
- const routeTransformExecutor = createRouteTransformExecutor({
- parallelRouteTransform:
- parallelRouteTransform ?? shouldParallelizeRouteTransforms(routeCount),
- routeChunkCache,
- splitRouteModules: Boolean(splitRouteModules),
- isBuild,
- });
const routeChunkOptions = {
splitRouteModules,
rootRouteFile,
@@ -303,7 +287,6 @@ const createClassicModePlan = async ({
kind: 'classic',
artifacts,
routeChunkConfig,
- routeTransformExecutor,
routeChunkOptions,
manifestChunkNames,
webEntries: {
diff --git a/tests/index.test.ts b/tests/index.test.ts
index 9d264c2c..a839ee70 100644
--- a/tests/index.test.ts
+++ b/tests/index.test.ts
@@ -2,6 +2,7 @@ import { createStubRsbuild } from '@scripts/test-helper';
import { describe, expect, it, rstest } from '@rstest/core';
import { execFileSync } from 'node:child_process';
import * as fs from 'node:fs';
+import { readFile } from 'node:fs/promises';
import { createRequire } from 'node:module';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
@@ -59,6 +60,15 @@ const captureEnv = (keys: string[]) => {
};
describe('pluginReactRouter', () => {
+ it('acquires the classic route transform executor only from PluginScope', async () => {
+ const source = await readFile(
+ join(process.cwd(), 'src/mode-plan.ts'),
+ 'utf8'
+ );
+
+ expect(source).not.toContain('createRouteTransformExecutor');
+ });
+
it('shares one Effect runtime and closes it from every Rsbuild shutdown path', async () => {
const rsbuild = await createStubRsbuild({
rsbuildConfig: {},
From eca5afa70c0d7dde5646b8e96b5af8986b4ed2c8 Mon Sep 17 00:00:00 2001
From: Zack Jackson <25274700+ScriptedAlchemy@users.noreply.github.com>
Date: Sat, 18 Jul 2026 01:44:24 +0000
Subject: [PATCH 06/53] refactor: scope dev background resources
---
src/dev-background-resources.ts | 119 ++++++++-----------------
src/effect-runtime.ts | 96 ++++++++++----------
src/index.ts | 29 +++---
src/lazy-compilation-prewarm.ts | 27 +++++-
src/route-watch.ts | 78 ++++++++--------
src/typegen.ts | 71 ++++++++++-----
tests/effect-runtime.test.ts | 20 ++++-
tests/lazy-compilation-prewarm.test.ts | 52 +++++++++--
tests/route-watch.test.ts | 52 ++++++++++-
tests/typegen.test.ts | 72 ++++++++++++---
10 files changed, 377 insertions(+), 239 deletions(-)
diff --git a/src/dev-background-resources.ts b/src/dev-background-resources.ts
index ce476dda..bcae0d95 100644
--- a/src/dev-background-resources.ts
+++ b/src/dev-background-resources.ts
@@ -7,11 +7,10 @@ import {
createDelayedPluginTask,
DEV_BACKGROUND_STARTUP_DELAY_MS,
normalizeEffectError,
- runPluginEffect,
- tryPluginPromise,
+ type PluginEffectRuntime,
} from './effect-runtime.js';
import {
- createLazyCompilationPrewarmController,
+ acquireLazyCompilationPrewarm,
normalizeLazyCompilationPrewarmOptions,
} from './lazy-compilation-prewarm.js';
import {
@@ -20,8 +19,8 @@ import {
} from './manifest.js';
import type { RouteTransformExecutor } from './parallel-route-transforms.js';
import {
+ acquireRouteTopologyWatcher,
createRouteManifestSnapshot,
- createRouteTopologyWatcher,
ensureDevRestartMarker,
type WatchFileConfig,
} from './route-watch.js';
@@ -29,6 +28,7 @@ import type { PluginOptions } from './types.js';
type RegisterReactRouterDevBackgroundResourcesOptions = {
api: RsbuildPluginAPI;
+ runtime: PluginEffectRuntime;
isBuild: boolean;
lazyCompilationPrewarm: PluginOptions['unstableLazyCompilationPrewarm'];
routeTransformExecutor?: RouteTransformExecutor;
@@ -112,27 +112,9 @@ export const createReactRouterRouteWatchFiles = ({
return watchFiles;
};
-const closeAll = async (
- message: string,
- closers: Array<() => Promise>
-): Promise => {
- const results = await Promise.allSettled(closers.map(closer => closer()));
- const errors = results
- .filter(
- (result): result is PromiseRejectedResult => result.status === 'rejected'
- )
- .map(result => normalizeEffectError(result.reason));
-
- if (errors.length === 1) {
- throw errors[0];
- }
- if (errors.length > 1) {
- throw new AggregateError(errors, message);
- }
-};
-
-export const registerReactRouterDevBackgroundResources = ({
+export const registerReactRouterDevBackgroundResources = async ({
api,
+ runtime,
isBuild,
lazyCompilationPrewarm,
routeTransformExecutor,
@@ -141,9 +123,8 @@ export const registerReactRouterDevBackgroundResources = ({
getRouteTopology,
initialRouteTopology,
onRouteTopologyChange,
-}: RegisterReactRouterDevBackgroundResourcesOptions): ReactRouterDevBackgroundResources => {
- let closeActiveRouteTopologyWatcher: (() => Promise) | undefined;
- let routeTopologyWatcherClosed = false;
+}: RegisterReactRouterDevBackgroundResourcesOptions): Promise => {
+ let routeTopologyWatcherStarted = false;
const reportRouteTopologyWatcherError = (error: unknown): void => {
api.logger.warn(
@@ -152,54 +133,44 @@ export const registerReactRouterDevBackgroundResources = ({
};
const routeTopologyWatcherTask = createDelayedPluginTask({
+ runtime,
delayMs: DEV_BACKGROUND_STARTUP_DELAY_MS,
run: () =>
- Effect.gen(function* () {
- yield* tryPluginPromise(() =>
- ensureDevRestartMarker(routeRestartMarkerPath)
- );
- const closeWatcher = yield* tryPluginPromise(() =>
- createRouteTopologyWatcher({
- watchDirectory,
- getRouteTopology,
- initialRouteTopology,
- restartMarkerPath: routeRestartMarkerPath,
- onRouteTopologyChange,
- onError: reportRouteTopologyWatcherError,
- })
- );
- if (routeTopologyWatcherClosed) {
- yield* tryPluginPromise(() => closeWatcher());
- return;
- }
- closeActiveRouteTopologyWatcher = closeWatcher;
- }),
+ acquireRouteTopologyWatcher({
+ runtime,
+ watchDirectory,
+ getRouteTopology,
+ initialRouteTopology,
+ restartMarkerPath: routeRestartMarkerPath,
+ onRouteTopologyChange,
+ onError: reportRouteTopologyWatcherError,
+ }).pipe(
+ Effect.tap(() =>
+ Effect.sync(() => (routeTopologyWatcherStarted = true))
+ ),
+ Effect.asVoid
+ ),
onError: reportRouteTopologyWatcherError,
});
- const scheduleRouteTopologyWatcher = (): void => {
- if (routeTopologyWatcherClosed || closeActiveRouteTopologyWatcher) {
- return;
- }
- routeTopologyWatcherTask.schedule();
- };
-
const lazyCompilationPrewarmConfig = normalizeLazyCompilationPrewarmOptions(
lazyCompilationPrewarm
);
const lazyCompilationPrewarmController = lazyCompilationPrewarmConfig
- ? createLazyCompilationPrewarmController({
- config: lazyCompilationPrewarmConfig,
- onError: error =>
- api.logger.warn(
- `[${PLUGIN_NAME}] Lazy compilation prewarm skipped: ${error.message}`
- ),
- })
+ ? await runtime.runPromise(
+ acquireLazyCompilationPrewarm({
+ runtime,
+ config: lazyCompilationPrewarmConfig,
+ onError: error =>
+ api.logger.warn(
+ `[${PLUGIN_NAME}] Lazy compilation prewarm skipped: ${error.message}`
+ ),
+ })
+ )
: null;
if (!isBuild) {
api.onBeforeStartDevServer(async () => {
- routeTopologyWatcherClosed = false;
await ensureDevRestartMarker(routeRestartMarkerPath);
});
@@ -211,7 +182,9 @@ export const registerReactRouterDevBackgroundResources = ({
});
api.onAfterDevCompile(() => {
- scheduleRouteTopologyWatcher();
+ if (!routeTopologyWatcherStarted) {
+ routeTopologyWatcherTask.schedule();
+ }
lazyCompilationPrewarmController?.schedule();
});
@@ -220,26 +193,6 @@ export const registerReactRouterDevBackgroundResources = ({
routeTransformExecutor?.prewarm();
}
- const closeRouteTopologyWatcher = async (): Promise => {
- routeTopologyWatcherClosed = true;
- await runPluginEffect(routeTopologyWatcherTask.cancelEffect());
- await closeActiveRouteTopologyWatcher?.();
- closeActiveRouteTopologyWatcher = undefined;
- };
-
- const closeLazyCompilationPrewarm = async (): Promise => {
- await runPluginEffect(
- lazyCompilationPrewarmController?.cancelEffect() ?? Effect.void
- );
- };
-
- api.onCloseDevServer(() =>
- closeAll(
- '[rsbuild-plugin-react-router] Failed to close dev server resources.',
- [closeRouteTopologyWatcher, closeLazyCompilationPrewarm]
- )
- );
-
return {
setManifest(manifest) {
lazyCompilationPrewarmController?.setManifest(manifest);
diff --git a/src/effect-runtime.ts b/src/effect-runtime.ts
index 59d2ee3e..f7c1b8d2 100644
--- a/src/effect-runtime.ts
+++ b/src/effect-runtime.ts
@@ -106,48 +106,53 @@ export const tryPluginPromise = (
catch: normalizeEffectError,
});
-type DelayedPluginTask = {
- schedule(): void;
- reschedule(): void;
- cancelEffect(): Effect.Effect;
- cancel(): Promise;
-};
-
export const createDelayedPluginTask = ({
+ runtime,
delayMs,
run,
onError,
}: {
+ runtime: PluginEffectRuntime;
delayMs: number;
- run: () => Effect.Effect;
+ run: () => Effect.Effect;
onError: (error: Error) => void;
-}): DelayedPluginTask => {
- let activeFiber: ReturnType | undefined;
- let version = 0;
-
- const cancelActiveEffect = (): Effect.Effect =>
- Effect.sync(() => {
- const fiber = activeFiber;
- activeFiber = undefined;
- return fiber;
- }).pipe(
- Effect.flatMap(fiber =>
- fiber ? Fiber.interrupt(fiber).pipe(Effect.asVoid) : Effect.void
- )
- );
+}) => {
+ let fiber: ReturnType | null | undefined;
+
+ const cancelEffect = (): Effect.Effect =>
+ Effect.suspend(() => {
+ const activeFiber = fiber;
+ fiber = null;
+ return activeFiber
+ ? Fiber.interrupt(activeFiber).pipe(Effect.asVoid)
+ : Effect.void;
+ });
- const cancelEffect = (): Effect.Effect =>
- Effect.sync(() => {
- version += 1;
- }).pipe(Effect.zipRight(cancelActiveEffect()));
+ const cancel = (): Promise => {
+ const activeFiber = fiber;
+ return activeFiber
+ ? runtime.runPromise(
+ Fiber.interrupt(activeFiber).pipe(
+ Effect.ensuring(
+ Effect.sync(() => {
+ if (fiber === activeFiber) {
+ fiber = undefined;
+ }
+ })
+ ),
+ Effect.asVoid
+ )
+ )
+ : Promise.resolve();
+ };
- const start = (taskVersion: number): void => {
- if (activeFiber || version !== taskVersion) {
+ const start = (): void => {
+ if (fiber !== undefined) {
return;
}
- let fiber: ReturnType;
- fiber = Effect.runFork(
+ let activeFiber: ReturnType;
+ activeFiber = runtime.runFork(
Effect.sleep(Duration.millis(delayMs)).pipe(
Effect.zipRight(Effect.suspend(run)),
Effect.catchAll(error =>
@@ -157,37 +162,28 @@ export const createDelayedPluginTask = ({
),
Effect.ensuring(
Effect.sync(() => {
- if (activeFiber === fiber) {
- activeFiber = undefined;
+ if (fiber === activeFiber) {
+ fiber = undefined;
}
})
)
)
);
- activeFiber = fiber;
+ fiber = activeFiber;
};
return {
- schedule(): void {
- if (activeFiber) {
- return;
- }
- version += 1;
- start(version);
- },
+ schedule: (): void => start(),
reschedule(): void {
- version += 1;
- const taskVersion = version;
- void runPluginEffect(cancelActiveEffect())
- .then(() => start(taskVersion))
- .catch(onError);
+ if (fiber) {
+ void cancel().then(start).catch(onError);
+ } else {
+ start();
+ }
},
- cancelEffect,
-
- async cancel(): Promise {
- await runPluginEffect(cancelEffect());
- },
+ cancelEffect: (): Effect.Effect => cancelEffect(),
+ cancel: (): Promise => cancel(),
};
};
diff --git a/src/index.ts b/src/index.ts
index 794c8a0a..ddb19a59 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -232,7 +232,10 @@ export const pluginReactRouter = (
buildEnd,
} = resolvedConfig;
- registerReactRouterTypegen(api, { appDirectory });
+ await registerReactRouterTypegen(api, {
+ runtime: effectRuntime,
+ appDirectory,
+ });
const hasExplicitServerOutput = Object.prototype.hasOwnProperty.call(
options,
@@ -609,17 +612,19 @@ export const pluginReactRouter = (
});
}
- const devBackgroundResources = registerReactRouterDevBackgroundResources({
- api,
- isBuild,
- lazyCompilationPrewarm: pluginOptions.unstableLazyCompilationPrewarm,
- routeTransformExecutor,
- routeRestartMarkerPath,
- watchDirectory,
- getRouteTopology: routeTopology.getRouteTopology,
- initialRouteTopology: routeTopology.initialRouteTopology,
- onRouteTopologyChange: pluginOptions.onRouteTopologyChange,
- });
+ const devBackgroundResources =
+ await registerReactRouterDevBackgroundResources({
+ api,
+ runtime: effectRuntime,
+ isBuild,
+ lazyCompilationPrewarm: pluginOptions.unstableLazyCompilationPrewarm,
+ routeTransformExecutor,
+ routeRestartMarkerPath,
+ watchDirectory,
+ getRouteTopology: routeTopology.getRouteTopology,
+ initialRouteTopology: routeTopology.initialRouteTopology,
+ onRouteTopologyChange: pluginOptions.onRouteTopologyChange,
+ });
const stageLatestManifests = (
manifest: ReactRouterManifest,
diff --git a/src/lazy-compilation-prewarm.ts b/src/lazy-compilation-prewarm.ts
index 78ccbe03..03894a9f 100644
--- a/src/lazy-compilation-prewarm.ts
+++ b/src/lazy-compilation-prewarm.ts
@@ -1,7 +1,12 @@
import * as Effect from 'effect/Effect';
import type { ReactRouterManifestForDev } from './manifest.js';
import type { RouteManifestItem } from './types.js';
-import { createDelayedPluginTask, tryPluginPromise } from './effect-runtime.js';
+import {
+ createDelayedPluginTask,
+ type PluginEffectRuntime,
+ PluginScope,
+ tryPluginPromise,
+} from './effect-runtime.js';
const DEFAULT_LAZY_COMPILATION_TRIGGER_PREFIX = '/_rspack/lazy/trigger';
const DEFAULT_PREWARM_DELAY_MS = 0;
@@ -9,17 +14,17 @@ const DEFAULT_ROUTE_PREWARM_LIMIT = 8;
const PREWARM_FETCH_CONCURRENCY = 8;
const PREWARM_TRIGGER_CANDIDATES = 4;
-type LazyCompilationPrewarmConfig = {
+export type LazyCompilationPrewarmConfig = {
entry: boolean;
routeLimit: number;
delayMs: number;
};
-type LazyCompilationPrewarmController = {
+export type LazyCompilationPrewarmController = {
setServerOrigin(origin: string): void;
setManifest(manifest: ReactRouterManifestForDev | null): void;
schedule(): void;
- cancelEffect(): Effect.Effect;
+ cancelEffect(): Effect.Effect;
};
export type RspackLazyCompilationTriggerClient = {
@@ -221,9 +226,11 @@ const prewarmLazyCompilation = ({
});
export const createLazyCompilationPrewarmController = ({
+ runtime,
config,
onError,
}: {
+ runtime: PluginEffectRuntime;
config: LazyCompilationPrewarmConfig;
onError: (error: Error) => void;
}): LazyCompilationPrewarmController => {
@@ -231,6 +238,7 @@ export const createLazyCompilationPrewarmController = ({
let manifest: ReactRouterManifestForDev | null = null;
let lastPrewarmAssetsKey: string | undefined;
const task = createDelayedPluginTask({
+ runtime,
delayMs: config.delayMs,
run: () =>
Effect.gen(function* () {
@@ -277,3 +285,14 @@ export const createLazyCompilationPrewarmController = ({
},
};
};
+
+export const acquireLazyCompilationPrewarm = (
+ options: Parameters[0]
+): Effect.Effect =>
+ Effect.gen(function* () {
+ const pluginScope = yield* PluginScope;
+ return yield* pluginScope.acquire(
+ Effect.sync(() => createLazyCompilationPrewarmController(options)),
+ controller => controller.cancelEffect()
+ );
+ });
diff --git a/src/route-watch.ts b/src/route-watch.ts
index 9f6b22b9..7c74990c 100644
--- a/src/route-watch.ts
+++ b/src/route-watch.ts
@@ -6,7 +6,8 @@ import { dirname, resolve } from 'pathe';
import { getCappedPluginConcurrency } from './concurrency.js';
import {
createDelayedPluginTask,
- runPluginEffect,
+ type PluginEffectRuntime,
+ PluginScope,
tryPluginPromise,
} from './effect-runtime.js';
import type { Route } from './types.js';
@@ -47,6 +48,17 @@ type WatchDirectoryEntry = (
onError: (error: unknown) => void
) => DirectoryWatcher;
+export type CreateRouteTopologyWatcherOptions = {
+ runtime: PluginEffectRuntime;
+ watchDirectory: string;
+ getRouteTopology: () => Promise>;
+ initialRouteTopology?: Set;
+ restartMarkerPath: string;
+ onError: (error: unknown) => void;
+ onRouteTopologyChange?: () => void | Promise;
+ watchDirectoryEntry?: WatchDirectoryEntry;
+};
+
const defaultWatchDirectoryEntry: WatchDirectoryEntry = (
directory,
onChange,
@@ -120,10 +132,6 @@ const areSetsEqual = (left: Set, right: Set): boolean => {
return true;
};
-const readRouteDirectories = (watchDirectory: string): Promise> => {
- return runPluginEffect(readRouteDirectoriesEffect(watchDirectory));
-};
-
const readRouteDirectoriesEffect = (
watchDirectory: string
): Effect.Effect, Error, never> => {
@@ -149,6 +157,7 @@ const readRouteDirectoriesEffect = (
};
export const createRouteTopologyWatcher = async ({
+ runtime,
watchDirectory,
getRouteTopology,
initialRouteTopology,
@@ -156,16 +165,10 @@ export const createRouteTopologyWatcher = async ({
onError,
onRouteTopologyChange,
watchDirectoryEntry: watchDirectoryOverride = defaultWatchDirectoryEntry,
-}: {
- watchDirectory: string;
- getRouteTopology: () => Promise>;
- initialRouteTopology?: Set;
- restartMarkerPath: string;
- onError: (error: unknown) => void;
- onRouteTopologyChange?: () => void | Promise;
- watchDirectoryEntry?: WatchDirectoryEntry;
-}): Promise<() => Promise> => {
- const discoveredDirectories = await readRouteDirectories(watchDirectory);
+}: CreateRouteTopologyWatcherOptions): Promise<() => Promise> => {
+ const discoveredDirectories = await runtime.runPromise(
+ readRouteDirectoriesEffect(watchDirectory)
+ );
let discoveredState: RouteDirectoryState;
try {
discoveredState = {
@@ -187,7 +190,6 @@ export const createRouteTopologyWatcher = async ({
routeTopology: initialRouteTopology ?? discoveredState.routeTopology,
};
let closed = false;
- let rescanQueue = Promise.resolve();
const directoryWatchers = new Map();
const touchRestartMarkerEffect = (): Effect.Effect =>
@@ -306,52 +308,46 @@ export const createRouteTopologyWatcher = async ({
);
};
- const rescan = (): Promise => {
- rescanQueue = rescanQueue.then(
- () => runPluginEffect(runRescanEffect()),
- () => runPluginEffect(runRescanEffect())
- );
- return rescanQueue;
- };
-
const rescanTask = createDelayedPluginTask({
+ runtime,
delayMs: ROUTE_TOPOLOGY_RESCAN_DEBOUNCE_MS,
run: () =>
Effect.suspend(() =>
- closed ? Effect.void : tryPluginPromise(rescan).pipe(Effect.asVoid)
+ closed ? Effect.void : Effect.uninterruptible(runRescanEffect())
),
onError,
});
- const cancelScheduledRescan = (): Promise =>
- runPluginEffect(rescanTask.cancelEffect());
-
- const applyNextState = async (nextState: RouteDirectoryState) => {
- await runPluginEffect(applyNextStateEffect(nextState));
- };
-
try {
- await applyNextState(discoveredState);
+ await runtime.runPromise(applyNextStateEffect(discoveredState));
} catch (error) {
onError(error);
}
return async () => {
if (closed) {
- await cancelScheduledRescan();
- await rescanQueue;
+ await rescanTask.cancel();
return;
}
closed = true;
- await cancelScheduledRescan();
- for (const watcher of directoryWatchers.values()) {
- watcher.close();
- }
- directoryWatchers.clear();
- await rescanQueue;
+ await rescanTask.cancel();
for (const watcher of directoryWatchers.values()) {
watcher.close();
}
directoryWatchers.clear();
};
};
+
+export const acquireRouteTopologyWatcher = (
+ options: CreateRouteTopologyWatcherOptions
+): Effect.Effect<() => Promise, Error, PluginScope> =>
+ Effect.gen(function* () {
+ const pluginScope = yield* PluginScope;
+ return yield* pluginScope.acquire(
+ tryPluginPromise(() => createRouteTopologyWatcher(options)),
+ close =>
+ tryPluginPromise(close).pipe(
+ Effect.catchAll(error => Effect.sync(() => options.onError(error)))
+ )
+ );
+ });
diff --git a/src/typegen.ts b/src/typegen.ts
index c7b48912..f9019667 100644
--- a/src/typegen.ts
+++ b/src/typegen.ts
@@ -3,7 +3,12 @@ import { dirname, resolve } from 'node:path';
import type { RsbuildPluginAPI } from '@rsbuild/core';
import type { ResultPromise } from 'execa';
import * as Effect from 'effect/Effect';
-import { createDelayedPluginTask, tryPluginPromise } from './effect-runtime.js';
+import {
+ createDelayedPluginTask,
+ type PluginEffectRuntime,
+ PluginScope,
+ tryPluginPromise,
+} from './effect-runtime.js';
import { resolvePackageJson } from './ssr-externals.js';
// Quiet period with no dev compiles before the typegen watch starts. Long
@@ -69,6 +74,7 @@ export const createReactRouterTypegenRunner = (
): ReactRouterTypegenRunner => {
let typegenProcess: ResultPromise | undefined;
let typegenCommand: TypegenCommand | undefined;
+ let watchGeneration = 0;
const getTypegenCommand = (): TypegenCommand => {
typegenCommand ??= (appDirectory
@@ -96,7 +102,11 @@ export const createReactRouterTypegenRunner = (
return;
}
+ const generation = watchGeneration;
const execa = await loadExeca();
+ if (typegenProcess || generation !== watchGeneration) {
+ return;
+ }
const { command, args } = getTypegenCommand();
const process = execa(command, [...args, 'typegen', '--watch'], {
stdio: 'inherit',
@@ -108,6 +118,7 @@ export const createReactRouterTypegenRunner = (
},
async closeWatch(): Promise {
+ watchGeneration += 1;
const process = typegenProcess;
typegenProcess = undefined;
if (!process) {
@@ -128,36 +139,55 @@ export const createReactRouterTypegenRunner = (
};
};
-export const registerReactRouterTypegen = (
+export const registerReactRouterTypegen = async (
api: RsbuildPluginAPI,
{
+ runtime,
runner,
devWatchDelayMs = TYPEGEN_IDLE_DELAY_MS,
appDirectory,
}: {
+ runtime: PluginEffectRuntime;
runner?: ReactRouterTypegenRunner;
devWatchDelayMs?: number;
appDirectory?: string;
- } = {}
-): void => {
+ }
+): Promise => {
const resolvedRunner =
runner ?? createReactRouterTypegenRunner(loadDefaultExeca, appDirectory);
- let devWatchStarted = false;
- const devWatchTask = createDelayedPluginTask({
- delayMs: devWatchDelayMs,
- run: () =>
- tryPluginPromise(() => {
- devWatchStarted = true;
- return resolvedRunner.startWatch();
- }).pipe(Effect.asVoid),
- onError(error) {
- api.logger.warn(
- `[react-router] Failed to start React Router typegen watch: ${error}`
- );
- },
- });
if (api.context.action !== 'build') {
+ let devWatchStarted = false;
+ const devWatchTask = createDelayedPluginTask({
+ runtime,
+ delayMs: devWatchDelayMs,
+ run: () =>
+ tryPluginPromise(() => {
+ devWatchStarted = true;
+ return resolvedRunner.startWatch();
+ }).pipe(Effect.asVoid),
+ onError(error) {
+ api.logger.warn(
+ `[react-router] Failed to start React Router typegen watch: ${error}`
+ );
+ },
+ });
+ await runtime.runPromise(
+ Effect.gen(function* () {
+ const pluginScope = yield* PluginScope;
+ yield* pluginScope.acquire(Effect.void, () =>
+ devWatchTask
+ .cancelEffect()
+ .pipe(
+ Effect.zipRight(
+ Effect.orDie(
+ tryPluginPromise(() => resolvedRunner.closeWatch())
+ )
+ )
+ )
+ );
+ })
+ );
// Reschedule on every compile so the typegen watch only starts after a
// quiet period with no compiles. Starting it during the initial compile
// burst competes with HMR rebuilds for CPU on small machines.
@@ -169,10 +199,5 @@ export const registerReactRouterTypegen = (
});
}
- api.onCloseDevServer(async () => {
- await devWatchTask.cancel();
- await resolvedRunner.closeWatch();
- });
-
api.onBeforeBuild(() => resolvedRunner.runBuild());
};
diff --git a/tests/effect-runtime.test.ts b/tests/effect-runtime.test.ts
index 93f775df..aed1a61b 100644
--- a/tests/effect-runtime.test.ts
+++ b/tests/effect-runtime.test.ts
@@ -87,7 +87,9 @@ describe('effect runtime helpers', () => {
it('runs delayed plugin tasks after their delay', async () => {
const run = rstest.fn();
+ const runtime = createPluginEffectRuntime();
const task = createDelayedPluginTask({
+ runtime,
delayMs: 10,
run: () => Effect.sync(run),
onError: error => {
@@ -98,11 +100,14 @@ describe('effect runtime helpers', () => {
task.schedule();
expect(run).not.toHaveBeenCalled();
await expect.poll(() => run.mock.calls.length, { timeout: 1000 }).toBe(1);
+ await runtime.dispose();
});
it('reschedules delayed plugin tasks by replacing the pending run', async () => {
const run = rstest.fn();
+ const runtime = createPluginEffectRuntime();
const task = createDelayedPluginTask({
+ runtime,
delayMs: 10,
run: () => Effect.sync(run),
onError: error => {
@@ -115,11 +120,14 @@ describe('effect runtime helpers', () => {
task.reschedule();
await expect.poll(() => run.mock.calls.length, { timeout: 1000 }).toBe(1);
+ await runtime.dispose();
});
it('cancels delayed plugin tasks before they start', async () => {
const run = rstest.fn();
+ const runtime = createPluginEffectRuntime();
const task = createDelayedPluginTask({
+ runtime,
delayMs: 1000,
run: () => Effect.sync(run),
onError: error => {
@@ -132,12 +140,15 @@ describe('effect runtime helpers', () => {
await new Promise(resolve => setTimeout(resolve, 20));
expect(run).not.toHaveBeenCalled();
+ await runtime.dispose();
});
- it('supports Effect-based cancellation for delayed plugin tasks', async () => {
+ it('cancels delayed plugin tasks when the plugin runtime is disposed', async () => {
const run = rstest.fn();
+ const runtime = createPluginEffectRuntime();
const task = createDelayedPluginTask({
- delayMs: 1000,
+ runtime,
+ delayMs: 25,
run: () => Effect.sync(run),
onError: error => {
throw error;
@@ -145,9 +156,10 @@ describe('effect runtime helpers', () => {
});
task.schedule();
- await runPluginEffect(task.cancelEffect());
- await new Promise(resolve => setTimeout(resolve, 20));
+ await runtime.dispose();
+ await new Promise(resolve => setTimeout(resolve, 50));
expect(run).not.toHaveBeenCalled();
});
+
});
diff --git a/tests/lazy-compilation-prewarm.test.ts b/tests/lazy-compilation-prewarm.test.ts
index a187fb91..c777dc91 100644
--- a/tests/lazy-compilation-prewarm.test.ts
+++ b/tests/lazy-compilation-prewarm.test.ts
@@ -1,8 +1,8 @@
import { describe, expect, it, rstest } from '@rstest/core';
-import { runPluginEffect } from '../src/effect-runtime';
+import { createPluginEffectRuntime } from '../src/effect-runtime';
import {
+ acquireLazyCompilationPrewarm,
collectLazyCompilationPrewarmAssets,
- createLazyCompilationPrewarmController,
createRspackLazyCompilationTriggerClient,
normalizeLazyCompilationPrewarmOptions,
} from '../src/lazy-compilation-prewarm';
@@ -128,12 +128,16 @@ describe('lazy compilation prewarm helpers', () => {
}
) as unknown as typeof fetch;
- const controller = createLazyCompilationPrewarmController({
- config,
- onError: error => {
- throw error;
- },
- });
+ const runtime = createPluginEffectRuntime();
+ const controller = await runtime.runPromise(
+ acquireLazyCompilationPrewarm({
+ runtime,
+ config,
+ onError: error => {
+ throw error;
+ },
+ })
+ );
try {
controller.setServerOrigin('http://localhost:3000');
@@ -151,8 +155,38 @@ describe('lazy compilation prewarm helpers', () => {
.toBe(true);
await expect.poll(() => posts.length).toBeGreaterThan(0);
} finally {
- await runPluginEffect(controller.cancelEffect());
+ await runtime.dispose();
globalThis.fetch = originalFetch;
}
});
+
+ it('does not fetch after runtime disposal cancels a scheduled prewarm', async () => {
+ const config = normalizeLazyCompilationPrewarmOptions(true);
+ if (!config) {
+ throw new Error('Expected prewarm config.');
+ }
+
+ const runtime = createPluginEffectRuntime();
+ const fetchSpy = rstest.spyOn(globalThis, 'fetch');
+ const controller = await runtime.runPromise(
+ acquireLazyCompilationPrewarm({
+ runtime,
+ config: { ...config, delayMs: 1_000 },
+ onError: error => {
+ throw error;
+ },
+ })
+ );
+
+ try {
+ controller.setServerOrigin('http://localhost:3000');
+ controller.setManifest(createManifest());
+ controller.schedule();
+ await runtime.dispose();
+
+ expect(fetchSpy).not.toHaveBeenCalled();
+ } finally {
+ fetchSpy.mockRestore();
+ }
+ });
});
diff --git a/tests/route-watch.test.ts b/tests/route-watch.test.ts
index 38b9791b..12662896 100644
--- a/tests/route-watch.test.ts
+++ b/tests/route-watch.test.ts
@@ -8,15 +8,60 @@ import {
} from 'node:fs';
import { tmpdir } from 'node:os';
import { join, resolve } from 'node:path';
-import { describe, expect, it, rstest } from '@rstest/core';
+import { afterAll, describe, expect, it, rstest } from '@rstest/core';
+import { createPluginEffectRuntime } from '../src/effect-runtime';
import {
+ acquireRouteTopologyWatcher,
createRouteManifestSnapshot,
- createRouteTopologyWatcher,
+ createRouteTopologyWatcher as createRouteTopologyWatcherWithRuntime,
ensureDevRestartMarker,
getRouteRestartMarkerPath,
+ type CreateRouteTopologyWatcherOptions,
} from '../src/route-watch';
+const routeWatchRuntime = createPluginEffectRuntime();
+const createRouteTopologyWatcher = (
+ options: Omit
+) =>
+ createRouteTopologyWatcherWithRuntime({
+ runtime: routeWatchRuntime,
+ ...options,
+ });
+
+afterAll(() => routeWatchRuntime.dispose());
+
describe('route watch restart marker', () => {
+ it('closes all route watchers when the plugin scope is disposed', async () => {
+ const root = mkdtempSync(join(tmpdir(), 'rr-route-watch-'));
+ const markerPath = join(root, 'build/.react-router-route-watch');
+ const watchedDirectory = join(root, 'app');
+ const closeWatcher = rstest.fn();
+ const runtime = createPluginEffectRuntime();
+ mkdirSync(watchedDirectory, { recursive: true });
+
+ try {
+ await runtime.runPromise(
+ acquireRouteTopologyWatcher({
+ runtime,
+ watchDirectory: watchedDirectory,
+ restartMarkerPath: markerPath,
+ getRouteTopology: async () => new Set(['initial']),
+ onError: error => {
+ throw error;
+ },
+ watchDirectoryEntry: () => ({ close: closeWatcher }),
+ })
+ );
+
+ await runtime.dispose();
+
+ expect(closeWatcher).toHaveBeenCalledTimes(1);
+ } finally {
+ await runtime.dispose();
+ rmSync(root, { recursive: true, force: true });
+ }
+ });
+
it('allows a topology callback to await watcher shutdown', async () => {
const root = mkdtempSync(join(tmpdir(), 'rr-route-watch-'));
const markerPath = join(root, 'build/.react-router-route-watch');
@@ -101,9 +146,10 @@ describe('route watch restart marker', () => {
const closePromise = close();
releaseRescan();
await closePromise;
+ await close();
expect(readFileSync(markerPath, 'utf8')).toBe(initialMarker);
- expect(closeWatcher).toHaveBeenCalled();
+ expect(closeWatcher).toHaveBeenCalledTimes(1);
} finally {
releaseRescan();
rmSync(root, { recursive: true, force: true });
diff --git a/tests/typegen.test.ts b/tests/typegen.test.ts
index a67c89bb..69350b8a 100644
--- a/tests/typegen.test.ts
+++ b/tests/typegen.test.ts
@@ -1,5 +1,6 @@
import { describe, expect, it, rstest } from '@rstest/core';
import type { ResultPromise } from 'execa';
+import { createPluginEffectRuntime } from '../src/effect-runtime';
import {
createReactRouterTypegenRunner,
registerReactRouterTypegen,
@@ -56,6 +57,23 @@ describe('React Router typegen runner', () => {
expect(execa).toHaveBeenCalledTimes(2);
});
+ it('does not launch a watch after close finishes during execa loading', async () => {
+ let resolveExeca!: (execa: ReturnType) => void;
+ const execaLoaded = new Promise>(resolve => {
+ resolveExeca = resolve;
+ });
+ const process = createProcess();
+ const execa = rstest.fn().mockReturnValue(process.process);
+ const runner = createReactRouterTypegenRunner(async () => execaLoaded);
+
+ const startWatch = runner.startWatch();
+ await runner.closeWatch();
+ resolveExeca(execa);
+ await startWatch;
+
+ expect(execa).not.toHaveBeenCalled();
+ });
+
it('runs one-shot build typegen through npx when no app directory is given', async () => {
const execa = rstest.fn().mockResolvedValue(undefined);
const runner = createReactRouterTypegenRunner(async () => execa);
@@ -111,6 +129,7 @@ describe('React Router typegen runner', () => {
closeWatch: rstest.fn().mockResolvedValue(undefined),
runBuild: rstest.fn().mockResolvedValue(undefined),
};
+ const runtime = createPluginEffectRuntime();
const api = {
context: { action: 'dev' },
logger: { warn: rstest.fn() },
@@ -122,7 +141,11 @@ describe('React Router typegen runner', () => {
onBeforeBuild: rstest.fn(),
};
- registerReactRouterTypegen(api as never, { runner, devWatchDelayMs: 0 });
+ await registerReactRouterTypegen(api as never, {
+ runtime,
+ runner,
+ devWatchDelayMs: 0,
+ });
expect(api.onBeforeStartDevServer).not.toHaveBeenCalled();
const result = afterDevCompile();
@@ -130,6 +153,7 @@ describe('React Router typegen runner', () => {
afterDevCompile();
expect(startWatch).not.toHaveBeenCalled();
await expect.poll(() => startWatch.mock.calls.length).toBe(1);
+ await runtime.dispose();
});
it('defers the dev watch while compiles keep happening', async () => {
@@ -140,6 +164,7 @@ describe('React Router typegen runner', () => {
closeWatch: rstest.fn().mockResolvedValue(undefined),
runBuild: rstest.fn().mockResolvedValue(undefined),
};
+ const runtime = createPluginEffectRuntime();
const api = {
context: { action: 'dev' },
logger: { warn: rstest.fn() },
@@ -151,7 +176,11 @@ describe('React Router typegen runner', () => {
onBeforeBuild: rstest.fn(),
};
- registerReactRouterTypegen(api as never, { runner, devWatchDelayMs: 200 });
+ await registerReactRouterTypegen(api as never, {
+ runtime,
+ runner,
+ devWatchDelayMs: 200,
+ });
// Compiles arriving faster than the idle delay keep pushing the start out.
for (let i = 0; i < 4; i += 1) {
@@ -168,11 +197,11 @@ describe('React Router typegen runner', () => {
afterDevCompile();
await new Promise(resolve => setTimeout(resolve, 100));
expect(startWatch).toHaveBeenCalledTimes(1);
+ await runtime.dispose();
});
it('cancels delayed dev watch startup on close', async () => {
let afterDevCompile!: () => void;
- let closeDevServer!: () => Promise;
const startWatch = rstest.fn().mockResolvedValue(undefined);
const closeWatch = rstest.fn().mockResolvedValue(undefined);
const runner: ReactRouterTypegenRunner = {
@@ -180,6 +209,7 @@ describe('React Router typegen runner', () => {
closeWatch,
runBuild: rstest.fn().mockResolvedValue(undefined),
};
+ const runtime = createPluginEffectRuntime();
const api = {
context: { action: 'dev' },
logger: { warn: rstest.fn() },
@@ -187,31 +217,52 @@ describe('React Router typegen runner', () => {
afterDevCompile = callback;
}),
onBeforeStartDevServer: rstest.fn(),
- onCloseDevServer: rstest.fn(callback => {
- closeDevServer = callback;
- }),
+ onCloseDevServer: rstest.fn(),
onBeforeBuild: rstest.fn(),
};
- registerReactRouterTypegen(api as never, {
+ await registerReactRouterTypegen(api as never, {
+ runtime,
runner,
devWatchDelayMs: 1000,
});
afterDevCompile();
- await closeDevServer();
+ await runtime.dispose();
await new Promise(resolve => setTimeout(resolve, 20));
expect(startWatch).not.toHaveBeenCalled();
expect(closeWatch).toHaveBeenCalledTimes(1);
});
- it('does not register the dev watch hook during production builds', () => {
+ it('surfaces typegen close failures during plugin disposal', async () => {
+ const runtime = createPluginEffectRuntime();
+ const runner: ReactRouterTypegenRunner = {
+ startWatch: rstest.fn().mockResolvedValue(undefined),
+ closeWatch: rstest.fn().mockRejectedValue(new Error('typegen close failed')),
+ runBuild: rstest.fn().mockResolvedValue(undefined),
+ };
+ const api = {
+ context: { action: 'dev' },
+ logger: { warn: rstest.fn() },
+ onAfterDevCompile: rstest.fn(),
+ onBeforeStartDevServer: rstest.fn(),
+ onCloseDevServer: rstest.fn(),
+ onBeforeBuild: rstest.fn(),
+ };
+
+ await registerReactRouterTypegen(api as never, { runtime, runner });
+
+ await expect(runtime.dispose()).rejects.toThrow('typegen close failed');
+ });
+
+ it('does not register the dev watch hook during production builds', async () => {
const runner: ReactRouterTypegenRunner = {
startWatch: rstest.fn().mockResolvedValue(undefined),
closeWatch: rstest.fn().mockResolvedValue(undefined),
runBuild: rstest.fn().mockResolvedValue(undefined),
};
+ const runtime = createPluginEffectRuntime();
const api = {
context: { action: 'build' },
logger: { warn: rstest.fn() },
@@ -221,9 +272,10 @@ describe('React Router typegen runner', () => {
onBeforeBuild: rstest.fn(),
};
- registerReactRouterTypegen(api as never, { runner });
+ await registerReactRouterTypegen(api as never, { runtime, runner });
expect(api.onAfterDevCompile).not.toHaveBeenCalled();
expect(api.onBeforeBuild).toHaveBeenCalled();
+ await runtime.dispose();
});
});
From 769055a4139949b0db097062d7a0faedd8e2ac0c Mon Sep 17 00:00:00 2001
From: Zack Jackson <25274700+ScriptedAlchemy@users.noreply.github.com>
Date: Sat, 18 Jul 2026 02:07:51 +0000
Subject: [PATCH 07/53] fix: stop route watcher work after close
---
src/dev-background-resources.ts | 7 +++
src/effect-runtime.ts | 33 +++++++-----
src/lazy-compilation-prewarm.ts | 9 ++--
src/route-watch.ts | 69 ++++++++++++--------------
src/typegen.ts | 25 ++++------
tests/dev-background-resources.test.ts | 46 +++++++++++++++++
tests/route-watch.test.ts | 45 +++++++++++++++--
7 files changed, 160 insertions(+), 74 deletions(-)
create mode 100644 tests/dev-background-resources.test.ts
diff --git a/src/dev-background-resources.ts b/src/dev-background-resources.ts
index bcae0d95..f1b0f544 100644
--- a/src/dev-background-resources.ts
+++ b/src/dev-background-resources.ts
@@ -8,6 +8,7 @@ import {
DEV_BACKGROUND_STARTUP_DELAY_MS,
normalizeEffectError,
type PluginEffectRuntime,
+ PluginScope,
} from './effect-runtime.js';
import {
acquireLazyCompilationPrewarm,
@@ -170,6 +171,12 @@ export const registerReactRouterDevBackgroundResources = async ({
: null;
if (!isBuild) {
+ await runtime.runPromise(
+ Effect.flatMap(PluginScope, scope =>
+ scope.acquire(Effect.void, routeTopologyWatcherTask.cancelEffect)
+ )
+ );
+
api.onBeforeStartDevServer(async () => {
await ensureDevRestartMarker(routeRestartMarkerPath);
});
diff --git a/src/effect-runtime.ts b/src/effect-runtime.ts
index f7c1b8d2..f7ae3d54 100644
--- a/src/effect-runtime.ts
+++ b/src/effect-runtime.ts
@@ -106,6 +106,13 @@ export const tryPluginPromise = (
catch: normalizeEffectError,
});
+type DelayedPluginTask = {
+ schedule(): void;
+ reschedule(): void;
+ cancelEffect(): Effect.Effect;
+ cancel(): Promise;
+};
+
export const createDelayedPluginTask = ({
runtime,
delayMs,
@@ -116,7 +123,7 @@ export const createDelayedPluginTask = ({
delayMs: number;
run: () => Effect.Effect;
onError: (error: Error) => void;
-}) => {
+}): DelayedPluginTask => {
let fiber: ReturnType | null | undefined;
const cancelEffect = (): Effect.Effect =>
@@ -172,18 +179,18 @@ export const createDelayedPluginTask = ({
fiber = activeFiber;
};
+ const reschedule = (): void => {
+ if (fiber) {
+ void cancel().then(start).catch(onError);
+ } else {
+ start();
+ }
+ };
+
return {
- schedule: (): void => start(),
-
- reschedule(): void {
- if (fiber) {
- void cancel().then(start).catch(onError);
- } else {
- start();
- }
- },
-
- cancelEffect: (): Effect.Effect => cancelEffect(),
- cancel: (): Promise => cancel(),
+ schedule: start,
+ reschedule: reschedule,
+ cancelEffect: cancelEffect,
+ cancel: cancel,
};
};
diff --git a/src/lazy-compilation-prewarm.ts b/src/lazy-compilation-prewarm.ts
index 03894a9f..626c4208 100644
--- a/src/lazy-compilation-prewarm.ts
+++ b/src/lazy-compilation-prewarm.ts
@@ -289,10 +289,9 @@ export const createLazyCompilationPrewarmController = ({
export const acquireLazyCompilationPrewarm = (
options: Parameters[0]
): Effect.Effect =>
- Effect.gen(function* () {
- const pluginScope = yield* PluginScope;
- return yield* pluginScope.acquire(
+ Effect.flatMap(PluginScope, pluginScope =>
+ pluginScope.acquire(
Effect.sync(() => createLazyCompilationPrewarmController(options)),
controller => controller.cancelEffect()
- );
- });
+ )
+ );
diff --git a/src/route-watch.ts b/src/route-watch.ts
index 7c74990c..5ebe4650 100644
--- a/src/route-watch.ts
+++ b/src/route-watch.ts
@@ -156,7 +156,7 @@ const readRouteDirectoriesEffect = (
return walkDirectory(watchDirectory).pipe(Effect.as(directories));
};
-export const createRouteTopologyWatcher = async ({
+const createRouteTopologyWatcher = async ({
runtime,
watchDirectory,
getRouteTopology,
@@ -165,7 +165,7 @@ export const createRouteTopologyWatcher = async ({
onError,
onRouteTopologyChange,
watchDirectoryEntry: watchDirectoryOverride = defaultWatchDirectoryEntry,
-}: CreateRouteTopologyWatcherOptions): Promise<() => Promise> => {
+}: CreateRouteTopologyWatcherOptions) => {
const discoveredDirectories = await runtime.runPromise(
readRouteDirectoriesEffect(watchDirectory)
);
@@ -221,8 +221,11 @@ export const createRouteTopologyWatcher = async ({
let watcher: DirectoryWatcher;
watcher = watchDirectoryOverride(
directory,
- () => rescanTask.reschedule(),
+ () => {
+ if (!closed) rescanTask.reschedule();
+ },
error => {
+ if (closed) return;
if (directoryWatchers.get(directory) === watcher) {
watcher.close();
directoryWatchers.delete(directory);
@@ -246,9 +249,7 @@ export const createRouteTopologyWatcher = async ({
nextState: RouteDirectoryState
): Effect.Effect =>
Effect.suspend(() => {
- if (closed) {
- return Effect.void;
- }
+ if (closed) return Effect.void;
syncDirectoryWatchers(nextState.directories);
if (!areSetsEqual(state.routeTopology, nextState.routeTopology)) {
if (onRouteTopologyChange) {
@@ -281,27 +282,20 @@ export const createRouteTopologyWatcher = async ({
const runRescanEffect = (): Effect.Effect => {
let nextDirectories: Set | undefined;
return Effect.gen(function* () {
- if (closed) {
- return;
- }
+ if (closed) return;
nextDirectories = yield* readRouteDirectoriesEffect(watchDirectory);
- if (closed) {
- return;
- }
+ if (closed) return;
const nextState = {
directories: nextDirectories,
routeTopology: yield* tryPluginPromise(getRouteTopology),
};
- if (closed) {
- return;
- }
+ if (closed) return;
yield* applyNextStateEffect(nextState);
}).pipe(
Effect.catchAll(error =>
Effect.sync(() => {
- if (nextDirectories && !closed) {
+ if (nextDirectories && !closed)
syncDirectoryWatchers(nextDirectories);
- }
onError(error);
})
)
@@ -324,30 +318,31 @@ export const createRouteTopologyWatcher = async ({
onError(error);
}
- return async () => {
- if (closed) {
- await rescanTask.cancel();
- return;
- }
- closed = true;
- await rescanTask.cancel();
- for (const watcher of directoryWatchers.values()) {
- watcher.close();
- }
- directoryWatchers.clear();
- };
+ const closeEffect = (): Effect.Effect =>
+ tryPluginPromise(() => {
+ if (closed) return;
+ closed = true;
+ for (const watcher of directoryWatchers.values()) {
+ watcher.close();
+ }
+ directoryWatchers.clear();
+ }).pipe(Effect.zipRight(rescanTask.cancelEffect()));
+
+ const close = () => runtime.runPromise(closeEffect());
+ return Object.assign(close, { closeEffect });
};
export const acquireRouteTopologyWatcher = (
options: CreateRouteTopologyWatcherOptions
): Effect.Effect<() => Promise, Error, PluginScope> =>
- Effect.gen(function* () {
- const pluginScope = yield* PluginScope;
- return yield* pluginScope.acquire(
+ Effect.flatMap(PluginScope, pluginScope =>
+ pluginScope.acquire(
tryPluginPromise(() => createRouteTopologyWatcher(options)),
close =>
- tryPluginPromise(close).pipe(
- Effect.catchAll(error => Effect.sync(() => options.onError(error)))
- )
- );
- });
+ close
+ .closeEffect()
+ .pipe(
+ Effect.catchAll(error => Effect.sync(() => options.onError(error)))
+ )
+ )
+ );
diff --git a/src/typegen.ts b/src/typegen.ts
index f9019667..2f512c79 100644
--- a/src/typegen.ts
+++ b/src/typegen.ts
@@ -172,21 +172,18 @@ export const registerReactRouterTypegen = async (
);
},
});
- await runtime.runPromise(
- Effect.gen(function* () {
- const pluginScope = yield* PluginScope;
- yield* pluginScope.acquire(Effect.void, () =>
- devWatchTask
- .cancelEffect()
- .pipe(
- Effect.zipRight(
- Effect.orDie(
- tryPluginPromise(() => resolvedRunner.closeWatch())
- )
- )
- )
+ const closeWatchEffect = () =>
+ devWatchTask
+ .cancelEffect()
+ .pipe(
+ Effect.zipRight(
+ Effect.orDie(tryPluginPromise(() => resolvedRunner.closeWatch()))
+ )
);
- })
+ await runtime.runPromise(
+ Effect.flatMap(PluginScope, pluginScope =>
+ pluginScope.acquire(Effect.void, closeWatchEffect)
+ )
);
// Reschedule on every compile so the typegen watch only starts after a
// quiet period with no compiles. Starting it during the initial compile
diff --git a/tests/dev-background-resources.test.ts b/tests/dev-background-resources.test.ts
new file mode 100644
index 00000000..1a8065a5
--- /dev/null
+++ b/tests/dev-background-resources.test.ts
@@ -0,0 +1,46 @@
+import { describe, expect, it, rstest } from '@rstest/core';
+import {
+ registerReactRouterDevBackgroundResources,
+} from '../src/dev-background-resources';
+import { createPluginEffectRuntime } from '../src/effect-runtime';
+
+describe('dev background resources', () => {
+ it('does not restart route watching from a compile callback after scope close', async () => {
+ const runtime = createPluginEffectRuntime();
+ const warn = rstest.fn();
+ const runFork = rstest.spyOn(runtime, 'runFork');
+ let onAfterDevCompile: (() => void) | undefined;
+ const api = {
+ logger: { warn },
+ onBeforeStartDevServer: rstest.fn(),
+ onAfterStartDevServer: rstest.fn(),
+ onAfterDevCompile: rstest.fn((callback: () => void) => {
+ onAfterDevCompile = callback;
+ }),
+ };
+
+ try {
+ await registerReactRouterDevBackgroundResources({
+ api: api as never,
+ runtime,
+ isBuild: false,
+ lazyCompilationPrewarm: false,
+ routeRestartMarkerPath: '/project/.react-router/route-watch',
+ watchDirectory: '/project/app',
+ getRouteTopology: async () => new Set(['initial']),
+ initialRouteTopology: new Set(['initial']),
+ onRouteTopologyChange: undefined,
+ });
+ await runtime.dispose();
+ runFork.mockClear();
+
+ onAfterDevCompile?.();
+
+ expect(onAfterDevCompile).toBeDefined();
+ expect(runFork).not.toHaveBeenCalled();
+ expect(warn).not.toHaveBeenCalled();
+ } finally {
+ await runtime.dispose();
+ }
+ });
+});
diff --git a/tests/route-watch.test.ts b/tests/route-watch.test.ts
index 12662896..aa0621c1 100644
--- a/tests/route-watch.test.ts
+++ b/tests/route-watch.test.ts
@@ -13,7 +13,6 @@ import { createPluginEffectRuntime } from '../src/effect-runtime';
import {
acquireRouteTopologyWatcher,
createRouteManifestSnapshot,
- createRouteTopologyWatcher as createRouteTopologyWatcherWithRuntime,
ensureDevRestartMarker,
getRouteRestartMarkerPath,
type CreateRouteTopologyWatcherOptions,
@@ -23,10 +22,9 @@ const routeWatchRuntime = createPluginEffectRuntime();
const createRouteTopologyWatcher = (
options: Omit
) =>
- createRouteTopologyWatcherWithRuntime({
- runtime: routeWatchRuntime,
- ...options,
- });
+ routeWatchRuntime.runPromise(
+ acquireRouteTopologyWatcher({ runtime: routeWatchRuntime, ...options })
+ );
afterAll(() => routeWatchRuntime.dispose());
@@ -156,6 +154,43 @@ describe('route watch restart marker', () => {
}
});
+ it('does not schedule a rescan from a retained directory callback after close', async () => {
+ const root = mkdtempSync(join(tmpdir(), 'rr-route-watch-'));
+ const markerPath = join(root, 'build/.react-router-route-watch');
+ const watchedDirectory = join(root, 'app');
+ const runtime = createPluginEffectRuntime();
+ let triggerChange!: () => void;
+ const runFork = rstest.spyOn(runtime, 'runFork');
+ mkdirSync(watchedDirectory, { recursive: true });
+
+ try {
+ const close = await runtime.runPromise(
+ acquireRouteTopologyWatcher({
+ runtime,
+ watchDirectory: watchedDirectory,
+ restartMarkerPath: markerPath,
+ getRouteTopology: async () => new Set(['initial']),
+ onError: error => {
+ throw error;
+ },
+ watchDirectoryEntry: (_directory, onChange) => {
+ triggerChange = onChange;
+ return { close: () => {} };
+ },
+ })
+ );
+
+ await close();
+ runFork.mockClear();
+ triggerChange();
+
+ expect(runFork).not.toHaveBeenCalled();
+ } finally {
+ await runtime.dispose();
+ rmSync(root, { recursive: true, force: true });
+ }
+ });
+
it('places the restart marker beside the app directory', () => {
expect(getRouteRestartMarkerPath('/project/app')).toBe(
resolve('/project', '.react-router/route-watch')
From 82d007aefbb1875e988a3c94e5cfeb18d206ebfd Mon Sep 17 00:00:00 2001
From: Zack Jackson <25274700+ScriptedAlchemy@users.noreply.github.com>
Date: Sat, 18 Jul 2026 02:19:20 +0000
Subject: [PATCH 08/53] fix: finalize route watch after close errors
---
src/route-watch.ts | 2 +-
tests/route-watch.test.ts | 52 +++++++++++++++++++++++++++++++++++++++
2 files changed, 53 insertions(+), 1 deletion(-)
diff --git a/src/route-watch.ts b/src/route-watch.ts
index 5ebe4650..1cf519ea 100644
--- a/src/route-watch.ts
+++ b/src/route-watch.ts
@@ -326,7 +326,7 @@ const createRouteTopologyWatcher = async ({
watcher.close();
}
directoryWatchers.clear();
- }).pipe(Effect.zipRight(rescanTask.cancelEffect()));
+ }).pipe(Effect.ensuring(rescanTask.cancelEffect()));
const close = () => runtime.runPromise(closeEffect());
return Object.assign(close, { closeEffect });
diff --git a/tests/route-watch.test.ts b/tests/route-watch.test.ts
index aa0621c1..4f4d66c6 100644
--- a/tests/route-watch.test.ts
+++ b/tests/route-watch.test.ts
@@ -9,6 +9,7 @@ import {
import { tmpdir } from 'node:os';
import { join, resolve } from 'node:path';
import { afterAll, describe, expect, it, rstest } from '@rstest/core';
+import * as Option from 'effect/Option';
import { createPluginEffectRuntime } from '../src/effect-runtime';
import {
acquireRouteTopologyWatcher,
@@ -191,6 +192,57 @@ describe('route watch restart marker', () => {
}
});
+ it('finalizes a pending rescan when watcher close fails', async () => {
+ const root = mkdtempSync(join(tmpdir(), 'rr-route-watch-'));
+ const markerPath = join(root, 'build/.react-router-route-watch');
+ const watchedDirectory = join(root, 'app');
+ const runtime = createPluginEffectRuntime();
+ const closeError = new Error('watcher close failed');
+ const runFork = runtime.runFork;
+ let rescanFiber: ReturnType | undefined;
+ let triggerChange!: () => void;
+ rstest.spyOn(runtime, 'runFork').mockImplementation((effect, options) => {
+ const fiber = runFork(effect, options);
+ rescanFiber = fiber;
+ return fiber;
+ });
+ mkdirSync(watchedDirectory, { recursive: true });
+
+ try {
+ const close = await runtime.runPromise(
+ acquireRouteTopologyWatcher({
+ runtime,
+ watchDirectory: watchedDirectory,
+ restartMarkerPath: markerPath,
+ getRouteTopology: async () => new Set(['initial']),
+ onError: () => {},
+ watchDirectoryEntry: (_directory, onChange) => {
+ triggerChange = onChange;
+ return {
+ close: () => {
+ throw closeError;
+ },
+ };
+ },
+ })
+ );
+
+ triggerChange();
+ if (!rescanFiber) {
+ throw new Error('Expected route watcher rescan to be scheduled.');
+ }
+ const fiber = rescanFiber;
+ expect(Option.isNone(await runtime.runPromise(fiber.poll))).toBe(true);
+
+ await expect(close()).rejects.toThrow(closeError.message);
+
+ expect(Option.isSome(await runtime.runPromise(fiber.poll))).toBe(true);
+ } finally {
+ await runtime.dispose();
+ rmSync(root, { recursive: true, force: true });
+ }
+ });
+
it('places the restart marker beside the app directory', () => {
expect(getRouteRestartMarkerPath('/project/app')).toBe(
resolve('/project', '.react-router/route-watch')
From f62eff0e80d319da297f7dd315ea9129714dad72 Mon Sep 17 00:00:00 2001
From: Zack Jackson <25274700+ScriptedAlchemy@users.noreply.github.com>
Date: Sat, 18 Jul 2026 02:32:38 +0000
Subject: [PATCH 09/53] refactor: simplify dev session close observation
---
src/dev-runtime-session.ts | 32 ++++++++++++----------------
tests/dev-runtime-controller.test.ts | 14 ++++++++++++
2 files changed, 28 insertions(+), 18 deletions(-)
diff --git a/src/dev-runtime-session.ts b/src/dev-runtime-session.ts
index 54dd4607..e6b10f6f 100644
--- a/src/dev-runtime-session.ts
+++ b/src/dev-runtime-session.ts
@@ -1,13 +1,8 @@
import type { RsbuildDevServer } from '@rsbuild/core';
-import * as Effect from 'effect/Effect';
import { PLUGIN_NAME } from './constants.js';
import type { ReactRouterDevRuntime } from './dev-generation.js';
import type { DevCompilerPair } from './dev-runtime-compilation.js';
-import {
- runPluginEffect,
- tryPluginPromise,
- tryPluginSync,
-} from './effect-runtime.js';
+import { normalizeEffectError } from './effect-runtime.js';
export type RuntimeBinding = {
id: number;
@@ -115,18 +110,19 @@ export const createDevRuntimeSessionManager = (
if (observation.promise) {
return observation.promise;
}
- observation.promise = runPluginEffect(
- tryPluginPromise(close).pipe(
- Effect.tap(() =>
- tryPluginSync(() => applyCloseOutcome(observation, { ok: true }))
- ),
- Effect.catchAll(cause =>
- tryPluginSync(() =>
- applyCloseOutcome(observation, { ok: false, cause })
- ).pipe(Effect.zipRight(Effect.fail(cause)))
- )
- )
- );
+ let closePromise: Promise;
+ try {
+ closePromise = Promise.resolve(close());
+ } catch (cause) {
+ closePromise = Promise.reject(normalizeEffectError(cause));
+ }
+ observation.promise = closePromise
+ .then(() => applyCloseOutcome(observation, { ok: true }))
+ .catch(cause => {
+ const error = normalizeEffectError(cause);
+ applyCloseOutcome(observation, { ok: false, cause: error });
+ throw error;
+ });
return observation.promise;
};
closeObservationByServer.set(server, observation);
diff --git a/tests/dev-runtime-controller.test.ts b/tests/dev-runtime-controller.test.ts
index 343d24f9..fc611745 100644
--- a/tests/dev-runtime-controller.test.ts
+++ b/tests/dev-runtime-controller.test.ts
@@ -8,6 +8,8 @@ import type {
Rspack,
} from '@rsbuild/core';
import { describe, expect, it, rstest } from '@rstest/core';
+import { readFile } from 'node:fs/promises';
+import { resolve } from 'node:path';
import { createReactRouterDevRuntimeController } from '../src/dev-runtime-controller';
import {
createBuild,
@@ -306,6 +308,18 @@ const createHarness = (
};
describe('React Router development runtime controller', () => {
+ it('observes dev-server close with a native promise boundary', async () => {
+ const source = await readFile(
+ resolve(process.cwd(), 'src/dev-runtime-session.ts'),
+ 'utf8'
+ );
+
+ expect(source).toContain('Promise.resolve(close())');
+ expect(source).not.toMatch(
+ /effect\/Effect|runPluginEffect|tryPluginPromise|tryPluginSync/
+ );
+ });
+
it('validates lifecycle state before default startup hooks', () => {
const { beforeOrder, closeOrder, startOrder } = createHarness();
expect(beforeOrder).toBe('pre');
From d1f41742e29fda7ac4be8f14b3de1fc3160b08f3 Mon Sep 17 00:00:00 2001
From: Zack Jackson <25274700+ScriptedAlchemy@users.noreply.github.com>
Date: Sat, 18 Jul 2026 02:47:02 +0000
Subject: [PATCH 10/53] refactor: simplify dev attempt finalization
---
src/dev-runtime-controller.ts | 88 ++++++++++++++---------------------
1 file changed, 34 insertions(+), 54 deletions(-)
diff --git a/src/dev-runtime-controller.ts b/src/dev-runtime-controller.ts
index bc36ae40..30f5cee2 100644
--- a/src/dev-runtime-controller.ts
+++ b/src/dev-runtime-controller.ts
@@ -1,5 +1,4 @@
import type { RsbuildConfig, RsbuildPluginAPI, Rspack } from '@rsbuild/core';
-import * as Effect from 'effect/Effect';
import type { ServerBuild } from 'react-router';
import { PLUGIN_NAME } from './constants.js';
import { escapeHtml } from './plugin-utils.js';
@@ -33,12 +32,7 @@ import {
createDevRuntimeSessionManager,
type RuntimeBinding,
} from './dev-runtime-session.js';
-import {
- normalizeEffectError,
- runPluginEffect,
- tryPluginPromise,
- tryPluginSync,
-} from './effect-runtime.js';
+import { normalizeEffectError } from './effect-runtime.js';
type ServerSetup = Exclude<
NonNullable['setup']>,
@@ -154,58 +148,44 @@ export const createReactRouterDevRuntimeController = ({
NonNullable
>();
- const finishRuntimeAttemptEffect = (
+ const finishRuntimeAttempt = async (
binding: RuntimeBinding,
pair: DevCompilerPair,
stats: DevRuntimeStats,
changes: Parameters[1],
identity: Parameters[2]
- ): Effect.Effect =>
- tryPluginPromise(() =>
- binding.runtime.finishAttempt(stats, changes, identity)
- ).pipe(
- Effect.flatMap(result =>
- tryPluginSync(() => {
- if (sessions.getActiveBinding()?.id !== binding.id) {
- return;
- }
- if (result === 'retry-node') {
- pair.node.watching?.invalidate();
- return;
- }
- if (
- result === 'committed' &&
- changes.node.known &&
- identity.node !== undefined &&
- hdrSignaledNodeIdentity.get(pair) !== identity.node &&
- Array.from(changes.node.files).some(
- file => !isHdrRevisionFile(file) && !isCssSourceFile(file)
- )
- ) {
- hdrSignaledNodeIdentity.set(pair, identity.node);
- onNodeRebuildCommitted?.();
- }
- })
- ),
- Effect.catchAll(cause =>
- tryPluginSync(() => {
- if (sessions.getActiveBinding()?.id === binding.id) {
- binding.runtime.failAttempt(normalizeEffectError(cause));
- }
- })
- )
- );
-
- const finishRuntimeAttempt = (
- binding: RuntimeBinding,
- pair: DevCompilerPair,
- stats: DevRuntimeStats,
- changes: Parameters[1],
- identity: Parameters[2]
- ): Promise =>
- runPluginEffect(
- finishRuntimeAttemptEffect(binding, pair, stats, changes, identity)
- );
+ ): Promise => {
+ try {
+ const result = await binding.runtime.finishAttempt(
+ stats,
+ changes,
+ identity
+ );
+ if (sessions.getActiveBinding()?.id !== binding.id) {
+ return;
+ }
+ if (result === 'retry-node') {
+ pair.node.watching?.invalidate();
+ return;
+ }
+ if (
+ result === 'committed' &&
+ changes.node.known &&
+ identity.node !== undefined &&
+ hdrSignaledNodeIdentity.get(pair) !== identity.node &&
+ Array.from(changes.node.files).some(
+ file => !isHdrRevisionFile(file) && !isCssSourceFile(file)
+ )
+ ) {
+ hdrSignaledNodeIdentity.set(pair, identity.node);
+ onNodeRebuildCommitted?.();
+ }
+ } catch (cause) {
+ if (sessions.getActiveBinding()?.id === binding.id) {
+ binding.runtime.failAttempt(normalizeEffectError(cause));
+ }
+ }
+ };
const flushSettledAttempt = (
binding: RuntimeBinding,
From c7b8ccadb2546b0b5bbc0af3adfe0bfe33ddb827 Mon Sep 17 00:00:00 2001
From: Zack Jackson <25274700+ScriptedAlchemy@users.noreply.github.com>
Date: Sat, 18 Jul 2026 02:59:07 +0000
Subject: [PATCH 11/53] refactor: simplify server build resolution
---
src/dev-runtime-artifacts.ts | 10 ++--
src/server-build-resolution.ts | 84 +++++++++++++++-------------------
src/server-utils.ts | 14 ++----
3 files changed, 46 insertions(+), 62 deletions(-)
diff --git a/src/dev-runtime-artifacts.ts b/src/dev-runtime-artifacts.ts
index c82a3548..d5472683 100644
--- a/src/dev-runtime-artifacts.ts
+++ b/src/dev-runtime-artifacts.ts
@@ -5,7 +5,7 @@ import type { ServerBuild } from 'react-router';
import type { ReactRouterManifestForDev } from './manifest.js';
import { getCappedPluginConcurrency } from './concurrency.js';
import { runPluginEffect, tryPluginPromise } from './effect-runtime.js';
-import { resolveServerBuildModuleEffect } from './server-build-resolution.js';
+import { resolveServerBuildModule } from './server-build-resolution.js';
export type ReactRouterDevManifest = ReactRouterManifestForDev;
@@ -140,9 +140,11 @@ const startServerBuildEvaluationEffect = (
): Effect.Effect =>
tryPluginPromise(() => server.environments.node.loadBundle(entryName)).pipe(
Effect.flatMap(buildModule =>
- resolveServerBuildModuleEffect(
- buildModule,
- `Server entry ${JSON.stringify(entryName)}`
+ tryPluginPromise(() =>
+ resolveServerBuildModule(
+ buildModule,
+ `Server entry ${JSON.stringify(entryName)}`
+ )
)
)
);
diff --git a/src/server-build-resolution.ts b/src/server-build-resolution.ts
index 8c73f98c..4d33b5ac 100644
--- a/src/server-build-resolution.ts
+++ b/src/server-build-resolution.ts
@@ -1,10 +1,7 @@
-// Internal module: exposes the Effect-based ServerBuild resolution used by
-// dev-runtime code. Not re-exported from the package entry so the public
-// declaration graph stays free of `effect` types; external callers go through
-// the Promise wrappers in server-utils.ts.
-import * as Effect from 'effect/Effect';
+// Internal module: exposes ServerBuild resolution used by dev-runtime code.
+// External callers go through the Promise wrappers in server-utils.ts.
import type { ServerBuild } from 'react-router';
-import { tryPluginPromise, tryPluginSync } from './effect-runtime.js';
+import { normalizeEffectError } from './effect-runtime.js';
const RESOLVABLE_BUILD_EXPORTS = new Set([
'allowedActionOrigins',
@@ -40,31 +37,25 @@ function isRouteDiscovery(value: unknown): boolean {
);
}
-function resolveBuildExportsEffect(
+async function resolveBuildExports(
build: Record
-): Effect.Effect, Error, never> {
+): Promise> {
const resolved = { ...build };
- return Effect.forEach(
- Object.keys(build),
- key =>
- Effect.gen(function* () {
- if (!RESOLVABLE_BUILD_EXPORTS.has(key)) {
- return;
- }
- const value = build[key];
- if (typeof value === 'function' && value.length === 0) {
- const result = yield* tryPluginSync(() => value());
- resolved[key] = isPromiseLike(result)
- ? yield* tryPluginPromise(() => result)
- : result;
- return;
- }
- if (isPromiseLike(value)) {
- resolved[key] = yield* tryPluginPromise(() => value);
- }
- }),
- { discard: true }
- ).pipe(Effect.as(resolved));
+ for (const key of Object.keys(build)) {
+ if (!RESOLVABLE_BUILD_EXPORTS.has(key)) {
+ continue;
+ }
+ const value = build[key];
+ if (typeof value === 'function' && value.length === 0) {
+ const result = value();
+ resolved[key] = isPromiseLike(result) ? await result : result;
+ continue;
+ }
+ if (isPromiseLike(value)) {
+ resolved[key] = await value;
+ }
+ }
+ return resolved;
}
function isServerBuild(value: unknown): value is ServerBuild {
@@ -86,26 +77,25 @@ function isServerBuild(value: unknown): value is ServerBuild {
);
}
-function resolveServerBuildCandidateEffect(
+async function resolveServerBuildCandidate(
candidate: unknown
-): Effect.Effect {
+): Promise {
if (!isRecord(candidate)) {
- return Effect.succeed(undefined);
+ return undefined;
}
- return resolveBuildExportsEffect(candidate).pipe(
- Effect.map(resolved => (isServerBuild(resolved) ? resolved : undefined))
- );
+ const resolved = await resolveBuildExports(candidate);
+ return isServerBuild(resolved) ? resolved : undefined;
}
-export function resolveServerBuildModuleEffect(
+export async function resolveServerBuildModule(
buildModule: unknown,
source: string
-): Effect.Effect {
- return Effect.gen(function* () {
+): Promise {
+ try {
const moduleValue = isPromiseLike(buildModule)
- ? yield* tryPluginPromise(() => buildModule)
+ ? await buildModule
: buildModule;
- const candidates = [() => moduleValue];
+ const candidates: Array<() => unknown> = [() => moduleValue];
if (isRecord(moduleValue)) {
if ('default' in moduleValue) {
candidates.push(() => moduleValue.default);
@@ -116,16 +106,16 @@ export function resolveServerBuildModuleEffect(
}
for (const getCandidate of candidates) {
- const candidate = yield* tryPluginPromise(() => getCandidate());
- const serverBuild = yield* resolveServerBuildCandidateEffect(candidate);
+ const candidate = await getCandidate();
+ const serverBuild = await resolveServerBuildCandidate(candidate);
if (serverBuild) {
return serverBuild;
}
}
- return yield* Effect.fail(
- new Error(
- `[rsbuild-plugin-react-router] ${source} did not contain a valid React Router ServerBuild.`
- )
+ throw new Error(
+ `[rsbuild-plugin-react-router] ${source} did not contain a valid React Router ServerBuild.`
);
- });
+ } catch (cause) {
+ throw normalizeEffectError(cause);
+ }
}
diff --git a/src/server-utils.ts b/src/server-utils.ts
index b080732f..cf2b59e3 100644
--- a/src/server-utils.ts
+++ b/src/server-utils.ts
@@ -1,7 +1,6 @@
import { resolve } from 'pathe';
import type { ServerBuild } from 'react-router';
-import { runPluginEffect } from './effect-runtime.js';
-import { resolveServerBuildModuleEffect } from './server-build-resolution.js';
+import { resolveServerBuildModule } from './server-build-resolution.js';
import type { Route } from './types.js';
/**
@@ -96,19 +95,12 @@ function generateServerBuild(
return generateStaticTemplate(routes, options);
}
-export function resolveServerBuildModule(
- buildModule: unknown,
- source: string
-): Promise {
- return runPluginEffect(resolveServerBuildModuleEffect(buildModule, source));
-}
+export { resolveServerBuildModule };
export function resolveReactRouterServerBuild(
buildModule: unknown
): Promise {
- return runPluginEffect(
- resolveServerBuildModuleEffect(buildModule, 'Imported module')
- );
+ return resolveServerBuildModule(buildModule, 'Imported module');
}
export { generateServerBuild };
From d873a05ece3ed382be2ca2faa916159106725bd4 Mon Sep 17 00:00:00 2001
From: Zack Jackson <25274700+ScriptedAlchemy@users.noreply.github.com>
Date: Sat, 18 Jul 2026 03:11:50 +0000
Subject: [PATCH 12/53] test: remove obsolete effect implementation checks
---
tests/dev-runtime-controller.test.ts | 14 --------------
tests/effect-runtime.test.ts | 16 ----------------
tests/server-utils.test.ts | 11 -----------
3 files changed, 41 deletions(-)
diff --git a/tests/dev-runtime-controller.test.ts b/tests/dev-runtime-controller.test.ts
index fc611745..343d24f9 100644
--- a/tests/dev-runtime-controller.test.ts
+++ b/tests/dev-runtime-controller.test.ts
@@ -8,8 +8,6 @@ import type {
Rspack,
} from '@rsbuild/core';
import { describe, expect, it, rstest } from '@rstest/core';
-import { readFile } from 'node:fs/promises';
-import { resolve } from 'node:path';
import { createReactRouterDevRuntimeController } from '../src/dev-runtime-controller';
import {
createBuild,
@@ -308,18 +306,6 @@ const createHarness = (
};
describe('React Router development runtime controller', () => {
- it('observes dev-server close with a native promise boundary', async () => {
- const source = await readFile(
- resolve(process.cwd(), 'src/dev-runtime-session.ts'),
- 'utf8'
- );
-
- expect(source).toContain('Promise.resolve(close())');
- expect(source).not.toMatch(
- /effect\/Effect|runPluginEffect|tryPluginPromise|tryPluginSync/
- );
- });
-
it('validates lifecycle state before default startup hooks', () => {
const { beforeOrder, closeOrder, startOrder } = createHarness();
expect(beforeOrder).toBe('pre');
diff --git a/tests/effect-runtime.test.ts b/tests/effect-runtime.test.ts
index aed1a61b..d4306747 100644
--- a/tests/effect-runtime.test.ts
+++ b/tests/effect-runtime.test.ts
@@ -1,6 +1,5 @@
import { describe, expect, it, rstest } from '@rstest/core';
import * as Effect from 'effect/Effect';
-import { readFile } from 'node:fs/promises';
import {
createPluginEffectRuntime,
createDelayedPluginTask,
@@ -10,21 +9,6 @@ import {
} from '../src/effect-runtime';
describe('effect runtime helpers', () => {
- it('centralizes raw Effect runners in effect-runtime.ts', async () => {
- const files = ['index.ts', 'manifest.ts', 'route-watch.ts'];
- const sources = await Promise.all(
- files.map(file =>
- readFile(new URL(`../src/${file}`, import.meta.url), 'utf8')
- )
- );
-
- for (const source of sources) {
- expect(source).not.toMatch(/Effect\.run(?:Promise|Fork|Sync)/);
- expect(source).not.toContain('ManagedRuntime.make');
- }
- expect(sources[0]).not.toContain('runPluginEffect');
- });
-
it('releases dynamically acquired resources when the runtime is disposed', async () => {
const events: string[] = [];
const runtime = createPluginEffectRuntime();
diff --git a/tests/server-utils.test.ts b/tests/server-utils.test.ts
index 8f01667b..f2a64d91 100644
--- a/tests/server-utils.test.ts
+++ b/tests/server-utils.test.ts
@@ -104,17 +104,6 @@ describe('resolveReactRouterServerBuild', () => {
).resolves.toMatchObject({ assets: { version: 'async' } });
});
- it('resolves server builds through the Effect path', async () => {
- const build = createBuild('effect');
-
- await expect(
- resolveReactRouterServerBuild({
- ...build,
- assets: async () => build.assets,
- })
- ).resolves.toMatchObject({ assets: { version: 'effect' } });
- });
-
it('rejects modules without a React Router server build', async () => {
await expect(
resolveReactRouterServerBuild({ default: { routes: {} } })
From f1efe4f67d9b8cf23c5c31a11d13c724591e6f72 Mon Sep 17 00:00:00 2001
From: Zack Jackson <25274700+ScriptedAlchemy@users.noreply.github.com>
Date: Sat, 18 Jul 2026 03:16:47 +0000
Subject: [PATCH 13/53] refactor: simplify route transform construction
---
src/parallel-route-transforms.ts | 20 ++++----------------
1 file changed, 4 insertions(+), 16 deletions(-)
diff --git a/src/parallel-route-transforms.ts b/src/parallel-route-transforms.ts
index f8795a15..b9e915b8 100644
--- a/src/parallel-route-transforms.ts
+++ b/src/parallel-route-transforms.ts
@@ -406,22 +406,10 @@ class ParallelRouteTransformExecutor implements RouteTransformExecutor {
}
}
-export const createRouteTransformExecutor = ({
- parallelRouteTransform,
- routeChunkCache,
- splitRouteModules,
- isBuild,
-}: RouteTransformExecutorOptions = {}): RouteTransformExecutor => {
- return createRouteTransformExecutorWithWorkerFactory(
- {
- parallelRouteTransform,
- routeChunkCache,
- splitRouteModules,
- isBuild,
- },
- createDefaultWorker
- );
-};
+export const createRouteTransformExecutor = (
+ options: RouteTransformExecutorOptions = {}
+): RouteTransformExecutor =>
+ createRouteTransformExecutorWithWorkerFactory(options, createDefaultWorker);
const createRouteTransformExecutorWithWorkerFactory = (
{
From bd761d904fa9dd551ef8b586385b2cd4d6927b06 Mon Sep 17 00:00:00 2001
From: Zack Jackson <25274700+ScriptedAlchemy@users.noreply.github.com>
Date: Sat, 18 Jul 2026 03:32:16 +0000
Subject: [PATCH 14/53] refactor: consolidate prerender file emission
---
src/prerender-build.ts | 44 ++++++++++++++++++---------
src/rsc-prerender.ts | 67 +++++++++++++++++++-----------------------
2 files changed, 61 insertions(+), 50 deletions(-)
diff --git a/src/prerender-build.ts b/src/prerender-build.ts
index a6afdde5..96c20c93 100644
--- a/src/prerender-build.ts
+++ b/src/prerender-build.ts
@@ -160,6 +160,20 @@ export const withBuildRequest = (
handle: (request: Request) => Promise
): Promise => runPluginEffect(createBuildRequestEffect(input, init, handle));
+export const writePrerenderedFile = async (
+ clientBuildDir: string,
+ filePath: string,
+ contents: string | Uint8Array
+): Promise => {
+ const normalizedPath = filePath.startsWith('/')
+ ? filePath.slice(1)
+ : filePath;
+ const outputPath = resolve(clientBuildDir, ...normalizedPath.split('/'));
+ await mkdir(dirname(outputPath), { recursive: true });
+ await writeFile(outputPath, contents);
+ return outputPath;
+};
+
const prerenderData = async ({
handler,
prerenderPath,
@@ -211,12 +225,11 @@ const prerenderData = async ({
);
}
- const outputPath = resolve(
+ const outputPath = await writePrerenderedFile(
clientBuildDir,
- ...outputNormalizedPath.split('/')
+ outputNormalizedPath,
+ data
);
- await mkdir(dirname(outputPath), { recursive: true });
- await writeFile(outputPath, data);
api.logger.info(
`Prerender (data): ${prerenderPath} -> ${relative(
process.cwd(),
@@ -273,13 +286,11 @@ const prerenderRoute = async ({
);
}
- const outputPath = resolve(
+ const outputPath = await writePrerenderedFile(
clientBuildDir,
- ...normalizedPath.split('/'),
- 'index.html'
+ `${normalizedPath}index.html`,
+ html
);
- await mkdir(dirname(outputPath), { recursive: true });
- await writeFile(outputPath, html);
api.logger.info(
`Prerender (html): ${prerenderPath} -> ${relative(
process.cwd(),
@@ -323,9 +334,11 @@ const prerenderResourceRoute = async ({
);
}
- const outputPath = resolve(clientBuildDir, ...normalizedPath.split('/'));
- await mkdir(dirname(outputPath), { recursive: true });
- await writeFile(outputPath, content);
+ const outputPath = await writePrerenderedFile(
+ clientBuildDir,
+ normalizedPath,
+ content
+ );
api.logger.info(
`Prerender (resource): ${prerenderPath} -> ${relative(
process.cwd(),
@@ -389,8 +402,11 @@ const handleSpaMode = async ({
);
}
- const outputPath = resolve(clientBuildDir, filename);
- await writeFile(outputPath, html);
+ const outputPath = await writePrerenderedFile(
+ clientBuildDir,
+ filename,
+ html
+ );
const prettyPath = relative(process.cwd(), outputPath);
if (build.prerender?.length) {
api.logger.info(`Prerender (html): SPA Fallback -> ${prettyPath}`);
diff --git a/src/rsc-prerender.ts b/src/rsc-prerender.ts
index ce1f572f..4a4136c3 100644
--- a/src/rsc-prerender.ts
+++ b/src/rsc-prerender.ts
@@ -1,12 +1,13 @@
import { existsSync } from 'node:fs';
-import { mkdir, writeFile } from 'node:fs/promises';
+import { mkdir } from 'node:fs/promises';
import { pathToFileURL } from 'node:url';
import type { RsbuildPluginAPI } from '@rsbuild/core';
-import { dirname, relative, resolve } from 'pathe';
+import { relative, resolve } from 'pathe';
import { PLUGIN_NAME, SPA_FALLBACK_HTML_FILE } from './constants.js';
import {
createBoundedPrerenderTasksEffect,
withBuildRequest,
+ writePrerenderedFile,
} from './prerender-build.js';
import { normalizeAssetPrefix } from './plugin-utils.js';
import { getPrerenderConcurrency } from './prerender.js';
@@ -167,23 +168,17 @@ const resolveRscRequestHandler = (
return handler as RscRequestHandler;
};
-const writePrerenderedFile = async ({
- api,
- clientBuildDir,
- filePath,
- contents,
-}: {
- api: RscPrerenderBuildApi;
- clientBuildDir: string;
- filePath: string;
- contents: string | Uint8Array;
-}): Promise => {
- const normalizedPath = filePath.startsWith('/')
- ? filePath.slice(1)
- : filePath;
- const outputPath = resolve(clientBuildDir, ...normalizedPath.split('/'));
- await mkdir(dirname(outputPath), { recursive: true });
- await writeFile(outputPath, contents);
+const writeRscPrerenderedFile = async (
+ api: RscPrerenderBuildApi,
+ clientBuildDir: string,
+ filePath: string,
+ contents: string | Uint8Array
+): Promise => {
+ const outputPath = await writePrerenderedFile(
+ clientBuildDir,
+ filePath,
+ contents
+ );
api.logger.info(`Prerendered ${relative(process.cwd(), outputPath)}`);
};
@@ -226,16 +221,16 @@ const prerenderRscUrl = async ({
if (redirectStatusCodes.has(response.status)) {
const location = response.headers.get('Location') ?? '';
- await writePrerenderedFile({
+ await writeRscPrerenderedFile(
api,
clientBuildDir,
- filePath: getRscHtmlFilePath(outputPathname),
- contents: createRedirectHtml({
+ getRscHtmlFilePath(outputPathname),
+ createRedirectHtml({
pathname,
location,
status: response.status,
- }),
- });
+ })
+ );
return;
}
@@ -245,20 +240,20 @@ const prerenderRscUrl = async ({
if (isHtml) {
const html = await response.text();
- await writePrerenderedFile({
+ await writeRscPrerenderedFile(
api,
clientBuildDir,
- filePath: getRscHtmlFilePath(outputPathname),
- contents: html,
- });
+ getRscHtmlFilePath(outputPathname),
+ html
+ );
const flightData = extractRscFlightData(html);
if (flightData !== null) {
- await writePrerenderedFile({
+ await writeRscPrerenderedFile(
api,
clientBuildDir,
- filePath: getRscPayloadFilePath(outputPathname),
- contents: flightData,
- });
+ getRscPayloadFilePath(outputPathname),
+ flightData
+ );
}
return;
}
@@ -266,12 +261,12 @@ const prerenderRscUrl = async ({
// Non-HTML response (e.g. a resource route): emit the raw payload at the
// request path and also prerender its `.rsc` variant for client
// navigations, matching upstream RSC behavior.
- await writePrerenderedFile({
+ await writeRscPrerenderedFile(
api,
clientBuildDir,
- filePath: outputPathname,
- contents: new Uint8Array(await response.arrayBuffer()),
- });
+ outputPathname,
+ new Uint8Array(await response.arrayBuffer())
+ );
if (!pathname.endsWith('.rsc')) {
const dataUrl = new URL(url);
dataUrl.pathname += '.rsc';
From 83627bc0ae7f1eed47b0b38d8362cdb6a8b36ed1 Mon Sep 17 00:00:00 2001
From: Zack Jackson <25274700+ScriptedAlchemy@users.noreply.github.com>
Date: Sat, 18 Jul 2026 03:34:40 +0000
Subject: [PATCH 15/53] Revert "refactor: consolidate prerender file emission"
This reverts commit bd761d904fa9dd551ef8b586385b2cd4d6927b06.
---
src/prerender-build.ts | 44 +++++++++------------------
src/rsc-prerender.ts | 67 +++++++++++++++++++++++-------------------
2 files changed, 50 insertions(+), 61 deletions(-)
diff --git a/src/prerender-build.ts b/src/prerender-build.ts
index 96c20c93..a6afdde5 100644
--- a/src/prerender-build.ts
+++ b/src/prerender-build.ts
@@ -160,20 +160,6 @@ export const withBuildRequest = (
handle: (request: Request) => Promise
): Promise => runPluginEffect(createBuildRequestEffect(input, init, handle));
-export const writePrerenderedFile = async (
- clientBuildDir: string,
- filePath: string,
- contents: string | Uint8Array
-): Promise => {
- const normalizedPath = filePath.startsWith('/')
- ? filePath.slice(1)
- : filePath;
- const outputPath = resolve(clientBuildDir, ...normalizedPath.split('/'));
- await mkdir(dirname(outputPath), { recursive: true });
- await writeFile(outputPath, contents);
- return outputPath;
-};
-
const prerenderData = async ({
handler,
prerenderPath,
@@ -225,11 +211,12 @@ const prerenderData = async ({
);
}
- const outputPath = await writePrerenderedFile(
+ const outputPath = resolve(
clientBuildDir,
- outputNormalizedPath,
- data
+ ...outputNormalizedPath.split('/')
);
+ await mkdir(dirname(outputPath), { recursive: true });
+ await writeFile(outputPath, data);
api.logger.info(
`Prerender (data): ${prerenderPath} -> ${relative(
process.cwd(),
@@ -286,11 +273,13 @@ const prerenderRoute = async ({
);
}
- const outputPath = await writePrerenderedFile(
+ const outputPath = resolve(
clientBuildDir,
- `${normalizedPath}index.html`,
- html
+ ...normalizedPath.split('/'),
+ 'index.html'
);
+ await mkdir(dirname(outputPath), { recursive: true });
+ await writeFile(outputPath, html);
api.logger.info(
`Prerender (html): ${prerenderPath} -> ${relative(
process.cwd(),
@@ -334,11 +323,9 @@ const prerenderResourceRoute = async ({
);
}
- const outputPath = await writePrerenderedFile(
- clientBuildDir,
- normalizedPath,
- content
- );
+ const outputPath = resolve(clientBuildDir, ...normalizedPath.split('/'));
+ await mkdir(dirname(outputPath), { recursive: true });
+ await writeFile(outputPath, content);
api.logger.info(
`Prerender (resource): ${prerenderPath} -> ${relative(
process.cwd(),
@@ -402,11 +389,8 @@ const handleSpaMode = async ({
);
}
- const outputPath = await writePrerenderedFile(
- clientBuildDir,
- filename,
- html
- );
+ const outputPath = resolve(clientBuildDir, filename);
+ await writeFile(outputPath, html);
const prettyPath = relative(process.cwd(), outputPath);
if (build.prerender?.length) {
api.logger.info(`Prerender (html): SPA Fallback -> ${prettyPath}`);
diff --git a/src/rsc-prerender.ts b/src/rsc-prerender.ts
index 4a4136c3..ce1f572f 100644
--- a/src/rsc-prerender.ts
+++ b/src/rsc-prerender.ts
@@ -1,13 +1,12 @@
import { existsSync } from 'node:fs';
-import { mkdir } from 'node:fs/promises';
+import { mkdir, writeFile } from 'node:fs/promises';
import { pathToFileURL } from 'node:url';
import type { RsbuildPluginAPI } from '@rsbuild/core';
-import { relative, resolve } from 'pathe';
+import { dirname, relative, resolve } from 'pathe';
import { PLUGIN_NAME, SPA_FALLBACK_HTML_FILE } from './constants.js';
import {
createBoundedPrerenderTasksEffect,
withBuildRequest,
- writePrerenderedFile,
} from './prerender-build.js';
import { normalizeAssetPrefix } from './plugin-utils.js';
import { getPrerenderConcurrency } from './prerender.js';
@@ -168,17 +167,23 @@ const resolveRscRequestHandler = (
return handler as RscRequestHandler;
};
-const writeRscPrerenderedFile = async (
- api: RscPrerenderBuildApi,
- clientBuildDir: string,
- filePath: string,
- contents: string | Uint8Array
-): Promise => {
- const outputPath = await writePrerenderedFile(
- clientBuildDir,
- filePath,
- contents
- );
+const writePrerenderedFile = async ({
+ api,
+ clientBuildDir,
+ filePath,
+ contents,
+}: {
+ api: RscPrerenderBuildApi;
+ clientBuildDir: string;
+ filePath: string;
+ contents: string | Uint8Array;
+}): Promise => {
+ const normalizedPath = filePath.startsWith('/')
+ ? filePath.slice(1)
+ : filePath;
+ const outputPath = resolve(clientBuildDir, ...normalizedPath.split('/'));
+ await mkdir(dirname(outputPath), { recursive: true });
+ await writeFile(outputPath, contents);
api.logger.info(`Prerendered ${relative(process.cwd(), outputPath)}`);
};
@@ -221,16 +226,16 @@ const prerenderRscUrl = async ({
if (redirectStatusCodes.has(response.status)) {
const location = response.headers.get('Location') ?? '';
- await writeRscPrerenderedFile(
+ await writePrerenderedFile({
api,
clientBuildDir,
- getRscHtmlFilePath(outputPathname),
- createRedirectHtml({
+ filePath: getRscHtmlFilePath(outputPathname),
+ contents: createRedirectHtml({
pathname,
location,
status: response.status,
- })
- );
+ }),
+ });
return;
}
@@ -240,20 +245,20 @@ const prerenderRscUrl = async ({
if (isHtml) {
const html = await response.text();
- await writeRscPrerenderedFile(
+ await writePrerenderedFile({
api,
clientBuildDir,
- getRscHtmlFilePath(outputPathname),
- html
- );
+ filePath: getRscHtmlFilePath(outputPathname),
+ contents: html,
+ });
const flightData = extractRscFlightData(html);
if (flightData !== null) {
- await writeRscPrerenderedFile(
+ await writePrerenderedFile({
api,
clientBuildDir,
- getRscPayloadFilePath(outputPathname),
- flightData
- );
+ filePath: getRscPayloadFilePath(outputPathname),
+ contents: flightData,
+ });
}
return;
}
@@ -261,12 +266,12 @@ const prerenderRscUrl = async ({
// Non-HTML response (e.g. a resource route): emit the raw payload at the
// request path and also prerender its `.rsc` variant for client
// navigations, matching upstream RSC behavior.
- await writeRscPrerenderedFile(
+ await writePrerenderedFile({
api,
clientBuildDir,
- outputPathname,
- new Uint8Array(await response.arrayBuffer())
- );
+ filePath: outputPathname,
+ contents: new Uint8Array(await response.arrayBuffer()),
+ });
if (!pathname.endsWith('.rsc')) {
const dataUrl = new URL(url);
dataUrl.pathname += '.rsc';
From bb87fcd28dde7466c716faa9abf14c7ba97c3d4e Mon Sep 17 00:00:00 2001
From: Zack Jackson <25274700+ScriptedAlchemy@users.noreply.github.com>
Date: Sat, 18 Jul 2026 03:37:22 +0000
Subject: [PATCH 16/53] refactor: simplify route chunk signatures
---
src/route-chunks.ts | 28 ++++++----------------------
1 file changed, 6 insertions(+), 22 deletions(-)
diff --git a/src/route-chunks.ts b/src/route-chunks.ts
index 2c9e2403..7e72621d 100644
--- a/src/route-chunks.ts
+++ b/src/route-chunks.ts
@@ -788,17 +788,12 @@ export const detectRouteChunks = (
};
};
-export const getRouteChunkCode: (
+export const getRouteChunkCode = (
code: string,
chunkName: RouteChunkName,
cache: RouteChunkCache | undefined,
cacheKey: string
-) => string | undefined = (
- code: string,
- chunkName: RouteChunkName,
- cache: RouteChunkCache | undefined,
- cacheKey: string
-) => {
+): string | undefined => {
const analysisCache = cache ?? new Map();
if (chunkName === 'main') {
const exportDependencies = getExportDependencies(
@@ -900,17 +895,12 @@ export const buildManifestChunkValidity = (
!exportNames.has(exportName) || hasRouteChunkByExportName[exportName]
);
-export const detectRouteChunksIfEnabled: (
+export const detectRouteChunksIfEnabled = async (
cache: RouteChunkCache | undefined,
config: RouteChunkConfig,
id: string,
code: string
-) => Promise = async (
- cache: RouteChunkCache | undefined,
- config: RouteChunkConfig,
- id: string,
- code: string
-) => {
+): Promise => {
const noRouteChunks = (): RouteChunkInfo => ({
exportNames: [],
chunkedExports: [] as RouteChunkExportName[],
@@ -927,19 +917,13 @@ export const detectRouteChunksIfEnabled: (
return detectRouteChunks(code, cache, cacheKey);
};
-export const getRouteChunkIfEnabled: (
- cache: RouteChunkCache | undefined,
- config: RouteChunkConfig,
- id: string,
- chunkName: RouteChunkName,
- code: string
-) => Promise = async (
+export const getRouteChunkIfEnabled = async (
cache: RouteChunkCache | undefined,
config: RouteChunkConfig,
id: string,
chunkName: RouteChunkName,
code: string
-) => {
+): Promise => {
if (!config.splitRouteModules) {
return null;
}
From 03f861160ff37ee186f2494d92e46e41bd6ec021 Mon Sep 17 00:00:00 2001
From: Zack Jackson <25274700+ScriptedAlchemy@users.noreply.github.com>
Date: Sat, 18 Jul 2026 03:38:52 +0000
Subject: [PATCH 17/53] test: deduplicate lifecycle test fixtures
---
tests/effect-runtime.test.ts | 60 ++++------
tests/typegen.test.ts | 220 ++++++++++++++++-------------------
2 files changed, 121 insertions(+), 159 deletions(-)
diff --git a/tests/effect-runtime.test.ts b/tests/effect-runtime.test.ts
index d4306747..931a6a56 100644
--- a/tests/effect-runtime.test.ts
+++ b/tests/effect-runtime.test.ts
@@ -8,6 +8,22 @@ import {
tryPluginSync,
} from '../src/effect-runtime';
+const createDelayedTaskFixture = (delayMs: number) => {
+ const run = rstest.fn();
+ const onError = rstest.fn((error: Error) => {
+ throw error;
+ });
+ const runtime = createPluginEffectRuntime();
+ const task = createDelayedPluginTask({
+ runtime,
+ delayMs,
+ run: () => Effect.sync(run),
+ onError,
+ });
+
+ return { runtime, run, onError, task };
+};
+
describe('effect runtime helpers', () => {
it('releases dynamically acquired resources when the runtime is disposed', async () => {
const events: string[] = [];
@@ -70,16 +86,7 @@ describe('effect runtime helpers', () => {
});
it('runs delayed plugin tasks after their delay', async () => {
- const run = rstest.fn();
- const runtime = createPluginEffectRuntime();
- const task = createDelayedPluginTask({
- runtime,
- delayMs: 10,
- run: () => Effect.sync(run),
- onError: error => {
- throw error;
- },
- });
+ const { runtime, run, task } = createDelayedTaskFixture(10);
task.schedule();
expect(run).not.toHaveBeenCalled();
@@ -88,16 +95,7 @@ describe('effect runtime helpers', () => {
});
it('reschedules delayed plugin tasks by replacing the pending run', async () => {
- const run = rstest.fn();
- const runtime = createPluginEffectRuntime();
- const task = createDelayedPluginTask({
- runtime,
- delayMs: 10,
- run: () => Effect.sync(run),
- onError: error => {
- throw error;
- },
- });
+ const { runtime, run, task } = createDelayedTaskFixture(10);
task.schedule();
task.reschedule();
@@ -108,16 +106,7 @@ describe('effect runtime helpers', () => {
});
it('cancels delayed plugin tasks before they start', async () => {
- const run = rstest.fn();
- const runtime = createPluginEffectRuntime();
- const task = createDelayedPluginTask({
- runtime,
- delayMs: 1000,
- run: () => Effect.sync(run),
- onError: error => {
- throw error;
- },
- });
+ const { runtime, run, task } = createDelayedTaskFixture(1000);
task.schedule();
await task.cancel();
@@ -128,16 +117,7 @@ describe('effect runtime helpers', () => {
});
it('cancels delayed plugin tasks when the plugin runtime is disposed', async () => {
- const run = rstest.fn();
- const runtime = createPluginEffectRuntime();
- const task = createDelayedPluginTask({
- runtime,
- delayMs: 25,
- run: () => Effect.sync(run),
- onError: error => {
- throw error;
- },
- });
+ const { runtime, run, task } = createDelayedTaskFixture(25);
task.schedule();
await runtime.dispose();
diff --git a/tests/typegen.test.ts b/tests/typegen.test.ts
index 69350b8a..76106f2f 100644
--- a/tests/typegen.test.ts
+++ b/tests/typegen.test.ts
@@ -19,6 +19,52 @@ const createProcess = () => {
return { process, rejectProcess };
};
+const createDefaultTypegenRunner = () => ({
+ startWatch: rstest.fn().mockResolvedValue(undefined),
+ closeWatch: rstest.fn().mockResolvedValue(undefined),
+ runBuild: rstest.fn().mockResolvedValue(undefined),
+});
+
+type TypegenRunnerSpies = ReturnType;
+
+const createTypegenRegistrationHarness = (
+ {
+ action = 'dev',
+ runner: runnerOverrides,
+ }: {
+ action?: 'dev' | 'build';
+ runner?: Partial;
+ } = {}
+) => {
+ let afterDevCompile: (() => void) | undefined;
+ const runnerSpies = { ...createDefaultTypegenRunner(), ...runnerOverrides };
+ const runner: ReactRouterTypegenRunner = runnerSpies;
+ const runtime = createPluginEffectRuntime();
+ const api = {
+ context: { action },
+ logger: { warn: rstest.fn() },
+ onAfterDevCompile: rstest.fn((callback: () => void) => {
+ afterDevCompile = callback;
+ }),
+ onBeforeStartDevServer: rstest.fn(),
+ onCloseDevServer: rstest.fn(),
+ onBeforeBuild: rstest.fn(),
+ };
+
+ return {
+ runtime,
+ api,
+ runner,
+ ...runnerSpies,
+ get afterDevCompile(): () => void {
+ if (!afterDevCompile) {
+ throw new Error('expected onAfterDevCompile to be registered');
+ }
+ return afterDevCompile;
+ },
+ };
+};
+
describe('React Router typegen runner', () => {
it('starts one watch process and kills it on close', async () => {
const first = createProcess();
@@ -122,160 +168,96 @@ describe('React Router typegen runner', () => {
});
it('starts dev watch after the first dev compile without blocking startup', async () => {
- let afterDevCompile!: () => void;
- const startWatch = rstest.fn().mockResolvedValue(undefined);
- const runner: ReactRouterTypegenRunner = {
- startWatch,
- closeWatch: rstest.fn().mockResolvedValue(undefined),
- runBuild: rstest.fn().mockResolvedValue(undefined),
- };
- const runtime = createPluginEffectRuntime();
- const api = {
- context: { action: 'dev' },
- logger: { warn: rstest.fn() },
- onAfterDevCompile: rstest.fn(callback => {
- afterDevCompile = callback;
- }),
- onBeforeStartDevServer: rstest.fn(),
- onCloseDevServer: rstest.fn(),
- onBeforeBuild: rstest.fn(),
- };
-
- await registerReactRouterTypegen(api as never, {
- runtime,
- runner,
+ const harness = createTypegenRegistrationHarness();
+
+ await registerReactRouterTypegen(harness.api as never, {
+ runtime: harness.runtime,
+ runner: harness.runner,
devWatchDelayMs: 0,
});
- expect(api.onBeforeStartDevServer).not.toHaveBeenCalled();
- const result = afterDevCompile();
+ expect(harness.api.onBeforeStartDevServer).not.toHaveBeenCalled();
+ const result = harness.afterDevCompile();
expect(result).toBeUndefined();
- afterDevCompile();
- expect(startWatch).not.toHaveBeenCalled();
- await expect.poll(() => startWatch.mock.calls.length).toBe(1);
- await runtime.dispose();
+ harness.afterDevCompile();
+ expect(harness.startWatch).not.toHaveBeenCalled();
+ await expect.poll(() => harness.startWatch.mock.calls.length).toBe(1);
+ await harness.runtime.dispose();
});
it('defers the dev watch while compiles keep happening', async () => {
- let afterDevCompile!: () => void;
- const startWatch = rstest.fn().mockResolvedValue(undefined);
- const runner: ReactRouterTypegenRunner = {
- startWatch,
- closeWatch: rstest.fn().mockResolvedValue(undefined),
- runBuild: rstest.fn().mockResolvedValue(undefined),
- };
- const runtime = createPluginEffectRuntime();
- const api = {
- context: { action: 'dev' },
- logger: { warn: rstest.fn() },
- onAfterDevCompile: rstest.fn(callback => {
- afterDevCompile = callback;
- }),
- onBeforeStartDevServer: rstest.fn(),
- onCloseDevServer: rstest.fn(),
- onBeforeBuild: rstest.fn(),
- };
-
- await registerReactRouterTypegen(api as never, {
- runtime,
- runner,
+ const harness = createTypegenRegistrationHarness();
+
+ await registerReactRouterTypegen(harness.api as never, {
+ runtime: harness.runtime,
+ runner: harness.runner,
devWatchDelayMs: 200,
});
// Compiles arriving faster than the idle delay keep pushing the start out.
for (let i = 0; i < 4; i += 1) {
- afterDevCompile();
+ harness.afterDevCompile();
await new Promise(resolve => setTimeout(resolve, 50));
- expect(startWatch).not.toHaveBeenCalled();
+ expect(harness.startWatch).not.toHaveBeenCalled();
}
await expect
- .poll(() => startWatch.mock.calls.length, { timeout: 1000 })
+ .poll(() => harness.startWatch.mock.calls.length, { timeout: 1000 })
.toBe(1);
// Once started, later compiles do not restart or duplicate the watch.
- afterDevCompile();
+ harness.afterDevCompile();
await new Promise(resolve => setTimeout(resolve, 100));
- expect(startWatch).toHaveBeenCalledTimes(1);
- await runtime.dispose();
+ expect(harness.startWatch).toHaveBeenCalledTimes(1);
+ await harness.runtime.dispose();
});
it('cancels delayed dev watch startup on close', async () => {
- let afterDevCompile!: () => void;
- const startWatch = rstest.fn().mockResolvedValue(undefined);
const closeWatch = rstest.fn().mockResolvedValue(undefined);
- const runner: ReactRouterTypegenRunner = {
- startWatch,
- closeWatch,
- runBuild: rstest.fn().mockResolvedValue(undefined),
- };
- const runtime = createPluginEffectRuntime();
- const api = {
- context: { action: 'dev' },
- logger: { warn: rstest.fn() },
- onAfterDevCompile: rstest.fn(callback => {
- afterDevCompile = callback;
- }),
- onBeforeStartDevServer: rstest.fn(),
- onCloseDevServer: rstest.fn(),
- onBeforeBuild: rstest.fn(),
- };
-
- await registerReactRouterTypegen(api as never, {
- runtime,
- runner,
+ const harness = createTypegenRegistrationHarness({
+ runner: { closeWatch },
+ });
+
+ await registerReactRouterTypegen(harness.api as never, {
+ runtime: harness.runtime,
+ runner: harness.runner,
devWatchDelayMs: 1000,
});
- afterDevCompile();
- await runtime.dispose();
+ harness.afterDevCompile();
+ await harness.runtime.dispose();
await new Promise(resolve => setTimeout(resolve, 20));
- expect(startWatch).not.toHaveBeenCalled();
+ expect(harness.startWatch).not.toHaveBeenCalled();
expect(closeWatch).toHaveBeenCalledTimes(1);
});
it('surfaces typegen close failures during plugin disposal', async () => {
- const runtime = createPluginEffectRuntime();
- const runner: ReactRouterTypegenRunner = {
- startWatch: rstest.fn().mockResolvedValue(undefined),
- closeWatch: rstest.fn().mockRejectedValue(new Error('typegen close failed')),
- runBuild: rstest.fn().mockResolvedValue(undefined),
- };
- const api = {
- context: { action: 'dev' },
- logger: { warn: rstest.fn() },
- onAfterDevCompile: rstest.fn(),
- onBeforeStartDevServer: rstest.fn(),
- onCloseDevServer: rstest.fn(),
- onBeforeBuild: rstest.fn(),
- };
-
- await registerReactRouterTypegen(api as never, { runtime, runner });
-
- await expect(runtime.dispose()).rejects.toThrow('typegen close failed');
+ const closeWatch = rstest
+ .fn()
+ .mockRejectedValue(new Error('typegen close failed'));
+ const harness = createTypegenRegistrationHarness({
+ runner: { closeWatch },
+ });
+
+ await registerReactRouterTypegen(harness.api as never, {
+ runtime: harness.runtime,
+ runner: harness.runner,
+ });
+
+ await expect(harness.runtime.dispose()).rejects.toThrow('typegen close failed');
});
it('does not register the dev watch hook during production builds', async () => {
- const runner: ReactRouterTypegenRunner = {
- startWatch: rstest.fn().mockResolvedValue(undefined),
- closeWatch: rstest.fn().mockResolvedValue(undefined),
- runBuild: rstest.fn().mockResolvedValue(undefined),
- };
- const runtime = createPluginEffectRuntime();
- const api = {
- context: { action: 'build' },
- logger: { warn: rstest.fn() },
- onAfterDevCompile: rstest.fn(),
- onBeforeStartDevServer: rstest.fn(),
- onCloseDevServer: rstest.fn(),
- onBeforeBuild: rstest.fn(),
- };
-
- await registerReactRouterTypegen(api as never, { runtime, runner });
-
- expect(api.onAfterDevCompile).not.toHaveBeenCalled();
- expect(api.onBeforeBuild).toHaveBeenCalled();
- await runtime.dispose();
+ const harness = createTypegenRegistrationHarness({ action: 'build' });
+
+ await registerReactRouterTypegen(harness.api as never, {
+ runtime: harness.runtime,
+ runner: harness.runner,
+ });
+
+ expect(harness.api.onAfterDevCompile).not.toHaveBeenCalled();
+ expect(harness.api.onBeforeBuild).toHaveBeenCalled();
+ await harness.runtime.dispose();
});
});
From 1438dce007e22e6dc29d6b689fcbe08bb25f9989 Mon Sep 17 00:00:00 2001
From: Zack Jackson <25274700+ScriptedAlchemy@users.noreply.github.com>
Date: Sat, 18 Jul 2026 03:47:00 +0000
Subject: [PATCH 18/53] refactor: simplify server utility helpers
---
src/server-utils.ts | 23 +++++++----------------
src/ssr-externals.ts | 17 +++--------------
2 files changed, 10 insertions(+), 30 deletions(-)
diff --git a/src/server-utils.ts b/src/server-utils.ts
index cf2b59e3..4f9f50ac 100644
--- a/src/server-utils.ts
+++ b/src/server-utils.ts
@@ -33,7 +33,13 @@ interface ServerBuildOptions {
| undefined;
}
-function generateStaticTemplate(
+/**
+ * Generates the server build module content
+ * @param routes The route manifest
+ * @param options Build options
+ * @returns The generated module content as a string
+ */
+export function generateServerBuild(
routes: Record,
options: ServerBuildOptions
): string {
@@ -82,19 +88,6 @@ function generateStaticTemplate(
`;
}
-/**
- * Generates the server build module content
- * @param routes The route manifest
- * @param options Build options
- * @returns The generated module content as a string
- */
-function generateServerBuild(
- routes: Record,
- options: ServerBuildOptions
-): string {
- return generateStaticTemplate(routes, options);
-}
-
export { resolveServerBuildModule };
export function resolveReactRouterServerBuild(
@@ -102,5 +95,3 @@ export function resolveReactRouterServerBuild(
): Promise {
return resolveServerBuildModule(buildModule, 'Imported module');
}
-
-export { generateServerBuild };
diff --git a/src/ssr-externals.ts b/src/ssr-externals.ts
index e2fc69fd..79f45762 100644
--- a/src/ssr-externals.ts
+++ b/src/ssr-externals.ts
@@ -41,19 +41,8 @@ function isPathInNodeModules(pathname: string): boolean {
}
export function getSsrExternals(rootDirectory: string): string[] {
- const externals: string[] = [];
-
- for (const name of REACT_ROUTER_EXTERNALS) {
+ return REACT_ROUTER_EXTERNALS.filter(name => {
const resolved = resolvePackageJson(name, rootDirectory);
- if (!resolved) {
- continue;
- }
-
- const realPath = safeRealpath(resolved);
- if (!isPathInNodeModules(realPath)) {
- externals.push(name);
- }
- }
-
- return externals;
+ return resolved !== null && !isPathInNodeModules(safeRealpath(resolved));
+ });
}
From 904f5ca485a1db18f47d047c0f6f380db757113f Mon Sep 17 00:00:00 2001
From: Zack Jackson <25274700+ScriptedAlchemy@users.noreply.github.com>
Date: Sat, 18 Jul 2026 03:52:50 +0000
Subject: [PATCH 19/53] refactor: simplify node utility branches
---
src/route-chunks.ts | 2 +-
src/warnings/warn-on-client-source-maps.ts | 16 ++++++----------
2 files changed, 7 insertions(+), 11 deletions(-)
diff --git a/src/route-chunks.ts b/src/route-chunks.ts
index 7e72621d..fc0d140c 100644
--- a/src/route-chunks.ts
+++ b/src/route-chunks.ts
@@ -829,7 +829,7 @@ export const getRouteChunkModuleId = (
`${routeChunkQueryStringPrefix}${encodeURIComponent(chunkName)}`
}`;
-export const isRouteChunkModuleId: (id: string) => boolean = (id: string) =>
+export const isRouteChunkModuleId = (id: string): boolean =>
getRouteChunkNameFromModuleId(id) !== null;
const isRouteChunkName = (name: string): name is RouteChunkName =>
diff --git a/src/warnings/warn-on-client-source-maps.ts b/src/warnings/warn-on-client-source-maps.ts
index 25c818af..2266e5fa 100644
--- a/src/warnings/warn-on-client-source-maps.ts
+++ b/src/warnings/warn-on-client-source-maps.ts
@@ -13,10 +13,8 @@ export function isSourceMapEnabled(value: unknown): boolean {
// Rsbuild normalizes `output.sourceMap` into either:
// - boolean
// - { js?: devtool; css: boolean }
- if (value === true) return true;
- if (value === false || value == null) return false;
- if (typeof value === 'string') return true;
- if (typeof value === 'object') {
+ if (value === true || typeof value === 'string') return true;
+ if (value !== null && typeof value === 'object') {
const js = (value as SourceMapConfigObject).js;
// Any truthy devtool string/object means source maps are on for JS.
return Boolean(js);
@@ -26,12 +24,11 @@ export function isSourceMapEnabled(value: unknown): boolean {
function isDevtoolSourceMap(value: unknown): boolean {
if (value === true) return true;
- if (value == null || value === false) return false;
if (typeof value === 'string') {
return value.includes('source-map');
}
// Unknown object shape - treat as enabled to be safe.
- return typeof value === 'object';
+ return value !== null && typeof value === 'object';
}
export function getClientSourceMapSetting(
@@ -58,10 +55,9 @@ export function getClientDevtoolSetting(
}
function getDevtoolFromRspackConfig(config?: ToolsRspackConfig): unknown {
- if (!config) return undefined;
- if (typeof config === 'function') return undefined;
- if (typeof config !== 'object') return undefined;
- return (config as { devtool?: unknown }).devtool;
+ return config !== null && typeof config === 'object'
+ ? (config as { devtool?: unknown }).devtool
+ : undefined;
}
export function warnOnClientSourceMaps(
From bea9b6b7542aeec555d79cfdac60382d3124b0e1 Mon Sep 17 00:00:00 2001
From: Zack Jackson <25274700+ScriptedAlchemy@users.noreply.github.com>
Date: Sat, 18 Jul 2026 04:00:33 +0000
Subject: [PATCH 20/53] refactor: inline route id extension handling
---
src/plugin-utils.ts | 6 +-----
tests/plugin-utils.test.ts | 29 +----------------------------
2 files changed, 2 insertions(+), 33 deletions(-)
diff --git a/src/plugin-utils.ts b/src/plugin-utils.ts
index afc757f7..db6d343e 100644
--- a/src/plugin-utils.ts
+++ b/src/plugin-utils.ts
@@ -109,12 +109,8 @@ export function resolveEffectiveAssetPrefix(config: {
return normalizeAssetPrefix(devPrefix ?? outputPrefix);
}
-export function stripFileExtension(file: string): string {
- return file.replace(/\.[^/.]+$/, '');
-}
-
export function createRouteId(file: string): string {
- return normalize(stripFileExtension(file));
+ return normalize(file.replace(/\.[^/.]+$/, ''));
}
export function findEntryFile(basePath: string): string {
diff --git a/tests/plugin-utils.test.ts b/tests/plugin-utils.test.ts
index a64c1a32..ad1e3e79 100644
--- a/tests/plugin-utils.test.ts
+++ b/tests/plugin-utils.test.ts
@@ -2,7 +2,6 @@ import { describe, expect, it } from '@rstest/core';
import { generate, parse } from '../src/yuku';
import {
combineURLs,
- stripFileExtension,
createRouteId,
generateWithProps,
normalizeAssetPrefix,
@@ -43,35 +42,9 @@ describe('plugin-utils', () => {
});
});
- describe('stripFileExtension', () => {
- it('should strip .tsx extension', () => {
- expect(stripFileExtension('file.tsx')).toBe('file');
- });
-
- it('should strip .ts extension', () => {
- expect(stripFileExtension('file.ts')).toBe('file');
- });
-
- it('should strip .jsx extension', () => {
- expect(stripFileExtension('file.jsx')).toBe('file');
- });
-
- it('should strip .js extension', () => {
- expect(stripFileExtension('file.js')).toBe('file');
- });
-
- it('should handle files with multiple dots', () => {
- expect(stripFileExtension('file.test.tsx')).toBe('file.test');
- });
-
- it('should handle paths with directories', () => {
- expect(stripFileExtension('routes/home.tsx')).toBe('routes/home');
- });
- });
-
describe('createRouteId', () => {
it('should create route ID from file path', () => {
- expect(createRouteId('routes/home.tsx')).toBe('routes/home');
+ expect(createRouteId('routes/home.test.tsx')).toBe('routes/home.test');
});
it('should normalize path separators', () => {
From 98f9a2dcc7ede75ac0b1aeb22a106ce3335f549a Mon Sep 17 00:00:00 2001
From: Zack Jackson <25274700+ScriptedAlchemy@users.noreply.github.com>
Date: Sat, 18 Jul 2026 04:04:06 +0000
Subject: [PATCH 21/53] refactor: consolidate route expression conversion
---
src/route-component-transform.ts | 31 ++++++++++++-------------------
1 file changed, 12 insertions(+), 19 deletions(-)
diff --git a/src/route-component-transform.ts b/src/route-component-transform.ts
index 15205ac3..68fc5c57 100644
--- a/src/route-component-transform.ts
+++ b/src/route-component-transform.ts
@@ -16,21 +16,14 @@ import {
type AnyNode,
} from './route-ast.js';
-export function toFunctionExpression(decl: AnyNode): AnyNode {
- return {
- ...decl,
- type: 'FunctionExpression',
- declare: undefined,
- };
-}
-
-export function toClassExpression(decl: AnyNode): AnyNode {
- return {
- ...decl,
- type: 'ClassExpression',
- declare: undefined,
- };
-}
+const toExpression = (
+ decl: AnyNode,
+ type: 'FunctionExpression' | 'ClassExpression'
+): AnyNode => ({
+ ...decl,
+ type,
+ declare: undefined,
+});
const getComponentExportName = (exportedName: string): string | null => {
if (exportedName === 'default') {
@@ -132,9 +125,9 @@ export const transformRoute = (ast: ParseResult | AnyNode): void => {
const uid = getHocUid(`with${name}Props`);
const expression =
declaration.type === 'FunctionDeclaration'
- ? toFunctionExpression(declaration)
+ ? toExpression(declaration, 'FunctionExpression')
: declaration.type === 'ClassDeclaration'
- ? toClassExpression(declaration)
+ ? toExpression(declaration, 'ClassExpression')
: declaration;
return variableDeclaration(name, callExpression(uid, [expression]));
}
@@ -166,9 +159,9 @@ export const transformRoute = (ast: ParseResult | AnyNode): void => {
}
const expression =
declaration.type === 'FunctionDeclaration'
- ? toFunctionExpression(declaration)
+ ? toExpression(declaration, 'FunctionExpression')
: declaration.type === 'ClassDeclaration'
- ? toClassExpression(declaration)
+ ? toExpression(declaration, 'ClassExpression')
: declaration;
statement.declaration = callExpression(uid, [expression]);
continue;
From d92e234eb9717016efba5f288d57c9d6583e8fcd Mon Sep 17 00:00:00 2001
From: Zack Jackson <25274700+ScriptedAlchemy@users.noreply.github.com>
Date: Sat, 18 Jul 2026 04:11:23 +0000
Subject: [PATCH 22/53] test: enforce effect-free runtime outputs
---
scripts/test-package-interop.mts | 28 ++++++++++++++++++++++++++++
1 file changed, 28 insertions(+)
diff --git a/scripts/test-package-interop.mts b/scripts/test-package-interop.mts
index ef8efe12..2d03b4c1 100644
--- a/scripts/test-package-interop.mts
+++ b/scripts/test-package-interop.mts
@@ -48,6 +48,7 @@ const verifyRegistration = async (writer, reader) => {
onAfterStartDevServer: noop,
onCloseDevServer: collect(closes),
onCloseBuild: noop,
+ onExit: collect(closes),
onAfterEnvironmentCompile: noop,
onAfterBuild: noop,
processAssets: noop,
@@ -106,6 +107,32 @@ const verifyPackIncludesOriginalSource = async () => {
);
};
+const verifyEffectFreeRuntimeOutputs = () => {
+ const dist = new URL('../dist/', import.meta.url);
+ const pending = [
+ 'parallel-route-transform-worker.js',
+ 'templates/entry.client.js',
+ 'templates/entry.client.cjs',
+ 'templates/entry.rsc.client.js',
+ ].map(file => new URL(file, dist));
+ const visited = new Set();
+
+ while (pending.length > 0) {
+ const file = pending.pop();
+ if (!file || visited.has(file.href)) continue;
+ visited.add(file.href);
+ const source = readFileSync(file, 'utf8');
+ assert.doesNotMatch(source, /["']effect(?:\/|["'])/);
+ for (const [, specifier] of source.matchAll(
+ /(?:\bfrom\s*|\b(?:import|require)\s*\(?\s*)["'](\.{1,2}\/[^"']+)["']/g
+ )) {
+ const dependency = new URL(specifier, file);
+ assert(dependency.href.startsWith(dist.href));
+ pending.push(dependency);
+ }
+ }
+};
+
const verifyRscPublicSurface = (esm, commonjs) => {
assert.equal(typeof esm.pluginReactRouterRSC, 'function');
assert.equal(typeof commonjs.pluginReactRouterRSC, 'function');
@@ -147,6 +174,7 @@ const verifyRscPublicSurface = (esm, commonjs) => {
const main = async () => {
const [esm, commonjs] = await loadEntryPoints();
await verifyPackIncludesOriginalSource();
+ verifyEffectFreeRuntimeOutputs();
verifyRscPublicSurface(esm, commonjs);
process.chdir(
fileURLToPath(new URL('../tests/fixtures/dev-runtime/', import.meta.url))
From fb7647ad615e534ffa68a3ab5a983cb62718cd3e Mon Sep 17 00:00:00 2001
From: Zack Jackson <25274700+ScriptedAlchemy@users.noreply.github.com>
Date: Sat, 18 Jul 2026 05:29:07 +0000
Subject: [PATCH 23/53] refactor: scope plugin background fibers
---
src/effect-runtime.ts | 26 ++++++++++----------
tests/effect-runtime.test.ts | 46 ++++++++++++++++++++++++++++++++++++
2 files changed, 58 insertions(+), 14 deletions(-)
diff --git a/src/effect-runtime.ts b/src/effect-runtime.ts
index f7ae3d54..4d1faaa8 100644
--- a/src/effect-runtime.ts
+++ b/src/effect-runtime.ts
@@ -2,8 +2,10 @@ import * as Cause from 'effect/Cause';
import * as Context from 'effect/Context';
import * as Duration from 'effect/Duration';
import * as Effect from 'effect/Effect';
+import * as ExecutionStrategy from 'effect/ExecutionStrategy';
import * as Exit from 'effect/Exit';
import * as Fiber from 'effect/Fiber';
+import * as FiberSet from 'effect/FiberSet';
import * as Layer from 'effect/Layer';
import * as ManagedRuntime from 'effect/ManagedRuntime';
import * as Option from 'effect/Option';
@@ -16,6 +18,7 @@ type Acquire = (
export interface PluginScope {
readonly acquire: Acquire;
+ readonly fibers: FiberSet.FiberSet;
}
export const PluginScope: Context.Tag =
@@ -25,11 +28,14 @@ const PluginScopeLive = Layer.scoped(
PluginScope,
Effect.gen(function* () {
const scope = yield* Effect.scope;
+ const resources = yield* Scope.fork(scope, ExecutionStrategy.sequential);
+ const fibers = yield* FiberSet.make();
return {
acquire: (acquire, release) =>
Effect.acquireRelease(acquire, release).pipe(
- Effect.provideService(Scope.Scope, scope)
+ Effect.provideService(Scope.Scope, resources)
),
+ fibers,
};
})
);
@@ -43,28 +49,20 @@ export type PluginEffectRuntime = Pick<
export const createPluginEffectRuntime = (): PluginEffectRuntime => {
const runtime = ManagedRuntime.make(PluginScopeLive);
- const supervisedFibers = new Set>();
- const runFork: typeof runtime.runFork = (effect, options) => {
- const fiber = runtime.runFork(effect, options);
- supervisedFibers.add(fiber);
- fiber.addObserver(() => {
- supervisedFibers.delete(fiber);
- });
- return fiber;
- };
// ManagedRuntime builds lazily; initialize its scoped layer before forks.
runtime.runSync(Effect.void);
+ const pluginScope = runtime.runSync(PluginScope);
+ const runFork: typeof runtime.runFork = runtime.runSync(
+ FiberSet.runtime(pluginScope.fibers)()
+ );
let disposePromise: Promise | undefined;
return {
runPromise: runtime.runPromise,
runFork,
- dispose: (): Promise =>
- (disposePromise ??= runtime
- .runPromise(Fiber.interruptAll(supervisedFibers))
- .then(() => runtime.dispose())),
+ dispose: (): Promise => (disposePromise ??= runtime.dispose()),
};
};
diff --git a/tests/effect-runtime.test.ts b/tests/effect-runtime.test.ts
index 931a6a56..f8f27e5f 100644
--- a/tests/effect-runtime.test.ts
+++ b/tests/effect-runtime.test.ts
@@ -1,5 +1,6 @@
import { describe, expect, it, rstest } from '@rstest/core';
import * as Effect from 'effect/Effect';
+import * as Fiber from 'effect/Fiber';
import {
createPluginEffectRuntime,
createDelayedPluginTask,
@@ -67,6 +68,51 @@ describe('effect runtime helpers', () => {
expect(finalized).toBe(1);
});
+ it('settles shutdown when a fiber forks during finalization', async () => {
+ const runtime = createPluginEffectRuntime();
+ let nestedFiber: ReturnType | undefined;
+ let nestedRan = false;
+ let resolveNestedStarted!: () => void;
+ const nestedStarted = new Promise(resolve => {
+ resolveNestedStarted = resolve;
+ });
+
+ runtime.runFork(
+ Effect.never.pipe(
+ Effect.ensuring(
+ Effect.sync(() => {
+ nestedFiber = runtime.runFork(
+ Effect.sync(() => {
+ nestedRan = true;
+ }).pipe(Effect.zipRight(Effect.never))
+ );
+ resolveNestedStarted();
+ })
+ )
+ )
+ );
+
+ const dispose = runtime.dispose();
+ await nestedStarted;
+ let settled = false;
+ try {
+ settled = await Promise.race([
+ dispose.then(() => true),
+ new Promise(resolve => setTimeout(() => resolve(false), 1_000)),
+ ]);
+ expect(settled).toBe(true);
+ if (nestedFiber) {
+ await Effect.runPromise(Fiber.await(nestedFiber));
+ }
+ expect(nestedRan).toBe(false);
+ } finally {
+ if (!settled && nestedFiber) {
+ await runtime.runPromise(Fiber.interrupt(nestedFiber).pipe(Effect.asVoid));
+ }
+ await dispose;
+ }
+ });
+
it('preserves typed errors at promise boundaries', async () => {
const error = new Error('typed failure');
From 803fab5fd3b7a3989e4988c51834d3c7aa58ea38 Mon Sep 17 00:00:00 2001
From: Zack Jackson <25274700+ScriptedAlchemy@users.noreply.github.com>
Date: Sat, 18 Jul 2026 05:29:22 +0000
Subject: [PATCH 24/53] refactor: inline SSR external path helpers
---
src/ssr-externals.ts | 19 ++++++-------------
1 file changed, 6 insertions(+), 13 deletions(-)
diff --git a/src/ssr-externals.ts b/src/ssr-externals.ts
index 79f45762..941670a3 100644
--- a/src/ssr-externals.ts
+++ b/src/ssr-externals.ts
@@ -28,21 +28,14 @@ export function resolvePackageJson(
}
}
-function safeRealpath(pathname: string): string {
- try {
- return realpathSync(pathname);
- } catch {
- return pathname;
- }
-}
-
-function isPathInNodeModules(pathname: string): boolean {
- return pathname.split(sep).includes('node_modules');
-}
-
export function getSsrExternals(rootDirectory: string): string[] {
return REACT_ROUTER_EXTERNALS.filter(name => {
const resolved = resolvePackageJson(name, rootDirectory);
- return resolved !== null && !isPathInNodeModules(safeRealpath(resolved));
+ if (resolved === null) return false;
+ let packageJsonPath = resolved;
+ try {
+ packageJsonPath = realpathSync(packageJsonPath);
+ } catch {}
+ return !packageJsonPath.split(sep).includes('node_modules');
});
}
From 41c4b9e22de1f40879660beb824fa70583ef7881 Mon Sep 17 00:00:00 2001
From: Zack Jackson <25274700+ScriptedAlchemy@users.noreply.github.com>
Date: Sat, 18 Jul 2026 05:41:21 +0000
Subject: [PATCH 25/53] fix: defer plugin fiber runtime initialization
---
src/effect-runtime.ts | 15 +++++++--------
tests/effect-runtime.test.ts | 14 ++++++++++++--
2 files changed, 19 insertions(+), 10 deletions(-)
diff --git a/src/effect-runtime.ts b/src/effect-runtime.ts
index 4d1faaa8..244e2a82 100644
--- a/src/effect-runtime.ts
+++ b/src/effect-runtime.ts
@@ -49,19 +49,18 @@ export type PluginEffectRuntime = Pick<
export const createPluginEffectRuntime = (): PluginEffectRuntime => {
const runtime = ManagedRuntime.make(PluginScopeLive);
-
- // ManagedRuntime builds lazily; initialize its scoped layer before forks.
- runtime.runSync(Effect.void);
- const pluginScope = runtime.runSync(PluginScope);
- const runFork: typeof runtime.runFork = runtime.runSync(
- FiberSet.runtime(pluginScope.fibers)()
- );
+ let fiberRunFork: typeof runtime.runFork | undefined;
let disposePromise: Promise | undefined;
return {
runPromise: runtime.runPromise,
- runFork,
+ runFork: (effect, options) =>
+ (fiberRunFork ??= runtime.runSync(
+ Effect.flatMap(PluginScope, pluginScope =>
+ FiberSet.runtime(pluginScope.fibers)()
+ )
+ ))(effect, options),
dispose: (): Promise => (disposePromise ??= runtime.dispose()),
};
};
diff --git a/tests/effect-runtime.test.ts b/tests/effect-runtime.test.ts
index f8f27e5f..aa88dcdc 100644
--- a/tests/effect-runtime.test.ts
+++ b/tests/effect-runtime.test.ts
@@ -26,7 +26,7 @@ const createDelayedTaskFixture = (delayMs: number) => {
};
describe('effect runtime helpers', () => {
- it('releases dynamically acquired resources when the runtime is disposed', async () => {
+ it('releases resources after interrupting fibers when the runtime is disposed', async () => {
const events: string[] = [];
const runtime = createPluginEffectRuntime();
@@ -46,8 +46,18 @@ describe('effect runtime helpers', () => {
})
);
+ runtime.runFork(
+ Effect.never.pipe(
+ Effect.ensuring(
+ Effect.sync(() => {
+ events.push('fiber');
+ })
+ )
+ )
+ );
+
await runtime.dispose();
- expect(events).toEqual(['acquire', 'release:resource']);
+ expect(events).toEqual(['acquire', 'fiber', 'release:resource']);
});
it('interrupts supervised fibers and disposes idempotently', async () => {
From 2d5cf6abea66cf4f29bb4cfff1e77240dc35b7d2 Mon Sep 17 00:00:00 2001
From: Zack Jackson <25274700+ScriptedAlchemy@users.noreply.github.com>
Date: Sat, 18 Jul 2026 05:51:07 +0000
Subject: [PATCH 26/53] test: remove redundant executor source guard
---
tests/index.test.ts | 9 ---------
1 file changed, 9 deletions(-)
diff --git a/tests/index.test.ts b/tests/index.test.ts
index a839ee70..1bb100db 100644
--- a/tests/index.test.ts
+++ b/tests/index.test.ts
@@ -60,15 +60,6 @@ const captureEnv = (keys: string[]) => {
};
describe('pluginReactRouter', () => {
- it('acquires the classic route transform executor only from PluginScope', async () => {
- const source = await readFile(
- join(process.cwd(), 'src/mode-plan.ts'),
- 'utf8'
- );
-
- expect(source).not.toContain('createRouteTransformExecutor');
- });
-
it('shares one Effect runtime and closes it from every Rsbuild shutdown path', async () => {
const rsbuild = await createStubRsbuild({
rsbuildConfig: {},
From 084b19ef7bc69c477cf035743a202f662ccc9841 Mon Sep 17 00:00:00 2001
From: Zack Jackson <25274700+ScriptedAlchemy@users.noreply.github.com>
Date: Sat, 18 Jul 2026 05:58:25 +0000
Subject: [PATCH 27/53] docs: update effect architecture version
---
docs/superpowers/plans/2026-07-17-effect-server-architecture.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/superpowers/plans/2026-07-17-effect-server-architecture.md b/docs/superpowers/plans/2026-07-17-effect-server-architecture.md
index 053e8e5c..c671feac 100644
--- a/docs/superpowers/plans/2026-07-17-effect-server-architecture.md
+++ b/docs/superpowers/plans/2026-07-17-effect-server-architecture.md
@@ -6,7 +6,7 @@
**Architecture:** Create one `ManagedRuntime` per plugin instance. Rsbuild hooks and exported Promise APIs are the only runtime boundaries; internal Node-side workflows return `Effect`. A runtime-owned `PluginScope` service owns dynamically created workers, watchers, timers, background fibers, and dev-server resources. Pure transforms, generated/browser runtime code, and worker entrypoints remain ordinary TypeScript.
-**Tech Stack:** TypeScript 5.9, Effect 3.21, Rsbuild/Rspack, Rstest, Playwright, Node `worker_threads`, existing benchmark harness.
+**Tech Stack:** TypeScript 5.9, Effect 3.22, Rsbuild/Rspack, Rstest, Playwright, Node `worker_threads`, existing benchmark harness.
## Global Constraints
From 80cbe8a49a0fc4f54d85fefb84930fcac9dcaa2f Mon Sep 17 00:00:00 2001
From: Zack Jackson <25274700+ScriptedAlchemy@users.noreply.github.com>
Date: Sat, 18 Jul 2026 06:31:23 +0000
Subject: [PATCH 28/53] refactor: use native Effect resource scopes
---
src/dev-background-resources.ts | 5 +---
src/effect-runtime.ts | 41 +++++++++++++-------------------
src/lazy-compilation-prewarm.ts | 12 ++++------
src/parallel-route-transforms.ts | 9 ++++---
src/route-watch.ts | 22 ++++++++---------
src/typegen.ts | 5 +---
tests/effect-runtime.test.ts | 22 +++++++----------
7 files changed, 47 insertions(+), 69 deletions(-)
diff --git a/src/dev-background-resources.ts b/src/dev-background-resources.ts
index f1b0f544..eaae083f 100644
--- a/src/dev-background-resources.ts
+++ b/src/dev-background-resources.ts
@@ -8,7 +8,6 @@ import {
DEV_BACKGROUND_STARTUP_DELAY_MS,
normalizeEffectError,
type PluginEffectRuntime,
- PluginScope,
} from './effect-runtime.js';
import {
acquireLazyCompilationPrewarm,
@@ -172,9 +171,7 @@ export const registerReactRouterDevBackgroundResources = async ({
if (!isBuild) {
await runtime.runPromise(
- Effect.flatMap(PluginScope, scope =>
- scope.acquire(Effect.void, routeTopologyWatcherTask.cancelEffect)
- )
+ Effect.acquireRelease(Effect.void, routeTopologyWatcherTask.cancelEffect)
);
api.onBeforeStartDevServer(async () => {
diff --git a/src/effect-runtime.ts b/src/effect-runtime.ts
index 244e2a82..5b411f69 100644
--- a/src/effect-runtime.ts
+++ b/src/effect-runtime.ts
@@ -11,44 +11,37 @@ import * as ManagedRuntime from 'effect/ManagedRuntime';
import * as Option from 'effect/Option';
import * as Scope from 'effect/Scope';
-type Acquire = (
- acquire: Effect.Effect,
- release: (resource: A) => Effect.Effect
-) => Effect.Effect;
-
-export interface PluginScope {
- readonly acquire: Acquire;
- readonly fibers: FiberSet.FiberSet;
+interface PluginFibers {
+ readonly _: unique symbol;
}
-export const PluginScope: Context.Tag =
- Context.GenericTag('rsbuild-plugin-react-router/PluginScope');
+const PluginFibers: Context.Tag =
+ Context.GenericTag(
+ 'rsbuild-plugin-react-router/PluginFibers'
+ );
+
+type PluginRuntimeContext = Scope.Scope | PluginFibers;
-const PluginScopeLive = Layer.scoped(
- PluginScope,
+const PluginRuntimeLive = Layer.scopedContext(
Effect.gen(function* () {
const scope = yield* Effect.scope;
const resources = yield* Scope.fork(scope, ExecutionStrategy.sequential);
const fibers = yield* FiberSet.make();
- return {
- acquire: (acquire, release) =>
- Effect.acquireRelease(acquire, release).pipe(
- Effect.provideService(Scope.Scope, resources)
- ),
- fibers,
- };
+ return Context.make(Scope.Scope, resources).pipe(
+ Context.add(PluginFibers, fibers)
+ );
})
);
export type PluginEffectRuntime = Pick<
- ManagedRuntime.ManagedRuntime,
+ ManagedRuntime.ManagedRuntime,
'runFork' | 'runPromise'
> & {
readonly dispose: () => Promise;
};
export const createPluginEffectRuntime = (): PluginEffectRuntime => {
- const runtime = ManagedRuntime.make(PluginScopeLive);
+ const runtime = ManagedRuntime.make(PluginRuntimeLive);
let fiberRunFork: typeof runtime.runFork | undefined;
let disposePromise: Promise | undefined;
@@ -57,8 +50,8 @@ export const createPluginEffectRuntime = (): PluginEffectRuntime => {
runPromise: runtime.runPromise,
runFork: (effect, options) =>
(fiberRunFork ??= runtime.runSync(
- Effect.flatMap(PluginScope, pluginScope =>
- FiberSet.runtime(pluginScope.fibers)()
+ Effect.flatMap(PluginFibers, fibers =>
+ FiberSet.runtime(fibers)()
)
))(effect, options),
dispose: (): Promise => (disposePromise ??= runtime.dispose()),
@@ -118,7 +111,7 @@ export const createDelayedPluginTask = ({
}: {
runtime: PluginEffectRuntime;
delayMs: number;
- run: () => Effect.Effect;
+ run: () => Effect.Effect;
onError: (error: Error) => void;
}): DelayedPluginTask => {
let fiber: ReturnType | null | undefined;
diff --git a/src/lazy-compilation-prewarm.ts b/src/lazy-compilation-prewarm.ts
index 626c4208..4bc3d073 100644
--- a/src/lazy-compilation-prewarm.ts
+++ b/src/lazy-compilation-prewarm.ts
@@ -1,10 +1,10 @@
import * as Effect from 'effect/Effect';
+import type * as Scope from 'effect/Scope';
import type { ReactRouterManifestForDev } from './manifest.js';
import type { RouteManifestItem } from './types.js';
import {
createDelayedPluginTask,
type PluginEffectRuntime,
- PluginScope,
tryPluginPromise,
} from './effect-runtime.js';
@@ -288,10 +288,8 @@ export const createLazyCompilationPrewarmController = ({
export const acquireLazyCompilationPrewarm = (
options: Parameters[0]
-): Effect.Effect =>
- Effect.flatMap(PluginScope, pluginScope =>
- pluginScope.acquire(
- Effect.sync(() => createLazyCompilationPrewarmController(options)),
- controller => controller.cancelEffect()
- )
+): Effect.Effect =>
+ Effect.acquireRelease(
+ Effect.sync(() => createLazyCompilationPrewarmController(options)),
+ controller => controller.cancelEffect()
);
diff --git a/src/parallel-route-transforms.ts b/src/parallel-route-transforms.ts
index b9e915b8..5dfca400 100644
--- a/src/parallel-route-transforms.ts
+++ b/src/parallel-route-transforms.ts
@@ -1,10 +1,10 @@
import { Worker } from 'node:worker_threads';
import * as Effect from 'effect/Effect';
+import type * as Scope from 'effect/Scope';
import { setBoundedCacheEntry } from './bounded-cache.js';
import { getAvailableCpuCount, getDefaultConcurrency } from './concurrency.js';
import {
normalizeEffectError,
- PluginScope,
tryPluginPromise,
} from './effect-runtime.js';
import {
@@ -456,8 +456,7 @@ const acquireRouteTransformExecutorWithWorkerFactory = Effect.fn(
options: RouteTransformExecutorOptions,
createWorker: RouteTransformWorkerFactory
) {
- const pluginScope = yield* PluginScope;
- return yield* pluginScope.acquire(
+ return yield* Effect.acquireRelease(
Effect.sync(() =>
createRouteTransformExecutorWithWorkerFactory(options, createWorker)
),
@@ -470,11 +469,11 @@ const acquireRouteTransformExecutorWithWorkerFactory = Effect.fn(
export const acquireRouteTransformExecutor = (
options: RouteTransformExecutorOptions = {}
-): Effect.Effect =>
+): Effect.Effect =>
acquireRouteTransformExecutorWithWorkerFactory(options, createDefaultWorker);
export const acquireRouteTransformExecutorForTesting = (
options: RouteTransformExecutorOptions,
createWorker: RouteTransformWorkerFactory
-): Effect.Effect =>
+): Effect.Effect =>
acquireRouteTransformExecutorWithWorkerFactory(options, createWorker);
diff --git a/src/route-watch.ts b/src/route-watch.ts
index 1cf519ea..6f924909 100644
--- a/src/route-watch.ts
+++ b/src/route-watch.ts
@@ -2,12 +2,12 @@ import { watch, type FSWatcher } from 'node:fs';
import { access, mkdir, readdir, writeFile } from 'node:fs/promises';
import type { RsbuildConfig } from '@rsbuild/core';
import * as Effect from 'effect/Effect';
+import type * as Scope from 'effect/Scope';
import { dirname, resolve } from 'pathe';
import { getCappedPluginConcurrency } from './concurrency.js';
import {
createDelayedPluginTask,
type PluginEffectRuntime,
- PluginScope,
tryPluginPromise,
} from './effect-runtime.js';
import type { Route } from './types.js';
@@ -334,15 +334,13 @@ const createRouteTopologyWatcher = async ({
export const acquireRouteTopologyWatcher = (
options: CreateRouteTopologyWatcherOptions
-): Effect.Effect<() => Promise, Error, PluginScope> =>
- Effect.flatMap(PluginScope, pluginScope =>
- pluginScope.acquire(
- tryPluginPromise(() => createRouteTopologyWatcher(options)),
- close =>
- close
- .closeEffect()
- .pipe(
- Effect.catchAll(error => Effect.sync(() => options.onError(error)))
- )
- )
+): Effect.Effect<() => Promise, Error, Scope.Scope> =>
+ Effect.acquireRelease(
+ tryPluginPromise(() => createRouteTopologyWatcher(options)),
+ close =>
+ close
+ .closeEffect()
+ .pipe(
+ Effect.catchAll(error => Effect.sync(() => options.onError(error)))
+ )
);
diff --git a/src/typegen.ts b/src/typegen.ts
index 2f512c79..9955cc7a 100644
--- a/src/typegen.ts
+++ b/src/typegen.ts
@@ -6,7 +6,6 @@ import * as Effect from 'effect/Effect';
import {
createDelayedPluginTask,
type PluginEffectRuntime,
- PluginScope,
tryPluginPromise,
} from './effect-runtime.js';
import { resolvePackageJson } from './ssr-externals.js';
@@ -181,9 +180,7 @@ export const registerReactRouterTypegen = async (
)
);
await runtime.runPromise(
- Effect.flatMap(PluginScope, pluginScope =>
- pluginScope.acquire(Effect.void, closeWatchEffect)
- )
+ Effect.acquireRelease(Effect.void, closeWatchEffect)
);
// Reschedule on every compile so the typegen watch only starts after a
// quiet period with no compiles. Starting it during the initial compile
diff --git a/tests/effect-runtime.test.ts b/tests/effect-runtime.test.ts
index aa88dcdc..7b8e9e81 100644
--- a/tests/effect-runtime.test.ts
+++ b/tests/effect-runtime.test.ts
@@ -4,7 +4,6 @@ import * as Fiber from 'effect/Fiber';
import {
createPluginEffectRuntime,
createDelayedPluginTask,
- PluginScope,
runPluginEffect,
tryPluginSync,
} from '../src/effect-runtime';
@@ -31,19 +30,16 @@ describe('effect runtime helpers', () => {
const runtime = createPluginEffectRuntime();
await runtime.runPromise(
- Effect.gen(function* () {
- const scope = yield* PluginScope;
- return yield* scope.acquire(
+ Effect.acquireRelease(
+ Effect.sync(() => {
+ events.push('acquire');
+ return 'resource';
+ }),
+ resource =>
Effect.sync(() => {
- events.push('acquire');
- return 'resource';
- }),
- resource =>
- Effect.sync(() => {
- events.push(`release:${resource}`);
- })
- );
- })
+ events.push(`release:${resource}`);
+ })
+ )
);
runtime.runFork(
From 77f6c488aa1bb222fb6941631093409a084df880 Mon Sep 17 00:00:00 2001
From: Zack Jackson <25274700+ScriptedAlchemy@users.noreply.github.com>
Date: Sat, 18 Jul 2026 06:37:30 +0000
Subject: [PATCH 29/53] test: consolidate runtime lifecycle coverage
---
scripts/test-package-interop.mts | 31 ++++++++++++++++++------------
tests/effect-runtime.test.ts | 20 +------------------
tests/index.test.ts | 27 --------------------------
tests/setup.ts | 33 +++-----------------------------
4 files changed, 23 insertions(+), 88 deletions(-)
diff --git a/scripts/test-package-interop.mts b/scripts/test-package-interop.mts
index 2d03b4c1..8e363a64 100644
--- a/scripts/test-package-interop.mts
+++ b/scripts/test-package-interop.mts
@@ -24,6 +24,8 @@ const build = {
const collect = hooks => hook => hooks.push(hook);
const noop = () => undefined;
+const invoke = hook =>
+ typeof hook === 'function' ? hook() : hook.handler?.();
const loadEntryPoints = async () => {
const esm = await import('../dist/index.js');
@@ -33,7 +35,9 @@ const loadEntryPoints = async () => {
const verifyRegistration = async (writer, reader) => {
const starts = [];
- const closes = [];
+ const closeBuilds = [];
+ const closeDevServers = [];
+ const exits = [];
const api = {
context: { action: 'dev', rootPath: process.cwd() },
logger: { info: noop, warn: noop, error: noop },
@@ -46,9 +50,9 @@ const verifyRegistration = async (writer, reader) => {
isPluginExists: () => false,
onBeforeStartDevServer: collect(starts),
onAfterStartDevServer: noop,
- onCloseDevServer: collect(closes),
- onCloseBuild: noop,
- onExit: collect(closes),
+ onCloseDevServer: collect(closeDevServers),
+ onCloseBuild: collect(closeBuilds),
+ onExit: collect(exits),
onAfterEnvironmentCompile: noop,
onAfterBuild: noop,
processAssets: noop,
@@ -60,9 +64,16 @@ const verifyRegistration = async (writer, reader) => {
await writer.pluginReactRouter({ customServer: true }).setup(api);
const startHook = starts.find(hook => hook.order === 'pre');
- const closeHook = closes.find(hook => hook.order === 'pre');
+ const closeHook = closeDevServers.find(hook => hook.order === 'pre');
assert(startHook, 'Expected a pre dev-server start hook');
assert(closeHook, 'Expected a pre dev-server close hook');
+ assert.equal(closeBuilds.length, 1);
+ assert.equal(exits.length, 1);
+ assert.equal(closeBuilds[0], exits[0]);
+ assert.deepEqual(
+ closeDevServers.filter(hook => typeof hook === 'function'),
+ closeBuilds
+ );
const start = startHook.handler;
const server = {
close: async () => undefined,
@@ -72,13 +83,9 @@ const verifyRegistration = async (writer, reader) => {
await start({ environments: {}, server });
const pending = reader.loadReactRouterServerBuild(server);
- for (const close of closes) {
- if (typeof close === 'function') {
- await close();
- } else {
- await close.handler?.();
- }
- }
+ await Promise.all(
+ [...closeBuilds, ...closeDevServers, ...exits].map(invoke)
+ );
await assert.rejects(pending, /closed before a React Router build was ready/);
await assert.rejects(
reader.loadReactRouterServerBuild(server),
diff --git a/tests/effect-runtime.test.ts b/tests/effect-runtime.test.ts
index 7b8e9e81..86cdfdc6 100644
--- a/tests/effect-runtime.test.ts
+++ b/tests/effect-runtime.test.ts
@@ -52,26 +52,8 @@ describe('effect runtime helpers', () => {
)
);
- await runtime.dispose();
- expect(events).toEqual(['acquire', 'fiber', 'release:resource']);
- });
-
- it('interrupts supervised fibers and disposes idempotently', async () => {
- let finalized = 0;
- const runtime = createPluginEffectRuntime();
-
- runtime.runFork(
- Effect.never.pipe(
- Effect.ensuring(
- Effect.sync(() => {
- finalized += 1;
- })
- )
- )
- );
-
await Promise.all([runtime.dispose(), runtime.dispose()]);
- expect(finalized).toBe(1);
+ expect(events).toEqual(['acquire', 'fiber', 'release:resource']);
});
it('settles shutdown when a fiber forks during finalization', async () => {
diff --git a/tests/index.test.ts b/tests/index.test.ts
index 1bb100db..99525b63 100644
--- a/tests/index.test.ts
+++ b/tests/index.test.ts
@@ -2,7 +2,6 @@ import { createStubRsbuild } from '@scripts/test-helper';
import { describe, expect, it, rstest } from '@rstest/core';
import { execFileSync } from 'node:child_process';
import * as fs from 'node:fs';
-import { readFile } from 'node:fs/promises';
import { createRequire } from 'node:module';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
@@ -60,32 +59,6 @@ const captureEnv = (keys: string[]) => {
};
describe('pluginReactRouter', () => {
- it('shares one Effect runtime and closes it from every Rsbuild shutdown path', async () => {
- const rsbuild = await createStubRsbuild({
- rsbuildConfig: {},
- });
-
- rsbuild.addPlugins([pluginReactRouter()]);
- await rsbuild.unwrapConfig();
-
- expect(rsbuild.shutdownHooks.onCloseBuild).toHaveLength(1);
- expect(rsbuild.shutdownHooks.onExit).toHaveLength(1);
- expect(rsbuild.shutdownHooks.onCloseBuild[0]).toBe(
- rsbuild.shutdownHooks.onCloseDevServer[0]
- );
- expect(rsbuild.shutdownHooks.onCloseBuild[0]).toBe(
- rsbuild.shutdownHooks.onExit[0]
- );
-
- await Promise.all(
- ['onCloseBuild', 'onCloseDevServer', 'onExit'].map(
- rsbuild.runShutdownHook
- )
- );
-
- expect(rsbuild.logger.error).not.toHaveBeenCalled();
- });
-
it('should configure basic plugin options', async () => {
const rsbuild = await createStubRsbuild({
rsbuildConfig: {},
diff --git a/tests/setup.ts b/tests/setup.ts
index 89786562..cb449e62 100644
--- a/tests/setup.ts
+++ b/tests/setup.ts
@@ -95,12 +95,6 @@ const deepMerge = (base: any, overrides: any): any => {
return result;
};
-type ShutdownHookName = 'onCloseBuild' | 'onCloseDevServer' | 'onExit';
-type ShutdownCallback = () => unknown;
-type ShutdownHookRegistration =
- | ShutdownCallback
- | { handler: ShutdownCallback };
-
// Mock the @scripts/test-helper module
rstest.mock('@scripts/test-helper', () => ({
createStubRsbuild: rstest.fn().mockImplementation(async ({ action = 'dev', rsbuildConfig = {} } = {}) => {
@@ -155,30 +149,15 @@ rstest.mock('@scripts/test-helper', () => ({
const mergeRsbuildConfig = (a: any, b: any) => deepMerge(a, b);
const pending: Promise[] = [];
- const shutdownHooks: Record = {
- onCloseBuild: [],
- onCloseDevServer: [],
- onExit: [],
- };
- const registerShutdownHook = (hookName: ShutdownHookName) => (
- registration: ShutdownHookRegistration
- ): void => {
- const callback =
- typeof registration === 'function'
- ? registration
- : registration.handler;
- shutdownHooks[hookName].push(callback);
- };
-
const stub: any = {
addPlugins: rstest.fn(),
unwrapConfig: rstest.fn(),
processAssets: rstest.fn(),
onBeforeStartDevServer: rstest.fn(),
onAfterStartDevServer: rstest.fn(),
- onCloseDevServer: rstest.fn(registerShutdownHook('onCloseDevServer')),
- onCloseBuild: rstest.fn(registerShutdownHook('onCloseBuild')),
- onExit: rstest.fn(registerShutdownHook('onExit')),
+ onCloseDevServer: rstest.fn(),
+ onCloseBuild: rstest.fn(),
+ onExit: rstest.fn(),
onBeforeBuild: rstest.fn(),
onAfterBuild: rstest.fn(),
onBeforeDevCompile: rstest.fn(),
@@ -211,12 +190,6 @@ rstest.mock('@scripts/test-helper', () => ({
},
},
},
- shutdownHooks,
- runShutdownHook: async (hookName: ShutdownHookName): Promise => {
- await Promise.all(
- shutdownHooks[hookName].map(callback => callback())
- );
- },
};
stub.modifyRsbuildConfig.mockImplementation((arg: any) => {
From 2d5960ec09ba4902fe3dc0267b788138aa519a71 Mon Sep 17 00:00:00 2001
From: Zack Jackson <25274700+ScriptedAlchemy@users.noreply.github.com>
Date: Sat, 18 Jul 2026 06:46:05 +0000
Subject: [PATCH 30/53] test: simplify lifecycle coverage
---
rstest.config.ts | 12 -------
tests/parallel-route-transforms.test.ts | 9 +++---
tests/route-watch.test.ts | 42 +++----------------------
tests/typegen.test.ts | 28 +++++------------
4 files changed, 17 insertions(+), 74 deletions(-)
diff --git a/rstest.config.ts b/rstest.config.ts
index f9e96d46..8e7ba5a1 100644
--- a/rstest.config.ts
+++ b/rstest.config.ts
@@ -1,18 +1,6 @@
import { defineConfig } from '@rstest/core';
export default defineConfig({
- tools: {
- rspack: {
- module: {
- rules: [
- {
- test: /(?:effect-runtime|parallel-route-transforms)\.test\.ts$/,
- parser: { url: false },
- },
- ],
- },
- },
- },
include: [
'tests/**/*.test.ts',
'tests/react-router-framework/react-router-dev/__tests__/rsc-virtual-route-modules-test.ts',
diff --git a/tests/parallel-route-transforms.test.ts b/tests/parallel-route-transforms.test.ts
index 7fcc2d0b..21f42ee4 100644
--- a/tests/parallel-route-transforms.test.ts
+++ b/tests/parallel-route-transforms.test.ts
@@ -1,4 +1,5 @@
-import { readFile } from 'node:fs/promises';
+import { readFileSync } from 'node:fs';
+import { resolve } from 'node:path';
import { describe, expect, it } from '@rstest/core';
import { getExportNames } from '../src/export-utils';
import {
@@ -134,9 +135,9 @@ const resolveWorkerMessage = (
};
describe('parallel route transforms', () => {
- it('keeps the worker entrypoint Effect-free', async () => {
- const source = await readFile(
- new URL('../src/parallel-route-transform-worker.ts', import.meta.url),
+ it('keeps the worker entrypoint Effect-free', () => {
+ const source = readFileSync(
+ resolve(process.cwd(), 'src/parallel-route-transform-worker.ts'),
'utf8'
);
diff --git a/tests/route-watch.test.ts b/tests/route-watch.test.ts
index 4f4d66c6..cfacd6dc 100644
--- a/tests/route-watch.test.ts
+++ b/tests/route-watch.test.ts
@@ -117,6 +117,7 @@ describe('route watch restart marker', () => {
});
let triggerChange!: () => void;
const closeWatcher = rstest.fn();
+ const runFork = rstest.spyOn(routeWatchRuntime, 'runFork');
try {
const close = await createRouteTopologyWatcher({
@@ -145,49 +146,16 @@ describe('route watch restart marker', () => {
const closePromise = close();
releaseRescan();
await closePromise;
- await close();
-
- expect(readFileSync(markerPath, 'utf8')).toBe(initialMarker);
- expect(closeWatcher).toHaveBeenCalledTimes(1);
- } finally {
- releaseRescan();
- rmSync(root, { recursive: true, force: true });
- }
- });
-
- it('does not schedule a rescan from a retained directory callback after close', async () => {
- const root = mkdtempSync(join(tmpdir(), 'rr-route-watch-'));
- const markerPath = join(root, 'build/.react-router-route-watch');
- const watchedDirectory = join(root, 'app');
- const runtime = createPluginEffectRuntime();
- let triggerChange!: () => void;
- const runFork = rstest.spyOn(runtime, 'runFork');
- mkdirSync(watchedDirectory, { recursive: true });
-
- try {
- const close = await runtime.runPromise(
- acquireRouteTopologyWatcher({
- runtime,
- watchDirectory: watchedDirectory,
- restartMarkerPath: markerPath,
- getRouteTopology: async () => new Set(['initial']),
- onError: error => {
- throw error;
- },
- watchDirectoryEntry: (_directory, onChange) => {
- triggerChange = onChange;
- return { close: () => {} };
- },
- })
- );
-
await close();
runFork.mockClear();
triggerChange();
+ expect(readFileSync(markerPath, 'utf8')).toBe(initialMarker);
+ expect(closeWatcher).toHaveBeenCalledTimes(1);
expect(runFork).not.toHaveBeenCalled();
} finally {
- await runtime.dispose();
+ runFork.mockRestore();
+ releaseRescan();
rmSync(root, { recursive: true, force: true });
}
});
diff --git a/tests/typegen.test.ts b/tests/typegen.test.ts
index 76106f2f..4afa934c 100644
--- a/tests/typegen.test.ts
+++ b/tests/typegen.test.ts
@@ -212,27 +212,7 @@ describe('React Router typegen runner', () => {
await harness.runtime.dispose();
});
- it('cancels delayed dev watch startup on close', async () => {
- const closeWatch = rstest.fn().mockResolvedValue(undefined);
- const harness = createTypegenRegistrationHarness({
- runner: { closeWatch },
- });
-
- await registerReactRouterTypegen(harness.api as never, {
- runtime: harness.runtime,
- runner: harness.runner,
- devWatchDelayMs: 1000,
- });
-
- harness.afterDevCompile();
- await harness.runtime.dispose();
- await new Promise(resolve => setTimeout(resolve, 20));
-
- expect(harness.startWatch).not.toHaveBeenCalled();
- expect(closeWatch).toHaveBeenCalledTimes(1);
- });
-
- it('surfaces typegen close failures during plugin disposal', async () => {
+ it('cancels delayed startup and surfaces close failures during disposal', async () => {
const closeWatch = rstest
.fn()
.mockRejectedValue(new Error('typegen close failed'));
@@ -243,9 +223,15 @@ describe('React Router typegen runner', () => {
await registerReactRouterTypegen(harness.api as never, {
runtime: harness.runtime,
runner: harness.runner,
+ devWatchDelayMs: 25,
});
+ harness.afterDevCompile();
await expect(harness.runtime.dispose()).rejects.toThrow('typegen close failed');
+ await new Promise(resolve => setTimeout(resolve, 50));
+
+ expect(harness.startWatch).not.toHaveBeenCalled();
+ expect(closeWatch).toHaveBeenCalledTimes(1);
});
it('does not register the dev watch hook during production builds', async () => {
From 3a5514225d6e94118b6ef8d5126b8bf383796124 Mon Sep 17 00:00:00 2001
From: Zack Jackson <25274700+ScriptedAlchemy@users.noreply.github.com>
Date: Sat, 18 Jul 2026 06:56:00 +0000
Subject: [PATCH 31/53] refactor: remove unused effect sync adapter
---
.../2026-07-17-effect-server-architecture.md | 4 ++--
src/effect-runtime.ts | 8 --------
tests/effect-runtime.test.ts | 20 -------------------
3 files changed, 2 insertions(+), 30 deletions(-)
diff --git a/docs/superpowers/plans/2026-07-17-effect-server-architecture.md b/docs/superpowers/plans/2026-07-17-effect-server-architecture.md
index c671feac..718e5edf 100644
--- a/docs/superpowers/plans/2026-07-17-effect-server-architecture.md
+++ b/docs/superpowers/plans/2026-07-17-effect-server-architecture.md
@@ -145,7 +145,7 @@ git commit -m "test: protect worker runtime boundary"
**Interfaces:**
- Produces: `PluginScope`, `PluginEffectRuntime`, and `createPluginEffectRuntime()`.
-- Preserves temporarily: `runPluginEffect`, `tryPluginSync`, `tryPluginPromise`, `createDelayedPluginTask` while callers migrate.
+- Preserves temporarily: `runPluginEffect`, `tryPluginPromise`, `createDelayedPluginTask` while callers migrate.
- Later tasks consume: `runtime.runPromise`, `runtime.runFork`, `runtime.dispose`, and `PluginScope.acquire`.
- [ ] **Step 1: Write failing lifecycle tests**
@@ -944,7 +944,7 @@ git commit -m "refactor: compose server workflows with effect"
**Interfaces:**
- Deletes: `runPluginEffect` if no public compatibility adapter requires it.
-- Retains: `tryPluginSync`, `tryPluginPromise`, `normalizeEffectError` only if they eliminate repeated boundary code.
+- Retains: `tryPluginPromise`, `normalizeEffectError` only if they eliminate repeated boundary code.
- Produces: one error policy at Promise/Rsbuild boundaries and typed recovery only where behavior differs.
- [ ] **Step 1: Inventory remaining runtime crossings**
diff --git a/src/effect-runtime.ts b/src/effect-runtime.ts
index 5b411f69..ea6d1184 100644
--- a/src/effect-runtime.ts
+++ b/src/effect-runtime.ts
@@ -80,14 +80,6 @@ export const runPluginEffect = async (
throw normalizeEffectCause(exit.cause);
};
-export const tryPluginSync = (
- evaluate: () => A
-): Effect.Effect =>
- Effect.try({
- try: evaluate,
- catch: normalizeEffectError,
- });
-
export const tryPluginPromise = (
evaluate: () => PromiseLike | A
): Effect.Effect =>
diff --git a/tests/effect-runtime.test.ts b/tests/effect-runtime.test.ts
index 86cdfdc6..19cfb78b 100644
--- a/tests/effect-runtime.test.ts
+++ b/tests/effect-runtime.test.ts
@@ -4,8 +4,6 @@ import * as Fiber from 'effect/Fiber';
import {
createPluginEffectRuntime,
createDelayedPluginTask,
- runPluginEffect,
- tryPluginSync,
} from '../src/effect-runtime';
const createDelayedTaskFixture = (delayMs: number) => {
@@ -101,24 +99,6 @@ describe('effect runtime helpers', () => {
}
});
- it('preserves typed errors at promise boundaries', async () => {
- const error = new Error('typed failure');
-
- await expect(runPluginEffect(tryPluginSync(() => {
- throw error;
- }))).rejects.toBe(error);
- });
-
- it('normalizes synchronous thrown causes to errors', async () => {
- await expect(
- runPluginEffect(
- tryPluginSync(() => {
- throw 'dev runtime failed';
- })
- )
- ).rejects.toThrow('dev runtime failed');
- });
-
it('runs delayed plugin tasks after their delay', async () => {
const { runtime, run, task } = createDelayedTaskFixture(10);
From 0f72f3584faefb8e35d56ad4ce5f64b1e7b6596f Mon Sep 17 00:00:00 2001
From: Zack Jackson <25274700+ScriptedAlchemy@users.noreply.github.com>
Date: Sat, 18 Jul 2026 06:56:00 +0000
Subject: [PATCH 32/53] ci: benchmark forced route transform startup
---
.github/workflows/benchmark.yml | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml
index a05265c7..ccac6693 100644
--- a/.github/workflows/benchmark.yml
+++ b/.github/workflows/benchmark.yml
@@ -27,11 +27,13 @@ jobs:
profile: ci-small
build_order: base,head
dev_order: head,base
+ parallel_route_transform: auto
- shard: large
label: large fixtures
profile: ci-large
build_order: head,base
dev_order: base,head
+ parallel_route_transform: 1
env:
BENCHMARK_PROFILE: ${{ matrix.profile }}
@@ -41,6 +43,7 @@ jobs:
BENCHMARK_ITERATIONS: '5'
BENCHMARK_WARMUP: '1'
BENCHMARK_DEV_ROUTES: auto
+ BENCHMARK_PARALLEL_ROUTE_TRANSFORM: ${{ matrix.parallel_route_transform }}
steps:
- name: Checkout PR head
@@ -120,6 +123,7 @@ jobs:
--iterations "$BENCHMARK_BUILD_ITERATIONS" \
--large-iterations "$BENCHMARK_LARGE_ITERATIONS" \
--warmup "$BENCHMARK_WARMUP" \
+ --parallel-route-transform "$BENCHMARK_PARALLEL_ROUTE_TRANSFORM" \
--clean build \
--format both \
--log-performance \
@@ -150,6 +154,7 @@ jobs:
--large-iterations "$BENCHMARK_LARGE_ITERATIONS" \
--warmup "$BENCHMARK_WARMUP" \
--dev-routes "$BENCHMARK_DEV_ROUTES" \
+ --parallel-route-transform "$BENCHMARK_PARALLEL_ROUTE_TRANSFORM" \
--clean build \
--format both \
--log-performance \
From b876eb4d192aa1ba2dfa659d0c2774e4a28ab09e Mon Sep 17 00:00:00 2001
From: Zack Jackson <25274700+ScriptedAlchemy@users.noreply.github.com>
Date: Sat, 18 Jul 2026 07:06:50 +0000
Subject: [PATCH 33/53] fix: interrupt route rescans during scope disposal
---
src/route-watch.ts | 4 +-
tests/route-watch.test.ts | 87 ++++++++++++++-------------------------
2 files changed, 31 insertions(+), 60 deletions(-)
diff --git a/src/route-watch.ts b/src/route-watch.ts
index 6f924909..db0dae5b 100644
--- a/src/route-watch.ts
+++ b/src/route-watch.ts
@@ -306,9 +306,7 @@ const createRouteTopologyWatcher = async ({
runtime,
delayMs: ROUTE_TOPOLOGY_RESCAN_DEBOUNCE_MS,
run: () =>
- Effect.suspend(() =>
- closed ? Effect.void : Effect.uninterruptible(runRescanEffect())
- ),
+ Effect.suspend(() => (closed ? Effect.void : runRescanEffect())),
onError,
});
diff --git a/tests/route-watch.test.ts b/tests/route-watch.test.ts
index cfacd6dc..a7e710cd 100644
--- a/tests/route-watch.test.ts
+++ b/tests/route-watch.test.ts
@@ -30,37 +30,6 @@ const createRouteTopologyWatcher = (
afterAll(() => routeWatchRuntime.dispose());
describe('route watch restart marker', () => {
- it('closes all route watchers when the plugin scope is disposed', async () => {
- const root = mkdtempSync(join(tmpdir(), 'rr-route-watch-'));
- const markerPath = join(root, 'build/.react-router-route-watch');
- const watchedDirectory = join(root, 'app');
- const closeWatcher = rstest.fn();
- const runtime = createPluginEffectRuntime();
- mkdirSync(watchedDirectory, { recursive: true });
-
- try {
- await runtime.runPromise(
- acquireRouteTopologyWatcher({
- runtime,
- watchDirectory: watchedDirectory,
- restartMarkerPath: markerPath,
- getRouteTopology: async () => new Set(['initial']),
- onError: error => {
- throw error;
- },
- watchDirectoryEntry: () => ({ close: closeWatcher }),
- })
- );
-
- await runtime.dispose();
-
- expect(closeWatcher).toHaveBeenCalledTimes(1);
- } finally {
- await runtime.dispose();
- rmSync(root, { recursive: true, force: true });
- }
- });
-
it('allows a topology callback to await watcher shutdown', async () => {
const root = mkdtempSync(join(tmpdir(), 'rr-route-watch-'));
const markerPath = join(root, 'build/.react-router-route-watch');
@@ -99,7 +68,7 @@ describe('route watch restart marker', () => {
}
});
- it('does not recreate watchers or touch the marker after close', async () => {
+ it('does not recreate watchers or touch the marker after scope disposal', async () => {
const root = mkdtempSync(join(tmpdir(), 'rr-route-watch-'));
const markerPath = join(root, 'build/.react-router-route-watch');
const watchedDirectory = join(root, 'app');
@@ -117,36 +86,39 @@ describe('route watch restart marker', () => {
});
let triggerChange!: () => void;
const closeWatcher = rstest.fn();
- const runFork = rstest.spyOn(routeWatchRuntime, 'runFork');
+ const runtime = createPluginEffectRuntime();
+ const runFork = rstest.spyOn(runtime, 'runFork');
try {
- const close = await createRouteTopologyWatcher({
- watchDirectory: watchedDirectory,
- restartMarkerPath: markerPath,
- onError: error => {
- throw error;
- },
- getRouteTopology: async () => {
- topologyReads += 1;
- if (topologyReads === 1) {
- return new Set(['initial']);
- }
- markRescanStarted();
- await rescanReleased;
- return new Set(['changed']);
- },
- watchDirectoryEntry: (_directory, onChange) => {
- triggerChange = onChange;
- return { close: closeWatcher };
- },
- });
+ await runtime.runPromise(
+ acquireRouteTopologyWatcher({
+ runtime,
+ watchDirectory: watchedDirectory,
+ restartMarkerPath: markerPath,
+ onError: error => {
+ throw error;
+ },
+ getRouteTopology: async () => {
+ topologyReads += 1;
+ if (topologyReads === 1) {
+ return new Set(['initial']);
+ }
+ markRescanStarted();
+ await rescanReleased;
+ return new Set(['changed']);
+ },
+ watchDirectoryEntry: (_directory, onChange) => {
+ triggerChange = onChange;
+ return { close: closeWatcher };
+ },
+ })
+ );
triggerChange();
await rescanStarted;
- const closePromise = close();
+ await runtime.dispose();
releaseRescan();
- await closePromise;
- await close();
+ await new Promise(resolve => setImmediate(resolve));
runFork.mockClear();
triggerChange();
@@ -154,8 +126,9 @@ describe('route watch restart marker', () => {
expect(closeWatcher).toHaveBeenCalledTimes(1);
expect(runFork).not.toHaveBeenCalled();
} finally {
- runFork.mockRestore();
releaseRescan();
+ await runtime.dispose();
+ runFork.mockRestore();
rmSync(root, { recursive: true, force: true });
}
});
From a33cdd060d362d6344681bb95e66e1e73651514c Mon Sep 17 00:00:00 2001
From: Zack Jackson <25274700+ScriptedAlchemy@users.noreply.github.com>
Date: Sat, 18 Jul 2026 07:06:50 +0000
Subject: [PATCH 34/53] refactor: simplify scoped effect cleanup
---
scripts/test-package-interop.mts | 3 +--
src/dev-runtime-artifacts.ts | 13 +++++--------
src/effect-runtime.ts | 2 --
src/parallel-route-transforms.ts | 5 +----
src/typegen.ts | 4 +---
tests/effect-runtime.test.ts | 4 ++--
6 files changed, 10 insertions(+), 21 deletions(-)
diff --git a/scripts/test-package-interop.mts b/scripts/test-package-interop.mts
index 8e363a64..03ce5818 100644
--- a/scripts/test-package-interop.mts
+++ b/scripts/test-package-interop.mts
@@ -68,8 +68,7 @@ const verifyRegistration = async (writer, reader) => {
assert(startHook, 'Expected a pre dev-server start hook');
assert(closeHook, 'Expected a pre dev-server close hook');
assert.equal(closeBuilds.length, 1);
- assert.equal(exits.length, 1);
- assert.equal(closeBuilds[0], exits[0]);
+ assert.deepEqual(closeBuilds, exits);
assert.deepEqual(
closeDevServers.filter(hook => typeof hook === 'function'),
closeBuilds
diff --git a/src/dev-runtime-artifacts.ts b/src/dev-runtime-artifacts.ts
index d5472683..20be0f4a 100644
--- a/src/dev-runtime-artifacts.ts
+++ b/src/dev-runtime-artifacts.ts
@@ -137,7 +137,7 @@ export const getEnvironmentStats = (
const startServerBuildEvaluationEffect = (
server: RsbuildDevServer,
entryName: string
-): Effect.Effect =>
+): Effect.Effect =>
tryPluginPromise(() => server.environments.node.loadBundle(entryName)).pipe(
Effect.flatMap(buildModule =>
tryPluginPromise(() =>
@@ -146,7 +146,8 @@ const startServerBuildEvaluationEffect = (
`Server entry ${JSON.stringify(entryName)}`
)
)
- )
+ ),
+ Effect.map(build => [entryName, build] as const)
);
const evaluateServerBuildsEffect = (
@@ -154,12 +155,8 @@ const evaluateServerBuildsEffect = (
entryNames: readonly string[]
): Effect.Effect =>
Effect.forEach(
- entryNames.map(entryName =>
- startServerBuildEvaluationEffect(server, entryName).pipe(
- Effect.map(build => [entryName, build] as const)
- )
- ),
- evaluation => evaluation,
+ entryNames,
+ entryName => startServerBuildEvaluationEffect(server, entryName),
{ concurrency: getCappedPluginConcurrency() }
).pipe(
Effect.map(
diff --git a/src/effect-runtime.ts b/src/effect-runtime.ts
index ea6d1184..14c19c52 100644
--- a/src/effect-runtime.ts
+++ b/src/effect-runtime.ts
@@ -92,7 +92,6 @@ type DelayedPluginTask = {
schedule(): void;
reschedule(): void;
cancelEffect(): Effect.Effect;
- cancel(): Promise;
};
export const createDelayedPluginTask = ({
@@ -173,6 +172,5 @@ export const createDelayedPluginTask = ({
schedule: start,
reschedule: reschedule,
cancelEffect: cancelEffect,
- cancel: cancel,
};
};
diff --git a/src/parallel-route-transforms.ts b/src/parallel-route-transforms.ts
index 5dfca400..21a2a4f9 100644
--- a/src/parallel-route-transforms.ts
+++ b/src/parallel-route-transforms.ts
@@ -460,10 +460,7 @@ const acquireRouteTransformExecutorWithWorkerFactory = Effect.fn(
Effect.sync(() =>
createRouteTransformExecutorWithWorkerFactory(options, createWorker)
),
- executor =>
- tryPluginPromise(() => executor.close()).pipe(
- Effect.catchAll(() => Effect.void)
- )
+ executor => Effect.ignore(tryPluginPromise(() => executor.close()))
);
});
diff --git a/src/typegen.ts b/src/typegen.ts
index 9955cc7a..6eeaa8f6 100644
--- a/src/typegen.ts
+++ b/src/typegen.ts
@@ -179,9 +179,7 @@ export const registerReactRouterTypegen = async (
Effect.orDie(tryPluginPromise(() => resolvedRunner.closeWatch()))
)
);
- await runtime.runPromise(
- Effect.acquireRelease(Effect.void, closeWatchEffect)
- );
+ await runtime.runPromise(Effect.addFinalizer(closeWatchEffect));
// Reschedule on every compile so the typegen watch only starts after a
// quiet period with no compiles. Starting it during the initial compile
// burst competes with HMR rebuilds for CPU on small machines.
diff --git a/tests/effect-runtime.test.ts b/tests/effect-runtime.test.ts
index 19cfb78b..68350a96 100644
--- a/tests/effect-runtime.test.ts
+++ b/tests/effect-runtime.test.ts
@@ -19,7 +19,7 @@ const createDelayedTaskFixture = (delayMs: number) => {
onError,
});
- return { runtime, run, onError, task };
+ return { runtime, run, task };
};
describe('effect runtime helpers', () => {
@@ -123,7 +123,7 @@ describe('effect runtime helpers', () => {
const { runtime, run, task } = createDelayedTaskFixture(1000);
task.schedule();
- await task.cancel();
+ await runtime.runPromise(task.cancelEffect());
await new Promise(resolve => setTimeout(resolve, 20));
expect(run).not.toHaveBeenCalled();
From 839a2cafa0832dc5698db316d2483dc505d62921 Mon Sep 17 00:00:00 2001
From: Zack Jackson <25274700+ScriptedAlchemy@users.noreply.github.com>
Date: Sat, 18 Jul 2026 07:24:59 +0000
Subject: [PATCH 35/53] refactor: remove nested effect runners
---
src/dev-generation.ts | 9 +++++----
src/effect-runtime.ts | 23 ++++-------------------
tests/effect-runtime.test.ts | 1 +
3 files changed, 10 insertions(+), 23 deletions(-)
diff --git a/src/dev-generation.ts b/src/dev-generation.ts
index 8071f79e..9862f9fd 100644
--- a/src/dev-generation.ts
+++ b/src/dev-generation.ts
@@ -1,6 +1,7 @@
import type { RsbuildDevServer, Rspack } from '@rsbuild/core';
import * as EffectDeferred from 'effect/Deferred';
import * as Effect from 'effect/Effect';
+import * as FiberId from 'effect/FiberId';
import type { ServerBuild } from 'react-router';
import { HMR_PATCHABLE_ROUTE_FLAGS } from './route-artifacts.js';
import {
@@ -264,7 +265,7 @@ const hasRouteManifestMetadataChanges = (
const createReadinessDeferred = (): EffectDeferred.Deferred<
CommittedGeneration,
Error
-> => Effect.runSync(EffectDeferred.make());
+> => EffectDeferred.unsafeMake(FiberId.none);
export const createReactRouterDevRuntime = ({
server,
@@ -355,7 +356,7 @@ export const createReactRouterDevRuntime = ({
if (state.kind === 'starting') {
const { readiness } = state;
state = { kind: 'failed', attemptId, error };
- Effect.runSync(EffectDeferred.fail(readiness, error));
+ EffectDeferred.unsafeDone(readiness, Effect.fail(error));
} else if (state.kind === 'ready') {
state = { ...state, pendingAttemptId: null };
}
@@ -374,7 +375,7 @@ export const createReactRouterDevRuntime = ({
if (state.kind === 'starting') {
const { readiness } = state;
state = { kind: 'ready', committed, pendingAttemptId: null };
- Effect.runSync(EffectDeferred.succeed(readiness, committed));
+ EffectDeferred.unsafeDone(readiness, Effect.succeed(committed));
} else if (state.kind === 'ready') {
state = { kind: 'ready', committed, pendingAttemptId: null };
}
@@ -654,7 +655,7 @@ export const createReactRouterDevRuntime = ({
'[rsbuild-plugin-react-router] The development server closed before a React Router build was ready.'
);
if (state.kind === 'starting') {
- Effect.runSync(EffectDeferred.fail(state.readiness, closeError));
+ EffectDeferred.unsafeDone(state.readiness, Effect.fail(closeError));
}
state = { kind: 'closed', error: closeError };
},
diff --git a/src/effect-runtime.ts b/src/effect-runtime.ts
index 14c19c52..de80d7a1 100644
--- a/src/effect-runtime.ts
+++ b/src/effect-runtime.ts
@@ -116,24 +116,6 @@ export const createDelayedPluginTask = ({
: Effect.void;
});
- const cancel = (): Promise => {
- const activeFiber = fiber;
- return activeFiber
- ? runtime.runPromise(
- Fiber.interrupt(activeFiber).pipe(
- Effect.ensuring(
- Effect.sync(() => {
- if (fiber === activeFiber) {
- fiber = undefined;
- }
- })
- ),
- Effect.asVoid
- )
- )
- : Promise.resolve();
- };
-
const start = (): void => {
if (fiber !== undefined) {
return;
@@ -162,7 +144,10 @@ export const createDelayedPluginTask = ({
const reschedule = (): void => {
if (fiber) {
- void cancel().then(start).catch(onError);
+ void runtime
+ .runPromise(Fiber.interrupt(fiber))
+ .then(start)
+ .catch(onError);
} else {
start();
}
diff --git a/tests/effect-runtime.test.ts b/tests/effect-runtime.test.ts
index 68350a96..ec600e6d 100644
--- a/tests/effect-runtime.test.ts
+++ b/tests/effect-runtime.test.ts
@@ -134,6 +134,7 @@ describe('effect runtime helpers', () => {
const { runtime, run, task } = createDelayedTaskFixture(25);
task.schedule();
+ task.reschedule();
await runtime.dispose();
await new Promise(resolve => setTimeout(resolve, 50));
From e8793b44dce424c7967572203943afeb97a3da4c Mon Sep 17 00:00:00 2001
From: Zack Jackson <25274700+ScriptedAlchemy@users.noreply.github.com>
Date: Sat, 18 Jul 2026 07:39:46 +0000
Subject: [PATCH 36/53] refactor: consolidate config effect boundary
---
src/index.ts | 6 ++++--
src/react-router-config.ts | 5 -----
tests/react-router-config.test.ts | 34 +++----------------------------
3 files changed, 7 insertions(+), 38 deletions(-)
diff --git a/src/index.ts b/src/index.ts
index ddb19a59..e288ba6a 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -25,7 +25,7 @@ import { validatePrerenderConfig } from './prerender.js';
import { runReactRouterPrerenderBuild } from './prerender-build.js';
import { runReactRouterRscPrerenderBuild } from './rsc-prerender.js';
import {
- resolveReactRouterConfig,
+ resolveReactRouterConfigEffect,
resolveRouteDiscoveryConfig,
type ResolvedReactRouterConfig,
} from './react-router-config.js';
@@ -213,7 +213,9 @@ export const pluginReactRouter = (
userAndPresetConfig,
presets: configPresets,
hasConfiguredServerModuleFormat,
- } = await resolveReactRouterConfig(reactRouterUserConfig);
+ } = await effectRuntime.runPromise(
+ resolveReactRouterConfigEffect(reactRouterUserConfig)
+ );
const {
appDirectory,
diff --git a/src/react-router-config.ts b/src/react-router-config.ts
index 82bde6b4..437d4179 100644
--- a/src/react-router-config.ts
+++ b/src/react-router-config.ts
@@ -287,8 +287,3 @@ export const resolveReactRouterConfigEffect = (
userAndPresetConfigs.serverModuleFormat !== undefined,
};
});
-
-export const resolveReactRouterConfig = (
- reactRouterUserConfig: Config
-): Promise =>
- runPluginEffect(resolveReactRouterConfigEffect(reactRouterUserConfig));
diff --git a/tests/react-router-config.test.ts b/tests/react-router-config.test.ts
index cc523c23..c26d5b89 100644
--- a/tests/react-router-config.test.ts
+++ b/tests/react-router-config.test.ts
@@ -1,12 +1,14 @@
import { describe, expect, it } from '@rstest/core';
import {
getDefaultTrailingSlashAwareDataRequests,
- resolveReactRouterConfig,
resolveReactRouterConfigEffect,
} from '../src/react-router-config';
import { runPluginEffect } from '../src/effect-runtime';
import type { Config } from '../src/react-router-config';
+const resolveReactRouterConfig = (config: Config) =>
+ runPluginEffect(resolveReactRouterConfigEffect(config));
+
describe('resolveReactRouterConfig', () => {
it('merges presets and combines buildEnd hooks', async () => {
let buildEndCalls = 0;
@@ -37,36 +39,6 @@ describe('resolveReactRouterConfig', () => {
expect(buildEndCalls).toBe(2);
});
- it('resolves presets through the Effect config path', async () => {
- let buildEndCalls = 0;
- const result = await runPluginEffect(
- resolveReactRouterConfigEffect({
- presets: [
- {
- name: 'preset-a',
- reactRouterConfig: async () => ({
- basename: '/effect-preset',
- buildEnd: async () => {
- buildEndCalls += 1;
- },
- }),
- },
- ],
- buildEnd: async () => {
- buildEndCalls += 1;
- },
- })
- );
-
- expect(result.resolved.basename).toBe('/effect-preset');
- await result.resolved.buildEnd?.({
- buildManifest: { routes: {} },
- reactRouterConfig: result.resolved,
- rsbuildConfig: {} as any,
- });
- expect(buildEndCalls).toBe(2);
- });
-
it('preserves server bundle selection in SSR mode', async () => {
const serverBundles = async () => 'bundle';
From 7cb755d6fcda94e3d2c8795d76d0b9b7d92fded9 Mon Sep 17 00:00:00 2001
From: Zack Jackson <25274700+ScriptedAlchemy@users.noreply.github.com>
Date: Sat, 18 Jul 2026 07:39:46 +0000
Subject: [PATCH 37/53] test: disable rstest worker isolation
---
rstest.config.ts | 1 +
1 file changed, 1 insertion(+)
diff --git a/rstest.config.ts b/rstest.config.ts
index 8e7ba5a1..95aacbba 100644
--- a/rstest.config.ts
+++ b/rstest.config.ts
@@ -6,6 +6,7 @@ export default defineConfig({
'tests/react-router-framework/react-router-dev/__tests__/rsc-virtual-route-modules-test.ts',
],
exclude: ['**/node_modules/**', '**/dist/**', 'examples/**'],
+ isolate: false,
testEnvironment: 'node',
setupFiles: ['./tests/setup.ts'],
});
From 2f2a25ec46dd57fa78591b64296571f87e9dbe00 Mon Sep 17 00:00:00 2001
From: Zack Jackson <25274700+ScriptedAlchemy@users.noreply.github.com>
Date: Sat, 18 Jul 2026 07:57:12 +0000
Subject: [PATCH 38/53] refactor: simplify plugin runtime disposal
---
scripts/test-package-interop.mts | 12 +++---------
src/index.ts | 8 +++-----
2 files changed, 6 insertions(+), 14 deletions(-)
diff --git a/scripts/test-package-interop.mts b/scripts/test-package-interop.mts
index 03ce5818..ce106ee4 100644
--- a/scripts/test-package-interop.mts
+++ b/scripts/test-package-interop.mts
@@ -24,8 +24,7 @@ const build = {
const collect = hooks => hook => hooks.push(hook);
const noop = () => undefined;
-const invoke = hook =>
- typeof hook === 'function' ? hook() : hook.handler?.();
+const invoke = hook => (typeof hook === 'function' ? hook() : hook.handler?.());
const loadEntryPoints = async () => {
const esm = await import('../dist/index.js');
@@ -69,10 +68,7 @@ const verifyRegistration = async (writer, reader) => {
assert(closeHook, 'Expected a pre dev-server close hook');
assert.equal(closeBuilds.length, 1);
assert.deepEqual(closeBuilds, exits);
- assert.deepEqual(
- closeDevServers.filter(hook => typeof hook === 'function'),
- closeBuilds
- );
+ assert.deepEqual(closeDevServers, [closeBuilds[0], closeHook]);
const start = startHook.handler;
const server = {
close: async () => undefined,
@@ -82,9 +78,7 @@ const verifyRegistration = async (writer, reader) => {
await start({ environments: {}, server });
const pending = reader.loadReactRouterServerBuild(server);
- await Promise.all(
- [...closeBuilds, ...closeDevServers, ...exits].map(invoke)
- );
+ await Promise.all([...closeBuilds, ...closeDevServers, ...exits].map(invoke));
await assert.rejects(pending, /closed before a React Router build was ready/);
await assert.rejects(
reader.loadReactRouterServerBuild(server),
diff --git a/src/index.ts b/src/index.ts
index e288ba6a..58ee8156 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -121,11 +121,9 @@ export const pluginReactRouter = (
async setup(api) {
const effectRuntime = createPluginEffectRuntime();
- const disposeEffectRuntime = (): Promise => effectRuntime.dispose();
-
- api.onCloseBuild(disposeEffectRuntime);
- api.onCloseDevServer(disposeEffectRuntime);
- api.onExit(disposeEffectRuntime);
+ api.onCloseBuild(effectRuntime.dispose);
+ api.onCloseDevServer(effectRuntime.dispose);
+ api.onExit(effectRuntime.dispose);
const defaultOptions = {
customServer: false,
From 264893d94f35a70e77a6d459060dcb2f30ab9532 Mon Sep 17 00:00:00 2001
From: Zack Jackson <25274700+ScriptedAlchemy@users.noreply.github.com>
Date: Sat, 18 Jul 2026 07:57:12 +0000
Subject: [PATCH 39/53] refactor: compose prerender request effects
---
src/prerender-build.ts | 96 +++++++++++++++++++-----------------------
1 file changed, 44 insertions(+), 52 deletions(-)
diff --git a/src/prerender-build.ts b/src/prerender-build.ts
index a6afdde5..727fe4a7 100644
--- a/src/prerender-build.ts
+++ b/src/prerender-build.ts
@@ -160,7 +160,7 @@ export const withBuildRequest = (
handle: (request: Request) => Promise
): Promise => runPluginEffect(createBuildRequestEffect(input, init, handle));
-const prerenderData = async ({
+const prerenderDataEffect = ({
handler,
prerenderPath,
onlyRoutes,
@@ -178,7 +178,7 @@ const prerenderData = async ({
trailingSlashAwareDataRequests: boolean;
api: PrerenderBuildApi;
requestInit?: RequestInit;
-}): Promise => {
+}) => {
const dataOutputPath = createDataRequestPath(
prerenderPath,
trailingSlashAwareDataRequests
@@ -199,7 +199,7 @@ const prerenderData = async ({
url.searchParams.set('_routes', onlyRoutes.join(','));
}
- return withBuildRequest(url, requestInit, async request => {
+ return createBuildRequestEffect(url, requestInit, async request => {
const response = await handler(request);
const data = await response.text();
@@ -227,7 +227,7 @@ const prerenderData = async ({
});
};
-const prerenderRoute = async ({
+const prerenderRouteEffect = ({
handler,
prerenderPath,
clientBuildDir,
@@ -241,9 +241,9 @@ const prerenderRoute = async ({
basename: string;
api: PrerenderBuildApi;
requestInit?: RequestInit;
-}): Promise => {
+}) => {
const normalizedPath = `${basename}${prerenderPath}/`.replace(/\/\/+/g, '/');
- await withBuildRequest(
+ return createBuildRequestEffect(
`http://localhost${normalizedPath}`,
requestInit,
async request => {
@@ -290,7 +290,7 @@ const prerenderRoute = async ({
);
};
-const prerenderResourceRoute = async ({
+const prerenderResourceRouteEffect = ({
handler,
prerenderPath,
clientBuildDir,
@@ -304,11 +304,11 @@ const prerenderResourceRoute = async ({
basename: string;
api: PrerenderBuildApi;
requestInit?: RequestInit;
-}): Promise => {
+}) => {
const normalizedPath = `${basename}${prerenderPath}/`
.replace(/\/\/+/g, '/')
.replace(/\/$/g, '');
- await withBuildRequest(
+ return createBuildRequestEffect(
`http://localhost${normalizedPath}`,
requestInit,
async request => {
@@ -480,27 +480,23 @@ const createPrerenderPathEffect = ({
if (isResourceRoute) {
if (manifestRoute.loader && routeId) {
- yield* tryPluginPromise(() =>
- prerenderData({
- handler: requestHandler,
- prerenderPath: path,
- onlyRoutes: [routeId],
- clientBuildDir,
- basename,
- trailingSlashAwareDataRequests:
- future.unstable_trailingSlashAwareDataRequests,
- api,
- })
- );
- yield* tryPluginPromise(() =>
- prerenderResourceRoute({
- handler: requestHandler,
- prerenderPath: path,
- clientBuildDir,
- basename,
- api,
- })
- );
+ yield* prerenderDataEffect({
+ handler: requestHandler,
+ prerenderPath: path,
+ onlyRoutes: [routeId],
+ clientBuildDir,
+ basename,
+ trailingSlashAwareDataRequests:
+ future.unstable_trailingSlashAwareDataRequests,
+ api,
+ });
+ yield* prerenderResourceRouteEffect({
+ handler: requestHandler,
+ prerenderPath: path,
+ clientBuildDir,
+ basename,
+ api,
+ });
} else {
yield* Effect.sync(() => {
api.logger.warn(
@@ -519,30 +515,26 @@ const createPrerenderPathEffect = ({
return build.assets?.routes?.[matchedRouteId]?.hasLoader;
});
const data = hasLoaders
- ? yield* tryPluginPromise(() =>
- prerenderData({
- handler: requestHandler,
- prerenderPath: path,
- onlyRoutes: null,
- clientBuildDir,
- basename,
- trailingSlashAwareDataRequests:
- future.unstable_trailingSlashAwareDataRequests,
- api,
- })
- )
+ ? yield* prerenderDataEffect({
+ handler: requestHandler,
+ prerenderPath: path,
+ onlyRoutes: null,
+ clientBuildDir,
+ basename,
+ trailingSlashAwareDataRequests:
+ future.unstable_trailingSlashAwareDataRequests,
+ api,
+ })
: undefined;
- yield* tryPluginPromise(() =>
- prerenderRoute({
- handler: requestHandler,
- prerenderPath: path,
- clientBuildDir,
- basename,
- api,
- requestInit: data ? createPrerenderDataRequestInit(data) : undefined,
- })
- );
+ yield* prerenderRouteEffect({
+ handler: requestHandler,
+ prerenderPath: path,
+ clientBuildDir,
+ basename,
+ api,
+ requestInit: data ? createPrerenderDataRequestInit(data) : undefined,
+ });
});
const createPrerenderDataRequestInit = (
From ca1a5f556d1dda1b84579d0142ac0b9213edced1 Mon Sep 17 00:00:00 2001
From: Zack Jackson <25274700+ScriptedAlchemy@users.noreply.github.com>
Date: Sat, 18 Jul 2026 08:20:46 +0000
Subject: [PATCH 40/53] refactor: compose route watcher startup
---
src/route-watch.ts | 305 ++++++++++++++++++--------------------
tests/route-watch.test.ts | 14 +-
2 files changed, 157 insertions(+), 162 deletions(-)
diff --git a/src/route-watch.ts b/src/route-watch.ts
index db0dae5b..e100afaf 100644
--- a/src/route-watch.ts
+++ b/src/route-watch.ts
@@ -156,7 +156,7 @@ const readRouteDirectoriesEffect = (
return walkDirectory(watchDirectory).pipe(Effect.as(directories));
};
-const createRouteTopologyWatcher = async ({
+const createRouteTopologyWatcherEffect = ({
runtime,
watchDirectory,
getRouteTopology,
@@ -165,180 +165,171 @@ const createRouteTopologyWatcher = async ({
onError,
onRouteTopologyChange,
watchDirectoryEntry: watchDirectoryOverride = defaultWatchDirectoryEntry,
-}: CreateRouteTopologyWatcherOptions) => {
- const discoveredDirectories = await runtime.runPromise(
- readRouteDirectoriesEffect(watchDirectory)
- );
- let discoveredState: RouteDirectoryState;
- try {
- discoveredState = {
+}: CreateRouteTopologyWatcherOptions) =>
+ Effect.gen(function* () {
+ const discoveredDirectories =
+ yield* readRouteDirectoriesEffect(watchDirectory);
+ const routeTopology = yield* tryPluginPromise(getRouteTopology).pipe(
+ Effect.catchAll(error => {
+ if (!initialRouteTopology) return Effect.fail(error);
+ onError(error);
+ return Effect.succeed(initialRouteTopology);
+ })
+ );
+ const discoveredState: RouteDirectoryState = {
directories: discoveredDirectories,
- routeTopology: await getRouteTopology(),
+ routeTopology,
};
- } catch (error) {
- if (!initialRouteTopology) {
- throw error;
- }
- onError(error);
- discoveredState = {
- directories: discoveredDirectories,
- routeTopology: initialRouteTopology,
+ let state = {
+ ...discoveredState,
+ routeTopology: initialRouteTopology ?? discoveredState.routeTopology,
};
- }
- let state = {
- ...discoveredState,
- routeTopology: initialRouteTopology ?? discoveredState.routeTopology,
- };
- let closed = false;
- const directoryWatchers = new Map();
+ let closed = false;
+ const directoryWatchers = new Map();
- const touchRestartMarkerEffect = (): Effect.Effect =>
- tryPluginPromise(() =>
- mkdir(dirname(restartMarkerPath), { recursive: true })
- ).pipe(
- Effect.zipRight(
- tryPluginPromise(() => writeFile(restartMarkerPath, String(Date.now())))
- )
- );
+ const touchRestartMarkerEffect = (): Effect.Effect =>
+ tryPluginPromise(() =>
+ mkdir(dirname(restartMarkerPath), { recursive: true })
+ ).pipe(
+ Effect.zipRight(
+ tryPluginPromise(() =>
+ writeFile(restartMarkerPath, String(Date.now()))
+ )
+ )
+ );
- const closeRemovedDirectoryWatchers = (
- nextDirectories: Set
- ): void => {
- for (const [directory, watcher] of directoryWatchers) {
- if (!nextDirectories.has(directory)) {
- watcher.close();
- directoryWatchers.delete(directory);
+ const closeRemovedDirectoryWatchers = (
+ nextDirectories: Set
+ ): void => {
+ for (const [directory, watcher] of directoryWatchers) {
+ if (!nextDirectories.has(directory)) {
+ watcher.close();
+ directoryWatchers.delete(directory);
+ }
}
- }
- };
+ };
- const watchNewDirectories = (nextDirectories: Set): void => {
- for (const directory of nextDirectories) {
- if (directoryWatchers.has(directory)) {
- continue;
- }
- try {
- let watcher: DirectoryWatcher;
- watcher = watchDirectoryOverride(
- directory,
- () => {
- if (!closed) rescanTask.reschedule();
- },
- error => {
- if (closed) return;
- if (directoryWatchers.get(directory) === watcher) {
- watcher.close();
- directoryWatchers.delete(directory);
+ const watchNewDirectories = (nextDirectories: Set): void => {
+ for (const directory of nextDirectories) {
+ if (directoryWatchers.has(directory)) {
+ continue;
+ }
+ try {
+ let watcher: DirectoryWatcher;
+ watcher = watchDirectoryOverride(
+ directory,
+ () => {
+ if (!closed) rescanTask.reschedule();
+ },
+ error => {
+ if (closed) return;
+ if (directoryWatchers.get(directory) === watcher) {
+ watcher.close();
+ directoryWatchers.delete(directory);
+ }
+ onError(error);
}
- onError(error);
- }
- );
- directoryWatchers.set(directory, watcher);
- } catch (error) {
- onError(error);
+ );
+ directoryWatchers.set(directory, watcher);
+ } catch (error) {
+ onError(error);
+ }
}
- }
- };
+ };
- const syncDirectoryWatchers = (nextDirectories: Set): void => {
- closeRemovedDirectoryWatchers(nextDirectories);
- watchNewDirectories(nextDirectories);
- };
+ const syncDirectoryWatchers = (nextDirectories: Set): void => {
+ closeRemovedDirectoryWatchers(nextDirectories);
+ watchNewDirectories(nextDirectories);
+ };
- const applyNextStateEffect = (
- nextState: RouteDirectoryState
- ): Effect.Effect =>
- Effect.suspend(() => {
- if (closed) return Effect.void;
- syncDirectoryWatchers(nextState.directories);
- if (!areSetsEqual(state.routeTopology, nextState.routeTopology)) {
- if (onRouteTopologyChange) {
- // This is a notification boundary, not part of the rescan
- // transaction. A custom-server callback may close this watcher while
- // replacing its compiler, so awaiting it here would deadlock close().
- state = nextState;
- return Effect.sync(() => {
- try {
- void Promise.resolve(onRouteTopologyChange()).catch(onError);
- } catch (error) {
- onError(error);
- }
- });
- }
- return touchRestartMarkerEffect().pipe(
- Effect.zipRight(
- Effect.sync(() => {
- if (!closed) {
- state = nextState;
+ const applyNextStateEffect = (
+ nextState: RouteDirectoryState
+ ): Effect.Effect =>
+ Effect.suspend(() => {
+ if (closed) return Effect.void;
+ syncDirectoryWatchers(nextState.directories);
+ if (!areSetsEqual(state.routeTopology, nextState.routeTopology)) {
+ if (onRouteTopologyChange) {
+ // This is a notification boundary, not part of the rescan
+ // transaction. A custom-server callback may close this watcher while
+ // replacing its compiler, so awaiting it here would deadlock close().
+ state = nextState;
+ return Effect.sync(() => {
+ try {
+ void Promise.resolve(onRouteTopologyChange()).catch(onError);
+ } catch (error) {
+ onError(error);
}
- })
- )
- );
- }
- state = nextState;
- return Effect.void;
- });
+ });
+ }
+ return touchRestartMarkerEffect().pipe(
+ Effect.zipRight(
+ Effect.sync(() => {
+ if (!closed) {
+ state = nextState;
+ }
+ })
+ )
+ );
+ }
+ state = nextState;
+ return Effect.void;
+ });
- const runRescanEffect = (): Effect.Effect => {
- let nextDirectories: Set | undefined;
- return Effect.gen(function* () {
- if (closed) return;
- nextDirectories = yield* readRouteDirectoriesEffect(watchDirectory);
- if (closed) return;
- const nextState = {
- directories: nextDirectories,
- routeTopology: yield* tryPluginPromise(getRouteTopology),
- };
- if (closed) return;
- yield* applyNextStateEffect(nextState);
- }).pipe(
- Effect.catchAll(error =>
- Effect.sync(() => {
- if (nextDirectories && !closed)
- syncDirectoryWatchers(nextDirectories);
- onError(error);
- })
- )
- );
- };
+ const runRescanEffect = (): Effect.Effect => {
+ let nextDirectories: Set | undefined;
+ return Effect.gen(function* () {
+ if (closed) return;
+ nextDirectories = yield* readRouteDirectoriesEffect(watchDirectory);
+ if (closed) return;
+ const nextState = {
+ directories: nextDirectories,
+ routeTopology: yield* tryPluginPromise(getRouteTopology),
+ };
+ if (closed) return;
+ yield* applyNextStateEffect(nextState);
+ }).pipe(
+ Effect.catchAll(error =>
+ Effect.sync(() => {
+ if (nextDirectories && !closed)
+ syncDirectoryWatchers(nextDirectories);
+ onError(error);
+ })
+ )
+ );
+ };
- const rescanTask = createDelayedPluginTask({
- runtime,
- delayMs: ROUTE_TOPOLOGY_RESCAN_DEBOUNCE_MS,
- run: () =>
- Effect.suspend(() => (closed ? Effect.void : runRescanEffect())),
- onError,
- });
+ const rescanTask = createDelayedPluginTask({
+ runtime,
+ delayMs: ROUTE_TOPOLOGY_RESCAN_DEBOUNCE_MS,
+ run: () =>
+ Effect.suspend(() => (closed ? Effect.void : runRescanEffect())),
+ onError,
+ });
- try {
- await runtime.runPromise(applyNextStateEffect(discoveredState));
- } catch (error) {
- onError(error);
- }
+ const closeEffect = (): Effect.Effect =>
+ tryPluginPromise(() => {
+ if (closed) return;
+ closed = true;
+ for (const watcher of directoryWatchers.values()) {
+ watcher.close();
+ }
+ directoryWatchers.clear();
+ }).pipe(Effect.ensuring(rescanTask.cancelEffect()));
- const closeEffect = (): Effect.Effect =>
- tryPluginPromise(() => {
- if (closed) return;
- closed = true;
- for (const watcher of directoryWatchers.values()) {
- watcher.close();
- }
- directoryWatchers.clear();
- }).pipe(Effect.ensuring(rescanTask.cancelEffect()));
+ yield* applyNextStateEffect(discoveredState).pipe(
+ Effect.catchAll(error => Effect.sync(() => onError(error))),
+ Effect.onInterrupt(() => Effect.ignore(closeEffect()))
+ );
- const close = () => runtime.runPromise(closeEffect());
- return Object.assign(close, { closeEffect });
-};
+ return closeEffect;
+ });
export const acquireRouteTopologyWatcher = (
options: CreateRouteTopologyWatcherOptions
-): Effect.Effect<() => Promise, Error, Scope.Scope> =>
- Effect.acquireRelease(
- tryPluginPromise(() => createRouteTopologyWatcher(options)),
- close =>
- close
- .closeEffect()
- .pipe(
- Effect.catchAll(error => Effect.sync(() => options.onError(error)))
- )
+): Effect.Effect<() => Effect.Effect, Error, Scope.Scope> =>
+ Effect.acquireRelease(createRouteTopologyWatcherEffect(options), close =>
+ close().pipe(
+ Effect.catchAll(error => Effect.sync(() => options.onError(error)))
+ )
);
diff --git a/tests/route-watch.test.ts b/tests/route-watch.test.ts
index a7e710cd..2abb4178 100644
--- a/tests/route-watch.test.ts
+++ b/tests/route-watch.test.ts
@@ -20,12 +20,14 @@ import {
} from '../src/route-watch';
const routeWatchRuntime = createPluginEffectRuntime();
-const createRouteTopologyWatcher = (
+const createRouteTopologyWatcher = async (
options: Omit
-) =>
- routeWatchRuntime.runPromise(
+) => {
+ const closeEffect = await routeWatchRuntime.runPromise(
acquireRouteTopologyWatcher({ runtime: routeWatchRuntime, ...options })
);
+ return () => routeWatchRuntime.runPromise(closeEffect());
+};
afterAll(() => routeWatchRuntime.dispose());
@@ -150,7 +152,7 @@ describe('route watch restart marker', () => {
mkdirSync(watchedDirectory, { recursive: true });
try {
- const close = await runtime.runPromise(
+ const closeEffect = await runtime.runPromise(
acquireRouteTopologyWatcher({
runtime,
watchDirectory: watchedDirectory,
@@ -175,7 +177,9 @@ describe('route watch restart marker', () => {
const fiber = rescanFiber;
expect(Option.isNone(await runtime.runPromise(fiber.poll))).toBe(true);
- await expect(close()).rejects.toThrow(closeError.message);
+ await expect(runtime.runPromise(closeEffect())).rejects.toThrow(
+ closeError.message
+ );
expect(Option.isSome(await runtime.runPromise(fiber.poll))).toBe(true);
} finally {
From 4b2b1728fb78a305dc454ca57e3c14d8044e789d Mon Sep 17 00:00:00 2001
From: Zack Jackson <25274700+ScriptedAlchemy@users.noreply.github.com>
Date: Sat, 18 Jul 2026 08:24:01 +0000
Subject: [PATCH 41/53] refactor: compose rsc prerender requests
---
src/rsc-prerender.ts | 36 ++++++++++++++++++------------------
1 file changed, 18 insertions(+), 18 deletions(-)
diff --git a/src/rsc-prerender.ts b/src/rsc-prerender.ts
index ce1f572f..3d8c7b14 100644
--- a/src/rsc-prerender.ts
+++ b/src/rsc-prerender.ts
@@ -3,15 +3,16 @@ import { mkdir, writeFile } from 'node:fs/promises';
import { pathToFileURL } from 'node:url';
import type { RsbuildPluginAPI } from '@rsbuild/core';
import { dirname, relative, resolve } from 'pathe';
+import * as Effect from 'effect/Effect';
import { PLUGIN_NAME, SPA_FALLBACK_HTML_FILE } from './constants.js';
import {
+ createBuildRequestEffect,
createBoundedPrerenderTasksEffect,
- withBuildRequest,
} from './prerender-build.js';
import { normalizeAssetPrefix } from './plugin-utils.js';
import { getPrerenderConcurrency } from './prerender.js';
import type { Config } from './react-router-config.js';
-import { runPluginEffect, tryPluginPromise } from './effect-runtime.js';
+import { runPluginEffect } from './effect-runtime.js';
/**
* RSC-mode prerendering.
@@ -205,7 +206,7 @@ const assertPrerenderableResponse = (
);
};
-const prerenderRscUrl = async ({
+const prerenderRscUrlEffect = ({
api,
artifactPath,
clientBuildDir,
@@ -217,8 +218,8 @@ const prerenderRscUrl = async ({
clientBuildDir: string;
handler: RscRequestHandler;
url: URL;
-}): Promise =>
- withBuildRequest(url, undefined, async request => {
+}): Effect.Effect =>
+ createBuildRequestEffect(url, undefined, async request => {
const response = await handler(request);
const pathname = url.pathname;
const outputPathname = artifactPath ?? pathname;
@@ -236,7 +237,7 @@ const prerenderRscUrl = async ({
status: response.status,
}),
});
- return;
+ return Effect.void;
}
const isHtml = Boolean(
@@ -260,7 +261,7 @@ const prerenderRscUrl = async ({
contents: flightData,
});
}
- return;
+ return Effect.void;
}
// Non-HTML response (e.g. a resource route): emit the raw payload at the
@@ -275,7 +276,7 @@ const prerenderRscUrl = async ({
if (!pathname.endsWith('.rsc')) {
const dataUrl = new URL(url);
dataUrl.pathname += '.rsc';
- await prerenderRscUrl({
+ return prerenderRscUrlEffect({
api,
artifactPath: `${outputPathname}.rsc`,
clientBuildDir,
@@ -283,7 +284,8 @@ const prerenderRscUrl = async ({
url: dataUrl,
});
}
- });
+ return Effect.void;
+ }).pipe(Effect.flatten);
export const runReactRouterRscPrerenderBuild = async (
options: RunReactRouterRscPrerenderBuildOptions
@@ -341,15 +343,13 @@ export const runReactRouterRscPrerenderBuild = async (
prerenderRequests,
getPrerenderConcurrency(prerenderConfig),
prerenderRequest =>
- tryPluginPromise(() =>
- prerenderRscUrl({
- api,
- artifactPath: prerenderRequest.artifactPath,
- clientBuildDir,
- handler,
- url: new URL(`http://localhost${prerenderRequest.requestPath}`),
- })
- )
+ prerenderRscUrlEffect({
+ api,
+ artifactPath: prerenderRequest.artifactPath,
+ clientBuildDir,
+ handler,
+ url: new URL(`http://localhost${prerenderRequest.requestPath}`),
+ })
)
);
} finally {
From 09da4533c47807c51a1c76f7b8d7f1a9d56aa179 Mon Sep 17 00:00:00 2001
From: Zack Jackson <25274700+ScriptedAlchemy@users.noreply.github.com>
Date: Sat, 18 Jul 2026 08:31:30 +0000
Subject: [PATCH 42/53] refactor: simplify plugin fiber context
---
src/effect-runtime.ts | 17 ++++++-----------
1 file changed, 6 insertions(+), 11 deletions(-)
diff --git a/src/effect-runtime.ts b/src/effect-runtime.ts
index de80d7a1..fea7a8b0 100644
--- a/src/effect-runtime.ts
+++ b/src/effect-runtime.ts
@@ -11,16 +11,11 @@ import * as ManagedRuntime from 'effect/ManagedRuntime';
import * as Option from 'effect/Option';
import * as Scope from 'effect/Scope';
-interface PluginFibers {
- readonly _: unique symbol;
-}
-
-const PluginFibers: Context.Tag =
- Context.GenericTag(
- 'rsbuild-plugin-react-router/PluginFibers'
- );
+const PluginFibers = Context.GenericTag(
+ 'rsbuild-plugin-react-router/PluginFibers'
+);
-type PluginRuntimeContext = Scope.Scope | PluginFibers;
+type PluginRuntimeContext = Scope.Scope | FiberSet.FiberSet;
const PluginRuntimeLive = Layer.scopedContext(
Effect.gen(function* () {
@@ -155,7 +150,7 @@ export const createDelayedPluginTask = ({
return {
schedule: start,
- reschedule: reschedule,
- cancelEffect: cancelEffect,
+ reschedule,
+ cancelEffect,
};
};
From 3f814ed6f94b9b172e9bcae825e84fb9724c74a6 Mon Sep 17 00:00:00 2001
From: Zack Jackson <25274700+ScriptedAlchemy@users.noreply.github.com>
Date: Sat, 18 Jul 2026 08:42:23 +0000
Subject: [PATCH 43/53] refactor: supervise delayed task rescheduling
---
src/effect-runtime.ts | 7 +++----
tests/effect-runtime.test.ts | 1 +
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/effect-runtime.ts b/src/effect-runtime.ts
index fea7a8b0..1a78d89a 100644
--- a/src/effect-runtime.ts
+++ b/src/effect-runtime.ts
@@ -139,10 +139,9 @@ export const createDelayedPluginTask = ({
const reschedule = (): void => {
if (fiber) {
- void runtime
- .runPromise(Fiber.interrupt(fiber))
- .then(start)
- .catch(onError);
+ runtime.runFork(
+ Fiber.interrupt(fiber).pipe(Effect.ensuring(Effect.sync(start)))
+ );
} else {
start();
}
diff --git a/tests/effect-runtime.test.ts b/tests/effect-runtime.test.ts
index ec600e6d..7f45708c 100644
--- a/tests/effect-runtime.test.ts
+++ b/tests/effect-runtime.test.ts
@@ -123,6 +123,7 @@ describe('effect runtime helpers', () => {
const { runtime, run, task } = createDelayedTaskFixture(1000);
task.schedule();
+ task.reschedule();
await runtime.runPromise(task.cancelEffect());
await new Promise(resolve => setTimeout(resolve, 20));
From 60d6438bed463561dfe858c68697034b438bd5c2 Mon Sep 17 00:00:00 2001
From: Zack Jackson <25274700+ScriptedAlchemy@users.noreply.github.com>
Date: Sat, 18 Jul 2026 08:42:23 +0000
Subject: [PATCH 44/53] refactor: inline prerender task orchestration
---
src/prerender-build.ts | 52 ++++++++++++++----------------------------
1 file changed, 17 insertions(+), 35 deletions(-)
diff --git a/src/prerender-build.ts b/src/prerender-build.ts
index 727fe4a7..7c6e18e2 100644
--- a/src/prerender-build.ts
+++ b/src/prerender-build.ts
@@ -550,35 +550,6 @@ const createPrerenderDataRequestInit = (
: undefined;
};
-const runPrerenderPaths = async ({
- build,
- requestHandler,
- clientBuildDir,
- options,
-}: {
- build: PrerenderServerBuild;
- requestHandler: (request: Request) => Promise;
- clientBuildDir: string;
- options: RunReactRouterPrerenderBuildOptions;
-}): Promise => {
- const { prerenderConfig, prerenderPaths } = options;
- const buildRoutes = createPrerenderRoutes(build.routes);
- const concurrency = getPrerenderConcurrency(prerenderConfig);
-
- await runPluginEffect(
- createBoundedPrerenderTasksEffect(prerenderPaths, concurrency, path =>
- createPrerenderPathEffect({
- path,
- build,
- buildRoutes,
- requestHandler,
- clientBuildDir,
- options,
- })
- )
- );
-};
-
export const runReactRouterPrerenderBuild = async (
options: RunReactRouterPrerenderBuildOptions
): Promise => {
@@ -592,6 +563,7 @@ export const runReactRouterPrerenderBuild = async (
serverBuildFile,
ssr,
isPrerenderEnabled,
+ prerenderConfig,
prerenderPaths,
routes,
latestBrowserManifest,
@@ -667,12 +639,22 @@ export const runReactRouterPrerenderBuild = async (
);
}
- await runPrerenderPaths({
- build,
- requestHandler,
- clientBuildDir,
- options,
- });
+ const buildRoutes = createPrerenderRoutes(build.routes);
+ await runPluginEffect(
+ createBoundedPrerenderTasksEffect(
+ prerenderPaths,
+ getPrerenderConcurrency(prerenderConfig),
+ path =>
+ createPrerenderPathEffect({
+ path,
+ build,
+ buildRoutes,
+ requestHandler,
+ clientBuildDir,
+ options,
+ })
+ )
+ );
}
if (!ssr) {
From e56435ea4e35ed81e1f6c387b230d37b3a62b311 Mon Sep 17 00:00:00 2001
From: Zack Jackson <25274700+ScriptedAlchemy@users.noreply.github.com>
Date: Sat, 18 Jul 2026 08:42:23 +0000
Subject: [PATCH 45/53] refactor: simplify merged build end hooks
---
src/react-router-config.ts | 17 +++++++----------
tests/react-router-config.test.ts | 8 ++++----
2 files changed, 11 insertions(+), 14 deletions(-)
diff --git a/src/react-router-config.ts b/src/react-router-config.ts
index 437d4179..c969b270 100644
--- a/src/react-router-config.ts
+++ b/src/react-router-config.ts
@@ -6,7 +6,7 @@ import type { NormalizedConfig } from '@rsbuild/core';
import type { RouteConfigEntry } from '@react-router/dev/routes';
import * as Effect from 'effect/Effect';
import { getCappedPluginConcurrency } from './concurrency.js';
-import { runPluginEffect, tryPluginPromise } from './effect-runtime.js';
+import { normalizeEffectError, tryPluginPromise } from './effect-runtime.js';
import { getPackageVersion, parseVersionMajorMinor } from './plugin-utils.js';
import type { PrerenderConfigObject, PrerenderPathsConfig } from './types.js';
@@ -158,15 +158,12 @@ const mergeReactRouterConfig = (...configs: Config[]): Config => {
buildEnd: async (
...args: Parameters>
) => {
- await runPluginEffect(
- Effect.all(
- [
- tryPluginPromise(() => configA.buildEnd?.(...args)),
- tryPluginPromise(() => configB.buildEnd?.(...args)),
- ],
- { discard: true }
- )
- );
+ try {
+ await configA.buildEnd?.(...args);
+ await configB.buildEnd?.(...args);
+ } catch (cause) {
+ throw normalizeEffectError(cause);
+ }
},
}
: {}),
diff --git a/tests/react-router-config.test.ts b/tests/react-router-config.test.ts
index c26d5b89..e6bb44a2 100644
--- a/tests/react-router-config.test.ts
+++ b/tests/react-router-config.test.ts
@@ -11,7 +11,7 @@ const resolveReactRouterConfig = (config: Config) =>
describe('resolveReactRouterConfig', () => {
it('merges presets and combines buildEnd hooks', async () => {
- let buildEndCalls = 0;
+ const buildEndCalls: string[] = [];
const result = await resolveReactRouterConfig({
presets: [
{
@@ -20,13 +20,13 @@ describe('resolveReactRouterConfig', () => {
basename: '/preset',
future: { v8_middleware: true },
buildEnd: async () => {
- buildEndCalls += 1;
+ buildEndCalls.push('preset');
},
}),
},
],
buildEnd: async () => {
- buildEndCalls += 1;
+ buildEndCalls.push('user');
},
});
@@ -36,7 +36,7 @@ describe('resolveReactRouterConfig', () => {
reactRouterConfig: result.resolved,
rsbuildConfig: {} as any,
});
- expect(buildEndCalls).toBe(2);
+ expect(buildEndCalls).toEqual(['preset', 'user']);
});
it('preserves server bundle selection in SSR mode', async () => {
From fbe35ce21f1cb382d97e0862b9b2a5e9d94dc2ed Mon Sep 17 00:00:00 2001
From: Zack Jackson <25274700+ScriptedAlchemy@users.noreply.github.com>
Date: Tue, 21 Jul 2026 01:19:07 +0000
Subject: [PATCH 46/53] test: update rstest and restore isolation
---
package.json | 4 +-
pnpm-lock.yaml | 671 ++++++++++++++++++++++++++++++++++++++---------
rstest.config.ts | 1 -
3 files changed, 544 insertions(+), 132 deletions(-)
diff --git a/package.json b/package.json
index 1a78aa66..0aeaba27 100644
--- a/package.json
+++ b/package.json
@@ -125,8 +125,8 @@
"@rsbuild/plugin-tailwindcss": "2.0.3",
"@rslib/core": "^0.22.1",
"@rspack/core": "2.1.0",
- "@rstest/core": "^0.8.1",
- "@rstest/coverage-istanbul": "^0.2.0",
+ "@rstest/core": "^0.11.2",
+ "@rstest/coverage-istanbul": "^0.11.2",
"@swc/helpers": "^0.5.23",
"@types/fs-extra": "11.0.4",
"@types/jsesc": "^3.0.3",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 13f81d21..e06820bc 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -19,7 +19,7 @@ importers:
version: 0.13.3
'@rspack/plugin-react-refresh':
specifier: ^2.0.2
- version: 2.0.2(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(react-refresh@0.18.0)
+ version: 2.0.2(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(react-refresh@0.18.0)
execa:
specifier: ^9.6.1
version: 9.6.1
@@ -86,7 +86,7 @@ importers:
version: 1.1.3(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(webpack@5.108.3(lightningcss@1.32.0))
'@rsbuild/plugin-react':
specifier: 2.1.0
- version: 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))
+ version: 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))
'@rsbuild/plugin-sass':
specifier: 1.5.3
version: 1.5.3(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))
@@ -103,11 +103,11 @@ importers:
specifier: 2.1.0
version: 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)
'@rstest/core':
- specifier: ^0.8.1
- version: 0.8.1(jsdom@27.4.0(@noble/hashes@2.2.0))
+ specifier: ^0.11.2
+ version: 0.11.2(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0)(jsdom@27.4.0(@noble/hashes@2.2.0))
'@rstest/coverage-istanbul':
- specifier: ^0.2.0
- version: 0.2.0(@rstest/core@0.8.1(jsdom@27.4.0(@noble/hashes@2.2.0)))
+ specifier: ^0.11.2
+ version: 0.11.2(@rstest/core@0.11.2(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0)(jsdom@27.4.0(@noble/hashes@2.2.0)))
'@swc/helpers':
specifier: ^0.5.23
version: 0.5.23
@@ -279,13 +279,13 @@ importers:
version: 2.0.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))
'@rsbuild/plugin-react':
specifier: 2.1.0
- version: 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))
+ version: 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))
'@rsbuild/plugin-svgr':
specifier: 2.0.4
- version: 2.0.4(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(typescript@6.0.1-rc)
+ version: 2.0.4(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(typescript@6.0.1-rc)
'@rsbuild/plugin-tailwindcss':
specifier: 2.0.3
- version: 2.0.3(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
+ version: 2.0.3(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
'@types/babel__core':
specifier: 7.20.5
version: 7.20.5
@@ -361,7 +361,7 @@ importers:
version: 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)
'@rsbuild/plugin-react':
specifier: ^2.1.0
- version: 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))
+ version: 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))
'@types/node':
specifier: ^25.0.10
version: 25.0.10
@@ -416,7 +416,7 @@ importers:
version: 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)
'@rsbuild/plugin-react':
specifier: ^2.1.0
- version: 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))
+ version: 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))
'@tailwindcss/postcss':
specifier: ^4.1.18
version: 4.3.2
@@ -471,16 +471,16 @@ importers:
version: 1.61.1
'@react-router/dev':
specifier: ^7.13.0
- version: 7.14.0(@react-router/serve@7.14.0(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3))(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0))(wrangler@4.107.0(@cloudflare/workers-types@4.20260702.1))
+ version: 7.14.0(@react-router/serve@7.14.0(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3))(@types/node@25.9.5)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.9.5)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0))(wrangler@4.107.0(@cloudflare/workers-types@4.20260702.1))
'@rsbuild/core':
specifier: 2.1.0
version: 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)
'@rsbuild/plugin-react':
specifier: ^2.1.0
- version: 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))
+ version: 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))
'@rsdoctor/rspack-plugin':
specifier: ^1.5.13
- version: 1.5.17(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
+ version: 1.5.17(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
'@tailwindcss/postcss':
specifier: ^4.1.18
version: 4.3.2
@@ -541,10 +541,10 @@ importers:
version: 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)
'@rsbuild/plugin-less':
specifier: ^1.6.4
- version: 1.6.4(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
+ version: 1.6.4(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
'@rsbuild/plugin-react':
specifier: ^2.1.0
- version: 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))
+ version: 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))
'@rsbuild/plugin-sass':
specifier: ^1.5.3
version: 1.5.3(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))
@@ -664,7 +664,7 @@ importers:
version: 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)
'@rsbuild/plugin-react':
specifier: 2.1.0
- version: 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))
+ version: 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))
'@sentry/node':
specifier: 10.37.0
version: 10.37.0
@@ -971,13 +971,13 @@ importers:
version: 0.6.1
'@module-federation/enhanced':
specifier: 2.5.1
- version: 2.5.1(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.3)(webpack@5.108.3(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16))
+ version: 2.5.1(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.3)(webpack@5.108.3(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16))
'@module-federation/node':
specifier: 2.7.44
- version: 2.7.44(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(typescript@5.9.3)(webpack@5.108.3(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16))
+ version: 2.7.44(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(typescript@5.9.3)(webpack@5.108.3(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16))
'@module-federation/rsbuild-plugin':
specifier: 2.5.1
- version: 2.5.1(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.3)(webpack@5.108.3(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16))
+ version: 2.5.1(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.3)(webpack@5.108.3(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16))
'@nasa-gcn/remix-seo':
specifier: 2.0.1
version: 2.0.1(@remix-run/react@2.17.5(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3))(@remix-run/server-runtime@2.17.4(typescript@5.9.3))
@@ -1034,7 +1034,7 @@ importers:
version: 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)
'@rsbuild/plugin-react':
specifier: 2.1.0
- version: 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))
+ version: 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))
'@sentry/node':
specifier: 10.37.0
version: 10.37.0
@@ -1335,13 +1335,13 @@ importers:
version: 0.6.1
'@module-federation/enhanced':
specifier: 2.5.1
- version: 2.5.1(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.3)(webpack@5.108.3(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16))
+ version: 2.5.1(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.3)(webpack@5.108.3(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16))
'@module-federation/node':
specifier: 2.7.44
- version: 2.7.44(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(typescript@5.9.3)(webpack@5.108.3(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16))
+ version: 2.7.44(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(typescript@5.9.3)(webpack@5.108.3(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16))
'@module-federation/rsbuild-plugin':
specifier: 2.5.1
- version: 2.5.1(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.3)(webpack@5.108.3(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16))
+ version: 2.5.1(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.3)(webpack@5.108.3(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16))
'@nasa-gcn/remix-seo':
specifier: 2.0.1
version: 2.0.1(@remix-run/react@2.17.5(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3))(@remix-run/server-runtime@2.17.4(typescript@5.9.3))
@@ -1398,7 +1398,7 @@ importers:
version: 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)
'@rsbuild/plugin-react':
specifier: 2.1.0
- version: 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))
+ version: 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))
'@sentry/node':
specifier: 10.37.0
version: 10.37.0
@@ -1703,7 +1703,7 @@ importers:
version: 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)
'@rsbuild/plugin-react':
specifier: ^2.1.0
- version: 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))
+ version: 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))
'@tailwindcss/postcss':
specifier: ^4.1.18
version: 4.3.2
@@ -1779,7 +1779,7 @@ importers:
version: 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)
'@rsbuild/plugin-react':
specifier: 2.1.0
- version: 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))
+ version: 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))
'@types/node':
specifier: ^25.0.10
version: 25.0.10
@@ -1824,7 +1824,7 @@ importers:
version: 8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
react-server-dom-rspack:
specifier: 0.0.2
- version: 0.0.2(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ version: 0.0.2(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
devDependencies:
'@playwright/test':
specifier: ^1.58.0
@@ -1837,7 +1837,7 @@ importers:
version: 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)
'@rsbuild/plugin-react':
specifier: 2.1.0
- version: 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))
+ version: 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))
'@types/node':
specifier: ^25.0.10
version: 25.0.10
@@ -1855,7 +1855,7 @@ importers:
version: link:../..
rsbuild-plugin-rsc:
specifier: ^0.1.1
- version: 0.1.1(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(react-server-dom-rspack@0.0.2(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(react-dom@19.2.7(react@19.2.7))(react@19.2.7))
+ version: 0.1.1(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(react-server-dom-rspack@0.0.2(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(react-dom@19.2.7(react@19.2.7))(react@19.2.7))
typescript:
specifier: ^5.9.3
version: 5.9.3
@@ -1895,7 +1895,7 @@ importers:
version: 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)
'@rsbuild/plugin-react':
specifier: ^2.1.0
- version: 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))
+ version: 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))
'@tailwindcss/postcss':
specifier: ^4.1.18
version: 4.3.2
@@ -2018,7 +2018,7 @@ importers:
version: 8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
react-server-dom-rspack:
specifier: 0.0.2
- version: 0.0.2(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ version: 0.0.2(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
semver:
specifier: ^7.8.1
version: 7.8.5
@@ -2097,22 +2097,22 @@ importers:
version: 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)
'@rsbuild/plugin-less':
specifier: 1.6.4
- version: 1.6.4(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
+ version: 1.6.4(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
'@rsbuild/plugin-mdx':
specifier: ^1.1.3
version: 1.1.3(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(webpack@5.108.3(lightningcss@1.32.0))
'@rsbuild/plugin-react':
specifier: 2.1.0
- version: 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))
+ version: 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))
'@rsbuild/plugin-sass':
specifier: 1.5.3
version: 1.5.3(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))
'@rsbuild/plugin-svgr':
specifier: 2.0.4
- version: 2.0.4(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(typescript@5.9.3)
+ version: 2.0.4(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(typescript@5.9.3)
'@rsbuild/plugin-tailwindcss':
specifier: 2.0.3
- version: 2.0.3(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
+ version: 2.0.3(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
'@types/node':
specifier: ^22.19.19
version: 22.20.0
@@ -2175,7 +2175,7 @@ importers:
version: 8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
react-server-dom-rspack:
specifier: 0.0.2
- version: 0.0.2(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ version: 0.0.2(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
devDependencies:
'@mdx-js/rollup':
specifier: ^3.1.1
@@ -2194,22 +2194,22 @@ importers:
version: 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)
'@rsbuild/plugin-less':
specifier: 1.6.4
- version: 1.6.4(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
+ version: 1.6.4(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
'@rsbuild/plugin-mdx':
specifier: ^1.1.3
version: 1.1.3(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(webpack@5.108.3(lightningcss@1.32.0))
'@rsbuild/plugin-react':
specifier: 2.1.0
- version: 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))
+ version: 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))
'@rsbuild/plugin-sass':
specifier: 1.5.3
version: 1.5.3(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))
'@rsbuild/plugin-svgr':
specifier: 2.0.4
- version: 2.0.4(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(typescript@5.9.3)
+ version: 2.0.4(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(typescript@5.9.3)
'@rsbuild/plugin-tailwindcss':
specifier: 2.0.3
- version: 2.0.3(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
+ version: 2.0.3(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
'@types/express':
specifier: ^5.0.0
version: 5.0.6
@@ -2236,7 +2236,7 @@ importers:
version: link:../../../../..
rsbuild-plugin-rsc:
specifier: ^0.1.1
- version: 0.1.1(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(react-server-dom-rspack@0.0.2(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(react-dom@19.2.7(react@19.2.7))(react@19.2.7))
+ version: 0.1.1(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(react-server-dom-rspack@0.0.2(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(react-dom@19.2.7(react@19.2.7))(react@19.2.7))
tailwindcss:
specifier: 4.3.2
version: 4.3.2
@@ -2269,14 +2269,14 @@ importers:
version: 8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
react-server-dom-rspack:
specifier: 0.0.2
- version: 0.0.2(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ version: 0.0.2(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
devDependencies:
'@rsbuild/core':
specifier: 2.1.0
version: 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)
'@rsbuild/plugin-react':
specifier: 2.1.0
- version: 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))
+ version: 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))
'@types/express':
specifier: ^5.0.0
version: 5.0.6
@@ -2297,7 +2297,7 @@ importers:
version: link:../../../../..
rsbuild-plugin-rsc:
specifier: ^0.1.1
- version: 0.1.1(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(react-server-dom-rspack@0.0.2(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(react-dom@19.2.7(react@19.2.7))(react@19.2.7))
+ version: 0.1.1(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(react-server-dom-rspack@0.0.2(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(react-dom@19.2.7(react@19.2.7))(react@19.2.7))
typescript:
specifier: ^5.9.3
version: 5.9.3
@@ -2721,12 +2721,18 @@ packages:
'@emnapi/core@1.11.1':
resolution: {integrity: sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==}
+ '@emnapi/core@1.11.2':
+ resolution: {integrity: sha512-TC8MkTuZUtcTSiFeuC0ksCh9QIJ5+F21MvZ4Wn4ORfYaFJ/0dsiudv5tVkejgwZlwQ39jL9WWDe2lz8x0WglOA==}
+
'@emnapi/runtime@1.10.0':
resolution: {integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==}
'@emnapi/runtime@1.11.1':
resolution: {integrity: sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==}
+ '@emnapi/runtime@1.11.2':
+ resolution: {integrity: sha512-kyOl3X0DuTiT1h2ft8r2fYO8JYtU9a9Xis/zBSiGArNaagCOWx90N1k2wxp18czFDH+OgcWGb5ZP/XMt3dcyPA==}
+
'@emnapi/wasi-threads@1.2.1':
resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==}
@@ -5065,6 +5071,16 @@ packages:
core-js:
optional: true
+ '@rsbuild/core@2.1.6':
+ resolution: {integrity: sha512-w2WxblstOgHnDElkqJZVO/jM/EqPaEhg7zqQhON3Xu3Mj9FlVOJx+SgimOtghFzxLt8x7atX4oMUtMTNSZGf0Q==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ hasBin: true
+ peerDependencies:
+ core-js: '>= 3.0.0'
+ peerDependenciesMeta:
+ core-js:
+ optional: true
+
'@rsbuild/plugin-babel@2.0.0':
resolution: {integrity: sha512-JSiKJhsebDItVYPLY4dpZ0vw2Acvqv4jQtuAIDri+o6qBYzCzDXP936F6REiitVNTWh3H2rCyT8eM/WADTuFow==}
peerDependencies:
@@ -5191,6 +5207,11 @@ packages:
cpu: [arm64]
os: [darwin]
+ '@rspack/binding-darwin-arm64@2.1.4':
+ resolution: {integrity: sha512-3Xcs01iw48F4WeE4SHga6bCNb/UEFvtQX4P4eMIaJfGPjTQuxfabGE8yCPm9e3tpLZ5uo+IBnJ6nh5r6tIDOXQ==}
+ cpu: [arm64]
+ os: [darwin]
+
'@rspack/binding-darwin-x64@1.7.12':
resolution: {integrity: sha512-jnOp+/UXOJa9xqUb8KXH03sysoO2e4Ij6tw6MqDdmdj8n/A8PQENRPUbW9AwXpPtVDJPus9r4fi7b3+6e4B8Hg==}
cpu: [x64]
@@ -5206,6 +5227,11 @@ packages:
cpu: [x64]
os: [darwin]
+ '@rspack/binding-darwin-x64@2.1.4':
+ resolution: {integrity: sha512-bz/AsCplLs+3fXULPQU9d4r8H4PdeljgHFyItvVIrA/NKZqzQ8sX0topf/zJVZAOtPH7GNnrKjq0/F0U2DHikQ==}
+ cpu: [x64]
+ os: [darwin]
+
'@rspack/binding-linux-arm64-gnu@1.7.12':
resolution: {integrity: sha512-C8owWG+yvo7X0oVLIXetkoJhIFBP1LYNcAQqtgLmJnQLQDklGuP83dKC+zISGQWpjawHfZ1ER96vLgoTrxKZdw==}
cpu: [arm64]
@@ -5221,6 +5247,11 @@ packages:
cpu: [arm64]
os: [linux]
+ '@rspack/binding-linux-arm64-gnu@2.1.4':
+ resolution: {integrity: sha512-x0HQTLU1MusCtNamuXxf3ayEPkvh9uuaq4wVyBqveRkn4FznSOoHUsxTAKMnjGARX+vdLV/y/SwWJRDp2RI4zw==}
+ cpu: [arm64]
+ os: [linux]
+
'@rspack/binding-linux-arm64-musl@1.7.12':
resolution: {integrity: sha512-i51WWI64aRpsfSki6rN0aepPqXkVfS+vZM7+4bWDcmnhUmdMvhIPcYg0QRk3DtyJnu33jqNLM0WHY78k00NyfA==}
cpu: [arm64]
@@ -5236,16 +5267,31 @@ packages:
cpu: [arm64]
os: [linux]
+ '@rspack/binding-linux-arm64-musl@2.1.4':
+ resolution: {integrity: sha512-SEYCQD9UflKJMkYGnG5nt2gcsqdkgJsQmryBC/jxo+bOuY9gUSReU99FqCt7WRQrsHLbGeAFeTWs1QzItWG/Bw==}
+ cpu: [arm64]
+ os: [linux]
+
'@rspack/binding-linux-riscv64-gnu@2.1.0':
resolution: {integrity: sha512-B3ENZHIBi5u1Apt6RJ62QSCabCijI5l86Sm2AEDYpQnqqBj3vIc+Br9HJHvNjK8PNWs1WfmD//UTUmQqZbYpKQ==}
cpu: [riscv64]
os: [linux]
+ '@rspack/binding-linux-riscv64-gnu@2.1.4':
+ resolution: {integrity: sha512-jYtQKtnDRaVfyasvTGY04Z7m+xDWZYVwAIEOB4hP7czM7FVLOMgHlMlvw/EgF0DNHrBthqbPfBIS2tP50CzpEA==}
+ cpu: [riscv64]
+ os: [linux]
+
'@rspack/binding-linux-riscv64-musl@2.1.0':
resolution: {integrity: sha512-Qho1S8bW2BKRsJjl/f39GoyPRznF8ZarIgxZdVCIkn4k+3veggKWxqR1WWKoMj/LfykQd1uG3FF6n7zy5IfxWw==}
cpu: [riscv64]
os: [linux]
+ '@rspack/binding-linux-riscv64-musl@2.1.4':
+ resolution: {integrity: sha512-ZgxKjQAm9pidq2kChQO2PqKI9OQpLuGD7iPBuyT0gQg3m1+7vvbbkArLBPzJ12CQHVZvqua7n/QGx8pQjJI2Zw==}
+ cpu: [riscv64]
+ os: [linux]
+
'@rspack/binding-linux-x64-gnu@1.7.12':
resolution: {integrity: sha512-MSos0FuPEefqo9V92ULd5hggKG29EkSNg1zDcypy0OkpsKh5pfjVxTLYFXgTcVyFoUQQbdG8zFBzYbwmJ8V4ew==}
cpu: [x64]
@@ -5261,6 +5307,11 @@ packages:
cpu: [x64]
os: [linux]
+ '@rspack/binding-linux-x64-gnu@2.1.4':
+ resolution: {integrity: sha512-C53B3e6M4yzlYn4hDxR9cHZV+HqkfFGR0zuhH8QCfdBfN5KyGsmXmujiFU85ANEvBgf9CF8VreBQeJ20lyto/g==}
+ cpu: [x64]
+ os: [linux]
+
'@rspack/binding-linux-x64-musl@1.7.12':
resolution: {integrity: sha512-JcAMVKXOnjfpC3coWjCFPWD3Yl8RBw6a+IXQQ8mfRlHaHMIiOv8IfZqx15XRxMUn49CtP7Z0Na8iiAg2aKrcfw==}
cpu: [x64]
@@ -5276,6 +5327,11 @@ packages:
cpu: [x64]
os: [linux]
+ '@rspack/binding-linux-x64-musl@2.1.4':
+ resolution: {integrity: sha512-UaeG3FRo7e5RameQvWRNQ6KeXF29LEk67U/ohb9tF4U38mKH1OGqhmwCf5yLkqiOSgOi7Ff3ireOZD83Fso1iw==}
+ cpu: [x64]
+ os: [linux]
+
'@rspack/binding-wasm32-wasi@1.7.12':
resolution: {integrity: sha512-n+ZqP6ZMc0nhOgvadg5VhEs9ojtbES80AcWeFnmGkbzIszvGSO63GKNiRkXtjJ9KFuRzytbbmsCqkUVH+Tywxg==}
cpu: [wasm32]
@@ -5288,6 +5344,10 @@ packages:
resolution: {integrity: sha512-ZsDDduXaEF1SpyGz2OFuEU9Tzm0pKtbtCYviymiNtQS+3lx6rXyv+FaK0oIWn+gWL+gVNamplxKnNNR2jZsp5w==}
cpu: [wasm32]
+ '@rspack/binding-wasm32-wasi@2.1.4':
+ resolution: {integrity: sha512-0P1WZEfu7JOPzD/jQfk9U/6gnRFc7RpvYCQaYZVVYZNJ2gU6O0/yLegRGKNK/2L0zjYwiD0ynhOIBVUUERf5Pw==}
+ cpu: [wasm32]
+
'@rspack/binding-win32-arm64-msvc@1.7.12':
resolution: {integrity: sha512-8+h5fYDXYdmugbdfZ+D1y8IQ3rv2EhSfyGP7vBe+bjNyaMa4jWrpucmZbtxojUL1AzaeuHbvMdj9UO/gelk/+g==}
cpu: [arm64]
@@ -5303,6 +5363,11 @@ packages:
cpu: [arm64]
os: [win32]
+ '@rspack/binding-win32-arm64-msvc@2.1.4':
+ resolution: {integrity: sha512-+aStQipk1EakLRPfD+/aQbtmTXfxqSWetcRRDWV3cAsD4ebv1tF8FLKYY1PCaFpQbX1FxzCBGF0KHxkAsi9cxA==}
+ cpu: [arm64]
+ os: [win32]
+
'@rspack/binding-win32-ia32-msvc@1.7.12':
resolution: {integrity: sha512-cDMGwTRSa2p9fNBVe1wTRkF2AEXZ9ARWW36QeC5CkLaI0Ezz8lvhF2+CSOPnhaQ1O1qtn0L0SF+lFnrY+I7xGQ==}
cpu: [ia32]
@@ -5318,6 +5383,11 @@ packages:
cpu: [ia32]
os: [win32]
+ '@rspack/binding-win32-ia32-msvc@2.1.4':
+ resolution: {integrity: sha512-7nyrLRQ07j6i6omZuwiwwTI6rpjdy/Su3niLDAG7WsLL21u3/UQQrw6Fvm8SH3XYteghoHLSM3dhgaisuUhdJg==}
+ cpu: [ia32]
+ os: [win32]
+
'@rspack/binding-win32-x64-msvc@1.7.12':
resolution: {integrity: sha512-wIqFvlgFqrgUyj/6S/FJcvShnkZOmIeXTfqvheLY67MGq8qd8jb1YimQVKAIrmWB3yuJKUFACI3Ag1UBtEedEA==}
cpu: [x64]
@@ -5333,6 +5403,11 @@ packages:
cpu: [x64]
os: [win32]
+ '@rspack/binding-win32-x64-msvc@2.1.4':
+ resolution: {integrity: sha512-Z4je7JBaDpO9vvNMgCxlycycRJq+GQwwuXSXagW2xvvGM10Ij63YVtIehSMG9xaW8PED41oc9nWndoEsiD60pA==}
+ cpu: [x64]
+ os: [win32]
+
'@rspack/binding@1.7.12':
resolution: {integrity: sha512-f4HHuLbvuld8Ba4iB/4ibse5XrKxFrgmM3S4P2AOKnPlekAFlBjmltCuaTL/W2ggYvILaVY+YcFXrEH1rrKeQA==}
@@ -5342,6 +5417,9 @@ packages:
'@rspack/binding@2.1.0':
resolution: {integrity: sha512-LsXFIOOYDutHk44SAOcVQa5iA7lhYwEbD+nZhgmCiGJvKKh0UIpBj6EAsBsB6omEK5GEXvjDeLFieKgbYW08QQ==}
+ '@rspack/binding@2.1.4':
+ resolution: {integrity: sha512-iye4BaTYtTt0qa39avWwEsUobVxFNhQHr6B8reFeKU7sdaZsC9LUoDR8JAt5gOnbnzFMPdKgrdU/VzkC6rXbIg==}
+
'@rspack/core@1.7.12':
resolution: {integrity: sha512-6CwFIHlhRmXfZoMj3v9MZ1SMTPBn+cHVXeMIeaGp5sufqinKsISbsqHu6ZMJu2wDSmZLdmQJX6zLxkhcAUlhkQ==}
engines: {node: '>=18.12.0'}
@@ -5375,6 +5453,18 @@ packages:
'@swc/helpers':
optional: true
+ '@rspack/core@2.1.4':
+ resolution: {integrity: sha512-lpJgtr+JAXuDAMBJfRJ1LHyWVuYJyhZu6L6aj9t4lipUU03qwakHnzn3vwSCr68PsVvVPzR6NbJE1gJienSV0g==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ peerDependencies:
+ '@module-federation/runtime-tools': ^0.24.1 || ^2.0.0
+ '@swc/helpers': ^0.5.23
+ peerDependenciesMeta:
+ '@module-federation/runtime-tools':
+ optional: true
+ '@swc/helpers':
+ optional: true
+
'@rspack/lite-tapable@1.1.0':
resolution: {integrity: sha512-E2B0JhYFmVAwdDiG14+DW0Di4Ze4Jg10Pc4/lILUrd5DRCaklduz2OvJ5HYQ6G+hd+WTzqQb3QnDNfK4yvAFYw==}
@@ -5443,6 +5533,19 @@ packages:
'@rspack/resolver@0.2.8':
resolution: {integrity: sha512-FBWqdHhzS8mcf/WN4Ktzr7EaeaN+hsxbN98EweegX3924beZuY6H70CSFWCv1fIHAieCUv/9XCjKggHvhCsLwA==}
+ '@rstest/core@0.11.2':
+ resolution: {integrity: sha512-/CTLkvogDGozY+bCYsc+sKJDDsU/LvDdEZC545+QO1Iv9ds+6vgSWuA+Q2iQF3cReeSkOI9ySARfFemcCoSKpQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ hasBin: true
+ peerDependencies:
+ happy-dom: ^20.8.3
+ jsdom: '*'
+ peerDependenciesMeta:
+ happy-dom:
+ optional: true
+ jsdom:
+ optional: true
+
'@rstest/core@0.8.1':
resolution: {integrity: sha512-7d/2fm2V91pVx/rRtZ2gl6Zh4hVMivtDl4RgHFhBOrxi//UwhKISeF5gS/CSwpCgfOf10TzJRXqdI17ueUBNMQ==}
engines: {node: '>=18.12.0'}
@@ -5456,6 +5559,11 @@ packages:
jsdom:
optional: true
+ '@rstest/coverage-istanbul@0.11.2':
+ resolution: {integrity: sha512-Zynn5AhEsVky02/YNM6008/kgGoT/B/kJTRUpff00nIAbQBmbGYOesk1T8xtDwo6c3Zja4EgTvtsTtML3S80UQ==}
+ peerDependencies:
+ '@rstest/core': ^0.11.2
+
'@rstest/coverage-istanbul@0.2.0':
resolution: {integrity: sha512-ghFY2XkjL2PFarLaG+BxFTJYbVXtAtcqW5rjOqheiOYV6PqlOHFf/s+fLkKeOTc1oVoL3A/9vjMBc8fOgjAF/A==}
peerDependencies:
@@ -5985,6 +6093,9 @@ packages:
'@types/node@25.0.10':
resolution: {integrity: sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg==}
+ '@types/node@25.9.5':
+ resolution: {integrity: sha512-OScDchr2fwuUmWdf4kZ9h7PcJiYDVInhJizG/biAq3cAvqwYktuy/TYGGdZNMtNTFUP7rnb0NU4TUdm82kt4Rg==}
+
'@types/pegjs@0.10.6':
resolution: {integrity: sha512-eLYXDbZWXh2uxf+w8sXS8d6KSoXTswfps6fvCUuVAGN8eRpfe7h9eSRydxiSJvo9Bf+GzifsDOr9TMQlmJdmkw==}
@@ -6731,8 +6842,8 @@ packages:
engines: {node: '>=6.0.0'}
hasBin: true
- baseline-browser-mapping@2.10.42:
- resolution: {integrity: sha512-c/jurFrDLyui7o1J86yLkRu4LMsTYcBohveus7/I2Hzdn9KIP2bdJPTue/lR1KH46enoPbD77GKeSYNdyPoD3Q==}
+ baseline-browser-mapping@2.10.44:
+ resolution: {integrity: sha512-T3ghW+sl/ZJ8w1v/yQx3qvJ9040DWoLBz8JT/CILbAKcFyG9b2MRe75v6W5uXjv6uH1lumK2Kv46y2zSkcej0Q==}
engines: {node: '>=6.0.0'}
hasBin: true
@@ -6821,8 +6932,8 @@ packages:
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
hasBin: true
- browserslist@4.28.5:
- resolution: {integrity: sha512-Cu2E6QejHWzuDMTkuwgpABFgDfZrXLQq5V13YOACZx4mFAG4IwGTbTfHPMr4WtxlHoXSM8FIuRwYYCz5XiabaQ==}
+ browserslist@4.28.6:
+ resolution: {integrity: sha512-FQBYNK15VMslhLHpA7+n+n1GOlF1kId2xcCg7/j95f24AOF6VDYMNH4mFxF7KuaTdv627faazpOAjFzMrfJOUw==}
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
hasBin: true
@@ -6878,8 +6989,8 @@ packages:
caniuse-lite@1.0.30001800:
resolution: {integrity: sha512-MMHtuAz9Ys840zAY5F4k6fV5GaivZ9sPk+nz0mY+GYVzRBnYkN0mpqkSR92oWRQ19yQWo4HvBV/FnC16AJX8MA==}
- caniuse-lite@1.0.30001803:
- resolution: {integrity: sha512-g/uHREV2ZpK9qMalCsWaxmA6ol+DX8GYhuf3T40RKoP+oL7vhRJh8LNt73PCjpnR6l14FzfPrB5Yux4PKm2meg==}
+ caniuse-lite@1.0.30001806:
+ resolution: {integrity: sha512-72Cuvd95zbSYPKq6Fhg8eDJRlzgWDf7/mtoZv6Qe/DYNCEBdNxoA3+rZAU2ZhGCpZlns3EssFavaZomckT5Uuw==}
ccount@2.0.1:
resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==}
@@ -7427,8 +7538,8 @@ packages:
electron-to-chromium@1.5.385:
resolution: {integrity: sha512-78sa/M08MNAYHQfjoWMvOlKQqZ0ElhSm/L5HNUc96VZ3b+KvDVnngFm8sYQy0XrhTRgAhggHr5abA7yTvRdo4Q==}
- electron-to-chromium@1.5.389:
- resolution: {integrity: sha512-cEto7aeOqBfU1D+c5py5pE+ooscKE75JifxLBdFUZsqAxRS6y7kebtxAZvICszSl05gPjYHDTjY+lXpyGvpJbg==}
+ electron-to-chromium@1.5.393:
+ resolution: {integrity: sha512-kiDJdIUawuEIcp9XoICKp1iTYDEbgguIPq526N1Q7jIQDeQ3CqoMx71025PI/7E48Ddtw2HuWsVjY7afEgNxmg==}
emoji-regex@8.0.0:
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
@@ -7468,8 +7579,8 @@ packages:
resolution: {integrity: sha512-aNnGCvbJ/RIyWo1IuhNdVjnNF+EjH9wpzpNHt+ci/m9He9LJvUN8wrCcXjp9cWsGNAuvSpVFTx/vraAFQ8qGjQ==}
engines: {node: '>=10.13.0'}
- enhanced-resolve@5.24.2:
- resolution: {integrity: sha512-rpsZEGT1jFuve6QlpyRp9ckQ+kN61hvF9BzCPyMdaKTm8UJce96KBn3sorXOFXlzjPrs3Vc4T1NsSroZ3PxlFw==}
+ enhanced-resolve@5.24.3:
+ resolution: {integrity: sha512-PwKooW9JUzh5chmYfHM3IQl5OkK2u2Nm011MgeZrss3JmFraUx/fqrf78kk8GUMYoibx/14MdwTl/1WKkG7TpQ==}
engines: {node: '>=10.13.0'}
enquirer@2.4.1:
@@ -7536,6 +7647,9 @@ packages:
es-module-lexer@2.3.0:
resolution: {integrity: sha512-KLdwQm2NvGLDkQDCGvmiQrhkd0JbMzXthwQAUgWjQuQdBLFa3eiBP5arXZyA+f8x+x7OXgud6bq2rxjGtHV2tw==}
+ es-module-lexer@2.3.1:
+ resolution: {integrity: sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA==}
+
es-object-atoms@1.1.2:
resolution: {integrity: sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==}
engines: {node: '>= 0.4'}
@@ -9257,6 +9371,10 @@ packages:
resolution: {integrity: sha512-J6l92tKHX6w8Jy5nO1Vuc01NoIiRGi/d6qBKVxh+IQ8Cr3b6HbVNfKiF8ZpFKufTwpwxMmce2W3iQZ861ZRyTg==}
engines: {node: '>=18'}
+ node-releases@2.0.51:
+ resolution: {integrity: sha512-wRNIrw4DmVLKQlbgOMdkMx27Wrpzes2hh5Jtbi2bjPd+4wJstWIqP5A+lscnqbm0xxmT5Bpg8Lec5ItEBwx6BQ==}
+ engines: {node: '>=18'}
+
node-schedule@2.1.1:
resolution: {integrity: sha512-OXdegQq03OmXEjt2hZP33W2YPs/E5BcFQks46+G2gAxs4gHOIVD1u7EqlYLYSKsaIpyKCK9Gbk0ta1/gjRSMRQ==}
engines: {node: '>=6'}
@@ -10459,14 +10577,14 @@ packages:
serialize-javascript@6.0.2:
resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==}
- seroval-plugins@1.5.5:
- resolution: {integrity: sha512-+BDhqYM6CEn3x09v44dpa9p6974FuUB2dxk+Ctn04k0cO1Zt6QODTXfmEZK0eBaTe/fJBvP4NMGuNJ+R8T+QMg==}
+ seroval-plugins@1.5.6:
+ resolution: {integrity: sha512-HXuLAX2pu/UByPpaeo/TaMfvMIi+1QqIoPJYCcAtU8QkVNwgR6MPlGuCQTErV1JwraaMbYaWVIBX7mppzGLATQ==}
engines: {node: '>=10'}
peerDependencies:
seroval: ^1.0
- seroval@1.5.5:
- resolution: {integrity: sha512-bSjOuPcwPKLSJNhr9+bZxA20nQxVle5J5MNsYRVE6cIg7KpRLXGupymePavu0jrxlPiPsr4xGZSB8yUY2sH2sw==}
+ seroval@1.5.6:
+ resolution: {integrity: sha512-rVQVWjjSvlINzaQPZH5JFqsqEsIWdTxY3iJZCnTL/5gQbXIRooVZKI60tVCkOVfzcRPejboxO2t0P89dg5mQaA==}
engines: {node: '>=10'}
serve-handler@6.1.7:
@@ -11018,6 +11136,9 @@ packages:
undici-types@7.16.0:
resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==}
+ undici-types@7.24.6:
+ resolution: {integrity: sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==}
+
undici@7.24.7:
resolution: {integrity: sha512-H/nlJ/h0ggGC+uRL3ovD+G0i4bqhvsDOpbDv7At5eFLlj2b41L8QliGbnl2H7SnDiYhENphh1tQFJZf+MyfLsQ==}
engines: {node: '>=20.18.1'}
@@ -12047,6 +12168,12 @@ snapshots:
tslib: 2.8.1
optional: true
+ '@emnapi/core@1.11.2':
+ dependencies:
+ '@emnapi/wasi-threads': 1.2.2
+ tslib: 2.8.1
+ optional: true
+
'@emnapi/runtime@1.10.0':
dependencies:
tslib: 2.8.1
@@ -12057,6 +12184,11 @@ snapshots:
tslib: 2.8.1
optional: true
+ '@emnapi/runtime@1.11.2':
+ dependencies:
+ tslib: 2.8.1
+ optional: true
+
'@emnapi/wasi-threads@1.2.1':
dependencies:
tslib: 2.8.1
@@ -12726,7 +12858,7 @@ snapshots:
- node-fetch
- utf-8-validate
- '@module-federation/enhanced@2.5.1(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.3)(webpack@5.108.3(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16))':
+ '@module-federation/enhanced@2.5.1(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.3)(webpack@5.108.3(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16))':
dependencies:
'@module-federation/bridge-react-webpack-plugin': 2.5.1(node-fetch@2.7.0(encoding@0.1.13))
'@module-federation/cli': 2.5.1(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.3)
@@ -12735,7 +12867,7 @@ snapshots:
'@module-federation/inject-external-runtime-core-plugin': 2.5.1(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))
'@module-federation/managers': 2.5.1(node-fetch@2.7.0(encoding@0.1.13))
'@module-federation/manifest': 2.5.1(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.3)
- '@module-federation/rspack': 2.5.1(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.3)
+ '@module-federation/rspack': 2.5.1(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.3)
'@module-federation/runtime-tools': 2.5.1(node-fetch@2.7.0(encoding@0.1.13))
'@module-federation/sdk': 2.5.1(node-fetch@2.7.0(encoding@0.1.13))
'@module-federation/webpack-bundler-runtime': 2.5.1(node-fetch@2.7.0(encoding@0.1.13))
@@ -12779,9 +12911,9 @@ snapshots:
- utf-8-validate
- vue-tsc
- '@module-federation/node@2.7.44(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(typescript@5.9.3)(webpack@5.108.3(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16))':
+ '@module-federation/node@2.7.44(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(typescript@5.9.3)(webpack@5.108.3(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16))':
dependencies:
- '@module-federation/enhanced': 2.5.1(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.3)(webpack@5.108.3(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16))
+ '@module-federation/enhanced': 2.5.1(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.3)(webpack@5.108.3(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16))
'@module-federation/runtime': 2.5.1(node-fetch@2.7.0(encoding@0.1.13))
'@module-federation/sdk': 2.5.1(node-fetch@2.7.0(encoding@0.1.13))
encoding: 0.1.13
@@ -12796,10 +12928,10 @@ snapshots:
- utf-8-validate
- vue-tsc
- '@module-federation/rsbuild-plugin@2.5.1(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.3)(webpack@5.108.3(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16))':
+ '@module-federation/rsbuild-plugin@2.5.1(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.3)(webpack@5.108.3(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16))':
dependencies:
- '@module-federation/enhanced': 2.5.1(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.3)(webpack@5.108.3(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16))
- '@module-federation/node': 2.7.44(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(typescript@5.9.3)(webpack@5.108.3(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16))
+ '@module-federation/enhanced': 2.5.1(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.3)(webpack@5.108.3(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16))
+ '@module-federation/node': 2.7.44(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(typescript@5.9.3)(webpack@5.108.3(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16))
'@module-federation/sdk': 2.5.1(node-fetch@2.7.0(encoding@0.1.13))
optionalDependencies:
'@rsbuild/core': 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)
@@ -12812,7 +12944,7 @@ snapshots:
- vue-tsc
- webpack
- '@module-federation/rspack@2.5.1(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.3)':
+ '@module-federation/rspack@2.5.1(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.3)':
dependencies:
'@module-federation/bridge-react-webpack-plugin': 2.5.1(node-fetch@2.7.0(encoding@0.1.13))
'@module-federation/dts-plugin': 2.5.1(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.3)
@@ -12821,7 +12953,7 @@ snapshots:
'@module-federation/manifest': 2.5.1(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.3)
'@module-federation/runtime-tools': 2.5.1(node-fetch@2.7.0(encoding@0.1.13))
'@module-federation/sdk': 2.5.1(node-fetch@2.7.0(encoding@0.1.13))
- '@rspack/core': 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)
+ '@rspack/core': 2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)
optionalDependencies:
typescript: 5.9.3
transitivePeerDependencies:
@@ -12928,10 +13060,10 @@ snapshots:
'@tybys/wasm-util': 0.10.3
optional: true
- '@napi-rs/wasm-runtime@1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)':
+ '@napi-rs/wasm-runtime@1.1.6(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)':
dependencies:
- '@emnapi/core': 1.11.1
- '@emnapi/runtime': 1.11.1
+ '@emnapi/core': 1.11.2
+ '@emnapi/runtime': 1.11.2
'@tybys/wasm-util': 0.10.3
optional: true
@@ -14088,6 +14220,57 @@ snapshots:
- tsx
- yaml
+ '@react-router/dev@7.14.0(@react-router/serve@7.14.0(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3))(@types/node@25.9.5)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.9.5)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0))(wrangler@4.107.0(@cloudflare/workers-types@4.20260702.1))':
+ dependencies:
+ '@babel/core': 7.29.0
+ '@babel/generator': 7.29.7
+ '@babel/parser': 7.29.7
+ '@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.0)
+ '@babel/preset-typescript': 7.29.7(@babel/core@7.29.0)
+ '@babel/traverse': 7.29.7
+ '@babel/types': 7.29.7
+ '@react-router/node': 7.14.0(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3)
+ '@remix-run/node-fetch-server': 0.13.3
+ arg: 5.0.2
+ babel-dead-code-elimination: 1.0.12
+ chokidar: 4.0.3
+ dedent: 1.7.2
+ es-module-lexer: 1.7.0
+ exit-hook: 2.2.1
+ isbot: 5.1.34
+ jsesc: 3.0.2
+ lodash: 4.18.1
+ p-map: 7.0.5
+ pathe: 1.1.2
+ picocolors: 1.1.1
+ pkg-types: 2.3.1
+ prettier: 3.8.1
+ react-refresh: 0.14.2
+ react-router: 7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ semver: 7.8.5
+ tinyglobby: 0.2.17
+ valibot: 1.4.2(typescript@5.9.3)
+ vite: 7.3.1(@types/node@25.9.5)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0)
+ vite-node: 3.2.4(@types/node@25.9.5)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0)
+ optionalDependencies:
+ '@react-router/serve': 7.14.0(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3)
+ typescript: 5.9.3
+ wrangler: 4.107.0(@cloudflare/workers-types@4.20260702.1)
+ transitivePeerDependencies:
+ - '@types/node'
+ - babel-plugin-macros
+ - jiti
+ - less
+ - lightningcss
+ - sass
+ - sass-embedded
+ - stylus
+ - sugarss
+ - supports-color
+ - terser
+ - tsx
+ - yaml
+
'@react-router/dev@8.1.0(@react-router/serve@8.1.0(react-router@7.18.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3))(react-router@7.18.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0))(wrangler@4.107.0(@cloudflare/workers-types@4.20260702.1))':
dependencies:
'@babel/core': 7.29.7
@@ -14548,6 +14731,15 @@ snapshots:
transitivePeerDependencies:
- '@module-federation/runtime-tools'
+ '@rsbuild/core@2.1.6(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0)':
+ dependencies:
+ '@rspack/core': 2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)
+ '@swc/helpers': 0.5.23
+ optionalDependencies:
+ core-js: 3.47.0
+ transitivePeerDependencies:
+ - '@module-federation/runtime-tools'
+
'@rsbuild/plugin-babel@2.0.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))':
dependencies:
'@babel/core': 7.29.7
@@ -14583,6 +14775,18 @@ snapshots:
- '@rspack/core'
- webpack
+ '@rsbuild/plugin-less@1.6.4(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))':
+ dependencies:
+ deepmerge: 4.3.1
+ less: 4.6.7
+ less-loader: 12.3.3(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(less@4.6.7)(webpack@5.108.3(lightningcss@1.32.0))
+ reduce-configs: 1.1.2
+ optionalDependencies:
+ '@rsbuild/core': 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)
+ transitivePeerDependencies:
+ - '@rspack/core'
+ - webpack
+
'@rsbuild/plugin-mdx@1.1.3(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(webpack@5.108.3(lightningcss@1.32.0))':
dependencies:
'@mdx-js/loader': 3.1.1(webpack@5.108.3(lightningcss@1.32.0))
@@ -14592,9 +14796,27 @@ snapshots:
- supports-color
- webpack
- '@rsbuild/plugin-react@2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))':
+ '@rsbuild/plugin-react@2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))':
+ dependencies:
+ '@rspack/plugin-react-refresh': 2.0.2(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(react-refresh@0.18.0)
+ react-refresh: 0.18.0
+ optionalDependencies:
+ '@rsbuild/core': 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)
+ transitivePeerDependencies:
+ - '@rspack/core'
+
+ '@rsbuild/plugin-react@2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))':
+ dependencies:
+ '@rspack/plugin-react-refresh': 2.0.2(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(react-refresh@0.18.0)
+ react-refresh: 0.18.0
+ optionalDependencies:
+ '@rsbuild/core': 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)
+ transitivePeerDependencies:
+ - '@rspack/core'
+
+ '@rsbuild/plugin-react@2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))':
dependencies:
- '@rspack/plugin-react-refresh': 2.0.2(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(react-refresh@0.18.0)
+ '@rspack/plugin-react-refresh': 2.0.2(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(react-refresh@0.18.0)
react-refresh: 0.18.0
optionalDependencies:
'@rsbuild/core': 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)
@@ -14613,7 +14835,7 @@ snapshots:
'@rsbuild/plugin-svgr@2.0.4(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(typescript@5.9.3)':
dependencies:
- '@rsbuild/plugin-react': 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))
+ '@rsbuild/plugin-react': 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))
'@svgr/core': 8.1.0(typescript@5.9.3)
'@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0(typescript@5.9.3))
'@svgr/plugin-svgo': 8.1.0(@svgr/core@8.1.0(typescript@5.9.3))(typescript@5.9.3)
@@ -14626,9 +14848,24 @@ snapshots:
- supports-color
- typescript
- '@rsbuild/plugin-svgr@2.0.4(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(typescript@6.0.1-rc)':
+ '@rsbuild/plugin-svgr@2.0.4(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(typescript@5.9.3)':
dependencies:
- '@rsbuild/plugin-react': 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))
+ '@rsbuild/plugin-react': 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))
+ '@svgr/core': 8.1.0(typescript@5.9.3)
+ '@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0(typescript@5.9.3))
+ '@svgr/plugin-svgo': 8.1.0(@svgr/core@8.1.0(typescript@5.9.3))(typescript@5.9.3)
+ deepmerge: 4.3.1
+ loader-utils: 3.3.1
+ optionalDependencies:
+ '@rsbuild/core': 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)
+ transitivePeerDependencies:
+ - '@rspack/core'
+ - supports-color
+ - typescript
+
+ '@rsbuild/plugin-svgr@2.0.4(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(typescript@6.0.1-rc)':
+ dependencies:
+ '@rsbuild/plugin-react': 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))
'@svgr/core': 8.1.0(typescript@6.0.1-rc)
'@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0(typescript@5.9.3))
'@svgr/plugin-svgo': 8.1.0(@svgr/core@8.1.0(typescript@5.9.3))(typescript@6.0.1-rc)
@@ -14650,16 +14887,25 @@ snapshots:
- '@rspack/core'
- webpack
+ '@rsbuild/plugin-tailwindcss@2.0.3(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))':
+ dependencies:
+ '@tailwindcss/webpack': 4.3.2(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
+ optionalDependencies:
+ '@rsbuild/core': 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)
+ transitivePeerDependencies:
+ - '@rspack/core'
+ - webpack
+
'@rsdoctor/client@1.5.17': {}
- '@rsdoctor/core@1.5.17(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))':
+ '@rsdoctor/core@1.5.17(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))':
dependencies:
'@rsbuild/plugin-check-syntax': 1.6.1(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))
- '@rsdoctor/graph': 1.5.17(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
- '@rsdoctor/sdk': 1.5.17(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
- '@rsdoctor/types': 1.5.17(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
- '@rsdoctor/utils': 1.5.17(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
- '@rspack/resolver': 0.2.8(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)
+ '@rsdoctor/graph': 1.5.17(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
+ '@rsdoctor/sdk': 1.5.17(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
+ '@rsdoctor/types': 1.5.17(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
+ '@rsdoctor/utils': 1.5.17(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
+ '@rspack/resolver': 0.2.8(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)
browserslist-load-config: 1.0.3
es-toolkit: 1.49.0
filesize: 11.0.19
@@ -14676,10 +14922,10 @@ snapshots:
- utf-8-validate
- webpack
- '@rsdoctor/graph@1.5.17(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))':
+ '@rsdoctor/graph@1.5.17(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))':
dependencies:
- '@rsdoctor/types': 1.5.17(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
- '@rsdoctor/utils': 1.5.17(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
+ '@rsdoctor/types': 1.5.17(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
+ '@rsdoctor/utils': 1.5.17(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
es-toolkit: 1.49.0
path-browserify: 1.0.1
source-map: 0.7.6
@@ -14687,15 +14933,15 @@ snapshots:
- '@rspack/core'
- webpack
- '@rsdoctor/rspack-plugin@1.5.17(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))':
+ '@rsdoctor/rspack-plugin@1.5.17(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))':
dependencies:
- '@rsdoctor/core': 1.5.17(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
- '@rsdoctor/graph': 1.5.17(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
- '@rsdoctor/sdk': 1.5.17(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
- '@rsdoctor/types': 1.5.17(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
- '@rsdoctor/utils': 1.5.17(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
+ '@rsdoctor/core': 1.5.17(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
+ '@rsdoctor/graph': 1.5.17(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
+ '@rsdoctor/sdk': 1.5.17(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
+ '@rsdoctor/types': 1.5.17(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
+ '@rsdoctor/utils': 1.5.17(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
optionalDependencies:
- '@rspack/core': 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)
+ '@rspack/core': 2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)
transitivePeerDependencies:
- '@emnapi/core'
- '@emnapi/runtime'
@@ -14705,12 +14951,12 @@ snapshots:
- utf-8-validate
- webpack
- '@rsdoctor/sdk@1.5.17(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))':
+ '@rsdoctor/sdk@1.5.17(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))':
dependencies:
'@rsdoctor/client': 1.5.17
- '@rsdoctor/graph': 1.5.17(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
- '@rsdoctor/types': 1.5.17(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
- '@rsdoctor/utils': 1.5.17(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
+ '@rsdoctor/graph': 1.5.17(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
+ '@rsdoctor/types': 1.5.17(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
+ '@rsdoctor/utils': 1.5.17(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
launch-editor: 2.14.1
safer-buffer: 2.1.2
socket.io: 4.8.1
@@ -14722,20 +14968,20 @@ snapshots:
- utf-8-validate
- webpack
- '@rsdoctor/types@1.5.17(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))':
+ '@rsdoctor/types@1.5.17(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))':
dependencies:
'@types/connect': 3.4.38
'@types/estree': 1.0.5
'@types/tapable': 2.3.0
source-map: 0.7.6
optionalDependencies:
- '@rspack/core': 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)
+ '@rspack/core': 2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)
webpack: 5.108.3(lightningcss@1.32.0)
- '@rsdoctor/utils@1.5.17(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))':
+ '@rsdoctor/utils@1.5.17(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))':
dependencies:
'@babel/code-frame': 7.26.2
- '@rsdoctor/types': 1.5.17(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
+ '@rsdoctor/types': 1.5.17(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
'@types/estree': 1.0.5
acorn: 8.17.0
acorn-import-attributes: 1.9.5(acorn@8.17.0)
@@ -14773,6 +15019,9 @@ snapshots:
'@rspack/binding-darwin-arm64@2.1.0':
optional: true
+ '@rspack/binding-darwin-arm64@2.1.4':
+ optional: true
+
'@rspack/binding-darwin-x64@1.7.12':
optional: true
@@ -14782,6 +15031,9 @@ snapshots:
'@rspack/binding-darwin-x64@2.1.0':
optional: true
+ '@rspack/binding-darwin-x64@2.1.4':
+ optional: true
+
'@rspack/binding-linux-arm64-gnu@1.7.12':
optional: true
@@ -14791,6 +15043,9 @@ snapshots:
'@rspack/binding-linux-arm64-gnu@2.1.0':
optional: true
+ '@rspack/binding-linux-arm64-gnu@2.1.4':
+ optional: true
+
'@rspack/binding-linux-arm64-musl@1.7.12':
optional: true
@@ -14800,12 +15055,21 @@ snapshots:
'@rspack/binding-linux-arm64-musl@2.1.0':
optional: true
+ '@rspack/binding-linux-arm64-musl@2.1.4':
+ optional: true
+
'@rspack/binding-linux-riscv64-gnu@2.1.0':
optional: true
+ '@rspack/binding-linux-riscv64-gnu@2.1.4':
+ optional: true
+
'@rspack/binding-linux-riscv64-musl@2.1.0':
optional: true
+ '@rspack/binding-linux-riscv64-musl@2.1.4':
+ optional: true
+
'@rspack/binding-linux-x64-gnu@1.7.12':
optional: true
@@ -14815,6 +15079,9 @@ snapshots:
'@rspack/binding-linux-x64-gnu@2.1.0':
optional: true
+ '@rspack/binding-linux-x64-gnu@2.1.4':
+ optional: true
+
'@rspack/binding-linux-x64-musl@1.7.12':
optional: true
@@ -14824,6 +15091,9 @@ snapshots:
'@rspack/binding-linux-x64-musl@2.1.0':
optional: true
+ '@rspack/binding-linux-x64-musl@2.1.4':
+ optional: true
+
'@rspack/binding-wasm32-wasi@1.7.12':
dependencies:
'@napi-rs/wasm-runtime': 1.0.7
@@ -14843,6 +15113,13 @@ snapshots:
'@napi-rs/wasm-runtime': 1.1.5(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)
optional: true
+ '@rspack/binding-wasm32-wasi@2.1.4':
+ dependencies:
+ '@emnapi/core': 1.11.2
+ '@emnapi/runtime': 1.11.2
+ '@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)
+ optional: true
+
'@rspack/binding-win32-arm64-msvc@1.7.12':
optional: true
@@ -14852,6 +15129,9 @@ snapshots:
'@rspack/binding-win32-arm64-msvc@2.1.0':
optional: true
+ '@rspack/binding-win32-arm64-msvc@2.1.4':
+ optional: true
+
'@rspack/binding-win32-ia32-msvc@1.7.12':
optional: true
@@ -14861,6 +15141,9 @@ snapshots:
'@rspack/binding-win32-ia32-msvc@2.1.0':
optional: true
+ '@rspack/binding-win32-ia32-msvc@2.1.4':
+ optional: true
+
'@rspack/binding-win32-x64-msvc@1.7.12':
optional: true
@@ -14870,6 +15153,9 @@ snapshots:
'@rspack/binding-win32-x64-msvc@2.1.0':
optional: true
+ '@rspack/binding-win32-x64-msvc@2.1.4':
+ optional: true
+
'@rspack/binding@1.7.12':
optionalDependencies:
'@rspack/binding-darwin-arm64': 1.7.12
@@ -14911,6 +15197,21 @@ snapshots:
'@rspack/binding-win32-ia32-msvc': 2.1.0
'@rspack/binding-win32-x64-msvc': 2.1.0
+ '@rspack/binding@2.1.4':
+ optionalDependencies:
+ '@rspack/binding-darwin-arm64': 2.1.4
+ '@rspack/binding-darwin-x64': 2.1.4
+ '@rspack/binding-linux-arm64-gnu': 2.1.4
+ '@rspack/binding-linux-arm64-musl': 2.1.4
+ '@rspack/binding-linux-riscv64-gnu': 2.1.4
+ '@rspack/binding-linux-riscv64-musl': 2.1.4
+ '@rspack/binding-linux-x64-gnu': 2.1.4
+ '@rspack/binding-linux-x64-musl': 2.1.4
+ '@rspack/binding-wasm32-wasi': 2.1.4
+ '@rspack/binding-win32-arm64-msvc': 2.1.4
+ '@rspack/binding-win32-ia32-msvc': 2.1.4
+ '@rspack/binding-win32-x64-msvc': 2.1.4
+
'@rspack/core@1.7.12(@swc/helpers@0.5.23)':
dependencies:
'@module-federation/runtime-tools': 0.22.0
@@ -14933,16 +15234,29 @@ snapshots:
'@module-federation/runtime-tools': 2.5.1(node-fetch@2.7.0(encoding@0.1.13))
'@swc/helpers': 0.5.23
+ '@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)':
+ dependencies:
+ '@rspack/binding': 2.1.4
+ optionalDependencies:
+ '@module-federation/runtime-tools': 2.5.1(node-fetch@2.7.0(encoding@0.1.13))
+ '@swc/helpers': 0.5.23
+
'@rspack/lite-tapable@1.1.0': {}
'@rspack/lite-tapable@1.1.2': {}
- '@rspack/plugin-react-refresh@2.0.2(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(react-refresh@0.18.0)':
+ '@rspack/plugin-react-refresh@2.0.2(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(react-refresh@0.18.0)':
dependencies:
react-refresh: 0.18.0
optionalDependencies:
'@rspack/core': 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)
+ '@rspack/plugin-react-refresh@2.0.2(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(react-refresh@0.18.0)':
+ dependencies:
+ react-refresh: 0.18.0
+ optionalDependencies:
+ '@rspack/core': 2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)
+
'@rspack/resolver-binding-darwin-arm64@0.2.8':
optional: true
@@ -14961,9 +15275,9 @@ snapshots:
'@rspack/resolver-binding-linux-x64-musl@0.2.8':
optional: true
- '@rspack/resolver-binding-wasm32-wasi@0.2.8(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)':
+ '@rspack/resolver-binding-wasm32-wasi@0.2.8(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)':
dependencies:
- '@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)
+ '@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)
transitivePeerDependencies:
- '@emnapi/core'
- '@emnapi/runtime'
@@ -14978,7 +15292,7 @@ snapshots:
'@rspack/resolver-binding-win32-x64-msvc@0.2.8':
optional: true
- '@rspack/resolver@0.2.8(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)':
+ '@rspack/resolver@0.2.8(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)':
optionalDependencies:
'@rspack/resolver-binding-darwin-arm64': 0.2.8
'@rspack/resolver-binding-darwin-x64': 0.2.8
@@ -14986,7 +15300,7 @@ snapshots:
'@rspack/resolver-binding-linux-arm64-musl': 0.2.8
'@rspack/resolver-binding-linux-x64-gnu': 0.2.8
'@rspack/resolver-binding-linux-x64-musl': 0.2.8
- '@rspack/resolver-binding-wasm32-wasi': 0.2.8(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)
+ '@rspack/resolver-binding-wasm32-wasi': 0.2.8(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)
'@rspack/resolver-binding-win32-arm64-msvc': 0.2.8
'@rspack/resolver-binding-win32-ia32-msvc': 0.2.8
'@rspack/resolver-binding-win32-x64-msvc': 0.2.8
@@ -14994,6 +15308,16 @@ snapshots:
- '@emnapi/core'
- '@emnapi/runtime'
+ '@rstest/core@0.11.2(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0)(jsdom@27.4.0(@noble/hashes@2.2.0))':
+ dependencies:
+ '@rsbuild/core': 2.1.6(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0)
+ '@types/chai': 5.2.3
+ optionalDependencies:
+ jsdom: 27.4.0(@noble/hashes@2.2.0)
+ transitivePeerDependencies:
+ - '@module-federation/runtime-tools'
+ - core-js
+
'@rstest/core@0.8.1(jsdom@27.4.0(@noble/hashes@2.2.0))':
dependencies:
'@rsbuild/core': 1.7.2
@@ -15002,6 +15326,17 @@ snapshots:
optionalDependencies:
jsdom: 27.4.0(@noble/hashes@2.2.0)
+ '@rstest/coverage-istanbul@0.11.2(@rstest/core@0.11.2(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0)(jsdom@27.4.0(@noble/hashes@2.2.0)))':
+ dependencies:
+ '@rstest/core': 0.11.2(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0)(jsdom@27.4.0(@noble/hashes@2.2.0))
+ istanbul-lib-coverage: 3.2.2
+ istanbul-lib-report: 3.0.1
+ istanbul-lib-source-maps: 5.0.6
+ istanbul-reports: 3.2.0
+ swc-plugin-coverage-instrument: 0.0.32
+ transitivePeerDependencies:
+ - supports-color
+
'@rstest/coverage-istanbul@0.2.0(@rstest/core@0.8.1(jsdom@27.4.0(@noble/hashes@2.2.0)))':
dependencies:
'@rstest/core': 0.8.1(jsdom@27.4.0(@noble/hashes@2.2.0))
@@ -15385,6 +15720,16 @@ snapshots:
'@rspack/core': 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)
webpack: 5.108.3(lightningcss@1.32.0)
+ '@tailwindcss/webpack@4.3.2(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))':
+ dependencies:
+ '@alloc/quick-lru': 5.2.0
+ '@tailwindcss/node': 4.3.2
+ '@tailwindcss/oxide': 4.3.2
+ tailwindcss: 4.3.2
+ optionalDependencies:
+ '@rspack/core': 2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)
+ webpack: 5.108.3(lightningcss@1.32.0)
+
'@tanstack/devtools-client@0.0.5':
dependencies:
'@tanstack/devtools-event-client': 0.4.4
@@ -15661,6 +16006,10 @@ snapshots:
dependencies:
undici-types: 7.16.0
+ '@types/node@25.9.5':
+ dependencies:
+ undici-types: 7.24.6
+
'@types/pegjs@0.10.6': {}
'@types/pg-pool@2.0.7':
@@ -16418,7 +16767,7 @@ snapshots:
baseline-browser-mapping@2.10.41: {}
- baseline-browser-mapping@2.10.42: {}
+ baseline-browser-mapping@2.10.44: {}
basic-auth@2.0.1:
dependencies:
@@ -16540,13 +16889,13 @@ snapshots:
node-releases: 2.0.50
update-browserslist-db: 1.2.3(browserslist@4.28.4)
- browserslist@4.28.5:
+ browserslist@4.28.6:
dependencies:
- baseline-browser-mapping: 2.10.42
- caniuse-lite: 1.0.30001803
- electron-to-chromium: 1.5.389
- node-releases: 2.0.50
- update-browserslist-db: 1.2.3(browserslist@4.28.5)
+ baseline-browser-mapping: 2.10.44
+ caniuse-lite: 1.0.30001806
+ electron-to-chromium: 1.5.393
+ node-releases: 2.0.51
+ update-browserslist-db: 1.2.3(browserslist@4.28.6)
buffer-from@1.1.2: {}
@@ -16593,7 +16942,7 @@ snapshots:
caniuse-lite@1.0.30001800: {}
- caniuse-lite@1.0.30001803: {}
+ caniuse-lite@1.0.30001806: {}
ccount@2.0.1: {}
@@ -17110,7 +17459,7 @@ snapshots:
electron-to-chromium@1.5.385: {}
- electron-to-chromium@1.5.389: {}
+ electron-to-chromium@1.5.393: {}
emoji-regex@8.0.0: {}
@@ -17159,7 +17508,7 @@ snapshots:
graceful-fs: 4.2.11
tapable: 2.3.3
- enhanced-resolve@5.24.2:
+ enhanced-resolve@5.24.3:
dependencies:
graceful-fs: 4.2.11
tapable: 2.3.3
@@ -17283,6 +17632,8 @@ snapshots:
es-module-lexer@2.3.0: {}
+ es-module-lexer@2.3.1: {}
+
es-object-atoms@1.1.2:
dependencies:
es-errors: 1.3.0
@@ -18466,7 +18817,7 @@ snapshots:
jest-worker@27.5.1:
dependencies:
- '@types/node': 25.0.10
+ '@types/node': 25.9.5
merge-stream: 2.0.0
supports-color: 8.1.1
@@ -18618,6 +18969,13 @@ snapshots:
'@rspack/core': 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)
webpack: 5.108.3(lightningcss@1.32.0)
+ less-loader@12.3.3(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(less@4.6.7)(webpack@5.108.3(lightningcss@1.32.0)):
+ dependencies:
+ less: 4.6.7
+ optionalDependencies:
+ '@rspack/core': 2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)
+ webpack: 5.108.3(lightningcss@1.32.0)
+
less@4.6.7:
dependencies:
copy-anything: 3.0.5
@@ -19359,6 +19717,8 @@ snapshots:
node-releases@2.0.50: {}
+ node-releases@2.0.51: {}
+
node-schedule@2.1.1:
dependencies:
cron-parser: 4.9.0
@@ -20035,6 +20395,12 @@ snapshots:
react: 19.2.7
react-dom: 19.2.7(react@19.2.7)
+ react-server-dom-rspack@0.0.2(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(react-dom@19.2.7(react@19.2.7))(react@19.2.7):
+ dependencies:
+ '@rspack/core': 2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+
react-style-singleton@2.2.3(@types/react@19.2.10)(react@19.2.7):
dependencies:
get-nonce: 1.0.1
@@ -20333,6 +20699,11 @@ snapshots:
'@rsbuild/core': 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)
react-server-dom-rspack: 0.0.2(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ rsbuild-plugin-rsc@0.1.1(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(react-server-dom-rspack@0.0.2(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)):
+ dependencies:
+ '@rsbuild/core': 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)
+ react-server-dom-rspack: 0.0.2(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+
rslog@2.1.3: {}
run-parallel@1.2.0:
@@ -20536,11 +20907,11 @@ snapshots:
dependencies:
randombytes: 2.1.0
- seroval-plugins@1.5.5(seroval@1.5.5):
+ seroval-plugins@1.5.6(seroval@1.5.6):
dependencies:
- seroval: 1.5.5
+ seroval: 1.5.6
- seroval@1.5.5: {}
+ seroval@1.5.6: {}
serve-handler@6.1.7:
dependencies:
@@ -20754,8 +21125,8 @@ snapshots:
solid-js@1.9.14:
dependencies:
csstype: 3.2.3
- seroval: 1.5.5
- seroval-plugins: 1.5.5(seroval@1.5.5)
+ seroval: 1.5.6
+ seroval-plugins: 1.5.6(seroval@1.5.6)
sonner@2.0.7(react-dom@19.2.7(react@19.2.7))(react@19.2.7):
dependencies:
@@ -21178,6 +21549,8 @@ snapshots:
undici-types@7.16.0: {}
+ undici-types@7.24.6: {}
+
undici@7.24.7: {}
undici@7.28.0: {}
@@ -21268,9 +21641,9 @@ snapshots:
escalade: 3.2.0
picocolors: 1.1.1
- update-browserslist-db@1.2.3(browserslist@4.28.5):
+ update-browserslist-db@1.2.3(browserslist@4.28.6):
dependencies:
- browserslist: 4.28.5
+ browserslist: 4.28.6
escalade: 3.2.0
picocolors: 1.1.1
@@ -21380,6 +21753,27 @@ snapshots:
- tsx
- yaml
+ vite-node@3.2.4(@types/node@25.9.5)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0):
+ dependencies:
+ cac: 6.7.14
+ debug: 4.4.3
+ es-module-lexer: 1.7.0
+ pathe: 2.0.3
+ vite: 7.3.1(@types/node@25.9.5)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0)
+ transitivePeerDependencies:
+ - '@types/node'
+ - jiti
+ - less
+ - lightningcss
+ - sass
+ - sass-embedded
+ - stylus
+ - sugarss
+ - supports-color
+ - terser
+ - tsx
+ - yaml
+
vite@7.3.1(@types/node@22.15.31)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0):
dependencies:
esbuild: 0.27.2
@@ -21437,6 +21831,25 @@ snapshots:
terser: 5.49.0
tsx: 4.21.0
+ vite@7.3.1(@types/node@25.9.5)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0):
+ dependencies:
+ esbuild: 0.27.2
+ fdir: 6.5.0(picomatch@4.0.5)
+ picomatch: 4.0.5
+ postcss: 8.5.16
+ rollup: 4.62.2
+ tinyglobby: 0.2.17
+ optionalDependencies:
+ '@types/node': 25.9.5
+ fsevents: 2.3.3
+ jiti: 2.7.0
+ less: 4.6.7
+ lightningcss: 1.32.0
+ sass: 1.100.0
+ sass-embedded: 1.100.0
+ terser: 5.49.0
+ tsx: 4.21.0
+
w3c-xmlserializer@5.0.0:
dependencies:
xml-name-validator: 5.0.0
@@ -21490,10 +21903,10 @@ snapshots:
'@webassemblyjs/wasm-parser': 1.14.1
acorn: 8.17.0
acorn-import-phases: 1.0.4(acorn@8.17.0)
- browserslist: 4.28.5
+ browserslist: 4.28.6
chrome-trace-event: 1.0.4
- enhanced-resolve: 5.24.2
- es-module-lexer: 2.3.0
+ enhanced-resolve: 5.24.3
+ es-module-lexer: 2.3.1
eslint-scope: 5.1.1
events: 3.3.0
graceful-fs: 4.2.11
@@ -21529,10 +21942,10 @@ snapshots:
'@webassemblyjs/wasm-parser': 1.14.1
acorn: 8.17.0
acorn-import-phases: 1.0.4(acorn@8.17.0)
- browserslist: 4.28.5
+ browserslist: 4.28.6
chrome-trace-event: 1.0.4
- enhanced-resolve: 5.24.2
- es-module-lexer: 2.3.0
+ enhanced-resolve: 5.24.3
+ es-module-lexer: 2.3.1
eslint-scope: 5.1.1
events: 3.3.0
graceful-fs: 4.2.11
diff --git a/rstest.config.ts b/rstest.config.ts
index 95aacbba..8e7ba5a1 100644
--- a/rstest.config.ts
+++ b/rstest.config.ts
@@ -6,7 +6,6 @@ export default defineConfig({
'tests/react-router-framework/react-router-dev/__tests__/rsc-virtual-route-modules-test.ts',
],
exclude: ['**/node_modules/**', '**/dist/**', 'examples/**'],
- isolate: false,
testEnvironment: 'node',
setupFiles: ['./tests/setup.ts'],
});
From d005c48d6af8431b1bd21823569420f393fb61aa Mon Sep 17 00:00:00 2001
From: Zack Jackson <25274700+ScriptedAlchemy@users.noreply.github.com>
Date: Tue, 21 Jul 2026 06:17:18 +0000
Subject: [PATCH 47/53] fix: order effect runtime shutdown
---
src/effect-runtime.ts | 37 +++++++++++---
tests/effect-runtime.test.ts | 4 +-
.../integration/route-added-test.ts | 21 ++++++--
tests/route-watch.test.ts | 49 +++++++++++++++++++
4 files changed, 98 insertions(+), 13 deletions(-)
diff --git a/src/effect-runtime.ts b/src/effect-runtime.ts
index 1a78d89a..573713ce 100644
--- a/src/effect-runtime.ts
+++ b/src/effect-runtime.ts
@@ -14,16 +14,25 @@ import * as Scope from 'effect/Scope';
const PluginFibers = Context.GenericTag(
'rsbuild-plugin-react-router/PluginFibers'
);
+type PluginResources = { readonly scope: Scope.CloseableScope };
+const PluginResources: Context.Tag =
+ Context.GenericTag(
+ 'rsbuild-plugin-react-router/PluginResources'
+ );
-type PluginRuntimeContext = Scope.Scope | FiberSet.FiberSet;
+type PluginRuntimeContext = Scope.Scope | FiberSet.FiberSet | PluginResources;
const PluginRuntimeLive = Layer.scopedContext(
Effect.gen(function* () {
const scope = yield* Effect.scope;
const resources = yield* Scope.fork(scope, ExecutionStrategy.sequential);
- const fibers = yield* FiberSet.make();
+ const fiberScope = yield* Scope.fork(scope, ExecutionStrategy.sequential);
+ const fibers = yield* FiberSet.make().pipe(
+ Effect.provideService(Scope.Scope, fiberScope)
+ );
return Context.make(Scope.Scope, resources).pipe(
- Context.add(PluginFibers, fibers)
+ Context.add(PluginFibers, fibers),
+ Context.add(PluginResources, { scope: resources })
);
})
);
@@ -43,13 +52,27 @@ export const createPluginEffectRuntime = (): PluginEffectRuntime => {
return {
runPromise: runtime.runPromise,
- runFork: (effect, options) =>
- (fiberRunFork ??= runtime.runSync(
+ runFork: (effect, options) => {
+ if (disposePromise) return Effect.runFork(Effect.interrupt);
+ return (fiberRunFork ??= runtime.runSync(
Effect.flatMap(PluginFibers, fibers =>
FiberSet.runtime(fibers)()
)
- ))(effect, options),
- dispose: (): Promise => (disposePromise ??= runtime.dispose()),
+ ))(effect, options);
+ },
+ // Resource finalizers cancel their owned fibers before runtime disposal
+ // closes the fiber scope and interrupts any stragglers. Deferring by one
+ // microtask also lets a managed fiber request its own shutdown safely.
+ dispose: (): Promise =>
+ (disposePromise ??= Promise.resolve()
+ .then(() =>
+ runtime.runPromise(
+ Effect.flatMap(PluginResources, ({ scope }) =>
+ Scope.close(scope, Exit.void)
+ )
+ )
+ )
+ .finally(() => runtime.dispose())),
};
};
diff --git a/tests/effect-runtime.test.ts b/tests/effect-runtime.test.ts
index 7f45708c..53885bd3 100644
--- a/tests/effect-runtime.test.ts
+++ b/tests/effect-runtime.test.ts
@@ -23,7 +23,7 @@ const createDelayedTaskFixture = (delayMs: number) => {
};
describe('effect runtime helpers', () => {
- it('releases resources after interrupting fibers when the runtime is disposed', async () => {
+ it('releases resources before interrupting remaining fibers', async () => {
const events: string[] = [];
const runtime = createPluginEffectRuntime();
@@ -51,7 +51,7 @@ describe('effect runtime helpers', () => {
);
await Promise.all([runtime.dispose(), runtime.dispose()]);
- expect(events).toEqual(['acquire', 'fiber', 'release:resource']);
+ expect(events).toEqual(['acquire', 'release:resource', 'fiber']);
});
it('settles shutdown when a fiber forks during finalization', async () => {
diff --git a/tests/react-router-framework/integration/route-added-test.ts b/tests/react-router-framework/integration/route-added-test.ts
index 77fe10c2..f4cb228e 100644
--- a/tests/react-router-framework/integration/route-added-test.ts
+++ b/tests/react-router-framework/integration/route-added-test.ts
@@ -67,10 +67,23 @@ test.describe(async () => {
// client is not notified of new route addition (https://github.com/remix-run/remix/issues/7894)
// however server can handle new route
await expect
- .poll(async () => {
- await page.goto(`http://localhost:${port}/new`);
- return page.getByText("new route").isVisible();
- })
+ .poll(
+ async () => {
+ try {
+ await page.goto(`http://localhost:${port}/new`);
+ } catch (error) {
+ if (
+ error instanceof Error &&
+ error.message.includes("ERR_CONNECTION_REFUSED")
+ ) {
+ return false;
+ }
+ throw error;
+ }
+ return page.getByText("new route").isVisible();
+ },
+ { timeout: 10_000 },
+ )
.toBe(true);
});
});
diff --git a/tests/route-watch.test.ts b/tests/route-watch.test.ts
index 2abb4178..2174564c 100644
--- a/tests/route-watch.test.ts
+++ b/tests/route-watch.test.ts
@@ -32,6 +32,55 @@ const createRouteTopologyWatcher = async (
afterAll(() => routeWatchRuntime.dispose());
describe('route watch restart marker', () => {
+ it('settles runtime disposal triggered by a topology change', async () => {
+ const root = mkdtempSync(join(tmpdir(), 'rr-route-watch-'));
+ const markerPath = join(root, 'build/.react-router-route-watch');
+ const watchedDirectory = join(root, 'app');
+ mkdirSync(watchedDirectory, { recursive: true });
+ let topology = new Set(['initial']);
+ const errors: unknown[] = [];
+ let triggerChange!: () => void;
+ let disposePromise: Promise | undefined;
+ const runtime = createPluginEffectRuntime();
+
+ try {
+ await runtime.runPromise(
+ acquireRouteTopologyWatcher({
+ runtime,
+ watchDirectory: watchedDirectory,
+ restartMarkerPath: markerPath,
+ getRouteTopology: async () => topology,
+ onRouteTopologyChange: () => {
+ disposePromise = runtime.dispose();
+ },
+ onError: error => errors.push(error),
+ watchDirectoryEntry: (_directory, onChange) => {
+ triggerChange = onChange;
+ return { close: () => {} };
+ },
+ })
+ );
+
+ topology = new Set(['changed']);
+ triggerChange();
+
+ await expect.poll(() => disposePromise !== undefined).toBe(true);
+ expect(errors).toEqual([]);
+ if (!disposePromise) throw new Error('Expected runtime disposal.');
+ await expect(
+ Promise.race([
+ disposePromise,
+ new Promise((_, reject) =>
+ setTimeout(() => reject(new Error('dispose timed out')), 1_000)
+ ),
+ ])
+ ).resolves.toBeUndefined();
+ } finally {
+ await runtime.dispose();
+ rmSync(root, { recursive: true, force: true });
+ }
+ });
+
it('allows a topology callback to await watcher shutdown', async () => {
const root = mkdtempSync(join(tmpdir(), 'rr-route-watch-'));
const markerPath = join(root, 'build/.react-router-route-watch');
From 27ab2ce254aaba69cff13c0e4be0fa69897ada67 Mon Sep 17 00:00:00 2001
From: Zack Jackson <25274700+ScriptedAlchemy@users.noreply.github.com>
Date: Tue, 21 Jul 2026 07:25:33 +0000
Subject: [PATCH 48/53] chore: version packages
---
.../align-refresh-registration-backfill.md | 8 -------
.changeset/classic-dev-hmr.md | 5 ----
.changeset/docs-and-playwright.md | 6 -----
.changeset/export-config-type.md | 11 ---------
.../prerender-legacy-and-warning-paths.md | 7 ------
.changeset/test-ci-hygiene.md | 6 -----
CHANGELOG.md | 23 +++++++++++++++++++
package.json | 2 +-
8 files changed, 24 insertions(+), 44 deletions(-)
delete mode 100644 .changeset/align-refresh-registration-backfill.md
delete mode 100644 .changeset/classic-dev-hmr.md
delete mode 100644 .changeset/docs-and-playwright.md
delete mode 100644 .changeset/export-config-type.md
delete mode 100644 .changeset/prerender-legacy-and-warning-paths.md
delete mode 100644 .changeset/test-ci-hygiene.md
diff --git a/.changeset/align-refresh-registration-backfill.md b/.changeset/align-refresh-registration-backfill.md
deleted file mode 100644
index bf9cba17..00000000
--- a/.changeset/align-refresh-registration-backfill.md
+++ /dev/null
@@ -1,8 +0,0 @@
----
-'rsbuild-plugin-react-router': patch
----
-
-Align the Fast Refresh registration backfill with react-refresh's own
-component-detection rules so `memo`/`forwardRef` components in pre-lowered
-(MDX) routes register for HMR. Multi-declarator lists, curried arrows, and
-require/import interop callees no longer produce false registrations.
diff --git a/.changeset/classic-dev-hmr.md b/.changeset/classic-dev-hmr.md
deleted file mode 100644
index ae7ed986..00000000
--- a/.changeset/classic-dev-hmr.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'rsbuild-plugin-react-router': minor
----
-
-Add state-preserving Hot Module Replacement for route modules in development: route updates now apply React Refresh registration and in-place route patching instead of triggering a full page reload. Server code changes also trigger hot data revalidation, so loader data refreshes without a reload. This degrades gracefully to the previous full-reload behavior when `@rsbuild/plugin-react` isn't present or Fast Refresh is disabled.
diff --git a/.changeset/docs-and-playwright.md b/.changeset/docs-and-playwright.md
deleted file mode 100644
index 8294338f..00000000
--- a/.changeset/docs-and-playwright.md
+++ /dev/null
@@ -1,6 +0,0 @@
----
----
-
-Docs and test tooling only — no published code changes: README dependency
-examples now match the plugin's actual `^7.13.0` floor, Playwright is bumped
-to 1.61, and CI installs browsers via the locally pinned binary.
diff --git a/.changeset/export-config-type.md b/.changeset/export-config-type.md
deleted file mode 100644
index 6228d36a..00000000
--- a/.changeset/export-config-type.md
+++ /dev/null
@@ -1,11 +0,0 @@
----
-'rsbuild-plugin-react-router': patch
----
-
-Export `ReactRouterRsbuildConfig`, the plugin's typed `react-router.config.*`
-shape (React Router's `Config` plus plugin-supported options such as
-`splitRouteModules`), so projects no longer need to import types from
-`@react-router/dev/config` or hand-roll intersections. `@react-router/dev`
-is declared as an optional peer dependency since the exported config and
-route types resolve from it; this is not a breaking change — nothing new is
-required at install or runtime.
diff --git a/.changeset/prerender-legacy-and-warning-paths.md b/.changeset/prerender-legacy-and-warning-paths.md
deleted file mode 100644
index ef9be19c..00000000
--- a/.changeset/prerender-legacy-and-warning-paths.md
+++ /dev/null
@@ -1,7 +0,0 @@
----
-'rsbuild-plugin-react-router': patch
----
-
-Prerender fixes: root route data now flows through the legacy handler path,
-and the dynamic/splat prerender warning strips the leading slash only for
-top-level dynamic segments so nested paths are reported correctly.
diff --git a/.changeset/test-ci-hygiene.md b/.changeset/test-ci-hygiene.md
deleted file mode 100644
index 188dc64a..00000000
--- a/.changeset/test-ci-hygiene.md
+++ /dev/null
@@ -1,6 +0,0 @@
----
----
-
-Test and repo hygiene only — no published code changes, no release needed:
-remove a committed Playwright artifact and ignore nested Playwright output
-directories.
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c4654259..969209bd 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,28 @@
# rsbuild-plugin-react-router
+## 0.4.0
+
+### Minor Changes
+
+- 2c155f2: Add state-preserving Hot Module Replacement for route modules in development: route updates now apply React Refresh registration and in-place route patching instead of triggering a full page reload. Server code changes also trigger hot data revalidation, so loader data refreshes without a reload. This degrades gracefully to the previous full-reload behavior when `@rsbuild/plugin-react` isn't present or Fast Refresh is disabled.
+
+### Patch Changes
+
+- 2c155f2: Align the Fast Refresh registration backfill with react-refresh's own
+ component-detection rules so `memo`/`forwardRef` components in pre-lowered
+ (MDX) routes register for HMR. Multi-declarator lists, curried arrows, and
+ require/import interop callees no longer produce false registrations.
+- 96ed301: Export `ReactRouterRsbuildConfig`, the plugin's typed `react-router.config.*`
+ shape (React Router's `Config` plus plugin-supported options such as
+ `splitRouteModules`), so projects no longer need to import types from
+ `@react-router/dev/config` or hand-roll intersections. `@react-router/dev`
+ is declared as an optional peer dependency since the exported config and
+ route types resolve from it; this is not a breaking change — nothing new is
+ required at install or runtime.
+- 35a2036: Prerender fixes: root route data now flows through the legacy handler path,
+ and the dynamic/splat prerender warning strips the leading slash only for
+ top-level dynamic segments so nested paths are reported correctly.
+
## 0.3.1
### Patch Changes
diff --git a/package.json b/package.json
index e8130f7a..1bfc869c 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "rsbuild-plugin-react-router",
- "version": "0.3.1",
+ "version": "0.4.0",
"description": "React Router plugin for Rsbuild",
"repository": {
"type": "git",
From dde2a2d08396d0d27913f561c7b5a0f3f549ee4b Mon Sep 17 00:00:00 2001
From: Zack Jackson <25274700+ScriptedAlchemy@users.noreply.github.com>
Date: Tue, 21 Jul 2026 22:01:27 +0000
Subject: [PATCH 49/53] fix: preserve in-memory dev output
---
.changeset/quiet-disks-rest.md | 5 +++++
.../tests/e2e/dev-route-watch.test.ts | 4 ----
src/index.ts | 1 -
tests/features.test.ts | 2 +-
tests/index.test.ts | 15 ++++++++++++++-
5 files changed, 20 insertions(+), 7 deletions(-)
create mode 100644 .changeset/quiet-disks-rest.md
diff --git a/.changeset/quiet-disks-rest.md b/.changeset/quiet-disks-rest.md
new file mode 100644
index 00000000..3f881289
--- /dev/null
+++ b/.changeset/quiet-disks-rest.md
@@ -0,0 +1,5 @@
+---
+'rsbuild-plugin-react-router': patch
+---
+
+Stop forcing development output to disk so `dev.writeToDisk: false` and Rsbuild's in-memory default are preserved.
diff --git a/examples/default-template/tests/e2e/dev-route-watch.test.ts b/examples/default-template/tests/e2e/dev-route-watch.test.ts
index 56dceef2..cc0e3ed2 100644
--- a/examples/default-template/tests/e2e/dev-route-watch.test.ts
+++ b/examples/default-template/tests/e2e/dev-route-watch.test.ts
@@ -144,7 +144,6 @@ test.describe('dev route watch', () => {
}) => {
await page.goto('/');
await expect(page.locator('h1')).toContainText('Welcome to React Router');
- const restartMarkerBeforeAdd = readRestartMarkerVersion();
writeFileSync(
addedRoutePath,
@@ -161,9 +160,6 @@ test.describe('dev route watch', () => {
await page.goto(addedRouteUrl);
await expect(page.locator('h1')).toHaveText(addedRouteText);
- await expect
- .poll(readRestartMarkerVersion, { timeout: 10000 })
- .not.toBe(restartMarkerBeforeAdd);
const restartMarkerBefore = readRestartMarkerVersion();
writeFileSync(
addedRoutePath,
diff --git a/src/index.ts b/src/index.ts
index 7144c910..f7abb10b 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -800,7 +800,6 @@ export const pluginReactRouter = (
assetPrefix: config.output?.assetPrefix || '/',
},
dev: {
- writeToDisk: true,
...lazyCompilation,
watchFiles: mergeWatchFiles(config.dev?.watchFiles, routeWatchFiles),
},
diff --git a/tests/features.test.ts b/tests/features.test.ts
index 6e1489f2..28b505cf 100644
--- a/tests/features.test.ts
+++ b/tests/features.test.ts
@@ -33,7 +33,7 @@ describe('pluginReactRouter', () => {
// The plugin should not override Rsbuild's HMR defaults.
expect(config.dev.hmr).toBe(true);
expect(config.dev.liveReload).toBe(true);
- expect(config.dev.writeToDisk).toBe(true);
+ expect(config.dev.writeToDisk).toBe(false);
});
it('should register the dev server middleware via server.setup', async () => {
diff --git a/tests/index.test.ts b/tests/index.test.ts
index 617e1683..8452bebd 100644
--- a/tests/index.test.ts
+++ b/tests/index.test.ts
@@ -60,7 +60,7 @@ describe('pluginReactRouter', () => {
// The plugin should not override Rsbuild's HMR defaults.
expect(config.dev.hmr).toBe(true);
expect(config.dev.liveReload).toBe(true);
- expect(config.dev.writeToDisk).toBe(true);
+ expect(config.dev.writeToDisk).toBe(false);
expect(config.dev.lazyCompilation).toMatchObject({
entries: true,
imports: true,
@@ -72,6 +72,19 @@ describe('pluginReactRouter', () => {
).toBe(false);
});
+ it('preserves an explicit writeToDisk override', async () => {
+ const rsbuild = await createStubRsbuild({
+ rsbuildConfig: {
+ dev: { writeToDisk: true },
+ },
+ });
+
+ rsbuild.addPlugins([pluginReactRouter()]);
+ const config = await rsbuild.unwrapConfig();
+
+ expect(config.dev.writeToDisk).toBe(true);
+ });
+
it('adds the committed custom-server build entry only in development', async () => {
const devRsbuild = await createStubRsbuild({ rsbuildConfig: {} });
devRsbuild.addPlugins([pluginReactRouter({ customServer: true })]);
From 94ed3cc93841f4a3e5b6dd02ed2f4cd24dcef2ed Mon Sep 17 00:00:00 2001
From: Zack Jackson <25274700+ScriptedAlchemy@users.noreply.github.com>
Date: Tue, 21 Jul 2026 22:38:54 +0000
Subject: [PATCH 50/53] chore: version packages
---
.changeset/quiet-disks-rest.md | 5 -----
CHANGELOG.md | 6 ++++++
package.json | 2 +-
3 files changed, 7 insertions(+), 6 deletions(-)
delete mode 100644 .changeset/quiet-disks-rest.md
diff --git a/.changeset/quiet-disks-rest.md b/.changeset/quiet-disks-rest.md
deleted file mode 100644
index 3f881289..00000000
--- a/.changeset/quiet-disks-rest.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'rsbuild-plugin-react-router': patch
----
-
-Stop forcing development output to disk so `dev.writeToDisk: false` and Rsbuild's in-memory default are preserved.
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 969209bd..a2b8b261 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,11 @@
# rsbuild-plugin-react-router
+## 0.4.1
+
+### Patch Changes
+
+- dde2a2d: Stop forcing development output to disk so `dev.writeToDisk: false` and Rsbuild's in-memory default are preserved.
+
## 0.4.0
### Minor Changes
diff --git a/package.json b/package.json
index 1bfc869c..bffd2486 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "rsbuild-plugin-react-router",
- "version": "0.4.0",
+ "version": "0.4.1",
"description": "React Router plugin for Rsbuild",
"repository": {
"type": "git",
From 5c9499719577940482df4dc1aef3b6e5f106b305 Mon Sep 17 00:00:00 2001
From: Zack Jackson <25274700+ScriptedAlchemy@users.noreply.github.com>
Date: Sat, 25 Jul 2026 01:05:55 +0000
Subject: [PATCH 51/53] test: update rstest to 0.11.4
---
package.json | 4 +-
pnpm-lock.yaml | 235 ++++++++++++++++++++++++-------------------------
2 files changed, 116 insertions(+), 123 deletions(-)
diff --git a/package.json b/package.json
index 46c32831..0d6d48bb 100644
--- a/package.json
+++ b/package.json
@@ -125,8 +125,8 @@
"@rsbuild/plugin-tailwindcss": "2.0.3",
"@rslib/core": "^0.22.1",
"@rspack/core": "2.1.0",
- "@rstest/core": "^0.11.2",
- "@rstest/coverage-istanbul": "^0.11.2",
+ "@rstest/core": "^0.11.4",
+ "@rstest/coverage-istanbul": "^0.11.4",
"@swc/helpers": "^0.5.23",
"@types/fs-extra": "11.0.4",
"@types/jsesc": "^3.0.3",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index e06820bc..e12f8dac 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -19,7 +19,7 @@ importers:
version: 0.13.3
'@rspack/plugin-react-refresh':
specifier: ^2.0.2
- version: 2.0.2(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(react-refresh@0.18.0)
+ version: 2.0.2(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(react-refresh@0.18.0)
execa:
specifier: ^9.6.1
version: 9.6.1
@@ -80,34 +80,34 @@ importers:
version: 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)
'@rsbuild/plugin-less':
specifier: 1.6.4
- version: 1.6.4(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
+ version: 1.6.4(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
'@rsbuild/plugin-mdx':
specifier: ^1.1.3
- version: 1.1.3(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(webpack@5.108.3(lightningcss@1.32.0))
+ version: 1.1.3(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(webpack@5.108.3(lightningcss@1.32.0))
'@rsbuild/plugin-react':
specifier: 2.1.0
- version: 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))
+ version: 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))
'@rsbuild/plugin-sass':
specifier: 1.5.3
- version: 1.5.3(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))
+ version: 1.5.3(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))
'@rsbuild/plugin-svgr':
specifier: 2.0.4
- version: 2.0.4(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(typescript@5.9.3)
+ version: 2.0.4(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(typescript@5.9.3)
'@rsbuild/plugin-tailwindcss':
specifier: 2.0.3
- version: 2.0.3(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
+ version: 2.0.3(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
'@rslib/core':
specifier: ^0.22.1
- version: 0.22.1(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0)(typescript@5.9.3)
+ version: 0.22.1(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)(typescript@5.9.3)
'@rspack/core':
specifier: 2.1.0
version: 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)
'@rstest/core':
- specifier: ^0.11.2
- version: 0.11.2(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0)(jsdom@27.4.0(@noble/hashes@2.2.0))
+ specifier: ^0.11.4
+ version: 0.11.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)(jsdom@27.4.0(@noble/hashes@2.2.0))
'@rstest/coverage-istanbul':
- specifier: ^0.11.2
- version: 0.11.2(@rstest/core@0.11.2(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0)(jsdom@27.4.0(@noble/hashes@2.2.0)))
+ specifier: ^0.11.4
+ version: 0.11.4(@rstest/core@0.11.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)(jsdom@27.4.0(@noble/hashes@2.2.0)))
'@swc/helpers':
specifier: ^0.5.23
version: 0.5.23
@@ -182,10 +182,10 @@ importers:
version: 7.18.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
react-server-dom-rspack:
specifier: 0.0.2
- version: 0.0.2(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ version: 0.0.2(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
rsbuild-plugin-rsc:
specifier: ^0.1.1
- version: 0.1.1(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(react-server-dom-rspack@0.0.2(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(react-dom@19.2.7(react@19.2.7))(react@19.2.7))
+ version: 0.1.1(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(react-server-dom-rspack@0.0.2(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(react-dom@19.2.7(react@19.2.7))(react@19.2.7))
semver:
specifier: ^7.8.5
version: 7.8.5
@@ -276,16 +276,16 @@ importers:
version: 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)
'@rsbuild/plugin-babel':
specifier: 2.0.0
- version: 2.0.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))
+ version: 2.0.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))
'@rsbuild/plugin-react':
specifier: 2.1.0
version: 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))
'@rsbuild/plugin-svgr':
specifier: 2.0.4
- version: 2.0.4(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(typescript@6.0.1-rc)
+ version: 2.0.4(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(typescript@6.0.1-rc)
'@rsbuild/plugin-tailwindcss':
specifier: 2.0.3
- version: 2.0.3(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
+ version: 2.0.3(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
'@types/babel__core':
specifier: 7.20.5
version: 7.20.5
@@ -318,7 +318,7 @@ importers:
version: 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)
'@rslib/core':
specifier: 0.22.1
- version: 0.22.1(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0)(typescript@5.9.3)
+ version: 0.22.1(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)(typescript@5.9.3)
'@types/node':
specifier: ^25.0.10
version: 25.0.10
@@ -480,7 +480,7 @@ importers:
version: 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))
'@rsdoctor/rspack-plugin':
specifier: ^1.5.13
- version: 1.5.17(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
+ version: 1.5.17(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
'@tailwindcss/postcss':
specifier: ^4.1.18
version: 4.3.2
@@ -541,13 +541,13 @@ importers:
version: 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)
'@rsbuild/plugin-less':
specifier: ^1.6.4
- version: 1.6.4(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
+ version: 1.6.4(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
'@rsbuild/plugin-react':
specifier: ^2.1.0
version: 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))
'@rsbuild/plugin-sass':
specifier: ^1.5.3
- version: 1.5.3(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))
+ version: 1.5.3(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))
'@tailwindcss/postcss':
specifier: ^4.1.18
version: 4.3.2
@@ -1824,7 +1824,7 @@ importers:
version: 8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
react-server-dom-rspack:
specifier: 0.0.2
- version: 0.0.2(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ version: 0.0.2(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
devDependencies:
'@playwright/test':
specifier: ^1.58.0
@@ -1855,7 +1855,7 @@ importers:
version: link:../..
rsbuild-plugin-rsc:
specifier: ^0.1.1
- version: 0.1.1(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(react-server-dom-rspack@0.0.2(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(react-dom@19.2.7(react@19.2.7))(react@19.2.7))
+ version: 0.1.1(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(react-server-dom-rspack@0.0.2(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(react-dom@19.2.7(react@19.2.7))(react@19.2.7))
typescript:
specifier: ^5.9.3
version: 5.9.3
@@ -2018,7 +2018,7 @@ importers:
version: 8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
react-server-dom-rspack:
specifier: 0.0.2
- version: 0.0.2(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ version: 0.0.2(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
semver:
specifier: ^7.8.1
version: 7.8.5
@@ -2097,22 +2097,22 @@ importers:
version: 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)
'@rsbuild/plugin-less':
specifier: 1.6.4
- version: 1.6.4(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
+ version: 1.6.4(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
'@rsbuild/plugin-mdx':
specifier: ^1.1.3
- version: 1.1.3(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(webpack@5.108.3(lightningcss@1.32.0))
+ version: 1.1.3(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(webpack@5.108.3(lightningcss@1.32.0))
'@rsbuild/plugin-react':
specifier: 2.1.0
version: 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))
'@rsbuild/plugin-sass':
specifier: 1.5.3
- version: 1.5.3(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))
+ version: 1.5.3(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))
'@rsbuild/plugin-svgr':
specifier: 2.0.4
- version: 2.0.4(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(typescript@5.9.3)
+ version: 2.0.4(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(typescript@5.9.3)
'@rsbuild/plugin-tailwindcss':
specifier: 2.0.3
- version: 2.0.3(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
+ version: 2.0.3(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
'@types/node':
specifier: ^22.19.19
version: 22.20.0
@@ -2175,7 +2175,7 @@ importers:
version: 8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
react-server-dom-rspack:
specifier: 0.0.2
- version: 0.0.2(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ version: 0.0.2(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
devDependencies:
'@mdx-js/rollup':
specifier: ^3.1.1
@@ -2194,22 +2194,22 @@ importers:
version: 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)
'@rsbuild/plugin-less':
specifier: 1.6.4
- version: 1.6.4(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
+ version: 1.6.4(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
'@rsbuild/plugin-mdx':
specifier: ^1.1.3
- version: 1.1.3(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(webpack@5.108.3(lightningcss@1.32.0))
+ version: 1.1.3(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(webpack@5.108.3(lightningcss@1.32.0))
'@rsbuild/plugin-react':
specifier: 2.1.0
version: 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))
'@rsbuild/plugin-sass':
specifier: 1.5.3
- version: 1.5.3(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))
+ version: 1.5.3(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))
'@rsbuild/plugin-svgr':
specifier: 2.0.4
- version: 2.0.4(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(typescript@5.9.3)
+ version: 2.0.4(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(typescript@5.9.3)
'@rsbuild/plugin-tailwindcss':
specifier: 2.0.3
- version: 2.0.3(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
+ version: 2.0.3(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
'@types/express':
specifier: ^5.0.0
version: 5.0.6
@@ -2236,7 +2236,7 @@ importers:
version: link:../../../../..
rsbuild-plugin-rsc:
specifier: ^0.1.1
- version: 0.1.1(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(react-server-dom-rspack@0.0.2(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(react-dom@19.2.7(react@19.2.7))(react@19.2.7))
+ version: 0.1.1(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(react-server-dom-rspack@0.0.2(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(react-dom@19.2.7(react@19.2.7))(react@19.2.7))
tailwindcss:
specifier: 4.3.2
version: 4.3.2
@@ -2269,7 +2269,7 @@ importers:
version: 8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
react-server-dom-rspack:
specifier: 0.0.2
- version: 0.0.2(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ version: 0.0.2(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
devDependencies:
'@rsbuild/core':
specifier: 2.1.0
@@ -2297,7 +2297,7 @@ importers:
version: link:../../../../..
rsbuild-plugin-rsc:
specifier: ^0.1.1
- version: 0.1.1(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(react-server-dom-rspack@0.0.2(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(react-dom@19.2.7(react@19.2.7))(react@19.2.7))
+ version: 0.1.1(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(react-server-dom-rspack@0.0.2(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(react-dom@19.2.7(react@19.2.7))(react@19.2.7))
typescript:
specifier: ^5.9.3
version: 5.9.3
@@ -5533,8 +5533,8 @@ packages:
'@rspack/resolver@0.2.8':
resolution: {integrity: sha512-FBWqdHhzS8mcf/WN4Ktzr7EaeaN+hsxbN98EweegX3924beZuY6H70CSFWCv1fIHAieCUv/9XCjKggHvhCsLwA==}
- '@rstest/core@0.11.2':
- resolution: {integrity: sha512-/CTLkvogDGozY+bCYsc+sKJDDsU/LvDdEZC545+QO1Iv9ds+6vgSWuA+Q2iQF3cReeSkOI9ySARfFemcCoSKpQ==}
+ '@rstest/core@0.11.4':
+ resolution: {integrity: sha512-HfBP4mmvLVq2vpyRvHHaeu1+K2e9XSjDCHbj15s+NExnniS2b+y/ABXyjJldpRSoiirKnfIqY7BVGOaO+G+W0w==}
engines: {node: ^20.19.0 || >=22.12.0}
hasBin: true
peerDependencies:
@@ -5559,10 +5559,10 @@ packages:
jsdom:
optional: true
- '@rstest/coverage-istanbul@0.11.2':
- resolution: {integrity: sha512-Zynn5AhEsVky02/YNM6008/kgGoT/B/kJTRUpff00nIAbQBmbGYOesk1T8xtDwo6c3Zja4EgTvtsTtML3S80UQ==}
+ '@rstest/coverage-istanbul@0.11.4':
+ resolution: {integrity: sha512-InsiiS3koVjBoiIlBDLcpGT8njCtmqWzr5NyY8abRlOux8MCaEyodialg6GZXy1PNhvR2FyCiy2kFFRxxNDjgw==}
peerDependencies:
- '@rstest/core': ^0.11.2
+ '@rstest/core': ^0.11.4
'@rstest/coverage-istanbul@0.2.0':
resolution: {integrity: sha512-ghFY2XkjL2PFarLaG+BxFTJYbVXtAtcqW5rjOqheiOYV6PqlOHFf/s+fLkKeOTc1oVoL3A/9vjMBc8fOgjAF/A==}
@@ -14713,9 +14713,9 @@ snapshots:
core-js: 3.47.0
jiti: 2.7.0
- '@rsbuild/core@2.0.15(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0)':
+ '@rsbuild/core@2.0.15(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)':
dependencies:
- '@rspack/core': 2.0.8(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23)
+ '@rspack/core': 2.0.8(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)
'@swc/helpers': 0.5.23
optionalDependencies:
core-js: 3.47.0
@@ -14731,7 +14731,7 @@ snapshots:
transitivePeerDependencies:
- '@module-federation/runtime-tools'
- '@rsbuild/core@2.1.6(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0)':
+ '@rsbuild/core@2.1.6(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)':
dependencies:
'@rspack/core': 2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)
'@swc/helpers': 0.5.23
@@ -14740,7 +14740,7 @@ snapshots:
transitivePeerDependencies:
- '@module-federation/runtime-tools'
- '@rsbuild/plugin-babel@2.0.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))':
+ '@rsbuild/plugin-babel@2.0.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))':
dependencies:
'@babel/core': 7.29.7
'@babel/plugin-proposal-decorators': 7.29.7(@babel/core@7.29.7)
@@ -14753,7 +14753,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@rsbuild/plugin-check-syntax@1.6.1(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))':
+ '@rsbuild/plugin-check-syntax@1.6.1(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))':
dependencies:
acorn: 8.17.0
browserslist-to-es-version: 1.4.2
@@ -14763,11 +14763,11 @@ snapshots:
optionalDependencies:
'@rsbuild/core': 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)
- '@rsbuild/plugin-less@1.6.4(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))':
+ '@rsbuild/plugin-less@1.6.4(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))':
dependencies:
deepmerge: 4.3.1
less: 4.6.7
- less-loader: 12.3.3(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(less@4.6.7)(webpack@5.108.3(lightningcss@1.32.0))
+ less-loader: 12.3.3(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(less@4.6.7)(webpack@5.108.3(lightningcss@1.32.0))
reduce-configs: 1.1.2
optionalDependencies:
'@rsbuild/core': 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)
@@ -14775,11 +14775,11 @@ snapshots:
- '@rspack/core'
- webpack
- '@rsbuild/plugin-less@1.6.4(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))':
+ '@rsbuild/plugin-less@1.6.4(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))':
dependencies:
deepmerge: 4.3.1
less: 4.6.7
- less-loader: 12.3.3(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(less@4.6.7)(webpack@5.108.3(lightningcss@1.32.0))
+ less-loader: 12.3.3(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(less@4.6.7)(webpack@5.108.3(lightningcss@1.32.0))
reduce-configs: 1.1.2
optionalDependencies:
'@rsbuild/core': 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)
@@ -14787,7 +14787,7 @@ snapshots:
- '@rspack/core'
- webpack
- '@rsbuild/plugin-mdx@1.1.3(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(webpack@5.108.3(lightningcss@1.32.0))':
+ '@rsbuild/plugin-mdx@1.1.3(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(webpack@5.108.3(lightningcss@1.32.0))':
dependencies:
'@mdx-js/loader': 3.1.1(webpack@5.108.3(lightningcss@1.32.0))
optionalDependencies:
@@ -14796,25 +14796,16 @@ snapshots:
- supports-color
- webpack
- '@rsbuild/plugin-react@2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))':
+ '@rsbuild/plugin-react@2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))':
dependencies:
- '@rspack/plugin-react-refresh': 2.0.2(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(react-refresh@0.18.0)
+ '@rspack/plugin-react-refresh': 2.0.2(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(react-refresh@0.18.0)
react-refresh: 0.18.0
optionalDependencies:
'@rsbuild/core': 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)
transitivePeerDependencies:
- '@rspack/core'
- '@rsbuild/plugin-react@2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))':
- dependencies:
- '@rspack/plugin-react-refresh': 2.0.2(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(react-refresh@0.18.0)
- react-refresh: 0.18.0
- optionalDependencies:
- '@rsbuild/core': 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)
- transitivePeerDependencies:
- - '@rspack/core'
-
- '@rsbuild/plugin-react@2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))':
+ '@rsbuild/plugin-react@2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))':
dependencies:
'@rspack/plugin-react-refresh': 2.0.2(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(react-refresh@0.18.0)
react-refresh: 0.18.0
@@ -14823,7 +14814,7 @@ snapshots:
transitivePeerDependencies:
- '@rspack/core'
- '@rsbuild/plugin-sass@1.5.3(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))':
+ '@rsbuild/plugin-sass@1.5.3(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))':
dependencies:
deepmerge: 4.3.1
loader-utils: 2.0.4
@@ -14833,9 +14824,9 @@ snapshots:
optionalDependencies:
'@rsbuild/core': 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)
- '@rsbuild/plugin-svgr@2.0.4(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(typescript@5.9.3)':
+ '@rsbuild/plugin-svgr@2.0.4(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(typescript@5.9.3)':
dependencies:
- '@rsbuild/plugin-react': 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))
+ '@rsbuild/plugin-react': 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))
'@svgr/core': 8.1.0(typescript@5.9.3)
'@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0(typescript@5.9.3))
'@svgr/plugin-svgo': 8.1.0(@svgr/core@8.1.0(typescript@5.9.3))(typescript@5.9.3)
@@ -14848,9 +14839,9 @@ snapshots:
- supports-color
- typescript
- '@rsbuild/plugin-svgr@2.0.4(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(typescript@5.9.3)':
+ '@rsbuild/plugin-svgr@2.0.4(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(typescript@5.9.3)':
dependencies:
- '@rsbuild/plugin-react': 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))
+ '@rsbuild/plugin-react': 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))
'@svgr/core': 8.1.0(typescript@5.9.3)
'@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0(typescript@5.9.3))
'@svgr/plugin-svgo': 8.1.0(@svgr/core@8.1.0(typescript@5.9.3))(typescript@5.9.3)
@@ -14863,9 +14854,9 @@ snapshots:
- supports-color
- typescript
- '@rsbuild/plugin-svgr@2.0.4(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(typescript@6.0.1-rc)':
+ '@rsbuild/plugin-svgr@2.0.4(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(typescript@6.0.1-rc)':
dependencies:
- '@rsbuild/plugin-react': 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))
+ '@rsbuild/plugin-react': 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))
'@svgr/core': 8.1.0(typescript@6.0.1-rc)
'@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0(typescript@5.9.3))
'@svgr/plugin-svgo': 8.1.0(@svgr/core@8.1.0(typescript@5.9.3))(typescript@6.0.1-rc)
@@ -14878,18 +14869,18 @@ snapshots:
- supports-color
- typescript
- '@rsbuild/plugin-tailwindcss@2.0.3(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))':
+ '@rsbuild/plugin-tailwindcss@2.0.3(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))':
dependencies:
- '@tailwindcss/webpack': 4.3.2(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
+ '@tailwindcss/webpack': 4.3.2(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
optionalDependencies:
'@rsbuild/core': 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)
transitivePeerDependencies:
- '@rspack/core'
- webpack
- '@rsbuild/plugin-tailwindcss@2.0.3(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))':
+ '@rsbuild/plugin-tailwindcss@2.0.3(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))':
dependencies:
- '@tailwindcss/webpack': 4.3.2(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
+ '@tailwindcss/webpack': 4.3.2(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
optionalDependencies:
'@rsbuild/core': 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)
transitivePeerDependencies:
@@ -14898,13 +14889,13 @@ snapshots:
'@rsdoctor/client@1.5.17': {}
- '@rsdoctor/core@1.5.17(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))':
+ '@rsdoctor/core@1.5.17(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))':
dependencies:
- '@rsbuild/plugin-check-syntax': 1.6.1(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))
- '@rsdoctor/graph': 1.5.17(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
- '@rsdoctor/sdk': 1.5.17(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
- '@rsdoctor/types': 1.5.17(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
- '@rsdoctor/utils': 1.5.17(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
+ '@rsbuild/plugin-check-syntax': 1.6.1(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))
+ '@rsdoctor/graph': 1.5.17(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
+ '@rsdoctor/sdk': 1.5.17(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
+ '@rsdoctor/types': 1.5.17(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
+ '@rsdoctor/utils': 1.5.17(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
'@rspack/resolver': 0.2.8(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)
browserslist-load-config: 1.0.3
es-toolkit: 1.49.0
@@ -14922,10 +14913,10 @@ snapshots:
- utf-8-validate
- webpack
- '@rsdoctor/graph@1.5.17(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))':
+ '@rsdoctor/graph@1.5.17(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))':
dependencies:
- '@rsdoctor/types': 1.5.17(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
- '@rsdoctor/utils': 1.5.17(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
+ '@rsdoctor/types': 1.5.17(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
+ '@rsdoctor/utils': 1.5.17(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
es-toolkit: 1.49.0
path-browserify: 1.0.1
source-map: 0.7.6
@@ -14933,13 +14924,13 @@ snapshots:
- '@rspack/core'
- webpack
- '@rsdoctor/rspack-plugin@1.5.17(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))':
+ '@rsdoctor/rspack-plugin@1.5.17(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))':
dependencies:
- '@rsdoctor/core': 1.5.17(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
- '@rsdoctor/graph': 1.5.17(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
- '@rsdoctor/sdk': 1.5.17(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
- '@rsdoctor/types': 1.5.17(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
- '@rsdoctor/utils': 1.5.17(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
+ '@rsdoctor/core': 1.5.17(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
+ '@rsdoctor/graph': 1.5.17(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
+ '@rsdoctor/sdk': 1.5.17(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
+ '@rsdoctor/types': 1.5.17(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
+ '@rsdoctor/utils': 1.5.17(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
optionalDependencies:
'@rspack/core': 2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)
transitivePeerDependencies:
@@ -14951,12 +14942,12 @@ snapshots:
- utf-8-validate
- webpack
- '@rsdoctor/sdk@1.5.17(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))':
+ '@rsdoctor/sdk@1.5.17(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))':
dependencies:
'@rsdoctor/client': 1.5.17
- '@rsdoctor/graph': 1.5.17(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
- '@rsdoctor/types': 1.5.17(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
- '@rsdoctor/utils': 1.5.17(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
+ '@rsdoctor/graph': 1.5.17(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
+ '@rsdoctor/types': 1.5.17(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
+ '@rsdoctor/utils': 1.5.17(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
launch-editor: 2.14.1
safer-buffer: 2.1.2
socket.io: 4.8.1
@@ -14968,7 +14959,7 @@ snapshots:
- utf-8-validate
- webpack
- '@rsdoctor/types@1.5.17(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))':
+ '@rsdoctor/types@1.5.17(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))':
dependencies:
'@types/connect': 3.4.38
'@types/estree': 1.0.5
@@ -14978,10 +14969,10 @@ snapshots:
'@rspack/core': 2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)
webpack: 5.108.3(lightningcss@1.32.0)
- '@rsdoctor/utils@1.5.17(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))':
+ '@rsdoctor/utils@1.5.17(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))':
dependencies:
'@babel/code-frame': 7.26.2
- '@rsdoctor/types': 1.5.17(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
+ '@rsdoctor/types': 1.5.17(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))
'@types/estree': 1.0.5
acorn: 8.17.0
acorn-import-attributes: 1.9.5(acorn@8.17.0)
@@ -14999,10 +14990,10 @@ snapshots:
- '@rspack/core'
- webpack
- '@rslib/core@0.22.1(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0)(typescript@5.9.3)':
+ '@rslib/core@0.22.1(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)(typescript@5.9.3)':
dependencies:
- '@rsbuild/core': 2.0.15(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0)
- rsbuild-plugin-dts: 0.22.1(@rsbuild/core@2.0.15(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(typescript@5.9.3)
+ '@rsbuild/core': 2.0.15(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)
+ rsbuild-plugin-dts: 0.22.1(@rsbuild/core@2.0.15(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(typescript@5.9.3)
optionalDependencies:
typescript: 5.9.3
transitivePeerDependencies:
@@ -15220,7 +15211,7 @@ snapshots:
optionalDependencies:
'@swc/helpers': 0.5.23
- '@rspack/core@2.0.8(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23)':
+ '@rspack/core@2.0.8(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)':
dependencies:
'@rspack/binding': 2.0.8
optionalDependencies:
@@ -15245,7 +15236,7 @@ snapshots:
'@rspack/lite-tapable@1.1.2': {}
- '@rspack/plugin-react-refresh@2.0.2(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(react-refresh@0.18.0)':
+ '@rspack/plugin-react-refresh@2.0.2(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(react-refresh@0.18.0)':
dependencies:
react-refresh: 0.18.0
optionalDependencies:
@@ -15308,9 +15299,9 @@ snapshots:
- '@emnapi/core'
- '@emnapi/runtime'
- '@rstest/core@0.11.2(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0)(jsdom@27.4.0(@noble/hashes@2.2.0))':
+ '@rstest/core@0.11.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)(jsdom@27.4.0(@noble/hashes@2.2.0))':
dependencies:
- '@rsbuild/core': 2.1.6(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0)
+ '@rsbuild/core': 2.1.6(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)
'@types/chai': 5.2.3
optionalDependencies:
jsdom: 27.4.0(@noble/hashes@2.2.0)
@@ -15326,9 +15317,9 @@ snapshots:
optionalDependencies:
jsdom: 27.4.0(@noble/hashes@2.2.0)
- '@rstest/coverage-istanbul@0.11.2(@rstest/core@0.11.2(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0)(jsdom@27.4.0(@noble/hashes@2.2.0)))':
+ '@rstest/coverage-istanbul@0.11.4(@rstest/core@0.11.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)(jsdom@27.4.0(@noble/hashes@2.2.0)))':
dependencies:
- '@rstest/core': 0.11.2(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0)(jsdom@27.4.0(@noble/hashes@2.2.0))
+ '@rstest/core': 0.11.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)(jsdom@27.4.0(@noble/hashes@2.2.0))
istanbul-lib-coverage: 3.2.2
istanbul-lib-report: 3.0.1
istanbul-lib-source-maps: 5.0.6
@@ -15710,7 +15701,7 @@ snapshots:
postcss: 8.5.16
tailwindcss: 4.3.2
- '@tailwindcss/webpack@4.3.2(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))':
+ '@tailwindcss/webpack@4.3.2(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))':
dependencies:
'@alloc/quick-lru': 5.2.0
'@tailwindcss/node': 4.3.2
@@ -15720,7 +15711,7 @@ snapshots:
'@rspack/core': 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)
webpack: 5.108.3(lightningcss@1.32.0)
- '@tailwindcss/webpack@4.3.2(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))':
+ '@tailwindcss/webpack@4.3.2(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))':
dependencies:
'@alloc/quick-lru': 5.2.0
'@tailwindcss/node': 4.3.2
@@ -16009,6 +16000,7 @@ snapshots:
'@types/node@25.9.5':
dependencies:
undici-types: 7.24.6
+ optional: true
'@types/pegjs@0.10.6': {}
@@ -18817,7 +18809,7 @@ snapshots:
jest-worker@27.5.1:
dependencies:
- '@types/node': 25.9.5
+ '@types/node': 25.0.10
merge-stream: 2.0.0
supports-color: 8.1.1
@@ -18962,14 +18954,14 @@ snapshots:
leaflet@1.9.4: {}
- less-loader@12.3.3(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(less@4.6.7)(webpack@5.108.3(lightningcss@1.32.0)):
+ less-loader@12.3.3(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(less@4.6.7)(webpack@5.108.3(lightningcss@1.32.0)):
dependencies:
less: 4.6.7
optionalDependencies:
'@rspack/core': 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)
webpack: 5.108.3(lightningcss@1.32.0)
- less-loader@12.3.3(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(less@4.6.7)(webpack@5.108.3(lightningcss@1.32.0)):
+ less-loader@12.3.3(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(less@4.6.7)(webpack@5.108.3(lightningcss@1.32.0)):
dependencies:
less: 4.6.7
optionalDependencies:
@@ -20389,13 +20381,13 @@ snapshots:
optionalDependencies:
react-dom: 19.2.7(react@19.2.7)
- react-server-dom-rspack@0.0.2(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(react-dom@19.2.7(react@19.2.7))(react@19.2.7):
+ react-server-dom-rspack@0.0.2(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(react-dom@19.2.7(react@19.2.7))(react@19.2.7):
dependencies:
'@rspack/core': 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)
react: 19.2.7
react-dom: 19.2.7(react@19.2.7)
- react-server-dom-rspack@0.0.2(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(react-dom@19.2.7(react@19.2.7))(react@19.2.7):
+ react-server-dom-rspack@0.0.2(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(react-dom@19.2.7(react@19.2.7))(react@19.2.7):
dependencies:
'@rspack/core': 2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)
react: 19.2.7
@@ -20687,22 +20679,22 @@ snapshots:
transitivePeerDependencies:
- supports-color
- rsbuild-plugin-dts@0.22.1(@rsbuild/core@2.0.15(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(typescript@5.9.3):
+ rsbuild-plugin-dts@0.22.1(@rsbuild/core@2.0.15(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(typescript@5.9.3):
dependencies:
'@ast-grep/napi': 0.37.0
- '@rsbuild/core': 2.0.15(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0)
+ '@rsbuild/core': 2.0.15(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)
optionalDependencies:
typescript: 5.9.3
- rsbuild-plugin-rsc@0.1.1(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(react-server-dom-rspack@0.0.2(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)):
+ rsbuild-plugin-rsc@0.1.1(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(react-server-dom-rspack@0.0.2(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)):
dependencies:
'@rsbuild/core': 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)
- react-server-dom-rspack: 0.0.2(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ react-server-dom-rspack: 0.0.2(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
- rsbuild-plugin-rsc@0.1.1(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(react-server-dom-rspack@0.0.2(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)):
+ rsbuild-plugin-rsc@0.1.1(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(react-server-dom-rspack@0.0.2(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)):
dependencies:
'@rsbuild/core': 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)
- react-server-dom-rspack: 0.0.2(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ react-server-dom-rspack: 0.0.2(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
rslog@2.1.3: {}
@@ -21549,7 +21541,8 @@ snapshots:
undici-types@7.16.0: {}
- undici-types@7.24.6: {}
+ undici-types@7.24.6:
+ optional: true
undici@7.24.7: {}
From 8676f0a6b274b6e24513a29165f0352e6b95d200 Mon Sep 17 00:00:00 2001
From: Zack Jackson <25274700+ScriptedAlchemy@users.noreply.github.com>
Date: Sat, 25 Jul 2026 01:11:40 +0000
Subject: [PATCH 52/53] docs: align effect plan with maintainability goals
---
.../2026-07-17-effect-server-architecture.md | 55 ++++++++++---------
1 file changed, 29 insertions(+), 26 deletions(-)
diff --git a/docs/superpowers/plans/2026-07-17-effect-server-architecture.md b/docs/superpowers/plans/2026-07-17-effect-server-architecture.md
index 718e5edf..3e5f9023 100644
--- a/docs/superpowers/plans/2026-07-17-effect-server-architecture.md
+++ b/docs/superpowers/plans/2026-07-17-effect-server-architecture.md
@@ -2,27 +2,30 @@
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
-**Goal:** Make Effect the orchestration and lifecycle spine for all Node-side plugin and server code while reducing handwritten LOC and preserving build, dev-startup, worker, and runtime performance.
+**Goal:** Make Effect the orchestration and lifecycle spine for Node-side plugin and server resources where it materially simplifies ownership and composition, while preserving build, dev-startup, worker, and runtime performance.
-**Architecture:** Create one `ManagedRuntime` per plugin instance. Rsbuild hooks and exported Promise APIs are the only runtime boundaries; internal Node-side workflows return `Effect`. A runtime-owned `PluginScope` service owns dynamically created workers, watchers, timers, background fibers, and dev-server resources. Pure transforms, generated/browser runtime code, and worker entrypoints remain ordinary TypeScript.
+**Architecture:** Create one `ManagedRuntime` per plugin instance. Rsbuild hooks and exported Promise APIs are runtime boundaries; effectful internal workflows compose as `Effect`, while synchronous callback-driven state transitions remain ordinary TypeScript. The runtime's native `Scope` owns dynamically created workers, watchers, delayed work, and background fibers. Pure transforms, generated/browser runtime code, and worker entrypoints remain ordinary TypeScript.
**Tech Stack:** TypeScript 5.9, Effect 3.22, Rsbuild/Rspack, Rstest, Playwright, Node `worker_threads`, existing benchmark harness.
## Global Constraints
+> **Execution refinement:** Early task examples name a custom `PluginScope` service and propose Effect-based dev-session state. The accepted implementation uses the runtime-provided native `Scope` plus a supervised `FiberSet`, and keeps Rsbuild's synchronous dev-session transitions plain. These choices remove a forwarding abstraction and avoid runtime crossings in synchronous callbacks while preserving the ownership model described here.
+
- Scope is Node-side plugin/server code only. Do not add Effect to browser runtime, generated browser modules, templates shipped to applications, or worker entrypoints.
- `src/parallel-route-transform-worker.ts` and its transitive worker-only hot path remain Effect-free unless a separate benchmark-backed design is approved.
- Effect may own `RouteTransformExecutor` acquisition, supervision, and shutdown in the parent process; `postMessage`, worker task execution, serialization, and worker caches remain plain TypeScript.
- Use direct Effect subpath imports such as `effect/Effect` and `effect/ManagedRuntime`; do not import the `effect` barrel from production source.
- Create one `ManagedRuntime` per `pluginReactRouter(...).setup(api)` invocation. Do not create a runtime per hook, route, transform, request, compiler, or worker.
- Keep pure synchronous helpers pure. Do not wrap string transforms, AST transforms, manifest shaping, cache access, predicates, or object construction in `Effect.sync` unless they cross a lifecycle/error boundary.
-- Use `Effect.fn` for reusable Node-side workflows. Do not use `Effect.fnUntraced` unless a benchmark identifies tracing overhead in that exact function.
+- Use `Effect.fn` for reusable orchestration boundaries when the named span materially improves ownership or diagnostics. Do not add it to per-route, per-module, per-request, or other measured hot paths without benchmark evidence.
+- Keep synchronous Rsbuild callback state machines in plain TypeScript when converting them would introduce repeated Promise/Effect crossings or runtime calls. Effect should remove manual asynchronous ownership, not wrap every mutation.
- Add tagged errors only where callers recover differently. Continue using `Error` for opaque third-party failures when a new error class would only add ceremony.
- Preserve public exports and Promise-returning APIs unless a separate breaking-change decision is approved.
- Production behavior and error messages remain unchanged unless a task explicitly names the intended change.
-- Every migration tranche must end with fewer Node-side production LOC than it started with. The complete branch must reduce total tracked handwritten TypeScript/JavaScript LOC, including tests and benchmark support.
+- Treat LOC as a diagnostic, not an acceptance gate. Prefer less production code when clarity is equal, but accept additional tests, benchmarks, or narrowly justified runtime code when the result is easier to understand and maintain.
- Performance acceptance: no benchmark median may regress by more than 2%; no p95/tail metric, dev readiness, HMR update, worker-enabled build, CPU time, or maximum RSS metric may regress by more than 5%.
-- If a tranche misses either LOC or performance gates, revert or redesign that tranche before continuing. Do not defer the regression to final cleanup.
+- If a tranche misses the performance gate or adds abstraction without removing equivalent manual coordination, revert or redesign it before continuing.
- Keep commits tranche-local and independently green. Never mix browser-runtime changes, dependency upgrades, formatting sweeps, or unrelated refactors into this branch.
---
@@ -31,12 +34,12 @@
Keep the repository's flat `src/` layout. Do not create an `effect/` directory or split every service into its own file.
-- `src/effect-runtime.ts`: error normalization, foreign sync/Promise constructors, `PluginScope`, and the single plugin `ManagedRuntime` factory.
+- `src/effect-runtime.ts`: error normalization, foreign Promise constructors, native scoped resource/fiber ownership, and the single plugin `ManagedRuntime` factory.
- `src/index.ts`: constructs the plugin runtime, adapts Rsbuild hooks to `runtime.runPromise`, and disposes the runtime from close hooks.
- `src/dev-background-resources.ts`: scoped acquisition of route watchers, lazy-compilation prewarm, and the parent-side transform executor.
- `src/route-watch.ts`: Effect-native watcher lifecycle and serialized rescan workflow.
-- `src/dev-runtime-session.ts`: session ownership and close sequencing using Effect primitives.
-- `src/dev-runtime-controller.ts`: compiler/runtime orchestration, interruptible delayed work, and cleanup.
+- `src/dev-runtime-session.ts`: synchronous session state and close observation required by Rsbuild's callback API.
+- `src/dev-runtime-controller.ts`: compiler/runtime orchestration; asynchronous delayed work and plugin-owned cleanup use the plugin runtime where they simplify ownership.
- `src/dev-generation.ts`: readiness and generation state transitions using `Deferred` plus an Effect workflow boundary.
- `src/lazy-compilation-prewarm.ts`: interruptible prewarm scheduling owned by the plugin scope.
- `src/parallel-route-transforms.ts`: Promise-based worker dispatch remains; acquisition/close becomes a parent-side scoped resource.
@@ -262,13 +265,13 @@ pnpm typecheck
Expected: PASS.
-- [ ] **Step 5: Check the tranche LOC direction**
+- [ ] **Step 5: Review tranche size and maintainability**
```bash
git diff --numstat -- src/effect-runtime.ts tests/effect-runtime.test.ts
```
-This foundation may be temporarily positive. Task 3 must make Tasks 2–3 net-negative in production `src/` LOC before the tranche is accepted.
+This foundation may be positive. Review Tasks 2–3 together and confirm the added runtime code replaces duplicated lifecycle ownership; LOC alone does not determine acceptance.
- [ ] **Step 6: Commit the runtime foundation**
@@ -378,13 +381,13 @@ pnpm typecheck
Expected: PASS.
-- [ ] **Step 6: Enforce the first LOC gate**
+- [ ] **Step 6: Review the first runtime tranche**
```bash
git diff HEAD~1 --numstat -- src/effect-runtime.ts src/index.ts
```
-Tasks 2–3 together must be net-negative in production lines after removing redundant runner/normalization code. If they are positive, keep `PluginScope` but defer the `index.ts` boundary commit until Task 4 removes more lifecycle code.
+Tasks 2–3 together must centralize runtime creation, disposal, and failure normalization without adding a second ownership path. Use the line diff to identify accidental duplication, not as a hard gate.
- [ ] **Step 7: Commit the boundary**
@@ -492,7 +495,7 @@ node scripts/compare-benchmarks.mts --before=.benchmark/effect-server-architectu
Expected: worker-enabled production build remains faster than inline; all acceptance thresholds pass.
-- [ ] **Step 7: Enforce LOC and commit**
+- [ ] **Step 7: Review maintainability and commit**
```bash
git diff --numstat -- src/parallel-route-transforms.ts src/dev-background-resources.ts src/index.ts
@@ -664,7 +667,7 @@ pnpm typecheck
Expected: PASS. Explicitly verify double close, close during rescan, failed watcher startup, reschedule, and callback-triggered close cases.
-- [ ] **Step 6: Run dev startup/HMR benchmarks and enforce LOC**
+- [ ] **Step 6: Run dev startup/HMR benchmarks and review maintainability**
Repeat both large dev benchmark commands with `after-task-5` output paths. Reject the tranche if readiness or update time exceeds the global thresholds.
@@ -766,7 +769,7 @@ pnpm rstest run tests/dev-runtime-controller.test.ts tests/dev-generation.test.t
pnpm typecheck
```
-- [ ] **Step 7: Run dev performance and LOC gates**
+- [ ] **Step 7: Run dev performance and maintainability checks**
Run large dev inline and worker benchmarks plus the existing HMR/HDR fail-fast suite:
@@ -775,7 +778,7 @@ pnpm test:react-router-framework:failfast
git diff --numstat -- src/dev-runtime-session.ts src/dev-runtime-controller.ts src/dev-generation.ts src/dev-runtime-compilation.ts src/dev-runtime-artifacts.ts
```
-Expected: no gate regression; production LOC is net-negative.
+Expected: no performance regression; Effect removes manual ownership or state coordination wherever it is introduced.
- [ ] **Step 8: Commit**
@@ -913,7 +916,7 @@ pnpm typecheck
Expected: PASS.
-- [ ] **Step 7: Run full build benchmarks and LOC gate**
+- [ ] **Step 7: Run full build benchmarks and review maintainability**
Repeat `full-build`, `1024-inline`, and `1024-workers` benchmarks with `after-task-7` output paths. Verify route analysis and prerender concurrency did not regress.
@@ -921,7 +924,7 @@ Repeat `full-build`, `1024-inline`, and `1024-workers` benchmarks with `after-ta
git diff --numstat -- src/react-router-config.ts src/build-manifest.ts src/manifest.ts src/prerender-build.ts src/rsc-prerender.ts src/server-build-resolution.ts src/server-utils.ts src/typegen.ts
```
-Expected: production LOC is net-negative.
+Expected: no performance regression and no redundant Promise/Effect boundary remains.
- [ ] **Step 8: Commit**
@@ -998,14 +1001,14 @@ pnpm typecheck
Expected: PASS.
-- [ ] **Step 6: Enforce crossing and LOC gates**
+- [ ] **Step 6: Enforce crossing policy and review source size**
```bash
rg -n "Effect\.run(?:Promise|PromiseExit|Fork|Sync)|ManagedRuntime\.make" src --glob '*.ts'
git diff --numstat $(cat .benchmark/effect-server-architecture/base-commit.txt)..HEAD -- src
```
-Expected: runner search is limited to `src/effect-runtime.ts`; cumulative production LOC is lower than baseline.
+Expected: runner search is limited to `src/effect-runtime.ts`; source-size changes are understood and justified by simpler ownership or stronger coverage.
- [ ] **Step 7: Commit**
@@ -1096,7 +1099,7 @@ Skip unchanged paths in `git add`; do not create an empty commit.
---
-### Task 10: Final Performance, LOC, Health, and Compatibility Gate
+### Task 10: Final Performance, Health, Maintainability, and Compatibility Gate
**Files:**
@@ -1106,7 +1109,7 @@ Skip unchanged paths in `git add`; do not create an empty commit.
**Interfaces:**
-- Delivers: behavior-compatible Node-side architecture with one managed runtime, scoped resources, Effect-free workers/browser code, lower LOC, and accepted performance.
+- Delivers: behavior-compatible Node-side architecture with one managed runtime, scoped resources, Effect-free workers/browser code, clearer ownership, and accepted performance.
- [ ] **Step 1: Run final source-policy searches**
@@ -1143,7 +1146,7 @@ pnpm bench:synthetic-app -- --plugin-root "$PWD" --profile=all --runs=5
Expected: every metric satisfies the 2% median/5% tail gates; worker-enabled 1024-route build retains its benefit; constrained dev startup does not absorb additional Effect worker cost.
-- [ ] **Step 4: Enforce final LOC gates**
+- [ ] **Step 4: Record final LOC as a diagnostic**
```bash
git ls-files -z '*.ts' '*.tsx' '*.js' '*.mjs' '*.mts' \
@@ -1156,7 +1159,7 @@ diff -u .benchmark/effect-server-architecture/base-total-loc.txt .benchmark/effe
diff -u .benchmark/effect-server-architecture/base-src-loc.txt .benchmark/effect-server-architecture/final-src-loc.txt || true
```
-Manually compare the final `total` rows. Both tracked handwritten total LOC and `src/` LOC must be lower. Test deletion is allowed only when it removes obsolete adapter-specific coverage and the same behavior remains covered through public APIs.
+Manually compare the final `total` rows. Explain material growth and confirm it comes from useful tests, benchmark support, or code that replaces more complex ownership. Test deletion is allowed only when it removes obsolete adapter-specific coverage and the same behavior remains covered through public APIs.
- [ ] **Step 5: Re-run TraceDecay health and affected-test analysis**
@@ -1182,7 +1185,7 @@ Confirm all of these are true in the final diff:
- no Promise-to-Effect-to-Promise bounce inside internal workflows;
- public Promise APIs preserved;
- fewer manual timer handles, `closed` flags, close promises, and `Promise.allSettled` cleanup aggregators;
-- fewer production lines overall.
+- production ownership and boundary code is easier to follow even where line count is unchanged.
- [ ] **Step 7: Confirm release documentation remains unchanged**
@@ -1208,7 +1211,7 @@ Stop and redesign the current tranche when any condition occurs:
- Effect is imported by `src/parallel-route-transform-worker.ts` or an emitted worker chunk.
- A runtime is created inside a hook callback, route loop, request handler, worker, or transform.
- Worker-enabled dev readiness, worker-enabled 1024-route build, HMR update latency, CPU, or RSS exceeds the performance thresholds.
-- A tranche adds production LOC after removing its superseded implementation.
+- A tranche adds another abstraction or ownership path without simplifying the superseded implementation.
- Resource cleanup requires both a runtime finalizer and the old manual close-hook path.
- An internal workflow repeatedly crosses Promise/Effect boundaries.
- Browser/template output contains Effect runtime code.
From 1cd74486478f95f744d8f1756b21da52a5806217 Mon Sep 17 00:00:00 2001
From: Zack Jackson <25274700+ScriptedAlchemy@users.noreply.github.com>
Date: Sat, 25 Jul 2026 01:29:28 +0000
Subject: [PATCH 53/53] fix: map in-memory dev server stacks
---
src/dev-source-maps.ts | 77 ++++++++++++++++++++++++++++++++++-
src/index.ts | 8 ++++
tests/dev-source-maps.test.ts | 52 +++++++++++++++++++++++
3 files changed, 136 insertions(+), 1 deletion(-)
create mode 100644 tests/dev-source-maps.test.ts
diff --git a/src/dev-source-maps.ts b/src/dev-source-maps.ts
index 1d5c3a90..00cdeb4a 100644
--- a/src/dev-source-maps.ts
+++ b/src/dev-source-maps.ts
@@ -2,12 +2,18 @@ import { existsSync, readFileSync, statSync } from 'node:fs';
import { SourceMap, type SourceMapPayload } from 'node:module';
import { dirname, isAbsolute, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
+import type { Rspack } from '@rsbuild/core';
type CachedSourceMap = {
cacheKey: string;
sourceMap: SourceMap | null;
};
+type InMemorySourceMap = {
+ load: () => SourceMapPayload | null;
+ sourceMap?: SourceMap | null;
+};
+
const SOURCE_MAPPING_URL_MARKER = '//# sourceMappingURL=';
const GENERATED_JS_FRAME_RE =
/(file:\/\/\/[^\s()]+\.m?js|\/[^\s()]+\.m?js):(\d+):(\d+)/g;
@@ -15,6 +21,8 @@ const GENERATED_JS_FRAME_RE =
let installed = false;
let previousPrepareStackTrace: typeof Error.prepareStackTrace;
const sourceMapCache = new Map();
+const inMemorySourceMaps = new Map();
+const generatedFilesByOutputPath = new Map>();
const getGeneratedFilePath = (fileName: string): string => {
if (fileName.startsWith('file://')) {
@@ -52,6 +60,22 @@ const parseSourceMapPayload = (sourceMapJson: string): SourceMapPayload => {
};
};
+const toSourceMapPayload = (value: unknown): SourceMapPayload | null => {
+ if (!value || typeof value !== 'object') {
+ return null;
+ }
+ const payload = value as Partial;
+ return {
+ file: payload.file ?? '',
+ version: payload.version ?? 3,
+ sources: payload.sources ?? [],
+ sourcesContent: payload.sourcesContent ?? [],
+ names: payload.names ?? [],
+ mappings: payload.mappings ?? '',
+ sourceRoot: payload.sourceRoot ?? '',
+ };
+};
+
const readInlineSourceMap = (filePath: string): string | null => {
const source = readFileSync(filePath, 'utf8');
const markerIndex = source.lastIndexOf(SOURCE_MAPPING_URL_MARKER);
@@ -95,6 +119,20 @@ const readSourceMapPayload = (filePath: string): SourceMapPayload | null => {
};
const getSourceMap = (filePath: string): SourceMap | null => {
+ const inMemory = inMemorySourceMaps.get(filePath);
+ if (inMemory) {
+ if (inMemory.sourceMap !== undefined) {
+ return inMemory.sourceMap;
+ }
+ try {
+ const payload = inMemory.load();
+ inMemory.sourceMap = payload ? new SourceMap(payload) : null;
+ } catch {
+ inMemory.sourceMap = null;
+ }
+ return inMemory.sourceMap;
+ }
+
const cacheKey = getCacheKey(filePath);
if (!cacheKey) {
return null;
@@ -117,6 +155,43 @@ const getSourceMap = (filePath: string): SourceMap | null => {
return sourceMap;
};
+export const registerDevServerSourceMaps = (
+ compilation: Pick
+): void => {
+ const outputPath = compilation.outputOptions.path;
+ if (!outputPath) {
+ return;
+ }
+ const previousFiles = generatedFilesByOutputPath.get(outputPath);
+ if (previousFiles) {
+ for (const filePath of previousFiles) {
+ inMemorySourceMaps.delete(filePath);
+ }
+ }
+
+ const assets = compilation.getAssets();
+ const assetsByName = new Map(assets.map(asset => [asset.name, asset]));
+ const generatedFiles = new Set();
+
+ for (const asset of assets) {
+ if (!/\.m?js$/.test(asset.name)) {
+ continue;
+ }
+
+ const generatedFilePath = resolve(outputPath, asset.name);
+ const externalMap = assetsByName.get(`${asset.name}.map`);
+ const load = externalMap
+ ? () =>
+ parseSourceMapPayload(externalMap.source.buffer().toString('utf8'))
+ : () => toSourceMapPayload(asset.source.map());
+
+ generatedFiles.add(generatedFilePath);
+ inMemorySourceMaps.set(generatedFilePath, { load });
+ }
+
+ generatedFilesByOutputPath.set(outputPath, generatedFiles);
+};
+
const resolveOriginalFileName = (
generatedFilePath: string,
originalFileName: string
@@ -131,7 +206,7 @@ const resolveOriginalFileName = (
return resolve(dirname(generatedFilePath), originalFileName);
};
-const remapDevServerStack = (stack: string): string =>
+export const remapDevServerStack = (stack: string): string =>
stack.replace(GENERATED_JS_FRAME_RE, (match, fileName, line, column) => {
const generatedFilePath = getGeneratedFilePath(fileName);
const sourceMap = getSourceMap(generatedFilePath);
diff --git a/src/index.ts b/src/index.ts
index fc3632be..a22fca80 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -86,6 +86,7 @@ import {
shouldParallelizeRouteTransforms,
} from './parallel-route-transforms.js';
import { createQuerylessRouteImportPlugin } from './route-imports.js';
+import { registerDevServerSourceMaps } from './dev-source-maps.js';
export type { Config as ReactRouterRsbuildConfig } from './react-router-config.js';
export { loadReactRouterServerBuild } from './dev-generation.js';
@@ -423,6 +424,13 @@ export const pluginReactRouter = (
} as ResolvedReactRouterConfig;
const isBuild = api.context.action === 'build';
+ if (!isBuild) {
+ api.onAfterEnvironmentCompile(({ environment, stats }) => {
+ if (environment.name === 'node' && stats && !stats.hasErrors()) {
+ registerDevServerSourceMaps(stats.compilation);
+ }
+ });
+ }
const shouldDependOnWebCompiler = !shouldParallelizeEnvironmentBuilds({
isBuild,
});
diff --git a/tests/dev-source-maps.test.ts b/tests/dev-source-maps.test.ts
new file mode 100644
index 00000000..9b87bc75
--- /dev/null
+++ b/tests/dev-source-maps.test.ts
@@ -0,0 +1,52 @@
+import { describe, expect, it, rstest } from '@rstest/core';
+import {
+ registerDevServerSourceMaps,
+ remapDevServerStack,
+} from '../src/dev-source-maps.js';
+
+describe('dev source maps', () => {
+ it('lazily reads source maps from in-memory compilation assets', () => {
+ const source = rstest.fn(() =>
+ Buffer.from(
+ JSON.stringify({
+ version: 3,
+ file: 'static/js/app.js',
+ sources: ['/project/app/error-stacktrace.tsx'],
+ sourcesContent: ['throw new Error("test")'],
+ names: [],
+ mappings: 'AAAA',
+ })
+ )
+ );
+
+ registerDevServerSourceMaps({
+ outputOptions: { path: '/project/build/server' },
+ getAssets: () =>
+ [
+ {
+ name: 'static/js/app.js',
+ source: { map: () => null },
+ info: {},
+ },
+ {
+ name: 'static/js/app.js.map',
+ source: { buffer: source },
+ info: {},
+ },
+ ] as never,
+ });
+
+ expect(source).not.toHaveBeenCalled();
+ expect(
+ remapDevServerStack(
+ 'at TestRoute (/project/build/server/static/js/app.js:1:1)'
+ )
+ ).toBe('at TestRoute (/project/app/error-stacktrace.tsx:1:1)');
+ expect(source).toHaveBeenCalledTimes(1);
+
+ remapDevServerStack(
+ 'at TestRoute (/project/build/server/static/js/app.js:1:1)'
+ );
+ expect(source).toHaveBeenCalledTimes(1);
+ });
+});