Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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 e2e/react-start/rsc-hmr/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
node_modules
dist
test-results
playwright-report
port*.txt
24 changes: 24 additions & 0 deletions e2e/react-start/rsc-hmr/eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// @ts-check

import tsParser from '@typescript-eslint/parser'
import startPlugin from '@tanstack/eslint-plugin-start'

export default [
{
files: ['src/**/*.{ts,tsx}'],
languageOptions: {
parser: tsParser,
parserOptions: {
project: './tsconfig.json',
tsconfigRootDir: import.meta.dirname,
},
},
plugins: {
'@tanstack/start': startPlugin,
},
rules: {
'@tanstack/start/no-client-code-in-server-component': 'error',
'@tanstack/start/no-async-client-component': 'error',
},
},
]
33 changes: 33 additions & 0 deletions e2e/react-start/rsc-hmr/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"name": "tanstack-react-start-e2e-rsc-hmr",
"private": true,
"sideEffects": false,
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
"type": "module",
"scripts": {
"dev": "vite dev --port 3000",
"dev:e2e": "vite dev --port $PORT",
"build": "vite build && tsc --noEmit",
"test:e2e": "MODE=dev playwright test --project=chromium"
},
"dependencies": {
"@tanstack/react-router": "workspace:^",
"@tanstack/react-router-devtools": "workspace:^",
"@tanstack/react-start": "workspace:^",
"react": "^19.0.0",
"react-dom": "^19.0.0"
},
"devDependencies": {
"@playwright/test": "^1.50.1",
"@tanstack/eslint-plugin-start": "workspace:^",
"@tanstack/router-e2e-utils": "workspace:^",
"@types/node": "^22.10.2",
"@types/react": "^19.0.8",
"@types/react-dom": "^19.0.3",
"@typescript-eslint/parser": "^8.23.0",
"@vitejs/plugin-react": "^6.0.1",
"@vitejs/plugin-rsc": "^0.5.20",
"eslint": "^9.22.0",
"typescript": "^5.7.2",
"vite": "^8.0.0"
}
}
39 changes: 39 additions & 0 deletions e2e/react-start/rsc-hmr/playwright.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { defineConfig, devices } from '@playwright/test'
import { getTestServerPort } from '@tanstack/router-e2e-utils'
import packageJson from './package.json' with { type: 'json' }

const PORT = await getTestServerPort(packageJson.name)
const baseURL = `http://localhost:${PORT}`

export default defineConfig({
testDir: './tests',
workers: 1,
reporter: [['line']],

globalSetup: './tests/setup/global.setup.ts',
globalTeardown: './tests/setup/global.teardown.ts',

use: {
baseURL,
},

webServer: {
command: `pnpm dev:e2e`,
url: baseURL,
reuseExistingServer: !process.env.CI,
stdout: 'pipe',
env: {
VITE_NODE_ENV: 'test',
PORT: String(PORT),
},
},

projects: [
{
name: 'chromium',
use: {
...devices['Desktop Chrome'],
},
},
],
})
104 changes: 104 additions & 0 deletions e2e/react-start/rsc-hmr/src/routeTree.gen.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/* 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.

import { Route as rootRouteImport } from './routes/__root'
import { Route as RscHmrGlobalCssRouteImport } from './routes/rsc-hmr-global-css'
import { Route as RscHmrCssModulesRouteImport } from './routes/rsc-hmr-css-modules'
import { Route as IndexRouteImport } from './routes/index'

const RscHmrGlobalCssRoute = RscHmrGlobalCssRouteImport.update({
id: '/rsc-hmr-global-css',
path: '/rsc-hmr-global-css',
getParentRoute: () => rootRouteImport,
} as any)
const RscHmrCssModulesRoute = RscHmrCssModulesRouteImport.update({
id: '/rsc-hmr-css-modules',
path: '/rsc-hmr-css-modules',
getParentRoute: () => rootRouteImport,
} as any)
const IndexRoute = IndexRouteImport.update({
id: '/',
path: '/',
getParentRoute: () => rootRouteImport,
} as any)

export interface FileRoutesByFullPath {
'/': typeof IndexRoute
'/rsc-hmr-css-modules': typeof RscHmrCssModulesRoute
'/rsc-hmr-global-css': typeof RscHmrGlobalCssRoute
}
export interface FileRoutesByTo {
'/': typeof IndexRoute
'/rsc-hmr-css-modules': typeof RscHmrCssModulesRoute
'/rsc-hmr-global-css': typeof RscHmrGlobalCssRoute
}
export interface FileRoutesById {
__root__: typeof rootRouteImport
'/': typeof IndexRoute
'/rsc-hmr-css-modules': typeof RscHmrCssModulesRoute
'/rsc-hmr-global-css': typeof RscHmrGlobalCssRoute
}
export interface FileRouteTypes {
fileRoutesByFullPath: FileRoutesByFullPath
fullPaths: '/' | '/rsc-hmr-css-modules' | '/rsc-hmr-global-css'
fileRoutesByTo: FileRoutesByTo
to: '/' | '/rsc-hmr-css-modules' | '/rsc-hmr-global-css'
id: '__root__' | '/' | '/rsc-hmr-css-modules' | '/rsc-hmr-global-css'
fileRoutesById: FileRoutesById
}
export interface RootRouteChildren {
IndexRoute: typeof IndexRoute
RscHmrCssModulesRoute: typeof RscHmrCssModulesRoute
RscHmrGlobalCssRoute: typeof RscHmrGlobalCssRoute
}

declare module '@tanstack/react-router' {
interface FileRoutesByPath {
'/rsc-hmr-global-css': {
id: '/rsc-hmr-global-css'
path: '/rsc-hmr-global-css'
fullPath: '/rsc-hmr-global-css'
preLoaderRoute: typeof RscHmrGlobalCssRouteImport
parentRoute: typeof rootRouteImport
}
'/rsc-hmr-css-modules': {
id: '/rsc-hmr-css-modules'
path: '/rsc-hmr-css-modules'
fullPath: '/rsc-hmr-css-modules'
preLoaderRoute: typeof RscHmrCssModulesRouteImport
parentRoute: typeof rootRouteImport
}
'/': {
id: '/'
path: '/'
fullPath: '/'
preLoaderRoute: typeof IndexRouteImport
parentRoute: typeof rootRouteImport
}
}
}

const rootRouteChildren: RootRouteChildren = {
IndexRoute: IndexRoute,
RscHmrCssModulesRoute: RscHmrCssModulesRoute,
RscHmrGlobalCssRoute: RscHmrGlobalCssRoute,
}
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' {
interface Register {
ssr: true
router: Awaited<ReturnType<typeof getRouter>>
}
}
12 changes: 12 additions & 0 deletions e2e/react-start/rsc-hmr/src/router.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { createRouter } from '@tanstack/react-router'
import { routeTree } from './routeTree.gen'

export function getRouter() {
const router = createRouter({
routeTree,
scrollRestoration: true,
defaultPreload: 'intent',
})

return router
}
79 changes: 79 additions & 0 deletions e2e/react-start/rsc-hmr/src/routes/__root.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/// <reference types="vite/client" />
import {
ClientOnly,
HeadContent,
Link,
Outlet,
Scripts,
createRootRoute,
} from '@tanstack/react-router'
import type { ReactNode } from 'react'

export const Route = createRootRoute({
head: () => ({
meta: [
{ charSet: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
],
}),
component: RootComponent,
})

function RootComponent() {
return (
<RootDocument>
<RootContent />
</RootDocument>
)
}

function RootDocument({ children }: { children: ReactNode }) {
return (
<html>
<head>
<HeadContent />
</head>
<body>
{children}
<Scripts />
</body>
</html>
)
}

function RootContent() {
const navLinks = [
{ testId: 'nav-home', to: '/', label: 'Home' },
{
testId: 'nav-global-css',
to: '/rsc-hmr-global-css',
label: 'Global CSS',
},
{
testId: 'nav-css-modules',
to: '/rsc-hmr-css-modules',
label: 'CSS Modules',
},
] as const

return (
<>
<nav style={{ display: 'flex', gap: 12, padding: 12 }}>
{navLinks.map((link) => (
<Link
activeProps={{ style: { fontWeight: 'bold' } }}
data-testid={link.testId}
key={link.testId}
to={link.to}
>
{link.label}
</Link>
))}
</nav>
<ClientOnly>
<span data-testid="hydrated">hydrated</span>
</ClientOnly>
<Outlet />
</>
)
}
17 changes: 17 additions & 0 deletions e2e/react-start/rsc-hmr/src/routes/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { createFileRoute } from '@tanstack/react-router'

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

function Home() {
return (
<main data-testid="home" style={{ padding: 16 }}>
<h1>RSC CSS HMR playground</h1>
<p>
Open one of the routes above and edit the corresponding CSS file in
<code> src/utils/ </code> to exercise CSS HMR through the RSC renderer.
</p>
</main>
)
}
15 changes: 15 additions & 0 deletions e2e/react-start/rsc-hmr/src/routes/rsc-hmr-css-modules.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { createFileRoute } from '@tanstack/react-router'
import { getCssModulesCardServerComponent } from '~/utils/cssModulesCardServerComponent'

export const Route = createFileRoute('/rsc-hmr-css-modules')({
loader: async () => {
const Server = await getCssModulesCardServerComponent()
return { Server }
},
component: RscHmrCssModules,
})

function RscHmrCssModules() {
const { Server } = Route.useLoaderData()
return <>{Server}</>
}
15 changes: 15 additions & 0 deletions e2e/react-start/rsc-hmr/src/routes/rsc-hmr-global-css.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { createFileRoute } from '@tanstack/react-router'
import { getGlobalCssCardServerComponent } from '~/utils/globalCssCardServerComponent'

export const Route = createFileRoute('/rsc-hmr-global-css')({
loader: async () => {
const Server = await getGlobalCssCardServerComponent()
return { Server }
},
component: RscHmrGlobalCss,
})

function RscHmrGlobalCss() {
const { Server } = Route.useLoaderData()
return <>{Server}</>
}
13 changes: 13 additions & 0 deletions e2e/react-start/rsc-hmr/src/utils/CssModulesCard.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
.card {
padding: 20px;
border: 1px solid #ddd;
border-radius: 8px;
margin: 20px;
}

.title {
color: rgb(128, 0, 128);
text-transform: uppercase;
letter-spacing: 0.05em;
font-size: 1.5rem;
}
12 changes: 12 additions & 0 deletions e2e/react-start/rsc-hmr/src/utils/CssModulesCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/// <reference types="vite/client" />
import styles from './CssModulesCard.module.css'

export function CssModulesCard() {
return (
<div className={styles.card} data-testid="rsc-hmr-modules-card">
<h2 className={styles.title} data-testid="rsc-hmr-modules-title">
Server Rendered
</h2>
</div>
)
}
13 changes: 13 additions & 0 deletions e2e/react-start/rsc-hmr/src/utils/GlobalCssCard.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
.rsc-hmr-global-card {
padding: 20px;
border: 1px solid #ddd;
border-radius: 8px;
margin: 20px;
}

.rsc-hmr-global-title {
color: rgb(128, 0, 128);
text-transform: uppercase;
letter-spacing: 0.05em;
font-size: 1.5rem;
}
Loading
Loading