Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
104 commits
Select commit Hold shift + click to select a range
cab475a
Add rsbuild Start plugin implementation
cursoragent Feb 8, 2026
0f73fe3
Implement rsbuild Start plugin parity
cursoragent Feb 8, 2026
bbd2c2d
Fix SSR server static index handling
cursoragent Feb 8, 2026
d0ade15
Fix rsbuild preview and css handling
cursoragent Feb 8, 2026
20fe2f2
Fix redirect serialization heuristics
cursoragent Feb 9, 2026
9c1a6b2
Merge pull request #1 from ScriptedAlchemy/cursor/rsbuild-plugin-spa-…
ScriptedAlchemy Feb 9, 2026
87f6ea4
Harden PR CI and refine rsbuild build env handling
cursoragent Feb 9, 2026
ad20294
Merge pull request #5 from ScriptedAlchemy/cursor/branch-implementati…
ScriptedAlchemy Feb 9, 2026
8d0e57d
Revert PR workflow audit changes
ScriptedAlchemy Feb 9, 2026
fbde76b
ci: apply automated fixes
autofix-ci[bot] Feb 9, 2026
ffc2b97
ci: retrigger PR checks after preview service failure
ScriptedAlchemy Feb 9, 2026
1d4155b
fix: address PR review issues in rsbuild integration
ScriptedAlchemy Feb 9, 2026
13a7530
ci: apply automated fixes
autofix-ci[bot] Feb 9, 2026
448340d
fix: restore response typing in server fn fetcher
ScriptedAlchemy Feb 9, 2026
4ccdde4
fix: satisfy response-like eslint guard in server handler
ScriptedAlchemy Feb 10, 2026
21d9b11
refactor: resolve remaining PR #6623 review suggestions
ScriptedAlchemy Feb 10, 2026
56efe06
ci: apply automated fixes
autofix-ci[bot] Feb 10, 2026
ef21285
fix: address remaining unresolved PR review threads
ScriptedAlchemy Feb 10, 2026
48a5700
add federation-aware rsbuild server runtime defaults
cursoragent Feb 10, 2026
40fc4b0
Switch federation shared setup to async startup
cursoragent Feb 10, 2026
272d671
Remove generated build artifacts from fixtures
cursoragent Feb 10, 2026
92a0c88
Add MF mode matrix coverage and dynamic route registration
cursoragent Feb 10, 2026
ac4d31c
chore: add core rsbuild-plugin auto-env patch file
cursoragent Feb 12, 2026
971f352
chore: remove temporary rsbuild plugin patch file
cursoragent Feb 12, 2026
a290354
chore: update federation deps and patch rsbuild-plugin env handling
cursoragent Feb 12, 2026
dccf288
chore: use PR 4427 federation packages and drop local patch
cursoragent Feb 14, 2026
8da2584
chore: move rsbuild federation fixtures to plugin-only wiring
cursoragent Feb 14, 2026
25dc370
chore: upgrade rsbuild federation canary to PR 4427 head
cursoragent Feb 14, 2026
c94c0a8
fix: enforce script remoteType for node federation remotes
cursoragent Feb 14, 2026
2003de1
fix: disable node remote shared fallbacks for plugin-only SSR
cursoragent Feb 14, 2026
2c03d98
docs: clarify node federation shared ownership constraints
cursoragent Feb 14, 2026
a53f0d2
docs: add host note for node federation compatibility
cursoragent Feb 14, 2026
69971f7
docs: annotate SSR remote loading requirements in host configs
cursoragent Feb 14, 2026
d7fa659
docs: add e2e federation fixture run and SSR notes
cursoragent Feb 14, 2026
a21d9ac
docs: note remote shared import flags in e2e host readme
cursoragent Feb 14, 2026
2d5f115
fix: scope import false shared config to node federation target
cursoragent Feb 14, 2026
0076bea
test: assert SSR manifest stays node-compatible for remote
cursoragent Feb 14, 2026
44bd89e
docs: document SSR manifest compatibility contract
cursoragent Feb 14, 2026
11d67d0
docs: align example SSR manifest expectations
cursoragent Feb 14, 2026
74de9bd
refactor: split web and node shared configs for remote federation
cursoragent Feb 14, 2026
243938e
docs: clarify web vs node shared config expectations
cursoragent Feb 14, 2026
4448777
test: assert browser manifest shared assets contract
cursoragent Feb 14, 2026
2a9771d
docs: document browser manifest federation contract
cursoragent Feb 14, 2026
c15356c
test: refactor manifest assertions with typed helper
cursoragent Feb 14, 2026
497f5b8
test: assert shared entries exist in manifest contracts
cursoragent Feb 14, 2026
49a105a
test: assert manifest publicPath http contracts
cursoragent Feb 14, 2026
0578627
test: assert shared asset paths stay relative
cursoragent Feb 14, 2026
dc025cc
test: assert exposed asset paths stay relative
cursoragent Feb 14, 2026
56a688e
test: assert expose ids and paths in manifests
cursoragent Feb 14, 2026
798be68
test: assert wildcard shared metadata for SSR manifests
cursoragent Feb 14, 2026
cbb9dd4
test: verify remoteEntry payload and types metadata
cursoragent Feb 14, 2026
9eab273
test: lock remote types metadata contracts
cursoragent Feb 14, 2026
27358e1
test: assert manifest has no nested remotes
cursoragent Feb 14, 2026
ce0c9c2
test: enforce exact expose entry count
cursoragent Feb 14, 2026
74ef8a6
test: verify expose assets resolve over manifest paths
cursoragent Feb 14, 2026
92c89ca
test: assert javascript content-type for remote assets
cursoragent Feb 14, 2026
c5ec84f
docs: note javascript content-type requirement
cursoragent Feb 14, 2026
1410078
test: verify federated types archive over http
cursoragent Feb 14, 2026
bb4f444
test: resolve types artifact from manifest metadata
cursoragent Feb 14, 2026
be8116b
test: read types archive path from manifest
cursoragent Feb 14, 2026
7c805ca
test: validate federation stats endpoints are JSON
cursoragent Feb 14, 2026
e731921
test: assert federation manifests and stats are JSON
cursoragent Feb 14, 2026
3269882
test: assert federation stats shared ownership contract
cursoragent Feb 14, 2026
46c8cd5
docs: describe stats shared import contract
cursoragent Feb 14, 2026
494d314
test: assert federation manifest identity metadata
cursoragent Feb 14, 2026
8e2498e
test: verify federation stats expose file metadata
cursoragent Feb 14, 2026
5d4a2f1
test: assert manifest and stats contract parity
cursoragent Feb 14, 2026
6b534d8
docs: note manifest and stats parity contract
cursoragent Feb 14, 2026
cbddcf7
test: assert stats ids and cardinality contracts
cursoragent Feb 14, 2026
1ece27e
docs: record stats id and count invariants
cursoragent Feb 14, 2026
333b6ce
test: tighten manifest and stats metadata parity
cursoragent Feb 14, 2026
c09c87b
docs: mention build and plugin metadata parity
cursoragent Feb 14, 2026
40c2ee2
test: compare manifest and stats shared/expose assets
cursoragent Feb 14, 2026
84db5c5
docs: describe manifest-stats asset list parity
cursoragent Feb 14, 2026
4166ac8
test: include css and types path parity in stats
cursoragent Feb 14, 2026
1575669
docs: add css and types metadata parity notes
cursoragent Feb 14, 2026
16c8a69
test: validate json endpoint payload structure
cursoragent Feb 14, 2026
e68e2eb
docs: note json payload structure expectations
cursoragent Feb 14, 2026
eb337e9
test: require .js suffix on manifest asset paths
cursoragent Feb 14, 2026
1cbbf3e
docs: mention js asset suffix contract
cursoragent Feb 14, 2026
8301c3f
test: assert endpoint-specific manifest metadata values
cursoragent Feb 14, 2026
1472355
docs: describe dist and ssr endpoint metadata expectations
cursoragent Feb 14, 2026
a4ad977
test: verify endpoint build metadata consistency
cursoragent Feb 14, 2026
96dedec
docs: note endpoint build metadata consistency
cursoragent Feb 14, 2026
9211638
test: assert shared versions on all json endpoints
cursoragent Feb 14, 2026
e4bca87
docs: note endpoint shared version expectations
cursoragent Feb 14, 2026
c999f3c
test: enforce shared metadata on json endpoints
cursoragent Feb 14, 2026
636f944
docs: note endpoint shared entry set and remotes
cursoragent Feb 14, 2026
4c9f5c5
test: assert expose id and path on json endpoints
cursoragent Feb 14, 2026
ee48946
docs: add endpoint expose identity expectations
cursoragent Feb 14, 2026
2606abd
test: assert shared identity across federation json endpoints
cursoragent Feb 14, 2026
508f6a2
test: assert endpoint expose file metadata on stats
cursoragent Feb 14, 2026
99320fa
docs: note endpoint expose requires and file contracts
cursoragent Feb 14, 2026
19b50ba
test: enforce semver pluginVersion on federation endpoints
cursoragent Feb 14, 2026
03c370b
test: assert endpoint global metadata invariants
cursoragent Feb 14, 2026
c263d47
docs: document endpoint global metadata invariants
cursoragent Feb 14, 2026
50f6c10
test: assert endpoint metadata name and build invariants
cursoragent Feb 14, 2026
7591159
docs: note endpoint identity metadata invariants
cursoragent Feb 14, 2026
fe8fb67
test: assert endpoint types path and name invariants
cursoragent Feb 14, 2026
ac13b55
test: distinguish expose metadata contracts by endpoint type
cursoragent Feb 14, 2026
0c1fbc8
test: assert shared runtime flags by endpoint type
cursoragent Feb 14, 2026
13220b2
test: assert shared usage arrays by endpoint type
cursoragent Feb 14, 2026
2cbcbc8
test: assert endpoint shared and expose asset semantics
cursoragent Feb 14, 2026
7927316
test: assert endpoint async and css asset emptiness
cursoragent Feb 14, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions e2e/react-start/basic/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@
"sideEffects": false,
"type": "module",
"scripts": {
"dev": "vite dev --port 3000",
"dev:e2e": "vite dev",
"build": "vite build && tsc --noEmit",
"build:spa": "MODE=spa vite build && tsc --noEmit",
"build:prerender": "MODE=prerender vite build && tsc --noEmit",
"preview": "vite preview",
"dev": "node scripts/run-bundler.mjs dev --port 3000",
"dev:e2e": "node scripts/run-bundler.mjs dev",
"build": "node scripts/run-bundler.mjs build",
"build:spa": "MODE=spa node scripts/run-bundler.mjs build",
"build:prerender": "MODE=prerender node scripts/run-bundler.mjs build",
"preview": "node scripts/run-bundler.mjs preview",
"start": "node server.js",
"test:e2e:startDummyServer": "node -e 'import(\"./tests/setup/global.setup.ts\").then(m => m.default())' &",
"test:e2e:stopDummyServer": "node -e 'import(\"./tests/setup/global.teardown.ts\").then(m => m.default())'",
Expand All @@ -33,6 +33,8 @@
},
"devDependencies": {
"@playwright/test": "^1.50.1",
"@rsbuild/core": "^1.2.4",
"@rsbuild/plugin-react": "^1.1.0",
"@tailwindcss/vite": "^4.1.18",
"@tanstack/router-e2e-utils": "workspace:^",
"@types/js-cookie": "^3.0.6",
Expand Down
49 changes: 49 additions & 0 deletions e2e/react-start/basic/rsbuild.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import path from 'node:path'
import { fileURLToPath } from 'node:url'
import { defineConfig } from '@rsbuild/core'
import { pluginReact } from '@rsbuild/plugin-react'
import { tanstackStart } from '@tanstack/react-start/plugin/rsbuild'
import { isSpaMode } from './tests/utils/isSpaMode'
import { isPrerender } from './tests/utils/isPrerender'

const currentDir = path.dirname(fileURLToPath(import.meta.url))

const spaModeConfiguration = {
enabled: true,
prerender: {
outputPath: 'index.html',
},
}

const prerenderConfiguration = {
enabled: true,
filter: (page: { path: string }) =>
![
'/this-route-does-not-exist',
'/redirect',
'/i-do-not-exist',
'/not-found/via-beforeLoad',
'/not-found/via-loader',
'/specialChars/search',
'/specialChars/hash',
'/specialChars/malformed',
'/users',
].some((p) => page.path.includes(p)),
maxRedirects: 100,
}

export default defineConfig({
plugins: [
pluginReact(),
...tanstackStart({
spa: isSpaMode ? spaModeConfiguration : undefined,
prerender: isPrerender ? prerenderConfiguration : undefined,
}),
],
tools: {},
source: {
alias: {
'~': path.resolve(currentDir, 'src'),
},
},
})
57 changes: 57 additions & 0 deletions e2e/react-start/basic/scripts/run-bundler.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { spawn } from 'node:child_process'

const command = process.argv[2]
const args = process.argv.slice(3)

if (!command) {
console.error('Missing bundler command')
process.exit(1)
}

const bundler = process.env.BUNDLER === 'rsbuild' ? 'rsbuild' : 'vite'

const extractPort = (args) => {
const portIndex = args.indexOf('--port')
if (portIndex >= 0 && args[portIndex + 1]) {
return args[portIndex + 1]
}
return null
}

const run = (cmd, cmdArgs) =>
new Promise((resolve, reject) => {
const child = spawn(cmd, cmdArgs, {
stdio: 'inherit',
env: process.env,
shell: process.platform === 'win32',
})
child.on('error', (error) => {
reject(error)
})
child.on('close', (code) => {
if (code === 0) {
resolve()
} else {
reject(new Error(`${cmd} exited with code ${code}`))
}
})
})

try {
if (bundler === 'rsbuild' && command === 'preview') {
const port = extractPort(args)
if (port) {
process.env.PORT = port
}
await run('node', ['server.js'])
} else {
await run(bundler, [command, ...args])

if (command === 'build') {
await run('tsc', ['--noEmit'])
}
}
} catch (error) {
console.error(error)
process.exit(1)
}
7 changes: 6 additions & 1 deletion e2e/react-start/basic/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,12 @@ export async function createStartServer() {

// to keep testing uniform stop express from redirecting /posts to /posts/
// when serving pre-rendered pages
app.use(express.static('./dist/client', { redirect: !isPrerender }))
app.use(
express.static('./dist/client', {
redirect: !isPrerender,
...(isPrerender ? {} : { index: false }),
}),
)

app.use(async (req, res, next) => {
try {
Expand Down
7 changes: 1 addition & 6 deletions e2e/react-start/basic/src/routeTree.gen.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,3 @@
/* eslint-disable */

// @ts-nocheck

// noinspection JSUnusedGlobalSymbols

// This file was automatically generated by TanStack Router.
// You should NOT make any changes in this file as it will be overwritten.
// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
Expand Down Expand Up @@ -1365,6 +1359,7 @@ export const routeTree = rootRouteImport
._addFileChildren(rootRouteChildren)
._addFileTypes<FileRouteTypes>()


import type { getRouter } from './router.tsx'
import type { createStart } from '@tanstack/react-start'
declare module '@tanstack/react-start' {
Expand Down
3 changes: 1 addition & 2 deletions e2e/react-start/basic/src/routes/__root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {

import { DefaultCatchBoundary } from '~/components/DefaultCatchBoundary'
import { NotFound } from '~/components/NotFound'
import appCss from '~/styles/app.css?url'
import '~/styles/app.css'
import { seo } from '~/utils/seo'

export const Route = createRootRoute({
Expand All @@ -30,7 +30,6 @@ export const Route = createRootRoute({
}),
],
links: [
{ rel: 'stylesheet', href: appCss },
{
rel: 'apple-touch-icon',
sizes: '180x180',
Expand Down
85 changes: 56 additions & 29 deletions e2e/react-start/basic/src/styles/app.css
Original file line number Diff line number Diff line change
@@ -1,30 +1,57 @@
@import 'tailwindcss' source('../');

@layer base {
*,
::after,
::before,
::backdrop,
::file-selector-button {
border-color: var(--color-gray-200, currentcolor);
}
}

@layer base {
html {
color-scheme: light dark;
}

* {
@apply border-gray-200 dark:border-gray-800;
}

html,
body {
@apply text-gray-900 bg-gray-50 dark:bg-gray-950 dark:text-gray-200;
}

.using-mouse * {
outline: none !important;
}
*,
*::before,
*::after,
::backdrop,
::file-selector-button {
box-sizing: border-box;
border-color: #e5e7eb;
}

html {
color-scheme: light dark;
}

body {
margin: 0;
font-family:
system-ui,
-apple-system,
BlinkMacSystemFont,
'Segoe UI',
sans-serif;
background-color: #f9fafb;
color: #111827;
}

.using-mouse * {
outline: none !important;
}

.p-2 {
padding: 0.5rem;
}

.py-2 {
padding-top: 0.5rem;
padding-bottom: 0.5rem;
}

.flex {
display: flex;
}

.gap-2 {
gap: 0.5rem;
}

.text-lg {
font-size: 1.125rem;
}

.font-bold {
font-weight: 700;
}

.italic {
font-style: italic;
}
100 changes: 100 additions & 0 deletions e2e/react-start/module-federation-rsbuild-host/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# E2E Fixture: TanStack Start Module Federation Host (Rsbuild)

This fixture validates host behavior for Rsbuild + Module Federation in:

- `ssr`
- `spa`
- `prerender`

It consumes the paired remote fixture at:

- `../module-federation-rsbuild-remote`

## Commands

```sh
# SSR only
pnpm test:e2e:ssr

# SPA only
pnpm test:e2e:spa

# Prerender only
pnpm test:e2e:prerender

# Full mode matrix
pnpm test:e2e
```

## Node SSR federation requirement

Server remotes are loaded over HTTP from the remote SSR output and use:

- `remoteType: 'script'` on the host SSR plugin config.
- `shared.react/react-dom.import: false` on the remote node-target config.

The e2e suite also validates the remote SSR manifest contract:

- `metaData.remoteEntry.type === 'commonjs-module'`
- `metaData.publicPath` points to `http://<remote-origin>/ssr/`
- `metaData.types.zip === ''` and `metaData.types.api === ''`
- React/ReactDOM shared entries use wildcard metadata (`version: '*'`, `requiredVersion: '^*'`) in SSR manifest.
- React/ReactDOM shared fallback asset lists are empty in SSR manifest.
- Exposed module JS assets remain relative `static/js/...` paths.
- JS asset paths are expected to end with `.js`.

It also validates the browser manifest contract for the remote web target:

- `metaData.remoteEntry.type === 'global'`
- `metaData.publicPath` points to `http://<remote-origin>/`
- `metaData.types.zip === '@mf-types.zip'` and `metaData.types.api === '@mf-types.d.ts'`
- React/ReactDOM shared entries use concrete non-wildcard versions in browser manifest.
- React/ReactDOM shared fallback asset lists are populated in browser manifest.
- Browser shared JS asset entries remain relative `static/js/...` paths (resolved via HTTP `publicPath`).
- Exposed module JS assets remain relative `static/js/...` paths.

Federation stats contract is also verified:

- SSR stats shared entries set `import: false` for `react` and `react-dom`.
- Browser stats shared entries omit `import` and keep standard web sharing metadata.
- Manifest and stats metadata stay aligned (identity, remote entry type/publicPath, types metadata, shared/expose names).
- Stats outputs keep exact expected cardinality (`shared: 2`, `exposes: 3`) and stable ids (`mf_remote:*`).
- Alignment also covers build/plugin metadata (`buildInfo`, `pluginVersion`) and remoteEntry name/path fields.
- Shared/expose JS asset lists are aligned between manifest and stats for both browser and SSR outputs.
- Types metadata parity includes `types.path` and `types.name`, and shared/expose CSS asset lists are also aligned.
- JSON endpoint checks also validate basic payload structure (`id`, `name`, `metaData.remoteEntry.name`, `pluginVersion`).
- Endpoint payloads also keep identity metadata stable (`metaData.name: 'mf_remote'`, `metaData.type: 'app'`, and `buildInfo.buildVersion: 'local'`).
- Endpoint payloads also preserve global metadata invariants (`metaData.globalName: 'mf_remote'`, `metaData.prefetchInterface: false`, `metaData.remoteEntry.path: ''`).
- JSON endpoint checks assert path-specific metadata values:
- `/dist/*` endpoints return `remoteEntry.type: 'global'`, browser `types` metadata, and root publicPath.
- `/ssr/*` endpoints return `remoteEntry.type: 'commonjs-module'`, empty SSR `types` metadata, and `/ssr/` publicPath.
- Endpoint `types.path` and `types.name` metadata are expected to stay empty strings (`''`) across both browser and SSR JSON payloads.
- Endpoint payloads also keep consistent plugin/build metadata across all JSON endpoints (`pluginVersion`, `buildVersion`, `buildName`).
- `pluginVersion` values are also expected to be SemVer-like strings on all JSON endpoints.
- JSON endpoint payloads also enforce mode-correct shared version semantics (`react`/`react-dom` wildcard only on SSR endpoints).
- Each JSON endpoint payload also keeps `remotes: []` and exactly two shared entries (`react`, `react-dom`).
- Shared identity in each endpoint payload remains stable (`mf_remote:react`, `mf_remote:react-dom`).
- Shared entries remain singleton on all endpoint payloads.
- Stats endpoints retain shared runtime flags (`shareScope: 'default'`, `eager: false`, and SSR-only `import: false`), while manifest endpoints omit those fields.
- Stats endpoints also keep shared usage arrays (`usedIn`, `usedExports`) as empty arrays, while manifest endpoints omit them.
- Endpoint shared JS asset semantics remain mode-correct (`[]` for SSR shared entries, non-empty relative `static/js/*.js` for browser shared entries).
- Shared endpoint payloads keep async JS asset arrays empty and CSS asset arrays empty.
- Endpoint payloads also retain expose identity/path contracts for `message`, `routes`, and `server-data`.
- Stats endpoint expose `requires` arrays remain empty; manifest endpoint payloads omit `requires`.
- Stats endpoint payloads also retain stable expose `file` metadata (`src/message.tsx`, `src/routes.tsx`, `src/server-data.ts`).
- Endpoint expose sync JS asset lists are non-empty and keep relative `static/js/*.js` paths.
- Endpoint expose async JS and CSS asset arrays remain empty.

Remote entry payloads are also validated directly over HTTP at:

- `/dist/remoteEntry.js`
- `/ssr/remoteEntry.js`

and must return JavaScript content-types (not HTML fallback pages).

The browser types artifact is also validated at:

- `/dist/@mf-types.zip`

and must be retrievable over HTTP as a non-HTML payload.

Loading
Loading