Skip to content

feat: support client-only SPA recovery builds - #161

Closed
ScriptedAlchemy wants to merge 6 commits into
mainfrom
feat/spa-strip-server-route-exports
Closed

ScriptedAlchemy wants to merge 6 commits into
mainfrom
feat/spa-strip-server-route-exports

Conversation

@ScriptedAlchemy

@ScriptedAlchemy ScriptedAlchemy commented Oct 21, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Adds the narrow stripServerOnlyRouteExportsInSpa plugin option for client-only recovery artifacts built from route sources that are normally shared with SSR.

Dependency and review scope

This PR depends on #160 (f4db610). It targets main only so the repository's pkg-pr-new workflow publishes a combined preview package for TTArch integration. Do not merge this PR before #160. The SPA recovery feature is the top commit, 79fe88c; review that commit for this PR's independent implementation. After #160 lands, this branch can be rebased so the final diff contains only the SPA recovery change.

Supported behavior

When explicitly enabled for plain SPA mode (ssr: false with no prerender paths), the plugin:

  • validates that every source loader, action, or middleware has an explicit clientLoader, clientAction, or clientMiddleware
  • strips loader, action, middleware, and headers from browser and build-time server route modules
  • preserves explicit client exports and the root HydrateFallback
  • removes server loader/action flags and exports from generated browser/server manifest metadata
  • retains the existing non-root HydrateFallback validation

The default remains unchanged. The option does not apply to ssr: false prerender builds and is rejected in RSC mode. It does not synthesize serverLoader, convert server behavior to client behavior, emit an SSR snapshot, or allow a second router runtime.

Verification

  • pnpm typecheck
  • focused Rstest suite: 60 passed
  • existing SPA validation Playwright cases: 6 passed across split route modules on/off
  • new real SPA build/browser fixture: 2 passed
    • source server exports throw if invoked and their markers are absent from all emitted JS
    • direct deep-route static fallback
    • offline client navigation
    • explicit clientLoader and clientAction
    • manifest hasLoader/hasAction false with client flags true
    • zero .data/.rsc responses and zero browser errors
  • pnpm build
  • pnpm test:package-interop

A full pnpm test run reached 848 passing tests but hit 13 unrelated route-imports.test.ts resource-identity failures and one pre-existing stale benchmark temp-fixture resolution error. The focused affected suites pass.

@github-actions

github-actions Bot commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

Benchmark results

Case Base Head Delta
build-256-ssr 2074.3 ms 2083.5 ms +0.4%
dev-48-ssr 922.4 ms 946.7 ms +2.6%

@ScriptedAlchemy
ScriptedAlchemy changed the base branch from feat/remix-api-compat to main October 21, 2026 16:20
@ScriptedAlchemy

Copy link
Copy Markdown
Collaborator Author

Temporarily closing and reopening to deliver a qualifying pull-request lifecycle event to pkg-pr-new; this PR was originally opened against a base excluded by the preview workflow. No code or merge state is changing.

@pkg-pr-new

pkg-pr-new Bot commented Oct 21, 2026 •

Copy link
Copy Markdown

Open in StackBlitz

pnpm add https://pkg.pr.new/rsbuild-plugin-react-router@a1b4ca0

commit: a1b4ca0

@codspeed

codspeed Bot commented Oct 21, 2026 •

Copy link
Copy Markdown

Merging this PR will not alter performance

1 warning

⚠️ 1 benchmark is dominated by system calls

System calls cannot be simulated consistently and are left out of the measure. The reported value understates the real cost. Switch to the Walltime instrument to measure them.

Learn more about system calls

See affected benchmarks

✅ 1 untouched benchmark
⏩ 2 skipped benchmarks1


Comparing feat/spa-strip-server-route-exports (a1b4ca0) with main (a09dd6a)

Open in CodSpeed

Footnotes

  1. 2 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩

@ScriptedAlchemy

Copy link
Copy Markdown
Collaborator Author

Closing: the Remix 2 / React Router 6 support for TTArk will live as a TTArk-owned package in its monorepo instead of in this plugin. Keeping the branch for reference.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant