diff --git a/e2e/vue-router/basepath-file-based/.gitignore b/e2e/vue-router/basepath-file-based/.gitignore new file mode 100644 index 00000000000..4d2da67b504 --- /dev/null +++ b/e2e/vue-router/basepath-file-based/.gitignore @@ -0,0 +1,11 @@ +node_modules +.DS_Store +dist +dist-hash +dist-ssr +*.local + +/test-results/ +/playwright-report/ +/blob-report/ +/playwright/.cache/ diff --git a/e2e/vue-router/basepath-file-based/index.html b/e2e/vue-router/basepath-file-based/index.html new file mode 100644 index 00000000000..21e30f16951 --- /dev/null +++ b/e2e/vue-router/basepath-file-based/index.html @@ -0,0 +1,11 @@ + + + + + + + +
+ + + diff --git a/e2e/vue-router/basepath-file-based/package.json b/e2e/vue-router/basepath-file-based/package.json new file mode 100644 index 00000000000..c46fe082281 --- /dev/null +++ b/e2e/vue-router/basepath-file-based/package.json @@ -0,0 +1,34 @@ +{ + "name": "tanstack-router-e2e-vue-basepath-file-based", + "private": true, + "type": "module", + "scripts": { + "dev": "vite --port 3000", + "dev:e2e": "vite", + "build": "vite build && tsc --noEmit", + "preview": "vite preview", + "start": "vite", + "test:e2e": "rm -rf port*.txt; playwright test --project=chromium" + }, + "dependencies": { + "@tailwindcss/postcss": "^4.1.15", + "@tanstack/router-plugin": "workspace:^", + "@tanstack/vue-query": "^5.90.0", + "@tanstack/vue-query-devtools": "^6.1.2", + "@tanstack/vue-router": "workspace:^", + "@tanstack/vue-router-devtools": "workspace:^", + "postcss": "^8.5.1", + "redaxios": "^0.5.1", + "tailwindcss": "^4.1.17", + "vue": "^3.5.16" + }, + "devDependencies": { + "@playwright/test": "^1.50.1", + "@tanstack/router-e2e-utils": "workspace:^", + "@vitejs/plugin-vue": "^5.2.3", + "@vitejs/plugin-vue-jsx": "^4.1.2", + "typescript": "~5.8.3", + "vite": "^7.1.7", + "vue-tsc": "^3.1.5" + } +} diff --git a/e2e/vue-router/basepath-file-based/playwright.config.ts b/e2e/vue-router/basepath-file-based/playwright.config.ts new file mode 100644 index 00000000000..6069bbe59f4 --- /dev/null +++ b/e2e/vue-router/basepath-file-based/playwright.config.ts @@ -0,0 +1,41 @@ +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}` +/** + * 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_SERVER_PORT=${PORT} VITE_EXTERNAL_PORT=${EXTERNAL_PORT} pnpm build && VITE_SERVER_PORT=${PORT} pnpm preview --port ${PORT}`, + url: baseURL, + reuseExistingServer: !process.env.CI, + stdout: 'pipe', + }, + + projects: [ + { + name: 'chromium', + use: { ...devices['Desktop Chrome'] }, + }, + ], +}) diff --git a/e2e/vue-router/basepath-file-based/src/main.tsx b/e2e/vue-router/basepath-file-based/src/main.tsx new file mode 100644 index 00000000000..e573c4a2003 --- /dev/null +++ b/e2e/vue-router/basepath-file-based/src/main.tsx @@ -0,0 +1,30 @@ +import { createApp, h } from 'vue' +import { RouterProvider, createRouter } from '@tanstack/vue-router' +import { routeTree } from './routeTree.gen' + +// Set up a Router instance +const router = createRouter({ + routeTree, + defaultPreload: 'intent', + defaultStaleTime: 5000, + scrollRestoration: true, + basepath: '/app/', +}) + +// Register things for typesafety +declare module '@tanstack/vue-router' { + interface Register { + router: typeof router + } +} + +const rootElement = document.getElementById('app')! + +if (!rootElement.innerHTML) { + const app = createApp({ + setup() { + return () => h(RouterProvider, { router }) + }, + }) + app.mount('#app') +} diff --git a/e2e/vue-router/basepath-file-based/src/routeTree.gen.ts b/e2e/vue-router/basepath-file-based/src/routeTree.gen.ts new file mode 100644 index 00000000000..82b561baf1a --- /dev/null +++ b/e2e/vue-router/basepath-file-based/src/routeTree.gen.ts @@ -0,0 +1,77 @@ +/* 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 AboutRouteImport } from './routes/about' +import { Route as IndexRouteImport } from './routes/index' + +const AboutRoute = AboutRouteImport.update({ + id: '/about', + path: '/about', + getParentRoute: () => rootRouteImport, +} as any) +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => rootRouteImport, +} as any) + +export interface FileRoutesByFullPath { + '/': typeof IndexRoute + '/about': typeof AboutRoute +} +export interface FileRoutesByTo { + '/': typeof IndexRoute + '/about': typeof AboutRoute +} +export interface FileRoutesById { + __root__: typeof rootRouteImport + '/': typeof IndexRoute + '/about': typeof AboutRoute +} +export interface FileRouteTypes { + fileRoutesByFullPath: FileRoutesByFullPath + fullPaths: '/' | '/about' + fileRoutesByTo: FileRoutesByTo + to: '/' | '/about' + id: '__root__' | '/' | '/about' + fileRoutesById: FileRoutesById +} +export interface RootRouteChildren { + IndexRoute: typeof IndexRoute + AboutRoute: typeof AboutRoute +} + +declare module '@tanstack/vue-router' { + interface FileRoutesByPath { + '/about': { + id: '/about' + path: '/about' + fullPath: '/about' + preLoaderRoute: typeof AboutRouteImport + parentRoute: typeof rootRouteImport + } + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport + parentRoute: typeof rootRouteImport + } + } +} + +const rootRouteChildren: RootRouteChildren = { + IndexRoute: IndexRoute, + AboutRoute: AboutRoute, +} +export const routeTree = rootRouteImport + ._addFileChildren(rootRouteChildren) + ._addFileTypes() diff --git a/e2e/vue-router/basepath-file-based/src/routes/__root.tsx b/e2e/vue-router/basepath-file-based/src/routes/__root.tsx new file mode 100644 index 00000000000..815b2762f6b --- /dev/null +++ b/e2e/vue-router/basepath-file-based/src/routes/__root.tsx @@ -0,0 +1,3 @@ +import { createRootRoute } from '@tanstack/vue-router' + +export const Route = createRootRoute() diff --git a/e2e/vue-router/basepath-file-based/src/routes/about.tsx b/e2e/vue-router/basepath-file-based/src/routes/about.tsx new file mode 100644 index 00000000000..9fd1a78dd90 --- /dev/null +++ b/e2e/vue-router/basepath-file-based/src/routes/about.tsx @@ -0,0 +1,20 @@ +import { createFileRoute } from '@tanstack/vue-router' + +export const Route = createFileRoute('/about')({ + component: RouteComponent, +}) + +function RouteComponent() { + const navigate = Route.useNavigate() + + return ( +
+ +
+ ) +} diff --git a/e2e/vue-router/basepath-file-based/src/routes/index.tsx b/e2e/vue-router/basepath-file-based/src/routes/index.tsx new file mode 100644 index 00000000000..9fc5d3b2adf --- /dev/null +++ b/e2e/vue-router/basepath-file-based/src/routes/index.tsx @@ -0,0 +1,25 @@ +import { createFileRoute } from '@tanstack/vue-router' + +export const Route = createFileRoute('/')({ + component: App, +}) + +function App() { + const navigate = Route.useNavigate() + + return ( +
+ +
+ ) +} diff --git a/e2e/vue-router/basepath-file-based/tests/reload-document.test.ts b/e2e/vue-router/basepath-file-based/tests/reload-document.test.ts new file mode 100644 index 00000000000..3e60f3bedcb --- /dev/null +++ b/e2e/vue-router/basepath-file-based/tests/reload-document.test.ts @@ -0,0 +1,18 @@ +import { expect, test } from '@playwright/test' + +test('navigate() respects basepath for when reloadDocument=true', async ({ + page, +}) => { + await page.goto(`/app/`) + await expect(page.getByTestId(`home-component`)).toBeInViewport() + + const aboutBtn = page.getByTestId(`to-about-btn`) + await aboutBtn.click() + await page.waitForURL('/app/about') + await expect(page.getByTestId(`about-component`)).toBeInViewport() + + const homeBtn = page.getByTestId(`to-home-btn`) + await homeBtn.click() + await page.waitForURL('/app/') + await expect(page.getByTestId(`home-component`)).toBeInViewport() +}) diff --git a/e2e/vue-router/basepath-file-based/tests/setup/global.setup.ts b/e2e/vue-router/basepath-file-based/tests/setup/global.setup.ts new file mode 100644 index 00000000000..3593d10ab90 --- /dev/null +++ b/e2e/vue-router/basepath-file-based/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-router/basepath-file-based/tests/setup/global.teardown.ts b/e2e/vue-router/basepath-file-based/tests/setup/global.teardown.ts new file mode 100644 index 00000000000..62fd79911cc --- /dev/null +++ b/e2e/vue-router/basepath-file-based/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-router/basepath-file-based/tsconfig.json b/e2e/vue-router/basepath-file-based/tsconfig.json new file mode 100644 index 00000000000..3ed4d1e8fca --- /dev/null +++ b/e2e/vue-router/basepath-file-based/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "strict": true, + "esModuleInterop": true, + "jsx": "preserve", + "jsxImportSource": "vue", + "target": "ESNext", + "moduleResolution": "Bundler", + "module": "ESNext", + "skipLibCheck": true, + "resolveJsonModule": true, + "allowJs": true, + "types": ["vite/client"] + }, + "exclude": ["node_modules", "dist"] +} diff --git a/e2e/vue-router/basepath-file-based/vite.config.js b/e2e/vue-router/basepath-file-based/vite.config.js new file mode 100644 index 00000000000..1ee80a8b80d --- /dev/null +++ b/e2e/vue-router/basepath-file-based/vite.config.js @@ -0,0 +1,17 @@ +import { defineConfig } from 'vite' +import vue from '@vitejs/plugin-vue' +import vueJsx from '@vitejs/plugin-vue-jsx' +import { tanstackRouter } from '@tanstack/router-plugin/vite' + +// https://vitejs.dev/config/ +export default defineConfig({ + base: '/app/', + plugins: [ + tanstackRouter({ + target: 'vue', + autoCodeSplitting: true, + }), + vue(), + vueJsx(), + ], +}) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0794615d037..302823f5b2f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -3795,6 +3795,61 @@ 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.1)(lightningcss@1.30.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.8.1)) + e2e/vue-router/basepath-file-based: + dependencies: + '@tailwindcss/postcss': + specifier: ^4.1.15 + version: 4.1.15 + '@tanstack/router-plugin': + specifier: workspace:* + version: link:../../../packages/router-plugin + '@tanstack/vue-query': + specifier: ^5.90.0 + version: 5.92.0(vue@3.5.25(typescript@5.8.3)) + '@tanstack/vue-query-devtools': + specifier: ^6.1.2 + version: 6.1.2(@tanstack/vue-query@5.92.0(vue@3.5.25(typescript@5.8.3)))(vue@3.5.25(typescript@5.8.3)) + '@tanstack/vue-router': + specifier: workspace:* + version: link:../../../packages/vue-router + '@tanstack/vue-router-devtools': + specifier: workspace:* + version: link:../../../packages/vue-router-devtools + postcss: + specifier: ^8.5.1 + version: 8.5.6 + redaxios: + specifier: ^0.5.1 + version: 0.5.1 + tailwindcss: + specifier: ^4.1.17 + version: 4.1.17 + vue: + specifier: ^3.5.16 + version: 3.5.25(typescript@5.8.3) + devDependencies: + '@playwright/test': + specifier: ^1.56.1 + version: 1.56.1 + '@tanstack/router-e2e-utils': + specifier: workspace:^ + version: link:../../e2e-utils + '@vitejs/plugin-vue': + specifier: ^5.2.3 + 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.8.3)) + '@vitejs/plugin-vue-jsx': + specifier: ^4.1.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.8.3)) + typescript: + specifier: ~5.8.3 + version: 5.8.3 + 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) + vue-tsc: + specifier: ^3.1.5 + version: 3.1.5(typescript@5.8.3) + e2e/vue-router/basic: dependencies: '@tailwindcss/postcss':