From caae48cf3ae843a30f527fc81d66d52f4973cc80 Mon Sep 17 00:00:00 2001
From: Logan Anderson <43075109+logan-anderson@users.noreply.github.com>
Date: Thu, 24 Oct 2024 00:00:54 -0400
Subject: [PATCH] refactor(start): update app directory handling in
defineConfig function (#2617)
Co-authored-by: Sean Cassiere <33615041+SeanCassiere@users.noreply.github.com>
---
e2e/start/basic-tsr-config/.gitignore | 24 +++++
e2e/start/basic-tsr-config/.prettierignore | 4 +
e2e/start/basic-tsr-config/README.md | 72 +++++++++++++++
e2e/start/basic-tsr-config/app.config.ts | 8 ++
e2e/start/basic-tsr-config/package.json | 25 ++++++
.../basic-tsr-config/playwright.config.ts | 30 +++++++
e2e/start/basic-tsr-config/src/app/client.tsx | 9 ++
.../basic-tsr-config/src/app/routeTree.gen.ts | 90 +++++++++++++++++++
e2e/start/basic-tsr-config/src/app/router.tsx | 17 ++++
.../src/app/routes/__root.tsx | 44 +++++++++
.../basic-tsr-config/src/app/routes/index.tsx | 35 ++++++++
e2e/start/basic-tsr-config/src/app/ssr.tsx | 14 +++
e2e/start/basic-tsr-config/tests/app.spec.ts | 8 ++
e2e/start/basic-tsr-config/tsconfig.json | 22 +++++
packages/start/src/config/index.ts | 29 +++---
pnpm-lock.yaml | 31 +++++++
16 files changed, 450 insertions(+), 12 deletions(-)
create mode 100644 e2e/start/basic-tsr-config/.gitignore
create mode 100644 e2e/start/basic-tsr-config/.prettierignore
create mode 100644 e2e/start/basic-tsr-config/README.md
create mode 100644 e2e/start/basic-tsr-config/app.config.ts
create mode 100644 e2e/start/basic-tsr-config/package.json
create mode 100644 e2e/start/basic-tsr-config/playwright.config.ts
create mode 100644 e2e/start/basic-tsr-config/src/app/client.tsx
create mode 100644 e2e/start/basic-tsr-config/src/app/routeTree.gen.ts
create mode 100644 e2e/start/basic-tsr-config/src/app/router.tsx
create mode 100644 e2e/start/basic-tsr-config/src/app/routes/__root.tsx
create mode 100644 e2e/start/basic-tsr-config/src/app/routes/index.tsx
create mode 100644 e2e/start/basic-tsr-config/src/app/ssr.tsx
create mode 100644 e2e/start/basic-tsr-config/tests/app.spec.ts
create mode 100644 e2e/start/basic-tsr-config/tsconfig.json
diff --git a/e2e/start/basic-tsr-config/.gitignore b/e2e/start/basic-tsr-config/.gitignore
new file mode 100644
index 0000000000..2b76174be5
--- /dev/null
+++ b/e2e/start/basic-tsr-config/.gitignore
@@ -0,0 +1,24 @@
+node_modules
+package-lock.json
+yarn.lock
+
+.DS_Store
+.cache
+.env
+.vercel
+.output
+.vinxi
+
+/build/
+/api/
+/server/build
+/public/build
+.vinxi
+# Sentry Config File
+.env.sentry-build-plugin
+/test-results/
+/playwright-report/
+/blob-report/
+/playwright/.cache/
+
+count.txt
diff --git a/e2e/start/basic-tsr-config/.prettierignore b/e2e/start/basic-tsr-config/.prettierignore
new file mode 100644
index 0000000000..2be5eaa6ec
--- /dev/null
+++ b/e2e/start/basic-tsr-config/.prettierignore
@@ -0,0 +1,4 @@
+**/build
+**/public
+pnpm-lock.yaml
+routeTree.gen.ts
\ No newline at end of file
diff --git a/e2e/start/basic-tsr-config/README.md b/e2e/start/basic-tsr-config/README.md
new file mode 100644
index 0000000000..eb580a5bf8
--- /dev/null
+++ b/e2e/start/basic-tsr-config/README.md
@@ -0,0 +1,72 @@
+# Welcome to TanStack.com!
+
+This site is built with TanStack Router!
+
+- [TanStack Router Docs](https://tanstack.com/router)
+
+It's deployed automagically with Vercel!
+
+- [Vercel](https://vercel.com/)
+
+## Development
+
+From your terminal:
+
+```sh
+pnpm install
+pnpm dev
+```
+
+This starts your app in development mode, rebuilding assets on file changes.
+
+## Editing and previewing the docs of TanStack projects locally
+
+The documentations for all TanStack projects except for `React Charts` are hosted on [https://tanstack.com](https://tanstack.com), powered by this TanStack Router app.
+In production, the markdown doc pages are fetched from the GitHub repos of the projects, but in development they are read from the local file system.
+
+Follow these steps if you want to edit the doc pages of a project (in these steps we'll assume it's [`TanStack/form`](https://github.com/tanstack/form)) and preview them locally :
+
+1. Create a new directory called `tanstack`.
+
+```sh
+mkdir tanstack
+```
+
+2. Enter the directory and clone this repo and the repo of the project there.
+
+```sh
+cd tanstack
+git clone git@github.com:TanStack/tanstack.com.git
+git clone git@github.com:TanStack/form.git
+```
+
+> [!NOTE]
+> Your `tanstack` directory should look like this:
+>
+> ```
+> tanstack/
+> |
+> +-- form/
+> |
+> +-- tanstack.com/
+> ```
+
+> [!WARNING]
+> Make sure the name of the directory in your local file system matches the name of the project's repo. For example, `tanstack/form` must be cloned into `form` (this is the default) instead of `some-other-name`, because that way, the doc pages won't be found.
+
+3. Enter the `tanstack/tanstack.com` directory, install the dependencies and run the app in dev mode:
+
+```sh
+cd tanstack.com
+pnpm i
+# The app will run on https://localhost:3000 by default
+pnpm dev
+```
+
+4. Now you can visit http://localhost:3000/form/latest/docs/overview in the browser and see the changes you make in `tanstack/form/docs`.
+
+> [!NOTE]
+> The updated pages need to be manually reloaded in the browser.
+
+> [!WARNING]
+> You will need to update the `docs/config.json` file (in the project's repo) if you add a new doc page!
diff --git a/e2e/start/basic-tsr-config/app.config.ts b/e2e/start/basic-tsr-config/app.config.ts
new file mode 100644
index 0000000000..257f60c11b
--- /dev/null
+++ b/e2e/start/basic-tsr-config/app.config.ts
@@ -0,0 +1,8 @@
+// app.config.ts
+import { defineConfig } from '@tanstack/start/config'
+
+export default defineConfig({
+ tsr: {
+ appDirectory: './src/app',
+ },
+})
diff --git a/e2e/start/basic-tsr-config/package.json b/e2e/start/basic-tsr-config/package.json
new file mode 100644
index 0000000000..571ad8d1ec
--- /dev/null
+++ b/e2e/start/basic-tsr-config/package.json
@@ -0,0 +1,25 @@
+{
+ "name": "tanstack-start-e2e-basic-tsr-config",
+ "private": true,
+ "sideEffects": false,
+ "type": "module",
+ "scripts": {
+ "dev": "vinxi dev",
+ "build": "vinxi build",
+ "start": "vinxi start",
+ "test:e2e": "playwright test --project=chromium"
+ },
+ "dependencies": {
+ "@tanstack/react-router": "^1.75.0",
+ "@tanstack/start": "^1.76.0",
+ "react": "^18.3.1",
+ "react-dom": "^18.3.1",
+ "vinxi": "0.4.3"
+ },
+ "devDependencies": {
+ "@types/node": "^22.5.4",
+ "@types/react": "^18.2.65",
+ "@types/react-dom": "^18.2.21",
+ "typescript": "^5.6.2"
+ }
+}
diff --git a/e2e/start/basic-tsr-config/playwright.config.ts b/e2e/start/basic-tsr-config/playwright.config.ts
new file mode 100644
index 0000000000..d075e822af
--- /dev/null
+++ b/e2e/start/basic-tsr-config/playwright.config.ts
@@ -0,0 +1,30 @@
+import { defineConfig, devices } from '@playwright/test'
+
+/**
+ * See https://playwright.dev/docs/test-configuration.
+ */
+export default defineConfig({
+ testDir: './tests',
+
+ reporter: [['line']],
+
+ use: {
+ /* Base URL to use in actions like `await page.goto('/')`. */
+ baseURL: 'http://localhost:3000/',
+ },
+
+ webServer: {
+ // TODO: build && start seems broken, use that if it's working
+ command: 'pnpm run dev',
+ url: 'http://localhost:3000',
+ reuseExistingServer: !process.env.CI,
+ stdout: 'pipe',
+ },
+
+ projects: [
+ {
+ name: 'chromium',
+ use: { ...devices['Desktop Chrome'] },
+ },
+ ],
+})
diff --git a/e2e/start/basic-tsr-config/src/app/client.tsx b/e2e/start/basic-tsr-config/src/app/client.tsx
new file mode 100644
index 0000000000..7f70815af8
--- /dev/null
+++ b/e2e/start/basic-tsr-config/src/app/client.tsx
@@ -0,0 +1,9 @@
+// app/client.tsx
+///
+import { hydrateRoot } from 'react-dom/client'
+import { StartClient } from '@tanstack/start'
+import { createRouter } from './router'
+
+const router = createRouter()
+
+hydrateRoot(document.getElementById('root')!, )
diff --git a/e2e/start/basic-tsr-config/src/app/routeTree.gen.ts b/e2e/start/basic-tsr-config/src/app/routeTree.gen.ts
new file mode 100644
index 0000000000..e61965e7f5
--- /dev/null
+++ b/e2e/start/basic-tsr-config/src/app/routeTree.gen.ts
@@ -0,0 +1,90 @@
+/* prettier-ignore-start */
+
+/* eslint-disable */
+
+// @ts-nocheck
+
+// noinspection JSUnusedGlobalSymbols
+
+// This file is auto-generated by TanStack Router
+
+// Import Routes
+
+import { Route as rootRoute } from './routes/__root'
+import { Route as IndexImport } from './routes/index'
+
+// Create/Update Routes
+
+const IndexRoute = IndexImport.update({
+ id: '/',
+ path: '/',
+ getParentRoute: () => rootRoute,
+} as any)
+
+// Populate the FileRoutesByPath interface
+
+declare module '@tanstack/react-router' {
+ interface FileRoutesByPath {
+ '/': {
+ id: '/'
+ path: '/'
+ fullPath: '/'
+ preLoaderRoute: typeof IndexImport
+ parentRoute: typeof rootRoute
+ }
+ }
+}
+
+// Create and export the route tree
+
+export interface FileRoutesByFullPath {
+ '/': typeof IndexRoute
+}
+
+export interface FileRoutesByTo {
+ '/': typeof IndexRoute
+}
+
+export interface FileRoutesById {
+ __root__: typeof rootRoute
+ '/': typeof IndexRoute
+}
+
+export interface FileRouteTypes {
+ fileRoutesByFullPath: FileRoutesByFullPath
+ fullPaths: '/'
+ fileRoutesByTo: FileRoutesByTo
+ to: '/'
+ id: '__root__' | '/'
+ fileRoutesById: FileRoutesById
+}
+
+export interface RootRouteChildren {
+ IndexRoute: typeof IndexRoute
+}
+
+const rootRouteChildren: RootRouteChildren = {
+ IndexRoute: IndexRoute,
+}
+
+export const routeTree = rootRoute
+ ._addFileChildren(rootRouteChildren)
+ ._addFileTypes()
+
+/* prettier-ignore-end */
+
+/* ROUTE_MANIFEST_START
+{
+ "routes": {
+ "__root__": {
+ "filePath": "__root.tsx",
+ "children": [
+ "/"
+ ]
+ },
+ "/": {
+ "filePath": "index.tsx"
+ }
+ }
+}
+ROUTE_MANIFEST_END */
diff --git a/e2e/start/basic-tsr-config/src/app/router.tsx b/e2e/start/basic-tsr-config/src/app/router.tsx
new file mode 100644
index 0000000000..dc640c5f4c
--- /dev/null
+++ b/e2e/start/basic-tsr-config/src/app/router.tsx
@@ -0,0 +1,17 @@
+// app/router.tsx
+import { createRouter as createTanStackRouter } from '@tanstack/react-router'
+import { routeTree } from './routeTree.gen'
+
+export function createRouter() {
+ const router = createTanStackRouter({
+ routeTree,
+ })
+
+ return router
+}
+
+declare module '@tanstack/react-router' {
+ interface Register {
+ router: ReturnType
+ }
+}
diff --git a/e2e/start/basic-tsr-config/src/app/routes/__root.tsx b/e2e/start/basic-tsr-config/src/app/routes/__root.tsx
new file mode 100644
index 0000000000..9e0dc3f4e0
--- /dev/null
+++ b/e2e/start/basic-tsr-config/src/app/routes/__root.tsx
@@ -0,0 +1,44 @@
+// app/routes/__root.tsx
+import { createRootRoute } from '@tanstack/react-router'
+import { Outlet, ScrollRestoration } from '@tanstack/react-router'
+import { Body, Head, Html, Meta, Scripts } from '@tanstack/start'
+import * as React from 'react'
+
+export const Route = createRootRoute({
+ meta: () => [
+ {
+ charSet: 'utf-8',
+ },
+ {
+ name: 'viewport',
+ content: 'width=device-width, initial-scale=1',
+ },
+ {
+ title: 'TanStack Start Starter',
+ },
+ ],
+ component: RootComponent,
+})
+
+function RootComponent() {
+ return (
+
+
+
+ )
+}
+
+function RootDocument({ children }: { children: React.ReactNode }) {
+ return (
+
+
+
+
+
+ {children}
+
+
+
+
+ )
+}
diff --git a/e2e/start/basic-tsr-config/src/app/routes/index.tsx b/e2e/start/basic-tsr-config/src/app/routes/index.tsx
new file mode 100644
index 0000000000..051c6eaa09
--- /dev/null
+++ b/e2e/start/basic-tsr-config/src/app/routes/index.tsx
@@ -0,0 +1,35 @@
+// app/routes/index.tsx
+import { createFileRoute, useRouter } from '@tanstack/react-router'
+import { createServerFn } from '@tanstack/start'
+
+let count = 0
+
+const getCount = createServerFn('GET', () => {
+ return count
+})
+
+const updateCount = createServerFn('POST', async (addBy: number) => {
+ count += addBy
+})
+
+export const Route = createFileRoute('/')({
+ component: Home,
+ loader: async () => await getCount(),
+})
+
+function Home() {
+ const router = useRouter()
+ const state = Route.useLoaderData()
+
+ return (
+
+ )
+}
diff --git a/e2e/start/basic-tsr-config/src/app/ssr.tsx b/e2e/start/basic-tsr-config/src/app/ssr.tsx
new file mode 100644
index 0000000000..c74eeaedc7
--- /dev/null
+++ b/e2e/start/basic-tsr-config/src/app/ssr.tsx
@@ -0,0 +1,14 @@
+// app/ssr.tsx
+///
+import {
+ createStartHandler,
+ defaultStreamHandler,
+} from '@tanstack/start/server'
+import { getRouterManifest } from '@tanstack/start/router-manifest'
+
+import { createRouter } from './router'
+
+export default createStartHandler({
+ createRouter,
+ getRouterManifest,
+})(defaultStreamHandler)
diff --git a/e2e/start/basic-tsr-config/tests/app.spec.ts b/e2e/start/basic-tsr-config/tests/app.spec.ts
new file mode 100644
index 0000000000..f61b9788db
--- /dev/null
+++ b/e2e/start/basic-tsr-config/tests/app.spec.ts
@@ -0,0 +1,8 @@
+import { expect, test } from '@playwright/test'
+
+test('opening the app', async ({ page }) => {
+ await page.goto('/')
+ await expect(page.getByText('Add 1 to 0?')).toBeTruthy()
+ await page.click('button')
+ await expect(page.getByText('Add 1 to 1?')).toBeTruthy()
+})
diff --git a/e2e/start/basic-tsr-config/tsconfig.json b/e2e/start/basic-tsr-config/tsconfig.json
new file mode 100644
index 0000000000..d1b5b77660
--- /dev/null
+++ b/e2e/start/basic-tsr-config/tsconfig.json
@@ -0,0 +1,22 @@
+{
+ "include": ["**/*.ts", "**/*.tsx"],
+ "compilerOptions": {
+ "strict": true,
+ "esModuleInterop": true,
+ "jsx": "react-jsx",
+ "module": "ESNext",
+ "moduleResolution": "Bundler",
+ "lib": ["DOM", "DOM.Iterable", "ES2022"],
+ "isolatedModules": true,
+ "resolveJsonModule": true,
+ "skipLibCheck": true,
+ "target": "ES2022",
+ "allowJs": true,
+ "forceConsistentCasingInFileNames": true,
+ "baseUrl": ".",
+ "paths": {
+ "~/*": ["./app/*"]
+ },
+ "noEmit": true
+ }
+}
diff --git a/packages/start/src/config/index.ts b/packages/start/src/config/index.ts
index dd74662b4e..2861e65e2a 100644
--- a/packages/start/src/config/index.ts
+++ b/packages/start/src/config/index.ts
@@ -207,7 +207,7 @@ const routersSchema = z.object({
})
const tsrConfig = configSchema.partial().extend({
- appDirectory: z.string(),
+ appDirectory: z.string().optional(),
})
const inlineConfigSchema = z.object({
@@ -222,15 +222,16 @@ export type TanStackStartDefineConfigOptions = z.infer<
typeof inlineConfigSchema
>
-function setTsrDefaults(
- config: TanStackStartDefineConfigOptions['tsr'],
-): Partial {
+function setTsrDefaults(config: TanStackStartDefineConfigOptions['tsr']) {
+ // Normally these are `./src/___`, but we're using `./app/___` for Start stuff
+ const appDirectory = config?.appDirectory ?? './app'
return {
...config,
- // Normally these are `./src/___`, but we're using `./app/___` for Start stuff
- appDirectory: config?.appDirectory ?? './app',
- routesDirectory: config?.routesDirectory ?? './app/routes',
- generatedRouteTree: config?.generatedRouteTree ?? './app/routeTree.gen.ts',
+ appDirectory: config?.appDirectory ?? appDirectory,
+ routesDirectory:
+ config?.routesDirectory ?? path.join(appDirectory, 'routes'),
+ generatedRouteTree:
+ config?.generatedRouteTree ?? path.join(appDirectory, 'routeTree.gen.ts'),
experimental: {
...config?.experimental,
},
@@ -248,16 +249,20 @@ export function defineConfig(
const deploymentPreset = checkDeploymentPresetInput(
configDeploymentPreset || 'vercel',
)
+ const tsr = setTsrDefaults(opts.tsr)
+ const appDirectory = tsr.appDirectory
- const tsrConfig = getConfig(setTsrDefaults(opts.tsr))
+ const tsrConfig = getConfig(tsr)
const clientBase = opts.routers?.client?.base || '/_build'
const serverBase = opts.routers?.server?.base || '/_server'
const apiBase = opts.tsr?.apiBase || '/api'
- const clientEntry = opts.routers?.client?.entry || './app/client.tsx'
- const ssrEntry = opts.routers?.ssr?.entry || './app/ssr.tsx'
- const apiEntry = opts.routers?.api?.entry || './app/api.ts'
+ const clientEntry =
+ opts.routers?.client?.entry || path.join(appDirectory, 'client.tsx')
+ const ssrEntry =
+ opts.routers?.ssr?.entry || path.join(appDirectory, 'ssr.tsx')
+ const apiEntry = opts.routers?.api?.entry || path.join(appDirectory, 'api.ts')
const apiEntryExists = existsSync(apiEntry)
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 7ff32a88c3..d93fa880d4 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -642,6 +642,37 @@ importers:
specifier: ^5.0.1
version: 5.0.1(typescript@5.6.3)(vite@5.4.9(@types/node@22.7.4)(terser@5.31.1))
+ e2e/start/basic-tsr-config:
+ dependencies:
+ '@tanstack/react-router':
+ specifier: workspace:*
+ version: link:../../../packages/react-router
+ '@tanstack/start':
+ specifier: workspace:*
+ version: link:../../../packages/start
+ react:
+ specifier: ^18.3.1
+ version: 18.3.1
+ react-dom:
+ specifier: ^18.3.1
+ version: 18.3.1(react@18.3.1)
+ vinxi:
+ specifier: 0.4.3
+ version: 0.4.3(@types/node@22.7.4)(ioredis@5.4.1)(terser@5.31.1)(webpack-sources@3.2.3)
+ devDependencies:
+ '@types/node':
+ specifier: ^22.5.4
+ version: 22.7.4
+ '@types/react':
+ specifier: ^18.2.65
+ version: 18.3.11
+ '@types/react-dom':
+ specifier: ^18.2.21
+ version: 18.3.0
+ typescript:
+ specifier: ^5.6.2
+ version: 5.6.3
+
e2e/start/clerk-basic:
dependencies:
'@clerk/tanstack-start':