Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 5 additions & 0 deletions .changeset/fair-assets-align.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@tanstack/start-plugin-core': patch
---

Fix Rsbuild SSR asset URLs for `?url` imports by aligning server public asset paths with the client build.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

24 changes: 21 additions & 3 deletions e2e/react-start/rsc-rsbuild/src/routeTree.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,19 @@

import { Route as rootRouteImport } from './routes/__root'
import { Route as RscNodeModuleClientRouteImport } from './routes/rsc-node-module-client'
import { Route as RscCssUrlRouteImport } from './routes/rsc-css-url'
import { Route as IndexRouteImport } from './routes/index'

const RscNodeModuleClientRoute = RscNodeModuleClientRouteImport.update({
id: '/rsc-node-module-client',
path: '/rsc-node-module-client',
getParentRoute: () => rootRouteImport,
} as any)
const RscCssUrlRoute = RscCssUrlRouteImport.update({
id: '/rsc-css-url',
path: '/rsc-css-url',
getParentRoute: () => rootRouteImport,
} as any)
const IndexRoute = IndexRouteImport.update({
id: '/',
path: '/',
Expand All @@ -25,27 +31,31 @@ const IndexRoute = IndexRouteImport.update({

export interface FileRoutesByFullPath {
'/': typeof IndexRoute
'/rsc-css-url': typeof RscCssUrlRoute
'/rsc-node-module-client': typeof RscNodeModuleClientRoute
}
export interface FileRoutesByTo {
'/': typeof IndexRoute
'/rsc-css-url': typeof RscCssUrlRoute
'/rsc-node-module-client': typeof RscNodeModuleClientRoute
}
export interface FileRoutesById {
__root__: typeof rootRouteImport
'/': typeof IndexRoute
'/rsc-css-url': typeof RscCssUrlRoute
'/rsc-node-module-client': typeof RscNodeModuleClientRoute
}
export interface FileRouteTypes {
fileRoutesByFullPath: FileRoutesByFullPath
fullPaths: '/' | '/rsc-node-module-client'
fullPaths: '/' | '/rsc-css-url' | '/rsc-node-module-client'
fileRoutesByTo: FileRoutesByTo
to: '/' | '/rsc-node-module-client'
id: '__root__' | '/' | '/rsc-node-module-client'
to: '/' | '/rsc-css-url' | '/rsc-node-module-client'
id: '__root__' | '/' | '/rsc-css-url' | '/rsc-node-module-client'
fileRoutesById: FileRoutesById
}
export interface RootRouteChildren {
IndexRoute: typeof IndexRoute
RscCssUrlRoute: typeof RscCssUrlRoute
RscNodeModuleClientRoute: typeof RscNodeModuleClientRoute
}

Expand All @@ -58,6 +68,13 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof RscNodeModuleClientRouteImport
parentRoute: typeof rootRouteImport
}
'/rsc-css-url': {
id: '/rsc-css-url'
path: '/rsc-css-url'
fullPath: '/rsc-css-url'
preLoaderRoute: typeof RscCssUrlRouteImport
parentRoute: typeof rootRouteImport
}
'/': {
id: '/'
path: '/'
Expand All @@ -70,6 +87,7 @@ declare module '@tanstack/react-router' {

const rootRouteChildren: RootRouteChildren = {
IndexRoute: IndexRoute,
RscCssUrlRoute: RscCssUrlRoute,
RscNodeModuleClientRoute: RscNodeModuleClientRoute,
}
export const routeTree = rootRouteImport
Expand Down
154 changes: 150 additions & 4 deletions e2e/react-start/rsc-rsbuild/src/routes/index.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,160 @@
import { Link, createFileRoute } from '@tanstack/react-router'
import { createFileRoute, Link, linkOptions } from '@tanstack/react-router'
import type { CSSProperties } from 'react'

export const Route = createFileRoute('/')({
component: Home,
})

const examples = linkOptions([
{
to: '/rsc-node-module-client',
title: 'Node module client component',
description:
'Hydrates a client component imported from node_modules inside an RSC route.',
marker: 'RSC',
markerColor: '#0284c7',
},
{
to: '/rsc-css-url',
title: 'css?url stylesheet',
description:
'Applies a stylesheet imported with the ?url query from route head metadata.',
marker: 'CSS',
markerColor: '#16a34a',
},
])

const colors = {
server: '#0284c7',
client: '#16a34a',
async: '#f59e0b',
}

const styles = {
page: {
maxWidth: '800px',
padding: '20px',
fontFamily:
'-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
},
title: {
margin: '0 0 8px 0',
color: '#1e293b',
fontSize: '24px',
},
description: {
color: '#64748b',
lineHeight: '1.5',
marginBottom: '20px',
},
legend: {
display: 'flex',
gap: '16px',
marginBottom: '20px',
padding: '12px',
backgroundColor: '#f8fafc',
borderRadius: '8px',
flexWrap: 'wrap',
},
legendItem: {
display: 'flex',
alignItems: 'center',
gap: '6px',
},
legendText: {
color: '#475569',
fontSize: '13px',
},
legendColor: {
width: '16px',
height: '16px',
borderRadius: '4px',
},
grid: {
display: 'grid',
gap: '16px',
gridTemplateColumns: 'repeat(auto-fill, minmax(280px, 1fr))',
},
card: {
display: 'block',
padding: '16px',
backgroundColor: '#f8fafc',
borderRadius: '8px',
border: '1px solid #e2e8f0',
textDecoration: 'none',
},
marker: {
display: 'grid',
placeItems: 'center',
width: '40px',
height: '32px',
marginBottom: '8px',
borderRadius: '6px',
color: 'white',
fontSize: '13px',
fontWeight: 'bold',
},
cardTitle: {
marginBottom: '4px',
color: '#0f172a',
fontWeight: 'bold',
},
cardDescription: {
color: '#64748b',
fontSize: '13px',
lineHeight: '1.4',
},
} satisfies Record<string, CSSProperties>

function legendColor(color: string): CSSProperties {
return {
...styles.legendColor,
backgroundColor: color,
}
}

function markerStyle(color: string): CSSProperties {
return {
...styles.marker,
backgroundColor: color,
}
}

function Home() {
return (
<main>
<h1>Rsbuild RSC fixture</h1>
<Link to="/rsc-node-module-client">Node module client component</Link>
<main style={styles.page} data-testid="index-page">
<h1 data-testid="home-title" style={styles.title}>
React Server Components Rsbuild E2E Tests
</h1>
<p style={styles.description}>
These examples cover Rsbuild-specific RSC behavior with clear visual
distinction between server-rendered content and client-side assets.
</p>

<div style={styles.legend}>
<div style={styles.legendItem}>
<span style={legendColor(colors.server)} />
<span style={styles.legendText}>Server Rendered (RSC)</span>
</div>
<div style={styles.legendItem}>
<span style={legendColor(colors.client)} />
<span style={styles.legendText}>Client Hydration and Assets</span>
</div>
<div style={styles.legendItem}>
<span style={legendColor(colors.async)} />
<span style={styles.legendText}>Rsbuild SSR Coverage</span>
</div>
</div>

<nav style={styles.grid}>
{examples.map(({ marker, markerColor, title, description, ...link }) => (
<Link key={link.to} {...link} style={styles.card}>
<div style={markerStyle(markerColor)}>{marker}</div>
<div style={styles.cardTitle}>{title}</div>
<div style={styles.cardDescription}>{description}</div>
</Link>
))}
</nav>
</main>
)
}
30 changes: 30 additions & 0 deletions e2e/react-start/rsc-rsbuild/src/routes/rsc-css-url.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
.rsc-css-url-card {
display: grid;
gap: 12px;
max-width: 480px;
padding: 18px;
background-color: #ecfdf5;
border: 2px solid #10b981;
border-radius: 8px;
color: #064e3b;
}

.rsc-css-url-badge {
width: max-content;
padding: 3px 8px;
background-color: #047857;
border-radius: 4px;
color: #fff;
font-size: 11px;
font-weight: 700;
}

.rsc-css-url-title {
margin: 0;
font-size: 18px;
}

.rsc-css-url-text {
margin: 0;
line-height: 1.5;
}
24 changes: 24 additions & 0 deletions e2e/react-start/rsc-rsbuild/src/routes/rsc-css-url.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { createFileRoute } from '@tanstack/react-router'
import cssUrl from './rsc-css-url.css?url'

export const Route = createFileRoute('/rsc-css-url')({
head: () => ({
links: [{ rel: 'stylesheet', href: cssUrl }],
}),
component: RscCssUrlComponent,
})

function RscCssUrlComponent() {
return (
<main>
<h1 data-testid="rsc-css-url-title">RSC css?url stylesheet</h1>
<section className="rsc-css-url-card" data-testid="rsc-css-url-card">
<span className="rsc-css-url-badge">CSS URL</span>
<h2 className="rsc-css-url-title">Stylesheet imported with ?url</h2>
<p className="rsc-css-url-text">
The SSR head should point at a client-served asset URL.
</p>
</section>
</main>
)
}
Loading