diff --git a/e2e/vue-start/custom-basepath/.gitignore b/e2e/vue-start/custom-basepath/.gitignore new file mode 100644 index 00000000000..a79d5cf1299 --- /dev/null +++ b/e2e/vue-start/custom-basepath/.gitignore @@ -0,0 +1,20 @@ +node_modules +package-lock.json +yarn.lock + +.DS_Store +.cache +.env +.vercel +.output + +/build/ +/api/ +/server/build +/public/build +# Sentry Config File +.env.sentry-build-plugin +/test-results/ +/playwright-report/ +/blob-report/ +/playwright/.cache/ diff --git a/e2e/vue-start/custom-basepath/.prettierignore b/e2e/vue-start/custom-basepath/.prettierignore new file mode 100644 index 00000000000..2be5eaa6ece --- /dev/null +++ b/e2e/vue-start/custom-basepath/.prettierignore @@ -0,0 +1,4 @@ +**/build +**/public +pnpm-lock.yaml +routeTree.gen.ts \ No newline at end of file diff --git a/e2e/vue-start/custom-basepath/express-server.ts b/e2e/vue-start/custom-basepath/express-server.ts new file mode 100644 index 00000000000..6fa802e4476 --- /dev/null +++ b/e2e/vue-start/custom-basepath/express-server.ts @@ -0,0 +1,44 @@ +import express from 'express' +import { toNodeHandler } from 'srvx/node' + +const DEVELOPMENT = process.env.NODE_ENV === 'development' +const PORT = Number.parseInt(process.env.PORT || '3000') + +const app = express() + +if (DEVELOPMENT) { + const viteDevServer = await import('vite').then((vite) => + vite.createServer({ + server: { middlewareMode: true }, + }), + ) + app.use(viteDevServer.middlewares) + app.use(async (req, res, next) => { + try { + const { default: serverEntry } = + await viteDevServer.ssrLoadModule('./src/server.ts') + const handler = toNodeHandler(serverEntry.fetch) + await handler(req, res) + } catch (error) { + if (typeof error === 'object' && error instanceof Error) { + viteDevServer.ssrFixStacktrace(error) + } + next(error) + } + }) +} else { + const { default: handler } = await import('./dist/server/server.js') + const nodeHandler = toNodeHandler(handler.fetch) + app.use('/custom/basepath', express.static('dist/client')) + app.use(async (req, res, next) => { + try { + await nodeHandler(req, res) + } catch (error) { + next(error) + } + }) +} + +app.listen(PORT, () => { + console.log(`Server is running on http://localhost:${PORT}`) +}) diff --git a/e2e/vue-start/custom-basepath/package.json b/e2e/vue-start/custom-basepath/package.json new file mode 100644 index 00000000000..b897f78d832 --- /dev/null +++ b/e2e/vue-start/custom-basepath/package.json @@ -0,0 +1,38 @@ +{ + "name": "tanstack-vue-start-e2e-custom-basepath", + "private": true, + "sideEffects": false, + "type": "module", + "scripts": { + "dev": "cross-env NODE_ENV=development tsx express-server.ts", + "build": "vite build && tsc --noEmit", + "preview": "vite preview", + "start": "tsx express-server.ts", + "test:e2e": "rm -rf port*.txt; playwright test --project=chromium" + }, + "dependencies": { + "@tanstack/vue-router": "workspace:^", + "@tanstack/vue-router-devtools": "workspace:^", + "@tanstack/vue-start": "workspace:^", + "express": "^4.21.2", + "redaxios": "^0.5.1", + "vue": "^3.5.25" + }, + "devDependencies": { + "@playwright/test": "^1.50.1", + "@tailwindcss/postcss": "^4.1.15", + "@tanstack/router-e2e-utils": "workspace:^", + "@types/express": "^5.0.3", + "@types/node": "^22.10.2", + "cross-env": "^10.0.0", + "postcss": "^8.5.1", + "srvx": "^0.9.8", + "tailwindcss": "^4.1.17", + "tsx": "^4.20.3", + "typescript": "^5.7.2", + "vite": "^7.1.7", + "@vitejs/plugin-vue": "^6.0.3", + "@vitejs/plugin-vue-jsx": "^5.1.2", + "vite-tsconfig-paths": "^5.1.4" + } +} diff --git a/e2e/vue-start/custom-basepath/playwright.config.ts b/e2e/vue-start/custom-basepath/playwright.config.ts new file mode 100644 index 00000000000..5095425f473 --- /dev/null +++ b/e2e/vue-start/custom-basepath/playwright.config.ts @@ -0,0 +1,42 @@ +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}/custom/basepath` + +/** + * 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: `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`, + url: baseURL, + reuseExistingServer: !process.env.CI, + stdout: 'pipe', + }, + + projects: [ + { + name: 'chromium', + use: { ...devices['Desktop Chrome'] }, + }, + ], +}) diff --git a/e2e/vue-start/custom-basepath/postcss.config.mjs b/e2e/vue-start/custom-basepath/postcss.config.mjs new file mode 100644 index 00000000000..a7f73a2d1d7 --- /dev/null +++ b/e2e/vue-start/custom-basepath/postcss.config.mjs @@ -0,0 +1,5 @@ +export default { + plugins: { + '@tailwindcss/postcss': {}, + }, +} diff --git a/e2e/vue-start/custom-basepath/public/android-chrome-192x192.png b/e2e/vue-start/custom-basepath/public/android-chrome-192x192.png new file mode 100644 index 00000000000..09c8324f8c6 Binary files /dev/null and b/e2e/vue-start/custom-basepath/public/android-chrome-192x192.png differ diff --git a/e2e/vue-start/custom-basepath/public/android-chrome-512x512.png b/e2e/vue-start/custom-basepath/public/android-chrome-512x512.png new file mode 100644 index 00000000000..11d626ea3d0 Binary files /dev/null and b/e2e/vue-start/custom-basepath/public/android-chrome-512x512.png differ diff --git a/e2e/vue-start/custom-basepath/public/apple-touch-icon.png b/e2e/vue-start/custom-basepath/public/apple-touch-icon.png new file mode 100644 index 00000000000..5a9423cc02c Binary files /dev/null and b/e2e/vue-start/custom-basepath/public/apple-touch-icon.png differ diff --git a/e2e/vue-start/custom-basepath/public/favicon-16x16.png b/e2e/vue-start/custom-basepath/public/favicon-16x16.png new file mode 100644 index 00000000000..e3389b00443 Binary files /dev/null and b/e2e/vue-start/custom-basepath/public/favicon-16x16.png differ diff --git a/e2e/vue-start/custom-basepath/public/favicon-32x32.png b/e2e/vue-start/custom-basepath/public/favicon-32x32.png new file mode 100644 index 00000000000..900c77d444c Binary files /dev/null and b/e2e/vue-start/custom-basepath/public/favicon-32x32.png differ diff --git a/e2e/vue-start/custom-basepath/public/favicon.ico b/e2e/vue-start/custom-basepath/public/favicon.ico new file mode 100644 index 00000000000..1a1751676f7 Binary files /dev/null and b/e2e/vue-start/custom-basepath/public/favicon.ico differ diff --git a/e2e/vue-start/custom-basepath/public/favicon.png b/e2e/vue-start/custom-basepath/public/favicon.png new file mode 100644 index 00000000000..1e77bc06091 Binary files /dev/null and b/e2e/vue-start/custom-basepath/public/favicon.png differ diff --git a/e2e/vue-start/custom-basepath/public/script.js b/e2e/vue-start/custom-basepath/public/script.js new file mode 100644 index 00000000000..897477e7d0a --- /dev/null +++ b/e2e/vue-start/custom-basepath/public/script.js @@ -0,0 +1,2 @@ +console.log('SCRIPT_1 loaded') +window.SCRIPT_1 = true diff --git a/e2e/vue-start/custom-basepath/public/script2.js b/e2e/vue-start/custom-basepath/public/script2.js new file mode 100644 index 00000000000..819af30daf9 --- /dev/null +++ b/e2e/vue-start/custom-basepath/public/script2.js @@ -0,0 +1,2 @@ +console.log('SCRIPT_2 loaded') +window.SCRIPT_2 = true diff --git a/e2e/vue-start/custom-basepath/public/site.webmanifest b/e2e/vue-start/custom-basepath/public/site.webmanifest new file mode 100644 index 00000000000..fa99de77db6 --- /dev/null +++ b/e2e/vue-start/custom-basepath/public/site.webmanifest @@ -0,0 +1,19 @@ +{ + "name": "", + "short_name": "", + "icons": [ + { + "src": "/android-chrome-192x192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "/android-chrome-512x512.png", + "sizes": "512x512", + "type": "image/png" + } + ], + "theme_color": "#ffffff", + "background_color": "#ffffff", + "display": "standalone" +} diff --git a/e2e/vue-start/custom-basepath/src/components/CustomMessage.tsx b/e2e/vue-start/custom-basepath/src/components/CustomMessage.tsx new file mode 100644 index 00000000000..c038b66d2fe --- /dev/null +++ b/e2e/vue-start/custom-basepath/src/components/CustomMessage.tsx @@ -0,0 +1,8 @@ +export function CustomMessage({ message }: { message: string }) { + return ( +
+
This is a custom message:
+

{message}

+
+ ) +} diff --git a/e2e/vue-start/custom-basepath/src/components/DefaultCatchBoundary.tsx b/e2e/vue-start/custom-basepath/src/components/DefaultCatchBoundary.tsx new file mode 100644 index 00000000000..b1f818dd747 --- /dev/null +++ b/e2e/vue-start/custom-basepath/src/components/DefaultCatchBoundary.tsx @@ -0,0 +1,53 @@ +import { + ErrorComponent, + Link, + rootRouteId, + useMatch, + useRouter, +} from '@tanstack/vue-router' +import type { ErrorComponentProps } from '@tanstack/vue-router' + +export function DefaultCatchBoundary({ error }: ErrorComponentProps) { + const router = useRouter() + const isRoot = useMatch({ + strict: false, + select: (state) => state.id === rootRouteId, + }) + + console.error(error) + + return ( +
+ +
+ + {isRoot.value ? ( + + Home + + ) : ( + { + e.preventDefault() + window.history.back() + }} + > + Go Back + + )} +
+
+ ) +} diff --git a/e2e/vue-start/custom-basepath/src/components/NotFound.tsx b/e2e/vue-start/custom-basepath/src/components/NotFound.tsx new file mode 100644 index 00000000000..944e35c12c6 --- /dev/null +++ b/e2e/vue-start/custom-basepath/src/components/NotFound.tsx @@ -0,0 +1,25 @@ +import { Link } from '@tanstack/vue-router' + +export function NotFound({ children }: { children?: any }) { + return ( +
+
+ {children ||

The page you are looking for does not exist.

} +
+

+ + + Start Over + +

+
+ ) +} diff --git a/e2e/vue-start/custom-basepath/src/components/PostErrorComponent.tsx b/e2e/vue-start/custom-basepath/src/components/PostErrorComponent.tsx new file mode 100644 index 00000000000..6aef8ebdc01 --- /dev/null +++ b/e2e/vue-start/custom-basepath/src/components/PostErrorComponent.tsx @@ -0,0 +1,5 @@ +import { ErrorComponent, ErrorComponentProps } from '@tanstack/vue-router' + +export function PostErrorComponent({ error }: ErrorComponentProps) { + return +} diff --git a/e2e/vue-start/custom-basepath/src/components/UserErrorComponent.tsx b/e2e/vue-start/custom-basepath/src/components/UserErrorComponent.tsx new file mode 100644 index 00000000000..34151b8e52d --- /dev/null +++ b/e2e/vue-start/custom-basepath/src/components/UserErrorComponent.tsx @@ -0,0 +1,6 @@ +import { ErrorComponent } from '@tanstack/vue-router' +import type { ErrorComponentProps } from '@tanstack/vue-router' + +export function UserErrorComponent({ error }: ErrorComponentProps) { + return +} diff --git a/e2e/vue-start/custom-basepath/src/routeTree.gen.ts b/e2e/vue-start/custom-basepath/src/routeTree.gen.ts new file mode 100644 index 00000000000..2aa189fd3c1 --- /dev/null +++ b/e2e/vue-start/custom-basepath/src/routeTree.gen.ts @@ -0,0 +1,367 @@ +/* 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 UsersRouteImport } from './routes/users' +import { Route as PostsRouteImport } from './routes/posts' +import { Route as LogoutRouteImport } from './routes/logout' +import { Route as DeferredRouteImport } from './routes/deferred' +import { Route as IndexRouteImport } from './routes/index' +import { Route as UsersIndexRouteImport } from './routes/users.index' +import { Route as RedirectIndexRouteImport } from './routes/redirect.index' +import { Route as PostsIndexRouteImport } from './routes/posts.index' +import { Route as UsersUserIdRouteImport } from './routes/users.$userId' +import { Route as RedirectThrowItRouteImport } from './routes/redirect.throw-it' +import { Route as PostsPostIdRouteImport } from './routes/posts.$postId' +import { Route as ApiUsersRouteImport } from './routes/api/users' +import { Route as PostsPostIdDeepRouteImport } from './routes/posts_.$postId.deep' +import { Route as ApiUsersUserIdRouteImport } from './routes/api/users.$userId' + +const UsersRoute = UsersRouteImport.update({ + id: '/users', + path: '/users', + getParentRoute: () => rootRouteImport, +} as any) +const PostsRoute = PostsRouteImport.update({ + id: '/posts', + path: '/posts', + getParentRoute: () => rootRouteImport, +} as any) +const LogoutRoute = LogoutRouteImport.update({ + id: '/logout', + path: '/logout', + getParentRoute: () => rootRouteImport, +} as any) +const DeferredRoute = DeferredRouteImport.update({ + id: '/deferred', + path: '/deferred', + getParentRoute: () => rootRouteImport, +} as any) +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => rootRouteImport, +} as any) +const UsersIndexRoute = UsersIndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => UsersRoute, +} as any) +const RedirectIndexRoute = RedirectIndexRouteImport.update({ + id: '/redirect/', + path: '/redirect/', + getParentRoute: () => rootRouteImport, +} as any) +const PostsIndexRoute = PostsIndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => PostsRoute, +} as any) +const UsersUserIdRoute = UsersUserIdRouteImport.update({ + id: '/$userId', + path: '/$userId', + getParentRoute: () => UsersRoute, +} as any) +const RedirectThrowItRoute = RedirectThrowItRouteImport.update({ + id: '/redirect/throw-it', + path: '/redirect/throw-it', + getParentRoute: () => rootRouteImport, +} as any) +const PostsPostIdRoute = PostsPostIdRouteImport.update({ + id: '/$postId', + path: '/$postId', + getParentRoute: () => PostsRoute, +} as any) +const ApiUsersRoute = ApiUsersRouteImport.update({ + id: '/api/users', + path: '/api/users', + getParentRoute: () => rootRouteImport, +} as any) +const PostsPostIdDeepRoute = PostsPostIdDeepRouteImport.update({ + id: '/posts_/$postId/deep', + path: '/posts/$postId/deep', + getParentRoute: () => rootRouteImport, +} as any) +const ApiUsersUserIdRoute = ApiUsersUserIdRouteImport.update({ + id: '/$userId', + path: '/$userId', + getParentRoute: () => ApiUsersRoute, +} as any) + +export interface FileRoutesByFullPath { + '/': typeof IndexRoute + '/deferred': typeof DeferredRoute + '/logout': typeof LogoutRoute + '/posts': typeof PostsRouteWithChildren + '/users': typeof UsersRouteWithChildren + '/api/users': typeof ApiUsersRouteWithChildren + '/posts/$postId': typeof PostsPostIdRoute + '/redirect/throw-it': typeof RedirectThrowItRoute + '/users/$userId': typeof UsersUserIdRoute + '/posts/': typeof PostsIndexRoute + '/redirect': typeof RedirectIndexRoute + '/users/': typeof UsersIndexRoute + '/api/users/$userId': typeof ApiUsersUserIdRoute + '/posts/$postId/deep': typeof PostsPostIdDeepRoute +} +export interface FileRoutesByTo { + '/': typeof IndexRoute + '/deferred': typeof DeferredRoute + '/logout': typeof LogoutRoute + '/api/users': typeof ApiUsersRouteWithChildren + '/posts/$postId': typeof PostsPostIdRoute + '/redirect/throw-it': typeof RedirectThrowItRoute + '/users/$userId': typeof UsersUserIdRoute + '/posts': typeof PostsIndexRoute + '/redirect': typeof RedirectIndexRoute + '/users': typeof UsersIndexRoute + '/api/users/$userId': typeof ApiUsersUserIdRoute + '/posts/$postId/deep': typeof PostsPostIdDeepRoute +} +export interface FileRoutesById { + __root__: typeof rootRouteImport + '/': typeof IndexRoute + '/deferred': typeof DeferredRoute + '/logout': typeof LogoutRoute + '/posts': typeof PostsRouteWithChildren + '/users': typeof UsersRouteWithChildren + '/api/users': typeof ApiUsersRouteWithChildren + '/posts/$postId': typeof PostsPostIdRoute + '/redirect/throw-it': typeof RedirectThrowItRoute + '/users/$userId': typeof UsersUserIdRoute + '/posts/': typeof PostsIndexRoute + '/redirect/': typeof RedirectIndexRoute + '/users/': typeof UsersIndexRoute + '/api/users/$userId': typeof ApiUsersUserIdRoute + '/posts_/$postId/deep': typeof PostsPostIdDeepRoute +} +export interface FileRouteTypes { + fileRoutesByFullPath: FileRoutesByFullPath + fullPaths: + | '/' + | '/deferred' + | '/logout' + | '/posts' + | '/users' + | '/api/users' + | '/posts/$postId' + | '/redirect/throw-it' + | '/users/$userId' + | '/posts/' + | '/redirect' + | '/users/' + | '/api/users/$userId' + | '/posts/$postId/deep' + fileRoutesByTo: FileRoutesByTo + to: + | '/' + | '/deferred' + | '/logout' + | '/api/users' + | '/posts/$postId' + | '/redirect/throw-it' + | '/users/$userId' + | '/posts' + | '/redirect' + | '/users' + | '/api/users/$userId' + | '/posts/$postId/deep' + id: + | '__root__' + | '/' + | '/deferred' + | '/logout' + | '/posts' + | '/users' + | '/api/users' + | '/posts/$postId' + | '/redirect/throw-it' + | '/users/$userId' + | '/posts/' + | '/redirect/' + | '/users/' + | '/api/users/$userId' + | '/posts_/$postId/deep' + fileRoutesById: FileRoutesById +} +export interface RootRouteChildren { + IndexRoute: typeof IndexRoute + DeferredRoute: typeof DeferredRoute + LogoutRoute: typeof LogoutRoute + PostsRoute: typeof PostsRouteWithChildren + UsersRoute: typeof UsersRouteWithChildren + ApiUsersRoute: typeof ApiUsersRouteWithChildren + RedirectThrowItRoute: typeof RedirectThrowItRoute + RedirectIndexRoute: typeof RedirectIndexRoute + PostsPostIdDeepRoute: typeof PostsPostIdDeepRoute +} + +declare module '@tanstack/vue-router' { + interface FileRoutesByPath { + '/users': { + id: '/users' + path: '/users' + fullPath: '/users' + preLoaderRoute: typeof UsersRouteImport + parentRoute: typeof rootRouteImport + } + '/posts': { + id: '/posts' + path: '/posts' + fullPath: '/posts' + preLoaderRoute: typeof PostsRouteImport + parentRoute: typeof rootRouteImport + } + '/logout': { + id: '/logout' + path: '/logout' + fullPath: '/logout' + preLoaderRoute: typeof LogoutRouteImport + parentRoute: typeof rootRouteImport + } + '/deferred': { + id: '/deferred' + path: '/deferred' + fullPath: '/deferred' + preLoaderRoute: typeof DeferredRouteImport + parentRoute: typeof rootRouteImport + } + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport + parentRoute: typeof rootRouteImport + } + '/users/': { + id: '/users/' + path: '/' + fullPath: '/users/' + preLoaderRoute: typeof UsersIndexRouteImport + parentRoute: typeof UsersRoute + } + '/redirect/': { + id: '/redirect/' + path: '/redirect' + fullPath: '/redirect' + preLoaderRoute: typeof RedirectIndexRouteImport + parentRoute: typeof rootRouteImport + } + '/posts/': { + id: '/posts/' + path: '/' + fullPath: '/posts/' + preLoaderRoute: typeof PostsIndexRouteImport + parentRoute: typeof PostsRoute + } + '/users/$userId': { + id: '/users/$userId' + path: '/$userId' + fullPath: '/users/$userId' + preLoaderRoute: typeof UsersUserIdRouteImport + parentRoute: typeof UsersRoute + } + '/redirect/throw-it': { + id: '/redirect/throw-it' + path: '/redirect/throw-it' + fullPath: '/redirect/throw-it' + preLoaderRoute: typeof RedirectThrowItRouteImport + parentRoute: typeof rootRouteImport + } + '/posts/$postId': { + id: '/posts/$postId' + path: '/$postId' + fullPath: '/posts/$postId' + preLoaderRoute: typeof PostsPostIdRouteImport + parentRoute: typeof PostsRoute + } + '/api/users': { + id: '/api/users' + path: '/api/users' + fullPath: '/api/users' + preLoaderRoute: typeof ApiUsersRouteImport + parentRoute: typeof rootRouteImport + } + '/posts_/$postId/deep': { + id: '/posts_/$postId/deep' + path: '/posts/$postId/deep' + fullPath: '/posts/$postId/deep' + preLoaderRoute: typeof PostsPostIdDeepRouteImport + parentRoute: typeof rootRouteImport + } + '/api/users/$userId': { + id: '/api/users/$userId' + path: '/$userId' + fullPath: '/api/users/$userId' + preLoaderRoute: typeof ApiUsersUserIdRouteImport + parentRoute: typeof ApiUsersRoute + } + } +} + +interface PostsRouteChildren { + PostsPostIdRoute: typeof PostsPostIdRoute + PostsIndexRoute: typeof PostsIndexRoute +} + +const PostsRouteChildren: PostsRouteChildren = { + PostsPostIdRoute: PostsPostIdRoute, + PostsIndexRoute: PostsIndexRoute, +} + +const PostsRouteWithChildren = PostsRoute._addFileChildren(PostsRouteChildren) + +interface UsersRouteChildren { + UsersUserIdRoute: typeof UsersUserIdRoute + UsersIndexRoute: typeof UsersIndexRoute +} + +const UsersRouteChildren: UsersRouteChildren = { + UsersUserIdRoute: UsersUserIdRoute, + UsersIndexRoute: UsersIndexRoute, +} + +const UsersRouteWithChildren = UsersRoute._addFileChildren(UsersRouteChildren) + +interface ApiUsersRouteChildren { + ApiUsersUserIdRoute: typeof ApiUsersUserIdRoute +} + +const ApiUsersRouteChildren: ApiUsersRouteChildren = { + ApiUsersUserIdRoute: ApiUsersUserIdRoute, +} + +const ApiUsersRouteWithChildren = ApiUsersRoute._addFileChildren( + ApiUsersRouteChildren, +) + +const rootRouteChildren: RootRouteChildren = { + IndexRoute: IndexRoute, + DeferredRoute: DeferredRoute, + LogoutRoute: LogoutRoute, + PostsRoute: PostsRouteWithChildren, + UsersRoute: UsersRouteWithChildren, + ApiUsersRoute: ApiUsersRouteWithChildren, + RedirectThrowItRoute: RedirectThrowItRoute, + RedirectIndexRoute: RedirectIndexRoute, + PostsPostIdDeepRoute: PostsPostIdDeepRoute, +} +export const routeTree = rootRouteImport + ._addFileChildren(rootRouteChildren) + ._addFileTypes() + +import type { getRouter } from './router.tsx' +import type { createStart } from '@tanstack/vue-start' +declare module '@tanstack/vue-start' { + interface Register { + ssr: true + router: Awaited> + } +} diff --git a/e2e/vue-start/custom-basepath/src/router.tsx b/e2e/vue-start/custom-basepath/src/router.tsx new file mode 100644 index 00000000000..81103bf0bd6 --- /dev/null +++ b/e2e/vue-start/custom-basepath/src/router.tsx @@ -0,0 +1,18 @@ +import { createRouter } from '@tanstack/vue-router' +import { routeTree } from './routeTree.gen' +import { DefaultCatchBoundary } from './components/DefaultCatchBoundary' +import { NotFound } from './components/NotFound' +import { basepath } from './utils/basepath' + +export function getRouter() { + const router = createRouter({ + routeTree, + defaultPreload: 'intent', + defaultErrorComponent: DefaultCatchBoundary, + defaultNotFoundComponent: () => , + scrollRestoration: true, + basepath: basepath, + }) + + return router +} diff --git a/e2e/vue-start/custom-basepath/src/routes/__root.tsx b/e2e/vue-start/custom-basepath/src/routes/__root.tsx new file mode 100644 index 00000000000..8bc947b03a5 --- /dev/null +++ b/e2e/vue-start/custom-basepath/src/routes/__root.tsx @@ -0,0 +1,114 @@ +import { + Body, + HeadContent, + Html, + Link, + Outlet, + Scripts, + createRootRoute, +} from '@tanstack/vue-router' + +import { TanStackRouterDevtoolsInProd } from '@tanstack/vue-router-devtools' +import { NotFound } from '~/components/NotFound' +import appCss from '~/styles/app.css?url' +import { seo } from '~/utils/seo' + +export const Route = createRootRoute({ + head: () => ({ + meta: [ + { + name: 'viewport', + content: 'width=device-width, initial-scale=1', + }, + ...seo({ + title: + 'TanStack Start | Type-Safe, Client-First, Full-Stack Vue Framework', + description: `TanStack Start is a type-safe, client-first, full-stack Vue framework. `, + }), + ], + links: [ + { rel: 'stylesheet', href: appCss }, + { + rel: 'apple-touch-icon', + sizes: '180x180', + href: '/apple-touch-icon.png', + }, + { + rel: 'icon', + type: 'image/png', + sizes: '32x32', + href: '/favicon-32x32.png', + }, + { + rel: 'icon', + type: 'image/png', + sizes: '16x16', + href: '/favicon-16x16.png', + }, + { rel: 'manifest', href: '/site.webmanifest', color: '#fffff' }, + { rel: 'icon', href: '/favicon.ico' }, + ], + }), + errorComponent: (props) =>

{props.error.stack}

, + notFoundComponent: () => , + component: RootComponent, +}) + +function RootComponent() { + return ( + + + + + +
+ + Home + {' '} + + Posts + {' '} + + Users + {' '} + + Deferred + {' '} + + This Route Does Not Exist + +
+ + + + + + ) +} diff --git a/e2e/vue-start/custom-basepath/src/routes/api/users.$userId.ts b/e2e/vue-start/custom-basepath/src/routes/api/users.$userId.ts new file mode 100644 index 00000000000..5c55163c7f5 --- /dev/null +++ b/e2e/vue-start/custom-basepath/src/routes/api/users.$userId.ts @@ -0,0 +1,34 @@ +import { createFileRoute } from '@tanstack/vue-router' +import { json } from '@tanstack/vue-start' +import type { User } from '~/utils/users' + +let queryURL = 'https://jsonplaceholder.typicode.com' + +if (import.meta.env.VITE_NODE_ENV === 'test') { + queryURL = `http://localhost:${import.meta.env.VITE_EXTERNAL_PORT}` +} + +export const Route = createFileRoute('/api/users/$userId')({ + server: { + handlers: { + GET: async ({ params, request }) => { + console.info(`Fetching users by id=${params.userId}... @`, request.url) + try { + const res = await fetch(`${queryURL}/users/${params.userId}`) + if (!res.ok) { + throw new Error('Failed to fetch user') + } + const user = (await res.json()) as User + return json({ + id: user.id, + name: user.name, + email: user.email, + }) + } catch (e) { + console.error(e) + return json({ error: 'User not found' }, { status: 404 }) + } + }, + }, + }, +}) diff --git a/e2e/vue-start/custom-basepath/src/routes/api/users.ts b/e2e/vue-start/custom-basepath/src/routes/api/users.ts new file mode 100644 index 00000000000..5f2ab51270e --- /dev/null +++ b/e2e/vue-start/custom-basepath/src/routes/api/users.ts @@ -0,0 +1,65 @@ +import { createFileRoute } from '@tanstack/vue-router' +import { createMiddleware, json } from '@tanstack/vue-start' +import type { User } from '~/utils/users' + +const userLoggerMiddleware = createMiddleware().server( + async ({ next, request }) => { + console.info('In: /users') + const result = await next() + result.response.headers.set('x-users', 'true') + console.info('Out: /users') + return result + }, +) + +const testParentMiddleware = createMiddleware().server( + async ({ next, request }) => { + console.info('In: testParentMiddleware') + const result = await next() + result.response.headers.set('x-test-parent', 'true') + console.info('Out: testParentMiddleware') + return result + }, +) + +const testMiddleware = createMiddleware() + .middleware([testParentMiddleware]) + .server(async ({ next, request }) => { + console.info('In: testMiddleware') + const result = await next() + result.response.headers.set('x-test', 'true') + // if (Math.random() > 0.5) { + // throw new Response(null, { + // status: 302, + // headers: { Location: 'https://www.google.com' }, + // }) + // } + console.info('Out: testMiddleware') + return result + }) + +let queryURL = 'https://jsonplaceholder.typicode.com' + +if (import.meta.env.VITE_NODE_ENV === 'test') { + queryURL = `http://localhost:${import.meta.env.VITE_EXTERNAL_PORT}` +} + +export const Route = createFileRoute('/api/users')({ + server: { + middleware: [testMiddleware, userLoggerMiddleware, testParentMiddleware], + handlers: { + GET: async ({ request }) => { + console.info('Fetching users... @', request.url) + const res = await fetch(`${queryURL}/users`) + if (!res.ok) { + throw new Error('Failed to fetch users') + } + const data = (await res.json()) as Array + const list = data.slice(0, 10) + return json( + list.map((u) => ({ id: u.id, name: u.name, email: u.email })), + ) + }, + }, + }, +}) diff --git a/e2e/vue-start/custom-basepath/src/routes/deferred.tsx b/e2e/vue-start/custom-basepath/src/routes/deferred.tsx new file mode 100644 index 00000000000..7dbd50b0303 --- /dev/null +++ b/e2e/vue-start/custom-basepath/src/routes/deferred.tsx @@ -0,0 +1,79 @@ +import { Await, createFileRoute } from '@tanstack/vue-router' +import { createServerFn } from '@tanstack/vue-start' +import { Suspense, defineComponent, ref } from 'vue' + +const personServerFn = createServerFn({ method: 'GET' }) + .inputValidator((data: { name: string }) => data) + .handler(({ data }) => { + return { name: data.name, randomNumber: Math.floor(Math.random() * 100) } + }) + +const slowServerFn = createServerFn({ method: 'GET' }) + .inputValidator((data: { name: string }) => data) + .handler(async ({ data }) => { + await new Promise((r) => setTimeout(r, 1000)) + return { name: data.name, randomNumber: Math.floor(Math.random() * 100) } + }) + +const Deferred = defineComponent({ + setup() { + const count = ref(0) + const loaderData = Route.useLoaderData() + + return () => ( +
+
+ {loaderData.value.person.name} -{' '} + {loaderData.value.person.randomNumber} +
+ + {{ + default: () => ( + ( +
+ {data.name} - {data.randomNumber} +
+ )} + /> + ), + fallback: () =>
Loading person...
, + }} +
+ + {{ + default: () => ( + ( +

{data}

+ )} + /> + ), + fallback: () =>
Loading stuff...
, + }} +
+
Count: {count.value}
+
+ +
+
+ ) + }, +}) + +export const Route = createFileRoute('/deferred')({ + loader: async () => { + return { + deferredStuff: new Promise((r) => + setTimeout(() => r('Hello deferred!'), 2000), + ), + deferredPerson: slowServerFn({ data: { name: 'Tanner Linsley' } }), + person: await personServerFn({ data: { name: 'John Doe' } }), + } + }, + component: Deferred, +}) diff --git a/e2e/vue-start/custom-basepath/src/routes/index.tsx b/e2e/vue-start/custom-basepath/src/routes/index.tsx new file mode 100644 index 00000000000..67b348889b3 --- /dev/null +++ b/e2e/vue-start/custom-basepath/src/routes/index.tsx @@ -0,0 +1,15 @@ +import { createFileRoute } from '@tanstack/vue-router' +import { CustomMessage } from '~/components/CustomMessage' + +export const Route = createFileRoute('/')({ + component: Home, +}) + +function Home() { + return ( +
+

Welcome Home!!!

+ +
+ ) +} diff --git a/e2e/vue-start/custom-basepath/src/routes/logout.tsx b/e2e/vue-start/custom-basepath/src/routes/logout.tsx new file mode 100644 index 00000000000..f5db27dfc73 --- /dev/null +++ b/e2e/vue-start/custom-basepath/src/routes/logout.tsx @@ -0,0 +1,32 @@ +import { createFileRoute, redirect } from '@tanstack/vue-router' +import { createServerFn } from '@tanstack/vue-start' + +const logoutFn = createServerFn({ + method: 'POST', +}).handler(async () => { + // do logout stuff here + throw redirect({ + to: '/', + }) +}) + +export const Route = createFileRoute('/logout')({ + component: Home, +}) + +function Home() { + return ( +
+

Logout Page

+

+ This form tests that server function URLs correctly include the app's + basepath. The form action should be '/custom/basepath/_serverFn/...' not + just '/_serverFn/...' +

+
+ + +
+
+ ) +} diff --git a/e2e/vue-start/custom-basepath/src/routes/posts.$postId.tsx b/e2e/vue-start/custom-basepath/src/routes/posts.$postId.tsx new file mode 100644 index 00000000000..b4e5a8a6a25 --- /dev/null +++ b/e2e/vue-start/custom-basepath/src/routes/posts.$postId.tsx @@ -0,0 +1,35 @@ +import { Link, createFileRoute } from '@tanstack/vue-router' + +import { fetchPost } from '~/utils/posts' +import { NotFound } from '~/components/NotFound' +import { PostErrorComponent } from '~/components/PostErrorComponent' + +export const Route = createFileRoute('/posts/$postId')({ + loader: async ({ params: { postId } }) => fetchPost({ data: postId }), + errorComponent: PostErrorComponent, + component: PostComponent, + notFoundComponent: () => { + return Post not found + }, +}) + +function PostComponent() { + const post = Route.useLoaderData() + + return ( +
+

{post.value.title}

+
{post.value.body}
+ + Deep View + +
+ ) +} diff --git a/e2e/vue-start/custom-basepath/src/routes/posts.index.tsx b/e2e/vue-start/custom-basepath/src/routes/posts.index.tsx new file mode 100644 index 00000000000..85e7324cedd --- /dev/null +++ b/e2e/vue-start/custom-basepath/src/routes/posts.index.tsx @@ -0,0 +1,8 @@ +import { createFileRoute } from '@tanstack/vue-router' +export const Route = createFileRoute('/posts/')({ + component: PostsIndexComponent, +}) + +function PostsIndexComponent() { + return
Select a post.
+} diff --git a/e2e/vue-start/custom-basepath/src/routes/posts.tsx b/e2e/vue-start/custom-basepath/src/routes/posts.tsx new file mode 100644 index 00000000000..1a9d58a31db --- /dev/null +++ b/e2e/vue-start/custom-basepath/src/routes/posts.tsx @@ -0,0 +1,42 @@ +import { Link, Outlet, createFileRoute } from '@tanstack/vue-router' + +import { fetchPosts } from '~/utils/posts' + +export const Route = createFileRoute('/posts')({ + head: () => ({ + meta: [ + { + title: 'Posts page', + }, + ], + }), + loader: async () => fetchPosts(), + component: PostsComponent, +}) + +function PostsComponent() { + const posts = Route.useLoaderData() + + return ( +
+
    + {posts.value.map((post) => ( +
  • + +
    {post.title.substring(0, 20)}
    + +
  • + ))} +
+
+ +
+ ) +} diff --git a/e2e/vue-start/custom-basepath/src/routes/posts_.$postId.deep.tsx b/e2e/vue-start/custom-basepath/src/routes/posts_.$postId.deep.tsx new file mode 100644 index 00000000000..a52c6f32f74 --- /dev/null +++ b/e2e/vue-start/custom-basepath/src/routes/posts_.$postId.deep.tsx @@ -0,0 +1,24 @@ +import { Link, createFileRoute } from '@tanstack/vue-router' +import { PostErrorComponent } from '~/components/PostErrorComponent' + +import { fetchPost } from '~/utils/posts' + +export const Route = createFileRoute('/posts_/$postId/deep')({ + loader: async ({ params: { postId } }) => fetchPost({ data: postId }), + errorComponent: PostErrorComponent, + component: PostDeepComponent, +}) + +function PostDeepComponent() { + const post = Route.useLoaderData() + + return ( +
+ + ← All Posts + +

{post.value.title}

+
{post.value.body}
+
+ ) +} diff --git a/e2e/vue-start/custom-basepath/src/routes/redirect.index.tsx b/e2e/vue-start/custom-basepath/src/routes/redirect.index.tsx new file mode 100644 index 00000000000..33e31cae530 --- /dev/null +++ b/e2e/vue-start/custom-basepath/src/routes/redirect.index.tsx @@ -0,0 +1,15 @@ +import { Link, createFileRoute } from '@tanstack/vue-router' + +export const Route = createFileRoute('/redirect/')({ + component: RouteComponent, +}) + +function RouteComponent() { + return ( +
+ +
Throw It
+ +
+ ) +} diff --git a/e2e/vue-start/custom-basepath/src/routes/redirect.throw-it.tsx b/e2e/vue-start/custom-basepath/src/routes/redirect.throw-it.tsx new file mode 100644 index 00000000000..e3d6b849c8b --- /dev/null +++ b/e2e/vue-start/custom-basepath/src/routes/redirect.throw-it.tsx @@ -0,0 +1,10 @@ +import { createFileRoute, redirect } from '@tanstack/vue-router' + +export const Route = createFileRoute('/redirect/throw-it')({ + beforeLoad: () => { + throw redirect({ + to: '/posts/$postId', + params: { postId: '1' }, + }) + }, +}) diff --git a/e2e/vue-start/custom-basepath/src/routes/users.$userId.tsx b/e2e/vue-start/custom-basepath/src/routes/users.$userId.tsx new file mode 100644 index 00000000000..fb7fce5f4c4 --- /dev/null +++ b/e2e/vue-start/custom-basepath/src/routes/users.$userId.tsx @@ -0,0 +1,34 @@ +import { createFileRoute } from '@tanstack/vue-router' +import axios from 'redaxios' + +import type { User } from '~/utils/users' +import { NotFound } from '~/components/NotFound' +import { UserErrorComponent } from '~/components/UserErrorComponent' +import { basepath } from '~/utils/basepath' + +export const Route = createFileRoute('/users/$userId')({ + loader: async ({ params: { userId } }) => { + return await axios + .get(basepath + '/api/users/' + userId) + .then((r) => r.data) + .catch(() => { + throw new Error('Failed to fetch user') + }) + }, + errorComponent: UserErrorComponent, + component: UserComponent, + notFoundComponent: () => { + return User not found + }, +}) + +function UserComponent() { + const user = Route.useLoaderData() + + return ( +
+

{user.value.name}

+
{user.value.email}
+
+ ) +} diff --git a/e2e/vue-start/custom-basepath/src/routes/users.index.tsx b/e2e/vue-start/custom-basepath/src/routes/users.index.tsx new file mode 100644 index 00000000000..8fc7daca956 --- /dev/null +++ b/e2e/vue-start/custom-basepath/src/routes/users.index.tsx @@ -0,0 +1,8 @@ +import { createFileRoute } from '@tanstack/vue-router' +export const Route = createFileRoute('/users/')({ + component: UsersIndexComponent, +}) + +function UsersIndexComponent() { + return
Select a user.
+} diff --git a/e2e/vue-start/custom-basepath/src/routes/users.tsx b/e2e/vue-start/custom-basepath/src/routes/users.tsx new file mode 100644 index 00000000000..1432e504516 --- /dev/null +++ b/e2e/vue-start/custom-basepath/src/routes/users.tsx @@ -0,0 +1,46 @@ +import { Link, Outlet, createFileRoute } from '@tanstack/vue-router' +import axios from 'redaxios' +import type { User } from '~/utils/users' +import { basepath } from '~/utils/basepath' + +export const Route = createFileRoute('/users')({ + loader: async () => { + return await axios + .get>(basepath + '/api/users') + .then((r) => r.data) + .catch(() => { + throw new Error('Failed to fetch users') + }) + }, + component: UsersComponent, +}) + +function UsersComponent() { + const users = Route.useLoaderData() + + return ( +
+
    + {[ + ...users.value, + { id: 'i-do-not-exist', name: 'Non-existent User', email: '' }, + ].map((user) => ( +
  • + +
    {user.name}
    + +
  • + ))} +
+
+ +
+ ) +} diff --git a/e2e/vue-start/custom-basepath/src/server.ts b/e2e/vue-start/custom-basepath/src/server.ts new file mode 100644 index 00000000000..2dbc91706d6 --- /dev/null +++ b/e2e/vue-start/custom-basepath/src/server.ts @@ -0,0 +1,7 @@ +import handler from '@tanstack/vue-start/server-entry' + +export default { + fetch(request: Request) { + return handler.fetch(request) + }, +} diff --git a/e2e/vue-start/custom-basepath/src/styles/app.css b/e2e/vue-start/custom-basepath/src/styles/app.css new file mode 100644 index 00000000000..c36c737cd46 --- /dev/null +++ b/e2e/vue-start/custom-basepath/src/styles/app.css @@ -0,0 +1,30 @@ +@import 'tailwindcss'; + +@layer base { + *, + ::after, + ::before, + ::backdrop, + ::file-selector-button { + border-color: var(--color-gray-200, currentcolor); + } +} + +@layer base { + html { + color-scheme: light dark; + } + + * { + @apply border-gray-200 dark:border-gray-800; + } + + html, + body { + @apply text-gray-900 bg-gray-50 dark:bg-gray-950 dark:text-gray-200; + } + + .using-mouse * { + outline: none !important; + } +} diff --git a/e2e/vue-start/custom-basepath/src/utils/basepath.ts b/e2e/vue-start/custom-basepath/src/utils/basepath.ts new file mode 100644 index 00000000000..6e719f196cf --- /dev/null +++ b/e2e/vue-start/custom-basepath/src/utils/basepath.ts @@ -0,0 +1 @@ +export const basepath = '/custom/basepath' diff --git a/e2e/vue-start/custom-basepath/src/utils/posts.tsx b/e2e/vue-start/custom-basepath/src/utils/posts.tsx new file mode 100644 index 00000000000..a193bbc7afb --- /dev/null +++ b/e2e/vue-start/custom-basepath/src/utils/posts.tsx @@ -0,0 +1,42 @@ +import { notFound } from '@tanstack/vue-router' +import { createServerFn } from '@tanstack/vue-start' +import axios from 'redaxios' + +export type PostType = { + id: string + title: string + body: string +} + +let queryURL = 'https://jsonplaceholder.typicode.com' + +if (import.meta.env.VITE_NODE_ENV === 'test') { + queryURL = `http://localhost:${import.meta.env.VITE_EXTERNAL_PORT}` +} + +export const fetchPost = createServerFn({ method: 'GET' }) + .inputValidator((postId: string) => postId) + .handler(async ({ data: postId }) => { + console.info(`Fetching post with id ${postId}...`) + const post = await axios + .get(`${queryURL}/posts/${postId}`) + .then((r) => r.data) + .catch((err) => { + console.error(err) + if (err.status === 404) { + throw notFound() + } + throw err + }) + + return post + }) + +export const fetchPosts = createServerFn({ method: 'GET' }).handler( + async () => { + console.info('Fetching posts...') + return axios + .get>(`${queryURL}/posts`) + .then((r) => r.data.slice(0, 10)) + }, +) diff --git a/e2e/vue-start/custom-basepath/src/utils/seo.ts b/e2e/vue-start/custom-basepath/src/utils/seo.ts new file mode 100644 index 00000000000..d18ad84b74e --- /dev/null +++ b/e2e/vue-start/custom-basepath/src/utils/seo.ts @@ -0,0 +1,33 @@ +export const seo = ({ + title, + description, + keywords, + image, +}: { + title: string + description?: string + image?: string + keywords?: string +}) => { + const tags = [ + { title }, + { name: 'description', content: description }, + { name: 'keywords', content: keywords }, + { name: 'twitter:title', content: title }, + { name: 'twitter:description', content: description }, + { name: 'twitter:creator', content: '@tannerlinsley' }, + { name: 'twitter:site', content: '@tannerlinsley' }, + { name: 'og:type', content: 'website' }, + { name: 'og:title', content: title }, + { name: 'og:description', content: description }, + ...(image + ? [ + { name: 'twitter:image', content: image }, + { name: 'twitter:card', content: 'summary_large_image' }, + { name: 'og:image', content: image }, + ] + : []), + ] + + return tags +} diff --git a/e2e/vue-start/custom-basepath/src/utils/users.tsx b/e2e/vue-start/custom-basepath/src/utils/users.tsx new file mode 100644 index 00000000000..46be4b15804 --- /dev/null +++ b/e2e/vue-start/custom-basepath/src/utils/users.tsx @@ -0,0 +1,9 @@ +export type User = { + id: number + name: string + email: string +} + +const PORT = process.env.VITE_SERVER_PORT || 3000 + +export const DEPLOY_URL = `http://localhost:${PORT}` diff --git a/e2e/vue-start/custom-basepath/src/vite-env.d.ts b/e2e/vue-start/custom-basepath/src/vite-env.d.ts new file mode 100644 index 00000000000..0b2af560d60 --- /dev/null +++ b/e2e/vue-start/custom-basepath/src/vite-env.d.ts @@ -0,0 +1,4 @@ +declare module '*?url' { + const url: string + export default url +} diff --git a/e2e/vue-start/custom-basepath/tests/navigation.spec.ts b/e2e/vue-start/custom-basepath/tests/navigation.spec.ts new file mode 100644 index 00000000000..e66f14be9e2 --- /dev/null +++ b/e2e/vue-start/custom-basepath/tests/navigation.spec.ts @@ -0,0 +1,78 @@ +import { expect, test } from '@playwright/test' + +test('Navigating to post', async ({ page }) => { + await page.goto('/') + + await page.getByRole('link', { name: 'Posts' }).click() + await page.getByRole('link', { name: 'sunt aut facere repe' }).click() + await page.getByRole('link', { name: 'Deep View' }).click() + await expect(page.getByRole('heading')).toContainText('sunt aut facere') +}) + +test('Navigating to user', async ({ page }) => { + await page.goto('/') + + await page.getByRole('link', { name: 'Users' }).click() + await page.getByRole('link', { name: 'Leanne Graham' }).click() + await expect(page.getByRole('heading')).toContainText('Leanne Graham') +}) + +test('Navigating to a not-found route', async ({ page }) => { + await page.goto('/') + + await page.getByRole('link', { name: 'This Route Does Not Exist' }).click() + await page.getByRole('link', { name: 'Start Over' }).click() + await expect(page.getByRole('heading')).toContainText('Welcome Home!') +}) + +test('Should change title on client side navigation', async ({ page }) => { + await page.goto('/') + + await page.getByRole('link', { name: 'Posts' }).click() + + await expect(page).toHaveTitle('Posts page') +}) + +test('Server function URLs correctly include app basepath', async ({ + page, +}) => { + await page.goto('/logout') + + const form = page.locator('form') + const actionUrl = await form.getAttribute('action') + + expect(actionUrl).toMatch(/^\/custom\/basepath\/_serverFn\//) +}) + +test('client-side redirect', async ({ page, baseURL }) => { + await page.goto('/redirect') + await page.getByTestId('link-to-throw-it').click() + await page.waitForLoadState('networkidle') + + expect(await page.getByTestId('post-view').isVisible()).toBe(true) + expect(page.url()).toBe(`${baseURL}/posts/1`) +}) + +test('server-side redirect', async ({ page, baseURL }) => { + await page.goto('/redirect/throw-it') + await page.waitForLoadState('networkidle') + + expect(await page.getByTestId('post-view').isVisible()).toBe(true) + expect(page.url()).toBe(`${baseURL}/posts/1`) + + // do not follow redirects since we want to test the Location header + // first go to the route WITHOUT the base path, this will just add the base path + await page.request + .get('/redirect/throw-it', { maxRedirects: 0 }) + .then((res) => { + const headers = new Headers(res.headers()) + expect(headers.get('location')).toBe('/custom/basepath/redirect/throw-it') + }) + // now go to the route WITH the base path, this will redirect to the final destination + await page.request + .get('/custom/basepath/redirect/throw-it', { maxRedirects: 0 }) + .then((res) => { + const headers = new Headers(res.headers()) + expect(headers.get('location')).toBe('/custom/basepath/posts/1') + }) +}) diff --git a/e2e/vue-start/custom-basepath/tests/setup/global.setup.ts b/e2e/vue-start/custom-basepath/tests/setup/global.setup.ts new file mode 100644 index 00000000000..3593d10ab90 --- /dev/null +++ b/e2e/vue-start/custom-basepath/tests/setup/global.setup.ts @@ -0,0 +1,6 @@ +import { e2eStartDummyServer } from '@tanstack/router-e2e-utils' +import packageJson from '../../package.json' with { type: 'json' } + +export default async function setup() { + await e2eStartDummyServer(packageJson.name) +} diff --git a/e2e/vue-start/custom-basepath/tests/setup/global.teardown.ts b/e2e/vue-start/custom-basepath/tests/setup/global.teardown.ts new file mode 100644 index 00000000000..62fd79911cc --- /dev/null +++ b/e2e/vue-start/custom-basepath/tests/setup/global.teardown.ts @@ -0,0 +1,6 @@ +import { e2eStopDummyServer } from '@tanstack/router-e2e-utils' +import packageJson from '../../package.json' with { type: 'json' } + +export default async function teardown() { + await e2eStopDummyServer(packageJson.name) +} diff --git a/e2e/vue-start/custom-basepath/tsconfig.json b/e2e/vue-start/custom-basepath/tsconfig.json new file mode 100644 index 00000000000..6d933a5b2c9 --- /dev/null +++ b/e2e/vue-start/custom-basepath/tsconfig.json @@ -0,0 +1,24 @@ +{ + "include": ["**/*.ts", "**/*.tsx", "public/script*.js"], + "compilerOptions": { + "strict": true, + "esModuleInterop": true, + "jsx": "preserve", + "jsxImportSource": "vue", + "module": "ESNext", + "moduleResolution": "Bundler", + "lib": ["DOM", "DOM.Iterable", "ES2022"], + "isolatedModules": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "target": "ES2022", + "allowJs": true, + "forceConsistentCasingInFileNames": true, + "baseUrl": ".", + "paths": { + "~/*": ["./src/*"] + }, + "noEmit": true, + "types": ["vite/client"] + } +} diff --git a/e2e/vue-start/custom-basepath/vite.config.ts b/e2e/vue-start/custom-basepath/vite.config.ts new file mode 100644 index 00000000000..8f0fee2ce7f --- /dev/null +++ b/e2e/vue-start/custom-basepath/vite.config.ts @@ -0,0 +1,20 @@ +import { defineConfig } from 'vite' +import tsConfigPaths from 'vite-tsconfig-paths' +import { tanstackStart } from '@tanstack/vue-start/plugin/vite' +import vue from '@vitejs/plugin-vue' +import vueJsx from '@vitejs/plugin-vue-jsx' + +export default defineConfig({ + base: '/custom/basepath', + server: { + port: 3000, + }, + plugins: [ + tsConfigPaths({ + projects: ['./tsconfig.json'], + }), + tanstackStart(), + vue(), + vueJsx(), + ], +}) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9185025de54..bf7f2bd9905 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1458,7 +1458,7 @@ importers: version: 2.6.0 vite: specifier: ^7.1.7 - version: 7.1.7(@types/node@22.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.8.1) + version: 7.1.7(@types/node@22.10.2)(jiti@1.21.7)(lightningcss@1.30.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.8.1) devDependencies: '@playwright/test': specifier: ^1.56.1 @@ -1477,7 +1477,7 @@ importers: version: 19.2.2(@types/react@19.2.2) '@vitejs/plugin-react': specifier: ^4.3.4 - version: 4.7.0(vite@7.1.7(@types/node@22.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.8.1)) + version: 4.7.0(vite@7.1.7(@types/node@22.10.2)(jiti@1.21.7)(lightningcss@1.30.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.8.1)) autoprefixer: specifier: ^10.4.20 version: 10.4.20(postcss@8.5.6) @@ -1492,7 +1492,7 @@ importers: version: 5.9.2 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.1)(lightningcss@1.30.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.8.1)) + version: 5.1.4(typescript@5.9.2)(vite@7.1.7(@types/node@22.10.2)(jiti@1.21.7)(lightningcss@1.30.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.8.1)) e2e/react-start/custom-basepath: dependencies: @@ -4800,6 +4800,73 @@ importers: specifier: ^5.1.4 version: 5.1.4(typescript@5.9.2)(vite@7.1.7(@types/node@22.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.8.1)) + e2e/vue-start/custom-basepath: + dependencies: + '@tanstack/vue-router': + specifier: workspace:* + version: link:../../../packages/vue-router + '@tanstack/vue-router-devtools': + specifier: workspace:* + version: link:../../../packages/vue-router-devtools + '@tanstack/vue-start': + specifier: workspace:* + version: link:../../../packages/vue-start + express: + specifier: ^4.21.2 + version: 4.21.2 + redaxios: + specifier: ^0.5.1 + version: 0.5.1 + vue: + specifier: ^3.5.25 + version: 3.5.25(typescript@5.9.2) + devDependencies: + '@playwright/test': + specifier: ^1.56.1 + version: 1.56.1 + '@tailwindcss/postcss': + specifier: ^4.1.15 + version: 4.1.15 + '@tanstack/router-e2e-utils': + specifier: workspace:^ + version: link:../../e2e-utils + '@types/express': + specifier: ^5.0.3 + version: 5.0.3 + '@types/node': + specifier: 22.10.2 + version: 22.10.2 + '@vitejs/plugin-vue': + specifier: ^6.0.3 + version: 6.0.3(vite@7.1.7(@types/node@22.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.8.1))(vue@3.5.25(typescript@5.9.2)) + '@vitejs/plugin-vue-jsx': + specifier: ^5.1.2 + version: 5.1.2(vite@7.1.7(@types/node@22.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.8.1))(vue@3.5.25(typescript@5.9.2)) + cross-env: + specifier: ^10.0.0 + version: 10.0.0 + postcss: + specifier: ^8.5.1 + version: 8.5.6 + srvx: + specifier: ^0.9.8 + version: 0.9.8 + tailwindcss: + specifier: ^4.1.17 + version: 4.1.17 + tsx: + specifier: ^4.20.3 + version: 4.20.3 + 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.1)(lightningcss@1.30.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.8.1) + 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.1)(lightningcss@1.30.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.8.1)) + e2e/vue-start/query-integration: dependencies: '@tanstack/vue-query': @@ -7599,7 +7666,7 @@ importers: version: 7.1.7(@types/node@22.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.8.1) vitest: specifier: ^3.2.4 - version: 3.2.4(@types/node@22.10.2)(@vitest/browser@3.0.6(@types/node@22.10.2)(playwright@1.56.1)(typescript@5.9.2)(vite@7.1.7(@types/node@22.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.8.1))(vitest@3.2.4))(@vitest/ui@3.0.6(vitest@3.2.4))(jiti@2.6.1)(jsdom@27.0.0(postcss@8.5.6))(lightningcss@1.30.2)(msw@2.7.0(@types/node@22.10.2)(typescript@5.9.2))(terser@5.37.0)(tsx@4.20.3)(yaml@2.8.1) + version: 3.2.4(@types/node@22.10.2)(@vitest/browser@3.0.6)(@vitest/ui@3.0.6)(jiti@2.6.1)(jsdom@27.0.0(postcss@8.5.6))(lightningcss@1.30.2)(msw@2.7.0(@types/node@22.10.2)(typescript@5.9.2))(terser@5.37.0)(tsx@4.20.3)(yaml@2.8.1) web-vitals: specifier: ^5.1.0 version: 5.1.0 @@ -10006,7 +10073,7 @@ importers: devDependencies: '@netlify/vite-plugin-tanstack-start': specifier: ^1.1.4 - version: 1.1.4(@tanstack/solid-start@packages+solid-start)(babel-plugin-macros@3.1.0)(db0@0.3.4(@electric-sql/pglite@0.3.2)(@libsql/client@0.15.15)(mysql2@3.15.3))(encoding@0.1.13)(ioredis@5.8.0)(rollup@4.52.5)(vite@7.1.7(@types/node@22.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.8.1)) + version: 1.1.4(@tanstack/solid-start@packages+solid-start)(babel-plugin-macros@3.1.0)(db0@0.3.4(@electric-sql/pglite@0.3.2)(@libsql/client@0.15.15)(mysql2@3.15.3))(ioredis@5.8.0)(rollup@4.52.5)(vite@7.1.7(@types/node@22.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.8.1)) '@tailwindcss/postcss': specifier: ^4.1.15 version: 4.1.15 @@ -10244,7 +10311,7 @@ importers: version: 2.11.10(@testing-library/jest-dom@6.6.3)(solid-js@1.9.10)(vite@7.1.7(@types/node@22.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.8.1)) vitest: specifier: ^3.2.4 - version: 3.2.4(@types/node@22.10.2)(@vitest/browser@3.0.6(@types/node@22.10.2)(playwright@1.56.1)(typescript@5.9.2)(vite@7.1.7(@types/node@22.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.8.1))(vitest@3.2.4))(@vitest/ui@3.0.6(vitest@3.2.4))(jiti@2.6.1)(jsdom@27.0.0(postcss@8.5.6))(lightningcss@1.30.2)(msw@2.7.0(@types/node@22.10.2)(typescript@5.9.2))(terser@5.37.0)(tsx@4.20.3)(yaml@2.8.1) + version: 3.2.4(@types/node@22.10.2)(@vitest/browser@3.0.6)(@vitest/ui@3.0.6)(jiti@2.6.1)(jsdom@27.0.0(postcss@8.5.6))(lightningcss@1.30.2)(msw@2.7.0(@types/node@22.10.2)(typescript@5.9.2))(terser@5.37.0)(tsx@4.20.3)(yaml@2.8.1) web-vitals: specifier: ^5.1.0 version: 5.1.0 @@ -10735,16 +10802,16 @@ importers: devDependencies: '@vitejs/plugin-vue': specifier: ^5.2.3 - version: 5.2.4(vite@7.1.7(@types/node@22.10.2)(jiti@1.21.7)(lightningcss@1.30.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.8.1))(vue@3.5.25(typescript@5.9.2)) + version: 5.2.4(vite@7.1.7(@types/node@22.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.8.1))(vue@3.5.25(typescript@5.9.2)) '@vitejs/plugin-vue-jsx': specifier: ^4.1.2 - version: 4.2.0(vite@7.1.7(@types/node@22.10.2)(jiti@1.21.7)(lightningcss@1.30.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.8.1))(vue@3.5.25(typescript@5.9.2)) + version: 4.2.0(vite@7.1.7(@types/node@22.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.8.1))(vue@3.5.25(typescript@5.9.2)) typescript: specifier: ^5.7.2 version: 5.9.2 vite: specifier: ^7.1.7 - version: 7.1.7(@types/node@22.10.2)(jiti@1.21.7)(lightningcss@1.30.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.8.1) + version: 7.1.7(@types/node@22.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.8.1) vue-tsc: specifier: ^3.1.5 version: 3.1.5(typescript@5.9.2) @@ -26270,13 +26337,13 @@ snapshots: uuid: 11.1.0 write-file-atomic: 5.0.1 - '@netlify/dev@4.6.3(db0@0.3.4(@electric-sql/pglite@0.3.2)(@libsql/client@0.15.15)(mysql2@3.15.3))(encoding@0.1.13)(ioredis@5.8.0)(rollup@4.52.5)': + '@netlify/dev@4.6.3(db0@0.3.4(@electric-sql/pglite@0.3.2)(@libsql/client@0.15.15)(mysql2@3.15.3))(ioredis@5.8.0)(rollup@4.52.5)': dependencies: '@netlify/blobs': 10.1.0 '@netlify/config': 23.2.0 '@netlify/dev-utils': 4.3.0 '@netlify/edge-functions-dev': 1.0.0 - '@netlify/functions-dev': 1.0.0(encoding@0.1.13)(rollup@4.52.5) + '@netlify/functions-dev': 1.0.0(rollup@4.52.5) '@netlify/headers': 2.1.0 '@netlify/images': 1.3.0(@netlify/blobs@10.1.0)(db0@0.3.4(@electric-sql/pglite@0.3.2)(@libsql/client@0.15.15)(mysql2@3.15.3))(ioredis@5.8.0) '@netlify/redirects': 3.1.0 @@ -26344,12 +26411,12 @@ snapshots: dependencies: '@netlify/types': 2.1.0 - '@netlify/functions-dev@1.0.0(encoding@0.1.13)(rollup@4.52.5)': + '@netlify/functions-dev@1.0.0(rollup@4.52.5)': dependencies: '@netlify/blobs': 10.1.0 '@netlify/dev-utils': 4.3.0 '@netlify/functions': 5.0.0 - '@netlify/zip-it-and-ship-it': 14.1.11(encoding@0.1.13)(rollup@4.52.5) + '@netlify/zip-it-and-ship-it': 14.1.11(rollup@4.52.5) cron-parser: 4.9.0 decache: 4.6.2 extract-zip: 2.0.1 @@ -26439,9 +26506,9 @@ snapshots: '@netlify/types@2.1.0': {} - '@netlify/vite-plugin-tanstack-start@1.1.4(@tanstack/solid-start@packages+solid-start)(babel-plugin-macros@3.1.0)(db0@0.3.4(@electric-sql/pglite@0.3.2)(@libsql/client@0.15.15)(mysql2@3.15.3))(encoding@0.1.13)(ioredis@5.8.0)(rollup@4.52.5)(vite@7.1.7(@types/node@22.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.8.1))': + '@netlify/vite-plugin-tanstack-start@1.1.4(@tanstack/solid-start@packages+solid-start)(babel-plugin-macros@3.1.0)(db0@0.3.4(@electric-sql/pglite@0.3.2)(@libsql/client@0.15.15)(mysql2@3.15.3))(ioredis@5.8.0)(rollup@4.52.5)(vite@7.1.7(@types/node@22.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.8.1))': dependencies: - '@netlify/vite-plugin': 2.7.4(babel-plugin-macros@3.1.0)(db0@0.3.4(@electric-sql/pglite@0.3.2)(@libsql/client@0.15.15)(mysql2@3.15.3))(encoding@0.1.13)(ioredis@5.8.0)(rollup@4.52.5)(vite@7.1.7(@types/node@22.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.8.1)) + '@netlify/vite-plugin': 2.7.4(babel-plugin-macros@3.1.0)(db0@0.3.4(@electric-sql/pglite@0.3.2)(@libsql/client@0.15.15)(mysql2@3.15.3))(ioredis@5.8.0)(rollup@4.52.5)(vite@7.1.7(@types/node@22.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.8.1)) vite: 7.1.7(@types/node@22.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.8.1) optionalDependencies: '@tanstack/solid-start': link:packages/solid-start @@ -26469,9 +26536,9 @@ snapshots: - supports-color - uploadthing - '@netlify/vite-plugin@2.7.4(babel-plugin-macros@3.1.0)(db0@0.3.4(@electric-sql/pglite@0.3.2)(@libsql/client@0.15.15)(mysql2@3.15.3))(encoding@0.1.13)(ioredis@5.8.0)(rollup@4.52.5)(vite@7.1.7(@types/node@22.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.8.1))': + '@netlify/vite-plugin@2.7.4(babel-plugin-macros@3.1.0)(db0@0.3.4(@electric-sql/pglite@0.3.2)(@libsql/client@0.15.15)(mysql2@3.15.3))(ioredis@5.8.0)(rollup@4.52.5)(vite@7.1.7(@types/node@22.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.8.1))': dependencies: - '@netlify/dev': 4.6.3(db0@0.3.4(@electric-sql/pglite@0.3.2)(@libsql/client@0.15.15)(mysql2@3.15.3))(encoding@0.1.13)(ioredis@5.8.0)(rollup@4.52.5) + '@netlify/dev': 4.6.3(db0@0.3.4(@electric-sql/pglite@0.3.2)(@libsql/client@0.15.15)(mysql2@3.15.3))(ioredis@5.8.0)(rollup@4.52.5) '@netlify/dev-utils': 4.3.0 dedent: 1.7.0(babel-plugin-macros@3.1.0) vite: 7.1.7(@types/node@22.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.8.1) @@ -26499,13 +26566,13 @@ snapshots: - supports-color - uploadthing - '@netlify/zip-it-and-ship-it@14.1.11(encoding@0.1.13)(rollup@4.52.5)': + '@netlify/zip-it-and-ship-it@14.1.11(rollup@4.52.5)': dependencies: '@babel/parser': 7.28.5 '@babel/types': 7.28.4 '@netlify/binary-info': 1.0.0 '@netlify/serverless-functions-api': 2.7.1 - '@vercel/nft': 0.29.4(encoding@0.1.13)(rollup@4.52.5) + '@vercel/nft': 0.29.4(rollup@4.52.5) archiver: 7.0.1 common-path-prefix: 3.0.0 copy-file: 11.1.0 @@ -29581,7 +29648,7 @@ snapshots: '@unrs/resolver-binding-win32-x64-msvc@1.11.1': optional: true - '@vercel/nft@0.29.4(encoding@0.1.13)(rollup@4.52.5)': + '@vercel/nft@0.29.4(rollup@4.52.5)': dependencies: '@mapbox/node-pre-gyp': 2.0.0(encoding@0.1.13) '@rollup/pluginutils': 5.1.4(rollup@4.52.5) @@ -29642,6 +29709,18 @@ snapshots: transitivePeerDependencies: - supports-color + '@vitejs/plugin-react@4.7.0(vite@7.1.7(@types/node@22.10.2)(jiti@1.21.7)(lightningcss@1.30.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.8.1))': + dependencies: + '@babel/core': 7.28.5 + '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.28.5) + '@rolldown/pluginutils': 1.0.0-beta.27 + '@types/babel__core': 7.20.5 + react-refresh: 0.17.0 + vite: 7.1.7(@types/node@22.10.2)(jiti@1.21.7)(lightningcss@1.30.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.8.1) + transitivePeerDependencies: + - supports-color + '@vitejs/plugin-react@4.7.0(vite@7.1.7(@types/node@22.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.8.1))': dependencies: '@babel/core': 7.28.5 @@ -29678,17 +29757,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@vitejs/plugin-vue-jsx@4.2.0(vite@7.1.7(@types/node@22.10.2)(jiti@1.21.7)(lightningcss@1.30.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.8.1))(vue@3.5.25(typescript@5.9.2))': - dependencies: - '@babel/core': 7.28.5 - '@babel/plugin-transform-typescript': 7.28.5(@babel/core@7.28.5) - '@rolldown/pluginutils': 1.0.0-beta.40 - '@vue/babel-plugin-jsx': 1.5.0(@babel/core@7.28.5) - vite: 7.1.7(@types/node@22.10.2)(jiti@1.21.7)(lightningcss@1.30.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.8.1) - vue: 3.5.25(typescript@5.9.2) - transitivePeerDependencies: - - supports-color - '@vitejs/plugin-vue-jsx@4.2.0(vite@7.1.7(@types/node@22.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.8.1))(vue@3.5.25(typescript@5.8.3))': dependencies: '@babel/core': 7.28.5 @@ -29723,11 +29791,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@vitejs/plugin-vue@5.2.4(vite@7.1.7(@types/node@22.10.2)(jiti@1.21.7)(lightningcss@1.30.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.8.1))(vue@3.5.25(typescript@5.9.2))': - dependencies: - vite: 7.1.7(@types/node@22.10.2)(jiti@1.21.7)(lightningcss@1.30.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.8.1) - vue: 3.5.25(typescript@5.9.2) - '@vitejs/plugin-vue@5.2.4(vite@7.1.7(@types/node@22.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.8.1))(vue@3.5.25(typescript@5.8.3))': dependencies: vite: 7.1.7(@types/node@22.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.8.1) @@ -36560,6 +36623,17 @@ snapshots: - supports-color - typescript + vite-tsconfig-paths@5.1.4(typescript@5.9.2)(vite@7.1.7(@types/node@22.10.2)(jiti@1.21.7)(lightningcss@1.30.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.8.1)): + dependencies: + debug: 4.4.3 + globrex: 0.1.2 + tsconfck: 3.1.4(typescript@5.9.2) + optionalDependencies: + vite: 7.1.7(@types/node@22.10.2)(jiti@1.21.7)(lightningcss@1.30.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.8.1) + transitivePeerDependencies: + - supports-color + - typescript + vite-tsconfig-paths@5.1.4(typescript@5.9.2)(vite@7.1.7(@types/node@22.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.8.1)): dependencies: debug: 4.4.3 @@ -36609,7 +36683,7 @@ snapshots: optionalDependencies: vite: 7.1.7(@types/node@22.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.8.1) - vitest@3.2.4(@types/node@22.10.2)(@vitest/browser@3.0.6(@types/node@22.10.2)(playwright@1.56.1)(typescript@5.9.2)(vite@7.1.7(@types/node@22.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.8.1))(vitest@3.2.4))(@vitest/ui@3.0.6(vitest@3.2.4))(jiti@2.6.1)(jsdom@27.0.0(postcss@8.5.6))(lightningcss@1.30.2)(msw@2.7.0(@types/node@22.10.2)(typescript@5.9.2))(terser@5.37.0)(tsx@4.20.3)(yaml@2.8.1): + vitest@3.2.4(@types/node@22.10.2)(@vitest/browser@3.0.6)(@vitest/ui@3.0.6)(jiti@2.6.1)(jsdom@25.0.1)(lightningcss@1.30.2)(msw@2.7.0(@types/node@22.10.2)(typescript@5.9.2))(terser@5.37.0)(tsx@4.20.3)(yaml@2.8.1): dependencies: '@types/chai': 5.2.2 '@vitest/expect': 3.2.4 @@ -36638,7 +36712,7 @@ snapshots: '@types/node': 22.10.2 '@vitest/browser': 3.0.6(@types/node@22.10.2)(playwright@1.56.1)(typescript@5.9.2)(vite@7.1.7(@types/node@22.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.8.1))(vitest@3.2.4) '@vitest/ui': 3.0.6(vitest@3.2.4) - jsdom: 27.0.0(postcss@8.5.6) + jsdom: 25.0.1 transitivePeerDependencies: - jiti - less @@ -36653,7 +36727,7 @@ snapshots: - tsx - yaml - vitest@3.2.4(@types/node@22.10.2)(@vitest/browser@3.0.6)(@vitest/ui@3.0.6)(jiti@2.6.1)(jsdom@25.0.1)(lightningcss@1.30.2)(msw@2.7.0(@types/node@22.10.2)(typescript@5.9.2))(terser@5.37.0)(tsx@4.20.3)(yaml@2.8.1): + vitest@3.2.4(@types/node@22.10.2)(@vitest/browser@3.0.6)(@vitest/ui@3.0.6)(jiti@2.6.1)(jsdom@27.0.0(postcss@8.5.6))(lightningcss@1.30.2)(msw@2.7.0(@types/node@22.10.2)(typescript@5.9.2))(terser@5.37.0)(tsx@4.20.3)(yaml@2.8.1): dependencies: '@types/chai': 5.2.2 '@vitest/expect': 3.2.4 @@ -36682,7 +36756,7 @@ snapshots: '@types/node': 22.10.2 '@vitest/browser': 3.0.6(@types/node@22.10.2)(playwright@1.56.1)(typescript@5.9.2)(vite@7.1.7(@types/node@22.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.8.1))(vitest@3.2.4) '@vitest/ui': 3.0.6(vitest@3.2.4) - jsdom: 25.0.1 + jsdom: 27.0.0(postcss@8.5.6) transitivePeerDependencies: - jiti - less diff --git a/port-tanstack-router-e2e-react-basepath-file-based-external.txt b/port-tanstack-router-e2e-react-basepath-file-based-external.txt new file mode 100644 index 00000000000..f0c469dc4c5 --- /dev/null +++ b/port-tanstack-router-e2e-react-basepath-file-based-external.txt @@ -0,0 +1 @@ +52266 \ No newline at end of file diff --git a/port-tanstack-router-e2e-react-basepath-file-based.txt b/port-tanstack-router-e2e-react-basepath-file-based.txt new file mode 100644 index 00000000000..7f6b832d293 --- /dev/null +++ b/port-tanstack-router-e2e-react-basepath-file-based.txt @@ -0,0 +1 @@ +52265 \ No newline at end of file