diff --git a/docs/router/config.json b/docs/router/config.json index dc742a73cfb..dcdcb378ef7 100644 --- a/docs/router/config.json +++ b/docs/router/config.json @@ -681,6 +681,10 @@ { "label": "With tRPC", "to": "framework/solid/examples/with-trpc" + }, + { + "label": "Monorepo with Solid Query", + "to": "framework/solid/examples/router-monorepo-solid-query" } ] } diff --git a/examples/solid/router-monorepo-solid-query/.gitignore b/examples/solid/router-monorepo-solid-query/.gitignore new file mode 100644 index 00000000000..e5cc582a454 --- /dev/null +++ b/examples/solid/router-monorepo-solid-query/.gitignore @@ -0,0 +1,12 @@ +node_modules +.DS_Store +dist +dist-ssr +*.local +*.js + +/test-results/ +/playwright-report/ +/blob-report/ +/playwright/.cache/ +pnpm-workspace.yaml \ No newline at end of file diff --git a/examples/solid/router-monorepo-solid-query/.stackblitzrc b/examples/solid/router-monorepo-solid-query/.stackblitzrc new file mode 100644 index 00000000000..5cedc82a840 --- /dev/null +++ b/examples/solid/router-monorepo-solid-query/.stackblitzrc @@ -0,0 +1,3 @@ +{ + "startCommand": "cp pnpm-workspace.yaml.example pnpm-workspace.yaml && pnpm install && pnpm dev" +} diff --git a/examples/solid/router-monorepo-solid-query/.vscode/settings.json b/examples/solid/router-monorepo-solid-query/.vscode/settings.json new file mode 100644 index 00000000000..00b5278e580 --- /dev/null +++ b/examples/solid/router-monorepo-solid-query/.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/solid/router-monorepo-solid-query/README.md b/examples/solid/router-monorepo-solid-query/README.md new file mode 100644 index 00000000000..58610fb1e89 --- /dev/null +++ b/examples/solid/router-monorepo-solid-query/README.md @@ -0,0 +1,31 @@ +# Example of a monorepo with router and feature libraries + +To run this example: + +- `npm install` or `yarn` +- `npm dev` or `yarn dev` + +A challenge with TanStack Router in a monorepo setup is that it requires TypeScript type augmentations. However, if you set this up directly in the final app, the links inside the libraries won’t be type-safe. To solve this in a monorepo, you need a separate library just for the router, without any components, and then integrate it with the app. + +This example showcases this approach using the following packages: + +- `packages/router` is the router library +- `packages/post-query` is the post query collection library +- `packages/post-feature` is the posts ui library +- `packages/app` is the app + +With this approach, we can use the query options from the data library both in the router and the feature library without creating circular dependencies. + +Since the router library re-exports the router components, importing them in the feature library ensures they remain type-safe, as they’re linked to the TypeScript augmentations. + +Finally, in the app, we can create a map of routes to components ([`packages/app/src/main.tsx`](./packages/app/src/main.tsx)), which ties the router to the components. **We could enforce lazy loading here, but it was left out for simplicity.** With this setup, we now have a fully type-safe router! + +Here is what it looks like in the monorepo: + +![graph](./assets/graph.png) + +## Stackblitz limitation + +### TypeScript IDE feedback + +Due to a limitation on Stackblitz, the example's types are not properly inferred in the IDE, however as soon as you click on fork in the bottom right corner, the types should be correctly inferred. diff --git a/examples/solid/router-monorepo-solid-query/assets/graph.png b/examples/solid/router-monorepo-solid-query/assets/graph.png new file mode 100644 index 00000000000..1dcfd81f476 Binary files /dev/null and b/examples/solid/router-monorepo-solid-query/assets/graph.png differ diff --git a/examples/solid/router-monorepo-solid-query/package.json b/examples/solid/router-monorepo-solid-query/package.json new file mode 100644 index 00000000000..9553e5ddf30 --- /dev/null +++ b/examples/solid/router-monorepo-solid-query/package.json @@ -0,0 +1,31 @@ +{ + "name": "router-solid-monorepo-solid-query", + "version": "1.0.0", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "post-query": "pnpm --filter @router-solid-mono-solid-query/post-query", + "post-feature": "pnpm --filter @router-solid-mono-solid-query/post-feature", + "router": "pnpm --filter @router-solid-mono-solid-query/router", + "app": "pnpm --filter @router-solid-mono-solid-query/app", + "dev": "pnpm post-query build && pnpm router build && pnpm post-feature build && pnpm app dev" + }, + "dependencies": { + "@tanstack/solid-query": "^5.90.0", + "@tanstack/solid-query-devtools": "^5.90.0", + "@tanstack/solid-router": "^1.135.2", + "@tanstack/solid-router-devtools": "^1.135.2", + "@tanstack/router-plugin": "^1.135.2", + "solid-js": "^1.9.10", + "redaxios": "^0.5.1" + }, + "devDependencies": { + "@types/node": "^22.10.2", + "typescript": "^5.7.2", + "vite-plugin-solid": "^2.11.10", + "vite": "^7.1.7", + "vite-plugin-dts": "^4.5.4" + }, + "keywords": [], + "author": "", + "license": "ISC" +} diff --git a/examples/solid/router-monorepo-solid-query/packages/app/index.html b/examples/solid/router-monorepo-solid-query/packages/app/index.html new file mode 100644 index 00000000000..9b6335c0ac1 --- /dev/null +++ b/examples/solid/router-monorepo-solid-query/packages/app/index.html @@ -0,0 +1,12 @@ + + + + + + Vite App + + +
+ + + diff --git a/examples/solid/router-monorepo-solid-query/packages/app/package.json b/examples/solid/router-monorepo-solid-query/packages/app/package.json new file mode 100644 index 00000000000..ed728b34759 --- /dev/null +++ b/examples/solid/router-monorepo-solid-query/packages/app/package.json @@ -0,0 +1,36 @@ +{ + "name": "@router-solid-mono-solid-query/app", + "private": true, + "type": "module", + "scripts": { + "dev": "vite --port=3001", + "build": "vite build && tsc --noEmit", + "serve": "vite preview", + "start": "vite" + }, + "dependencies": { + "@tanstack/solid-query": "^5.90.0", + "@router-solid-mono-solid-query/post-feature": "workspace:*", + "@router-solid-mono-solid-query/router": "workspace:*", + "solid-js": "^1.9.10" + }, + "devDependencies": { + "vite-plugin-solid": "^2.11.10", + "typescript": "^5.7.2", + "@tanstack/solid-router-devtools": "^1.135.2", + "postcss": "^8.5.1", + "autoprefixer": "^10.4.20", + "tailwindcss": "^3.4.17", + "vite": "^7.1.7", + "vite-plugin-dts": "^4.5.4" + }, + "nx": { + "targets": { + "dev": { + "dependsOn": [ + "^build" + ] + } + } + } +} diff --git a/examples/solid/router-monorepo-solid-query/packages/app/src/main.tsx b/examples/solid/router-monorepo-solid-query/packages/app/src/main.tsx new file mode 100644 index 00000000000..8740ce06a79 --- /dev/null +++ b/examples/solid/router-monorepo-solid-query/packages/app/src/main.tsx @@ -0,0 +1,60 @@ +import { render } from 'solid-js/web' +import { RouterProvider } from '@tanstack/solid-router' +import { QueryClientProvider } from '@tanstack/solid-query' +import { queryClient, router } from '@router-solid-mono-solid-query/router' +import { + PostErrorComponent, + PostIdComponent, + PostsListComponent, +} from '@router-solid-mono-solid-query/post-feature' +import { RootComponent } from './rootComponent' +import type { RouterIds } from '@router-solid-mono-solid-query/router' +import './style.css' +import type { JSX } from 'solid-js' +// Not lazy loaded for simplicity, but you could expose from your library component +// individually, and enforce here to use react lazy components via typings +// so that you have code splitting +const routerMap = { + '/': PostsListComponent, + '/$postId': PostIdComponent, + __root__: RootComponent, +} as const satisfies Record JSX.Element> + +Object.entries(routerMap).forEach(([path, component]) => { + const foundRoute = router.routesById[path as RouterIds] + + foundRoute.update({ + component: component, + }) +}) + +// And you can do the same logic with custom error pages, and any other properties +const errorComponentMap = { + '/': null, + '/$postId': PostErrorComponent, + __root__: null, +} + +Object.entries(errorComponentMap).forEach(([path, component]) => { + if (!component) { + return + } + + const foundRoute = router.routesById[path as RouterIds] + foundRoute.update({ + errorComponent: component, + }) +}) + +const rootElement = document.getElementById('app')! + +if (!rootElement.innerHTML) { + render( + () => ( + + + + ), + rootElement, + ) +} diff --git a/examples/solid/router-monorepo-solid-query/packages/app/src/rootComponent.tsx b/examples/solid/router-monorepo-solid-query/packages/app/src/rootComponent.tsx new file mode 100644 index 00000000000..294d380fdd7 --- /dev/null +++ b/examples/solid/router-monorepo-solid-query/packages/app/src/rootComponent.tsx @@ -0,0 +1,25 @@ +import { Link, Outlet } from '@router-solid-mono-solid-query/router' +import { TanStackRouterDevtools } from '@tanstack/solid-router-devtools' +import { SolidQueryDevtools } from '@tanstack/solid-query-devtools' + +export function RootComponent() { + return ( + <> +
+ + Posts list + +
+
+ + + + + ) +} diff --git a/examples/solid/router-monorepo-solid-query/packages/app/src/style.css b/examples/solid/router-monorepo-solid-query/packages/app/src/style.css new file mode 100644 index 00000000000..0b8e317099c --- /dev/null +++ b/examples/solid/router-monorepo-solid-query/packages/app/src/style.css @@ -0,0 +1,13 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +html { + color-scheme: light dark; +} +* { + @apply border-gray-200 dark:border-gray-800; +} +body { + @apply bg-gray-50 text-gray-950 dark:bg-gray-900 dark:text-gray-200; +} diff --git a/examples/solid/router-monorepo-solid-query/packages/app/tsconfig.json b/examples/solid/router-monorepo-solid-query/packages/app/tsconfig.json new file mode 100644 index 00000000000..a972899814c --- /dev/null +++ b/examples/solid/router-monorepo-solid-query/packages/app/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "strict": true, + "esModuleInterop": true, + "jsx": "preserve", + "jsxImportSource": "solid-js", + "target": "ESNext", + "moduleResolution": "Bundler", + "module": "ESNext", + "lib": ["DOM", "DOM.Iterable", "ES2022"], + "skipLibCheck": true + } +} diff --git a/examples/solid/router-monorepo-solid-query/packages/app/vite.config.ts b/examples/solid/router-monorepo-solid-query/packages/app/vite.config.ts new file mode 100644 index 00000000000..da736445be1 --- /dev/null +++ b/examples/solid/router-monorepo-solid-query/packages/app/vite.config.ts @@ -0,0 +1,37 @@ +import { fileURLToPath } from 'node:url' +import * as path from 'node:path' +import { defineConfig } from 'vite' +import dts from 'vite-plugin-dts' +import solid from 'vite-plugin-solid' + +const __filename = fileURLToPath(import.meta.url) +const __dirname = path.dirname(__filename) + +export default defineConfig({ + root: __dirname, + cacheDir: '../../node_modules/.vite/packages/router', + + server: { + port: 4200, + host: 'localhost', + }, + + preview: { + port: 4300, + host: 'localhost', + }, + plugins: [ + dts({ + entryRoot: 'src', + tsconfigPath: path.join(__dirname, 'tsconfig.json'), + }), + solid(), + ], + build: { + outDir: './dist', + emptyOutDir: true, + commonjsOptions: { + transformMixedEsModules: true, + }, + }, +}) diff --git a/examples/solid/router-monorepo-solid-query/packages/post-feature/package.json b/examples/solid/router-monorepo-solid-query/packages/post-feature/package.json new file mode 100644 index 00000000000..add3eb11732 --- /dev/null +++ b/examples/solid/router-monorepo-solid-query/packages/post-feature/package.json @@ -0,0 +1,22 @@ +{ + "name": "@router-solid-mono-solid-query/post-feature", + "private": true, + "type": "module", + "scripts": { + "build": "vite build && tsc --noEmit" + }, + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "dependencies": { + "@tanstack/solid-query": "^5.90.0", + "@router-solid-mono-solid-query/post-query": "workspace:*", + "@router-solid-mono-solid-query/router": "workspace:*", + "solid-js": "^1.9.10" + }, + "devDependencies": { + "vite-plugin-solid": "^2.11.10", + "typescript": "^5.7.2", + "vite": "^7.1.7", + "vite-plugin-dts": "^4.5.4" + } +} diff --git a/examples/solid/router-monorepo-solid-query/packages/post-feature/src/PostError.tsx b/examples/solid/router-monorepo-solid-query/packages/post-feature/src/PostError.tsx new file mode 100644 index 00000000000..8a506434261 --- /dev/null +++ b/examples/solid/router-monorepo-solid-query/packages/post-feature/src/PostError.tsx @@ -0,0 +1,28 @@ +// import { createEffect } from 'solid-js' +import { PostNotFoundError } from '@router-solid-mono-solid-query/post-query' +// import { useQueryErrorResetBoundary } from '@tanstack/solid-query' +import { + ErrorComponent, + useRouter, +} from '@router-solid-mono-solid-query/router' +import type { ErrorComponentProps } from '@router-solid-mono-solid-query/router' + +export function PostErrorComponent({ error, reset }: ErrorComponentProps) { + const router = useRouter() + if (error instanceof PostNotFoundError) { + return
{error.message}
+ } + + return ( +
+ + +
+ ) +} diff --git a/examples/solid/router-monorepo-solid-query/packages/post-feature/src/PostIdPage.tsx b/examples/solid/router-monorepo-solid-query/packages/post-feature/src/PostIdPage.tsx new file mode 100644 index 00000000000..6bfc036be77 --- /dev/null +++ b/examples/solid/router-monorepo-solid-query/packages/post-feature/src/PostIdPage.tsx @@ -0,0 +1,18 @@ +import { useQuery } from '@tanstack/solid-query' +import { postQueryOptions } from '@router-solid-mono-solid-query/post-query' + +import { getRouteApi } from '@router-solid-mono-solid-query/router' + +const route = getRouteApi('/$postId') + +export function PostIdComponent() { + const postId = route.useParams()().postId + const { data: post } = useQuery(() => postQueryOptions(postId)) + + return ( +
+

{post?.title}

+
{post?.body}
+
+ ) +} diff --git a/examples/solid/router-monorepo-solid-query/packages/post-feature/src/PostList.tsx b/examples/solid/router-monorepo-solid-query/packages/post-feature/src/PostList.tsx new file mode 100644 index 00000000000..06ce0bf9c11 --- /dev/null +++ b/examples/solid/router-monorepo-solid-query/packages/post-feature/src/PostList.tsx @@ -0,0 +1,36 @@ +import { useQuery } from '@tanstack/solid-query' +import { postsQueryOptions } from '@router-solid-mono-solid-query/post-query' +import { Link, Outlet } from '@router-solid-mono-solid-query/router' + +export function PostsListComponent() { + const postsQuery = useQuery(() => postsQueryOptions) + const posts = postsQuery.data + + return ( +
+
    + {[ + ...(posts || []), + { id: 'i-do-not-exist', title: 'Non-existent Post' }, + ].map((post) => { + return ( +
  • + +
    {post.title.substring(0, 20)}
    + +
  • + ) + })} +
+
+ +
+ ) +} diff --git a/examples/solid/router-monorepo-solid-query/packages/post-feature/src/index.ts b/examples/solid/router-monorepo-solid-query/packages/post-feature/src/index.ts new file mode 100644 index 00000000000..2cf25f117d0 --- /dev/null +++ b/examples/solid/router-monorepo-solid-query/packages/post-feature/src/index.ts @@ -0,0 +1,3 @@ +export * from './PostIdPage' +export * from './PostList' +export * from './PostError' diff --git a/examples/solid/router-monorepo-solid-query/packages/post-feature/tsconfig.json b/examples/solid/router-monorepo-solid-query/packages/post-feature/tsconfig.json new file mode 100644 index 00000000000..a972899814c --- /dev/null +++ b/examples/solid/router-monorepo-solid-query/packages/post-feature/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "strict": true, + "esModuleInterop": true, + "jsx": "preserve", + "jsxImportSource": "solid-js", + "target": "ESNext", + "moduleResolution": "Bundler", + "module": "ESNext", + "lib": ["DOM", "DOM.Iterable", "ES2022"], + "skipLibCheck": true + } +} diff --git a/examples/solid/router-monorepo-solid-query/packages/post-feature/vite.config.ts b/examples/solid/router-monorepo-solid-query/packages/post-feature/vite.config.ts new file mode 100644 index 00000000000..a195b2687d9 --- /dev/null +++ b/examples/solid/router-monorepo-solid-query/packages/post-feature/vite.config.ts @@ -0,0 +1,44 @@ +import * as path from 'node:path' +import { fileURLToPath } from 'node:url' +import { defineConfig } from 'vite' +import dts from 'vite-plugin-dts' +import solid from 'vite-plugin-solid' + +const __filename = fileURLToPath(import.meta.url) +const __dirname = path.dirname(__filename) + +export default defineConfig({ + root: __dirname, + cacheDir: '../../node_modules/.vite/packages/router', + plugins: [ + dts({ + entryRoot: 'src', + tsconfigPath: path.join(__dirname, 'tsconfig.json'), + }), + solid(), + ], + build: { + outDir: './dist', + emptyOutDir: true, + commonjsOptions: { + transformMixedEsModules: true, + }, + lib: { + entry: 'src/index.ts', + name: 'router', + fileName: 'index', + formats: ['es'], + }, + rollupOptions: { + // External packages that should not be bundled into your library. + external: [ + 'solid-js', + 'solid-js/web', + '@tanstack/solid-query', + '@tanstack/solid-router', + '@router-solid-mono-solid-query/post-query', + '@router-solid-mono-solid-query/router', + ], + }, + }, +}) diff --git a/examples/solid/router-monorepo-solid-query/packages/post-query/package.json b/examples/solid/router-monorepo-solid-query/packages/post-query/package.json new file mode 100644 index 00000000000..d1d066701f9 --- /dev/null +++ b/examples/solid/router-monorepo-solid-query/packages/post-query/package.json @@ -0,0 +1,20 @@ +{ + "name": "@router-solid-mono-solid-query/post-query", + "private": true, + "type": "module", + "scripts": { + "build": "vite build && tsc --noEmit" + }, + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "dependencies": { + "@tanstack/solid-query": "^5.90.0", + "redaxios": "^0.5.1", + "zod": "^3.24.2" + }, + "devDependencies": { + "vite-plugin-solid": "^2.11.10", + "typescript": "^5.7.2", + "vite": "^7.1.7" + } +} diff --git a/examples/solid/router-monorepo-solid-query/packages/post-query/src/index.ts b/examples/solid/router-monorepo-solid-query/packages/post-query/src/index.ts new file mode 100644 index 00000000000..3adf336fea6 --- /dev/null +++ b/examples/solid/router-monorepo-solid-query/packages/post-query/src/index.ts @@ -0,0 +1,3 @@ +export * from './postQueryOptions' +export * from './postsQueryOptions' +export { PostNotFoundError } from './posts' diff --git a/examples/solid/router-monorepo-solid-query/packages/post-query/src/postQueryOptions.tsx b/examples/solid/router-monorepo-solid-query/packages/post-query/src/postQueryOptions.tsx new file mode 100644 index 00000000000..fe118d7bdea --- /dev/null +++ b/examples/solid/router-monorepo-solid-query/packages/post-query/src/postQueryOptions.tsx @@ -0,0 +1,8 @@ +import { queryOptions } from '@tanstack/solid-query' +import { fetchPost } from './posts' + +export const postQueryOptions = (postId: string) => + queryOptions({ + queryKey: ['posts', { postId }], + queryFn: () => fetchPost(postId), + }) diff --git a/examples/solid/router-monorepo-solid-query/packages/post-query/src/posts.tsx b/examples/solid/router-monorepo-solid-query/packages/post-query/src/posts.tsx new file mode 100644 index 00000000000..d551659b92e --- /dev/null +++ b/examples/solid/router-monorepo-solid-query/packages/post-query/src/posts.tsx @@ -0,0 +1,33 @@ +import axios from 'redaxios' + +export type PostType = { + id: string + title: string + body: string +} + +export class PostNotFoundError extends Error {} + +export const fetchPost = async (postId: string) => { + console.info(`Fetching post with id ${postId}...`) + await new Promise((r) => setTimeout(r, 500)) + const post = await axios + .get(`https://jsonplaceholder.typicode.com/posts/${postId}`) + .then((r) => r.data) + .catch((err) => { + if (err.status === 404) { + throw new PostNotFoundError(`Post with id "${postId}" not found!`) + } + throw err + }) + + return post +} + +export const fetchPosts = async () => { + console.info('Fetching posts...') + await new Promise((r) => setTimeout(r, 500)) + return axios + .get>('https://jsonplaceholder.typicode.com/posts') + .then((r) => r.data.slice(0, 10)) +} diff --git a/examples/solid/router-monorepo-solid-query/packages/post-query/src/postsQueryOptions.tsx b/examples/solid/router-monorepo-solid-query/packages/post-query/src/postsQueryOptions.tsx new file mode 100644 index 00000000000..ce59b8a0bb4 --- /dev/null +++ b/examples/solid/router-monorepo-solid-query/packages/post-query/src/postsQueryOptions.tsx @@ -0,0 +1,7 @@ +import { queryOptions } from '@tanstack/solid-query' +import { fetchPosts } from './posts' + +export const postsQueryOptions = queryOptions({ + queryKey: ['posts'], + queryFn: () => fetchPosts(), +}) diff --git a/examples/solid/router-monorepo-solid-query/packages/post-query/tsconfig.json b/examples/solid/router-monorepo-solid-query/packages/post-query/tsconfig.json new file mode 100644 index 00000000000..a972899814c --- /dev/null +++ b/examples/solid/router-monorepo-solid-query/packages/post-query/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "strict": true, + "esModuleInterop": true, + "jsx": "preserve", + "jsxImportSource": "solid-js", + "target": "ESNext", + "moduleResolution": "Bundler", + "module": "ESNext", + "lib": ["DOM", "DOM.Iterable", "ES2022"], + "skipLibCheck": true + } +} diff --git a/examples/solid/router-monorepo-solid-query/packages/post-query/vite.config.ts b/examples/solid/router-monorepo-solid-query/packages/post-query/vite.config.ts new file mode 100644 index 00000000000..ca7192f9e9d --- /dev/null +++ b/examples/solid/router-monorepo-solid-query/packages/post-query/vite.config.ts @@ -0,0 +1,40 @@ +import { fileURLToPath } from 'node:url' +import * as path from 'node:path' +import { defineConfig } from 'vite' +import dts from 'vite-plugin-dts' + +const __filename = fileURLToPath(import.meta.url) +const __dirname = path.dirname(__filename) + +export default defineConfig({ + root: __dirname, + cacheDir: '../../node_modules/.vite/packages/router', + plugins: [ + dts({ + entryRoot: 'src', + tsconfigPath: path.join(__dirname, 'tsconfig.json'), + }), + ], + build: { + outDir: './dist', + emptyOutDir: true, + commonjsOptions: { + transformMixedEsModules: true, + }, + lib: { + entry: 'src/index.ts', + name: 'router', + fileName: 'index', + formats: ['es'], + }, + rollupOptions: { + // External packages that should not be bundled into your library. + external: [ + 'solid-js', + 'solid-js/web', + '@tanstack/solid-query', + 'redaxios', + ], + }, + }, +}) diff --git a/examples/solid/router-monorepo-solid-query/packages/router/package.json b/examples/solid/router-monorepo-solid-query/packages/router/package.json new file mode 100644 index 00000000000..a8fe0d264f0 --- /dev/null +++ b/examples/solid/router-monorepo-solid-query/packages/router/package.json @@ -0,0 +1,26 @@ +{ + "name": "@router-solid-mono-solid-query/router", + "private": true, + "type": "module", + "scripts": { + "build": "vite build && tsc --noEmit" + }, + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "dependencies": { + "@tanstack/history": "^1.133.28", + "@tanstack/solid-query": "^5.90.0", + "@tanstack/solid-router": "^1.135.2", + "@tanstack/router-plugin": "^1.135.2", + "@router-solid-mono-solid-query/post-query": "workspace:*", + "redaxios": "^0.5.1", + "zod": "^3.24.2", + "solid-js": "^1.9.10" + }, + "devDependencies": { + "vite-plugin-solid": "^2.11.10", + "typescript": "^5.7.2", + "vite": "^7.1.7", + "vite-plugin-dts": "^4.5.4" + } +} diff --git a/examples/solid/router-monorepo-solid-query/packages/router/src/index.ts b/examples/solid/router-monorepo-solid-query/packages/router/src/index.ts new file mode 100644 index 00000000000..86b1b7e867d --- /dev/null +++ b/examples/solid/router-monorepo-solid-query/packages/router/src/index.ts @@ -0,0 +1,25 @@ +import { queryClient, router } from './router' + +export type { RouterType, RouterIds } from './router' + +// Register the router instance for type safety +declare module '@tanstack/solid-router' { + interface Register { + router: typeof router + } +} + +export { router, queryClient } + +// By re exporting the api from TanStack router, we can enforce that other packages +// rely on this one instead, making the type register being applied +export { + Outlet, + Link, + useRouteContext, + useRouter, + RouterProvider, + getRouteApi, + ErrorComponent, +} from '@tanstack/solid-router' +export type { ErrorComponentProps } from '@tanstack/solid-router' diff --git a/examples/solid/router-monorepo-solid-query/packages/router/src/routeTree.gen.ts b/examples/solid/router-monorepo-solid-query/packages/router/src/routeTree.gen.ts new file mode 100644 index 00000000000..e8f1a5370ba --- /dev/null +++ b/examples/solid/router-monorepo-solid-query/packages/router/src/routeTree.gen.ts @@ -0,0 +1,77 @@ +/* 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 PostIdRouteImport } from './routes/$postId' +import { Route as IndexRouteImport } from './routes/index' + +const PostIdRoute = PostIdRouteImport.update({ + id: '/$postId', + path: '/$postId', + getParentRoute: () => rootRouteImport, +} as any) +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => rootRouteImport, +} as any) + +export interface FileRoutesByFullPath { + '/': typeof IndexRoute + '/$postId': typeof PostIdRoute +} +export interface FileRoutesByTo { + '/': typeof IndexRoute + '/$postId': typeof PostIdRoute +} +export interface FileRoutesById { + __root__: typeof rootRouteImport + '/': typeof IndexRoute + '/$postId': typeof PostIdRoute +} +export interface FileRouteTypes { + fileRoutesByFullPath: FileRoutesByFullPath + fullPaths: '/' | '/$postId' + fileRoutesByTo: FileRoutesByTo + to: '/' | '/$postId' + id: '__root__' | '/' | '/$postId' + fileRoutesById: FileRoutesById +} +export interface RootRouteChildren { + IndexRoute: typeof IndexRoute + PostIdRoute: typeof PostIdRoute +} + +declare module '@tanstack/solid-router' { + interface FileRoutesByPath { + '/$postId': { + id: '/$postId' + path: '/$postId' + fullPath: '/$postId' + preLoaderRoute: typeof PostIdRouteImport + parentRoute: typeof rootRouteImport + } + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport + parentRoute: typeof rootRouteImport + } + } +} + +const rootRouteChildren: RootRouteChildren = { + IndexRoute: IndexRoute, + PostIdRoute: PostIdRoute, +} +export const routeTree = rootRouteImport + ._addFileChildren(rootRouteChildren) + ._addFileTypes() diff --git a/examples/solid/router-monorepo-solid-query/packages/router/src/router.tsx b/examples/solid/router-monorepo-solid-query/packages/router/src/router.tsx new file mode 100644 index 00000000000..6153a54a8bc --- /dev/null +++ b/examples/solid/router-monorepo-solid-query/packages/router/src/router.tsx @@ -0,0 +1,30 @@ +import React from 'react' +import { createRouter } from '@tanstack/solid-router' + +// Import the generated route tree +import { QueryClient } from '@tanstack/solid-query' +import { routeTree } from './routeTree.gen' +import type { RouteIds } from '@tanstack/solid-router' + +export const queryClient = new QueryClient() + +// Set up a Router instance +export const router = createRouter({ + routeTree, + context: { + queryClient, + }, + defaultPendingComponent: () => ( +
Loading form global pending component...
+ ), + // This make the loader only wait 200ms before showing the pending component, instead of the default 1000ms + defaultPendingMs: 200, + defaultPreload: 'intent', + // Since we're using React Query, we don't want loader calls to ever be stale + // This will ensure that the loader is always called when the route is preloaded or visited + defaultPreloadStaleTime: 0, + scrollRestoration: true, +}) + +export type RouterType = typeof router +export type RouterIds = RouteIds diff --git a/examples/solid/router-monorepo-solid-query/packages/router/src/routes/$postId.ts b/examples/solid/router-monorepo-solid-query/packages/router/src/routes/$postId.ts new file mode 100644 index 00000000000..4f02efa7420 --- /dev/null +++ b/examples/solid/router-monorepo-solid-query/packages/router/src/routes/$postId.ts @@ -0,0 +1,8 @@ +import { createFileRoute } from '@tanstack/solid-router' +import { postQueryOptions } from '@router-solid-mono-solid-query/post-query' + +export const Route = createFileRoute('/$postId')({ + loader: ({ context: { queryClient }, params: { postId } }) => { + return queryClient.ensureQueryData(postQueryOptions(postId)) + }, +}) diff --git a/examples/solid/router-monorepo-solid-query/packages/router/src/routes/__root.tsx b/examples/solid/router-monorepo-solid-query/packages/router/src/routes/__root.tsx new file mode 100644 index 00000000000..781855fe150 --- /dev/null +++ b/examples/solid/router-monorepo-solid-query/packages/router/src/routes/__root.tsx @@ -0,0 +1,15 @@ +import { Link, createRootRouteWithContext } from '@tanstack/solid-router' +import type { QueryClient } from '@tanstack/solid-query' + +export const Route = createRootRouteWithContext<{ + queryClient: QueryClient +}>()({ + notFoundComponent: () => { + return ( +
+

This is the notFoundComponent configured on root route

+ Start Over +
+ ) + }, +}) diff --git a/examples/solid/router-monorepo-solid-query/packages/router/src/routes/index.ts b/examples/solid/router-monorepo-solid-query/packages/router/src/routes/index.ts new file mode 100644 index 00000000000..decabd3561a --- /dev/null +++ b/examples/solid/router-monorepo-solid-query/packages/router/src/routes/index.ts @@ -0,0 +1,8 @@ +import { createFileRoute } from '@tanstack/solid-router' +import { postsQueryOptions } from '@router-solid-mono-solid-query/post-query' + +export const Route = createFileRoute('/')({ + loader: ({ context: { queryClient } }) => { + return queryClient.ensureQueryData(postsQueryOptions) + }, +}) diff --git a/examples/solid/router-monorepo-solid-query/packages/router/tsconfig.json b/examples/solid/router-monorepo-solid-query/packages/router/tsconfig.json new file mode 100644 index 00000000000..b986b1be622 --- /dev/null +++ b/examples/solid/router-monorepo-solid-query/packages/router/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "strict": true, + "esModuleInterop": true, + "jsx": "preserve", + "jsxImportSource": "solid-js", + "target": "ESNext", + "moduleResolution": "Bundler", + "module": "ESNext", + "lib": ["DOM", "DOM.Iterable", "ES2022"], + "skipLibCheck": true + }, + "include": ["src"] +} diff --git a/examples/solid/router-monorepo-solid-query/packages/router/vite.config.ts b/examples/solid/router-monorepo-solid-query/packages/router/vite.config.ts new file mode 100644 index 00000000000..438ad4b27f6 --- /dev/null +++ b/examples/solid/router-monorepo-solid-query/packages/router/vite.config.ts @@ -0,0 +1,45 @@ +import { defineConfig } from 'vite' +import dts from 'vite-plugin-dts' +import * as path from 'node:path' +import { fileURLToPath } from 'node:url' +import { tanstackRouter } from '@tanstack/router-plugin/vite' +import solid from 'vite-plugin-solid' + +const __filename = fileURLToPath(import.meta.url) +const __dirname = path.dirname(__filename) + +export default defineConfig({ + root: __dirname, + cacheDir: '../../node_modules/.vite/packages/router', + plugins: [ + dts({ + entryRoot: 'src', + tsconfigPath: path.join(__dirname, 'tsconfig.json'), + }), + solid(), + tanstackRouter({ target: 'solid' }), + ], + build: { + outDir: './dist', + emptyOutDir: true, + commonjsOptions: { + transformMixedEsModules: true, + }, + lib: { + entry: 'src/index.ts', + name: 'router', + fileName: 'index', + formats: ['es'], + }, + rollupOptions: { + // External packages that should not be bundled into your library. + external: [ + 'solid-js', + 'solid-js/web', + '@tanstack/solid-query', + '@tanstack/solid-router', + '@router-solid-mono-solid-query/post-query', + ], + }, + }, +}) diff --git a/examples/solid/router-monorepo-solid-query/pnpm-workspace.yaml.example b/examples/solid/router-monorepo-solid-query/pnpm-workspace.yaml.example new file mode 100644 index 00000000000..4340350e193 --- /dev/null +++ b/examples/solid/router-monorepo-solid-query/pnpm-workspace.yaml.example @@ -0,0 +1,2 @@ +packages: + - 'packages/*' \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 24fcc5fcff0..265d37d3df1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7844,6 +7844,46 @@ importers: specifier: ^5.2.0 version: 5.2.0(webpack-cli@5.1.4)(webpack@5.97.1) + examples/solid/router-monorepo-solid-query: + dependencies: + '@tanstack/router-plugin': + specifier: workspace:* + version: link:../../../packages/router-plugin + '@tanstack/solid-query': + specifier: ^5.90.0 + version: 5.90.9(solid-js@1.9.10) + '@tanstack/solid-query-devtools': + specifier: ^5.90.0 + version: 5.90.4(@tanstack/solid-query@5.90.9(solid-js@1.9.10))(solid-js@1.9.10) + '@tanstack/solid-router': + specifier: ^1.135.2 + version: link:../../../packages/solid-router + '@tanstack/solid-router-devtools': + specifier: workspace:^ + version: link:../../../packages/solid-router-devtools + redaxios: + specifier: ^0.5.1 + version: 0.5.1 + solid-js: + specifier: 1.9.10 + version: 1.9.10 + devDependencies: + '@types/node': + specifier: 22.10.2 + version: 22.10.2 + typescript: + specifier: ^5.7.2 + version: 5.9.2 + vite: + specifier: ^7.1.7 + version: 7.1.7(@types/node@22.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.8.1) + vite-plugin-dts: + specifier: 4.0.3 + version: 4.0.3(@types/node@22.10.2)(rollup@4.52.5)(typescript@5.9.2)(vite@7.1.7(@types/node@22.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.8.1)) + vite-plugin-solid: + specifier: ^2.11.10 + version: 2.11.10(@testing-library/jest-dom@6.6.3)(solid-js@1.9.10)(vite@7.1.7(@types/node@22.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.37.0)(tsx@4.20.3)(yaml@2.8.1)) + examples/solid/scroll-restoration: dependencies: '@tailwindcss/postcss':