diff --git a/docs/router/framework/solid/installation/with-esbuild.md b/docs/router/framework/solid/installation/with-esbuild.md new file mode 100644 index 00000000000..5ec22130507 --- /dev/null +++ b/docs/router/framework/solid/installation/with-esbuild.md @@ -0,0 +1,99 @@ +--- +title: Installation with Esbuild +--- + +[//]: # 'BundlerConfiguration' + +To use file-based routing with **Esbuild**, you'll need to install the `@tanstack/router-plugin` package. + +```sh +npm install -D @tanstack/router-plugin +``` + +Once installed, you'll need to add the plugin to your configuration. + +```tsx +// build.js +import * as esbuild from 'esbuild' +import { solidPlugin } from 'esbuild-plugin-solid' +import { tanstackRouter } from '@tanstack/router-plugin/esbuild' + +const isDev = process.argv.includes('--dev') + +const ctx = await esbuild.context({ + entryPoints: ['src/main.tsx'], + outfile: 'dist/main.js', + minify: !isDev, + bundle: true, + format: 'esm', + target: ['esnext'], + sourcemap: true, + plugins: [ + solidPlugin(), + tanstackRouter({ target: 'solid', autoCodeSplitting: true }), + ], +}) + +if (isDev) { + await ctx.watch() + const { host, port } = await ctx.serve({ servedir: '.', port: 3005 }) + console.log(`Server running at http://${host || 'localhost'}:${port}`) +} else { + await ctx.rebuild() + await ctx.dispose() +} +``` + +Or, you can clone our [Quickstart Esbuild example](https://github.com/TanStack/router/tree/main/examples/solid/quickstart-esbuild-file-based) and get started. + +Now that you've added the plugin to your Esbuild configuration, you're all set to start using file-based routing with TanStack Router. + +[//]: # 'BundlerConfiguration' + +## Ignoring the generated route tree file + +If your project is configured to use a linter and/or formatter, you may want to ignore the generated route tree file. This file is managed by TanStack Router and therefore shouldn't be changed by your linter or formatter. + +Here are some resources to help you ignore the generated route tree file: + +- Prettier - [https://prettier.io/docs/en/ignore.html#ignoring-files-prettierignore](https://prettier.io/docs/en/ignore.html#ignoring-files-prettierignore) +- ESLint - [https://eslint.org/docs/latest/use/configure/ignore#ignoring-files](https://eslint.org/docs/latest/use/configure/ignore#ignoring-files) +- Biome - [https://biomejs.dev/reference/configuration/#filesignore](https://biomejs.dev/reference/configuration/#filesignore) + +> [!WARNING] +> If you are using VSCode, you may experience the route tree file unexpectedly open (with errors) after renaming a route. + +You can prevent that from the VSCode settings by marking the file as readonly. Our recommendation is to also exclude it from search results and file watcher with the following settings: + +```json +{ + "files.readonlyInclude": { + "**/routeTree.gen.ts": true + }, + "files.watcherExclude": { + "**/routeTree.gen.ts": true + }, + "search.exclude": { + "**/routeTree.gen.ts": true + } +} +``` + +You can use those settings either at a user level or only for a single workspace by creating the file `.vscode/settings.json` at the root of your project. + +## Configuration + +When using the TanStack Router Plugin with Esbuild for File-based routing, it comes with some sane defaults that should work for most projects: + +```json +{ + "routesDirectory": "./src/routes", + "generatedRouteTree": "./src/routeTree.gen.ts", + "routeFileIgnorePrefix": "-", + "quoteStyle": "single" +} +``` + +If these defaults work for your project, you don't need to configure anything at all! However, if you need to customize the configuration, you can do so by editing the configuration object passed into the `tanstackRouter` function. + +You can find all the available configuration options in the [File-based Routing API Reference](../../../../api/file-based-routing.md). diff --git a/examples/solid/quickstart-esbuild-file-based/.gitignore b/examples/solid/quickstart-esbuild-file-based/.gitignore new file mode 100644 index 00000000000..d451ff16c10 --- /dev/null +++ b/examples/solid/quickstart-esbuild-file-based/.gitignore @@ -0,0 +1,5 @@ +node_modules +.DS_Store +dist +dist-ssr +*.local diff --git a/examples/solid/quickstart-esbuild-file-based/.vscode/settings.json b/examples/solid/quickstart-esbuild-file-based/.vscode/settings.json new file mode 100644 index 00000000000..00b5278e580 --- /dev/null +++ b/examples/solid/quickstart-esbuild-file-based/.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/quickstart-esbuild-file-based/README.md b/examples/solid/quickstart-esbuild-file-based/README.md new file mode 100644 index 00000000000..115199d292c --- /dev/null +++ b/examples/solid/quickstart-esbuild-file-based/README.md @@ -0,0 +1,6 @@ +# Example + +To run this example: + +- `npm install` or `yarn` +- `npm start` or `yarn start` diff --git a/examples/solid/quickstart-esbuild-file-based/build.js b/examples/solid/quickstart-esbuild-file-based/build.js new file mode 100644 index 00000000000..090e2f2721c --- /dev/null +++ b/examples/solid/quickstart-esbuild-file-based/build.js @@ -0,0 +1,29 @@ +#!/usr/bin/env node +import * as esbuild from 'esbuild' +import { solidPlugin } from 'esbuild-plugin-solid' +import { tanstackRouter } from '@tanstack/router-plugin/esbuild' + +const isDev = process.argv.includes('--dev') + +const ctx = await esbuild.context({ + entryPoints: ['src/main.tsx'], + outfile: 'dist/main.js', + minify: !isDev, + bundle: true, + format: 'esm', + target: ['esnext'], + sourcemap: true, + plugins: [ + solidPlugin(), + tanstackRouter({ target: 'solid', autoCodeSplitting: true }), + ], +}) + +if (isDev) { + await ctx.watch() + const { host, port } = await ctx.serve({ servedir: '.', port: 3005 }) + console.log(`Server running at http://${host || 'localhost'}:${port}`) +} else { + await ctx.rebuild() + await ctx.dispose() +} diff --git a/examples/solid/quickstart-esbuild-file-based/index.html b/examples/solid/quickstart-esbuild-file-based/index.html new file mode 100644 index 00000000000..b67448091c2 --- /dev/null +++ b/examples/solid/quickstart-esbuild-file-based/index.html @@ -0,0 +1,24 @@ + + + + + + Vite App + + + + +
+ + + diff --git a/examples/solid/quickstart-esbuild-file-based/package.json b/examples/solid/quickstart-esbuild-file-based/package.json new file mode 100644 index 00000000000..58dd42a3713 --- /dev/null +++ b/examples/solid/quickstart-esbuild-file-based/package.json @@ -0,0 +1,23 @@ +{ + "name": "tanstack-router-solid-example-quickstart-esbuild-file-based", + "private": true, + "type": "module", + "scripts": { + "dev": "node build.js --dev", + "build": "node build.js", + "serve": "node build.js --dev", + "start": "dev" + }, + "dependencies": { + "@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", + "zod": "^3.24.2" + }, + "devDependencies": { + "esbuild": "^0.25.0", + "esbuild-plugin-solid": "^0.6.0" + } +} diff --git a/examples/solid/quickstart-esbuild-file-based/src/main.tsx b/examples/solid/quickstart-esbuild-file-based/src/main.tsx new file mode 100644 index 00000000000..820c4eae6f0 --- /dev/null +++ b/examples/solid/quickstart-esbuild-file-based/src/main.tsx @@ -0,0 +1,23 @@ +import { render } from 'solid-js/web' +import { RouterProvider, createRouter } from '@tanstack/solid-router' +import { routeTree } from './routeTree.gen' + +// Set up a Router instance +const router = createRouter({ + routeTree, + defaultPreload: 'intent', + scrollRestoration: true, +}) + +// Register things for typesafety +declare module '@tanstack/solid-router' { + interface Register { + router: typeof router + } +} + +const rootElement = document.getElementById('app')! + +if (!rootElement.innerHTML) { + render(() => , rootElement) +} diff --git a/examples/solid/quickstart-esbuild-file-based/src/posts.tsx b/examples/solid/quickstart-esbuild-file-based/src/posts.tsx new file mode 100644 index 00000000000..d551659b92e --- /dev/null +++ b/examples/solid/quickstart-esbuild-file-based/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/quickstart-esbuild-file-based/src/routeTree.gen.ts b/examples/solid/quickstart-esbuild-file-based/src/routeTree.gen.ts new file mode 100644 index 00000000000..3cf641e77e9 --- /dev/null +++ b/examples/solid/quickstart-esbuild-file-based/src/routeTree.gen.ts @@ -0,0 +1,111 @@ +/* prettier-ignore-start */ + +/* eslint-disable */ + +// @ts-nocheck + +// noinspection JSUnusedGlobalSymbols + +// This file is auto-generated by TanStack Router + +// Import Routes + +import { Route as rootRoute } from './routes/__root' +import { Route as AboutImport } from './routes/about' +import { Route as IndexImport } from './routes/index' + +// Create/Update Routes + +const AboutRoute = AboutImport.update({ + path: '/about', + getParentRoute: () => rootRoute, +} as any) + +const IndexRoute = IndexImport.update({ + path: '/', + getParentRoute: () => rootRoute, +} as any) + +// Populate the FileRoutesByPath interface + +declare module '@tanstack/solid-router' { + interface FileRoutesByPath { + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexImport + parentRoute: typeof rootRoute + } + '/about': { + id: '/about' + path: '/about' + fullPath: '/about' + preLoaderRoute: typeof AboutImport + parentRoute: typeof rootRoute + } + } +} + +// Create and export the route tree + +export interface FileRoutesByFullPath { + '/': typeof IndexRoute + '/about': typeof AboutRoute +} + +export interface FileRoutesByTo { + '/': typeof IndexRoute + '/about': typeof AboutRoute +} + +export interface FileRoutesById { + __root__: typeof rootRoute + '/': typeof IndexRoute + '/about': typeof AboutRoute +} + +export interface FileRouteTypes { + fileRoutesByFullPath: FileRoutesByFullPath + fullPaths: '/' | '/about' + fileRoutesByTo: FileRoutesByTo + to: '/' | '/about' + id: '__root__' | '/' | '/about' + fileRoutesById: FileRoutesById +} + +export interface RootRouteChildren { + IndexRoute: typeof IndexRoute + AboutRoute: typeof AboutRoute +} + +const rootRouteChildren: RootRouteChildren = { + IndexRoute: IndexRoute, + AboutRoute: AboutRoute, +} + +export const routeTree = rootRoute + ._addFileChildren(rootRouteChildren) + ._addFileTypes() + +/* prettier-ignore-end */ + +/* ROUTE_MANIFEST_START +{ + "routes": { + "__root__": { + "filePath": "__root.tsx", + "children": [ + "/", + "/about" + ] + }, + "/": { + "filePath": "index.tsx" + }, + "/about": { + "filePath": "about.tsx" + } + } +} +ROUTE_MANIFEST_END */ diff --git a/examples/solid/quickstart-esbuild-file-based/src/routes/__root.tsx b/examples/solid/quickstart-esbuild-file-based/src/routes/__root.tsx new file mode 100644 index 00000000000..e7ab4e99f11 --- /dev/null +++ b/examples/solid/quickstart-esbuild-file-based/src/routes/__root.tsx @@ -0,0 +1,35 @@ +import { Link, Outlet, createRootRoute } from '@tanstack/solid-router' +import { TanStackRouterDevtools } from '@tanstack/solid-router-devtools' + +export const Route = createRootRoute({ + component: RootComponent, +}) + +function RootComponent() { + return ( + <> +
+ + Home + {' '} + + About + +
+
+ + + + ) +} diff --git a/examples/solid/quickstart-esbuild-file-based/src/routes/about.tsx b/examples/solid/quickstart-esbuild-file-based/src/routes/about.tsx new file mode 100644 index 00000000000..45962235134 --- /dev/null +++ b/examples/solid/quickstart-esbuild-file-based/src/routes/about.tsx @@ -0,0 +1,13 @@ +import { createFileRoute } from '@tanstack/solid-router' + +export const Route = createFileRoute('/about')({ + component: AboutComponent, +}) + +function AboutComponent() { + return ( +
+

About

+
+ ) +} diff --git a/examples/solid/quickstart-esbuild-file-based/src/routes/index.tsx b/examples/solid/quickstart-esbuild-file-based/src/routes/index.tsx new file mode 100644 index 00000000000..c71b5d33f2e --- /dev/null +++ b/examples/solid/quickstart-esbuild-file-based/src/routes/index.tsx @@ -0,0 +1,13 @@ +import { createFileRoute } from '@tanstack/solid-router' + +export const Route = createFileRoute('/')({ + component: HomeComponent, +}) + +function HomeComponent() { + return ( +
+

Welcome Home!

+
+ ) +} diff --git a/examples/solid/quickstart-esbuild-file-based/tsconfig.json b/examples/solid/quickstart-esbuild-file-based/tsconfig.json new file mode 100644 index 00000000000..6e3ba6f1103 --- /dev/null +++ b/examples/solid/quickstart-esbuild-file-based/tsconfig.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "strict": true, + "esModuleInterop": true, + "jsx": "preserve", + "jsxImportSource": "solid-js", + "lib": ["DOM", "DOM.Iterable", "ES2022"], + "skipLibCheck": true + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d923b085422..d5156a1109a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7281,6 +7281,34 @@ importers: 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/quickstart-esbuild-file-based: + dependencies: + '@tanstack/router-plugin': + specifier: workspace:* + version: link:../../../packages/router-plugin + '@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 + zod: + specifier: ^3.24.2 + version: 3.25.57 + devDependencies: + esbuild: + specifier: ^0.25.0 + version: 0.25.10 + esbuild-plugin-solid: + specifier: ^0.6.0 + version: 0.6.0(esbuild@0.25.10)(solid-js@1.9.10) + examples/solid/quickstart-file-based: dependencies: '@tailwindcss/postcss': @@ -26886,6 +26914,16 @@ snapshots: dependencies: es-errors: 1.3.0 + esbuild-plugin-solid@0.6.0(esbuild@0.25.10)(solid-js@1.9.10): + dependencies: + '@babel/core': 7.27.4 + '@babel/preset-typescript': 7.27.1(@babel/core@7.27.4) + babel-preset-solid: 1.9.3(@babel/core@7.27.4) + esbuild: 0.25.10 + solid-js: 1.9.10 + transitivePeerDependencies: + - supports-color + esbuild-plugin-solid@0.6.0(esbuild@0.25.4)(solid-js@1.9.10): dependencies: '@babel/core': 7.27.4