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':