From df63308aa7ef50359c417bf2c78b28c1b5380ccc Mon Sep 17 00:00:00 2001
From: Birk Skyum <birk.skyum@pm.me>
Date: Tue, 16 Dec 2025 17:53:52 +0100
Subject: [PATCH 1/3] sentry integration vue

---
 e2e/vue-router/generator-cli-only/test-results/.last-run.json | 4 ++++
 1 file changed, 4 insertions(+)
 create mode 100644 e2e/vue-router/generator-cli-only/test-results/.last-run.json

diff --git a/e2e/vue-router/generator-cli-only/test-results/.last-run.json b/e2e/vue-router/generator-cli-only/test-results/.last-run.json
new file mode 100644
index 0000000000..cbcc1fbac1
--- /dev/null
+++ b/e2e/vue-router/generator-cli-only/test-results/.last-run.json
@@ -0,0 +1,4 @@
+{
+  "status": "passed",
+  "failedTests": []
+}
\ No newline at end of file

From c5e5e10cd1f0c6454614c89b82e18c6c09ee6da4 Mon Sep 17 00:00:00 2001
From: Birk Skyum <birk.skyum@pm.me>
Date: Tue, 16 Dec 2025 18:19:23 +0100
Subject: [PATCH 2/3] vue-router-generator-cli-only

---
 e2e/vue-router/generator-cli-only/.gitignore  |  11 +
 e2e/vue-router/generator-cli-only/index.html  |  12 +
 .../generator-cli-only/package.json           |  32 +++
 .../generator-cli-only/playwright.config.ts   |  41 ++++
 .../generator-cli-only/postcss.config.mjs     |   5 +
 .../generator-cli-only/src/main.tsx           |  29 +++
 .../generator-cli-only/src/posts.ts           |  36 +++
 .../generator-cli-only/src/routeTree.gen.ts   | 230 ++++++++++++++++++
 .../generator-cli-only/src/routes/__root.tsx  |  61 +++++
 .../src/routes/_pathlessLayout.tsx            |  16 ++
 .../routes/_pathlessLayout/_nested-layout.tsx |  34 +++
 .../_nested-layout/route-a.tsx                |  11 +
 .../_nested-layout/route-b.tsx                |  11 +
 .../generator-cli-only/src/routes/index.tsx   |  13 +
 .../src/routes/posts.$postId.tsx              |  27 ++
 .../src/routes/posts.index.tsx                |   9 +
 .../src/routes/posts.route.tsx                |  38 +++
 .../generator-cli-only/src/styles.css         |  21 ++
 .../generator-cli-only/tests/app.spec.ts      |  35 +++
 .../tests/setup/global.setup.ts               |   6 +
 .../tests/setup/global.teardown.ts            |   6 +
 .../generator-cli-only/tsconfig.json          |  16 ++
 .../generator-cli-only/tsr.config.json        |   5 +
 .../generator-cli-only/vite.config.js         |  11 +
 pnpm-lock.yaml                                |  49 ++++
 25 files changed, 765 insertions(+)
 create mode 100644 e2e/vue-router/generator-cli-only/.gitignore
 create mode 100644 e2e/vue-router/generator-cli-only/index.html
 create mode 100644 e2e/vue-router/generator-cli-only/package.json
 create mode 100644 e2e/vue-router/generator-cli-only/playwright.config.ts
 create mode 100644 e2e/vue-router/generator-cli-only/postcss.config.mjs
 create mode 100644 e2e/vue-router/generator-cli-only/src/main.tsx
 create mode 100644 e2e/vue-router/generator-cli-only/src/posts.ts
 create mode 100644 e2e/vue-router/generator-cli-only/src/routeTree.gen.ts
 create mode 100644 e2e/vue-router/generator-cli-only/src/routes/__root.tsx
 create mode 100644 e2e/vue-router/generator-cli-only/src/routes/_pathlessLayout.tsx
 create mode 100644 e2e/vue-router/generator-cli-only/src/routes/_pathlessLayout/_nested-layout.tsx
 create mode 100644 e2e/vue-router/generator-cli-only/src/routes/_pathlessLayout/_nested-layout/route-a.tsx
 create mode 100644 e2e/vue-router/generator-cli-only/src/routes/_pathlessLayout/_nested-layout/route-b.tsx
 create mode 100644 e2e/vue-router/generator-cli-only/src/routes/index.tsx
 create mode 100644 e2e/vue-router/generator-cli-only/src/routes/posts.$postId.tsx
 create mode 100644 e2e/vue-router/generator-cli-only/src/routes/posts.index.tsx
 create mode 100644 e2e/vue-router/generator-cli-only/src/routes/posts.route.tsx
 create mode 100644 e2e/vue-router/generator-cli-only/src/styles.css
 create mode 100644 e2e/vue-router/generator-cli-only/tests/app.spec.ts
 create mode 100644 e2e/vue-router/generator-cli-only/tests/setup/global.setup.ts
 create mode 100644 e2e/vue-router/generator-cli-only/tests/setup/global.teardown.ts
 create mode 100644 e2e/vue-router/generator-cli-only/tsconfig.json
 create mode 100644 e2e/vue-router/generator-cli-only/tsr.config.json
 create mode 100644 e2e/vue-router/generator-cli-only/vite.config.js

diff --git a/e2e/vue-router/generator-cli-only/.gitignore b/e2e/vue-router/generator-cli-only/.gitignore
new file mode 100644
index 0000000000..218ef7614b
--- /dev/null
+++ b/e2e/vue-router/generator-cli-only/.gitignore
@@ -0,0 +1,11 @@
+node_modules
+.DS_Store
+dist
+dist-hash
+dist-ssr
+*.local
+
+/test-results/
+/playwright-report/
+/blob-report/
+/playwright/.cache/
\ No newline at end of file
diff --git a/e2e/vue-router/generator-cli-only/index.html b/e2e/vue-router/generator-cli-only/index.html
new file mode 100644
index 0000000000..9b6335c0ac
--- /dev/null
+++ b/e2e/vue-router/generator-cli-only/index.html
@@ -0,0 +1,12 @@
+<!doctype html>
+<html lang="en">
+  <head>
+    <meta charset="UTF-8" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+    <title>Vite App</title>
+  </head>
+  <body>
+    <div id="app"></div>
+    <script type="module" src="/src/main.tsx"></script>
+  </body>
+</html>
diff --git a/e2e/vue-router/generator-cli-only/package.json b/e2e/vue-router/generator-cli-only/package.json
new file mode 100644
index 0000000000..21e5d5bdef
--- /dev/null
+++ b/e2e/vue-router/generator-cli-only/package.json
@@ -0,0 +1,32 @@
+{
+  "name": "tanstack-router-e2e-vue-generator-cli-only",
+  "private": true,
+  "type": "module",
+  "scripts": {
+    "dev": "tsr generate && vite --port 3000",
+    "dev:e2e": "tsr generate && vite",
+    "build": "tsr generate && vite build && tsc --noEmit",
+    "preview": "vite preview",
+    "start": "tsr generate && vite",
+    "test:e2e": "rm -rf port*.txt; playwright test --project=chromium"
+  },
+  "dependencies": {
+    "@tailwindcss/postcss": "^4.1.15",
+    "@tanstack/vue-router": "workspace:^",
+    "@tanstack/vue-router-devtools": "workspace:^",
+    "@tanstack/router-cli": "workspace:^",
+    "postcss": "^8.5.1",
+    "redaxios": "^0.5.1",
+    "vue": "^3.5.16",
+    "tailwindcss": "^4.1.17"
+  },
+  "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/generator-cli-only/playwright.config.ts b/e2e/vue-router/generator-cli-only/playwright.config.ts
new file mode 100644
index 0000000000..c9d9a76ae7
--- /dev/null
+++ b/e2e/vue-router/generator-cli-only/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_EXTERNAL_PORT=${EXTERNAL_PORT} VITE_SERVER_PORT=${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/generator-cli-only/postcss.config.mjs b/e2e/vue-router/generator-cli-only/postcss.config.mjs
new file mode 100644
index 0000000000..a7f73a2d1d
--- /dev/null
+++ b/e2e/vue-router/generator-cli-only/postcss.config.mjs
@@ -0,0 +1,5 @@
+export default {
+  plugins: {
+    '@tailwindcss/postcss': {},
+  },
+}
diff --git a/e2e/vue-router/generator-cli-only/src/main.tsx b/e2e/vue-router/generator-cli-only/src/main.tsx
new file mode 100644
index 0000000000..b2c2639396
--- /dev/null
+++ b/e2e/vue-router/generator-cli-only/src/main.tsx
@@ -0,0 +1,29 @@
+import { RouterProvider, createRouter } from '@tanstack/vue-router'
+import { createApp } from 'vue'
+import { routeTree } from './routeTree.gen'
+import './styles.css'
+
+// Set up a Router instance
+const router = createRouter({
+  routeTree,
+  defaultPreload: 'intent',
+  defaultStaleTime: 5000,
+  scrollRestoration: true,
+})
+
+// Register things for typesafety
+declare module '@tanstack/vue-router' {
+  interface Register {
+    router: typeof router
+  }
+}
+
+const rootElement = document.getElementById('app')!
+
+if (!rootElement.innerHTML) {
+  createApp({
+    setup() {
+      return () => <RouterProvider router={router} />
+    },
+  }).mount('#app')
+}
diff --git a/e2e/vue-router/generator-cli-only/src/posts.ts b/e2e/vue-router/generator-cli-only/src/posts.ts
new file mode 100644
index 0000000000..e859645746
--- /dev/null
+++ b/e2e/vue-router/generator-cli-only/src/posts.ts
@@ -0,0 +1,36 @@
+import axios from 'redaxios'
+
+export class NotFoundError extends Error {}
+
+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 fetchPosts = async () => {
+  console.info('Fetching posts...')
+  return axios
+    .get<Array<PostType>>(`${queryURL}/posts`)
+    .then((r) => r.data.slice(0, 10))
+}
+
+export const fetchPost = async (postId: string) => {
+  console.info(`Fetching post with id ${postId}...`)
+  const post = await axios
+    .get<PostType>(`${queryURL}/posts/${postId}`)
+    .then((r) => r.data)
+
+  // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
+  if (!post) {
+    throw new NotFoundError(`Post with id "${postId}" not found!`)
+  }
+
+  return post
+}
diff --git a/e2e/vue-router/generator-cli-only/src/routeTree.gen.ts b/e2e/vue-router/generator-cli-only/src/routeTree.gen.ts
new file mode 100644
index 0000000000..aabddd8880
--- /dev/null
+++ b/e2e/vue-router/generator-cli-only/src/routeTree.gen.ts
@@ -0,0 +1,230 @@
+/* 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 PathlessLayoutRouteImport } from './routes/_pathlessLayout'
+import { Route as PostsRouteRouteImport } from './routes/posts.route'
+import { Route as IndexRouteImport } from './routes/index'
+import { Route as PostsIndexRouteImport } from './routes/posts.index'
+import { Route as PostsPostIdRouteImport } from './routes/posts.$postId'
+import { Route as PathlessLayoutNestedLayoutRouteImport } from './routes/_pathlessLayout/_nested-layout'
+import { Route as PathlessLayoutNestedLayoutRouteBRouteImport } from './routes/_pathlessLayout/_nested-layout/route-b'
+import { Route as PathlessLayoutNestedLayoutRouteARouteImport } from './routes/_pathlessLayout/_nested-layout/route-a'
+
+const PathlessLayoutRoute = PathlessLayoutRouteImport.update({
+  id: '/_pathlessLayout',
+  getParentRoute: () => rootRouteImport,
+} as any)
+const PostsRouteRoute = PostsRouteRouteImport.update({
+  id: '/posts',
+  path: '/posts',
+  getParentRoute: () => rootRouteImport,
+} as any)
+const IndexRoute = IndexRouteImport.update({
+  id: '/',
+  path: '/',
+  getParentRoute: () => rootRouteImport,
+} as any)
+const PostsIndexRoute = PostsIndexRouteImport.update({
+  id: '/',
+  path: '/',
+  getParentRoute: () => PostsRouteRoute,
+} as any)
+const PostsPostIdRoute = PostsPostIdRouteImport.update({
+  id: '/$postId',
+  path: '/$postId',
+  getParentRoute: () => PostsRouteRoute,
+} as any)
+const PathlessLayoutNestedLayoutRoute =
+  PathlessLayoutNestedLayoutRouteImport.update({
+    id: '/_nested-layout',
+    getParentRoute: () => PathlessLayoutRoute,
+  } as any)
+const PathlessLayoutNestedLayoutRouteBRoute =
+  PathlessLayoutNestedLayoutRouteBRouteImport.update({
+    id: '/route-b',
+    path: '/route-b',
+    getParentRoute: () => PathlessLayoutNestedLayoutRoute,
+  } as any)
+const PathlessLayoutNestedLayoutRouteARoute =
+  PathlessLayoutNestedLayoutRouteARouteImport.update({
+    id: '/route-a',
+    path: '/route-a',
+    getParentRoute: () => PathlessLayoutNestedLayoutRoute,
+  } as any)
+
+export interface FileRoutesByFullPath {
+  '/': typeof IndexRoute
+  '/posts': typeof PostsRouteRouteWithChildren
+  '/posts/$postId': typeof PostsPostIdRoute
+  '/posts/': typeof PostsIndexRoute
+  '/route-a': typeof PathlessLayoutNestedLayoutRouteARoute
+  '/route-b': typeof PathlessLayoutNestedLayoutRouteBRoute
+}
+export interface FileRoutesByTo {
+  '/': typeof IndexRoute
+  '/posts/$postId': typeof PostsPostIdRoute
+  '/posts': typeof PostsIndexRoute
+  '/route-a': typeof PathlessLayoutNestedLayoutRouteARoute
+  '/route-b': typeof PathlessLayoutNestedLayoutRouteBRoute
+}
+export interface FileRoutesById {
+  __root__: typeof rootRouteImport
+  '/': typeof IndexRoute
+  '/posts': typeof PostsRouteRouteWithChildren
+  '/_pathlessLayout': typeof PathlessLayoutRouteWithChildren
+  '/_pathlessLayout/_nested-layout': typeof PathlessLayoutNestedLayoutRouteWithChildren
+  '/posts/$postId': typeof PostsPostIdRoute
+  '/posts/': typeof PostsIndexRoute
+  '/_pathlessLayout/_nested-layout/route-a': typeof PathlessLayoutNestedLayoutRouteARoute
+  '/_pathlessLayout/_nested-layout/route-b': typeof PathlessLayoutNestedLayoutRouteBRoute
+}
+export interface FileRouteTypes {
+  fileRoutesByFullPath: FileRoutesByFullPath
+  fullPaths:
+    | '/'
+    | '/posts'
+    | '/posts/$postId'
+    | '/posts/'
+    | '/route-a'
+    | '/route-b'
+  fileRoutesByTo: FileRoutesByTo
+  to: '/' | '/posts/$postId' | '/posts' | '/route-a' | '/route-b'
+  id:
+    | '__root__'
+    | '/'
+    | '/posts'
+    | '/_pathlessLayout'
+    | '/_pathlessLayout/_nested-layout'
+    | '/posts/$postId'
+    | '/posts/'
+    | '/_pathlessLayout/_nested-layout/route-a'
+    | '/_pathlessLayout/_nested-layout/route-b'
+  fileRoutesById: FileRoutesById
+}
+export interface RootRouteChildren {
+  IndexRoute: typeof IndexRoute
+  PostsRouteRoute: typeof PostsRouteRouteWithChildren
+  PathlessLayoutRoute: typeof PathlessLayoutRouteWithChildren
+}
+
+declare module '@tanstack/vue-router' {
+  interface FileRoutesByPath {
+    '/_pathlessLayout': {
+      id: '/_pathlessLayout'
+      path: ''
+      fullPath: ''
+      preLoaderRoute: typeof PathlessLayoutRouteImport
+      parentRoute: typeof rootRouteImport
+    }
+    '/posts': {
+      id: '/posts'
+      path: '/posts'
+      fullPath: '/posts'
+      preLoaderRoute: typeof PostsRouteRouteImport
+      parentRoute: typeof rootRouteImport
+    }
+    '/': {
+      id: '/'
+      path: '/'
+      fullPath: '/'
+      preLoaderRoute: typeof IndexRouteImport
+      parentRoute: typeof rootRouteImport
+    }
+    '/posts/': {
+      id: '/posts/'
+      path: '/'
+      fullPath: '/posts/'
+      preLoaderRoute: typeof PostsIndexRouteImport
+      parentRoute: typeof PostsRouteRoute
+    }
+    '/posts/$postId': {
+      id: '/posts/$postId'
+      path: '/$postId'
+      fullPath: '/posts/$postId'
+      preLoaderRoute: typeof PostsPostIdRouteImport
+      parentRoute: typeof PostsRouteRoute
+    }
+    '/_pathlessLayout/_nested-layout': {
+      id: '/_pathlessLayout/_nested-layout'
+      path: ''
+      fullPath: ''
+      preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteImport
+      parentRoute: typeof PathlessLayoutRoute
+    }
+    '/_pathlessLayout/_nested-layout/route-b': {
+      id: '/_pathlessLayout/_nested-layout/route-b'
+      path: '/route-b'
+      fullPath: '/route-b'
+      preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteBRouteImport
+      parentRoute: typeof PathlessLayoutNestedLayoutRoute
+    }
+    '/_pathlessLayout/_nested-layout/route-a': {
+      id: '/_pathlessLayout/_nested-layout/route-a'
+      path: '/route-a'
+      fullPath: '/route-a'
+      preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteARouteImport
+      parentRoute: typeof PathlessLayoutNestedLayoutRoute
+    }
+  }
+}
+
+interface PostsRouteRouteChildren {
+  PostsPostIdRoute: typeof PostsPostIdRoute
+  PostsIndexRoute: typeof PostsIndexRoute
+}
+
+const PostsRouteRouteChildren: PostsRouteRouteChildren = {
+  PostsPostIdRoute: PostsPostIdRoute,
+  PostsIndexRoute: PostsIndexRoute,
+}
+
+const PostsRouteRouteWithChildren = PostsRouteRoute._addFileChildren(
+  PostsRouteRouteChildren,
+)
+
+interface PathlessLayoutNestedLayoutRouteChildren {
+  PathlessLayoutNestedLayoutRouteARoute: typeof PathlessLayoutNestedLayoutRouteARoute
+  PathlessLayoutNestedLayoutRouteBRoute: typeof PathlessLayoutNestedLayoutRouteBRoute
+}
+
+const PathlessLayoutNestedLayoutRouteChildren: PathlessLayoutNestedLayoutRouteChildren =
+  {
+    PathlessLayoutNestedLayoutRouteARoute:
+      PathlessLayoutNestedLayoutRouteARoute,
+    PathlessLayoutNestedLayoutRouteBRoute:
+      PathlessLayoutNestedLayoutRouteBRoute,
+  }
+
+const PathlessLayoutNestedLayoutRouteWithChildren =
+  PathlessLayoutNestedLayoutRoute._addFileChildren(
+    PathlessLayoutNestedLayoutRouteChildren,
+  )
+
+interface PathlessLayoutRouteChildren {
+  PathlessLayoutNestedLayoutRoute: typeof PathlessLayoutNestedLayoutRouteWithChildren
+}
+
+const PathlessLayoutRouteChildren: PathlessLayoutRouteChildren = {
+  PathlessLayoutNestedLayoutRoute: PathlessLayoutNestedLayoutRouteWithChildren,
+}
+
+const PathlessLayoutRouteWithChildren = PathlessLayoutRoute._addFileChildren(
+  PathlessLayoutRouteChildren,
+)
+
+const rootRouteChildren: RootRouteChildren = {
+  IndexRoute: IndexRoute,
+  PostsRouteRoute: PostsRouteRouteWithChildren,
+  PathlessLayoutRoute: PathlessLayoutRouteWithChildren,
+}
+export const routeTree = rootRouteImport
+  ._addFileChildren(rootRouteChildren)
+  ._addFileTypes<FileRouteTypes>()
diff --git a/e2e/vue-router/generator-cli-only/src/routes/__root.tsx b/e2e/vue-router/generator-cli-only/src/routes/__root.tsx
new file mode 100644
index 0000000000..8ee58439b6
--- /dev/null
+++ b/e2e/vue-router/generator-cli-only/src/routes/__root.tsx
@@ -0,0 +1,61 @@
+import { Link, Outlet, createRootRoute } from '@tanstack/vue-router'
+import { TanStackRouterDevtools } from '@tanstack/vue-router-devtools'
+
+export const Route = createRootRoute({
+  component: RootComponent,
+  notFoundComponent: () => {
+    return (
+      <div>
+        <p>This is the notFoundComponent configured on root route</p>
+        <Link to="/">Start Over</Link>
+      </div>
+    )
+  },
+})
+
+function RootComponent() {
+  return (
+    <>
+      <div class="p-2 flex gap-2 text-lg border-b">
+        <Link
+          to="/"
+          activeProps={{
+            class: 'font-bold',
+          }}
+          activeOptions={{ exact: true }}
+        >
+          Home
+        </Link>{' '}
+        <Link
+          to="/posts"
+          activeProps={{
+            class: 'font-bold',
+          }}
+        >
+          Posts
+        </Link>{' '}
+        <Link
+          to="/route-a"
+          activeProps={{
+            class: 'font-bold',
+          }}
+        >
+          Pathless Layout
+        </Link>{' '}
+        <Link
+          // @ts-expect-error
+          to="/this-route-does-not-exist"
+          activeProps={{
+            class: 'font-bold',
+          }}
+        >
+          This Route Does Not Exist
+        </Link>
+      </div>
+      <hr />
+      <Outlet />
+      {/* Start rendering router matches */}
+      <TanStackRouterDevtools position="bottom-right" />
+    </>
+  )
+}
diff --git a/e2e/vue-router/generator-cli-only/src/routes/_pathlessLayout.tsx b/e2e/vue-router/generator-cli-only/src/routes/_pathlessLayout.tsx
new file mode 100644
index 0000000000..f8b9f0133b
--- /dev/null
+++ b/e2e/vue-router/generator-cli-only/src/routes/_pathlessLayout.tsx
@@ -0,0 +1,16 @@
+import { Outlet, createFileRoute } from '@tanstack/vue-router'
+
+export const Route = createFileRoute('/_pathlessLayout')({
+  component: LayoutComponent,
+})
+
+function LayoutComponent() {
+  return (
+    <div class="p-2">
+      <div class="border-b">I'm a pathless layout</div>
+      <div>
+        <Outlet />
+      </div>
+    </div>
+  )
+}
diff --git a/e2e/vue-router/generator-cli-only/src/routes/_pathlessLayout/_nested-layout.tsx b/e2e/vue-router/generator-cli-only/src/routes/_pathlessLayout/_nested-layout.tsx
new file mode 100644
index 0000000000..7c127b5fda
--- /dev/null
+++ b/e2e/vue-router/generator-cli-only/src/routes/_pathlessLayout/_nested-layout.tsx
@@ -0,0 +1,34 @@
+import { Link, Outlet, createFileRoute } from '@tanstack/vue-router'
+
+export const Route = createFileRoute('/_pathlessLayout/_nested-layout')({
+  component: LayoutComponent,
+})
+
+function LayoutComponent() {
+  return (
+    <div>
+      <div>I'm a nested pathless layout</div>
+      <div class="flex gap-2 border-b">
+        <Link
+          to="/route-a"
+          activeProps={{
+            class: 'font-bold',
+          }}
+        >
+          Go to route A
+        </Link>
+        <Link
+          to="/route-b"
+          activeProps={{
+            class: 'font-bold',
+          }}
+        >
+          Go to route B
+        </Link>
+      </div>
+      <div>
+        <Outlet />
+      </div>
+    </div>
+  )
+}
diff --git a/e2e/vue-router/generator-cli-only/src/routes/_pathlessLayout/_nested-layout/route-a.tsx b/e2e/vue-router/generator-cli-only/src/routes/_pathlessLayout/_nested-layout/route-a.tsx
new file mode 100644
index 0000000000..ca7efcc016
--- /dev/null
+++ b/e2e/vue-router/generator-cli-only/src/routes/_pathlessLayout/_nested-layout/route-a.tsx
@@ -0,0 +1,11 @@
+import { createFileRoute } from '@tanstack/vue-router'
+
+export const Route = createFileRoute('/_pathlessLayout/_nested-layout/route-a')(
+  {
+    component: LayoutAComponent,
+  },
+)
+
+function LayoutAComponent() {
+  return <div>I'm layout A!</div>
+}
diff --git a/e2e/vue-router/generator-cli-only/src/routes/_pathlessLayout/_nested-layout/route-b.tsx b/e2e/vue-router/generator-cli-only/src/routes/_pathlessLayout/_nested-layout/route-b.tsx
new file mode 100644
index 0000000000..f9cbed42f5
--- /dev/null
+++ b/e2e/vue-router/generator-cli-only/src/routes/_pathlessLayout/_nested-layout/route-b.tsx
@@ -0,0 +1,11 @@
+import { createFileRoute } from '@tanstack/vue-router'
+
+export const Route = createFileRoute('/_pathlessLayout/_nested-layout/route-b')(
+  {
+    component: LayoutBComponent,
+  },
+)
+
+function LayoutBComponent() {
+  return <div>I'm layout B!</div>
+}
diff --git a/e2e/vue-router/generator-cli-only/src/routes/index.tsx b/e2e/vue-router/generator-cli-only/src/routes/index.tsx
new file mode 100644
index 0000000000..9ba2b6a455
--- /dev/null
+++ b/e2e/vue-router/generator-cli-only/src/routes/index.tsx
@@ -0,0 +1,13 @@
+import { createFileRoute } from '@tanstack/vue-router'
+
+export const Route = createFileRoute('/')({
+  component: Home,
+})
+
+function Home() {
+  return (
+    <div class="p-2">
+      <h3>Welcome Home!</h3>
+    </div>
+  )
+}
diff --git a/e2e/vue-router/generator-cli-only/src/routes/posts.$postId.tsx b/e2e/vue-router/generator-cli-only/src/routes/posts.$postId.tsx
new file mode 100644
index 0000000000..f4f1b04f88
--- /dev/null
+++ b/e2e/vue-router/generator-cli-only/src/routes/posts.$postId.tsx
@@ -0,0 +1,27 @@
+import { ErrorComponent, createFileRoute } from '@tanstack/vue-router'
+import { fetchPost } from '../posts'
+import type { ErrorComponentProps } from '@tanstack/vue-router'
+
+export const Route = createFileRoute('/posts/$postId')({
+  loader: async ({ params: { postId } }) => fetchPost(postId),
+  errorComponent: PostErrorComponent,
+  notFoundComponent: () => {
+    return <p>Post not found</p>
+  },
+  component: PostComponent,
+})
+
+export function PostErrorComponent({ error }: ErrorComponentProps) {
+  return <ErrorComponent error={error} />
+}
+
+function PostComponent() {
+  const post = Route.useLoaderData()
+
+  return (
+    <div class="space-y-2">
+      <h4 class="text-xl font-bold underline">{post.value.title}</h4>
+      <div class="text-sm">{post.value.body}</div>
+    </div>
+  )
+}
diff --git a/e2e/vue-router/generator-cli-only/src/routes/posts.index.tsx b/e2e/vue-router/generator-cli-only/src/routes/posts.index.tsx
new file mode 100644
index 0000000000..a48cbbf215
--- /dev/null
+++ b/e2e/vue-router/generator-cli-only/src/routes/posts.index.tsx
@@ -0,0 +1,9 @@
+import { createFileRoute } from '@tanstack/vue-router'
+
+export const Route = createFileRoute('/posts/')({
+  component: PostsIndexComponent,
+})
+
+function PostsIndexComponent() {
+  return <div>Select a post.</div>
+}
diff --git a/e2e/vue-router/generator-cli-only/src/routes/posts.route.tsx b/e2e/vue-router/generator-cli-only/src/routes/posts.route.tsx
new file mode 100644
index 0000000000..6bf3a9d8ff
--- /dev/null
+++ b/e2e/vue-router/generator-cli-only/src/routes/posts.route.tsx
@@ -0,0 +1,38 @@
+import { Link, Outlet, createFileRoute } from '@tanstack/vue-router'
+import { fetchPosts } from '../posts'
+
+export const Route = createFileRoute('/posts')({
+  loader: fetchPosts,
+  component: PostsLayoutComponent,
+})
+
+function PostsLayoutComponent() {
+  const posts = Route.useLoaderData()
+
+  return (
+    <div class="p-2 flex gap-2">
+      <ul class="list-disc pl-4">
+        {[...posts.value, { id: 'i-do-not-exist', title: 'Non-existent Post' }].map(
+          (post) => {
+            return (
+              <li class="whitespace-nowrap">
+                <Link
+                  to="/posts/$postId"
+                  params={{
+                    postId: post.id,
+                  }}
+                  class="block py-1 text-blue-600 hover:opacity-75"
+                  activeProps={{ class: 'font-bold underline' }}
+                >
+                  <div>{post.title.substring(0, 20)}</div>
+                </Link>
+              </li>
+            )
+          },
+        )}
+      </ul>
+      <hr />
+      <Outlet />
+    </div>
+  )
+}
diff --git a/e2e/vue-router/generator-cli-only/src/styles.css b/e2e/vue-router/generator-cli-only/src/styles.css
new file mode 100644
index 0000000000..37a1064738
--- /dev/null
+++ b/e2e/vue-router/generator-cli-only/src/styles.css
@@ -0,0 +1,21 @@
+@import 'tailwindcss';
+
+@layer base {
+  *,
+  ::after,
+  ::before,
+  ::backdrop,
+  ::file-selector-button {
+    border-color: var(--color-gray-200, currentcolor);
+  }
+}
+
+html {
+  color-scheme: light dark;
+}
+* {
+  @apply border-gray-200 dark:border-gray-800;
+}
+body {
+  @apply bg-gray-50 text-gray-950 dark:bg-gray-900 dark:text-gray-200;
+}
diff --git a/e2e/vue-router/generator-cli-only/tests/app.spec.ts b/e2e/vue-router/generator-cli-only/tests/app.spec.ts
new file mode 100644
index 0000000000..5a83a314f2
--- /dev/null
+++ b/e2e/vue-router/generator-cli-only/tests/app.spec.ts
@@ -0,0 +1,35 @@
+import { expect, test } from '@playwright/test'
+
+test.beforeEach(async ({ page }) => {
+  await page.goto('/')
+})
+
+test('Navigating to a post page', async ({ page }) => {
+  await page.getByRole('link', { name: 'Posts', exact: true }).click()
+  await page.getByRole('link', { name: 'sunt aut facere repe' }).click()
+  await expect(page.getByRole('heading')).toContainText('sunt aut facere')
+})
+
+test('Navigating nested pathless layouts', async ({ page }) => {
+  await page.getByRole('link', { name: 'Pathless Layout', exact: true }).click()
+
+  await expect(page.locator('#app')).toContainText("I'm a pathless layout")
+  await expect(page.locator('#app')).toContainText(
+    "I'm a nested pathless layout",
+  )
+
+  await page.getByRole('link', { name: 'Go to route A' }).click()
+  await expect(page.locator('#app')).toContainText("I'm layout A!")
+
+  await page.getByRole('link', { name: 'Go to route B' }).click()
+  await expect(page.locator('#app')).toContainText("I'm layout B!")
+})
+
+test('Navigating to a not-found route', async ({ page }) => {
+  await page.getByRole('link', { name: 'This Route Does Not Exist' }).click()
+  await expect(page.getByRole('paragraph')).toContainText(
+    'This is the notFoundComponent configured on root route',
+  )
+  await page.getByRole('link', { name: 'Start Over' }).click()
+  await expect(page.getByRole('heading')).toContainText('Welcome Home!')
+})
diff --git a/e2e/vue-router/generator-cli-only/tests/setup/global.setup.ts b/e2e/vue-router/generator-cli-only/tests/setup/global.setup.ts
new file mode 100644
index 0000000000..3593d10ab9
--- /dev/null
+++ b/e2e/vue-router/generator-cli-only/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/generator-cli-only/tests/setup/global.teardown.ts b/e2e/vue-router/generator-cli-only/tests/setup/global.teardown.ts
new file mode 100644
index 0000000000..62fd79911c
--- /dev/null
+++ b/e2e/vue-router/generator-cli-only/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/generator-cli-only/tsconfig.json b/e2e/vue-router/generator-cli-only/tsconfig.json
new file mode 100644
index 0000000000..0d93a5d5a8
--- /dev/null
+++ b/e2e/vue-router/generator-cli-only/tsconfig.json
@@ -0,0 +1,16 @@
+{
+  "compilerOptions": {
+    "strict": true,
+    "esModuleInterop": true,
+    "jsx": "preserve",
+    "jsxImportSource": "vue",
+    "target": "ESNext",
+    "moduleResolution": "Bundler",
+    "module": "ESNext",
+    "resolveJsonModule": true,
+    "allowJs": true,
+    "skipLibCheck": true,
+    "types": ["vite/client"]
+  },
+  "exclude": ["node_modules", "dist"]
+}
diff --git a/e2e/vue-router/generator-cli-only/tsr.config.json b/e2e/vue-router/generator-cli-only/tsr.config.json
new file mode 100644
index 0000000000..8bbc6bfa5a
--- /dev/null
+++ b/e2e/vue-router/generator-cli-only/tsr.config.json
@@ -0,0 +1,5 @@
+{
+  "routesDirectory": "./src/routes",
+  "generatedRouteTree": "./src/routeTree.gen.ts",
+  "target": "vue"
+}
diff --git a/e2e/vue-router/generator-cli-only/vite.config.js b/e2e/vue-router/generator-cli-only/vite.config.js
new file mode 100644
index 0000000000..de9cd2a82a
--- /dev/null
+++ b/e2e/vue-router/generator-cli-only/vite.config.js
@@ -0,0 +1,11 @@
+import { defineConfig } from 'vite'
+import vue from '@vitejs/plugin-vue'
+import vueJsx from '@vitejs/plugin-vue-jsx'
+
+// https://vitejs.dev/config/
+export default defineConfig({
+  plugins: [
+    vue(),
+    vueJsx(),
+  ],
+})
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index ea9bb54e8b..2c1bbb8d9a 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -4244,6 +4244,55 @@ importers:
         specifier: ^3.1.5
         version: 3.1.5(typescript@5.8.3)
 
+  e2e/vue-router/generator-cli-only:
+    dependencies:
+      '@tailwindcss/postcss':
+        specifier: ^4.1.15
+        version: 4.1.15
+      '@tanstack/router-cli':
+        specifier: workspace:*
+        version: link:../../../packages/router-cli
+      '@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/sentry-integration:
     dependencies:
       '@sentry/tracing':

From 74ff5830aa45395cf4b63ee1c26528025f751f2f Mon Sep 17 00:00:00 2001
From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com>
Date: Tue, 16 Dec 2025 17:21:36 +0000
Subject: [PATCH 3/3] ci: apply automated fixes

---
 .../src/routes/posts.route.tsx                | 37 ++++++++++---------
 .../generator-cli-only/vite.config.js         |  5 +--
 2 files changed, 20 insertions(+), 22 deletions(-)

diff --git a/e2e/vue-router/generator-cli-only/src/routes/posts.route.tsx b/e2e/vue-router/generator-cli-only/src/routes/posts.route.tsx
index 6bf3a9d8ff..eaaa35fd5c 100644
--- a/e2e/vue-router/generator-cli-only/src/routes/posts.route.tsx
+++ b/e2e/vue-router/generator-cli-only/src/routes/posts.route.tsx
@@ -12,24 +12,25 @@ function PostsLayoutComponent() {
   return (
     <div class="p-2 flex gap-2">
       <ul class="list-disc pl-4">
-        {[...posts.value, { id: 'i-do-not-exist', title: 'Non-existent Post' }].map(
-          (post) => {
-            return (
-              <li class="whitespace-nowrap">
-                <Link
-                  to="/posts/$postId"
-                  params={{
-                    postId: post.id,
-                  }}
-                  class="block py-1 text-blue-600 hover:opacity-75"
-                  activeProps={{ class: 'font-bold underline' }}
-                >
-                  <div>{post.title.substring(0, 20)}</div>
-                </Link>
-              </li>
-            )
-          },
-        )}
+        {[
+          ...posts.value,
+          { id: 'i-do-not-exist', title: 'Non-existent Post' },
+        ].map((post) => {
+          return (
+            <li class="whitespace-nowrap">
+              <Link
+                to="/posts/$postId"
+                params={{
+                  postId: post.id,
+                }}
+                class="block py-1 text-blue-600 hover:opacity-75"
+                activeProps={{ class: 'font-bold underline' }}
+              >
+                <div>{post.title.substring(0, 20)}</div>
+              </Link>
+            </li>
+          )
+        })}
       </ul>
       <hr />
       <Outlet />
diff --git a/e2e/vue-router/generator-cli-only/vite.config.js b/e2e/vue-router/generator-cli-only/vite.config.js
index de9cd2a82a..33d651a84c 100644
--- a/e2e/vue-router/generator-cli-only/vite.config.js
+++ b/e2e/vue-router/generator-cli-only/vite.config.js
@@ -4,8 +4,5 @@ import vueJsx from '@vitejs/plugin-vue-jsx'
 
 // https://vitejs.dev/config/
 export default defineConfig({
-  plugins: [
-    vue(),
-    vueJsx(),
-  ],
+  plugins: [vue(), vueJsx()],
 })
