From d82ac55556241211cb5d241d20e3da9128e19bb1 Mon Sep 17 00:00:00 2001 From: wookhyung Date: Wed, 16 Jul 2025 17:41:47 +0900 Subject: [PATCH 1/7] fix(start-server-core): properly merge middleware context objects --- packages/start-server-core/src/createStartHandler.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/start-server-core/src/createStartHandler.ts b/packages/start-server-core/src/createStartHandler.ts index f42b7619ce4..bcdb8032f38 100644 --- a/packages/start-server-core/src/createStartHandler.ts +++ b/packages/start-server-core/src/createStartHandler.ts @@ -452,7 +452,14 @@ function executeMiddleware(middlewares: TODO, ctx: TODO) { // Allow the middleware to call the next middleware in the chain next: async (nextCtx: TODO) => { // Allow the caller to extend the context for the next middleware - const nextResult = await next({ ...ctx, ...nextCtx }) + const nextResult = await next({ + ...ctx, + ...nextCtx, + context: { + ...ctx.context, + ...(nextCtx?.context || {}), + }, + }) // Merge the result into the context\ return Object.assign(ctx, handleCtxResult(nextResult)) From d0bad029270cad1a2357ea4bdd152785d2fa808c Mon Sep 17 00:00:00 2001 From: wookhyung Date: Wed, 16 Jul 2025 21:13:59 +0900 Subject: [PATCH 2/7] feat: add middleware context merging test implementation --- .../server-functions/src/routeTree.gen.ts | 71 +++++++++++++++++++ .../src/routes/api/middleware-context.ts | 28 ++++++++ .../server-functions/src/routes/index.tsx | 5 ++ .../merge-server-fn-middleware-context.tsx | 67 +++++++++++++++++ .../tests/server-functions.spec.ts | 16 +++++ 5 files changed, 187 insertions(+) create mode 100644 e2e/react-start/server-functions/src/routes/api/middleware-context.ts create mode 100644 e2e/react-start/server-functions/src/routes/merge-server-fn-middleware-context.tsx diff --git a/e2e/react-start/server-functions/src/routeTree.gen.ts b/e2e/react-start/server-functions/src/routeTree.gen.ts index a80a2843b62..a9243a658b1 100644 --- a/e2e/react-start/server-functions/src/routeTree.gen.ts +++ b/e2e/react-start/server-functions/src/routeTree.gen.ts @@ -8,6 +8,8 @@ // 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 { createServerRootRoute } from '@tanstack/react-start/server' + import { Route as rootRouteImport } from './routes/__root' import { Route as SubmitPostFormdataRouteImport } from './routes/submit-post-formdata' import { Route as StatusRouteImport } from './routes/status' @@ -15,6 +17,7 @@ import { Route as SerializeFormDataRouteImport } from './routes/serialize-form-d import { Route as ReturnNullRouteImport } from './routes/return-null' import { Route as RawResponseRouteImport } from './routes/raw-response' import { Route as MultipartRouteImport } from './routes/multipart' +import { Route as MergeServerFnMiddlewareContextRouteImport } from './routes/merge-server-fn-middleware-context' import { Route as IsomorphicFnsRouteImport } from './routes/isomorphic-fns' import { Route as HeadersRouteImport } from './routes/headers' import { Route as EnvOnlyRouteImport } from './routes/env-only' @@ -26,6 +29,9 @@ import { Route as FormdataRedirectIndexRouteImport } from './routes/formdata-red import { Route as CookiesIndexRouteImport } from './routes/cookies/index' import { Route as CookiesSetRouteImport } from './routes/cookies/set' import { Route as FormdataRedirectTargetNameRouteImport } from './routes/formdata-redirect/target.$name' +import { ServerRoute as ApiMiddlewareContextServerRouteImport } from './routes/api/middleware-context' + +const rootServerRouteImport = createServerRootRoute() const SubmitPostFormdataRoute = SubmitPostFormdataRouteImport.update({ id: '/submit-post-formdata', @@ -57,6 +63,12 @@ const MultipartRoute = MultipartRouteImport.update({ path: '/multipart', getParentRoute: () => rootRouteImport, } as any) +const MergeServerFnMiddlewareContextRoute = + MergeServerFnMiddlewareContextRouteImport.update({ + id: '/merge-server-fn-middleware-context', + path: '/merge-server-fn-middleware-context', + getParentRoute: () => rootRouteImport, + } as any) const IsomorphicFnsRoute = IsomorphicFnsRouteImport.update({ id: '/isomorphic-fns', path: '/isomorphic-fns', @@ -113,6 +125,12 @@ const FormdataRedirectTargetNameRoute = path: '/formdata-redirect/target/$name', getParentRoute: () => rootRouteImport, } as any) +const ApiMiddlewareContextServerRoute = + ApiMiddlewareContextServerRouteImport.update({ + id: '/api/middleware-context', + path: '/api/middleware-context', + getParentRoute: () => rootServerRouteImport, + } as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -122,6 +140,7 @@ export interface FileRoutesByFullPath { '/env-only': typeof EnvOnlyRoute '/headers': typeof HeadersRoute '/isomorphic-fns': typeof IsomorphicFnsRoute + '/merge-server-fn-middleware-context': typeof MergeServerFnMiddlewareContextRoute '/multipart': typeof MultipartRoute '/raw-response': typeof RawResponseRoute '/return-null': typeof ReturnNullRoute @@ -141,6 +160,7 @@ export interface FileRoutesByTo { '/env-only': typeof EnvOnlyRoute '/headers': typeof HeadersRoute '/isomorphic-fns': typeof IsomorphicFnsRoute + '/merge-server-fn-middleware-context': typeof MergeServerFnMiddlewareContextRoute '/multipart': typeof MultipartRoute '/raw-response': typeof RawResponseRoute '/return-null': typeof ReturnNullRoute @@ -161,6 +181,7 @@ export interface FileRoutesById { '/env-only': typeof EnvOnlyRoute '/headers': typeof HeadersRoute '/isomorphic-fns': typeof IsomorphicFnsRoute + '/merge-server-fn-middleware-context': typeof MergeServerFnMiddlewareContextRoute '/multipart': typeof MultipartRoute '/raw-response': typeof RawResponseRoute '/return-null': typeof ReturnNullRoute @@ -182,6 +203,7 @@ export interface FileRouteTypes { | '/env-only' | '/headers' | '/isomorphic-fns' + | '/merge-server-fn-middleware-context' | '/multipart' | '/raw-response' | '/return-null' @@ -201,6 +223,7 @@ export interface FileRouteTypes { | '/env-only' | '/headers' | '/isomorphic-fns' + | '/merge-server-fn-middleware-context' | '/multipart' | '/raw-response' | '/return-null' @@ -220,6 +243,7 @@ export interface FileRouteTypes { | '/env-only' | '/headers' | '/isomorphic-fns' + | '/merge-server-fn-middleware-context' | '/multipart' | '/raw-response' | '/return-null' @@ -240,6 +264,7 @@ export interface RootRouteChildren { EnvOnlyRoute: typeof EnvOnlyRoute HeadersRoute: typeof HeadersRoute IsomorphicFnsRoute: typeof IsomorphicFnsRoute + MergeServerFnMiddlewareContextRoute: typeof MergeServerFnMiddlewareContextRoute MultipartRoute: typeof MultipartRoute RawResponseRoute: typeof RawResponseRoute ReturnNullRoute: typeof ReturnNullRoute @@ -251,6 +276,27 @@ export interface RootRouteChildren { FormdataRedirectIndexRoute: typeof FormdataRedirectIndexRoute FormdataRedirectTargetNameRoute: typeof FormdataRedirectTargetNameRoute } +export interface FileServerRoutesByFullPath { + '/api/middleware-context': typeof ApiMiddlewareContextServerRoute +} +export interface FileServerRoutesByTo { + '/api/middleware-context': typeof ApiMiddlewareContextServerRoute +} +export interface FileServerRoutesById { + __root__: typeof rootServerRouteImport + '/api/middleware-context': typeof ApiMiddlewareContextServerRoute +} +export interface FileServerRouteTypes { + fileServerRoutesByFullPath: FileServerRoutesByFullPath + fullPaths: '/api/middleware-context' + fileServerRoutesByTo: FileServerRoutesByTo + to: '/api/middleware-context' + id: '__root__' | '/api/middleware-context' + fileServerRoutesById: FileServerRoutesById +} +export interface RootServerRouteChildren { + ApiMiddlewareContextServerRoute: typeof ApiMiddlewareContextServerRoute +} declare module '@tanstack/react-router' { interface FileRoutesByPath { @@ -296,6 +342,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof MultipartRouteImport parentRoute: typeof rootRouteImport } + '/merge-server-fn-middleware-context': { + id: '/merge-server-fn-middleware-context' + path: '/merge-server-fn-middleware-context' + fullPath: '/merge-server-fn-middleware-context' + preLoaderRoute: typeof MergeServerFnMiddlewareContextRouteImport + parentRoute: typeof rootRouteImport + } '/isomorphic-fns': { id: '/isomorphic-fns' path: '/isomorphic-fns' @@ -375,6 +428,17 @@ declare module '@tanstack/react-router' { } } } +declare module '@tanstack/react-start/server' { + interface ServerFileRoutesByPath { + '/api/middleware-context': { + id: '/api/middleware-context' + path: '/api/middleware-context' + fullPath: '/api/middleware-context' + preLoaderRoute: typeof ApiMiddlewareContextServerRouteImport + parentRoute: typeof rootServerRouteImport + } + } +} const rootRouteChildren: RootRouteChildren = { IndexRoute: IndexRoute, @@ -384,6 +448,7 @@ const rootRouteChildren: RootRouteChildren = { EnvOnlyRoute: EnvOnlyRoute, HeadersRoute: HeadersRoute, IsomorphicFnsRoute: IsomorphicFnsRoute, + MergeServerFnMiddlewareContextRoute: MergeServerFnMiddlewareContextRoute, MultipartRoute: MultipartRoute, RawResponseRoute: RawResponseRoute, ReturnNullRoute: ReturnNullRoute, @@ -398,3 +463,9 @@ const rootRouteChildren: RootRouteChildren = { export const routeTree = rootRouteImport ._addFileChildren(rootRouteChildren) ._addFileTypes() +const rootServerRouteChildren: RootServerRouteChildren = { + ApiMiddlewareContextServerRoute: ApiMiddlewareContextServerRoute, +} +export const serverRouteTree = rootServerRouteImport + ._addFileChildren(rootServerRouteChildren) + ._addFileTypes() diff --git a/e2e/react-start/server-functions/src/routes/api/middleware-context.ts b/e2e/react-start/server-functions/src/routes/api/middleware-context.ts new file mode 100644 index 00000000000..bc75e29126d --- /dev/null +++ b/e2e/react-start/server-functions/src/routes/api/middleware-context.ts @@ -0,0 +1,28 @@ +import { createServerFileRoute } from '@tanstack/react-start/server' +import { createMiddleware, json } from '@tanstack/react-start' + +const testParentMiddleware = createMiddleware({ type: 'request' }).server( + async ({ next }) => { + const result = await next({ context: { testParent: true } }) + return result + }, +) + +const testMiddleware = createMiddleware({ type: 'request' }) + .middleware([testParentMiddleware]) + .server(async ({ next }) => { + const result = await next({ context: { test: true } }) + return result + }) + +export const ServerRoute = createServerFileRoute('/api/middleware-context') + .middleware([testMiddleware]) + .methods({ + GET: ({ request, context }) => { + return json({ + url: request.url, + context: context, + expectedContext: { testParent: true, test: true }, + }) + }, + }) diff --git a/e2e/react-start/server-functions/src/routes/index.tsx b/e2e/react-start/server-functions/src/routes/index.tsx index 61195d1fc53..15cd99d8f73 100644 --- a/e2e/react-start/server-functions/src/routes/index.tsx +++ b/e2e/react-start/server-functions/src/routes/index.tsx @@ -82,6 +82,11 @@ function Home() { server function redirects when FormData is submitted (via no-JS) +
  • + + server function middleware context is merged correctly + +
  • ) diff --git a/e2e/react-start/server-functions/src/routes/merge-server-fn-middleware-context.tsx b/e2e/react-start/server-functions/src/routes/merge-server-fn-middleware-context.tsx new file mode 100644 index 00000000000..62a3d6be44e --- /dev/null +++ b/e2e/react-start/server-functions/src/routes/merge-server-fn-middleware-context.tsx @@ -0,0 +1,67 @@ +import { createFileRoute } from '@tanstack/react-router' +import * as React from 'react' + +export const Route = createFileRoute('/merge-server-fn-middleware-context')({ + component: () => , +}) + +function MergeServerFnMiddlewareContext() { + const [apiResponse, setApiResponse] = React.useState(null) + + const fetchMiddlewareContext = async () => { + try { + const response = await fetch('/api/middleware-context') + const data = await response.json() + setApiResponse(data) + } catch (error) { + console.error('Error fetching middleware context:', error) + setApiResponse({ error: 'Failed to fetch' }) + } + } + + return ( +
    +

    Merge Server Function Middleware Context Test

    +
    + + + {apiResponse && ( +
    +

    API Response:

    +
    +              {JSON.stringify(apiResponse, null, 2)}
    +            
    + +
    +

    Context Verification:

    +
    + {JSON.stringify(apiResponse.context, null, 2)} +
    + +
    + Has testParent:{' '} + {apiResponse.context?.testParent ? 'true' : 'false'} +
    + +
    + Has test: {apiResponse.context?.test ? 'true' : 'false'} +
    +
    +
    + )} +
    +
    + ) +} diff --git a/e2e/react-start/server-functions/tests/server-functions.spec.ts b/e2e/react-start/server-functions/tests/server-functions.spec.ts index 8a5fc524cc2..e8afbec0c7d 100644 --- a/e2e/react-start/server-functions/tests/server-functions.spec.ts +++ b/e2e/react-start/server-functions/tests/server-functions.spec.ts @@ -315,6 +315,22 @@ test('raw response', async ({ page }) => { await expect(page.getByTestId('response')).toContainText(expectedValue) }) + +test('merge-server-fn-middleware-context', async ({ page }) => { + await page.goto('/merge-server-fn-middleware-context') + + await page.waitForLoadState('networkidle') + + await page.getByTestId('test-middleware-context-btn').click() + await page.waitForLoadState('networkidle') + + await expect(page.getByTestId('has-test-parent')).toContainText('true') + await expect(page.getByTestId('has-test')).toContainText('true') + + const contextResult = await page.getByTestId('context-result').textContent() + expect(contextResult).toContain('testParent') + expect(contextResult).toContain('test') +}) ;[{ mode: 'js' }, { mode: 'no-js' }].forEach(({ mode }) => { test(`Server function can redirect when sending formdata: mode = ${mode}`, async ({ page, From 7891ac2a78225affc97bcf9d3a61cf33ad9ccaeb Mon Sep 17 00:00:00 2001 From: wookhyung Date: Thu, 17 Jul 2025 11:21:25 +0900 Subject: [PATCH 3/7] test: add server-routes e2e test apps for middleware context merging --- .../tests/server-functions.spec.ts | 15 -- e2e/react-start/server-routes/.gitignore | 18 +++ e2e/react-start/server-routes/.prettierignore | 4 + e2e/react-start/server-routes/package.json | 40 +++++ .../server-routes/playwright.config.ts | 35 +++++ .../server-routes/postcss.config.mjs | 6 + .../server-routes/public/favicon.ico | Bin 0 -> 15406 bytes .../server-routes/public/favicon.png | Bin 0 -> 1507 bytes .../src/components/DefaultCatchBoundary.tsx | 53 +++++++ .../server-routes/src/components/NotFound.tsx | 25 ++++ .../server-routes/src/routeTree.gen.ts | 127 ++++++++++++++++ e2e/react-start/server-routes/src/router.tsx | 22 +++ .../server-routes/src/routes/__root.tsx | 82 +++++++++++ .../src/routes/api/middleware-context.ts | 28 ++++ .../server-routes/src/routes/index.tsx | 20 +++ .../merge-server-fn-middleware-context.tsx | 68 +++++++++ .../server-routes/src/styles/app.css | 22 +++ .../server-routes/tailwind.config.mjs | 4 + .../server-routes/tests/fixture.ts | 28 ++++ .../server-routes/tests/server-routes.spec.ts | 17 +++ e2e/react-start/server-routes/tsconfig.json | 23 +++ e2e/react-start/server-routes/vite.config.ts | 12 ++ e2e/solid-start/server-routes/.gitignore | 20 +++ e2e/solid-start/server-routes/.prettierignore | 4 + e2e/solid-start/server-routes/package.json | 37 +++++ .../server-routes/playwright.config.ts | 35 +++++ .../server-routes/postcss.config.mjs | 6 + .../server-routes/public/favicon.ico | Bin 0 -> 15406 bytes .../server-routes/public/favicon.png | Bin 0 -> 1507 bytes .../src/components/DefaultCatchBoundary.tsx | 53 +++++++ .../server-routes/src/components/NotFound.tsx | 25 ++++ .../server-routes/src/routeTree.gen.ts | 127 ++++++++++++++++ e2e/solid-start/server-routes/src/router.tsx | 22 +++ .../server-routes/src/routes/__root.tsx | 34 +++++ .../src/routes/api/middleware-context.ts | 28 ++++ .../server-routes/src/routes/index.tsx | 20 +++ .../merge-server-fn-middleware-context.tsx | 68 +++++++++ .../server-routes/src/styles/app.css | 22 +++ .../server-routes/src/vite-env.d.ts | 4 + .../server-routes/tailwind.config.mjs | 4 + .../server-routes/tests/fixture.ts | 28 ++++ .../server-routes/tests/server-routes.spec.ts | 17 +++ e2e/solid-start/server-routes/tsconfig.json | 23 +++ e2e/solid-start/server-routes/vite.config.ts | 15 ++ pnpm-lock.yaml | 137 ++++++++++++++++++ 45 files changed, 1363 insertions(+), 15 deletions(-) create mode 100644 e2e/react-start/server-routes/.gitignore create mode 100644 e2e/react-start/server-routes/.prettierignore create mode 100644 e2e/react-start/server-routes/package.json create mode 100644 e2e/react-start/server-routes/playwright.config.ts create mode 100644 e2e/react-start/server-routes/postcss.config.mjs create mode 100644 e2e/react-start/server-routes/public/favicon.ico create mode 100644 e2e/react-start/server-routes/public/favicon.png create mode 100644 e2e/react-start/server-routes/src/components/DefaultCatchBoundary.tsx create mode 100644 e2e/react-start/server-routes/src/components/NotFound.tsx create mode 100644 e2e/react-start/server-routes/src/routeTree.gen.ts create mode 100644 e2e/react-start/server-routes/src/router.tsx create mode 100644 e2e/react-start/server-routes/src/routes/__root.tsx create mode 100644 e2e/react-start/server-routes/src/routes/api/middleware-context.ts create mode 100644 e2e/react-start/server-routes/src/routes/index.tsx create mode 100644 e2e/react-start/server-routes/src/routes/merge-server-fn-middleware-context.tsx create mode 100644 e2e/react-start/server-routes/src/styles/app.css create mode 100644 e2e/react-start/server-routes/tailwind.config.mjs create mode 100644 e2e/react-start/server-routes/tests/fixture.ts create mode 100644 e2e/react-start/server-routes/tests/server-routes.spec.ts create mode 100644 e2e/react-start/server-routes/tsconfig.json create mode 100644 e2e/react-start/server-routes/vite.config.ts create mode 100644 e2e/solid-start/server-routes/.gitignore create mode 100644 e2e/solid-start/server-routes/.prettierignore create mode 100644 e2e/solid-start/server-routes/package.json create mode 100644 e2e/solid-start/server-routes/playwright.config.ts create mode 100644 e2e/solid-start/server-routes/postcss.config.mjs create mode 100644 e2e/solid-start/server-routes/public/favicon.ico create mode 100644 e2e/solid-start/server-routes/public/favicon.png create mode 100644 e2e/solid-start/server-routes/src/components/DefaultCatchBoundary.tsx create mode 100644 e2e/solid-start/server-routes/src/components/NotFound.tsx create mode 100644 e2e/solid-start/server-routes/src/routeTree.gen.ts create mode 100644 e2e/solid-start/server-routes/src/router.tsx create mode 100644 e2e/solid-start/server-routes/src/routes/__root.tsx create mode 100644 e2e/solid-start/server-routes/src/routes/api/middleware-context.ts create mode 100644 e2e/solid-start/server-routes/src/routes/index.tsx create mode 100644 e2e/solid-start/server-routes/src/routes/merge-server-fn-middleware-context.tsx create mode 100644 e2e/solid-start/server-routes/src/styles/app.css create mode 100644 e2e/solid-start/server-routes/src/vite-env.d.ts create mode 100644 e2e/solid-start/server-routes/tailwind.config.mjs create mode 100644 e2e/solid-start/server-routes/tests/fixture.ts create mode 100644 e2e/solid-start/server-routes/tests/server-routes.spec.ts create mode 100644 e2e/solid-start/server-routes/tsconfig.json create mode 100644 e2e/solid-start/server-routes/vite.config.ts diff --git a/e2e/react-start/server-functions/tests/server-functions.spec.ts b/e2e/react-start/server-functions/tests/server-functions.spec.ts index e8afbec0c7d..e73ec0e4464 100644 --- a/e2e/react-start/server-functions/tests/server-functions.spec.ts +++ b/e2e/react-start/server-functions/tests/server-functions.spec.ts @@ -316,21 +316,6 @@ test('raw response', async ({ page }) => { await expect(page.getByTestId('response')).toContainText(expectedValue) }) -test('merge-server-fn-middleware-context', async ({ page }) => { - await page.goto('/merge-server-fn-middleware-context') - - await page.waitForLoadState('networkidle') - - await page.getByTestId('test-middleware-context-btn').click() - await page.waitForLoadState('networkidle') - - await expect(page.getByTestId('has-test-parent')).toContainText('true') - await expect(page.getByTestId('has-test')).toContainText('true') - - const contextResult = await page.getByTestId('context-result').textContent() - expect(contextResult).toContain('testParent') - expect(contextResult).toContain('test') -}) ;[{ mode: 'js' }, { mode: 'no-js' }].forEach(({ mode }) => { test(`Server function can redirect when sending formdata: mode = ${mode}`, async ({ page, diff --git a/e2e/react-start/server-routes/.gitignore b/e2e/react-start/server-routes/.gitignore new file mode 100644 index 00000000000..ca63f498851 --- /dev/null +++ b/e2e/react-start/server-routes/.gitignore @@ -0,0 +1,18 @@ +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/react-start/server-routes/.prettierignore b/e2e/react-start/server-routes/.prettierignore new file mode 100644 index 00000000000..2be5eaa6ece --- /dev/null +++ b/e2e/react-start/server-routes/.prettierignore @@ -0,0 +1,4 @@ +**/build +**/public +pnpm-lock.yaml +routeTree.gen.ts \ No newline at end of file diff --git a/e2e/react-start/server-routes/package.json b/e2e/react-start/server-routes/package.json new file mode 100644 index 00000000000..e7352c35ced --- /dev/null +++ b/e2e/react-start/server-routes/package.json @@ -0,0 +1,40 @@ +{ + "name": "tanstack-react-start-e2e-server-routes", + "private": true, + "sideEffects": false, + "type": "module", + "scripts": { + "dev": "vite dev --port 3000", + "dev:e2e": "vite dev", + "build": "vite build && tsc --noEmit", + "start": "node .output/server/index.mjs", + "test:e2e": "playwright test --project=chromium" + }, + "dependencies": { + "@tanstack/react-router": "workspace:^", + "@tanstack/react-router-devtools": "workspace:^", + "@tanstack/react-start": "workspace:^", + "js-cookie": "^3.0.5", + "react": "^19.0.0", + "react-dom": "^19.0.0", + "redaxios": "^0.5.1", + "tailwind-merge": "^2.6.0", + "vite": "^6.3.5", + "zod": "^3.24.2" + }, + "devDependencies": { + "@playwright/test": "^1.50.1", + "@tanstack/router-e2e-utils": "workspace:^", + "@types/js-cookie": "^3.0.6", + "@types/node": "^22.10.2", + "@types/react": "^19.0.8", + "@types/react-dom": "^19.0.3", + "@vitejs/plugin-react": "^4.3.4", + "autoprefixer": "^10.4.20", + "combinate": "^1.1.11", + "postcss": "^8.5.1", + "tailwindcss": "^3.4.17", + "typescript": "^5.7.2", + "vite-tsconfig-paths": "^5.1.4" + } +} diff --git a/e2e/react-start/server-routes/playwright.config.ts b/e2e/react-start/server-routes/playwright.config.ts new file mode 100644 index 00000000000..88ff69f92e7 --- /dev/null +++ b/e2e/react-start/server-routes/playwright.config.ts @@ -0,0 +1,35 @@ +import { defineConfig, devices } from '@playwright/test' +import { derivePort } from '@tanstack/router-e2e-utils' +import packageJson from './package.json' with { type: 'json' } + +export const PORT = derivePort(packageJson.name) +const baseURL = `http://localhost:${PORT}` + +/** + * See https://playwright.dev/docs/test-configuration. + */ +export default defineConfig({ + testDir: './tests', + workers: 1, + + reporter: [['line']], + + use: { + /* Base URL to use in actions like `await page.goto('/')`. */ + baseURL, + }, + + webServer: { + command: `VITE_SERVER_PORT=${PORT} pnpm build && PORT=${PORT} VITE_SERVER_PORT=${PORT} pnpm start`, + url: baseURL, + reuseExistingServer: !process.env.CI, + stdout: 'pipe', + }, + + projects: [ + { + name: 'chromium', + use: { ...devices['Desktop Chrome'] }, + }, + ], +}) diff --git a/e2e/react-start/server-routes/postcss.config.mjs b/e2e/react-start/server-routes/postcss.config.mjs new file mode 100644 index 00000000000..2e7af2b7f1a --- /dev/null +++ b/e2e/react-start/server-routes/postcss.config.mjs @@ -0,0 +1,6 @@ +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} diff --git a/e2e/react-start/server-routes/public/favicon.ico b/e2e/react-start/server-routes/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..1a1751676f7e22811b1070572093996c93c87617 GIT binary patch literal 15406 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 literal 0 HcmV?d00001 diff --git a/e2e/react-start/server-routes/public/favicon.png b/e2e/react-start/server-routes/public/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..1e77bc06091ade4496525a09d8900675afcf03f0 GIT binary patch literal 1507 zcmV<91swW`P)Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91AfN*P1ONa40RR91AOHXW0IY^$^8f$^O-V#SR9Fd>S3Qp$MHK9ro!#4$ zEP@L_hX|b@f=!*_42h6mKu7{)4)_U*>1>0bCkUj;Z1X!7 zHe(Ew^Oi(|bW3J~xu+)XbtFF?4>!7TH$>(D_atUQVEj(8fGvYu2NF33#JZX>)(Vj8 zIi@z>Glt?6t~;Lf(|C8F>;WF^8F<^s7Scr!sZc01uB?HMHoL5+FZ>B(g+r-)?Sn)#3Zal#?G@GAwO5U27MpGOlC2+_saA)rl zP-<@-n~;PQOlm|Hi<+W;NdR;5+=zADzM&?!+CPD36=cGwHy6!D^vPEHG?rO`K>G|M z3FposX{yT132wuw1OR3Um_5JoKB#6?!QgBupIT;?YIr;WcpmuCE>S75mZid+ens#E zGPuYjiG0UNNVWu=f!Id^?9)34)eIpu-`j_~W0iAQzK(}XYc_!;87Tk~?4tq|h=2(! zuq0HCiNK)@+ocCKR3q1REdUju>HdYxd>JX@%oOibg+J~D+}rhz54D!NfC{h-OYk{M zkzmFtdrL@nL0bm8nF@pob1CeLC>12ef#in-Bzv2!wi)Iuwq24)`AH}|0QNQ^f$KHv z?5PBPo1*#GAuAk+Poe`?UJ>mP`@~d4a(103j0lwUx@_+$#B&VC%7r>#2$HIiD`KO8L|s3Yp%M}BT0;NJDzZtPnx=4%enhU zhW*pNN0t`^4%5MKAR+}=^Q?QeqQ`>bbK zf+-ji$Uz8V0?LpX@kh`k%DL)GCA2=@SJNKg56Wh>>pr=7{1PmHqG|~=AdLV3002ov JPDHLkV1ivgp)>#h literal 0 HcmV?d00001 diff --git a/e2e/react-start/server-routes/src/components/DefaultCatchBoundary.tsx b/e2e/react-start/server-routes/src/components/DefaultCatchBoundary.tsx new file mode 100644 index 00000000000..15f316681cc --- /dev/null +++ b/e2e/react-start/server-routes/src/components/DefaultCatchBoundary.tsx @@ -0,0 +1,53 @@ +import { + ErrorComponent, + Link, + rootRouteId, + useMatch, + useRouter, +} from '@tanstack/react-router' +import type { ErrorComponentProps } from '@tanstack/react-router' + +export function DefaultCatchBoundary({ error }: ErrorComponentProps) { + const router = useRouter() + const isRoot = useMatch({ + strict: false, + select: (state) => state.id === rootRouteId, + }) + + console.error(error) + + return ( +
    + +
    + + {isRoot ? ( + + Home + + ) : ( + { + e.preventDefault() + window.history.back() + }} + > + Go Back + + )} +
    +
    + ) +} diff --git a/e2e/react-start/server-routes/src/components/NotFound.tsx b/e2e/react-start/server-routes/src/components/NotFound.tsx new file mode 100644 index 00000000000..af4e0e74946 --- /dev/null +++ b/e2e/react-start/server-routes/src/components/NotFound.tsx @@ -0,0 +1,25 @@ +import { Link } from '@tanstack/react-router' + +export function NotFound({ children }: { children?: any }) { + return ( +
    +
    + {children ||

    The page you are looking for does not exist.

    } +
    +

    + + + Start Over + +

    +
    + ) +} diff --git a/e2e/react-start/server-routes/src/routeTree.gen.ts b/e2e/react-start/server-routes/src/routeTree.gen.ts new file mode 100644 index 00000000000..1d1d61ae496 --- /dev/null +++ b/e2e/react-start/server-routes/src/routeTree.gen.ts @@ -0,0 +1,127 @@ +/* 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 { createServerRootRoute } from '@tanstack/react-start/server' + +import { Route as rootRouteImport } from './routes/__root' +import { Route as MergeServerFnMiddlewareContextRouteImport } from './routes/merge-server-fn-middleware-context' +import { Route as IndexRouteImport } from './routes/index' +import { ServerRoute as ApiMiddlewareContextServerRouteImport } from './routes/api/middleware-context' + +const rootServerRouteImport = createServerRootRoute() + +const MergeServerFnMiddlewareContextRoute = + MergeServerFnMiddlewareContextRouteImport.update({ + id: '/merge-server-fn-middleware-context', + path: '/merge-server-fn-middleware-context', + getParentRoute: () => rootRouteImport, + } as any) +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => rootRouteImport, +} as any) +const ApiMiddlewareContextServerRoute = + ApiMiddlewareContextServerRouteImport.update({ + id: '/api/middleware-context', + path: '/api/middleware-context', + getParentRoute: () => rootServerRouteImport, + } as any) + +export interface FileRoutesByFullPath { + '/': typeof IndexRoute + '/merge-server-fn-middleware-context': typeof MergeServerFnMiddlewareContextRoute +} +export interface FileRoutesByTo { + '/': typeof IndexRoute + '/merge-server-fn-middleware-context': typeof MergeServerFnMiddlewareContextRoute +} +export interface FileRoutesById { + __root__: typeof rootRouteImport + '/': typeof IndexRoute + '/merge-server-fn-middleware-context': typeof MergeServerFnMiddlewareContextRoute +} +export interface FileRouteTypes { + fileRoutesByFullPath: FileRoutesByFullPath + fullPaths: '/' | '/merge-server-fn-middleware-context' + fileRoutesByTo: FileRoutesByTo + to: '/' | '/merge-server-fn-middleware-context' + id: '__root__' | '/' | '/merge-server-fn-middleware-context' + fileRoutesById: FileRoutesById +} +export interface RootRouteChildren { + IndexRoute: typeof IndexRoute + MergeServerFnMiddlewareContextRoute: typeof MergeServerFnMiddlewareContextRoute +} +export interface FileServerRoutesByFullPath { + '/api/middleware-context': typeof ApiMiddlewareContextServerRoute +} +export interface FileServerRoutesByTo { + '/api/middleware-context': typeof ApiMiddlewareContextServerRoute +} +export interface FileServerRoutesById { + __root__: typeof rootServerRouteImport + '/api/middleware-context': typeof ApiMiddlewareContextServerRoute +} +export interface FileServerRouteTypes { + fileServerRoutesByFullPath: FileServerRoutesByFullPath + fullPaths: '/api/middleware-context' + fileServerRoutesByTo: FileServerRoutesByTo + to: '/api/middleware-context' + id: '__root__' | '/api/middleware-context' + fileServerRoutesById: FileServerRoutesById +} +export interface RootServerRouteChildren { + ApiMiddlewareContextServerRoute: typeof ApiMiddlewareContextServerRoute +} + +declare module '@tanstack/react-router' { + interface FileRoutesByPath { + '/merge-server-fn-middleware-context': { + id: '/merge-server-fn-middleware-context' + path: '/merge-server-fn-middleware-context' + fullPath: '/merge-server-fn-middleware-context' + preLoaderRoute: typeof MergeServerFnMiddlewareContextRouteImport + parentRoute: typeof rootRouteImport + } + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport + parentRoute: typeof rootRouteImport + } + } +} +declare module '@tanstack/react-start/server' { + interface ServerFileRoutesByPath { + '/api/middleware-context': { + id: '/api/middleware-context' + path: '/api/middleware-context' + fullPath: '/api/middleware-context' + preLoaderRoute: typeof ApiMiddlewareContextServerRouteImport + parentRoute: typeof rootServerRouteImport + } + } +} + +const rootRouteChildren: RootRouteChildren = { + IndexRoute: IndexRoute, + MergeServerFnMiddlewareContextRoute: MergeServerFnMiddlewareContextRoute, +} +export const routeTree = rootRouteImport + ._addFileChildren(rootRouteChildren) + ._addFileTypes() +const rootServerRouteChildren: RootServerRouteChildren = { + ApiMiddlewareContextServerRoute: ApiMiddlewareContextServerRoute, +} +export const serverRouteTree = rootServerRouteImport + ._addFileChildren(rootServerRouteChildren) + ._addFileTypes() diff --git a/e2e/react-start/server-routes/src/router.tsx b/e2e/react-start/server-routes/src/router.tsx new file mode 100644 index 00000000000..c76eb0210cc --- /dev/null +++ b/e2e/react-start/server-routes/src/router.tsx @@ -0,0 +1,22 @@ +import { createRouter as createTanStackRouter } from '@tanstack/react-router' +import { routeTree } from './routeTree.gen' +import { DefaultCatchBoundary } from './components/DefaultCatchBoundary' +import { NotFound } from './components/NotFound' + +export function createRouter() { + const router = createTanStackRouter({ + routeTree, + defaultPreload: 'intent', + defaultErrorComponent: DefaultCatchBoundary, + defaultNotFoundComponent: () => , + scrollRestoration: true, + }) + + return router +} + +declare module '@tanstack/react-router' { + interface Register { + router: ReturnType + } +} diff --git a/e2e/react-start/server-routes/src/routes/__root.tsx b/e2e/react-start/server-routes/src/routes/__root.tsx new file mode 100644 index 00000000000..64f5cbaf34c --- /dev/null +++ b/e2e/react-start/server-routes/src/routes/__root.tsx @@ -0,0 +1,82 @@ +/// +import * as React from 'react' +import { + HeadContent, + Link, + Outlet, + Scripts, + createRootRoute, +} from '@tanstack/react-router' + +import { DefaultCatchBoundary } from '~/components/DefaultCatchBoundary' +import { NotFound } from '~/components/NotFound' +import appCss from '~/styles/app.css?url' + +export const Route = createRootRoute({ + head: () => ({ + meta: [ + { + charSet: 'utf-8', + }, + { + name: 'viewport', + content: 'width=device-width, initial-scale=1', + }, + ], + links: [{ rel: 'stylesheet', href: appCss }], + }), + errorComponent: (props) => { + return ( + + + + ) + }, + notFoundComponent: () => , + component: RootComponent, +}) + +function RootComponent() { + return ( + + + + ) +} + +const RouterDevtools = + process.env.NODE_ENV === 'production' + ? () => null // Render nothing in production + : React.lazy(() => + // Lazy load in development + import('@tanstack/react-router-devtools').then((res) => ({ + default: res.TanStackRouterDevtools, + })), + ) + +function RootDocument({ children }: { children: React.ReactNode }) { + return ( + + + + + +
    + + Home + +
    +
    + {children} + + + + + ) +} diff --git a/e2e/react-start/server-routes/src/routes/api/middleware-context.ts b/e2e/react-start/server-routes/src/routes/api/middleware-context.ts new file mode 100644 index 00000000000..bc75e29126d --- /dev/null +++ b/e2e/react-start/server-routes/src/routes/api/middleware-context.ts @@ -0,0 +1,28 @@ +import { createServerFileRoute } from '@tanstack/react-start/server' +import { createMiddleware, json } from '@tanstack/react-start' + +const testParentMiddleware = createMiddleware({ type: 'request' }).server( + async ({ next }) => { + const result = await next({ context: { testParent: true } }) + return result + }, +) + +const testMiddleware = createMiddleware({ type: 'request' }) + .middleware([testParentMiddleware]) + .server(async ({ next }) => { + const result = await next({ context: { test: true } }) + return result + }) + +export const ServerRoute = createServerFileRoute('/api/middleware-context') + .middleware([testMiddleware]) + .methods({ + GET: ({ request, context }) => { + return json({ + url: request.url, + context: context, + expectedContext: { testParent: true, test: true }, + }) + }, + }) diff --git a/e2e/react-start/server-routes/src/routes/index.tsx b/e2e/react-start/server-routes/src/routes/index.tsx new file mode 100644 index 00000000000..ef9dc9ab85d --- /dev/null +++ b/e2e/react-start/server-routes/src/routes/index.tsx @@ -0,0 +1,20 @@ +import { Link, createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute('/')({ + component: Home, +}) + +function Home() { + return ( +
    +

    Server Routes E2E tests

    +
      +
    • + + server function middleware context is merged correctly + +
    • +
    +
    + ) +} diff --git a/e2e/react-start/server-routes/src/routes/merge-server-fn-middleware-context.tsx b/e2e/react-start/server-routes/src/routes/merge-server-fn-middleware-context.tsx new file mode 100644 index 00000000000..5c358bb9204 --- /dev/null +++ b/e2e/react-start/server-routes/src/routes/merge-server-fn-middleware-context.tsx @@ -0,0 +1,68 @@ +import { createFileRoute } from '@tanstack/react-router' +import * as React from 'react' + +export const Route = createFileRoute('/merge-server-fn-middleware-context')({ + component: () => , +}) + +function MergeServerFnMiddlewareContext() { + const [apiResponse, setApiResponse] = React.useState(null) + + const fetchMiddlewareContext = async () => { + try { + const response = await fetch('/api/middleware-context') + const data = await response.json() + setApiResponse(data) + } catch (error) { + console.error('Error fetching middleware context:', error) + setApiResponse({ error: 'Failed to fetch' }) + } + } + + return ( +
    +

    Merge Server Function Middleware Context Test

    +
    + + + {apiResponse && ( +
    +

    API Response:

    +
    +              {JSON.stringify(apiResponse, null, 2)}
    +            
    + +
    +

    Context Verification:

    +
    + {JSON.stringify(apiResponse.context, null, 2)} +
    + +
    + Has testParent:{' '} + {apiResponse.context?.testParent ? 'true' : 'false'} +
    + +
    + Has test: {apiResponse.context?.test ? 'true' : 'false'} +
    +
    +
    + )} +
    +
    + ) +} diff --git a/e2e/react-start/server-routes/src/styles/app.css b/e2e/react-start/server-routes/src/styles/app.css new file mode 100644 index 00000000000..c53c8706654 --- /dev/null +++ b/e2e/react-start/server-routes/src/styles/app.css @@ -0,0 +1,22 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +@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/react-start/server-routes/tailwind.config.mjs b/e2e/react-start/server-routes/tailwind.config.mjs new file mode 100644 index 00000000000..e49f4eb776e --- /dev/null +++ b/e2e/react-start/server-routes/tailwind.config.mjs @@ -0,0 +1,4 @@ +/** @type {import('tailwindcss').Config} */ +export default { + content: ['./src/**/*.{js,jsx,ts,tsx}'], +} diff --git a/e2e/react-start/server-routes/tests/fixture.ts b/e2e/react-start/server-routes/tests/fixture.ts new file mode 100644 index 00000000000..abb7b1d564d --- /dev/null +++ b/e2e/react-start/server-routes/tests/fixture.ts @@ -0,0 +1,28 @@ +import { test as base, expect } from '@playwright/test' + +export interface TestFixtureOptions { + whitelistErrors: Array +} +export const test = base.extend({ + whitelistErrors: [[], { option: true }], + page: async ({ page, whitelistErrors }, use) => { + const errorMessages: Array = [] + page.on('console', (m) => { + if (m.type() === 'error') { + const text = m.text() + for (const whitelistError of whitelistErrors) { + if ( + (typeof whitelistError === 'string' && + text.includes(whitelistError)) || + (whitelistError instanceof RegExp && whitelistError.test(text)) + ) { + return + } + } + errorMessages.push(text) + } + }) + await use(page) + expect(errorMessages).toEqual([]) + }, +}) diff --git a/e2e/react-start/server-routes/tests/server-routes.spec.ts b/e2e/react-start/server-routes/tests/server-routes.spec.ts new file mode 100644 index 00000000000..5fa196e4e8d --- /dev/null +++ b/e2e/react-start/server-routes/tests/server-routes.spec.ts @@ -0,0 +1,17 @@ +import { expect, test } from '@playwright/test' + +test('merge-server-fn-middleware-context', async ({ page }) => { + await page.goto('/merge-server-fn-middleware-context') + + await page.waitForLoadState('networkidle') + + await page.getByTestId('test-middleware-context-btn').click() + await page.waitForLoadState('networkidle') + + await expect(page.getByTestId('has-test-parent')).toContainText('true') + await expect(page.getByTestId('has-test')).toContainText('true') + + const contextResult = await page.getByTestId('context-result').textContent() + expect(contextResult).toContain('testParent') + expect(contextResult).toContain('test') +}) \ No newline at end of file diff --git a/e2e/react-start/server-routes/tsconfig.json b/e2e/react-start/server-routes/tsconfig.json new file mode 100644 index 00000000000..d35a4b17f48 --- /dev/null +++ b/e2e/react-start/server-routes/tsconfig.json @@ -0,0 +1,23 @@ +{ + "include": ["**/*.ts", "**/*.tsx", "public/script*.js"], + "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": { + "~/*": ["./src/*"] + }, + "noEmit": true, + "types": ["vite/client"] + } +} diff --git a/e2e/react-start/server-routes/vite.config.ts b/e2e/react-start/server-routes/vite.config.ts new file mode 100644 index 00000000000..1df337cd40d --- /dev/null +++ b/e2e/react-start/server-routes/vite.config.ts @@ -0,0 +1,12 @@ +import { defineConfig } from 'vite' +import tsConfigPaths from 'vite-tsconfig-paths' +import { tanstackStart } from '@tanstack/react-start/plugin/vite' + +export default defineConfig({ + plugins: [ + tsConfigPaths({ + projects: ['./tsconfig.json'], + }), + tanstackStart(), + ], +}) diff --git a/e2e/solid-start/server-routes/.gitignore b/e2e/solid-start/server-routes/.gitignore new file mode 100644 index 00000000000..a79d5cf1299 --- /dev/null +++ b/e2e/solid-start/server-routes/.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/solid-start/server-routes/.prettierignore b/e2e/solid-start/server-routes/.prettierignore new file mode 100644 index 00000000000..2be5eaa6ece --- /dev/null +++ b/e2e/solid-start/server-routes/.prettierignore @@ -0,0 +1,4 @@ +**/build +**/public +pnpm-lock.yaml +routeTree.gen.ts \ No newline at end of file diff --git a/e2e/solid-start/server-routes/package.json b/e2e/solid-start/server-routes/package.json new file mode 100644 index 00000000000..c2bec1aa27a --- /dev/null +++ b/e2e/solid-start/server-routes/package.json @@ -0,0 +1,37 @@ +{ + "name": "tanstack-solid-start-e2e-server-routes", + "private": true, + "sideEffects": false, + "type": "module", + "scripts": { + "dev": "vite dev --port 3000", + "dev:e2e": "vite dev", + "build": "vite build && tsc --noEmit", + "start": "node .output/server/index.mjs", + "test:e2e": "playwright test --project=chromium" + }, + "dependencies": { + "@tanstack/solid-router": "workspace:^", + "@tanstack/solid-router-devtools": "workspace:^", + "@tanstack/solid-start": "workspace:^", + "js-cookie": "^3.0.5", + "redaxios": "^0.5.1", + "solid-js": "^1.9.5", + "tailwind-merge": "^2.6.0", + "vite": "6.3.5", + "zod": "^3.24.2" + }, + "devDependencies": { + "@playwright/test": "^1.50.1", + "@tanstack/router-e2e-utils": "workspace:^", + "@types/js-cookie": "^3.0.6", + "@types/node": "^22.10.2", + "autoprefixer": "^10.4.20", + "combinate": "^1.1.11", + "postcss": "^8.5.1", + "tailwindcss": "^3.4.17", + "typescript": "^5.7.2", + "vite-plugin-solid": "^2.11.6", + "vite-tsconfig-paths": "^5.1.4" + } +} diff --git a/e2e/solid-start/server-routes/playwright.config.ts b/e2e/solid-start/server-routes/playwright.config.ts new file mode 100644 index 00000000000..afe39a46b18 --- /dev/null +++ b/e2e/solid-start/server-routes/playwright.config.ts @@ -0,0 +1,35 @@ +import { defineConfig, devices } from '@playwright/test' +import { derivePort } from '@tanstack/router-e2e-utils' +import packageJson from './package.json' with { type: 'json' } + +export const PORT = derivePort(packageJson.name) +const baseURL = `http://localhost:${PORT}` + +/** + * See https://playwright.dev/docs/test-configuration. + */ +export default defineConfig({ + testDir: './tests', + workers: 1, + + reporter: [['line']], + + use: { + /* Base URL to use in actions like `await page.goto('/')`. */ + baseURL, + }, + + webServer: { + command: `pnpm build && 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/solid-start/server-routes/postcss.config.mjs b/e2e/solid-start/server-routes/postcss.config.mjs new file mode 100644 index 00000000000..2e7af2b7f1a --- /dev/null +++ b/e2e/solid-start/server-routes/postcss.config.mjs @@ -0,0 +1,6 @@ +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} diff --git a/e2e/solid-start/server-routes/public/favicon.ico b/e2e/solid-start/server-routes/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..1a1751676f7e22811b1070572093996c93c87617 GIT binary patch literal 15406 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 literal 0 HcmV?d00001 diff --git a/e2e/solid-start/server-routes/public/favicon.png b/e2e/solid-start/server-routes/public/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..1e77bc06091ade4496525a09d8900675afcf03f0 GIT binary patch literal 1507 zcmV<91swW`P)Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91AfN*P1ONa40RR91AOHXW0IY^$^8f$^O-V#SR9Fd>S3Qp$MHK9ro!#4$ zEP@L_hX|b@f=!*_42h6mKu7{)4)_U*>1>0bCkUj;Z1X!7 zHe(Ew^Oi(|bW3J~xu+)XbtFF?4>!7TH$>(D_atUQVEj(8fGvYu2NF33#JZX>)(Vj8 zIi@z>Glt?6t~;Lf(|C8F>;WF^8F<^s7Scr!sZc01uB?HMHoL5+FZ>B(g+r-)?Sn)#3Zal#?G@GAwO5U27MpGOlC2+_saA)rl zP-<@-n~;PQOlm|Hi<+W;NdR;5+=zADzM&?!+CPD36=cGwHy6!D^vPEHG?rO`K>G|M z3FposX{yT132wuw1OR3Um_5JoKB#6?!QgBupIT;?YIr;WcpmuCE>S75mZid+ens#E zGPuYjiG0UNNVWu=f!Id^?9)34)eIpu-`j_~W0iAQzK(}XYc_!;87Tk~?4tq|h=2(! zuq0HCiNK)@+ocCKR3q1REdUju>HdYxd>JX@%oOibg+J~D+}rhz54D!NfC{h-OYk{M zkzmFtdrL@nL0bm8nF@pob1CeLC>12ef#in-Bzv2!wi)Iuwq24)`AH}|0QNQ^f$KHv z?5PBPo1*#GAuAk+Poe`?UJ>mP`@~d4a(103j0lwUx@_+$#B&VC%7r>#2$HIiD`KO8L|s3Yp%M}BT0;NJDzZtPnx=4%enhU zhW*pNN0t`^4%5MKAR+}=^Q?QeqQ`>bbK zf+-ji$Uz8V0?LpX@kh`k%DL)GCA2=@SJNKg56Wh>>pr=7{1PmHqG|~=AdLV3002ov JPDHLkV1ivgp)>#h literal 0 HcmV?d00001 diff --git a/e2e/solid-start/server-routes/src/components/DefaultCatchBoundary.tsx b/e2e/solid-start/server-routes/src/components/DefaultCatchBoundary.tsx new file mode 100644 index 00000000000..32aed20e675 --- /dev/null +++ b/e2e/solid-start/server-routes/src/components/DefaultCatchBoundary.tsx @@ -0,0 +1,53 @@ +import { + ErrorComponent, + Link, + rootRouteId, + useMatch, + useRouter, +} from '@tanstack/solid-router' +import type { ErrorComponentProps } from '@tanstack/solid-router' + +export function DefaultCatchBoundary({ error }: ErrorComponentProps) { + const router = useRouter() + const isRoot = useMatch({ + strict: false, + select: (state) => state.id === rootRouteId, + }) + + console.error(error) + + return ( +
    + +
    + + {isRoot() ? ( + + Home + + ) : ( + { + e.preventDefault() + window.history.back() + }} + > + Go Back + + )} +
    +
    + ) +} diff --git a/e2e/solid-start/server-routes/src/components/NotFound.tsx b/e2e/solid-start/server-routes/src/components/NotFound.tsx new file mode 100644 index 00000000000..eb0a968d393 --- /dev/null +++ b/e2e/solid-start/server-routes/src/components/NotFound.tsx @@ -0,0 +1,25 @@ +import { Link } from '@tanstack/solid-router' + +export function NotFound({ children }: { children?: any }) { + return ( +
    +
    + {children ||

    The page you are looking for does not exist.

    } +
    +

    + + + Start Over + +

    +
    + ) +} diff --git a/e2e/solid-start/server-routes/src/routeTree.gen.ts b/e2e/solid-start/server-routes/src/routeTree.gen.ts new file mode 100644 index 00000000000..17ffa3450c0 --- /dev/null +++ b/e2e/solid-start/server-routes/src/routeTree.gen.ts @@ -0,0 +1,127 @@ +/* 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 { createServerRootRoute } from '@tanstack/solid-start/server' + +import { Route as rootRouteImport } from './routes/__root' +import { Route as MergeServerFnMiddlewareContextRouteImport } from './routes/merge-server-fn-middleware-context' +import { Route as IndexRouteImport } from './routes/index' +import { ServerRoute as ApiMiddlewareContextServerRouteImport } from './routes/api/middleware-context' + +const rootServerRouteImport = createServerRootRoute() + +const MergeServerFnMiddlewareContextRoute = + MergeServerFnMiddlewareContextRouteImport.update({ + id: '/merge-server-fn-middleware-context', + path: '/merge-server-fn-middleware-context', + getParentRoute: () => rootRouteImport, + } as any) +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => rootRouteImport, +} as any) +const ApiMiddlewareContextServerRoute = + ApiMiddlewareContextServerRouteImport.update({ + id: '/api/middleware-context', + path: '/api/middleware-context', + getParentRoute: () => rootServerRouteImport, + } as any) + +export interface FileRoutesByFullPath { + '/': typeof IndexRoute + '/merge-server-fn-middleware-context': typeof MergeServerFnMiddlewareContextRoute +} +export interface FileRoutesByTo { + '/': typeof IndexRoute + '/merge-server-fn-middleware-context': typeof MergeServerFnMiddlewareContextRoute +} +export interface FileRoutesById { + __root__: typeof rootRouteImport + '/': typeof IndexRoute + '/merge-server-fn-middleware-context': typeof MergeServerFnMiddlewareContextRoute +} +export interface FileRouteTypes { + fileRoutesByFullPath: FileRoutesByFullPath + fullPaths: '/' | '/merge-server-fn-middleware-context' + fileRoutesByTo: FileRoutesByTo + to: '/' | '/merge-server-fn-middleware-context' + id: '__root__' | '/' | '/merge-server-fn-middleware-context' + fileRoutesById: FileRoutesById +} +export interface RootRouteChildren { + IndexRoute: typeof IndexRoute + MergeServerFnMiddlewareContextRoute: typeof MergeServerFnMiddlewareContextRoute +} +export interface FileServerRoutesByFullPath { + '/api/middleware-context': typeof ApiMiddlewareContextServerRoute +} +export interface FileServerRoutesByTo { + '/api/middleware-context': typeof ApiMiddlewareContextServerRoute +} +export interface FileServerRoutesById { + __root__: typeof rootServerRouteImport + '/api/middleware-context': typeof ApiMiddlewareContextServerRoute +} +export interface FileServerRouteTypes { + fileServerRoutesByFullPath: FileServerRoutesByFullPath + fullPaths: '/api/middleware-context' + fileServerRoutesByTo: FileServerRoutesByTo + to: '/api/middleware-context' + id: '__root__' | '/api/middleware-context' + fileServerRoutesById: FileServerRoutesById +} +export interface RootServerRouteChildren { + ApiMiddlewareContextServerRoute: typeof ApiMiddlewareContextServerRoute +} + +declare module '@tanstack/solid-router' { + interface FileRoutesByPath { + '/merge-server-fn-middleware-context': { + id: '/merge-server-fn-middleware-context' + path: '/merge-server-fn-middleware-context' + fullPath: '/merge-server-fn-middleware-context' + preLoaderRoute: typeof MergeServerFnMiddlewareContextRouteImport + parentRoute: typeof rootRouteImport + } + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport + parentRoute: typeof rootRouteImport + } + } +} +declare module '@tanstack/solid-start/server' { + interface ServerFileRoutesByPath { + '/api/middleware-context': { + id: '/api/middleware-context' + path: '/api/middleware-context' + fullPath: '/api/middleware-context' + preLoaderRoute: typeof ApiMiddlewareContextServerRouteImport + parentRoute: typeof rootServerRouteImport + } + } +} + +const rootRouteChildren: RootRouteChildren = { + IndexRoute: IndexRoute, + MergeServerFnMiddlewareContextRoute: MergeServerFnMiddlewareContextRoute, +} +export const routeTree = rootRouteImport + ._addFileChildren(rootRouteChildren) + ._addFileTypes() +const rootServerRouteChildren: RootServerRouteChildren = { + ApiMiddlewareContextServerRoute: ApiMiddlewareContextServerRoute, +} +export const serverRouteTree = rootServerRouteImport + ._addFileChildren(rootServerRouteChildren) + ._addFileTypes() diff --git a/e2e/solid-start/server-routes/src/router.tsx b/e2e/solid-start/server-routes/src/router.tsx new file mode 100644 index 00000000000..c45bed4758c --- /dev/null +++ b/e2e/solid-start/server-routes/src/router.tsx @@ -0,0 +1,22 @@ +import { createRouter as createTanStackRouter } from '@tanstack/solid-router' +import { routeTree } from './routeTree.gen' +import { DefaultCatchBoundary } from './components/DefaultCatchBoundary' +import { NotFound } from './components/NotFound' + +export function createRouter() { + const router = createTanStackRouter({ + routeTree, + defaultPreload: 'intent', + defaultErrorComponent: DefaultCatchBoundary, + defaultNotFoundComponent: () => , + scrollRestoration: true, + }) + + return router +} + +declare module '@tanstack/solid-router' { + interface Register { + router: ReturnType + } +} diff --git a/e2e/solid-start/server-routes/src/routes/__root.tsx b/e2e/solid-start/server-routes/src/routes/__root.tsx new file mode 100644 index 00000000000..c1855d436ac --- /dev/null +++ b/e2e/solid-start/server-routes/src/routes/__root.tsx @@ -0,0 +1,34 @@ +import { Outlet, createRootRoute } from '@tanstack/solid-router' + +import { TanStackRouterDevtools } from '@tanstack/solid-router-devtools' +import { NotFound } from '~/components/NotFound' +import appCss from '~/styles/app.css?url' + +export const Route = createRootRoute({ + head: () => ({ + meta: [ + { + charSet: 'utf-8', + }, + { + name: 'viewport', + content: 'width=device-width, initial-scale=1', + }, + ], + links: [{ rel: 'stylesheet', href: appCss }], + }), + errorComponent: (props) => { + return

    {props.error.stack}

    + }, + notFoundComponent: () => , + component: RootComponent, +}) + +function RootComponent() { + return ( + <> + + + + ) +} diff --git a/e2e/solid-start/server-routes/src/routes/api/middleware-context.ts b/e2e/solid-start/server-routes/src/routes/api/middleware-context.ts new file mode 100644 index 00000000000..ef338345943 --- /dev/null +++ b/e2e/solid-start/server-routes/src/routes/api/middleware-context.ts @@ -0,0 +1,28 @@ +import { createServerFileRoute } from '@tanstack/solid-start/server' +import { createMiddleware, json } from '@tanstack/solid-start' + +const testParentMiddleware = createMiddleware({ type: 'request' }).server( + async ({ next }) => { + const result = await next({ context: { testParent: true } }) + return result + }, +) + +const testMiddleware = createMiddleware({ type: 'request' }) + .middleware([testParentMiddleware]) + .server(async ({ next }) => { + const result = await next({ context: { test: true } }) + return result + }) + +export const ServerRoute = createServerFileRoute('/api/middleware-context') + .middleware([testMiddleware]) + .methods({ + GET: ({ request, context }) => { + return json({ + url: request.url, + context: context, + expectedContext: { testParent: true, test: true }, + }) + }, + }) diff --git a/e2e/solid-start/server-routes/src/routes/index.tsx b/e2e/solid-start/server-routes/src/routes/index.tsx new file mode 100644 index 00000000000..8f4906f3d49 --- /dev/null +++ b/e2e/solid-start/server-routes/src/routes/index.tsx @@ -0,0 +1,20 @@ +import { Link, createFileRoute } from '@tanstack/solid-router' + +export const Route = createFileRoute('/')({ + component: Home, +}) + +function Home() { + return ( +
    +

    Server routes E2E tests

    +
      +
    • + + server function middleware context is merged correctly + +
    • +
    +
    + ) +} diff --git a/e2e/solid-start/server-routes/src/routes/merge-server-fn-middleware-context.tsx b/e2e/solid-start/server-routes/src/routes/merge-server-fn-middleware-context.tsx new file mode 100644 index 00000000000..a8aa2db9664 --- /dev/null +++ b/e2e/solid-start/server-routes/src/routes/merge-server-fn-middleware-context.tsx @@ -0,0 +1,68 @@ +import { createFileRoute } from '@tanstack/solid-router' +import { createSignal } from 'solid-js' + +export const Route = createFileRoute('/merge-server-fn-middleware-context')({ + component: () => , +}) + +function MergeServerFnMiddlewareContext() { + const [apiResponse, setApiResponse] = createSignal(null) + + const fetchMiddlewareContext = async () => { + try { + const response = await fetch('/api/middleware-context') + const data = await response.json() + setApiResponse(data) + } catch (error) { + console.error('Error fetching middleware context:', error) + setApiResponse({ error: 'Failed to fetch' }) + } + } + + return ( +
    +

    Merge Server Function Middleware Context Test

    +
    + + + {apiResponse() && ( +
    +

    API Response:

    +
    +              {JSON.stringify(apiResponse(), null, 2)}
    +            
    + +
    +

    Context Verification:

    +
    + {JSON.stringify(apiResponse()?.context, null, 2)} +
    + +
    + Has testParent:{' '} + {apiResponse()?.context?.testParent ? 'true' : 'false'} +
    + +
    + Has test: {apiResponse()?.context?.test ? 'true' : 'false'} +
    +
    +
    + )} +
    +
    + ) +} diff --git a/e2e/solid-start/server-routes/src/styles/app.css b/e2e/solid-start/server-routes/src/styles/app.css new file mode 100644 index 00000000000..c53c8706654 --- /dev/null +++ b/e2e/solid-start/server-routes/src/styles/app.css @@ -0,0 +1,22 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +@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/solid-start/server-routes/src/vite-env.d.ts b/e2e/solid-start/server-routes/src/vite-env.d.ts new file mode 100644 index 00000000000..0b2af560d60 --- /dev/null +++ b/e2e/solid-start/server-routes/src/vite-env.d.ts @@ -0,0 +1,4 @@ +declare module '*?url' { + const url: string + export default url +} diff --git a/e2e/solid-start/server-routes/tailwind.config.mjs b/e2e/solid-start/server-routes/tailwind.config.mjs new file mode 100644 index 00000000000..e49f4eb776e --- /dev/null +++ b/e2e/solid-start/server-routes/tailwind.config.mjs @@ -0,0 +1,4 @@ +/** @type {import('tailwindcss').Config} */ +export default { + content: ['./src/**/*.{js,jsx,ts,tsx}'], +} diff --git a/e2e/solid-start/server-routes/tests/fixture.ts b/e2e/solid-start/server-routes/tests/fixture.ts new file mode 100644 index 00000000000..abb7b1d564d --- /dev/null +++ b/e2e/solid-start/server-routes/tests/fixture.ts @@ -0,0 +1,28 @@ +import { test as base, expect } from '@playwright/test' + +export interface TestFixtureOptions { + whitelistErrors: Array +} +export const test = base.extend({ + whitelistErrors: [[], { option: true }], + page: async ({ page, whitelistErrors }, use) => { + const errorMessages: Array = [] + page.on('console', (m) => { + if (m.type() === 'error') { + const text = m.text() + for (const whitelistError of whitelistErrors) { + if ( + (typeof whitelistError === 'string' && + text.includes(whitelistError)) || + (whitelistError instanceof RegExp && whitelistError.test(text)) + ) { + return + } + } + errorMessages.push(text) + } + }) + await use(page) + expect(errorMessages).toEqual([]) + }, +}) diff --git a/e2e/solid-start/server-routes/tests/server-routes.spec.ts b/e2e/solid-start/server-routes/tests/server-routes.spec.ts new file mode 100644 index 00000000000..5fa196e4e8d --- /dev/null +++ b/e2e/solid-start/server-routes/tests/server-routes.spec.ts @@ -0,0 +1,17 @@ +import { expect, test } from '@playwright/test' + +test('merge-server-fn-middleware-context', async ({ page }) => { + await page.goto('/merge-server-fn-middleware-context') + + await page.waitForLoadState('networkidle') + + await page.getByTestId('test-middleware-context-btn').click() + await page.waitForLoadState('networkidle') + + await expect(page.getByTestId('has-test-parent')).toContainText('true') + await expect(page.getByTestId('has-test')).toContainText('true') + + const contextResult = await page.getByTestId('context-result').textContent() + expect(contextResult).toContain('testParent') + expect(contextResult).toContain('test') +}) \ No newline at end of file diff --git a/e2e/solid-start/server-routes/tsconfig.json b/e2e/solid-start/server-routes/tsconfig.json new file mode 100644 index 00000000000..57ea27b2868 --- /dev/null +++ b/e2e/solid-start/server-routes/tsconfig.json @@ -0,0 +1,23 @@ +{ + "include": ["**/*.ts", "**/*.tsx", "public/script*.js"], + "compilerOptions": { + "strict": true, + "esModuleInterop": true, + "jsx": "preserve", + "jsxImportSource": "solid-js", + "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 + } +} diff --git a/e2e/solid-start/server-routes/vite.config.ts b/e2e/solid-start/server-routes/vite.config.ts new file mode 100644 index 00000000000..bae1bfaad6e --- /dev/null +++ b/e2e/solid-start/server-routes/vite.config.ts @@ -0,0 +1,15 @@ +import { defineConfig } from 'vite' +import tsConfigPaths from 'vite-tsconfig-paths' +import { tanstackStart } from '@tanstack/solid-start/plugin/vite' + +export default defineConfig({ + server: { + port: 3000, + }, + plugins: [ + tsConfigPaths({ + projects: ['./tsconfig.json'], + }), + tanstackStart(), + ], +}) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 92cd28eea78..7f8b2c26317 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1484,6 +1484,79 @@ importers: specifier: ^5.1.4 version: 5.1.4(typescript@5.8.2)(vite@6.3.5(@types/node@22.13.4)(jiti@2.4.2)(lightningcss@1.29.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.7.0)) + e2e/react-start/server-routes: + dependencies: + '@tanstack/react-router': + specifier: workspace:* + version: link:../../../packages/react-router + '@tanstack/react-router-devtools': + specifier: workspace:^ + version: link:../../../packages/react-router-devtools + '@tanstack/react-start': + specifier: workspace:* + version: link:../../../packages/react-start + js-cookie: + specifier: ^3.0.5 + version: 3.0.5 + react: + specifier: ^19.0.0 + version: 19.0.0 + react-dom: + specifier: ^19.0.0 + version: 19.0.0(react@19.0.0) + redaxios: + specifier: ^0.5.1 + version: 0.5.1 + tailwind-merge: + specifier: ^2.6.0 + version: 2.6.0 + vite: + specifier: 6.3.5 + version: 6.3.5(@types/node@22.13.4)(jiti@2.4.2)(lightningcss@1.29.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.7.0) + zod: + specifier: ^3.24.2 + version: 3.25.57 + devDependencies: + '@playwright/test': + specifier: ^1.52.0 + version: 1.52.0 + '@tanstack/router-e2e-utils': + specifier: workspace:^ + version: link:../../e2e-utils + '@types/js-cookie': + specifier: ^3.0.6 + version: 3.0.6 + '@types/node': + specifier: ^22.10.2 + version: 22.13.4 + '@types/react': + specifier: ^19.0.8 + version: 19.0.8 + '@types/react-dom': + specifier: ^19.0.3 + version: 19.0.3(@types/react@19.0.8) + '@vitejs/plugin-react': + specifier: ^4.3.4 + version: 4.6.0(vite@6.3.5(@types/node@22.13.4)(jiti@2.4.2)(lightningcss@1.29.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.7.0)) + autoprefixer: + specifier: ^10.4.20 + version: 10.4.20(postcss@8.5.3) + combinate: + specifier: ^1.1.11 + version: 1.1.11 + postcss: + specifier: ^8.5.1 + version: 8.5.3 + tailwindcss: + specifier: ^3.4.17 + version: 3.4.17 + typescript: + specifier: ^5.7.2 + version: 5.8.3 + vite-tsconfig-paths: + specifier: ^5.1.4 + version: 5.1.4(typescript@5.8.3)(vite@6.3.5(@types/node@22.13.4)(jiti@2.4.2)(lightningcss@1.29.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.7.0)) + e2e/react-start/spa-mode: dependencies: '@tanstack/react-router': @@ -2498,6 +2571,70 @@ importers: specifier: ^5.1.4 version: 5.1.4(typescript@5.8.2)(vite@6.3.5(@types/node@22.13.4)(jiti@2.4.2)(lightningcss@1.29.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.7.0)) + e2e/solid-start/server-routes: + dependencies: + '@tanstack/solid-router': + specifier: workspace:^ + version: link:../../../packages/solid-router + '@tanstack/solid-router-devtools': + specifier: workspace:^ + version: link:../../../packages/solid-router-devtools + '@tanstack/solid-start': + specifier: workspace:* + version: link:../../../packages/solid-start + js-cookie: + specifier: ^3.0.5 + version: 3.0.5 + redaxios: + specifier: ^0.5.1 + version: 0.5.1 + solid-js: + specifier: ^1.9.5 + version: 1.9.5 + tailwind-merge: + specifier: ^2.6.0 + version: 2.6.0 + vite: + specifier: 6.3.5 + version: 6.3.5(@types/node@22.13.4)(jiti@2.4.2)(lightningcss@1.29.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.7.0) + zod: + specifier: ^3.24.2 + version: 3.25.57 + devDependencies: + '@playwright/test': + specifier: ^1.52.0 + version: 1.52.0 + '@tanstack/router-e2e-utils': + specifier: workspace:^ + version: link:../../e2e-utils + '@types/js-cookie': + specifier: ^3.0.6 + version: 3.0.6 + '@types/node': + specifier: ^22.10.2 + version: 22.13.4 + autoprefixer: + specifier: ^10.4.20 + version: 10.4.20(postcss@8.5.3) + combinate: + specifier: ^1.1.11 + version: 1.1.11 + postcss: + specifier: ^8.5.1 + version: 8.5.3 + tailwindcss: + specifier: ^3.4.17 + version: 3.4.17 + typescript: + specifier: ^5.7.2 + version: 5.8.3 + vite-plugin-solid: + specifier: ^2.11.6 + version: 2.11.7(@testing-library/jest-dom@6.6.3)(solid-js@1.9.5)(vite@6.3.5(@types/node@22.13.4)(jiti@2.4.2)(lightningcss@1.29.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.7.0)) + vite-tsconfig-paths: + specifier: ^5.1.4 + version: 5.1.4(typescript@5.8.3)(vite@6.3.5(@types/node@22.13.4)(jiti@2.4.2)(lightningcss@1.29.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.7.0)) + e2e/solid-start/spa-mode: dependencies: '@tanstack/solid-router': From 5db3e1d94a6ea01463be9f691a38d7042bd41048 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Thu, 17 Jul 2025 02:22:45 +0000 Subject: [PATCH 4/7] ci: apply automated fixes --- e2e/react-start/server-functions/tests/server-functions.spec.ts | 1 - e2e/react-start/server-routes/tests/server-routes.spec.ts | 2 +- e2e/solid-start/server-routes/tests/server-routes.spec.ts | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/e2e/react-start/server-functions/tests/server-functions.spec.ts b/e2e/react-start/server-functions/tests/server-functions.spec.ts index e73ec0e4464..8a5fc524cc2 100644 --- a/e2e/react-start/server-functions/tests/server-functions.spec.ts +++ b/e2e/react-start/server-functions/tests/server-functions.spec.ts @@ -315,7 +315,6 @@ test('raw response', async ({ page }) => { await expect(page.getByTestId('response')).toContainText(expectedValue) }) - ;[{ mode: 'js' }, { mode: 'no-js' }].forEach(({ mode }) => { test(`Server function can redirect when sending formdata: mode = ${mode}`, async ({ page, diff --git a/e2e/react-start/server-routes/tests/server-routes.spec.ts b/e2e/react-start/server-routes/tests/server-routes.spec.ts index 5fa196e4e8d..098a40b2efe 100644 --- a/e2e/react-start/server-routes/tests/server-routes.spec.ts +++ b/e2e/react-start/server-routes/tests/server-routes.spec.ts @@ -14,4 +14,4 @@ test('merge-server-fn-middleware-context', async ({ page }) => { const contextResult = await page.getByTestId('context-result').textContent() expect(contextResult).toContain('testParent') expect(contextResult).toContain('test') -}) \ No newline at end of file +}) diff --git a/e2e/solid-start/server-routes/tests/server-routes.spec.ts b/e2e/solid-start/server-routes/tests/server-routes.spec.ts index 5fa196e4e8d..098a40b2efe 100644 --- a/e2e/solid-start/server-routes/tests/server-routes.spec.ts +++ b/e2e/solid-start/server-routes/tests/server-routes.spec.ts @@ -14,4 +14,4 @@ test('merge-server-fn-middleware-context', async ({ page }) => { const contextResult = await page.getByTestId('context-result').textContent() expect(contextResult).toContain('testParent') expect(contextResult).toContain('test') -}) \ No newline at end of file +}) From e2c16ca1d94e99742f9e7474156c42f9bbc76bdd Mon Sep 17 00:00:00 2001 From: wookhyung Date: Thu, 17 Jul 2025 15:18:47 +0900 Subject: [PATCH 5/7] fix: remove merge server function middleware context --- .../server-functions/src/routeTree.gen.ts | 71 ------------------- .../src/routes/api/middleware-context.ts | 28 -------- .../server-functions/src/routes/index.tsx | 5 -- .../merge-server-fn-middleware-context.tsx | 67 ----------------- 4 files changed, 171 deletions(-) delete mode 100644 e2e/react-start/server-functions/src/routes/api/middleware-context.ts delete mode 100644 e2e/react-start/server-functions/src/routes/merge-server-fn-middleware-context.tsx diff --git a/e2e/react-start/server-functions/src/routeTree.gen.ts b/e2e/react-start/server-functions/src/routeTree.gen.ts index a9243a658b1..a80a2843b62 100644 --- a/e2e/react-start/server-functions/src/routeTree.gen.ts +++ b/e2e/react-start/server-functions/src/routeTree.gen.ts @@ -8,8 +8,6 @@ // 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 { createServerRootRoute } from '@tanstack/react-start/server' - import { Route as rootRouteImport } from './routes/__root' import { Route as SubmitPostFormdataRouteImport } from './routes/submit-post-formdata' import { Route as StatusRouteImport } from './routes/status' @@ -17,7 +15,6 @@ import { Route as SerializeFormDataRouteImport } from './routes/serialize-form-d import { Route as ReturnNullRouteImport } from './routes/return-null' import { Route as RawResponseRouteImport } from './routes/raw-response' import { Route as MultipartRouteImport } from './routes/multipart' -import { Route as MergeServerFnMiddlewareContextRouteImport } from './routes/merge-server-fn-middleware-context' import { Route as IsomorphicFnsRouteImport } from './routes/isomorphic-fns' import { Route as HeadersRouteImport } from './routes/headers' import { Route as EnvOnlyRouteImport } from './routes/env-only' @@ -29,9 +26,6 @@ import { Route as FormdataRedirectIndexRouteImport } from './routes/formdata-red import { Route as CookiesIndexRouteImport } from './routes/cookies/index' import { Route as CookiesSetRouteImport } from './routes/cookies/set' import { Route as FormdataRedirectTargetNameRouteImport } from './routes/formdata-redirect/target.$name' -import { ServerRoute as ApiMiddlewareContextServerRouteImport } from './routes/api/middleware-context' - -const rootServerRouteImport = createServerRootRoute() const SubmitPostFormdataRoute = SubmitPostFormdataRouteImport.update({ id: '/submit-post-formdata', @@ -63,12 +57,6 @@ const MultipartRoute = MultipartRouteImport.update({ path: '/multipart', getParentRoute: () => rootRouteImport, } as any) -const MergeServerFnMiddlewareContextRoute = - MergeServerFnMiddlewareContextRouteImport.update({ - id: '/merge-server-fn-middleware-context', - path: '/merge-server-fn-middleware-context', - getParentRoute: () => rootRouteImport, - } as any) const IsomorphicFnsRoute = IsomorphicFnsRouteImport.update({ id: '/isomorphic-fns', path: '/isomorphic-fns', @@ -125,12 +113,6 @@ const FormdataRedirectTargetNameRoute = path: '/formdata-redirect/target/$name', getParentRoute: () => rootRouteImport, } as any) -const ApiMiddlewareContextServerRoute = - ApiMiddlewareContextServerRouteImport.update({ - id: '/api/middleware-context', - path: '/api/middleware-context', - getParentRoute: () => rootServerRouteImport, - } as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -140,7 +122,6 @@ export interface FileRoutesByFullPath { '/env-only': typeof EnvOnlyRoute '/headers': typeof HeadersRoute '/isomorphic-fns': typeof IsomorphicFnsRoute - '/merge-server-fn-middleware-context': typeof MergeServerFnMiddlewareContextRoute '/multipart': typeof MultipartRoute '/raw-response': typeof RawResponseRoute '/return-null': typeof ReturnNullRoute @@ -160,7 +141,6 @@ export interface FileRoutesByTo { '/env-only': typeof EnvOnlyRoute '/headers': typeof HeadersRoute '/isomorphic-fns': typeof IsomorphicFnsRoute - '/merge-server-fn-middleware-context': typeof MergeServerFnMiddlewareContextRoute '/multipart': typeof MultipartRoute '/raw-response': typeof RawResponseRoute '/return-null': typeof ReturnNullRoute @@ -181,7 +161,6 @@ export interface FileRoutesById { '/env-only': typeof EnvOnlyRoute '/headers': typeof HeadersRoute '/isomorphic-fns': typeof IsomorphicFnsRoute - '/merge-server-fn-middleware-context': typeof MergeServerFnMiddlewareContextRoute '/multipart': typeof MultipartRoute '/raw-response': typeof RawResponseRoute '/return-null': typeof ReturnNullRoute @@ -203,7 +182,6 @@ export interface FileRouteTypes { | '/env-only' | '/headers' | '/isomorphic-fns' - | '/merge-server-fn-middleware-context' | '/multipart' | '/raw-response' | '/return-null' @@ -223,7 +201,6 @@ export interface FileRouteTypes { | '/env-only' | '/headers' | '/isomorphic-fns' - | '/merge-server-fn-middleware-context' | '/multipart' | '/raw-response' | '/return-null' @@ -243,7 +220,6 @@ export interface FileRouteTypes { | '/env-only' | '/headers' | '/isomorphic-fns' - | '/merge-server-fn-middleware-context' | '/multipart' | '/raw-response' | '/return-null' @@ -264,7 +240,6 @@ export interface RootRouteChildren { EnvOnlyRoute: typeof EnvOnlyRoute HeadersRoute: typeof HeadersRoute IsomorphicFnsRoute: typeof IsomorphicFnsRoute - MergeServerFnMiddlewareContextRoute: typeof MergeServerFnMiddlewareContextRoute MultipartRoute: typeof MultipartRoute RawResponseRoute: typeof RawResponseRoute ReturnNullRoute: typeof ReturnNullRoute @@ -276,27 +251,6 @@ export interface RootRouteChildren { FormdataRedirectIndexRoute: typeof FormdataRedirectIndexRoute FormdataRedirectTargetNameRoute: typeof FormdataRedirectTargetNameRoute } -export interface FileServerRoutesByFullPath { - '/api/middleware-context': typeof ApiMiddlewareContextServerRoute -} -export interface FileServerRoutesByTo { - '/api/middleware-context': typeof ApiMiddlewareContextServerRoute -} -export interface FileServerRoutesById { - __root__: typeof rootServerRouteImport - '/api/middleware-context': typeof ApiMiddlewareContextServerRoute -} -export interface FileServerRouteTypes { - fileServerRoutesByFullPath: FileServerRoutesByFullPath - fullPaths: '/api/middleware-context' - fileServerRoutesByTo: FileServerRoutesByTo - to: '/api/middleware-context' - id: '__root__' | '/api/middleware-context' - fileServerRoutesById: FileServerRoutesById -} -export interface RootServerRouteChildren { - ApiMiddlewareContextServerRoute: typeof ApiMiddlewareContextServerRoute -} declare module '@tanstack/react-router' { interface FileRoutesByPath { @@ -342,13 +296,6 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof MultipartRouteImport parentRoute: typeof rootRouteImport } - '/merge-server-fn-middleware-context': { - id: '/merge-server-fn-middleware-context' - path: '/merge-server-fn-middleware-context' - fullPath: '/merge-server-fn-middleware-context' - preLoaderRoute: typeof MergeServerFnMiddlewareContextRouteImport - parentRoute: typeof rootRouteImport - } '/isomorphic-fns': { id: '/isomorphic-fns' path: '/isomorphic-fns' @@ -428,17 +375,6 @@ declare module '@tanstack/react-router' { } } } -declare module '@tanstack/react-start/server' { - interface ServerFileRoutesByPath { - '/api/middleware-context': { - id: '/api/middleware-context' - path: '/api/middleware-context' - fullPath: '/api/middleware-context' - preLoaderRoute: typeof ApiMiddlewareContextServerRouteImport - parentRoute: typeof rootServerRouteImport - } - } -} const rootRouteChildren: RootRouteChildren = { IndexRoute: IndexRoute, @@ -448,7 +384,6 @@ const rootRouteChildren: RootRouteChildren = { EnvOnlyRoute: EnvOnlyRoute, HeadersRoute: HeadersRoute, IsomorphicFnsRoute: IsomorphicFnsRoute, - MergeServerFnMiddlewareContextRoute: MergeServerFnMiddlewareContextRoute, MultipartRoute: MultipartRoute, RawResponseRoute: RawResponseRoute, ReturnNullRoute: ReturnNullRoute, @@ -463,9 +398,3 @@ const rootRouteChildren: RootRouteChildren = { export const routeTree = rootRouteImport ._addFileChildren(rootRouteChildren) ._addFileTypes() -const rootServerRouteChildren: RootServerRouteChildren = { - ApiMiddlewareContextServerRoute: ApiMiddlewareContextServerRoute, -} -export const serverRouteTree = rootServerRouteImport - ._addFileChildren(rootServerRouteChildren) - ._addFileTypes() diff --git a/e2e/react-start/server-functions/src/routes/api/middleware-context.ts b/e2e/react-start/server-functions/src/routes/api/middleware-context.ts deleted file mode 100644 index bc75e29126d..00000000000 --- a/e2e/react-start/server-functions/src/routes/api/middleware-context.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { createServerFileRoute } from '@tanstack/react-start/server' -import { createMiddleware, json } from '@tanstack/react-start' - -const testParentMiddleware = createMiddleware({ type: 'request' }).server( - async ({ next }) => { - const result = await next({ context: { testParent: true } }) - return result - }, -) - -const testMiddleware = createMiddleware({ type: 'request' }) - .middleware([testParentMiddleware]) - .server(async ({ next }) => { - const result = await next({ context: { test: true } }) - return result - }) - -export const ServerRoute = createServerFileRoute('/api/middleware-context') - .middleware([testMiddleware]) - .methods({ - GET: ({ request, context }) => { - return json({ - url: request.url, - context: context, - expectedContext: { testParent: true, test: true }, - }) - }, - }) diff --git a/e2e/react-start/server-functions/src/routes/index.tsx b/e2e/react-start/server-functions/src/routes/index.tsx index 15cd99d8f73..61195d1fc53 100644 --- a/e2e/react-start/server-functions/src/routes/index.tsx +++ b/e2e/react-start/server-functions/src/routes/index.tsx @@ -82,11 +82,6 @@ function Home() { server function redirects when FormData is submitted (via no-JS) -
  • - - server function middleware context is merged correctly - -
  • ) diff --git a/e2e/react-start/server-functions/src/routes/merge-server-fn-middleware-context.tsx b/e2e/react-start/server-functions/src/routes/merge-server-fn-middleware-context.tsx deleted file mode 100644 index 62a3d6be44e..00000000000 --- a/e2e/react-start/server-functions/src/routes/merge-server-fn-middleware-context.tsx +++ /dev/null @@ -1,67 +0,0 @@ -import { createFileRoute } from '@tanstack/react-router' -import * as React from 'react' - -export const Route = createFileRoute('/merge-server-fn-middleware-context')({ - component: () => , -}) - -function MergeServerFnMiddlewareContext() { - const [apiResponse, setApiResponse] = React.useState(null) - - const fetchMiddlewareContext = async () => { - try { - const response = await fetch('/api/middleware-context') - const data = await response.json() - setApiResponse(data) - } catch (error) { - console.error('Error fetching middleware context:', error) - setApiResponse({ error: 'Failed to fetch' }) - } - } - - return ( -
    -

    Merge Server Function Middleware Context Test

    -
    - - - {apiResponse && ( -
    -

    API Response:

    -
    -              {JSON.stringify(apiResponse, null, 2)}
    -            
    - -
    -

    Context Verification:

    -
    - {JSON.stringify(apiResponse.context, null, 2)} -
    - -
    - Has testParent:{' '} - {apiResponse.context?.testParent ? 'true' : 'false'} -
    - -
    - Has test: {apiResponse.context?.test ? 'true' : 'false'} -
    -
    -
    - )} -
    -
    - ) -} From 6f70391c74a7f837cb7e31d8e095a4f9bc505584 Mon Sep 17 00:00:00 2001 From: wookhyung Date: Thu, 17 Jul 2025 15:31:38 +0900 Subject: [PATCH 6/7] trigger From 6233165cf971a45836e56880ca58205ad321d513 Mon Sep 17 00:00:00 2001 From: wookhyung Date: Thu, 17 Jul 2025 16:23:26 +0900 Subject: [PATCH 7/7] fix: add await for derivePort --- e2e/react-start/server-routes/playwright.config.ts | 2 +- e2e/solid-start/server-routes/playwright.config.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/e2e/react-start/server-routes/playwright.config.ts b/e2e/react-start/server-routes/playwright.config.ts index 88ff69f92e7..5fa107c0202 100644 --- a/e2e/react-start/server-routes/playwright.config.ts +++ b/e2e/react-start/server-routes/playwright.config.ts @@ -2,7 +2,7 @@ import { defineConfig, devices } from '@playwright/test' import { derivePort } from '@tanstack/router-e2e-utils' import packageJson from './package.json' with { type: 'json' } -export const PORT = derivePort(packageJson.name) +export const PORT = await derivePort(packageJson.name) const baseURL = `http://localhost:${PORT}` /** diff --git a/e2e/solid-start/server-routes/playwright.config.ts b/e2e/solid-start/server-routes/playwright.config.ts index afe39a46b18..953f7bd4971 100644 --- a/e2e/solid-start/server-routes/playwright.config.ts +++ b/e2e/solid-start/server-routes/playwright.config.ts @@ -2,7 +2,7 @@ import { defineConfig, devices } from '@playwright/test' import { derivePort } from '@tanstack/router-e2e-utils' import packageJson from './package.json' with { type: 'json' } -export const PORT = derivePort(packageJson.name) +export const PORT = await derivePort(packageJson.name) const baseURL = `http://localhost:${PORT}` /**