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/.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 \ diff --git a/CHANGELOG.md b/CHANGELOG.md index ead9b63c..38cbde8f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,34 @@ # 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 + +- 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/docs/superpowers/plans/2026-07-17-effect-server-architecture.md b/docs/superpowers/plans/2026-07-17-effect-server-architecture.md index 053e8e5c..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.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 +> **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. @@ -145,7 +148,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** @@ -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** @@ -944,7 +947,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** @@ -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. 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 3f5b07d7..fb14ca74 100644 --- a/examples/default-template/tests/e2e/dev-route-watch.test.ts +++ b/examples/default-template/tests/e2e/dev-route-watch.test.ts @@ -141,7 +141,6 @@ test.describe('dev route watch', () => { }) => { await page.goto('/'); await expect(page.locator('h1')).toContainText('Welcome to React Router'); - const restartMarkerBeforeAdd = readRestartMarkerVersion(); writeFileSync( addedRoutePath, @@ -158,9 +157,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/package.json b/package.json index f5987eb9..0d6d48bb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rsbuild-plugin-react-router", - "version": "0.3.1", + "version": "0.4.1", "description": "React Router plugin for Rsbuild", "repository": { "type": "git", @@ -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.4", + "@rstest/coverage-istanbul": "^0.11.4", "@swc/helpers": "^0.5.23", "@types/fs-extra": "11.0.4", "@types/jsesc": "^3.0.3", @@ -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..e12f8dac 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -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(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.8.1 - version: 0.8.1(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.2.0 - version: 0.2.0(@rstest/core@0.8.1(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 @@ -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 @@ -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.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(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.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.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 @@ -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(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.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.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.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)) + 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 @@ -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(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 @@ -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(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 @@ -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(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.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.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.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)) + 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.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.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.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.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(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.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.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.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)) + 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.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.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.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.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.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,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(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 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(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 @@ -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.4': + resolution: {integrity: sha512-HfBP4mmvLVq2vpyRvHHaeu1+K2e9XSjDCHbj15s+NExnniS2b+y/ABXyjJldpRSoiirKnfIqY7BVGOaO+G+W0w==} + 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.4': + resolution: {integrity: sha512-InsiiS3koVjBoiIlBDLcpGT8njCtmqWzr5NyY8abRlOux8MCaEyodialg6GZXy1PNhvR2FyCiy2kFFRxxNDjgw==} + peerDependencies: + '@rstest/core': ^0.11.4 + '@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==} @@ -7421,14 +7532,14 @@ 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==} - 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 @@ -14530,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 @@ -14548,7 +14731,16 @@ 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/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 + 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(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) @@ -14561,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 @@ -14571,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) @@ -14583,7 +14775,19 @@ 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-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(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) + transitivePeerDependencies: + - '@rspack/core' + - webpack + + '@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: @@ -14601,7 +14805,16 @@ 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-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-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 @@ -14611,7 +14824,7 @@ 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(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) @@ -14626,9 +14839,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(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(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)) + '@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(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(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) @@ -14641,9 +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(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(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: @@ -14652,14 +14889,14 @@ snapshots: '@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(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.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) + '@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 filesize: 11.0.19 @@ -14676,10 +14913,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(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.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(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 @@ -14687,15 +14924,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(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.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(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.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 +14942,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(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.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(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 @@ -14722,20 +14959,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(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 '@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(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.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(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) @@ -14753,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: @@ -14773,6 +15010,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 +15022,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 +15034,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 +15046,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 +15070,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 +15082,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 +15104,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 +15120,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 +15132,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 +15144,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 +15188,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 @@ -14919,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: @@ -14933,6 +15225,13 @@ 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': {} @@ -14943,6 +15242,12 @@ snapshots: 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 +15266,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 +15283,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 +15291,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 +15299,16 @@ snapshots: - '@emnapi/core' - '@emnapi/runtime' + '@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(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) + 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 +15317,17 @@ snapshots: optionalDependencies: 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.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 + 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)) @@ -15375,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 @@ -15385,6 +15711,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(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 + '@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 +15997,11 @@ snapshots: dependencies: undici-types: 7.16.0 + '@types/node@25.9.5': + dependencies: + undici-types: 7.24.6 + optional: true + '@types/pegjs@0.10.6': {} '@types/pg-pool@2.0.7': @@ -16418,7 +16759,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 +16881,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 +16934,7 @@ snapshots: caniuse-lite@1.0.30001800: {} - caniuse-lite@1.0.30001803: {} + caniuse-lite@1.0.30001806: {} ccount@2.0.1: {} @@ -17103,14 +17444,14 @@ 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 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 +17500,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 +17624,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 @@ -18611,13 +18954,20 @@ 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(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.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 +19709,8 @@ snapshots: node-releases@2.0.50: {} + node-releases@2.0.51: {} + node-schedule@2.1.1: dependencies: cron-parser: 4.9.0 @@ -20029,12 +20381,18 @@ 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(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 + 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 @@ -20321,17 +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(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(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: {} @@ -20536,11 +20899,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 +21117,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 +21541,9 @@ snapshots: undici-types@7.16.0: {} + undici-types@7.24.6: + optional: true + undici@7.24.7: {} undici@7.28.0: {} @@ -21268,9 +21634,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 +21746,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 +21824,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 +21896,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 +21935,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/scripts/test-package-interop.mts b/scripts/test-package-interop.mts index ef8efe12..ce106ee4 100644 --- a/scripts/test-package-interop.mts +++ b/scripts/test-package-interop.mts @@ -24,6 +24,7 @@ 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 +34,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,8 +49,9 @@ const verifyRegistration = async (writer, reader) => { isPluginExists: () => false, onBeforeStartDevServer: collect(starts), onAfterStartDevServer: noop, - onCloseDevServer: collect(closes), - onCloseBuild: noop, + onCloseDevServer: collect(closeDevServers), + onCloseBuild: collect(closeBuilds), + onExit: collect(exits), onAfterEnvironmentCompile: noop, onAfterBuild: noop, processAssets: noop, @@ -59,9 +63,12 @@ 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.deepEqual(closeBuilds, exits); + assert.deepEqual(closeDevServers, [closeBuilds[0], closeHook]); const start = startHook.handler; const server = { close: async () => undefined, @@ -71,13 +78,7 @@ 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), @@ -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)) diff --git a/src/dev-background-resources.ts b/src/dev-background-resources.ts index 73ba8fca..eaae083f 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,48 @@ 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) { + await runtime.runPromise( + Effect.acquireRelease(Effect.void, routeTopologyWatcherTask.cancelEffect) + ); + api.onBeforeStartDevServer(async () => { - routeTopologyWatcherClosed = false; await ensureDevRestartMarker(routeRestartMarkerPath); }); @@ -211,7 +186,9 @@ export const registerReactRouterDevBackgroundResources = ({ }); api.onAfterDevCompile(() => { - scheduleRouteTopologyWatcher(); + if (!routeTopologyWatcherStarted) { + routeTopologyWatcherTask.schedule(); + } lazyCompilationPrewarmController?.schedule(); }); @@ -220,34 +197,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 - ); - }; - - const closeRouteTransformExecutor = (): Promise => - routeTransformExecutor?.close() ?? Promise.resolve(); - - api.onCloseDevServer(() => - closeAll( - '[rsbuild-plugin-react-router] Failed to close dev server resources.', - [ - closeRouteTopologyWatcher, - closeLazyCompilationPrewarm, - closeRouteTransformExecutor, - ] - ) - ); - api.onCloseBuild(closeRouteTransformExecutor); - return { setManifest(manifest) { lazyCompilationPrewarmController?.setManifest(manifest); 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/dev-runtime-artifacts.ts b/src/dev-runtime-artifacts.ts index c82a3548..20be0f4a 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; @@ -137,14 +137,17 @@ export const getEnvironmentStats = ( const startServerBuildEvaluationEffect = ( server: RsbuildDevServer, entryName: string -): Effect.Effect => +): 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)}` + ) ) - ) + ), + Effect.map(build => [entryName, build] as const) ); const evaluateServerBuildsEffect = ( @@ -152,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/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, 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/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/effect-runtime.ts b/src/effect-runtime.ts index cf9aca3e..573713ce 100644 --- a/src/effect-runtime.ts +++ b/src/effect-runtime.ts @@ -1,9 +1,80 @@ 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'; +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 | PluginResources; + +const PluginRuntimeLive = Layer.scopedContext( + Effect.gen(function* () { + const scope = yield* Effect.scope; + const resources = yield* Scope.fork(scope, ExecutionStrategy.sequential); + 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(PluginResources, { scope: resources }) + ); + }) +); + +export type PluginEffectRuntime = Pick< + ManagedRuntime.ManagedRuntime, + 'runFork' | 'runPromise' +> & { + readonly dispose: () => Promise; +}; + +export const createPluginEffectRuntime = (): PluginEffectRuntime => { + const runtime = ManagedRuntime.make(PluginRuntimeLive); + let fiberRunFork: typeof runtime.runFork | undefined; + + let disposePromise: Promise | undefined; + + return { + runPromise: runtime.runPromise, + runFork: (effect, options) => { + if (disposePromise) return Effect.runFork(Effect.interrupt); + return (fiberRunFork ??= runtime.runSync( + Effect.flatMap(PluginFibers, fibers => + FiberSet.runtime(fibers)() + ) + ))(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())), + }; +}; export const DEV_BACKGROUND_STARTUP_DELAY_MS = 3_000; @@ -27,14 +98,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 => @@ -46,45 +109,38 @@ export const tryPluginPromise = ( type DelayedPluginTask = { schedule(): void; reschedule(): void; - cancelEffect(): Effect.Effect; - cancel(): Promise; + cancelEffect(): Effect.Effect; }; 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 - ) - ); - - const cancelEffect = (): Effect.Effect => - Effect.sync(() => { - version += 1; - }).pipe(Effect.zipRight(cancelActiveEffect())); - - const start = (taskVersion: number): void => { - if (activeFiber || version !== taskVersion) { + 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 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 => @@ -94,37 +150,29 @@ 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); - }, - - reschedule(): void { - version += 1; - const taskVersion = version; - void runPluginEffect(cancelActiveEffect()) - .then(() => start(taskVersion)) - .catch(onError); - }, + const reschedule = (): void => { + if (fiber) { + runtime.runFork( + Fiber.interrupt(fiber).pipe(Effect.ensuring(Effect.sync(start))) + ); + } else { + start(); + } + }; + return { + schedule: start, + reschedule, cancelEffect, - - async cancel(): Promise { - await runPluginEffect(cancelEffect()); - }, }; }; diff --git a/src/index.ts b/src/index.ts index b01f6fc4..a22fca80 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'; @@ -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,7 +81,12 @@ 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'; +import { registerDevServerSourceMaps } from './dev-source-maps.js'; export type { Config as ReactRouterRsbuildConfig } from './react-router-config.js'; export { loadReactRouterServerBuild } from './dev-generation.js'; @@ -113,6 +121,11 @@ export const pluginReactRouter = ( name: PLUGIN_NAME, async setup(api) { + const effectRuntime = createPluginEffectRuntime(); + api.onCloseBuild(effectRuntime.dispose); + api.onCloseDevServer(effectRuntime.dispose); + api.onExit(effectRuntime.dispose); + const defaultOptions = { customServer: false, lazyCompilation: true, @@ -199,7 +212,9 @@ export const pluginReactRouter = ( userAndPresetConfig, presets: configPresets, hasConfiguredServerModuleFormat, - } = await resolveReactRouterConfig(reactRouterUserConfig); + } = await effectRuntime.runPromise( + resolveReactRouterConfigEffect(reactRouterUserConfig) + ); const { appDirectory, @@ -218,7 +233,10 @@ export const pluginReactRouter = ( buildEnd, } = resolvedConfig; - registerReactRouterTypegen(api, { appDirectory }); + await registerReactRouterTypegen(api, { + runtime: effectRuntime, + appDirectory, + }); const hasExplicitServerOutput = Object.prototype.hasOwnProperty.call( options, @@ -406,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, }); @@ -518,10 +543,8 @@ export const pluginReactRouter = ( finalEntryClientPath, future, hasServerApp, - parallelRouteTransform: pluginOptions.parallelRouteTransform, reactRouterConfig: resolvedConfigWithRoutes, routeChunkCache, - routeCount, serverAppPath, shouldDependOnWebCompiler, devHmr: @@ -539,6 +562,20 @@ export const pluginReactRouter = ( : undefined, })); + const routeTransformExecutor = + modePlan.kind === 'classic' + ? await effectRuntime.runPromise( + acquireRouteTransformExecutor({ + parallelRouteTransform: + pluginOptions.parallelRouteTransform ?? + shouldParallelizeRouteTransforms(routeCount), + routeChunkCache, + splitRouteModules: Boolean(splitRouteModules), + isBuild, + }) + ) + : undefined; + const { manifestChunkNames } = modePlan; let sendRscDevUpdate: (() => void) | undefined; @@ -583,20 +620,19 @@ export const pluginReactRouter = ( }); } - const devBackgroundResources = registerReactRouterDevBackgroundResources({ - api, - isBuild, - lazyCompilationPrewarm: pluginOptions.unstableLazyCompilationPrewarm, - routeTransformExecutor: - modePlan.kind === 'classic' - ? modePlan.routeTransformExecutor - : undefined, - 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, @@ -682,7 +718,7 @@ export const pluginReactRouter = ( if (modePlan.kind === 'classic') { api.onAfterBuild(({ environments }) => - runPluginEffect( + effectRuntime.runPromise( tryPluginPromise(() => runReactRouterPrerenderBuild({ api, @@ -713,7 +749,7 @@ export const pluginReactRouter = ( ); } else { api.onAfterBuild(({ environments }) => - runPluginEffect( + effectRuntime.runPromise( tryPluginPromise(() => runReactRouterRscPrerenderBuild({ api, @@ -827,7 +863,6 @@ export const pluginReactRouter = ( }, server: modePlan.server, dev: { - writeToDisk: true, ...lazyCompilation, watchFiles: mergeWatchFiles(config.dev?.watchFiles, routeWatchFiles), }, @@ -982,6 +1017,10 @@ export const pluginReactRouter = ( performanceProfiler, }); } else { + if (!routeTransformExecutor) { + throw new Error('Route transform executor was not initialized.'); + } + registerModifyBrowserManifestAssets( api, routes, @@ -1018,7 +1057,7 @@ export const pluginReactRouter = ( getAssetPrefix: () => assetPrefix, routeChunkOptions: modePlan.routeChunkOptions, routeModuleAnalysis, - routeTransformExecutor: modePlan.routeTransformExecutor, + routeTransformExecutor, routeByFilePath, routeChunkConfig: modePlan.routeChunkConfig, isBuild, diff --git a/src/lazy-compilation-prewarm.ts b/src/lazy-compilation-prewarm.ts index 78ccbe03..4bc3d073 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 * as Scope from 'effect/Scope'; import type { ReactRouterManifestForDev } from './manifest.js'; import type { RouteManifestItem } from './types.js'; -import { createDelayedPluginTask, tryPluginPromise } from './effect-runtime.js'; +import { + createDelayedPluginTask, + type PluginEffectRuntime, + 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,11 @@ export const createLazyCompilationPrewarmController = ({ }, }; }; + +export const acquireLazyCompilationPrewarm = ( + options: Parameters[0] +): Effect.Effect => + Effect.acquireRelease( + Effect.sync(() => createLazyCompilationPrewarmController(options)), + controller => controller.cancelEffect() + ); 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/src/parallel-route-transforms.ts b/src/parallel-route-transforms.ts index 06adae3e..21a2a4f9 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 type * as Scope from 'effect/Scope'; import { setBoundedCacheEntry } from './bounded-cache.js'; import { getAvailableCpuCount, getDefaultConcurrency } from './concurrency.js'; -import { normalizeEffectError } from './effect-runtime.js'; +import { + normalizeEffectError, + tryPluginPromise, +} from './effect-runtime.js'; import { executeRouteTransformTask, type RouteTransformResult, @@ -401,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 = ( { @@ -456,3 +449,28 @@ export const createRouteTransformExecutorForTesting = ( createWorker: RouteTransformWorkerFactory ): RouteTransformExecutor => createRouteTransformExecutorWithWorkerFactory(options, createWorker); + +const acquireRouteTransformExecutorWithWorkerFactory = Effect.fn( + 'RouteTransformExecutor.acquire' +)(function* ( + options: RouteTransformExecutorOptions, + createWorker: RouteTransformWorkerFactory +) { + return yield* Effect.acquireRelease( + Effect.sync(() => + createRouteTransformExecutorWithWorkerFactory(options, createWorker) + ), + executor => Effect.ignore(tryPluginPromise(() => executor.close())) + ); +}); + +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/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/src/prerender-build.ts b/src/prerender-build.ts index a6afdde5..7c6e18e2 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 = ( @@ -558,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 => { @@ -600,6 +563,7 @@ export const runReactRouterPrerenderBuild = async ( serverBuildFile, ssr, isPrerenderEnabled, + prerenderConfig, prerenderPaths, routes, latestBrowserManifest, @@ -675,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) { diff --git a/src/react-router-config.ts b/src/react-router-config.ts index 82bde6b4..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); + } }, } : {}), @@ -287,8 +284,3 @@ export const resolveReactRouterConfigEffect = ( userAndPresetConfigs.serverModuleFormat !== undefined, }; }); - -export const resolveReactRouterConfig = ( - reactRouterUserConfig: Config -): Promise => - runPluginEffect(resolveReactRouterConfigEffect(reactRouterUserConfig)); diff --git a/src/route-chunks.ts b/src/route-chunks.ts index 2c9e2403..fc0d140c 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( @@ -834,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 => @@ -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; } 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; diff --git a/src/route-watch.ts b/src/route-watch.ts index 9f6b22b9..e100afaf 100644 --- a/src/route-watch.ts +++ b/src/route-watch.ts @@ -2,11 +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, - runPluginEffect, + type PluginEffectRuntime, 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> => { @@ -148,7 +156,8 @@ const readRouteDirectoriesEffect = ( return walkDirectory(watchDirectory).pipe(Effect.as(directories)); }; -export const createRouteTopologyWatcher = async ({ +const createRouteTopologyWatcherEffect = ({ + runtime, watchDirectory, getRouteTopology, initialRouteTopology, @@ -156,202 +165,171 @@ 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); - 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; - let rescanQueue = Promise.resolve(); - 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 closeRemovedDirectoryWatchers = ( - nextDirectories: Set - ): void => { - for (const [directory, watcher] of directoryWatchers) { - if (!nextDirectories.has(directory)) { - watcher.close(); - directoryWatchers.delete(directory); - } - } - }; + const touchRestartMarkerEffect = (): Effect.Effect => + tryPluginPromise(() => + mkdir(dirname(restartMarkerPath), { recursive: true }) + ).pipe( + Effect.zipRight( + tryPluginPromise(() => + writeFile(restartMarkerPath, String(Date.now())) + ) + ) + ); - const watchNewDirectories = (nextDirectories: Set): void => { - for (const directory of nextDirectories) { - if (directoryWatchers.has(directory)) { - continue; - } - try { - let watcher: DirectoryWatcher; - watcher = watchDirectoryOverride( - directory, - () => rescanTask.reschedule(), - error => { - if (directoryWatchers.get(directory) === watcher) { - watcher.close(); - directoryWatchers.delete(directory); - } - onError(error); - } - ); - directoryWatchers.set(directory, watcher); - } catch (error) { - onError(error); + const closeRemovedDirectoryWatchers = ( + nextDirectories: Set + ): void => { + for (const [directory, watcher] of directoryWatchers) { + if (!nextDirectories.has(directory)) { + watcher.close(); + directoryWatchers.delete(directory); + } } - } - }; - - 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) { + 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); } - }); + ); + directoryWatchers.set(directory, watcher); + } catch (error) { + onError(error); } - 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); + 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); + } + }); } - onError(error); - }) - ) - ); - }; + return touchRestartMarkerEffect().pipe( + Effect.zipRight( + Effect.sync(() => { + if (!closed) { + state = nextState; + } + }) + ) + ); + } + state = nextState; + return Effect.void; + }); - const rescan = (): Promise => { - rescanQueue = rescanQueue.then( - () => runPluginEffect(runRescanEffect()), - () => runPluginEffect(runRescanEffect()) - ); - return rescanQueue; - }; + 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({ - delayMs: ROUTE_TOPOLOGY_RESCAN_DEBOUNCE_MS, - run: () => - Effect.suspend(() => - closed ? Effect.void : tryPluginPromise(rescan).pipe(Effect.asVoid) - ), - onError, - }); + const rescanTask = createDelayedPluginTask({ + runtime, + delayMs: ROUTE_TOPOLOGY_RESCAN_DEBOUNCE_MS, + run: () => + Effect.suspend(() => (closed ? Effect.void : runRescanEffect())), + onError, + }); - const cancelScheduledRescan = (): Promise => - runPluginEffect(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())); - const applyNextState = async (nextState: RouteDirectoryState) => { - await runPluginEffect(applyNextStateEffect(nextState)); - }; + yield* applyNextStateEffect(discoveredState).pipe( + Effect.catchAll(error => Effect.sync(() => onError(error))), + Effect.onInterrupt(() => Effect.ignore(closeEffect())) + ); - try { - await applyNextState(discoveredState); - } catch (error) { - onError(error); - } + return closeEffect; + }); - return async () => { - if (closed) { - await cancelScheduledRescan(); - await rescanQueue; - return; - } - closed = true; - await cancelScheduledRescan(); - for (const watcher of directoryWatchers.values()) { - watcher.close(); - } - directoryWatchers.clear(); - await rescanQueue; - for (const watcher of directoryWatchers.values()) { - watcher.close(); - } - directoryWatchers.clear(); - }; -}; +export const acquireRouteTopologyWatcher = ( + options: CreateRouteTopologyWatcherOptions +): 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/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 { 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..4f9f50ac 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'; /** @@ -34,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 { @@ -83,32 +88,10 @@ 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 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 }; diff --git a/src/ssr-externals.ts b/src/ssr-externals.ts index e2fc69fd..941670a3 100644 --- a/src/ssr-externals.ts +++ b/src/ssr-externals.ts @@ -28,32 +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[] { - 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; + if (resolved === null) return false; + let packageJsonPath = resolved; + try { + packageJsonPath = realpathSync(packageJsonPath); + } catch {} + return !packageJsonPath.split(sep).includes('node_modules'); + }); } diff --git a/src/typegen.ts b/src/typegen.ts index c7b48912..6eeaa8f6 100644 --- a/src/typegen.ts +++ b/src/typegen.ts @@ -3,7 +3,11 @@ 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, + 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 +73,7 @@ export const createReactRouterTypegenRunner = ( ): ReactRouterTypegenRunner => { let typegenProcess: ResultPromise | undefined; let typegenCommand: TypegenCommand | undefined; + let watchGeneration = 0; const getTypegenCommand = (): TypegenCommand => { typegenCommand ??= (appDirectory @@ -96,7 +101,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 +117,7 @@ export const createReactRouterTypegenRunner = ( }, async closeWatch(): Promise { + watchGeneration += 1; const process = typegenProcess; typegenProcess = undefined; if (!process) { @@ -128,36 +138,48 @@ 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}` + ); + }, + }); + const closeWatchEffect = () => + devWatchTask + .cancelEffect() + .pipe( + Effect.zipRight( + Effect.orDie(tryPluginPromise(() => resolvedRunner.closeWatch())) + ) + ); + 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. @@ -169,10 +191,5 @@ export const registerReactRouterTypegen = ( }); } - api.onCloseDevServer(async () => { - await devWatchTask.cancel(); - await resolvedRunner.closeWatch(); - }); - api.onBeforeBuild(() => resolvedRunner.runBuild()); }; 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( 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/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); + }); +}); diff --git a/tests/effect-runtime.test.ts b/tests/effect-runtime.test.ts index 785dfa6d..53885bd3 100644 --- a/tests/effect-runtime.test.ts +++ b/tests/effect-runtime.test.ts @@ -1,93 +1,145 @@ import { describe, expect, it, rstest } from '@rstest/core'; import * as Effect from 'effect/Effect'; +import * as Fiber from 'effect/Fiber'; import { + createPluginEffectRuntime, createDelayedPluginTask, - runPluginEffect, - 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, task }; +}; + describe('effect runtime helpers', () => { - it('preserves typed errors at promise boundaries', async () => { - const error = new Error('typed failure'); + it('releases resources before interrupting remaining fibers', async () => { + const events: string[] = []; + const runtime = createPluginEffectRuntime(); + + await runtime.runPromise( + Effect.acquireRelease( + Effect.sync(() => { + events.push('acquire'); + return 'resource'; + }), + resource => + Effect.sync(() => { + events.push(`release:${resource}`); + }) + ) + ); + + runtime.runFork( + Effect.never.pipe( + Effect.ensuring( + Effect.sync(() => { + events.push('fiber'); + }) + ) + ) + ); - await expect(runPluginEffect(tryPluginSync(() => { - throw error; - }))).rejects.toBe(error); + await Promise.all([runtime.dispose(), runtime.dispose()]); + expect(events).toEqual(['acquire', 'release:resource', 'fiber']); }); - it('normalizes synchronous thrown causes to errors', async () => { - await expect( - runPluginEffect( - tryPluginSync(() => { - throw 'dev runtime failed'; - }) + 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(); + }) + ) ) - ).rejects.toThrow('dev runtime failed'); + ); + + 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('runs delayed plugin tasks after their delay', async () => { - const run = rstest.fn(); - const task = createDelayedPluginTask({ - delayMs: 10, - run: () => Effect.sync(run), - onError: error => { - throw error; - }, - }); + const { runtime, run, task } = createDelayedTaskFixture(10); 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 task = createDelayedPluginTask({ - delayMs: 10, - run: () => Effect.sync(run), - onError: error => { - throw error; - }, - }); + const { runtime, run, task } = createDelayedTaskFixture(10); task.schedule(); task.reschedule(); 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 task = createDelayedPluginTask({ - delayMs: 1000, - run: () => Effect.sync(run), - onError: error => { - throw error; - }, - }); + const { runtime, run, task } = createDelayedTaskFixture(1000); task.schedule(); - await task.cancel(); + task.reschedule(); + await runtime.runPromise(task.cancelEffect()); await new Promise(resolve => setTimeout(resolve, 20)); expect(run).not.toHaveBeenCalled(); + await runtime.dispose(); }); - it('supports Effect-based cancellation for delayed plugin tasks', async () => { - const run = rstest.fn(); - const task = createDelayedPluginTask({ - delayMs: 1000, - run: () => Effect.sync(run), - onError: error => { - throw error; - }, - }); + it('cancels delayed plugin tasks when the plugin runtime is disposed', async () => { + const { runtime, run, task } = createDelayedTaskFixture(25); task.schedule(); - await runPluginEffect(task.cancelEffect()); - await new Promise(resolve => setTimeout(resolve, 20)); + task.reschedule(); + await runtime.dispose(); + await new Promise(resolve => setTimeout(resolve, 50)); expect(run).not.toHaveBeenCalled(); }); + }); diff --git a/tests/features.test.ts b/tests/features.test.ts index 60ff96ab..4519f13b 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 99525b63..19404df3 100644 --- a/tests/index.test.ts +++ b/tests/index.test.ts @@ -70,7 +70,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, @@ -96,6 +96,19 @@ describe('pluginReactRouter', () => { }); }); + 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 })]); 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/parallel-route-transforms.test.ts b/tests/parallel-route-transforms.test.ts index 38abdf55..21f42ee4 100644 --- a/tests/parallel-route-transforms.test.ts +++ b/tests/parallel-route-transforms.test.ts @@ -1,3 +1,5 @@ +import { readFileSync } from 'node:fs'; +import { resolve } from 'node:path'; import { describe, expect, it } from '@rstest/core'; import { getExportNames } from '../src/export-utils'; import { @@ -8,10 +10,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, @@ -131,6 +135,33 @@ const resolveWorkerMessage = ( }; describe('parallel route transforms', () => { + it('keeps the worker entrypoint Effect-free', () => { + const source = readFileSync( + resolve(process.cwd(), 'src/parallel-route-transform-worker.ts'), + 'utf8' + ); + + expect(source).not.toMatch(/from ['"]effect(?:\/|['"])/); + 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/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', () => { diff --git a/tests/react-router-config.test.ts b/tests/react-router-config.test.ts index cc523c23..e6bb44a2 100644 --- a/tests/react-router-config.test.ts +++ b/tests/react-router-config.test.ts @@ -1,15 +1,17 @@ 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; + const buildEndCalls: string[] = []; const result = await resolveReactRouterConfig({ presets: [ { @@ -18,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'); }, }); @@ -34,37 +36,7 @@ describe('resolveReactRouterConfig', () => { reactRouterConfig: result.resolved, rsbuildConfig: {} as any, }); - 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); + expect(buildEndCalls).toEqual(['preset', 'user']); }); it('preserves server bundle selection in SSR mode', 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 38b9791b..2174564c 100644 --- a/tests/route-watch.test.ts +++ b/tests/route-watch.test.ts @@ -8,15 +8,79 @@ 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 * as Option from 'effect/Option'; +import { createPluginEffectRuntime } from '../src/effect-runtime'; import { + acquireRouteTopologyWatcher, createRouteManifestSnapshot, - createRouteTopologyWatcher, ensureDevRestartMarker, getRouteRestartMarkerPath, + type CreateRouteTopologyWatcherOptions, } from '../src/route-watch'; +const routeWatchRuntime = createPluginEffectRuntime(); +const createRouteTopologyWatcher = async ( + options: Omit +) => { + const closeEffect = await routeWatchRuntime.runPromise( + acquireRouteTopologyWatcher({ runtime: routeWatchRuntime, ...options }) + ); + return () => routeWatchRuntime.runPromise(closeEffect()); +}; + +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'); @@ -55,7 +119,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'); @@ -73,39 +137,102 @@ describe('route watch restart marker', () => { }); let triggerChange!: () => void; const closeWatcher = rstest.fn(); + 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 new Promise(resolve => setImmediate(resolve)); + runFork.mockClear(); + triggerChange(); expect(readFileSync(markerPath, 'utf8')).toBe(initialMarker); - expect(closeWatcher).toHaveBeenCalled(); + expect(closeWatcher).toHaveBeenCalledTimes(1); + expect(runFork).not.toHaveBeenCalled(); } finally { releaseRescan(); + await runtime.dispose(); + runFork.mockRestore(); + rmSync(root, { recursive: true, force: true }); + } + }); + + 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 closeEffect = 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(runtime.runPromise(closeEffect())).rejects.toThrow( + closeError.message + ); + + expect(Option.isSome(await runtime.runPromise(fiber.poll))).toBe(true); + } finally { + await runtime.dispose(); rmSync(root, { recursive: true, force: true }); } }); 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: {} } }) diff --git a/tests/setup.ts b/tests/setup.ts index d34d9b34..cb449e62 100644 --- a/tests/setup.ts +++ b/tests/setup.ts @@ -149,7 +149,6 @@ rstest.mock('@scripts/test-helper', () => ({ const mergeRsbuildConfig = (a: any, b: any) => deepMerge(a, b); const pending: Promise[] = []; - const stub: any = { addPlugins: rstest.fn(), unwrapConfig: rstest.fn(), @@ -158,6 +157,7 @@ rstest.mock('@scripts/test-helper', () => ({ onAfterStartDevServer: rstest.fn(), onCloseDevServer: rstest.fn(), onCloseBuild: rstest.fn(), + onExit: rstest.fn(), onBeforeBuild: rstest.fn(), onAfterBuild: rstest.fn(), onBeforeDevCompile: rstest.fn(), diff --git a/tests/typegen.test.ts b/tests/typegen.test.ts index a67c89bb..4afa934c 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, @@ -18,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(); @@ -56,6 +103,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); @@ -104,126 +168,82 @@ 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 api = { - context: { action: 'dev' }, - logger: { warn: rstest.fn() }, - onAfterDevCompile: rstest.fn(callback => { - afterDevCompile = callback; - }), - onBeforeStartDevServer: rstest.fn(), - onCloseDevServer: rstest.fn(), - onBeforeBuild: rstest.fn(), - }; - - registerReactRouterTypegen(api as never, { runner, devWatchDelayMs: 0 }); - - expect(api.onBeforeStartDevServer).not.toHaveBeenCalled(); - const result = afterDevCompile(); + const harness = createTypegenRegistrationHarness(); + + await registerReactRouterTypegen(harness.api as never, { + runtime: harness.runtime, + runner: harness.runner, + devWatchDelayMs: 0, + }); + + 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); + 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 api = { - context: { action: 'dev' }, - logger: { warn: rstest.fn() }, - onAfterDevCompile: rstest.fn(callback => { - afterDevCompile = callback; - }), - onBeforeStartDevServer: rstest.fn(), - onCloseDevServer: rstest.fn(), - onBeforeBuild: rstest.fn(), - }; - - registerReactRouterTypegen(api as never, { runner, devWatchDelayMs: 200 }); + 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); + expect(harness.startWatch).toHaveBeenCalledTimes(1); + await harness.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 = { - startWatch, - closeWatch, - runBuild: rstest.fn().mockResolvedValue(undefined), - }; - const api = { - context: { action: 'dev' }, - logger: { warn: rstest.fn() }, - onAfterDevCompile: rstest.fn(callback => { - afterDevCompile = callback; - }), - onBeforeStartDevServer: rstest.fn(), - onCloseDevServer: rstest.fn(callback => { - closeDevServer = callback; - }), - onBeforeBuild: rstest.fn(), - }; - - registerReactRouterTypegen(api as never, { - runner, - devWatchDelayMs: 1000, + it('cancels delayed startup and surfaces close failures during disposal', async () => { + 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, + devWatchDelayMs: 25, }); - afterDevCompile(); - await closeDevServer(); - await new Promise(resolve => setTimeout(resolve, 20)); + harness.afterDevCompile(); + await expect(harness.runtime.dispose()).rejects.toThrow('typegen close failed'); + await new Promise(resolve => setTimeout(resolve, 50)); - expect(startWatch).not.toHaveBeenCalled(); + expect(harness.startWatch).not.toHaveBeenCalled(); expect(closeWatch).toHaveBeenCalledTimes(1); }); - it('does not register the dev watch hook during production builds', () => { - const runner: ReactRouterTypegenRunner = { - startWatch: rstest.fn().mockResolvedValue(undefined), - closeWatch: rstest.fn().mockResolvedValue(undefined), - runBuild: rstest.fn().mockResolvedValue(undefined), - }; - const api = { - context: { action: 'build' }, - logger: { warn: rstest.fn() }, - onAfterDevCompile: rstest.fn(), - onBeforeStartDevServer: rstest.fn(), - onCloseDevServer: rstest.fn(), - onBeforeBuild: rstest.fn(), - }; - - registerReactRouterTypegen(api as never, { runner }); - - expect(api.onAfterDevCompile).not.toHaveBeenCalled(); - expect(api.onBeforeBuild).toHaveBeenCalled(); + it('does not register the dev watch hook during production builds', async () => { + 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(); }); });