Skip to content
Merged
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 e2e/react-router/escaped-special-strings/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
node_modules
dist
port*.txt
test-results
playwright-report
12 changes: 12 additions & 0 deletions e2e/react-router/escaped-special-strings/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Escaped Special Strings E2E Test</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
28 changes: 28 additions & 0 deletions e2e/react-router/escaped-special-strings/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"name": "tanstack-router-e2e-react-escaped-special-strings",
"private": true,
"type": "module",
"scripts": {
"dev": "vite --port 3000",
"dev:e2e": "vite",
"build": "vite build && tsc --noEmit",
"preview": "vite preview",
"start": "vite",
"test:e2e": "pnpm run test:e2e:default",
"test:e2e:default": "rm -rf port*.txt; playwright test --project=chromium"
},
"dependencies": {
"@tanstack/react-router": "workspace:^",
"@tanstack/router-plugin": "workspace:^",
"react": "^19.0.0",
"react-dom": "^19.0.0"
},
"devDependencies": {
"@playwright/test": "^1.50.1",
"@tanstack/router-e2e-utils": "workspace:^",
"@types/react": "^19.0.8",
"@types/react-dom": "^19.0.3",
"@vitejs/plugin-react": "^4.3.4",
"vite": "^7.1.7"
}
}
53 changes: 53 additions & 0 deletions e2e/react-router/escaped-special-strings/playwright.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { defineConfig, devices } from '@playwright/test'
import {
getDummyServerPort,
getTestServerPort,
} from '@tanstack/router-e2e-utils'
import packageJson from './package.json' with { type: 'json' }

const PORT = await getTestServerPort(packageJson.name)
const EXTERNAL_PORT = await getDummyServerPort(packageJson.name)
const baseURL = `http://localhost:${PORT}`
const command = `pnpm build && pnpm preview --port ${PORT}`

console.info('Running with mode: ', process.env.MODE || 'default')

/**
* See https://playwright.dev/docs/test-configuration.
*/
export default defineConfig({
testDir: './tests',
workers: 1,

reporter: [['line']],

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

use: {
/* Base URL to use in actions like `await page.goto('/')`. */
baseURL,
},

webServer: {
command,
url: baseURL,
reuseExistingServer: !process.env.CI,
stdout: 'pipe',
env: {
MODE: process.env.MODE || '',
VITE_MODE: process.env.MODE || '',
VITE_NODE_ENV: 'test',
VITE_EXTERNAL_PORT: String(EXTERNAL_PORT),
VITE_SERVER_PORT: String(PORT),
PORT: String(PORT),
},
},

projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
],
})
24 changes: 24 additions & 0 deletions e2e/react-router/escaped-special-strings/src/main.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import React from 'react'
import ReactDOM from 'react-dom/client'
import { RouterProvider, createRouter } from '@tanstack/react-router'
import { routeTree } from './routeTree.gen'

// Set up a Router instance
const router = createRouter({
routeTree,
defaultPreload: 'intent',
})

// Register things for typesafety
declare module '@tanstack/react-router' {
interface Register {
router: typeof router
}
}

const rootElement = document.getElementById('app')!

if (!rootElement.innerHTML) {
const root = ReactDOM.createRoot(rootElement)
root.render(<RouterProvider router={router} />)
}
131 changes: 131 additions & 0 deletions e2e/react-router/escaped-special-strings/src/routeTree.gen.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
/* 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 IndexRouteImport } from './routes/[index]'
import { Route as RouteRouteImport } from './routes/[route]'
import { Route as LazyRouteImport } from './routes/[lazy]'
import { Route as BlogRouteImport } from './routes/blog[_]'
import { Route as LayoutRouteImport } from './routes/[_]layout'

const IndexRoute = IndexRouteImport.update({
id: '/index',
path: '/index',
getParentRoute: () => rootRouteImport,
} as any)
const RouteRoute = RouteRouteImport.update({
id: '/route',
path: '/route',
getParentRoute: () => rootRouteImport,
} as any)
const LazyRoute = LazyRouteImport.update({
id: '/lazy',
path: '/lazy',
getParentRoute: () => rootRouteImport,
} as any)
const BlogRoute = BlogRouteImport.update({
id: '/blog_',
path: '/blog_',
getParentRoute: () => rootRouteImport,
} as any)
const LayoutRoute = LayoutRouteImport.update({
id: '/_layout',
path: '/_layout',
getParentRoute: () => rootRouteImport,
} as any)

export interface FileRoutesByFullPath {
'/_layout': typeof LayoutRoute
'/blog_': typeof BlogRoute
'/index': typeof IndexRoute
'/lazy': typeof LazyRoute
'/route': typeof RouteRoute
}
export interface FileRoutesByTo {
'/_layout': typeof LayoutRoute
'/blog_': typeof BlogRoute
'/index': typeof IndexRoute
'/lazy': typeof LazyRoute
'/route': typeof RouteRoute
}
export interface FileRoutesById {
__root__: typeof rootRouteImport
'/_layout': typeof LayoutRoute
'/blog_': typeof BlogRoute
'/index': typeof IndexRoute
'/lazy': typeof LazyRoute
'/route': typeof RouteRoute
}
export interface FileRouteTypes {
fileRoutesByFullPath: FileRoutesByFullPath
fullPaths: '/_layout' | '/blog_' | '/index' | '/lazy' | '/route'
fileRoutesByTo: FileRoutesByTo
to: '/_layout' | '/blog_' | '/index' | '/lazy' | '/route'
id: '__root__' | '/_layout' | '/blog_' | '/index' | '/lazy' | '/route'
fileRoutesById: FileRoutesById
}
export interface RootRouteChildren {
LayoutRoute: typeof LayoutRoute
BlogRoute: typeof BlogRoute
IndexRoute: typeof IndexRoute
LazyRoute: typeof LazyRoute
RouteRoute: typeof RouteRoute
}

declare module '@tanstack/react-router' {
interface FileRoutesByPath {
'/index': {
id: '/index'
path: '/index'
fullPath: '/index'
preLoaderRoute: typeof IndexRouteImport
parentRoute: typeof rootRouteImport
}
'/route': {
id: '/route'
path: '/route'
fullPath: '/route'
preLoaderRoute: typeof RouteRouteImport
parentRoute: typeof rootRouteImport
}
'/lazy': {
id: '/lazy'
path: '/lazy'
fullPath: '/lazy'
preLoaderRoute: typeof LazyRouteImport
parentRoute: typeof rootRouteImport
}
'/blog_': {
id: '/blog_'
path: '/blog_'
fullPath: '/blog_'
preLoaderRoute: typeof BlogRouteImport
parentRoute: typeof rootRouteImport
}
'/_layout': {
id: '/_layout'
path: '/_layout'
fullPath: '/_layout'
preLoaderRoute: typeof LayoutRouteImport
parentRoute: typeof rootRouteImport
}
}
}

const rootRouteChildren: RootRouteChildren = {
LayoutRoute: LayoutRoute,
BlogRoute: BlogRoute,
IndexRoute: IndexRoute,
LazyRoute: LazyRoute,
RouteRoute: RouteRoute,
}
export const routeTree = rootRouteImport
._addFileChildren(rootRouteChildren)
._addFileTypes<FileRouteTypes>()
21 changes: 21 additions & 0 deletions e2e/react-router/escaped-special-strings/src/routes/[_]layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { createFileRoute } from '@tanstack/react-router'

// This file uses [_]layout escaping to create a literal /_layout path
// instead of being treated as a pathless layout route
export const Route = createFileRoute('/_layout')({
component: EscapedUnderscoreLayoutComponent,
})

function EscapedUnderscoreLayoutComponent() {
return (
<div>
<h2 data-testid="page-title">Escaped Underscore Layout Page</h2>
<p data-testid="page-path">/_layout</p>
<p data-testid="page-description">
This route was created using [_]layout.tsx to escape the leading
underscore. It renders at the literal path /_layout instead of being a
pathless layout.
</p>
</div>
)
}
21 changes: 21 additions & 0 deletions e2e/react-router/escaped-special-strings/src/routes/[index].tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { createFileRoute } from '@tanstack/react-router'

// This file uses [index] escaping to create a literal /index route
// instead of being treated as an index route for the parent
export const Route = createFileRoute('/index')({
component: EscapedIndexComponent,
})

function EscapedIndexComponent() {
return (
<div>
<h2 data-testid="page-title">Escaped Index Page</h2>
<p data-testid="page-path">/index</p>
<p data-testid="page-description">
This route was created using [index].tsx to escape the special "index"
token. It renders at the literal path /index instead of being the index
route.
</p>
</div>
)
}
21 changes: 21 additions & 0 deletions e2e/react-router/escaped-special-strings/src/routes/[lazy].tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { createFileRoute } from '@tanstack/react-router'

// This file uses [lazy] escaping to create a literal /lazy path
// instead of being treated as a lazy-loaded route
export const Route = createFileRoute('/lazy')({
component: EscapedLazyComponent,
})

function EscapedLazyComponent() {
return (
<div>
<h2 data-testid="page-title">Escaped Lazy Page</h2>
<p data-testid="page-path">/lazy</p>
<p data-testid="page-description">
This route was created using [lazy].tsx to escape the special "lazy"
token. It renders at the literal path /lazy instead of being a
lazy-loaded route.
</p>
</div>
)
}
21 changes: 21 additions & 0 deletions e2e/react-router/escaped-special-strings/src/routes/[route].tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { createFileRoute } from '@tanstack/react-router'

// This file uses [route] escaping to create a literal /route path
// instead of being treated as a layout configuration file
export const Route = createFileRoute('/route')({
component: EscapedRouteComponent,
})

function EscapedRouteComponent() {
return (
<div>
<h2 data-testid="page-title">Escaped Route Page</h2>
<p data-testid="page-path">/route</p>
<p data-testid="page-description">
This route was created using [route].tsx to escape the special "route"
token. It renders at the literal path /route instead of being a layout
configuration.
</p>
</div>
)
}
48 changes: 48 additions & 0 deletions e2e/react-router/escaped-special-strings/src/routes/__root.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { Link, Outlet, createRootRoute } from '@tanstack/react-router'

export const Route = createRootRoute({
component: RootComponent,
notFoundComponent: () => {
return (
<div>
<p data-testid="not-found">Page not found</p>
<Link to="/index">Go to /index</Link>
</div>
)
},
})

function RootComponent() {
return (
<div>
<h1>Escaped Special Strings Test</h1>
<nav
style={{
display: 'flex',
gap: '1rem',
padding: '1rem',
borderBottom: '1px solid #ccc',
}}
>
<Link to="/index" data-testid="link-index">
/index
</Link>
<Link to="/route" data-testid="link-route">
/route
</Link>
<Link to="/lazy" data-testid="link-lazy">
/lazy
</Link>
<Link to="/_layout" data-testid="link-underscore-layout">
/_layout
</Link>
<Link to="/blog_" data-testid="link-blog-underscore">
/blog_
</Link>
</nav>
<main style={{ padding: '1rem' }}>
<Outlet />
</main>
</div>
)
}
Loading
Loading