diff --git a/e2e/react-start/basic/package.json b/e2e/react-start/basic/package.json index df58e0ae22e..ae4d3939f5f 100644 --- a/e2e/react-start/basic/package.json +++ b/e2e/react-start/basic/package.json @@ -8,7 +8,9 @@ "dev:e2e": "vite dev", "build": "vite build && tsc --noEmit", "start": "pnpx srvx --prod -s ../client dist/server/server.js", - "test:e2e": "rm -rf port*.txt; playwright test --project=chromium" + "test:e2e:spaMode": "rm -rf port*.txt; MODE=spa playwright test --project=chromium", + "test:e2e:ssrMode": "rm -rf port*.txt; playwright test --project=chromium", + "test:e2e": "pnpm run test:e2e:spaMode && pnpm run test:e2e:ssrMode" }, "dependencies": { "@tanstack/react-router": "workspace:^", diff --git a/e2e/react-start/basic/playwright.config.ts b/e2e/react-start/basic/playwright.config.ts index b0c365f8bd1..42b2938b328 100644 --- a/e2e/react-start/basic/playwright.config.ts +++ b/e2e/react-start/basic/playwright.config.ts @@ -3,19 +3,22 @@ import { getDummyServerPort, getTestServerPort, } from '@tanstack/router-e2e-utils' +import { isSpaMode } from 'tests/utils/isSpaMode' 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 spaModeCommand = `pnpm build && pnpm dev:e2e --port=${PORT}` +const ssrModeCommand = `pnpm build && pnpm start` +console.log('running in spa mode: ', isSpaMode.toString()) /** * See https://playwright.dev/docs/test-configuration. */ export default defineConfig({ testDir: './tests', workers: 1, - reporter: [['line']], globalSetup: './tests/setup/global.setup.ts', @@ -27,16 +30,25 @@ export default defineConfig({ }, webServer: { - command: `VITE_NODE_ENV="test" VITE_EXTERNAL_PORT=${EXTERNAL_PORT} pnpm build && VITE_NODE_ENV="test" VITE_EXTERNAL_PORT=${EXTERNAL_PORT} VITE_SERVER_PORT=${PORT} PORT=${PORT} pnpm start`, + command: isSpaMode ? spaModeCommand : ssrModeCommand, url: baseURL, reuseExistingServer: !process.env.CI, stdout: 'pipe', + env: { + 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'] }, + use: { + ...devices['Desktop Chrome'], + }, }, ], }) diff --git a/e2e/react-start/basic/src/routeTree.gen.ts b/e2e/react-start/basic/src/routeTree.gen.ts index a88d7953940..e29bb26d3ce 100644 --- a/e2e/react-start/basic/src/routeTree.gen.ts +++ b/e2e/react-start/basic/src/routeTree.gen.ts @@ -9,7 +9,6 @@ // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. import { createFileRoute } from '@tanstack/react-router' -import type { CreateFileRoute, FileRoutesByPath } from '@tanstack/react-router' import { Route as rootRouteImport } from './routes/__root' import { Route as Char45824Char54620Char48124Char44397RouteImport } from './routes/대한민국' @@ -548,11 +547,11 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof PostsRouteImport parentRoute: typeof rootRouteImport } - '/inline-scripts': { - id: '/inline-scripts' - path: '/inline-scripts' - fullPath: '/inline-scripts' - preLoaderRoute: typeof InlineScriptsRouteImport + '/links': { + id: '/links' + path: '/links' + fullPath: '/links' + preLoaderRoute: typeof LinksRouteImport parentRoute: typeof rootRouteImport } '/inline-scripts': { @@ -562,13 +561,6 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof InlineScriptsRouteImport parentRoute: typeof rootRouteImport } - '/links': { - id: '/links' - path: '/links' - fullPath: '/links' - preLoaderRoute: typeof LinksRouteImport - parentRoute: typeof rootRouteImport - } '/deferred': { id: '/deferred' path: '/deferred' @@ -785,254 +777,6 @@ declare module '@tanstack/react-router' { fullPath: '/redirect/$target/serverFn/via-beforeLoad' preLoaderRoute: typeof RedirectTargetServerFnViaBeforeLoadRouteImport parentRoute: typeof RedirectTargetRoute - '/foo/$bar/$qux/_here/': { - id: '/foo/$bar/$qux/_here/' - path: '/' - fullPath: '/foo/$bar/$qux/' - preLoaderRoute: typeof FooBarQuxHereIndexRouteImport - parentRoute: typeof FooBarQuxHereRoute - } - '/foo/$bar/$qux': { - id: '/foo/$bar/$qux' - path: '/foo/$bar/$qux' - fullPath: '/foo/$bar/$qux' - preLoaderRoute: typeof FooBarQuxRouteImport - parentRoute: typeof rootRouteImport - } - } -} -declare module '@tanstack/react-start/server' { - interface ServerFileRoutesByPath { - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: unknown - parentRoute: typeof rootServerRouteImport - } - '/not-found': { - id: '/not-found' - path: '/not-found' - fullPath: '/not-found' - preLoaderRoute: unknown - parentRoute: typeof rootServerRouteImport - } - '/search-params': { - id: '/search-params' - path: '/search-params' - fullPath: '/search-params' - preLoaderRoute: unknown - parentRoute: typeof rootServerRouteImport - } - '/_layout': { - id: '/_layout' - path: '' - fullPath: '' - preLoaderRoute: unknown - parentRoute: typeof rootServerRouteImport - } - '/deferred': { - id: '/deferred' - path: '/deferred' - fullPath: '/deferred' - preLoaderRoute: unknown - parentRoute: typeof rootServerRouteImport - } - '/inline-scripts': { - id: '/inline-scripts' - path: '/inline-scripts' - fullPath: '/inline-scripts' - preLoaderRoute: unknown - parentRoute: typeof rootServerRouteImport - } - '/links': { - id: '/links' - path: '/links' - fullPath: '/links' - preLoaderRoute: unknown - parentRoute: typeof rootServerRouteImport - } - '/posts': { - id: '/posts' - path: '/posts' - fullPath: '/posts' - preLoaderRoute: unknown - parentRoute: typeof rootServerRouteImport - } - '/scripts': { - id: '/scripts' - path: '/scripts' - fullPath: '/scripts' - preLoaderRoute: unknown - parentRoute: typeof rootServerRouteImport - } - '/stream': { - id: '/stream' - path: '/stream' - fullPath: '/stream' - preLoaderRoute: unknown - parentRoute: typeof rootServerRouteImport - } - '/users': { - id: '/users' - path: '/users' - fullPath: '/users' - preLoaderRoute: unknown - parentRoute: typeof rootServerRouteImport - } - '/대한민국': { - id: '/대한민국' - path: '/대한민국' - fullPath: '/대한민국' - preLoaderRoute: unknown - parentRoute: typeof rootServerRouteImport - } - '/_layout/_layout-2': { - id: '/_layout/_layout-2' - path: '' - fullPath: '' - preLoaderRoute: unknown - parentRoute: typeof rootServerRouteImport - } - '/api/users': { - id: '/api/users' - path: '/api/users' - fullPath: '/api/users' - preLoaderRoute: typeof ApiUsersServerRouteImport - parentRoute: typeof rootServerRouteImport - } - '/not-found/via-beforeLoad': { - id: '/not-found/via-beforeLoad' - path: '/via-beforeLoad' - fullPath: '/not-found/via-beforeLoad' - preLoaderRoute: unknown - parentRoute: typeof rootServerRouteImport - } - '/not-found/via-loader': { - id: '/not-found/via-loader' - path: '/via-loader' - fullPath: '/not-found/via-loader' - preLoaderRoute: unknown - parentRoute: typeof rootServerRouteImport - } - '/posts/$postId': { - id: '/posts/$postId' - path: '/$postId' - fullPath: '/posts/$postId' - preLoaderRoute: unknown - parentRoute: typeof rootServerRouteImport - } - '/redirect/$target': { - id: '/redirect/$target' - path: '/redirect/$target' - fullPath: '/redirect/$target' - preLoaderRoute: unknown - parentRoute: typeof rootServerRouteImport - } - '/search-params/default': { - id: '/search-params/default' - path: '/default' - fullPath: '/search-params/default' - preLoaderRoute: unknown - parentRoute: typeof rootServerRouteImport - } - '/search-params/loader-throws-redirect': { - id: '/search-params/loader-throws-redirect' - path: '/loader-throws-redirect' - fullPath: '/search-params/loader-throws-redirect' - preLoaderRoute: unknown - parentRoute: typeof rootServerRouteImport - } - '/users/$userId': { - id: '/users/$userId' - path: '/$userId' - fullPath: '/users/$userId' - preLoaderRoute: unknown - parentRoute: typeof rootServerRouteImport - } - '/not-found/': { - id: '/not-found/' - path: '/' - fullPath: '/not-found/' - preLoaderRoute: unknown - parentRoute: typeof rootServerRouteImport - } - '/posts/': { - id: '/posts/' - path: '/' - fullPath: '/posts/' - preLoaderRoute: unknown - parentRoute: typeof rootServerRouteImport - } - '/redirect/': { - id: '/redirect/' - path: '/redirect' - fullPath: '/redirect' - preLoaderRoute: unknown - parentRoute: typeof rootServerRouteImport - } - '/search-params/': { - id: '/search-params/' - path: '/' - fullPath: '/search-params/' - preLoaderRoute: unknown - parentRoute: typeof rootServerRouteImport - } - '/users/': { - id: '/users/' - path: '/' - fullPath: '/users/' - preLoaderRoute: unknown - parentRoute: typeof rootServerRouteImport - } - '/_layout/_layout-2/layout-a': { - id: '/_layout/_layout-2/layout-a' - path: '/layout-a' - fullPath: '/layout-a' - preLoaderRoute: unknown - parentRoute: typeof rootServerRouteImport - } - '/_layout/_layout-2/layout-b': { - id: '/_layout/_layout-2/layout-b' - path: '/layout-b' - fullPath: '/layout-b' - preLoaderRoute: unknown - parentRoute: typeof rootServerRouteImport - } - '/api/users/$id': { - id: '/api/users/$id' - path: '/$id' - fullPath: '/api/users/$id' - preLoaderRoute: typeof ApiUsersIdServerRouteImport - parentRoute: typeof ApiUsersServerRoute - } - '/posts_/$postId/deep': { - id: '/posts_/$postId/deep' - path: '/posts/$postId/deep' - fullPath: '/posts/$postId/deep' - preLoaderRoute: unknown - parentRoute: typeof rootServerRouteImport - } - '/redirect/$target/via-beforeLoad': { - id: '/redirect/$target/via-beforeLoad' - path: '/via-beforeLoad' - fullPath: '/redirect/$target/via-beforeLoad' - preLoaderRoute: unknown - parentRoute: typeof rootServerRouteImport - } - '/redirect/$target/via-loader': { - id: '/redirect/$target/via-loader' - path: '/via-loader' - fullPath: '/redirect/$target/via-loader' - preLoaderRoute: unknown - parentRoute: typeof rootServerRouteImport - } - '/redirect/$target/': { - id: '/redirect/$target/' - path: '/' - fullPath: '/redirect/$target/' - preLoaderRoute: unknown - parentRoute: typeof rootServerRouteImport } '/foo/$bar/$qux/_here': { id: '/foo/$bar/$qux/_here' @@ -1051,374 +795,6 @@ declare module '@tanstack/react-start/server' { } } -declare module './routes/index' { - const createFileRoute: CreateFileRoute< - '/', - FileRoutesByPath['/']['parentRoute'], - FileRoutesByPath['/']['id'], - FileRoutesByPath['/']['path'], - FileRoutesByPath['/']['fullPath'] - > -} -declare module './routes/not-found/route' { - const createFileRoute: CreateFileRoute< - '/not-found', - FileRoutesByPath['/not-found']['parentRoute'], - FileRoutesByPath['/not-found']['id'], - FileRoutesByPath['/not-found']['path'], - FileRoutesByPath['/not-found']['fullPath'] - > -} -declare module './routes/search-params/route' { - const createFileRoute: CreateFileRoute< - '/search-params', - FileRoutesByPath['/search-params']['parentRoute'], - FileRoutesByPath['/search-params']['id'], - FileRoutesByPath['/search-params']['path'], - FileRoutesByPath['/search-params']['fullPath'] - > -} -declare module './routes/_layout' { - const createFileRoute: CreateFileRoute< - '/_layout', - FileRoutesByPath['/_layout']['parentRoute'], - FileRoutesByPath['/_layout']['id'], - FileRoutesByPath['/_layout']['path'], - FileRoutesByPath['/_layout']['fullPath'] - > -} -declare module './routes/deferred' { - const createFileRoute: CreateFileRoute< - '/deferred', - FileRoutesByPath['/deferred']['parentRoute'], - FileRoutesByPath['/deferred']['id'], - FileRoutesByPath['/deferred']['path'], - FileRoutesByPath['/deferred']['fullPath'] - > - - const createServerFileRoute: CreateServerFileRoute< - ServerFileRoutesByPath['/deferred']['parentRoute'], - ServerFileRoutesByPath['/deferred']['id'], - ServerFileRoutesByPath['/deferred']['path'], - ServerFileRoutesByPath['/deferred']['fullPath'], - unknown - > -} -declare module './routes/inline-scripts' { - const createFileRoute: CreateFileRoute< - '/inline-scripts', - FileRoutesByPath['/inline-scripts']['parentRoute'], - FileRoutesByPath['/inline-scripts']['id'], - FileRoutesByPath['/inline-scripts']['path'], - FileRoutesByPath['/inline-scripts']['fullPath'] - > - - const createServerFileRoute: CreateServerFileRoute< - ServerFileRoutesByPath['/inline-scripts']['parentRoute'], - ServerFileRoutesByPath['/inline-scripts']['id'], - ServerFileRoutesByPath['/inline-scripts']['path'], - ServerFileRoutesByPath['/inline-scripts']['fullPath'], - unknown - > -} -declare module './routes/links' { - const createFileRoute: CreateFileRoute< - '/links', - FileRoutesByPath['/links']['parentRoute'], - FileRoutesByPath['/links']['id'], - FileRoutesByPath['/links']['path'], - FileRoutesByPath['/links']['fullPath'] - > -} -declare module './routes/posts' { - const createFileRoute: CreateFileRoute< - '/posts', - FileRoutesByPath['/posts']['parentRoute'], - FileRoutesByPath['/posts']['id'], - FileRoutesByPath['/posts']['path'], - FileRoutesByPath['/posts']['fullPath'] - > -} -declare module './routes/scripts' { - const createFileRoute: CreateFileRoute< - '/scripts', - FileRoutesByPath['/scripts']['parentRoute'], - FileRoutesByPath['/scripts']['id'], - FileRoutesByPath['/scripts']['path'], - FileRoutesByPath['/scripts']['fullPath'] - > -} -declare module './routes/stream' { - const createFileRoute: CreateFileRoute< - '/stream', - FileRoutesByPath['/stream']['parentRoute'], - FileRoutesByPath['/stream']['id'], - FileRoutesByPath['/stream']['path'], - FileRoutesByPath['/stream']['fullPath'] - > -} -declare module './routes/users' { - const createFileRoute: CreateFileRoute< - '/users', - FileRoutesByPath['/users']['parentRoute'], - FileRoutesByPath['/users']['id'], - FileRoutesByPath['/users']['path'], - FileRoutesByPath['/users']['fullPath'] - > -} -declare module './routes/대한민국' { - const createFileRoute: CreateFileRoute< - '/대한민국', - FileRoutesByPath['/대한민국']['parentRoute'], - FileRoutesByPath['/대한민국']['id'], - FileRoutesByPath['/대한민국']['path'], - FileRoutesByPath['/대한민국']['fullPath'] - > -} -declare module './routes/_layout/_layout-2' { - const createFileRoute: CreateFileRoute< - '/_layout/_layout-2', - FileRoutesByPath['/_layout/_layout-2']['parentRoute'], - FileRoutesByPath['/_layout/_layout-2']['id'], - FileRoutesByPath['/_layout/_layout-2']['path'], - FileRoutesByPath['/_layout/_layout-2']['fullPath'] - > -} -declare module './routes/api.users' { - const createFileRoute: CreateFileRoute< - '/api/users', - FileRoutesByPath['/api/users']['parentRoute'], - FileRoutesByPath['/api/users']['id'], - FileRoutesByPath['/api/users']['path'], - FileRoutesByPath['/api/users']['fullPath'] - > -} -declare module './routes/not-found/via-beforeLoad' { - const createFileRoute: CreateFileRoute< - '/not-found/via-beforeLoad', - FileRoutesByPath['/not-found/via-beforeLoad']['parentRoute'], - FileRoutesByPath['/not-found/via-beforeLoad']['id'], - FileRoutesByPath['/not-found/via-beforeLoad']['path'], - FileRoutesByPath['/not-found/via-beforeLoad']['fullPath'] - > -} -declare module './routes/not-found/via-loader' { - const createFileRoute: CreateFileRoute< - '/not-found/via-loader', - FileRoutesByPath['/not-found/via-loader']['parentRoute'], - FileRoutesByPath['/not-found/via-loader']['id'], - FileRoutesByPath['/not-found/via-loader']['path'], - FileRoutesByPath['/not-found/via-loader']['fullPath'] - > -} -declare module './routes/posts.$postId' { - const createFileRoute: CreateFileRoute< - '/posts/$postId', - FileRoutesByPath['/posts/$postId']['parentRoute'], - FileRoutesByPath['/posts/$postId']['id'], - FileRoutesByPath['/posts/$postId']['path'], - FileRoutesByPath['/posts/$postId']['fullPath'] - > -} -declare module './routes/redirect/$target' { - const createFileRoute: CreateFileRoute< - '/redirect/$target', - FileRoutesByPath['/redirect/$target']['parentRoute'], - FileRoutesByPath['/redirect/$target']['id'], - FileRoutesByPath['/redirect/$target']['path'], - FileRoutesByPath['/redirect/$target']['fullPath'] - > -} -declare module './routes/search-params/default' { - const createFileRoute: CreateFileRoute< - '/search-params/default', - FileRoutesByPath['/search-params/default']['parentRoute'], - FileRoutesByPath['/search-params/default']['id'], - FileRoutesByPath['/search-params/default']['path'], - FileRoutesByPath['/search-params/default']['fullPath'] - > -} -declare module './routes/search-params/loader-throws-redirect' { - const createFileRoute: CreateFileRoute< - '/search-params/loader-throws-redirect', - FileRoutesByPath['/search-params/loader-throws-redirect']['parentRoute'], - FileRoutesByPath['/search-params/loader-throws-redirect']['id'], - FileRoutesByPath['/search-params/loader-throws-redirect']['path'], - FileRoutesByPath['/search-params/loader-throws-redirect']['fullPath'] - > -} -declare module './routes/users.$userId' { - const createFileRoute: CreateFileRoute< - '/users/$userId', - FileRoutesByPath['/users/$userId']['parentRoute'], - FileRoutesByPath['/users/$userId']['id'], - FileRoutesByPath['/users/$userId']['path'], - FileRoutesByPath['/users/$userId']['fullPath'] - > -} -declare module './routes/not-found/index' { - const createFileRoute: CreateFileRoute< - '/not-found/', - FileRoutesByPath['/not-found/']['parentRoute'], - FileRoutesByPath['/not-found/']['id'], - FileRoutesByPath['/not-found/']['path'], - FileRoutesByPath['/not-found/']['fullPath'] - > -} -declare module './routes/posts.index' { - const createFileRoute: CreateFileRoute< - '/posts/', - FileRoutesByPath['/posts/']['parentRoute'], - FileRoutesByPath['/posts/']['id'], - FileRoutesByPath['/posts/']['path'], - FileRoutesByPath['/posts/']['fullPath'] - > -} -declare module './routes/redirect/index' { - const createFileRoute: CreateFileRoute< - '/redirect/', - FileRoutesByPath['/redirect/']['parentRoute'], - FileRoutesByPath['/redirect/']['id'], - FileRoutesByPath['/redirect/']['path'], - FileRoutesByPath['/redirect/']['fullPath'] - > -} -declare module './routes/search-params/index' { - const createFileRoute: CreateFileRoute< - '/search-params/', - FileRoutesByPath['/search-params/']['parentRoute'], - FileRoutesByPath['/search-params/']['id'], - FileRoutesByPath['/search-params/']['path'], - FileRoutesByPath['/search-params/']['fullPath'] - > -} -declare module './routes/users.index' { - const createFileRoute: CreateFileRoute< - '/users/', - FileRoutesByPath['/users/']['parentRoute'], - FileRoutesByPath['/users/']['id'], - FileRoutesByPath['/users/']['path'], - FileRoutesByPath['/users/']['fullPath'] - > -} -declare module './routes/_layout/_layout-2/layout-a' { - const createFileRoute: CreateFileRoute< - '/_layout/_layout-2/layout-a', - FileRoutesByPath['/_layout/_layout-2/layout-a']['parentRoute'], - FileRoutesByPath['/_layout/_layout-2/layout-a']['id'], - FileRoutesByPath['/_layout/_layout-2/layout-a']['path'], - FileRoutesByPath['/_layout/_layout-2/layout-a']['fullPath'] - > -} -declare module './routes/_layout/_layout-2/layout-b' { - const createFileRoute: CreateFileRoute< - '/_layout/_layout-2/layout-b', - FileRoutesByPath['/_layout/_layout-2/layout-b']['parentRoute'], - FileRoutesByPath['/_layout/_layout-2/layout-b']['id'], - FileRoutesByPath['/_layout/_layout-2/layout-b']['path'], - FileRoutesByPath['/_layout/_layout-2/layout-b']['fullPath'] - > -} -declare module './routes/api/users.$id' { - const createFileRoute: CreateFileRoute< - '/api/users/$id', - FileRoutesByPath['/api/users/$id']['parentRoute'], - FileRoutesByPath['/api/users/$id']['id'], - FileRoutesByPath['/api/users/$id']['path'], - FileRoutesByPath['/api/users/$id']['fullPath'] - > -} -declare module './routes/posts_.$postId.deep' { - const createFileRoute: CreateFileRoute< - '/posts_/$postId/deep', - FileRoutesByPath['/posts_/$postId/deep']['parentRoute'], - FileRoutesByPath['/posts_/$postId/deep']['id'], - FileRoutesByPath['/posts_/$postId/deep']['path'], - FileRoutesByPath['/posts_/$postId/deep']['fullPath'] - > -} -declare module './routes/redirect/$target/via-beforeLoad' { - const createFileRoute: CreateFileRoute< - '/redirect/$target/via-beforeLoad', - FileRoutesByPath['/redirect/$target/via-beforeLoad']['parentRoute'], - FileRoutesByPath['/redirect/$target/via-beforeLoad']['id'], - FileRoutesByPath['/redirect/$target/via-beforeLoad']['path'], - FileRoutesByPath['/redirect/$target/via-beforeLoad']['fullPath'] - > -} -declare module './routes/redirect/$target/via-loader' { - const createFileRoute: CreateFileRoute< - '/redirect/$target/via-loader', - FileRoutesByPath['/redirect/$target/via-loader']['parentRoute'], - FileRoutesByPath['/redirect/$target/via-loader']['id'], - FileRoutesByPath['/redirect/$target/via-loader']['path'], - FileRoutesByPath['/redirect/$target/via-loader']['fullPath'] - > -} -declare module './routes/redirect/$target/index' { - const createFileRoute: CreateFileRoute< - '/redirect/$target/', - FileRoutesByPath['/redirect/$target/']['parentRoute'], - FileRoutesByPath['/redirect/$target/']['id'], - FileRoutesByPath['/redirect/$target/']['path'], - FileRoutesByPath['/redirect/$target/']['fullPath'] - > -} -declare module './routes/foo/$bar/$qux/_here' { - const createFileRoute: CreateFileRoute< - '/foo/$bar/$qux/_here', - FileRoutesByPath['/foo/$bar/$qux/_here']['parentRoute'], - FileRoutesByPath['/foo/$bar/$qux/_here']['id'], - FileRoutesByPath['/foo/$bar/$qux/_here']['path'], - FileRoutesByPath['/foo/$bar/$qux/_here']['fullPath'] - > -} -declare module './routes/redirect/$target/serverFn/via-beforeLoad' { - const createFileRoute: CreateFileRoute< - '/redirect/$target/serverFn/via-beforeLoad', - FileRoutesByPath['/redirect/$target/serverFn/via-beforeLoad']['parentRoute'], - FileRoutesByPath['/redirect/$target/serverFn/via-beforeLoad']['id'], - FileRoutesByPath['/redirect/$target/serverFn/via-beforeLoad']['path'], - FileRoutesByPath['/redirect/$target/serverFn/via-beforeLoad']['fullPath'] - > -} -declare module './routes/redirect/$target/serverFn/via-loader' { - const createFileRoute: CreateFileRoute< - '/redirect/$target/serverFn/via-loader', - FileRoutesByPath['/redirect/$target/serverFn/via-loader']['parentRoute'], - FileRoutesByPath['/redirect/$target/serverFn/via-loader']['id'], - FileRoutesByPath['/redirect/$target/serverFn/via-loader']['path'], - FileRoutesByPath['/redirect/$target/serverFn/via-loader']['fullPath'] - > -} -declare module './routes/redirect/$target/serverFn/via-useServerFn' { - const createFileRoute: CreateFileRoute< - '/redirect/$target/serverFn/via-useServerFn', - FileRoutesByPath['/redirect/$target/serverFn/via-useServerFn']['parentRoute'], - FileRoutesByPath['/redirect/$target/serverFn/via-useServerFn']['id'], - FileRoutesByPath['/redirect/$target/serverFn/via-useServerFn']['path'], - FileRoutesByPath['/redirect/$target/serverFn/via-useServerFn']['fullPath'] - > -} -declare module './routes/redirect/$target/serverFn/index' { - const createFileRoute: CreateFileRoute< - '/redirect/$target/serverFn/', - FileRoutesByPath['/redirect/$target/serverFn/']['parentRoute'], - FileRoutesByPath['/redirect/$target/serverFn/']['id'], - FileRoutesByPath['/redirect/$target/serverFn/']['path'], - FileRoutesByPath['/redirect/$target/serverFn/']['fullPath'] - > -} -declare module './routes/foo/$bar/$qux/_here/index' { - const createFileRoute: CreateFileRoute< - '/foo/$bar/$qux/_here/', - FileRoutesByPath['/foo/$bar/$qux/_here/']['parentRoute'], - FileRoutesByPath['/foo/$bar/$qux/_here/']['id'], - FileRoutesByPath['/foo/$bar/$qux/_here/']['path'], - FileRoutesByPath['/foo/$bar/$qux/_here/']['fullPath'] - > -} - interface NotFoundRouteRouteChildren { NotFoundViaBeforeLoadRoute: typeof NotFoundViaBeforeLoadRoute NotFoundViaLoaderRoute: typeof NotFoundViaLoaderRoute diff --git a/e2e/react-start/basic/src/routes/__root.tsx b/e2e/react-start/basic/src/routes/__root.tsx index 9c3a7138b0e..c7d87cad188 100644 --- a/e2e/react-start/basic/src/routes/__root.tsx +++ b/e2e/react-start/basic/src/routes/__root.tsx @@ -178,7 +178,9 @@ function RootDocument({ children }: { children: React.ReactNode }) {
{children}
This is an inline styled div
- + + + diff --git a/e2e/react-start/basic/src/routes/_layout.tsx b/e2e/react-start/basic/src/routes/_layout.tsx index 5c4a461d8d9..02ddbb1cd94 100644 --- a/e2e/react-start/basic/src/routes/_layout.tsx +++ b/e2e/react-start/basic/src/routes/_layout.tsx @@ -1,6 +1,6 @@ -import { Outlet } from '@tanstack/react-router' +import { Outlet, createFileRoute } from '@tanstack/react-router' -export const Route = createFileRoute({ +export const Route = createFileRoute('/_layout')({ component: LayoutComponent, }) diff --git a/e2e/react-start/basic/src/routes/_layout/_layout-2.tsx b/e2e/react-start/basic/src/routes/_layout/_layout-2.tsx index 483b910862b..3b7dbf29031 100644 --- a/e2e/react-start/basic/src/routes/_layout/_layout-2.tsx +++ b/e2e/react-start/basic/src/routes/_layout/_layout-2.tsx @@ -1,6 +1,6 @@ -import { Link, Outlet } from '@tanstack/react-router' +import { Link, Outlet, createFileRoute } from '@tanstack/react-router' -export const Route = createFileRoute({ +export const Route = createFileRoute('/_layout/_layout-2')({ component: LayoutComponent, }) diff --git a/e2e/react-start/basic/src/routes/_layout/_layout-2/layout-a.tsx b/e2e/react-start/basic/src/routes/_layout/_layout-2/layout-a.tsx index a190b242028..61e19b4d9f1 100644 --- a/e2e/react-start/basic/src/routes/_layout/_layout-2/layout-a.tsx +++ b/e2e/react-start/basic/src/routes/_layout/_layout-2/layout-a.tsx @@ -1,4 +1,6 @@ -export const Route = createFileRoute({ +import { createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute('/_layout/_layout-2/layout-a')({ component: LayoutAComponent, }) diff --git a/e2e/react-start/basic/src/routes/_layout/_layout-2/layout-b.tsx b/e2e/react-start/basic/src/routes/_layout/_layout-2/layout-b.tsx index 505f8f6fbf8..cceed1fb9ad 100644 --- a/e2e/react-start/basic/src/routes/_layout/_layout-2/layout-b.tsx +++ b/e2e/react-start/basic/src/routes/_layout/_layout-2/layout-b.tsx @@ -1,4 +1,6 @@ -export const Route = createFileRoute({ +import { createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute('/_layout/_layout-2/layout-b')({ component: LayoutBComponent, }) diff --git a/e2e/react-start/basic/src/routes/api.users.ts b/e2e/react-start/basic/src/routes/api.users.ts index a760bd201ee..a03076490b8 100644 --- a/e2e/react-start/basic/src/routes/api.users.ts +++ b/e2e/react-start/basic/src/routes/api.users.ts @@ -1,3 +1,4 @@ +import { createFileRoute } from '@tanstack/react-router' import { json } from '@tanstack/react-start' import axios from 'redaxios' @@ -9,7 +10,7 @@ if (import.meta.env.VITE_NODE_ENV === 'test') { queryURL = `http://localhost:${import.meta.env.VITE_EXTERNAL_PORT}` } -export const Route = createFileRoute({ +export const Route = createFileRoute('/api/users')({ server: { handlers: { GET: async ({ request }) => { diff --git a/e2e/react-start/basic/src/routes/api/users.$id.ts b/e2e/react-start/basic/src/routes/api/users.$id.ts index 41338898447..2d2c279e931 100644 --- a/e2e/react-start/basic/src/routes/api/users.$id.ts +++ b/e2e/react-start/basic/src/routes/api/users.$id.ts @@ -1,3 +1,4 @@ +import { createFileRoute } from '@tanstack/react-router' import { json } from '@tanstack/react-start' import axios from 'redaxios' import type { User } from '~/utils/users' @@ -8,7 +9,7 @@ if (import.meta.env.VITE_NODE_ENV === 'test') { queryURL = `http://localhost:${import.meta.env.VITE_EXTERNAL_PORT}` } -export const Route = createFileRoute({ +export const Route = createFileRoute('/api/users/$id')({ server: { handlers: { GET: async ({ request, params }) => { diff --git a/e2e/react-start/basic/src/routes/deferred.tsx b/e2e/react-start/basic/src/routes/deferred.tsx index b8c050d0965..9c6e3064b88 100644 --- a/e2e/react-start/basic/src/routes/deferred.tsx +++ b/e2e/react-start/basic/src/routes/deferred.tsx @@ -1,4 +1,4 @@ -import { Await } from '@tanstack/react-router' +import { Await, createFileRoute } from '@tanstack/react-router' import { createServerFn } from '@tanstack/react-start' import { Suspense, useState } from 'react' @@ -15,7 +15,7 @@ const slowServerFn = createServerFn({ method: 'GET' }) return { name: data.name, randomNumber: Math.floor(Math.random() * 100) } }) -export const Route = createFileRoute({ +export const Route = createFileRoute('/deferred')({ loader: async () => { return { deferredStuff: new Promise((r) => diff --git a/e2e/react-start/basic/src/routes/foo/$bar/$qux/_here.tsx b/e2e/react-start/basic/src/routes/foo/$bar/$qux/_here.tsx index 9e236629fb6..95a5599e237 100644 --- a/e2e/react-start/basic/src/routes/foo/$bar/$qux/_here.tsx +++ b/e2e/react-start/basic/src/routes/foo/$bar/$qux/_here.tsx @@ -1,6 +1,6 @@ -import { Outlet } from '@tanstack/react-router' +import { Outlet, createFileRoute } from '@tanstack/react-router' -export const Route = createFileRoute({ +export const Route = createFileRoute('/foo/$bar/$qux/_here')({ component: LayoutComponent, }) diff --git a/e2e/react-start/basic/src/routes/foo/$bar/$qux/_here/index.tsx b/e2e/react-start/basic/src/routes/foo/$bar/$qux/_here/index.tsx index f824b5243b4..924c8bb3c16 100644 --- a/e2e/react-start/basic/src/routes/foo/$bar/$qux/_here/index.tsx +++ b/e2e/react-start/basic/src/routes/foo/$bar/$qux/_here/index.tsx @@ -1,4 +1,6 @@ -export const Route = createFileRoute({ +import { createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute('/foo/$bar/$qux/_here/')({ component: RouteComponent, }) diff --git a/e2e/react-start/basic/src/routes/index.tsx b/e2e/react-start/basic/src/routes/index.tsx index f2bb5d3f32b..37169a78b4a 100644 --- a/e2e/react-start/basic/src/routes/index.tsx +++ b/e2e/react-start/basic/src/routes/index.tsx @@ -1,6 +1,7 @@ +import { createFileRoute } from '@tanstack/react-router' import { CustomMessage } from '~/components/CustomMessage' -export const Route = createFileRoute({ +export const Route = createFileRoute('/')({ component: Home, }) diff --git a/e2e/react-start/basic/src/routes/inline-scripts.tsx b/e2e/react-start/basic/src/routes/inline-scripts.tsx index 282cb93804c..86255c63bd4 100644 --- a/e2e/react-start/basic/src/routes/inline-scripts.tsx +++ b/e2e/react-start/basic/src/routes/inline-scripts.tsx @@ -1,4 +1,6 @@ -export const Route = createFileRoute({ +import { createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute('/inline-scripts')({ head: () => ({ scripts: [ { diff --git a/e2e/react-start/basic/src/routes/links.tsx b/e2e/react-start/basic/src/routes/links.tsx index 81b53edfd79..adc8fe9c4d7 100644 --- a/e2e/react-start/basic/src/routes/links.tsx +++ b/e2e/react-start/basic/src/routes/links.tsx @@ -1,6 +1,6 @@ -import { Link } from '@tanstack/react-router' +import { Link, createFileRoute } from '@tanstack/react-router' -export const Route = createFileRoute({ +export const Route = createFileRoute('/links')({ component: () => { const navigate = Route.useNavigate() return ( diff --git a/e2e/react-start/basic/src/routes/not-found/index.tsx b/e2e/react-start/basic/src/routes/not-found/index.tsx index 7abe4a389f8..e754f83c74b 100644 --- a/e2e/react-start/basic/src/routes/not-found/index.tsx +++ b/e2e/react-start/basic/src/routes/not-found/index.tsx @@ -1,6 +1,6 @@ -import { Link } from '@tanstack/react-router' +import { Link, createFileRoute } from '@tanstack/react-router' -export const Route = createFileRoute({ +export const Route = createFileRoute('/not-found/')({ component: () => { const preload = Route.useSearch({ select: (s) => s.preload }) return ( diff --git a/e2e/react-start/basic/src/routes/not-found/route.tsx b/e2e/react-start/basic/src/routes/not-found/route.tsx index b8343167f5e..e604e098fd0 100644 --- a/e2e/react-start/basic/src/routes/not-found/route.tsx +++ b/e2e/react-start/basic/src/routes/not-found/route.tsx @@ -1,6 +1,7 @@ +import { createFileRoute } from '@tanstack/react-router' import z from 'zod' -export const Route = createFileRoute({ +export const Route = createFileRoute('/not-found')({ validateSearch: z.object({ preload: z.literal(false).optional(), }), diff --git a/e2e/react-start/basic/src/routes/not-found/via-beforeLoad.tsx b/e2e/react-start/basic/src/routes/not-found/via-beforeLoad.tsx index cc04e59dece..85164dbab5b 100644 --- a/e2e/react-start/basic/src/routes/not-found/via-beforeLoad.tsx +++ b/e2e/react-start/basic/src/routes/not-found/via-beforeLoad.tsx @@ -1,6 +1,6 @@ -import { notFound } from '@tanstack/react-router' +import { createFileRoute, notFound } from '@tanstack/react-router' -export const Route = createFileRoute({ +export const Route = createFileRoute('/not-found/via-beforeLoad')({ beforeLoad: () => { throw notFound() }, diff --git a/e2e/react-start/basic/src/routes/not-found/via-loader.tsx b/e2e/react-start/basic/src/routes/not-found/via-loader.tsx index 8ca69f7bb60..6174b27f775 100644 --- a/e2e/react-start/basic/src/routes/not-found/via-loader.tsx +++ b/e2e/react-start/basic/src/routes/not-found/via-loader.tsx @@ -1,6 +1,6 @@ -import { notFound } from '@tanstack/react-router' +import { createFileRoute, notFound } from '@tanstack/react-router' -export const Route = createFileRoute({ +export const Route = createFileRoute('/not-found/via-loader')({ loader: () => { throw notFound() }, diff --git a/e2e/react-start/basic/src/routes/posts.$postId.tsx b/e2e/react-start/basic/src/routes/posts.$postId.tsx index 68c663c7e8a..09d00685829 100644 --- a/e2e/react-start/basic/src/routes/posts.$postId.tsx +++ b/e2e/react-start/basic/src/routes/posts.$postId.tsx @@ -1,10 +1,10 @@ -import { ErrorComponent, Link } from '@tanstack/react-router' +import { ErrorComponent, Link, createFileRoute } from '@tanstack/react-router' import type { ErrorComponentProps } from '@tanstack/react-router' import { fetchPost } from '~/utils/posts' import { NotFound } from '~/components/NotFound' -export const Route = createFileRoute({ +export const Route = createFileRoute('/posts/$postId')({ loader: async ({ params: { postId } }) => fetchPost({ data: postId }), errorComponent: PostErrorComponent, component: PostComponent, @@ -13,7 +13,7 @@ export const Route = createFileRoute({ }, }) -export function PostErrorComponent({ error }: ErrorComponentProps) { +function PostErrorComponent({ error }: ErrorComponentProps) { return } diff --git a/e2e/react-start/basic/src/routes/posts.index.tsx b/e2e/react-start/basic/src/routes/posts.index.tsx index 07e41d1b0b8..1bad79b0f7c 100644 --- a/e2e/react-start/basic/src/routes/posts.index.tsx +++ b/e2e/react-start/basic/src/routes/posts.index.tsx @@ -1,4 +1,6 @@ -export const Route = createFileRoute({ +import { createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute('/posts/')({ component: PostsIndexComponent, }) diff --git a/e2e/react-start/basic/src/routes/posts.tsx b/e2e/react-start/basic/src/routes/posts.tsx index 6fa5bfbe8b6..0f69c183419 100644 --- a/e2e/react-start/basic/src/routes/posts.tsx +++ b/e2e/react-start/basic/src/routes/posts.tsx @@ -1,8 +1,8 @@ -import { Link, Outlet } from '@tanstack/react-router' +import { Link, Outlet, createFileRoute } from '@tanstack/react-router' import { fetchPosts } from '~/utils/posts' -export const Route = createFileRoute({ +export const Route = createFileRoute('/posts')({ head: () => ({ meta: [ { diff --git a/e2e/react-start/basic/src/routes/posts_.$postId.deep.tsx b/e2e/react-start/basic/src/routes/posts_.$postId.deep.tsx index ea326087b8c..1f785f5f7ff 100644 --- a/e2e/react-start/basic/src/routes/posts_.$postId.deep.tsx +++ b/e2e/react-start/basic/src/routes/posts_.$postId.deep.tsx @@ -1,14 +1,17 @@ -import { Link } from '@tanstack/react-router' - -import { PostErrorComponent } from './posts.$postId' +import { ErrorComponent, Link, createFileRoute } from '@tanstack/react-router' +import type { ErrorComponentProps } from '@tanstack/react-router' import { fetchPost } from '~/utils/posts' -export const Route = createFileRoute({ +export const Route = createFileRoute('/posts_/$postId/deep')({ loader: async ({ params: { postId } }) => fetchPost({ data: postId }), - errorComponent: PostErrorComponent, + errorComponent: PostDeepErrorComponent, component: PostDeepComponent, }) +function PostDeepErrorComponent({ error }: ErrorComponentProps) { + return +} + function PostDeepComponent() { const post = Route.useLoaderData() diff --git a/e2e/react-start/basic/src/routes/redirect/$target.tsx b/e2e/react-start/basic/src/routes/redirect/$target.tsx index 099a7ab0bee..686f1c7056a 100644 --- a/e2e/react-start/basic/src/routes/redirect/$target.tsx +++ b/e2e/react-start/basic/src/routes/redirect/$target.tsx @@ -1,7 +1,7 @@ -import { retainSearchParams } from '@tanstack/react-router' +import { createFileRoute, retainSearchParams } from '@tanstack/react-router' import z from 'zod' -export const Route = createFileRoute({ +export const Route = createFileRoute('/redirect/$target')({ params: { parse: (p) => z diff --git a/e2e/react-start/basic/src/routes/redirect/$target/index.tsx b/e2e/react-start/basic/src/routes/redirect/$target/index.tsx index d4878e09907..f399d965cf1 100644 --- a/e2e/react-start/basic/src/routes/redirect/$target/index.tsx +++ b/e2e/react-start/basic/src/routes/redirect/$target/index.tsx @@ -1,6 +1,6 @@ -import { Link } from '@tanstack/react-router' +import { Link, createFileRoute } from '@tanstack/react-router' -export const Route = createFileRoute({ +export const Route = createFileRoute('/redirect/$target/')({ component: () => { const preload = Route.useSearch({ select: (s) => s.preload }) return ( diff --git a/e2e/react-start/basic/src/routes/redirect/$target/serverFn/index.tsx b/e2e/react-start/basic/src/routes/redirect/$target/serverFn/index.tsx index 12118917d6e..ddb0d8e9964 100644 --- a/e2e/react-start/basic/src/routes/redirect/$target/serverFn/index.tsx +++ b/e2e/react-start/basic/src/routes/redirect/$target/serverFn/index.tsx @@ -1,6 +1,6 @@ -import { Link } from '@tanstack/react-router' +import { Link, createFileRoute } from '@tanstack/react-router' -export const Route = createFileRoute({ +export const Route = createFileRoute('/redirect/$target/serverFn/')({ component: () => (

diff --git a/e2e/react-start/basic/src/routes/redirect/$target/serverFn/via-beforeLoad.tsx b/e2e/react-start/basic/src/routes/redirect/$target/serverFn/via-beforeLoad.tsx index 3aad8bffa4c..eed26559f34 100644 --- a/e2e/react-start/basic/src/routes/redirect/$target/serverFn/via-beforeLoad.tsx +++ b/e2e/react-start/basic/src/routes/redirect/$target/serverFn/via-beforeLoad.tsx @@ -1,6 +1,9 @@ +import { createFileRoute } from '@tanstack/react-router' import { throwRedirect } from '~/components/throwRedirect' -export const Route = createFileRoute({ +export const Route = createFileRoute( + '/redirect/$target/serverFn/via-beforeLoad', +)({ beforeLoad: ({ params: { target }, search: { reloadDocument, externalHost }, diff --git a/e2e/react-start/basic/src/routes/redirect/$target/serverFn/via-loader.tsx b/e2e/react-start/basic/src/routes/redirect/$target/serverFn/via-loader.tsx index 3087fa2e152..1db205e3115 100644 --- a/e2e/react-start/basic/src/routes/redirect/$target/serverFn/via-loader.tsx +++ b/e2e/react-start/basic/src/routes/redirect/$target/serverFn/via-loader.tsx @@ -1,6 +1,7 @@ +import { createFileRoute } from '@tanstack/react-router' import { throwRedirect } from '~/components/throwRedirect' -export const Route = createFileRoute({ +export const Route = createFileRoute('/redirect/$target/serverFn/via-loader')({ loaderDeps: ({ search: { reloadDocument, externalHost } }) => ({ reloadDocument, externalHost, diff --git a/e2e/react-start/basic/src/routes/redirect/$target/serverFn/via-useServerFn.tsx b/e2e/react-start/basic/src/routes/redirect/$target/serverFn/via-useServerFn.tsx index 98816a067a9..866bb19b10e 100644 --- a/e2e/react-start/basic/src/routes/redirect/$target/serverFn/via-useServerFn.tsx +++ b/e2e/react-start/basic/src/routes/redirect/$target/serverFn/via-useServerFn.tsx @@ -1,6 +1,9 @@ +import { createFileRoute } from '@tanstack/react-router' import { RedirectOnClick } from '~/components/RedirectOnClick' -export const Route = createFileRoute({ +export const Route = createFileRoute( + '/redirect/$target/serverFn/via-useServerFn', +)({ component: () => { const { target } = Route.useParams() const { reloadDocument, externalHost } = Route.useSearch() diff --git a/e2e/react-start/basic/src/routes/redirect/$target/via-beforeLoad.tsx b/e2e/react-start/basic/src/routes/redirect/$target/via-beforeLoad.tsx index 15176c3a0c3..3b30323869a 100644 --- a/e2e/react-start/basic/src/routes/redirect/$target/via-beforeLoad.tsx +++ b/e2e/react-start/basic/src/routes/redirect/$target/via-beforeLoad.tsx @@ -1,6 +1,6 @@ -import { redirect } from '@tanstack/react-router' +import { createFileRoute, redirect } from '@tanstack/react-router' -export const Route = createFileRoute({ +export const Route = createFileRoute('/redirect/$target/via-beforeLoad')({ beforeLoad: ({ params: { target }, search: { reloadDocument, externalHost }, diff --git a/e2e/react-start/basic/src/routes/redirect/$target/via-loader.tsx b/e2e/react-start/basic/src/routes/redirect/$target/via-loader.tsx index 4ba4b16dda3..c88a3e66f10 100644 --- a/e2e/react-start/basic/src/routes/redirect/$target/via-loader.tsx +++ b/e2e/react-start/basic/src/routes/redirect/$target/via-loader.tsx @@ -1,6 +1,6 @@ -import { redirect } from '@tanstack/react-router' +import { createFileRoute, redirect } from '@tanstack/react-router' -export const Route = createFileRoute({ +export const Route = createFileRoute('/redirect/$target/via-loader')({ loaderDeps: ({ search: { reloadDocument, externalHost } }) => ({ reloadDocument, externalHost, diff --git a/e2e/react-start/basic/src/routes/redirect/index.tsx b/e2e/react-start/basic/src/routes/redirect/index.tsx index a400e552ab5..c0b26a1df4f 100644 --- a/e2e/react-start/basic/src/routes/redirect/index.tsx +++ b/e2e/react-start/basic/src/routes/redirect/index.tsx @@ -1,6 +1,6 @@ -import { Link } from '@tanstack/react-router' +import { Link, createFileRoute } from '@tanstack/react-router' -export const Route = createFileRoute({ +export const Route = createFileRoute('/redirect/')({ component: () => (
({ scripts: [ { diff --git a/e2e/react-start/basic/src/routes/search-params/default.tsx b/e2e/react-start/basic/src/routes/search-params/default.tsx index ebeb195c74a..19e1149b8d2 100644 --- a/e2e/react-start/basic/src/routes/search-params/default.tsx +++ b/e2e/react-start/basic/src/routes/search-params/default.tsx @@ -1,6 +1,7 @@ +import { createFileRoute } from '@tanstack/react-router' import { z } from 'zod' -export const Route = createFileRoute({ +export const Route = createFileRoute('/search-params/default')({ validateSearch: z.object({ default: z.string().default('d1'), }), diff --git a/e2e/react-start/basic/src/routes/search-params/index.tsx b/e2e/react-start/basic/src/routes/search-params/index.tsx index 437b6a4122a..c0d4a55ac85 100644 --- a/e2e/react-start/basic/src/routes/search-params/index.tsx +++ b/e2e/react-start/basic/src/routes/search-params/index.tsx @@ -1,6 +1,6 @@ -import { Link } from '@tanstack/react-router' +import { Link, createFileRoute } from '@tanstack/react-router' -export const Route = createFileRoute({ +export const Route = createFileRoute('/search-params/')({ component: RouteComponent, }) diff --git a/e2e/react-start/basic/src/routes/search-params/loader-throws-redirect.tsx b/e2e/react-start/basic/src/routes/search-params/loader-throws-redirect.tsx index f2baeb4e276..c55628ad331 100644 --- a/e2e/react-start/basic/src/routes/search-params/loader-throws-redirect.tsx +++ b/e2e/react-start/basic/src/routes/search-params/loader-throws-redirect.tsx @@ -1,7 +1,7 @@ -import { redirect } from '@tanstack/react-router' +import { createFileRoute, redirect } from '@tanstack/react-router' import { z } from 'zod' -export const Route = createFileRoute({ +export const Route = createFileRoute('/search-params/loader-throws-redirect')({ validateSearch: z.object({ step: z.enum(['a', 'b', 'c']).optional(), }), diff --git a/e2e/react-start/basic/src/routes/search-params/route.tsx b/e2e/react-start/basic/src/routes/search-params/route.tsx index a9d4a9f8bc2..5adb281c41b 100644 --- a/e2e/react-start/basic/src/routes/search-params/route.tsx +++ b/e2e/react-start/basic/src/routes/search-params/route.tsx @@ -1,4 +1,6 @@ -export const Route = createFileRoute({ +import { createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute('/search-params')({ beforeLoad: async () => { await new Promise((resolve) => setTimeout(resolve, 1000)) return { hello: 'world' as string } diff --git a/e2e/react-start/basic/src/routes/stream.tsx b/e2e/react-start/basic/src/routes/stream.tsx index a113492e1ce..691792ac214 100644 --- a/e2e/react-start/basic/src/routes/stream.tsx +++ b/e2e/react-start/basic/src/routes/stream.tsx @@ -1,7 +1,7 @@ -import { Await } from '@tanstack/react-router' +import { Await, createFileRoute } from '@tanstack/react-router' import { useEffect, useState } from 'react' -export const Route = createFileRoute({ +export const Route = createFileRoute('/stream')({ component: Home, loader() { return { diff --git a/e2e/react-start/basic/src/routes/users.$userId.tsx b/e2e/react-start/basic/src/routes/users.$userId.tsx index 87f81e0f53a..e293be37317 100644 --- a/e2e/react-start/basic/src/routes/users.$userId.tsx +++ b/e2e/react-start/basic/src/routes/users.$userId.tsx @@ -1,11 +1,11 @@ -import { ErrorComponent } from '@tanstack/react-router' +import { ErrorComponent, createFileRoute } from '@tanstack/react-router' import axios from 'redaxios' import type { ErrorComponentProps } from '@tanstack/react-router' import type { User } from '~/utils/users' import { NotFound } from '~/components/NotFound' -export const Route = createFileRoute({ +export const Route = createFileRoute('/users/$userId')({ loader: async ({ params: { userId } }) => { return await axios .get('/api/users/' + userId) diff --git a/e2e/react-start/basic/src/routes/users.index.tsx b/e2e/react-start/basic/src/routes/users.index.tsx index 662e8b6c682..b6b0ee67fbf 100644 --- a/e2e/react-start/basic/src/routes/users.index.tsx +++ b/e2e/react-start/basic/src/routes/users.index.tsx @@ -1,4 +1,6 @@ -export const Route = createFileRoute({ +import { createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute('/users/')({ component: UsersIndexComponent, }) diff --git a/e2e/react-start/basic/src/routes/users.tsx b/e2e/react-start/basic/src/routes/users.tsx index 7a07e4e9fdb..7b08d616527 100644 --- a/e2e/react-start/basic/src/routes/users.tsx +++ b/e2e/react-start/basic/src/routes/users.tsx @@ -1,10 +1,9 @@ -import { Link, Outlet } from '@tanstack/react-router' +import { Link, Outlet, createFileRoute } from '@tanstack/react-router' import axios from 'redaxios' import type { User } from '~/utils/users' -import { DEPLOY_URL } from '~/utils/users' -export const Route = createFileRoute({ +export const Route = createFileRoute('/users')({ loader: async () => { return await axios .get>('/api/users') diff --git "a/e2e/react-start/basic/src/routes/\353\214\200\355\225\234\353\257\274\352\265\255.tsx" "b/e2e/react-start/basic/src/routes/\353\214\200\355\225\234\353\257\274\352\265\255.tsx" index 99448326772..c70cb5096a9 100644 --- "a/e2e/react-start/basic/src/routes/\353\214\200\355\225\234\353\257\274\352\265\255.tsx" +++ "b/e2e/react-start/basic/src/routes/\353\214\200\355\225\234\353\257\274\352\265\255.tsx" @@ -1,4 +1,6 @@ -export const Route = createFileRoute({ +import { createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute('/대한민국')({ component: RouteComponent, }) diff --git a/e2e/react-start/basic/tests/navigation.spec.ts b/e2e/react-start/basic/tests/navigation.spec.ts index c067581c0cc..3eaac7453c0 100644 --- a/e2e/react-start/basic/tests/navigation.spec.ts +++ b/e2e/react-start/basic/tests/navigation.spec.ts @@ -1,6 +1,7 @@ import { expect } from '@playwright/test' import { test } from '@tanstack/router-e2e-utils' +import { isSpaMode } from 'tests/utils/isSpaMode' test.use({ whitelistErrors: [ @@ -44,13 +45,17 @@ test('client side navigating to a route with scripts', async ({ page }) => { await page.getByRole('link', { name: 'Scripts', exact: true }).click() await expect(page.getByTestId('scripts-test-heading')).toBeInViewport() expect(await page.evaluate('window.SCRIPT_1')).toBe(true) - expect(await page.evaluate('window.SCRIPT_2')).toBe(undefined) + expect(await page.evaluate('window.SCRIPT_2')).toBe( + isSpaMode ? true : undefined, + ) }) test('directly going to a route with scripts', async ({ page }) => { await page.goto('/scripts') expect(await page.evaluate('window.SCRIPT_1')).toBe(true) - expect(await page.evaluate('window.SCRIPT_2')).toBe(undefined) + expect(await page.evaluate('window.SCRIPT_2')).toBe( + isSpaMode ? true : undefined, + ) }) test('Navigating to a not-found route', async ({ page }) => { diff --git a/e2e/react-start/basic/tests/redirect.spec.ts b/e2e/react-start/basic/tests/redirect.spec.ts index 78c57d6a00b..9936c8c7ea3 100644 --- a/e2e/react-start/basic/tests/redirect.spec.ts +++ b/e2e/react-start/basic/tests/redirect.spec.ts @@ -6,6 +6,7 @@ import { getTestServerPort, test, } from '@tanstack/router-e2e-utils' +import { isSpaMode } from '../tests/utils/isSpaMode' import packageJson from '../package.json' with { type: 'json' } // somehow playwright does not correctly import default exports @@ -15,144 +16,209 @@ const PORT = await getTestServerPort(packageJson.name) const EXTERNAL_HOST_PORT = await getDummyServerPort(packageJson.name) test.describe('redirects', () => { - const internalNavigationTestMatrix = combinate({ - thrower: ['beforeLoad', 'loader'] as const, - reloadDocument: [false, true] as const, - preload: [false, true] as const, - }) + test.describe('internal', () => { + const internalNavigationTestMatrix = combinate({ + thrower: ['beforeLoad', 'loader'] as const, + reloadDocument: [false, true] as const, + preload: [false, true] as const, + }) - internalNavigationTestMatrix.forEach( - ({ thrower, reloadDocument, preload }) => { - test(`internal target, navigation: thrower: ${thrower}, reloadDocument: ${reloadDocument}, preload: ${preload}`, async ({ - page, - }) => { - await page.goto( - `/redirect/internal${preload === false ? '?preload=false' : ''}`, - ) - const link = page.getByTestId( - `via-${thrower}${reloadDocument ? '-reloadDocument' : ''}`, - ) + internalNavigationTestMatrix.forEach( + ({ thrower, reloadDocument, preload }) => { + test(`internal target, navigation: thrower: ${thrower}, reloadDocument: ${reloadDocument}, preload: ${preload}`, async ({ + page, + }) => { + await page.goto( + `/redirect/internal${preload === false ? '?preload=false' : ''}`, + ) - await page.waitForLoadState('networkidle') - let requestHappened = false - - const requestPromise = new Promise((resolve) => { - page.on('request', (request) => { - if ( - request.url().startsWith(`http://localhost:${PORT}/_serverFn/`) - ) { - requestHappened = true - resolve() - } + const link = page.getByTestId( + `via-${thrower}${reloadDocument ? '-reloadDocument' : ''}`, + ) + + await page.waitForLoadState('networkidle') + let requestHappened = false + + const requestPromise = new Promise((resolve) => { + page.on('request', (request) => { + if ( + request.url().startsWith(`http://localhost:${PORT}/_serverFn/`) + ) { + requestHappened = true + resolve() + } + }) + }) + await link.focus() + + const expectRequestHappened = preload && !reloadDocument + const timeoutPromise = new Promise((resolve) => + setTimeout(resolve, expectRequestHappened ? 5000 : 500), + ) + await Promise.race([requestPromise, timeoutPromise]) + expect(requestHappened).toBe(expectRequestHappened) + let fullPageLoad = false + page.on('domcontentloaded', () => { + fullPageLoad = true }) + + await link.click() + + const url = `http://localhost:${PORT}/posts` + + await page.waitForURL(url) + expect(page.url()).toBe(url) + await expect(page.getByTestId('PostsIndexComponent')).toBeInViewport() + expect(fullPageLoad).toBe(reloadDocument) }) - await link.focus() - - const expectRequestHappened = preload && !reloadDocument - const timeoutPromise = new Promise((resolve) => - setTimeout(resolve, expectRequestHappened ? 5000 : 500), - ) - await Promise.race([requestPromise, timeoutPromise]) - expect(requestHappened).toBe(expectRequestHappened) - let fullPageLoad = false - page.on('domcontentloaded', () => { - fullPageLoad = true + }, + ) + + const internalDirectVisitTestMatrix = combinate({ + thrower: ['beforeLoad', 'loader'] as const, + reloadDocument: [false, true] as const, + }) + + internalDirectVisitTestMatrix.forEach(({ thrower, reloadDocument }) => { + if (isSpaMode) { + test.use({ + whitelistErrors: [ + /A tree hydrated but some attributes of the server rendered HTML/, + ], }) + } - await link.click() + test(`internal target, direct visit: thrower: ${thrower}, reloadDocument: ${reloadDocument}`, async ({ + page, + }) => { + await page.goto(`/redirect/internal/via-${thrower}`) + await page.waitForLoadState('networkidle') const url = `http://localhost:${PORT}/posts` - await page.waitForURL(url) expect(page.url()).toBe(url) await expect(page.getByTestId('PostsIndexComponent')).toBeInViewport() - expect(fullPageLoad).toBe(reloadDocument) }) - }, - ) - - const internalDirectVisitTestMatrix = combinate({ - thrower: ['beforeLoad', 'loader'] as const, - reloadDocument: [false, true] as const, + }) }) - internalDirectVisitTestMatrix.forEach(({ thrower, reloadDocument }) => { - test(`internal target, direct visit: thrower: ${thrower}, reloadDocument: ${reloadDocument}`, async ({ - page, - }) => { - await page.goto(`/redirect/internal/via-${thrower}`) + test.describe('external', () => { + const externalTestMatrix = combinate({ + scenario: ['navigate', 'direct_visit'] as const, + thrower: ['beforeLoad', 'loader'] as const, + }) + + externalTestMatrix.forEach(({ scenario, thrower }) => { + test(`external target: scenario: ${scenario}, thrower: ${thrower}`, async ({ + page, + }) => { + const q = queryString.stringify({ + externalHost: `http://localhost:${EXTERNAL_HOST_PORT}/`, + }) + + if (scenario === 'navigate') { + await page.goto(`/redirect/external?${q}`) + await page.waitForLoadState('networkidle') + const link = page.getByTestId(`via-${thrower}`) + await link.focus() + await link.click() + } else { + await page.goto(`/redirect/external/via-${thrower}?${q}`) + } - const url = `http://localhost:${PORT}/posts` + const url = `http://localhost:${EXTERNAL_HOST_PORT}/` - await page.waitForURL(url) - expect(page.url()).toBe(url) - await page.waitForLoadState('networkidle') - await expect(page.getByTestId('PostsIndexComponent')).toBeInViewport() + await page.waitForURL(url) + expect(page.url()).toBe(url) + }) }) }) - const externalTestMatrix = combinate({ - scenario: ['navigate', 'direct_visit'] as const, - thrower: ['beforeLoad', 'loader'] as const, - }) + test.describe('serverFn', () => { + const serverFnTestMatrix = combinate({ + target: ['internal', 'external'] as const, + scenario: ['navigate', 'direct_visit'] as const, + thrower: ['beforeLoad', 'loader'] as const, + reloadDocument: [false, true] as const, + }) - externalTestMatrix.forEach(({ scenario, thrower }) => { - test(`external target: scenario: ${scenario}, thrower: ${thrower}`, async ({ - page, - }) => { - const q = queryString.stringify({ - externalHost: `http://localhost:${EXTERNAL_HOST_PORT}/`, - }) + serverFnTestMatrix.forEach( + ({ target, thrower, scenario, reloadDocument }) => { + test(`serverFn redirects to target: ${target}, scenario: ${scenario}, thrower: ${thrower}, reloadDocument: ${reloadDocument}`, async ({ + page, + }) => { + let fullPageLoad = false + const q = queryString.stringify({ + externalHost: `http://localhost:${EXTERNAL_HOST_PORT}/`, + reloadDocument, + }) - if (scenario === 'navigate') { - await page.goto(`/redirect/external?${q}`) - await page.waitForLoadState('networkidle') - const link = page.getByTestId(`via-${thrower}`) - await link.focus() - await link.click() - } else { - await page.goto(`/redirect/external/via-${thrower}?${q}`) - } + if (scenario === 'navigate') { + await page.goto(`/redirect/${target}/serverFn?${q}`) + await page.waitForLoadState('networkidle') - const url = `http://localhost:${EXTERNAL_HOST_PORT}/` + const link = page.getByTestId( + `via-${thrower}${reloadDocument ? '-reloadDocument' : ''}`, + ) - await page.waitForURL(url) - expect(page.url()).toBe(url) - }) - }) + page.on('domcontentloaded', () => { + fullPageLoad = true + }) + + await link.focus() + await page.waitForLoadState('networkidle') + await link.click() + } else { + await page.goto(`/redirect/${target}/serverFn/via-${thrower}?${q}`) + } + + const url = + target === 'internal' + ? `http://localhost:${PORT}/posts` + : `http://localhost:${EXTERNAL_HOST_PORT}/` + + await page.waitForURL(url) + + expect(page.url()).toBe(url) - const serverFnTestMatrix = combinate({ - target: ['internal', 'external'] as const, - scenario: ['navigate', 'direct_visit'] as const, - thrower: ['beforeLoad', 'loader'] as const, - reloadDocument: [false, true] as const, + if (target === 'internal' && scenario === 'navigate') { + await expect( + page.getByTestId('PostsIndexComponent'), + ).toBeInViewport() + expect(fullPageLoad).toBe(reloadDocument) + } + }) + }, + ) }) - serverFnTestMatrix.forEach( - ({ target, thrower, scenario, reloadDocument }) => { - test(`serverFn redirects to target: ${target}, scenario: ${scenario}, thrower: ${thrower}, reloadDocument: ${reloadDocument}`, async ({ + test.describe('useServerFn', () => { + const useServerFnTestMatrix = combinate({ + target: ['internal', 'external'] as const, + reloadDocument: [false, true] as const, + }) + + useServerFnTestMatrix.forEach(({ target, reloadDocument }) => { + test(`useServerFn redirects to target: ${target}, reloadDocument: ${reloadDocument}`, async ({ page, }) => { - let fullPageLoad = false const q = queryString.stringify({ externalHost: `http://localhost:${EXTERNAL_HOST_PORT}/`, reloadDocument, }) - if (scenario === 'navigate') { - await page.goto(`/redirect/${target}/serverFn?${q}`) - await page.waitForLoadState('networkidle') - const link = page.getByTestId( - `via-${thrower}${reloadDocument ? '-reloadDocument' : ''}`, - ) - page.on('domcontentloaded', () => { - fullPageLoad = true - }) - await link.focus() - await link.click() - } else { - await page.goto(`/redirect/${target}/serverFn/via-${thrower}?${q}`) - } + await page.goto(`/redirect/${target}/serverFn/via-useServerFn?${q}`) + + await page.waitForLoadState('networkidle') + + const button = page.getByTestId('redirect-on-click') + + let fullPageLoad = false + page.on('domcontentloaded', () => { + fullPageLoad = true + }) + + await button.click() const url = target === 'internal' @@ -160,51 +226,11 @@ test.describe('redirects', () => { : `http://localhost:${EXTERNAL_HOST_PORT}/` await page.waitForURL(url) expect(page.url()).toBe(url) - if (target === 'internal' && scenario === 'navigate') { + if (target === 'internal') { await expect(page.getByTestId('PostsIndexComponent')).toBeInViewport() expect(fullPageLoad).toBe(reloadDocument) } }) - }, - ) - - const useServerFnTestMatrix = combinate({ - target: ['internal', 'external'] as const, - reloadDocument: [false, true] as const, - }) - - useServerFnTestMatrix.forEach(({ target, reloadDocument }) => { - test(`useServerFn redirects to target: ${target}, reloadDocument: ${reloadDocument}`, async ({ - page, - }) => { - const q = queryString.stringify({ - externalHost: `http://localhost:${EXTERNAL_HOST_PORT}/`, - reloadDocument, - }) - - await page.goto(`/redirect/${target}/serverFn/via-useServerFn?${q}`) - - await page.waitForLoadState('networkidle') - - const button = page.getByTestId('redirect-on-click') - - let fullPageLoad = false - page.on('domcontentloaded', () => { - fullPageLoad = true - }) - - await button.click() - - const url = - target === 'internal' - ? `http://localhost:${PORT}/posts` - : `http://localhost:${EXTERNAL_HOST_PORT}/` - await page.waitForURL(url) - expect(page.url()).toBe(url) - if (target === 'internal') { - await expect(page.getByTestId('PostsIndexComponent')).toBeInViewport() - expect(fullPageLoad).toBe(reloadDocument) - } }) }) }) diff --git a/e2e/react-start/basic/tests/search-params.spec.ts b/e2e/react-start/basic/tests/search-params.spec.ts index d7ce6178900..e0762774ace 100644 --- a/e2e/react-start/basic/tests/search-params.spec.ts +++ b/e2e/react-start/basic/tests/search-params.spec.ts @@ -1,5 +1,6 @@ import { expect } from '@playwright/test' import { test } from '@tanstack/router-e2e-utils' +import { isSpaMode } from 'tests/utils/isSpaMode' import type { Response } from '@playwright/test' function expectRedirect(response: Response | null, endsWith: string) { @@ -25,7 +26,11 @@ test.describe('/search-params/loader-throws-redirect', () => { page, }) => { const response = await page.goto('/search-params/loader-throws-redirect') - expectRedirect(response, '/search-params/loader-throws-redirect?step=a') + + if (!isSpaMode) { + expectRedirect(response, '/search-params/loader-throws-redirect?step=a') + } + await expect(page.getByTestId('search-param')).toContainText('a') expect(page.url().endsWith('/search-params/loader-throws-redirect?step=a')) }) diff --git a/e2e/react-start/basic/tests/utils/isSpaMode.ts b/e2e/react-start/basic/tests/utils/isSpaMode.ts new file mode 100644 index 00000000000..b4edb829a8f --- /dev/null +++ b/e2e/react-start/basic/tests/utils/isSpaMode.ts @@ -0,0 +1 @@ +export const isSpaMode: boolean = process.env.MODE === 'spa' diff --git a/e2e/react-start/basic/vite.config.ts b/e2e/react-start/basic/vite.config.ts index 0a38b8adb01..81ea0c795b3 100644 --- a/e2e/react-start/basic/vite.config.ts +++ b/e2e/react-start/basic/vite.config.ts @@ -2,6 +2,16 @@ import { defineConfig } from 'vite' import tsConfigPaths from 'vite-tsconfig-paths' import { tanstackStart } from '@tanstack/react-start/plugin/vite' import viteReact from '@vitejs/plugin-react' +import { isSpaMode } from './tests/utils/isSpaMode' + +const spaMode = isSpaMode + +const spaModeConfiguration = { + enabled: true, + prerender: { + outputPath: 'index.html', + }, +} export default defineConfig({ server: { @@ -12,7 +22,9 @@ export default defineConfig({ projects: ['./tsconfig.json'], }), // @ts-ignore we want to keep one test with verboseFileRoutes off even though the option is hidden - tanstackStart({ router: { verboseFileRoutes: false } }), + tanstackStart({ + spa: spaMode ? spaModeConfiguration : undefined, + }), viteReact(), ], }) diff --git a/e2e/solid-start/basic/package.json b/e2e/solid-start/basic/package.json index c1941b3aaa7..efff8adf93f 100644 --- a/e2e/solid-start/basic/package.json +++ b/e2e/solid-start/basic/package.json @@ -8,7 +8,9 @@ "dev:e2e": "vite dev", "build": "vite build && tsc --noEmit", "start": "pnpx srvx --prod -s ../client dist/server/server.js", - "test:e2e": "rm -rf port*.txt; playwright test --project=chromium" + "test:e2e:spaMode": "rm -rf port*.txt; MODE=spa playwright test --project=chromium", + "test:e2e:ssrMode": "rm -rf port*.txt; playwright test --project=chromium", + "test:e2e": "pnpm run test:e2e:spaMode && pnpm run test:e2e:ssrMode" }, "dependencies": { "@tanstack/solid-router": "workspace:^", diff --git a/e2e/solid-start/basic/playwright.config.ts b/e2e/solid-start/basic/playwright.config.ts index b0c365f8bd1..8775f859a70 100644 --- a/e2e/solid-start/basic/playwright.config.ts +++ b/e2e/solid-start/basic/playwright.config.ts @@ -4,10 +4,13 @@ import { getTestServerPort, } from '@tanstack/router-e2e-utils' import packageJson from './package.json' with { type: 'json' } +import { isSpaMode } from './tests/utils/isSpaMode' const PORT = await getTestServerPort(packageJson.name) const EXTERNAL_PORT = await getDummyServerPort(packageJson.name) const baseURL = `http://localhost:${PORT}` +const spaModeCommand = `pnpm build && pnpm dev:e2e --port=${PORT}` +const ssrModeCommand = `pnpm build && pnpm start` /** * See https://playwright.dev/docs/test-configuration. @@ -27,10 +30,17 @@ export default defineConfig({ }, webServer: { - command: `VITE_NODE_ENV="test" VITE_EXTERNAL_PORT=${EXTERNAL_PORT} pnpm build && VITE_NODE_ENV="test" VITE_EXTERNAL_PORT=${EXTERNAL_PORT} VITE_SERVER_PORT=${PORT} PORT=${PORT} pnpm start`, + command: isSpaMode ? spaModeCommand : ssrModeCommand, url: baseURL, reuseExistingServer: !process.env.CI, stdout: 'pipe', + env: { + MODE: process.env.MODE || '', + VITE_NODE_ENV: 'test', + VITE_EXTERNAL_PORT: String(EXTERNAL_PORT), + VITE_SERVER_PORT: String(PORT), + PORT: String(PORT), + }, }, projects: [ diff --git a/e2e/solid-start/basic/tests/navigation.spec.ts b/e2e/solid-start/basic/tests/navigation.spec.ts index 6f90383afbc..86229eac006 100644 --- a/e2e/solid-start/basic/tests/navigation.spec.ts +++ b/e2e/solid-start/basic/tests/navigation.spec.ts @@ -1,5 +1,6 @@ import { expect } from '@playwright/test' import { test } from '@tanstack/router-e2e-utils' +import { isSpaMode } from './utils/isSpaMode' test.use({ whitelistErrors: [ @@ -43,13 +44,17 @@ test('client side navigating to a route with scripts', async ({ page }) => { await page.getByRole('link', { name: 'Scripts', exact: true }).click() await expect(page.getByTestId('scripts-test-heading')).toBeInViewport() expect(await page.evaluate('window.SCRIPT_1')).toBe(true) - expect(await page.evaluate('window.SCRIPT_2')).toBe(undefined) + expect(await page.evaluate('window.SCRIPT_2')).toBe( + isSpaMode ? true : undefined, + ) }) test('directly going to a route with scripts', async ({ page }) => { await page.goto('/scripts') expect(await page.evaluate('window.SCRIPT_1')).toBe(true) - expect(await page.evaluate('window.SCRIPT_2')).toBe(undefined) + expect(await page.evaluate('window.SCRIPT_2')).toBe( + isSpaMode ? true : undefined, + ) }) test('Navigating to a not-found route', async ({ page }) => { diff --git a/e2e/solid-start/basic/tests/utils/isSpaMode.ts b/e2e/solid-start/basic/tests/utils/isSpaMode.ts new file mode 100644 index 00000000000..b4edb829a8f --- /dev/null +++ b/e2e/solid-start/basic/tests/utils/isSpaMode.ts @@ -0,0 +1 @@ +export const isSpaMode: boolean = process.env.MODE === 'spa' diff --git a/packages/router-core/src/path.ts b/packages/router-core/src/path.ts index 95f9f34fa2f..6c2e1c8e58b 100644 --- a/packages/router-core/src/path.ts +++ b/packages/router-core/src/path.ts @@ -1,3 +1,4 @@ +import { rootRouteId } from './root' import { last } from './utils' import type { LRUCache } from './lru-cache' import type { MatchLocation } from './RouterProvider' @@ -276,7 +277,9 @@ function baseParsePathname( ...split.map((part): Segment => { // strip tailing underscore for non-nested paths const partToMatch = - !basePathValues && part.slice(-1) === '_' ? part.slice(0, -1) : part + !basePathValues && part !== rootRouteId && part.slice(-1) === '_' + ? part.slice(0, -1) + : part // Check for wildcard with curly braces: prefix{$}suffix const wildcardBracesMatch = partToMatch.match(WILDCARD_W_CURLY_BRACES_RE) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e9e0b9a23f5..f133ab5198b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1210,6 +1210,76 @@ importers: specifier: ^5.1.4 version: 5.1.4(typescript@5.8.2)(vite@7.1.7(@types/node@22.10.2)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.37.0)(tsx@4.20.3)(yaml@2.7.0)) + e2e/react-start/basic-spa: + dependencies: + '@tanstack/react-router': + specifier: workspace:* + version: link:../../../packages/react-router + '@tanstack/react-router-devtools': + specifier: workspace:^ + version: link:../../../packages/react-router-devtools + '@tanstack/react-start': + specifier: workspace:* + version: link:../../../packages/react-start + react: + specifier: ^19.0.0 + version: 19.0.0 + react-dom: + specifier: ^19.0.0 + version: 19.0.0(react@19.0.0) + redaxios: + specifier: ^0.5.1 + version: 0.5.1 + tailwind-merge: + specifier: ^2.6.0 + version: 2.6.0 + zod: + specifier: ^3.24.2 + version: 3.25.57 + devDependencies: + '@playwright/test': + specifier: ^1.52.0 + version: 1.52.0 + '@tanstack/router-e2e-utils': + specifier: workspace:* + version: link:../../e2e-utils + '@types/node': + specifier: 22.10.2 + version: 22.10.2 + '@types/react': + specifier: ^19.0.8 + version: 19.0.8 + '@types/react-dom': + specifier: ^19.0.3 + version: 19.0.3(@types/react@19.0.8) + '@vitejs/plugin-react': + specifier: ^4.3.4 + version: 4.6.0(vite@7.1.7(@types/node@22.10.2)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.37.0)(tsx@4.20.3)(yaml@2.7.0)) + autoprefixer: + specifier: ^10.4.20 + version: 10.4.20(postcss@8.5.6) + combinate: + specifier: ^1.1.11 + version: 1.1.11 + postcss: + specifier: ^8.5.1 + version: 8.5.6 + srvx: + specifier: ^0.8.6 + version: 0.8.7 + tailwindcss: + specifier: ^3.4.17 + version: 3.4.17 + typescript: + specifier: ^5.7.2 + version: 5.9.2 + vite: + specifier: ^7.1.7 + version: 7.1.7(@types/node@22.10.2)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.37.0)(tsx@4.20.3)(yaml@2.7.0) + vite-tsconfig-paths: + specifier: ^5.1.4 + version: 5.1.4(typescript@5.9.2)(vite@7.1.7(@types/node@22.10.2)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.37.0)(tsx@4.20.3)(yaml@2.7.0)) + e2e/react-start/basic-tsr-config: dependencies: '@tanstack/react-router':