diff --git a/docs/start/framework/react/getting-started.md b/docs/start/framework/react/getting-started.md index 565dc4b4cf..0320fdab4e 100644 --- a/docs/start/framework/react/getting-started.md +++ b/docs/start/framework/react/getting-started.md @@ -39,6 +39,7 @@ To use a different example, replace `start-basic` with one of the slugs below. #### Examples - [Basic](https://github.com/TanStack/router/tree/main/examples/react/start-basic) (`start-basic`) +- [Basic + Rsbuild](https://github.com/TanStack/router/tree/main/examples/react/start-basic-rsbuild) (`start-basic-rsbuild`) - [Basic + Auth](https://github.com/TanStack/router/tree/main/examples/react/start-basic-auth) (`start-basic-auth`) - [Counter](https://github.com/TanStack/router/tree/main/examples/react/start-counter) (`start-counter`) - [Basic + React Query](https://github.com/TanStack/router/tree/main/examples/react/start-basic-react-query) (`start-basic-react-query`) diff --git a/examples/react/start-basic-rsbuild/.devcontainer/devcontainer.json b/examples/react/start-basic-rsbuild/.devcontainer/devcontainer.json new file mode 100644 index 0000000000..e5dafce188 --- /dev/null +++ b/examples/react/start-basic-rsbuild/.devcontainer/devcontainer.json @@ -0,0 +1,3 @@ +{ + "image": "mcr.microsoft.com/devcontainers/typescript-node:24" +} diff --git a/examples/react/start-basic-rsbuild/.gitignore b/examples/react/start-basic-rsbuild/.gitignore new file mode 100644 index 0000000000..1f189d9bc0 --- /dev/null +++ b/examples/react/start-basic-rsbuild/.gitignore @@ -0,0 +1,19 @@ +node_modules +package-lock.json +yarn.lock + +.DS_Store +.cache +.env +.vercel +dist +/build/ +/api/ +/server/build +/public/build# Sentry Config File +.env.sentry-build-plugin +/test-results/ +/playwright-report/ +/blob-report/ +/playwright/.cache/ +.tanstack diff --git a/examples/react/start-basic-rsbuild/.prettierignore b/examples/react/start-basic-rsbuild/.prettierignore new file mode 100644 index 0000000000..2be5eaa6ec --- /dev/null +++ b/examples/react/start-basic-rsbuild/.prettierignore @@ -0,0 +1,4 @@ +**/build +**/public +pnpm-lock.yaml +routeTree.gen.ts \ No newline at end of file diff --git a/examples/react/start-basic-rsbuild/.vscode/settings.json b/examples/react/start-basic-rsbuild/.vscode/settings.json new file mode 100644 index 0000000000..00b5278e58 --- /dev/null +++ b/examples/react/start-basic-rsbuild/.vscode/settings.json @@ -0,0 +1,11 @@ +{ + "files.watcherExclude": { + "**/routeTree.gen.ts": true + }, + "search.exclude": { + "**/routeTree.gen.ts": true + }, + "files.readonlyInclude": { + "**/routeTree.gen.ts": true + } +} diff --git a/examples/react/start-basic-rsbuild/README.md b/examples/react/start-basic-rsbuild/README.md new file mode 100644 index 0000000000..64117c915b --- /dev/null +++ b/examples/react/start-basic-rsbuild/README.md @@ -0,0 +1,36 @@ +# TanStack Start - Basic Rsbuild Example + +This is the basic TanStack Start Rsbuild example, demonstrating the fundamentals of building applications with TanStack Router and TanStack Start. + +- [TanStack Router Docs](https://tanstack.com/router) + +It's deployed automagically with Netlify! + +- [Netlify](https://netlify.com/) + +## Start a new project based on this example + +To start a new project based on this example, run: + +```sh +npx gitpick TanStack/router/tree/main/examples/react/start-basic-rsbuild start-basic-rsbuild +``` + +## Getting Started + +From your terminal: + +```sh +pnpm install +pnpm dev +``` + +This starts your app in development mode, rebuilding assets on file changes. + +## Build + +To build the app for production: + +```sh +pnpm build +``` diff --git a/examples/react/start-basic-rsbuild/package.json b/examples/react/start-basic-rsbuild/package.json new file mode 100644 index 0000000000..2bbd7496e4 --- /dev/null +++ b/examples/react/start-basic-rsbuild/package.json @@ -0,0 +1,32 @@ +{ + "name": "tanstack-start-example-basic-rsbuild", + "private": true, + "sideEffects": false, + "type": "module", + "scripts": { + "dev": "rsbuild dev --port 3000", + "build": "rsbuild build && tsc --noEmit", + "preview": "rsbuild preview", + "start": "srvx --prod -s ../client dist/server/index.js" + }, + "dependencies": { + "@tanstack/react-router": "workspace:*", + "@tanstack/react-router-devtools": "workspace:^", + "@tanstack/react-start": "workspace:*", + "react": "^19.2.3", + "react-dom": "^19.2.3", + "tailwind-merge": "^3.6.0", + "zod": "^4.4.3" + }, + "devDependencies": { + "@rsbuild/core": "^2.0.11", + "@rsbuild/plugin-react": "^2.0.0", + "@rsbuild/plugin-tailwindcss": "^2.0.3", + "@types/node": "25.0.9", + "@types/react": "^19.2.8", + "@types/react-dom": "^19.2.3", + "srvx": "^0.11.18", + "tailwindcss": "^4.2.2", + "typescript": "^6.0.2" + } +} diff --git a/examples/react/start-basic-rsbuild/public/android-chrome-192x192.png b/examples/react/start-basic-rsbuild/public/android-chrome-192x192.png new file mode 100644 index 0000000000..09c8324f8c Binary files /dev/null and b/examples/react/start-basic-rsbuild/public/android-chrome-192x192.png differ diff --git a/examples/react/start-basic-rsbuild/public/android-chrome-512x512.png b/examples/react/start-basic-rsbuild/public/android-chrome-512x512.png new file mode 100644 index 0000000000..11d626ea3d Binary files /dev/null and b/examples/react/start-basic-rsbuild/public/android-chrome-512x512.png differ diff --git a/examples/react/start-basic-rsbuild/public/apple-touch-icon.png b/examples/react/start-basic-rsbuild/public/apple-touch-icon.png new file mode 100644 index 0000000000..5a9423cc02 Binary files /dev/null and b/examples/react/start-basic-rsbuild/public/apple-touch-icon.png differ diff --git a/examples/react/start-basic-rsbuild/public/favicon-16x16.png b/examples/react/start-basic-rsbuild/public/favicon-16x16.png new file mode 100644 index 0000000000..e3389b0044 Binary files /dev/null and b/examples/react/start-basic-rsbuild/public/favicon-16x16.png differ diff --git a/examples/react/start-basic-rsbuild/public/favicon-32x32.png b/examples/react/start-basic-rsbuild/public/favicon-32x32.png new file mode 100644 index 0000000000..900c77d444 Binary files /dev/null and b/examples/react/start-basic-rsbuild/public/favicon-32x32.png differ diff --git a/examples/react/start-basic-rsbuild/public/favicon.ico b/examples/react/start-basic-rsbuild/public/favicon.ico new file mode 100644 index 0000000000..1a1751676f Binary files /dev/null and b/examples/react/start-basic-rsbuild/public/favicon.ico differ diff --git a/examples/react/start-basic-rsbuild/public/favicon.png b/examples/react/start-basic-rsbuild/public/favicon.png new file mode 100644 index 0000000000..1e77bc0609 Binary files /dev/null and b/examples/react/start-basic-rsbuild/public/favicon.png differ diff --git a/examples/react/start-basic-rsbuild/public/site.webmanifest b/examples/react/start-basic-rsbuild/public/site.webmanifest new file mode 100644 index 0000000000..fa99de77db --- /dev/null +++ b/examples/react/start-basic-rsbuild/public/site.webmanifest @@ -0,0 +1,19 @@ +{ + "name": "", + "short_name": "", + "icons": [ + { + "src": "/android-chrome-192x192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "/android-chrome-512x512.png", + "sizes": "512x512", + "type": "image/png" + } + ], + "theme_color": "#ffffff", + "background_color": "#ffffff", + "display": "standalone" +} diff --git a/examples/react/start-basic-rsbuild/rsbuild.config.ts b/examples/react/start-basic-rsbuild/rsbuild.config.ts new file mode 100644 index 0000000000..3705696818 --- /dev/null +++ b/examples/react/start-basic-rsbuild/rsbuild.config.ts @@ -0,0 +1,17 @@ +import { tanstackStart } from '@tanstack/react-start/plugin/rsbuild' +import { defineConfig } from '@rsbuild/core' +import { pluginReact } from '@rsbuild/plugin-react' +import { pluginTailwindcss } from '@rsbuild/plugin-tailwindcss' + +export default defineConfig({ + server: { + port: 3000, + }, + plugins: [ + pluginTailwindcss(), + pluginReact(), + tanstackStart({ + srcDirectory: 'src', + }), + ], +}) diff --git a/examples/react/start-basic-rsbuild/src/components/DefaultCatchBoundary.tsx b/examples/react/start-basic-rsbuild/src/components/DefaultCatchBoundary.tsx new file mode 100644 index 0000000000..4a22649ed4 --- /dev/null +++ b/examples/react/start-basic-rsbuild/src/components/DefaultCatchBoundary.tsx @@ -0,0 +1,51 @@ +import { + ErrorComponent, + Link, + useLocation, + useRouter, +} from '@tanstack/react-router' +import type { ErrorComponentProps } from '@tanstack/react-router' + +export function DefaultCatchBoundary({ error }: ErrorComponentProps) { + const router = useRouter() + const isRoot = useLocation({ + select: (location) => location.pathname === '/', + }) + + console.error('DefaultCatchBoundary Error:', error) + + return ( +
+ +
+ + {isRoot ? ( + + Home + + ) : ( + { + e.preventDefault() + window.history.back() + }} + > + Go Back + + )} +
+
+ ) +} diff --git a/examples/react/start-basic-rsbuild/src/components/NotFound.tsx b/examples/react/start-basic-rsbuild/src/components/NotFound.tsx new file mode 100644 index 0000000000..fd4e75befe --- /dev/null +++ b/examples/react/start-basic-rsbuild/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/examples/react/start-basic-rsbuild/src/components/PostError.tsx b/examples/react/start-basic-rsbuild/src/components/PostError.tsx new file mode 100644 index 0000000000..271f2d1de1 --- /dev/null +++ b/examples/react/start-basic-rsbuild/src/components/PostError.tsx @@ -0,0 +1,6 @@ +import { ErrorComponent } from '@tanstack/react-router' +import type { ErrorComponentProps } from '@tanstack/react-router' + +export function PostErrorComponent({ error }: ErrorComponentProps) { + return +} diff --git a/examples/react/start-basic-rsbuild/src/components/UserError.tsx b/examples/react/start-basic-rsbuild/src/components/UserError.tsx new file mode 100644 index 0000000000..af5d0c6f16 --- /dev/null +++ b/examples/react/start-basic-rsbuild/src/components/UserError.tsx @@ -0,0 +1,6 @@ +import { ErrorComponent } from '@tanstack/react-router' +import type { ErrorComponentProps } from '@tanstack/react-router' + +export function UserErrorComponent({ error }: ErrorComponentProps) { + return +} diff --git a/examples/react/start-basic-rsbuild/src/env.d.ts b/examples/react/start-basic-rsbuild/src/env.d.ts new file mode 100644 index 0000000000..b0ac762b09 --- /dev/null +++ b/examples/react/start-basic-rsbuild/src/env.d.ts @@ -0,0 +1 @@ +/// diff --git a/examples/react/start-basic-rsbuild/src/routeTree.gen.ts b/examples/react/start-basic-rsbuild/src/routeTree.gen.ts new file mode 100644 index 0000000000..0bd698a666 --- /dev/null +++ b/examples/react/start-basic-rsbuild/src/routeTree.gen.ts @@ -0,0 +1,447 @@ +/* eslint-disable */ + +// @ts-nocheck + +// noinspection JSUnusedGlobalSymbols + +// This file was automatically generated by TanStack Router. +// You should NOT make any changes in this file as it will be overwritten. +// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. + +import { Route as rootRouteImport } from './routes/__root' +import { Route as UsersRouteImport } from './routes/users' +import { Route as RedirectRouteImport } from './routes/redirect' +import { Route as PostsRouteImport } from './routes/posts' +import { Route as DeferredRouteImport } from './routes/deferred' +import { Route as CustomScriptDotjsRouteImport } from './routes/customScript[.]js' +import { Route as PathlessLayoutRouteImport } from './routes/_pathlessLayout' +import { Route as IndexRouteImport } from './routes/index' +import { Route as UsersIndexRouteImport } from './routes/users.index' +import { Route as PostsIndexRouteImport } from './routes/posts.index' +import { Route as UsersUserIdRouteImport } from './routes/users.$userId' +import { Route as PostsPostIdRouteImport } from './routes/posts.$postId' +import { Route as ApiUsersRouteImport } from './routes/api/users' +import { Route as PathlessLayoutNestedLayoutRouteImport } from './routes/_pathlessLayout/_nested-layout' +import { Route as PostsPostIdDeepRouteImport } from './routes/posts_.$postId.deep' +import { Route as ApiUsersUserIdRouteImport } from './routes/api/users.$userId' +import { Route as PathlessLayoutNestedLayoutRouteBRouteImport } from './routes/_pathlessLayout/_nested-layout/route-b' +import { Route as PathlessLayoutNestedLayoutRouteARouteImport } from './routes/_pathlessLayout/_nested-layout/route-a' + +const UsersRoute = UsersRouteImport.update({ + id: '/users', + path: '/users', + getParentRoute: () => rootRouteImport, +} as any) +const RedirectRoute = RedirectRouteImport.update({ + id: '/redirect', + path: '/redirect', + getParentRoute: () => rootRouteImport, +} as any) +const PostsRoute = PostsRouteImport.update({ + id: '/posts', + path: '/posts', + getParentRoute: () => rootRouteImport, +} as any) +const DeferredRoute = DeferredRouteImport.update({ + id: '/deferred', + path: '/deferred', + getParentRoute: () => rootRouteImport, +} as any) +const CustomScriptDotjsRoute = CustomScriptDotjsRouteImport.update({ + id: '/customScript.js', + path: '/customScript.js', + getParentRoute: () => rootRouteImport, +} as any) +const PathlessLayoutRoute = PathlessLayoutRouteImport.update({ + id: '/_pathlessLayout', + getParentRoute: () => rootRouteImport, +} as any) +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => rootRouteImport, +} as any) +const UsersIndexRoute = UsersIndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => UsersRoute, +} as any) +const PostsIndexRoute = PostsIndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => PostsRoute, +} as any) +const UsersUserIdRoute = UsersUserIdRouteImport.update({ + id: '/$userId', + path: '/$userId', + getParentRoute: () => UsersRoute, +} as any) +const PostsPostIdRoute = PostsPostIdRouteImport.update({ + id: '/$postId', + path: '/$postId', + getParentRoute: () => PostsRoute, +} as any) +const ApiUsersRoute = ApiUsersRouteImport.update({ + id: '/api/users', + path: '/api/users', + getParentRoute: () => rootRouteImport, +} as any) +const PathlessLayoutNestedLayoutRoute = + PathlessLayoutNestedLayoutRouteImport.update({ + id: '/_nested-layout', + getParentRoute: () => PathlessLayoutRoute, + } as any) +const PostsPostIdDeepRoute = PostsPostIdDeepRouteImport.update({ + id: '/posts_/$postId/deep', + path: '/posts/$postId/deep', + getParentRoute: () => rootRouteImport, +} as any) +const ApiUsersUserIdRoute = ApiUsersUserIdRouteImport.update({ + id: '/$userId', + path: '/$userId', + getParentRoute: () => ApiUsersRoute, +} as any) +const PathlessLayoutNestedLayoutRouteBRoute = + PathlessLayoutNestedLayoutRouteBRouteImport.update({ + id: '/route-b', + path: '/route-b', + getParentRoute: () => PathlessLayoutNestedLayoutRoute, + } as any) +const PathlessLayoutNestedLayoutRouteARoute = + PathlessLayoutNestedLayoutRouteARouteImport.update({ + id: '/route-a', + path: '/route-a', + getParentRoute: () => PathlessLayoutNestedLayoutRoute, + } as any) + +export interface FileRoutesByFullPath { + '/': typeof IndexRoute + '/customScript.js': typeof CustomScriptDotjsRoute + '/deferred': typeof DeferredRoute + '/posts': typeof PostsRouteWithChildren + '/redirect': typeof RedirectRoute + '/users': typeof UsersRouteWithChildren + '/api/users': typeof ApiUsersRouteWithChildren + '/posts/$postId': typeof PostsPostIdRoute + '/users/$userId': typeof UsersUserIdRoute + '/posts/': typeof PostsIndexRoute + '/users/': typeof UsersIndexRoute + '/route-a': typeof PathlessLayoutNestedLayoutRouteARoute + '/route-b': typeof PathlessLayoutNestedLayoutRouteBRoute + '/api/users/$userId': typeof ApiUsersUserIdRoute + '/posts/$postId/deep': typeof PostsPostIdDeepRoute +} +export interface FileRoutesByTo { + '/': typeof IndexRoute + '/customScript.js': typeof CustomScriptDotjsRoute + '/deferred': typeof DeferredRoute + '/redirect': typeof RedirectRoute + '/api/users': typeof ApiUsersRouteWithChildren + '/posts/$postId': typeof PostsPostIdRoute + '/users/$userId': typeof UsersUserIdRoute + '/posts': typeof PostsIndexRoute + '/users': typeof UsersIndexRoute + '/route-a': typeof PathlessLayoutNestedLayoutRouteARoute + '/route-b': typeof PathlessLayoutNestedLayoutRouteBRoute + '/api/users/$userId': typeof ApiUsersUserIdRoute + '/posts/$postId/deep': typeof PostsPostIdDeepRoute +} +export interface FileRoutesById { + __root__: typeof rootRouteImport + '/': typeof IndexRoute + '/_pathlessLayout': typeof PathlessLayoutRouteWithChildren + '/customScript.js': typeof CustomScriptDotjsRoute + '/deferred': typeof DeferredRoute + '/posts': typeof PostsRouteWithChildren + '/redirect': typeof RedirectRoute + '/users': typeof UsersRouteWithChildren + '/_pathlessLayout/_nested-layout': typeof PathlessLayoutNestedLayoutRouteWithChildren + '/api/users': typeof ApiUsersRouteWithChildren + '/posts/$postId': typeof PostsPostIdRoute + '/users/$userId': typeof UsersUserIdRoute + '/posts/': typeof PostsIndexRoute + '/users/': typeof UsersIndexRoute + '/_pathlessLayout/_nested-layout/route-a': typeof PathlessLayoutNestedLayoutRouteARoute + '/_pathlessLayout/_nested-layout/route-b': typeof PathlessLayoutNestedLayoutRouteBRoute + '/api/users/$userId': typeof ApiUsersUserIdRoute + '/posts_/$postId/deep': typeof PostsPostIdDeepRoute +} +export interface FileRouteTypes { + fileRoutesByFullPath: FileRoutesByFullPath + fullPaths: + | '/' + | '/customScript.js' + | '/deferred' + | '/posts' + | '/redirect' + | '/users' + | '/api/users' + | '/posts/$postId' + | '/users/$userId' + | '/posts/' + | '/users/' + | '/route-a' + | '/route-b' + | '/api/users/$userId' + | '/posts/$postId/deep' + fileRoutesByTo: FileRoutesByTo + to: + | '/' + | '/customScript.js' + | '/deferred' + | '/redirect' + | '/api/users' + | '/posts/$postId' + | '/users/$userId' + | '/posts' + | '/users' + | '/route-a' + | '/route-b' + | '/api/users/$userId' + | '/posts/$postId/deep' + id: + | '__root__' + | '/' + | '/_pathlessLayout' + | '/customScript.js' + | '/deferred' + | '/posts' + | '/redirect' + | '/users' + | '/_pathlessLayout/_nested-layout' + | '/api/users' + | '/posts/$postId' + | '/users/$userId' + | '/posts/' + | '/users/' + | '/_pathlessLayout/_nested-layout/route-a' + | '/_pathlessLayout/_nested-layout/route-b' + | '/api/users/$userId' + | '/posts_/$postId/deep' + fileRoutesById: FileRoutesById +} +export interface RootRouteChildren { + IndexRoute: typeof IndexRoute + PathlessLayoutRoute: typeof PathlessLayoutRouteWithChildren + CustomScriptDotjsRoute: typeof CustomScriptDotjsRoute + DeferredRoute: typeof DeferredRoute + PostsRoute: typeof PostsRouteWithChildren + RedirectRoute: typeof RedirectRoute + UsersRoute: typeof UsersRouteWithChildren + ApiUsersRoute: typeof ApiUsersRouteWithChildren + PostsPostIdDeepRoute: typeof PostsPostIdDeepRoute +} + +declare module '@tanstack/react-router' { + interface FileRoutesByPath { + '/users': { + id: '/users' + path: '/users' + fullPath: '/users' + preLoaderRoute: typeof UsersRouteImport + parentRoute: typeof rootRouteImport + } + '/redirect': { + id: '/redirect' + path: '/redirect' + fullPath: '/redirect' + preLoaderRoute: typeof RedirectRouteImport + parentRoute: typeof rootRouteImport + } + '/posts': { + id: '/posts' + path: '/posts' + fullPath: '/posts' + preLoaderRoute: typeof PostsRouteImport + parentRoute: typeof rootRouteImport + } + '/deferred': { + id: '/deferred' + path: '/deferred' + fullPath: '/deferred' + preLoaderRoute: typeof DeferredRouteImport + parentRoute: typeof rootRouteImport + } + '/customScript.js': { + id: '/customScript.js' + path: '/customScript.js' + fullPath: '/customScript.js' + preLoaderRoute: typeof CustomScriptDotjsRouteImport + parentRoute: typeof rootRouteImport + } + '/_pathlessLayout': { + id: '/_pathlessLayout' + path: '' + fullPath: '/' + preLoaderRoute: typeof PathlessLayoutRouteImport + parentRoute: typeof rootRouteImport + } + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport + parentRoute: typeof rootRouteImport + } + '/users/': { + id: '/users/' + path: '/' + fullPath: '/users/' + preLoaderRoute: typeof UsersIndexRouteImport + parentRoute: typeof UsersRoute + } + '/posts/': { + id: '/posts/' + path: '/' + fullPath: '/posts/' + preLoaderRoute: typeof PostsIndexRouteImport + parentRoute: typeof PostsRoute + } + '/users/$userId': { + id: '/users/$userId' + path: '/$userId' + fullPath: '/users/$userId' + preLoaderRoute: typeof UsersUserIdRouteImport + parentRoute: typeof UsersRoute + } + '/posts/$postId': { + id: '/posts/$postId' + path: '/$postId' + fullPath: '/posts/$postId' + preLoaderRoute: typeof PostsPostIdRouteImport + parentRoute: typeof PostsRoute + } + '/api/users': { + id: '/api/users' + path: '/api/users' + fullPath: '/api/users' + preLoaderRoute: typeof ApiUsersRouteImport + parentRoute: typeof rootRouteImport + } + '/_pathlessLayout/_nested-layout': { + id: '/_pathlessLayout/_nested-layout' + path: '' + fullPath: '/' + preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteImport + parentRoute: typeof PathlessLayoutRoute + } + '/posts_/$postId/deep': { + id: '/posts_/$postId/deep' + path: '/posts/$postId/deep' + fullPath: '/posts/$postId/deep' + preLoaderRoute: typeof PostsPostIdDeepRouteImport + parentRoute: typeof rootRouteImport + } + '/api/users/$userId': { + id: '/api/users/$userId' + path: '/$userId' + fullPath: '/api/users/$userId' + preLoaderRoute: typeof ApiUsersUserIdRouteImport + parentRoute: typeof ApiUsersRoute + } + '/_pathlessLayout/_nested-layout/route-b': { + id: '/_pathlessLayout/_nested-layout/route-b' + path: '/route-b' + fullPath: '/route-b' + preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteBRouteImport + parentRoute: typeof PathlessLayoutNestedLayoutRoute + } + '/_pathlessLayout/_nested-layout/route-a': { + id: '/_pathlessLayout/_nested-layout/route-a' + path: '/route-a' + fullPath: '/route-a' + preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteARouteImport + parentRoute: typeof PathlessLayoutNestedLayoutRoute + } + } +} + +interface PathlessLayoutNestedLayoutRouteChildren { + PathlessLayoutNestedLayoutRouteARoute: typeof PathlessLayoutNestedLayoutRouteARoute + PathlessLayoutNestedLayoutRouteBRoute: typeof PathlessLayoutNestedLayoutRouteBRoute +} + +const PathlessLayoutNestedLayoutRouteChildren: PathlessLayoutNestedLayoutRouteChildren = + { + PathlessLayoutNestedLayoutRouteARoute: + PathlessLayoutNestedLayoutRouteARoute, + PathlessLayoutNestedLayoutRouteBRoute: + PathlessLayoutNestedLayoutRouteBRoute, + } + +const PathlessLayoutNestedLayoutRouteWithChildren = + PathlessLayoutNestedLayoutRoute._addFileChildren( + PathlessLayoutNestedLayoutRouteChildren, + ) + +interface PathlessLayoutRouteChildren { + PathlessLayoutNestedLayoutRoute: typeof PathlessLayoutNestedLayoutRouteWithChildren +} + +const PathlessLayoutRouteChildren: PathlessLayoutRouteChildren = { + PathlessLayoutNestedLayoutRoute: PathlessLayoutNestedLayoutRouteWithChildren, +} + +const PathlessLayoutRouteWithChildren = PathlessLayoutRoute._addFileChildren( + PathlessLayoutRouteChildren, +) + +interface PostsRouteChildren { + PostsPostIdRoute: typeof PostsPostIdRoute + PostsIndexRoute: typeof PostsIndexRoute +} + +const PostsRouteChildren: PostsRouteChildren = { + PostsPostIdRoute: PostsPostIdRoute, + PostsIndexRoute: PostsIndexRoute, +} + +const PostsRouteWithChildren = PostsRoute._addFileChildren(PostsRouteChildren) + +interface UsersRouteChildren { + UsersUserIdRoute: typeof UsersUserIdRoute + UsersIndexRoute: typeof UsersIndexRoute +} + +const UsersRouteChildren: UsersRouteChildren = { + UsersUserIdRoute: UsersUserIdRoute, + UsersIndexRoute: UsersIndexRoute, +} + +const UsersRouteWithChildren = UsersRoute._addFileChildren(UsersRouteChildren) + +interface ApiUsersRouteChildren { + ApiUsersUserIdRoute: typeof ApiUsersUserIdRoute +} + +const ApiUsersRouteChildren: ApiUsersRouteChildren = { + ApiUsersUserIdRoute: ApiUsersUserIdRoute, +} + +const ApiUsersRouteWithChildren = ApiUsersRoute._addFileChildren( + ApiUsersRouteChildren, +) + +const rootRouteChildren: RootRouteChildren = { + IndexRoute: IndexRoute, + PathlessLayoutRoute: PathlessLayoutRouteWithChildren, + CustomScriptDotjsRoute: CustomScriptDotjsRoute, + DeferredRoute: DeferredRoute, + PostsRoute: PostsRouteWithChildren, + RedirectRoute: RedirectRoute, + UsersRoute: UsersRouteWithChildren, + ApiUsersRoute: ApiUsersRouteWithChildren, + PostsPostIdDeepRoute: PostsPostIdDeepRoute, +} +export const routeTree = rootRouteImport + ._addFileChildren(rootRouteChildren) + ._addFileTypes() + +import type { getRouter } from './router.tsx' +import type { createStart } from '@tanstack/react-start' +declare module '@tanstack/react-start' { + interface Register { + ssr: true + router: Awaited> + } +} diff --git a/examples/react/start-basic-rsbuild/src/router.tsx b/examples/react/start-basic-rsbuild/src/router.tsx new file mode 100644 index 0000000000..f380f73be7 --- /dev/null +++ b/examples/react/start-basic-rsbuild/src/router.tsx @@ -0,0 +1,15 @@ +import { createRouter } from '@tanstack/react-router' +import { routeTree } from './routeTree.gen' +import { DefaultCatchBoundary } from './components/DefaultCatchBoundary' +import { NotFound } from './components/NotFound' + +export function getRouter() { + const router = createRouter({ + routeTree, + defaultPreload: 'intent', + defaultErrorComponent: DefaultCatchBoundary, + defaultNotFoundComponent: () => , + scrollRestoration: true, + }) + return router +} diff --git a/examples/react/start-basic-rsbuild/src/routes/__root.tsx b/examples/react/start-basic-rsbuild/src/routes/__root.tsx new file mode 100644 index 0000000000..6d4aed1fe0 --- /dev/null +++ b/examples/react/start-basic-rsbuild/src/routes/__root.tsx @@ -0,0 +1,130 @@ +import { + HeadContent, + Link, + Scripts, + createRootRoute, +} from '@tanstack/react-router' +import { TanStackRouterDevtools } from '@tanstack/react-router-devtools' +import * as React from 'react' +import { DefaultCatchBoundary } from '~/components/DefaultCatchBoundary' +import { NotFound } from '~/components/NotFound' +import appCss from '~/styles/app.css?url' +import { seo } from '~/utils/seo' + +export const Route = createRootRoute({ + head: () => ({ + meta: [ + { + charSet: 'utf-8', + }, + { + name: 'viewport', + content: 'width=device-width, initial-scale=1', + }, + ...seo({ + title: + 'TanStack Start | Type-Safe, Client-First, Full-Stack React Framework', + description: `TanStack Start is a type-safe, client-first, full-stack React framework. `, + }), + ], + links: [ + { rel: 'stylesheet', href: appCss }, + { + rel: 'apple-touch-icon', + sizes: '180x180', + href: '/apple-touch-icon.png', + }, + { + rel: 'icon', + type: 'image/png', + sizes: '32x32', + href: '/favicon-32x32.png', + }, + { + rel: 'icon', + type: 'image/png', + sizes: '16x16', + href: '/favicon-16x16.png', + }, + { rel: 'manifest', href: '/site.webmanifest', color: '#fffff' }, + { rel: 'icon', href: '/favicon.ico' }, + ], + scripts: [ + { + src: '/customScript.js', + type: 'text/javascript', + }, + ], + }), + errorComponent: DefaultCatchBoundary, + notFoundComponent: () => , + shellComponent: RootDocument, +}) + +function RootDocument({ children }: { children: React.ReactNode }) { + return ( + + + + + +
+ + Home + {' '} + + Posts + {' '} + + Users + {' '} + + Pathless Layout + {' '} + + Deferred + {' '} + + This Route Does Not Exist + +
+
+ {children} + + + + + ) +} diff --git a/examples/react/start-basic-rsbuild/src/routes/_pathlessLayout.tsx b/examples/react/start-basic-rsbuild/src/routes/_pathlessLayout.tsx new file mode 100644 index 0000000000..c3b12442b8 --- /dev/null +++ b/examples/react/start-basic-rsbuild/src/routes/_pathlessLayout.tsx @@ -0,0 +1,16 @@ +import { Outlet, createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute('/_pathlessLayout')({ + component: LayoutComponent, +}) + +function LayoutComponent() { + return ( +
+
I'm a layout
+
+ +
+
+ ) +} diff --git a/examples/react/start-basic-rsbuild/src/routes/_pathlessLayout/_nested-layout.tsx b/examples/react/start-basic-rsbuild/src/routes/_pathlessLayout/_nested-layout.tsx new file mode 100644 index 0000000000..9a48b73a46 --- /dev/null +++ b/examples/react/start-basic-rsbuild/src/routes/_pathlessLayout/_nested-layout.tsx @@ -0,0 +1,34 @@ +import { Link, Outlet, createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute('/_pathlessLayout/_nested-layout')({ + component: LayoutComponent, +}) + +function LayoutComponent() { + return ( +
+
I'm a nested layout
+
+ + Go to route A + + + Go to route B + +
+
+ +
+
+ ) +} diff --git a/examples/react/start-basic-rsbuild/src/routes/_pathlessLayout/_nested-layout/route-a.tsx b/examples/react/start-basic-rsbuild/src/routes/_pathlessLayout/_nested-layout/route-a.tsx new file mode 100644 index 0000000000..426a8fe486 --- /dev/null +++ b/examples/react/start-basic-rsbuild/src/routes/_pathlessLayout/_nested-layout/route-a.tsx @@ -0,0 +1,11 @@ +import { createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute('/_pathlessLayout/_nested-layout/route-a')( + { + component: LayoutAComponent, + }, +) + +function LayoutAComponent() { + return
I'm A!
+} diff --git a/examples/react/start-basic-rsbuild/src/routes/_pathlessLayout/_nested-layout/route-b.tsx b/examples/react/start-basic-rsbuild/src/routes/_pathlessLayout/_nested-layout/route-b.tsx new file mode 100644 index 0000000000..20facf2daf --- /dev/null +++ b/examples/react/start-basic-rsbuild/src/routes/_pathlessLayout/_nested-layout/route-b.tsx @@ -0,0 +1,11 @@ +import { createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute('/_pathlessLayout/_nested-layout/route-b')( + { + component: LayoutBComponent, + }, +) + +function LayoutBComponent() { + return
I'm B!
+} diff --git a/examples/react/start-basic-rsbuild/src/routes/api/users.$userId.ts b/examples/react/start-basic-rsbuild/src/routes/api/users.$userId.ts new file mode 100644 index 0000000000..ee34d7a189 --- /dev/null +++ b/examples/react/start-basic-rsbuild/src/routes/api/users.$userId.ts @@ -0,0 +1,31 @@ +import { createFileRoute } from '@tanstack/react-router' +import type { User } from '~/utils/users' + +export const Route = createFileRoute('/api/users/$userId')({ + server: { + handlers: { + GET: async ({ params, request }) => { + console.info(`Fetching users by id=${params.userId}... @`, request.url) + try { + const res = await fetch( + 'https://jsonplaceholder.typicode.com/users/' + params.userId, + ) + if (!res.ok) { + throw new Error('Failed to fetch user') + } + + const user = (await res.json()) as User + + return Response.json({ + id: user.id, + name: user.name, + email: user.email, + }) + } catch (e) { + console.error(e) + return Response.json({ error: 'User not found' }, { status: 404 }) + } + }, + }, + }, +}) diff --git a/examples/react/start-basic-rsbuild/src/routes/api/users.ts b/examples/react/start-basic-rsbuild/src/routes/api/users.ts new file mode 100644 index 0000000000..4cecd01852 --- /dev/null +++ b/examples/react/start-basic-rsbuild/src/routes/api/users.ts @@ -0,0 +1,63 @@ +import { createFileRoute } from '@tanstack/react-router' +import { getRequestHeaders } from '@tanstack/react-start/server' +import { createMiddleware } from '@tanstack/react-start' +import type { User } from '~/utils/users' + +const userLoggerMiddleware = createMiddleware().server(async ({ next }) => { + console.info('In: /users') + console.info('Request Headers:', getRequestHeaders()) + const result = await next() + result.response.headers.set('x-users', 'true') + console.info('Out: /users') + return result +}) + +const testParentMiddleware = createMiddleware().server(async ({ next }) => { + console.info('In: testParentMiddleware') + const result = await next() + result.response.headers.set('x-test-parent', 'true') + console.info('Out: testParentMiddleware') + return result +}) + +const testMiddleware = createMiddleware() + .middleware([testParentMiddleware]) + .server(async ({ next }) => { + console.info('In: testMiddleware') + const result = await next() + result.response.headers.set('x-test', 'true') + + // if (Math.random() > 0.5) { + // throw new Response(null, { + // status: 302, + // headers: { Location: 'https://www.google.com' }, + // }) + // } + + console.info('Out: testMiddleware') + return result + }) + +export const Route = createFileRoute('/api/users')({ + server: { + middleware: [testMiddleware, userLoggerMiddleware], + handlers: { + GET: async ({ request }) => { + console.info('GET /api/users @', request.url) + console.info('Fetching users... @', request.url) + const res = await fetch('https://jsonplaceholder.typicode.com/users') + if (!res.ok) { + throw new Error('Failed to fetch users') + } + + const data = (await res.json()) as Array + + const list = data.slice(0, 10) + + return Response.json( + list.map((u) => ({ id: u.id, name: u.name, email: u.email })), + ) + }, + }, + }, +}) diff --git a/examples/react/start-basic-rsbuild/src/routes/customScript[.]js.ts b/examples/react/start-basic-rsbuild/src/routes/customScript[.]js.ts new file mode 100644 index 0000000000..811471ac15 --- /dev/null +++ b/examples/react/start-basic-rsbuild/src/routes/customScript[.]js.ts @@ -0,0 +1,15 @@ +import { createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute('/customScript.js')({ + server: { + handlers: { + GET: () => { + return new Response('console.log("Hello from customScript.js!")', { + headers: { + 'Content-Type': 'application/javascript', + }, + }) + }, + }, + }, +}) diff --git a/examples/react/start-basic-rsbuild/src/routes/deferred.tsx b/examples/react/start-basic-rsbuild/src/routes/deferred.tsx new file mode 100644 index 0000000000..f3e09d1d4e --- /dev/null +++ b/examples/react/start-basic-rsbuild/src/routes/deferred.tsx @@ -0,0 +1,62 @@ +import { Await, createFileRoute } from '@tanstack/react-router' +import { createServerFn } from '@tanstack/react-start' +import { Suspense, useState } from 'react' + +const personServerFn = createServerFn({ method: 'GET' }) + .validator((d: string) => d) + .handler(({ data: name }) => { + return { name, randomNumber: Math.floor(Math.random() * 100) } + }) + +const slowServerFn = createServerFn({ method: 'GET' }) + .validator((d: string) => d) + .handler(async ({ data: name }) => { + await new Promise((r) => setTimeout(r, 1000)) + return { name, randomNumber: Math.floor(Math.random() * 100) } + }) + +export const Route = createFileRoute('/deferred')({ + loader: async () => { + return { + deferredStuff: new Promise((r) => + setTimeout(() => r('Hello deferred!'), 2000), + ), + deferredPerson: slowServerFn({ data: 'Tanner Linsley' }), + person: await personServerFn({ data: 'John Doe' }), + } + }, + component: Deferred, +}) + +function Deferred() { + const [count, setCount] = useState(0) + const { deferredStuff, deferredPerson, person } = Route.useLoaderData() + + return ( +
+
+ {person.name} - {person.randomNumber} +
+ Loading person...
}> + ( +
+ {data.name} - {data.randomNumber} +
+ )} + /> + + Loading stuff...}> +

{data}

} + /> +
+
Count: {count}
+
+ +
+ + ) +} diff --git a/examples/react/start-basic-rsbuild/src/routes/index.tsx b/examples/react/start-basic-rsbuild/src/routes/index.tsx new file mode 100644 index 0000000000..09a907cb18 --- /dev/null +++ b/examples/react/start-basic-rsbuild/src/routes/index.tsx @@ -0,0 +1,13 @@ +import { createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute('/')({ + component: Home, +}) + +function Home() { + return ( +
+

Welcome Home!!!

+
+ ) +} diff --git a/examples/react/start-basic-rsbuild/src/routes/posts.$postId.tsx b/examples/react/start-basic-rsbuild/src/routes/posts.$postId.tsx new file mode 100644 index 0000000000..65905bf8c4 --- /dev/null +++ b/examples/react/start-basic-rsbuild/src/routes/posts.$postId.tsx @@ -0,0 +1,34 @@ +import { Link, createFileRoute } from '@tanstack/react-router' +import { fetchPost } from '../utils/posts' +import { NotFound } from '~/components/NotFound' +import { PostErrorComponent } from '~/components/PostError' + +export const Route = createFileRoute('/posts/$postId')({ + loader: ({ params: { postId } }) => fetchPost({ data: postId }), + errorComponent: PostErrorComponent, + component: PostComponent, + notFoundComponent: () => { + return Post not found + }, +}) + +function PostComponent() { + const post = Route.useLoaderData() + + return ( +
+

{post.title}

+
{post.body}
+ + Deep View + +
+ ) +} diff --git a/examples/react/start-basic-rsbuild/src/routes/posts.index.tsx b/examples/react/start-basic-rsbuild/src/routes/posts.index.tsx new file mode 100644 index 0000000000..5b5f08f95b --- /dev/null +++ b/examples/react/start-basic-rsbuild/src/routes/posts.index.tsx @@ -0,0 +1,9 @@ +import { createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute('/posts/')({ + component: PostsIndexComponent, +}) + +function PostsIndexComponent() { + return
Select a post.
+} diff --git a/examples/react/start-basic-rsbuild/src/routes/posts.tsx b/examples/react/start-basic-rsbuild/src/routes/posts.tsx new file mode 100644 index 0000000000..9b9162be7c --- /dev/null +++ b/examples/react/start-basic-rsbuild/src/routes/posts.tsx @@ -0,0 +1,38 @@ +import { Link, Outlet, createFileRoute } from '@tanstack/react-router' +import { fetchPosts } from '../utils/posts' + +export const Route = createFileRoute('/posts')({ + loader: async () => fetchPosts(), + component: PostsComponent, +}) + +function PostsComponent() { + const posts = Route.useLoaderData() + + return ( +
+
    + {[...posts, { id: 'i-do-not-exist', title: 'Non-existent Post' }].map( + (post) => { + return ( +
  • + +
    {post.title.substring(0, 20)}
    + +
  • + ) + }, + )} +
+
+ +
+ ) +} diff --git a/examples/react/start-basic-rsbuild/src/routes/posts_.$postId.deep.tsx b/examples/react/start-basic-rsbuild/src/routes/posts_.$postId.deep.tsx new file mode 100644 index 0000000000..29e6c39b5a --- /dev/null +++ b/examples/react/start-basic-rsbuild/src/routes/posts_.$postId.deep.tsx @@ -0,0 +1,29 @@ +import { Link, createFileRoute } from '@tanstack/react-router' +import { fetchPost } from '../utils/posts' +import { PostErrorComponent } from '~/components/PostError' + +export const Route = createFileRoute('/posts_/$postId/deep')({ + loader: async ({ params: { postId } }) => + fetchPost({ + data: postId, + }), + errorComponent: PostErrorComponent, + component: PostDeepComponent, +}) + +function PostDeepComponent() { + const post = Route.useLoaderData() + + return ( +
+ + ← All Posts + +

{post.title}

+
{post.body}
+
+ ) +} diff --git a/examples/react/start-basic-rsbuild/src/routes/redirect.tsx b/examples/react/start-basic-rsbuild/src/routes/redirect.tsx new file mode 100644 index 0000000000..ea34b6e420 --- /dev/null +++ b/examples/react/start-basic-rsbuild/src/routes/redirect.tsx @@ -0,0 +1,9 @@ +import { createFileRoute, redirect } from '@tanstack/react-router' + +export const Route = createFileRoute('/redirect')({ + beforeLoad: () => { + throw redirect({ + to: '/posts', + }) + }, +}) diff --git a/examples/react/start-basic-rsbuild/src/routes/users.$userId.tsx b/examples/react/start-basic-rsbuild/src/routes/users.$userId.tsx new file mode 100644 index 0000000000..4e4d470075 --- /dev/null +++ b/examples/react/start-basic-rsbuild/src/routes/users.$userId.tsx @@ -0,0 +1,45 @@ +import { createFileRoute } from '@tanstack/react-router' +import { NotFound } from '../components/NotFound' +import { UserErrorComponent } from '../components/UserError' +import type { User } from '../utils/users' + +export const Route = createFileRoute('/users/$userId')({ + loader: async ({ params: { userId } }) => { + try { + const res = await fetch('/api/users/' + userId) + if (!res.ok) { + throw new Error('Unexpected status code') + } + + const data = await res.json() + + return data as User + } catch { + throw new Error('Failed to fetch user') + } + }, + errorComponent: UserErrorComponent, + component: UserComponent, + notFoundComponent: () => { + return User not found + }, +}) + +function UserComponent() { + const user = Route.useLoaderData() + + return ( +
+

{user.name}

+
{user.email}
+ +
+ ) +} diff --git a/examples/react/start-basic-rsbuild/src/routes/users.index.tsx b/examples/react/start-basic-rsbuild/src/routes/users.index.tsx new file mode 100644 index 0000000000..27d136987e --- /dev/null +++ b/examples/react/start-basic-rsbuild/src/routes/users.index.tsx @@ -0,0 +1,19 @@ +import { createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute('/users/')({ + component: UsersIndexComponent, +}) + +function UsersIndexComponent() { + return ( +
+ Select a user or{' '} + + view as JSON + +
+ ) +} diff --git a/examples/react/start-basic-rsbuild/src/routes/users.tsx b/examples/react/start-basic-rsbuild/src/routes/users.tsx new file mode 100644 index 0000000000..64ca3ce156 --- /dev/null +++ b/examples/react/start-basic-rsbuild/src/routes/users.tsx @@ -0,0 +1,49 @@ +import { Link, Outlet, createFileRoute } from '@tanstack/react-router' +import type { User } from '../utils/users' + +export const Route = createFileRoute('/users')({ + loader: async () => { + const res = await fetch('/api/users') + + if (!res.ok) { + throw new Error('Unexpected status code') + } + + const data = await res.json() + + return data as Array + }, + component: UsersComponent, +}) + +function UsersComponent() { + const users = Route.useLoaderData() + + return ( +
+
    + {[ + ...users, + { id: 'i-do-not-exist', name: 'Non-existent User', email: '' }, + ].map((user) => { + return ( +
  • + +
    {user.name}
    + +
  • + ) + })} +
+
+ +
+ ) +} diff --git a/examples/react/start-basic-rsbuild/src/styles/app.css b/examples/react/start-basic-rsbuild/src/styles/app.css new file mode 100644 index 0000000000..37c1f5a6e2 --- /dev/null +++ b/examples/react/start-basic-rsbuild/src/styles/app.css @@ -0,0 +1,30 @@ +@import 'tailwindcss' source('../'); + +@layer base { + *, + ::after, + ::before, + ::backdrop, + ::file-selector-button { + border-color: var(--color-gray-200, currentcolor); + } +} + +@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/examples/react/start-basic-rsbuild/src/utils/loggingMiddleware.tsx b/examples/react/start-basic-rsbuild/src/utils/loggingMiddleware.tsx new file mode 100644 index 0000000000..3ea9a06439 --- /dev/null +++ b/examples/react/start-basic-rsbuild/src/utils/loggingMiddleware.tsx @@ -0,0 +1,41 @@ +import { createMiddleware } from '@tanstack/react-start' + +const preLogMiddleware = createMiddleware({ type: 'function' }) + .client(async (ctx) => { + const clientTime = new Date() + + return ctx.next({ + context: { + clientTime, + }, + sendContext: { + clientTime, + }, + }) + }) + .server(async (ctx) => { + const serverTime = new Date() + + return ctx.next({ + sendContext: { + serverTime, + durationToServer: + serverTime.getTime() - ctx.context.clientTime.getTime(), + }, + }) + }) + +export const logMiddleware = createMiddleware({ type: 'function' }) + .middleware([preLogMiddleware]) + .client(async (ctx) => { + const res = await ctx.next() + + const now = new Date() + console.log('Client Req/Res:', { + duration: now.getTime() - res.context.clientTime.getTime(), + durationToServer: res.context.durationToServer, + durationFromServer: now.getTime() - res.context.serverTime.getTime(), + }) + + return res + }) diff --git a/examples/react/start-basic-rsbuild/src/utils/posts.tsx b/examples/react/start-basic-rsbuild/src/utils/posts.tsx new file mode 100644 index 0000000000..ea7dd0a721 --- /dev/null +++ b/examples/react/start-basic-rsbuild/src/utils/posts.tsx @@ -0,0 +1,40 @@ +import { notFound } from '@tanstack/react-router' +import { createServerFn } from '@tanstack/react-start' + +export type PostType = { + id: number + title: string + body: string +} + +export const fetchPost = createServerFn({ method: 'POST' }) + .validator((d: string) => d) + .handler(async ({ data }) => { + console.info(`Fetching post with id ${data}...`) + const res = await fetch( + `https://jsonplaceholder.typicode.com/posts/${data}`, + ) + if (!res.ok) { + if (res.status === 404) { + throw notFound() + } + + throw new Error('Failed to fetch post') + } + + const post = await res.json() + + return post as PostType + }) + +export const fetchPosts = createServerFn().handler(async () => { + console.info('Fetching posts...') + const res = await fetch('https://jsonplaceholder.typicode.com/posts') + if (!res.ok) { + throw new Error('Failed to fetch posts') + } + + const posts = await res.json() + + return (posts as Array).slice(0, 10) +}) diff --git a/examples/react/start-basic-rsbuild/src/utils/seo.ts b/examples/react/start-basic-rsbuild/src/utils/seo.ts new file mode 100644 index 0000000000..d18ad84b74 --- /dev/null +++ b/examples/react/start-basic-rsbuild/src/utils/seo.ts @@ -0,0 +1,33 @@ +export const seo = ({ + title, + description, + keywords, + image, +}: { + title: string + description?: string + image?: string + keywords?: string +}) => { + const tags = [ + { title }, + { name: 'description', content: description }, + { name: 'keywords', content: keywords }, + { name: 'twitter:title', content: title }, + { name: 'twitter:description', content: description }, + { name: 'twitter:creator', content: '@tannerlinsley' }, + { name: 'twitter:site', content: '@tannerlinsley' }, + { name: 'og:type', content: 'website' }, + { name: 'og:title', content: title }, + { name: 'og:description', content: description }, + ...(image + ? [ + { name: 'twitter:image', content: image }, + { name: 'twitter:card', content: 'summary_large_image' }, + { name: 'og:image', content: image }, + ] + : []), + ] + + return tags +} diff --git a/examples/react/start-basic-rsbuild/src/utils/users.tsx b/examples/react/start-basic-rsbuild/src/utils/users.tsx new file mode 100644 index 0000000000..7ba645b383 --- /dev/null +++ b/examples/react/start-basic-rsbuild/src/utils/users.tsx @@ -0,0 +1,5 @@ +export type User = { + id: number + name: string + email: string +} diff --git a/examples/react/start-basic-rsbuild/tsconfig.json b/examples/react/start-basic-rsbuild/tsconfig.json new file mode 100644 index 0000000000..cc479423a1 --- /dev/null +++ b/examples/react/start-basic-rsbuild/tsconfig.json @@ -0,0 +1,21 @@ +{ + "include": ["**/*.ts", "**/*.tsx", "**/*.d.ts"], + "compilerOptions": { + "strict": true, + "esModuleInterop": true, + "jsx": "react-jsx", + "module": "ESNext", + "moduleResolution": "Bundler", + "lib": ["DOM", "DOM.Iterable", "ES2024"], + "isolatedModules": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "target": "ES2024", + "allowJs": true, + "forceConsistentCasingInFileNames": true, + "paths": { + "~/*": ["./src/*"] + }, + "noEmit": true + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 62525d6c48..93ebbff7fc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9299,6 +9299,58 @@ importers: specifier: ^8.0.14 version: 8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0) + examples/react/start-basic-rsbuild: + 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 + react: + specifier: ^19.2.3 + version: 19.2.3 + react-dom: + specifier: ^19.2.3 + version: 19.2.3(react@19.2.3) + tailwind-merge: + specifier: ^3.6.0 + version: 3.6.0 + zod: + specifier: ^4.4.3 + version: 4.4.3 + devDependencies: + '@rsbuild/core': + specifier: ^2.0.11 + version: 2.0.14 + '@rsbuild/plugin-react': + specifier: ^2.0.0 + version: 2.0.0(@rsbuild/core@2.0.14)(@rspack/core@2.0.8(@swc/helpers@0.5.23)) + '@rsbuild/plugin-tailwindcss': + specifier: ^2.0.3 + version: 2.0.3(@rsbuild/core@2.0.14)(@rspack/core@2.0.8(@swc/helpers@0.5.23))(webpack@5.104.0(@swc/core@1.15.33(@swc/helpers@0.5.23))(esbuild@0.27.4)) + '@types/node': + specifier: 25.0.9 + version: 25.0.9 + '@types/react': + specifier: ^19.2.8 + version: 19.2.9 + '@types/react-dom': + specifier: ^19.2.3 + version: 19.2.3(@types/react@19.2.9) + srvx: + specifier: ^0.11.18 + version: 0.11.18 + tailwindcss: + specifier: ^4.2.2 + version: 4.2.2 + typescript: + specifier: ^6.0.2 + version: 6.0.2 + examples/react/start-basic-static: dependencies: '@tanstack/react-router': @@ -13405,7 +13457,7 @@ importers: version: 0.1.7 h3-v2: specifier: npm:h3@2.0.1-rc.20 - version: h3@2.0.1-rc.20(crossws@0.4.5(srvx@0.11.15)) + version: h3@2.0.1-rc.20(crossws@0.4.5(srvx@0.11.18)) seroval: specifier: ^1.5.4 version: 1.5.4 @@ -18434,6 +18486,14 @@ packages: '@rsbuild/core': optional: true + '@rsbuild/plugin-tailwindcss@2.0.3': + resolution: {integrity: sha512-sl7mN0fyoP0W+zEyFR2xEMVAS2cZPlOAqsXwI8Ei9tUSvV7V4G8Xw2WBVCOQ/B4KEFfJ6H1+TEuPlAzHLEfc4g==} + peerDependencies: + '@rsbuild/core': ^2.0.0 + peerDependenciesMeta: + '@rsbuild/core': + optional: true + '@rsbuild/plugin-vue-jsx@1.1.1': resolution: {integrity: sha512-6QHm0dmx5cdgpvMXv7bSrTzuDi1uDBw7SaydmN8kQ/uhaxRPWMNMU3g6rGYKhu+6ExHKwtXOf8CI2YtXwDaP9Q==} peerDependencies: @@ -19120,36 +19180,69 @@ packages: '@tailwindcss/node@4.2.2': resolution: {integrity: sha512-pXS+wJ2gZpVXqFaUEjojq7jzMpTGf8rU6ipJz5ovJV6PUGmlJ+jvIwGrzdHdQ80Sg+wmQxUFuoW1UAAwHNEdFA==} + '@tailwindcss/node@4.3.1': + resolution: {integrity: sha512-6NDaqRoAMSXD1mr/RXu0HBvNE9a2n5tHPsxu9XHLws8o4Twes5rBM2205SUUiJ9goAtadrN6xTGX0UDEwp/N4A==} + '@tailwindcss/oxide-android-arm64@4.2.2': resolution: {integrity: sha512-dXGR1n+P3B6748jZO/SvHZq7qBOqqzQ+yFrXpoOWWALWndF9MoSKAT3Q0fYgAzYzGhxNYOoysRvYlpixRBBoDg==} engines: {node: '>= 20'} cpu: [arm64] os: [android] + '@tailwindcss/oxide-android-arm64@4.3.1': + resolution: {integrity: sha512-SVlyf61g374l5cHyg8x9kf5xmLcOaxvOTsbsqDnSsDJaKOEFZ7GCvi84VAVGpxojYOs1+3K6M0UjXfqPU8vmOQ==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [android] + '@tailwindcss/oxide-darwin-arm64@4.2.2': resolution: {integrity: sha512-iq9Qjr6knfMpZHj55/37ouZeykwbDqF21gPFtfnhCCKGDcPI/21FKC9XdMO/XyBM7qKORx6UIhGgg6jLl7BZlg==} engines: {node: '>= 20'} cpu: [arm64] os: [darwin] + '@tailwindcss/oxide-darwin-arm64@4.3.1': + resolution: {integrity: sha512-hVnWLwv+e/l7c4WKyVtHVrIPvYdqWHjRB3MDIqARynzFtnQg85kmQEFCbV9Ja0VVx4xXTIiDWY60Y7iz/iNoDA==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [darwin] + '@tailwindcss/oxide-darwin-x64@4.2.2': resolution: {integrity: sha512-BlR+2c3nzc8f2G639LpL89YY4bdcIdUmiOOkv2GQv4/4M0vJlpXEa0JXNHhCHU7VWOKWT/CjqHdTP8aUuDJkuw==} engines: {node: '>= 20'} cpu: [x64] os: [darwin] + '@tailwindcss/oxide-darwin-x64@4.3.1': + resolution: {integrity: sha512-Cf7abu0WVgbhU7ANgPUnSAvm7nCvMweusHb8FnaHlLfv/Caq4GYaEZg7ZImzzmjx4lIAfuS8q+eLIS7A7IzxIg==} + engines: {node: '>= 20'} + cpu: [x64] + os: [darwin] + '@tailwindcss/oxide-freebsd-x64@4.2.2': resolution: {integrity: sha512-YUqUgrGMSu2CDO82hzlQ5qSb5xmx3RUrke/QgnoEx7KvmRJHQuZHZmZTLSuuHwFf0DJPybFMXMYf+WJdxHy/nQ==} engines: {node: '>= 20'} cpu: [x64] os: [freebsd] + '@tailwindcss/oxide-freebsd-x64@4.3.1': + resolution: {integrity: sha512-ZZqzX2Y+GXtXXfqSfpJhDm60OoZfvLHLCgm+J7NVqgHHJjG/m9ugZI77RwTsVd4fnBJuCFP6Ae6kTJb71UdS8g==} + engines: {node: '>= 20'} + cpu: [x64] + os: [freebsd] + '@tailwindcss/oxide-linux-arm-gnueabihf@4.2.2': resolution: {integrity: sha512-FPdhvsW6g06T9BWT0qTwiVZYE2WIFo2dY5aCSpjG/S/u1tby+wXoslXS0kl3/KXnULlLr1E3NPRRw0g7t2kgaQ==} engines: {node: '>= 20'} cpu: [arm] os: [linux] + '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.1': + resolution: {integrity: sha512-/Ah/xik0LaMYfv9DZ0S/t4pBlBNYOcqtRwusjgovHkvT8ixueWCLyJjsaF5kQIckjb4IT8Q6K6p/iPmZMixYgg==} + engines: {node: '>= 20'} + cpu: [arm] + os: [linux] + '@tailwindcss/oxide-linux-arm64-gnu@4.2.2': resolution: {integrity: sha512-4og1V+ftEPXGttOO7eCmW7VICmzzJWgMx+QXAJRAhjrSjumCwWqMfkDrNu1LXEQzNAwz28NCUpucgQPrR4S2yw==} engines: {node: '>= 20'} @@ -19157,6 +19250,13 @@ packages: os: [linux] libc: [glibc] + '@tailwindcss/oxide-linux-arm64-gnu@4.3.1': + resolution: {integrity: sha512-gqdFoVJlw444GvpnheZLHmvTzSxI/cOUUh2KSNejQjTcYkW062SVD+En0rUgD+QV91bz1XGIGtt1HJd48xUGbQ==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [linux] + libc: [glibc] + '@tailwindcss/oxide-linux-arm64-musl@4.2.2': resolution: {integrity: sha512-oCfG/mS+/+XRlwNjnsNLVwnMWYH7tn/kYPsNPh+JSOMlnt93mYNCKHYzylRhI51X+TbR+ufNhhKKzm6QkqX8ag==} engines: {node: '>= 20'} @@ -19164,6 +19264,13 @@ packages: os: [linux] libc: [musl] + '@tailwindcss/oxide-linux-arm64-musl@4.3.1': + resolution: {integrity: sha512-Bwv9KwOvE0VKa86xPFif9b9c3Y1NxOV1P0gLti/IYaWEsQYZXDlxfGEtA8mdDZ7SG3wyNXAWYT5SIn3giL57oA==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [linux] + libc: [musl] + '@tailwindcss/oxide-linux-x64-gnu@4.2.2': resolution: {integrity: sha512-rTAGAkDgqbXHNp/xW0iugLVmX62wOp2PoE39BTCGKjv3Iocf6AFbRP/wZT/kuCxC9QBh9Pu8XPkv/zCZB2mcMg==} engines: {node: '>= 20'} @@ -19171,6 +19278,13 @@ packages: os: [linux] libc: [glibc] + '@tailwindcss/oxide-linux-x64-gnu@4.3.1': + resolution: {integrity: sha512-Ymi8O8T15HYQdOUWUtTI6ldN0neHP85FC+Qz32xTcZ7iJXtem/x8ITev0o1e9e5rkqj4lONZfTRLvkmin1+tKg==} + engines: {node: '>= 20'} + cpu: [x64] + os: [linux] + libc: [glibc] + '@tailwindcss/oxide-linux-x64-musl@4.2.2': resolution: {integrity: sha512-XW3t3qwbIwiSyRCggeO2zxe3KWaEbM0/kW9e8+0XpBgyKU4ATYzcVSMKteZJ1iukJ3HgHBjbg9P5YPRCVUxlnQ==} engines: {node: '>= 20'} @@ -19178,6 +19292,13 @@ packages: os: [linux] libc: [musl] + '@tailwindcss/oxide-linux-x64-musl@4.3.1': + resolution: {integrity: sha512-M+P/91qJ6uILLw4k2G93GMDRAXj61SMvFQYt39AqvUqYgExXpLL5aepfns7sj4HiAQeolirQF9E0lzRvdf4zPQ==} + engines: {node: '>= 20'} + cpu: [x64] + os: [linux] + libc: [musl] + '@tailwindcss/oxide-wasm32-wasi@4.2.2': resolution: {integrity: sha512-eKSztKsmEsn1O5lJ4ZAfyn41NfG7vzCg496YiGtMDV86jz1q/irhms5O0VrY6ZwTUkFy/EKG3RfWgxSI3VbZ8Q==} engines: {node: '>=14.0.0'} @@ -19190,22 +19311,50 @@ packages: - '@emnapi/wasi-threads' - tslib + '@tailwindcss/oxide-wasm32-wasi@4.3.1': + resolution: {integrity: sha512-zsM8uOeqvVGHsAXsJxsT28ttosFahLJKCLOTUBqRAtKnVgGSRitds9T432QiT8b77Yga7JIBkulIRRlJPtYhRA==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + bundledDependencies: + - '@napi-rs/wasm-runtime' + - '@emnapi/core' + - '@emnapi/runtime' + - '@tybys/wasm-util' + - '@emnapi/wasi-threads' + - tslib + '@tailwindcss/oxide-win32-arm64-msvc@4.2.2': resolution: {integrity: sha512-qPmaQM4iKu5mxpsrWZMOZRgZv1tOZpUm+zdhhQP0VhJfyGGO3aUKdbh3gDZc/dPLQwW4eSqWGrrcWNBZWUWaXQ==} engines: {node: '>= 20'} cpu: [arm64] os: [win32] + '@tailwindcss/oxide-win32-arm64-msvc@4.3.1': + resolution: {integrity: sha512-aiNvSq9BsVk8V513lDKlrCFAgf8qBMPZTpgEhInL+NwQqs97mYmupVMrPrgBBSL8Pv/0zXu9MrMF9rMun1ZeNg==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [win32] + '@tailwindcss/oxide-win32-x64-msvc@4.2.2': resolution: {integrity: sha512-1T/37VvI7WyH66b+vqHj/cLwnCxt7Qt3WFu5Q8hk65aOvlwAhs7rAp1VkulBJw/N4tMirXjVnylTR72uI0HGcA==} engines: {node: '>= 20'} cpu: [x64] os: [win32] + '@tailwindcss/oxide-win32-x64-msvc@4.3.1': + resolution: {integrity: sha512-xDEyu1rg290472FEGaKHnzyDyh5QH+AlWvsU5hMoMtPpzmKlRI0jaYKCgSHDYtaQWZOYbMaduSyCwFwY4n1HmA==} + engines: {node: '>= 20'} + cpu: [x64] + os: [win32] + '@tailwindcss/oxide@4.2.2': resolution: {integrity: sha512-qEUA07+E5kehxYp9BVMpq9E8vnJuBHfJEC0vPC5e7iL/hw7HR61aDKoVoKzrG+QKp56vhNZe4qwkRmMC0zDLvg==} engines: {node: '>= 20'} + '@tailwindcss/oxide@4.3.1': + resolution: {integrity: sha512-yVPyo8RNkabVr3O2EhHEE0Rewu7YKzc1DhIqfL46LKveFrmu9XbDazNOJY7/GRuvw1h6u3utWnR29H/p5JPlgA==} + engines: {node: '>= 20'} + '@tailwindcss/postcss@4.2.2': resolution: {integrity: sha512-n4goKQbW8RVXIbNKRB/45LzyUqN451deQK0nzIeauVEqjlI49slUlgKYJM2QyUzap/PcpnS7kzSUmPb1sCRvYQ==} @@ -19214,6 +19363,17 @@ packages: peerDependencies: vite: ^8.0.14 + '@tailwindcss/webpack@4.3.1': + resolution: {integrity: sha512-HM33EjYPbkMlBGcYoVA/pk/hML3wAn2JOnjF79eDWVLuktOhRczDwWsSBQBOWV6LLBVAgGZS/pZ519OHUF8vKg==} + peerDependencies: + '@rspack/core': ^1.0.0 || ^2.0.0 + webpack: ^5.0.0 + peerDependenciesMeta: + '@rspack/core': + optional: true + webpack: + optional: true + '@tanstack/devtools-client@0.0.4': resolution: {integrity: sha512-LefnH9KE9uRDEWifc3QDcooskA8ikfs41bybDTgpYQpyTUspZnaEdUdya9Hry0KYxZ8nos0S3nNbsP79KHqr6Q==} engines: {node: '>=18'} @@ -21753,6 +21913,10 @@ packages: resolution: {integrity: sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA==} engines: {node: '>=10.13.0'} + enhanced-resolve@5.21.6: + resolution: {integrity: sha512-aNnGCvbJ/RIyWo1IuhNdVjnNF+EjH9wpzpNHt+ci/m9He9LJvUN8wrCcXjp9cWsGNAuvSpVFTx/vraAFQ8qGjQ==} + engines: {node: '>=10.13.0'} + enquirer@2.3.6: resolution: {integrity: sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==} engines: {node: '>=8.6'} @@ -25396,6 +25560,11 @@ packages: engines: {node: '>=20.16.0'} hasBin: true + srvx@0.11.18: + resolution: {integrity: sha512-7/EW5sPdC1bU7iq1tgTvCZqUQDkJdsqIVzYqBv7SuBfQQ10oWkKj4KYNOw0H4Ig26bXuUYDA7XTKxB+/HC5SRw==} + engines: {node: '>=20.16.0'} + hasBin: true + srvx@0.8.16: resolution: {integrity: sha512-hmcGW4CgroeSmzgF1Ihwgl+Ths0JqAJ7HwjP2X7e3JzY7u4IydLMcdnlqGQiQGUswz+PO9oh/KtCpOISIvs9QQ==} engines: {node: '>=20.16.0'} @@ -25577,6 +25746,9 @@ packages: tailwindcss@4.2.2: resolution: {integrity: sha512-KWBIxs1Xb6NoLdMVqhbhgwZf2PGBpPEiwOqgI4pFIYbNTfBXiKYyWoTsXgBQ9WFg/OlhnvHaY+AEpW7wSmFo2Q==} + tailwindcss@4.3.1: + resolution: {integrity: sha512-hk+TB1m+K8CYNrP6rjQaq/Y+4Zylwpa87mLYBKCunwnnQ9p+fHb7kmSfGqyEJoxF/O6CDyABWVFEafNSYKll+Q==} + tapable@2.2.1: resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} engines: {node: '>=6'} @@ -25585,6 +25757,10 @@ packages: resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==} engines: {node: '>=6'} + tapable@2.3.3: + resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==} + engines: {node: '>=6'} + tar-stream@2.2.0: resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} engines: {node: '>=6'} @@ -31826,6 +32002,15 @@ snapshots: - solid-js - supports-color + '@rsbuild/plugin-tailwindcss@2.0.3(@rsbuild/core@2.0.14)(@rspack/core@2.0.8(@swc/helpers@0.5.23))(webpack@5.104.0(@swc/core@1.15.33(@swc/helpers@0.5.23))(esbuild@0.27.4))': + dependencies: + '@tailwindcss/webpack': 4.3.1(@rspack/core@2.0.8(@swc/helpers@0.5.23))(webpack@5.104.0(@swc/core@1.15.33(@swc/helpers@0.5.23))(esbuild@0.27.4)) + optionalDependencies: + '@rsbuild/core': 2.0.14 + transitivePeerDependencies: + - '@rspack/core' + - webpack + '@rsbuild/plugin-vue-jsx@1.1.1(@babel/core@7.29.0)(@rsbuild/core@2.0.14)': dependencies: '@rsbuild/plugin-babel': 1.1.2(@rsbuild/core@2.0.14) @@ -32501,42 +32686,88 @@ snapshots: source-map-js: 1.2.1 tailwindcss: 4.2.2 + '@tailwindcss/node@4.3.1': + dependencies: + '@jridgewell/remapping': 2.3.5 + enhanced-resolve: 5.21.6 + jiti: 2.7.0 + lightningcss: 1.32.0 + magic-string: 0.30.21 + source-map-js: 1.2.1 + tailwindcss: 4.3.1 + '@tailwindcss/oxide-android-arm64@4.2.2': optional: true + '@tailwindcss/oxide-android-arm64@4.3.1': + optional: true + '@tailwindcss/oxide-darwin-arm64@4.2.2': optional: true + '@tailwindcss/oxide-darwin-arm64@4.3.1': + optional: true + '@tailwindcss/oxide-darwin-x64@4.2.2': optional: true + '@tailwindcss/oxide-darwin-x64@4.3.1': + optional: true + '@tailwindcss/oxide-freebsd-x64@4.2.2': optional: true + '@tailwindcss/oxide-freebsd-x64@4.3.1': + optional: true + '@tailwindcss/oxide-linux-arm-gnueabihf@4.2.2': optional: true + '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.1': + optional: true + '@tailwindcss/oxide-linux-arm64-gnu@4.2.2': optional: true + '@tailwindcss/oxide-linux-arm64-gnu@4.3.1': + optional: true + '@tailwindcss/oxide-linux-arm64-musl@4.2.2': optional: true + '@tailwindcss/oxide-linux-arm64-musl@4.3.1': + optional: true + '@tailwindcss/oxide-linux-x64-gnu@4.2.2': optional: true + '@tailwindcss/oxide-linux-x64-gnu@4.3.1': + optional: true + '@tailwindcss/oxide-linux-x64-musl@4.2.2': optional: true + '@tailwindcss/oxide-linux-x64-musl@4.3.1': + optional: true + '@tailwindcss/oxide-wasm32-wasi@4.2.2': optional: true + '@tailwindcss/oxide-wasm32-wasi@4.3.1': + optional: true + '@tailwindcss/oxide-win32-arm64-msvc@4.2.2': optional: true + '@tailwindcss/oxide-win32-arm64-msvc@4.3.1': + optional: true + '@tailwindcss/oxide-win32-x64-msvc@4.2.2': optional: true + '@tailwindcss/oxide-win32-x64-msvc@4.3.1': + optional: true + '@tailwindcss/oxide@4.2.2': optionalDependencies: '@tailwindcss/oxide-android-arm64': 4.2.2 @@ -32552,6 +32783,21 @@ snapshots: '@tailwindcss/oxide-win32-arm64-msvc': 4.2.2 '@tailwindcss/oxide-win32-x64-msvc': 4.2.2 + '@tailwindcss/oxide@4.3.1': + optionalDependencies: + '@tailwindcss/oxide-android-arm64': 4.3.1 + '@tailwindcss/oxide-darwin-arm64': 4.3.1 + '@tailwindcss/oxide-darwin-x64': 4.3.1 + '@tailwindcss/oxide-freebsd-x64': 4.3.1 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.3.1 + '@tailwindcss/oxide-linux-arm64-gnu': 4.3.1 + '@tailwindcss/oxide-linux-arm64-musl': 4.3.1 + '@tailwindcss/oxide-linux-x64-gnu': 4.3.1 + '@tailwindcss/oxide-linux-x64-musl': 4.3.1 + '@tailwindcss/oxide-wasm32-wasi': 4.3.1 + '@tailwindcss/oxide-win32-arm64-msvc': 4.3.1 + '@tailwindcss/oxide-win32-x64-msvc': 4.3.1 + '@tailwindcss/postcss@4.2.2': dependencies: '@alloc/quick-lru': 5.2.0 @@ -32567,6 +32813,16 @@ snapshots: tailwindcss: 4.2.2 vite: 8.0.14(@types/node@25.0.9)(esbuild@0.27.4)(jiti@2.7.0)(sass@1.97.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.9.0) + '@tailwindcss/webpack@4.3.1(@rspack/core@2.0.8(@swc/helpers@0.5.23))(webpack@5.104.0(@swc/core@1.15.33(@swc/helpers@0.5.23))(esbuild@0.27.4))': + dependencies: + '@alloc/quick-lru': 5.2.0 + '@tailwindcss/node': 4.3.1 + '@tailwindcss/oxide': 4.3.1 + tailwindcss: 4.3.1 + optionalDependencies: + '@rspack/core': 2.0.8(@swc/helpers@0.5.23) + webpack: 5.104.0(@swc/core@1.15.33(@swc/helpers@0.5.23))(esbuild@0.27.4) + '@tanstack/devtools-client@0.0.4': dependencies: '@tanstack/devtools-event-client': 0.3.4 @@ -35336,9 +35592,9 @@ snapshots: optionalDependencies: srvx: 0.11.15 - crossws@0.4.5(srvx@0.11.15): + crossws@0.4.5(srvx@0.11.18): optionalDependencies: - srvx: 0.11.15 + srvx: 0.11.18 optional: true crossws@0.4.5(srvx@0.8.16): @@ -35775,6 +36031,11 @@ snapshots: graceful-fs: 4.2.11 tapable: 2.3.0 + enhanced-resolve@5.21.6: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.3.3 + enquirer@2.3.6: dependencies: ansi-colors: 4.1.3 @@ -36926,12 +37187,12 @@ snapshots: optionalDependencies: crossws: 0.4.5(srvx@0.8.16) - h3@2.0.1-rc.20(crossws@0.4.5(srvx@0.11.15)): + h3@2.0.1-rc.20(crossws@0.4.5(srvx@0.11.18)): dependencies: rou3: 0.8.1 srvx: 0.11.15 optionalDependencies: - crossws: 0.4.5(srvx@0.11.15) + crossws: 0.4.5(srvx@0.11.18) handle-thing@2.0.1: {} @@ -40298,6 +40559,8 @@ snapshots: srvx@0.11.15: {} + srvx@0.11.18: {} + srvx@0.8.16: {} stable-hash-x@0.2.0: {} @@ -40461,10 +40724,14 @@ snapshots: tailwindcss@4.2.2: {} + tailwindcss@4.3.1: {} + tapable@2.2.1: {} tapable@2.3.0: {} + tapable@2.3.3: {} + tar-stream@2.2.0: dependencies: bl: 4.1.0 @@ -41492,7 +41759,7 @@ snapshots: acorn-import-phases: 1.0.4(acorn@8.15.0) browserslist: 4.28.1 chrome-trace-event: 1.0.4 - enhanced-resolve: 5.18.3 + enhanced-resolve: 5.21.6 es-module-lexer: 2.0.0 eslint-scope: 5.1.1 events: 3.3.0 @@ -41503,7 +41770,7 @@ snapshots: mime-types: 2.1.35 neo-async: 2.6.2 schema-utils: 4.3.3 - tapable: 2.3.0 + tapable: 2.3.3 terser-webpack-plugin: 5.3.16(@swc/core@1.15.33(@swc/helpers@0.5.23))(esbuild@0.27.4)(webpack@5.104.0(@swc/core@1.15.33(@swc/helpers@0.5.23))(esbuild@0.27.4)) watchpack: 2.4.4 webpack-sources: 3.3.3